.. _running-slitherlink: Running the Slitherlink Problem =============================== Setting up properly the experimentation environment required to evaluate a solving approach can result in a time-consuming task, and also a source of bugs conducting to wrong evaluations. OptiLog provides support in this sense automating as much as possible some parts of the process. Now that we have a *tuned* configuration for our ``solve_slitherlink`` function (see :ref:`tuning-slitherlink`), we are interested on comparing its performance with respect to the default one. To achieve that we will create an execution scenario by using OptiLog's *Running* module: .. _slither-exec: .. code:: python :number-lines: from optilog.running import RunningScenario from optilog.blackbox import ExecutionConstraints, RunSolver from slitherlink import solve_slitherlink def solve_slitherlink_no_remove(instance, seed): return solve_slitherlink(instance, seed, remove_unconstr_subcycles=False) if __name__ == "__main__": runner = RunningScenario( solvers={ "default-remove-subcycles": solve_slitherlink, "default-no-remove": solve_slitherlink_no_remove, "gga": "./configs/best-conf.sh", }, tasks="./instances/test/*.txt", submit_file="./enque.sh", constraints=ExecutionConstraints( s_virtual_memory="6G", s_wall_time=300, enforcer=RunSolver(), ), slots=1, seeds=[1, 2, 3], unbuffer=True, ) runner.generate_scenario(scenario_dir="./default_running") First, we describe the settings of our scenario. In particular, we will run the *default* configuration (which removes the unconstrained subcycles) by executing the ``solve_slitherlink`` function, the *default* configuration without removing the unconstrained subcycles (the ``solve_slitherlink_no_remove`` function) and the *tuned* configuration found by GGA (that OptiLog automatically extracted in the script ``./configs/best-conf.sh``). We generated another set of instances that are different to the ones used in the :ref:`Tuning process `. These instances are located by expanding the glob ``./instances/test/*.txt``. Other options such as the Wallclock time limit ``s_wall_time`` and the amount of memory available ``s_virtual_memory`` can be specified through ``ExecutionConstraints``. A list of seeds is provided to the ``seeds`` parameter such that each experiment will be executed for each of the seeds. By default, OptiLog incorporates compatibility for two optional tools, ``unbuffer`` :cite:p:`unbuffer2021`, to automatically flush to the log files and ``runsolver`` :cite:p:`runsolver`, to constraint the number of resources (time and memory) available to the process. In order to use these tools, they have to be available in the ``PATH``. OptiLog provides a running environment on which to execute tasks to a computing-agnostic backend. This means that the underlying tasks need to be delegated to a Job Scheduler like ``SGE`` :cite:p:`sge-10.5555/560889.792378` or ``Task Spooler`` :cite:p:`tsp2021` to get executed. The special parameter ``submit_file`` points to the script in charge of submitting each job to the scheduler. See the :ref:`submit-command-examples` section for some examples of submission commands for different backends. Finally, the method ``generate_scenario()`` of the :ref:`scenario generation code ` creates an scenario directory containing all the necessary settings to run the experiments (by default it will create a directory called ``default_running``). Then, the user can easily run these experiments by using the ``optilog-running`` command provided by OptiLog: .. _exp-launch: .. code-block:: console $ optilog-running default_running/ submit Your job 8575807 ("default-remove-subcycles") has been submitted (...) Your job 8576306 ("gga") has been submitted Unless a specific directory for storing the logs is indicated using the ``logs`` parameter, the directory ``./default_running/logs`` will be automatically created with subdirectories ``output`` and ``error`` where the logs for stdout and stderr will be stored. Processing Experimental Results ------------------------------- We include in OptiLog a new functionality within the *Running* module to automatically process the logs of the experiments. We show the code to parse the logs for the Slitherlink problem, and the output of its execution. .. _log-processing: .. code:: python :number-lines: from optilog.running import ParsingInfo, parse_scenario pi = ParsingInfo() pi.add_filter("res", r"^s (.+)", timestamp=True) df = parse_scenario("./default_running", parsing_info=pi) print(df.head()) def solved(col): return (col == "YES").sum() / col.shape[0] def parK(col, k=10): return col.fillna(1000 * 300 * k).mean() def stats(df, solver): print("* Pctg solved:", df[(solver, "res")].agg(solved)) print("* PAR10:", df[(solver, "time_res")].agg(parK), end="\n\n") print("DEFAULT (remove subcycles)") stats(df, "default-remove-subcycles") print("DEFAULT (no remove subcycles)") stats(df, "default-no-remove") print("GGA") stats(df, "gga") .. _log-processing-out: .. code-block:: console gga default-no-remove default-remove-subcycles res cpu_time_res wall_time_res res cpu_time_res wall_time_res res cpu_time_res wall_time_res instance seed 130.txt 1 YES 155.54 156.03 None NaN NaN None NaN NaN 108.txt 1 YES 96.19 97.24 None NaN NaN YES 264.00 269.59 145.txt 1 YES 145.39 146.96 YES 117.20 119.40 YES 123.47 125.48 109.txt 1 YES 288.89 293.30 YES 255.70 266.47 YES 257.62 263.16 136.txt 1 None NaN NaN YES 73.44 74.40 None NaN NaN DEFAULT (remove subcycles) * Pctg solved: 0.51 * PAR10: 1470064.2421999997 DEFAULT (no remove subcycles) * Pctg solved: 0.43 * PAR10: 1710052.2067000002 GGA * Pctg solved: 0.89 * PAR10: 330131.0026 The experiments are parsed line by line following a set of filters that are described with a ``ParsingInfo`` object. OptiLog includes template parsers for SAT and MaxSAT output following the SAT Competition and MaxSAT Evaluation :cite:p:`bacchus2020maxsat` output format. In this example, a ``ParsingInfo`` object is instantiated (line 3). Line 4 adds a filter that parses the result of the experiment (``s YES`` or ``s NO``), and also records the time at which the result is reported in milliseconds. More advanced options are also supported, such as the automatic casting of the parsed results and the retrieval of all the matches of the regular expression. The ``parse_scenario`` function call (line 6) parses the result of the experiments and returns a Pandas :cite:p:`reback2020pandas` dataframe with the parsed data. Lines 10-20 process the experiment results by using some basic Pandas functions. Finally, lines 23-28 show the final results of our experiments. In particular, we found that the automatic configuration of the ``solve_slitherlink`` function with the *Tuning* module and GGA allows us to solve about 25\% more instances than the default configuration, and improves the PAR10 score by about 66\%.