Skip to content

Commit

Permalink
Make parsing a dedicated experiment step. (#117)
Browse files Browse the repository at this point in the history
  • Loading branch information
jendrikseipp committed Oct 21, 2023
1 parent f9c2629 commit 4c01311
Show file tree
Hide file tree
Showing 31 changed files with 266 additions and 308 deletions.
8 changes: 4 additions & 4 deletions docs/downward.tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,15 @@ Run tutorial experiment

The files below are two experiment scripts, a ``project.py`` module that bundles
common functionality for all experiments related to the project, a parser
script, and a script for collecting results and making reports. You can use the
module, and a script for collecting results and making reports. You can use the
files as a basis for your own experiments. They are available in the `Lab repo
<https://github.com/aibasel/lab/tree/main/examples/downward>`_. Copy the files
into ``experiments/my-exp-dir``.

.. highlight:: bash

Make sure the experiment script and the parser are executable. Then you
can see the available steps with ::
Make sure the experiment script is executable. Then you can see the available
steps with ::

./2020-09-11-A-cg-vs-ff.py

Expand Down Expand Up @@ -171,7 +171,7 @@ reference on all Downward Lab classes.
.. literalinclude:: ../examples/downward/project.py
:caption:

.. literalinclude:: ../examples/downward/parser.py
.. literalinclude:: ../examples/downward/custom_parser.py
:caption:

.. literalinclude:: ../examples/downward/01-evaluation.py
Expand Down
43 changes: 37 additions & 6 deletions docs/faq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,43 @@ again as above.
I forgot to parse something. How can I run only the parsers again?
------------------------------------------------------------------

See the `parsing documentation <lab.parser.html>`_ for how to write
parsers. Once you have fixed your existing parsers or added new parsers,
add ``exp.add_parse_again_step()`` to your experiment script
``my-exp.py`` and then call ::

./my-exp.py parse-again
Now that parsing is done in its own experiment step, simply consult the `parsing
documentation <lab.parser.html>`_ for how to amend your parsers and then run the
"parse" experiment step again with ::

./my-exp.py parse


.. _portparsers:

How do I port my parsers to version 8.x?
----------------------------------------

Since version 8.0, Lab has a dedicated "parse" experiment step. First of all,
what are the benefits of this?

* No need to write parsers in separate files.
* Log output from solvers and parsers remains separate.
* No need for ``exp.add_parse_again_step()``. Parsing and re-parsing is now
exactly the same.
* Parsers are checked for syntax errors before the experiment is run.
* Parsing runs much faster (for an experiment with 3 algorithms and 5 parsers
the parsing time went down from 51 minutes to 5 minutes, both measured on
cold file system caches).
* As before, you can let the Slurm environment do the parsing for you and get
notified when the report is finished: ``./myexp.py build start parse fetch
report``

To adapt your parsers to this new API, you need to make the following changes:

* Your parser module (e.g., "custom_parser.py") does not have to be executable
anymore, but it must be importable and expose a :class:`Parser
<lab.parser.Parser>` instance (see the changes to the `translator parser
<https://github.com/aibasel/lab/pull/117/files#diff-0a679939eb576c6b402a00ab9b08a3339ecefe3713dc96f9ac6b0e05de9ff4f2>`_
for an example). Then, instead of ``exp.add_parser("custom_parser.py")`` use
``from custom_parser import MyParser`` and ``exp.add_parser(MyParser())``.
* Remove ``exp.add_parse_again_step()`` and insert ``exp.add_step("parse",
exp.parse)`` after ``exp.add_step("start", exp.start_runs)``.


How can I compute a new attribute from multiple runs?
Expand Down
2 changes: 1 addition & 1 deletion docs/ff.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ Downward experiments, we recommend taking a look at the

Here is a simple parser for FF:

.. literalinclude:: ../examples/ff/ff-parser.py
.. literalinclude:: ../examples/ff/ff_parser.py
:caption:
13 changes: 7 additions & 6 deletions docs/lab.concepts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ Concepts
========

An **experiment** consists of multiple **steps**. Most experiments will
have steps for building and executing the experiment:
have steps for building and executing the experiment, and parsing logs:

>>> from lab.experiment import Experiment
>>> exp = Experiment()
>>> exp.add_step("build", exp.build)
>>> exp.add_step("start", exp.start_runs)
>>> exp.add_step("parse", exp.parse)

Moreover, there are usually steps for **fetching** the results and making
**reports**:
Expand All @@ -18,11 +19,11 @@ Moreover, there are usually steps for **fetching** the results and making
>>> exp.add_fetcher(name="fetch")
>>> exp.add_report(Report(attributes=["error"]))

The "build" step creates all necessary files for running the experiment in
the **experiment directory**. After the "start" step has finished running
the experiment, we can fetch the result from the experiment directory to
the **evaluation directory**. All reports only operate on evaluation
directories.
The "build" step creates all necessary files for running the experiment in the
**experiment directory**. After the "start" step has finished running the
experiment, we can parse data from logs and generated files into "properties"
files, and then fetch all properties files from the experiment directory to the
**evaluation directory**. All reports only operate on evaluation directories.

An experiment usually also has multiple **runs**, one for each pair of
algorithm and benchmark.
Expand Down
7 changes: 1 addition & 6 deletions docs/lab.tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,7 @@ Select steps by name or index::

./exp.py build
./exp.py 2
./exp.py 3 4

Here is the parser that the experiment uses:

.. literalinclude:: ../examples/vertex-cover/parser.py
:caption:
./exp.py 3 4 5

Find out how to create your own experiments by browsing the `Lab API
<lab.experiment.html>`_.
12 changes: 12 additions & 0 deletions docs/news.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
Changelog
=========

v8.0 (2023-10-21)
-----------------

Lab
^^^
* Make parsing a separate experiment step, see :ref:`FAQs <portparsers>` for motivation and upgrade instructions (Jendrik Seipp).

Downward Lab
^^^^^^^^^^^^
* None.


v7.5 (2023-10-21)
-----------------

Expand Down
2 changes: 1 addition & 1 deletion docs/singularity.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Downward Lab.

The experiment script needs a parser and a helper script:

.. literalinclude:: ../examples/singularity/singularity-parser.py
.. literalinclude:: ../examples/singularity/singularity_parser.py
:caption:

.. literalinclude:: ../examples/singularity/run-singularity.sh
Expand Down
22 changes: 11 additions & 11 deletions downward/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@

from downward import suites
from downward.cached_revision import CachedFastDownwardRevision
from downward.parsers.anytime_search_parser import AnytimeSearchParser
from downward.parsers.exitcode_parser import ExitcodeParser
from downward.parsers.planner_parser import PlannerParser
from downward.parsers.single_search_parser import SingleSearchParser
from downward.parsers.translator_parser import TranslatorParser
from lab import tools
from lab.experiment import Experiment, get_default_data_dir, Run


DIR = os.path.dirname(os.path.abspath(__file__))
DOWNWARD_SCRIPTS_DIR = os.path.join(DIR, "scripts")


class FastDownwardAlgorithm:
"""
A Fast Downward algorithm is the combination of revision, driver options and
Expand Down Expand Up @@ -122,32 +123,31 @@ class FastDownwardExperiment(Experiment):
>>> exp = FastDownwardExperiment()
>>> exp.add_step("build", exp.build)
>>> exp.add_step("start", exp.start_runs)
>>> exp.add_step("parse", exp.parse)
>>> exp.add_fetcher(name="fetch")
"""

# Built-in parsers that can be passed to exp.add_parser().

#: Parsed attributes: "error", "planner_exit_code", "unsolvable".
EXITCODE_PARSER = os.path.join(DOWNWARD_SCRIPTS_DIR, "exitcode-parser.py")
EXITCODE_PARSER = ExitcodeParser()

#: Parsed attributes: "translator_peak_memory", "translator_time_done", etc.
TRANSLATOR_PARSER = os.path.join(DOWNWARD_SCRIPTS_DIR, "translator-parser.py")
TRANSLATOR_PARSER = TranslatorParser()

#: Parsed attributes: "coverage", "memory", "total_time", etc.
SINGLE_SEARCH_PARSER = os.path.join(DOWNWARD_SCRIPTS_DIR, "single-search-parser.py")
SINGLE_SEARCH_PARSER = SingleSearchParser()

#: Parsed attributes: "cost", "cost:all", "coverage".
ANYTIME_SEARCH_PARSER = os.path.join(
DOWNWARD_SCRIPTS_DIR, "anytime-search-parser.py"
)
ANYTIME_SEARCH_PARSER = AnytimeSearchParser()

#: Used attributes: "memory", "total_time",
#: "translator_peak_memory", "translator_time_done".
#:
#: Parsed attributes: "node", "planner_memory", "planner_time",
#: "planner_wall_clock_time", "score_planner_memory", "score_planner_time".
PLANNER_PARSER = os.path.join(DOWNWARD_SCRIPTS_DIR, "planner-parser.py")
PLANNER_PARSER = PlannerParser()

def __init__(self, path=None, environment=None, revision_cache=None):
"""
Expand Down
Empty file added downward/parsers/__init__.py
Empty file.
32 changes: 15 additions & 17 deletions downward/scripts/anytime-search-parser.py → downward/parsers/anytime_search_parser.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#! /usr/bin/env python

"""
Parse anytime-search runs of Fast Downward. This includes iterated
searches and portfolios.
Expand Down Expand Up @@ -54,18 +52,18 @@ def add_memory(content, props):
props["memory"] = raw_memory


def main():
parser = Parser()
parser.add_pattern("raw_memory", r"Peak memory: (.+) KB", type=int),
parser.add_function(find_all_matches("cost:all", r"Plan cost: (.+)\n", type=float))
parser.add_function(
find_all_matches("steps:all", r"Plan length: (.+) step\(s\).\n", type=float)
)
parser.add_function(reduce_to_min("cost:all", "cost"))
parser.add_function(reduce_to_min("steps:all", "steps"))
parser.add_function(coverage)
parser.add_function(add_memory)
parser.parse()


main()
class AnytimeSearchParser(Parser):
def __init__(self):
super().__init__()

self.add_pattern("raw_memory", r"Peak memory: (.+) KB", type=int)
self.add_function(
find_all_matches("cost:all", r"Plan cost: (.+)\n", type=float)
)
self.add_function(
find_all_matches("steps:all", r"Plan length: (.+) step\(s\).\n", type=float)
)
self.add_function(reduce_to_min("cost:all", "cost"))
self.add_function(reduce_to_min("steps:all", "steps"))
self.add_function(coverage)
self.add_function(add_memory)
14 changes: 2 additions & 12 deletions downward/scripts/exitcode-parser.py → downward/parsers/exitcode_parser.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#! /usr/bin/env python

"""
Parse Fast Downward exit code and store a message describing the outcome
in the "error" attribute.
Expand Down Expand Up @@ -43,9 +41,9 @@ def parse_exit_code(content, props):
props.add_unexplained_error(outcome.msg)


class ExitCodeParser(Parser):
class ExitcodeParser(Parser):
def __init__(self):
Parser.__init__(self)
super().__init__()
self.add_pattern(
"planner_exit_code",
r"planner exit code: (.+)\n",
Expand All @@ -54,11 +52,3 @@ def __init__(self):
required=True,
)
self.add_function(parse_exit_code)


def main():
parser = ExitCodeParser()
parser.parse()


main()
10 changes: 0 additions & 10 deletions downward/scripts/planner-parser.py → downward/parsers/planner_parser.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#! /usr/bin/env python

from lab import tools
from lab.parser import Parser

Expand Down Expand Up @@ -96,11 +94,3 @@ def __init__(self):
self.add_function(add_planner_memory)
self.add_function(add_planner_time)
self.add_function(add_planner_scores)


def main():
parser = PlannerParser()
parser.parse()


main()
10 changes: 0 additions & 10 deletions downward/scripts/single-search-parser.py → downward/parsers/single_search_parser.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#! /usr/bin/env python

"""
Regular expressions and functions for parsing single-search runs of Fast Downward.
"""
Expand Down Expand Up @@ -160,11 +158,3 @@ def __init__(self):
self.add_function(add_initial_h_values)
self.add_function(ensure_minimum_times)
self.add_function(add_scores)


def main():
parser = SingleSearchParser()
parser.parse()


main()
7 changes: 0 additions & 7 deletions downward/scripts/translator-parser.py → downward/parsers/translator_parser.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#! /usr/bin/env python

"""
Regular expressions and functions for parsing translator logs.
"""
Expand Down Expand Up @@ -72,8 +70,3 @@ def __init__(self):
self.add_function(parse_translator_timestamps)
self.add_function(parse_old_statistics)
self.add_function(parse_statistics)


if __name__ == "__main__":
parser = TranslatorParser()
parser.parse()
5 changes: 4 additions & 1 deletion examples/downward/2020-09-11-A-cg-vs-ff.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import os
import shutil

import custom_parser

import project


Expand Down Expand Up @@ -64,11 +66,12 @@
exp.add_parser(exp.EXITCODE_PARSER)
exp.add_parser(exp.TRANSLATOR_PARSER)
exp.add_parser(exp.SINGLE_SEARCH_PARSER)
exp.add_parser(project.DIR / "parser.py")
exp.add_parser(custom_parser.get_parser())
exp.add_parser(exp.PLANNER_PARSER)

exp.add_step("build", exp.build)
exp.add_step("start", exp.start_runs)
exp.add_step("parse", exp.parse)
exp.add_fetcher(name="fetch")

if not project.REMOTE:
Expand Down
5 changes: 4 additions & 1 deletion examples/downward/2020-09-11-B-bounded-cost.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import os
import shutil

import custom_parser

from downward import suites
from downward.cached_revision import CachedFastDownwardRevision
from downward.experiment import FastDownwardAlgorithm, FastDownwardRun
Expand Down Expand Up @@ -89,11 +91,12 @@
exp.add_parser(project.FastDownwardExperiment.EXITCODE_PARSER)
exp.add_parser(project.FastDownwardExperiment.TRANSLATOR_PARSER)
exp.add_parser(project.FastDownwardExperiment.SINGLE_SEARCH_PARSER)
exp.add_parser(project.DIR / "parser.py")
exp.add_parser(custom_parser.get_parser())
exp.add_parser(project.FastDownwardExperiment.PLANNER_PARSER)

exp.add_step("build", exp.build)
exp.add_step("start", exp.start_runs)
exp.add_step("parse", exp.parse)
exp.add_fetcher(name="fetch")

if not project.REMOTE:
Expand Down

0 comments on commit 4c01311

Please sign in to comment.