Full Code of TUM-DAML/seml for AI

master 2e6d78b97d18 cached
103 files
922.5 KB
291.9k tokens
441 symbols
1 requests
Download .txt
Showing preview only (964K chars total). Download the full file or copy to clipboard to get everything.
Repository: TUM-DAML/seml
Branch: master
Commit: 2e6d78b97d18
Files: 103
Total size: 922.5 KB

Directory structure:
gitextract_7hga3fho/

├── .github/
│   ├── ISSUE_TEMPLATE.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── actions.yaml
│       └── precommit.yaml
├── .gitignore
├── .pre-commit-config.yaml
├── .python-version
├── LICENSE
├── MANIFEST.in
├── README.md
├── ci/
│   └── examples/
│       ├── example_config.yaml
│       ├── example_experiment.py
│       ├── example_reschedule.py
│       └── example_reschedule_config.yaml
├── docs.md
├── examples/
│   ├── .ruff.toml
│   ├── README.md
│   ├── advanced_example_config.yaml
│   ├── advanced_example_experiment.py
│   ├── config/
│   │   └── flip_augmentation.yaml
│   ├── example_config.yaml
│   ├── example_experiment.py
│   ├── logs/
│   │   └── .gitignore
│   ├── notebooks/
│   │   └── experiment_results.ipynb
│   └── tutorial/
│       ├── example_config.yaml
│       ├── example_experiment.py
│       ├── intro_slides.ipynb
│       ├── intro_slides.slides.html
│       └── seml.drawio
├── pyproject.toml
├── src/
│   └── seml/
│       ├── __init__.py
│       ├── __main__.py
│       ├── cli_utils/
│       │   ├── __init__.py
│       │   ├── cache.py
│       │   ├── cli_states.py
│       │   └── module_hider.py
│       ├── commands/
│       │   ├── __init__.py
│       │   ├── add.py
│       │   ├── configure.py
│       │   ├── description.py
│       │   ├── manage.py
│       │   ├── migration.py
│       │   ├── print.py
│       │   ├── project.py
│       │   ├── slurm.py
│       │   ├── sources.py
│       │   └── start.py
│       ├── console/
│       │   └── __init__.py
│       ├── database.py
│       ├── document.py
│       ├── evaluation.py
│       ├── experiment/
│       │   ├── __init__.py
│       │   ├── command.py
│       │   ├── config.py
│       │   ├── description.py
│       │   ├── experiment.py
│       │   ├── mattermost_observer.py
│       │   ├── observers.py
│       │   ├── parameters.py
│       │   └── sources.py
│       ├── settings.py
│       ├── templates/
│       │   └── slurm/
│       │       ├── jupyter_template.sh
│       │       └── slurm_template.sh
│       └── utils/
│           ├── __init__.py
│           ├── errors.py
│           ├── io.py
│           ├── json.py
│           ├── multi_process.py
│           ├── network.py
│           ├── slurm.py
│           ├── ssh_forward.py
│           └── yaml.py
└── test/
    ├── .ruff.toml
    ├── __init__.py
    ├── resources/
    │   ├── config/
    │   │   ├── config_nested_parameter_collections.yaml
    │   │   ├── config_resolve_config.yaml
    │   │   ├── config_resolve_config_named_1.json
    │   │   ├── config_resolve_config_named_1.yaml
    │   │   ├── config_resolve_config_named_2.json
    │   │   ├── config_resolve_config_named_2.yaml
    │   │   ├── config_resolve_with_interpolation.yaml
    │   │   ├── config_slurm_default.yaml
    │   │   ├── config_slurm_default_empty_sbatch.yaml
    │   │   ├── config_slurm_experiment.yaml
    │   │   ├── config_slurm_experiments_and_tasks.yaml
    │   │   ├── config_slurm_template.yaml
    │   │   ├── config_with_all_types.yaml
    │   │   ├── config_with_dict_choice.yaml
    │   │   ├── config_with_duplicate_parameters_1.yaml
    │   │   ├── config_with_duplicate_parameters_2.yaml
    │   │   ├── config_with_duplicate_parameters_3.yaml
    │   │   ├── config_with_duplicate_random_parameters_1.yaml
    │   │   ├── config_with_empty_dictionary.yaml
    │   │   ├── config_with_grid.yaml
    │   │   ├── config_with_named_config.yaml
    │   │   ├── config_with_parameter_collections.yaml
    │   │   ├── config_with_parameter_collections_random.yaml
    │   │   └── config_with_zipped_parameters.yaml
    │   └── scripts/
    │       ├── experiment_resolve_config.py
    │       └── experiment_resolve_config_interpolate.py
    ├── test_config.py
    ├── test_start.py
    └── test_utils.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
### Expected Behavior


### Actual Behavior


### Steps to Reproduce the Problem

  1.
  1.
  1.

### Error message:
<!-- If any, paste the *full* error message inside a code block
as above (starting from line Traceback)
-->

```
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  ...
```

### Specifications

<details><summary>Details</summary>

  - Version:
  - Python version:
  - Platform:
  - Anaconda environment (`conda list`):

</details>


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--
Thank you for contributing a pull request!
Please name and describe your PR as you would write a
commit message.
-->

### Reference issue
<!--Example: Closes gh-WXYZ.-->


### What does this implement/fix?
<!--Please explain your changes.-->


### Additional information
<!--Any additional information you think is important.-->
- [ ] I updated the docs via typer-cli with `_SEML_COMPLETE=1 typer seml.__main__ utils docs --name seml --output docs.md` or did not change the CLI.


================================================
FILE: .github/workflows/actions.yaml
================================================
name: Test

on: [push, pull_request]

jobs:
  # Run CLI test and pytest
  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.10", "3.11", "3.12", "3.13"]
        resolution: ["highest", "lowest-direct"]

    steps:
      # Checkout the code
      - uses: actions/checkout@v4
      # Install uv
      - name: Install uv
        uses: astral-sh/setup-uv@v6
        with:
          enable-cache: true
      # Test whether the CLI is working
      - name: Test CLI
        run: uv run -p ${{ matrix.python-version }} --resolution ${{ matrix.resolution }} -U seml --help
      # Test with pytest
      - name: Test with pytest
        run: |
          cd test
          uv run -p ${{ matrix.python-version }} --resolution ${{ matrix.resolution }} -U pytest

  # Test commands in a dummy environment
  commands:
    runs-on: ubuntu-22.04 # this is the lastest release supported by MongoDB
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.10", "3.11", "3.12", "3.13"]
        resolution: ["highest", "lowest-direct"]
    # MySQL for Slurm
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: root
        ports:
          - "8888:3306"
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

    steps:
      - name: Start MongoDB
        uses: supercharge/mongodb-github-action@1.11.0
        with:
          mongodb-username: admin
          mongodb-password: admin
          mongodb-port: 27017
      - name: Install mongosh
        run: |
          sudo apt-get install gnupg
          wget -qO- https://www.mongodb.org/static/pgp/server-7.0.asc | sudo tee /etc/apt/trusted.gpg.d/server-7.0.asc
          echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
          sudo apt-get update
          sudo apt-get install -y mongodb-mongosh
          mongosh --version
      - name: Create seml MongoDB user
        run: |
          mongosh --host localhost:27017 -u admin -p admin --authenticationDatabase admin <<EOF
            db = db.getSiblingDB('seml');
            db.createUser({ user: 'seml', pwd: 'seml', roles: [{ role: 'readWrite', db: 'seml' }] });
          EOF
      - name: Setup Slurm Cluster
        uses: koesterlab/setup-slurm-action@v1
      # Checkout the code
      - uses: actions/checkout@v4
      - name: Install uv
        uses: astral-sh/setup-uv@v6
        with:
          enable-cache: true
      - name: Setup virtual env
        run: uv sync -p ${{ matrix.python-version }} --resolution ${{ matrix.resolution }} -U
      - name: Setup seml config
        run: uv run seml configure --host=localhost --port=27017 --database=seml --username=seml --password=seml
      - name: Add
        run: uv run seml test_collection add ci/examples/example_config.yaml
      - name: Start
        run: uv run seml test_collection start
      - name: Hold
        run: uv run seml test_collection hold
      - name: Release
        run: uv run seml test_collection release
      - name: Status
        run: uv run seml test_collection status
      - name: Queue
        run: uv run seml queue
      - name: Cancel
        run: uv run seml test_collection cancel -y
      - name: Delete
        run: uv run seml test_collection delete -y
      - name: Reload sources
        run: uv run seml test_collection add ci/examples/example_config.yaml reload-sources -y
      - name: Set description
        run: uv run seml test_collection description set -y 'Hello World ${config.dataset}!'
      - name: List description
        run: uv run seml test_collection description list
      - name: Delete description
        run: uv run seml test_collection description delete -y
      - name: List
        run: uv run seml list


================================================
FILE: .github/workflows/precommit.yaml
================================================
name: precommit
on: [push, pull_request]

jobs:
  precommit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Cache pre-commit # taken from https://github.com/pre-commit/action/blob/main/action.yml
        uses: actions/cache@v4
        with:
          path: ~/.cache/pre-commit
          key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }}
      - name: Install uv
        uses: astral-sh/setup-uv@v6
        with:
          enable-cache: true
      - name: Setup virtual environment
        run: uv sync
      - name: Install pre-commit
        run: uv tool install pre-commit --with pre-commit-uv
      - name: Run pre-commit
        run: uv tool run pre-commit run --show-diff-on-failure --color=always --all-files


================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*.egg-info
build/
dist/

# Latex aux files
*.synctex.gz
*.log
*.aux
*.out
*.dvi
*.xwm
*.nav
*.toc
*.snm
comment.cut
*.fdb_latexmk
*.fls
*.blg

# Jupyter
*.ipynb_checkpoints

# Code editors
.idea/
.vscode/


================================================
FILE: .pre-commit-config.yaml
================================================
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v6.0.0
    hooks:
      - id: check-case-conflict
      - id: check-toml
      - id: check-xml
      - id: check-yaml
        exclude: |
          (?x)^(
              test/resources/config/config_with_duplicate_parameters_3.yaml
          )$
      - id: check-added-large-files
      - id: trailing-whitespace
  - repo: https://github.com/asottile/pyupgrade
    rev: v3.21.2
    hooks:
      - id: pyupgrade
        name: pyupgrade
        args: [--py38-plus]
        exclude: .*\/__main__.py # typer doesn't supper PEP604 yet, so let's stick to the old typing
      - id: pyupgrade
        name: pyupgrade (__main__.py)
        args: [--py38-plus, --keep-runtime-typing]
        files: .*\/__main__.py # We run ruff after pyugprade to remove unused imports
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.14.6
    hooks:
      - id: ruff # Run the linter.
        args: [--fix]
      - id: ruff # Sort improts
        name: sort imports with ruff
        args: [--select, I, --fix]
      - id: ruff-format # Run the formatter.
  - repo: local # Run pyright type checker
    hooks:
      - id: pyright
        name: pyright check
        entry: uv run pyright src/
        language: system
        types: [python]
        pass_filenames: false
        always_run: true


================================================
FILE: .python-version
================================================
3.10

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2023 Johannes Klicpera, Daniel Zügner, Nicholas Gao
Technical University of Munich

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: MANIFEST.in
================================================
include README.md
include LICENSE

recursive-exclude test *
recursive-exclude examples *

global-exclude */__pycache__/*
global-exclude *.pyc

recursive-include src/seml/templates/ *


================================================
FILE: README.md
================================================
![Github Actions](https://github.com/TUM-DAML/seml/workflows/Test/badge.svg)

# `SEML`: Slurm Experiment Management Library
**`SEML`** is the missing link between the open-source workload scheduling system `Slurm`, the experiment management tool `sacred`, and a `MongoDB` experiment database. It is lightweight, hackable, written in pure Python, and scales to thousands of experiments.

Keeping track of computational experiments can be annoying and failure to do so can lead to lost results, duplicate running of the same experiments, and lots of headaches.
While workload scheduling systems such as [`Slurm`](https://slurm.schedmd.com/overview.html) make it easy to run many experiments in parallel on a cluster, it can be hard to keep track of which parameter configurations are running, failed, or completed.
[`sacred`](https://github.com/IDSIA/sacred) is a great tool to collect and manage experiments and their results, especially when used with a [`MongoDB`](https://www.mongodb.com/). However, it is lacking integration with workload schedulers.

**`SEML`** enables you to
* very easily define hyperparameter search spaces using YAML files,
* run these hyperparameter configurations on a compute cluster using `Slurm`,
* and to track the experimental results using `sacred` and `MongoDB`.


In addition, **`SEML`** offers many more features to make your life easier, such as
* automatically saving and loading your source code for reproducibility,
* easy debugging on Slurm or locally,
* automatically checking your experiment configurations,
* extending Slurm with local workers,
* and keeping track of resource usage (experiment runtime, RAM, etc.).

## Get started
### New projects
The fastest way to get started with `SEML` is via [`uv`](https://docs.astral.sh/uv/):
1. Install `uv`:
    ```bash
    curl -LsSf https://astral.sh/uv/install.sh | sh
    ```
2. Setup a new project
    ```bash
    # uvx will execute `SEML` in a temporary virtual environment
    # and run it to setup your new project.
    uvx seml project init my_new_project
    ```
3. Setup a virtual environment
    ```bash
    cd my_new_project
    uv sync
    ```
4. Activate your virtual environment
    ```bash
    source .venv/bin/activate
    ```
5. Configure `SEML`:
    ```bash
    seml configure
    ```

When executing `SEML` make sure to always use the `seml` command from your project's virtual environment and only use `uvx seml` for high-level commands that do not affect experiments (like setting up new projects).

### Existing projects
If you want to include `SEML` into existing projects, you can install it via:
```bash
pip install seml
```
Then configure your MongoDB via:
```bash
seml configure
```


### SSH Port Forwarding
If your MongoDB is only accessible via an SSH port forward, **`SEML`** allows you to directly configure this as well if you install the `ssh_forward` dependencies via:
```bash
pip install seml[ssh_forward]
```
It remains to configure the SSH settings:
```bash
seml configure --ssh_forward
```

### Development
For development, we recommend [`uv`](https://docs.astral.sh/uv/) which you can install via
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
Setup the right environment use and activate it:
```bash
uv sync --locked
source .venv/bin/activate
```
Alternatively, you can install the repository in any Python environment via:
```bash
pip install -e .[dev]
```

#### Pre-commit hooks
Make sure to install the pre-commit hooks via
```bash
pre-commit install
```

## Documentation
Documentation is available in our [docs.md](docs.md) or via the CLI:
```python
seml --help
```

## Example
See our simple [example](examples) to get familiar with how **`SEML`** works.

## CLI completion
SEML supports command line completion. To install this feature run:
```bash
seml --install-completion {shell}
```

If you are using the zsh shell, you might have to append `compinit -D` to the `~/.zshrc` file (see this [issue](https://github.com/tiangolo/typer/issues/180#issuecomment-812620805)).

## Slurm version

SEML should work with Slurm 18.08 and above out of the box. Version 17.11 and earlier do not have a SIGNALING job state, which you have to remove from the SLURM_STATES defined in SEML's settings (`seml/settings.py`). Earlier versions have not been tested and might have other issues.

## Contact
Contact us at zuegnerd@in.tum.de, johannes.gasteiger@tum.de, or n.gao@tum.de for any questions.

## Cite
When you use SEML in your own work, please cite the software along the lines of the following bibtex:

```
@software{seml_2023,
  author = {Z{\"u}gner, Daniel and Gasteiger, Johannes and Gao, Nicholas and Dominik Fuchsgruber},
  title = {{SEML: Slurm Experiment Management Library}},
  url = {https://github.com/TUM-DAML/seml},
  version = {0.4.0},
  year = {2023}
}
```


Copyright (C) 2023
Daniel Zügner, Johannes Gasteiger, Nicholas Gao, Dominik Fuchsgruber
Technical University of Munich


================================================
FILE: ci/examples/example_config.yaml
================================================
# Experiment configuration file for CI

seml:
  executable: example_experiment.py
  name: example_experiment
  output_dir: logs
  project_root_dir: .
  description: An example configuration.

slurm:
  - experiments_per_job: 1
    sbatch_options:
      mem: 1G
      cpus-per-task: 1
      time: 0-08:00

###### BEGIN PARAMETER CONFIGURATION ######

fixed:
  max_epochs: 500

grid:
  learning_rate:
    type: loguniform
    min: 1e-5
    max: 1e-1
    num: 1

random:
  samples: 1
  seed: 821

  # SEML supports dot-notation for nested dictionaries.
  regularization_params.dropout:
    type: uniform
    min: 0.0
    max: 0.7
    seed: 222

small_datasets:
  grid:
    dataset:
      type: choice
      options:
        - small_dataset_1
        - small_dataset_2

    hidden_sizes:
      type: choice
      options:
        - [16]
        - [32, 16] # this will be parsed into a Python list.

  random:
    samples: 3
    seed: 2223

    max_epochs:
      type: randint
      min: 200
      max: 1000

large_datasets:
  fixed:
    max_epochs: 1000

  grid:
    learning_rate:
      type: choice
      options:
        - 0.001

    dataset:
      type: choice
      options:
        - large_dataset_1
        - large_dataset_2

    hidden_sizes:
      type: choice
      options:
        - [64]
        - [64, 32]


================================================
FILE: ci/examples/example_experiment.py
================================================
import logging

import numpy as np

from seml import Experiment

ex = Experiment()


@ex.automain
def run(
    dataset: str,
    hidden_sizes: list,
    learning_rate: float,
    max_epochs: int,
    regularization_params: dict,
):
    # Note that regularization_params contains the corresponding sub-dictionary from the configuration.
    logging.info('Received the following configuration:')
    logging.info(
        f'Dataset: {dataset}, hidden sizes: {hidden_sizes}, learning_rate: {learning_rate}, '
        f'max_epochs: {max_epochs}, regularization: {regularization_params}'
    )

    #  do your processing here

    results = {
        'test_acc': 0.5 + 0.3 * np.random.randn(),
        'test_loss': np.random.uniform(0, 10),
        # ...
    }
    # the returned result will be written into the database
    return results


================================================
FILE: ci/examples/example_reschedule.py
================================================
import logging
from time import sleep

from seml import Experiment

ex = Experiment()


@ex.reschedule_hook
def reschedule(step: int):
    logging.info(f'Reschedule triggered at step {step}.')
    return {'checkpoint': step}


@ex.automain
def run(
    n_steps: int,
    checkpoint: int | None = None,
):
    logging.info('Starting experiment with the following parameters:')
    logging.info(f'n_steps: {n_steps}, checkpoint: {checkpoint}')

    if checkpoint is not None:
        logging.info(f'Resuming from checkpoint: {checkpoint}')
        # Load your model/state from the checkpoint here

    # Simulate some processing
    for step in range(checkpoint or 0, n_steps):
        reschedule(step)
        logging.info(f'Processing step {step + 1}/{n_steps}')
        sleep(1.0)

    logging.info('Experiment completed successfully.')
    return


================================================
FILE: ci/examples/example_reschedule_config.yaml
================================================
seml:
  executable: example_reschedule.py
  name: example_reschedule_experiment
  output_dir: logs
  project_root_dir: .
  description: An example configuration with rescheduling.
  reschedule_timeout: 120 # seconds

slurm:
  - experiments_per_job: 1
    sbatch_options:
      mem: 1G
      cpus-per-task: 1
      time: 00:03:00
      partition: cpu_all

fixed:
  n_steps: 1000


================================================
FILE: docs.md
================================================
# `seml`

SEML - Slurm Experiment Management Library.

**Usage**:

```console
$ seml [OPTIONS] COLLECTION COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...
```

**Arguments**:

* `COLLECTION`: The name of the database collection to use.  [required]

**Options**:

* `--migration-skip`: Skip the migration of the database collection.
* `--migration-backup`: Backup the database collection before migration.
* `-v, --verbose`: Whether to print debug messages.
* `-V, --version`: Print the version number.
* `--install-completion`: Install completion for the current shell.
* `--show-completion`: Show completion for the current shell, to copy it or customize the installation.
* `--help`: Show this message and exit.

**Commands**:

* `add`: Add experiments to the database as defined...
* `cancel`: Cancel the Slurm job/job step...
* `claim-experiment`: Claim an experiment from the database.
* `clean-db`: Remove orphaned artifacts in the DB from...
* `clean-jobs`: Cancel empty pending jobs.
* `configure`: Configure SEML (database, argument...
* `delete`: Delete experiments by ID or state (cancels...
* `description`: Manage descriptions of the experiments in...
* `detect-duplicates`: Prints duplicate experiment configurations.
* `detect-killed`: Detect experiments where the corresponding...
* `download-sources`: Download source files from the database to...
* `drop`: Drop collections from the database.
* `hold`: Hold queued experiments via SLURM.
* `launch-worker`: Launch a local worker that runs PENDING jobs.
* `list`: Lists all collections in the database.
* `prepare-experiment`: Fetch experiment from database, prepare it...
* `print-command`: Print the commands that would be executed...
* `print-experiment`: Print the experiment document.
* `print-fail-trace`: Prints fail traces of all failed experiments.
* `print-output`: Print the output of experiments.
* `project`: Setting up new projects.
* `queue`: Prints the collections of the given job IDs.
* `release`: Release held experiments via SLURM.
* `reload-sources`: Reload stashed source files.
* `reset`: Reset the state of experiments by setting...
* `start`: Fetch staged experiments from the database...
* `start-jupyter`: Start a Jupyter slurm job.
* `status`: Report status of experiments in the...
* `update-working-dir`: Change the working directory of...

## `seml add`

Add experiments to the database as defined in the configuration.

**Usage**:

```console
$ seml add [OPTIONS] CONFIG_FILES...
```

**Arguments**:

* `CONFIG_FILES...`: Path to the YAML configuration file for the experiment.  [required]

**Options**:

* `-nh, --no-hash`: By default, we use the hash of the config dictionary to filter out duplicates (by comparing all dictionary values individually). Only disable this if you have a good reason as it is faster.
* `-ncs, --no-sanity-check`: Disable this if the check fails unexpectedly when using advanced Sacred features or to accelerate adding.
* `-ncc, --no-code-checkpoint`: Disable this if you want your experiments to use the current codeinstead of the code at the time of adding.
* `-f, --force`: Force adding the experiment even if it already exists in the database.
* `-o, --overwrite-params DICT`: Dictionary (passed as a string, e.g. '{"epochs": 100}') to overwrite parameters in the config.
* `-d, --description TEXT`: A description for the experiment.
* `--no-resolve-descriptions`: Whether to prevent using omegaconf to resolve experiment descriptions
* `--help`: Show this message and exit.

## `seml cancel`

Cancel the Slurm job/job step corresponding to experiments, filtered by ID or state.

**Usage**:

```console
$ seml cancel [OPTIONS]
```

**Options**:

* `-id, --sacred-id INTEGER`: Sacred ID (_id in the database collection) of the experiment. Takes precedence over other filters.
* `-fd, --filter-dict DICT`: Dictionary (passed as a string, e.g. '{"config.dataset": "cora_ml"}') to filter the experiments by.
* `-b, --batch-id INTEGER`: Batch ID (batch_id in the database collection) of the experiments. Experiments that were staged together have the same batch_id.
* `-s, --filter-states [STAGED|QUEUED|PENDING|RUNNING|FAILED|KILLED|INTERRUPTED|COMPLETED]`: List of states to filter the experiments by. If empty (""), all states are considered.  [default: PENDING, RUNNING]
* `-w, --wait`: Wait until all jobs are properly cancelled.
* `-y, --yes`: Automatically confirm all dialogues with yes.
* `--help`: Show this message and exit.

## `seml claim-experiment`

Claim an experiment from the database.

**Usage**:

```console
$ seml claim-experiment [OPTIONS] SACRED_IDS...
```

**Arguments**:

* `SACRED_IDS...`: Sacred IDs (_id in the database collection) of the experiments to claim.  [required]

**Options**:

* `--help`: Show this message and exit.

## `seml clean-db`

Remove orphaned artifacts in the DB from runs which have been deleted..

**Usage**:

```console
$ seml clean-db [OPTIONS]
```

**Options**:

* `-y, --yes`: Automatically confirm all dialogues with yes.
* `--help`: Show this message and exit.

## `seml clean-jobs`

Cancel empty pending jobs.

**Usage**:

```console
$ seml clean-jobs [OPTIONS] SACRED_IDS...
```

**Arguments**:

* `SACRED_IDS...`: Sacred IDs (_id in the database collection) of the experiments to claim.  [required]

**Options**:

* `--help`: Show this message and exit.

## `seml configure`

Configure SEML (database, argument completion, ...).

**Usage**:

```console
$ seml configure [OPTIONS]
```

**Options**:

* `--host TEXT`: The host of the MongoDB server.
* `--port INTEGER`: The port of the MongoDB server.
* `--database TEXT`: The name of the MongoDB database to use.
* `--username TEXT`: The username for the MongoDB server.
* `--password TEXT`: The password for the MongoDB server.
* `-sf, --ssh-forward`: Configure SSH forwarding settings for MongoDB.
* `--help`: Show this message and exit.

## `seml delete`

Delete experiments by ID or state (cancels Slurm jobs first if not --no-cancel).

**Usage**:

```console
$ seml delete [OPTIONS]
```

**Options**:

* `-id, --sacred-id INTEGER`: Sacred ID (_id in the database collection) of the experiment. Takes precedence over other filters.
* `-s, --filter-states [STAGED|QUEUED|PENDING|RUNNING|FAILED|KILLED|INTERRUPTED|COMPLETED]`: List of states to filter the experiments by. If empty (""), all states are considered.  [default: STAGED, QUEUED, FAILED, KILLED, INTERRUPTED]
* `-fd, --filter-dict DICT`: Dictionary (passed as a string, e.g. '{"config.dataset": "cora_ml"}') to filter the experiments by.
* `-b, --batch-id INTEGER`: Batch ID (batch_id in the database collection) of the experiments. Experiments that were staged together have the same batch_id.
* `-nc, --no-cancel`: Do not cancel the experiments before deleting them.
* `-y, --yes`: Automatically confirm all dialogues with yes.
* `--help`: Show this message and exit.

## `seml description`

Manage descriptions of the experiments in a collection.

**Usage**:

```console
$ seml description [OPTIONS] COMMAND [ARGS]...
```

**Options**:

* `--help`: Show this message and exit.

**Commands**:

* `delete`: Deletes the description of experiment(s).
* `list`: Lists the descriptions of all experiments.
* `set`: Sets the description of experiment(s).

### `seml description delete`

Deletes the description of experiment(s).

**Usage**:

```console
$ seml description delete [OPTIONS]
```

**Options**:

* `-id, --sacred-id INTEGER`: Sacred ID (_id in the database collection) of the experiment. Takes precedence over other filters.
* `-s, --filter-states [STAGED|QUEUED|PENDING|RUNNING|FAILED|KILLED|INTERRUPTED|COMPLETED]`: List of states to filter the experiments by. If empty (""), all states are considered.
* `-fd, --filter-dict DICT`: Dictionary (passed as a string, e.g. '{"config.dataset": "cora_ml"}') to filter the experiments by.
* `-b, --batch-id INTEGER`: Batch ID (batch_id in the database collection) of the experiments. Experiments that were staged together have the same batch_id.
* `-y, --yes`: Automatically confirm all dialogues with yes.
* `--help`: Show this message and exit.

### `seml description list`

Lists the descriptions of all experiments.

**Usage**:

```console
$ seml description list [OPTIONS]
```

**Options**:

* `-u, --update-status`: Whether to update the status of experiments in the database. This can take a while for large collections. Use only if necessary.
* `--help`: Show this message and exit.

### `seml description set`

Sets the description of experiment(s).

**Usage**:

```console
$ seml description set [OPTIONS] DESCRIPTION
```

**Arguments**:

* `DESCRIPTION`: The description to set.  [required]

**Options**:

* `-id, --sacred-id INTEGER`: Sacred ID (_id in the database collection) of the experiment. Takes precedence over other filters.
* `-s, --filter-states [STAGED|QUEUED|PENDING|RUNNING|FAILED|KILLED|INTERRUPTED|COMPLETED]`: List of states to filter the experiments by. If empty (""), all states are considered.
* `-fd, --filter-dict DICT`: Dictionary (passed as a string, e.g. '{"config.dataset": "cora_ml"}') to filter the experiments by.
* `-b, --batch-id INTEGER`: Batch ID (batch_id in the database collection) of the experiments. Experiments that were staged together have the same batch_id.
* `-y, --yes`: Automatically confirm all dialogues with yes.
* `--no-resolve-descriptions`: Whether to prevent using omegaconf to resolve experiment descriptions
* `--help`: Show this message and exit.

## `seml detect-duplicates`

Prints duplicate experiment configurations.

**Usage**:

```console
$ seml detect-duplicates [OPTIONS]
```

**Options**:

* `-s, --filter-states [STAGED|QUEUED|PENDING|RUNNING|FAILED|KILLED|INTERRUPTED|COMPLETED]`: List of states to filter the experiments by. If empty (""), all states are considered.  [default: STAGED, QUEUED, FAILED, KILLED, INTERRUPTED]
* `-fd, --filter-dict DICT`: Dictionary (passed as a string, e.g. '{"config.dataset": "cora_ml"}') to filter the experiments by.
* `-b, --batch-id INTEGER`: Batch ID (batch_id in the database collection) of the experiments. Experiments that were staged together have the same batch_id.
* `--help`: Show this message and exit.

## `seml detect-killed`

Detect experiments where the corresponding Slurm jobs were killed externally.

**Usage**:

```console
$ seml detect-killed [OPTIONS]
```

**Options**:

* `--help`: Show this message and exit.

## `seml download-sources`

Download source files from the database to the provided path.

**Usage**:

```console
$ seml download-sources [OPTIONS] TARGET_DIRECTORY
```

**Arguments**:

* `TARGET_DIRECTORY`: The directory where the source files should be restored.  [required]

**Options**:

* `-id, --sacred-id INTEGER`: Sacred ID (_id in the database collection) of the experiment. Takes precedence over other filters.
* `-s, --filter-states [STAGED|QUEUED|PENDING|RUNNING|FAILED|KILLED|INTERRUPTED|COMPLETED]`: List of states to filter the experiments by. If empty (""), all states are considered.
* `-fd, --filter-dict DICT`: Dictionary (passed as a string, e.g. '{"config.dataset": "cora_ml"}') to filter the experiments by.
* `-b, --batch-id INTEGER`: Batch ID (batch_id in the database collection) of the experiments. Experiments that were staged together have the same batch_id.
* `--help`: Show this message and exit.

## `seml drop`

Drop collections from the database.

Note: This is a dangerous operation and should only be used if you know what you are doing.

**Usage**:

```console
$ seml drop [OPTIONS] [PATTERN]
```

**Arguments**:

* `[PATTERN]`: A regex that must match the collections to print.  [default: .*]

**Options**:

* `-y, --yes`: Automatically confirm all dialogues with yes.
* `--help`: Show this message and exit.

## `seml hold`

Hold queued experiments via SLURM.

**Usage**:

```console
$ seml hold [OPTIONS]
```

**Options**:

* `-b, --batch-id INTEGER`: Batch ID (batch_id in the database collection) of the experiments. Experiments that were staged together have the same batch_id.
* `--help`: Show this message and exit.

## `seml launch-worker`

Launch a local worker that runs PENDING jobs.

**Usage**:

```console
$ seml launch-worker [OPTIONS]
```

**Options**:

* `-n, --num-experiments INTEGER`: Number of experiments to start. 0: all (staged) experiments   [default: 0]
* `-nf, --no-file-output`: Do not write the experiment's output to a file.
* `-ss, --steal-slurm`: Local jobs 'steal' from the Slurm queue, i.e. also execute experiments waiting for execution via Slurm.
* `-pm, --post-mortem`: Activate post-mortem debugging with pdb.
* `-d, --debug`: Run a single interactive experiment without Sacred observers and with post-mortem debugging. Implies `--verbose --num-exps 1 --post-mortem --output-to-console`.
* `-ds, --debug-server`: Run the experiment with a debug server, to which you can remotely connect with e.g. VS Code. Implies `--debug`.
* `-o, --output-to-console`: Write the experiment's output to the console.
* `-wg, --worker-gpus TEXT`: The IDs of the GPUs used by the local worker. Will be directly passed to CUDA_VISIBLE_DEVICES.
* `-wc, --worker-cpus INTEGER`: The number of CPUs used by the local worker. Will be directly passed to OMP_NUM_THREADS.
* `-we, --worker-env DICT`: Further environment variables to be set for the local worker.
* `-id, --sacred-id INTEGER`: Sacred ID (_id in the database collection) of the experiment. Takes precedence over other filters.
* `-fd, --filter-dict DICT`: Dictionary (passed as a string, e.g. '{"config.dataset": "cora_ml"}') to filter the experiments by.
* `-b, --batch-id INTEGER`: Batch ID (batch_id in the database collection) of the experiments. Experiments that were staged together have the same batch_id.
* `--help`: Show this message and exit.

## `seml list`

Lists all collections in the database.

**Usage**:

```console
$ seml list [OPTIONS] [PATTERN]
```

**Arguments**:

* `[PATTERN]`: A regex that must match the collections to print.  [default: .*]

**Options**:

* `-p, --progress`: Whether to print a progress bar for iterating over collections.
* `-u, --update-status`: Whether to update the status of experiments in the database. This can take a while for large collections. Use only if necessary.
* `-fd, --full-descriptions`: Whether to print full descriptions (possibly with line breaks).
* `--help`: Show this message and exit.

## `seml prepare-experiment`

Fetch experiment from database, prepare it and print the command to execute it.

**Usage**:

```console
$ seml prepare-experiment [OPTIONS]
```

**Options**:

* `-id, --sacred-id INTEGER`: Sacred ID (_id in the database collection) of the experiment. Takes precedence over other filters.  [required]
* `-v, --verbose`: Whether to print debug messages.
* `-u, --unobserved`: Run the experiments without Sacred observers.
* `-pm, --post-mortem`: Activate post-mortem debugging with pdb.
* `-ssd, --stored-sources-dir TEXT`: Load source files into this directory before starting.
* `-ds, --debug-server`: Run the experiment with a debug server, to which you can remotely connect with e.g. VS Code. Implies `--debug`.
* `--help`: Show this message and exit.

## `seml print-command`

Print the commands that would be executed by `start`.

**Usage**:

```console
$ seml print-command [OPTIONS]
```

**Options**:

* `-id, --sacred-id INTEGER`: Sacred ID (_id in the database collection) of the experiment. Takes precedence over other filters.
* `-s, --filter-states [STAGED|QUEUED|PENDING|RUNNING|FAILED|KILLED|INTERRUPTED|COMPLETED]`: List of states to filter the experiments by. If empty (""), all states are considered.  [default: STAGED, QUEUED]
* `-fd, --filter-dict DICT`: Dictionary (passed as a string, e.g. '{"config.dataset": "cora_ml"}') to filter the experiments by.
* `-b, --batch-id INTEGER`: Batch ID (batch_id in the database collection) of the experiments. Experiments that were staged together have the same batch_id.
* `-n, --num-experiments INTEGER`: Number of experiments to start. 0: all (staged) experiments   [default: 0]
* `-wg, --worker-gpus TEXT`: The IDs of the GPUs used by the local worker. Will be directly passed to CUDA_VISIBLE_DEVICES.
* `-wc, --worker-cpus INTEGER`: The number of CPUs used by the local worker. Will be directly passed to OMP_NUM_THREADS.
* `-we, --worker-env DICT`: Further environment variables to be set for the local worker.
* `--unresolved`: Whether to print the unresolved command.
* `--no-interpolation`: Whether disable variable interpolation. Only compatible with --unresolved.
* `--help`: Show this message and exit.

## `seml print-experiment`

Print the experiment document.

**Usage**:

```console
$ seml print-experiment [OPTIONS]
```

**Options**:

* `-id, --sacred-id INTEGER`: Sacred ID (_id in the database collection) of the experiment. Takes precedence over other filters.
* `-s, --filter-states [STAGED|QUEUED|PENDING|RUNNING|FAILED|KILLED|INTERRUPTED|COMPLETED]`: List of states to filter the experiments by. If empty (""), all states are considered.  [default: PENDING, STAGED, QUEUED, RUNNING, FAILED, KILLED, INTERRUPTED, COMPLETED]
* `-fd, --filter-dict DICT`: Dictionary (passed as a string, e.g. '{"config.dataset": "cora_ml"}') to filter the experiments by.
* `-b, --batch-id INTEGER`: Batch ID (batch_id in the database collection) of the experiments. Experiments that were staged together have the same batch_id.
* `-p, --projection KEY`: List of configuration keys, e.g., `config.model`, to additionally print.
* `-F, --format TEXT`: The format in which to print the experiment document.  [default: yaml]
* `--help`: Show this message and exit.

## `seml print-fail-trace`

Prints fail traces of all failed experiments.

**Usage**:

```console
$ seml print-fail-trace [OPTIONS]
```

**Options**:

* `-id, --sacred-id INTEGER`: Sacred ID (_id in the database collection) of the experiment. Takes precedence over other filters.
* `-fd, --filter-dict DICT`: Dictionary (passed as a string, e.g. '{"config.dataset": "cora_ml"}') to filter the experiments by.
* `-b, --batch-id INTEGER`: Batch ID (batch_id in the database collection) of the experiments. Experiments that were staged together have the same batch_id.
* `-s, --filter-states [STAGED|QUEUED|PENDING|RUNNING|FAILED|KILLED|INTERRUPTED|COMPLETED]`: List of states to filter the experiments by. If empty (""), all states are considered.  [default: FAILED, KILLED, INTERRUPTED]
* `-p, --projection KEY`: List of configuration keys, e.g., `config.model`, to additionally print.
* `--help`: Show this message and exit.

## `seml print-output`

Print the output of experiments.

**Usage**:

```console
$ seml print-output [OPTIONS]
```

**Options**:

* `-id, --sacred-id INTEGER`: Sacred ID (_id in the database collection) of the experiment. Takes precedence over other filters.
* `-s, --filter-states [STAGED|QUEUED|PENDING|RUNNING|FAILED|KILLED|INTERRUPTED|COMPLETED]`: List of states to filter the experiments by. If empty (""), all states are considered.  [default: RUNNING, FAILED, KILLED, INTERRUPTED, COMPLETED]
* `-fd, --filter-dict DICT`: Dictionary (passed as a string, e.g. '{"config.dataset": "cora_ml"}') to filter the experiments by.
* `-b, --batch-id INTEGER`: Batch ID (batch_id in the database collection) of the experiments. Experiments that were staged together have the same batch_id.
* `-sl, --slurm`: Whether to print the Slurm output instead of the experiment output.
* `-h, --head INTEGER`: Print the first n lines of the output.
* `-t, --tail INTEGER`: Print the last n lines of the output.
* `--help`: Show this message and exit.

## `seml project`

Setting up new projects.

**Usage**:

```console
$ seml project [OPTIONS] COMMAND [ARGS]...
```

**Options**:

* `--help`: Show this message and exit.

**Commands**:

* `init`: Initialize a new project in the given...
* `list-templates`: List available project templates.

### `seml project init`

Initialize a new project in the given directory.

**Usage**:

```console
$ seml project init [OPTIONS] [DIRECTORY]
```

**Arguments**:

* `[DIRECTORY]`: The directory in which to initialize the project.  [default: .]

**Options**:

* `-t, --template TEXT`: The template to use for the project. To view available templates use `seml project list-templates`.  [default: default]
* `-n, --name TEXT`: The name of the project. (By default inferred from the directory name.)
* `-u, --username TEXT`: The author name to use for the project. (By default inferred from $USER)
* `-m, --usermail TEXT`: The author email to use for the project. (By default empty.)
* `-r, --git-remote TEXT`: The git remote to use for the project. (By default SETTINGS.TEMPLATE_REMOTE.)
* `-c, --git-commit TEXT`: The exact git commit to use. May also be a tag or branch (By default latest)
* `-y, --yes`: Automatically confirm all dialogues with yes.
* `--help`: Show this message and exit.

### `seml project list-templates`

List available project templates.

**Usage**:

```console
$ seml project list-templates [OPTIONS]
```

**Options**:

* `-r, --git-remote TEXT`: The git remote to use for the project. (By default SETTINGS.TEMPLATE_REMOTE.)
* `-c, --git-commit TEXT`: The exact git commit to use. May also be a tag or branch (By default latest)
* `--help`: Show this message and exit.

## `seml queue`

Prints the collections of the given job IDs. If none is specified, all jobs are considered.

**Usage**:

```console
$ seml queue [OPTIONS] [JOB_IDS]...
```

**Arguments**:

* `[JOB_IDS]...`: The job IDs of the experiments to get the collection for.

**Options**:

* `-s, --filter-states [STAGED|QUEUED|PENDING|RUNNING|FAILED|KILLED|INTERRUPTED|COMPLETED]`: List of states to filter the experiments by. If empty (""), all states are considered.  [default: PENDING, RUNNING]
* `-a, --all`: Whether to attempt finding the collection of the jobs of all users.
* `-w, --watch`: Whether to watch the queue.
* `--help`: Show this message and exit.

## `seml release`

Release held experiments via SLURM.

**Usage**:

```console
$ seml release [OPTIONS]
```

**Options**:

* `-b, --batch-id INTEGER`: Batch ID (batch_id in the database collection) of the experiments. Experiments that were staged together have the same batch_id.
* `--help`: Show this message and exit.

## `seml reload-sources`

Reload stashed source files.

**Usage**:

```console
$ seml reload-sources [OPTIONS]
```

**Options**:

* `-k, -keep-old`: Keep the old source files in the database.
* `-b, --batch-ids INTEGER`: Batch IDs (batch_id in the database collection) of the experiments. Experiments that were staged together have the same batch_id.
* `-y, --yes`: Automatically confirm all dialogues with yes.
* `--help`: Show this message and exit.

## `seml reset`

Reset the state of experiments by setting their state to STAGED and cleaning their database entry.
Does not cancel Slurm jobs.

**Usage**:

```console
$ seml reset [OPTIONS]
```

**Options**:

* `-id, --sacred-id INTEGER`: Sacred ID (_id in the database collection) of the experiment. Takes precedence over other filters.
* `-s, --filter-states [STAGED|QUEUED|PENDING|RUNNING|FAILED|KILLED|INTERRUPTED|COMPLETED]`: List of states to filter the experiments by. If empty (""), all states are considered.  [default: FAILED, KILLED, INTERRUPTED]
* `-fd, --filter-dict DICT`: Dictionary (passed as a string, e.g. '{"config.dataset": "cora_ml"}') to filter the experiments by.
* `-b, --batch-id INTEGER`: Batch ID (batch_id in the database collection) of the experiments. Experiments that were staged together have the same batch_id.
* `-y, --yes`: Automatically confirm all dialogues with yes.
* `--help`: Show this message and exit.

## `seml start`

Fetch staged experiments from the database and run them (by default via Slurm).

**Usage**:

```console
$ seml start [OPTIONS]
```

**Options**:

* `-id, --sacred-id INTEGER`: Sacred ID (_id in the database collection) of the experiment. Takes precedence over other filters.
* `-fd, --filter-dict DICT`: Dictionary (passed as a string, e.g. '{"config.dataset": "cora_ml"}') to filter the experiments by.
* `-b, --batch-id INTEGER`: Batch ID (batch_id in the database collection) of the experiments. Experiments that were staged together have the same batch_id.
* `-d, --debug`: Run a single interactive experiment without Sacred observers and with post-mortem debugging. Implies `--verbose --num-exps 1 --post-mortem --output-to-console`.
* `-ds, --debug-server`: Run the experiment with a debug server, to which you can remotely connect with e.g. VS Code. Implies `--debug`.
* `-l, --local`: Run the experiment locally instead of on a Slurm cluster.
* `-nw, --no-worker`: Do not launch a local worker after setting experiments' state to PENDING.
* `-n, --num-experiments INTEGER`: Number of experiments to start. 0: all (staged) experiments   [default: 0]
* `-nf, --no-file-output`: Do not write the experiment's output to a file.
* `-ss, --steal-slurm`: Local jobs 'steal' from the Slurm queue, i.e. also execute experiments waiting for execution via Slurm.
* `-pm, --post-mortem`: Activate post-mortem debugging with pdb.
* `-o, --output-to-console`: Write the experiment's output to the console.
* `-wg, --worker-gpus TEXT`: The IDs of the GPUs used by the local worker. Will be directly passed to CUDA_VISIBLE_DEVICES.
* `-wc, --worker-cpus INTEGER`: The number of CPUs used by the local worker. Will be directly passed to OMP_NUM_THREADS.
* `-we, --worker-env DICT`: Further environment variables to be set for the local worker.
* `--help`: Show this message and exit.

## `seml start-jupyter`

Start a Jupyter slurm job. Uses SBATCH options defined in settings.py under
SBATCH_OPTIONS_TEMPLATES.JUPYTER

**Usage**:

```console
$ seml start-jupyter [OPTIONS]
```

**Options**:

* `-l, --lab`: Start a jupyter-lab instance instead of jupyter notebook.
* `-c, --conda-env TEXT`: Start the Jupyter instance in a Conda environment.
* `-sb, --sbatch-options DICT`: Dictionary (passed as a string, e.g. '{"gres": "gpu:2"}') to request two GPUs.
* `--help`: Show this message and exit.

## `seml status`

Report status of experiments in the database collection.

**Usage**:

```console
$ seml status [OPTIONS]
```

**Options**:

* `-u, --update-status`: Whether to update the status of experiments in the database. This can take a while for large collections. Use only if necessary.  [default: True]
* `-p, --projection KEY`: List of configuration keys, e.g., `config.model`, to additionally print.
* `--help`: Show this message and exit.

## `seml update-working-dir`

Change the working directory of experiments in case you moved the source code to a different location.

**Usage**:

```console
$ seml update-working-dir [OPTIONS] WORKING_DIR
```

**Arguments**:

* `WORKING_DIR`: The new working directory for the experiments.  [required]

**Options**:

* `-b, --batch-ids INTEGER`: Batch IDs (batch_id in the database collection) of the experiments. Experiments that were staged together have the same batch_id.
* `--help`: Show this message and exit.


================================================
FILE: examples/.ruff.toml
================================================
[lint]
ignore = [
    "F841", # ignore errors due to unused variables
]


================================================
FILE: examples/README.md
================================================
# Start a Jupyter job
To start a Jupyter instance, you can use the convenience function `seml start-jupyter`. This requires having Jupyter Notebook or Jupyter Lab installed in the current (or specified) environment.

To modify the default Slurm `SBATCH`
options, see `seml/settings.py`. The easiest way of changing these is via a file in `$HOME/.config/seml/settings.py`.
This file must contain a `SETTINGS` dictionary, structured in the same way as the one in `seml/settings.py`.

After the Jupyter instance has successfully started, `seml` will provide useful information such as the hostname and
port of the instance, e.g.:
```
Started Jupyter job in Slurm job with ID 12345.
The logfile of the job is /nfs/homedirs/zuegnerd/libraries/seml/slurm-6322311.out.
Trying to fetch the machine and port of the Jupyter instance once the job is running... (ctrl-C to cancel).
Jupyter instance is starting up...
Startup completed. The Jupyter instance is running at 'gpuxx.kdd.in.tum.de:8889'.
To stop the job, run 'scancel 12345'.
```
# Experiment tracking example
This example will show you how to track your experiments using Sacred, how to perform hyperparameter search and how to perform the experiments in a distributed manner on our Slurm cluster.


## MongoDB configuration
Before starting, please make sure you have your MongoDB credentials stored in `$HOME/.config/seml/mongodb.config`. The easiest way to do so is to run `seml configure`, which will store your credentials in the correct format in the right place.


## Experiment configuration

In `example_config.yaml` we define the parameter configurations that will be run.
For a more advanced example with modular structure using
[Sacred prefixes](https://sacred.readthedocs.io/en/stable/configuration.html#prefix),
see the [advanced example configuration](advanced_example_config.yaml) and the corresponding
[experiment](advanced_example_experiment.py).
<details><summary><b>Example config file</b></summary>

```yaml
seml:
  executable: examples/example_experiment.py
  name: example_experiment
  output_dir: examples/logs
  project_root_dir: ..

slurm:
  experiments_per_job: 1
  sbatch_options:
    gres: gpu:1       # num GPUs
    mem: 16G          # memory
    cpus-per-task: 2  # num cores
    time: 0-08:00     # max time, D-HH:MM

###### BEGIN PARAMETER CONFIGURATION ######

fixed:
  max_epochs: 500

grid:

  learning_rate:
    type: loguniform
    min: 1e-5
    max: 1e-1
    num: 5

random:
  samples: 3
  seed: 821

  # SEML supports dot-notation for nested dictionaries.
  regularization_params.dropout:
    type: uniform
    min: 0.0
    max: 0.7
    seed: 222

small_datasets:

  grid:
    dataset:
      type: choice
      options:
        - small_dataset_1
        - small_dataset_2

    hidden_sizes:
      type: choice
      options:
        - [16]
        - [32, 16]  # this will be parsed into a Python list.

  random:
    samples: 3
    seed: 2223

    max_epochs:
       type: randint
       min: 200
       max: 1000

large_datasets:

  fixed:
    max_epochs: 1000

  grid:
    learning_rate:
      type: choice
      options:
        - 0.001

    dataset:
      type: choice
      options:
        - large_dataset_1
        - large_dataset_2

    hidden_sizes:
      type: choice
      options:
        - [64]
        - [64, 32]
```
</details>
There are two special blocks for meta-configuration: `seml` and `slurm`.

### `seml` block
The `seml` block is required for every experiment. It has to contain the following values:
   - `executable`: Name of the Python script containing the experiment. The path should be relative to the `project_root_dir`.
                   For backward compatibility SEML also supports paths relative to the location of the config file.
                   In case there are files present both relative to the project root and the config file, the former takes precedence.
Optionally, it can contain
   - `name`: Prefix for output file and Slurm job name. Default: Collection name
   - `output_dir`: Directory to store log files in. Default: Current directory
   - `conda_environment`: Specifies which Anaconda virtual environment will be activated before the experiment is executed.
                          Default: The environment used when queuing.
   - `project_root_dir`: (Relative or absolute) path to the root of the project. seml will then upload all the source
                         files imported by the experiment to the MongoDB. Moreover, the uploaded source files will be
                         downloaded before starting an experiment, so any changes to the source files in the project
                         between staging and starting the experiment will have no effect.
### `slurm` block
The special 'slurm' block contains the slurm parameters. This block and all values are optional. Possible values are:
   - `experiments_per_job`: Number of parallel experiments to run in each Slurm job. Note that only experiments from the same batch share a job. Default: 1
   - `max_simultaneous_jobs`: Maximum number of simultaneously running Slurm jobs per job array. Default: No restriction
   - `sbatch_options_template`: Name of a custom template of `SBATCH` options. Define your own templates in `settings.py`
     under `SBATCH_OPTIONS_TEMPLATES`, e.g. for long-running jobs, CPU-only jobs, etc.
   - `sbatch_options`: dictionary that contains custom values that will be passed to `sbatch`, specifying e.g. the
                       memory and the number of GPUs to be allocated. See [here](https://slurm.schedmd.com/sbatch.html)
                       for possible parameters of `sbatch` (prepended dashes are not required). Values provided here
                       overwrite any values defined in a `SBATCH` options template.

### Sub-configurations
In the `small_datasets` and `large_datasets` (names are of course only examples; you can name sub-configs as you like) we have specified different sets of parameters to try.
They will be combined with the parameters in `grid` in the root of the document.

If a specific configuration (e.g. `large_datasets`) defines the same parameters as a higher-level configuration (e.g., the "root" configuration),
 they will override the ones defined before, e.g. the learning rate in the example above.
This means that for all configurations in the `large_datasets` the learning rate will be `0.001` and not `0.01` or
`0.05` as defined in the root of the document.
This can be nested arbitrarily deeply (be aware of combinatorial explosion of the parameter space, though).

If a parameter is defined in (at least) two **different blocks** in `[grid, random, fixed]` on the same level, `seml` will throw an error to avoid ambiguity.
If a parameter is re-defined in a sub-configuration, the redefinition overrides any previous definitions of that parameter.

### Grid parameters
In an experiment config, under `grid` you can define parameters that should be sampled from a regular grid. Currently supported
are:
   - `choice`: List the different values you want to evaluate under `options`.
   - `range`: Specify the `min`, `max`, and `step`. Parameter values will be generated using `np.arange(min, max, step)`.
   - `uniform`: Specify the `min`, `max`, and `num`. Parameter values will be generated using
                `np.linspace(min, max, num, endpoint=True)`
   - `loguniform`: Specify `min`, `max`, and `num`. Parameter values will be uniformly generated in log space (base 10).

Additionally, `grid` parameters might be coupled by setting the `zip_id` property. All parameters with the same `zip_id` are treated as a single dimension when constructing the cartesian product of parameters. This ensures that zipped parameters only change jointly.

### Random parameters
Under 'random' you can specify parameters for which you want to try several random values. Specify the number
of samples per parameter with the `samples` value and optionally the random seed with `seed` as in the examples below. Supported parameter types are:
  - `choice`: Randomly samples `<samples>` entries (with replacement) from the list in `options`
  - `uniform`: Uniformly samples between `min` and `max` as specified in the parameter dict.
  - `loguniform`:  Uniformly samples in log space between `min` and `max` as specified in the parameter dict.
  - `randint`: Randomly samples integers between `min` (included) and `max` (excluded).

### Named Configurations
`sacred`, the on which experiments are based on, allows to define subgroups of configurations via its [named configurations](https://sacred.readthedocs.io/en/stable/configuration.html#named-configurations) feature. These can either be defined in external files (yaml, json, ...) or in functions decorated with `experiment.named_config`. SEML also supports this functionality by defining parameter groups that have the prefix `'+'`. Two config values can be defined for such parameter groups:
- `name`: The name of the named config, i.e. the name of the python function or the path to the file to load
- `priority`: Defines in which order the named configs will be loaded. Configs with lower priority will be listed first and thus resolved first. Therefore, the highest priority item will have the highest precedence. If no priority is given, this will be treated as `infinity`. Ties are broken based on the name of the named config.



### Variable Interpolation
Config values can be interpolated relative to other values using [OmegaConf](https://omegaconf.readthedocs.io/en/2.3_branch/usage.html#variable-interpolation). For example:

```yaml
model.name: resnet
dataset: mnist
something.name: ${model.name}_${dataset} # will have value: resnet_mnist
```

## Add experiments to database

All SEML commands follow the pattern
```
seml [database_collection_name] [command] [command_options]
```

To insert the experiments to the database, open a terminal on a machine with access to the `Slurm` system. Move to this directory and run

```
seml seml_example add example_config.yaml
```

If you open your MongoDB (e.g. with the software `robo3t`), you should now find a collection `seml_example` with the staged experiments.
Note that the collection name is specified _before_ the operation (`add`).

To see what the option `--force` does, run the above command again. The output should now read something like:

```
72 of 72 experiments were already found in the database. They were not added again.
```

That is, the script checks whether experiments with the same configuration are already in the database collection.
In this case, they are not added to the database to avoid redundant computations. In order to force add duplicates to the database, use the `--force` argument.

All experiments are now already in the database collection you specified and in the STAGED state.

## Run experiments using Slurm
To run the staged experiments on the Slurm cluster, run:
```bash
seml seml_example start
```
This will start all experiments in the MongoDB collection `seml_example` that currently are in the STAGED state.

## Run experiments locally
You can also run your experiments locally without Slurm. For this, add the `--local` option:
```bash
seml seml_example start --local
```
You can even have multiple local workers running jobs in parallel. To add a local worker, run
```bash
seml seml_example launch-worker --worker-gpus="1" --worker-cpus=8
```
In this example, the worker will use the GPU with ID 1 (i.e., set `CUDA_VISIBLE_DEVICES="1"`) and can use 8 CPU cores.

The `--steal-slurm` option allows local workers to pop experiments from the Slurm queue. Since SEML checks the
database state of each experiment before actually executing it via Slurm, there is no risk of running duplicate
experiments.

## Debugging experiments

To run an interactive debug session on Slurm (or locally) you can start an experiment with the `--debug` option.

For even more convenience you can also use VS Code for a remote debug session. First make sure that your experiments were added to the database with the `--no-code-checkpoint` option:

```
seml seml_example add example_config.yaml -ncc
```

This will prevent the caching of your code in the MongoDB and allow you to directly run the code that is in your working directory, set breakpoints and interactively step through your code in VS Code.

To start a remote debug server run:

```
seml seml_example start --debug-server
```

This will add your experiment to the queue, wait for the necessary resources to be assigned, spawn a debug process on the server and print the debug server's IP address and port number. The experiment will only start running once the VS Code client is attached.

To attach to the debug server you need to add the printed IP address and port number to the `.vscode/launch.json` config:
```
{
    "configurations": [
        {
            "name": "Python: Attach",
            "type": "python",
            "request": "attach",
            "connect": {
                "host": "YOUR_DEBUG_SERVER_IP",
                "port": YOUR_DEBUG_SERVER_PORT
            }
        }
    ]
}
```
The IP address and port number of the debug server might change at every start, so make sure to update the `host` and `port` launch config.
Note: The "restart" operation of the VS Code Debugger is not supported.

## Running multiple experiments per Slurm job
Often a single experiment does not fully utilize the GPU and requires much less GPU RAM than available. Thus, we can often
run multiple experiments per Slurm job (which commonly uses a single GPU) to increase the throughput of our experiments.
This can be done by setting the `experiments_per_job` argument in the `slurm` block of the config file.

Note that this will only run your own experiments in parallel on a GPU. It will never run
your experiments on a GPU that is reserved by another user's job.
Furthermore, only experiments from the same batch share jobs.

## Check the status of your Slurm jobs

You can check the status of your Slurm jobs by running `squeue` or `seml seml_example status`
in the terminal. To check the console output of a experiment, open the corresponding logfile, e.g. `cat slurm-564.out`.

To check whether some experiments may have failed due to errors, you can run:
```bash
seml seml_example status
```

You can cancel (interrupt) all pending and running experiments with
```bash
seml seml_example cancel
```

You can reset all failed, killed, or interrupted experiments to STAGED with
```bash
seml seml_example reset
```

You can delete all staged, failed, killed, or interrupted experiments with
```bash
seml seml_example delete
```

These three commands also support passing a specific Sacred ID and a custom list of states.

Moreover, you can specifically cancel/reset/delete experiments that match a custom dictionary, e.g.
```bash
seml seml_example cancel --filter-dict '{"config.dataset":"cora_ml", "config.hidden_sizes": [16]}'
```

Finally, you can manually detect experiments whose corresponding Slurm jobs were killed unexpectedly with
```bash
seml seml_example detect-killed
```
(Detection is run automatically when executing the `status`, `delete`, `reset`, and `cancel` commands and therefore rarely necessary to do manually.)

### Batches
`seml` assigns each experiment a batch ID, where all experiments that were staged together get the same batch ID.
You can use this to cancel all the experiments from the last configuration that you've started, e.g. if you find a bug.
Use
```bash
seml seml_example cancel --batch-id i
```
or equivalently
 ```bash
seml seml_example cancel --filter-dict '{"batch_id": i}'
```
to cancel all jobs from batch `i`.

## Retrieve and evaluate results
See the [example notebook](notebooks/experiment_results.ipynb) for an example of how to retrieve and evaluate our toy experiment's results.


## Command chaining
`seml` also supports command chaining to execute multiple `seml` commands sequentially, i.e.,
```bash
seml seml_example add advanced_example_config.yaml start
```
to add a config file and start it immediately after or
```
seml seml_example cancel -y reset -y reload-sources start
```
to cancel experiments, reset them, reload their source files and restarting them.


================================================
FILE: examples/advanced_example_config.yaml
================================================
# Experiment configuration file.
#
# There are two special blocks. The 'seml' block is required for every experiment.
# It has to contain the following values:
# executable:        Name of the Python script containing the experiment. The path should be relative to the `project_root_dir`.
#                    For backward compatibility SEML also supports paths relative to the location of the config file.
#                    In case there are files present both relative to the project root and the config file,
#                    the former takes precedence.
# It can optionally also contain the following values:
# name:              Prefix for output file and Slurm job name. Default: Collection name
# output_dir:        Directory to store log files in. Default: Current directory
# conda_environment: Specifies which Anaconda virtual environment will be activated before the experiment is executed.
#                    Default: The environment used when queuing.
# project_root_dir:  (Relative or absolute) path to the root of the project. seml will then upload all the source
#                    files imported by the experiment to the MongoDB. Moreover, the uploaded source files will be
#                    downloaded before starting an experiment, so any changes to the source files in the project
#                    between queueing and starting the experiment will have no effect.
#
# The special 'slurm' block contains the slurm parameters. This block and all values are optional. Possible values are:
# experiments_per_job:   Number of parallel experiments to run in each Slurm job.
#                        Note that only experiments from the same batch share a job. Default: 1
# max_simultaneous_jobs: Maximum number of simultaneously running Slurm jobs per job array. Default: No restriction
# sbatch_options:        dictionary that contains custom values that will be passed to `sbatch`, specifying e.g.
#                        the memory and number of GPUs to be allocated (prepended dashes are not required). See
#                        https://slurm.schedmd.com/sbatch.html for all possible options.
#
# Parameters under 'fixed' will be used for all the experiments.
#
# Under 'grid' you can define parameters that should be sampled from a regular grid. Options are:
#   - choice:     List the different values you want to evaluate under 'choices' as in the example below.
#   - range:      Specify the min, max, and step. Parameter values will be generated using np.arange(min, max, step).
#   - uniform:    Specify the min, max, and num. Parameter values will be generated using
#                 np.linspace(min, max, num, endpoint=True)
#   - loguniform: Specify min, max, and num. Parameter values will be uniformly generated in log space (base 10).
# Additionally, one may supply the 'zip_id' argument to zip multiple parameters to a single dimension.
# This causes these parameters to only change jointly. All parameters within a group must have the same number of options.
#
#
# Under 'random' you can specify parameters for which you want to try several random values. Specify the number
# of samples per parameter with the 'samples' value as in the examples below.
# Specify the the seed under the 'random' dict or directly for the desired parameter(s).
# Supported parameter types are:
#   - choice:      Randomly samples <samples> entries (with replacement) from the list in parameter['options']
#   - uniform:     Uniformly samples between 'min' and 'max' as specified in the parameter dict.
#   - loguniform:  Uniformly samples in log space between 'min' and 'max' as specified in the parameter dict.
#   - randint:     Randomly samples integers between 'min' (included) and 'max' (excluded).
#
# The configuration file can be nested (as the example below) so that we can run different parameter sets
# e.g. for different datasets or models.
# We take the cartesian product of all `grid` parameters on a path and sample all random parameters on the path.
# The number of random parameters sampled will be max{n_samples} of all n_samples on the path. This is done because
# we need the same number of samples from all random parameters in a configuration.
#
# More specific settings (i.e., further down the hierarchy) always overwrite more general ones.

seml:
  executable: advanced_example_experiment.py
  name: advanced_example_experiment
  output_dir: logs
  project_root_dir: .
  description: "An advanced example configuration. We can also use variable interpolation here: ${config.model.model_type}"
  reschedule_timeout: 300 # The time (in seconds) that are left on the job before SEML will try to reschedule unfinished experiments.
  # Note that you have to implement a `reschedule_hook` to use this feature.

slurm:
  - experiments_per_job: 1
    max_simultaneous_jobs: 4 # Restrict number of simultaneously running jobs per job array
    sbatch_options:
      gres: gpu:1 # num GPUs
      mem: 16G # memory
      cpus-per-task: 2 # num cores
      time: 0-08:00 # max time, D-HH:MM
      partition: gpu_gtx1080 # use the 1080ti partition
  # We can also increase the number of jobs if we run on A100s
  - experiments_per_job: 4
    max_simultaneous_jobs: 4 # Restrict number of simultaneously running jobs per job array
    sbatch_options:
      gres: gpu:1 # num GPUs
      mem: 16G # memory
      cpus-per-task: 8 # num cores
      time: 0-08:00 # max time, D-HH:MM
      partition: gpu_a100 # Use A100

###### BEGIN PARAMETER CONFIGURATION ######

fixed:
  training.patience: 20
  training.num_epochs: 100
  optimization:
    optimizer_type: Adam
  +batchnorm.priority: 2

grid:
  # SEML supports dot-notation for nested parameter dictionaries. E.g., `model.model_type` resolves to
  # {'model': {'model_type': xxx}}
  model.model_type:
    type: choice
    options:
      - variant_1
      - variant_2
    zip_id: model_dataset # The model_type jointly changes with all other parameters of the mode_dataset group

  # The `+` prefix indicates that a named config should be used. The `name` attribute selects which named config is run
  # Here, setting `batchnorm.name` to `batchnorm`, will run the named config `batchnorm` (see `advanced_example_experiment.py`)
  # Setting `batchnorm.priority` to `2`, will affect the order in which the named configs are loaded
  +batchnorm.name:
    type: choice
    options:
      - batchnorm
      - no_batchnorm

  # Instead of setting the `name` attribute, you can equivalently set `preprocessing` to a the corresponding named config.
  # seml will infer to set the `name` attribute for you
  +preprocessing:
    type: choice
    options:
      - preprocessing_none
      - preprocessing_normalize

  # You can also specify named configs from file paths, e.g. yaml files. These paths are expected to be relative to `project_root_dir`.
  +augmentation:
    type: choice
    options:
      - config/flip_augmentation.yaml

  optimization.regularization.weight_decay:
    type: loguniform
    min: 1e-6
    max: 1e-3
    num: 4

random:
  samples: 5
  seed: 7059
  model.model_params.dropout:
    type: uniform
    min: 0.0
    max: 0.5

large_datasets:
  grid:
    data.dataset:
      type: choice
      options:
        - large_dataset_1
        - large_dataset_2
      zip_id: model_dataset # Use variant_1 for large_dataset_1 and variant_2 for large_dataset_2

    model.model_params.hidden_sizes:
      type: choice
      options:
        - [64]
        - [64, 32]

small_datasets:
  grid:
    data.dataset:
      type: choice
      options:
        - small_dataset_1
        - small_dataset_2
      zip_id: model_dataset # Use variant_1 for small_dataset_1 and variant_2 for small_dataset_2

    model.model_params.hidden_sizes:
      type: choice
      options:
        - [32]
        - [32, 16]


================================================
FILE: examples/advanced_example_experiment.py
================================================
"""
This is an advanced experiment example, which makes use of sacred's captured functions with prefixes.
We wrap all the experiment-specific functionality inside the "ExperimentWrapper" class, and define methods with sacred's
@ex.capture decorator. This allows a modular design of the configuration, where certain sub-dictionaries (e.g., "data")
are parsed by a specific method. This avoids having one large "main" function which takes all parameters as input.
"""

import numpy as np
from seml import Experiment

ex = Experiment()


# Named configs can be used to define subconfigurations in a modular way. They can be composed in the experiment's configuration yaml file.


@ex.named_config
def preprocessing_none():
    """A named configuration that can be enabled in the configuration yaml file"""
    preprocessing = {
        "mean": 0.0,
        "std": 1.0,
    }


@ex.named_config
def preprocessing_normalize():
    """A named configuration that can be enabled in the configuration yaml file"""
    preprocessing = {
        "mean": 0.377,
        "std": 0.23,
    }


@ex.named_config
def batchnorm():
    """A named configuration that can be enabled in the configuration yaml file"""
    model = {"batchnorm": True}


@ex.named_config
def no_batchnorm():
    """A named configuration that can be enabled in the configuration yaml file"""
    model = {"batchnorm": False, "residual": False}


@ex.config
def config():
    name = "${config.model.model_type}_${config.data.dataset}"


class ModelVariant1:
    """
    A dummy model variant 1, which could, e.g., be a certain model or baseline in practice.
    """

    def __init__(self, hidden_sizes, dropout, batchnorm, residual):
        self.hidden_sizes = hidden_sizes
        self.dropout = dropout
        self.batchnorm = batchnorm
        self.residual = residual


class ModelVariant2:
    """
    A dummy model variant 2, which could, e.g., be a certain model or baseline in practice.
    """

    def __init__(self, hidden_sizes, dropout, batchnorm, residual):
        self.hidden_sizes = hidden_sizes
        self.dropout = dropout
        self.batchnorm = batchnorm
        self.residual = residual


class ExperimentWrapper:
    """
    A simple wrapper around a sacred experiment, making use of sacred's captured functions with prefixes.
    This allows a modular design of the configuration, where certain sub-dictionaries (e.g., "data") are parsed by
    specific method. This avoids having one large "main" function which takes all parameters as input.
    """

    def __init__(self, init_all=True):
        if init_all:
            self.init_all()

    # With the prefix option we can "filter" the configuration for the sub-dictionary under "data".
    @ex.capture(prefix="data")
    def init_dataset(self, dataset):
        """
        Perform dataset loading, preprocessing etc.
        Since we set prefix="data", this method only gets passed the respective sub-dictionary, enabling a modular
        experiment design.
        """
        if dataset == "large_dataset_1":
            self.data = "load_dataset_here"
        elif dataset == "large_dataset_2":
            self.data = "and so on"
        # ...
        else:
            self.data = "..."

    @ex.capture(prefix="model")
    def init_model(
        self,
        model_type: str,
        model_params: dict,
        batchnorm: bool,
        residual: bool = True,
    ):
        if model_type == "variant_1":
            # Here we can pass the "model_params" dict to the constructor directly, which can be very useful in
            # practice, since we don't have to do any model-specific processing of the config dictionary.
            self.model = ModelVariant1(
                **model_params, batchnorm=batchnorm, residual=residual
            )
        elif model_type == "variant_2":
            self.model = ModelVariant2(
                **model_params, batchnorm=batchnorm, residual=residual
            )

    @ex.capture(prefix="optimization")
    def init_optimizer(self, regularization: dict, optimizer_type: str):
        weight_decay = regularization["weight_decay"]
        self.optimizer = optimizer_type  # initialize optimizer

    @ex.capture(prefix="preprocessing")
    def init_preprocessing(self, mean: float, std: float):
        self.preprocessing_parameters = (mean, std)

    @ex.capture(prefix="augmentation")
    def init_augmentation(self, flip: bool):
        self.augmentation_parameters = (flip,)

    def init_all(self):
        """
        Sequentially run the sub-initializers of the experiment.
        """
        self.init_dataset()
        self.init_model()
        self.init_optimizer()
        self.init_preprocessing()
        self.init_augmentation()

    @ex.capture(prefix="training")
    def train(self, patience, num_epochs):
        # everything is set up
        for e in range(num_epochs):
            # simulate training

            # calling reschedule hook
            reschedule_hook(model_weights={}, step=e)
            continue
        results = {
            "test_acc": 0.5 + 0.3 * np.random.randn(),
            "test_loss": np.random.uniform(0, 10),
            # ...
        }
        return results


# We can call this command, e.g., from a Jupyter notebook with init_all=False to get an "empty" experiment wrapper,
# where we can then for instance load a pretrained model to inspect the performance.
@ex.command(unobserved=True)
def get_experiment(init_all=False):
    print("get_experiment")
    experiment = ExperimentWrapper(init_all=init_all)
    return experiment


# This function will be called when the reschedule is triggered.
# It should save the current state of the experiment and return a
# dictionary that may be used to update the configuration upon rescheduling.
# You are responsible for implementing the actual saving/loading of the experiment state
# due to the updated config.
@ex.reschedule_hook
def reschedule_hook(model_weights, step, **kwargs):
    # Here you would save the current state of the experiment
    # and return any necessary configuration updates.

    # !!! You will need to call this function regularly from within your training loop
    # to check if rescheduling is needed.
    # Pass everything you need to store your state to this function.
    return {"checkpoint_path": "path/to/saved/checkpoint"}


# This function will be called by default. Note that we could in principle manually pass an experiment instance,
# e.g., obtained by loading a model from the database or by calling this from a Jupyter notebook.
@ex.automain
def train(experiment=None):
    if experiment is None:
        experiment = ExperimentWrapper()
    return experiment.train()


================================================
FILE: examples/config/flip_augmentation.yaml
================================================
augmentation:
  flip: True

================================================
FILE: examples/example_config.yaml
================================================
# Experiment configuration file.
#
# There are two special blocks. The 'seml' block is required for every experiment.
# It has to contain the following values:
# executable:        Name of the Python script containing the experiment. The path should be relative to the `project_root_dir`.
#                    For backward compatibility SEML also supports paths relative to the location of the config file.
#                    In case there are files present both relative to the project root and the config file,
#                    the former takes precedence.
# It can optionally also contain the following values:
# name:              Prefix for output file and Slurm job name. Default: Collection name
# output_dir:        Directory to store log files in. Default: Current directory
# conda_environment: Specifies which Anaconda virtual environment will be activated before the experiment is executed.
#                    Default: The environment used when queuing.
# project_root_dir:  (Relative or absolute) path to the root of the project. seml will then upload all the source
#                    files imported by the experiment to the MongoDB. Moreover, the uploaded source files will be
#                    downloaded before starting an experiment, so any changes to the source files in the project
#                    between queueing and starting the experiment will have no effect.
#
# The special 'slurm' block contains the slurm parameters. This block and all values are optional. Possible values are:
# experiments_per_job:     Number of parallel experiments to run in each Slurm job.
#                          Note that only experiments from the same batch share a job. Default: 1
# max_simultaneous_jobs:   Maximum number of simultaneously running Slurm jobs per job array. Default: No restriction
# sbatch_options_template: Name of a custom template of `SBATCH` options. Define your own templates in `settings.py`
#                          under `SBATCH_OPTIONS_TEMPLATES`, e.g. for long-running jobs, CPU-only jobs, etc.
# sbatch_options:          dictionary that contains custom values that will be passed to `sbatch`, specifying e.g.
#                          the memory and number of GPUs to be allocated (prepended dashes are not required). See
#                          https://slurm.schedmd.com/sbatch.html for all possible options.
#
# Parameters under 'fixed' will be used for all the experiments.
#
# Under 'grid' you can define parameters that should be sampled from a regular grid. Options are:
#   - choice:     List the different values you want to evaluate under 'choices' as in the example below.
#   - range:      Specify the min, max, and step. Parameter values will be generated using np.arange(min, max, step).
#   - uniform:    Specify the min, max, and num. Parameter values will be generated using
#                 np.linspace(min, max, num, endpoint=True)
#   - loguniform: Specify min, max, and num. Parameter values will be uniformly generated in log space (base 10).
#
# Under 'random' you can specify parameters for which you want to try several random values. Specify the number
# of samples per parameter with the 'samples' value as in the examples below.
# Specify the the seed under the 'random' dict or directly for the desired parameter(s).
# Supported parameter types are:
#   - choice:      Randomly samples <samples> entries (with replacement) from the list in parameter['options']
#   - uniform:     Uniformly samples between 'min' and 'max' as specified in the parameter dict.
#   - loguniform:  Uniformly samples in log space between 'min' and 'max' as specified in the parameter dict.
#   - randint:     Randomly samples integers between 'min' (included) and 'max' (excluded).
#
# The configuration file can be nested (as the example below) so that we can run different parameter sets
# e.g. for different datasets or models.
# We take the cartesian product of all `grid` parameters on a path and sample all random parameters on the path.
# The number of random parameters sampled will be max{n_samples} of all n_samples on the path. This is done because
# we need the same number of samples from all random parameters in a configuration.
#
# More specific settings (i.e., further down the hierarchy) always overwrite more general ones.

seml:
  executable: examples/example_experiment.py
  name: example_experiment
  output_dir: examples/logs
  project_root_dir: ..
  description: An example configuration.

slurm:
  - experiments_per_job: 1
    sbatch_options:
      gres: gpu:0
      mem: 1G
      cpus-per-task: 1
      time: 0-08:00
      partition: cpu_all,cpu_large
  - experiments_per_job: 4
    sbatch_options:
      gres: gpu:1 # num GPUs
      mem: 1G # memory
      cpus-per-task: 1 # num cores
      time: 0-08:00 # max time, D-HH:MM
      partition: gpu_gtx1080
  - experiments_per_job: 16
    sbatch_options:
      gres: gpu:1 # num GPUs
      mem: 1G # memory
      cpus-per-task: 1 # num cores
      time: 0-08:00 # max time, D-HH:MM
      partition: gpu_a100

###### BEGIN PARAMETER CONFIGURATION ######

fixed:
  max_epochs: 500

grid:
  learning_rate:
    type: loguniform
    min: 1e-5
    max: 1e-1
    num: 1

random:
  samples: 1
  seed: 821

  # SEML supports dot-notation for nested dictionaries.
  regularization_params.dropout:
    type: uniform
    min: 0.0
    max: 0.7
    seed: 222

small_datasets:
  grid:
    dataset:
      type: choice
      options:
        - small_dataset_1
        - small_dataset_2

    hidden_sizes:
      type: choice
      options:
        - [16]
        - [32, 16] # this will be parsed into a Python list.

  random:
    samples: 3
    seed: 2223

    max_epochs:
      type: randint
      min: 200
      max: 1000

large_datasets:
  fixed:
    max_epochs: 1000

  grid:
    learning_rate:
      type: choice
      options:
        - 0.001

    dataset:
      type: choice
      options:
        - large_dataset_1
        - large_dataset_2

    hidden_sizes:
      type: choice
      options:
        - [64]
        - [64, 32]


================================================
FILE: examples/example_experiment.py
================================================
import logging

import numpy as np
from seml import Experiment

ex = Experiment()


@ex.automain
def run(
    dataset: str,
    hidden_sizes: list,
    learning_rate: float,
    max_epochs: int,
    regularization_params: dict,
):
    # Note that regularization_params contains the corresponding sub-dictionary from the configuration.
    logging.info("Received the following configuration:")
    logging.info(
        f"Dataset: {dataset}, hidden sizes: {hidden_sizes}, learning_rate: {learning_rate}, "
        f"max_epochs: {max_epochs}, regularization: {regularization_params}"
    )

    #  do your processing here

    results = {
        "test_acc": 0.5 + 0.3 * np.random.randn(),
        "test_loss": np.random.uniform(0, 10),
        # ...
    }
    # the returned result will be written into the database
    return results


================================================
FILE: examples/logs/.gitignore
================================================
# Ignore everything in this directory
*
# Except this file
!.gitignore

================================================
FILE: examples/notebooks/experiment_results.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/nfs/homedirs/zuegnerd/libraries/seml/seml/database.py:7: TqdmExperimentalWarning: Using `tqdm.autonotebook.tqdm` in notebook mode. Use `tqdm.tqdm` instead to force console mode (e.g. in jupyter console)\n",
      "  from tqdm.autonotebook import tqdm\n"
     ]
    }
   ],
   "source": [
    "import seml\n",
    "from matplotlib import pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "373af497df2e440bb7ba65bb735be9a2",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "HBox(children=(FloatProgress(value=0.0, max=72.0), HTML(value='')))"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "2c9a3e5d482f4243a743dcebddd2ede1",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "HBox(children=(FloatProgress(value=0.0, max=72.0), HTML(value='')))"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/nfs/homedirs/zuegnerd/libraries/seml/seml/evaluation.py:80: FutureWarning: pandas.io.json.json_normalize is deprecated, use pandas.json_normalize instead\n",
      "  parsed = pd.io.json.json_normalize(parsed, sep='.')\n"
     ]
    }
   ],
   "source": [
    "results = seml.get_results(\"seml_example\", to_data_frame=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>_id</th>\n",
       "      <th>config.dataset</th>\n",
       "      <th>config.db_collection</th>\n",
       "      <th>config.hidden_sizes</th>\n",
       "      <th>config.learning_rate</th>\n",
       "      <th>config.max_epochs</th>\n",
       "      <th>config.overwrite</th>\n",
       "      <th>config.regularization_params.dropout</th>\n",
       "      <th>config.seed</th>\n",
       "      <th>result.test_acc</th>\n",
       "      <th>result.test_loss</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>small_dataset_1</td>\n",
       "      <td>seml_example</td>\n",
       "      <td>[16]</td>\n",
       "      <td>0.00001</td>\n",
       "      <td>519</td>\n",
       "      <td>1</td>\n",
       "      <td>0.440696</td>\n",
       "      <td>49592375</td>\n",
       "      <td>0.738594</td>\n",
       "      <td>0.333637</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "      <td>small_dataset_1</td>\n",
       "      <td>seml_example</td>\n",
       "      <td>[16]</td>\n",
       "      <td>0.00001</td>\n",
       "      <td>626</td>\n",
       "      <td>2</td>\n",
       "      <td>0.446743</td>\n",
       "      <td>912760071</td>\n",
       "      <td>0.714019</td>\n",
       "      <td>6.445487</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>3</td>\n",
       "      <td>small_dataset_1</td>\n",
       "      <td>seml_example</td>\n",
       "      <td>[16]</td>\n",
       "      <td>0.00001</td>\n",
       "      <td>768</td>\n",
       "      <td>3</td>\n",
       "      <td>0.011091</td>\n",
       "      <td>946656409</td>\n",
       "      <td>0.268248</td>\n",
       "      <td>8.519550</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>4</td>\n",
       "      <td>small_dataset_1</td>\n",
       "      <td>seml_example</td>\n",
       "      <td>[32, 16]</td>\n",
       "      <td>0.00001</td>\n",
       "      <td>519</td>\n",
       "      <td>4</td>\n",
       "      <td>0.440696</td>\n",
       "      <td>488101063</td>\n",
       "      <td>0.334359</td>\n",
       "      <td>9.716397</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>5</td>\n",
       "      <td>small_dataset_1</td>\n",
       "      <td>seml_example</td>\n",
       "      <td>[32, 16]</td>\n",
       "      <td>0.00001</td>\n",
       "      <td>626</td>\n",
       "      <td>5</td>\n",
       "      <td>0.446743</td>\n",
       "      <td>682478206</td>\n",
       "      <td>0.248766</td>\n",
       "      <td>8.092757</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   _id   config.dataset config.db_collection config.hidden_sizes  \\\n",
       "0    1  small_dataset_1         seml_example                [16]   \n",
       "1    2  small_dataset_1         seml_example                [16]   \n",
       "2    3  small_dataset_1         seml_example                [16]   \n",
       "3    4  small_dataset_1         seml_example            [32, 16]   \n",
       "4    5  small_dataset_1         seml_example            [32, 16]   \n",
       "\n",
       "   config.learning_rate  config.max_epochs  config.overwrite  \\\n",
       "0               0.00001                519                 1   \n",
       "1               0.00001                626                 2   \n",
       "2               0.00001                768                 3   \n",
       "3               0.00001                519                 4   \n",
       "4               0.00001                626                 5   \n",
       "\n",
       "   config.regularization_params.dropout  config.seed  result.test_acc  \\\n",
       "0                              0.440696     49592375         0.738594   \n",
       "1                              0.446743    912760071         0.714019   \n",
       "2                              0.011091    946656409         0.268248   \n",
       "3                              0.440696    488101063         0.334359   \n",
       "4                              0.446743    682478206         0.248766   \n",
       "\n",
       "   result.test_loss  \n",
       "0          0.333637  \n",
       "1          6.445487  \n",
       "2          8.519550  \n",
       "3          9.716397  \n",
       "4          8.092757  "
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "results.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Pandas doesn't like lists as groupby keys.\n",
    "results[\"config.hidden_sizes\"] = results[\"config.hidden_sizes\"].astype(str)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "config.dataset   config.hidden_sizes\n",
       "large_dataset_1  [64, 32]               6.082730\n",
       "                 [64]                   6.708074\n",
       "large_dataset_2  [64, 32]               4.429962\n",
       "                 [64]                   5.904191\n",
       "small_dataset_1  [16]                   5.063250\n",
       "                 [32, 16]               5.105564\n",
       "small_dataset_2  [16]                   6.231065\n",
       "                 [32, 16]               4.949711\n",
       "Name: result.test_loss, dtype: float64"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "results.groupby([\"config.dataset\", \"config.hidden_sizes\"])[\"result.test_loss\"].agg(\n",
    "    \"mean\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.scatter(results[\"result.test_acc\"], results[\"result.test_loss\"])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python (torch)",
   "language": "python",
   "name": "torch"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}


================================================
FILE: examples/tutorial/example_config.yaml
================================================
seml:
  executable: examples/tutorial/example_experiment.py
  name: example_experiment
  output_dir: examples/logs
  project_root_dir: ../..

slurm:
  experiments_per_job: 1
  sbatch_options:
    gres: gpu:1       # num GPUs
    mem: 16G          # memory
    cpus-per-task: 2  # num cores
    time: 0-08:00     # max time, D-HH:MM

fixed:
  max_epochs: 500

grid:
  learning_rate:
    type: choice
    options:
      - 1
      - 2
      - 3

small_datasets:
  fixed:
    hidden_sizes: [32, 16] # this will be parsed into a Python list.

large_datasets:

  fixed:
    hidden_sizes: [128, 64, 32]


================================================
FILE: examples/tutorial/example_experiment.py
================================================
import logging
import time

import numpy as np
import seml
from sacred import Experiment

ex = Experiment()
seml.setup_logger(ex)


@ex.config
def config():
    overwrite = None
    db_collection = None
    if db_collection is not None:
        ex.observers.append(
            seml.create_mongodb_observer(db_collection, overwrite=overwrite)
        )


@ex.automain
def run(hidden_sizes: list, learning_rate: float, max_epochs: int):
    # Note that regularization_params contains the corresponding sub-dictionary from the configuration.
    logging.info("Received the following configuration:")
    logging.info(
        f"Hidden sizes: {hidden_sizes}, learning_rate: {learning_rate}, "
        f"max_epochs: {max_epochs}"
    )
    # res = hidden_sizes / 2
    #  do your processing here
    time.sleep(60)
    results = {
        "test_acc": learning_rate * np.sqrt(np.arange(1, 1001, 1))
        + np.random.uniform(0, 5),
        # ...
    }
    # the returned result will be written into the database
    return results


================================================
FILE: examples/tutorial/intro_slides.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Introduction to   \n",
    "# `SEML`: Slurm Experiment Management Library"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Why `SEML`?\n",
    "\n",
    "In a nutshell, **`SEML`** enables you to leverage the massive parallelization of a compute cluster without boilerplate code or having to worry about keeping track of experiments.\n",
    "That is, it enables you to:\n",
    "* very easily define hyperparameter search spaces using YAML files,\n",
    "* run these hyperparameter configurations on a compute cluster using `Slurm`,\n",
    "* and to track the experimental results using `sacred` and `MongoDB`.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "In addition, **`SEML`** offers many more features to make your life easier, such as\n",
    "* automatically saving and loading your source code for reproducibility,\n",
    "* collecting experiment results into a `Pandas` dataframe,\n",
    "* easy debugging on Slurm or locally,\n",
    "* automatically checking your experiment configurations,\n",
    "* extending Slurm with local workers,\n",
    "* and keeping track of resource usage (experiment runtime, RAM, etc.).\n",
    "\n",
    "You can even get notified on Mattermost whenever an experiment starts, completes, or fails!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## How does it work?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAqsAAAH7CAYAAADrfdabAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAAsTAAALEwEAmpwYAAEAAElEQVR4nOzdd3xUVdrA8d+50yeT3klIgCRAKKGJ9A6CXbH3tq7v6rqLrq6uZe3r6qprb2sXWLuuoijSRQGBSO+B9EB6nz7n/eNOQkLvCXC+n89lkrlz733unYQ8c+5zzhFSShRFURRFURSlPdLaOgBFURRFURRF2ReVrCqKoiiKoijtlkpWFUVRFEVRlHZLJauKoiiKoihKu6WSVUVRFEVRFKXdUsmqoiiKoiiK0m6pZFVRFEVRFEVpt1SyqiiKoiiKorRbxrYOQFGUo0/TtO4Oh+PfdXV1jraORTmxaZoWsFgsPzudzmeklJVtHY+iKKcelawqyklICBE6fvz4ke+88469rWNRTmz5+fn+s846q7qoqMjc1rEoinJqUsmqopykjEYjERERbR2GcoKrrq5GCNHWYSiKcgpTNauKoiiKoihKu6WSVUVRFEVRFKXdUsmqoiiKoiiK0m6pZFVRFEVRFEVpt1SyqiiKoiiKorRbKllVFEVRFEVR2i2VrCqKoiiKoijtlkpWFUVRFEVRlHZLJauKoiiKoihKu6WSVUVRFEVRFKXdUsmqoiiKoiiK0m6pZFVRFEVRFEVpt1SyqiiKoiiKorRbxrYOQFGUE1d+fj6zZ8/G7/cTHh7O8OHDSUxMxO/3s3XrVgwGA126dMFgMBzRcTweD7/88gu9evUiJibmKEW/b1JKduzYQUlJCT169CAnJwdN08jMzGz1Oq/Xy6ZNmwgJCSEuLo7ly5fTr18/wsLCqK6uxuFwYDQe+X+zHo+HX3/9lfz8fIYPH07Hjh0RQhzxfhVFUU4EqmVVUZRD5vV6mTlzJu+//z4ulwuAgoICXnnlFXJzc/F6vfz0008sXboUn893xMdzuVy8++675OfnH/G+DoaUki1btvDNN9/Q0NCwz9e53W7mzJlDdnY2fr+/+flt27bx6aefNl+bI42lqKiIzz77jOrq6iPen6IoyolGtawqinJImhK5efPmcf755zN48GAMBgONjY28++67TJ06lT/96U+tXh8IBJpbAoUQSCmb1zU913Jd0/Mt17XcX9Nj07rdX99ym5br9ndO+zoeQI8ePfZ6/Jax2mw2Ro4cCcC6devYuHEjgUBgv8ff27ntfk2aklWbzcall15KdHS0alVVFOWUolpWFeUUFggEqKuro7i4mNLSUrxe7wG38fv9rFu3jpSUFPr06YOm6f+N2O12brnlFu655x7MZjOBQIB169YxatQoevXqxZ133klJSQlSSurq6nj11Vfp1q0b3bp14/nnn8fpdOL3+1m1ahVjx46lW7dujBo1ilmzZrVqtaypqeGZZ55h8uTJbNmyhSVLlnDuuefSvXt3zjrrLDZt2tQqQfR4PEydOpX33nsPj8cD6MnkU089RV5eHgsXLqRbt250796dLl268Pe//53GxsZW5/z666/z9ttvI6UkLy+PG264gczMTC644AK2bt0KQGlpKf/3f//HvHnzePbZZ3n33Xf5/e9/z5tvvsmjjz5KVVUVUkqWLFnCE088QWVlZatjeDweFi9eTFZWFt26dePKK69k48aNrFq1iquuuoo33niDe+65h507dx7em60oinKCUsmqopzCysrKuO++++jevTsjR47khx9+OOA2Pp+PxsZGoqKiMJlMrVr5jEYjZrMZ0G/dl5eX8+WXX7Js2TJSU1P5/vvvcblcTJs2DbfbzZo1a1i9ejU1NTVMnTqVgoICXnzxRaZMmcKGDRt47bXXKCoqar79XVVVxbRp0/D7/bzzzjvExsby3Xff8de//pXNmzfzwAMPsHr16lbJrclkonPnzuTk5LBjxw78fj+bNm3CarViMBjIzs5mwYIFbN68mW+//ZYNGzY0J6Atz9nn81FTU8PHH3/MxIkTWbt2LU8//TQFBQV4PB6klHg8HlJSUrjrrru48cYbefPNNxkyZAj19fWUlJTg9/vZsGEDcXFxhIWFNe9fSsnSpUt56623+Oqrr1i7di1nnnkm06dPJyUlhQ8//JA//OEPPPPMMyQkJBzJW64oinLCUWUAinIK27ZtGx9++CF1dXVs2rSJ559/nnPOOeeA2wkhMBgM+70dbTKZOO2004iKikLTNBISEqisrKS+vp7ly5djMpn4xz/+AUBJSQl5eXlkZGSQnp7O4MGDMRqN9OrVi169elFbW4vf7+eFF15ACMETTzxBZGQkNTU1CCF49913yc7O5pJLLmHYsGGt4tI0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wsLCuO6668jMzGx13NzcXN59913Ky8uxWCxs376dSZMmcfnll/Pqq69itVqx2+3k5OSgaRp33nknaWlpLFmyhOnTp2M0GgkPD2fr1q1ccMEFrc4rEAiwceNGpk2bRnV1NUIIIiIiuOaaa3A4HLzyyis0NjY2T/96ww030KNHD6ZPn055eTlhYWHk5eXh9/u55ZZbKCsrY+3atezYsYNBgwaRn59PWVkZd9xxB3PnzuWrr75qjjcvL49rrrmG4cOH8/7777Nq1SpsNht+v5/bb7+dHj16qFZZRVFOWSpZVRTlkHi9XpYsWYLJZOKxxx4jLCyMTZs2YbVaWbp0Kdu3b+eZZ54hLCyMuXPn8vnnn5OVlcUvv/xCeno6l156KX6/n2eeeQa3243f7+ebb74hJSWFO++8EyEEX3/9Ne+99x4PPfRQq3rWkJAQLr30Urp3747JZOLHH3/kf//7X3Mrrclk4s4778TlcvHUU0+xefNmIiMj+emnn7jooosYO3YsJSUl/PWvf93jvFwuF7NnzyY1NZUHH3wQKSWbNm0iNDSUuXPn0tjYyKOPPordbuerr77iww8/5KGHHsLtdmOz2bj99tsRQvDee++xadMmLrnkElatWkWXLl0YP348U6dOBaCkpITs7GxuvPFGTj/9dDZt2sQTTzwBQFFREbNnz+aVV16hQ4cOrF+/noiIiGP/piqKorRjajQARVEOidfrpby8nJSUFEJDQzEajfTs2ZPU1FS2bdvGiBEjiIiIwGAwNE9XWlRURElJCenp6dhsNux2OwMGDCAkJASXy8WaNWt48MEH6devH3379uWee+5h+/bte9SzxsXFkZ6ezvvvv8/gwYO5/fbbcblczetPO+00TCYTZrMZu92Oz+ejqqqKiooKunXrhtFoJD4+niFDhuxxXo2NjVRWVpKZmYnZbMZms9G3b19iYmLYsmULo0ePJiwsDKPRyKhRo5r3bbfbyczMxGq1YjQam48bCAT2OEZT2YDH46Fz584YDAaSk5Pp378/oLc0d+jQgQkTJnDttddisVhISEg4yu+goijKiUUlq4qiHDIp5R63pffWWamxsZGamhqklPvszFRfX095eTnvvfce2dnZZGdns27dOqZPn05oaGir/f/www+MHDmSbdu2MWPGDN5++21CQkKaX7O32s6m47aMd1+31A+2w1VdXR11dXUAzYP9H6q9xeBwOHj11VdZvHgxXbt25eyzz+avf/3rce+EdqoQVmEQ4aKDCBfpIlykCpswHdL2QpiFEClCiHghxBENKyGEsAX3lbz7voQQkUKINCFEtBDihPu7HbxOXYLX6YSLX2l76odGUZRDYjKZiI2NpaSkBJfLRSAQoKCggNzcXFJTU1m0aBE1NTX4/X42bNiA1WolOTmZxMREcnJycLlcNDY2kp2dTUNDA+Hh4fTu3Ztly5bh9/sxm80UFhaybt26Vq2TUkry8vK48cYbm2/HL168uDlp3JeoqCiio6PZtGkTPp+P0tJSFi9evMfr7HY7UVFRbN++Hb/fj8/nY8uWLezYsYP09HQWLFhAXV0dPp+PlStXEhkZeci36IUQxMXFYTabm49TVFTEb7/9BkBNTQ0//fQTVquVhx9+mGnTpuF0Olu1HitHURIOBnItF/Myl/ENA0g/xD10Bh4FLgYOe/w1oX9y6QJMAR4BonZ7yUjgceAqIISjIJg42vYVjxCioxBi9zgOVwLwd+CP7HluhywYX+y+4t/PdpoQIlEIoUogTzDqDVMU5ZCYTCYGDRrEF198wcMPP9zcEWn06NEMGjSIlStX8uijj+L3+/F4PFx44YXExsYycuRIPv/8c+6//37cbjc+n4/k5GSsVivXXXcdr7zyClOmTMHhcABwxhln0KtXr+bjCiHo2bMn06dP56677sJutyOEQAhBTU3NPuONjIxkzJgxfPbZZ3zzzTeEhobi9Xr3eJ3VamX8+PF88cUX3HvvvWiaRnR0NGeddRajR49m3bp1PPTQQ/h8PrxeL9dddx022yH9rQSgQ4cO9O3bl3fffZePPvoIq9XaHI/X62XmzJlMmzaNsLAwAM4///zma6IcXTJH1gD/FNeIH7Hx8mHswgW8L6Wcd0Rx6E3664A7hRAXAoHd1v9PCLEQ6LW37Q9TH2A9ULiXdQKwAUelSV9KmS+E+D160n00GIFMYCvgPITtzEBfYBGw/0+5SrsijvU4g4qiHH8Gg2HgRRddNP+TTz45JqPtN9VeNrUOxsXF0blz5+ahq9avX4/T6SQmJoaMjAxMJlPzsFB5eXlIKenYsSNCCBITEzEYDBQVFbFt2zZAH7Kpa9eue9zW93g8bNmyhYqKCmw2G126dKGiooKEhATKy8ubWzubBuS32+1ER0fj8/nIycmhtLSUyMhIwsPDsVqtxMbGtrodHwgEKCoqIi8vDyEESUlJJCcnYzAYqK6uZvPmzbjdbhITE0lLS0NKyY4dO7BYLERF6Q1GpaWlCCGIjY2lsLAQm81GbGwspaWluN3u5qGsNm/eTG1tbXMLcZ8+fRgxYgQ7d+5sHsIrMjKSrl27Yjab22w0gNzcXP+IESO+LSwsvEVKuaNNgggS40QEdp5E0CP4VDX1/FnOlbmit+hLZ/6G3opXg4830KhB414gdLddrWEH98pfZT2AuEYMwMbLrOdGuUhuOOh4hEgFurRMVoOtpIOBPwPxwFfAcuBXKaVXCPE74BqgGHgGyJYt/hAHk9WFUsqK3Y4ViZ6srpRS1gWfMwLnAzegt7jOA16UUlYH1ycA9wD9gZ+CcSwEzkRv5RSAG1gJPC2lLAq2pt4LJAP/Ch5PBvdnA24HzgLygtusE0LEAn8CSoEhwffgVinlxhbxm9GT1ZVSyvKDvL5ZwEvBb38CfgYWADcCv0P/sOAMntO/pZTVQogOwD/QW73XBWPMFUJEoLdcXwTUBLebAbwN9Ahep78Er8l9QA7wnJTSI4RIAp4OXpMC4HP28h4px45qWVUU5ZAJIUhISNhr55+QkBAGDhy4x/OappGUlNQ8XNXukpOTSU5O3u9xzWYzPXv2bPVcdHQ0QHNLZFN8KSkpzd+bTCa6d+9O9+7d97t/TdPo2LEjHTt23GNdZGQkgwYN2uP53c+n5TVpGUNcXBxSSnJycpg6dSqTJk2iZ8+eLF++nC1btjB58uT9XlcFSOJOaillHs8wlG505AHKsYhuYiDDeRSND/iCZUTTmdFMQfIOgtnAhODXVxLgFTRuwcMIYOYxiLIn0Bv4K1APDAcGACuEEJcBFcAFQBhwGVAJbD/MY3VBL+f7K+ABMoCzhRCfAhbgJuAd9FKFLsD1wBz0ZKsR2IyeNHuD3wNUBV/fP7hPoDlRvQZYCvwHiANGCiHqgKLgfoejJ3oCmCCEyJFS7nkb4yAIIUxAd+D/AD8wIXhMN/ABsA3I3Uv8fuB59JbTBOAcIcR/0BPUl9BbkxcH17vRE95s9A8VdmAj8BBwqR6GMABpwX1WAV3RW3V/OZzzUg6PSlYVRVGOEyEEnTp1Yvjw4Tz99NNUVFQQGxvLc889R4cOHdo6vPavlNU4mMJl3AwY8XMvaylnFAMRdKeCPzMMvcB3B27ARxxOYDt+KjBRipcizPg5Bn//gi2d8eitqPnBp78OrosDTMCMYAJXJYT4Jvj6Q05Wgx2VUtFbGe1AU+vsHMCKnriulFKuCT6/Irg0bd8A1Egpq1ruN9iKWh9c37IcIRk9OVwmpWwMxh/e4tjFwOxgK6YteK4W9ETykAVbocuB14JPrQJelVL6hRBO9A8C1U2tyMFzEkA0eoLbPRjDEvS7yDJ4TnXB7VqWAfiEEI3B4waCCbgv+L1fCFEGPIz+Xu1ET9ZbXTfl2FLJqqIoynFkNBoZP34848ePb+tQTigiTVhwYEDjGgrpTwojieA+BrONAqpIIZ8dPMYS5mDHQRdGA/lEEXcc/9JJ9OQsSQixLphwxaC3auagt+JZAG8w2YwHDuqW+D6OVYzeWrhAStnQcqUQohYYLIQIkVI2BG/Dj0BPpOvQ//5bgwleZ/R6zi1Syr1P77arxtMQ3L8JvfSggl2J8u7xHTYhhAMok1KODn4/GBgghNgSfIkJMAfjTwt+n4feAvswelLZH721t2UsVsAU/GDRFWgA8tFbg0OC++tJsCNY8Lr5geullM5gScBo9KS45EjOUTl4KllVFEVR2r9M7HTgDlzUUUwlJkxIvmM+66jCj8aX9ORuQvg9VmwkIoDvgUuQhKIndpkYuR5BODFcJMYJG4IJpJIKZJDMc2K8WIOX5+UCWXyoIQZb4TYC5wJvCCHs6EnqN0At+i3ox4J1lRp6/eX7QggL+ogC56G3YF4nhNgGvAGUodda9gbCgWohxPzguu3oieYLQoimutyfgXfRE7cC4J/BVt2K4PVo6jRVBjwR/H4N+m1wKYQYgF5WEImepFUFb6MvQK/z/HfwWA3Aj8Ay9MTuj4Aj2CrZD33kgkohxOeHWQoQDtwlhLAGv98GTA+2fEr05PIh9OTzJ/SSDi+wAXgxuE0ReulAIXrpgwc973k5+Nr5wPfBVtdtwG3o5RmrgHT092MO+nvTN3jcKuBLVMvqcaU6WCnKSehYd7BqEggEWo0BajQam3voHyopZfNQVZqmqelFD1HTWLZSyqN6/dpLBysRIjQMJKK3TDapkrX6bWxhFVbMxLGrEcaJnlA1DZVUj97D3Yue6GjodY5hBFsLgzxIdsq6AydYwQ5WXYG5LVskg8lcVHC/jegthP4WZQJN51AhpawJtuZFAhEtdu9FTyi9wW2sLdbVB/cpg7fcY1ucdz1QHkzqzOjJmjm4nx1NiWOwZbQpaa4FqoLb2NFrPZvI4P7qgkl1fPBY/mD89cG6zoTgdS1HLw0IB6qDC8HnhqF3KDtga3Jwny3PuyEYhz+43ho8N0Mw9urg8xYgqcU2AnC1WB8SvF4E42/ZWa3pvalGb6n1oNe6RqL/nIjgdSyTUqrx5I4j1bKqKMohCwQCVFZWMnv2bJ588kkCgQDx8fHceuutTJo0CZvNdsjJksfj4fPPP6ehoYGrr776sIaFOpX5/X6+/vpriouLuf7660+64a5kgwygt5Ttfb1LutBv5+6u9gC7PpIWMhN6feQYIcS/muo/gwnQHkMjSSl97OUcgnWilcFlb/bZyiuldLL380ZK6WHvQ1MRTFrz9vJ8I3or5t62ce/tWMEEsuV5uWhxLkKIDOA99BbQZXs/k73uc3/nvdf3OxjjXuMPrm9AT2J3f36v701QRXBR2ohKVhVFOWRlZWW89tprhIWFsWTJEmw2G3l5eTz77LP4/X4mT558WLM6BQKBg55FStnT/mYKU44+KeVW9KGQlP2QUm5Bb1VVlMOiklVFUQ6J3+9n1apVmEwmrr76aqxW/S5damoqt912G6WlpUgpqampYdq0aWzcuLF5/VVXXUVCQgI+n48ffviBH374AYDJkyczZMgQQE+E//GPf1BXV0f//v2ZPHkyDocDv9/PF198wU8//URERASXX345mZmZ+P1+li5dyowZM3A6nQwdOpTzzz8fi8XSKu7KykrefvttCgoKiIyMbN5eSsnWrVuZNm0alZWVDBkyhIsvvhgpJUuXLiUkJIQ+ffogpSQ7Oxu/389pp53GggULcLlcZGdnM2zYMEaMGMH8+fOZMWMGAOecc05zJ6rVq1fzxRdfUFtby7BhwzjvvPP2iA9g48aNvPXWW3g8Hnr06MGll15KZGQklZWVTJ8+nS1bttCpUyduuukmwsPD8Xg8zJo1i3nz5mG1WomMjGzer9/vZ+XKlXzxxRfU1dVxxhlncPbZZ6vyCkVRTjhqulVFOYU5nU7++9//MmnSJH73u9+Rn7/Xu4mt+Hw+SkpKSEhIIDQ0tFXy061bN0aMGIHf72fWrFl4PB4uueQSLrjgAvLy8pgxYwYul4tZs2bx2WefceaZZzJhwgTefPNNNm7ciMfjYc2aNc1J6vbt2/n2229pbGzkjTfeoLCwkCuvvJLTTjuNf//732zfvp0tW7bw888/M2rUKC6++GLWrFnDsmWt7zQ2Njby1Vdf4XA4uPTSS+nSpQszZsygqqqKDRs28Ic//IG+ffty4YUX8vPPPzN9+nS8Xi+rV69m69at+P1+/H4/GzduZN26dfh8PhYvXsz//vc/Ro4cSWZmJvPnz+fDDz9k4sSJTJw4kQ8++KA5lm+++Ybhw4dzySWXkJOTw6efforH0zyEJVJKNm3axDPPPEO/fv2YPHkyxcXFfPfdd5SWlvLEE09gMpm48sor8Xg8PPnkk9TV1bF48WJ++eUXzj77bAYNGsS8efMoLS0FYOnSpUydOpVx48Zx8cUXs2jRIr755hvV8qooyglHtawqyils48aNPPjgg2zbtg2DwYDP5+O9997b7zZSSoxGI+Hh4XvMMNXEbDZz/vnnI6XE7XZTV1dHz549cblcOJ1O5s+fz6WXXsrEiRMBGDt2LEIIli9fTs+ePRk7diwhISFUVlaSk5NDQUEBK1euZMqUKcTExNCpUyc2btzIjBkz6NOnD6WlpXTo0IGUlBQGDBiwR1wul4ucnBy6du1Keno6ffv2xWg0Yjab+e9//8u5557Lueeei6ZpRERE8MILL3DGGWfs9zoYDAZ69OjB0KFDkVIyf/58LrnkEs4880wAxowZg5SSr776io4dO9KrV6/mazx79myKi4vp1KkTsKsVtE+fPkyePBmr1drc0rxy5Urq6uq4/PLLiYiIICMjgzvvvJNVq1bx66+/MmLECEaNGoXH46GgoID6+nqklCxatIghQ4bQs2dPhBD4fD4+/vhjBg8eTFxc3IF+NBRFUdoNlawqyimsvr6eoqIipJT4fD42bdp0wG2EEPj9fioqKvD5fJhMpuZ1LpeL3NxcOnfuzM6dO1m3bh3ffvstGzZsoKGhgUsvvRS3201xcTHJyclomn5zJyQkBLfbjcViIT4+HrPZDOijAkgpKS8v56effmLz5s3NiagQgptvvpmePXsyb948/vznP2O32/nDH/5A79696dixY/P+w8PDOeOMM3jttdd47bXXGDFiBJdeeilpaWns2LGDAQMGNNfYpqSkYLFYqK3df78cg8HQHGttbS2FhYWtjmm326mvr2flypX8+OOPTJ06tXnbAQMGtCoD8Pv9lJaWEh0d3TyiQtP6kpISOnTogN2uD+wQERFBt27dyMvLw+fzER0djRACk8lEQkICxcXFOJ1O1q1bx8cff8ybb77ZfC2HDRvW6v1SlBNBcFSDTPTB/PfoFLbba21AVvDbDVLKA3WwU04AKllVlFNYx44dOeecc/j222+JioriuuuuO+A2JpOJTp06sWTJEmpra7Farc2lANnZ2Xz00Ufce++9fPbZZ2zcuJGrr76a008/nenTp1NXV4fBYMDhcFBfX3/QcXo8Hrp168Zrr71GYmLiHusffvhhXC4Xn3/+OTNmzGDlypXcfvvthIeHA3piOWrUKEaNGkVOTg6vvvoqr776Ktdffz12u526ul0dtysqKqivr9+jpjQQCNDY2LjX2+gGg4HQ0NA9zklKicPh4N577+Xiiy/eZ0u0pmnY7XZcLtce+3c4HNTV1eH36yMjuVwuCgoKyMrKQgiBy+VqFZ/X6yUQCGA0GnnkkUc455xzDnR5FaW9MwNDAacQ4gMpZWA/r7UBQ4AY9DFkV+zntc2CQ1d1BrYHRwZQ2hFVs6oop7Dk5GSefPJJPvvsM6ZNm8bVV199wG00TaN37954vV7efffd5mRp+/btTJ06lbFjx2IwGKirq+OCCy5g2LBhbNmyhf/97394PB5sNhtZWVnMnz8fl8uF1+tl+vTpe9SZtpSWlkZiYiLff/89Pp8Pl8vF66+/zqJFi1i1ahXvv/8+jY2NXH755c2381uORlBbW8v777/P4sWL6dSpE3fddRcZGRnYbDb69evH+++/T1FRES6XixkzZtCpUydiY2OxWq1s3LgRt9tNZWUlK1aswOvdc/hNm81G3759mTNnDi6XC5/PxyeffMJvv/1Gjx49+O233ygrKyMQCLBs2TLeeustampqmrc3Go1kZGSwevVqqqqqCAQCrFixgm+++Ybk5GSKi4vJzs4G4KeffqKmpoasrCw6duzI8uXLaWxspKysjO+//57KykpsNhtDhw7lo48+orxcH9Jy0aJFvPHGG83vl6KcKKSU9egD8R9wxigpZSX6pACfoI+TerBM6K236tZDO6SSVUU5hRmNRtLT0znrrLMYNWrUQY/NGRERwR//+EfCwsLo378/3bp148Ybb+Sss85i0qRJxMTEMHbsWJ5//nl69OjBE088wbhx42hoaMDpdHLllVficrnIysqiZ8+elJWV0bNnTwwGQ6skU9M0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L64zRiwrkrLw8HAGDBhA3759g1NTU5kzZ4787rvv/NLS0iRqwYLWyAkhwlBbbXZF9b8MRNUIvgxs8vkCPk3TtNOou6/K54Cfnx/bt29n48aN9OrVqyJZdTgczJo1i6lTp7Jv3z4mTpyIxWLho48+Yvfu3QAMGjSIW2+99ajzOZ1Ofv31VzZs2MC4ceOIiIjgxx9/ZPr06QDcc8899OvXD4/Hw9y5c/nuO1WzPmzYMK688koCAwOPi3Hx4sW8//77FbcbM2YMFouFGTNmMG3atIrH8eCDD9KmTRvmzJmD2Wxm2rRpXHbZZVxyySUsXbqUzz77DIvFwqWXXsrIkSOP20VLSsnu3bt59913yczMZODAgdx2223k5OTw0UcfsXDhQnJycnjiiSeOagU2c+ZMgoODGTx4MABz585FCEHXrl1ZvHgxJSUlzJ07l+DgYG666SZ69+6NEIL9+/fz/vvvk56ezoUXXlhRclH5uSkrKyMiIoJbbrmF8PBw3njjDQ4dOkRSUhIjRoxg+vTpXH311ezevZu8vDz27t3LihUraNWqFXfffTcJCQm4XC5++OEHfv75Z5o1a0afPn3wer2MGjUKi8WClJKCggKmTJnCrFmzcLvdXH/99bhcLn799VdeeeUViouLmThxIm3atAEgOzubd955h507d9KyZUvGjRt30rZi58L+/fvl999/L2fNmpW2bt26b3Nzc+dIKdc25tXsGgghOgEXoLbTzENtvzkLtZtR9ikO1RoRY4OHoahFdPuFEDFAQXVGZIUQ1krnyKylULVGrNaTVa/XW5GIwJ973ZdfV/6mXghx1HXlNYTlx5bXDpbXFHq93opa0vJzVK4vLD93+W0q19CVX9e6dWt69+7NkiWVFzSq3q/nnXceW7duZcSIEYSFhfH555/Tpk0bxowZQ15eHm+++SbNmzena9eugEqy5s2bx9y5cxk7dixhYWFMmTKF1NRUHn74YZxOJx9//HHF41yzZg333HMPLpeL33//nU2bNtG7d++KxyClZPny5bz55pv87W9/w26389VXX5GQkEBERARr166t2K1rxowZvPvuu/zjH/9g9erV5Obmct999xEbG8usWbPYtGkTDz30EE6nk+nTp2M2m7nkkkuOGkHctm0bd911FxMnTiQ6OppJkybx8ccfc+eddzJs2DC8Xi8333zzUf1cy4+L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hg0bRmxsbMWCpMjISPbv33/cucuFh4ezYsUKRo0aRUFBAT/88AO33HILADt37uTHH3+kY8eOpKamkp6ezujRo0lISGDWrFmsXr2avn37MmPGDDZv3ozH4yE3N5e2bdtyxRVXUFxczFdffVWRsAcHB+NyuXA4qlZKFRAQwAUXXMDUqVPp0KEDBQUFfPjhhwwfPvy42wYGBuL1enE4HBVviI5lsVjo3r07kyZNYtiwYfTr14+0tDSmTp3KFVdcQYsWLWqsZrWoqIj58+fzwQcfyK1bt36/f//+L6SU66SUh+pjz0RhsmILahE7sHvSI4N6ms9ouN7r9UT5h7Sr6dDqLCGEP2oF/3DUyNQuYCPwI2pkKkM37G80PMA2o3dqBHAhMEAIUV6DHIdKBlegEsfJwDLgKSllobFoqrqyUNuffo6RVEopT9RfMAZV35p3zOU21Busy4BbUMVEQagdsGahRku3GI+p/JhNxtchqJHeb42/dzlCiD9Qvw8C1Ut6n05UG49aS1allGRmZvLqq69WjEImJydX1JpedNFFPPXUU/Ts2ZOOHTty8cUX88EHH/D000/z3HPP8eKLL/L000/ToUMHhBDYbDaeeOIJwsPDefLJJ5k8eTIFBQUMGDAAk8nEc889x7XXXsuIESOQUnLXXXdx5MgRvF4vXbp04corr6RVq1a8+uqrPProo/ztb39j8ODBDBw4kJdffhkpJc8++2zFdHRoaChxcXH89a9/5c033+SKK67g7rvvJjs7m+uvv56//OUv7N27l969exMYGIjZbCYhIYEbb7yR//3vf/To0YO7776bl156ia5duyKE4IYbbuCRRx5BSsnvv/9Oz549KSsrY/DgwTz33HMEBARUPH8mk4lLL72UwsLCiuOvueYaHn74YdxuN08++SQdO3akWbNm3HfffcydO5eMjAwCAgIqpsP9/Py45ZZbeP/99+nVqxcej4fBgwfzz3/+87ja0wEDBvD8889z0003cfDgQUaMGMEzzzyD2WzGYrHg7+9/wkTs2muvZf369fTs2ZNLLrmEUaNGVTyOTp06UVZWRrdu3YiOjuapp56iW7duCCG45557mDhxIrfffjvXX389/fv3x9/fn8svv5wXX3yRbt26ERERwdixY0lJSSErK4vk5OSK7gzjxo3DbrdjMpmw2WxHPR6r1Yq/vz8mk4lLLrmE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
Download .txt
gitextract_7hga3fho/

├── .github/
│   ├── ISSUE_TEMPLATE.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── actions.yaml
│       └── precommit.yaml
├── .gitignore
├── .pre-commit-config.yaml
├── .python-version
├── LICENSE
├── MANIFEST.in
├── README.md
├── ci/
│   └── examples/
│       ├── example_config.yaml
│       ├── example_experiment.py
│       ├── example_reschedule.py
│       └── example_reschedule_config.yaml
├── docs.md
├── examples/
│   ├── .ruff.toml
│   ├── README.md
│   ├── advanced_example_config.yaml
│   ├── advanced_example_experiment.py
│   ├── config/
│   │   └── flip_augmentation.yaml
│   ├── example_config.yaml
│   ├── example_experiment.py
│   ├── logs/
│   │   └── .gitignore
│   ├── notebooks/
│   │   └── experiment_results.ipynb
│   └── tutorial/
│       ├── example_config.yaml
│       ├── example_experiment.py
│       ├── intro_slides.ipynb
│       ├── intro_slides.slides.html
│       └── seml.drawio
├── pyproject.toml
├── src/
│   └── seml/
│       ├── __init__.py
│       ├── __main__.py
│       ├── cli_utils/
│       │   ├── __init__.py
│       │   ├── cache.py
│       │   ├── cli_states.py
│       │   └── module_hider.py
│       ├── commands/
│       │   ├── __init__.py
│       │   ├── add.py
│       │   ├── configure.py
│       │   ├── description.py
│       │   ├── manage.py
│       │   ├── migration.py
│       │   ├── print.py
│       │   ├── project.py
│       │   ├── slurm.py
│       │   ├── sources.py
│       │   └── start.py
│       ├── console/
│       │   └── __init__.py
│       ├── database.py
│       ├── document.py
│       ├── evaluation.py
│       ├── experiment/
│       │   ├── __init__.py
│       │   ├── command.py
│       │   ├── config.py
│       │   ├── description.py
│       │   ├── experiment.py
│       │   ├── mattermost_observer.py
│       │   ├── observers.py
│       │   ├── parameters.py
│       │   └── sources.py
│       ├── settings.py
│       ├── templates/
│       │   └── slurm/
│       │       ├── jupyter_template.sh
│       │       └── slurm_template.sh
│       └── utils/
│           ├── __init__.py
│           ├── errors.py
│           ├── io.py
│           ├── json.py
│           ├── multi_process.py
│           ├── network.py
│           ├── slurm.py
│           ├── ssh_forward.py
│           └── yaml.py
└── test/
    ├── .ruff.toml
    ├── __init__.py
    ├── resources/
    │   ├── config/
    │   │   ├── config_nested_parameter_collections.yaml
    │   │   ├── config_resolve_config.yaml
    │   │   ├── config_resolve_config_named_1.json
    │   │   ├── config_resolve_config_named_1.yaml
    │   │   ├── config_resolve_config_named_2.json
    │   │   ├── config_resolve_config_named_2.yaml
    │   │   ├── config_resolve_with_interpolation.yaml
    │   │   ├── config_slurm_default.yaml
    │   │   ├── config_slurm_default_empty_sbatch.yaml
    │   │   ├── config_slurm_experiment.yaml
    │   │   ├── config_slurm_experiments_and_tasks.yaml
    │   │   ├── config_slurm_template.yaml
    │   │   ├── config_with_all_types.yaml
    │   │   ├── config_with_dict_choice.yaml
    │   │   ├── config_with_duplicate_parameters_1.yaml
    │   │   ├── config_with_duplicate_parameters_2.yaml
    │   │   ├── config_with_duplicate_parameters_3.yaml
    │   │   ├── config_with_duplicate_random_parameters_1.yaml
    │   │   ├── config_with_empty_dictionary.yaml
    │   │   ├── config_with_grid.yaml
    │   │   ├── config_with_named_config.yaml
    │   │   ├── config_with_parameter_collections.yaml
    │   │   ├── config_with_parameter_collections_random.yaml
    │   │   └── config_with_zipped_parameters.yaml
    │   └── scripts/
    │       ├── experiment_resolve_config.py
    │       └── experiment_resolve_config_interpolate.py
    ├── test_config.py
    ├── test_start.py
    └── test_utils.py
Download .txt
SYMBOL INDEX (441 symbols across 47 files)

FILE: ci/examples/example_experiment.py
  function run (line 11) | def run(

FILE: ci/examples/example_reschedule.py
  function reschedule (line 10) | def reschedule(step: int):
  function run (line 16) | def run(

FILE: examples/advanced_example_experiment.py
  function preprocessing_none (line 18) | def preprocessing_none():
  function preprocessing_normalize (line 27) | def preprocessing_normalize():
  function batchnorm (line 36) | def batchnorm():
  function no_batchnorm (line 42) | def no_batchnorm():
  function config (line 48) | def config():
  class ModelVariant1 (line 52) | class ModelVariant1:
    method __init__ (line 57) | def __init__(self, hidden_sizes, dropout, batchnorm, residual):
  class ModelVariant2 (line 64) | class ModelVariant2:
    method __init__ (line 69) | def __init__(self, hidden_sizes, dropout, batchnorm, residual):
  class ExperimentWrapper (line 76) | class ExperimentWrapper:
    method __init__ (line 83) | def __init__(self, init_all=True):
    method init_dataset (line 89) | def init_dataset(self, dataset):
    method init_model (line 104) | def init_model(
    method init_optimizer (line 123) | def init_optimizer(self, regularization: dict, optimizer_type: str):
    method init_preprocessing (line 128) | def init_preprocessing(self, mean: float, std: float):
    method init_augmentation (line 132) | def init_augmentation(self, flip: bool):
    method init_all (line 135) | def init_all(self):
    method train (line 146) | def train(self, patience, num_epochs):
  function get_experiment (line 165) | def get_experiment(init_all=False):
  function reschedule_hook (line 177) | def reschedule_hook(model_weights, step, **kwargs):
  function train (line 190) | def train(experiment=None):

FILE: examples/example_experiment.py
  function run (line 10) | def run(

FILE: examples/tutorial/example_experiment.py
  function config (line 13) | def config():
  function run (line 23) | def run(hidden_sizes: list, learning_rate: float, max_epochs: int):

FILE: src/seml/__main__.py
  function parse_dict (line 36) | def parse_dict(x: str):
  function restrict_collection (line 59) | def restrict_collection(require: bool = True):
  function collection_free_commands (line 79) | def collection_free_commands(app: typer.Typer) -> List[str]:
  function get_db_collections (line 89) | def get_db_collections():
  function first_argument_completer (line 100) | def first_argument_completer():
  function parse_optional_str_list (line 151) | def parse_optional_str_list(values: Optional[Sequence[str]]) -> List[str]:
  function version_callback (line 313) | def version_callback(value: bool):
  function callback (line 322) | def callback(
  function list_command (line 394) | def list_command(
  function clean_db_command (line 423) | def clean_db_command(ctx: typer.Context, yes: YesAnnotation = False):
  function configure_command (line 432) | def configure_command(
  function start_jupyter_command (line 496) | def start_jupyter_command(
  function cancel_command (line 527) | def cancel_command(
  function add_command (line 572) | def add_command(
  function start_command (line 661) | def start_command(
  function clean_jobs_command (line 723) | def clean_jobs_command(
  function prepare_experiment_command (line 742) | def prepare_experiment_command(
  function claim_experiment_command (line 800) | def claim_experiment_command(
  function launch_worker_command (line 819) | def launch_worker_command(
  function print_fail_trace_command (line 863) | def print_fail_trace_command(
  function reload_sources_command (line 892) | def reload_sources_command(
  function update_working_dir_command (line 929) | def update_working_dir_command(
  function print_command_command (line 964) | def print_command_command(
  function print_experiment_command (line 1013) | def print_experiment_command(
  function print_output_command (line 1054) | def print_output_command(
  function reset_command (line 1109) | def reset_command(
  function delete_command (line 1139) | def delete_command(
  function drop_command (line 1180) | def drop_command(
  function detect_killed_command (line 1200) | def detect_killed_command(
  function status_command (line 1213) | def status_command(
  function download_sources_command (line 1230) | def download_sources_command(
  function hold_command (line 1263) | def hold_command(
  function release_command (line 1281) | def release_command(
  function queue_command (line 1299) | def queue_command(
  function detect_duplicates_command (line 1348) | def detect_duplicates_command(
  function description_set_command (line 1374) | def description_set_command(
  function description_delete_command (line 1408) | def description_delete_command(
  function description_list_command (line 1433) | def description_list_command(
  function init_project_command (line 1453) | def init_project_command(
  function list_templates_command (line 1533) | def list_templates_command(
  class CommandTreeNode (line 1561) | class CommandTreeNode:
  function command_tree (line 1569) | def command_tree(app: typer.Typer) -> CommandTreeNode:
  function split_args (line 1584) | def split_args(
  function main (line 1629) | def main():

FILE: src/seml/cli_utils/cache.py
  class DiskCachedFunction (line 6) | class DiskCachedFunction(Generic[R]):
    method __init__ (line 7) | def __init__(
    method cache_path (line 18) | def cache_path(self):
    method __call__ (line 30) | def __call__(self) -> R:
    method clear_cache (line 60) | def clear_cache(self):
    method recompute_cache (line 71) | def recompute_cache(self):
  function cache_to_disk (line 78) | def cache_to_disk(name: str, time_to_live: float | None = None):

FILE: src/seml/cli_utils/cli_states.py
  class DummyStates (line 9) | class DummyStates:
    method __getitem__ (line 10) | def __getitem__(self, item):
    method __getattr__ (line 13) | def __getattr__(self, item):
    method values (line 16) | def values(self):

FILE: src/seml/cli_utils/module_hider.py
  class PackageNotFoundError (line 11) | class PackageNotFoundError(Exception): ...
  class FakeImportlibMetadata (line 14) | class FakeImportlibMetadata(Loader):
    method create_module (line 15) | def create_module(self, spec):
    method exec_module (line 18) | def exec_module(self, module):
  class ModuleHider (line 28) | class ModuleHider(MetaPathFinder):
    method __init__ (line 29) | def __init__(self, *hidden_modules: str, hide: bool = AUTOCOMPLETING) ...
    method find_spec (line 34) | def find_spec(self, fullname, path, target=None):
    method __enter__ (line 41) | def __enter__(self):
    method __exit__ (line 45) | def __exit__(self, exc_type, exc_val, exc_tb):

FILE: src/seml/commands/add.py
  function remove_existing_experiments (line 40) | def remove_existing_experiments(
  function add_configs (line 86) | def add_configs(
  function add_config_files (line 148) | def add_config_files(
  function add_config_file (line 197) | def add_config_file(

FILE: src/seml/commands/configure.py
  function prompt_ssh_forward (line 8) | def prompt_ssh_forward():
  function mongodb_configure (line 28) | def mongodb_configure(

FILE: src/seml/commands/description.py
  function collection_set_description (line 16) | def collection_set_description(
  function collection_delete_description (line 103) | def collection_delete_description(
  function collection_list_descriptions (line 149) | def collection_list_descriptions(db_collection_name: str, update_status:...

FILE: src/seml/commands/manage.py
  function should_check_killed (line 50) | def should_check_killed(filter_states: list[str] | None) -> bool:
  function cancel_empty_pending_jobs (line 70) | def cancel_empty_pending_jobs(db_collection_name: str, *sacred_ids: int):
  function cancel_jobs_without_experiments (line 115) | def cancel_jobs_without_experiments(*slurm_array_ids: str | int):
  function cancel_experiment_by_id (line 154) | def cancel_experiment_by_id(
  function cancel_experiments (line 250) | def cancel_experiments(
  function delete_experiments (line 392) | def delete_experiments(
  function drop_collections (line 483) | def drop_collections(
  function reset_slurm_dict (line 524) | def reset_slurm_dict(exp: ExperimentDoc):
  function get_experiment_reset_op (line 546) | def get_experiment_reset_op(exp: ExperimentDoc):
  function reset_single_experiment (line 600) | def reset_single_experiment(collection: Collection[ExperimentDoc], exp: ...
  function reset_experiments (line 613) | def reset_experiments(
  function detect_killed (line 661) | def detect_killed(db_collection_name: str, print_detected: bool = True):
  function get_experiment_files (line 744) | def get_experiment_files(experiment: ExperimentDoc) -> list[str]:
  function reload_sources (line 767) | def reload_sources(
  function detect_duplicates (line 980) | def detect_duplicates(

FILE: src/seml/commands/migration.py
  function migrate_collection (line 18) | def migrate_collection(db_collection_name: str, skip: bool, backup: bool):
  class Migration (line 78) | class Migration(Protocol):
    method __init__ (line 79) | def __init__(self, collection: Collection[ExperimentDoc]): ...
    method requires_migration (line 80) | def requires_migration(self) -> bool: ...
    method migrate (line 81) | def migrate(self) -> int: ...
    method name (line 82) | def name(self) -> str: ...
    method is_silent (line 83) | def is_silent(self) -> bool: ...
  class Migration04To05Slurm (line 86) | class Migration04To05Slurm(Migration):
    method __init__ (line 90) | def __init__(self, collection: Collection[ExperimentDoc]):
    method is_silent (line 93) | def is_silent(self):
    method requires_migration (line 96) | def requires_migration(self):
    method name (line 99) | def name(self):
    method migrate (line 102) | def migrate(self):
  class Migration05Version (line 139) | class Migration05Version(Migration):
    method __init__ (line 153) | def __init__(self, collection: Collection[ExperimentDoc]):
    method is_silent (line 156) | def is_silent(self):
    method requires_migration (line 159) | def requires_migration(self):
    method name (line 162) | def name(self):
    method migrate (line 165) | def migrate(self):

FILE: src/seml/commands/print.py
  function print_fail_trace (line 44) | def print_fail_trace(
  function print_status (line 144) | def print_status(
  function print_collections (line 268) | def print_collections(
  function print_duplicates (line 407) | def print_duplicates(
  function print_experiment (line 453) | def print_experiment(
  function print_output (line 532) | def print_output(
  function generate_queue_table (line 642) | def generate_queue_table(
  function print_queue (line 762) | def print_queue(
  function print_command (line 800) | def print_command(

FILE: src/seml/commands/project.py
  function init_project (line 11) | def init_project(
  function checkout_template_repo (line 118) | def checkout_template_repo(
  function get_available_templates (line 154) | def get_available_templates(
  function print_available_templates (line 176) | def print_available_templates(

FILE: src/seml/commands/slurm.py
  function hold_or_release_experiments (line 12) | def hold_or_release_experiments(

FILE: src/seml/commands/sources.py
  function download_sources (line 13) | def download_sources(

FILE: src/seml/commands/start.py
  function get_experiment_environment (line 49) | def get_experiment_environment(experiment: ExperimentDoc):
  function get_and_make_output_dir_path (line 57) | def get_and_make_output_dir_path(config: ExperimentDoc):
  function get_exp_name (line 71) | def get_exp_name(exp_config: ExperimentDoc, db_collection_name: str):
  function set_slurm_job_name (line 75) | def set_slurm_job_name(
  function create_slurm_options_string (line 96) | def create_slurm_options_string(slurm_options: SBatchOptions, srun: bool...
  function start_sbatch_job (line 130) | def start_sbatch_job(
  function start_srun_job (line 295) | def start_srun_job(
  function start_local_job (line 348) | def start_local_job(
  function chunk_list (line 485) | def chunk_list(exps: Sequence[ExperimentDoc]):
  function prepare_staged_experiments (line 507) | def prepare_staged_experiments(
  function add_to_slurm_queue (line 559) | def add_to_slurm_queue(
  function check_compute_node (line 665) | def check_compute_node():
  function start_local_worker (line 673) | def start_local_worker(
  function start_experiments (line 837) | def start_experiments(
  function start_jupyter_job (line 951) | def start_jupyter_job(
  function get_experiment_to_prepare (line 1055) | def get_experiment_to_prepare(
  function claim_experiment (line 1099) | def claim_experiment(db_collection_name: str, exp_ids: Sequence[int]):
  function prepare_experiment (line 1186) | def prepare_experiment(

FILE: src/seml/console/__init__.py
  function pause_live_widget (line 44) | def pause_live_widget():
  function track (line 55) | def track(*args, **kwargs):
  function prompt (line 86) | def prompt(*args, **kwargs):
  function Heading (line 91) | def Heading(text: str):
  function list_items (line 103) | def list_items(items: Sequence[str]):

FILE: src/seml/database.py
  function _get_collection (line 25) | def _get_collection(collection_name: str, mongodb_config: Hashabledict |...
  function get_collection (line 34) | def get_collection(collection_name: str, mongodb_config: dict[str, Any] ...
  function get_mongo_client (line 40) | def get_mongo_client(
  function get_database (line 67) | def get_database(
  function get_collections_from_mongo_shell_or_pymongo (line 74) | def get_collections_from_mongo_shell_or_pymongo(
  function get_mongodb_config (line 105) | def get_mongodb_config(path: str | Path = SETTINGS.DATABASE.MONGODB_CONF...
  function build_filter_dict (line 192) | def build_filter_dict(
  function get_max_in_collection (line 253) | def get_max_in_collection(
  function get_max_in_collection (line 261) | def get_max_in_collection(
  function get_max_in_collection (line 268) | def get_max_in_collection(
  function upload_file (line 296) | def upload_file(
  function upload_file_mt (line 340) | def upload_file_mt(x: tuple[str, str, int, str]) -> ObjectId | None:
  function delete_files (line 347) | def delete_files(
  function clean_unreferenced_artifacts (line 359) | def clean_unreferenced_artifacts(
  function update_working_dir (line 456) | def update_working_dir(

FILE: src/seml/document.py
  class SemlDocBase (line 16) | class SemlDocBase(TypedDict, total=False):
  class SemlFileConfig (line 46) | class SemlFileConfig(SemlDocBase, total=False):
  class SemlDoc (line 60) | class SemlDoc(SemlDocBase, total=False):
  class SemlConfig (line 92) | class SemlConfig(SemlDoc, total=False):
  class SlurmConfig (line 129) | class SlurmConfig(TypedDict):
  class SlurmDoc (line 148) | class SlurmDoc(SlurmConfig):
  class GitDoc (line 171) | class GitDoc(TypedDict):
  class ExecutionDoc (line 190) | class ExecutionDoc(TypedDict):
  class SacredExperimentDoc (line 216) | class SacredExperimentDoc(TypedDict):
  class GPUDoc (line 230) | class GPUDoc(TypedDict):
  class GPUsDoc (line 249) | class GPUsDoc(TypedDict):
  class HostDoc (line 265) | class HostDoc(TypedDict):
  class MetaDoc (line 295) | class MetaDoc(TypedDict):
  class StatDoc (line 319) | class StatDoc(TypedDict):
  class GPUStatDoc (line 338) | class GPUStatDoc(TypedDict):
  class StatsDoc (line 349) | class StatsDoc(TypedDict):
  class ExperimentDoc (line 359) | class ExperimentDoc(TypedDict, total=True):
  class ExperimentConfig (line 461) | class ExperimentConfig(TypedDict, total=False, closed=False):
  class SemlExperimentFile (line 470) | class SemlExperimentFile(ExperimentConfig, total=False, closed=True):

FILE: src/seml/evaluation.py
  function parse_jsonpickle (line 27) | def parse_jsonpickle(db_entry: ExperimentDoc):
  function get_results (line 44) | def get_results(
  function get_results (line 58) | def get_results(
  function get_results (line 71) | def get_results(

FILE: src/seml/experiment/__init__.py
  function Experiment (line 9) | def Experiment(*args, **kwargs):

FILE: src/seml/experiment/command.py
  function _generate_debug_attach_url (line 16) | def _generate_debug_attach_url(ip_address: str, port: int):
  function get_environment_variables (line 30) | def get_environment_variables(
  function get_config_overrides (line 51) | def get_config_overrides(config: Sequence[str]):
  function get_shell_command (line 57) | def get_shell_command(
  function value_to_string (line 77) | def value_to_string(value: Any, use_json: bool = False):
  function get_command_from_exp (line 88) | def get_command_from_exp(

FILE: src/seml/experiment/config.py
  function unpack_config (line 61) | def unpack_config(config):
  function extract_parameter_set (line 83) | def extract_parameter_set(input_config: dict, key: str):
  function convert_parameter_collections (line 95) | def convert_parameter_collections(input_config: dict):
  function standardize_config (line 129) | def standardize_config(config: dict):
  function invert_config (line 140) | def invert_config(config: dict):
  function detect_duplicate_parameters (line 151) | def detect_duplicate_parameters(
  function generate_configs (line 206) | def generate_configs(experiment_config, overwrite_params=None):
  function generate_named_config (line 347) | def generate_named_config(named_config_dict: dict) -> list[str]:
  function generate_named_configs (line 404) | def generate_named_configs(configs: list[dict]) -> tuple[list[dict], lis...
  function load_config_dict (line 434) | def load_config_dict(cfg_name: str):
  function _get_scaffold_state (line 470) | def _get_scaffold_state(scaffolding):
  function _set_scaffold_state (line 477) | def _set_scaffold_state(scaffolding, state):
  function _sacred_create_configs (line 485) | def _sacred_create_configs(
  function resolve_configs (line 606) | def resolve_configs(
  function check_config (line 666) | def check_config(
  function restore (line 742) | def restore(flat):
  function _convert_value (line 754) | def _convert_value(value):
  function convert_values (line 767) | def convert_values(val: Any):
  function read_config (line 779) | def read_config(config_path: str | Path):
  function determine_executable_and_working_dir (line 852) | def determine_executable_and_working_dir(
  function remove_prepended_dashes (line 904) | def remove_prepended_dashes(param_dict: dict[str, Any]) -> dict[str, Any]:
  function config_get_exclude_keys (line 929) | def config_get_exclude_keys(config_unresolved: dict | None = None) -> li...
  function create_starts_with_regex (line 952) | def create_starts_with_regex(*strings: str):
  function requires_interpolation (line 981) | def requires_interpolation(
  function escape_non_interpolated_dollars (line 1019) | def escape_non_interpolated_dollars(
  function resolve_interpolations (line 1050) | def resolve_interpolations(
  function remove_duplicates_in_list (line 1100) | def remove_duplicates_in_list(documents: Sequence[ExperimentDoc], use_ha...
  function remove_duplicates_in_db (line 1137) | def remove_duplicates_in_db(
  function remove_duplicates (line 1185) | def remove_duplicates(
  function check_slurm_config (line 1231) | def check_slurm_config(experiments_per_job: int, sbatch_options: SBatchO...
  function assemble_slurm_config_dict (line 1249) | def assemble_slurm_config_dict(experiment_slurm_config: SlurmConfig):

FILE: src/seml/experiment/description.py
  function resolve_description (line 11) | def resolve_description(description: str, config: Mapping) -> str:

FILE: src/seml/experiment/experiment.py
  class LoggerOptions (line 43) | class LoggerOptions(Enum):
  class RescheduleInterrupt (line 49) | class RescheduleInterrupt(SacredInterrupt):
  class Experiment (line 53) | class Experiment(ExperimentBase):
    method __init__ (line 57) | def __init__(
    method run (line 92) | def run(
    method reschedule_hook (line 116) | def reschedule_hook(self, f: Callable[P, dict]) -> Callable[P, None]:
    method _ensure_reschedule_hook_ready (line 187) | def _ensure_reschedule_hook_ready(self) -> bool:
    method _add_reschedule_config_to_db (line 227) | def _add_reschedule_config_to_db(self, config: dict):
    method _get_db_collection (line 237) | def _get_db_collection(self) -> Optional[Collection[ExperimentDoc]]:
    method _get_exp_id (line 249) | def _get_exp_id(self) -> Optional[int]:
    method _get_exp_document_from_db (line 259) | def _get_exp_document_from_db(self) -> Optional[ExperimentDoc]:
    method _is_reschedule_timeout_configured (line 273) | def _is_reschedule_timeout_configured(exp: ExperimentDoc) -> bool:
    method _is_local_execution (line 280) | def _is_local_execution(exp: ExperimentDoc) -> bool:
    method _get_reschedule_signal_file (line 285) | def _get_reschedule_signal_file(exp: ExperimentDoc) -> Path:
    method _touch_reschedule_request_file (line 292) | def _touch_reschedule_request_file(self) -> None:
  class MongoDbObserverConfig (line 300) | class MongoDbObserverConfig:
    method __init__ (line 301) | def __init__(self, experiment: Experiment):
    method __call__ (line 304) | def __call__(self, fixed=None, preset=None, fallback=None):
  class ClearObserverForMultiTaskConfig (line 326) | class ClearObserverForMultiTaskConfig:
    method __init__ (line 327) | def __init__(self, experiment: Experiment):
    method __call__ (line 330) | def __call__(self, fixed=None, preset=None, fallback=None):
  function setup_logger (line 348) | def setup_logger(
  function _collect_exp_stats (line 409) | def _collect_exp_stats(run):
  function collect_exp_stats (line 480) | def collect_exp_stats(run):

FILE: src/seml/experiment/mattermost_observer.py
  function to_local_timezone (line 16) | def to_local_timezone(dtime: datetime):
  class MattermostObserver (line 20) | class MattermostObserver(RunObserver):
    method from_config (line 27) | def from_config(cls, filename):
    method __init__ (line 39) | def __init__(
    method run (line 151) | def run(self):
    method started_event (line 155) | def started_event(
    method get_completed_text (line 189) | def get_completed_text(self):
    method get_started_text (line 192) | def get_started_text(self):
    method get_interrupted_text (line 195) | def get_interrupted_text(self):
    method get_failed_text (line 198) | def get_failed_text(self):
    method get_heartbeat_text (line 201) | def get_heartbeat_text(self):
    method completed_event (line 204) | def completed_event(self, stop_time, result):
    method interrupted_event (line 227) | def interrupted_event(self, interrupt_time, status):
    method failed_event (line 250) | def failed_event(self, fail_time, fail_trace):
    method heartbeat_event (line 273) | def heartbeat_event(self, info, captured_out, beat_time: datetime, res...

FILE: src/seml/experiment/observers.py
  function create_mongodb_observer (line 28) | def create_mongodb_observer(
  function create_file_storage_observer (line 73) | def create_file_storage_observer(
  function add_to_file_storage_observer (line 92) | def add_to_file_storage_observer(
  function create_slack_observer (line 126) | def create_slack_observer(webhook: str | None = None):
  function create_mattermost_observer (line 139) | def create_mattermost_observer(webhook=None, channel=None, **kwargs):
  function create_neptune_observer (line 176) | def create_neptune_observer(

FILE: src/seml/experiment/parameters.py
  function sample_random_configs (line 12) | def sample_random_configs(
  function sample_parameter (line 53) | def sample_parameter(
  function generate_grid (line 161) | def generate_grid(parameter: dict[str, Any], parent_key: str = ''):
  function zipped_dict (line 248) | def zipped_dict(input_dict: dict[str, tuple[Sequence, str]]):
  function cartesian_product_zipped_dict (line 283) | def cartesian_product_zipped_dict(zipped_dict: dict[str, dict[str, Seque...

FILE: src/seml/experiment/sources.py
  function import_exe (line 29) | def import_exe(executable: str, conda_env: str | None, working_dir: str):
  function get_imported_sources (line 77) | def get_imported_sources(
  function upload_sources (line 120) | def upload_sources(
  function get_git_info (line 155) | def get_git_info(filename: str, working_dir: str):
  function load_sources_from_db (line 193) | def load_sources_from_db(
  function delete_batch_sources (line 221) | def delete_batch_sources(collection: Collection[ExperimentDoc], batch_id...
  function delete_orphaned_sources (line 240) | def delete_orphaned_sources(

FILE: src/seml/settings.py
  class SettingsDict (line 27) | class SettingsDict(Mapping[str, T]):
    method __getattr__ (line 28) | def __getattr__(self, name: str) -> T: ...
  class DatabaseSettings (line 31) | class DatabaseSettings(SettingsDict):
  class States (line 35) | class States(SettingsDict[List[str]]):
  class SlurmStates (line 46) | class SlurmStates(SettingsDict[List[str]]):
  class FileObserverSettings (line 56) | class FileObserverSettings(SettingsDict[Any]):
  class SlackObserverSettings (line 60) | class SlackObserverSettings(SettingsDict[str]):
  class MattermostObserverSettings (line 64) | class MattermostObserverSettings(SettingsDict[str]):
  class NetupeObserverSettings (line 69) | class NetupeObserverSettings(SettingsDict[str]):
  class ObserverSettings (line 73) | class ObserverSettings(SettingsDict[Dict[str, str]]):
  class NamedConfigSettings (line 80) | class NamedConfigSettings(SettingsDict[str]):
  class ConfirmThresholdSettings (line 86) | class ConfirmThresholdSettings(SettingsDict[int]):
  class ExperimentSettings (line 94) | class ExperimentSettings(SettingsDict):
  class MigrationSettings (line 100) | class MigrationSettings(SettingsDict):
  class SSHForwardSettings (line 107) | class SSHForwardSettings(SettingsDict):
  class Settings (line 115) | class Settings(SettingsDict):

FILE: src/seml/utils/__init__.py
  function s_if (line 25) | def s_if(n: int) -> str:
  function unflatten (line 29) | def unflatten(
  function flatten (line 141) | def flatten(dictionary: Mapping[str, Any], parent_key: str = '', sep: st...
  function get_from_nested (line 173) | def get_from_nested(d: Mapping[str, Any], key: str, sep: str = '.') -> Any:
  function list_is_prefix (line 195) | def list_is_prefix(first: Sequence, second: Sequence) -> bool:
  function resolve_projection_path_conflicts (line 199) | def resolve_projection_path_conflicts(
  function chunker (line 246) | def chunker(seq: S, size: int) -> Generator[S]:
  function merge_dicts (line 268) | def merge_dicts(dict1: D, dict2: D) -> D: ...
  function merge_dicts (line 276) | def merge_dicts(dict1: dict, dict2: dict) -> dict: ...  # type: ignore
  function merge_dicts (line 279) | def merge_dicts(dict1: Mapping, dict2: Mapping) -> Mapping:
  function remove_keys_from_nested (line 318) | def remove_keys_from_nested(d: dict, keys: Iterable[str] = ()) -> dict:
  function make_hash (line 342) | def make_hash(d: dict, exclude_keys: Sequence[str] = ()):
  class Hashabledict (line 367) | class Hashabledict(dict):
    method __hash__ (line 368) | def __hash__(self):  # type: ignore - I don't think we can satisfy thi...
  function working_directory (line 375) | def working_directory(path: Path | str):
  function to_slices (line 392) | def to_slices(items: list[int]) -> list[tuple[int, int]]:
  function slice_to_str (line 422) | def slice_to_str(s: tuple[int, int]) -> str:
  function to_hashable (line 442) | def to_hashable(x: Any) -> Any:
  function warn_multiple_calls (line 469) | def warn_multiple_calls(warning: str, warn_after: int = 1):
  function load_text_resource (line 497) | def load_text_resource(path: str | Path):
  function assert_package_installed (line 526) | def assert_package_installed(package: str, error: str):
  function find_jupyter_host (line 544) | def find_jupyter_host(
  function get_virtual_env_path (line 620) | def get_virtual_env_path():
  function is_local_file (line 634) | def is_local_file(
  function smaller_than_version_filter (line 671) | def smaller_than_version_filter(version: tuple[int, int, int]):
  function utcnow (line 705) | def utcnow():
  function to_typeddict (line 725) | def to_typeddict(d: Mapping[str, Any], cls: type[TD], missing_ok: bool =...
  function drop_typeddict_difference (line 765) | def drop_typeddict_difference(obj: TD1, cls: type[TD1], cls2: type[TD2])...

FILE: src/seml/utils/errors.py
  class InputError (line 4) | class InputError(SystemExit):
  class ConfigError (line 10) | class ConfigError(InputError):
    method __init__ (line 13) | def __init__(self, message='The config file contains an error.'):
  class ExecutableError (line 17) | class ExecutableError(InputError):
    method __init__ (line 20) | def __init__(self, message='The Python executable has a problem.'):
  class MongoDBError (line 24) | class MongoDBError(InputError):
    method __init__ (line 27) | def __init__(self, message='The MongoDB or its config has a problem.'):
  class ArgumentError (line 31) | class ArgumentError(InputError):
    method __init__ (line 34) | def __init__(self, message='The parsed arguments contain an error.'):

FILE: src/seml/utils/io.py
  function tail_file (line 10) | def tail_file(path: str | Path, n: int = 1):

FILE: src/seml/utils/json.py
  class NumpyEncoder (line 7) | class NumpyEncoder(json.JSONEncoder):
    method default (line 8) | def default(self, o):
  class PythonEncoder (line 22) | class PythonEncoder(json.JSONEncoder):
    method iterencode (line 23) | def iterencode(self, o, _one_shot=False):
  function _make_iterencode (line 75) | def _make_iterencode(

FILE: src/seml/utils/multi_process.py
  function process_id (line 15) | def process_id():
  function local_id (line 19) | def local_id():
  function process_count (line 23) | def process_count():
  function is_main_process (line 27) | def is_main_process():
  function is_local_main_process (line 31) | def is_local_main_process():
  function is_running_in_multi_process (line 35) | def is_running_in_multi_process():
  class ChildProcessSkip (line 39) | class ChildProcessSkip(Exception): ...
  class MainProcessExecuteContext (line 42) | class MainProcessExecuteContext:
    method __enter__ (line 43) | def __enter__(self):
    method trace (line 49) | def trace(self, frame, event, arg):
    method __exit__ (line 52) | def __exit__(self, type, value, traceback):
  function only_on_main_process (line 64) | def only_on_main_process(func: Callable[P, R]) -> Callable[P, R | None]:...
  function only_on_main_process (line 68) | def only_on_main_process(func: None = None) -> MainProcessExecuteContext...
  function only_on_main_process (line 71) | def only_on_main_process(

FILE: src/seml/utils/network.py
  function get_network_interfaces (line 6) | def get_network_interfaces():
  function find_free_port (line 44) | def find_free_port():

FILE: src/seml/utils/slurm.py
  function get_cluster_name (line 12) | def get_cluster_name():
  function get_slurm_jobs (line 35) | def get_slurm_jobs(*job_ids: str):
  function parse_scontrol_job_info (line 73) | def parse_scontrol_job_info(job_info: str):
  function get_slurm_arrays_tasks (line 107) | def get_slurm_arrays_tasks(
  function get_current_slurm_array_id (line 158) | def get_current_slurm_array_id():
  function get_current_slurm_job_id (line 164) | def get_current_slurm_job_id():
  function cancel_slurm_jobs (line 168) | def cancel_slurm_jobs(*job_ids: str | int, state: str | None = None):
  function are_slurm_jobs_running (line 186) | def are_slurm_jobs_running(*job_ids: str):
  function wait_until_slurm_jobs_finished (line 213) | def wait_until_slurm_jobs_finished(*job_ids: str, timeout: int | float):

FILE: src/seml/utils/ssh_forward.py
  function retried_and_locked_ssh_port_forward (line 18) | def retried_and_locked_ssh_port_forward(
  function _ssh_forward_process (line 79) | def _ssh_forward_process(
  function _get_ssh_forward (line 128) | def _get_ssh_forward(ssh_config: dict[str, Any]):
  function get_forwarded_mongo_client (line 195) | def get_forwarded_mongo_client(

FILE: src/seml/utils/yaml.py
  class YamlUniqueLoader (line 6) | class YamlUniqueLoader(yaml.FullLoader):
  function construct_mapping (line 16) | def construct_mapping(loader, node, deep=False):
  class YamlDumper (line 34) | class YamlDumper(yaml.Dumper):
    method represent_mapping (line 35) | def represent_mapping(self, tag, mapping, flow_style=None):

FILE: test/resources/scripts/experiment_resolve_config.py
  function config (line 7) | def config():
  function py_named_1 (line 15) | def py_named_1():
  function py_named_2 (line 20) | def py_named_2():
  function main (line 25) | def main(foo, bar, py, json, yaml): ...

FILE: test/resources/scripts/experiment_resolve_config_interpolate.py
  function main (line 7) | def main(foo, bar, param1, interpolated_1, interpolated_2): ...

FILE: test/test_config.py
  class TestParseConfigDicts (line 16) | class TestParseConfigDicts(unittest.TestCase):
    method setUpClass (line 18) | def setUpClass(cls):
    method tearDownClass (line 23) | def tearDownClass(cls):
    method load_config_dict (line 73) | def load_config_dict(self, path):
    method test_config_inheritance (line 78) | def test_config_inheritance(self):
    method test_convert_parameter_collections (line 122) | def test_convert_parameter_collections(self):
    method test_resolve_config (line 144) | def test_resolve_config(self):
    method test_resolve_config_interpolation (line 232) | def test_resolve_config_interpolation(self):
    method test_unpack_config_dict (line 266) | def test_unpack_config_dict(self):
    method test_empty_dictionary (line 290) | def test_empty_dictionary(self):
    method test_overwrite_parameters (line 296) | def test_overwrite_parameters(self):
    method test_zipped_parameters (line 305) | def test_zipped_parameters(self):
    method test_named_config_python (line 316) | def test_named_config_python(self):
    method test_named_config_python_raises (line 355) | def test_named_config_python_raises(self):
    method test_duplicate_parameters (line 373) | def test_duplicate_parameters(self):
    method test_generate_configs (line 395) | def test_generate_configs(self):
    method test_config_experiments_error (line 421) | def test_config_experiments_error(self):

FILE: test/test_start.py
  class TestValueToString (line 6) | class TestValueToString(unittest.TestCase):
    method test_literal (line 7) | def test_literal(self):
    method test_list (line 14) | def test_list(self):
    method test_dict (line 57) | def test_dict(self):

FILE: test/test_utils.py
  class TestMergeDictionaries (line 6) | class TestMergeDictionaries(unittest.TestCase):
    method test_basic (line 7) | def test_basic(self):
    method test_nested (line 14) | def test_nested(self):
    method test_empty (line 21) | def test_empty(self):
    method test_fails_not_dict (line 30) | def test_fails_not_dict(self):
    method test_nested_non_dict_override (line 38) | def test_nested_non_dict_override(self):
  class TestUnflattenDictionaries (line 51) | class TestUnflattenDictionaries(unittest.TestCase):
    method test_basic (line 52) | def test_basic(self):
    method test_recursive (line 63) | def test_recursive(self):
    method test_merge_duplicate_keys (line 74) | def test_merge_duplicate_keys(self):
    method test_conflicting_keys (line 80) | def test_conflicting_keys(self):
    method test_unflatten_single_level (line 113) | def test_unflatten_single_level(self):
    method test_out_of_bounds (line 125) | def test_out_of_bounds(self):
    method test_errors (line 137) | def test_errors(self):
    method test_empty (line 144) | def test_empty(self):
    method test_recursive_with_levels (line 148) | def test_recursive_with_levels(self):
    method test_unflatten_multiple_levels (line 185) | def test_unflatten_multiple_levels(self):
Condensed preview — 103 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (989K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "chars": 471,
    "preview": "### Expected Behavior\n\n\n### Actual Behavior\n\n\n### Steps to Reproduce the Problem\n\n  1.\n  1.\n  1.\n\n### Error message:\n<!-"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 484,
    "preview": "<!--\nThank you for contributing a pull request!\nPlease name and describe your PR as you would write a\ncommit message.\n--"
  },
  {
    "path": ".github/workflows/actions.yaml",
    "chars": 3922,
    "preview": "name: Test\n\non: [push, pull_request]\n\njobs:\n  # Run CLI test and pytest\n  test:\n    runs-on: ubuntu-latest\n    strategy:"
  },
  {
    "path": ".github/workflows/precommit.yaml",
    "chars": 792,
    "preview": "name: precommit\non: [push, pull_request]\n\njobs:\n  precommit:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions"
  },
  {
    "path": ".gitignore",
    "chars": 268,
    "preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*.egg-info\nbuild/\ndist/\n\n# Latex aux files\n*.synctex.gz\n*"
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 1362,
    "preview": "repos:\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v6.0.0\n    hooks:\n      - id: check-case-confli"
  },
  {
    "path": ".python-version",
    "chars": 4,
    "preview": "3.10"
  },
  {
    "path": "LICENSE",
    "chars": 1134,
    "preview": "MIT License\n\nCopyright (c) 2023 Johannes Klicpera, Daniel Zügner, Nicholas Gao\nTechnical University of Munich\n\nPermissio"
  },
  {
    "path": "MANIFEST.in",
    "chars": 183,
    "preview": "include README.md\ninclude LICENSE\n\nrecursive-exclude test *\nrecursive-exclude examples *\n\nglobal-exclude */__pycache__/*"
  },
  {
    "path": "README.md",
    "chars": 4934,
    "preview": "![Github Actions](https://github.com/TUM-DAML/seml/workflows/Test/badge.svg)\n\n# `SEML`: Slurm Experiment Management Libr"
  },
  {
    "path": "ci/examples/example_config.yaml",
    "chars": 1314,
    "preview": "# Experiment configuration file for CI\n\nseml:\n  executable: example_experiment.py\n  name: example_experiment\n  output_di"
  },
  {
    "path": "ci/examples/example_experiment.py",
    "chars": 835,
    "preview": "import logging\n\nimport numpy as np\n\nfrom seml import Experiment\n\nex = Experiment()\n\n\n@ex.automain\ndef run(\n    dataset: "
  },
  {
    "path": "ci/examples/example_reschedule.py",
    "chars": 849,
    "preview": "import logging\nfrom time import sleep\n\nfrom seml import Experiment\n\nex = Experiment()\n\n\n@ex.reschedule_hook\ndef reschedu"
  },
  {
    "path": "ci/examples/example_reschedule_config.yaml",
    "chars": 378,
    "preview": "seml:\n  executable: example_reschedule.py\n  name: example_reschedule_experiment\n  output_dir: logs\n  project_root_dir: ."
  },
  {
    "path": "docs.md",
    "chars": 27013,
    "preview": "# `seml`\n\nSEML - Slurm Experiment Management Library.\n\n**Usage**:\n\n```console\n$ seml [OPTIONS] COLLECTION COMMAND1 [ARGS"
  },
  {
    "path": "examples/.ruff.toml",
    "chars": 72,
    "preview": "[lint]\nignore = [\n    \"F841\", # ignore errors due to unused variables\n]\n"
  },
  {
    "path": "examples/README.md",
    "chars": 16215,
    "preview": "# Start a Jupyter job\nTo start a Jupyter instance, you can use the convenience function `seml start-jupyter`. This requi"
  },
  {
    "path": "examples/advanced_example_config.yaml",
    "chars": 7800,
    "preview": "# Experiment configuration file.\n#\n# There are two special blocks. The 'seml' block is required for every experiment.\n# "
  },
  {
    "path": "examples/advanced_example_experiment.py",
    "chars": 6713,
    "preview": "\"\"\"\nThis is an advanced experiment example, which makes use of sacred's captured functions with prefixes.\nWe wrap all th"
  },
  {
    "path": "examples/config/flip_augmentation.yaml",
    "chars": 26,
    "preview": "augmentation:\n  flip: True"
  },
  {
    "path": "examples/example_config.yaml",
    "chars": 6060,
    "preview": "# Experiment configuration file.\n#\n# There are two special blocks. The 'seml' block is required for every experiment.\n# "
  },
  {
    "path": "examples/example_experiment.py",
    "chars": 834,
    "preview": "import logging\n\nimport numpy as np\nfrom seml import Experiment\n\nex = Experiment()\n\n\n@ex.automain\ndef run(\n    dataset: s"
  },
  {
    "path": "examples/logs/.gitignore",
    "chars": 70,
    "preview": "# Ignore everything in this directory\n*\n# Except this file\n!.gitignore"
  },
  {
    "path": "examples/notebooks/experiment_results.ipynb",
    "chars": 17414,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\":"
  },
  {
    "path": "examples/tutorial/example_config.yaml",
    "chars": 596,
    "preview": "seml:\n  executable: examples/tutorial/example_experiment.py\n  name: example_experiment\n  output_dir: examples/logs\n  pro"
  },
  {
    "path": "examples/tutorial/example_experiment.py",
    "chars": 1028,
    "preview": "import logging\nimport time\n\nimport numpy as np\nimport seml\nfrom sacred import Experiment\n\nex = Experiment()\nseml.setup_l"
  },
  {
    "path": "examples/tutorial/intro_slides.ipynb",
    "chars": 108572,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"slideshow\": {\n     \"slide_type\": \"slide\"\n    }\n   },"
  },
  {
    "path": "examples/tutorial/intro_slides.slides.html",
    "chars": 281300,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n\n<meta charset=\"utf-8\" />\n<meta http-equiv=\"X-UA-Compatible\" content=\"chrome=1\" />\n\n<meta "
  },
  {
    "path": "examples/tutorial/seml.drawio",
    "chars": 4154,
    "preview": "<mxfile host=\"Electron\" modified=\"2021-10-19T14:26:02.099Z\" agent=\"5.0 (Macintosh; Intel Mac OS X 11_0_1) AppleWebKit/53"
  },
  {
    "path": "pyproject.toml",
    "chars": 2136,
    "preview": "[project]\nname = \"seml\"\nversion = \"0.5.6\"\ndescription = \"Slurm Experiment Management Library\"\nreadme = \"README.md\"\nrequi"
  },
  {
    "path": "src/seml/__init__.py",
    "chars": 354,
    "preview": "from seml.cli_utils.module_hider import AUTOCOMPLETING\n\nif AUTOCOMPLETING:\n    __version__ = '0.0.0'\nelse:\n    import im"
  },
  {
    "path": "src/seml/__main__.py",
    "chars": 46971,
    "preview": "#!/usr/bin/env python\nimport functools\nimport logging\nimport os\nimport sys\nfrom contextlib import nullcontext\nfrom datac"
  },
  {
    "path": "src/seml/cli_utils/__init__.py",
    "chars": 149,
    "preview": "from .cache import cache_to_disk\nfrom .module_hider import AUTOCOMPLETING, ModuleHider\n\n__all__ = ['cache_to_disk', 'AUT"
  },
  {
    "path": "src/seml/cli_utils/cache.py",
    "chars": 2548,
    "preview": "from typing import Callable, Generic, TypeVar\n\nR = TypeVar('R')\n\n\nclass DiskCachedFunction(Generic[R]):\n    def __init__"
  },
  {
    "path": "src/seml/cli_utils/cli_states.py",
    "chars": 730,
    "preview": "# When autocompleting, we don't want to read the settings.py to retrieve the actual states.\n# Instead, we use a dummy cl"
  },
  {
    "path": "src/seml/cli_utils/module_hider.py",
    "chars": 1399,
    "preview": "from __future__ import annotations\n\nimport os\nimport sys\nfrom importlib.abc import Loader, MetaPathFinder\nfrom importlib"
  },
  {
    "path": "src/seml/commands/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/seml/commands/add.py",
    "chars": 10944,
    "preview": "from __future__ import annotations\n\nimport logging\nimport os\nfrom typing import TYPE_CHECKING, Any, cast\n\nfrom seml.data"
  },
  {
    "path": "src/seml/commands/configure.py",
    "chars": 2225,
    "preview": "from __future__ import annotations\n\nimport logging\n\nfrom seml.settings import SETTINGS\n\n\ndef prompt_ssh_forward():\n    \""
  },
  {
    "path": "src/seml/commands/description.py",
    "chars": 7212,
    "preview": "from __future__ import annotations\n\nimport logging\nfrom typing import Any\n\nfrom seml.commands.manage import detect_kille"
  },
  {
    "path": "src/seml/commands/manage.py",
    "chars": 35618,
    "preview": "from __future__ import annotations\n\nimport copy\nimport itertools\nimport logging\nimport re\nimport subprocess\nfrom typing "
  },
  {
    "path": "src/seml/commands/migration.py",
    "chars": 5837,
    "preview": "from __future__ import annotations\n\nimport logging\nfrom typing import TYPE_CHECKING, Protocol\n\nfrom seml.database import"
  },
  {
    "path": "src/seml/commands/print.py",
    "chars": 31094,
    "preview": "from __future__ import annotations\n\nimport logging\nimport os\nimport re\nimport time\nfrom collections import defaultdict\nf"
  },
  {
    "path": "src/seml/commands/project.py",
    "chars": 5965,
    "preview": "from __future__ import annotations\n\nimport logging\nimport os\nfrom contextlib import contextmanager\nfrom pathlib import P"
  },
  {
    "path": "src/seml/commands/slurm.py",
    "chars": 1756,
    "preview": "from __future__ import annotations\n\nimport logging\nimport subprocess\n\nfrom seml.commands.manage import detect_killed\nfro"
  },
  {
    "path": "src/seml/commands/sources.py",
    "chars": 2158,
    "preview": "from __future__ import annotations\n\nimport logging\nimport os\n\nfrom seml.database import build_filter_dict, get_collectio"
  },
  {
    "path": "src/seml/commands/start.py",
    "chars": 47349,
    "preview": "from __future__ import annotations\n\nimport copy\nimport logging\nimport math\nimport os\nimport shutil\nimport subprocess\nimp"
  },
  {
    "path": "src/seml/console/__init__.py",
    "chars": 3330,
    "preview": "from __future__ import annotations\n\nimport functools\nimport os\nfrom contextlib import contextmanager\nfrom typing import "
  },
  {
    "path": "src/seml/database.py",
    "chars": 15585,
    "preview": "from __future__ import annotations\n\nimport functools\nimport logging\nfrom pathlib import Path\nfrom typing import TYPE_CHE"
  },
  {
    "path": "src/seml/document.py",
    "chars": 14091,
    "preview": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Any, List, Optional, Union\n\nfrom typing_extensions"
  },
  {
    "path": "src/seml/evaluation.py",
    "chars": 4176,
    "preview": "from __future__ import annotations\n\nimport logging\nfrom copy import deepcopy\nfrom typing import (\n    TYPE_CHECKING,\n   "
  },
  {
    "path": "src/seml/experiment/__init__.py",
    "chars": 357,
    "preview": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from .experiment import Expe"
  },
  {
    "path": "src/seml/experiment/command.py",
    "chars": 6338,
    "preview": "from __future__ import annotations\n\nimport logging\nfrom typing import Any, List, Sequence, cast\n\nfrom seml.document impo"
  },
  {
    "path": "src/seml/experiment/config.py",
    "chars": 43906,
    "preview": "from __future__ import annotations\n\nimport ast\nimport copy\nimport functools\nimport itertools  # type: ignore - N.Gao: I "
  },
  {
    "path": "src/seml/experiment/description.py",
    "chars": 786,
    "preview": "from __future__ import annotations\n\nfrom typing import Mapping\n\nfrom seml.experiment.config import (\n    escape_non_inte"
  },
  {
    "path": "src/seml/experiment/experiment.py",
    "chars": 16775,
    "preview": "import datetime\nimport logging\nimport resource\nimport sys\nfrom enum import Enum\nfrom pathlib import Path\nfrom typing imp"
  },
  {
    "path": "src/seml/experiment/mattermost_observer.py",
    "chars": 11856,
    "preview": "from __future__ import annotations\n\nimport json\nimport re\nfrom datetime import datetime, timedelta, timezone\nfrom typing"
  },
  {
    "path": "src/seml/experiment/observers.py",
    "chars": 6937,
    "preview": "from __future__ import annotations\n\nimport logging\nimport os\nfrom typing import Any\n\nfrom seml.database import get_mongo"
  },
  {
    "path": "src/seml/experiment/parameters.py",
    "chars": 11083,
    "preview": "from __future__ import annotations\n\nimport itertools\nimport random\nimport uuid\nfrom typing import Any, DefaultDict, Sequ"
  },
  {
    "path": "src/seml/experiment/sources.py",
    "chars": 8746,
    "preview": "from __future__ import annotations\n\nimport functools\nimport importlib\nimport logging\nimport os\nimport sys\nfrom pathlib i"
  },
  {
    "path": "src/seml/settings.py",
    "chars": 10381,
    "preview": "from __future__ import annotations\n\nfrom pathlib import Path\nfrom runpy import run_path\nfrom typing import Any, Dict, Li"
  },
  {
    "path": "src/seml/templates/slurm/jupyter_template.sh",
    "chars": 647,
    "preview": "#!/bin/bash\n{sbatch_options}\n\n# Move either to project root dir or the config file path.\ncd ${{SLURM_SUBMIT_DIR}}\n\n# Pri"
  },
  {
    "path": "src/seml/templates/slurm/slurm_template.sh",
    "chars": 3813,
    "preview": "#!/bin/bash\n{sbatch_options}\n{reschedule_signal_directive}\n#SBATCH --open-mode=append\n\n# Execute optional bash commands\n"
  },
  {
    "path": "src/seml/utils/__init__.py",
    "chars": 22305,
    "preview": "from __future__ import annotations\n\nimport copy\nimport functools\nimport logging\nimport os\nimport time\nfrom contextlib im"
  },
  {
    "path": "src/seml/utils/errors.py",
    "chars": 1067,
    "preview": "from __future__ import annotations\n\n\nclass InputError(SystemExit):\n    \"\"\"Parent class for input errors that don't print"
  },
  {
    "path": "src/seml/utils/io.py",
    "chars": 899,
    "preview": "from __future__ import annotations\n\nimport os\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from pathlib impor"
  },
  {
    "path": "src/seml/utils/json.py",
    "chars": 9088,
    "preview": "# type: ignore - we ignore typing here since this is copied from the CPython repo\n# We need the custom json encoding for"
  },
  {
    "path": "src/seml/utils/multi_process.py",
    "chars": 1829,
    "preview": "from __future__ import annotations\n\nimport functools\nimport os\nimport sys\nfrom typing import Callable, TypeVar, overload"
  },
  {
    "path": "src/seml/utils/network.py",
    "chars": 1601,
    "preview": "from __future__ import annotations\n\nimport sys\n\n\ndef get_network_interfaces():\n    import array\n    import fcntl\n    imp"
  },
  {
    "path": "src/seml/utils/slurm.py",
    "chars": 7170,
    "preview": "from __future__ import annotations\n\nimport functools\nimport os\nimport subprocess\nimport time\n\nfrom seml.settings import "
  },
  {
    "path": "src/seml/utils/ssh_forward.py",
    "chars": 7161,
    "preview": "from __future__ import annotations\n\nimport atexit\nimport logging\nimport time\nfrom typing import TYPE_CHECKING, Any\n\nfrom"
  },
  {
    "path": "src/seml/utils/yaml.py",
    "chars": 1170,
    "preview": "import yaml\n\nfrom seml.utils.errors import ConfigError\n\n\nclass YamlUniqueLoader(yaml.FullLoader):\n    \"\"\"\n    Custom YAM"
  },
  {
    "path": "test/.ruff.toml",
    "chars": 71,
    "preview": "[lint]\nignore = [\n    \"F841\", # ignore errors due to unused variables\n]"
  },
  {
    "path": "test/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/resources/config/config_nested_parameter_collections.yaml",
    "chars": 276,
    "preview": "grid:\n\n  a.b.c:\n    type: choice\n    options:\n      - 999\n      - 1111\n\n  a:\n    type: parameter_collection\n    params:\n"
  },
  {
    "path": "test/resources/config/config_resolve_config.yaml",
    "chars": 628,
    "preview": "fixed:\n  foo: 33\n  bar: 44\n  +json.priority: 1\n  +yaml.priority: 2 # yaml can overwrite the json\n\ngrid:\n  # Load paramet"
  },
  {
    "path": "test/resources/config/config_resolve_config_named_1.json",
    "chars": 45,
    "preview": "{\n    \"json\" : {\n        \"value\" : 11\n    }\n}"
  },
  {
    "path": "test/resources/config/config_resolve_config_named_1.yaml",
    "chars": 19,
    "preview": "yaml:\n    value: -1"
  },
  {
    "path": "test/resources/config/config_resolve_config_named_2.json",
    "chars": 45,
    "preview": "{\n    \"json\" : {\n        \"value\" : 22\n    }\n}"
  },
  {
    "path": "test/resources/config/config_resolve_config_named_2.yaml",
    "chars": 100,
    "preview": "yaml:\n    value: -2\n\n# overwrites the json value due to higher priority value\njson:\n    value: 10000"
  },
  {
    "path": "test/resources/config/config_resolve_with_interpolation.yaml",
    "chars": 132,
    "preview": "fixed:\n  foo:\n    bar: 3\n  param1: 'value'\n  interpolated_1: ${config.foo.bar}_${config.param1}\n  interpolated_2: ${conf"
  },
  {
    "path": "test/resources/config/config_slurm_default.yaml",
    "chars": 115,
    "preview": "seml:\n  executable: test_config.py\n  name: example_experiment\n  output_dir: logs\n  project_root_dir: ../..\n\nslurm:\n"
  },
  {
    "path": "test/resources/config/config_slurm_default_empty_sbatch.yaml",
    "chars": 135,
    "preview": "seml:\n  executable: test_config.py\n  name: example_experiment\n  output_dir: logs\n  project_root_dir: ../..\n\nslurm:\n  - s"
  },
  {
    "path": "test/resources/config/config_slurm_experiment.yaml",
    "chars": 191,
    "preview": "seml:\n  executable: test_config.py\n  name: example_experiment\n  output_dir: logs\n  project_root_dir: ../..\n\nslurm:\n  - s"
  },
  {
    "path": "test/resources/config/config_slurm_experiments_and_tasks.yaml",
    "chars": 178,
    "preview": "seml:\n  executable: test_config.py\n  name: example_experiment\n  output_dir: logs\n  project_root_dir: ../..\n\nslurm:\n  - e"
  },
  {
    "path": "test/resources/config/config_slurm_template.yaml",
    "chars": 148,
    "preview": "seml:\n  executable: test_config.py\n  name: example_experiment\n  output_dir: logs\n  project_root_dir: ../..\n\nslurm:\n  - s"
  },
  {
    "path": "test/resources/config/config_with_all_types.yaml",
    "chars": 470,
    "preview": "fixed:\n  a: 333\n  b: 444\n\ngrid:\n  c:\n    type: choice\n    options:\n      - 555\n      - 666\n\n\nrandom:\n  samples: 3\n  seed"
  },
  {
    "path": "test/resources/config/config_with_dict_choice.yaml",
    "chars": 348,
    "preview": "grid:\n  coll1:\n    type: parameter_collection\n    params:\n      a:\n        type: choice\n        options:\n          - sub"
  },
  {
    "path": "test/resources/config/config_with_duplicate_parameters_1.yaml",
    "chars": 77,
    "preview": "fixed:\n  a: ccc\n\ngrid:\n  a:\n    type: choice\n    options:\n      - 1\n      - 2"
  },
  {
    "path": "test/resources/config/config_with_duplicate_parameters_2.yaml",
    "chars": 236,
    "preview": "fixed:\n  a:\n    b.c: 333\n\ngrid:\n  a:\n    type: parameter_collection\n    params:\n      b:\n        type: parameter_collect"
  },
  {
    "path": "test/resources/config/config_with_duplicate_parameters_3.yaml",
    "chars": 25,
    "preview": "fixed:\n  a: ccc\n  a: ddd\n"
  },
  {
    "path": "test/resources/config/config_with_duplicate_random_parameters_1.yaml",
    "chars": 129,
    "preview": "fixed:\n  seed: 33333\n  samples: 99\n\n\nrandom:\n  seed: 222\n  samples: 5\n\n  a:\n    type: uniform\n    min: 0\n    max: 1\n    "
  },
  {
    "path": "test/resources/config/config_with_empty_dictionary.yaml",
    "chars": 30,
    "preview": "fixed:\n  attribute.test: \"{}\"\n"
  },
  {
    "path": "test/resources/config/config_with_grid.yaml",
    "chars": 135,
    "preview": "grid:\n  dataset:\n    type: choice\n    options:\n      - small\n      - big\n\n  lr:\n    type: choice\n    options:\n      - 0."
  },
  {
    "path": "test/resources/config/config_with_named_config.yaml",
    "chars": 313,
    "preview": "fixed:\n  +model:\n    name: cora_ml\n  +evaluation:\n    priority: 1\n\ngrid:\n  dataset:\n    type: choice\n    options:\n      "
  },
  {
    "path": "test/resources/config/config_with_parameter_collections.yaml",
    "chars": 302,
    "preview": "grid:\n  coll1:\n    type: parameter_collection\n    params:\n      a:\n        type: choice\n        options:\n          - 1\n "
  },
  {
    "path": "test/resources/config/config_with_parameter_collections_random.yaml",
    "chars": 346,
    "preview": "grid:\n  coll1:\n    type: parameter_collection\n    params:\n      a:\n        type: choice\n        options:\n          - 1\n "
  },
  {
    "path": "test/resources/config/config_with_zipped_parameters.yaml",
    "chars": 250,
    "preview": "grid:\n  attribute.test:\n    type: choice\n    options:\n      - 1\n      - 2\n    zip_id: 2\n\n  learning_rate:\n    type: unif"
  },
  {
    "path": "test/resources/scripts/experiment_resolve_config.py",
    "chars": 302,
    "preview": "from sacred import Experiment\n\nex = Experiment()\n\n\n@ex.config\ndef config():\n    foo = 33\n    bar = {\n        \"fizz\": Non"
  },
  {
    "path": "test/resources/scripts/experiment_resolve_config_interpolate.py",
    "chars": 128,
    "preview": "from sacred import Experiment\n\nex = Experiment()\n\n\n@ex.automain\ndef main(foo, bar, param1, interpolated_1, interpolated_"
  },
  {
    "path": "test/test_config.py",
    "chars": 16372,
    "preview": "import copy\nimport os\nimport unittest\nfrom pathlib import Path\n\nimport yaml\nfrom seml import utils\nfrom seml.commands im"
  },
  {
    "path": "test/test_start.py",
    "chars": 4210,
    "preview": "import unittest\n\nfrom seml.experiment.command import value_to_string\n\n\nclass TestValueToString(unittest.TestCase):\n    d"
  },
  {
    "path": "test/test_utils.py",
    "chars": 8570,
    "preview": "import unittest\n\nfrom seml import utils\n\n\nclass TestMergeDictionaries(unittest.TestCase):\n    def test_basic(self):\n    "
  }
]

About this extraction

This page contains the full source code of the TUM-DAML/seml GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 103 files (922.5 KB), approximately 291.9k tokens, and a symbol index with 441 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!