Showing preview only (5,171K chars total). Download the full file or copy to clipboard to get everything.
Repository: thu-ml/tianshou
Branch: master
Commit: 1bbe05b3365f
Files: 303
Total size: 4.9 MB
Directory structure:
gitextract_z9q4ijrb/
├── .devcontainer/
│ └── devcontainer.json
├── .dockerignore
├── .github/
│ ├── ISSUE_TEMPLATE.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ ├── extra_sys.yml
│ ├── gputest.yml
│ ├── lint_and_docs.yml
│ ├── publish.yaml
│ └── pytest.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yaml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── MANIFEST.in
├── README.md
├── benchmark/
│ └── run_benchmark.py
├── docs/
│ ├── .gitignore
│ ├── 01_user_guide/
│ │ ├── 00_training_process.md
│ │ ├── 01_apis.md
│ │ ├── 02_core_abstractions.md
│ │ └── index.rst
│ ├── 02_deep_dives/
│ │ ├── 0_intro.md
│ │ ├── L1_Batch.ipynb
│ │ ├── L2_Buffer.ipynb
│ │ ├── L3_Environments.ipynb
│ │ ├── L4_GAE.ipynb
│ │ ├── L5_Collector.ipynb
│ │ └── L6_MARL.ipynb
│ ├── 04_benchmarks/
│ │ └── benchmarks.rst
│ ├── 05_developer_guide/
│ │ └── developer_guide.md
│ ├── 06_contributors/
│ │ └── contributors.rst
│ ├── _config.yml
│ ├── _static/
│ │ ├── css/
│ │ │ └── style.css
│ │ └── js/
│ │ ├── benchmark.js
│ │ ├── copybutton.js
│ │ ├── mujoco/
│ │ │ └── benchmark/
│ │ │ └── Ant-v4/
│ │ │ └── results.json
│ │ ├── v5.json
│ │ ├── vega-embed@5.js
│ │ ├── vega-lite@5.js
│ │ └── vega@5.js
│ ├── autogen_rst.py
│ ├── bibtex.json
│ ├── create_toc.py
│ ├── index.rst
│ ├── nbstripout.py
│ └── refs.bib
├── examples/
│ ├── __init__.py
│ ├── atari/
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── atari_c51.py
│ │ ├── atari_dqn.py
│ │ ├── atari_dqn_hl.py
│ │ ├── atari_fqf.py
│ │ ├── atari_iqn.py
│ │ ├── atari_iqn_hl.py
│ │ ├── atari_ppo.py
│ │ ├── atari_ppo_hl.py
│ │ ├── atari_qrdqn.py
│ │ ├── atari_rainbow.py
│ │ ├── atari_sac.py
│ │ └── atari_sac_hl.py
│ ├── box2d/
│ │ ├── README.md
│ │ ├── acrobot_dualdqn.py
│ │ ├── bipedal_bdq.py
│ │ ├── bipedal_hardcore_sac.py
│ │ ├── lunarlander_dqn.py
│ │ └── mcc_sac.py
│ ├── discrete/
│ │ ├── discrete_dqn.py
│ │ └── discrete_dqn_hl.py
│ ├── inverse/
│ │ ├── README.md
│ │ └── irl_gail.py
│ ├── modelbased/
│ │ └── README.md
│ ├── mujoco/
│ │ ├── README.md
│ │ ├── analysis.py
│ │ ├── fetch_her_ddpg.py
│ │ ├── mujoco_a2c.py
│ │ ├── mujoco_a2c_hl.py
│ │ ├── mujoco_ddpg.py
│ │ ├── mujoco_ddpg_hl.py
│ │ ├── mujoco_env.py
│ │ ├── mujoco_npg.py
│ │ ├── mujoco_npg_hl.py
│ │ ├── mujoco_ppo.py
│ │ ├── mujoco_ppo_hl.py
│ │ ├── mujoco_redq.py
│ │ ├── mujoco_redq_hl.py
│ │ ├── mujoco_reinforce.py
│ │ ├── mujoco_reinforce_hl.py
│ │ ├── mujoco_sac.py
│ │ ├── mujoco_sac_hl.py
│ │ ├── mujoco_td3.py
│ │ ├── mujoco_td3_hl.py
│ │ ├── mujoco_trpo.py
│ │ ├── mujoco_trpo_hl.py
│ │ ├── plotter.py
│ │ └── tools.py
│ ├── offline/
│ │ ├── README.md
│ │ ├── atari_bcq.py
│ │ ├── atari_cql.py
│ │ ├── atari_crr.py
│ │ ├── atari_il.py
│ │ ├── convert_rl_unplugged_atari.py
│ │ ├── d4rl_bcq.py
│ │ ├── d4rl_cql.py
│ │ ├── d4rl_il.py
│ │ ├── d4rl_td3_bc.py
│ │ └── utils.py
│ └── vizdoom/
│ ├── .gitignore
│ ├── README.md
│ ├── env.py
│ ├── maps/
│ │ ├── D1_basic.cfg
│ │ ├── D1_basic.wad
│ │ ├── D2_navigation.cfg
│ │ ├── D2_navigation.wad
│ │ ├── D3_battle.cfg
│ │ ├── D3_battle.wad
│ │ ├── D4_battle2.cfg
│ │ ├── D4_battle2.wad
│ │ ├── README.md
│ │ └── spectator.py
│ ├── replay.py
│ ├── vizdoom_c51.py
│ └── vizdoom_ppo.py
├── pyproject.toml
├── test/
│ ├── __init__.py
│ ├── base/
│ │ ├── __init__.py
│ │ ├── env.py
│ │ ├── test_action_space_sampling.py
│ │ ├── test_batch.py
│ │ ├── test_buffer.py
│ │ ├── test_collector.py
│ │ ├── test_env.py
│ │ ├── test_env_finite.py
│ │ ├── test_logger.py
│ │ ├── test_policy.py
│ │ ├── test_returns.py
│ │ ├── test_stats.py
│ │ └── test_utils.py
│ ├── continuous/
│ │ ├── __init__.py
│ │ ├── test_ddpg.py
│ │ ├── test_npg.py
│ │ ├── test_ppo.py
│ │ ├── test_redq.py
│ │ ├── test_sac_with_il.py
│ │ ├── test_td3.py
│ │ └── test_trpo.py
│ ├── determinism_test.py
│ ├── discrete/
│ │ ├── __init__.py
│ │ ├── test_a2c_with_il.py
│ │ ├── test_bdqn.py
│ │ ├── test_c51.py
│ │ ├── test_discrete_sac.py
│ │ ├── test_dqn.py
│ │ ├── test_drqn.py
│ │ ├── test_fqf.py
│ │ ├── test_iqn.py
│ │ ├── test_ppo_discrete.py
│ │ ├── test_qrdqn.py
│ │ ├── test_rainbow.py
│ │ └── test_reinforce.py
│ ├── highlevel/
│ │ ├── __init__.py
│ │ ├── env_factory.py
│ │ └── test_experiment_builder.py
│ ├── modelbased/
│ │ ├── __init__.py
│ │ ├── test_dqn_icm.py
│ │ ├── test_ppo_icm.py
│ │ └── test_psrl.py
│ ├── offline/
│ │ ├── __init__.py
│ │ ├── gather_cartpole_data.py
│ │ ├── gather_pendulum_data.py
│ │ ├── test_bcq.py
│ │ ├── test_cql.py
│ │ ├── test_discrete_bcq.py
│ │ ├── test_discrete_cql.py
│ │ ├── test_discrete_crr.py
│ │ ├── test_gail.py
│ │ └── test_td3_bc.py
│ └── pettingzoo/
│ ├── pistonball.py
│ ├── pistonball_continuous.py
│ ├── test_pistonball.py
│ ├── test_pistonball_continuous.py
│ ├── test_tic_tac_toe.py
│ └── tic_tac_toe.py
└── tianshou/
├── __init__.py
├── algorithm/
│ ├── __init__.py
│ ├── algorithm_base.py
│ ├── imitation/
│ │ ├── __init__.py
│ │ ├── bcq.py
│ │ ├── cql.py
│ │ ├── discrete_bcq.py
│ │ ├── discrete_cql.py
│ │ ├── discrete_crr.py
│ │ ├── gail.py
│ │ ├── imitation_base.py
│ │ └── td3_bc.py
│ ├── modelbased/
│ │ ├── __init__.py
│ │ ├── icm.py
│ │ └── psrl.py
│ ├── modelfree/
│ │ ├── __init__.py
│ │ ├── a2c.py
│ │ ├── bdqn.py
│ │ ├── c51.py
│ │ ├── ddpg.py
│ │ ├── discrete_sac.py
│ │ ├── dqn.py
│ │ ├── fqf.py
│ │ ├── iqn.py
│ │ ├── npg.py
│ │ ├── ppo.py
│ │ ├── qrdqn.py
│ │ ├── rainbow.py
│ │ ├── redq.py
│ │ ├── reinforce.py
│ │ ├── sac.py
│ │ ├── td3.py
│ │ └── trpo.py
│ ├── multiagent/
│ │ ├── __init__.py
│ │ └── marl.py
│ ├── optim.py
│ └── random.py
├── config.py
├── data/
│ ├── __init__.py
│ ├── batch.py
│ ├── buffer/
│ │ ├── __init__.py
│ │ ├── buffer_base.py
│ │ ├── cached.py
│ │ ├── her.py
│ │ ├── manager.py
│ │ ├── prio.py
│ │ └── vecbuf.py
│ ├── collector.py
│ ├── stats.py
│ ├── types.py
│ └── utils/
│ ├── __init__.py
│ ├── converter.py
│ └── segtree.py
├── env/
│ ├── __init__.py
│ ├── atari/
│ │ ├── atari_network.py
│ │ └── atari_wrapper.py
│ ├── gym_wrappers.py
│ ├── pettingzoo_env.py
│ ├── utils.py
│ ├── venv_wrappers.py
│ ├── venvs.py
│ └── worker/
│ ├── __init__.py
│ ├── dummy.py
│ ├── ray.py
│ ├── subproc.py
│ └── worker_base.py
├── evaluation/
│ ├── __init__.py
│ ├── launcher.py
│ └── rliable_evaluation.py
├── exploration/
│ ├── __init__.py
│ └── random.py
├── highlevel/
│ ├── __init__.py
│ ├── algorithm.py
│ ├── config.py
│ ├── env.py
│ ├── experiment.py
│ ├── logger.py
│ ├── module/
│ │ ├── __init__.py
│ │ ├── actor.py
│ │ ├── core.py
│ │ ├── critic.py
│ │ ├── intermediate.py
│ │ └── special.py
│ ├── params/
│ │ ├── __init__.py
│ │ ├── algorithm_params.py
│ │ ├── algorithm_wrapper.py
│ │ ├── alpha.py
│ │ ├── collector.py
│ │ ├── dist_fn.py
│ │ ├── env_param.py
│ │ ├── lr_scheduler.py
│ │ ├── noise.py
│ │ └── optim.py
│ ├── persistence.py
│ ├── trainer.py
│ └── world.py
├── py.typed
├── trainer.py
└── utils/
├── __init__.py
├── conversion.py
├── determinism.py
├── lagged_network.py
├── logger/
│ ├── __init__.py
│ ├── logger_base.py
│ ├── tensorboard.py
│ └── wandb.py
├── logging.py
├── net/
│ ├── __init__.py
│ ├── common.py
│ ├── continuous.py
│ └── discrete.py
├── print.py
├── progress_bar.py
├── space_info.py
├── statistics.py
├── torch_utils.py
└── warning.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .devcontainer/devcontainer.json
================================================
{
"name": "Tianshou",
"dockerFile": "../Dockerfile",
"workspaceFolder": "/workspaces/tianshou",
"runArgs": ["--shm-size=1g"],
"customizations": {
"vscode": {
"settings": {
"terminal.integrated.shell.linux": "/bin/bash",
"python.pythonPath": "/usr/local/bin/python"
},
"extensions": [
"ms-python.python",
"ms-toolsai.jupyter",
"ms-python.vscode-pylance"
]
}
},
"forwardPorts": [],
"postCreateCommand": "poetry install --with dev",
"remoteUser": "root"
}
================================================
FILE: .dockerignore
================================================
data
logs
test/log
docs/jupyter_execute
docs/.jupyter_cache
.lsp
.clj-kondo
docs/_build
coverage*
__pycache__
*.egg-info
*.egg
.*cache
dist
================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
- [ ] I have marked all applicable categories:
+ [ ] exception-raising bug
+ [ ] RL algorithm bug
+ [ ] documentation request (i.e. "X is missing from the documentation.")
+ [ ] new feature request
+ [ ] design request (i.e. "X should be changed to Y.")
- [ ] I have visited the [source website](https://github.com/thu-ml/tianshou/)
- [ ] I have searched through the [issue tracker](https://github.com/thu-ml/tianshou/issues) for duplicates
- [ ] I have mentioned version numbers, operating system and environment, where applicable:
```python
import tianshou, gymnasium as gym, torch, numpy, sys
print(tianshou.__version__, gym.__version__, torch.__version__, numpy.__version__, sys.version, sys.platform)
```
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
- [ ] I have added the correct label(s) to this Pull Request or linked the relevant issue(s)
- [ ] I have provided a description of the changes in this Pull Request
- [ ] I have added documentation for my changes and have listed relevant changes in CHANGELOG.md
- [ ] If applicable, I have added tests to cover my changes.
- [ ] If applicable, I have made sure that the determinism tests run through, meaning that my changes haven't influenced any aspect of training. See info in the contributing documentation.
- [ ] I have reformatted the code using `poe format`
- [ ] I have checked style and types with `poe lint` and `poe type-check`
- [ ] (Optional) I ran tests locally with `poe test`
(or a subset of them with `poe test-reduced`) ,and they pass
- [ ] (Optional) I have tested that documentation builds correctly with `poe doc-build`
================================================
FILE: .github/workflows/extra_sys.yml
================================================
name: Windows/MacOS
on:
pull_request:
branches:
- master
push:
branches:
- master
workflow_dispatch:
inputs:
debug_enabled:
type: boolean
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
required: false
default: false
jobs:
cpu-extra:
runs-on: ${{ matrix.os }}
if: "!contains(github.event.head_commit.message, 'ci skip')"
strategy:
matrix:
os: [macos-latest, windows-latest]
python-version: [3.11]
steps:
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
- name: Cancel previous run
uses: styfle/cancel-workflow-action@0.11.0
with:
access_token: ${{ github.token }}
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
# use poetry and cache installed packages, see https://github.com/marketplace/actions/python-poetry-action
- name: Install poetry
uses: abatilo/actions-poetry@v2
- name: Setup a local virtual environment (if no poetry.toml file)
run: |
poetry config virtualenvs.create true --local
poetry config virtualenvs.in-project true --local
- uses: actions/cache@v3
name: Define a cache for the virtual environment based on the dependencies lock file
with:
path: ./.venv
key: venv-${{ hashFiles('poetry.lock') }}
- name: Install the project dependencies
# ugly as hell, but well...
# see https://github.com/python-poetry/poetry/issues/7611
run: poetry install --with dev || poetry install --with dev || poetry install --with dev
- name: wandb login
run: poetry run wandb login e2366d661b89f2bee877c40bee15502d67b7abef
- name: Test with pytest
run: poetry run poe test-reduced
================================================
FILE: .github/workflows/gputest.yml
================================================
name: Ubuntu GPU
on:
pull_request:
branches:
- master
push:
branches:
- master
workflow_dispatch:
inputs:
debug_enabled:
type: boolean
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
required: false
default: false
jobs:
gpu:
runs-on: [self-hosted, Linux, X64]
if: "!contains(github.event.head_commit.message, 'ci skip')"
steps:
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
- name: Cancel previous run
uses: styfle/cancel-workflow-action@0.11.0
with:
access_token: ${{ github.token }}
- uses: actions/checkout@v3
- name: Set up Python 3.11
uses: actions/setup-python@v4
with:
python-version: "3.11"
# use poetry and cache installed packages, see https://github.com/marketplace/actions/python-poetry-action
- name: Install poetry
uses: abatilo/actions-poetry@v2
- name: Setup a local virtual environment (if no poetry.toml file)
run: |
poetry config virtualenvs.create true --local
poetry config virtualenvs.in-project true --local
- uses: actions/cache@v3
name: Define a cache for the virtual environment based on the dependencies lock file
with:
path: ./.venv
key: venv-${{ hashFiles('poetry.lock') }}
- name: Install the project dependencies
run: |
poetry install --with dev --extras "envpool"
- name: wandb login
run: |
poetry run wandb login e2366d661b89f2bee877c40bee15502d67b7abef
- name: Test with pytest
run: |
poetry run poe test
================================================
FILE: .github/workflows/lint_and_docs.yml
================================================
name: Check Formatting/Typing and Build Docs
on:
pull_request:
branches:
- master
push:
branches:
- master
workflow_dispatch:
inputs:
debug_enabled:
type: boolean
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
required: false
default: false
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
- name: Cancel previous run
uses: styfle/cancel-workflow-action@0.11.0
with:
access_token: ${{ github.token }}
- uses: actions/checkout@v3
- name: Set up Python 3.11
uses: actions/setup-python@v4
with:
python-version: 3.11
# use poetry and cache installed packages, see https://github.com/marketplace/actions/python-poetry-action
- name: Install poetry
uses: abatilo/actions-poetry@v2
- name: Setup a local virtual environment (if no poetry.toml file)
run: |
poetry config virtualenvs.create true --local
poetry config virtualenvs.in-project true --local
- uses: actions/cache@v3
name: Define a cache for the virtual environment based on the dependencies lock file
with:
path: ./.venv
key: venv-${{ hashFiles('poetry.lock') }}
- name: Install the project dependencies
run: |
poetry install --with dev --extras "eval"
- name: Check formatting
run: poetry run poe lint
- name: Check typing
run: poetry run poe type-check
- name: Build docs
run: MYSTNB_DEBUG=1 poetry run poe doc-build
- name: Show errors (if any)
if: failure()
run: find docs/_build/reports -name "*.err.log" -exec echo "--- {} ---" \; -exec cat {} \;
================================================
FILE: .github/workflows/publish.yaml
================================================
name: Upload Python Package
on:
release:
types: [created]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: 3.11
# use poetry and cache installed packages, see https://github.com/marketplace/actions/python-poetry-action
- name: Install poetry
uses: abatilo/actions-poetry@v2
- name: Setup a local virtual environment (if no poetry.toml file)
run: |
poetry config virtualenvs.create true --local
poetry config virtualenvs.in-project true --local
- name: Build and publish
env:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }}
run: |
if [ -z "${POETRY_PYPI_TOKEN_PYPI}" ]; then echo "Set the PYPI_TOKEN variable in your repository secrets"; exit 1; fi
poetry publish --build
================================================
FILE: .github/workflows/pytest.yml
================================================
name: Ubuntu
on:
pull_request:
branches:
- master
push:
branches:
- master
workflow_dispatch:
inputs:
debug_enabled:
type: boolean
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
required: false
default: false
# This job runs the test suite in two environments:
# - py_pinned: uses Python 3.11 with the existing poetry.lock file (our stable, pinned dev environment)
# - py_latest: latest Python version we want to support, without the lock file to furthermore install the newest dependency versions
#
# This ensures compatibility with both our controlled dev setup and the latest upstream packages,
# helping catch issues introduced by dependency updates or newer Python versions.
jobs:
cpu:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'ci skip')"
strategy:
matrix:
include:
- env_name: py_pinned
python-version: "3.11"
use_lock: true
- env_name: py_latest
python-version: "3.13"
use_lock: false
steps:
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
- name: Cancel previous run
uses: styfle/cancel-workflow-action@0.11.0
with:
access_token: ${{ github.token }}
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install poetry
uses: abatilo/actions-poetry@v2
- name: Setup a local virtual environment (if no poetry.toml file)
run: |
poetry config virtualenvs.create true --local
poetry config virtualenvs.in-project true --local
- name: Remove poetry.lock for latest dependency test
if: ${{ !matrix.use_lock }}
run: rm -f poetry.lock
- name: Define a cache for the virtual environment based on the dependencies lock file
if: matrix.use_lock
uses: actions/cache@v3
with:
path: ./.venv
key: venv-${{ matrix.env_name }}-${{ hashFiles('poetry.lock') }}
restore-keys: |
venv-${{ matrix.env_name }}-
- name: Install the project dependencies
run: |
if [ "${{ matrix.env_name }}" = "py_latest" ]; then
poetry install --with dev --extras "eval"
else
poetry install --with dev --extras "envpool eval"
fi
- name: List installed packages
run: |
poetry run pip list
- name: wandb login
run: |
poetry run wandb login e2366d661b89f2bee877c40bee15502d67b7abef
- name: Test with pytest
run: |
if [ "${{ matrix.env_name }}" = "py_pinned" ]; then
poetry run poe test
else
poetry run poe test-nocov
fi
- name: Upload coverage to Codecov
if: matrix.env_name == 'py_pinned'
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV }}
file: ./coverage.xml
flags: ${{ matrix.env_name }}
name: codecov-${{ matrix.env_name }}
fail_ci_if_error: false
================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# .idea folder
.idea/
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
venv/
/ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# customize
log/
MUJOCO_LOG.TXT
*.pth
.vscode/
.DS_Store
*.zip
*.pstats
*.swp
*.pkl
*.hdf5
wandb/
videos/
# might be needed for IDE plugins that can't read ruff config
.flake8
docs/notebooks/_build/
docs/conf.py
# temporary scripts (for ad-hoc testing), temp folder
/temp
/temp*.py
# Serena
/.serena
# determinism test snapshots
/test/resources/determinism/
================================================
FILE: .pre-commit-config.yaml
================================================
default_install_hook_types: [commit-msg, pre-commit]
default_stages: [commit, manual]
fail_fast: false
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: check-added-large-files
- repo: local
hooks:
- id: ruff
name: ruff
entry: poetry run ruff
require_serial: true
language: system
types: [python]
- id: ruff-nb
name: ruff-nb
entry: poetry run nbqa ruff .
require_serial: true
language: system
pass_filenames: false
types: [python]
- id: black
name: black
entry: poetry run black
require_serial: true
language: system
types: [python]
- id: poetry-check
name: poetry check
entry: poetry check
language: system
files: pyproject.toml
pass_filenames: false
- id: poetry-lock-check
name: poetry lock check
entry: poetry check
args: [--lock]
language: system
pass_filenames: false
- id: mypy
name: mypy
entry: poetry run mypy tianshou examples test
# filenames should not be passed as they would collide with the config in pyproject.toml
pass_filenames: false
files: '^tianshou(/[^/]*)*/[^/]*\.py$'
language: system
- id: mypy-nb
name: mypy-nb
entry: poetry run nbqa mypy
language: system
================================================
FILE: .readthedocs.yaml
================================================
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.11"
commands:
- mkdir -p $READTHEDOCS_OUTPUT/html
- curl -sSL https://install.python-poetry.org | python -
# - ~/.local/bin/poetry config virtualenvs.create false
- ~/.local/bin/poetry install --with dev -E eval
## Same as poe tasks, but unfortunately poe doesn't work with poetry not creating virtualenvs
- ~/.local/bin/poetry run python docs/autogen_rst.py
- ~/.local/bin/poetry run which jupyter-book
- ~/.local/bin/poetry run python docs/create_toc.py
- ~/.local/bin/poetry run jupyter-book config sphinx docs/
- ~/.local/bin/poetry run sphinx-build -W -b html docs $READTHEDOCS_OUTPUT/html
================================================
FILE: CHANGELOG.md
================================================
# Release 2.0.0 (2025-12-01)
This major release of Tianshou is a big step towards cleaner design and improved usability.
Given the large extent of the changes, it was not possible to maintain compatibility with the previous version.
* Persisted agents that were created with earlier versions cannot be loaded in v2.
* Source code from v1 can, however, be migrated to v2 with minimal effort.
See migration information below. For concrete examples, you may use git to diff individual
example scripts with the corresponding ones in `v1.2.0`.
This release is brought to you by [Applied AI Institute gGmbH](https://www.appliedai-institute.de).
Developers:
* Dr. Dominik Jain (@opcode81)
* Michael Panchenko (@MischaPanch)
## Runtime Environment Compatibility
Tianshou v2 is now compatible with
* Python 3.12 and Python 3.13 #1274
* newer versions of gymnasium (v1+) and numpy (v2+)
Our main test environment remains Python 3.11-based for the time being (see `poetry.lock`).
## Trainer Abstraction
* The trainer logic and configuration is now properly separated between the three cases of on-policy, off-policy
and offline learning: The base class is no longer a "God" class (formerly `BaseTrainer`) which does it all; logic and functionality has moved
to the respective subclasses (`OnPolicyTrainer`, `OffPolicyTrainer` and `OfflineTrainer`, with `OnlineTrainer`
being introduced as a base class for the two former specialisations).
* The trainers now use configuration objects with central documentation (which has been greatly improved to enhance
clarity and usability in general); every type of trainer now has a dedicated configuration class which provides
precisely the options that are applicable.
* The interface has been streamlined with improved naming of functions/parameters and limiting the public interface to purely
the methods and attributes a user should reasonably access.
* Further changes potentially affecting usage:
* We dropped the iterator semantics: Method `__next__` has been replaced by `execute_epoch`. #913
* We no longer report outdated statistics (e.g. on rewards/returns when a training step does not collect any full
episodes)
* See also "Issues resolved" below (as issue resolution can result in usage changes)
* The default value for `test_in_train` was changed from True to False (updating all usage sites to explicitly
set the parameter), because False is the more natural default, which does not make assumptions about
returns/score values computed for the data from a collection step being at all meaningful for early stopping
* The management of epsilon-greedy exploration for discrete Q-learning algorithms has been simplified:
* All respective Policy implementations (e.g. `DQNPolicy`, `C51Policy`, etc.) now accept two parameters
`eps_training` and `eps_inference`, which allows the training and test collection cases to be sufficiently
differentiated and makes the use of callback functions (`train_fn`, `test_fn`) unnecessary if only
constants are to be set.
* The setter method `set_eps` has been replaced with `set_eps_training` and `set_eps_inference` accordingly.
* Further internal changes unlikely to affect usage:
* Module `trainer.utils` was removed and the functions therein where moved to class `Trainer`
* The two places that collected and evaluated test episodes (`_test_in_train` and `_reset`) in addition to
`_test_step` were unified to use `_test_step` (with some minor parametrisation) and now log the results
of the test step accordingly.
* Issues resolved:
* Methods `run` and `reset`: Parameter `reset_prior_to_run` of `run` was never respected if it was set to `False`,
because the implementation of `__iter__` (now removed) would call `reset` regardless - and calling `reset`
is indeed necessary, because it initializes the training. The parameter was removed and replaced by
`reset_collectors` (such that `run` now replicates the parameters of `reset`).
* Inconsistent configuration options now raise exceptions rather than silently ignoring the issue in the
hope that default behaviour will achieve what the user intended.
One condition where `test_in_train` was silently set to `False` was removed and replaced by a warning.
* The stop criterion `stop_fn` did not consider scores as computed by `compute_score_fn` but instead always used
mean returns (i.e. it was assumed that the default implementation of `compute_score_fn` applies).
This is an inconsistency which has been resolved.
* The `gradient_step` counter was flawed (as it made assumptions about the underlying algorithms, which were
not valid). It has been replaced with an update step counter.
Members of `InfoStats` and parameters of `Logger` (and subclasses) were changed accordingly.
* Migration information at a glance:
* Training parameters are now passed via instances of configuration objects instead of directly as keyword arguments:
`OnPolicyTrainerParams`, `OffPolicyTrainerParams`, `OfflineTrainerParams`.
* Changed parameter default: Default for `test_in_train` was changed from True to False.
* Changed parameter names to improve clarity:
* `max_epoch` (`num_epochs` in high-level API) -> `max_epochs`
* `step_per_epoch` -> `epoch_num_steps`
* `episode_per_test` (`num_test_episodes` in high-level API) -> `test_step_num_episodes`
* `step_per_collect` -> `collection_step_num_env_steps`
* `episode_per_collect` -> collection_step_num_episodes`
* `update_per_step` -> `update_step_num_gradient_steps_per_sample`
* `repeat_per_collect` -> `update_step_num_repetitions`
* Trainer classes have been renamed:
* `OnpolicyTrainer` -> `OnPolicyTrainer`
* `OffpolicyTrainer` -> `OffPolicyTrainer`
* Method `run`: The parameter `reset_prior_to_run` was removed and replaced by `reset_collectors` (see above).
* Methods `run` and `reset`: The parameter `reset_buffer` was renamed to `reset_collector_buffers` for clarity
* Trainers are no longer iterators; manual usage (not using `run`) should simply call `reset` followed by
calls of `execute_epoch`.
## Algorithms and Policies
* We now conceptually differentiate between the learning algorithm and the policy being optimised:
* The abstraction `BasePolicy` is thus replaced by `Algorithm` and `Policy`, and the package was renamed
from `tianshou.policy` to `tianshou.algorithm`.
* Migration information: The instantiation of a policy is replaced by the instantiation of an `Algorithm`,
which is passed a `Policy`. In most cases, the former policy class name `<Name>Policy` is replaced by algorithm
class `<Name>`; exceptions are noted below.
* `ImitationPolicy` -> `OffPolicyImitationLearning`, `OfflineImitationLearning`
* `PGPolicy` -> `Reinforce`
* `MultiAgentPolicyManager` -> `MultiAgentOnPolicyAlgorithm`, `MultiAgentOffPolicyAlgorithm`
* `MARLRandomPolicy` -> `MARLRandomDiscreteMaskedOffPolicyAlgorithm`
For the respective subtype of `Policy` to use, see the respective algorithm class' constructor.
* Interface changes/improvements:
* Core methods have been renamed (and removed from the public interface; #898):
* `process_fn` -> `_preprocess_batch`
* `post_process_fn` -> `_postprocess_batch`
* `learn` -> `_update_with_batch`
* The updating interface has been cleaned up (#949):
* Functions `update` and `_update_with_batch` (formerly `learn`) no longer have `*args` and `**kwargs`.
* Instead, the interfaces for the offline, off-policy and on-policy cases are properly differentiated.
* New method `run_training`: The `Algorithm` abstraction can now directly initiate the learning process via this method.
* `Algorithms` no longer require `torch.optim.Optimizer` instances and instead require `OptimizerFactory`
instances, which create the actual optimizers internally. #959
The new `OptimizerFactory` abstraction simultaneously handles the creation of learning rate schedulers
for the optimizers created (via method `with_lr_scheduler_factory` and accompanying factory abstraction
`LRSchedulerFactory`).
The parameter `lr_scheduler` has thus been removed from all algorithm constructors.
* The flag `updating` has been removed (no internal usage, general usefulness questionable).
* Removed `max_action_num`, instead read it off from `action_space`
* Parameter changes:
* `actor_step_size` -> `trust_region_size` in NP
* `discount_factor` -> `gamma` (was already used internally almost everywhere)
* `reward_normalization` -> `return_standardization` or `return_scaling` (more precise naming) or removed (was actually unsupported by Q-learning algorithms)
* `return_standardization` in `Reinforce` and `DiscreteCRR` (as it applies standardization of returns)
* `return_scaling` in actor-critic on-policy algorithms (A2C, PPO, GAIL, NPG, TRPO)
* removed from Q-learning algorithms, where it was actually unsupported (DQN, C561, etc.)
* `clip_grad` -> `max_grad_norm` (for consistency)
* `clip_loss_grad` -> `huber_loss_delta` (allowing to control not only the use of the Huber loss but also its essential parameter)
* `estimation_step` -> `n_step_return_horizon` (more precise naming)
* Internal design improvements:
* Introduced an abstraction for the alpha parameter (coefficient of the entropy term)
in `SAC`, `DiscreteSAC` and other algorithms.
* Class hierarchy:
* Abstract base class `Alpha` base class with value property and update method
* `FixedAlpha` for constant entropy coefficients
* `AutoAlpha` for automatic entropy tuning (replaces the old tuple-based representation)
* The (auto-)updating logic is now completely encapsulated, reducing the complexity of the algorithms.
* Implementations for continuous and discrete cases now share the same abstraction,
making the codebase more consistent while preserving the original functionality.
* Introduced a policy base class `ContinuousPolicyWithExplorationNoise` which encapsulates noise generation
for continuous action spaces (e.g. relevant to `DDPG`, `SAC` and `REDQ`).
* Multi-agent RL methods are now differentiated by the type of the sub-algorithms being employed
(`MultiAgentOnPolicyAlgorithm`, `MultiAgentOffPolicyAlgorithm`), which renders all interfaces clean.
Helper class `MARLDispatcher` has been factored out to manage the dispatching of data to the respective agents.
* Algorithms now internally use a wrapper (`Algorithm.Optimizer`) around the optimizers; creation is handled
by method `_create_optimizer`.
* This facilitates backpropagation steps with gradient clipping.
* The optimizers of an Algorithm instance are now centrally tracked, such that we can ensure that the
optimizers' states are handled alongside the model parameters when calling `state_dict` or `load_state_dict`
on the `Algorithm` instance.
Special handling of the restoration of optimizers' state dicts was thus removed from examples and tests.
* Lagged networks (target networks) are now conveniently handled via the new algorithm mixins
`LaggedNetworkPolyakUpdateAlgorithmMixin` and `LaggedNetworkFullUpdateAlgorithmMixin`.
Using these mixins,
* a lagged network can simply be added by calling `_add_lagged_network`
* the torch method `train` must no longer be overridden to ensure that the target networks
are never set to train mode/remain in eval mode (which was prone to errors),
* a method which updates all target networks with their source networks is automatically
provided and does not need to be implemented specifically for every algorithm
(`_update_lagged_network_weights`).
All classes which make use of lagged networks were updated to use these mixins, simplifying
the implementations and reducing the potential for implementation errors.
(In the BCQ implementation, the VAE network was not correctly handled, but due to the way
in which examples were structured, it did not result in an error.)
* Fixed issues in the class hierarchy (particularly critical violations of the Liskov substitution principle):
* Introduced base classes (to retain factorization without abusive inheritance):
* `ActorCriticOnPolicyAlgorithm`
* `ActorCriticOffPolicyAlgorithm`
* `ActorDualCriticsOffPolicyAlgorithm` (extends `ActorCriticOffPolicyAlgorithm`)
* `QLearningOffPolicyAlgorithm`
* `A2C`: Inherit from `ActorCriticOnPolicyAlgorithm` instead of `Reinforce`
* `BDQN`:
* Inherit from `QLearningOffPolicyAlgorithm` instead of `DQN`
* Remove parameter `clip_loss_grad` (unused; only passed on to former base class)
* Remove parameter `estimation_step`, for which only one option was valid
* `C51`:
* Inherit from `QLearningOffPolicyAlgorithm` instead of `DQN`
* Remove parameters `clip_loss_grad` and `is_double` (unused; only passed on to former base class)
* `CQL`:
* Inherit directly from `OfflineAlgorithm` instead of `SAC` (off-policy).
* Remove parameter `estimation_step` (now `n_step_return_horizon`), which was not actually used (it was only passed it on to its
superclass).
* `DiscreteBCQ`:
* Inherit directly from `OfflineAlgorithm` instead of `DQN`
* Remove unused parameters `clip_loss_grad` and `is_double`, which were only passed on to
former the base class but actually unused.
* `DiscreteCQL`: Remove unused parameters `clip_loss_grad` and `is_double`, which were only passed on to
base class `QRDQN` (and unused by it).
* `DiscreteCRR`: Inherit directly from `OfflineAlgorithm` instead of `Reinforce` (on-policy)
* `FQF`: Remove unused parameters `clip_loss_grad` and `is_double`, which were only passed on to
base class `QRDQN` (and unused by it).
* `IQN`: Remove unused parameters `clip_loss_grad` and `is_double`, which were only passed on to
base class `QRDQN` (and unused by it).
* `NPG`: Inherit from `ActorCriticOnPolicyAlgorithm` instead of `A2C`
* `QRDQN`:
* Inherit from `QLearningOffPolicyAlgorithm` instead of `DQN`
* Remove parameters `clip_loss_grad` and `is_double` (unused; only passed on to former base class)
* `REDQ`: Inherit from `ActorCriticOffPolicyAlgorithm` instead of `DDPG`
* `SAC`: Inherit from `ActorDualCriticsOffPolicyAlgorithm` instead of `DDPG`
* `TD3`: Inherit from `ActorDualCriticsOffPolicyAlgorithm` instead of `DDPG`
## High-Level API
* Detailed optimizer configuration (analogous to the procedural API) is now possible:
* All optimizers can be configured in the respective algorithm-specific `Params` object by using
`OptimizerFactoryFactory` instances as parameter values (e.g. `optim`, `actor_optim`, `critic_optim`, etc.).
* Learning rate schedulers remain separate parameters and now use `LRSchedulerFactoryFactory`
instances. The respective parameter names now use the suffix `lr_scheduler` instead of `lr_scheduler_factory`
(as the precise nature need not be reflected in the name; brevity is preferable).
* `SamplingConfig` is replaced by `TrainingConfig` and subclasses differentiating off-policy and on-policy cases
appropriately (`OnPolicyTrainingConfig`, `OffPolicyTrainingConfig`).
* The `test_in_train` parameter is now exposed (default False).
* Inapplicable arguments can no longer be set in the respective subclass (e.g. `OffPolicyTrainingConfig` does not
contain parameter `repeat_per_collect`).
* All parameter names have been aligned with the new names used by `TrainerParams` (see above).
* Add option to customize the factory for the collector (`ExperimentBuilder.with_collector_factory`),
adding the abstraction `CollectorFactory`. #1256
## Peripheral Changes
* The `Actor` classes have been renamed for clarity (#1091):
* `BaseActor` -> `Actor`
* `continuous.ActorProb` -> `ContinuousActorProbabilistic`
* `coninuous.Actor` -> `ContinuousActorDeterministic`
* `discrete.Actor` -> `DiscreteActor`
* The `Critic` classes have been renamed for clarity (#1091):
* `continuous.Critic` -> `ContinuousCritic`
* `discrete.Critic` -> `DiscreteCritic`
* Moved Atari helper modules `atari_network` and `atari_wrapper` to the library under `tianshou.env.atari`.
* Fix issues pertaining to the torch device assignment of network components (#810):
* Remove 'device' member (and the corresponding constructor argument) from the following classes:
`BranchingNet`, `C51Net`, `ContinuousActorDeterministic`, `ContinuousActorProbabilistic`, `ContinuousCritic`,
`DiscreteActor`, `DiscreteCritic`, `DQNet`, `FullQuantileFunction`, `ImplicitQuantileNetwork`,
`IntrinsicCuriosityModule`, `MLPActor`, `MLP`, `Perturbation`, `QRDQNet`, `Rainbow`, `Recurrent`,
`RecurrentActorProb`, `RecurrentCritic`, `VAE`
* (Peripheral change:) Require the use of keyword arguments for the constructors of all of these classes
* Clean up handling of modules that define attribute `output_dim`, introducing the explicit base class
`ModuleWithVectorOutput`
* Interfaces where one could specify either a module with `output_dim` or additionally provide the output
dimension as an argument were changed to use `ModuleWithVectorOutput`.
* The high-level API class `IntermediateModule` can now provide a `ModuleWithVectorOutput` instance
(via adaptation if necessary).
* The class hierarchy of supporting `nn.Module` implementations was cleaned up (#1091):
* With the fundamental base classes `ActionReprNet` and `ActionReprNetWithVectorOutput`, we etablished a
well-defined interface for the most commonly used `forward` interface in Tianshou's algorithms & policies. #948
* Some network classes were renamed:
* `ScaledObsInputModule` -> `ScaledObsInputActionReprNet`
* `Rainbow` -> `RainbowNet`
* All modules containing base classes were renamed from `base` to a more descriptive name, rendering
file names unique.
# Release 1.2.0 (2025-06-23)
This is the final release in the 1.x series before Tianshou v2.0.0.
It resolves performance regressions introduced in v1.1.0 and resolves several issues,
partly by backporting improvements from the upcoming v2.0.0 release.
This release is brought to you by [Applied AI Institute gGmbH](https://www.appliedai-institute.de).
Core developers:
* Dr. Dominik Jain (@opcode81)
* Michael Panchenko (@MischaPanch)
## Changes/Improvements
- `trainer`:
- Custom scoring now supported for selecting the best model. #1202
- `highlevel`:
- `DiscreteSACExperimentBuilder`: Expose method `with_actor_factory_default` #1248 #1250
- `ActorFactoryDefault`: Fix parameters for hidden sizes and activation not being
passed on in the discrete case (affects `with_actor_factory_default` method of experiment builders)
- `ExperimentConfig`: Do not inherit from other classes, as this breaks automatic handling by
`jsonargparse` when the class is used to define interfaces (as in high-level API examples)
- `AutoAlphaFactoryDefault`: Differentiate discrete and continuous action spaces
and allow coefficient to be modified, adding an informative docstring
(previous implementation was reasonable only for continuous action spaces)
- Adjust usage in `atari_sac_hl` example accordingly.
- `NPGAgentFactory`, `TRPOAgentFactory`: Fix optimizer instantiation including the actor parameters
(which was misleadingly suggested in the docstring in the respective policy classes; docstrings were fixed),
as the actor parameters are intended to be handled via natural gradients internally
- `data`:
- `ReplayBuffer`: Fix collection of empty episodes being disallowed
- Collection was slow due to `isinstance` checks on Protocols and due to Buffer integrity validation. This was solved
by no longer performing `isinstance` on Protocols and by making the integrity validation disabled by default.
- Tests:
- We have introduced extensive **determinism tests** which allow to validate whether
training processes deterministically compute the same results across different development branches.
This is an important step towards ensuring reproducibility and consistency, which will be
instrumental in supporting Tianshou developers in their work, especially in the context of
algorithm development and evaluation.
## Breaking Changes
- `trainer`:
- `BaseTrainer.run` and `__iter__`: Resetting was never optional prior to running the trainer,
yet the recently introduced parameter `reset_prior_to_run` of `run` suggested that it _was_ optional.
Yet the parameter was ultimately not respected, because `__iter__` would always call `reset(reset_collectors=True, reset_buffer=False)`
regardless. The parameter was removed; instead, the parameters of `run` now mirror the parameters of `reset`,
and the implicit `reset` call in `__iter__` was removed.
This aligns with upcoming changes in Tianshou v2.0.0.
* NOTE: If you have been using a trainer without calling `run` but by directly iterating over it, you
will need to call `reset` on the trainer explicitly before iterating over the trainer.
* Using a trainer as an iterator is considered deprecated and support for this will be removed in Tianshou v2.0.0.
- `data`:
- `InfoStats` has a new non-optional field `best_score` which is used
for selecting the best model. #1202
- `highlevel`:
- Change the way in which seeding is handled: The mechanism introduced in v1.1.0
was completely revised:
- The `training_seed` and `test_seed` attributes were removed from `SamplingConfig`.
Instead, the seeds are derived from the seed defined in `ExperimentConfig`.
- Seed attributes of `EnvFactory` classes were removed.
Instead, seeds are passed to methods of `EnvFactory`.
# Release 1.1.0 (2024-08-10)
**NOTE**: This release introduced (potentially severe) performance regressions in data collection, please switch to a newer release for better performance.
## Highlights
### Evaluation Package
This release introduces a new package `evaluation` that integrates best
practices for running experiments (seeding test and train environmets) and for
evaluating them using the [rliable](https://github.com/google-research/rliable)
library. This should be especially useful for algorithm developers for comparing
performances and creating meaningful visualizations. **This functionality is
currently in alpha state** and will be further improved in the next releases.
You will need to install tianshou with the extra `eval` to use it.
The creation of multiple experiments with varying random seeds has been greatly
facilitated. Moreover, the `ExpLauncher` interface has been introduced and
implemented with several backends to support the execution of multiple
experiments in parallel.
An example for this using the high-level interfaces can be found
[here](examples/mujoco/mujoco_ppo_hl_multi.py), examples that use low-level
interfaces will follow soon.
### Improvements in Batch
Apart from that, several important
extensions have been added to internal data structures, most notably to `Batch`.
Batches now implement `__eq__` and can be meaningfully compared. Applying
operations in a nested fashion has been significantly simplified, and checking
for NaNs and dropping them is now possible.
One more notable change is that torch `Distribution` objects are now sliced when
slicing a batch. Previously, when a Batch with say 10 actions and a dist
corresponding to them was sliced to `[:3]`, the `dist` in the result would still
correspond to all 10 actions. Now, the dist is also "sliced" to be the
distribution of the first 3 actions.
A detailed list of changes can be found below.
## Changes/Improvements
- `evaluation`: New package for repeating the same experiment with multiple
seeds and aggregating the results. #1074 #1141 #1183
- `data`:
- `Batch`:
- Add methods `to_dict` and `to_list_of_dicts`. #1063 #1098
- Add methods `to_numpy_` and `to_torch_`. #1098, #1117
- Add `__eq__` (semantic equality check). #1098
- `keys()` deprecated in favor of `get_keys()` (needed to make iteration
consistent with naming) #1105.
- Major: new methods for applying functions to values, to check for NaNs
and drop them, and to set values. #1181
- Slicing a batch with a torch distribution now also slices the
distribution. #1181
- `data.collector`:
- `Collector`:
- Introduced `BaseCollector` as a base class for all collectors.
#1123
- Add method `close` #1063
- Method `reset` is now more granular (new flags controlling
behavior). #1063
- `CollectStats`: Add convenience
constructor `with_autogenerated_stats`. #1063
- `trainer`:
- Trainers can now control whether collectors should be reset prior to
training. #1063
- `policy`:
- introduced attribute `in_training_step` that is controlled by the trainer.
#1123
- policy automatically set to `eval` mode when collecting and to `train`
mode when updating. #1123
- Extended interface of `compute_action` to also support array-like inputs
#1169
- `highlevel`:
- `SamplingConfig`:
- Add support for `batch_size=None`. #1077
- Add `training_seed` for explicit seeding of training and test
environments, the `test_seed` is inferred from `training_seed`. #1074
- `experiment`:
- `Experiment` now has a `name` attribute, which can be set
using `ExperimentBuilder.with_name` and
which determines the default run name and therefore the persistence
subdirectory.
It can still be overridden in `Experiment.run()`, the new parameter
name being `run_name` rather than
`experiment_name` (although the latter will still be interpreted
correctly). #1074 #1131
- Add class `ExperimentCollection` for the convenient execution of
multiple experiment runs #1131
- The `World` object, containing all low-level objects needed for
experimentation,
can now be extracted from an `Experiment` instance. This enables
customizing
the experiment prior to its execution, bridging the low and high-level
interfaces. #1187
- `ExperimentBuilder`:
- Add method `build_seeded_collection` for the sound creation of
multiple
experiments with varying random seeds #1131
- Add method `copy` to facilitate the creation of multiple
experiments from a single builder #1131
- `env`:
- Added new `VectorEnvType` called `SUBPROC_SHARED_MEM_AUTO` and used in
for Atari and Mujoco venv creation. #1141
- `utils`:
- `logger`:
- Loggers can now restore the logged data into python by using the
new `restore_logged_data` method. #1074
- Wandb logger extended #1183
- `net.continuous.Critic`:
- Add flag `apply_preprocess_net_to_obs_only` to allow the
preprocessing network to be applied to the observations only (without
the actions concatenated), which is essential for the case where we
want
to reuse the actor's preprocessing network #1128
- `torch_utils` (new module)
- Added context managers `torch_train_mode`
and `policy_within_training_step` #1123
- `print`
- `DataclassPPrintMixin` now supports outputting a string, not just
printing the pretty repr. #1141
## Fixes
- `highlevel`:
- `CriticFactoryReuseActor`: Enable the Critic
flag `apply_preprocess_net_to_obs_only` for continuous critics,
fixing the case where we want to reuse an actor's preprocessing network
for the critic (affects usages
of the experiment builder method `with_critic_factory_use_actor` with
continuous environments) #1128
- Policy parameter `action_scaling` value `"default"` was not correctly
transformed to a Boolean value for
algorithms SAC, DDPG, TD3 and REDQ. The value `"default"` being truthy
caused action scaling to be enabled
even for discrete action spaces. #1191
- `atari_network.DQN`:
- Fix constructor input validation #1128
- Fix `output_dim` not being set if `features_only`=True
and `output_dim_added_layer` is not None #1128
- `PPOPolicy`:
- Fix `max_batchsize` not being used in `logp_old` computation
inside `process_fn` #1168
- Fix `Batch.__eq__` to allow comparing Batches with scalar array values #1185
## Internal Improvements
- `Collector`s rely less on state, the few stateful things are stored explicitly
instead of through a `.data` attribute. #1063
- Introduced a first iteration of a naming convention for vars in `Collector`s.
#1063
- Generally improved readability of Collector code and associated tests (still
quite some way to go). #1063
- Improved typing for `exploration_noise` and within Collector. #1063
- Better variable names related to model outputs (logits, dist input etc.).
#1032
- Improved typing for actors and critics, using Tianshou classes
like `Actor`, `ActorProb`, etc.,
instead of just `nn.Module`. #1032
- Added interfaces for most `Actor` and `Critic` classes to enforce the presence
of `forward` methods. #1032
- Simplified `PGPolicy` forward by unifying the `dist_fn` interface (see
associated breaking change). #1032
- Use `.mode` of distribution instead of relying on knowledge of the
distribution type. #1032
- Exception no longer raised on `len` of empty `Batch`. #1084
- tests and examples are covered by `mypy`. #1077
- `Actor` is more used, stricter typing by making it generic. #1077
- Use explicit multiprocessing context for creating `Pipe` in `subproc.py`.
#1102
## Breaking Changes
- `data`:
- `Collector`:
- Removed `.data` attribute. #1063
- Collectors no longer reset the environment on initialization.
Instead, the user might have to call `reset` expicitly or
pass `reset_before_collect=True` . #1063
- Removed `no_grad` argument from `collect` method (was unused in
tianshou). #1123
- `Batch`:
- Fixed `iter(Batch(...)` which now behaves the same way
as `Batch(...).__iter__()`.
Can be considered a bugfix. #1063
- The methods `to_numpy` and `to_torch` in are not in-place anymore
(use `to_numpy_` or `to_torch_` instead). #1098, #1117
- The method `Batch.is_empty` has been removed. Instead, the user can
simply check for emptiness of Batch by using `len` on dicts. #1144
- Stricter `cat_`, only concatenation of batches with the same structure
is allowed. #1181
- `to_torch` and `to_numpy` are no longer static methods.
So `Batch.to_numpy(batch)` should be replaced by `batch.to_numpy()`.
#1200
- `utils`:
- `logger`:
- `BaseLogger.prepare_dict_for_logging` is now abstract. #1074
- Removed deprecated and unused `BasicLogger` (only affects users who
subclassed it). #1074
- `utils.net`:
- `Recurrent` now receives and returns
a `RecurrentStateBatch` instead of a dict. #1077
- Modules with code that was copied from sensAI have been replaced by
imports from new dependency sensAI-utils:
- `tianshou.utils.logging` is replaced with `sensai.util.logging`
- `tianshou.utils.string` is replaced with `sensai.util.string`
- `tianshou.utils.pickle` is replaced with `sensai.util.pickle`
- `env`:
- All VectorEnvs now return a numpy array of info-dicts on reset instead of
a list. #1063
- `policy`:
- Changed interface of `dist_fn` in `PGPolicy` and all subclasses to take a
single argument in both
continuous and discrete cases. #1032
- `AtariEnvFactory` constructor (in examples, so not really breaking) now
requires explicit train and test seeds. #1074
- `EnvFactoryRegistered` now requires an explicit `test_seed` in the
constructor. #1074
- `highlevel`:
- `params`: The parameter `dist_fn` has been removed from the parameter
objects (`PGParams`, `A2CParams`, `PPOParams`, `NPGParams`, `TRPOParams`).
The correct distribution is now determined automatically based on the
actor factory being used, avoiding the possibility of
misspecification. Persisted configurations/policies continue to work as
expected, but code must not specify the `dist_fn` parameter.
#1194 #1195
- `env`:
- `EnvFactoryRegistered`: parameter `seed` has been replaced by the pair
of parameters `training_seed` and `test_seed`
Persisted instances will continue to work correctly.
Subclasses such as `AtariEnvFactory` are also affected requires
explicit train and test seeds. #1074
- `VectorEnvType`: `SUBPROC_SHARED_MEM` has been replaced
by `SUBPROC_SHARED_MEM_DEFAULT`. It is recommended to
use `SUBPROC_SHARED_MEM_AUTO` instead. However, persisted configs will
continue working. #1141
## Tests
- Fixed env seeding it `test_sac_with_il.py` so that the test doesn't fail
randomly. #1081
- Improved CI triggers and added telemetry (if requested by user) #1177
- Improved environment used in tests.
- Improved tests bach equality to check with scalar values #1185
## Dependencies
- [DeepDiff](https://github.com/seperman/deepdiff) added to help with diffs of
batches in tests. #1098
- Bumped black, idna, pillow
- New extra "eval"
- Bumped numba to >=60.0.0, permitting installation on python 3.12 # 1177
- New dependency sensai-utils
# Release 1.0.0 (2024-03-20)
This release focuses on updating and improving Tianshou internals (in particular, code quality) while creating relatively few breaking changes (apart from things like the python and dependencies' versions).
We view it as a significant step for transforming Tianshou into the go-to place both for RL researchers, as well as for RL practitioners working on industry projects.
This is the first release after the [appliedAI Institute](https://www.appliedai-institute.de/en/) (the [TransferLab](https://transferlab.ai/) division) has decided to further develop Tianshou and provide long-term support.
## Breaking Changes
- dropped support of python<3.11
- dropped support of gym, from now on only Gymnasium envs are supported
- removed functions like `offpolicy_trainer` in favor of `OffpolicyTrainer(...).run()` (this affects all example scripts)
- several breaking changes related to removing `**kwargs` from signatures, renamings of internal attributes (like `critic1` -> `critic`)
- Outputs of training methods are now dataclasses instead of dicts
## Functionality Extensions
### Major
- High level interfaces for experiments, demonstrated by the new example scripts with names ending in `_hl.py`
### Minor
- Method to compute action directly from a policy's observation, can be used for unrolling
- Support for custom keys in ReplayBuffer
- Support for CalQL as part of CQL
- Support for explicit setting of multiprocessing context for SubprocEnvWorker
- `critic2` no longer has to be explicitly constructed and passed if it is supposed to be the same network as `critic` (formerly `critic1`)
## Internal Improvements
### Build and Docs
- Completely changed the build pipeline. Tianshou now uses poetry, black, ruff, poethepoet, nbqa and other niceties.
- Notebook tutorials are now part of the repository (previously they were in a drive). They were fixed and are executed during the build as integration tests, in addition to serving as documentation. Parts of the content have been improved.
- Documentation is now built with jupyter book. JavaScript code has been slightly improved, JS dependencies are included as part of the repository.
- Many improvements in docstrings
### Typing
- Adding `BatchPrototypes` to cover the fields needed and returned by methods relying on batches in a backwards compatible way
- Removing `**kwargs` from policies' constructors
- Overall, much stricter and more correct typing. Removing `kwargs` and replacing dicts by dataclasses in several places.
- Making use of `Generic` to express different kinds of stats that can be returned by `learn` and `update`
- Improved typing in `tests` and `examples`, close to passing mypy
### General
- Reduced duplication, improved readability and simplified code in several places
- Use `dist.mode` instead of inferring `loc` or `argmax` from the `dist_fn` input
## Contributions
### The OG creators
- @Trinkle23897 participated in almost all aspects of the coordination and reviewed most of the merged PRs
- @nuance1979 participated in several discussions
### From appliedAI
The team working on this release of Tianshou consisted of @opcode81 @MischaPanch @maxhuettenrauch @carlocagnetta @bordeauxred
### External contributions
- @BFAnas participated in several discussions and contributed the CalQL implementation, extending the pre-processing logic.
- @dantp-ai fixed many mypy issues and improved the tests
- @arnaujc91 improved the logic of computing deterministic actions
- Many other contributors, among them many new ones participated in this release. The Tianshou team is very grateful for your contributions!
# Older Releases
See [releases on GitHub](https://github.com/thu-ml/tianshou/releases)
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Tianshou
Please refer to the ['Developer Guide' on tianshou.org](https://tianshou.org/en/latest/04_developer_guide/developer_guide.html).
================================================
FILE: Dockerfile
================================================
# Use the official Python image for the base image.
FROM --platform=linux/amd64 python:3.11-slim
# Set environment variables to make Python print directly to the terminal and avoid .pyc files.
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
# Install system dependencies required for the project.
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
build-essential \
git \
wget \
unzip \
libvips-dev \
gnupg2 \
&& rm -rf /var/lib/apt/lists/*
# Install pipx.
RUN python3 -m pip install --no-cache-dir pipx \
&& pipx ensurepath
# Add poetry to the path
ENV PATH="${PATH}:/root/.local/bin"
# Install the latest version of Poetry using pipx.
RUN pipx install poetry
# Set the working directory. IMPORTANT: can't be changed as needs to be in sync to the dir where the project is cloned
# to in the codespace
WORKDIR /workspaces/tianshou
# Copy the pyproject.toml and poetry.lock files (if available) into the image.
COPY pyproject.toml poetry.lock* README.md /workspaces/tianshou/
RUN poetry config virtualenvs.create false
RUN poetry install --no-root --with dev
# The entrypoint will perform an editable install, it is expected that the code is mounted in the container then
# If you don't want to mount the code, you should override the entrypoint
ENTRYPOINT ["/bin/bash", "-c", "poetry install --with dev && poetry run jupyter trust notebooks/*.ipynb docs/02_notebooks/*.ipynb && $0 $@"]
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2022 Tianshou contributors
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 LICENSE
================================================
FILE: README.md
================================================
<div align="center">
<a href="http://tianshou.readthedocs.io"><img width="300px" height="auto" src="https://github.com/thu-ml/tianshou/raw/master/docs/_static/images/tianshou-logo.png"></a>
</div>
---
[](https://pypi.org/project/tianshou/) [](https://github.com/conda-forge/tianshou-feedstock) [](https://tianshou.org/en/master/) [](https://github.com/thu-ml/tianshou/actions) [](https://codecov.io/gh/thu-ml/tianshou) [](https://github.com/thu-ml/tianshou/issues) [](https://github.com/thu-ml/tianshou/stargazers) [](https://github.com/thu-ml/tianshou/network) [](https://github.com/thu-ml/tianshou/blob/master/LICENSE)
> [!NOTE]
> **Tianshou version 2 is here!**
>
> We have released the new major version of Tianshou on PyPI.
> Version 2 is a complete overhaul of the software design of the procedural API, in which
> * we establish a clear separation between learning algorithms and policies (via the separate abstractions `Algorithm` and `Policy`).
> * we provide more well-defined, more usable interfaces with extensive documentation of all algorithm and trainer parameters,
> renaming some parameters to make their names more consistent and intuitive.
> * the class hierarchy is fully revised, establishing a clear separation between on-policy, off-policy and offline algorithms
> at the type level and ensuring that all inheritance relationships are meaningful.
>
> Because of the extent of the changes, this version is not backwards compatible with previous versions of Tianshou.
> For migration information, please see the [change log](CHANGELOG.md).
**Tianshou** ([天授](https://baike.baidu.com/item/%E5%A4%A9%E6%8E%88)) is a reinforcement learning (RL) library based on pure PyTorch and [Gymnasium](http://github.com/Farama-Foundation/Gymnasium). Tianshou's main features at a glance are:
1. Modular low-level interfaces for algorithm developers (RL researchers) that are both flexible, hackable and type-safe.
1. Convenient high-level interfaces for applications of RL (training an implemented algorithm on a custom environment).
1. Large scope: online (on- and off-policy) and offline RL, experimental support for multi-agent RL (MARL), experimental support for model-based RL, and more
Unlike other reinforcement learning libraries, which may have complex codebases,
unfriendly high-level APIs, or are not optimized for speed, Tianshou provides a high-performance, modularized framework
and user-friendly interfaces for building deep reinforcement learning agents. One more aspect that sets Tianshou apart is its
generality: it supports online and offline RL, multi-agent RL, and model-based algorithms.
Tianshou aims at enabling concise implementations, both for researchers and practitioners, without sacrificing flexibility.
Supported algorithms include:
- [Deep Q-Network (DQN)](https://storage.googleapis.com/deepmind-media/dqn/DQNNaturePaper.pdf)
- [Double DQN](https://arxiv.org/pdf/1509.06461.pdf)
- [Dueling DQN](https://arxiv.org/pdf/1511.06581.pdf)
- [Branching DQN](https://arxiv.org/pdf/1711.08946.pdf)
- [Categorical DQN (C51)](https://arxiv.org/pdf/1707.06887.pdf)
- [Rainbow DQN (Rainbow)](https://arxiv.org/pdf/1710.02298.pdf)
- [Quantile Regression DQN (QRDQN)](https://arxiv.org/pdf/1710.10044.pdf)
- [Implicit Quantile Network (IQN)](https://arxiv.org/pdf/1806.06923.pdf)
- [Fully-parameterized Quantile Function (FQF)](https://arxiv.org/pdf/1911.02140.pdf)
- [Policy Gradient (PG)](https://papers.nips.cc/paper/1713-policy-gradient-methods-for-reinforcement-learning-with-function-approximation.pdf)
- [Natural Policy Gradient (NPG)](https://proceedings.neurips.cc/paper/2001/file/4b86abe48d358ecf194c56c69108433e-Paper.pdf)
- [Advantage Actor-Critic (A2C)](https://openai.com/blog/baselines-acktr-a2c/)
- [Trust Region Policy Optimization (TRPO)](https://arxiv.org/pdf/1502.05477.pdf)
- [Proximal Policy Optimization (PPO)](https://arxiv.org/pdf/1707.06347.pdf)
- [Deep Deterministic Policy Gradient (DDPG)](https://arxiv.org/pdf/1509.02971.pdf)
- [Twin Delayed DDPG (TD3)](https://arxiv.org/pdf/1802.09477.pdf)
- [Soft Actor-Critic (SAC)](https://arxiv.org/pdf/1812.05905.pdf)
- [Randomized Ensembled Double Q-Learning (REDQ)](https://arxiv.org/pdf/2101.05982.pdf)
- [Discrete Soft Actor-Critic (SAC-Discrete)](https://arxiv.org/pdf/1910.07207.pdf)
- [Vanilla Imitation Learning](https://en.wikipedia.org/wiki/Apprenticeship_learning)
- [Batch-Constrained deep Q-Learning (BCQ)](https://arxiv.org/pdf/1812.02900.pdf)
- [Conservative Q-Learning (CQL)](https://arxiv.org/pdf/2006.04779.pdf)
- [Twin Delayed DDPG with Behavior Cloning (TD3+BC)](https://arxiv.org/pdf/2106.06860.pdf)
- [Discrete Batch-Constrained deep Q-Learning (BCQ-Discrete)](https://arxiv.org/pdf/1910.01708.pdf)
- [Discrete Conservative Q-Learning (CQL-Discrete)](https://arxiv.org/pdf/2006.04779.pdf)
- [Discrete Critic Regularized Regression (CRR-Discrete)](https://arxiv.org/pdf/2006.15134.pdf)
- [Generative Adversarial Imitation Learning (GAIL)](https://arxiv.org/pdf/1606.03476.pdf)
- [Prioritized Experience Replay (PER)](https://arxiv.org/pdf/1511.05952.pdf)
- [Generalized Advantage Estimator (GAE)](https://arxiv.org/pdf/1506.02438.pdf)
- [Posterior Sampling Reinforcement Learning (PSRL)](https://www.ece.uvic.ca/~bctill/papers/learning/Strens_2000.pdf)
- [Intrinsic Curiosity Module (ICM)](https://arxiv.org/pdf/1705.05363.pdf)
- [Hindsight Experience Replay (HER)](https://arxiv.org/pdf/1707.01495.pdf)
Other noteworthy features:
- Elegant framework with dual APIs:
- Tianshou's high-level API maximizes ease of use for application development while still retaining a high degree
of flexibility.
- The fundamental procedural API provides a maximum of flexibility for algorithm development without being
overly verbose.
- State-of-the-art results in [MuJoCo benchmarks](https://github.com/thu-ml/tianshou/tree/master/examples/mujoco) for REINFORCE/A2C/TRPO/PPO/DDPG/TD3/SAC algorithms
- Support for vectorized environments (synchronous or asynchronous) for all algorithms (see [usage](https://tianshou.readthedocs.io/en/master/01_tutorials/07_cheatsheet.html#parallel-sampling))
- Support for super-fast vectorized environments based on [EnvPool](https://github.com/sail-sg/envpool/) for all algorithms (see [usage](https://tianshou.readthedocs.io/en/master/01_tutorials/07_cheatsheet.html#envpool-integration))
- Support for recurrent state representations in actor networks and critic networks (RNN-style training for POMDPs) (see [usage](https://tianshou.readthedocs.io/en/master/01_tutorials/07_cheatsheet.html#rnn-style-training))
- Support any type of environment state/action (e.g. a dict, a self-defined class, ...) [Usage](https://tianshou.readthedocs.io/en/master/01_tutorials/07_cheatsheet.html#user-defined-environment-and-different-state-representation)
- Support for customized training processes (see [usage](https://tianshou.readthedocs.io/en/master/01_tutorials/07_cheatsheet.html#customize-training-process))
- Support n-step returns estimation and prioritized experience replay for all Q-learning based algorithms; GAE, nstep and PER are highly optimized thanks to numba's just-in-time compilation and vectorized numpy operations
- Support for multi-agent RL (see [usage](https://tianshou.readthedocs.io/en/master/01_tutorials/07_cheatsheet.html#multi-agent-reinforcement-learning))
- Support for logging based on both [TensorBoard](https://www.tensorflow.org/tensorboard) and [W&B](https://wandb.ai/)
- Support for multi-GPU training (see [usage](https://tianshou.readthedocs.io/en/master/01_tutorials/07_cheatsheet.html#multi-gpu))
- Comprehensive documentation, PEP8 code-style checking, type checking and thorough [tests](https://github.com/thu-ml/tianshou/actions)
In Chinese, Tianshou means divinely ordained, being derived to the gift of being born.
Tianshou is a reinforcement learning platform, and the nature of RL is not learn from humans.
So taking "Tianshou" means that there is no teacher to learn from, but rather to learn by oneself through constant interaction with the environment.
“天授”意指上天所授,引申为与生具有的天赋。天授是强化学习平台,而强化学习算法并不是向人类学习的,所以取“天授”意思是没有老师来教,而是自己通过跟环境不断交互来进行学习。
## Installation
Tianshou is currently hosted on [PyPI](https://pypi.org/project/tianshou/) and [conda-forge](https://github.com/conda-forge/tianshou-feedstock). It requires Python >= 3.11.
For installing the most recent version of Tianshou, the best way is clone the repository and install it with [poetry](https://python-poetry.org/)
(which you need to install on your system first)
```bash
git clone git@github.com:thu-ml/tianshou.git
cd tianshou
poetry install
```
You can also install the dev requirements by adding `--with dev` or the extras
for say mujoco and acceleration by [envpool](https://github.com/sail-sg/envpool)
by adding `--extras "mujoco envpool"`
If you wish to install multiple extras, ensure that you include them in a single command. Sequential calls to `poetry install --extras xxx` will overwrite prior installations, leaving only the last specified extras installed.
Or you may install all the following extras by adding `--all-extras`.
Available extras are:
- `atari` (for Atari environments)
- `box2d` (for Box2D environments)
- `classic_control` (for classic control (discrete) environments)
- `mujoco` (for MuJoCo environments)
- `mujoco-py` (for legacy mujoco-py environments[^1])
- `pybullet` (for pybullet environments)
- `robotics` (for gymnasium-robotics environments)
- `vizdoom` (for ViZDoom environments)
- `envpool` (for [envpool](https://github.com/sail-sg/envpool/) integration)
- `argparse` (in order to be able to run the high level API examples)
[^1]:
`mujoco-py` is a legacy package and is not recommended for new projects.
It is only included for compatibility with older projects.
Also note that there may be compatibility issues with macOS newer than
Monterey.
Otherwise, you can install the latest release from PyPI (currently
far behind the master) with the following command:
```bash
$ pip install tianshou
```
If you are using Anaconda or Miniconda, you can install Tianshou from conda-forge:
```bash
$ conda install tianshou -c conda-forge
```
Alternatively to the poetry install, you can also install the latest source version through GitHub:
```bash
$ pip install git+https://github.com/thu-ml/tianshou.git@master --upgrade
```
Finally, you may check the installation via your Python console as follows:
```python
import tianshou
print(tianshou.__version__)
```
If no errors are reported, you have successfully installed Tianshou.
## Documentation
Find example scripts in the [test/]( https://github.com/thu-ml/tianshou/blob/master/test) and [examples/](https://github.com/thu-ml/tianshou/blob/master/examples) folders.
Tutorials and API documentation are hosted on [tianshou.readthedocs.io](https://tianshou.readthedocs.io/).
## Why Tianshou?
### Comprehensive Functionality
### High Software Engineering Standards
| RL Platform | Documentation | Code Coverage | Type Hints | Last Update |
| ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | ----------------------------------------------------------------------------------------------------------------- |
| [Stable-Baselines3](https://github.com/DLR-RM/stable-baselines3) | [](https://stable-baselines3.readthedocs.io/en/master/?badge=master) | [](https://gitlab.com/araffin/stable-baselines3/-/commits/master) | :heavy_check_mark: |  |
| [Ray/RLlib](https://github.com/ray-project/ray/tree/master/rllib/) | [](http://docs.ray.io/en/master/rllib.html) | :heavy_minus_sign:<sup>(1)</sup> | :heavy_check_mark: |  |
| [SpinningUp](https://github.com/openai/spinningup) | [](https://spinningup.openai.com/) | :x: | :x: |  |
| [Dopamine](https://github.com/google/dopamine) | [](https://github.com/google/dopamine/tree/master/docs) | :x: | :x: |  |
| [ACME](https://github.com/deepmind/acme) | [](https://github.com/deepmind/acme/blob/master/docs/index.md) | :heavy_minus_sign:<sup>(1)</sup> | :heavy_check_mark: |  |
| [Sample Factory](https://github.com/alex-petrenko/sample-factory) | [:heavy_minus_sign:](https://arxiv.org/abs/2006.11751) | [](https://codecov.io/gh/alex-petrenko/sample-factory) | :x: |  |
| | | | | |
| [Tianshou](https://github.com/thu-ml/tianshou) | [](https://tianshou.readthedocs.io/en/master) | [](https://codecov.io/gh/thu-ml/tianshou) | :heavy_check_mark: |  |
<sup>(1): it has continuous integration but the coverage rate is not available</sup>
### Reproducible, High-Quality Results
Tianshou is rigorously tested. In contrast to other RL platforms, **our tests include the full agent training procedure for all of the implemented algorithms**. Our tests would fail once if any of the agents failed to achieve a consistent level of performance on limited epochs.
Our tests thus ensure reproducibility.
Check out the [GitHub Actions](https://github.com/thu-ml/tianshou/actions) page for more detail.
Atari and MuJoCo benchmark results can be found in the [examples/atari/](examples/atari/) and [examples/mujoco/](examples/mujoco/) folders respectively. **Our MuJoCo results reach or exceed the level of performance of most existing benchmarks.**
### Algorithm Abstraction
Reinforcement learning algorithms are build on abstractions for
- on-policy algorithms (`OnPolicyAlgorithm`),
- off-policy algorithms (`OffPolicyAlgorithm`), and
- offline algorithms (`OfflineAlgorithm`),
all of which clearly separate the core algorithm from the training process and the respective environment interactions.
In each case, the implementation of an algorithm necessarily involves only the implementation of methods for
- pre-processing a batch of data, augmenting it with necessary information/sufficient statistics for learning (`_preprocess_batch`),
- updating model parameters based on an augmented batch of data (`_update_with_batch`).
The implementation of these methods suffices for a new algorithm to be applicable within Tianshou,
making experimentation with new approaches particularly straightforward.
## Quick Start
Tianshou provides two API levels:
- the high-level interface, which provides ease of use for end users seeking to run deep reinforcement learning applications
- the procedural interface, which provides a maximum of control, especially for very advanced users and developers of reinforcement learning algorithms.
In the following, let us consider an example application using the _CartPole_ gymnasium environment.
We shall apply the deep Q-network (DQN) learning algorithm using both APIs.
### High-Level API
In the high-level API, the basis for an RL experiment is an `ExperimentBuilder`
with which we can build the experiment we then seek to run.
Since we want to use DQN, we use the specialization `DQNExperimentBuilder`.
The high-level API provides largely declarative semantics, i.e. the code is
almost exclusively concerned with configuration that controls what to do
(rather than how to do it).
```python
from tianshou.highlevel.config import OffPolicyTrainingConfig
from tianshou.highlevel.env import (
EnvFactoryRegistered,
VectorEnvType,
)
from tianshou.highlevel.experiment import DQNExperimentBuilder, ExperimentConfig
from tianshou.highlevel.params.algorithm_params import DQNParams
from tianshou.highlevel.trainer import (
EpochStopCallbackRewardThreshold,
)
experiment = (
DQNExperimentBuilder(
EnvFactoryRegistered(
task="CartPole-v1",
venv_type=VectorEnvType.DUMMY,
training_seed=0,
test_seed=10,
),
ExperimentConfig(
persistence_enabled=False,
watch=True,
watch_render=1 / 35,
watch_num_episodes=100,
),
OffPolicyTrainingConfig(
max_epochs=10,
epoch_num_steps=10000,
batch_size=64,
num_training_envs=10,
num_test_envs=100,
buffer_size=20000,
collection_step_num_env_steps=10,
update_step_num_gradient_steps_per_sample=1 / 10,
),
)
.with_dqn_params(
DQNParams(
lr=1e-3,
gamma=0.9,
n_step_return_horizon=3,
target_update_freq=320,
eps_training=0.3,
eps_inference=0.0,
),
)
.with_model_factory_default(hidden_sizes=(64, 64))
.with_epoch_stop_callback(EpochStopCallbackRewardThreshold(195))
.build()
)
experiment.run()
```
The experiment builder takes three arguments:
- the environment factory for the creation of environments. In this case,
we use an existing factory implementation for gymnasium environments.
- the experiment configuration, which controls persistence and the overall
experiment flow. In this case, we have configured that we want to observe
the agent's behavior after it is trained (`watch=True`) for a number of
episodes (`watch_num_episodes=100`). We have disabled persistence, because
we do not want to save training logs, the agent or its configuration for
future use.
- the training configuration, which controls fundamental training parameters,
such as the total number of epochs we run the experiment for (`num_epochs=10`)
and the number of environment steps each epoch shall consist of
(`epoch_num_steps=10000`).
Every epoch consists of a series of data collection (rollout) steps and
training steps.
The parameter `collection_step_num_env_steps` controls the amount of data that is
collected in each collection step and after each collection step, we
perform a training step, applying a gradient-based update based on a sample
of data (`batch_size=64`) taken from the buffer of data that has been
collected. For further details, see the documentation of configuration class.
We then proceed to configure some of the parameters of the DQN algorithm itself:
For instance, we control the epsilon parameter for exploration.
We want to use random exploration during rollouts for training (`eps_training`),
but we don't when evaluating the agent's performance in the test environments
(`eps_inference`).
Furthermore, we configure model parameters of the network for the Q function,
parametrising the number of hidden layers of the default MLP factory.
Find the script in [examples/discrete/discrete_dqn_hl.py](examples/discrete/discrete_dqn_hl.py).
Here's a run (with the training time cut short):
<p align="center" style="text-algin:center">
<img src="docs/_static/images/discrete_dqn_hl.gif">
</p>
Find many further applications of the high-level API in the `examples/` folder;
look for scripts ending with `_hl.py`.
Note that most of these examples require the extra `argparse`
(install it by adding `--extras argparse` when invoking poetry).
### Procedural API
Let us now consider an analogous example in the procedural API.
Find the full script in [examples/discrete/discrete_dqn.py](https://github.com/thu-ml/tianshou/blob/master/examples/discrete/discrete_dqn.py).
First, import the relevant packages:
```python
import gymnasium as gym
import tianshou as ts
from tianshou.algorithm.modelfree.dqn import DiscreteQLearningPolicy
from tianshou.algorithm.optim import AdamOptimizerFactory
from tianshou.data import CollectStats
from tianshou.trainer import OffPolicyTrainerParams
from tianshou.utils.net.common import Net
from tianshou.utils.space_info import SpaceInfo
from torch.utils.tensorboard import SummaryWriter
```
Define hyper-parameters:
```python
task = 'CartPole-v1'
lr, epoch, batch_size = 1e-3, 10, 64
num_training_envs, num_test_envs = 10, 100
gamma, n_step, target_freq = 0.9, 3, 320
buffer_size = 20000
eps_train, eps_test = 0.1, 0.05
epoch_num_steps, collection_step_num_env_steps = 10000, 10
```
Initialize the logger:
```python
logger = ts.utils.TensorboardLogger(SummaryWriter('log/dqn'))
```
Create the environments:
```python
# You can also try SubprocVectorEnv, which will use parallelization
training_envs = ts.env.DummyVectorEnv([lambda: gym.make(task) for _ in range(num_training_envs)])
test_envs = ts.env.DummyVectorEnv([lambda: gym.make(task) for _ in range(num_test_envs)])
```
Create the network, policy, and algorithm:
```python
# Create the network
# Note: You can easily define other networks.
# See https://tianshou.readthedocs.io/en/master/01_tutorials/00_dqn.html#build-the-network
env = gym.make(task, render_mode="human")
assert isinstance(env.action_space, gym.spaces.Discrete)
space_info = SpaceInfo.from_env(env)
state_shape = space_info.observation_info.obs_shape
action_shape = space_info.action_info.action_shape
net = Net(state_shape=state_shape, action_shape=action_shape, hidden_sizes=[128, 128, 128])
optim = AdamOptimizerFactory(lr=lr)
# Create the policy
policy = DiscreteQLearningPolicy(
model=net,
action_space=env.action_space,
eps_training=eps_train,
eps_inference=eps_test
)
# Create the algorithm with the policy and optimizer factory
algorithm = DQN(
policy=policy,
optim=AdamOptimizerFactory(lr=lr),
gamma=gamma,
n_step_return_horizon=n_step,
target_update_freq=target_freq
)
```
Set up the collectors:
```python
training_collector = ts.data.Collector[CollectStats](
algorithm,
training_envs,
ts.data.VectorReplayBuffer(buffer_size, num_training_envs),
exploration_noise=True,
)
test_collector = ts.data.Collector[CollectStats](
algorithm,
test_envs,
exploration_noise=True,
)
```
Let's train the model using the algorithm:
```python
result = algorithm.run_training(
OffPolicyTrainerParams(
training_collector=training_collector,
test_collector=test_collector,
max_epochs=epoch,
epoch_num_steps=epoch_num_steps,
collection_step_num_env_steps=collection_step_num_env_steps,
test_step_num_episodes=num_test_envs,
batch_size=batch_size,
update_step_num_gradient_steps_per_sample=1 / collection_step_num_env_steps,
stop_fn=lambda mean_rewards: mean_rewards >= env.spec.reward_threshold,
logger=logger,
test_in_training=True,
)
)
print(f"Finished training in {result.timing.total_time} seconds")
```
This is how you could manually save/load the trained policy (it's exactly the same as loading a `torch.nn.module`):
```python
torch.save(policy.state_dict(), 'dqn.pth')
policy.load_state_dict(torch.load('dqn.pth'))
```
Now let's watch the agent with 35 FPS:
```python
collector = ts.data.Collector(policy, env, exploration_noise=True)
collector.collect(n_episode=1, render=1 / 35)
```
Inspect the data saved in TensorBoard:
```bash
$ tensorboard --logdir log/dqn
```
Please read the [documentation](https://tianshou.readthedocs.io) for advanced usage.
## Contributing
Tianshou is still under development.
Further algorithms and features are continuously being added, and we always welcome contributions to help make Tianshou better.
If you would like to contribute, please check out [this link](https://tianshou.org/en/master/04_contributing/04_contributing.html).
## Citing Tianshou
If you find Tianshou useful, please cite it in your publications.
```latex
@article{tianshou,
author = {Jiayi Weng and Huayu Chen and Dong Yan and Kaichao You and Alexis Duburcq and Minghao Zhang and Yi Su and Hang Su and Jun Zhu},
title = {Tianshou: A Highly Modularized Deep Reinforcement Learning Library},
journal = {Journal of Machine Learning Research},
year = {2022},
volume = {23},
number = {267},
pages = {1--6},
url = {http://jmlr.org/papers/v23/21-1127.html}
}
```
## Acknowledgments
Tianshou is supported by [appliedAI Institute for Europe](https://www.appliedai-institute.de/en/),
who is committed to providing long-term support and development.
Tianshou was previously a reinforcement learning platform based on TensorFlow. You can check out the branch [`priv`](https://github.com/thu-ml/tianshou/tree/priv) for more detail. Many thanks to [Haosheng Zou](https://github.com/HaoshengZou)'s pioneering work for Tianshou before version 0.1.1.
We would like to thank [TSAIL](http://ml.cs.tsinghua.edu.cn/) and [Institute for Artificial Intelligence, Tsinghua University](http://ml.cs.tsinghua.edu.cn/thuai/) for providing such an excellent AI research platform.
================================================
FILE: benchmark/run_benchmark.py
================================================
"""Benchmark orchestration script for evaluating Tianshou's algorithm implementations.
This module provides automated benchmarking capabilities for reinforcement learning algorithms
across different environments (Atari, MuJoCo). It manages parallel experiment execution using
tmux sessions, handles experiment lifecycle, and aggregates results.
Key features:
- Discovers and runs multiple RL algorithm scripts in parallel
- Manages concurrency limits to prevent resource exhaustion
- Each script runs in its own isolated tmux session for easy monitoring
- Supports multiple tasks/environments per benchmark run
- Aggregates rliable evaluation results into a unified format
- Configurable experiment parameters (epochs, environments, parallel workers)
- Filtering capabilities to run subsets of algorithms or tasks
The script is designed to be run from the command line,
allowing easy customization of benchmark parameters without code modification.
Example usage:
python run_benchmark.py --benchmark_type mujoco --num_experiments 5 --max_concurrent_sessions 4
"""
import json
import subprocess
import sys
import time
from pathlib import Path
from typing import Literal
from sensai.util import logging
from sensai.util.logging import datetime_tag
TMUX_SESSION_PREFIX = "tianshou"
# Sleep durations in seconds
TMUX_SESSION_START_DELAY = 2
SESSION_CHECK_INTERVAL = 5
COMPLETION_CHECK_INTERVAL = 10
log = logging.getLogger("benchmark")
# Default tasks for each benchmark type
DEFAULT_TASKS = {
"mujoco": [
"Ant-v4",
"HalfCheetah-v4",
"Hopper-v4",
"Humanoid-v4",
"InvertedDoublePendulum-v4",
"InvertedPendulum-v4",
"Reacher-v4",
"Swimmer-v4",
"Walker2d-v4",
],
"atari": [
"PongNoFrameskip-v4",
"BreakoutNoFrameskip-v4",
"EnduroNoFrameskip-v4",
"QbertNoFrameskip-v4",
"MsPacmanNoFrameskip-v4",
"SeaquestNoFrameskip-v4",
"SpaceInvadersNoFrameskip-v4",
],
}
def find_script_paths(
benchmark_type: str, exclude_filter: str | None = None, include_filter: str = "**/*_hl.py"
) -> list[str]:
"""Return all Python scripts matching the glob filter under examples/<benchmark_type>."""
base_dir = Path(__file__).parent.parent / "examples" / benchmark_type
if not base_dir.exists():
raise FileNotFoundError(f"Directory '{base_dir}' does not exist.")
scripts = sorted(str(p) for p in base_dir.glob(include_filter))
if not scripts:
raise FileNotFoundError(
f"Did not find any scripts matching '{include_filter}' in '{base_dir}'."
)
# Apply exclusion filter if provided
if exclude_filter:
scripts = [s for s in scripts if not Path(s).match(exclude_filter)]
if not scripts:
raise FileNotFoundError(
f"No scripts remaining after applying exclude filter '{exclude_filter}'."
)
return scripts
def get_current_tmux_sessions(benchmark_type: str) -> list[str]:
"""List active tmux sessions starting with TMUX_SESSION_PREFIX."""
try:
output = subprocess.check_output(["tmux", "list-sessions"], stderr=subprocess.DEVNULL)
sessions = [
line.split(b":")[0].decode()
for line in output.splitlines()
if line.startswith(f"{TMUX_SESSION_PREFIX}_{benchmark_type}".encode())
]
return sessions
except subprocess.CalledProcessError:
return []
def start_tmux_session(
script_path: str,
persistence_base_dir: Path | str,
num_experiments: int,
benchmark_type: str,
task: str,
max_epochs: int | None = None,
epoch_num_steps: int | None = None,
experiment_launcher: Literal["sequential", "joblib"] | None = None,
num_training_envs: int | None = None,
num_test_envs: int | None = None,
) -> bool:
"""Start a tmux session running the given experiment script, returning True on success."""
# Normalize paths for Git Bash / Windows compatibility
python_exec = sys.executable.replace("\\", "/")
script_path = script_path.replace("\\", "/")
persistence_base_dir = str(persistence_base_dir).replace("\\", "/")
# Include task name in session to avoid collisions when running multiple tasks
script_name = Path(script_path).name.replace("_hl.py", "")
# Remove benchmark_type from name since we add it explicitly below
script_name = script_name.replace(benchmark_type, "").strip("_")
session_name = f"{TMUX_SESSION_PREFIX}_{benchmark_type}_{task}_{script_name}"
# Build command with optional max_epochs and epoch_num_steps
cmd_args = f"{python_exec} {script_path} --num_experiments {num_experiments} --persistence_base_dir {persistence_base_dir} --task {task}"
if max_epochs is not None:
cmd_args += f" --max_epochs {max_epochs}"
if epoch_num_steps is not None:
cmd_args += f" --epoch_num_steps {epoch_num_steps}"
if experiment_launcher is not None:
cmd_args += f" --experiment_launcher {experiment_launcher}"
if num_training_envs is not None:
cmd_args += f" --num_training_envs {num_training_envs}"
if num_test_envs is not None:
cmd_args += f" --num_test_envs {num_test_envs}"
cmd = [
"tmux",
"new-session",
"-d",
"-s",
session_name,
f"{cmd_args}; echo 'Finished {script_path}'; tmux kill-session -t {session_name}",
]
try:
subprocess.run(cmd, check=True)
log.info(
f"Started {script_path} in session '{session_name}'. Attach with:\ntmux attach -t {session_name}"
)
return True
except subprocess.CalledProcessError as e:
log.error(f"Failed to start {script_path} (session {session_name}): {e}")
return False
def aggregate_rliable_results(task_results_dir: str | Path) -> None:
"""Aggregate rliable results from all experiments into a single results.json per environment.
This form is expected by `benchmark.js` in the docs.
"""
task_results_dir = Path(task_results_dir)
if not task_results_dir.exists():
log.warning(f"Benchmark results directory does not exist: '{task_results_dir}'")
return
experiment_dirs = [d for d in task_results_dir.iterdir() if d.is_dir()]
aggregated_results = []
for experiment_dir in experiment_dirs:
agent_name = experiment_dir.name.split("Experiment")[0]
if not agent_name:
log.warning(
f"Could not extract agent name from directory: '{experiment_dir.name}', skipping..."
)
continue
rliable_file = experiment_dir / "rliable_evaluation_test.json"
if not rliable_file.exists():
log.warning(f"Missing rliable results file: '{rliable_file}', skipping...")
continue
try:
with open(rliable_file) as f:
result_entries = json.load(f)
for result_entry in result_entries:
result_entry["agent"] = agent_name
aggregated_results.append(result_entry)
except (OSError, json.JSONDecodeError) as e:
log.error(f"Failed to read or parse '{rliable_file}': {e}")
continue
if not aggregated_results:
log.warning(f"No results to aggregate for directory '{task_results_dir}'")
return
aggregated_results_path = task_results_dir / "results.json"
try:
with open(aggregated_results_path, "w") as f:
json.dump(aggregated_results, f, indent=4)
log.info(f"Aggregated {len(aggregated_results)} results to '{aggregated_results_path}'.")
except OSError as e:
log.error(f"Failed to write aggregated results to '{aggregated_results_path}': {e}")
def main(
max_concurrent_sessions: int | None = None,
benchmark_type: Literal["mujoco", "atari"] = "atari",
num_experiments: int = 1,
max_scripts: int = -1,
tasks: list[str] | None = None,
max_tasks: int = -1,
max_epochs: int | None = None,
epoch_num_steps: int | None = None,
num_training_envs: int | None = None,
num_test_envs: int | None = None,
experiment_launcher: Literal["sequential", "joblib"] | None = "sequential",
include_filter: str = "**/*_hl.py",
exclude_filter: str | None = None,
) -> None:
"""
Run the benchmarking by executing each selected script in its default configuration
(apart from explicitly overridden parameters) in its own tmux session in parallel.
Note that if you have unclosed tmux sessions from previous runs, they might count
towards the max_concurrent_sessions limit. You can terminate all sessions with
`tmux kill-server`.
:param max_concurrent_sessions: optionally restrict how many tmux sessions to open in parallel,
each script will run in a tmux session
:param benchmark_type: mujoco or atari
:param num_experiments: number of experiments to run per script
:param max_scripts: maximum number of scripts to run, -1 for all. Set this to a low number for testing.
:param tasks: optional list of task names to run benchmarks on. If None, uses default tasks for the benchmark_type.
:param max_tasks: maximum number of tasks to run, -1 for all. Set this to a low number for testing.
:param max_epochs: optional maximum number of training epochs to pass to all scripts. If None, uses script defaults.
:param epoch_num_steps: optional number of environment steps per epoch to pass to all scripts. If None, uses script defaults.
:param num_training_envs: optional number of training environments to pass to all scripts. If None, uses script defaults.
:param num_test_envs: optional number of test environments to pass to all scripts. If None, uses script defaults.
:param experiment_launcher: type of experiment launcher to use, only has an effect if `num_experiments>1`.
By default, will use the experiment launchers defined in the individual scripts.
:param include_filter: glob pattern to include scripts
:param exclude_filter: optional glob pattern to exclude scripts (e.g., "*ddpg*")
:return:
"""
# Use default tasks if none provided
if tasks is None:
tasks = DEFAULT_TASKS.get(benchmark_type, [])
if not tasks:
raise ValueError(
f"No default tasks found for benchmark_type '{benchmark_type}'. Please provide tasks manually."
)
# Limit number of tasks if specified
if max_tasks > 0:
log.info(f"Limiting to first {max_tasks}/{len(tasks)} tasks.")
tasks = tasks[:max_tasks]
log.info(f"Running benchmarks for {len(tasks)} task(s): {tasks}")
persistence_base_dir = Path(__file__).parent / "logs" / benchmark_type / datetime_tag()
# file logger for the global benchmarking logs, each individual experiment will log to its own file
log_file = persistence_base_dir / "benchmarking_run.txt"
log_file.parent.mkdir(parents=True, exist_ok=True)
logging.add_file_logger(log_file, append=False)
scripts = find_script_paths(
benchmark_type, exclude_filter=exclude_filter, include_filter=include_filter
)
if max_scripts > 0:
log.info(f"Limiting to first {max_scripts}/{len(scripts)} scripts.")
scripts = scripts[:max_scripts]
if max_concurrent_sessions is None:
max_concurrent_sessions = len(scripts)
# Run benchmarks for each task
for i_task, task in enumerate(tasks, 1):
log.info(
f"=== Starting benchmark batch for '{benchmark_type}' on task '{task}' ({i_task}/{len(tasks)}) "
f"for {len(scripts)} scripts with {max_concurrent_sessions} concurrent jobs ==="
)
for i_script, script in enumerate(scripts, start=1):
# Wait for free slot
has_printed_waiting_message = False
while len(get_current_tmux_sessions(benchmark_type)) >= max_concurrent_sessions:
if not has_printed_waiting_message:
log.info(
f"Max concurrent sessions reached ({max_concurrent_sessions}). "
f"Current sessions:\n{get_current_tmux_sessions(benchmark_type)}\nWaiting for a free slot..."
)
has_printed_waiting_message = True
time.sleep(SESSION_CHECK_INTERVAL)
log.info(f"Starting script {i_script}/{len(scripts)} for task '{task}'")
session_started = start_tmux_session(
script,
benchmark_type=benchmark_type,
persistence_base_dir=persistence_base_dir,
num_experiments=num_experiments,
task=task,
max_epochs=max_epochs,
epoch_num_steps=epoch_num_steps,
experiment_launcher=experiment_launcher,
num_training_envs=num_training_envs,
num_test_envs=num_test_envs,
)
if session_started:
time.sleep(TMUX_SESSION_START_DELAY) # Give tmux a moment to start the session
has_printed_final_waiting_message = False
# Wait for all sessions to complete before moving to next task
while len(get_current_tmux_sessions(benchmark_type)) > 0:
if not has_printed_final_waiting_message:
log.info(
f"All scripts for task '{task}' have been started, waiting for completion of remaining tmux sessions:\n"
f"{get_current_tmux_sessions(benchmark_type)}"
)
has_printed_final_waiting_message = True
time.sleep(COMPLETION_CHECK_INTERVAL)
log.info(f"All tmux sessions for task '{task}' have completed.")
# Aggregate results for this specific task (scripts create task-named directory automatically)
task_results_dir = persistence_base_dir / task
log.info(f"Aggregating results for task '{task}' from directory: {task_results_dir}")
try:
aggregate_rliable_results(str(task_results_dir))
except Exception as e:
log.error(f"Failed to aggregate rliable results for task '{task}': {e}\nContinuing...")
log.info(
f"=== Benchmark batch completed for all {len(scripts)} scripts and all {len(tasks)} task(s) ==="
)
if __name__ == "__main__":
logging.run_cli(main)
================================================
FILE: docs/.gitignore
================================================
/03_api/*
jupyter_execute
_toc.yml
.jupyter_cache
================================================
FILE: docs/01_user_guide/00_training_process.md
================================================
# The Reinforcement Learning Process
The following diagram illustrates the key mechanisms underlying the learning process in model-free reinforcement learning algorithms.
It shows how the agent interacts with the environment, collects experiences, and periodically updates its policy based on those experiences.
<div id="carousel" style="width: 85%; text-align:center; margin-bottom: 16px; border: 1px solid #ddd;">
<img id="carousel-image" src="../_static/images/agent-env-step1.png" style="width: 100%; border-radius:8px;" alt="RL Loop">
<div style="margin-bottom: 10px;">
<button onclick="prevImage()" style="padding: 0 10px; margin-right: 10px;">⇦ Prev</button>
<button onclick="nextImage()" style="padding: 0 10px;">Next ⇨</button>
</div>
<div id="caption" style="margin: 10px; text-align: left;"></div>
</div>
<script>
const images = [
{src: '../_static/images/agent-env-step1.png', caption: '<b>Step 1</b>: The agent receives the observable state from the environment.'},
{src: '../_static/images/agent-env-step2.png', caption: '<b>Step 2</b>: The agent uses its policy to select an action, passing it to the environment.'},
{src: '../_static/images/agent-env-step3.png', caption: '<b>Step 3</b>: The execution of the action results in a new state and produces a reward. The environment is specifically designed to provide feedback to the agent, returning (high) positive rewards for desirable states and low/negative rewards for undesirable states; rewards may be sparse, i.e. rewards for intermediate states may be zero. The agent records the action taken, the state transition and the reward received in its database of experiences (replay buffer). The agent repeats the process several times in order to fill the replay buffer with new transitions.'},
{src: '../_static/images/agent-env-step4.png', caption: '<b>Step 4</b>: Periodically, after having collected enough experiences, the agent uses the experience data to update its policy, i.e. the way it selects actions. The learning algorithm defines the corresponding update mechanism.'},
];
let index = 0;
function updateImage() {
document.getElementById('carousel-image').src = images[index].src;
document.getElementById('caption').innerHTML = images[index].caption;
}
function nextImage() {
index = (index + 1) % images.length;
updateImage();
}
function prevImage() {
index = (index - 1 + images.length) % images.length;
updateImage();
}
updateImage();
</script>
Accordingly, the key entities involved in the learning process are:
* The **environment**: This is the system the agent interacts with.
It provides the agent with observable states and rewards based on the actions taken by the agent.
* The agent's **policy**: This is the strategy used by the agent to decide which action to take in a given state.
The policy can be deterministic or stochastic and is typically represented by a neural network in deep reinforcement learning.
* The **replay buffer**: This is a data structure used to store the agent's experiences, which consist of state transitions,
actions taken, and rewards received.
The agent learns from past experience by sampling mini-batches from the buffer during the policy update phase.
* The **learning algorithm**: This defines how the agent updates its policy based on the experiences stored in the replay buffer.
Different algorithms have different update mechanisms, which can significantly affect the learning performance.
In some cases, the algorithm may also involve additional components (specifically neural networks), such as target networks or value
functions.
These entities have direct correspondences in Tianshou's codebase:
* The environment is represented by an instance of a class that inherits from `gymnasium.Env`, which is a standard interface for
reinforcement learning environments.
In practice, environments are typically vectorized to enable parallel interactions, increasing efficiency.
* The policy is encapsulated in the {class}`~tianshou.algorithm.algorithm_base.Policy` class, which provides methods for action selection.
* The replay buffer is implemented in the {class}`~tianshou.data.buffer.buffer_base.ReplayBuffer` class.
A {class}`~tianshou.data.collector.Collector` instance is used to manage the addition of new experiences to the replay buffer as the agent interacts with the
environment.
During the learning phase, the replay buffer may be sampled, providing an instance of {class}`~tianshou.data.batch.Batch` for the policy update.
* The abstraction for learning algorithms is given by the {class}`~tianshou.algorithm.algorithm_base.Algorithm` class, which defines how to update the policy using data from the
replay buffer.
(structuring-the-process)=
## Structuring the Process
The learning process itself is reified in Tianshou's {class}`~tianshou.trainer.trainer.Trainer` class, which orchestrates the interaction between the agent and the
environment, manages the replay buffer, and coordinates the policy updates according to the specified learning algorithm.
In general, the process can be described as executing a number of epochs as follows:
* **epoch**:
* repeat until a sufficient number of steps is reached (for online learning, typically environment step count)
* **training step**:
* for online learning algorithms …
* **collection step**: collect state transitions in the environment by running the agent
* (optionally) conduct a test step if collected data indicates promising behaviour
* **update step**: apply gradient updates using the algorithm’s update logic.
The update is based on …
* data from the preceding collection step only (on-policy learning)
* data from the collection step and previous data (off-policy learning)
* data from a user-provided replay buffer (offline learning)
* **test step**
* collect test episodes from dedicated test environments and evaluate agent performance
* (optionally) stop training early if performance is sufficiently high
```{admonition} Glossary
:class: note
The above introduces some of the key terms used throughout Tianshou.
```
Note that the above description encompasses several modes of model-free reinforcement learning, including:
* online learning (where the agent continuously interacts with the environment in order to collect new experiences)
* on-policy learning (where the policy is updated based on data collected using the current policy only)
* off-policy learning (where the policy is updated based on data collected using the current and previous policies)
* offline learning (where the replay buffer is pre-filled and not updated during training)
In Tianshou, the {class}`~tianshou.trainer.trainer.Trainer` and {class}`~tianshou.algorithm.algorithm_base.Algorithm` classes are specialised to handle these different modes accordingly.
================================================
FILE: docs/01_user_guide/01_apis.md
================================================
# Dual APIs
Tianshou provides two distinct APIs to serve different use cases and user preferences:
1. **high-level API**: a declarative, configuration-based interface designed for ease of use
2. **procedural API**: a flexible, imperative interface providing maximum control
Both APIs access the same underlying algorithm implementations, allowing you to choose the level
of abstraction that best fits your needs without sacrificing functionality.
## Overview
### High-Level API
The high-level API is built around the **builder pattern** and **declarative semantics**.
Instead of writing procedural code that sequentially constructs and connects components,
you declare _what_ you want through configuration objects and let Tianshou handle _how_ to
build and execute the experiment.
**Key characteristics:**
- centered around {class}`~tianshou.highlevel.experiment.ExperimentBuilder` classes (e.g., {class}`~tianshou.highlevel.experiment.DQNExperimentBuilder`, {class}`~tianshou.highlevel.experiment.PPOExperimentBuilder`, etc.)
- uses configuration dataclasses and factories for all relevant parameters
- automatically handles component creation and "wiring"
- provides sensible defaults that adapt to the nature of your environment
- includes built-in persistence, logging, and experiment management
- full type hints (but object structure is not flat; a proper IDE is required for seamless user experience)
### Procedural API
The procedural API provides explicit control over every component in the RL pipeline.
You manually create environments, networks, policies, algorithms, collectors, and
trainers, then wire them together.
**Key characteristics:**
- direct instantiation of all components
- explicit control over the training loop
- lower-level access to internal mechanisms
- minimal abstraction (closer to the implementation)
- ideal for algorithm development and research
## When to Use Which API
Use the high-level API when ...
- **you're applying existing algorithms** to new problems
- **you want to get started quickly** with minimal boilerplate
- **you need experiment management** with persistence, logging, and reproducibility
- **you prefer declarative code** that focuses on configuration
- **you're building applications** rather than developing new algorithms
Use the procedural API when:
- **you're developing new algorithms** or modifying existing ones
- **you need fine-grained control** over the training process
- **you want to understand** the internal workings of Tianshou
- **you're implementing custom components** not supported by the high-level API
- **you prefer imperative programming** where each step is explicit
- **you need maximum flexibility** for experimental research
## Comparison by Example
Let's compare both APIs by implementing the same DQN learning task on the CartPole environment.
### High-Level API Example
```python
from tianshou.highlevel.config import OffPolicyTrainingConfig
from tianshou.highlevel.env import EnvFactoryRegistered, VectorEnvType
from tianshou.highlevel.experiment import DQNExperimentBuilder, ExperimentConfig
from tianshou.highlevel.params.algorithm_params import DQNParams
from tianshou.highlevel.trainer import EpochStopCallbackRewardThreshold
# Build the experiment through configuration
experiment = (
DQNExperimentBuilder(
# Environment configuration
EnvFactoryRegistered(
task="CartPole-v1",
venv_type=VectorEnvType.DUMMY,
training_seed=0,
test_seed=10,
),
# Experiment settings
ExperimentConfig(
persistence_enabled=False,
watch=True,
watch_render=1 / 35,
watch_num_episodes=100,
),
# Training configuration
OffPolicyTrainingConfig(
max_epochs=10,
epoch_num_steps=10000,
batch_size=64,
num_training_envs=10,
num_test_envs=100,
buffer_size=20000,
collection_step_num_env_steps=10,
update_step_num_gradient_steps_per_sample=1 / 10,
),
)
# Algorithm-specific parameters
.with_dqn_params(
DQNParams(
lr=1e-3,
gamma=0.9,
n_step_return_horizon=3,
target_update_freq=320,
eps_training=0.3,
eps_inference=0.0,
),
)
# Network architecture
.with_model_factory_default(hidden_sizes=(64, 64))
# Stop condition
.with_epoch_stop_callback(EpochStopCallbackRewardThreshold(195))
.build()
)
# Run the experiment
experiment.run()
```
**What's happening here:**
1. We create an {class}`~tianshou.highlevel.experiment.ExperimentBuilder` with three main configuration objects
2. We chain builder methods to specify algorithm parameters, model architecture, and callbacks
3. We call `.build()` to construct the experiment
4. We call `.run()` to execute the entire training pipeline
The high-level API handles ...
- creating and configuring environments
- building the neural network
- instantiating the policy and algorithm
- setting up collectors and replay buffer
- managing the training loop
- watching the trained agent
### Procedural API Example
```python
import gymnasium as gym
import tianshou as ts
from tianshou.algorithm.modelfree.dqn import DiscreteQLearningPolicy
from tianshou.algorithm.optim import AdamOptimizerFactory
from tianshou.data import CollectStats
from tianshou.trainer import OffPolicyTrainerParams
from tianshou.utils.net.common import Net
from tianshou.utils.space_info import SpaceInfo
from torch.utils.tensorboard import SummaryWriter
# Define hyperparameters
task = "CartPole-v1"
lr, epoch, batch_size = 1e-3, 10, 64
num_training_envs, num_test_envs = 10, 100
gamma, n_step, target_freq = 0.9, 3, 320
buffer_size = 20000
eps_train, eps_test = 0.1, 0.05
epoch_num_steps, collection_step_num_env_steps = 10000, 10
# Set up logging
logger = ts.utils.TensorboardLogger(SummaryWriter("log/dqn"))
# Create environments
training_envs = ts.env.DummyVectorEnv([lambda: gym.make(task) for _ in range(num_training_envs)])
test_envs = ts.env.DummyVectorEnv([lambda: gym.make(task) for _ in range(num_test_envs)])
# Build the network
env = gym.make(task, render_mode="human")
space_info = SpaceInfo.from_env(env)
state_shape = space_info.observation_info.obs_shape
action_shape = space_info.action_info.action_shape
net = Net(state_shape=state_shape, action_shape=action_shape, hidden_sizes=[128, 128, 128])
# Create policy and algorithm
policy = DiscreteQLearningPolicy(
model=net,
action_space=env.action_space,
eps_training=eps_train,
eps_inference=eps_test,
)
algorithm = ts.algorithm.DQN(
policy=policy,
optim=AdamOptimizerFactory(lr=lr),
gamma=gamma,
n_step_return_horizon=n_step,
target_update_freq=target_freq,
)
# Set up collectors
training_collector = ts.data.Collector[CollectStats](
algorithm,
training_envs,
ts.data.VectorReplayBuffer(buffer_size, num_training_envs),
exploration_noise=True,
)
test_collector = ts.data.Collector[CollectStats](
algorithm,
test_envs,
exploration_noise=True,
)
# Define stop condition
def stop_fn(mean_rewards: float) -> bool:
if env.spec and env.spec.reward_threshold:
return mean_rewards >= env.spec.reward_threshold
return False
# Train the algorithm
result = algorithm.run_training(
OffPolicyTrainerParams(
training_collector=training_collector,
test_collector=test_collector,
max_epochs=epoch,
epoch_num_steps=epoch_num_steps,
collection_step_num_env_steps=collection_step_num_env_steps,
test_step_num_episodes=num_test_envs,
batch_size=batch_size,
update_step_num_gradient_steps_per_sample=1 / collection_step_num_env_steps,
stop_fn=stop_fn,
logger=logger,
test_in_training=True,
)
)
print(f"Finished training in {result.timing.total_time} seconds")
# Watch the trained agent
collector = ts.data.Collector[CollectStats](algorithm, env, exploration_noise=True)
collector.collect(n_episode=100, render=1 / 35)
```
**What's happening here:**
1. We explicitly define all hyperparameters as variables
2. We manually create the logger
3. We construct training and test environments
4. We build the neural network by extracting space information from the environment
5. We create the policy and algorithm objects
6. We set up collectors with a replay buffer
7. We define callback functions
8. We call `algorithm.run_training()` with explicit parameters
9. We manually set up and run the evaluation collector
The procedural API requires ...
- explicit creation of every component
- manual extraction of environment properties
- direct specification of all connections
## Key Concepts in the High-Level API
### ExperimentBuilder
The {class}`~tianshou.highlevel.experiment.ExperimentBuilder` is the core abstraction.
Each algorithm has its own builder (e.g., {class}`~tianshou.highlevel.experiment.DQNExperimentBuilder`, {class}`~tianshou.highlevel.experiment.PPOExperimentBuilder`, {class}`~tianshou.highlevel.experiment.SACExperimentBuilder`).
**Some methods you will find in experiment builders:**
- `.with_<algorithm>_params()` - Set algorithm-specific parameters
- `.with_model_factory()`, `.with_model_factory_default()` - Configure network architecture
- `.with_critic_factory()` - Configure critic network (for actor-critic methods)
- `.with_epoch_train_callback()` - Add function to be called at the beginning of the training step in each epoch
- `.with_epoch_test_callback()` - Add function to be called at the beginning of the test step in each epoch
- `.with_epoch_stop_callback()` - Define stopping conditions
- `.with_algorithm_wrapper_factory()` - Add algorithm wrappers (e.g., ICM)
### Configuration Objects
Three main configuration objects are required when constructing an experiment builder:
1. **Environment Configuration** ({class}`~tianshou.highlevel.env.EnvFactory` subclasses)
- Defines how to create and configure environments
- Existing factories:
- {class}`~tianshou.highlevel.env.EnvFactoryRegistered` - For the creation of environments registered in Gymnasium
- {class}`~tianshou.highlevel.env.atari.atari_wrapper.AtariEnvFactory` - For Atari environments with preprocessing
- Custom factories for your own environments can be created by subclassing {class}`~tianshou.highlevel.env.EnvFactory`
2. **Experiment Configuration** ({class}`~tianshou.highlevel.experiment.ExperimentConfig`):
General settings for the experiment, particularly related to
- logging
- randomization
- persistence
- watching the trained agent's performance after training
3. **Training Configuration** ({class}`~tianshou.highlevel.config.OffPolicyTrainingConfig`, {class}`~tianshou.highlevel.config.OnPolicyTrainingConfig`):
Defines all parameters related to the training process
### Parameter Classes
Algorithm parameters are defined in dataclasses specific to each algorithm (e.g., {class}`~tianshou.highlevel.params.algorithm_params.DQNParams`, {class}`~tianshou.highlevel.params.algorithm_params.PPOParams`).
The parameters are extensively documented.
```{note}
Make sure to use a modern IDE to take advantage of auto-completion and inline documentation!
```
### Factories
The high-level API uses factories extensively:
- **Model Factories**: Create neural networks (e.g., {class}`~tianshou.highlevel.module.intermediate.IntermediateModuleFactoryAtariDQN`)
- **Environment Factories**: Create and configure environments
- **Optimizer Factories**: Create optimizers with specific configurations
### Extensibility
The high-level API is designed to be extensible.
You can create custom factories (e.g. for your own models or your own environments) by subclassing the appropriate base classes
and then use them in the experiment builder.
If we have created a torch module in `CustomNetwork`, which we want to use within our policy,
we simply need to define a factory for it in order to apply it in the high-level API:
```python
from tianshou.highlevel.env import Environments
from tianshou.highlevel.module.core import TDevice
from tianshou.highlevel.module.intermediate import IntermediateModuleFactory, IntermediateModule
class CustomNetFactory(IntermediateModuleFactory):
def __init__(self, hidden_sizes: tuple[int, ...] = (128, 128)):
self.hidden_sizes = hidden_sizes
def create_intermediate_module(self, envs: Environments, device: TDevice) -> IntermediateModule:
obs_shape = envs.get_observation_shape()
action_shape = envs.get_action_shape()
# Your custom network creation logic
net = CustomNetwork(
obs_shape=obs_shape,
action_shape=action_shape,
hidden_sizes=self.hidden_sizes,
).to(device)
return IntermediateModule(net, net.output_dim)
experiment = (
DQNExperimentBuilder(...)
.with_model_factory(CustomNetFactory(hidden_sizes=(256, 256)))
.build()
)
```
## Key Concepts in the Procedural API
### Core Components
You manually create and connect ...
1. **environments**: e.g. using `gym.make()` and vectorization ({class}`~tianshou.env.DummyVectorEnv`, {class}`~tianshou.env.SubprocVectorEnv`)
2. **networks**: using {class}`~tianshou.utils.net.common.Net` or other PyTorch modules
3. **policies**: using algorithm-specific policy classes (e.g., {class}`~tianshou.algorithm.modelfree.dqn.DiscreteQLearningPolicy`)
4. **algorithms**: using algorithm classes (e.g., {class}`~tianshou.algorithm.modelfree.dqn.DQN`, {class}`~tianshou.algorithm.modelfree.ppo.PPO`, {class}`~tianshou.algorithm.modelfree.sac.SAC`)
5. **collectors**: using {class}`~tianshou.data.Collector` to gather experience
6. **buffers**: using {class}`~tianshou.data.buffer.VectorReplayBuffer` or {class}`~tianshou.data.buffer.ReplayBuffer`
7. **trainers**: using the respective trainer class and corresponding parameter class (e.g., {class}`~tianshou.trainer.OffPolicyTrainer` and {class}`~tianshou.trainer.OffPolicyTrainerParams`)
### Training Loop
The training is executed via `algorithm.run_training()`, which takes a trainer parameter object.
You can alternatively implement custom training loops (or even your own trainer class) for maximum flexibility.
## Additional Resources
- **high-Level API examples**: See `examples/` directory (scripts ending in `_hl.py`)
- **procedural API examples**: See `examples/` directory (scripts without suffix)
================================================
FILE: docs/01_user_guide/02_core_abstractions.md
================================================
# Core Abstractions
Tianshou's architecture is built around a number of key abstractions that work together to provide a modular and flexible reinforcement learning framework.
This document describes the conceptual foundation and functionality of each abstraction, helping you understand how they interact to enable RL agent training.
Knowing these abstractions is primarily relevant when using the procedural API – and particularly when implementing one's own learning algoriithms.
## Algorithm
The **{class}`~tianshou.algorithm.algorithm_base.Algorithm`** is the central abstraction representing the core of a reinforcement learning method (such as DQN, PPO, or SAC).
It implements the key steps within the {ref}`learning process <structuring-the-process>`, containing a {ref}`policy` and defining how to update it from experience data.
Since an Algorithm contains neural networks and manages their training, the class inherits from `torch.nn.Module`.
### Core Responsibilities
An Algorithm implements the details of an {ref}`update step <structuring-the-process>`:
1. **preprocessing**: Before the actual update begins, the algorithm prepares the training data.
This includes computing derived quantities that depend on temporal sequences, such as n-step returns, GAE advantages, or terminal state handling.
The {meth}`~tianshou.algorithm.algorithm_base.Algorithm._preprocess_batch` method handles this phase, often leveraging static methods like
{meth}`~tianshou.algorithm.algorithm_base.Algorithm.compute_nstep_return` and
{meth}`~tianshou.algorithm.algorithm_base.Algorithm.compute_episodic_return` to
efficiently compute returns using the buffer's temporal structure.
2. **network update**: The algorithm performs the actual neural network updates based on its specific learning method.
Each algorithm implements its own {meth}`~tianshou.algorithm.algorithm_base.Algorithm._update_with_batch` logic that defines how to update
the policy networks using the preprocessed batch data.
3. **postprocessing**: After the update, the algorithm may perform cleanup operations, such as updating prioritized replay buffer weights or other
algorithm-specific bookkeeping.
### Learning Orchestration
The Algorithm orchestrates the {ref}`update step <structuring-the-process>` through its
{meth}`~tianshou.algorithm.algorithm_base.Algorithm.update` method, which ensures these three phases execute in proper sequence.
It also manages optimizer state and learning rate schedulers, making them available for state persistence through
{meth}`~tianshou.algorithm.algorithm_base.Algorithm.state_dict` and
{meth}`~tianshou.algorithm.algorithm_base.Algorithm.load_state_dict` methods.
Each algorithm type (on-policy, off-policy, offline) creates its appropriate trainer through the
{meth}`~tianshou.algorithm.algorithm_base.Algorithm.create_trainer` method,
establishing the connection between the learning logic and the training loop.
(policy)=
## Policy
The **{class}`~tianshou.algorithm.algorithm_base.Policy`** represents the agent's decision-making component, i.e. the mapping from observations to actions.
While the Algorithm defines how to learn, the Policy defines what is learned and how to act.
Like Algorithm, the class inherits from `torch.nn.Module`.
### States of Operation
A Policy operates in two main modes:
- **training mode**: During training, the policy may employ exploration strategies, sample from action distributions, or add noise to encourage discovery.
Training mode is further divided into:
- *collecting state*: When gathering experience from environment interaction
- *updating state*: When performing network updates during learning
- **testing/inference mode**: During evaluation, the policy typically acts deterministically or uses the mode of predicted distributions to showcase
learned behavior without exploration.
The flag `is_within_training_step` controls the collection strategy, distinguishing between training and inference behavior.
### Key Methods
The Policy provides several essential methods:
- **{meth}`~tianshou.algorithm.algorithm_base.Policy.forward`**:
The core computation method that processes batched observations to produce action distributions or Q-values.
It takes a batch of environment data and optional hidden state (for recurrent policies),
returning a batch containing at minimum the "act" key,
and potentially "state" (hidden state) and "policy" (intermediate results to be stored in the buffer).
- **{meth}`~tianshou.algorithm.algorithm_base.Policy.compute_action`**:
A convenient method for inference that takes a single observation and returns a concrete action suitable for the environment.
This method internally calls `forward` with proper batching and unbatching.
- **{meth}`~tianshou.algorithm.algorithm_base.Policy.map_action`**: Transforms the raw neural network output to the environment's action space format, handling any necessary scaling or discretization.
The separation between `forward` (which works with batches) and
{meth}`~tianshou.algorithm.algorithm_base.Policy.compute_action` (which works with single observations) provides efficiency
during training and convenience during inference.
## Collector
The class **{class}`~tianshou.data.Collector`** bridges the gap between the policy and the environment(s),
managing the process of gathering experience data.
It enables efficient interaction with both single environments and vectorized environments (multiple parallel environments).
### Data Collection
The Collector's primary method, {meth}`~tianshou.data.Collector.collect`, orchestrates the environment interaction loop. It can collect either:
- a specified number of steps (`n_step`): useful for maintaining consistent training batch sizes
- a specified number of episodes (`n_episode`): useful for evaluation or when episode-level statistics are important
During collection, the Collector ...
1. obtains observations from the environment(s),
2. calls the policy to compute actions,
3. steps the environment(s) with these actions,
4. stores the resulting transitions (observation, action, reward, next observation, termination flags, and info) in the replay buffer,
5. manages episode boundaries and reset logic,
6. collects statistics such as episode returns, lengths, and collection speed.
### Hooks and Extensibility
The Collector supports customization through hooks that can be triggered at different points in the collection process:
- **step hooks**: called after each environment step
- **episode done hooks**: called when episodes complete
These hooks enable custom logging, curriculum learning, or other dynamic behaviors during data collection.
### Vectorized Environments
The Collector seamlessly handles vectorized environments, where multiple environment instances run in parallel.
This significantly speeds up data collection while maintaining correct episode boundaries and statistics for each environment instance.
## Trainer
The **{class}`~tianshou.trainer.Trainer`** orchestrates the complete training loop, coordinating data collection, policy updates, and evaluation.
It provides the high-level control flow that brings all components together.
### Trainer Types
Tianshou provides three main trainer types, each suited to different algorithm families:
- **{class}`~tianshou.trainer.OnPolicyTrainer`**: for algorithms that must learn from freshly collected data (e.g., PPO, A2C).
After each collection phase, the buffer is used for updates and thereafter is cleared.
- **{class}`~tianshou.trainer.OffPolicyTrainer`**: for algorithms that can learn from any past experience (e.g., DQN, SAC, DDPG).
Data accumulates in the replay buffer over time, and updates sample from this growing pool of experience.
- **{class}`~tianshou.trainer.OfflineTrainer`**: for algorithms that learn exclusively from a fixed dataset without any environment interaction (e.g., BCQ, CQL).
### Training Loop Structure
The training process is organized into epochs, where each epoch consists of:
1. **data collection**: The trainer uses the train collector to gather experience according to its algorithm type's needs
2. **policy update**: The algorithm performs one or more update steps using the collected data
3. **evaluation**: Periodically, the trainer uses the test collector to evaluate the current policy's performance
4. **logging**: Statistics from collection, updates, and evaluation are logged
5. **checkpointing**: The best policy (according to a scoring function) is saved
The trainer handles the detailed choreography of these steps, including determining when to collect more data,
how many update steps to perform, when to evaluate, and when to stop training (based on maximum epochs, timesteps, or early stopping criteria).
### Configuration
Trainers are configured through parameter dataclasses
({class}`~tianshou.trainer.OnPolicyTrainerParams`, {class}`~tianshou.trainer.OffPolicyTrainerParams`, {class}`~tianshou.trainer.OfflineTrainerParams`)
that specify in particular:
- training duration (number of epochs, steps per epoch)
- collectors for training and testing
- update frequency and batch size
- evaluation frequency
- logging and checkpointing settings
- early stopping criteria
## Batch
The class **{class}`~tianshou.data.Batch`** is Tianshou's flexible data structure for passing information between components.
It serves as the lingua franca of the framework, carrying everything from raw environment observations to computed returns and policy outputs.
### Design Philosophy
Batch is designed to be ...
- **flexible**: can contain any key-value pairs, with nested structures supported,
- **numpy/torch-compatible**: automatically converts lists to arrays and seamlessly works with both NumPy arrays and PyTorch tensors,
- **sliceable**: supports indexing and slicing operations that work across all contained data,
- **composable**: can be concatenated, stacked, and split to support batching operations.
### Type Safety with BatchProtocol
While `Batch` provides a flexible, dictionary-like structure for holding arbitrary data, this flexibility can make it challenging to statically type-check which attributes are present in a batch at any given point in the code. To address this, Tianshou uses **{class}`~tianshou.data.batch.BatchProtocol`** and derived protocols to specify the expected attributes while keeping the actual runtime type as `Batch`.
BatchProtocol is a Python `Protocol` (from `typing.Protocol`) that defines the interface of a Batch object, specifying which operations and attributes should be available. More importantly, Tianshou provides a rich set of derived protocols in {mod}`tianshou.data.types` that describe batches with specific sets of attributes commonly used throughout the framework:
- **{class}`~tianshou.data.types.ObsBatchProtocol`**: Contains `obs` and `info` - the minimal batch for policy forward passes
- **{class}`~tianshou.data.types.RolloutBatchProtocol`**: Adds `obs_next`, `act`, `rew`, `terminated`, and `truncated` - typical data from replay buffer sampling
- **{class}`~tianshou.data.types.BatchWithReturnsProtocol`**: Extends RolloutBatchProtocol with `returns` computed from rewards
- **{class}`~tianshou.data.types.BatchWithAdvantagesProtocol`**: Includes `adv` (advantages) and `v_s` (value estimates) for policy gradient methods
- **{class}`~tianshou.data.types.ActStateBatchProtocol`**: Contains `act` and `state` for policy outputs, especially with RNN support
- **{class}`~tianshou.data.types.ModelOutputBatchProtocol`**: Adds `logits` to action and state information
- **{class}`~tianshou.data.types.DistBatchProtocol`**: Contains action distributions (`dist`) for stochastic policies
- **{class}`~tianshou.data.types.PrioBatchProtocol`**: Includes `weight` for prioritized experience replay
These protocols serve as type hints in function signatures throughout Tianshou, making it explicit what attributes are expected and available. For example, a policy's `forward` method might accept an `ObsBatchProtocol` and return an `ActStateBatchProtocol`, clearly documenting the data contract. Despite these type annotations, the actual objects remain flexible `Batch` instances at runtime, preserving Tianshou's dynamic nature while improving code clarity and IDE support.
### Common Use Cases
Batches flow through the system carrying different types of information:
1. **environment data**: observations, rewards, done flags, and info from environment steps
2. **policy outputs**: actions, hidden states, and intermediate computations
3. **training data**: returns, advantages, and other computed quantities needed for learning
4. **sampling results**: batches sampled from the replay buffer for training
### Operations
Key operations on batches include:
- **attribute access**: dot notation (`batch.obs`) or dictionary-style access (`batch['obs']`)
- **slicing**: extract subsets with standard indexing (`batch[0:10]`, `batch[[1,3,5]]`)
- **stacking**: combine multiple batches along a new dimension
- **type conversion**: convert between NumPy and PyTorch with `to_numpy()` and `to_torch()`
- **null handling**: detect and remove null values with `hasnull()`, `isnull()`, and `dropnull()`
The first dimension of all data in a Batch represents the batch size, enabling vectorized operations.
## Buffer
A **buffer** (i.e. class {class}`~tianshou.data.buffer.ReplayBuffer` and its variants) manages the storage and retrieval of experience data.
It acts as the memory of the learning system, preserving the temporal structure of episodes while providing efficient access patterns.
### Storage Structure
Buffers store data in a circular queue fashion with a fixed maximum size. When the buffer fills, new data overwrites the oldest stored experiences.
All data is stored within a single underlying Batch object, with the buffer managing:
- **pointer tracking**: current insertion position
- **episode boundaries**: which transitions belong to which episodes
- **temporal relationships**: the sequential order of transitions
### Reserved Keys
Buffers use a standard set of keys for storing transitions:
- `obs`: Observation at time t
- `act`: Action taken at time t
- `rew`: Reward received at time t
- `terminated`: True if the episode ended naturally at time t
- `truncated`: True if the episode was cut off at time t (e.g., time limit)
- `done`: Automatically inferred as `terminated or truncated`
- `obs_next`: Observation at time t+1
- `info`: Additional information from the environment
- `policy`: Intermediate policy computations to be stored
### Core Operations
**adding data**: The {meth}`~tianshou.data.buffer.buffer_base.ReplayBuffer.add` method stores new transitions,
automatically handling episode boundaries and computing episode statistics (return, length)
when episodes complete.
**sampling**: The {meth}`~tianshou.data.buffer.buffer_base.ReplayBuffer.sample` method retrieves batches of experiences for training,
returning both the sampled batch and the corresponding indices.
The sample size can be specified, or set to 0 to retrieve all available data.
**temporal navigation**: The {meth}`~tianshou.data.buffer.buffer_base.ReplayBuffer.prev` and {meth}`~tianshou.data.buffer.ReplayBuffer.next`
methods enable traversal along the temporal sequence, respecting episode boundaries.
This is essential for computing n-step returns and other time-dependent quantities.
**persistence**: Buffers support saving and loading via pickle or HDF5 format, enabling dataset collection and offline learning.
### Buffer Variants
Tianshou provides specialized buffer types:
- **{class}`~tianshou.data.buffer.buffer_base.ReplayBuffer`**: the standard buffer for single environments
- **{class}`~tianshou.data.buffer.vecbuf.VectorReplayBuffer`**: manages separate sub-buffers for multiple parallel environments while maintaining chronological order
- **{class}`~tianshou.data.buffer.prio.PrioritizedReplayBuffer`**: samples transitions based on their TD-error or other priority metrics, using an efficient segment tree implementation
### Advanced Features
Buffers support sophisticated use cases:
- **frame stacking**: automatically stacks consecutive observations (useful for RNN inputs or Atari)
- **memory optimization**: option to skip storing next observations (useful for Atari where they can be inferred)
- **multi-modal observations**: handle observations with multiple components (e.g., image + vector)
## Logger
The **{class}`~tianshou.utils.logger.logger_base.BaseLogger`** abstraction provides a unified interface for recording and tracking training progress, metrics, and statistics.
It decouples the training loop from the specifics of where and how data is logged.
### Purpose
Loggers serve several essential functions:
- **progress tracking**: record timesteps, episodes, and epochs as training progresses
- **metric collection**: store performance indicators like rewards, losses, and success rates
- **experiment organization**: manage different data scopes (training, testing, updating)
- **reproducibility**: save training curves and hyperparameters for later analysis
### Logging Scopes
The framework organizes logged data into distinct scopes:
- **train data**: metrics from the training collector (episode returns, steps, collection speed)
- **test data**: evaluation metrics from the test collector
- **update data**: learning statistics from the algorithm (losses, gradients, learning rates)
- **info data**: additional custom metrics or metadata
Each scope has a corresponding log method (`log_train_data`, `log_test_data`, `log_update_data`, `log_info_data`) that the trainer calls at appropriate times.
### Implementations
Tianshou provides several logger implementations:
- **{class}`~tianshou.utils.logger.tensorboard.TensorboardLogger`**: writes to TensorBoard format for visualization with TensorBoard
- **{class}`~tianshou.utils.logger.wandb.WandbLogger`**: integrates with Weights & Biases for cloud-based experiment tracking
All implementations inherit from {class}`~tianshou.utils.logger.logger_base.BaseLogger` and share a common interface,
making it easy to switch between logging backends or use multiple loggers simultaneously.
### Data Preparation
Before writing, loggers prepare data through the `prepare_dict_for_logging` method, which can filter, transform, or aggregate metrics.
The `write` method then persists the prepared data to the logging backend with an associated step count.
## How They Work Together
These seven abstractions collaborate to enable reinforcement learning:
1. The **Trainer** initializes and orchestrates the training process.
2. The **Collector** uses the **Policy** to gather experience from environments.
3. Collected transitions are stored in the **Buffer** extracted as **Batches**.
4. The **Algorithm** samples from the **Buffer**, preprocesses the data, and updates the **Policy**.
5. The **Logger** records metrics throughout the process.
6. The cycle repeats until training completes.
This modular design allows each component to focus on its specific responsibility while maintaining clean interfaces.
You can customize individual components (e.g., implementing a new Algorithm or Buffer) without affecting the others,
making Tianshou both powerful and flexible.
================================================
FILE: docs/01_user_guide/index.rst
================================================
User Guide
==========
The user guide provides an introduction to core concepts, establishes the glossary of terms,
introduces Tianshou's dual API architecture and provides an overview of important abstractions.
================================================
FILE: docs/02_deep_dives/0_intro.md
================================================
# Deep Dives
Our deep dives are a collection of executable tutorials on some of the internal representations used by Tianshou.
Provided as notebooks, you can run them directly in Colab or download them to run them locally.
================================================
FILE: docs/02_deep_dives/L1_Batch.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Batch: Tianshou's Core Data Structure\n",
"\n",
"The `Batch` class is Tianshou's fundamental data structure for efficiently storing and manipulating heterogeneous data in reinforcement learning. This tutorial provides comprehensive guidance on understanding its conceptual foundations, operational behavior, and best practices.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pickle\n",
"from typing import cast\n",
"\n",
"import numpy as np\n",
"import torch\n",
"from torch.distributions import Categorical, Normal\n",
"\n",
"from tianshou.data import Batch\n",
"from tianshou.data.types import ActBatchProtocol, ObsBatchProtocol, RolloutBatchProtocol"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1. Introduction: Why Batch?\n",
"\n",
"### The Challenge in Reinforcement Learning\n",
"\n",
"Reinforcement learning algorithms face a fundamental data management challenge:\n",
"\n",
"1. **Diverse Data Requirements**: Different RL algorithms need different data fields:\n",
" - Basic algorithms: `state`, `action`, `reward`, `done`, `next_state`\n",
" - Actor-Critic: additionally `advantages`, `returns`, `values`\n",
" - Policy Gradient: additionally `log_probs`, `old_log_probs`\n",
" - Off-policy: additionally `priority_weights`\n",
"\n",
"2. **Heterogeneous Observation Spaces**: Environments return diverse observation types:\n",
" - Simple: vectors (`np.array([1.0, 2.0, 3.0])`)\n",
" - Complex: images (`np.array(shape=(84, 84, 3))`)\n",
" - Hybrid: dictionaries combining multiple modalities\n",
" ```python\n",
" obs = {\n",
" 'camera': np.array(shape=(64, 64, 3)),\n",
" 'velocity': np.array([1.2, 0.5]),\n",
" 'inventory': np.array([5, 2, 0])\n",
" }\n",
" ```\n",
"\n",
"3. **Data Flow Across Components**: Data must flow seamlessly through:\n",
" - Collectors (gathering experience from environments)\n",
" - Replay Buffers (storing and sampling transitions)\n",
" - Policies and Algorithms (learning and inference)\n",
"\n",
"### Why Not Alternatives?\n",
"\n",
"#### Plain Dictionaries\n",
"Dictionaries lack essential features\n",
"```python\n",
"data = {'obs': np.array([1, 2]), 'reward': np.array([1.0, 2.0])}\n",
"```\n",
"\n",
"They would work in principle but has no shape/length semantics, no indexing, and no type safety.\n",
"\n",
"#### TensorDict\n",
"While `TensorDict` (used in `pytorch-rl`) is a powerful alternative:\n",
"- **Batch supports arbitrary objects**, not just tensors (useful for object-dtype arrays, custom types)\n",
"- **Batch has better type checking** via `BatchProtocol` (enables IDE autocompletion)\n",
"- **Batch preceded TensorDict** and provides a stable foundation for Tianshou\n",
"- **TensorDict isn't part of core PyTorch** (external dependency)\n",
"\n",
"### What is Batch?\n",
"\n",
"**Batch = Dictionary + Array hybrid with RL-specific features**\n",
"\n",
"Key capabilities:\n",
"- **Dict-like**: Key-value storage with attribute access (`batch.obs`, `batch.reward`)\n",
"- **Array-like**: Shape, indexing, slicing (`batch[0]`, `batch[:10]`, `batch.shape`)\n",
"- **Hierarchical**: Nested structures for complex data\n",
"- **Type-safe**: Protocol-based typing for IDE support\n",
"- **RL-aware**: Special handling for distributions, missing values, heterogeneous aggregation"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2. Core Concepts\n",
"\n",
"### Hierarchical Named Tensors\n",
"\n",
"Batch stores **hierarchical named tensors** - collections of tensors whose identifiers form a structured hierarchy. Consider tensors `[t1, t2, t3, t4]` with names `[name1, name2, name3, name4]`, where `name1` and `name2` are under namespace `name0`. The fully qualified name of `t1` is `name0.name1`.\n",
"\n",
"### Tree Structure Visualization\n",
"\n",
"The structure can be visualized as a tree with:\n",
"- **Root**: The Batch object itself\n",
"- **Internal nodes**: Keys (names)\n",
"- **Leaf nodes**: Values (scalars, arrays, tensors)\n",
"\n",
"```mermaid\n",
"graph TD\n",
" root[\"Batch (root)\"]\n",
" root --> obs[\"obs\"]\n",
" root --> act[\"act\"]\n",
" root --> rew[\"rew\"]\n",
" obs --> camera[\"camera\"]\n",
" obs --> sensory[\"sensory\"]\n",
" camera --> cam_data[\"np.array(3,3)\"]\n",
" sensory --> sens_data[\"np.array(5,)\"]\n",
" act --> act_data[\"np.array(2,)\"]\n",
" rew --> rew_data[\"3.66\"]\n",
" \n",
" style root fill:#e1f5ff\n",
" style obs fill:#fff4e1\n",
" style act fill:#fff4e1\n",
" style rew fill:#fff4e1\n",
" style camera fill:#ffe1f5\n",
" style sensory fill:#ffe1f5\n",
" style cam_data fill:#e8f5e1\n",
" style sens_data fill:#e8f5e1\n",
" style act_data fill:#e8f5e1\n",
" style rew_data fill:#e8f5e1\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Example: hierarchical structure\n",
"data = {\n",
" \"action\": np.array([1.0, 2.0, 3.0]),\n",
" \"reward\": 3.66,\n",
" \"obs\": {\n",
" \"camera\": np.zeros((3, 3)),\n",
" \"sensory\": np.ones(5),\n",
" },\n",
"}\n",
"\n",
"batch = Batch(data)\n",
"print(batch)\n",
"print(\"\\nAccessing nested values:\")\n",
"print(f\"batch.obs.camera.shape = {batch.obs.camera.shape}\")\n",
"print(f\"batch.obs.sensory = {batch.obs.sensory}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Data Flow in RL Pipeline\n",
"\n",
"Batch facilitates data flow throughout the RL pipeline:\n",
"\n",
"```mermaid\n",
"graph LR\n",
" A[Collector] -->|ActBatchProtocol| B[Environment]\n",
" B[Environment + Action] -->|RolloutBatchProtocol| C[Replay Buffer]\n",
" C -->|RolloutBatchProtocol| D[Policy]\n",
" D -->|ActBatchProtocol| A\n",
" D -->|BatchWithAdvantages| E[Algorithm/Trainer]\n",
" E --> D\n",
" \n",
" style A fill:#e1f5ff\n",
" style B fill:#fff4e1\n",
" style C fill:#ffe1f5\n",
" style D fill:#e8f5e1\n",
" style E fill:#f5e1e1\n",
"```\n",
"\n",
"Each arrow represents a specific `BatchProtocol` that defines what fields are expected at that stage."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3. Basic Operations\n",
"\n",
"### 3.1 Construction\n",
"\n",
"Batch objects can be constructed in several ways:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# From keyword arguments\n",
"batch1 = Batch(a=4, b=[5, 5], c=\"hello\")\n",
"print(\"From kwargs:\", batch1)\n",
"\n",
"# From dictionary\n",
"batch2 = Batch({\"a\": 4, \"b\": [5, 5], \"c\": \"hello\"})\n",
"print(\"\\nFrom dict:\", batch2)\n",
"\n",
"# From list of dictionaries (automatically stacked)\n",
"batch3 = Batch([{\"a\": 1, \"b\": 2}, {\"a\": 3, \"b\": 4}])\n",
"print(\"\\nFrom list of dicts:\", batch3)\n",
"\n",
"# Nested batch\n",
"batch4 = Batch(obs=Batch(x=1, y=2), act=5)\n",
"print(\"\\nNested:\", batch4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3.2 Content Rules\n",
"\n",
"Understanding what Batch can store and how it converts data:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Keys must be strings\n",
"batch = Batch()\n",
"batch.key1 = \"value\"\n",
"batch.key2 = np.array([1, 2, 3])\n",
"print(\"Keys:\", list(batch.keys()))\n",
"\n",
"# Automatic conversions\n",
"demo = Batch(\n",
" scalar_int=5, # → np.array(5)\n",
" scalar_float=3.14, # → np.array(3.14)\n",
" list_nums=[1, 2, 3], # → np.array([1, 2, 3])\n",
" list_mixed=[1, \"hello\", None], # → np.array([1, \"hello\", None], dtype=object)\n",
" dict_val={\"x\": 1, \"y\": 2}, # → Batch(x=1, y=2)\n",
")\n",
"\n",
"print(\"\\nAutomatic conversions:\")\n",
"print(f\"scalar_int type: {type(demo.scalar_int)}, value: {demo.scalar_int}\")\n",
"print(f\"list_nums type: {type(demo.list_nums)}, dtype: {demo.list_nums.dtype}\")\n",
"print(f\"list_mixed dtype: {demo.list_mixed.dtype}\")\n",
"print(f\"dict_val type: {type(demo.dict_val)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Important conversions:**\n",
"- Lists of numbers → NumPy arrays\n",
"- Lists with mixed types → Object-dtype arrays\n",
"- Dictionaries → Batch objects (recursively)\n",
"- Scalars → NumPy scalars"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3.3 Access Patterns\n",
"\n",
"**Important: Understanding Iteration**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"batch = Batch(a=[1, 2, 3], b=[4, 5, 6])\n",
"\n",
"# Attribute vs dictionary access (equivalent)\n",
"print(\"Attribute access:\", batch.a)\n",
"print(\"Dict access:\", batch[\"a\"])\n",
"\n",
"# Getting keys\n",
"print(\"\\nKeys:\", list(batch.keys()))\n",
"\n",
"# Gotcha: Iteration is array like, not over keys\n",
"print(\"\\nIteration behavior:\")\n",
"print(\"for x in batch iterates over batch[0], batch[1], ..., NOT keys!\")\n",
"for i, item in enumerate(batch):\n",
" print(f\"batch[{i}] = {item}\")\n",
"\n",
"# This is different from dict behavior!\n",
"regular_dict = {\"a\": [1, 2, 3], \"b\": [4, 5, 6]}\n",
"print(\"\\nCompare with dict iteration (iterates over keys):\")\n",
"for key in regular_dict:\n",
" print(f\"key = {key}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": ""
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3.4 Indexing & Slicing\n",
"\n",
"Batch supports NumPy-like indexing and slicing:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"batch = Batch(a=np.array([[0.0, 2.0], [1.0, 3.0]]), b=[[5.0, -5.0], [1.0, -2.0]])\n",
"\n",
"print(\"Original batch shape:\", batch.shape)\n",
"print(\"Original batch length:\", len(batch))\n",
"\n",
"# Single index\n",
"print(\"\\nbatch[0]:\")\n",
"print(batch[0])\n",
"\n",
"# Slicing\n",
"print(\"\\nbatch[:1]:\")\n",
"print(batch[:1])\n",
"\n",
"# Advanced indexing\n",
"print(\"\\nbatch[[0, 1]]:\")\n",
"print(batch[[0, 1]])\n",
"\n",
"# Multi-dimensional indexing\n",
"print(\"\\nbatch[:, 0] (first column of all arrays):\")\n",
"print(batch[:, 0])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Broadcasting and in-place operations\n",
"batch[:, 1] += 10\n",
"print(\"After batch[:, 1] += 10:\")\n",
"print(batch)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3.5 Stack, Concatenate, and Split\n",
"\n",
"Combining and splitting batches:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": "# Stack: adds a new dimension\nbatch1 = Batch(a=np.array([1, 2]), b=np.array([5, 6]))\nbatch2 = Batch(a=np.array([3, 4]), b=np.array([7, 8]))\n\nstacked = Batch.stack([batch1, batch2])\nprint(\"Stacked:\")\nprint(stacked)\nprint(f\"Shape: {stacked.shape}\")\n\n# Concatenate: extends along existing dimension\nconcatenated = Batch.cat([batch1, batch2])\nprint(\"\\nConcatenated:\")\nprint(concatenated)\nprint(f\"Shape: {concatenated.shape}\")"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Split\n",
"batch = Batch(a=np.arange(10), b=np.arange(10, 20))\n",
"splits = list(batch.split(size=3, shuffle=False))\n",
"print(f\"Split into {len(splits)} batches:\")\n",
"for i, split in enumerate(splits):\n",
" print(f\"Split {i}: a={split.a}, length={len(split)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3.6 Data Type Conversion\n",
"\n",
"Converting between NumPy and PyTorch:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Create batch with NumPy arrays\n",
"batch = Batch(a=np.zeros((3, 4)), b=np.ones(5))\n",
"print(\"Original (NumPy):\")\n",
"print(f\"batch.a type: {type(batch.a)}\")\n",
"\n",
"# Convert to PyTorch (in-place)\n",
"batch.to_torch_(dtype=torch.float32, device=\"cpu\")\n",
"print(\"\\nAfter to_torch_():\")\n",
"print(f\"batch.a type: {type(batch.a)}\")\n",
"print(f\"batch.a dtype: {batch.a.dtype}\")\n",
"\n",
"# Convert back to NumPy (in-place)\n",
"batch.to_numpy_()\n",
"print(\"\\nAfter to_numpy_():\")\n",
"print(f\"batch.a type: {type(batch.a)}\")\n",
"\n",
"# Non-in-place versions return a new batch\n",
"batch_torch = batch.to_torch()\n",
"print(\"\\nOriginal batch unchanged:\", type(batch.a))\n",
"print(\"New batch:\", type(batch_torch.a))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4. Type Safety with Protocols\n",
"\n",
"### Why Protocols?\n",
"\n",
"Batch needs to be **flexible** (not fixed fields like dataclasses) but we still want **type safety** and **IDE autocompletion**. Protocols provide the best of both worlds:\n",
"\n",
"- **Runtime flexibility**: Add any fields dynamically\n",
"- **Static type checking**: Type checkers (mypy, pyright) verify correct usage\n",
"- **IDE support**: Autocompletion for expected fields\n",
"\n",
"### What is BatchProtocol?\n",
"\n",
"A `Protocol` defines an interface without implementation. Think of it as a contract: \"any object with these fields is valid.\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Creating a typed batch using cast\n",
"# This enables IDE autocompletion and type checking\n",
"\n",
"# ActBatchProtocol: just needs 'act' field\n",
"act_batch = cast(ActBatchProtocol, Batch(act=np.array([1, 2, 3])))\n",
"print(\"ActBatchProtocol:\", act_batch.act)\n",
"\n",
"# ObsBatchProtocol: needs 'obs' and 'info' fields\n",
"obs_batch = cast(\n",
" ObsBatchProtocol,\n",
" Batch(obs=np.array([[1.0, 2.0], [3.0, 4.0]]), info=np.array([{}, {}], dtype=object)),\n",
")\n",
"print(\"\\nObsBatchProtocol:\", obs_batch.obs)\n",
"\n",
"# RolloutBatchProtocol: needs obs, obs_next, act, rew, terminated, truncated\n",
"rollout_batch = cast(\n",
" RolloutBatchProtocol,\n",
" Batch(\n",
" obs=np.array([[1.0, 2.0], [3.0, 4.0]]),\n",
" obs_next=np.array([[2.0, 3.0], [4.0, 5.0]]),\n",
" act=np.array([0, 1]),\n",
" rew=np.array([1.0, 2.0]),\n",
" terminated=np.array([False, True]),\n",
" truncated=np.array([False, False]),\n",
" info=np.array([{}, {}], dtype=object),\n",
" ),\n",
")\n",
"print(\"\\nRolloutBatchProtocol reward:\", rollout_batch.rew)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Protocol Hierarchy\n",
"\n",
"Tianshou defines a hierarchy of protocols for different use cases:\n",
"\n",
"```mermaid\n",
"graph TD\n",
" BP[BatchProtocol<br/>Base protocol] --> OBP[ObsBatchProtocol<br/>obs, info]\n",
" BP --> ABP[ActBatchProtocol<br/>act]\n",
" ABP --> ASBP[ActStateBatchProtocol<br/>act, state]\n",
" OBP --> RBP[RolloutBatchProtocol<br/>+obs_next, act, rew,<br/>terminated, truncated]\n",
" RBP --> BWRP[BatchWithReturnsProtocol<br/>+returns]\n",
" BWRP --> BWAP[BatchWithAdvantagesProtocol<br/>+adv, v_s]\n",
" ASBP --> MOBP[ModelOutputBatchProtocol<br/>+logits]\n",
" MOBP --> DBP[DistBatchProtocol<br/>+dist]\n",
" DBP --> DLPBP[DistLogProbBatchProtocol<br/>+log_prob]\n",
" BWAP --> LOPBP[LogpOldProtocol<br/>+logp_old]\n",
" \n",
" style BP fill:#e1f5ff\n",
" style OBP fill:#fff4e1\n",
" style ABP fill:#fff4e1\n",
" style RBP fill:#ffe1f5\n",
" style BWRP fill:#e8f5e1\n",
" style BWAP fill:#e8f5e1\n",
" style DBP fill:#f5e1e1\n",
" style LOPBP fill:#e1e1f5\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Using Protocols in Functions\n",
"\n",
"Protocols enable type-safe function signatures:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def process_observations(batch: ObsBatchProtocol) -> np.ndarray:\n",
" \"\"\"Function that expects observations.\n",
"\n",
" IDE will autocomplete batch.obs and batch.info!\n",
" Type checker will verify these fields exist.\n",
" \"\"\"\n",
" # IDE knows batch.obs exists\n",
" return batch.obs if isinstance(batch.obs, np.ndarray) else np.array(batch.obs)\n",
"\n",
"\n",
"def compute_advantage(batch: RolloutBatchProtocol) -> np.ndarray:\n",
" \"\"\"Function that expects rollout data.\n",
"\n",
" IDE will autocomplete batch.rew, batch.obs_next, etc.\n",
" \"\"\"\n",
" # Simplified advantage computation\n",
" return batch.rew # IDE knows this exists\n",
"\n",
"\n",
"# Example usage\n",
"obs_data = Batch(obs=np.array([1, 2, 3]), info=np.array([{}], dtype=object))\n",
"result = process_observations(obs_data)\n",
"print(\"Processed obs:\", result)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Key Protocol Types:**\n",
"\n",
"- `ActBatchProtocol`: Just actions (for simple policies)\n",
"- `ObsBatchProtocol`: Observations and info\n",
"- `RolloutBatchProtocol`: Complete transitions (obs, act, rew, done, obs_next)\n",
"- `BatchWithReturnsProtocol`: Rollouts + computed returns\n",
"- `BatchWithAdvantagesProtocol`: Returns + advantages and values\n",
"- `DistBatchProtocol`: Contains distribution objects\n",
"- `LogpOldProtocol`: For importance sampling (PPO, etc.)\n",
"\n",
"See `tianshou/data/types.py` for the complete list!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 5. Distribution Slicing\n",
"\n",
"### Why Special Handling?\n",
"\n",
"PyTorch `Distribution` objects need special slicing because they're not simple arrays. When you slice `batch[0:2]`, Tianshou needs to slice the underlying distribution parameters correctly.\n",
"\n",
"### Supported Distributions\n",
"\n",
"Tianshou supports slicing for:\n",
"- `Categorical`: Discrete distributions\n",
"- `Normal`: Continuous Gaussian distributions\n",
"- `Independent`: Wraps other distributions"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Categorical distribution\n",
"probs = torch.tensor([[0.3, 0.7], [0.4, 0.6], [0.5, 0.5]])\n",
"dist = Categorical(probs=probs)\n",
"batch = Batch(dist=dist, values=np.array([1, 2, 3]))\n",
"\n",
"print(\"Original batch length:\", len(batch))\n",
"print(\"Original dist probs shape:\", batch.dist.probs.shape)\n",
"\n",
"# Slicing automatically handles the distribution\n",
"sliced = batch[0:2]\n",
"print(\"\\nSliced batch length:\", len(sliced))\n",
"print(\"Sliced dist probs shape:\", sliced.dist.probs.shape)\n",
"print(\"Sliced values:\", sliced.values)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Normal distribution\n",
"loc = torch.tensor([0.0, 1.0, 2.0])\n",
"scale = torch.tensor([1.0, 1.0, 1.0])\n",
"normal_dist = Normal(loc=loc, scale=scale)\n",
"batch_normal = Batch(dist=normal_dist, actions=np.array([0.5, 1.5, 2.5]))\n",
"\n",
"print(\"Normal distribution batch:\")\n",
"print(f\"Original mean: {batch_normal.dist.mean}\")\n",
"\n",
"# Index a single element\n",
"single = batch_normal[1]\n",
"print(f\"\\nSingle element mean: {single.dist.mean}\")\n",
"print(f\"Single element action: {single.actions}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Converting to At Least 2D\n",
"\n",
"Sometimes you need to ensure distributions have a batch dimension:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from tianshou.data.batch import dist_to_atleast_2d\n",
"\n",
"# Scalar distribution (no batch dimension)\n",
"scalar_dist = Categorical(probs=torch.tensor([0.3, 0.7]))\n",
"print(\"Scalar dist batch_shape:\", scalar_dist.batch_shape)\n",
"\n",
"# Convert to have batch dimension\n",
"batched_dist = dist_to_atleast_2d(scalar_dist)\n",
"print(\"Batched dist batch_shape:\", batched_dist.batch_shape)\n",
"\n",
"# For entire batch\n",
"scalar_batch = Batch(a=1, b=2, dist=Categorical(probs=torch.ones(3)))\n",
"print(\"\\nBefore to_at_least_2d:\", scalar_batch.dist.batch_shape)\n",
"\n",
"batch_2d = scalar_batch.to_at_least_2d()\n",
"print(\"After to_at_least_2d:\", batch_2d.dist.batch_shape)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Use Cases\n",
"\n",
"Distribution slicing is used in:\n",
"- **Policy sampling**: When policies output distributions, slicing batches preserves distribution structure\n",
"- **Replay buffer sampling**: Distributions are stored and retrieved correctly\n",
"- **Advantage computation**: Computing log probabilities on subsets of data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 6. Advanced Topics\n",
"\n",
"### 6.1 Key Reservation\n",
"\n",
"Sometimes you know what keys you'll need but don't have values yet. Reserve keys using empty `Batch()` objects:\n",
"\n",
"```mermaid\n",
"graph TD\n",
" root[\"Batch\"]\n",
" root --> a[\"key1: np.array([1,2,3])\"]\n",
" root --> b[\"key2: Batch() (reserved)\"]\n",
" root --> c[\"key3\"]\n",
" c --> c1[\"subkey1: Batch() (reserved)\"]\n",
" c --> c2[\"subkey2: np.array([4,5])\"]\n",
" \n",
" style root fill:#e1f5ff\n",
" style a fill:#e8f5e1\n",
" style b fill:#ffcccc\n",
" style c fill:#fff4e1\n",
" style c1 fill:#ffcccc\n",
" style c2 fill:#e8f5e1\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Reserving keys\n",
"batch = Batch(\n",
" known_field=np.array([1, 2]),\n",
" future_field=Batch(), # Reserved for later\n",
")\n",
"print(\"Batch with reserved key:\")\n",
"print(batch)\n",
"\n",
"# Later, assign actual data\n",
"batch.future_field = np.array([3, 4])\n",
"print(\"\\nAfter assignment:\")\n",
"print(batch)\n",
"\n",
"# Nested reservation\n",
"batch2 = Batch(\n",
" obs=Batch(\n",
" camera=Batch(), # Reserved\n",
" lidar=np.zeros(10),\n",
" )\n",
")\n",
"print(\"\\nNested reservation:\")\n",
"print(batch2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 6.2 Length and Shape Semantics\n",
"\n",
"Understanding when `len()` works and what `shape` means:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Normal case: all tensors same length\n",
"batch1 = Batch(a=[1, 2, 3], b=np.array([4, 5, 6]))\n",
"print(\"Normal batch:\")\n",
"print(f\"len(batch1) = {len(batch1)}\")\n",
"print(f\"batch1.shape = {batch1.shape}\")\n",
"\n",
"# Scalars have no length\n",
"batch2 = Batch(a=5, b=10)\n",
"print(\"\\nScalar batch:\")\n",
"print(f\"batch2.shape = {batch2.shape}\")\n",
"try:\n",
" print(f\"len(batch2) = {len(batch2)}\")\n",
"except TypeError as e:\n",
" print(f\"len(batch2) raises TypeError: {e}\")\n",
"\n",
"# Mixed lengths: returns minimum\n",
"batch3 = Batch(a=[1, 2], b=[3, 4, 5])\n",
"print(\"\\nMixed length batch:\")\n",
"print(f\"len(batch3) = {len(batch3)} (minimum of 2 and 3)\")\n",
"\n",
"# Reserved keys are ignored\n",
"batch4 = Batch(a=[1, 2, 3], reserved=Batch())\n",
"print(\"\\nBatch with reserved key:\")\n",
"print(f\"len(batch4) = {len(batch4)} (reserved key ignored)\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 6.3 Empty Batches\n",
"\n",
"Understanding different meanings of \"empty\":"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 1. No keys at all\n",
"empty1 = Batch()\n",
"print(\"No keys:\")\n",
"print(f\"len(empty1.get_keys()) = {len(list(empty1.get_keys()))}\")\n",
"print(f\"len(empty1) = {len(empty1)}\")\n",
"\n",
"# 2. Has keys but they're all reserved\n",
"empty2 = Batch(a=Batch(), b=Batch())\n",
"print(\"\\nReserved keys only:\")\n",
"print(f\"len(empty2.get_keys()) = {len(list(empty2.get_keys()))}\")\n",
"print(f\"len(empty2) = {len(empty2)}\")\n",
"\n",
"# 3. Has data but length is 0\n",
"empty3 = Batch(a=np.array([]), b=np.array([]))\n",
"print(\"\\nZero-length arrays:\")\n",
"print(f\"len(empty3.get_keys()) = {len(list(empty3.get_keys()))}\")\n",
"print(f\"len(empty3) = {len(empty3)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Checking emptiness:**\n",
"- `len(batch.get_keys()) == 0`: No keys (completely empty)\n",
"- `len(batch) == 0`: No data elements (may have reserved keys)\n",
"\n",
"**The `.empty()` and `.empty_()` methods:**\n",
"These reset values to zeros/None, different from checking emptiness:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"batch = Batch(a=[1, 2, 3], b=[\"x\", \"y\", \"z\"])\n",
"print(\"Original:\", batch)\n",
"\n",
"# Empty specific index\n",
"batch[0] = Batch.empty(batch[0])\n",
"print(\"\\nAfter emptying index 0:\")\n",
"print(batch)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 6.4 Heterogeneous Aggregation\n",
"\n",
"Stacking/concatenating batches with different keys:\n",
"\n",
"```mermaid\n",
"graph LR\n",
" A[\"Batch(a=[1,2], c=5)\"] --> C[\"Batch.stack\"]\n",
" B[\"Batch(b=[3,4], c=6)\"] --> C\n",
" C --> D[\"Batch(a=[[1,2],[0,0]],<br/>b=[[0,0],[3,4]],<br/>c=[5,6])\"]\n",
" \n",
" style A fill:#e1f5ff\n",
" style B fill:#fff4e1\n",
" style C fill:#ffe1f5\n",
" style D fill:#e8f5e1\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Stack with different keys (missing keys padded with zeros)\n",
"batch_a = Batch(a=np.ones((2, 3)), shared=np.array([1, 2]))\n",
"batch_b = Batch(b=np.zeros((2, 4)), shared=np.array([3, 4]))\n",
"\n",
"stacked = Batch.stack([batch_a, batch_b])\n",
"print(\"Stacked batch:\")\n",
"print(f\"a.shape = {stacked.a.shape} (padded with zeros for batch_b)\")\n",
"print(f\"b.shape = {stacked.b.shape} (padded with zeros for batch_a)\")\n",
"print(f\"shared.shape = {stacked.shared.shape} (in both batches)\")\n",
"print(stacked)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 6.5 Missing Values\n",
"\n",
"Handling `None` and `NaN` values:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Batch with missing values\n",
"batch = Batch(a=[1, 2, None, 4], b=[5.0, np.nan, 7.0, 8.0], c=[[1, 2], [3, 4], [5, 6], [7, 8]])\n",
"\n",
"# Check for nulls\n",
"print(\"Has null?\", batch.hasnull())\n",
"\n",
"# Get null mask\n",
"null_mask = batch.isnull()\n",
"print(\"\\nNull mask:\")\n",
"print(f\"a: {null_mask.a}\")\n",
"print(f\"b: {null_mask.b}\")\n",
"\n",
"# Drop rows with any null\n",
"clean_batch = batch.dropnull()\n",
"print(\"\\nAfter dropnull() (keeps rows 0 and 3):\")\n",
"print(f\"Length: {len(clean_batch)}\")\n",
"print(f\"a: {clean_batch.a}\")\n",
"print(f\"b: {clean_batch.b}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 6.6 Value Transformations\n",
"\n",
"Applying functions to all values recursively:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"batch = Batch(a=np.array([1, 2, 3]), nested=Batch(b=np.array([4.0, 5.0]), c=np.array([6, 7, 8])))\n",
"\n",
"# Apply transformation (returns new batch)\n",
"doubled = batch.apply_values_transform(lambda x: x * 2)\n",
"print(\"Original batch a:\", batch.a)\n",
"print(\"Doubled batch a:\", doubled.a)\n",
"print(\"Doubled nested.b:\", doubled.nested.b)\n",
"\n",
"# In-place transformation\n",
"batch.apply_values_transform(lambda x: x + 10, inplace=True)\n",
"print(\"\\nAfter in-place +10:\")\n",
"print(\"a:\", batch.a)\n",
"print(\"nested.b:\", batch.nested.b)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 7. Surprising Behaviors & Gotchas\n",
"\n",
"### Iteration Does NOT Iterate Over Keys!\n",
"\n",
"**This is the most common source of confusion:**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"batch = Batch(a=[1, 2, 3], b=[4, 5, 6])\n",
"\n",
"print(\"WRONG: This doesn't iterate over keys!\")\n",
"for item in batch:\n",
" print(f\"item = {item}\") # Prints batch[0], batch[1], batch[2]\n",
"\n",
"print(\"\\nCORRECT: To iterate over keys:\")\n",
"for key in batch.keys():\n",
" print(f\"key = {key}\")\n",
"\n",
"print(\"\\nCORRECT: To iterate over key-value pairs:\")\n",
"for key, value in batch.items():\n",
" print(f\"{key} = {value}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Automatic Type Conversions\n",
"\n",
"Be aware of these automatic conversions:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Lists become arrays\n",
"batch = Batch(a=[1, 2, 3])\n",
"print(\"List → array:\", type(batch.a), batch.a.dtype)\n",
"\n",
"# Dicts become Batch\n",
"batch = Batch(a={\"x\": 1, \"y\": 2})\n",
"print(\"Dict → Batch:\", type(batch.a))\n",
"\n",
"# Scalars become numpy scalars\n",
"batch = Batch(a=5)\n",
"print(\"Scalar → np.ndarray:\", type(batch.a), batch.a)\n",
"\n",
"# Mixed types → object dtype\n",
"batch = Batch(a=[1, \"hello\", None])\n",
"print(\"Mixed → object:\", batch.a.dtype, batch.a)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Length Edge Cases"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# 1. Scalars have no length\n",
"batch_scalar = Batch(a=5, b=10)\n",
"try:\n",
" len(batch_scalar)\n",
"except TypeError as e:\n",
" print(f\"Scalar batch: {e}\")\n",
"\n",
"# 2. Empty nested batches ignored in len()\n",
"batch_empty_nested = Batch(a=[1, 2, 3], b=Batch())\n",
"print(f\"\\nWith empty nested: len = {len(batch_empty_nested)} (ignores b)\")\n",
"\n",
"# 3. Different lengths: returns minimum\n",
"batch_different = Batch(a=[1, 2], b=[1, 2, 3, 4])\n",
"print(f\"Different lengths: len = {len(batch_different)} (minimum)\")\n",
"\n",
"# 4. None values don't affect length\n",
"batch_none = Batch(a=[1, 2, 3], b=None)\n",
"print(f\"With None: len = {len(batch_none)} (None ignored)\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### String Keys Only"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Integer keys not allowed\n",
"try:\n",
" batch = Batch({1: \"value\", 2: \"other\"})\n",
"except AssertionError as e:\n",
" print(\"Integer keys not allowed:\", e)\n",
"\n",
"# String keys work\n",
"batch = Batch({\"key1\": \"value\", \"key2\": \"other\"})\n",
"print(\"\\nString keys work:\", list(batch.keys()))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Cat vs Stack Behavior\n",
"\n",
"Recent changes have made concatenation stricter about structure:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Stack pads missing keys with zeros\n",
"b1 = Batch(a=[1, 2])\n",
"b2 = Batch(b=[3, 4])\n",
"stacked = Batch.stack([b1, b2])\n",
"print(\"Stack (different keys):\")\n",
"print(f\" a: {stacked.a} (b2.a padded with 0)\")\n",
"print(f\" b: {stacked.b} (b1.b padded with 0)\")\n",
"\n",
"# Cat requires same structure now\n",
"b3 = Batch(a=[1, 2], b=[3, 4])\n",
"b4 = Batch(a=[5, 6], b=[7, 8])\n",
"concatenated = Batch.cat([b3, b4])\n",
"print(\"\\nCat (same keys):\")\n",
"print(f\" a: {concatenated.a}\")\n",
"print(f\" b: {concatenated.b}\")\n",
"\n",
"# Cat with different structures raises error\n",
"try:\n",
" Batch.cat([b1, b2]) # Different keys!\n",
"except ValueError:\n",
" print(\"\\nCat with different keys: ValueError raised\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 8. Best Practices\n",
"\n",
"### When to Use Batch\n",
"\n",
"**Good use cases:**\n",
"- Collecting environment data (transitions, episodes)\n",
"- Storing replay buffer data\n",
"- Passing data between components (collector → buffer → policy)\n",
"- Handling heterogeneous observations (dict spaces)\n",
"\n",
"**Consider alternatives:**\n",
"- Simple scalar tracking (use regular variables)\n",
"- Pure tensor operations (use PyTorch tensors directly)\n",
"- Deeply nested arbitrary structures (use dataclasses)\n",
"\n",
"### Structuring Your Batches\n",
"\n",
"**Use protocols for type safety:**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Good: Use protocols for clear interfaces\n",
"def train_step(batch: RolloutBatchProtocol) -> float:\n",
" \"\"\"IDE knows what fields exist.\"\"\"\n",
" loss = ((batch.rew - 0.5) ** 2).mean() # Type-safe\n",
" return float(loss)\n",
"\n",
"\n",
"# Create properly typed batch\n",
"train_batch = cast(\n",
" RolloutBatchProtocol,\n",
" Batch(\n",
" obs=np.random.randn(10, 4),\n",
" obs_next=np.random.randn(10, 4),\n",
" act=np.random.randint(0, 2, 10),\n",
" rew=np.random.randn(10),\n",
" terminated=np.zeros(10, dtype=bool),\n",
" truncated=np.zeros(10, dtype=bool),\n",
" info=np.array([{}] * 10, dtype=object),\n",
" ),\n",
")\n",
"\n",
"loss = train_step(train_batch)\n",
"print(f\"Loss: {loss:.4f}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Consistent key naming:**\n",
"- Follow Tianshou conventions: `obs`, `act`, `rew`, `terminated`, `truncated`\n",
"- Use descriptive names: `camera_obs` not `co`\n",
"- Avoid name collisions with Batch methods: don't use `keys`, `items`, `get`, etc.\n",
"\n",
"**When to nest vs flatten:**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Good: Nest related data\n",
"batch_nested = Batch(\n",
" obs=Batch(\n",
" camera=np.zeros((32, 64, 64, 3)), lidar=np.zeros((32, 100)), position=np.zeros((32, 3))\n",
" ),\n",
" act=np.zeros(32),\n",
")\n",
"print(\"Nested structure for related obs:\")\n",
"print(f\" Access: batch.obs.camera.shape = {batch_nested.obs.camera.shape}\")\n",
"\n",
"# Less good: Flat structure loses semantic grouping\n",
"batch_flat = Batch(\n",
" camera=np.zeros((32, 64, 64, 3)),\n",
" lidar=np.zeros((32, 100)),\n",
" position=np.zeros((32, 3)),\n",
" act=np.zeros(32),\n",
")\n",
"print(\"\\nFlat structure (works but less clear):\")\n",
"print(f\" Access: batch.camera.shape = {batch_flat.camera.shape}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Performance Tips\n",
"\n",
"**Use in-place operations:**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import time\n",
"\n",
"batch = Batch(a=np.random.randn(1000, 100))\n",
"\n",
"# Creates copy\n",
"start = time.time()\n",
"for _ in range(100):\n",
" _ = batch.to_torch()\n",
"time_copy = time.time() - start\n",
"\n",
"# In-place (faster)\n",
"start = time.time()\n",
"for _ in range(100):\n",
" batch.to_torch_()\n",
" batch.to_numpy_()\n",
"time_inplace = time.time() - start\n",
"\n",
"print(f\"Copy: {time_copy:.4f}s\")\n",
"print(f\"In-place: {time_inplace:.4f}s\")\n",
"print(f\"Speedup: {time_copy / time_inplace:.1f}x\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Be mindful of copies:**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"arr = np.array([1, 2, 3])\n",
"\n",
"# Default: creates reference (be careful!)\n",
"batch1 = Batch(a=arr)\n",
"batch1.a[0] = 999\n",
"print(f\"Original array modified: {arr}\") # Changed!\n",
"\n",
"# Explicit copy when needed\n",
"arr = np.array([1, 2, 3])\n",
"batch2 = Batch(a=arr, copy=True)\n",
"batch2.a[0] = 999\n",
"print(f\"Original array preserved: {arr}\") # Unchanged"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Avoid unnecessary conversions:**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Inefficient: multiple conversions\n",
"batch = Batch(a=np.random.randn(100, 10))\n",
"batch.to_torch_()\n",
"batch.to_numpy_() # Unnecessary if we just need NumPy\n",
"\n",
"# Efficient: convert once, use many times\n",
"batch = Batch(a=np.random.randn(100, 10))\n",
"batch.to_torch_() # Convert once\n",
"# ... do torch operations ...\n",
"# Keep as torch if that's what you need!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Common Patterns\n",
"\n",
"**Pattern 1: Building batches incrementally**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Collect data from multiple steps\n",
"step_data = []\n",
"for i in range(5):\n",
" step_data.append({\"obs\": np.random.randn(4), \"act\": i, \"rew\": np.random.randn()})\n",
"\n",
"# Convert to batch (automatically stacks)\n",
"episode_batch = Batch(step_data)\n",
"print(\"Episode batch shape:\", episode_batch.shape)\n",
"print(\"obs shape:\", episode_batch.obs.shape)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Pattern 2: Slicing for mini-batches**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Large batch\n",
"large_batch = Batch(obs=np.random.randn(100, 4), act=np.random.randint(0, 2, 100))\n",
"\n",
"# Split into mini-batches\n",
"batch_size = 32\n",
"for mini_batch in large_batch.split(batch_size, shuffle=True):\n",
" print(f\"Mini-batch size: {len(mini_batch)}\")\n",
" # Train on mini_batch...\n",
" break # Just show one iteration"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Pattern 3: Extending batches**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Start with some data\n",
"batch = Batch(obs=np.array([[1, 2], [3, 4]]), act=np.array([0, 1]))\n",
"print(\"Initial:\", len(batch))\n",
"\n",
"# Add more data\n",
"new_data = Batch(obs=np.array([[5, 6]]), act=np.array([1]))\n",
"batch.cat_(new_data)\n",
"print(\"After cat_:\", len(batch))\n",
"print(\"obs:\", batch.obs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 9. Summary\n",
"\n",
"### Key Takeaways\n",
"\n",
"1. **Batch = Dict + Array**: Combines key-value storage with array operations\n",
"2. **Hierarchical Structure**: Perfect for complex RL data (nested observations, etc.)\n",
"3. **Type Safety via Protocols**: Use `BatchProtocol` subclasses for IDE support and type checking\n",
"4. **Special RL Features**: Distribution slicing, heterogeneous aggregation, missing value handling\n",
"5. **Remember**: Iteration is over indices, NOT keys!\n",
"\n",
"### Quick Reference\n",
"\n",
"| Operation | Code | Notes |\n",
"|-----------|------|-------|\n",
"| Create | `Batch(a=1, b=[2, 3])` | Auto-converts types |\n",
"| Access | `batch.a` or `batch[\"a\"]` | Equivalent |\n",
"| Index | `batch[0]`, `batch[:10]` | Returns sliced Batch |\n",
"| Iterate indices | `for item in batch:` | Yields batch[0], batch[1], ... |\n",
"| Iterate keys | `for k in batch.keys():` | Like dict |\n",
"| Stack | `Batch.stack([b1, b2])` | Adds dimension |\n",
"| Concatenate | `Batch.cat([b1, b2])` | Extends dimension |\n",
"| Split | `batch.split(size=10)` | Returns iterator |\n",
"| To PyTorch | `batch.to_torch_()` | In-place |\n",
"| To NumPy | `batch.to_numpy_()` | In-place |\n",
"| Transform | `batch.apply_values_transform(fn)` | Recursive |\n",
"\n",
"### Next Steps\n",
"\n",
"- **Collector Deep Dive**: See how Batch flows through data collection\n",
"- **Buffer Deep Dive**: Understand how Batch is stored and sampled\n",
"- **Policy Guide**: Learn how policies work with BatchProtocol\n",
"- **API Reference**: Full details at [Batch API documentation](https://tianshou.org/en/stable/api/tianshou.data.html#tianshou.data.Batch)\n",
"\n",
"### Questions?\n",
"\n",
"- Check the [Tianshou GitHub discussions](https://github.com/thu-ml/tianshou/discussions)\n",
"- Review [issue tracker](https://github.com/thu-ml/tianshou/issues) for known gotchas\n",
"- Read the [source code](https://github.com/thu-ml/tianshou/blob/master/tianshou/data/batch.py) - it's well-documented!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Appendix: Serialization & Advanced Topics\n",
"\n",
"### Pickle Support"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Batch objects are picklable\n",
"original = Batch(obs=Batch(a=0.0, c=torch.Tensor([1.0, 2.0])), np=np.zeros([3, 4]))\n",
"\n",
"# Serialize and deserialize\n",
"serialized = pickle.dumps(original)\n",
"restored = pickle.loads(serialized)\n",
"\n",
"print(\"Original obs.a:\", original.obs.a)\n",
"print(\"Restored obs.a:\", restored.obs.a)\n",
"print(\"Equal:\", original == restored)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Advanced Indexing"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Multi-dimensional data\n",
"batch = Batch(a=np.random.randn(5, 3, 2))\n",
"print(\"Original shape:\", batch.a.shape)\n",
"\n",
"# Various indexing operations\n",
"print(\"batch[0].a.shape:\", batch[0].a.shape)\n",
"print(\"batch[:, 0].a.shape:\", batch[:, 0].a.shape)\n",
"print(\"batch[[0, 2, 4]].a.shape:\", batch[[0, 2, 4]].a.shape)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"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.11.0"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
================================================
FILE: docs/02_deep_dives/L2_Buffer.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Buffer: Experience Replay in Tianshou\n",
"\n",
"The replay buffer is a fundamental component in reinforcement learning, particularly for off-policy algorithms. Tianshou's buffer implementation extends beyond simple data storage to provide sophisticated trajectory tracking, efficient sampling, and seamless integration with the RL training pipeline.\n",
"\n",
"This tutorial provides comprehensive coverage of Tianshou's buffer system, from basic concepts to advanced features and integration patterns."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pickle\n",
"import tempfile\n",
"\n",
"import numpy as np\n",
"\n",
"from tianshou.data import Batch, PrioritizedReplayBuffer, ReplayBuffer, VectorReplayBuffer"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1. Introduction: Why Buffers in Reinforcement Learning?\n",
"\n",
"### The Role of Experience Replay\n",
"\n",
"Experience replay is a critical technique in modern reinforcement learning that addresses three fundamental challenges:\n",
"\n",
"1. **Breaking Temporal Correlation**: Sequential experiences from an agent are highly correlated. Training directly on these sequences can lead to unstable learning. By storing experiences and sampling randomly, we break these correlations.\n",
"\n",
"2. **Sample Efficiency**: In RL, collecting data through environment interaction is often expensive. Experience replay allows us to reuse each experience multiple times for training, dramatically improving sample efficiency.\n",
"\n",
"3. **Mini-batch Training**: Modern deep learning requires mini-batch gradient descent. Buffers enable efficient batching of experiences for neural network training.\n",
"\n",
"### Why Not Alternatives?\n",
"\n",
"**Plain Python Lists**\n",
"- No efficient random sampling\n",
"- No automatic circular queue behavior\n",
"- No trajectory boundary tracking\n",
"- Poor memory management for large datasets\n",
"\n",
"**Simple Batch Storage**\n",
"- No automatic overwriting when full\n",
"- No episode metadata (returns, lengths)\n",
"- No methods for boundary navigation (prev/next)\n",
"- No specialized sampling strategies\n",
"\n",
"### Buffer = Batch + Trajectory Management + Sampling\n",
"\n",
"Tianshou's buffers build on the `Batch` class to provide:\n",
"- **Circular queue storage**: Automatic overwriting of oldest data\n",
"- **Trajectory tracking**: Episode boundaries, returns, and lengths\n",
"- **Efficient sampling**: Random access with various strategies\n",
"- **Integration utilities**: Seamless connection to Collector and Policy\n",
"\n",
"### Use Cases\n",
"\n",
"- **Off-policy algorithms**: DQN, SAC, TD3, DDPG require experience replay\n",
"- **On-policy with replay**: Some PPO implementations reuse buffer data\n",
"- **Offline RL**: Loading and using pre-collected datasets\n",
"- **Multi-environment training**: VectorReplayBuffer for parallel collection"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2. Buffer Types and Hierarchy\n",
"\n",
"Tianshou provides several buffer implementations, each designed for specific use cases. Understanding this hierarchy is crucial for choosing the right buffer.\n",
"\n",
"### Buffer Hierarchy\n",
"\n",
"```mermaid\n",
"graph TD\n",
" RB[ReplayBuffer<br/>Single environment<br/>Circular queue] --> RBM[ReplayBufferManager<br/>Manages multiple buffers<br/>Contiguous memory]\n",
" RBM --> VRB[VectorReplayBuffer<br/>Parallel environments<br/>Maintains temporal order]\n",
" \n",
" RB --> PRB[PrioritizedReplayBuffer<br/>TD-error based sampling<br/>Importance weights]\n",
" PRB --> PVRB[PrioritizedVectorReplayBuffer<br/>Prioritized + Parallel]\n",
" \n",
" RB --> CRB[CachedReplayBuffer<br/>Primary + auxiliary caches<br/>Imitation learning]\n",
" \n",
" RB --> HERB[HERReplayBuffer<br/>Hindsight Experience Replay<br/>Goal-conditioned RL]\n",
" HERB --> HVRB[HERVectorReplayBuffer<br/>HER + Parallel]\n",
" \n",
" style RB fill:#e1f5ff\n",
" style RBM fill:#fff4e1\n",
" style VRB fill:#ffe1f5\n",
" style PRB fill:#e8f5e1\n",
" style CRB fill:#f5e1e1\n",
" style HERB fill:#e1e1f5\n",
"```\n",
"\n",
"### When to Use Which Buffer\n",
"\n",
"**ReplayBuffer**: Single environment scenarios\n",
"- Simple setup and testing\n",
"- Debugging algorithms\n",
"- Low-parallelism training\n",
"\n",
"**VectorReplayBuffer**: Multiple parallel environments (most common)\n",
"- Standard production use case\n",
"- Efficient parallel data collection\n",
"- Maintains per-environment episode boundaries\n",
"\n",
"**PrioritizedReplayBuffer**: DQN variants with prioritization\n",
"- Rainbow DQN\n",
"- Algorithms requiring importance sampling\n",
"- When some transitions are more valuable than others\n",
"\n",
"**CachedReplayBuffer**: Separate primary and auxiliary caches\n",
"- Imitation learning (expert + agent data)\n",
"- GAIL and similar algorithms\n",
"- When you need different sampling strategies for different data sources\n",
"\n",
"**HERReplayBuffer**: Goal-conditioned reinforcement learning\n",
"- Sparse reward environments\n",
"- Robotics tasks with explicit goals\n",
"- Relabeling failed experiences with achieved goals"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3. Basic Operations\n",
"\n",
"### 3.1 Construction and Configuration\n",
"\n",
"The ReplayBuffer constructor accepts several important parameters that control its behavior:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Create a buffer with all configuration options\n",
"buf = ReplayBuffer(\n",
" size=20, # Maximum capacity (transitions)\n",
" stack_num=1, # Frame stacking for RNNs (default: 1, no stacking)\n",
" ignore_obs_next=False, # Save memory by not storing obs_next\n",
" save_only_last_obs=False, # For temporal stacking (Atari-style)\n",
" sample_avail=False, # Sample only valid indices for frame stacking\n",
" random_seed=42, # Reproducible sampling\n",
")\n",
"\n",
"print(f\"Buffer created: {buf}\")\n",
"print(f\"Max size: {buf.maxsize}\")\n",
"print(f\"Current length: {len(buf)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Parameter Explanations**:\n",
"\n",
"- `size`: Maximum number of transitions the buffer can hold. When full, oldest data is overwritten.\n",
"- `stack_num`: Number of consecutive frames to stack. Used for RNN inputs or frame-based
gitextract_z9q4ijrb/
├── .devcontainer/
│ └── devcontainer.json
├── .dockerignore
├── .github/
│ ├── ISSUE_TEMPLATE.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ ├── extra_sys.yml
│ ├── gputest.yml
│ ├── lint_and_docs.yml
│ ├── publish.yaml
│ └── pytest.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yaml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── MANIFEST.in
├── README.md
├── benchmark/
│ └── run_benchmark.py
├── docs/
│ ├── .gitignore
│ ├── 01_user_guide/
│ │ ├── 00_training_process.md
│ │ ├── 01_apis.md
│ │ ├── 02_core_abstractions.md
│ │ └── index.rst
│ ├── 02_deep_dives/
│ │ ├── 0_intro.md
│ │ ├── L1_Batch.ipynb
│ │ ├── L2_Buffer.ipynb
│ │ ├── L3_Environments.ipynb
│ │ ├── L4_GAE.ipynb
│ │ ├── L5_Collector.ipynb
│ │ └── L6_MARL.ipynb
│ ├── 04_benchmarks/
│ │ └── benchmarks.rst
│ ├── 05_developer_guide/
│ │ └── developer_guide.md
│ ├── 06_contributors/
│ │ └── contributors.rst
│ ├── _config.yml
│ ├── _static/
│ │ ├── css/
│ │ │ └── style.css
│ │ └── js/
│ │ ├── benchmark.js
│ │ ├── copybutton.js
│ │ ├── mujoco/
│ │ │ └── benchmark/
│ │ │ └── Ant-v4/
│ │ │ └── results.json
│ │ ├── v5.json
│ │ ├── vega-embed@5.js
│ │ ├── vega-lite@5.js
│ │ └── vega@5.js
│ ├── autogen_rst.py
│ ├── bibtex.json
│ ├── create_toc.py
│ ├── index.rst
│ ├── nbstripout.py
│ └── refs.bib
├── examples/
│ ├── __init__.py
│ ├── atari/
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── atari_c51.py
│ │ ├── atari_dqn.py
│ │ ├── atari_dqn_hl.py
│ │ ├── atari_fqf.py
│ │ ├── atari_iqn.py
│ │ ├── atari_iqn_hl.py
│ │ ├── atari_ppo.py
│ │ ├── atari_ppo_hl.py
│ │ ├── atari_qrdqn.py
│ │ ├── atari_rainbow.py
│ │ ├── atari_sac.py
│ │ └── atari_sac_hl.py
│ ├── box2d/
│ │ ├── README.md
│ │ ├── acrobot_dualdqn.py
│ │ ├── bipedal_bdq.py
│ │ ├── bipedal_hardcore_sac.py
│ │ ├── lunarlander_dqn.py
│ │ └── mcc_sac.py
│ ├── discrete/
│ │ ├── discrete_dqn.py
│ │ └── discrete_dqn_hl.py
│ ├── inverse/
│ │ ├── README.md
│ │ └── irl_gail.py
│ ├── modelbased/
│ │ └── README.md
│ ├── mujoco/
│ │ ├── README.md
│ │ ├── analysis.py
│ │ ├── fetch_her_ddpg.py
│ │ ├── mujoco_a2c.py
│ │ ├── mujoco_a2c_hl.py
│ │ ├── mujoco_ddpg.py
│ │ ├── mujoco_ddpg_hl.py
│ │ ├── mujoco_env.py
│ │ ├── mujoco_npg.py
│ │ ├── mujoco_npg_hl.py
│ │ ├── mujoco_ppo.py
│ │ ├── mujoco_ppo_hl.py
│ │ ├── mujoco_redq.py
│ │ ├── mujoco_redq_hl.py
│ │ ├── mujoco_reinforce.py
│ │ ├── mujoco_reinforce_hl.py
│ │ ├── mujoco_sac.py
│ │ ├── mujoco_sac_hl.py
│ │ ├── mujoco_td3.py
│ │ ├── mujoco_td3_hl.py
│ │ ├── mujoco_trpo.py
│ │ ├── mujoco_trpo_hl.py
│ │ ├── plotter.py
│ │ └── tools.py
│ ├── offline/
│ │ ├── README.md
│ │ ├── atari_bcq.py
│ │ ├── atari_cql.py
│ │ ├── atari_crr.py
│ │ ├── atari_il.py
│ │ ├── convert_rl_unplugged_atari.py
│ │ ├── d4rl_bcq.py
│ │ ├── d4rl_cql.py
│ │ ├── d4rl_il.py
│ │ ├── d4rl_td3_bc.py
│ │ └── utils.py
│ └── vizdoom/
│ ├── .gitignore
│ ├── README.md
│ ├── env.py
│ ├── maps/
│ │ ├── D1_basic.cfg
│ │ ├── D1_basic.wad
│ │ ├── D2_navigation.cfg
│ │ ├── D2_navigation.wad
│ │ ├── D3_battle.cfg
│ │ ├── D3_battle.wad
│ │ ├── D4_battle2.cfg
│ │ ├── D4_battle2.wad
│ │ ├── README.md
│ │ └── spectator.py
│ ├── replay.py
│ ├── vizdoom_c51.py
│ └── vizdoom_ppo.py
├── pyproject.toml
├── test/
│ ├── __init__.py
│ ├── base/
│ │ ├── __init__.py
│ │ ├── env.py
│ │ ├── test_action_space_sampling.py
│ │ ├── test_batch.py
│ │ ├── test_buffer.py
│ │ ├── test_collector.py
│ │ ├── test_env.py
│ │ ├── test_env_finite.py
│ │ ├── test_logger.py
│ │ ├── test_policy.py
│ │ ├── test_returns.py
│ │ ├── test_stats.py
│ │ └── test_utils.py
│ ├── continuous/
│ │ ├── __init__.py
│ │ ├── test_ddpg.py
│ │ ├── test_npg.py
│ │ ├── test_ppo.py
│ │ ├── test_redq.py
│ │ ├── test_sac_with_il.py
│ │ ├── test_td3.py
│ │ └── test_trpo.py
│ ├── determinism_test.py
│ ├── discrete/
│ │ ├── __init__.py
│ │ ├── test_a2c_with_il.py
│ │ ├── test_bdqn.py
│ │ ├── test_c51.py
│ │ ├── test_discrete_sac.py
│ │ ├── test_dqn.py
│ │ ├── test_drqn.py
│ │ ├── test_fqf.py
│ │ ├── test_iqn.py
│ │ ├── test_ppo_discrete.py
│ │ ├── test_qrdqn.py
│ │ ├── test_rainbow.py
│ │ └── test_reinforce.py
│ ├── highlevel/
│ │ ├── __init__.py
│ │ ├── env_factory.py
│ │ └── test_experiment_builder.py
│ ├── modelbased/
│ │ ├── __init__.py
│ │ ├── test_dqn_icm.py
│ │ ├── test_ppo_icm.py
│ │ └── test_psrl.py
│ ├── offline/
│ │ ├── __init__.py
│ │ ├── gather_cartpole_data.py
│ │ ├── gather_pendulum_data.py
│ │ ├── test_bcq.py
│ │ ├── test_cql.py
│ │ ├── test_discrete_bcq.py
│ │ ├── test_discrete_cql.py
│ │ ├── test_discrete_crr.py
│ │ ├── test_gail.py
│ │ └── test_td3_bc.py
│ └── pettingzoo/
│ ├── pistonball.py
│ ├── pistonball_continuous.py
│ ├── test_pistonball.py
│ ├── test_pistonball_continuous.py
│ ├── test_tic_tac_toe.py
│ └── tic_tac_toe.py
└── tianshou/
├── __init__.py
├── algorithm/
│ ├── __init__.py
│ ├── algorithm_base.py
│ ├── imitation/
│ │ ├── __init__.py
│ │ ├── bcq.py
│ │ ├── cql.py
│ │ ├── discrete_bcq.py
│ │ ├── discrete_cql.py
│ │ ├── discrete_crr.py
│ │ ├── gail.py
│ │ ├── imitation_base.py
│ │ └── td3_bc.py
│ ├── modelbased/
│ │ ├── __init__.py
│ │ ├── icm.py
│ │ └── psrl.py
│ ├── modelfree/
│ │ ├── __init__.py
│ │ ├── a2c.py
│ │ ├── bdqn.py
│ │ ├── c51.py
│ │ ├── ddpg.py
│ │ ├── discrete_sac.py
│ │ ├── dqn.py
│ │ ├── fqf.py
│ │ ├── iqn.py
│ │ ├── npg.py
│ │ ├── ppo.py
│ │ ├── qrdqn.py
│ │ ├── rainbow.py
│ │ ├── redq.py
│ │ ├── reinforce.py
│ │ ├── sac.py
│ │ ├── td3.py
│ │ └── trpo.py
│ ├── multiagent/
│ │ ├── __init__.py
│ │ └── marl.py
│ ├── optim.py
│ └── random.py
├── config.py
├── data/
│ ├── __init__.py
│ ├── batch.py
│ ├── buffer/
│ │ ├── __init__.py
│ │ ├── buffer_base.py
│ │ ├── cached.py
│ │ ├── her.py
│ │ ├── manager.py
│ │ ├── prio.py
│ │ └── vecbuf.py
│ ├── collector.py
│ ├── stats.py
│ ├── types.py
│ └── utils/
│ ├── __init__.py
│ ├── converter.py
│ └── segtree.py
├── env/
│ ├── __init__.py
│ ├── atari/
│ │ ├── atari_network.py
│ │ └── atari_wrapper.py
│ ├── gym_wrappers.py
│ ├── pettingzoo_env.py
│ ├── utils.py
│ ├── venv_wrappers.py
│ ├── venvs.py
│ └── worker/
│ ├── __init__.py
│ ├── dummy.py
│ ├── ray.py
│ ├── subproc.py
│ └── worker_base.py
├── evaluation/
│ ├── __init__.py
│ ├── launcher.py
│ └── rliable_evaluation.py
├── exploration/
│ ├── __init__.py
│ └── random.py
├── highlevel/
│ ├── __init__.py
│ ├── algorithm.py
│ ├── config.py
│ ├── env.py
│ ├── experiment.py
│ ├── logger.py
│ ├── module/
│ │ ├── __init__.py
│ │ ├── actor.py
│ │ ├── core.py
│ │ ├── critic.py
│ │ ├── intermediate.py
│ │ └── special.py
│ ├── params/
│ │ ├── __init__.py
│ │ ├── algorithm_params.py
│ │ ├── algorithm_wrapper.py
│ │ ├── alpha.py
│ │ ├── collector.py
│ │ ├── dist_fn.py
│ │ ├── env_param.py
│ │ ├── lr_scheduler.py
│ │ ├── noise.py
│ │ └── optim.py
│ ├── persistence.py
│ ├── trainer.py
│ └── world.py
├── py.typed
├── trainer.py
└── utils/
├── __init__.py
├── conversion.py
├── determinism.py
├── lagged_network.py
├── logger/
│ ├── __init__.py
│ ├── logger_base.py
│ ├── tensorboard.py
│ └── wandb.py
├── logging.py
├── net/
│ ├── __init__.py
│ ├── common.py
│ ├── continuous.py
│ └── discrete.py
├── print.py
├── progress_bar.py
├── space_info.py
├── statistics.py
├── torch_utils.py
└── warning.py
Showing preview only (442K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (4993 symbols across 213 files)
FILE: benchmark/run_benchmark.py
function find_script_paths (line 67) | def find_script_paths(
function get_current_tmux_sessions (line 92) | def get_current_tmux_sessions(benchmark_type: str) -> list[str]:
function start_tmux_session (line 106) | def start_tmux_session(
function aggregate_rliable_results (line 163) | def aggregate_rliable_results(task_results_dir: str | Path) -> None:
function main (line 211) | def main(
FILE: docs/_static/js/benchmark.js
function getDataSource (line 15) | function getDataSource(selectEnv, dirName) {
function showMujocoResults (line 64) | function showMujocoResults(elem) {
function showAtariResults (line 70) | function showAtariResults(elem) {
FILE: docs/_static/js/vega-embed@5.js
function i (line 1) | function i(e){var t=e+="",n=t.indexOf(":");return n>=0&&"xmlns"!==(t=e.s...
function o (line 1) | function o(e){return function(){var t=this.ownerDocument,r=this.namespac...
function a (line 1) | function a(e){return function(){return this.ownerDocument.createElementN...
function s (line 1) | function s(e){var t=i(e);return(t.local?a:o)(t)}
function l (line 1) | function l(){}
function c (line 1) | function c(e){return null==e?l:function(){return this.querySelector(e)}}
function u (line 1) | function u(){return[]}
function f (line 1) | function f(e){return new Array(e.length)}
function p (line 1) | function p(e,t){this.ownerDocument=e.ownerDocument,this.namespaceURI=e.n...
function d (line 1) | function d(e,t,n,r,i,o){for(var a,s=0,l=t.length,c=o.length;s<c;++s)(a=t...
function g (line 1) | function g(e,t,n,r,i,o,a){var s,l,c,u={},f=t.length,d=o.length,g=new Arr...
function m (line 1) | function m(e,t){return e<t?-1:e>t?1:e>=t?0:NaN}
function v (line 1) | function v(e){return function(){this.removeAttribute(e)}}
function E (line 1) | function E(e){return function(){this.removeAttributeNS(e.space,e.local)}}
function y (line 1) | function y(e,t){return function(){this.setAttribute(e,t)}}
function b (line 1) | function b(e,t){return function(){this.setAttributeNS(e.space,e.local,t)}}
function I (line 1) | function I(e,t){return function(){var n=t.apply(this,arguments);null==n?...
function O (line 1) | function O(e,t){return function(){var n=t.apply(this,arguments);null==n?...
function R (line 1) | function R(e){return e.ownerDocument&&e.ownerDocument.defaultView||e.doc...
function w (line 1) | function w(e){return function(){this.style.removeProperty(e)}}
function N (line 1) | function N(e,t,n){return function(){this.style.setProperty(e,t,n)}}
function A (line 1) | function A(e,t,n){return function(){var r=t.apply(this,arguments);null==...
function S (line 1) | function S(e){return function(){delete this[e]}}
function L (line 1) | function L(e,t){return function(){this[e]=t}}
function T (line 1) | function T(e,t){return function(){var n=t.apply(this,arguments);null==n?...
function x (line 1) | function x(e){return e.trim().split(/^|\s+/)}
function _ (line 1) | function _(e){return e.classList||new C(e)}
function C (line 1) | function C(e){this._node=e,this._names=x(e.getAttribute("class")||"")}
function P (line 1) | function P(e,t){for(var n=_(e),r=-1,i=t.length;++r<i;)n.add(t[r])}
function D (line 1) | function D(e,t){for(var n=_(e),r=-1,i=t.length;++r<i;)n.remove(t[r])}
function k (line 1) | function k(e){return function(){P(this,e)}}
function F (line 1) | function F(e){return function(){D(this,e)}}
function M (line 1) | function M(e,t){return function(){(t.apply(this,arguments)?P:D)(this,e)}}
function j (line 1) | function j(){this.textContent=""}
function $ (line 1) | function $(e){return function(){this.textContent=e}}
function G (line 1) | function G(e){return function(){var t=e.apply(this,arguments);this.textC...
function z (line 1) | function z(){this.innerHTML=""}
function U (line 1) | function U(e){return function(){this.innerHTML=e}}
function B (line 1) | function B(e){return function(){var t=e.apply(this,arguments);this.inner...
function X (line 1) | function X(){this.nextSibling&&this.parentNode.appendChild(this)}
function V (line 1) | function V(){this.previousSibling&&this.parentNode.insertBefore(this,thi...
function W (line 1) | function W(){return null}
function H (line 1) | function H(){var e=this.parentNode;e&&e.removeChild(this)}
function q (line 1) | function q(){return this.parentNode.insertBefore(this.cloneNode(!1),this...
function Y (line 1) | function Y(){return this.parentNode.insertBefore(this.cloneNode(!0),this...
function K (line 1) | function K(e,t,n){return e=Q(e,t,n),function(t){var n=t.relatedTarget;n&...
function Q (line 1) | function Q(e,t,n){return function(r){var i=Z;Z=r;try{e.call(this,this.__...
function ee (line 1) | function ee(e){return function(){var t=this.__on;if(t){for(var n,r=0,i=-...
function te (line 1) | function te(e,t,n){var r=J.hasOwnProperty(e.type)?K:Q;return function(i,...
function ne (line 1) | function ne(e,t,n){var r=R(e),i=r.CustomEvent;"function"==typeof i?i=new...
function re (line 1) | function re(e,t){return function(){return ne(this,e,t)}}
function ie (line 1) | function ie(e,t){return function(){return ne(this,e,t.apply(this,argumen...
function ae (line 1) | function ae(e,t){this._groups=e,this._parents=t}
function se (line 1) | function se(){return new ae([[document.documentElement]],oe)}
function t (line 1) | function t(t,n){return t&&n?e(t.__data__,n.__data__):!t-!n}
function ce (line 1) | function ce(e,t,n,r){return new(n||(n=Promise))((function(i,o){function ...
function pe (line 1) | function pe(e,t){return!1!==t.clone&&t.isMergeableObject(e)?me((n=e,Arra...
function he (line 1) | function he(e,t,n){return e.concat(t).map((function(e){return pe(e,n)}))}
function de (line 1) | function de(e){return Object.keys(e).concat(function(e){return Object.ge...
function ge (line 1) | function ge(e,t,n){var r={};return n.isMergeableObject(e)&&de(e).forEach...
function me (line 1) | function me(e,t,n){(n=n||{}).arrayMerge=n.arrayMerge||he,n.isMergeableOb...
function be (line 1) | function be(e,t){return e(t={exports:{}},t.exports),t.exports}
function c (line 1) | function c(e){s[e]=l++}
function f (line 1) | function f(e,t){if(t&&"object"==typeof t||(t={loose:!!t,includePrereleas...
function p (line 1) | function p(e,t){if(t&&"object"==typeof t||(t={loose:!!t,includePrereleas...
function d (line 1) | function d(e,t){var n=h.test(e),r=h.test(t);return n&&r&&(e=+e,t=+t),e==...
function g (line 1) | function g(e,t,n){return new p(e,n).compare(new p(t,n))}
function m (line 1) | function m(e,t,n){return g(e,t,n)>0}
function v (line 1) | function v(e,t,n){return g(e,t,n)<0}
function E (line 1) | function E(e,t,n){return 0===g(e,t,n)}
function y (line 1) | function y(e,t,n){return 0!==g(e,t,n)}
function b (line 1) | function b(e,t,n){return g(e,t,n)>=0}
function I (line 1) | function I(e,t,n){return g(e,t,n)<=0}
function O (line 1) | function O(e,t,n,r){switch(t){case"===":return"object"==typeof e&&(e=e.v...
function R (line 1) | function R(e,t){if(t&&"object"==typeof t||(t={loose:!!t,includePrereleas...
function N (line 1) | function N(e,t){if(t&&"object"==typeof t||(t={loose:!!t,includePrereleas...
function A (line 1) | function A(e,t){for(var n=!0,r=e.slice(),i=r.pop();n&&r.length;)n=r.ever...
function S (line 1) | function S(e){return!e||"x"===e.toLowerCase()||"*"===e}
function L (line 1) | function L(e,t,n,r,i,o,a,s,l,c,u,f,p){return((t=S(n)?"":S(r)?">="+n+".0....
function T (line 1) | function T(e,t,r){for(var i=0;i<e.length;i++)if(!e[i].test(t))return!1;i...
function x (line 1) | function x(e,t,n){try{t=new N(t,n)}catch(e){return!1}return t.test(e)}
function _ (line 1) | function _(e,t,n,r){var i,o,a,s,l;switch(e=new p(e,r),t=new N(t,r),n){ca...
function Ge (line 1) | function Ge(e,t,n){return e.fields=t||[],e.fname=n,e}
function ze (line 1) | function ze(e){throw Error(e)}
function Be (line 1) | function Be(e){return e===Object(e)}
function Xe (line 1) | function Xe(e){return"string"==typeof e}
function Ve (line 1) | function Ve(e){return Ue(e)?"["+e.map(Ve)+"]":Be(e)||Xe(e)?JSON.stringif...
function c (line 1) | function c(){i.push(l+e.substring(t,n)),l="",t=n+1}
function qe (line 1) | function qe(e,t){return JSON.stringify(e,function(e){const t=[];return f...
class Ye (line 1) | class Ye{constructor(e){this.options=Object.assign(Object.assign({},$e),...
method constructor (line 1) | constructor(e){this.options=Object.assign(Object.assign({},$e),e);cons...
method tooltipHandler (line 1) | tooltipHandler(e,t,n,r){if(null==r||""===r)return void this.el.classLi...
function ot (line 1) | function ot(e,t,n,r){const i=`<html><head>${t}</head><body><pre><code cl...
function at (line 1) | function at(t,n,r={}){return ce(this,void 0,void 0,(function*(){const i=...
function st (line 1) | function st(e,t={}){return ce(this,void 0,void 0,(function*(){const n=do...
FILE: docs/_static/js/vega-lite@5.js
function i (line 1) | function i(e){return!!e.or}
function r (line 1) | function r(e){return!!e.and}
function o (line 1) | function o(e){return!!e.not}
function a (line 1) | function a(e,t){if(o(e))a(e.not,t);else if(r(e))for(const n of e.and)a(n...
function s (line 1) | function s(e,t){return o(e)?{not:s(e.not,t)}:r(e)?{and:e.and.map((e=>s(e...
function c (line 1) | function c(e){throw new Error(e)}
function u (line 1) | function u(e,n){const i={};for(const r of n)t.hasOwnProperty(e,r)&&(i[r]...
function f (line 1) | function f(e,t){const n={...e};for(const e of t)delete n[e];return n}
function d (line 1) | function d(e){if(t.isNumber(e))return e;const n=t.isString(e)?e:X(e);if(...
function m (line 1) | function m(e){return!1===e||null===e}
function p (line 1) | function p(e,t){return e.includes(t)}
function g (line 1) | function g(e,t){let n=0;for(const[i,r]of e.entries())if(t(r,i,n++))retur...
function h (line 1) | function h(e,t){let n=0;for(const[i,r]of e.entries())if(!t(r,i,n++))retu...
function y (line 1) | function y(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),i=1;i<t;...
function v (line 1) | function v(e,n){for(const i of D(n))t.writeConfig(e,i,n[i],!0)}
function b (line 1) | function b(e,t){const n=[],i={};let r;for(const o of e)r=t(o),r in i||(i...
function x (line 1) | function x(e,t){if(e.size!==t.size)return!1;for(const n of e)if(!t.has(n...
function $ (line 1) | function $(e,t){for(const n of e)if(t.has(n))return!0;return!1}
function w (line 1) | function w(e){const n=new Set;for(const i of e){const e=t.splitAccessPat...
function k (line 1) | function k(e,t){return void 0===e||void 0===t||$(w(e),w(t))}
function S (line 1) | function S(e){return 0===D(e).length}
function O (line 1) | function O(e){return!0===e||!1===e}
function _ (line 1) | function _(e){const t=e.replace(/\W/g,"_");return(e.match(/^\d+/)?"_":""...
function N (line 1) | function N(e,t){return o(e)?`!(${N(e.not,t)})`:r(e)?`(${e.and.map((e=>N(...
function C (line 1) | function C(e,t){if(0===t.length)return!0;const n=t.shift();return n in e...
function P (line 1) | function P(e){return e.charAt(0).toUpperCase()+e.substr(1)}
function A (line 1) | function A(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[...
function j (line 1) | function j(e){return`${arguments.length>1&&void 0!==arguments[1]?argumen...
function T (line 1) | function T(e){return e.replace(/(\[|\]|\.|'|")/g,"\\$1")}
function E (line 1) | function E(e){return`${t.splitAccessPath(e).map(T).join("\\.")}`}
function M (line 1) | function M(e,t,n){return e.replace(new RegExp(t.replace(/[-/\\^$*+?.()|[...
function L (line 1) | function L(e){return`${t.splitAccessPath(e).join(".")}`}
function q (line 1) | function q(e){return e?t.splitAccessPath(e).length:0}
function U (line 1) | function U(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=...
function W (line 1) | function W(e){const t=++R;return e?String(e)+t:t}
function B (line 1) | function B(e){return I(e)?e:`__${e}`}
function I (line 1) | function I(e){return e.startsWith("__")}
function H (line 1) | function H(e){if(void 0!==e)return(e%360+360)%360}
function V (line 1) | function V(e){return!!t.isNumber(e)||!isNaN(e)&&!isNaN(parseFloat(e))}
function Y (line 1) | function Y(e,t){if(e===t)return!0;if(e&&t&&"object"==typeof e&&"object"=...
function X (line 1) | function X(e){const t=[];return function e(n){if(n&&n.toJSON&&"function"...
function Ae (line 1) | function Ae(e){return e in Pe}
function Te (line 1) | function Te(e){switch(e){case ce:return"y";case fe:return"y2";case ue:re...
function Ee (line 1) | function Ee(e){return e in je}
function qe (line 1) | function qe(e){return e===me||e===pe||e===ge}
function Ke (line 1) | function Ke(e){return!!We[e]}
function et (line 1) | function et(e){return tt(e)!==e}
function tt (line 1) | function tt(e){switch(e){case te:return Z;case ne:return ee;case fe:retu...
function nt (line 1) | function nt(e){if(Ae(e))switch(e){case se:return"startAngle";case le:ret...
function it (line 1) | function it(e){switch(e){case Z:return te;case ee:return ne;case ce:retu...
function rt (line 1) | function rt(e){switch(e){case Z:case te:return"width";case ee:case ne:re...
function ot (line 1) | function ot(e){switch(e){case Z:return"xOffset";case ee:return"yOffset";...
function at (line 1) | function at(e){switch(e){case Z:return"xOffset";case ee:return"yOffset"}}
function st (line 1) | function st(e){switch(e){case"xOffset":return"x";case"yOffset":return"y"}}
function zt (line 1) | function zt(e){return e in Dt}
function Nt (line 1) | function Nt(e){return"width"===e?Z:ee}
function Pt (line 1) | function Pt(e){return e in Ct}
function Ht (line 1) | function Ht(e){return!!Bt[e]}
function Vt (line 1) | function Vt(e,t){return function(e){switch(e){case me:case pe:case ge:ca...
function Qt (line 1) | function Qt(e){switch(e){case Z:case ee:case se:case oe:case ie:case re:...
function Zt (line 1) | function Zt(e){return!!e&&!!e.argmin}
function en (line 1) | function en(e){return!!e&&!!e.argmax}
function tn (line 1) | function tn(e){return t.isString(e)&&!!Jt[e]}
function rn (line 1) | function rn(e){return t.isString(e)&&nn.has(e)}
function sn (line 1) | function sn(e){return t.isBoolean(e)&&(e=ba(e,void 0)),"bin"+D(e).map((t...
function ln (line 1) | function ln(e){return!0===e||un(e)&&!e.binned}
function cn (line 1) | function cn(e){return"binned"===e||un(e)&&!0===e.binned}
function un (line 1) | function un(e){return t.isObject(e)}
function fn (line 1) | function fn(e){return e?.param}
function dn (line 1) | function dn(e){switch(e){case Q:case J:case ye:case me:case pe:case ge:c...
function mn (line 1) | function mn(e){return!!e?.expr}
function pn (line 1) | function pn(e){const t=D(e||{}),n={};for(const i of t)n[i]=Sn(e[i]);retu...
function gn (line 1) | function gn(e){const{anchor:t,frame:n,offset:i,orient:r,angle:o,limit:a,...
function hn (line 1) | function hn(e){return t.isString(e)||t.isArray(e)&&t.isString(e[0])}
function yn (line 1) | function yn(e){return!!e?.signal}
function vn (line 1) | function vn(e){return!!e.step}
function bn (line 1) | function bn(e){return!t.isArray(e)&&("field"in e&&"data"in e)}
function kn (line 1) | function kn(e){const n=t.isArray(e.condition)?e.condition.map(Dn):Dn(e.c...
function Sn (line 1) | function Sn(e){if(mn(e)){const{expr:t,...n}=e;return{signal:t,...n}}retu...
function Dn (line 1) | function Dn(e){if(mn(e)){const{expr:t,...n}=e;return{signal:t,...n}}retu...
function Fn (line 1) | function Fn(e){if(mn(e)){const{expr:t,...n}=e;return{signal:t,...n}}retu...
function zn (line 1) | function zn(e){return yn(e)?e.signal:t.stringValue(e.value)}
function On (line 1) | function On(e){return yn(e)?e.signal:null==e?null:t.stringValue(e)}
function _n (line 1) | function _n(e,t,n){for(const i of n){const n=Pn(i,t.markDef,t.config);vo...
function Nn (line 1) | function Nn(e){return[].concat(e.type,e.style??[])}
function Cn (line 1) | function Cn(e,t,n){let i=arguments.length>3&&void 0!==arguments[3]?argum...
function Pn (line 1) | function Pn(e,t,n){let{vgChannel:i}=arguments.length>3&&void 0!==argumen...
function An (line 1) | function An(e,t,n){return jn(e,Nn(t),n)}
function jn (line 1) | function jn(e,n,i){let r;n=t.array(n);for(const t of n){const n=i[t];n&&...
function Tn (line 1) | function Tn(e,n){return t.array(e).reduce(((e,t)=>(e.field.push(oa(t,n))...
function En (line 1) | function En(e,t){const n=[...e];return t.forEach((e=>{for(const t of n)i...
function Mn (line 1) | function Mn(e,n){return Y(e,n)||!n?e:e?[...t.array(e),...t.array(n)].joi...
function Ln (line 1) | function Ln(e,t){const n=e.value,i=t.value;if(null==n||null===i)return{e...
function qn (line 1) | function qn(e,t,n){return(t=function(e){var t=function(e,t){if("object"!...
function Un (line 1) | function Un(e,t,n){return function(e,t,n){if(t.set)t.set.call(e,n);else{...
function Rn (line 1) | function Rn(e,t,n){if(!t.has(e))throw new TypeError("attempted to "+n+" ...
function Wn (line 1) | function Wn(e,t,n){!function(e,t){if(t.has(e))throw new TypeError("Canno...
function Bn (line 1) | function Bn(e){return`Invalid specification ${X(e)}. Make sure the speci...
function Hn (line 1) | function Hn(e){return`${"width"==e?"Width":"Height"} "container" only wo...
function Vn (line 1) | function Vn(e){return`${"width"==e?"Width":"Height"} "container" only wo...
function Gn (line 1) | function Gn(e){return e?`Dropping "fit-${e}" because spec has discrete $...
function Yn (line 1) | function Yn(e){return`Unknown field for ${e}. Cannot calculate view size.`}
function Xn (line 1) | function Xn(e){return`Cannot project a selection on encoding channel "${...
function Qn (line 1) | function Qn(e,t){return`Cannot project a selection on encoding channel "...
function Jn (line 1) | function Jn(e){return`Selection not supported for ${e} yet.`}
function Zn (line 1) | function Zn(e){return`The "columns" property cannot be used when "${e}" ...
function ei (line 1) | function ei(e,t,n){return`An ancestor parsed field "${e}" as ${n} but a ...
function ti (line 1) | function ti(e){return`Config.customFormatTypes is not true, thus custom ...
function ni (line 1) | function ni(e){return`${e}Offset dropped because ${e} is continuous`}
function ii (line 1) | function ii(e){return`Invalid field type "${e}".`}
function ri (line 1) | function ri(e,t){const{fill:n,stroke:i}=t;return`Dropping color ${e} as ...
function oi (line 1) | function oi(e,t){return`Dropping ${X(e)} from channel "${t}" since it do...
function ai (line 1) | function ai(e,t,n){return`${e} dropped as it is incompatible with "${t}"...
function si (line 1) | function si(e){return`${e} encoding should be discrete (ordinal / nomina...
function li (line 1) | function li(e){return`${e} encoding should be discrete (ordinal / nomina...
function ci (line 1) | function ci(e,t){return`Using discrete channel "${e}" to encode "${t}" f...
function ui (line 1) | function ui(e){return`Using unaggregated domain with raw field has no ef...
function fi (line 1) | function fi(e){return`Unaggregated domain not applicable for "${e}" sinc...
function di (line 1) | function di(e){return`Unaggregated domain is currently unsupported for l...
function mi (line 1) | function mi(e,t,n){return`${n}-scale's "${t}" is dropped as it does not ...
function pi (line 1) | function pi(e){return`The step for "${e}" is dropped because the ${"widt...
function hi (line 1) | function hi(e,t){return`Invalid ${e}: ${X(t)}.`}
function yi (line 1) | function yi(e){return`1D error band does not support ${e}.`}
function vi (line 1) | function vi(e){return`Channel ${e} is required for "binned" bin.`}
function $i (line 1) | function $i(){xi.warn(...arguments)}
function wi (line 1) | function wi(e){if(e&&t.isObject(e))for(const t of Ni)if(t in e)return!0;...
function zi (line 1) | function zi(e,n){const i=[];if(n&&void 0!==e.day&&D(e).length>1&&($i(fun...
function Oi (line 1) | function Oi(e){const t=zi(e,!0).join(", ");return e.utc?`utc(${t})`:`dat...
function Ci (line 1) | function Ci(e){return t.isObject(e)?e.binned:Pi(e)}
function Pi (line 1) | function Pi(e){return e&&e.startsWith("binned")}
function Ai (line 1) | function Ai(e){return e.startsWith("utc")}
function Ti (line 1) | function Ti(e){return Ni.filter((t=>Mi(e,t)))}
function Ei (line 1) | function Ei(e){const t=Ti(e);return t[t.length-1]}
function Mi (line 1) | function Mi(e,t){const n=e.indexOf(t);return!(n<0)&&(!(n>0&&"seconds"===...
function Li (line 1) | function Li(e,t){let{end:n}=arguments.length>2&&void 0!==arguments[2]?ar...
function qi (line 1) | function qi(e){if(!e)return;return`timeUnitSpecifier(${X(Ti(e))}, ${X(ji...
function Ui (line 1) | function Ui(e){if(!e)return;let n;return t.isString(e)?n=Pi(e)?{unit:e.s...
function Ri (line 1) | function Ri(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments...
function Bi (line 1) | function Bi(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments...
function Ii (line 1) | function Ii(e){return!!e?.field&&void 0!==e.equal}
function Hi (line 1) | function Hi(e){return!!e?.field&&void 0!==e.lt}
function Vi (line 1) | function Vi(e){return!!e?.field&&void 0!==e.lte}
function Gi (line 1) | function Gi(e){return!!e?.field&&void 0!==e.gt}
function Yi (line 1) | function Yi(e){return!!e?.field&&void 0!==e.gte}
function Xi (line 1) | function Xi(e){if(e?.field){if(t.isArray(e.range)&&2===e.range.length)re...
function Qi (line 1) | function Qi(e){return!!e?.field&&(t.isArray(e.oneOf)||t.isArray(e.in))}
function Ji (line 1) | function Ji(e){return Qi(e)||Ii(e)||Xi(e)||Hi(e)||Gi(e)||Vi(e)||Yi(e)}
function Ki (line 1) | function Ki(e,t){return wa(e,{timeUnit:t,wrapTime:!0})}
function Zi (line 1) | function Zi(e){let t=!(arguments.length>1&&void 0!==arguments[1])||argum...
function er (line 1) | function er(e){return!(arguments.length>1&&void 0!==arguments[1])||argum...
function tr (line 1) | function tr(e){return Ji(e)&&e.timeUnit?{...e,timeUnit:Ui(e.timeUnit)}:e}
function nr (line 1) | function nr(e){return"quantitative"===e||"temporal"===e}
function ir (line 1) | function ir(e){return"ordinal"===e||"nominal"===e}
function fr (line 1) | function fr(e,t){const n=ur[e],i=ur[t];return n===i||"ordinal-position"=...
function mr (line 1) | function mr(e){return dr[e]}
function hr (line 1) | function hr(e){return pr.has(e)}
function xr (line 1) | function xr(e){return br.has(e)}
function $r (line 1) | function $r(e){return vr.has(e)}
function wr (line 1) | function wr(e){return gr.has(e)}
function kr (line 1) | function kr(e){return yr.has(e)}
function Sr (line 1) | function Sr(e){return e?.param}
function Ar (line 1) | function Ar(e,t){switch(t){case"type":case"domain":case"reverse":case"ra...
function jr (line 1) | function jr(e,t){switch(t){case"interpolate":case"scheme":case"domainMid...
function Qr (line 1) | function Qr(e){return["line","area","trail"].includes(e)}
function Jr (line 1) | function Jr(e){return["rect","bar","image","arc"].includes(e)}
function Zr (line 1) | function Zr(e){return e.type}
function io (line 1) | function io(e){return e&&null!=e.band}
function so (line 1) | function so(e){const{channel:t,channelDef:n,markDef:i,scale:r,config:o}=...
function lo (line 1) | function lo(e,t){return{test:co(e,!0),..."y"===tt(t)?{field:{group:"heig...
function co (line 1) | function co(e){let n=!(arguments.length>1&&void 0!==arguments[1])||argum...
function uo (line 1) | function uo(e,t,n,i){const r={};if(t&&(r.scale=t),Go(e)){const{datum:t}=...
function fo (line 1) | function fo(e){let{scaleName:t,fieldOrDatumDef:n,fieldOrDatumDef2:i,offs...
function mo (line 1) | function mo(e){let{channel:n,channelDef:i,channel2Def:r,markDef:o,config...
function po (line 1) | function po(e,t){return p(["x","x2"],e)&&"width"===t?{field:{group:"widt...
function go (line 1) | function go(e){return e&&"number"!==e&&"time"!==e}
function ho (line 1) | function ho(e,t,n){return`${e}(${t}${n?`, ${X(n)}`:""})`}
function vo (line 1) | function vo(e){let{fieldOrDatumDef:n,format:i,formatType:r,expr:o,normal...
function bo (line 1) | function bo(e,t,n){return Ho(e)?n?`${oa(e,{expr:t,suffix:"end"})}-${oa(e...
function xo (line 1) | function xo(e){let{fieldOrDatumDef:t,format:n,formatType:i,expr:r,normal...
function $o (line 1) | function $o(e,n,i,r,o,a){if(!t.isString(r)||!go(r)){if(void 0===i&&void ...
function wo (line 1) | function wo(e,t,n){return e&&(yn(e)||"number"===e||"time"===e)?e:$a(t)&&...
function ko (line 1) | function ko(e){let{type:n,specifiedFormat:i,config:r,normalizeStack:o}=e...
function So (line 1) | function So(e,t){return`format(${e}, "${t||""}")`}
function Do (line 1) | function Do(e,n,i,r){return go(i)?ho(i,e,n):So(e,(t.isString(n)?n:void 0...
function Fo (line 1) | function Fo(e,t,n,i,r){if(void 0===n&&void 0===i&&r.customFormatTypes&&r...
function _o (line 1) | function _o(e){return e in Oo}
function No (line 1) | function No(e){return!!e?.encoding}
function Co (line 1) | function Co(e){return e&&("count"===e.op||!!e.field)}
function Po (line 1) | function Po(e){return e&&t.isArray(e)}
function Ao (line 1) | function Ao(e){return"row"in e||"column"in e}
function jo (line 1) | function jo(e){return!!e&&"header"in e}
function To (line 1) | function To(e){return"facet"in e}
function Eo (line 1) | function Eo(e){const{field:t,timeUnit:n,bin:i,aggregate:r}=e;return{...n...
function Mo (line 1) | function Mo(e){return"sort"in e}
function Lo (line 1) | function Lo(e){let{fieldDef:t,fieldDef2:n,markDef:i,config:r}=e;if(Jo(t)...
function qo (line 1) | function qo(e){let{channel:t,fieldDef:n,fieldDef2:i,markDef:r,config:o,s...
function Uo (line 1) | function Uo(e,t,n,i){return!!(ln(e.bin)||e.timeUnit&&Ko(e)&&"temporal"==...
function Ro (line 1) | function Ro(e){return e&&!!e.sort&&!e.field}
function Wo (line 1) | function Wo(e){return e&&"condition"in e}
function Bo (line 1) | function Bo(e){const n=e?.condition;return!!n&&!t.isArray(n)&&Ho(n)}
function Io (line 1) | function Io(e){const n=e?.condition;return!!n&&!t.isArray(n)&&Jo(n)}
function Ho (line 1) | function Ho(e){return e&&(!!e.field||"count"===e.aggregate)}
function Vo (line 1) | function Vo(e){return e?.type}
function Go (line 1) | function Go(e){return e&&"datum"in e}
function Yo (line 1) | function Yo(e){return Ko(e)&&!aa(e)||Qo(e)}
function Xo (line 1) | function Xo(e){return Ko(e)&&"quantitative"===e.type&&!e.bin||Qo(e)}
function Qo (line 1) | function Qo(e){return Go(e)&&t.isNumber(e.datum)}
function Jo (line 1) | function Jo(e){return Ho(e)||Go(e)}
function Ko (line 1) | function Ko(e){return e&&("field"in e||"count"===e.aggregate)&&"type"in e}
function Zo (line 1) | function Zo(e){return e&&"value"in e&&"value"in e}
function ea (line 1) | function ea(e){return e&&("scale"in e||"sort"in e)}
function ta (line 1) | function ta(e){return e&&("axis"in e||"stack"in e||"impute"in e)}
function na (line 1) | function na(e){return e&&"legend"in e}
function ia (line 1) | function ia(e){return e&&("format"in e||"formatType"in e)}
function ra (line 1) | function ra(e){return f(e,["legend","axis","header","scale"])}
function oa (line 1) | function oa(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments...
function aa (line 1) | function aa(e){switch(e.type){case"nominal":case"ordinal":case"geojson":...
function ca (line 1) | function ca(e){la=e}
function ua (line 1) | function ua(e,t,n){let{allowDisabling:i,includeDefault:r=!0}=n;const o=f...
function fa (line 1) | function fa(e){return ta(e)&&e.axis?e.axis:na(e)&&e.legend?e.legend:jo(e...
function da (line 1) | function da(e,t){return la(e,t)}
function ma (line 1) | function ma(e){if(ia(e)){const{format:t,formatType:n}=e;return{format:t,...
function pa (line 1) | function pa(e){return Ho(e)?e:Bo(e)?e.condition:void 0}
function ga (line 1) | function ga(e){return Jo(e)?e:Io(e)?e.condition:void 0}
function ha (line 1) | function ha(e,n,i){let r=arguments.length>3&&void 0!==arguments[3]?argum...
function ya (line 1) | function ya(e,n,i,r){if(ia(e)){const{format:t,formatType:o,...a}=e;if(go...
function va (line 1) | function va(e,n){let{compositeMark:i=!1}=arguments.length>2&&void 0!==ar...
function ba (line 1) | function ba(e,n){return t.isBoolean(e)?{maxbins:dn(n)}:"binned"===e?{bin...
function $a (line 1) | function $a(e){const{formatType:t}=ma(e);return"time"===t||!t&&((n=e)&&(...
function wa (line 1) | function wa(e,n){let{timeUnit:i,type:r,wrapTime:o,undefinedIfExprNotRequ...
function ka (line 1) | function ka(e,t){const{type:n}=e;return t.map((t=>{const i=wa(t,{timeUni...
function Sa (line 1) | function Sa(e,t){return ln(e.bin)?Ht(t)&&["ordinal","nominal"].includes(...
function Fa (line 1) | function Fa(e){return e?.condition}
function Ca (line 1) | function Ca(e){return!!Na[e]}
function Aa (line 1) | function Aa(e){return"mark"in e}
class ja (line 1) | class ja{constructor(e,t){this.name=e,this.run=t}hasMatchingType(e){retu...
method constructor (line 1) | constructor(e,t){this.name=e,this.run=t}
method hasMatchingType (line 1) | hasMatchingType(e){return!!Aa(e)&&(Zr(t=e.mark)?t.type:t)===this.name;...
function Ta (line 1) | function Ta(e,n){const i=e&&e[n];return!!i&&(t.isArray(i)?g(i,(e=>!!e.fi...
function Ea (line 1) | function Ea(e,n){const i=e&&e[n];return!!i&&(t.isArray(i)?g(i,(e=>!!e.fi...
function Ma (line 1) | function Ma(e,t){if(zt(t)){const n=e[t];if((Ho(n)||Go(n))&&(ir(n.type)||...
function La (line 1) | function La(e){return g(Be,(n=>{if(Ta(e,n)){const i=e[n];if(t.isArray(i)...
function qa (line 1) | function qa(e,t){const n=[],i=[],r=[],o=[],a={};return Wa(e,((s,l)=>{if(...
function Ua (line 1) | function Ua(e,t,n){const i=Vt(t,n);if(!i)return!1;if("binned"===i){const...
function Ra (line 1) | function Ra(e,t){const n={};for(const i of D(e)){const r=ha(e[i],i,t,{co...
function Wa (line 1) | function Wa(e,n,i){if(e)for(const r of D(e)){const o=e[r];if(t.isArray(o...
function Ba (line 1) | function Ba(e,n){return D(n).reduce(((i,r)=>{switch(r){case Z:case ee:ca...
function Ia (line 1) | function Ia(e,n,i){let r=!(arguments.length>3&&void 0!==arguments[3])||a...
function Ha (line 1) | function Ha(e){const{title:t,field:n}=e;return U(t,n)}
function Va (line 1) | function Va(e,n,i,r,o){const{scale:a,axis:s}=i;return l=>{let{partName:c...
function Ga (line 1) | function Ga(e,n,i,r){const{clip:o,color:a,opacity:s}=e,l=e.type;return e...
function Ya (line 1) | function Ya(e,t,n){const{encoding:i}=e,r="vertical"===t?"y":"x",o=i[r],a...
function Xa (line 1) | function Xa(e,t){if(e?.aggregate){const{aggregate:n,...i}=e;return n!==t...
function Qa (line 1) | function Qa(e,t){const{mark:n,encoding:i}=e,{x:r,y:o}=i;if(Zr(n)&&n.orie...
function Za (line 1) | function Za(e){return t.isNumber(e)?"tukey":e}
function es (line 1) | function es(e,n){let{config:i}=n;e={...e,encoding:Ra(e.encoding,i)};cons...
function ts (line 1) | function ts(e){return[{op:"q1",field:e,as:`lower_box_${e}`},{op:"q3",fie...
function rs (line 1) | function rs(e,t){let{config:n}=t;e={...e,encoding:Ra(e.encoding,n)};cons...
function os (line 1) | function os(e,t){const{encoding:n}=e;if(function(e){return(Jo(e.x)||Jo(e...
function as (line 1) | function as(e,t,n){const{mark:i,encoding:r,params:o,projection:a,...s}=e...
function ss (line 1) | function ss(e,t,n){return`${P(e)} ${n} ${t}`}
function us (line 1) | function us(e,t){let{config:n}=t;e={...e,encoding:Ra(e.encoding,n)};cons...
function ds (line 1) | function ds(e,t,n){const i=new ja(e,t);fs[e]={normalizer:i,parts:n}}
function ws (line 1) | function ws(e){return"legend"===e||!!e?.legend}
function ks (line 1) | function ks(e){return ws(e)&&t.isObject(e)}
function Ss (line 1) | function Ss(e){return!!e?.select}
function Ds (line 1) | function Ds(e){const t=[];for(const n of e||[]){if(Ss(n))continue;const{...
function Fs (line 1) | function Fs(e){return"concat"in e}
function zs (line 1) | function zs(e){return"vconcat"in e}
function Os (line 1) | function Os(e){return"hconcat"in e}
function _s (line 1) | function _s(e){let{step:t,offsetIsDiscrete:n}=e;return n?t.for??"offset"...
function Ns (line 1) | function Ns(e){return t.isObject(e)&&void 0!==e.step}
function Cs (line 1) | function Cs(e){return e.view||e.width||e.height}
function As (line 1) | function As(e,t){return e[t]??e["width"===t?"continuousWidth":"continuou...
function js (line 1) | function js(e,t){const n=Ts(e,t);return Ns(n)?n.step:Es}
function Ts (line 1) | function Ts(e,t){return U(e[t]??e["width"===t?"discreteWidth":"discreteH...
function Rs (line 1) | function Rs(e){const t=D(e||{}),n={};for(const i of t){const t=e[i];n[i]...
function Bs (line 1) | function Bs(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[...
function Gs (line 1) | function Gs(e){e=l(e);for(const t of Hs)delete e[t];if(e.axis)for(const ...
function Ys (line 1) | function Ys(e,t,n,i){"view"===t&&(n="cell");const r={...i?e[t][i]:e[t],....
function Xs (line 1) | function Xs(e){return"layer"in e}
class Qs (line 1) | class Qs{map(e,t){return To(e)?this.mapFacet(e,t):function(e){return"rep...
method map (line 1) | map(e,t){return To(e)?this.mapFacet(e,t):function(e){return"repeat"in ...
method mapLayerOrUnit (line 1) | mapLayerOrUnit(e,t){if(Xs(e))return this.mapLayer(e,t);if(Aa(e))return...
method mapLayer (line 1) | mapLayer(e,t){return{...e,layer:e.layer.map((e=>this.mapLayerOrUnit(e,...
method mapHConcat (line 1) | mapHConcat(e,t){return{...e,hconcat:e.hconcat.map((e=>this.map(e,t)))}}
method mapVConcat (line 1) | mapVConcat(e,t){return{...e,vconcat:e.vconcat.map((e=>this.map(e,t)))}}
method mapConcat (line 1) | mapConcat(e,t){const{concat:n,...i}=e;return{...i,concat:n.map((e=>thi...
method mapFacet (line 1) | mapFacet(e,t){return{...e,spec:this.map(e.spec,t)}}
method mapRepeat (line 1) | mapRepeat(e,t){return{...e,spec:this.map(e.spec,t)}}
function el (line 1) | function el(e){return Ho(e)&&"quantitative"===Vo(e)&&!e.bin}
function tl (line 1) | function tl(e,t,n){let{orient:i,type:r}=n;const o="x"===t?"y":"radius",a...
function nl (line 1) | function nl(e,n){const i=Zr(e)?e:{type:e},r=i.type;if(!Ks.has(r))return ...
function il (line 1) | function il(e,t,n){const i=pn(e),r=Cn("orient",i,n);if(i.orient=function...
function rl (line 1) | function rl(e){const{point:t,line:n,...i}=e;return D(i).length>1?i:i.type}
function ol (line 1) | function ol(e){for(const t of["line","area","rule","trail"])e[t]&&(e={.....
function al (line 1) | function al(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments...
function sl (line 1) | function sl(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments...
class ll (line 1) | class ll{constructor(){qn(this,"name","path-overlay")}hasMatchingType(e,...
method constructor (line 1) | constructor(){qn(this,"name","path-overlay")}
method hasMatchingType (line 1) | hasMatchingType(e,t){if(Aa(e)){const{mark:n,encoding:i}=e,r=Zr(n)?n:{t...
method run (line 1) | run(e,t,n){const{config:i}=t,{params:r,projection:o,mark:a,name:s,enco...
function cl (line 1) | function cl(e,t){return t?Ao(e)?gl(e,t):dl(e,t):e}
function ul (line 1) | function ul(e,t){return t?gl(e,t):e}
function fl (line 1) | function fl(e,n,i){const r=n[e];return(o=r)&&!t.isString(o)&&"repeat"in ...
function dl (line 1) | function dl(e,t){if(void 0!==(e=fl("field",e,t))){if(null===e)return nul...
function ml (line 1) | function ml(e,t){if(Ho(e))return dl(e,t);{const n=fl("datum",e,t);return...
function pl (line 1) | function pl(e,t){if(!Jo(e)){if(Io(e)){const n=ml(e.condition,t);if(n)ret...
function gl (line 1) | function gl(e,n){const i={};for(const r in e)if(t.hasOwnProperty(e,r)){c...
class hl (line 1) | class hl{constructor(){qn(this,"name","RuleForRangedLine")}hasMatchingTy...
method constructor (line 1) | constructor(){qn(this,"name","RuleForRangedLine")}
method hasMatchingType (line 1) | hasMatchingType(e){if(Aa(e)){const{encoding:t,mark:n}=e;if("line"===n|...
method run (line 1) | run(e,n,i){const{encoding:r,mark:o}=e;var a,s;return $i((a=!!r.x2,s=!!...
function yl (line 1) | function yl(e){let{parentEncoding:n,encoding:i={},layer:r}=e,o={};if(n){...
function vl (line 1) | function vl(e){const{parentProjection:t,projection:n}=e;return t&&n&&$i(...
function bl (line 1) | function bl(e){return"filter"in e}
function xl (line 1) | function xl(e){return"lookup"in e}
function $l (line 1) | function $l(e){return"pivot"in e}
function wl (line 1) | function wl(e){return"density"in e}
function kl (line 1) | function kl(e){return"quantile"in e}
function Sl (line 1) | function Sl(e){return"regression"in e}
function Dl (line 1) | function Dl(e){return"loess"in e}
function Fl (line 1) | function Fl(e){return"sample"in e}
function zl (line 1) | function zl(e){return"window"in e}
function Ol (line 1) | function Ol(e){return"joinaggregate"in e}
function _l (line 1) | function _l(e){return"flatten"in e}
function Nl (line 1) | function Nl(e){return"calculate"in e}
function Cl (line 1) | function Cl(e){return"bin"in e}
function Pl (line 1) | function Pl(e){return"impute"in e}
function Al (line 1) | function Al(e){return"timeUnit"in e}
function jl (line 1) | function jl(e){return"aggregate"in e}
function Tl (line 1) | function Tl(e){return"stack"in e}
function El (line 1) | function El(e){return"fold"in e}
function Ml (line 1) | function Ml(e){return"extent"in e&&!("density"in e)}
function Ll (line 1) | function Ll(e,t){const{transform:n,...i}=e;if(n){return{...i,transform:n...
function ql (line 1) | function ql(e,n){const i=l(e);if(Ho(i)&&un(i.bin)&&(i.bin=Ul(i.bin)),ea(...
function Ul (line 1) | function Ul(e){const t=e.extent;if(t?.selection){const{selection:n,...i}...
function Rl (line 1) | function Rl(e,t){const n=e=>s(e,(e=>{const n={param:e,empty:t.emptySelec...
class Wl (line 1) | class Wl extends Qs{map(e,t){const n=t.selections??[];if(e.params&&!Aa(e...
method map (line 1) | map(e,t){const n=t.selections??[];if(e.params&&!Aa(e)){const t=[];for(...
method mapUnit (line 1) | mapUnit(e,n){const i=n.selections;if(!i||!i.length)return e;const r=(n...
function Bl (line 1) | function Bl(e,t){return e.name?{...t,path:(t.path??[]).concat(e.name)}:t}
function Il (line 1) | function Il(e,t){void 0===t&&(t=Bs(e.config));const n=function(e){let t=...
method constructor (line 1) | constructor(){super(...arguments),qn(this,"nonFacetUnitNormalizers",[Ka,...
method map (line 1) | map(e,t){if(Aa(e)){const n=Ta(e.encoding,Q),i=Ta(e.encoding,J),r=Ta(e.en...
method mapUnit (line 1) | mapUnit(e,t){const{parentEncoding:n,parentProjection:i}=t,r=ul(e.encodin...
method mapRepeat (line 1) | mapRepeat(e,n){return function(e){return!t.isArray(e.repeat)&&e.repeat.l...
method mapLayerRepeat (line 1) | mapLayerRepeat(e,t){const{repeat:n,spec:i,...r}=e,{row:o,column:a,layer:...
method mapNonLayerRepeat (line 1) | mapNonLayerRepeat(e,n){const{repeat:i,spec:r,data:o,...a}=e;!t.isArray(i...
method mapFacet (line 1) | mapFacet(e,t){const{facet:n}=e;return Ao(n)&&e.columns&&(e=f(e,["columns...
method mapUnitWithParentEncodingOrProjection (line 1) | mapUnitWithParentEncodingOrProjection(e,t){const{encoding:n,projection:i...
method mapFacetedUnit (line 1) | mapFacetedUnit(e,t){const{row:n,column:i,facet:r,...o}=e.encoding,{mark:...
method getFacetMappingAndLayout (line 1) | getFacetMappingAndLayout(e,t){const{row:n,column:i,facet:r}=e;if(n||i){r...
method mapLayer (line 1) | mapLayer(e,t){let{parentEncoding:n,parentProjection:i,...r}=t;const{enco...
method map (line 1) | map(e,t){return t.emptySelections??={},t.selectionPredicates??={},e=Ll(e...
method mapLayerOrUnit (line 1) | mapLayerOrUnit(e,t){if((e=Ll(e,t)).encoding){const n={};for(const[i,r]of...
method mapUnit (line 1) | mapUnit(e,t){const{selection:n,...i}=e;return n?{...i,params:z(n).map((e...
function Yl (line 1) | function Yl(e){return t.isString(e)?{type:e}:e??{}}
function Ql (line 1) | function Ql(e,t){const n={};for(const t of Xl)e&&void 0!==e[t]&&(n[t]=Sn...
class Jl (line 1) | class Jl{constructor(){let e=arguments.length>0&&void 0!==arguments[0]?a...
method constructor (line 1) | constructor(){let e=arguments.length>0&&void 0!==arguments[0]?argument...
method clone (line 1) | clone(){return new Jl(l(this.explicit),l(this.implicit))}
method combine (line 1) | combine(){return{...this.explicit,...this.implicit}}
method get (line 1) | get(e){return U(this.explicit[e],this.implicit[e])}
method getWithExplicit (line 1) | getWithExplicit(e){return void 0!==this.explicit[e]?{explicit:!0,value...
method setWithExplicit (line 1) | setWithExplicit(e,t){let{value:n,explicit:i}=t;void 0!==n&&this.set(e,...
method set (line 1) | set(e,t,n){return delete this[n?"implicit":"explicit"][e],this[n?"expl...
method copyKeyFromSplit (line 1) | copyKeyFromSplit(e,t){let{explicit:n,implicit:i}=t;void 0!==n[e]?this....
method copyKeyFromObject (line 1) | copyKeyFromObject(e,t){void 0!==t[e]&&this.set(e,t[e],!0)}
method copyAll (line 1) | copyAll(e){for(const t of D(e.combine())){const n=e.getWithExplicit(t)...
function Kl (line 1) | function Kl(e){return{explicit:!0,value:e}}
function Zl (line 1) | function Zl(e){return{explicit:!1,value:e}}
function ec (line 1) | function ec(e){return(t,n,i,r)=>{const o=e(t.value,n.value);return o>0?t...
function tc (line 1) | function tc(e,t,n,i){return e.explicit&&t.explicit&&$i(function(e,t,n,i)...
function nc (line 1) | function nc(e,t,n,i){let r=arguments.length>4&&void 0!==arguments[4]?arg...
class ic (line 1) | class ic extends Jl{constructor(){let e=arguments.length>0&&void 0!==arg...
method constructor (line 1) | constructor(){let e=arguments.length>0&&void 0!==arguments[0]?argument...
method clone (line 1) | clone(){const e=super.clone();return e.parseNothing=this.parseNothing,e}
function rc (line 1) | function rc(e){return"url"in e}
function oc (line 1) | function oc(e){return"values"in e}
function ac (line 1) | function ac(e){return"name"in e&&!rc(e)&&!oc(e)&&!sc(e)}
function sc (line 1) | function sc(e){return e&&(lc(e)||cc(e)||uc(e))}
function lc (line 1) | function lc(e){return"sequence"in e}
function cc (line 1) | function cc(e){return"sphere"in e}
function uc (line 1) | function uc(e){return"graticule"in e}
function dc (line 1) | function dc(e){const{signals:t,hasLegend:n,index:i,...r}=e;return r.fiel...
function mc (line 1) | function mc(e){let n=!(arguments.length>1&&void 0!==arguments[1])||argum...
function pc (line 1) | function pc(e,n){for(const i of F(e.component.selection??{})){const r=i....
function gc (line 1) | function gc(e,n){if(e.component.selection&&D(e.component.selection).leng...
function hc (line 1) | function hc(e,t){for(const n of F(e.component.selection??{}))for(const i...
function yc (line 1) | function yc(e){return e.map((e=>(e.on&&!e.on.length&&delete e.on,e)))}
class vc (line 1) | class vc{constructor(e,t){this.debugName=t,qn(this,"_children",[]),qn(th...
method constructor (line 1) | constructor(e,t){this.debugName=t,qn(this,"_children",[]),qn(this,"_pa...
method clone (line 1) | clone(){throw new Error("Cannot clone node")}
method parent (line 1) | get parent(){return this._parent}
method parent (line 1) | set parent(e){this._parent=e,e&&e.addChild(this)}
method children (line 1) | get children(){return this._children}
method numChildren (line 1) | numChildren(){return this._children.length}
method addChild (line 1) | addChild(e,t){this._children.includes(e)?$i("Attempt to add the same c...
method removeChild (line 1) | removeChild(e){const t=this._children.indexOf(e);return this._children...
method remove (line 1) | remove(){let e=this._parent.removeChild(this);for(const t of this._chi...
method insertAsParentOf (line 1) | insertAsParentOf(e){const t=e.parent;t.removeChild(this),this.parent=t...
method swapWithParent (line 1) | swapWithParent(){const e=this._parent,t=e.parent;for(const t of this._...
class bc (line 1) | class bc extends vc{clone(){const e=new this.constructor;return e.debugN...
method clone (line 1) | clone(){const e=new this.constructor;return e.debugName=`clone_${this....
method constructor (line 1) | constructor(e,t,n,i){super(e,t),this.type=n,this.refCounts=i,qn(this,"...
method dependentFields (line 1) | dependentFields(){return new Set}
method producedFields (line 1) | producedFields(){return new Set}
method hash (line 1) | hash(){return void 0===this._hash&&(this._hash=`Output ${W()}`),this._...
method getSource (line 1) | getSource(){return this.refCounts[this._name]++,this._source}
method isRequired (line 1) | isRequired(){return!!this.refCounts[this._name]}
method setSource (line 1) | setSource(e){this._source=e}
function xc (line 1) | function xc(e){return void 0!==e.as}
function $c (line 1) | function $c(e){return`${e}_end`}
class wc (line 1) | class wc extends vc{clone(){return new wc(null,l(this.timeUnits))}constr...
method clone (line 1) | clone(){return new wc(null,l(this.timeUnits))}
method constructor (line 1) | constructor(e,t){super(e),this.timeUnits=t}
method makeFromEncoding (line 1) | static makeFromEncoding(e,t){const n=t.reduceFieldDef(((e,n,i)=>{const...
method makeFromTransform (line 1) | static makeFromTransform(e,t){const{timeUnit:n,...i}={...t},r={...i,ti...
method merge (line 1) | merge(e){this.timeUnits={...this.timeUnits};for(const t in e.timeUnits...
method removeFormulas (line 1) | removeFormulas(e){const t={};for(const[n,i]of z(this.timeUnits)){const...
method producedFields (line 1) | producedFields(){return new Set(F(this.timeUnits).map((e=>xc(e)?e.as:$...
method dependentFields (line 1) | dependentFields(){return new Set(F(this.timeUnits).map((e=>e.field)))}
method hash (line 1) | hash(){return`TimeUnit ${d(this.timeUnits)}`}
method assemble (line 1) | assemble(){const e=[];for(const t of F(this.timeUnits)){const{rectBand...
function Dc (line 1) | function Dc(e){let{timeUnit:t,field:n,reverse:i}=e;const{unit:r,utc:o}=t...
function Fc (line 1) | function Fc(e,t,n){let[i,r]=e;if(void 0!==t&&.5!==t){const e=`datum['${i...
function zc (line 1) | function zc(e,t){let[n,i]=e;return`${1-t} * ${n} + ${t} * ${i}`}
class _c (line 1) | class _c{constructor(){qn(this,"hasChannel",void 0),qn(this,"hasField",v...
method constructor (line 1) | constructor(){qn(this,"hasChannel",void 0),qn(this,"hasField",void 0),...
function Pc (line 1) | function Pc(e,n){return`domain(${t.stringValue(e.scaleName(n))})`}
function Ac (line 1) | function Ac(e){return e.parent&&km(e.parent)&&(!e.parent.parent??Ac(e.pa...
function Uc (line 1) | function Uc(e,n,i,r){const o=Wo(n)&&n.condition,a=r(n);if(o){return{[i]:...
function Rc (line 1) | function Rc(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments...
function Wc (line 1) | function Wc(e,t){let n=arguments.length>2&&void 0!==arguments[2]?argumen...
function Bc (line 1) | function Bc(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments...
function Ic (line 1) | function Ic(e,n,i){let{reactiveGeom:r}=arguments.length>3&&void 0!==argu...
function Hc (line 1) | function Hc(e,t,n){let{reactiveGeom:i}=arguments.length>3&&void 0!==argu...
function Vc (line 1) | function Vc(e){const{markDef:t,config:n}=e,i=Cn("aria",t,n);return!1===i...
function Gc (line 1) | function Gc(e){const{mark:t,markDef:n,config:i}=e;if(!1===i.aria)return{...
function Yc (line 1) | function Yc(e){const{encoding:t,markDef:n,config:i,stack:r}=e,o=t.descri...
function Xc (line 1) | function Xc(e,t){let n=arguments.length>2&&void 0!==arguments[2]?argumen...
function Qc (line 1) | function Qc(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments...
function Jc (line 1) | function Jc(e){const{encoding:t,mark:n}=e,i=t.order;return!Qr(n)&&Zo(i)?...
function Kc (line 1) | function Kc(e){let{channel:t,markDef:n,encoding:i={},model:r,bandPositio...
function Zc (line 1) | function Zc(e,t,n){let{defaultPos:i,vgChannel:r}=n;const{encoding:o,mark...
function eu (line 1) | function eu(e){let{model:t,defaultPos:n,channel:i,scaleName:r,scale:o}=e...
function iu (line 1) | function iu(e,t,n){let i=arguments.length>3&&void 0!==arguments[3]?argum...
function ru (line 1) | function ru(e,t,n){let{defaultPos:i,defaultPos2:r,range:o}=n;return o?ou...
function ou (line 1) | function ou(e,t,n){let{defaultPos:i,defaultPos2:r}=n;const{markDef:o,con...
function au (line 1) | function au(e,t){const n=rt(e),i=nt(e);if(void 0!==t[i])return{[i]:po(e,...
function su (line 1) | function su(e,n){const{config:i,encoding:r,markDef:o}=e,a=o.type,s=it(n)...
function lu (line 1) | function lu(e,n,i,r,o,a,s){if(io(o)){if(!i)return{mult:o.band,field:{gro...
function cu (line 1) | function cu(e,t,n,i,r,o,a){if(Ae(e))return 0;const s="x"===e||"y2"===e,l...
function uu (line 1) | function uu(e){let{fieldDef:t,scaleName:n,bandPosition:i,offset:r,useRec...
function du (line 1) | function du(e,t){const{fill:n,stroke:i}="include"===t.color?Qc(e):{};ret...
function mu (line 1) | function mu(e,n,i){const{config:r,mark:o,markDef:a}=e;if("hide"===Cn("in...
function pu (line 1) | function pu(e,t){return xn.reduce(((n,i)=>(fu.has(i)||void 0===e[i]||"ig...
function gu (line 1) | function gu(e){const{config:t,markDef:n}=e;if(Cn("invalid",n,t)){const t...
function hu (line 1) | function hu(e,t){if(void 0!==t)return{[e]:Fn(t)}}
function i (line 1) | function i(e,i){-1!==e&&n[e].on&&n[e].on.push({events:t.clear,update:i})}
function zu (line 1) | function zu(e,t,n,i,r){const o=t.name,a=o+Su,s=o+Du,l=n.channel,c=Cc.def...
function Cu (line 1) | function Cu(e,t,n,i,r){const o=t.name,a=n.channel,s=Cc.defined(t),l=r.fi...
function Mu (line 1) | function Mu(e){let{escape:n}=arguments.length>1&&void 0!==arguments[1]?a...
function Lu (line 1) | function Lu(e){return F(e.component.selection??{}).reduce(((e,t)=>e||t.p...
function qu (line 1) | function qu(e,n){!t.isString(n.select)&&n.select.on||delete e.events,!t....
function Uu (line 1) | function Uu(e){const t=[];return"Identifier"===e.type?[e.name]:"Literal"...
function Ru (line 1) | function Ru(e){return"MemberExpression"===e.object.type?Ru(e.object):"da...
function Wu (line 1) | function Wu(e){const n=t.parseExpression(e),i=new Set;return n.visit((e=...
class Bu (line 1) | class Bu extends vc{clone(){return new Bu(null,this.model,l(this.filter)...
method clone (line 1) | clone(){return new Bu(null,this.model,l(this.filter))}
method constructor (line 1) | constructor(e,t,n){super(e),this.model=t,this.filter=n,qn(this,"expr",...
method dependentFields (line 1) | dependentFields(){return this._dependentFields}
method producedFields (line 1) | producedFields(){return new Set}
method assemble (line 1) | assemble(){return{type:"filter",expr:this.expr}}
method hash (line 1) | hash(){return`Filter ${this.expr}`}
function Iu (line 1) | function Iu(e,n,i){let r=arguments.length>3&&void 0!==arguments[3]?argum...
function Hu (line 1) | function Hu(e,n,i){const r=_(n),o=i.encoding;let a,s=i.field;try{a=e.get...
function Vu (line 1) | function Vu(e,n,i){return N(n,(n=>t.isString(n)?n:function(e){return e?....
function Gu (line 1) | function Gu(e,t,n,i){e.encode??={},e.encode[t]??={},e.encode[t].update??...
function Yu (line 1) | function Yu(e,n,i){let r=arguments.length>3&&void 0!==arguments[3]?argum...
function Xu (line 1) | function Xu(e){const{axes:t}=e.component,n=[];for(const i of Ft)if(t[i])...
function Qu (line 1) | function Qu(e,t,n,i){return Object.assign.apply(null,[{},...e.map((e=>{i...
function Ju (line 1) | function Ju(e,n){const i=[{}];for(const r of e){let e=n[r]?.style;if(e){...
function Ku (line 1) | function Ku(e,t,n){let i=arguments.length>3&&void 0!==arguments[3]?argum...
function ef (line 1) | function ef(e){return`(((${e.signal} % 360) + 360) % 360)`}
function tf (line 1) | function tf(e,t,n,i){if(void 0!==e){if("x"===n){if(yn(e)){const n=ef(e);...
function nf (line 1) | function nf(e,t,n){if(void 0===e)return;const i="x"===n,r=i?0:90,o=i?"bo...
function rf (line 1) | function rf(e,t){const n="x"===t?"x2":"y2",i=e.fieldDef(t),r=e.fieldDef(...
class of (line 1) | class of extends vc{clone(){return new of(null,l(this.transform))}constr...
method clone (line 1) | clone(){return new of(null,l(this.transform))}
method constructor (line 1) | constructor(e,t){super(e),this.transform=t,qn(this,"_dependentFields",...
method parseAllForSortIndex (line 1) | static parseAllForSortIndex(e,t){return t.forEachFieldDef(((t,n)=>{if(...
method producedFields (line 1) | producedFields(){return new Set([this.transform.as])}
method dependentFields (line 1) | dependentFields(){return this._dependentFields}
method assemble (line 1) | assemble(){return{type:"formula",expr:this.transform.calculate,as:this...
method hash (line 1) | hash(){return`Calculate ${d(this.transform)}`}
function af (line 1) | function af(e,t,n){return oa(e,{prefix:t,suffix:"sort_index",...n??{}})}
function sf (line 1) | function sf(e,t){return p(["top","bottom"],t)?"column":p(["left","right"...
function lf (line 1) | function lf(e,t,n,i){const r="row"===i?n.headerRow:"column"===i?n.header...
function cf (line 1) | function cf(e,t,n,i){const r={};for(const o of e){const e=lf(o,t||{},n,i...
function df (line 1) | function df(e,t){const n=e.component.layoutHeaders[t].title,i=e.config?e...
function mf (line 1) | function mf(e,t){switch(arguments.length>2&&void 0!==arguments[2]?argume...
function pf (line 1) | function pf(e,t){const n=tf(e,"row"===t?"left":"top","row"===t?"y":"x",!...
function gf (line 1) | function gf(e,t){const n=e.component.layoutHeaders[t],i=[];for(const r o...
function hf (line 1) | function hf(e,n){const{sort:i}=e;return Co(i)?{field:oa(i,{expr:"datum"}...
function yf (line 1) | function yf(e,t,n){const{format:i,formatType:r,labelAngle:o,labelAnchor:...
function vf (line 1) | function vf(e,t,n,i,r){if(r){let o=null;const{facetFieldDef:a}=i,s=e.con...
function xf (line 1) | function xf(e,t){return bf[t][e]}
function $f (line 1) | function $f(e,t,n,i,r){const o={};for(const a of i){if(!r[a])continue;co...
function wf (line 1) | function wf(e){return[...kf(e,"width"),...kf(e,"height"),...kf(e,"childW...
function kf (line 1) | function kf(e,t){const n="width"===t?"x":"y",i=e.component.layoutSize.ge...
function Sf (line 1) | function Sf(e,t){const n=`${e}_step`;return yn(t.step)?{name:n,update:t....
function Df (line 1) | function Df(e,t,n){const i=t.get("type"),r=t.get("padding"),o=U(t.get("p...
function Ff (line 1) | function Ff(e){return"childWidth"===e?"width":"childHeight"===e?"height":e}
function zf (line 1) | function zf(e,t){return D(e).reduce(((n,i)=>{const r=e[i];return{...n,.....
function Of (line 1) | function Of(e,t){if($m(t))return"theta"===e?"independent":"shared";if(km...
function _f (line 1) | function _f(e,t){const n=e.scale[t],i=zt(t)?"axis":"legend";return"indep...
class Cf (line 1) | class Cf extends Jl{}
function Af (line 1) | function Af(e){return Tf(e,((e,t)=>Math.max(e,t.value)))}
function jf (line 1) | function jf(e){return Tf(e,((e,t)=>U(e,t.value)))}
function Tf (line 1) | function Tf(e,n){return function(e){const n=e?.condition;return!!n&&(t.i...
function Ef (line 1) | function Ef(e,n,i){const r=n.get("selections");if(!r?.length)return;cons...
function Lf (line 1) | function Lf(e){const{legend:t}=e;return U(t.type,function(e){let{channel...
function qf (line 1) | function qf(e){let{legendConfig:t,legendType:n,orient:i,legend:r}=e;retu...
function Uf (line 1) | function Uf(e,t,n,i){return{signal:`clamp(${e.getSizeSignalRef(t).signal...
function Rf (line 1) | function Rf(e){const t=xm(e)?function(e){const{encoding:t}=e,n={};for(co...
function Wf (line 1) | function Wf(e,t,n,i){switch(t){case"disable":return void 0!==n;case"valu...
function Bf (line 1) | function Bf(e,t){let n=e.legend(t);const{markDef:i,encoding:r,config:o}=...
function If (line 1) | function If(e,t){if(!e)return t.clone();const n=e.getWithExplicit("orien...
function Hf (line 1) | function Hf(e,t){return"circle"===t.value?t:e}
function Vf (line 1) | function Vf(e){const t=e.component.legends,n={};for(const i of D(t)){con...
function Gf (line 1) | function Gf(e){return km(e)||wm(e)?function(e){return e.children.reduce(...
function Yf (line 1) | function Yf(e){const t=e.component.projection;if(!t||t.merged)return[];c...
class Qf (line 1) | class Qf extends Jl{constructor(e,t,n,i){super({...t},{name:e}),this.spe...
method constructor (line 1) | constructor(e,t,n,i){super({...t},{name:e}),this.specifiedProjection=t...
method isFit (line 1) | get isFit(){return!!this.data}
function Jf (line 1) | function Jf(e){e.component.projection=xm(e)?function(e){if(e.hasProjecti...
function Kf (line 1) | function Kf(e,t,n,i){if(Sa(t,n)){const r=xm(e)?e.axis(n)??e.legend(n)??{...
function Zf (line 1) | function Zf(e,t){return`${sn(e)}_${t}`}
function ed (line 1) | function ed(e,t,n){const i=Zf(ba(n,void 0)??{},t);return e.getName(`${i}...
function td (line 1) | function td(e,n,i){let r,o;r=function(e){return"as"in e}(e)?t.isString(e...
class nd (line 1) | class nd extends vc{clone(){return new nd(null,l(this.bins))}constructor...
method clone (line 1) | clone(){return new nd(null,l(this.bins))}
method constructor (line 1) | constructor(e,t){super(e),this.bins=t}
method makeFromEncoding (line 1) | static makeFromEncoding(e,t){const n=t.reduceFieldDef(((e,n,i)=>{if(Ko...
method makeFromTransform (line 1) | static makeFromTransform(e,t,n){const{key:i,binComponent:r}=td(t,t.bin...
method merge (line 1) | merge(e,t){for(const n of D(e.bins))n in this.bins?(t(e.bins[n].signal...
method producedFields (line 1) | producedFields(){return new Set(F(this.bins).map((e=>e.as)).flat(2))}
method dependentFields (line 1) | dependentFields(){return new Set(F(this.bins).map((e=>e.field)))}
method hash (line 1) | hash(){return`Bin ${d(this.bins)}`}
method assemble (line 1) | assemble(){return F(this.bins).flatMap((e=>{const t=[],[n,...i]=e.as,{...
function id (line 1) | function id(e,n,i,r){const o=xm(r)?r.encoding[it(n)]:void 0;if(Ko(i)&&xm...
class rd (line 1) | class rd extends vc{clone(){return new rd(null,new Set(this.dimensions),...
method clone (line 1) | clone(){return new rd(null,new Set(this.dimensions),l(this.measures))}
method constructor (line 1) | constructor(e,t,n){super(e),this.dimensions=t,this.measures=n}
method groupBy (line 1) | get groupBy(){return this.dimensions}
method makeFromEncoding (line 1) | static makeFromEncoding(e,t){let n=!1;t.forEachFieldDef((e=>{e.aggrega...
method makeFromTransform (line 1) | static makeFromTransform(e,t){const n=new Set,i={};for(const e of t.ag...
method merge (line 1) | merge(e){return x(this.dimensions,e.dimensions)?(function(e,t){for(con...
method addDimensions (line 1) | addDimensions(e){e.forEach(this.dimensions.add,this.dimensions)}
method dependentFields (line 1) | dependentFields(){return new Set([...this.dimensions,...D(this.measure...
method producedFields (line 1) | producedFields(){const e=new Set;for(const t of D(this.measures))for(c...
method hash (line 1) | hash(){return`Aggregate ${d({dimensions:this.dimensions,measures:this....
method assemble (line 1) | assemble(){const e=[],t=[],n=[];for(const i of D(this.measures))for(co...
class od (line 1) | class od extends vc{constructor(e,n,i,r){super(e),this.model=n,this.name...
method constructor (line 1) | constructor(e,n,i,r){super(e),this.model=n,this.name=i,this.data=r,qn(...
method hash (line 1) | hash(){let e="Facet";for(const t of Re)this[t]&&(e+=` ${t.charAt(0)}:$...
method fields (line 1) | get fields(){const e=[];for(const t of Re)this[t]?.fields&&e.push(...t...
method dependentFields (line 1) | dependentFields(){const e=new Set(this.fields);for(const t of Re)this[...
method producedFields (line 1) | producedFields(){return new Set}
method getSource (line 1) | getSource(){return this.name}
method getChildIndependentFieldsWithStep (line 1) | getChildIndependentFieldsWithStep(){const e={};for(const t of Ft){cons...
method assembleRowColumnHeaderData (line 1) | assembleRowColumnHeaderData(e,t,n){const i={row:"y",column:"x",facet:v...
method assembleFacetHeaderData (line 1) | assembleFacetHeaderData(e){const{columns:t}=this.model.layout,{layoutH...
method assemble (line 1) | assemble(){const e=[];let t=null;const n=this.getChildIndependentField...
function ad (line 1) | function ad(e){return e.startsWith("'")&&e.endsWith("'")||e.startsWith('...
function sd (line 1) | function sd(e){const n={};return a(e.filter,(e=>{if(Ji(e)){let i=null;Ii...
function ld (line 1) | function ld(e){const n={};function i(e){var i;$a(e)?n[e.field]="date":"q...
class cd (line 1) | class cd extends vc{clone(){return new cd(null,l(this._parse))}construct...
method clone (line 1) | clone(){return new cd(null,l(this._parse))}
method constructor (line 1) | constructor(e,t){super(e),qn(this,"_parse",void 0),this._parse=t}
method hash (line 1) | hash(){return`Parse ${d(this._parse)}`}
method makeExplicit (line 1) | static makeExplicit(e,t,n){let i={};const r=t.data;return!sc(r)&&r?.fo...
method makeWithAncestors (line 1) | static makeWithAncestors(e,t,n,i){for(const e of D(n)){const t=i.getWi...
method parse (line 1) | get parse(){return this._parse}
method merge (line 1) | merge(e){this._parse={...this._parse,...e.parse},e.remove()}
method assembleFormatParse (line 1) | assembleFormatParse(){const e={};for(const t of D(this._parse)){const ...
method producedFields (line 1) | producedFields(){return new Set(D(this._parse))}
method dependentFields (line 1) | dependentFields(){return new Set(D(this._parse))}
method assembleTransforms (line 1) | assembleTransforms(){let e=arguments.length>0&&void 0!==arguments[0]&&...
class ud (line 1) | class ud extends vc{clone(){return new ud(null)}constructor(e){super(e)}...
method clone (line 1) | clone(){return new ud(null)}
method constructor (line 1) | constructor(e){super(e)}
method dependentFields (line 1) | dependentFields(){return new Set}
method producedFields (line 1) | producedFields(){return new Set([xs])}
method hash (line 1) | hash(){return"Identifier"}
method assemble (line 1) | assemble(){return{type:"identifier",as:xs}}
class fd (line 1) | class fd extends vc{clone(){return new fd(null,this.params)}constructor(...
method clone (line 1) | clone(){return new fd(null,this.params)}
method constructor (line 1) | constructor(e,t){super(e),this.params=t}
method dependentFields (line 1) | dependentFields(){return new Set}
method producedFields (line 1) | producedFields(){}
method hash (line 1) | hash(){return`Graticule ${d(this.params)}`}
method assemble (line 1) | assemble(){return{type:"graticule",...!0===this.params?{}:this.params}}
class dd (line 1) | class dd extends vc{clone(){return new dd(null,this.params)}constructor(...
method clone (line 1) | clone(){return new dd(null,this.params)}
method constructor (line 1) | constructor(e,t){super(e),this.params=t}
method dependentFields (line 1) | dependentFields(){return new Set}
method producedFields (line 1) | producedFields(){return new Set([this.params.as??"data"])}
method hash (line 1) | hash(){return`Hash ${d(this.params)}`}
method assemble (line 1) | assemble(){return{type:"sequence",...this.params}}
class md (line 1) | class md extends vc{constructor(e){let t;if(super(null),qn(this,"_data",...
method constructor (line 1) | constructor(e){let t;if(super(null),qn(this,"_data",void 0),qn(this,"_...
method dependentFields (line 1) | dependentFields(){return new Set}
method producedFields (line 1) | producedFields(){}
method data (line 1) | get data(){return this._data}
method hasName (line 1) | hasName(){return!!this._name}
method isGenerator (line 1) | get isGenerator(){return this._generator}
method dataName (line 1) | get dataName(){return this._name}
method dataName (line 1) | set dataName(e){this._name=e}
method parent (line 1) | set parent(e){throw new Error("Source nodes have to be roots.")}
method remove (line 1) | remove(){throw new Error("Source nodes are roots and cannot be removed...
method hash (line 1) | hash(){throw new Error("Cannot hash sources")}
method assemble (line 1) | assemble(){return{name:this._name,...this._data,transform:[]}}
function pd (line 1) | function pd(e){return e instanceof md||e instanceof fd||e instanceof dd}
class hd (line 1) | class hd{constructor(){Wn(this,gd,{writable:!0,value:void 0}),Un(this,gd...
method constructor (line 1) | constructor(){Wn(this,gd,{writable:!0,value:void 0}),Un(this,gd,!1)}
method setModified (line 1) | setModified(){Un(this,gd,!0)}
method modifiedFlag (line 1) | get modifiedFlag(){return function(e,t){return t.get?t.get.call(e):t.v...
class yd (line 1) | class yd extends hd{getNodeDepths(e,t,n){n.set(e,t);for(const i of e.chi...
method getNodeDepths (line 1) | getNodeDepths(e,t,n){n.set(e,t);for(const i of e.children)this.getNode...
method optimize (line 1) | optimize(e){const t=[...this.getNodeDepths(e,0,new Map).entries()].sor...
class vd (line 1) | class vd extends hd{optimize(e){this.run(e);for(const t of e.children)th...
method optimize (line 1) | optimize(e){this.run(e);for(const t of e.children)this.optimize(t);ret...
class bd (line 1) | class bd extends vd{mergeNodes(e,t){const n=t.shift();for(const i of t)e...
method mergeNodes (line 1) | mergeNodes(e,t){const n=t.shift();for(const i of t)e.removeChild(i),i....
method run (line 1) | run(e){const t=e.children.map((e=>e.hash())),n={};for(let i=0;i<t.leng...
class xd (line 1) | class xd extends vd{constructor(e){super(),qn(this,"requiresSelectionId"...
method constructor (line 1) | constructor(e){super(),qn(this,"requiresSelectionId",void 0),this.requ...
method run (line 1) | run(e){e instanceof ud&&(this.requiresSelectionId&&(pd(e.parent)||e.pa...
class $d (line 1) | class $d extends hd{optimize(e){return this.run(e,new Set),this.modified...
method optimize (line 1) | optimize(e){return this.run(e,new Set),this.modifiedFlag}
method run (line 1) | run(e,t){let n=new Set;e instanceof wc&&(n=e.producedFields(),$(n,t)&&...
class wd (line 1) | class wd extends vd{constructor(){super()}run(e){e instanceof bc&&!e.isR...
method constructor (line 1) | constructor(){super()}
method run (line 1) | run(e){e instanceof bc&&!e.isRequired()&&(this.setModified(),e.remove())}
class kd (line 1) | class kd extends yd{run(e){if(!(pd(e)||e.numChildren()>1))for(const t of...
method run (line 1) | run(e){if(!(pd(e)||e.numChildren()>1))for(const t of e.children)if(t i...
class Sd (line 1) | class Sd extends yd{run(e){const t=[...e.children],n=e.children.filter((...
method run (line 1) | run(e){const t=[...e.children],n=e.children.filter((e=>e instanceof cd...
class Dd (line 1) | class Dd extends yd{run(e){e instanceof bc||e.numChildren()>0||e instanc...
method run (line 1) | run(e){e instanceof bc||e.numChildren()>0||e instanceof od||e instance...
class Fd (line 1) | class Fd extends yd{run(e){const t=e.children.filter((e=>e instanceof wc...
method run (line 1) | run(e){const t=e.children.filter((e=>e instanceof wc)),n=t.pop();for(c...
class zd (line 1) | class zd extends yd{run(e){const t=e.children.filter((e=>e instanceof rd...
method run (line 1) | run(e){const t=e.children.filter((e=>e instanceof rd)),n={};for(const ...
class Od (line 1) | class Od extends yd{constructor(e){super(),this.model=e}run(e){const t=!...
method constructor (line 1) | constructor(e){super(),this.model=e}
method run (line 1) | run(e){const t=!(pd(e)||e instanceof Bu||e instanceof cd||e instanceof...
class _d (line 1) | class _d extends yd{run(e){const t=[...e.children];if(!g(t,(e=>e instanc...
method run (line 1) | run(e){const t=[...e.children];if(!g(t,(e=>e instanceof bc))||e.numChi...
class Nd (line 1) | class Nd extends vc{clone(){return new Nd(null,l(this.transform))}constr...
method clone (line 1) | clone(){return new Nd(null,l(this.transform))}
method constructor (line 1) | constructor(e,t){super(e),this.transform=t}
method addDimensions (line 1) | addDimensions(e){this.transform.groupby=b(this.transform.groupby.conca...
method dependentFields (line 1) | dependentFields(){const e=new Set;return this.transform.groupby&&this....
method producedFields (line 1) | producedFields(){return new Set(this.transform.joinaggregate.map(this....
method getDefaultName (line 1) | getDefaultName(e){return e.as??oa(e)}
method hash (line 1) | hash(){return`JoinAggregateTransform ${d(this.transform)}`}
method assemble (line 1) | assemble(){const e=[],t=[],n=[];for(const i of this.transform.joinaggr...
class Cd (line 1) | class Cd extends vc{clone(){return new Cd(null,l(this._stack))}construct...
method clone (line 1) | clone(){return new Cd(null,l(this._stack))}
method constructor (line 1) | constructor(e,t){super(e),qn(this,"_stack",void 0),this._stack=t}
method makeFromTransform (line 1) | static makeFromTransform(e,n){const{stack:i,groupby:r,as:o,offset:a="z...
method makeFromEncoding (line 1) | static makeFromEncoding(e,n){const i=n.stack,{encoding:r}=n;if(!i)retu...
method stack (line 1) | get stack(){return this._stack}
method addDimensions (line 1) | addDimensions(e){this._stack.facetby.push(...e)}
method dependentFields (line 1) | dependentFields(){const e=new Set;return e.add(this._stack.stackField)...
method producedFields (line 1) | producedFields(){return new Set(this._stack.as)}
method hash (line 1) | hash(){return`Stack ${d(this._stack)}`}
method getGroupbyFields (line 1) | getGroupbyFields(){const{dimensionFieldDefs:e,impute:t,groupby:n}=this...
method assemble (line 1) | assemble(){const e=[],{facetby:t,dimensionFieldDefs:n,stackField:i,sta...
class Pd (line 1) | class Pd extends vc{clone(){return new Pd(null,l(this.transform))}constr...
method clone (line 1) | clone(){return new Pd(null,l(this.transform))}
method constructor (line 1) | constructor(e,t){super(e),this.transform=t}
method addDimensions (line 1) | addDimensions(e){this.transform.groupby=b(this.transform.groupby.conca...
method dependentFields (line 1) | dependentFields(){const e=new Set;return(this.transform.groupby??[]).f...
method producedFields (line 1) | producedFields(){return new Set(this.transform.window.map(this.getDefa...
method getDefaultName (line 1) | getDefaultName(e){return e.as??oa(e)}
method hash (line 1) | hash(){return`WindowTransform ${d(this.transform)}`}
method assemble (line 1) | assemble(){const e=[],t=[],n=[],i=[];for(const r of this.transform.win...
function Ad (line 1) | function Ad(e){if(e instanceof od)if(1!==e.numChildren()||e.children[0]i...
function jd (line 1) | function jd(e){if(e instanceof bc&&e.type===fc.Main&&1===e.numChildren()...
function Md (line 1) | function Md(e){for(const t of e){for(const e of t.children)if(e.parent!=...
function Ld (line 1) | function Ld(e,t){let n=!1;for(const i of t)n=e.optimize(i)||n;return n}
function qd (line 1) | function qd(e,t,n){let i=e.sources,r=!1;return r=Ld(new wd,i)||r,r=Ld(ne...
class Ud (line 1) | class Ud{constructor(e){qn(this,"signal",void 0),Object.defineProperty(t...
method constructor (line 1) | constructor(e){qn(this,"signal",void 0),Object.defineProperty(this,"si...
method fromName (line 1) | static fromName(e,t){return new Ud((()=>e(t)))}
function Rd (line 1) | function Rd(e){xm(e)?function(e){const t=e.component.scales;for(const n ...
function Wd (line 1) | function Wd(e,t){const n=e.getScaleComponent(t).get("type"),{encoding:i}...
function Bd (line 1) | function Bd(e,t,n){const i=Ui(n)?.unit;return"temporal"===t||i?function(...
function Id (line 1) | function Id(e,n,i,r){const{encoding:o,markDef:a,mark:s,config:l,stack:c}...
function Hd (line 1) | function Hd(e,t){const{op:n,field:i,order:r}=e;return{op:n??(t?"sum":zo)...
function Vd (line 1) | function Vd(e,t){const n=e.component.scales[t],i=e.specifiedScales[t].do...
function Gd (line 1) | function Gd(e,n){const{aggregate:i,type:r}=e;return i?t.isString(i)&&!an...
function Yd (line 1) | function Yd(e,t,n,i){return e.explicit&&t.explicit&&$i(function(e,t,n,i)...
function Xd (line 1) | function Xd(e){const n=b(e.map((e=>{if(bn(e)){const{sort:t,...n}=e;retur...
function Qd (line 1) | function Qd(e){if(bn(e)&&t.isString(e.field))return e.field;if(function(...
function Jd (line 1) | function Jd(e,t){const n=e.component.scales[t].get("domains").map((t=>(b...
function Kd (line 1) | function Kd(e){return km(e)||wm(e)?e.children.reduce(((e,t)=>e.concat(Kd...
function Zd (line 1) | function Zd(e){return D(e.component.scales).reduce(((n,i)=>{const r=e.co...
class em (line 1) | class em extends Jl{constructor(e,t){super({},{name:e}),qn(this,"merged"...
method constructor (line 1) | constructor(e,t){super({},{name:e}),qn(this,"merged",!1),this.setWithE...
method domainDefinitelyIncludesZero (line 1) | domainDefinitelyIncludesZero(){return!1!==this.get("zero")||g(this.get...
function nm (line 1) | function nm(e,n){const i=e.fieldDef(n);if(i?.bin){const{bin:r,field:o}=i...
function im (line 1) | function im(e,n){const i=n.specifiedScales[e],{size:r}=n,o=n.getScaleCom...
function rm (line 1) | function rm(e){return function(e){return!t.isString(e)&&!!e.name}(e)?{sc...
function om (line 1) | function om(e,t,n){let{center:i}=arguments.length>3&&void 0!==arguments[...
function am (line 1) | function am(e,n,i){const{encoding:r}=n,o=n.getScaleComponent(i),a=at(i),...
function sm (line 1) | function sm(e,t){if("offset"===_s({step:e,offsetIsDiscrete:xr(t)}))retur...
function lm (line 1) | function lm(e,t,n){const i=e===Z?"width":"height",r=t[i];return r||Ts(n,i)}
function cm (line 1) | function cm(e,t,n){if(t)return yn(t)?{signal:`${t.signal} ? 0 : ${cm(e,!...
function fm (line 1) | function fm(e,t,n){const i=Ns(e.width)?e.width.step:js(n,"width"),r=Ns(e...
function dm (line 1) | function dm(e,t){xm(e)?function(e,t){const n=e.component.scales,{config:...
function pm (line 1) | function pm(e){xm(e)?function(e){const t=e.component.scales;for(const n ...
function gm (line 1) | function gm(e,t){const n=e.component.scales;for(const n of e.children)"r...
function hm (line 1) | function hm(e,t,n,i){const r=function(e,t,n,i){switch(t.type){case"nomin...
function ym (line 1) | function ym(e){xm(e)?e.component.scales=function(e){const{encoding:t,mar...
class bm (line 1) | class bm{constructor(){qn(this,"nameMap",void 0),this.nameMap={}}rename(...
method constructor (line 1) | constructor(){qn(this,"nameMap",void 0),this.nameMap={}}
method rename (line 1) | rename(e,t){this.nameMap[e]=t}
method has (line 1) | has(e){return void 0!==this.nameMap[e]}
method get (line 1) | get(e){for(;this.nameMap[e]&&e!==this.nameMap[e];)e=this.nameMap[e];re...
function xm (line 1) | function xm(e){return"unit"===e?.type}
function $m (line 1) | function $m(e){return"facet"===e?.type}
function wm (line 1) | function wm(e){return"concat"===e?.type}
function km (line 1) | function km(e){return"layer"===e?.type}
class Sm (line 1) | class Sm{constructor(e,n,i,r,o,a,c){this.type=n,this.parent=i,this.confi...
method constructor (line 1) | constructor(e,n,i,r,o,a,c){this.type=n,this.parent=i,this.config=o,qn(...
method width (line 1) | get width(){return this.getSizeSignalRef("width")}
method height (line 1) | get height(){return this.getSizeSignalRef("height")}
method parse (line 1) | parse(){this.parseScale(),this.parseLayoutSize(),this.renameTopLevelLa...
method parseScale (line 1) | parseScale(){!function(e){let{ignoreRange:t}=arguments.length>1&&void ...
method parseProjection (line 1) | parseProjection(){Jf(this)}
method renameTopLevelLayoutSizeSignal (line 1) | renameTopLevelLayoutSizeSignal(){"width"!==this.getName("width")&&this...
method parseLegends (line 1) | parseLegends(){Rf(this)}
method assembleEncodeFromView (line 1) | assembleEncodeFromView(e){const{style:t,...n}=e,i={};for(const e of D(...
method assembleGroupEncodeEntry (line 1) | assembleGroupEncodeEntry(e){let t={};return this.view&&(t=this.assembl...
method assembleLayout (line 1) | assembleLayout(){if(!this.layout)return;const{spacing:e,...t}=this.lay...
method assembleDefaultLayout (line 1) | assembleDefaultLayout(){return{}}
method assembleHeaderMarks (line 1) | assembleHeaderMarks(){const{layoutHeaders:e}=this.component;let t=[];f...
method assembleAxes (line 1) | assembleAxes(){return function(e,t){const{x:n=[],y:i=[]}=e;return[...n...
method assembleLegends (line 1) | assembleLegends(){return Vf(this)}
method assembleProjections (line 1) | assembleProjections(){return Gf(this)}
method assembleTitle (line 1) | assembleTitle(){const{encoding:e,...t}=this.title??{},n={...gn(this.co...
method assembleGroup (line 1) | assembleGroup(){let e=arguments.length>0&&void 0!==arguments[0]?argume...
method getName (line 1) | getName(e){return _((this.name?`${this.name}_`:"")+e)}
method getDataName (line 1) | getDataName(e){return this.getName(fc[e].toLowerCase())}
method requestDataName (line 1) | requestDataName(e){const t=this.getDataName(e),n=this.component.data.o...
method getSizeSignalRef (line 1) | getSizeSignalRef(e){if($m(this.parent)){const t=Nt(Ff(e)),n=this.compo...
method lookupDataSource (line 1) | lookupDataSource(e){const t=this.component.data.outputNodes[e];return ...
method getSignalName (line 1) | getSignalName(e){return this.signalNameMap.get(e)}
method renameSignal (line 1) | renameSignal(e,t){this.signalNameMap.rename(e,t)}
method renameScale (line 1) | renameScale(e,t){this.scaleNameMap.rename(e,t)}
method renameProjection (line 1) | renameProjection(e,t){this.projectionNameMap.rename(e,t)}
method scaleName (line 1) | scaleName(e,t){return t?this.getName(e):Ke(e)&&Ht(e)&&this.component.s...
method projectionName (line 1) | projectionName(e){return e?this.getName("projection"):this.component.p...
method getScaleComponent (line 1) | getScaleComponent(e){if(!this.component.scales)throw new Error("getSca...
method getSelectionComponent (line 1) | getSelectionComponent(e,t){let n=this.component.selection[e];if(!n&&th...
method hasAxisOrientSignalRef (line 1) | hasAxisOrientSignalRef(){return this.component.axes.x?.some((e=>e.hasO...
class Dm (line 1) | class Dm extends Sm{vgField(e){let t=arguments.length>1&&void 0!==argume...
method vgField (line 1) | vgField(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1...
method reduceFieldDef (line 1) | reduceFieldDef(e,n){return function(e,n,i,r){return e?D(e).reduce(((i,...
method forEachFieldDef (line 1) | forEachFieldDef(e,t){Wa(this.getMapping(),((t,n)=>{const i=pa(t);i&&e(...
class Fm (line 1) | class Fm extends vc{clone(){return new Fm(null,l(this.transform))}constr...
method clone (line 1) | clone(){return new Fm(null,l(this.transform))}
method constructor (line 1) | constructor(e,t){super(e),this.transform=t,this.transform=l(t);const n...
method dependentFields (line 1) | dependentFields(){return new Set([this.transform.density,...this.trans...
method producedFields (line 1) | producedFields(){return new Set(this.transform.as)}
method hash (line 1) | hash(){return`DensityTransform ${d(this.transform)}`}
method assemble (line 1) | assemble(){const{density:e,...t}=this.transform,n={type:"kde",field:e,...
class zm (line 1) | class zm extends vc{clone(){return new zm(null,l(this.transform))}constr...
method clone (line 1) | clone(){return new zm(null,l(this.transform))}
method constructor (line 1) | constructor(e,t){super(e),this.transform=t,this.transform=l(t)}
method dependentFields (line 1) | dependentFields(){return new Set([this.transform.extent])}
method producedFields (line 1) | producedFields(){return new Set([])}
method hash (line 1) | hash(){return`ExtentTransform ${d(this.transform)}`}
method assemble (line 1) | assemble(){const{extent:e,param:t}=this.transform;return{type:"extent"...
class Om (line 1) | class Om extends vc{clone(){return new Om(null,{...this.filter})}constru...
method clone (line 1) | clone(){return new Om(null,{...this.filter})}
method constructor (line 1) | constructor(e,t){super(e),this.filter=t}
method make (line 1) | static make(e,t){const{config:n,mark:i,markDef:r}=t;if("filter"!==Cn("...
method dependentFields (line 1) | dependentFields(){return new Set(D(this.filter))}
method producedFields (line 1) | producedFields(){return new Set}
method hash (line 1) | hash(){return`FilterInvalid ${d(this.filter)}`}
method assemble (line 1) | assemble(){const e=D(this.filter).reduce(((e,t)=>{const n=this.filter[...
class _m (line 1) | class _m extends vc{clone(){return new _m(this.parent,l(this.transform))...
method clone (line 1) | clone(){return new _m(this.parent,l(this.transform))}
method constructor (line 1) | constructor(e,t){super(e),this.transform=t,this.transform=l(t);const{f...
method dependentFields (line 1) | dependentFields(){return new Set(this.transform.flatten)}
method producedFields (line 1) | producedFields(){return new Set(this.transform.as)}
method hash (line 1) | hash(){return`FlattenTransform ${d(this.transform)}`}
method assemble (line 1) | assemble(){const{flatten:e,as:t}=this.transform;return{type:"flatten",...
class Nm (line 1) | class Nm extends vc{clone(){return new Nm(null,l(this.transform))}constr...
method clone (line 1) | clone(){return new Nm(null,l(this.transform))}
method constructor (line 1) | constructor(e,t){super(e),this.transform=t,this.transform=l(t);const n...
method dependentFields (line 1) | dependentFields(){return new Set(this.transform.fold)}
method producedFields (line 1) | producedFields(){return new Set(this.transform.as)}
method hash (line 1) | hash(){return`FoldTransform ${d(this.transform)}`}
method assemble (line 1) | assemble(){const{fold:e,as:t}=this.transform;return{type:"fold",fields...
class Cm (line 1) | class Cm extends vc{clone(){return new Cm(null,l(this.fields),this.geojs...
method clone (line 1) | clone(){return new Cm(null,l(this.fields),this.geojson,this.signal)}
method parseAll (line 1) | static parseAll(e,t){if(t.component.projection&&!t.component.projectio...
method constructor (line 1) | constructor(e,t,n,i){super(e),this.fields=t,this.geojson=n,this.signal=i}
method dependentFields (line 1) | dependentFields(){const e=(this.fields??[]).filter(t.isString);return ...
method producedFields (line 1) | producedFields(){return new Set}
method hash (line 1) | hash(){return`GeoJSON ${this.geojson} ${this.signal} ${d(this.fields)}`}
method assemble (line 1) | assemble(){return[...this.geojson?[{type:"filter",expr:`isValid(datum[...
class Pm (line 1) | class Pm extends vc{clone(){return new Pm(null,this.projection,l(this.fi...
method clone (line 1) | clone(){return new Pm(null,this.projection,l(this.fields),l(this.as))}
method constructor (line 1) | constructor(e,t,n,i){super(e),this.projection=t,this.fields=n,this.as=i}
method parseAll (line 1) | static parseAll(e,t){if(!t.projectionName())return e;for(const n of[[u...
method dependentFields (line 1) | dependentFields(){return new Set(this.fields.filter(t.isString))}
method producedFields (line 1) | producedFields(){return new Set(this.as)}
method hash (line 1) | hash(){return`Geopoint ${this.projection} ${d(this.fields)} ${d(this.a...
method assemble (line 1) | assemble(){return{type:"geopoint",projection:this.projection,fields:th...
class Am (line 1) | class Am extends vc{clone(){return new Am(null,l(this.transform))}constr...
method clone (line 1) | clone(){return new Am(null,l(this.transform))}
method constructor (line 1) | constructor(e,t){super(e),this.transform=t}
method dependentFields (line 1) | dependentFields(){return new Set([this.transform.impute,this.transform...
method producedFields (line 1) | producedFields(){return new Set([this.transform.impute])}
method processSequence (line 1) | processSequence(e){const{start:t=0,stop:n,step:i}=e;return{signal:`seq...
method makeFromTransform (line 1) | static makeFromTransform(e,t){return new Am(e,t)}
method makeFromEncoding (line 1) | static makeFromEncoding(e,t){const n=t.encoding,i=n.x,r=n.y;if(Ho(i)&&...
method hash (line 1) | hash(){return`Impute ${d(this.transform)}`}
method assemble (line 1) | assemble(){const{impute:e,key:t,keyvals:n,method:i,groupby:r,value:o,f...
class jm (line 1) | class jm extends vc{clone(){return new jm(null,l(this.transform))}constr...
method clone (line 1) | clone(){return new jm(null,l(this.transform))}
method constructor (line 1) | constructor(e,t){super(e),this.transform=t,this.transform=l(t);const n...
method dependentFields (line 1) | dependentFields(){return new Set([this.transform.loess,this.transform....
method producedFields (line 1) | producedFields(){return new Set(this.transform.as)}
method hash (line 1) | hash(){return`LoessTransform ${d(this.transform)}`}
method assemble (line 1) | assemble(){const{loess:e,on:t,...n}=this.transform;return{type:"loess"...
class Tm (line 1) | class Tm extends vc{clone(){return new Tm(null,l(this.transform),this.se...
method clone (line 1) | clone(){return new Tm(null,l(this.transform),this.secondary)}
method constructor (line 1) | constructor(e,t,n){super(e),this.transform=t,this.secondary=n}
method make (line 1) | static make(e,t,n,i){const r=t.component.data.sources,{from:o}=n;let a...
method dependentFields (line 1) | dependentFields(){return new Set([this.transform.lookup])}
method producedFields (line 1) | producedFields(){return new Set(this.transform.as?t.array(this.transfo...
method hash (line 1) | hash(){return`Lookup ${d({transform:this.transform,secondary:this.seco...
method assemble (line 1) | assemble(){let e;if(this.transform.from.fields)e={values:this.transfor...
class Em (line 1) | class Em extends vc{clone(){return new Em(null,l(this.transform))}constr...
method clone (line 1) | clone(){return new Em(null,l(this.transform))}
method constructor (line 1) | constructor(e,t){super(e),this.transform=t,this.transform=l(t);const n...
method dependentFields (line 1) | dependentFields(){return new Set([this.transform.quantile,...this.tran...
method producedFields (line 1) | producedFields(){return new Set(this.transform.as)}
method hash (line 1) | hash(){return`QuantileTransform ${d(this.transform)}`}
method assemble (line 1) | assemble(){const{quantile:e,...t}=this.transform;return{type:"quantile...
class Mm (line 1) | class Mm extends vc{clone(){return new Mm(null,l(this.transform))}constr...
method clone (line 1) | clone(){return new Mm(null,l(this.transform))}
method constructor (line 1) | constructor(e,t){super(e),this.transform=t,this.transform=l(t);const n...
method dependentFields (line 1) | dependentFields(){return new Set([this.transform.regression,this.trans...
method producedFields (line 1) | producedFields(){return new Set(this.transform.as)}
method hash (line 1) | hash(){return`RegressionTransform ${d(this.transform)}`}
method assemble (line 1) | assemble(){const{regression:e,on:t,...n}=this.transform;return{type:"r...
class Lm (line 1) | class Lm extends vc{clone(){return new Lm(null,l(this.transform))}constr...
method clone (line 1) | clone(){return new Lm(null,l(this.transform))}
method constructor (line 1) | constructor(e,t){super(e),this.transform=t}
method addDimensions (line 1) | addDimensions(e){this.transform.groupby=b((this.transform.groupby??[])...
method producedFields (line 1) | producedFields(){}
method dependentFields (line 1) | dependentFields(){return new Set([this.transform.pivot,this.transform....
method hash (line 1) | hash(){return`PivotTransform ${d(this.transform)}`}
method assemble (line 1) | assemble(){const{pivot:e,value:t,groupby:n,limit:i,op:r}=this.transfor...
class qm (line 1) | class qm extends vc{clone(){return new qm(null,l(this.transform))}constr...
method clone (line 1) | clone(){return new qm(null,l(this.transform))}
method constructor (line 1) | constructor(e,t){super(e),this.transform=t}
method dependentFields (line 1) | dependentFields(){return new Set}
method producedFields (line 1) | producedFields(){return new Set}
method hash (line 1) | hash(){return`SampleTransform ${d(this.transform)}`}
method assemble (line 1) | assemble(){return{type:"sample",size:this.transform.sample}}
function Um (line 1) | function Um(e){let t=0;return function n(i,r){if(i instanceof md&&!i.isG...
function Rm (line 1) | function Rm(e){return"top"===e||"left"===e||yn(e)?"header":"footer"}
function Wm (line 1) | function Wm(e,n){const{facet:i,config:r,child:o,component:a}=e;if(e.chan...
function Bm (line 1) | function Bm(e,t,n){const i="row"===t?"height":"width";return{labels:n,si...
function Im (line 1) | function Im(e,t){const{child:n}=e;if(n.component.axes[t]){const{layoutHe...
function Hm (line 1) | function Hm(e){for(const t of e.children)t.parseLayoutSize()}
function Vm (line 1) | function Vm(e,t){const n=Ff(t),i=Nt(n),r=e.component.resolve,o=e.compone...
function Gm (line 1) | function Gm(e,t){const n="width"===t?"x":"y",i=e.config,r=e.getScaleComp...
function Ym (line 1) | function Ym(e,t,n){return oa(t,{suffix:`by_${oa(e)}`,...n??{}})}
class Xm (line 1) | class Xm extends Dm{constructor(e,t,n,i){super(e,"facet",t,n,i,e.resolve...
method constructor (line 1) | constructor(e,t,n,i){super(e,"facet",t,n,i,e.resolve),qn(this,"facet",...
method initFacet (line 1) | initFacet(e){if(!Ao(e))return{facet:this.initFacetFieldDef(e,"facet")}...
method initFacetFieldDef (line 1) | initFacetFieldDef(e,t){const n=va(e,t);return n.header?n.header=pn(n.h...
method channelHasField (line 1) | channelHasField(e){return!!this.facet[e]}
method fieldDef (line 1) | fieldDef(e){return this.facet[e]}
method parseData (line 1) | parseData(){this.component.data=Jm(this),this.child.parseData()}
method parseLayoutSize (line 1) | parseLayoutSize(){Hm(this)}
method parseSelections (line 1) | parseSelections(){this.child.parseSelections(),this.component.selectio...
method parseMarkGroup (line 1) | parseMarkGroup(){this.child.parseMarkGroup()}
method parseAxesAndHeaders (line 1) | parseAxesAndHeaders(){this.child.parseAxesAndHeaders(),function(e){for...
method assembleSelectionTopLevelSignals (line 1) | assembleSelectionTopLevelSignals(e){return this.child.assembleSelectio...
method assembleSignals (line 1) | assembleSignals(){return this.child.assembleSignals(),[]}
method assembleSelectionData (line 1) | assembleSelectionData(e){return this.child.assembleSelectionData(e)}
method getHeaderLayoutMixins (line 1) | getHeaderLayoutMixins(){const e={};for(const t of Re)for(const n of ff...
method assembleDefaultLayout (line 1) | assembleDefaultLayout(){const{column:e,row:t}=this.facet,n=e?this.colu...
method assembleLayoutSignals (line 1) | assembleLayoutSignals(){return this.child.assembleLayoutSignals()}
method columnDistinctSignal (line 1) | columnDistinctSignal(){if(!(this.parent&&this.parent instanceof Xm)){r...
method assembleGroupStyle (line 1) | assembleGroupStyle(){}
method assembleGroup (line 1) | assembleGroup(e){return this.parent&&this.parent instanceof Xm?{...thi...
method getCardinalityAggregateForChild (line 1) | getCardinalityAggregateForChild(){const e=[],t=[],n=[];if(this.child i...
method assembleFacet (line 1) | assembleFacet(){const{name:e,data:n}=this.component.data.facetRoot,{ro...
method facetSortFields (line 1) | facetSortFields(e){const{facet:n}=this,i=n[e];return i?Co(i.sort)?[Ym(...
method facetSortOrder (line 1) | facetSortOrder(e){const{facet:n}=this,i=n[e];if(i){const{sort:e}=i;ret...
method assembleLabelTitle (line 1) | assembleLabelTitle(){const{facet:e,config:t}=this;if(e.facet)return yf...
method assembleMarks (line 1) | assembleMarks(){const{child:e}=this,t=function(e){const t=[],n=Um(t);f...
method getMapping (line 1) | getMapping(){return this.facet}
function Qm (line 1) | function Qm(e,t){for(const n of t){const t=n.data;if(e.name&&n.hasName()...
function Jm (line 1) | function Jm(e){let t=function(e,t){if(e.data||!e.parent){if(null===e.dat...
class Km (line 1) | class Km extends Sm{constructor(e,t,n,i){super(e,"concat",t,n,i,e.resolv...
method constructor (line 1) | constructor(e,t,n,i){super(e,"concat",t,n,i,e.resolve),qn(this,"childr...
method parseData (line 1) | parseData(){this.component.data=Jm(this);for(const e of this.children)...
method parseSelections (line 1) | parseSelections(){this.component.selection={};for(const e of this.chil...
method parseMarkGroup (line 1) | parseMarkGroup(){for(const e of this.children)e.parseMarkGroup()}
method parseAxesAndHeaders (line 1) | parseAxesAndHeaders(){for(const e of this.children)e.parseAxesAndHeade...
method getChildren (line 1) | getChildren(e){return zs(e)?e.vconcat:Os(e)?e.hconcat:e.concat}
method parseLayoutSize (line 1) | parseLayoutSize(){!function(e){Hm(e);const t=1===e.layout.columns?"wid...
method parseAxisGroup (line 1) | parseAxisGroup(){return null}
method assembleSelectionTopLevelSignals (line 1) | assembleSelectionTopLevelSignals(e){return this.children.reduce(((e,t)...
method assembleSignals (line 1) | assembleSignals(){return this.children.forEach((e=>e.assembleSignals()...
method assembleLayoutSignals (line 1) | assembleLayoutSignals(){const e=wf(this);for(const t of this.children)...
method assembleSelectionData (line 1) | assembleSelectionData(e){return this.children.reduce(((e,t)=>t.assembl...
method assembleMarks (line 1) | assembleMarks(){return this.children.map((e=>{const t=e.assembleTitle(...
method assembleGroupStyle (line 1) | assembleGroupStyle(){}
method assembleDefaultLayout (line 1) | assembleDefaultLayout(){const e=this.layout.columns;return{...null!=e?...
class tp (line 1) | class tp extends Jl{constructor(){let e=arguments.length>0&&void 0!==arg...
method constructor (line 1) | constructor(){let e=arguments.length>0&&void 0!==arguments[0]?argument...
method clone (line 1) | clone(){return new tp(l(this.explicit),l(this.implicit),this.mainExtra...
method hasAxisPart (line 1) | hasAxisPart(e){return"axis"===e||("grid"===e||"title"===e?!!this.get(e...
method hasOrientSignalRef (line 1) | hasOrientSignalRef(){return yn(this.explicit.orient)}
function ip (line 1) | function ip(e,t){if(!e)return t.map((e=>e.clone()));{if(e.length!==t.len...
function rp (line 1) | function rp(e,t){for(const n of ep){const i=nc(e.getWithExplicit(n),t.ge...
function op (line 1) | function op(e,t,n,i,r){if("disable"===t)return void 0!==n;switch(n=n||{}...
function sp (line 1) | function sp(e,t){let n=t.axis(e);const i=new tp,r=ga(t.encoding[e]),{mar...
function lp (line 1) | function lp(e,t){const{config:n}=e;return{...du(e,{align:"ignore",baseli...
function cp (line 1) | function cp(e,t,n){return n?{shape:{value:n}}:Xc("shape",e)}
function fp (line 1) | function fp(e,t,n){if(void 0===Cn("align",e,n))return"center"}
function dp (line 1) | function dp(e,t,n){if(void 0===Cn("baseline",e,n))return"middle"}
function pp (line 1) | function pp(e){const{config:n,markDef:i}=e,{orient:r}=i,o="horizontal"==...
function hp (line 1) | function hp(e){if(p([Ur,Mr,Vr],e.mark)){const t=Ba(e.mark,e.encoding);if...
function bp (line 1) | function bp(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments...
class xp (line 1) | class xp extends Dm{constructor(e,n,i){let r=arguments.length>3&&void 0!...
method constructor (line 1) | constructor(e,n,i){let r=arguments.length>3&&void 0!==arguments[3]?arg...
method hasProjection (line 1) | get hasProjection(){const{encoding:e}=this,t=this.mark===Xr,n=e&&Me.so...
method scaleDomain (line 1) | scaleDomain(e){const t=this.specifiedScales[e];return t?t.domain:void 0}
method axis (line 1) | axis(e){return this.specifiedAxes[e]}
method legend (line 1) | legend(e){return this.specifiedLegends[e]}
method initScales (line 1) | initScales(e,t){return It.reduce(((e,n)=>{const i=ga(t[n]);return i&&(...
method initScale (line 1) | initScale(e){const{domain:n,range:i}=e,r=pn(e);return t.isArray(n)&&(r...
method initAxes (line 1) | initAxes(e){return Ft.reduce(((t,n)=>{const i=e[n];if(Jo(i)||n===Z&&Jo...
method initAxis (line 1) | initAxis(e){const t=D(e),n={};for(const i of t){const t=e[i];n[i]=Fa(t...
method initLegends (line 1) | initLegends(e){return Wt.reduce(((t,n)=>{const i=ga(e[n]);if(i&&functi...
method parseData (line 1) | parseData(){this.component.data=Jm(this)}
method parseLayoutSize (line 1) | parseLayoutSize(){!function(e){const{size:t,component:n}=e;for(const i...
method parseSelections (line 1) | parseSelections(){this.component.selection=function(e,n){const i={},r=...
method parseMarkGroup (line 1) | parseMarkGroup(){this.component.mark=hp(this)}
method parseAxesAndHeaders (line 1) | parseAxesAndHeaders(){var e;this.component.axes=(e=this,Ft.reduce(((t,...
method assembleSelectionTopLevelSignals (line 1) | assembleSelectionTopLevelSignals(e){return function(e,n){let i=!1;for(...
method assembleSignals (line 1) | assembleSignals(){return[...Xu(this),...pc(this,[])]}
method assembleSelectionData (line 1) | assembleSelectionData(e){return function(e,t){const n=[...t],i=Mu(e,{e...
method assembleLayout (line 1) | assembleLayout(){return null}
method assembleLayoutSignals (line 1) | assembleLayoutSignals(){return wf(this)}
method assembleMarks (line 1) | assembleMarks(){let e=this.component.mark??[];return this.parent&&km(t...
method assembleGroupStyle (line 1) | assembleGroupStyle(){const{style:e}=this.view||{};return void 0!==e?e:...
method getMapping (line 1) | getMapping(){return this.encoding}
method mark (line 1) | get mark(){return this.markDef.type}
method channelHasField (line 1) | channelHasField(e){return Ta(this.encoding,e)}
method fieldDef (line 1) | fieldDef(e){return pa(this.encoding[e])}
method typedFieldDef (line 1) | typedFieldDef(e){const t=this.fieldDef(e);return Ko(t)?t:null}
class $p (line 1) | class $p extends Sm{constructor(e,t,n,i,r){super(e,"layer",t,n,r,e.resol...
method constructor (line 1) | constructor(e,t,n,i,r){super(e,"layer",t,n,r,e.resolve,e.view),qn(this...
method parseData (line 1) | parseData(){this.component.data=Jm(this);for(const e of this.children)...
method parseLayoutSize (line 1) | parseLayoutSize(){var e;Hm(e=this),Vm(e,"width"),Vm(e,"height")}
method parseSelections (line 1) | parseSelections(){this.component.selection={};for(const e of this.chil...
method parseMarkGroup (line 1) | parseMarkGroup(){for(const e of this.children)e.parseMarkGroup()}
method parseAxesAndHeaders (line 1) | parseAxesAndHeaders(){!function(e){const{axes:t,resolve:n}=e.component...
method assembleSelectionTopLevelSignals (line 1) | assembleSelectionTopLevelSignals(e){return this.children.reduce(((e,t)...
method assembleSignals (line 1) | assembleSignals(){return this.children.reduce(((e,t)=>e.concat(t.assem...
method assembleLayoutSignals (line 1) | assembleLayoutSignals(){return this.children.reduce(((e,t)=>e.concat(t...
method assembleSelectionData (line 1) | assembleSelectionData(e){return this.children.reduce(((e,t)=>t.assembl...
method assembleGroupStyle (line 1) | assembleGroupStyle(){const e=new Set;for(const n of this.children)for(...
method assembleTitle (line 1) | assembleTitle(){let e=super.assembleTitle();if(e)return e;for(const t ...
method assembleLayout (line 1) | assembleLayout(){return null}
method assembleMarks (line 1) | assembleMarks(){return function(e,t){for(const n of e.children)xm(n)&&...
method assembleLegends (line 1) | assembleLegends(){return this.children.reduce(((e,t)=>e.concat(t.assem...
function wp (line 1) | function wp(e,t,n,i,r){if(To(e))return new Xm(e,t,n,r);if(Xs(e))return n...
FILE: docs/_static/js/vega@5.js
function e (line 1) | function e(t,e,n){return t.fields=e||[],t.fname=n,t}
function n (line 1) | function n(t){return null==t?null:t.fname}
function r (line 1) | function r(t){return null==t?null:t.fields}
function i (line 1) | function i(t){return 1===t.length?o(t[0]):a(t)}
function s (line 1) | function s(t){throw Error(t)}
function u (line 1) | function u(t){const e=[],n=t.length;let r,i,o,a=null,u=0,l="";function c...
function l (line 1) | function l(t,n,r){const o=u(t);return t=1===o.length?o[0]:t,e((r&&r.get|...
function m (line 1) | function m(t,e,n){const r=[e].concat([].slice.call(n));console[t].apply(...
function w (line 1) | function w(t,e){let n=arguments.length>2&&void 0!==arguments[2]?argument...
function A (line 1) | function A(t){return t===Object(t)}
function E (line 1) | function E(){for(var t=arguments.length,e=new Array(t),n=0;n<t;n++)e[n]=...
function D (line 1) | function D(t,e,n,r){if(!M(e))return;let i,o;if(A(n)&&!k(n))for(i in o=A(...
function C (line 1) | function C(t,e){if(null==t)return e;const n={},r=[];function i(t){n[t.na...
function F (line 1) | function F(t){return t[t.length-1]}
function S (line 1) | function S(t){return null==t||""===t?null:+t}
function O (line 1) | function O(t,e,n,r){const i=n(t[0]),o=n(F(t)),a=(o-i)*e;return[r(i-a),r(...
function R (line 1) | function R(t,e){return O(t,e,S,f)}
function U (line 1) | function U(t,e){var n=Math.sign(t[0]);return O(t,e,T(n),$(n))}
function L (line 1) | function L(t,e,n){return O(t,e,N(n),N(1/n))}
function q (line 1) | function q(t,e,n){return O(t,e,B(n),z(n))}
function P (line 1) | function P(t,e,n,r,i){const o=r(t[0]),a=r(F(t)),s=null!=e?r(e):(o+a)/2;r...
function j (line 1) | function j(t,e,n){return P(t,e,n,S,f)}
function I (line 1) | function I(t,e,n){const r=Math.sign(t[0]);return P(t,e,n,T(r),$(r))}
function W (line 1) | function W(t,e,n,r){return P(t,e,n,N(r),N(1/r))}
function H (line 1) | function H(t,e,n,r){return P(t,e,n,B(r),z(r))}
function Y (line 1) | function Y(t){return 1+~~(new Date(t).getMonth()/3)}
function G (line 1) | function G(t){return 1+~~(new Date(t).getUTCMonth()/3)}
function V (line 1) | function V(t){return null!=t?k(t)?t:[t]:[]}
function X (line 1) | function X(t,e,n){let r,i=t[0],o=t[1];return o<i&&(r=o,o=i,i=r),r=o-i,r>...
function J (line 1) | function J(t){return"function"==typeof t}
function Q (line 1) | function Q(t,n,i){i=i||{},n=V(n)||[];const o=[],a=[],s={},u=i.comparator...
function rt (line 1) | function rt(t){return J(t)?t:()=>t}
function it (line 1) | function it(t,e){let n;return r=>{n&&clearTimeout(n),n=setTimeout((()=>(...
function ot (line 1) | function ot(t){for(let e,n,r=1,i=arguments.length;r<i;++r)for(n in e=arg...
function at (line 1) | function at(t,e){let n,r,i,o,a=0;if(t&&(n=t.length))if(null==e){for(r=t[...
function st (line 1) | function st(t,e){const n=t.length;let r,i,o,a,s,u=-1;if(null==e){for(;++...
function lt (line 1) | function lt(t,e){return ut.call(t,e)}
function ft (line 1) | function ft(t){let e,n={};function r(t){return lt(n,t)&&n[t]!==ct}const ...
function ht (line 1) | function ht(t,e,n,r,i,o){if(!n&&0!==n)return o;const a=+n;let s,u=t[0],l...
function dt (line 1) | function dt(t,e,n){const r=t.prototype=Object.create(e.prototype);return...
function pt (line 1) | function pt(t,e,n,r){let i,o=e[0],a=e[e.length-1];return o>a&&(i=o,o=a,a...
function gt (line 1) | function gt(t){return"boolean"==typeof t}
function mt (line 1) | function mt(t){return"[object Date]"===Object.prototype.toString.call(t)}
function yt (line 1) | function yt(t){return t&&J(t[Symbol.iterator])}
function vt (line 1) | function vt(t){return"number"==typeof t}
function _t (line 1) | function _t(t){return"[object RegExp]"===Object.prototype.toString.call(t)}
function xt (line 1) | function xt(t){return"string"==typeof t}
function bt (line 1) | function bt(t,n,r){t&&(t=n?V(t).map((t=>t.replace(/\\(.)/g,"$1"))):V(t))...
function wt (line 1) | function wt(t,e){const n=t[0],r=F(t),i=+e;return i?1===i?r:n+i*(r-n):n}
function kt (line 1) | function kt(t){let e,n,r;t=+t||1e4;const i=()=>{e={},n={},r=0},o=(i,o)=>...
function At (line 1) | function At(t,e,n,r){const i=e.length,o=n.length;if(!o)return e;if(!i)re...
function Mt (line 1) | function Mt(t,e){let n="";for(;--e>=0;)n+=t;return n}
function Et (line 1) | function Et(t,e,n,r){const i=n||" ",o=t+"",a=e-o.length;return a<=0?o:"l...
function Dt (line 1) | function Dt(t){return t&&F(t)-t[0]||0}
function Ct (line 1) | function Ct(t){return k(t)?"["+t.map(Ct)+"]":A(t)||xt(t)?JSON.stringify(...
function Ft (line 1) | function Ft(t){return null==t||""===t?null:!(!t||"false"===t||"0"===t)&&...
function $t (line 1) | function $t(t,e){return e=e||St,null==t||""===t?null:e(t)}
function Tt (line 1) | function Tt(t){return null==t||""===t?null:t+""}
function Bt (line 1) | function Bt(t){const e={},n=t.length;for(let r=0;r<n;++r)e[t[r]]=!0;retu...
function zt (line 1) | function zt(t,e,n,r){const i=null!=r?r:"…",o=t+"",a=o.length,s=Math.max(...
function Nt (line 1) | function Nt(t,e,n){if(t)if(e){const r=t.length;for(let i=0;i<r;++i){cons...
function Pt (line 1) | function Pt(t){return new Function("d","return {"+t.map((function(t,e){r...
function jt (line 1) | function jt(t){var e=Object.create(null),n=[];return t.forEach((function...
function It (line 1) | function It(t,e){var n=t+"",r=n.length;return r<e?new Array(e-r+1).join(...
function Wt (line 1) | function Wt(t){var e,n=t.getUTCHours(),r=t.getUTCMinutes(),i=t.getUTCSec...
function Ht (line 1) | function Ht(t){var e=new RegExp('["'+t+"\n\r]"),n=t.charCodeAt(0);functi...
function Yt (line 1) | function Yt(t){return t}
function Gt (line 1) | function Gt(t,e){return"string"==typeof e&&(e=t.objects[e]),"GeometryCol...
function Vt (line 1) | function Vt(t,e){var n=e.id,r=e.bbox,i=null==e.properties?{}:e.propertie...
function Xt (line 1) | function Xt(t,e){var n=function(t){if(null==t)return Yt;var e,n,r=t.scal...
function Jt (line 1) | function Jt(t,e){var n={},r={},i={},o=[],a=-1;function s(t,e){for(var r ...
function Zt (line 1) | function Zt(t){return Xt(t,Qt.apply(this,arguments))}
function Qt (line 1) | function Qt(t,e,n){var r,i,o;if(arguments.length>1)r=function(t,e,n){var...
function Kt (line 1) | function Kt(t,e){return null==t||null==e?NaN:t<e?-1:t>e?1:t>=e?0:NaN}
function te (line 1) | function te(t,e){return null==t||null==e?NaN:e<t?-1:e>t?1:e>=t?0:NaN}
function ee (line 1) | function ee(t){let e,n,r;function i(t,r){let i=arguments.length>2&&void ...
function ne (line 1) | function ne(){return 0}
function re (line 1) | function re(t){return null===t?NaN:+t}
class se (line 1) | class se{constructor(){this._partials=new Float64Array(32),this._n=0}add...
method constructor (line 1) | constructor(){this._partials=new Float64Array(32),this._n=0}
method add (line 1) | add(t){const e=this._partials;let n=0;for(let r=0;r<this._n&&r<32;r++)...
method valueOf (line 1) | valueOf(){const t=this._partials;let e,n,r,i=this._n,o=0;if(i>0){for(o...
class ue (line 1) | class ue extends Map{constructor(t){let e=arguments.length>1&&void 0!==a...
method constructor (line 1) | constructor(t){let e=arguments.length>1&&void 0!==arguments[1]?argumen...
method get (line 1) | get(t){return super.get(ce(this,t))}
method has (line 1) | has(t){return super.has(ce(this,t))}
method set (line 1) | set(t,e){return super.set(fe(this,t),e)}
method delete (line 1) | delete(t){return super.delete(he(this,t))}
class le (line 1) | class le extends Set{constructor(t){let e=arguments.length>1&&void 0!==a...
method constructor (line 1) | constructor(t){let e=arguments.length>1&&void 0!==arguments[1]?argumen...
method has (line 1) | has(t){return super.has(ce(this,t))}
method add (line 1) | add(t){return super.add(fe(this,t))}
method delete (line 1) | delete(t){return super.delete(he(this,t))}
function ce (line 1) | function ce(t,e){let{_intern:n,_key:r}=t;const i=r(e);return n.has(i)?n....
function fe (line 1) | function fe(t,e){let{_intern:n,_key:r}=t;const i=r(e);return n.has(i)?n....
function he (line 1) | function he(t,e){let{_intern:n,_key:r}=t;const i=r(e);return n.has(i)&&(...
function de (line 1) | function de(t){return null!==t&&"object"==typeof t?t.valueOf():t}
function pe (line 1) | function pe(t,e){return(null==t||!(t>=t))-(null==e||!(e>=e))||(t<e?-1:t>...
function ve (line 1) | function ve(t,e,n){const r=(e-t)/Math.max(0,n),i=Math.floor(Math.log10(r...
function _e (line 1) | function _e(t,e,n){if(!((n=+n)>0))return[];if((t=+t)===(e=+e))return[t];...
function xe (line 1) | function xe(t,e,n){return ve(t=+t,e=+e,n=+n)[2]}
function be (line 1) | function be(t,e,n){n=+n;const r=(e=+e)<(t=+t),i=r?xe(e,t,n):xe(t,e,n);re...
function we (line 1) | function we(t,e){let n;if(void 0===e)for(const e of t)null!=e&&(n<e||voi...
function ke (line 1) | function ke(t,e){let n;if(void 0===e)for(const e of t)null!=e&&(n>e||voi...
function Ae (line 1) | function Ae(t,e){let n=arguments.length>2&&void 0!==arguments[2]?argumen...
function Me (line 1) | function Me(t,e,n){const r=t[e];t[e]=t[n],t[n]=r}
function Ee (line 1) | function Ee(t,e,n){if(t=Float64Array.from(function*(t,e){if(void 0===e)f...
function De (line 1) | function De(t,e){let n=arguments.length>2&&void 0!==arguments[2]?argumen...
function Ce (line 1) | function Ce(t,e){return Ee(t,.5,e)}
function Fe (line 1) | function Fe(t){return Array.from(function*(t){for(const e of t)yield*e}(...
function Se (line 1) | function Se(t,e,n){t=+t,e=+e,n=(i=arguments.length)<2?(e=t,t=0,1):i<3?1:...
function $e (line 1) | function $e(t,e){let n=0;if(void 0===e)for(let e of t)(e=+e)&&(n+=e);els...
function Te (line 1) | function Te(t){return t instanceof le?t:new le(t)}
function Be (line 1) | function Be(t,e){if((n=(t=e?t.toExponential(e-1):t.toExponential()).inde...
function ze (line 1) | function ze(t){return(t=Be(Math.abs(t)))?t[1]:NaN}
function Re (line 1) | function Re(t){if(!(e=Oe.exec(t)))throw new Error("invalid format: "+t);...
function Ue (line 1) | function Ue(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0...
function Le (line 1) | function Le(t,e){var n=Be(t,e);if(!n)return t+"";var r=n[0],i=n[1];retur...
function Pe (line 1) | function Pe(t){return t}
function Ge (line 1) | function Ge(t){var e,n,r=void 0===t.grouping||void 0===t.thousands?Pe:(e...
function Ve (line 1) | function Ve(t){return Math.max(0,-ze(Math.abs(t)))}
function Xe (line 1) | function Xe(t,e){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(z...
function Je (line 1) | function Je(t,e){return t=Math.abs(t),e=Math.abs(e)-t,Math.max(0,ze(e)-z...
function Ke (line 1) | function Ke(t,e,n,r){function i(e){return t(e=0===arguments.length?new D...
function yn (line 1) | function yn(t){return Ke((e=>{e.setDate(e.getDate()-(e.getDay()+7-t)%7),...
function Mn (line 1) | function Mn(t){return Ke((e=>{e.setUTCDate(e.getUTCDate()-(e.getUTCDay()...
function Rn (line 1) | function Rn(t,e,n,r,i,o){const a=[[ln,1,en],[ln,5,5e3],[ln,15,15e3],[ln,...
function er (line 1) | function er(t){const e=V(t).slice(),n={};e.length||s("Missing time unit....
function rr (line 1) | function rr(t,e){const n=ot({},nr,e),r=er(t),i=r.length;let o,a,s="",u=0...
function or (line 1) | function or(t){return ir.setFullYear(t),ir.setMonth(0),ir.setDate(1),ir....
function ar (line 1) | function ar(t){return ur(new Date(t))}
function sr (line 1) | function sr(t){return lr(new Date(t))}
function ur (line 1) | function ur(t){return pn.count(or(t.getFullYear())-1,t)}
function lr (line 1) | function lr(t){return vn.count(or(t.getFullYear())-1,t)}
function cr (line 1) | function cr(t){return or(t).getDay()}
function fr (line 1) | function fr(t,e,n,r,i,o,a){if(0<=t&&t<100){const s=new Date(-1,e,n,r,i,o...
function hr (line 1) | function hr(t){return pr(new Date(t))}
function dr (line 1) | function dr(t){return gr(new Date(t))}
function pr (line 1) | function pr(t){const e=Date.UTC(t.getUTCFullYear(),0,1);return gn.count(...
function gr (line 1) | function gr(t){const e=Date.UTC(t.getUTCFullYear(),0,1);return En.count(...
function mr (line 1) | function mr(t){return ir.setTime(Date.UTC(t,0,1)),ir.getUTCDay()}
function yr (line 1) | function yr(t,e,n,r,i,o,a){if(0<=t&&t<100){const t=new Date(Date.UTC(-1,...
function vr (line 1) | function vr(t,e,n,r,i){const o=e||1,a=F(t),s=(t,e,i)=>function(t,e,n,r){...
function _r (line 1) | function _r(t,e,n){return e+7*t-(n+6)%7}
function wr (line 1) | function wr(t,e){return vr(t,e||1,xr,br,fr)}
function Mr (line 1) | function Mr(t,e){return vr(t,e||1,kr,Ar,yr)}
function Cr (line 1) | function Cr(t){return Er[t]}
function Fr (line 1) | function Fr(t){return Dr[t]}
function Sr (line 1) | function Sr(t,e,n){return t?t.offset(e,n):void 0}
function $r (line 1) | function $r(t,e,n){return Sr(Cr(t),e,n)}
function Tr (line 1) | function Tr(t,e,n){return Sr(Fr(t),e,n)}
function Br (line 1) | function Br(t,e,n,r){return t?t.range(e,n,r):void 0}
function zr (line 1) | function zr(t,e,n,r){return Br(Cr(t),e,n,r)}
function Nr (line 1) | function Nr(t,e,n,r){return Br(Fr(t),e,n,r)}
function Jr (line 1) | function Jr(t){const e=t.extent,n=t.maxbins||40,r=Math.abs(Dt(e))/n;let ...
function Zr (line 1) | function Zr(t){if(0<=t.y&&t.y<100){var e=new Date(-1,t.m,t.d,t.H,t.M,t.S...
function Qr (line 1) | function Qr(t){if(0<=t.y&&t.y<100){var e=new Date(Date.UTC(-1,t.m,t.d,t....
function Kr (line 1) | function Kr(t,e,n){return{y:t,m:e,d:n,H:0,M:0,S:0,L:0}}
function ti (line 1) | function ti(t){var e=t.dateTime,n=t.date,r=t.time,i=t.periods,o=t.days,a...
function ci (line 1) | function ci(t,e,n){var r=t<0?"-":"",i=(r?-t:t)+"",o=i.length;return r+(o...
function fi (line 1) | function fi(t){return t.replace(li,"\\$&")}
function hi (line 1) | function hi(t){return new RegExp("^(?:"+t.map(fi).join("|")+")","i")}
function di (line 1) | function di(t){return new Map(t.map(((t,e)=>[t.toLowerCase(),e])))}
function pi (line 1) | function pi(t,e,n){var r=si.exec(e.slice(n,n+1));return r?(t.w=+r[0],n+r...
function gi (line 1) | function gi(t,e,n){var r=si.exec(e.slice(n,n+1));return r?(t.u=+r[0],n+r...
function mi (line 1) | function mi(t,e,n){var r=si.exec(e.slice(n,n+2));return r?(t.U=+r[0],n+r...
function yi (line 1) | function yi(t,e,n){var r=si.exec(e.slice(n,n+2));return r?(t.V=+r[0],n+r...
function vi (line 1) | function vi(t,e,n){var r=si.exec(e.slice(n,n+2));return r?(t.W=+r[0],n+r...
function _i (line 1) | function _i(t,e,n){var r=si.exec(e.slice(n,n+4));return r?(t.y=+r[0],n+r...
function xi (line 1) | function xi(t,e,n){var r=si.exec(e.slice(n,n+2));return r?(t.y=+r[0]+(+r...
function bi (line 1) | function bi(t,e,n){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(n,n...
function wi (line 1) | function wi(t,e,n){var r=si.exec(e.slice(n,n+1));return r?(t.q=3*r[0]-3,...
function ki (line 1) | function ki(t,e,n){var r=si.exec(e.slice(n,n+2));return r?(t.m=r[0]-1,n+...
function Ai (line 1) | function Ai(t,e,n){var r=si.exec(e.slice(n,n+2));return r?(t.d=+r[0],n+r...
function Mi (line 1) | function Mi(t,e,n){var r=si.exec(e.slice(n,n+3));return r?(t.m=0,t.d=+r[...
function Ei (line 1) | function Ei(t,e,n){var r=si.exec(e.slice(n,n+2));return r?(t.H=+r[0],n+r...
function Di (line 1) | function Di(t,e,n){var r=si.exec(e.slice(n,n+2));return r?(t.M=+r[0],n+r...
function Ci (line 1) | function Ci(t,e,n){var r=si.exec(e.slice(n,n+2));return r?(t.S=+r[0],n+r...
function Fi (line 1) | function Fi(t,e,n){var r=si.exec(e.slice(n,n+3));return r?(t.L=+r[0],n+r...
function Si (line 1) | function Si(t,e,n){var r=si.exec(e.slice(n,n+6));return r?(t.L=Math.floo...
function $i (line 1) | function $i(t,e,n){var r=ui.exec(e.slice(n,n+1));return r?n+r[0].length:-1}
function Ti (line 1) | function Ti(t,e,n){var r=si.exec(e.slice(n));return r?(t.Q=+r[0],n+r[0]....
function Bi (line 1) | function Bi(t,e,n){var r=si.exec(e.slice(n));return r?(t.s=+r[0],n+r[0]....
function zi (line 1) | function zi(t,e){return ci(t.getDate(),e,2)}
function Ni (line 1) | function Ni(t,e){return ci(t.getHours(),e,2)}
function Oi (line 1) | function Oi(t,e){return ci(t.getHours()%12||12,e,2)}
function Ri (line 1) | function Ri(t,e){return ci(1+pn.count(Nn(t),t),e,3)}
function Ui (line 1) | function Ui(t,e){return ci(t.getMilliseconds(),e,3)}
function Li (line 1) | function Li(t,e){return Ui(t,e)+"000"}
function qi (line 1) | function qi(t,e){return ci(t.getMonth()+1,e,2)}
function Pi (line 1) | function Pi(t,e){return ci(t.getMinutes(),e,2)}
function ji (line 1) | function ji(t,e){return ci(t.getSeconds(),e,2)}
function Ii (line 1) | function Ii(t){var e=t.getDay();return 0===e?7:e}
function Wi (line 1) | function Wi(t,e){return ci(vn.count(Nn(t)-1,t),e,2)}
function Hi (line 1) | function Hi(t){var e=t.getDay();return e>=4||0===e?wn(t):wn.ceil(t)}
function Yi (line 1) | function Yi(t,e){return t=Hi(t),ci(wn.count(Nn(t),t)+(4===Nn(t).getDay()...
function Gi (line 1) | function Gi(t){return t.getDay()}
function Vi (line 1) | function Vi(t,e){return ci(_n.count(Nn(t)-1,t),e,2)}
function Xi (line 1) | function Xi(t,e){return ci(t.getFullYear()%100,e,2)}
function Ji (line 1) | function Ji(t,e){return ci((t=Hi(t)).getFullYear()%100,e,2)}
function Zi (line 1) | function Zi(t,e){return ci(t.getFullYear()%1e4,e,4)}
function Qi (line 1) | function Qi(t,e){var n=t.getDay();return ci((t=n>=4||0===n?wn(t):wn.ceil...
function Ki (line 1) | function Ki(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+c...
function to (line 1) | function to(t,e){return ci(t.getUTCDate(),e,2)}
function eo (line 1) | function eo(t,e){return ci(t.getUTCHours(),e,2)}
function no (line 1) | function no(t,e){return ci(t.getUTCHours()%12||12,e,2)}
function ro (line 1) | function ro(t,e){return ci(1+gn.count(On(t),t),e,3)}
function io (line 1) | function io(t,e){return ci(t.getUTCMilliseconds(),e,3)}
function oo (line 1) | function oo(t,e){return io(t,e)+"000"}
function ao (line 1) | function ao(t,e){return ci(t.getUTCMonth()+1,e,2)}
function so (line 1) | function so(t,e){return ci(t.getUTCMinutes(),e,2)}
function uo (line 1) | function uo(t,e){return ci(t.getUTCSeconds(),e,2)}
function lo (line 1) | function lo(t){var e=t.getUTCDay();return 0===e?7:e}
function co (line 1) | function co(t,e){return ci(En.count(On(t)-1,t),e,2)}
function fo (line 1) | function fo(t){var e=t.getUTCDay();return e>=4||0===e?Sn(t):Sn.ceil(t)}
function ho (line 1) | function ho(t,e){return t=fo(t),ci(Sn.count(On(t),t)+(4===On(t).getUTCDa...
function po (line 1) | function po(t){return t.getUTCDay()}
function go (line 1) | function go(t,e){return ci(Dn.count(On(t)-1,t),e,2)}
function mo (line 1) | function mo(t,e){return ci(t.getUTCFullYear()%100,e,2)}
function yo (line 1) | function yo(t,e){return ci((t=fo(t)).getUTCFullYear()%100,e,2)}
function vo (line 1) | function vo(t,e){return ci(t.getUTCFullYear()%1e4,e,4)}
function _o (line 1) | function _o(t,e){var n=t.getUTCDay();return ci((t=n>=4||0===n?Sn(t):Sn.c...
function xo (line 1) | function xo(){return"+0000"}
function bo (line 1) | function bo(){return"%"}
function wo (line 1) | function wo(t){return+t}
function ko (line 1) | function ko(t){return Math.floor(+t/1e3)}
function Ao (line 1) | function Ao(t){const e={};return n=>e[n]||(e[n]=t(n))}
function Mo (line 1) | function Mo(t){const e=Ao(t.format),n=t.formatPrefix;return{format:e,for...
function Co (line 1) | function Co(){return Eo=Mo({format:Ie,formatPrefix:We})}
function Fo (line 1) | function Fo(t){return Mo(Ge(t))}
function So (line 1) | function So(t){return arguments.length?Eo=Fo(t):Eo}
function $o (line 1) | function $o(t,e,n){A(n=n||{})||s(`Invalid time multi-format specifier: $...
function To (line 1) | function To(t){const e=Ao(t.format),n=Ao(t.utcFormat);return{timeFormat:...
function Bo (line 1) | function Bo(){return Do=To({format:ni,parse:ri,utcFormat:ii,utcParse:oi})}
function zo (line 1) | function zo(t){return To(ti(t))}
function No (line 1) | function No(t){return arguments.length?Do=zo(t):Do}
function Ro (line 1) | function Ro(t,e){const n=t?Fo(t):So(),r=e?zo(e):No();return Oo(n,r)}
function Uo (line 1) | function Uo(t,e){const n=arguments.length;return n&&2!==n&&s("defaultLoc...
function Io (line 1) | async function Io(t,e){const n=await this.sanitize(t,e),r=n.href;return ...
function Wo (line 1) | async function Wo(t,e){e=ot({},this.options,e);const n=this.fileAccess,r...
function Ho (line 1) | function Ho(t){return t?e=>new Promise(((n,r)=>{t.readFile(e,((t,e)=>{t?...
function Yo (line 1) | async function Yo(){s("No file system access.")}
function Go (line 1) | function Go(t){return t?async function(e,n){const r=ot({},this.options.h...
function Vo (line 1) | async function Vo(){s("No HTTP fetch method available.")}
function ta (line 1) | function ta(t,e){if(!t||!t.length)return"unknown";const n=t.length,r=Qo....
function ea (line 1) | function ea(t,e){return e.reduce(((e,n)=>(e[n]=ta(t,n),e)),{})}
function na (line 1) | function na(t){const e=function(e,n){const r={delimiter:t};return ra(e,n...
function ra (line 1) | function ra(t,e){return e.header&&(t=e.header.map(Ct).join(e.delimiter)+...
function ia (line 1) | function ia(t,e){const n=e&&e.property?l(e.property):f;return!A(t)||(r=t...
function aa (line 1) | function aa(t,e){let n,r,i,o;return t=ia(t,e),e&&e.feature?(n=Gt,i=e.fea...
function ua (line 1) | function ua(t,e){return arguments.length>1?(sa[t]=e,this):lt(sa,t)?sa[t]...
function la (line 1) | function la(t){const e=ua(t);return e&&e.responseType||"text"}
function ca (line 1) | function ca(t,e,n,r){const i=ua((e=e||{}).type||"json");return i||s("Unk...
function ha (line 1) | function ha(t){const e=t||f,n=[],r={};return n.add=t=>{const i=e(t);retu...
function da (line 1) | async function da(t,e){try{await e(t)}catch(e){t.error(e)}}
function ma (line 1) | function ma(t){return!(!t||!ya(t))}
function ya (line 1) | function ya(t){return t[pa]}
function va (line 1) | function va(t,e){return t[pa]=e,t}
function _a (line 1) | function _a(t){const e=t===Object(t)?t:{data:t};return ya(e)?e:va(e,ga++)}
function xa (line 1) | function xa(t){return ba(t,_a({}))}
function ba (line 1) | function ba(t,e){for(const n in t)e[n]=t[n];return e}
function wa (line 1) | function wa(t,e){return va(e,ya(t))}
function ka (line 1) | function ka(t,e){return t?e?(n,r)=>t(n,r)||ya(e(n))-ya(e(r)):(e,n)=>t(e,...
function Aa (line 1) | function Aa(t){return t&&t.constructor===Ma}
function Ma (line 1) | function Ma(){const t=[],e=[],n=[],r=[],i=[];let o=null,a=!1;return{cons...
function Da (line 1) | function Da(){Object.defineProperty(this,Ea,{writable:!0,value:{}})}
method set (line 1) | set(t,e,n,r){const i=this,o=i[t],a=i[Ea];return null!=e&&e>=0?(o[e]!==n|...
method modified (line 1) | modified(t,e){const n=this[Ea];if(!arguments.length){for(const t in n)if...
method clear (line 1) | clear(){return this[Ea]={},this}
function Sa (line 1) | function Sa(t,e,n,r){this.id=++Ca,this.value=t,this.stamp=-1,this.rank=-...
function $a (line 1) | function $a(t){return function(e){const n=this.flags;return 0===argument...
method targets (line 1) | targets(){return this._targets||(this._targets=ha(c))}
method set (line 1) | set(t){return this.value!==t?(this.value=t,1):0}
method parameters (line 1) | parameters(t,e,n){e=!1!==e;const r=this._argval=this._argval||new Da,i=t...
method marshall (line 1) | marshall(t){const e=this._argval||Fa,n=this._argops;let r,i,o,a;if(n){co...
method detach (line 1) | detach(){const t=this._argops;let e,n,r,i;if(t)for(e=0,n=t.length;e<n;++...
method evaluate (line 1) | evaluate(t){const e=this._update;if(e){const n=this.marshall(t.stamp),r=...
method run (line 1) | run(t){if(t.stamp<this.stamp)return t.StopPropagation;let e;return this....
function Ba (line 1) | function Ba(t,e,n){this.id=++Ta,this.value=null,n&&(this.receive=n),t&&(...
function za (line 1) | function za(t,e,n){return new Ba(t,e,n)}
method targets (line 1) | targets(){return this._targets||(this._targets=ha(c))}
method consume (line 1) | consume(t){return arguments.length?(this._consume=!!t,this):!!this._cons...
method receive (line 1) | receive(t){if(this._filter(t)){const e=this.value=this._apply(t),n=this....
method filter (line 1) | filter(t){const e=za(t);return this.targets().add(e),e}
method apply (line 1) | apply(t){const e=za(null,t);return this.targets().add(e),e}
method merge (line 1) | merge(){const t=za();this.targets().add(t);for(let e=0,n=arguments.lengt...
method throttle (line 1) | throttle(t){let e=-1;return this.filter((()=>{const n=Date.now();return ...
method debounce (line 1) | debounce(t){const e=za();return this.targets().add(za(null,null,it(t,(t=...
method between (line 1) | between(t,e){let n=!1;return t.targets().add(za(null,null,(()=>n=!0))),e...
method detach (line 1) | detach(){this._filter=p,this._targets=null}
function Oa (line 1) | function Oa(t,e,n,r,i,o){const a=ot({},o,Na);let s,u;J(n)||(n=rt(n)),voi...
function Ra (line 1) | function Ra(t,e,n,r,i,o){if(void 0===r)e.targets().add(n);else{const a=o...
function La (line 1) | function La(t,e,n){this.dataflow=t,this.stamp=null==e?-1:e,this.add=[],t...
function qa (line 1) | function qa(t,e){const n=[];return Nt(t,e,(t=>n.push(t))),n}
function Pa (line 1) | function Pa(t,e){const n={};return t.visit(e,(t=>{n[ya(t)]=1})),t=>n[ya(...
function ja (line 1) | function ja(t,e){return t?(n,r)=>t(n,r)&&e(n,r):e}
function Ia (line 1) | function Ia(t,e,n,r){const i=this;let o=0;this.dataflow=t,this.stamp=e,t...
function Wa (line 1) | function Wa(t){return t.error("Dataflow already running. Use runAsync() ...
method fork (line 1) | fork(t){return new La(this.dataflow).init(this,t)}
method clone (line 1) | clone(){const t=this.fork(7);return t.add=t.add.slice(),t.rem=t.rem.slic...
method addAll (line 1) | addAll(){let t=this;return!t.source||t.add===t.rem||!t.rem.length&&t.sou...
method init (line 1) | init(t,e){const n=this;return n.stamp=t.stamp,n.encode=t.encode,!t.field...
method runAfter (line 1) | runAfter(t){this.dataflow.runAfter(t)}
method changed (line 1) | changed(t){const e=t||7;return 1&e&&this.add.length||2&e&&this.rem.lengt...
method reflow (line 1) | reflow(t){if(t)return this.fork(7).reflow();const e=this.add.length,n=th...
method clean (line 1) | clean(t){return arguments.length?(this.cleans=!!t,this):this.cleans}
method modifies (line 1) | modifies(t){const e=this.fields||(this.fields={});return k(t)?t.forEach(...
method modified (line 1) | modified(t,e){const n=this.fields;return!(!e&&!this.mod.length||!n)&&(ar...
method filter (line 1) | filter(t,e){const n=this;return 1&t&&(n.addF=ja(n.addF,e)),2&t&&(n.remF=...
method materialize (line 1) | materialize(t){const e=this;return 1&(t=t||7)&&e.addF&&(e.add=qa(e.add,e...
method visit (line 1) | visit(t,e){const n=this,r=e;if(16&t)return Nt(n.source,n.srcF,r),n;1&t&&...
method fork (line 1) | fork(t){const e=new La(this.dataflow).init(this,t&this.NO_FIELDS);return...
method changed (line 1) | changed(t){return this.changes&t}
method modified (line 1) | modified(t){const e=this,n=e.fields;return n&&e.changes&e.MOD?k(t)?t.som...
method filter (line 1) | filter(){s("MultiPulse does not support filtering.")}
method materialize (line 1) | materialize(){s("MultiPulse does not support materialization.")}
method visit (line 1) | visit(t,e){const n=this,r=n.pulses,i=r.length;let o=0;if(t&n.SOURCE)for(...
function Ya (line 1) | function Ya(t){let e=[];return{clear:()=>e=[],size:()=>e.length,peek:()=...
function Ga (line 1) | function Ga(t,e,n,r){let i,o;const a=t[n];for(;n>e&&(o=n-1>>1,i=t[o],r(a...
function Va (line 1) | function Va(){this.logger(w()),this.logLevel(v),this._clock=0,this._rank...
function Xa (line 1) | function Xa(t){return function(){return this._log[t].apply(this,argument...
function Ja (line 1) | function Ja(t,e){Sa.call(this,t,null,e)}
method stamp (line 1) | stamp(){return this._clock}
method loader (line 1) | loader(t){return arguments.length?(this._loader=t,this):this._loader}
method locale (line 1) | locale(t){return arguments.length?(this._locale=t,this):this._locale}
method logger (line 1) | logger(t){return arguments.length?(this._log=t,this):this._log}
method run (line 1) | run(t){if(t.stamp<this.stamp)return t.StopPropagation;let e;return this....
method evaluate (line 1) | evaluate(t){const e=this.marshall(t.stamp),n=this.transform(e,t);return ...
method transform (line 1) | transform(){}
function Qa (line 1) | function Qa(t){const e=Ka(t);return e&&e.Definition||null}
function Ka (line 1) | function Ka(t){return t=t&&t.toLowerCase(),lt(Za,t)?Za[t]:null}
function es (line 1) | function es(t,e,n){const r=Float64Array.from(ts(t,n));return r.sort(Kt),...
function ns (line 1) | function ns(t,e){return es(t,[.25,.5,.75],e)}
function rs (line 1) | function rs(t,e){const n=t.length,r=function(t,e){const n=function(t,e){...
function is (line 1) | function is(t){const e=t.maxbins||20,n=t.base||10,r=Math.log(n),i=t.divi...
function os (line 1) | function os(e,n,r,i){if(!e.length)return[void 0,void 0];const o=Float64A...
function as (line 1) | function as(t,e,n,r){r=r||(t=>t);const i=t.length,o=new Float64Array(i);...
function cs (line 1) | function cs(e,n){e=e||0,n=null==n?1:n;let r,i,o=0,a=0;if(ls==ls)o=ls,ls=...
function fs (line 1) | function fs(t,e,n){const r=(t-(e||0))/(n=null==n?1:n);return Math.exp(-....
function hs (line 1) | function hs(t,e,n){const r=(t-(e=e||0))/(n=null==n?1:n),i=Math.abs(r);le...
function ds (line 1) | function ds(t,e,n){return t<0||t>1?NaN:(e||0)+(null==n?1:n)*us*function(...
function ps (line 1) | function ps(t,e){let n,r;const i={mean(t){return arguments.length?(n=t||...
function gs (line 1) | function gs(e,n){const r=ps();let i=0;const o={data(t){return arguments....
function ms (line 1) | function ms(t,e){return t=t||0,e=null==e?1:e,Math.exp(t+cs()*e)}
function ys (line 1) | function ys(t,e,n){if(t<=0)return 0;e=e||0,n=null==n?1:n;const r=(Math.l...
function vs (line 1) | function vs(t,e,n){return hs(Math.log(t),e,n)}
function _s (line 1) | function _s(t,e,n){return Math.exp(ds(t,e,n))}
function xs (line 1) | function xs(t,e){let n,r;const i={mean(t){return arguments.length?(n=t||...
function bs (line 1) | function bs(e,n){let r,i=0;const o={weights(t){return arguments.length?(...
function ws (line 1) | function ws(e,n){return null==n&&(n=null==e?1:e,e=0),e+(n-e)*t.random()}
function ks (line 1) | function ks(t,e,n){return null==n&&(n=null==e?1:e,e=0),t>=e&&t<=n?1/(n-e...
function As (line 1) | function As(t,e,n){return null==n&&(n=null==e?1:e,e=0),t<e?0:t>n?1:(t-e)...
function Ms (line 1) | function Ms(t,e,n){return null==n&&(n=null==e?1:e,e=0),t>=0&&t<=1?e+t*(n...
function Es (line 1) | function Es(t,e){let n,r;const i={min(t){return arguments.length?(n=t||0...
function Ds (line 1) | function Ds(t,e,n){let r=0,i=0;for(const o of t){const t=n(o);null==e(o)...
function Cs (line 1) | function Cs(t,e,n,r){const i=r-t*t,o=Math.abs(i)<1e-24?0:(n-t*e)/i;retur...
function Fs (line 1) | function Fs(t,e,n,r){t=t.filter((t=>{let r=e(t),i=n(t);return null!=r&&(...
function Ss (line 1) | function Ss(t,e,n,r){let i,o,a=-1;for(const s of t)i=e(s),o=n(s),null!=i...
function $s (line 1) | function $s(t,e,n,r,i){let o=0,a=0;return Ss(t,e,n,((t,e)=>{const n=e-i(...
function Ts (line 1) | function Ts(t,e,n){let r=0,i=0,o=0,a=0,s=0;Ss(t,e,n,((t,e)=>{++s,r+=(t-r...
function Bs (line 1) | function Bs(t,e,n){let r=0,i=0,o=0,a=0,s=0;Ss(t,e,n,((t,e)=>{++s,t=Math....
function zs (line 1) | function zs(t,e,n){const[r,i,o,a]=Fs(t,e,n);let s,u,l,c=0,f=0,h=0,d=0,p=...
function Ns (line 1) | function Ns(t,e,n){let r=0,i=0,o=0,a=0,s=0,u=0;Ss(t,e,n,((t,e)=>{const n...
function Os (line 1) | function Os(t,e,n){const[r,i,o,a]=Fs(t,e,n),s=r.length;let u,l,c,f,h=0,d...
function Rs (line 1) | function Rs(t,e,n,r){if(0===r)return Ds(t,e,n);if(1===r)return Ts(t,e,n)...
function Us (line 1) | function Us(t,e,n,r){const i=Array(t);let o,a,s,u;for(o=0;o<t;++o)i[o]=0...
function Ls (line 1) | function Ls(t,e,n,r){const[i,o,a,s]=Fs(t,e,n,!0),u=i.length,l=Math.max(2...
function qs (line 1) | function qs(t){return(t=1-t*t*t)*t*t}
function Ps (line 1) | function Ps(t,e,n){const r=t[e];let i=n[0],o=n[1]+1;if(!(o>=t.length))fo...
function Is (line 1) | function Is(t,e,n,r){n=n||25,r=Math.max(n,r||200);const i=e=>[e,t(e)],o=...
function Ws (line 1) | function Ws(t,e,n,r,i){const o=Math.atan2(i*(n[1]-t[1]),r*(n[0]-t[0])),a...
function Hs (line 1) | function Hs(t){return t&&t.length?1===t.length?t[0]:(e=t,t=>{const n=e.l...
function Ys (line 1) | function Ys(t,e,n){return n||t+(e?"_"+e:"")}
function Zs (line 1) | function Zs(t,e,n){return Xs[t](n,e)}
function Qs (line 1) | function Qs(t,e){return t.idx-e.idx}
function Ks (line 1) | function Ks(){this.valid=0,this.missing=0,this._ops.forEach((t=>null==t....
function tu (line 1) | function tu(t,e){null!=t&&""!==t?t==t&&(++this.valid,this._ops.forEach((...
function eu (line 1) | function eu(t,e){null!=t&&""!==t?t==t&&(--this.valid,this._ops.forEach((...
function nu (line 1) | function nu(t){return this._out.forEach((e=>t[e.out]=e.value(this))),t}
function ru (line 1) | function ru(t,e){const n=e||f,r=function(t){const e={};t.forEach((t=>e[t...
function iu (line 1) | function iu(t){this._key=t?l(t):ya,this.reset()}
function au (line 1) | function au(t){Ja.call(this,null,t),this._adds=[],this._mods=[],this._al...
method transform (line 1) | transform(t,e){const n=this,r=e.fork(e.NO_SOURCE|e.NO_FIELDS),i=t.modifi...
method cross (line 1) | cross(){const t=this,e=t.value,n=t._dnames,r=n.map((()=>({}))),i=n.lengt...
method init (line 1) | init(t){const e=this._inputs=[],i=this._outputs=[],o={};function a(t){co...
method cell (line 1) | cell(t,e){let n=this.value[t];return n?0===n.num&&this._drop&&n.stamp<th...
method newcell (line 1) | newcell(t,e){const n={key:t,num:0,agg:null,tuple:this.newtuple(e,this._p...
method newtuple (line 1) | newtuple(t,e){const n=this._dnames,r=this._dims,i=r.length,o={};for(let ...
method clean (line 1) | clean(){const t=this.value;for(const e in t)0===t[e].num&&delete t[e]}
method add (line 1) | add(t){const e=this.cellkey(t),n=this.cell(e,t);if(n.num+=1,this._countO...
method rem (line 1) | rem(t){const e=this.cellkey(t),n=this.cell(e,t);if(n.num-=1,this._countO...
method celltuple (line 1) | celltuple(t){const e=t.tuple,n=this._counts;t.store&&t.data.values();for...
method changes (line 1) | changes(t){const e=this._adds,n=this._mods,r=this._prev,i=this._drop,o=t...
function su (line 1) | function su(t){Ja.call(this,null,t)}
function uu (line 1) | function uu(t,e,n){const r=t;let i=e||[],o=n||[],a={},s=0;return{add:t=>...
function lu (line 1) | function lu(t){Ja.call(this,[],t)}
function cu (line 1) | function cu(t){Sa.call(this,null,fu,t)}
function fu (line 1) | function fu(t){return this.value&&!t.modified()?this.value:Q(t.fields,t....
function hu (line 1) | function hu(t){Ja.call(this,null,t)}
function du (line 1) | function du(t){Ja.call(this,null,t)}
method transform (line 1) | transform(t,e){const n=!1!==t.interval,i=this._bins(t),o=i.start,a=i.ste...
method _bins (line 1) | _bins(t){if(this.value&&!t.modified())return this.value;const i=t.field,...
method transform (line 1) | transform(t,e){const n=e.fork(e.ALL),r=uu(ya,this.value,n.materialize(n....
method transform (line 1) | transform(t,e){const n=e=>n=>{for(var r,i=function(t,e,n){switch(e){case...
method _parameterCheck (line 1) | _parameterCheck(t,e){let n=!1;return!t.modified("stopwords")&&this._stop...
method _finish (line 1) | _finish(t,e){const n=this._counts,r=this._tuples||(this._tuples={}),i=e[...
method transform (line 1) | transform(t,e){const n=e.fork(e.NO_SOURCE),r=t.as||["a","b"],i=r[0],o=r[...
function mu (line 1) | function mu(t,e){const n=t[gu];lt(pu,n)||s("Unknown distribution functio...
function yu (line 1) | function yu(t){Ja.call(this,null,t)}
function xu (line 1) | function xu(t,e){return t?t.map(((t,r)=>e[r]||n(t))):null}
function bu (line 1) | function bu(t,e,n){const r=[],i=t=>t(u);let o,a,s,u,l,c;if(null==e)r.pus...
method transform (line 1) | transform(t,e){const n=e.fork(e.NO_SOURCE|e.NO_FIELDS);if(!this.value||e...
function wu (line 1) | function wu(t){Ja.call(this,null,t)}
function ku (line 1) | function ku(t){Sa.call(this,null,Au,t),this.modified(!0)}
function Au (line 1) | function Au(t){const i=t.expr;return this.value&&!t.modified("expr")?thi...
function Mu (line 1) | function Mu(t){Ja.call(this,[void 0,void 0],t)}
function Eu (line 1) | function Eu(t,e){Sa.call(this,t),this.parent=e,this.count=0}
function Du (line 1) | function Du(t){Ja.call(this,{},t),this._keys=ft();const e=this._targets=...
function Cu (line 1) | function Cu(t){Sa.call(this,null,Fu,t)}
function Fu (line 1) | function Fu(t){return this.value&&!t.modified()?this.value:k(t.name)?V(t...
function Su (line 1) | function Su(t){Ja.call(this,ft(),t)}
function $u (line 1) | function $u(t){Ja.call(this,[],t)}
function Tu (line 1) | function Tu(t){Ja.call(this,[],t)}
function Bu (line 1) | function Bu(t){Ja.call(this,null,t)}
function zu (line 1) | function zu(t){Ja.call(this,[],t)}
method transform (line 1) | transform(t,e){if(this.value&&!t.modified()&&!e.changed())return e;const...
method transform (line 1) | transform(t,e){const r=this.value,i=t.field,o=e.changed()||e.modified(i....
method connect (line 1) | connect(t){return this.detachSubflow=t.detachSubflow,this.targets().add(...
method add (line 1) | add(t){this.count+=1,this.value.add.push(t)}
method rem (line 1) | rem(t){this.count-=1,this.value.rem.push(t)}
method mod (line 1) | mod(t){this.value.mod.push(t)}
method init (line 1) | init(t){this.value.init(t,t.NO_SOURCE)}
method evaluate (line 1) | evaluate(){return this.value}
method activate (line 1) | activate(t){this._targets[this._targets.active++]=t}
method subflow (line 1) | subflow(t,e,n,r){const i=this.value;let o,a,s=lt(i,t)&&i[t];return s?s.v...
method clean (line 1) | clean(){const t=this.value;let e=0;for(const n in t)if(0===t[n].count){c...
method initTargets (line 1) | initTargets(t){const e=this._targets,n=e.length,r=t?t.length:0;let i=0;f...
method transform (line 1) | transform(t,e){const n=e.dataflow,r=t.key,i=t.subflow,o=this._keys,a=t.m...
method transform (line 1) | transform(t,e){const n=e.dataflow,r=this.value,i=e.fork(),o=i.add,a=i.re...
method transform (line 1) | transform(t,e){const n=e.fork(e.NO_SOURCE),r=t.fields,i=xu(r,t.as||[]),o...
method transform (line 1) | transform(t,e){const r=e.fork(e.NO_SOURCE),i=t.fields,o=i.map(n),a=t.as|...
method transform (line 1) | transform(t,e){const n=t.expr,r=t.as,i=t.modified(),o=t.initonly?e.ADD:i...
method transform (line 1) | transform(t,e){const n=e.fork(e.ALL),r=t.generator;let i,o,a,s=this.valu...
function Ru (line 1) | function Ru(t){Ja.call(this,[],t)}
function Uu (line 1) | function Uu(t){au.call(this,t)}
function Lu (line 1) | function Lu(t){Ja.call(this,null,t)}
function qu (line 1) | function qu(t){Sa.call(this,null,Pu,t)}
function Pu (line 1) | function Pu(t){return this.value&&!t.modified()?this.value:bt(t.fields,t...
function ju (line 1) | function ju(t){Ja.call(this,[],t),this._pending=null}
function Iu (line 1) | function Iu(t,e,n){n.forEach(_a);const r=e.fork(e.NO_FIELDS&e.NO_SOURCE)...
function Wu (line 1) | function Wu(t){Ja.call(this,{},t)}
function Hu (line 1) | function Hu(t){Sa.call(this,null,Yu,t)}
function Yu (line 1) | function Yu(t){if(this.value&&!t.modified())return this.value;const e=t....
function Gu (line 1) | function Gu(t){Sa.call(this,null,Vu,t)}
function Vu (line 1) | function Vu(t){return this.value&&!t.modified()?this.value:t.values.redu...
function Xu (line 1) | function Xu(t){Ja.call(this,null,t)}
function Ju (line 1) | function Ju(t){au.call(this,t)}
function Zu (line 1) | function Zu(t){Du.call(this,t)}
function Qu (line 1) | function Qu(t){Ja.call(this,null,t)}
function Ku (line 1) | function Ku(t){Ja.call(this,null,t)}
function tl (line 1) | function tl(t){Ja.call(this,null,t)}
method transform (line 1) | transform(t,e){var r,i,o,a,u,l,c,f,h,d,p=e.fork(e.ALL),g=function(t){var...
method transform (line 1) | transform(t,e){const n=this,r=t.modified();let i;return n.value&&(r||e.m...
method changes (line 1) | changes(){const t=this._adds,e=this._mods;let n,r;for(n=0,r=this._alen;n...
method transform (line 1) | transform(t,e){const r=e.fork(e.NO_SOURCE|e.NO_FIELDS);if(!this.value||e...
method transform (line 1) | transform(t,e){const n=e.dataflow;if(this._pending)return Iu(this,e,this...
method transform (line 1) | transform(t,e){const r=t.fields,i=t.index,o=t.values,a=null==t.default?n...
method transform (line 1) | transform(t,e){return this.modified(t.modified()),this.value=t,e.fork(e....
method transform (line 1) | transform(t,n){return this._transform(function(t,n){const i=t.field,o=t....
method transform (line 1) | transform(t,e){const n=t.subflow,i=t.field,o=t=>this.subflow(ya(t),n,e,t...
method transform (line 1) | transform(t,e){const n=e.fork(e.NO_SOURCE),r=t.fields,i=xu(t.fields,t.as...
method transform (line 1) | transform(t,e){return this.value=t.value,t.modified("value")?e.fork(e.NO...
function el (line 1) | function el(t){Ja.call(this,null,t)}
function nl (line 1) | function nl(t){Ja.call(this,[],t),this.count=0}
function rl (line 1) | function rl(t){Ja.call(this,null,t)}
function il (line 1) | function il(t){Ja.call(this,null,t),this.modified(!0)}
function ol (line 1) | function ol(t){Ja.call(this,null,t)}
method transform (line 1) | transform(t,e){const r=e.fork(e.NO_SOURCE|e.NO_FIELDS),i=t.as||["prob","...
method transform (line 1) | transform(t,e){let n,r;return this.value?r=this.value:(n=e=e.addAll(),r=...
method transform (line 1) | transform(e,n){const r=n.fork(n.NO_SOURCE),i=e.modified("size"),o=e.size...
method transform (line 1) | transform(t,e){if(this.value&&!t.modified())return;const n=e.materialize...
method transform (line 1) | transform(t,e){return this.value=e.source,e.changed()?e.fork(e.NO_SOURCE...
function sl (line 1) | function sl(t){Ja.call(this,ft(),t)}
function ul (line 1) | function ul(t){Ja.call(this,null,t)}
method transform (line 1) | transform(t,e){const n=t.field,i=!1!==t.interval,o="utc"===t.timezone,a=...
method _floor (line 1) | _floor(t,e){const n="utc"===t.timezone,{units:r,step:i}=t.units?{units:t...
method transform (line 1) | transform(t,e){const n=e.dataflow,r=t.field,i=this.value,o=t=>i.set(r(t)...
method transform (line 1) | transform(t,e){(!this.value||t.modified("field")||t.modified("sort")||e....
function fl (line 1) | function fl(t){const e=V(t.ops),i=V(t.fields),o=V(t.params),a=V(t.aggreg...
function dl (line 1) | function dl(t){Ja.call(this,{},t),this._mlen=0,this._mods=[]}
function pl (line 1) | function pl(t,e,n,r){const i=r.sort,o=i&&!r.ignorePeers,a=r.frame||[null...
function gl (line 1) | function gl(t,e,n,r){t.p0=t.i0,t.p1=t.i1,t.i0=null==e[0]?0:Math.max(0,n-...
function ml (line 1) | function ml(t,e){const n=t.i0,r=t.i1-1,i=t.compare,o=t.data,a=o.length-1...
method transform (line 1) | transform(t,e){this.stamp=e.stamp;const n=t.modified(),r=ka(t.sort),i=Hs...
method group (line 1) | group(t){let e=this.value[t];return e||(e=this.value[t]=uu(ya),e.stamp=-...
function vl (line 1) | function vl(t){return function(){return t}}
function Sl (line 1) | function Sl(t){return t>=1?Cl:t<=-1?-Cl:Math.asin(t)}
function Nl (line 1) | function Nl(t){this._+=t[0];for(let e=1,n=t.length;e<n;++e)this._+=argum...
method constructor (line 1) | constructor(t){this._x0=this._y0=this._x1=this._y1=null,this._="",this._...
method moveTo (line 1) | moveTo(t,e){this._append`M${this._x0=this._x1=+t},${this._y0=this._y1=+e}`}
method closePath (line 1) | closePath(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._...
method lineTo (line 1) | lineTo(t,e){this._append`L${this._x1=+t},${this._y1=+e}`}
method quadraticCurveTo (line 1) | quadraticCurveTo(t,e,n,r){this._append`Q${+t},${+e},${this._x1=+n},${thi...
method bezierCurveTo (line 1) | bezierCurveTo(t,e,n,r,i,o){this._append`C${+t},${+e},${+n},${+r},${this....
method arcTo (line 1) | arcTo(t,e,n,r,i){if(t=+t,e=+e,n=+n,r=+r,(i=+i)<0)throw new Error(`negati...
method arc (line 1) | arc(t,e,n,r,i,o){if(t=+t,e=+e,o=!!o,(n=+n)<0)throw new Error(`negative r...
method rect (line 1) | rect(t,e,n,r){this._append`M${this._x0=this._x1=+t},${this._y0=this._y1=...
method toString (line 1) | toString(){return this._}
function Rl (line 1) | function Rl(){return new Ol}
function Ul (line 1) | function Ul(t){let e=3;return t.digits=function(n){if(!arguments.length)...
function Ll (line 1) | function Ll(t){return t.innerRadius}
function ql (line 1) | function ql(t){return t.outerRadius}
function Pl (line 1) | function Pl(t){return t.startAngle}
function jl (line 1) | function jl(t){return t.endAngle}
function Il (line 1) | function Il(t){return t&&t.padAngle}
function Wl (line 1) | function Wl(t,e,n,r,i,o,a){var s=t-n,u=e-r,l=(a?o:-o)/Ml(s*s+u*u),c=l*u,...
function Hl (line 1) | function Hl(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}
function Yl (line 1) | function Yl(t){this._context=t}
function Gl (line 1) | function Gl(t){return new Yl(t)}
function Vl (line 1) | function Vl(t){return t[0]}
function Xl (line 1) | function Xl(t){return t[1]}
function Jl (line 1) | function Jl(t,e){var n=vl(!0),r=null,i=Gl,o=null,a=Ul(s);function s(s){v...
function Zl (line 1) | function Zl(t,e,n){var r=null,i=vl(!0),o=null,a=Gl,s=null,u=Ul(l);functi...
method draw (line 1) | draw(t,e){const n=Ml(e/Dl);t.moveTo(n,0),t.arc(0,0,n,0,Fl)}
function Kl (line 1) | function Kl(){}
function tc (line 1) | function tc(t,e,n){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t...
function ec (line 1) | function ec(t){this._context=t}
function nc (line 1) | function nc(t){this._context=t}
function rc (line 1) | function rc(t){this._context=t}
function ic (line 1) | function ic(t,e){this._basis=new ec(t),this._beta=e}
function n (line 1) | function n(t){return 1===e?new ec(t):new ic(t,e)}
function ac (line 1) | function ac(t,e,n){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._...
function sc (line 1) | function sc(t,e){this._context=t,this._k=(1-e)/6}
function n (line 1) | function n(t){return new sc(t,e)}
function lc (line 1) | function lc(t,e){this._context=t,this._k=(1-e)/6}
function n (line 1) | function n(t){return new lc(t,e)}
function fc (line 1) | function fc(t,e){this._context=t,this._k=(1-e)/6}
function n (line 1) | function n(t){return new fc(t,e)}
function dc (line 1) | function dc(t,e,n){var r=t._x1,i=t._y1,o=t._x2,a=t._y2;if(t._l01_a>El){v...
function pc (line 1) | function pc(t,e){this._context=t,this._alpha=e}
function n (line 1) | function n(t){return e?new pc(t,e):new sc(t,0)}
function mc (line 1) | function mc(t,e){this._context=t,this._alpha=e}
function n (line 1) | function n(t){return e?new mc(t,e):new lc(t,0)}
function vc (line 1) | function vc(t,e){this._context=t,this._alpha=e}
function n (line 1) | function n(t){return e?new vc(t,e):new fc(t,0)}
function xc (line 1) | function xc(t){this._context=t}
function bc (line 1) | function bc(t){return t<0?-1:1}
function wc (line 1) | function wc(t,e,n){var r=t._x1-t._x0,i=e-t._x1,o=(t._y1-t._y0)/(r||i<0&&...
function kc (line 1) | function kc(t,e){var n=t._x1-t._x0;return n?(3*(t._y1-t._y0)/n-e)/2:e}
function Ac (line 1) | function Ac(t,e,n){var r=t._x0,i=t._y0,o=t._x1,a=t._y1,s=(o-r)/3;t._cont...
function Mc (line 1) | function Mc(t){this._context=t}
function Ec (line 1) | function Ec(t){this._context=new Dc(t)}
function Dc (line 1) | function Dc(t){this._context=t}
function Cc (line 1) | function Cc(t){this._context=t}
function Fc (line 1) | function Fc(t){var e,n,r=t.length-1,i=new Array(r),o=new Array(r),a=new ...
function Sc (line 1) | function Sc(t,e){this._context=t,this._t=e}
function $c (line 1) | function $c(t,e){if("undefined"!=typeof document&&document.createElement...
function Bc (line 1) | function Bc(t,e){switch(arguments.length){case 0:break;case 1:this.range...
function zc (line 1) | function zc(t,e){switch(arguments.length){case 0:break;case 1:"function"...
function Oc (line 1) | function Oc(){var t=new ue,e=[],n=[],r=Nc;function i(i){let o=t.get(i);i...
function Rc (line 1) | function Rc(t,e,n){t.prototype=e.prototype=n,n.constructor=t}
function Uc (line 1) | function Uc(t,e){var n=Object.create(t.prototype);for(var r in e)n[r]=e[...
function Lc (line 1) | function Lc(){}
function Kc (line 1) | function Kc(){return this.rgb().formatHex()}
function tf (line 1) | function tf(){return this.rgb().formatRgb()}
function ef (line 1) | function ef(t){var e,n;return t=(t+"").trim().toLowerCase(),(e=Hc.exec(t...
function nf (line 1) | function nf(t){return new sf(t>>16&255,t>>8&255,255&t,1)}
function rf (line 1) | function rf(t,e,n,r){return r<=0&&(t=e=n=NaN),new sf(t,e,n,r)}
function of (line 1) | function of(t){return t instanceof Lc||(t=ef(t)),t?new sf((t=t.rgb()).r,...
function af (line 1) | function af(t,e,n,r){return 1===arguments.length?of(t):new sf(t,e,n,null...
function sf (line 1) | function sf(t,e,n,r){this.r=+t,this.g=+e,this.b=+n,this.opacity=+r}
function uf (line 1) | function uf(){return`#${hf(this.r)}${hf(this.g)}${hf(this.b)}`}
function lf (line 1) | function lf(){const t=cf(this.opacity);return`${1===t?"rgb(":"rgba("}${f...
function cf (line 1) | function cf(t){return isNaN(t)?1:Math.max(0,Math.min(1,t))}
function ff (line 1) | function ff(t){return Math.max(0,Math.min(255,Math.round(t)||0))}
function hf (line 1) | function hf(t){return((t=ff(t))<16?"0":"")+t.toString(16)}
function df (line 1) | function df(t,e,n,r){return r<=0?t=e=n=NaN:n<=0||n>=1?t=e=NaN:e<=0&&(t=N...
function pf (line 1) | function pf(t){if(t instanceof mf)return new mf(t.h,t.s,t.l,t.opacity);i...
function gf (line 1) | function gf(t,e,n,r){return 1===arguments.length?pf(t):new mf(t,e,n,null...
function mf (line 1) | function mf(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}
function yf (line 1) | function yf(t){return(t=(t||0)%360)<0?t+360:t}
function vf (line 1) | function vf(t){return Math.max(0,Math.min(1,t||0))}
function _f (line 1) | function _f(t,e,n){return 255*(t<60?e+(n-e)*t/60:t<180?n:t<240?e+(n-e)*(...
method copy (line 1) | copy(t){return Object.assign(new this.constructor,this,t)}
method displayable (line 1) | displayable(){return this.rgb().displayable()}
method brighter (line 1) | brighter(t){return t=null==t?Pc:Math.pow(Pc,t),new sf(this.r*t,this.g*t,...
method darker (line 1) | darker(t){return t=null==t?qc:Math.pow(qc,t),new sf(this.r*t,this.g*t,th...
method rgb (line 1) | rgb(){return this}
method clamp (line 1) | clamp(){return new sf(ff(this.r),ff(this.g),ff(this.b),cf(this.opacity))}
method displayable (line 1) | displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5...
method brighter (line 1) | brighter(t){return t=null==t?Pc:Math.pow(Pc,t),new mf(this.h,this.s,this...
method darker (line 1) | darker(t){return t=null==t?qc:Math.pow(qc,t),new mf(this.h,this.s,this.l...
method rgb (line 1) | rgb(){var t=this.h%360+360*(this.h<0),e=isNaN(t)||isNaN(this.s)?0:this.s...
method clamp (line 1) | clamp(){return new mf(yf(this.h),vf(this.s),vf(this.l),cf(this.opacity))}
method displayable (line 1) | displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&th...
method formatHsl (line 1) | formatHsl(){const t=cf(this.opacity);return`${1===t?"hsl(":"hsla("}${yf(...
function Ff (line 1) | function Ff(t){if(t instanceof $f)return new $f(t.l,t.a,t.b,t.opacity);i...
function Sf (line 1) | function Sf(t,e,n,r){return 1===arguments.length?Ff(t):new $f(t,e,n,null...
function $f (line 1) | function $f(t,e,n,r){this.l=+t,this.a=+e,this.b=+n,this.opacity=+r}
function Tf (line 1) | function Tf(t){return t>Cf?Math.pow(t,1/3):t/Df+Mf}
function Bf (line 1) | function Bf(t){return t>Ef?t*t*t:Df*(t-Mf)}
function zf (line 1) | function zf(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-....
function Nf (line 1) | function Nf(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}
function Of (line 1) | function Of(t,e,n,r){return 1===arguments.length?function(t){if(t instan...
function Rf (line 1) | function Rf(t,e,n,r){this.h=+t,this.c=+e,this.l=+n,this.opacity=+r}
function Uf (line 1) | function Uf(t){if(isNaN(t.h))return new $f(t.l,0,0,t.opacity);var e=t.h*...
method brighter (line 1) | brighter(t){return new $f(this.l+18*(null==t?1:t),this.a,this.b,this.opa...
method darker (line 1) | darker(t){return new $f(this.l-18*(null==t?1:t),this.a,this.b,this.opaci...
method rgb (line 1) | rgb(){var t=(this.l+16)/116,e=isNaN(this.a)?t:t+this.a/500,n=isNaN(this....
method brighter (line 1) | brighter(t){return new Rf(this.h,this.c,this.l+18*(null==t?1:t),this.opa...
method darker (line 1) | darker(t){return new Rf(this.h,this.c,this.l-18*(null==t?1:t),this.opaci...
method rgb (line 1) | rgb(){return Uf(this).rgb()}
function Gf (line 1) | function Gf(t,e,n,r){return 1===arguments.length?function(t){if(t instan...
function Vf (line 1) | function Vf(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}
function Xf (line 1) | function Xf(t,e,n,r,i){var o=t*t,a=o*t;return((1-3*t+3*o-a)*e+(4-6*o+3*a...
function Jf (line 1) | function Jf(t){var e=t.length-1;return function(n){var r=n<=0?n=0:n>=1?(...
function Zf (line 1) | function Zf(t){var e=t.length;return function(n){var r=Math.floor(((n%=1...
method brighter (line 1) | brighter(t){return t=null==t?Pc:Math.pow(Pc,t),new Vf(this.h,this.s,this...
method darker (line 1) | darker(t){return t=null==t?qc:Math.pow(qc,t),new Vf(this.h,this.s,this.l...
method rgb (line 1) | rgb(){var t=isNaN(this.h)?0:(this.h+120)*xf,e=+this.l,n=isNaN(this.s)?0:...
function Kf (line 1) | function Kf(t,e){return function(n){return t+n*e}}
function th (line 1) | function th(t,e){var n=e-t;return n?Kf(t,n>180||n<-180?n-360*Math.round(...
function eh (line 1) | function eh(t){return 1==(t=+t)?nh:function(e,n){return n-e?function(t,e...
function nh (line 1) | function nh(t,e){var n=e-t;return n?Kf(t,n):Qf(isNaN(t)?e:t)}
function r (line 1) | function r(t,e){var r=n((t=af(t)).r,(e=af(e)).r),i=n(t.g,e.g),o=n(t.b,e....
function ih (line 1) | function ih(t){return function(e){var n,r,i=e.length,o=new Array(i),a=ne...
function sh (line 1) | function sh(t,e){e||(e=[]);var n,r=t?Math.min(e.length,t.length):0,i=e.s...
function uh (line 1) | function uh(t){return ArrayBuffer.isView(t)&&!(t instanceof DataView)}
function lh (line 1) | function lh(t,e){var n,r=e?e.length:0,i=t?Math.min(r,t.length):0,o=new A...
function ch (line 1) | function ch(t,e){var n=new Date;return t=+t,e=+e,function(r){return n.se...
function fh (line 1) | function fh(t,e){return t=+t,e=+e,function(n){return t*(1-n)+e*n}}
function hh (line 1) | function hh(t,e){var n,r={},i={};for(n in null!==t&&"object"==typeof t||...
function gh (line 1) | function gh(t,e){var n,r,i,o=dh.lastIndex=ph.lastIndex=0,a=-1,s=[],u=[];...
function mh (line 1) | function mh(t,e){var n,r=typeof e;return null==e||"boolean"===r?Qf(e):("...
function yh (line 1) | function yh(t,e){return t=+t,e=+e,function(n){return Math.round(t*(1-n)+...
function bh (line 1) | function bh(t,e,n,r,i,o){var a,s,u;return(a=Math.sqrt(t*t+e*e))&&(t/=a,e...
function wh (line 1) | function wh(t,e,n,r){function i(t){return t.length?t.pop()+" ":""}return...
function Mh (line 1) | function Mh(t){return((t=Math.exp(t))+1/t)/2}
function i (line 1) | function i(t,i){var o,a,s=t[0],u=t[1],l=t[2],c=i[0],f=i[1],h=i[2],d=c-s,...
function Dh (line 1) | function Dh(t){return function(e,n){var r=t((e=gf(e)).h,(n=gf(n)).h),i=n...
function Sh (line 1) | function Sh(t){return function(e,n){var r=t((e=Of(e)).h,(n=Of(n)).h),i=n...
function Bh (line 1) | function Bh(t){return function e(n){function r(e,r){var i=t((e=Gf(e)).h,...
function Oh (line 1) | function Oh(t,e){void 0===e&&(e=t,t=mh);for(var n=0,r=e.length-1,i=e[0],...
function Uh (line 1) | function Uh(t){return+t}
function qh (line 1) | function qh(t){return t}
function Ph (line 1) | function Ph(t,e){return(e-=t=+t)?function(n){return(n-t)/e}:function(t){...
function jh (line 1) | function jh(t,e,n){var r=t[0],i=t[1],o=e[0],a=e[1];return i<r?(r=Ph(i,r)...
function Ih (line 1) | function Ih(t,e,n){var r=Math.min(t.length,e.length)-1,i=new Array(r),o=...
function Wh (line 1) | function Wh(t,e){return e.domain(t.domain()).range(t.range()).interpolat...
function Hh (line 1) | function Hh(){var t,e,n,r,i,o,a=Lh,s=Lh,u=mh,l=qh;function c(){var t=Mat...
function Yh (line 1) | function Yh(){return Hh()(qh,qh)}
function Gh (line 1) | function Gh(t,e,n,r){var i,o=be(t,e,n);switch((r=Re(null==r?",f":r)).typ...
function Vh (line 1) | function Vh(t){var e=t.domain;return t.ticks=function(t){var n=e();retur...
function Xh (line 1) | function Xh(t,e){var n,r=0,i=(t=t.slice()).length-1,o=t[r],a=t[i];return...
function Jh (line 1) | function Jh(t){return Math.log(t)}
function Zh (line 1) | function Zh(t){return Math.exp(t)}
function Qh (line 1) | function Qh(t){return-Math.log(-t)}
function Kh (line 1) | function Kh(t){return-Math.exp(-t)}
function td (line 1) | function td(t){return isFinite(t)?+("1e"+t):t<0?0:t}
function ed (line 1) | function ed(t){return(e,n)=>-t(-e,n)}
function nd (line 1) | function nd(t){const e=t(Jh,Zh),n=e.domain;let r,i,o=10;function a(){ret...
function rd (line 1) | function rd(t){return function(e){return Math.sign(e)*Math.log1p(Math.ab...
function id (line 1) | function id(t){return function(e){return Math.sign(e)*Math.expm1(Math.ab...
function od (line 1) | function od(t){var e=1,n=t(rd(e),id(e));return n.constant=function(n){re...
function ad (line 1) | function ad(t){return function(e){return e<0?-Math.pow(-e,t):Math.pow(e,...
function sd (line 1) | function sd(t){return t<0?-Math.sqrt(-t):Math.sqrt(t)}
function ud (line 1) | function ud(t){return t<0?-t*t:t*t}
function ld (line 1) | function ld(t){var e=t(qh,qh),n=1;return e.exponent=function(e){return a...
function cd (line 1) | function cd(){var t=ld(Hh());return t.copy=function(){return Wh(t,cd())....
function fd (line 1) | function fd(t){return new Date(t)}
function hd (line 1) | function hd(t){return t instanceof Date?+t:+new Date(+t)}
function dd (line 1) | function dd(t,e,n,r,i,o,a,s,u,l){var c=Yh(),f=c.invert,h=c.domain,d=l("....
function pd (line 1) | function pd(){var t,e,n,r,i,o=0,a=1,s=qh,u=!1;function l(e){return null=...
function gd (line 1) | function gd(t,e){return e.domain(t.domain()).interpolator(t.interpolator...
function md (line 1) | function md(){var t=Vh(pd()(qh));return t.copy=function(){return gd(t,md...
function yd (line 1) | function yd(){var t=ld(pd());return t.copy=function(){return gd(t,yd())....
function vd (line 1) | function vd(){var t,e,n,r,i,o,a,s=0,u=.5,l=1,c=1,f=qh,h=!1;function d(t)...
function _d (line 1) | function _d(){var t=ld(vd());return t.copy=function(){return gd(t,_d())....
function xd (line 1) | function xd(t,e,n){const r=t-e+2*n;return t?r>0?r:1:0}
function jd (line 1) | function jd(){const t=Oc().unknown(void 0),e=t.domain,n=t.range;let r,i,...
function Id (line 1) | function Id(t){const e=t.copy;return t.padding=t.paddingOuter,delete t.p...
function Vd (line 1) | function Vd(t){return t[Gd]=!0,t}
function Xd (line 1) | function Xd(t,e,n){return arguments.length>1?(Yd.set(t,function(t,e,n){c...
function Jd (line 1) | function Jd(t){return Yd.has(t)}
function Zd (line 1) | function Zd(t,e){const n=Yd.get(t);return n&&n.metadata[e]}
function Qd (line 1) | function Qd(t){return Zd(t,Rd)}
function Kd (line 1) | function Kd(t){return Zd(t,Ud)}
function tp (line 1) | function tp(t){return Zd(t,Ld)}
function ep (line 1) | function ep(t){return Zd(t,wd)}
function np (line 1) | function np(t){return Zd(t,qd)}
function rp (line 1) | function rp(t){return Zd(t,Sd)}
function r (line 1) | function r(t){return null==t||isNaN(t=+t)?n:t}
function o (line 1) | function o(){var t=0,e=Math.max(1,r.length);for(i=new Array(e-1);++t<e;)...
function a (line 1) | function a(t){return null==t||isNaN(t=+t)?e:r[oe(i,t)]}
function s (line 1) | function s(t){return null!=t&&t<=t?a[oe(o,t,0,i)]:e}
function u (line 1) | function u(){var t=-1;for(o=new Array(i);++t<i;)o[t]=((t+1)*r-(t-i)*n)/(...
function o (line 1) | function o(t){return null!=t&&t<=t?r[oe(n,t,0,i)]:e}
function r (line 1) | function r(t){return null==t||t!=t?void 0:n[(oe(e,t)-1)%n.length]}
function op (line 1) | function op(t,e){const n=e[0],r=F(e)-n;return function(e){return t(n+e*r)}}
function ap (line 1) | function ap(t,e,n){return Oh(lp(e||"rgb",n),t)}
function sp (line 1) | function sp(t,e){const n=new Array(e),r=e+1;for(let i=0;i<e;)n[i]=t(++i/...
function up (line 1) | function up(t,e,n){const r=n-e;let i,o,a;return r&&Number.isFinite(r)?(i...
function lp (line 1) | function lp(t,e){const n=Rh[function(t){return"interpolate"+t.toLowerCas...
function cp (line 1) | function cp(t){const e=t.length/6|0,n=new Array(e);for(let r=0;r<e;)n[r]...
function fp (line 1) | function fp(t,e){for(const n in t)dp(n,e(t[n]))}
function dp (line 1) | function dp(t,e){return t=t&&t.toLowerCase(),arguments.length>1?(hp[t]=e...
function _p (line 1) | function _p(t,e,n){let r;return vt(e)&&(t.bins&&(e=Math.max(e,t.bins.len...
function xp (line 1) | function xp(t,e,n){let r=t.range(),i=r[0],o=F(r),a=yp;if(i>o&&(r=o,o=i,i...
function bp (line 1) | function bp(t,e){return t.bins?xp(t,t.bins):t.ticks?t.ticks(e):t.domain()}
function wp (line 1) | function wp(t,e,n,r,i,o){const a=e.type;let s=mp;if(a===Ed||i===Ed)s=t.t...
function kp (line 1) | function kp(t,e,n){const r=bp(t,e),i=t.base(),o=Math.log(i),a=Math.max(1...
function Ep (line 1) | function Ep(t,e){return t.bins?function(t){const e=t.slice(0,-1);return ...
function Cp (line 1) | function Cp(t,e,n,r,i,o,a){const s=Mp[e.type]&&o!==Ed&&o!==Dd?function(t...
function zp (line 1) | function zp(t,e,n,r){const i=r||e.type;return xt(n)&&function(t){return ...
function Np (line 1) | function Np(t,e,n){n=n||{};const r=Math.max(3,n.maxlen||7),i=zp(t,e,n.fo...
function Up (line 1) | function Up(t){return t&&t.gradient}
function Lp (line 1) | function Lp(t,e,n){const r=t.gradient;let i=t.id,o="radial"===r?Rp:"";re...
function qp (line 1) | function qp(t,e){return null!=t?t:e}
function Pp (line 1) | function Pp(t,e){var n,r=[];return n={gradient:"linear",x1:t?t[0]:0,y1:t...
function Ip (line 1) | function Ip(t,e,n){var r=lt(jp,t)&&jp[t],i=null;return r&&(i=r.curve||r[...
function Xp (line 1) | function Xp(t){const e=[];return(t.match(Hp)||[]).forEach((t=>{let n=t[0...
function rg (line 1) | function rg(t){const e=ng.call(t);if(eg[e])return eg[e];var n=t[0],r=t[1...
function og (line 1) | function og(t,e,n){const r=ig[0]=t[0];if("a"===r||"A"===r)ig[1]=e*t[1],i...
function ag (line 1) | function ag(t,e,n,r,i,o){var a,s,u,l,c,f=null,h=0,d=0,p=0,g=0,m=0,y=0;nu...
function sg (line 1) | function sg(t,e,n,r){const i=function(t,e,n,r,i,o,a,s,u){const l=ng.call...
function cg (line 1) | function cg(t){return lt(lg,t)?lg[t]:function(t){if(!lt(fg,t)){const e=X...
function dg (line 1) | function dg(t){return t.x}
function pg (line 1) | function pg(t){return t.y}
function gg (line 1) | function gg(t){return t.width}
function mg (line 1) | function mg(t){return t.height}
function yg (line 1) | function yg(t){return"function"==typeof t?t:()=>+t}
function vg (line 1) | function vg(t,e,n){return Math.max(e,Math.min(t,n))}
function _g (line 1) | function _g(){var t=dg,e=pg,n=gg,r=mg,i=yg(0),o=i,a=i,s=i,u=null;functio...
function xg (line 1) | function xg(){var t,e,n,r,i,o,a,s,u=null;function l(t,e,n){const r=n/2;i...
function bg (line 1) | function bg(t,e){return null!=t?t:e}
function l (line 1) | function l(){var l,c,f=+t.apply(this,arguments),h=+e.apply(this,argument...
function i (line 1) | function i(){let i;if(n||(n=i=r()),t.apply(this,arguments).draw(n,+e.app...
function Tg (line 1) | function Tg(t){return t.cornerRadius||t.cornerRadiusTopLeft||t.cornerRad...
function Bg (line 1) | function Bg(t,e,n,r){return Fg.context(t)(e,n,r)}
function Ng (line 1) | function Ng(){zg=1}
function Og (line 1) | function Og(t,e,n){var r=e.clip,i=t._defs,o=e.clip_id||(e.clip_id="clip"...
function Rg (line 1) | function Rg(t){this.clear(),t&&this.union(t)}
function Ug (line 1) | function Ug(t){this.mark=t,this.bounds=this.bounds||new Rg}
function Lg (line 1) | function Lg(t){Ug.call(this,t),this.items=this.items||[]}
function qg (line 1) | function qg(t){this._pending=0,this._loader=t||fa()}
function Pg (line 1) | function Pg(t){t._pending+=1}
function jg (line 1) | function jg(t){t._pending-=1}
function Ig (line 1) | function Ig(t,e,n){if(e.stroke&&0!==e.opacity&&0!==e.strokeOpacity){cons...
method clone (line 1) | clone(){return new Rg(this)}
method clear (line 1) | clear(){return this.x1=+Number.MAX_VALUE,this.y1=+Number.MAX_VALUE,this....
method empty (line 1) | empty(){return this.x1===+Number.MAX_VALUE&&this.y1===+Number.MAX_VALUE&...
method equals (line 1) | equals(t){return this.x1===t.x1&&this.y1===t.y1&&this.x2===t.x2&&this.y2...
method set (line 1) | set(t,e,n,r){return n<t?(this.x2=t,this.x1=n):(this.x1=t,this.x2=n),r<e?...
method add (line 1) | add(t,e){return t<this.x1&&(this.x1=t),e<this.y1&&(this.y1=e),t>this.x2&...
method expand (line 1) | expand(t){return this.x1-=t,this.y1-=t,this.x2+=t,this.y2+=t,this}
method round (line 1) | round(){return this.x1=Math.floor(this.x1),this.y1=Math.floor(this.y1),t...
method scale (line 1) | scale(t){return this.x1*=t,this.y1*=t,this.x2*=t,this.y2*=t,this}
method translate (line 1) | translate(t,e){return this.x1+=t,this.x2+=t,this.y1+=e,this.y2+=e,this}
method rotate (line 1) | rotate(t,e,n){const r=this.rotatedPoints(t,e,n);return this.clear().add(...
method rotatedPoints (line 1) | rotatedPoints(t,e,n){var{x1:r,y1:i,x2:o,y2:a}=this,s=Math.cos(t),u=Math....
method union (line 1) | union(t){return t.x1<this.x1&&(this.x1=t.x1),t.y1<this.y1&&(this.y1=t.y1...
method intersect (line 1) | intersect(t){return t.x1>this.x1&&(this.x1=t.x1),t.y1>this.y1&&(this.y1=...
method encloses (line 1) | encloses(t){return t&&this.x1<=t.x1&&this.x2>=t.x2&&this.y1<=t.y1&&this....
method alignsWith (line 1) | alignsWith(t){return t&&(this.x1==t.x1||this.x2==t.x2||this.y1==t.y1||th...
method intersects (line 1) | intersects(t){return t&&!(this.x2<t.x1||this.x1>t.x2||this.y2<t.y1||this...
method contains (line 1) | contains(t,e){return!(t<this.x1||t>this.x2||e<this.y1||e>this.y2)}
method width (line 1) | width(){return this.x2-this.x1}
method height (line 1) | height(){return this.y2-this.y1}
method pending (line 1) | pending(){return this._pending}
method sanitizeURL (line 1) | sanitizeURL(t){const e=this;return Pg(e),e._loader.sanitize(t,{context:"...
method loadImage (line 1) | loadImage(t){const e=this,n=Tc();return Pg(e),e._loader.sanitize(t,{cont...
method ready (line 1) | ready(){const t=this;return new Promise((e=>{!function n(r){t.pending()?...
function sm (line 1) | function sm(t,e){return Hg=t,e?(Vg=e*Jp,Xg=Qg=Math.cos(Vg),Jg=Math.sin(V...
method beginPath (line 1) | beginPath(){}
method closePath (line 1) | closePath(){}
method rect (line 1) | rect(t,e,n,r){Vg?(om(t+n,e),om(t+n,e+r),om(t,e+r),am(t,e)):(Kg(t+n,e+r),...
method quadraticCurveTo (line 1) | quadraticCurveTo(t,e,n,r){const i=rm(t,e),o=im(t,e),a=rm(n,r),s=im(n,r);...
method bezierCurveTo (line 1) | bezierCurveTo(t,e,n,r,i,o){const a=rm(t,e),s=im(t,e),u=rm(n,r),l=im(n,r)...
method arc (line 1) | arc(t,e,n,r,i,o){if(r+=Vg,i+=Vg,Yg=n*Math.cos(i)+t,Gg=n*Math.sin(i)+e,Ma...
function lm (line 1) | function lm(t,e,n,r){const i=(t-e)/(t+n-2*e);0<i&&i<1&&r(t+(e-t)*i)}
function cm (line 1) | function cm(t,e,n,r,i){const o=r-t+3*e-3*n,a=t+n-2*e,s=t-e;let u,l=0,c=0...
function fm (line 1) | function fm(t,e,n,r,i){const o=1-t,a=o*o,s=t*t;return a*o*e+3*a*t*n+3*o*...
function pm (line 1) | function pm(t){return function(e,n){if(!hm)return!0;t(hm,e),dm.clear().u...
function gm (line 1) | function gm(t,e){return e.contains(t.x||0,t.y||0)}
function mm (line 1) | function mm(t,e){const n=t.x||0,r=t.y||0,i=t.width||0,o=t.height||0;retu...
function ym (line 1) | function ym(t,e){const n=t.x||0,r=t.y||0;return vm(e,n,r,null!=t.x2?t.x2...
function vm (line 1) | function vm(t,e,n,r,i){const{x1:o,y1:a,x2:s,y2:u}=t,l=r-e,c=i-n;let f,h,...
function _m (line 1) | function _m(t,e){t.globalCompositeOperation=e.blend||"source-over"}
function xm (line 1) | function xm(t,e){return null==t?e:t}
function bm (line 1) | function bm(t,e){const n=e.length;for(let r=0;r<n;++r)t.addColorStop(e[r...
function wm (line 1) | function wm(t,e,n){return Up(n)?function(t,e,n){const r=n.width(),i=n.he...
function km (line 1) | function km(t,e,n){return(n*=null==e.fillOpacity?1:e.fillOpacity)>0&&(t....
function Mm (line 1) | function Mm(t,e,n){var r=null!=(r=e.strokeWidth)?r:1;return!(r<=0)&&((n*...
function Em (line 1) | function Em(t,e){return t.zindex-e.zindex||t.index-e.index}
function Dm (line 1) | function Dm(t){if(!t.zdirty)return t.zitems;var e,n,r,i=t.items,o=[];for...
function Cm (line 1) | function Cm(t,e){var n,r,i=t.items;if(!i||!i.length)return;const o=Dm(t)...
function Fm (line 1) | function Fm(t,e){var n,r,i=t.items;if(!i||!i.length)return null;const o=...
function Sm (line 1) | function Sm(t){return function(e,n,r){Cm(n,(n=>{r&&!r.intersects(n.bound...
function $m (line 1) | function $m(t){return function(e,n,r){!n.items.length||r&&!r.intersects(...
function Tm (line 1) | function Tm(t,e,n,r){var i=null==n.opacity?1:n.opacity;0!==i&&(t(e,r)||(...
function Bm (line 1) | function Bm(t){return t=t||p,function(e,n,r,i,o,a){return r*=e.pixelRati...
function zm (line 1) | function zm(t,e){return function(n,r,i,o){var a,s,u=Array.isArray(r)?r[0...
function Nm (line 1) | function Nm(t){return Bm(zm(t))}
function Om (line 1) | function Om(t,e){return"translate("+t+","+e+")"}
function Rm (line 1) | function Rm(t){return"rotate("+t+")"}
function Um (line 1) | function Um(t){return Om(t.x||0,t.y||0)}
function Lm (line 1) | function Lm(t,e,n){function r(t,n){var r=n.x||0,i=n.y||0,o=n.angle||0;t....
function Pm (line 1) | function Pm(t,e,n){function r(t,n){t.beginPath(),e(t,n)}const i=zm(r);re...
function Im (line 1) | function Im(t,e){t.beginPath(),Tg(e)?Bg(t,e,0,0):t.rect(0,0,e.width||0,e...
function Wm (line 1) | function Wm(t){const e=xm(t.strokeWidth,1);return null!=t.strokeOffset?t...
function Hm (line 1) | function Hm(t,e){const n=Wm(e);t("d",Bg(null,e,n,n))}
function Ym (line 1) | function Ym(t,e,n,r){const i=Wm(e);t.beginPath(),Bg(t,e,(n||0)+i,(r||0)+i)}
function Qm (line 1) | function Qm(t,e){var n=t.image;return(!n||t.url&&t.url!==n.url)&&(n={com...
function Km (line 1) | function Km(t,e){return null!=t.width?t.width:e&&e.width?!1!==t.aspect&&...
function ty (line 1) | function ty(t,e){return null!=t.height?t.height:e&&e.height?!1!==t.aspec...
function ey (line 1) | function ey(t,e){return"center"===t?e/2:"right"===t?e:0}
function ny (line 1) | function ny(t,e){return"middle"===t?e/2:"bottom"===t?e:0}
function oy (line 1) | function oy(t,e){var n=e.path;if(null==n)return!0;var r=e.x||0,i=e.y||0,...
function sy (line 1) | function sy(t,e){t.beginPath(),Bg(t,e)}
function ly (line 1) | function ly(t,e,n){var r,i,o,a;return!(!e.stroke||!Mm(t,e,n))&&(r=e.x||0...
function gy (line 1) | function gy(t){py.width=t&&hm?vy:my}
function my (line 1) | function my(t,e){return yy(Ay(t,e),xy(t))}
function yy (line 1) | function yy(t,e){return~~(.8*t.length*e)}
function vy (line 1) | function vy(t,e){return xy(t)<=0||!(e=Ay(t,e))?0:_y(e,Ey(t))}
function _y (line 1) | function _y(t,e){const n=`(${e}) ${t}`;let r=dy.get(n);return void 0===r...
function xy (line 1) | function xy(t){return null!=t.fontSize?+t.fontSize||0:11}
function by (line 1) | function by(t){return null!=t.lineHeight?t.lineHeight:xy(t)+2}
function wy (line 1) | function wy(t){return e=t.lineBreak&&t.text&&!k(t.text)?t.text.split(t.l...
function ky (line 1) | function ky(t){const e=wy(t);return(k(e)?e.length-1:0)*by(t)}
function Ay (line 1) | function Ay(t,e){const n=null==e?"":(e+"").trim();return t.limit>0&&n.le...
function My (line 1) | function My(t,e){var n=t.font;return(e&&n?String(n).replace(/"/g,"'"):n)...
function Ey (line 1) | function Ey(t,e){return(t.fontStyle?t.fontStyle+" ":"")+(t.fontVariant?t...
function Dy (line 1) | function Dy(t){var e=t.baseline,n=xy(t);return Math.round("top"===e?.79*...
function Sy (line 1) | function Sy(t){var e,n=t.x||0,r=t.y||0,i=t.radius||0;return i&&(e=(t.the...
function $y (line 1) | function $y(t,e,n){var r,i=py.height(e),o=e.align,a=Sy(e),s=a.x1,u=a.y1,...
function Ny (line 1) | function Ny(t,e,n){var r=zy[t.mark.marktype],i=e||r.bound;return r.neste...
function Ry (line 1) | function Ry(t,e,n){var r,i,o,a,s=zy[t.marktype],u=s.bound,l=t.items,c=l&...
function Ly (line 1) | function Ly(t,e){return JSON.stringify(t,Uy,e)}
function qy (line 1) | function qy(t){return Py("string"==typeof t?JSON.parse(t):t)}
function Py (line 1) | function Py(t){var e,n,r,i=t.marktype,o=t.items;if(o)for(n=0,r=o.length;...
function jy (line 1) | function jy(t){arguments.length?this.root=qy(t):(this.root=Iy({marktype:...
function Iy (line 1) | function Iy(t,e){const n={bounds:new Rg,clip:!!t.clip,group:e,interactiv...
function Wy (line 1) | function Wy(t,e,n){return!t&&"undefined"!=typeof document&&document.crea...
function Hy (line 1) | function Hy(t,e){e=e.toLowerCase();for(var n=t.childNodes,r=0,i=n.length...
function Yy (line 1) | function Yy(t,e,n,r){var i,o=t.childNodes[e];return o&&o.tagName.toLower...
function Gy (line 1) | function Gy(t,e){for(var n=t.childNodes,r=n.length;r>e;)t.removeChild(n[...
function Vy (line 1) | function Vy(t){return"mark-"+t.marktype+(t.role?" role-"+t.role:"")+(t.n...
function Xy (line 1) | function Xy(t,e){const n=e.getBoundingClientRect();return[t.clientX-n.le...
function Jy (line 1) | function Jy(t,e){this._active=null,this._handlers={},this._loader=t||fa(...
function Zy (line 1) | function Zy(t,e,n,r){t.element().setAttribute("title",r||"")}
function Qy (line 1) | function Qy(t){this._el=null,this._bgcolor=null,this._loader=new qg(t)}
method toJSON (line 1) | toJSON(t){return Ly(this.root,t||0)}
method mark (line 1) | mark(t,e,n){const r=Iy(t,e=e||this.root.items[0]);return e.items[n]=r,r....
method initialize (line 1) | initialize(t,e,n){return this._el=t,this._obj=n||null,this.origin(e)}
method element (line 1) | element(){return this._el}
method canvas (line 1) | canvas(){return this._el&&this._el.firstChild}
method origin (line 1) | origin(t){return arguments.length?(this._origin=t||[0,0],this):this._ori...
method scene (line 1) | scene(t){return arguments.length?(this._scene=t,this):this._scene}
method on (line 1) | on(){}
method off (line 1) | off(){}
method _handlerIndex (line 1) | _handlerIndex(t,e,n){for(let r=t?t.length:0;--r>=0;)if(t[r].type===e&&(!...
method handlers (line 1) | handlers(t){const e=this._handlers,n=[];if(t)n.push(...e[this.eventName(...
method eventName (line 1) | eventName(t){const e=t.indexOf(".");return e<0?t:t.slice(0,e)}
method handleHref (line 1) | handleHref(t,e,n){this._loader.sanitize(n,{context:"href"}).then((e=>{co...
method handleTooltip (line 1) | handleTooltip(t,e,n){if(e&&null!=e.tooltip){e=function(t,e,n,r){var i,o,...
method getItemBoundingClientRect (line 1) | getItemBoundingClientRect(t){const e=this.canvas();if(!e)return;const n=...
method initialize (line 1) | initialize(t,e,n,r,i){return this._el=t,this.resize(e,n,r,i)}
method element (line 1) | element(){return this._el}
method canvas (line 1) | canvas(){return this._el&&this._el.firstChild}
method background (line 1) | background(t){return 0===arguments.length?this._bgcolor:(this._bgcolor=t...
method resize (line 1) | resize(t,e,n,r){return this._width=t,this._height=e,this._origin=n||[0,0...
method dirty (line 1) | dirty(){}
method render (line 1) | render(t,e){const n=this;return n._call=function(){n._render(t,e)},n._ca...
method _render (line 1) | _render(){}
method renderAsync (line 1) | renderAsync(t,e){const n=this.render(t,e);return this._ready?this._ready...
method _load (line 1) | _load(t,e){var n=this,r=n._loader[t](e);if(!n._ready){const t=n._call;n....
method sanitizeURL (line 1) | sanitizeURL(t){return this._load("sanitizeURL",t)}
method loadImage (line 1) | loadImage(t){return this._load("loadImage",t)}
function vv (line 1) | function vv(t,e){Jy.call(this,t,e),this._down=null,this._touch=null,this...
function _v (line 1) | function _v(t,e){(t=>t===hv||t===dv||t===pv?[hv,dv,pv]:[t])(e).forEach((...
function xv (line 1) | function xv(t,e,n){e.forEach((e=>t.fire(e,n)))}
function bv (line 1) | function bv(t,e,n){return function(r){const i=this._active,o=this.pickEv...
function wv (line 1) | function wv(t){return function(e){xv(this,t,e),this._active=null}}
function kv (line 1) | function kv(t,e,n,r,i,o){const a="undefined"!=typeof HTMLElement&&t inst...
function Av (line 1) | function Av(t){Qy.call(this,t),this._options={},this._redraw=!1,this._di...
method initialize (line 1) | initialize(t,e,n){return this._canvas=t&&Hy(t,"canvas"),[cv,av,nv,rv,iv,...
method canvas (line 1) | canvas(){return this._canvas}
method context (line 1) | context(){return this._canvas.getContext("2d")}
method DOMMouseScroll (line 1) | DOMMouseScroll(t){this.fire(fv,t)}
method pointerdown (line 1) | pointerdown(t){this._down=this._active,this.fire(nv,t)}
method mousedown (line 1) | mousedown(t){this._down=this._active,this.fire(av,t)}
method click (line 1) | click(t){this._down===this._active&&(this.fire(cv,t),this._down=null)}
method touchstart (line 1) | touchstart(t){this._touch=this.pickEvent(t.changedTouches[0]),this._firs...
method touchmove (line 1) | touchmove(t){this.fire(dv,t,!0)}
method touchend (line 1) | touchend(t){this.fire(pv,t,!0),this._touch=null}
method fire (line 1) | fire(t,e,n){const r=n?this._touch:this._active,i=this._handlers[t];if(e....
method on (line 1) | on(t,e){const n=this.eventName(t),r=this._handlers;return this._handlerI...
method off (line 1) | off(t,e){const n=this.eventName(t),r=this._handlers[n],i=this._handlerIn...
method pickEvent (line 1) | pickEvent(t){const e=Xy(t,this._canvas),n=this._origin;return this.pick(...
method pick (line 1) | pick(t,e,n,r,i){const o=this.context();return zy[t.marktype].pick.call(t...
function Ev (line 1) | function Ev(t,e){Jy.call(this,t,e);const n=this;n._hrefHandler=Dv(n,((t,...
method initialize (line 1) | initialize(t,e,n,r,i,o){return this._options=o||{},this._canvas=this._op...
method resize (line 1) | resize(t,e,n,r){if(Mv.resize.call(this,t,e,n,r),this._canvas)kv(this._ca...
method canvas (line 1) | canvas(){return this._canvas}
method context (line 1) | context(){return this._options.externalContext||(this._canvas?this._canv...
method dirty (line 1) | dirty(t){const e=this._tempb.clear().union(t.bounds);let n=t.mark.group;...
method _render (line 1) | _render(t,e){const n=this.context(),r=this._origin,i=this._width,o=this....
method draw (line 1) | draw(t,e,n,r){if("group"!==e.marktype&&null!=r&&!r.includes(e.marktype))...
method clear (line 1) | clear(t,e,n,r){const i=this._options,o=this.context();"pdf"===i.type||i....
method initialize (line 1) | initialize(t,e,n){let r=this._svg;return r&&(r.removeEventListener(yv,th...
method canvas (line 1) | canvas(){return this._svg}
method on (line 1) | on(t,e){const n=this.eventName(t),r=this._handlers;if(this._handlerIndex...
method off (line 1) | off(t,e){const n=this.eventName(t),r=this._handlers[n],i=this._handlerIn...
function Uv (line 1) | function Uv(t,e){const n=!1===e.aria;if(t(Cv,n||void 0),n||null==e.descr...
function Lv (line 1) | function Lv(t){return!1===t.aria?{[Cv]:!0}:Nv[t.role]?null:Ov[t.role]?fu...
function qv (line 1) | function qv(t){return V(t.text).join(" ")}
function Pv (line 1) | function Pv(t){try{return V(F(t.items).items[0].text).join(" ")}catch(t)...
function Iv (line 1) | function Iv(){let t="",e="",n="";const r=[],i=()=>e=n="",o=(t,n)=>{var r...
function Hv (line 1) | function Hv(t,e){if(t.open(e.tagName),e.hasAttributes()){const n=e.attri...
function Zv (line 1) | function Zv(t){Qy.call(this,t),this._dirtyID=0,this._dirty=[],this._svg=...
function Kv (line 1) | function Kv(t,e){for(;t&&t.dirty!==e;t=t.mark.group){if(t.dirty=e,!t.mar...
function t_ (line 1) | function t_(t,e,n){let r,i,o;if("radial"===e.gradient){let r=Yy(t,n++,"p...
function e_ (line 1) | function e_(t,e,n){let r;return(t=Yy(t,n,"clipPath",Jv)).setAttribute("i...
function n_ (line 1) | function n_(t,e,n,r,i){let o,a=t._svg;if(!a&&(o=e.ownerDocument,a=Wy(o,r...
method initialize (line 1) | initialize(t,e,n,r,i){return this._defs={},this._clearDefs(),t&&(this._s...
method background (line 1) | background(t){return arguments.length&&this._svg&&this._svg.style.setPro...
method resize (line 1) | resize(t,e,n,r){return Qv.resize.call(this,t,e,n,r),this._svg&&(u_(this....
method canvas (line 1) | canvas(){return this._svg}
method svg (line 1) | svg(){const t=this._svg,e=this._bgcolor;if(!t)return null;let n;e&&(t.re...
method _render (line 1) | _render(t,e){return this._dirtyCheck()&&(this._dirtyAll&&this._clearDefs...
method dirty (line 1) | dirty(t){t.dirty!==this._dirtyID&&(t.dirty=this._dirtyID,this._dirty.pus...
method isDirty (line 1) | isDirty(t){return this._dirtyAll||!t._svg||!t._svg.ownerSVGElement||t.di...
method _dirtyCheck (line 1) | _dirtyCheck(){this._dirtyAll=!0;const t=this._dirty;if(!t.length||!this....
method mark (line 1) | mark(t,e,n,r){if(!this.isDirty(e))return e._svg;const i=this._svg,o=e.ma...
method _update (line 1) | _update(t,e,n){r_=e,i_=e.__values__,Uv(a_,n),t.attr(a_,n,this);const r=o...
method style (line 1) | style(t,e){if(null!=e){for(const n in Yv){let r="font"===n?My(e):e[n];if...
method defs (line 1) | defs(){const t=this._svg,e=this._defs;let n=e.el,r=0;for(const i in e.gr...
method _clearDefs (line 1) | _clearDefs(){const t=this._defs;t.gradient={},t.clipping={}}
method group (line 1) | group(t,e,n){const r=r_=e.childNodes[2];i_=r.__values__,t.foreground(a_,...
method image (line 1) | image(t,e,n){!1===n.smooth?(s_(e,"image-rendering","optimizeSpeed"),s_(e...
method text (line 1) | text(t,e,n){const r=wy(n);let i,o,a,s;k(r)?(o=r.map((t=>Ay(n,t))),i=o.jo...
function a_ (line 1) | function a_(t,e,n){e!==i_[t]&&(n?function(t,e,n,r){null!=n?t.setAttribut...
function s_ (line 1) | function s_(t,e,n){n!==i_[e]&&(null==n?t.style.removeProperty(e):t.style...
function u_ (line 1) | function u_(t,e){for(const n in e)l_(t,n,e[n])}
function l_ (line 1) | function l_(t,e,n){null!=n?t.setAttribute(e,n):t.removeAttribute(e)}
function c_ (line 1) | function c_(){let t;return"undefined"==typeof window?"":(t=window.locati...
function f_ (line 1) | function f_(t){Qy.call(this,t),this._text=null,this._defs={gradient:{},c...
method svg (line 1) | svg(){return this._text}
method _render (line 1) | _render(t){const e=Iv();e.open("svg",ot({},Zm,{class:"marks",width:this....
method mark (line 1) | mark(t,e){const n=zy[e.marktype],r=n.tag,i=[Uv,n.attr];t.open("g",{class...
method href (line 1) | href(t){const e=t.href;let n;if(e){if(n=this._hrefs&&this._hrefs[e])retu...
method attr (line 1) | attr(t,e,n,r){const i={},o=(t,e,n,r)=>{i[r||t]=e};return Array.isArray(n...
method defs (line 1) | defs(t){const e=this._defs.gradient,n=this._defs.clipping;if(0!==Object....
function d_ (line 1) | function d_(t){Qy.call(this,t),this._svgRenderer=new Zv(t),this._canvasR...
function g_ (line 1) | function g_(t,e){vv.call(this,t,e)}
method initialize (line 1) | initialize(t,e,n,r,i){this._root_el=Yy(t,0,"div");const o=Yy(this._root_...
method dirty (line 1) | dirty(t){return h_.svgMarkTypes.includes(t.mark.marktype)?this._svgRende...
method _render (line 1) | _render(t,e){const n=(e??["arc","area","image","line","path","rect","rul...
method resize (line 1) | resize(t,e,n,r){return p_.resize.call(this,t,e,n,r),this._svgRenderer.re...
method background (line 1) | background(t){return h_.svgOnTop?this._canvasRenderer.background(t):this...
method initialize (line 1) | initialize(t,e,n){const r=Yy(Yy(t,0,"div"),h_.svgOnTop?0:1,"div");return...
function b_ (line 1) | function b_(t,e){return t=String(t||"").toLowerCase(),arguments.length>1...
function w_ (line 1) | function w_(t,e,n){const r=[],i=(new Rg).union(e),o=t.marktype;return o?...
function k_ (line 1) | function k_(t,e,n,r){if(function(t,e,n){return t.bounds&&e.intersects(t....
function A_ (line 1) | function A_(t,e,n,r){n&&n(t.mark)&&M_(t,e,zy.group.isect)&&r.push(t);con...
function M_ (line 1) | function M_(t,e,n){const r=t.bounds;return e.encloses(r)||e.intersects(r...
function D_ (line 1) | function D_(t){const e=t.clip;if(J(e))e(sm(E_.clear()));else{if(!e)retur...
function F_ (line 1) | function F_(t,e,n){return t===e||("path"===n?S_(t,e):t instanceof Date&&...
function S_ (line 1) | function S_(t,e){return F_(Xp(t),Xp(e))}
function px (line 1) | function px(t){Ja.call(this,null,t)}
function gx (line 1) | function gx(t,e,n){return e(t.bounds.clear(),t,n)}
method transform (line 1) | transform(t,e){const n=e.dataflow,r=t.mark,i=r.marktype,o=zy[i],a=o.boun...
function yx (line 1) | function yx(t){Ja.call(this,0,t)}
function vx (line 1) | function vx(t){Ja.call(this,null,t)}
function _x (line 1) | function _x(t){Ja.call(this,null,t)}
method transform (line 1) | transform(t,e){const n=(i=e.dataflow)._signals[mx]||(i._signals[mx]=i.ad...
method transform (line 1) | transform(t,e){let n=this.value;n||(n=e.dataflow.scenegraph().mark(t.mar...
function Ex (line 1) | function Ex(t){Ja.call(this,null,t)}
method transform (line 1) | transform(t,e){const n=xx[t.method]||xx.parity,r=t.separation||0;let i,o...
method transform (line 1) | transform(t,e){const n=e.dataflow;if(e.visit(e.ALL,(t=>n.dirty(t))),e.fi...
function Cx (line 1) | function Cx(t,e,n){return t[e]===n?0:(t[e]=n,1)}
function Fx (line 1) | function Fx(t){var e=t.items[0].orient;return e===T_||e===B_}
function Sx (line 1) | function Sx(t,e,n,r){var i,o,a=e.items[0],s=a.datum,u=null!=a.translate?...
function $x (line 1) | function $x(t,e,n,r,i,o,a,s){const u=e.bounds;if(e.auto){const s=a*(n+i+...
function zx (line 1) | function zx(t){return(new Rg).set(0,0,t.width||0,t.height||0)}
function Nx (line 1) | function Nx(t){const e=t.bounds.clone();return e.empty()?e.set(0,0,0,0):...
function Ox (line 1) | function Ox(t,e,n){const r=A(t)?t[e]:t;return null!=r?r:void 0!==n?n:0}
function Rx (line 1) | function Rx(t){return t<0?Math.ceil(-t):0}
function Ux (line 1) | function Ux(t,e,n){var r,i,o,a,s,u,l,c,f,h,d,p=!n.nodirty,g=n.bounds===f...
function Lx (line 1) | function Lx(t,e,n){var r,i,o,a,s,u,l,c=function(t){var e,n,r=t.items,i=r...
function qx (line 1) | function qx(t,e){return"x1"===e?t.x||0:"y1"===e?t.y||0:"x2"===e?(t.x||0)...
function Px (line 1) | function Px(t,e){return t.bounds[e]}
function jx (line 1) | function jx(t,e,n,r,i,o,a,s,u,l,c,f,h,d){var p,g,m,y,v,_,x,b,w,k=n.lengt...
function Ix (line 1) | function Ix(t,e,n,r,i,o){if(e){t.dirty(e);var a=n,s=n;r?a=Math.round(i.x...
function Wx (line 1) | function Wx(t,e,n,r,i,o,a){const s=function(t,e){const n=t[e]||{};return...
function Hx (line 1) | function Hx(t,e){var n,r,i=e.items[0],o=i.datum,a=i.orient,s=i.bounds,u=...
function Yx (line 1) | function Yx(t,e,n,r,i,o,a){const s="symbol"!==t.datum.type,u=n.datum.vgr...
function Gx (line 1) | function Gx(t,e,n,r){e.x+=n,e.y+=r,e.bounds.translate(n,r),e.mark.bounds...
function Vx (line 1) | function Vx(t){Ja.call(this,null,t)}
method transform (line 1) | transform(t,e){const n=e.dataflow;return t.mark.items.forEach((e=>{t.lay...
function Jx (line 1) | function Jx(t){Ja.call(this,null,t)}
function Zx (line 1) | function Zx(t){Ja.call(this,null,t)}
function Qx (line 1) | function Qx(){return _a({})}
function Kx (line 1) | function Kx(t){Ja.call(this,null,t)}
function tb (line 1) | function tb(t){Ja.call(this,[],t)}
method transform (line 1) | transform(t,e){if(this.value&&!t.modified())return e.StopPropagation;var...
method transform (line 1) | transform(t,e){var n=e.dataflow,r=e.fork(e.NO_SOURCE|e.NO_FIELDS),i=t.it...
method transform (line 1) | transform(t,e){var n=e.fork(e.ADD_REM),r=t.mod||!1,i=t.encoders,o=e.enco...
method transform (line 1) | transform(t,e){if(null!=this.value&&!t.modified())return e.StopPropagati...
function ob (line 1) | function ob(t){Ja.call(this,{},t)}
method transform (line 1) | transform(t,e){var n=t.sourceX||eb,r=t.sourceY||nb,i=t.targetX||rb,o=t.t...
function cb (line 1) | function cb(t){Ja.call(this,null,t)}
method transform (line 1) | transform(t,e){var n,r,i,o=t.as||["startAngle","endAngle"],a=o[0],s=o[1]...
function hb (line 1) | function hb(t){return Qd(t)&&t!==Cd}
function pb (line 1) | function pb(t){Ja.call(this,null,t),this.modified(!0)}
function gb (line 1) | function gb(t,e,n){ep(t)&&(Math.abs(e.reduce(((t,e)=>t+(e<0?-1:e>0?1:0))...
function mb (line 1) | function mb(t,e,n){return J(t)&&(e||n)?op(t,yb(e||[0,1],n)):t}
function yb (line 1) | function yb(t,e){return e?t.slice().reverse():t}
function vb (line 1) | function vb(t){Ja.call(this,null,t)}
method transform (line 1) | transform(t,e){var n=e.dataflow,r=this.value,i=function(t){var e,n=t.typ...
method transform (line 1) | transform(t,e){const n=t.modified("sort")||e.changed(e.ADD)||e.modified(...
function kb (line 1) | function kb(t){Ja.call(this,null,t)}
function Ab (line 1) | function Ab(t,e,n,r,i){for(var o,a=(e-t.sum)/2,s=t.length,u=0;u<s;++u)(o...
function Mb (line 1) | function Mb(t,e,n,r,i){for(var o,a=1/t.sum,s=0,u=t.length,l=0,c=0;l<u;++...
function Eb (line 1) | function Eb(t,e,n,r,i){for(var o,a,s=0,u=0,l=t.length,c=0;c<l;++c)(o=+n(...
method transform (line 1) | transform(t,e){var n,r,i,o,a=t.as||wb,s=a[0],u=a[1],l=ka(t.sort),c=t.fie...
function Xb (line 1) | function Xb(t){return t>1?0:t<-1?Sb:Math.acos(t)}
function Jb (line 1) | function Jb(t){return t>1?$b:t<-1?-$b:Math.asin(t)}
function Zb (line 1) | function Zb(){}
function Qb (line 1) | function Qb(t,e){t&&tw.hasOwnProperty(t.type)&&tw[t.type](t,e)}
function ew (line 1) | function ew(t,e,n){var r,i=-1,o=t.length-n;for(e.lineStart();++i<o;)r=t[...
function nw (line 1) | function nw(t,e){var n=-1,r=t.length;for(e.polygonStart();++n<r;)ew(t[n]...
function rw (line 1) | function rw(t,e){t&&Kb.hasOwnProperty(t.type)?Kb[t.type](t,e):Qb(t,e)}
function kw (line 1) | function kw(){ww.point=Mw}
function Aw (line 1) | function Aw(){Ew(iw,ow)}
function Mw (line 1) | function Mw(t,e){ww.point=Ew,iw=t,ow=e,aw=t*=Nb,sw=Lb(e=(e*=Nb)/2+Tb),uw...
function Ew (line 1) | function Ew(t,e){var n=(t*=Nb)-aw,r=n>=0?1:-1,i=r*n,o=Lb(e=(e*=Nb)/2+Tb)...
function Dw (line 1) | function Dw(t){return[Ub(t[1],t[0]),Jb(t[2])]}
function Cw (line 1) | function Cw(t){var e=t[0],n=t[1],r=Lb(n);return[r*Lb(e),r*Hb(e),Hb(n)]}
function Fw (line 1) | function Fw(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]}
function Sw (line 1) | function Sw(t,e){return[t[1]*e[2]-t[2]*e[1],t[2]*e[0]-t[0]*e[2],t[0]*e[1...
function $w (line 1) | function $w(t,e){t[0]+=e[0],t[1]+=e[1],t[2]+=e[2]}
function Tw (line 1) | function Tw(t,e){return[t[0]*e,t[1]*e,t[2]*e]}
function Bw (line 1) | function Bw(t){var e=Gb(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=e,t[1]/=e,t...
function Zw (line 1) | function Zw(t,e){vw.push(_w=[lw=t,fw=t]),e<cw&&(cw=e),e>hw&&(hw=e)}
function Qw (line 1) | function Qw(t,e){var n=Cw([t*Nb,e*Nb]);if(mw){var r=Sw(mw,n),i=Sw([r[1],...
function Kw (line 1) | function Kw(){Jw.point=Qw}
function tk (line 1) | function tk(){_w[0]=lw,_w[1]=fw,Jw.point=Zw,mw=null}
function ek (line 1) | function ek(t,e){if(mw){var n=t-dw;yw.add(Ob(n)>180?n+(n>0?360:-360):n)}...
function nk (line 1) | function nk(){ww.lineStart()}
function rk (line 1) | function rk(){ek(pw,gw),ww.lineEnd(),Ob(yw)>Cb&&(lw=-(fw=180)),_w[0]=lw,...
function ik (line 1) | function ik(t,e){return(e-=t)<0?e+360:e}
function ok (line 1) | function ok(t,e){return t[0]-e[0]}
function ak (line 1) | function ak(t,e){return t[0]<=t[1]?t[0]<=e&&e<=t[1]:e<t[0]||t[1]<e}
function uk (line 1) | function uk(t,e){t*=Nb;var n=Lb(e*=Nb);lk(n*Lb(t),n*Hb(t),Hb(e))}
function lk (line 1) | function lk(t,e,n){++zw,Ow+=(t-Ow)/zw,Rw+=(e-Rw)/zw,Uw+=(n-Uw)/zw}
function ck (line 1) | function ck(){sk.point=fk}
function fk (line 1) | function fk(t,e){t*=Nb;var n=Lb(e*=Nb);Gw=n*Lb(t),Vw=n*Hb(t),Xw=Hb(e),sk...
function hk (line 1) | function hk(t,e){t*=Nb;var n=Lb(e*=Nb),r=n*Lb(t),i=n*Hb(t),o=Hb(e),a=Ub(...
function dk (line 1) | function dk(){sk.point=uk}
function pk (line 1) | function pk(){sk.point=mk}
function gk (line 1) | function gk(){yk(Hw,Yw),sk.point=uk}
function mk (line 1) | function mk(t,e){Hw=t,Yw=e,t*=Nb,e*=Nb,sk.point=yk;var n=Lb(e);Gw=n*Lb(t...
function yk (line 1) | function yk(t,e){t*=Nb;var n=Lb(e*=Nb),r=n*Lb(t),i=n*Hb(t),o=Hb(e),a=Vw*...
function vk (line 1) | function vk(t,e){function n(n,r){return n=t(n,r),e(n[0],n[1])}return t.i...
function _k (line 1) | function _k(t,e){return Ob(t)>Sb&&(t-=Math.round(t/Bb)*Bb),[t,e]}
function xk (line 1) | function xk(t,e,n){return(t%=Bb)?e||n?vk(wk(t),kk(e,n)):wk(t):e||n?kk(e,...
function bk (line 1) | function bk(t){return function(e,n){return Ob(e+=t)>Sb&&(e-=Math.round(e...
function wk (line 1) | function wk(t){var e=bk(t);return e.invert=bk(-t),e}
function kk (line 1) | function kk(t,e){var n=Lb(t),r=Hb(t),i=Lb(e),o=Hb(e);function a(t,e){var...
function Ak (line 1) | function Ak(t,e){(e=Cw(e))[0]-=t,Bw(e);var n=Xb(-e[1]);return((-e[2]<0?-...
function Mk (line 1) | function Mk(){var t,e=[];return{point:function(e,n,r){t.push([e,n,r])},l...
function Ek (line 1) | function Ek(t,e){return Ob(t[0]-e[0])<Cb&&Ob(t[1]-e[1])<Cb}
function Dk (line 1) | function Dk(t,e,n,r){this.x=t,this.z=e,this.o=n,this.e=r,this.v=!1,this....
function Ck (line 1) | function Ck(t,e,n,r,i){var o,a,s=[],u=[];if(t.forEach((function(t){if(!(...
function Fk (line 1) | function Fk(t){if(e=t.length){for(var e,n,r=0,i=t[0];++r<e;)i.n=n=t[r],n...
function Sk (line 1) | function Sk(t){return Ob(t[0])<=Sb?t[0]:Yb(t[0])*((Ob(t[0])+Sb)%Bb-Sb)}
function $k (line 1) | function $k(t,e,n,r){return function(i){var o,a,s,u=e(i),l=Mk(),c=e(l),f...
function Tk (line 1) | function Tk(t){return t.length>1}
function Bk (line 1) | function Bk(t,e){return((t=t.x)[0]<0?t[1]-$b-Cb:$b-t[1])-((e=e.x)[0]<0?e...
function Nk (line 1) | function Nk(t){var e=Lb(t),n=6*Nb,r=e>0,i=Ob(e)>Cb;function o(t,n){retur...
function Uk (line 1) | function Uk(t,e,n,r){function i(i,o){return t<=i&&i<=n&&e<=o&&o<=r}funct...
function Lk (line 1) | function Lk(t,e,n){var r=Se(t,e-Cb,n).concat(e);return function(t){retur...
function qk (line 1) | function qk(t,e,n){var r=Se(t,e-Cb,n).concat(e);return function(t){retur...
function Xk (line 1) | function Xk(){Vk.point=Jk}
function Jk (line 1) | function Jk(t,e){Vk.point=Zk,Pk=Ik=t,jk=Wk=e}
function Zk (line 1) | function Zk(t,e){Gk.add(Wk*t-Ik*e),Ik=t,Wk=e}
function Qk (line 1) | function Qk(){Zk(Pk,jk)}
function vA (line 1) | function vA(t,e){uA+=t,lA+=e,++cA}
function _A (line 1) | function _A(){yA.point=xA}
function xA (line 1) | function xA(t,e){yA.point=bA,vA(aA=t,sA=e)}
function bA (line 1) | function bA(t,e){var n=t-aA,r=e-sA,i=Gb(n*n+r*r);fA+=i*(aA+t)/2,hA+=i*(s...
function wA (line 1) | function wA(){yA.point=vA}
function kA (line 1) | function kA(){yA.point=MA}
function AA (line 1) | function AA(){EA(iA,oA)}
function MA (line 1) | function MA(t,e){yA.point=EA,vA(iA=aA=t,oA=sA=e)}
function EA (line 1) | function EA(t,e){var n=t-aA,r=e-sA,i=Gb(n*n+r*r);fA+=i*(aA+t)/2,hA+=i*(s...
function DA (line 1) | function DA(t){this._context=t}
function NA (line 1) | function NA(t,e){zA.point=OA,FA=$A=t,SA=TA=e}
function OA (line 1) | function OA(t,e){$A-=t,TA-=e,BA.add(Gb($A*$A+TA*TA)),$A=t,TA=e}
class PA (line 1) | class PA{constructor(t){this._append=null==t?jA:function(t){const e=Math...
method constructor (line 1) | constructor(t){this._append=null==t?jA:function(t){const e=Math.floor(...
method pointRadius (line 1) | pointRadius(t){return this._radius=+t,this}
method polygonStart (line 1) | polygonStart(){this._line=0}
method polygonEnd (line 1) | polygonEnd(){this._line=NaN}
method lineStart (line 1) | lineStart(){this._point=0}
method lineEnd (line 1) | lineEnd(){0===this._line&&(this._+="Z"),this._point=NaN}
method point (line 1) | point(t,e){switch(this._point){case 0:this._append`M${t},${e}`,this._p...
method result (line
Condensed preview — 303 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (5,382K chars).
[
{
"path": ".devcontainer/devcontainer.json",
"chars": 584,
"preview": "{\n \"name\": \"Tianshou\",\n \"dockerFile\": \"../Dockerfile\",\n \"workspaceFolder\": \"/workspaces/tianshou\",\n \"runArgs"
},
{
"path": ".dockerignore",
"chars": 139,
"preview": "data\nlogs\ntest/log\ndocs/jupyter_execute\ndocs/.jupyter_cache\n.lsp\n.clj-kondo\ndocs/_build\ncoverage*\n__pycache__\n*.egg-info"
},
{
"path": ".github/ISSUE_TEMPLATE.md",
"chars": 738,
"preview": "- [ ] I have marked all applicable categories:\n + [ ] exception-raising bug\n + [ ] RL algorithm bug\n + [ ] docu"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 842,
"preview": "- [ ] I have added the correct label(s) to this Pull Request or linked the relevant issue(s)\n- [ ] I have provided a des"
},
{
"path": ".github/workflows/extra_sys.yml",
"chars": 2107,
"preview": "name: Windows/MacOS\n\non:\n pull_request:\n branches:\n - master\n push:\n branches:\n - master\n workflow_"
},
{
"path": ".github/workflows/gputest.yml",
"chars": 1847,
"preview": "name: Ubuntu GPU\n\non:\n pull_request:\n branches:\n - master\n push:\n branches:\n - master\n workflow_dis"
},
{
"path": ".github/workflows/lint_and_docs.yml",
"chars": 1968,
"preview": "name: Check Formatting/Typing and Build Docs\n\non:\n pull_request:\n branches:\n - master\n push:\n branches:\n "
},
{
"path": ".github/workflows/publish.yaml",
"chars": 934,
"preview": "name: Upload Python Package\n\non:\n release:\n types: [created]\n\njobs:\n deploy:\n runs-on: ubuntu-latest\n steps:\n"
},
{
"path": ".github/workflows/pytest.yml",
"chars": 3421,
"preview": "name: Ubuntu\n\non:\n pull_request:\n branches:\n - master\n push:\n branches:\n - master\n workflow_dispatc"
},
{
"path": ".gitignore",
"chars": 2388,
"preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# .idea folder\n.idea/\n\n#"
},
{
"path": ".pre-commit-config.yaml",
"chars": 1463,
"preview": "default_install_hook_types: [commit-msg, pre-commit]\ndefault_stages: [commit, manual]\nfail_fast: false\nrepos:\n - repo: "
},
{
"path": ".readthedocs.yaml",
"chars": 898,
"preview": "# .readthedocs.yaml\n# Read the Docs configuration file\n# See https://docs.readthedocs.io/en/stable/config-file/v2.html f"
},
{
"path": "CHANGELOG.md",
"chars": 38268,
"preview": "# Release 2.0.0 (2025-12-01)\n\nThis major release of Tianshou is a big step towards cleaner design and improved usability"
},
{
"path": "CONTRIBUTING.md",
"chars": 157,
"preview": "# Contributing to Tianshou\n\nPlease refer to the ['Developer Guide' on tianshou.org](https://tianshou.org/en/latest/04_de"
},
{
"path": "Dockerfile",
"chars": 1459,
"preview": "# Use the official Python image for the base image.\nFROM --platform=linux/amd64 python:3.11-slim\n\n# Set environment vari"
},
{
"path": "LICENSE",
"chars": 1078,
"preview": "MIT License\n\nCopyright (c) 2022 Tianshou contributors\n\nPermission is hereby granted, free of charge, to any person obtai"
},
{
"path": "MANIFEST.in",
"chars": 16,
"preview": "include LICENSE\n"
},
{
"path": "README.md",
"chars": 28734,
"preview": "<div align=\"center\">\n <a href=\"http://tianshou.readthedocs.io\"><img width=\"300px\" height=\"auto\" src=\"https://github.com"
},
{
"path": "benchmark/run_benchmark.py",
"chars": 14434,
"preview": "\"\"\"Benchmark orchestration script for evaluating Tianshou's algorithm implementations.\n\nThis module provides automated b"
},
{
"path": "docs/.gitignore",
"chars": 50,
"preview": "/03_api/*\njupyter_execute\n_toc.yml\n.jupyter_cache\n"
},
{
"path": "docs/01_user_guide/00_training_process.md",
"chars": 7020,
"preview": "# The Reinforcement Learning Process\n\nThe following diagram illustrates the key mechanisms underlying the learning proce"
},
{
"path": "docs/01_user_guide/01_apis.md",
"chars": 14569,
"preview": "# Dual APIs\n\nTianshou provides two distinct APIs to serve different use cases and user preferences:\n\n1. **high-level API"
},
{
"path": "docs/01_user_guide/02_core_abstractions.md",
"chars": 19390,
"preview": "# Core Abstractions\n\nTianshou's architecture is built around a number of key abstractions that work together to provide "
},
{
"path": "docs/01_user_guide/index.rst",
"chars": 211,
"preview": "User Guide\n==========\n\nThe user guide provides an introduction to core concepts, establishes the glossary of terms,\nintr"
},
{
"path": "docs/02_deep_dives/0_intro.md",
"chars": 224,
"preview": "# Deep Dives\n\nOur deep dives are a collection of executable tutorials on some of the internal representations used by Ti"
},
{
"path": "docs/02_deep_dives/L1_Batch.ipynb",
"chars": 46359,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {},\n \"source\": [\n \"# Batch: Tianshou's Core Data Struc"
},
{
"path": "docs/02_deep_dives/L2_Buffer.ipynb",
"chars": 68405,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {},\n \"source\": [\n \"# Buffer: Experience Replay in Tian"
},
{
"path": "docs/02_deep_dives/L3_Environments.ipynb",
"chars": 25450,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {},\n \"source\": [\n \"# Environments\\n\",\n \"\\n\",\n \"I"
},
{
"path": "docs/02_deep_dives/L4_GAE.ipynb",
"chars": 8900,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {\n \"id\": \"QJ5krjrcbuiA\"\n },\n \"source\": [\n \"# Gene"
},
{
"path": "docs/02_deep_dives/L5_Collector.ipynb",
"chars": 24900,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {\n \"id\": \"M98bqxdMsTXK\"\n },\n \"source\": [\n \"# Coll"
},
{
"path": "docs/02_deep_dives/L6_MARL.ipynb",
"chars": 30466,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {},\n \"source\": [\n \"# Multi-Agent Reinforcement Learnin"
},
{
"path": "docs/04_benchmarks/benchmarks.rst",
"chars": 852,
"preview": "Benchmarks\n==========\n\nTianshou's algorithm implementations lead to state-of-the-art results on standard benchmarks.\n\nAn"
},
{
"path": "docs/05_developer_guide/developer_guide.md",
"chars": 10861,
"preview": "# Developer Guide\n\nThe section addresses developers of Tianshou, providing information for \nboth casual contributors and"
},
{
"path": "docs/06_contributors/contributors.rst",
"chars": 1237,
"preview": "Contributors\n============\n\nWe always welcome contributions to help make Tianshou better!\nTianshou was originally created"
},
{
"path": "docs/_config.yml",
"chars": 8134,
"preview": "# Book settings\n# Learn more at https://jupyterbook.org/customize/config.html\n\n#########################################"
},
{
"path": "docs/_static/css/style.css",
"chars": 2633,
"preview": "body {\n font-family: \"Lato\",\"proxima-nova\",\"Helvetica Neue\",Arial,sans-serif;\n}\n\n/* Default header fonts are ugly */\n"
},
{
"path": "docs/_static/js/benchmark.js",
"chars": 3089,
"preview": "var mujoco_envs = [\n \"Ant-v4\",\n];\n\nvar atari_envs = [\n // \"PongNoFrameskip-v4\",\n // \"BreakoutNoFrameskip-v4\",\n "
},
{
"path": "docs/_static/js/copybutton.js",
"chars": 2848,
"preview": "document.addEventListener('DOMContentLoaded', function() {\n /* Add a [>>>] button on the top-right corner of code sa"
},
{
"path": "docs/_static/js/mujoco/benchmark/Ant-v4/results.json",
"chars": 108242,
"preview": "[{\"env_step\":0.0,\"rew\":-42.10716438293457,\"rew_std\":24.039944270941408,\"iqm\":-40.97881825764974,\"iqm_confidence_interval"
},
{
"path": "docs/_static/js/v5.json",
"chars": 1816939,
"preview": "{\n \"$ref\": \"#/definitions/TopLevelSpec\",\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"definitions\": {\n "
},
{
"path": "docs/_static/js/vega-embed@5.js",
"chars": 51168,
"preview": "!function(e,t){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=t(require(\"vega\"),require(\"vega-lite\""
},
{
"path": "docs/_static/js/vega-lite@5.js",
"chars": 249092,
"preview": "!function(e,t){\"object\"==typeof exports&&\"undefined\"!=typeof module?t(exports,require(\"vega\")):\"function\"==typeof define"
},
{
"path": "docs/_static/js/vega@5.js",
"chars": 514346,
"preview": "!function(t,e){\"object\"==typeof exports&&\"undefined\"!=typeof module?e(exports):\"function\"==typeof define&&define.amd?def"
},
{
"path": "docs/autogen_rst.py",
"chars": 5670,
"preview": "import logging\nimport os\nimport shutil\nfrom pathlib import Path\n\nlog = logging.getLogger(os.path.basename(__file__))\n\n\nd"
},
{
"path": "docs/bibtex.json",
"chars": 118,
"preview": "{\n \"cited\": {\n \"tutorials/dqn\": [\n \"DQN\",\n \"DDPG\",\n \"PPO\"\n ]\n }\n}"
},
{
"path": "docs/create_toc.py",
"chars": 299,
"preview": "import os\nfrom pathlib import Path\n\n# This script provides a platform-independent way of making the jupyter-book call (u"
},
{
"path": "docs/index.rst",
"chars": 5349,
"preview": "\nWelcome to Tianshou!\n====================\n\n**Tianshou** (`天授 <https://baike.baidu.com/item/%E5%A4%A9%E6%8E%88>`_) is a "
},
{
"path": "docs/nbstripout.py",
"chars": 334,
"preview": "\"\"\"Implements a platform-independent way of calling nbstripout (used in pyproject.toml).\"\"\"\n\nimport glob\nimport os\nfrom "
},
{
"path": "docs/refs.bib",
"chars": 2533,
"preview": "@article{DQN,\n author = {Volodymyr Mnih and\n Koray Kavukcuoglu and\n David Silver and\n "
},
{
"path": "examples/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "examples/atari/README.md",
"chars": 14834,
"preview": "# Atari Environment\n\n## EnvPool\n\nWe highly recommend using envpool to run the following experiments. To install, in a li"
},
{
"path": "examples/atari/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "examples/atari/atari_c51.py",
"chars": 7330,
"preview": "#!/usr/bin/env python3\n\nimport datetime\nimport os\nimport pprint\nimport sys\n\nimport numpy as np\nimport torch\nfrom sensai."
},
{
"path": "examples/atari/atari_dqn.py",
"chars": 8771,
"preview": "#!/usr/bin/env python3\n\nimport datetime\nimport os\nimport pprint\nimport sys\n\nimport numpy as np\nimport torch\nfrom sensai."
},
{
"path": "examples/atari/atari_dqn_hl.py",
"chars": 3173,
"preview": "#!/usr/bin/env python3\n\nimport os\nfrom typing import Literal\n\nfrom sensai.util import logging\n\nfrom tianshou.env.atari.a"
},
{
"path": "examples/atari/atari_fqf.py",
"chars": 8086,
"preview": "#!/usr/bin/env python3\n\nimport datetime\nimport os\nimport pprint\nimport sys\n\nimport numpy as np\nimport torch\nfrom sensai."
},
{
"path": "examples/atari/atari_iqn.py",
"chars": 7817,
"preview": "#!/usr/bin/env python3\n\nimport datetime\nimport os\nimport pprint\nimport sys\n\nimport numpy as np\nimport torch\nfrom sensai."
},
{
"path": "examples/atari/atari_iqn_hl.py",
"chars": 3351,
"preview": "#!/usr/bin/env python3\n\nimport os\nfrom typing import Literal\n\nfrom sensai.util import logging\n\nfrom tianshou.env.atari.a"
},
{
"path": "examples/atari/atari_ppo.py",
"chars": 9660,
"preview": "#!/usr/bin/env python3\n\nimport datetime\nimport os\nimport pprint\nimport sys\nfrom collections.abc import Sequence\nfrom typ"
},
{
"path": "examples/atari/atari_ppo_hl.py",
"chars": 3347,
"preview": "#!/usr/bin/env python3\n\nimport os\nfrom typing import Literal\n\nfrom sensai.util import logging\n\nfrom tianshou.env.atari.a"
},
{
"path": "examples/atari/atari_qrdqn.py",
"chars": 7353,
"preview": "#!/usr/bin/env python3\n\nimport datetime\nimport os\nimport pprint\nimport sys\n\nimport numpy as np\nimport torch\nfrom sensai."
},
{
"path": "examples/atari/atari_rainbow.py",
"chars": 8714,
"preview": "#!/usr/bin/env python3\n\nimport datetime\nimport os\nimport pprint\nimport sys\n\nimport numpy as np\nimport torch\nfrom sensai."
},
{
"path": "examples/atari/atari_sac.py",
"chars": 9137,
"preview": "#!/usr/bin/env python3\n\nimport datetime\nimport os\nimport pprint\nimport sys\n\nimport numpy as np\nimport torch\nfrom sensai."
},
{
"path": "examples/atari/atari_sac_hl.py",
"chars": 3056,
"preview": "#!/usr/bin/env python3\n\nimport os\nfrom typing import Literal\n\nfrom sensai.util import logging\n\nfrom tianshou.env.atari.a"
},
{
"path": "examples/box2d/README.md",
"chars": 937,
"preview": "# Bipedal-Hardcore-SAC\n\n- Our default choice: remove the done flag penalty, will soon converge to \\~280 reward within 10"
},
{
"path": "examples/box2d/acrobot_dualdqn.py",
"chars": 6266,
"preview": "import argparse\nimport os\nimport pprint\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensor"
},
{
"path": "examples/box2d/bipedal_bdq.py",
"chars": 6992,
"preview": "import argparse\nimport datetime\nimport os\nimport pprint\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom to"
},
{
"path": "examples/box2d/bipedal_hardcore_sac.py",
"chars": 7980,
"preview": "import argparse\nimport os\nimport pprint\nfrom typing import Any\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\n"
},
{
"path": "examples/box2d/lunarlander_dqn.py",
"chars": 6202,
"preview": "import argparse\nimport os\nimport pprint\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensor"
},
{
"path": "examples/box2d/mcc_sac.py",
"chars": 6638,
"preview": "import argparse\nimport os\nimport pprint\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensor"
},
{
"path": "examples/discrete/discrete_dqn.py",
"chars": 3633,
"preview": "import gymnasium as gym\nfrom torch.utils.tensorboard import SummaryWriter\n\nimport tianshou as ts\nfrom tianshou.algorithm"
},
{
"path": "examples/discrete/discrete_dqn_hl.py",
"chars": 2606,
"preview": "#!/usr/bin/env python3\n\nimport os\nfrom typing import Literal\n\nfrom sensai.util import logging\n\nfrom tianshou.highlevel.c"
},
{
"path": "examples/inverse/README.md",
"chars": 1761,
"preview": "# Inverse Reinforcement Learning\n\nIn inverse reinforcement learning setting, the agent learns a policy from interaction "
},
{
"path": "examples/inverse/irl_gail.py",
"chars": 10997,
"preview": "#!/usr/bin/env python3\n\nimport argparse\nimport datetime\nimport os\nimport pprint\nfrom typing import SupportsFloat, cast\n\n"
},
{
"path": "examples/modelbased/README.md",
"chars": 374,
"preview": "# PSRL\n\n`NChain-v0`: `python3 psrl.py --task NChain-v0 --epoch_num_steps 10 --rew-mean-prior 0 --rew-std-prior 1`\n\n`Froz"
},
{
"path": "examples/mujoco/README.md",
"chars": 43606,
"preview": "# Tianshou's Mujoco Benchmark\n\nWe benchmarked Tianshou algorithm implementations in 9 out of 13 environments from the Mu"
},
{
"path": "examples/mujoco/analysis.py",
"chars": 3635,
"preview": "#!/usr/bin/env python3\n\nimport argparse\nimport re\nfrom collections import defaultdict\nfrom os import PathLike\n\nimport nu"
},
{
"path": "examples/mujoco/fetch_her_ddpg.py",
"chars": 9871,
"preview": "#!/usr/bin/env python3\n# isort: skip_file\n\nimport argparse\nimport datetime\nimport os\nimport pprint\n\nimport gymnasium as "
},
{
"path": "examples/mujoco/mujoco_a2c.py",
"chars": 7526,
"preview": "#!/usr/bin/env python3\n\nimport datetime\nimport os\nimport pprint\nfrom typing import Literal\n\nimport numpy as np\nimport to"
},
{
"path": "examples/mujoco/mujoco_a2c_hl.py",
"chars": 3119,
"preview": "#!/usr/bin/env python3\n\nimport os\nfrom typing import Literal\n\nimport torch\nfrom sensai.util import logging\n\nfrom example"
},
{
"path": "examples/mujoco/mujoco_ddpg.py",
"chars": 6014,
"preview": "#!/usr/bin/env python3\n\nimport datetime\nimport os\nimport pprint\n\nimport numpy as np\nimport torch\nfrom mujoco_env import "
},
{
"path": "examples/mujoco/mujoco_ddpg_hl.py",
"chars": 2852,
"preview": "#!/usr/bin/env python3\n\nimport os\nfrom typing import Literal\n\nfrom sensai.util import logging\n\nfrom examples.mujoco.mujo"
},
{
"path": "examples/mujoco/mujoco_env.py",
"chars": 3634,
"preview": "import logging\nimport pickle\n\nfrom gymnasium import Env\n\nfrom tianshou.env import BaseVectorEnv, VectorEnvNormObs\nfrom t"
},
{
"path": "examples/mujoco/mujoco_npg.py",
"chars": 7511,
"preview": "#!/usr/bin/env python3\n\nimport datetime\nimport os\nimport pprint\nfrom typing import Literal\n\nimport numpy as np\nimport to"
},
{
"path": "examples/mujoco/mujoco_npg_hl.py",
"chars": 2999,
"preview": "#!/usr/bin/env python3\n\nimport os\nfrom typing import Literal\n\nimport torch\nfrom sensai.util import logging\n\nfrom example"
},
{
"path": "examples/mujoco/mujoco_ppo.py",
"chars": 7807,
"preview": "#!/usr/bin/env python3\n\nimport datetime\nimport os\nimport pprint\nfrom typing import Literal\n\nimport numpy as np\nimport to"
},
{
"path": "examples/mujoco/mujoco_ppo_hl.py",
"chars": 3153,
"preview": "#!/usr/bin/env python3\n\nimport os\nfrom typing import Literal\n\nimport torch\nfrom sensai.util import logging\n\nfrom example"
},
{
"path": "examples/mujoco/mujoco_redq.py",
"chars": 6721,
"preview": "#!/usr/bin/env python3\n\nimport datetime\nimport os\nimport pprint\nfrom typing import Literal\n\nimport numpy as np\nimport to"
},
{
"path": "examples/mujoco/mujoco_redq_hl.py",
"chars": 2857,
"preview": "#!/usr/bin/env python3\n\nimport os\nfrom typing import Literal\n\nfrom sensai.util import logging\n\nfrom examples.mujoco.mujo"
},
{
"path": "examples/mujoco/mujoco_reinforce.py",
"chars": 6967,
"preview": "#!/usr/bin/env python3\n\nimport datetime\nimport os\nimport pprint\nfrom typing import Literal\n\nimport numpy as np\nimport to"
},
{
"path": "examples/mujoco/mujoco_reinforce_hl.py",
"chars": 2820,
"preview": "#!/usr/bin/env python3\n\nimport os\nfrom typing import Literal\n\nimport torch\nfrom sensai.util import logging\n\nfrom example"
},
{
"path": "examples/mujoco/mujoco_sac.py",
"chars": 6487,
"preview": "#!/usr/bin/env python3\n\nimport datetime\nimport os\nimport pprint\n\nimport numpy as np\nimport torch\nfrom mujoco_env import "
},
{
"path": "examples/mujoco/mujoco_sac_hl.py",
"chars": 2891,
"preview": "#!/usr/bin/env python3\n\nimport os\nfrom typing import Literal\n\nfrom sensai.util import logging\n\nfrom examples.mujoco.mujo"
},
{
"path": "examples/mujoco/mujoco_td3.py",
"chars": 6492,
"preview": "#!/usr/bin/env python3\n\nimport datetime\nimport os\nimport pprint\n\nimport numpy as np\nimport torch\nfrom mujoco_env import "
},
{
"path": "examples/mujoco/mujoco_td3_hl.py",
"chars": 3140,
"preview": "#!/usr/bin/env python3\n\nimport os\nfrom typing import Literal\n\nimport torch\nfrom sensai.util import logging\n\nfrom example"
},
{
"path": "examples/mujoco/mujoco_trpo.py",
"chars": 7518,
"preview": "#!/usr/bin/env python3\n\nimport datetime\nimport os\nimport pprint\nfrom typing import Literal\n\nimport numpy as np\nimport to"
},
{
"path": "examples/mujoco/mujoco_trpo_hl.py",
"chars": 3075,
"preview": "#!/usr/bin/env python3\n\nimport os\nfrom typing import Literal\n\nimport torch\nfrom sensai.util import logging\n\nfrom example"
},
{
"path": "examples/mujoco/plotter.py",
"chars": 8736,
"preview": "#!/usr/bin/env python3\n\nimport argparse\nimport os\nimport re\nfrom typing import Any, Literal\n\nimport matplotlib.pyplot as"
},
{
"path": "examples/mujoco/tools.py",
"chars": 4900,
"preview": "#!/usr/bin/env python3\n\nimport argparse\nimport csv\nimport os\nimport re\nfrom collections import defaultdict\nfrom os impor"
},
{
"path": "examples/offline/README.md",
"chars": 11800,
"preview": "# Offline\n\nIn offline reinforcement learning setting, the agent learns a policy from a fixed dataset which is collected "
},
{
"path": "examples/offline/atari_bcq.py",
"chars": 7706,
"preview": "#!/usr/bin/env python3\n\nimport argparse\nimport datetime\nimport os\nimport pickle\nimport pprint\nimport sys\n\nimport numpy a"
},
{
"path": "examples/offline/atari_cql.py",
"chars": 7173,
"preview": "#!/usr/bin/env python3\n\nimport argparse\nimport datetime\nimport os\nimport pickle\nimport pprint\nimport sys\nfrom collection"
},
{
"path": "examples/offline/atari_crr.py",
"chars": 7718,
"preview": "#!/usr/bin/env python3\n\nimport argparse\nimport datetime\nimport os\nimport pickle\nimport pprint\nimport sys\n\nimport numpy a"
},
{
"path": "examples/offline/atari_il.py",
"chars": 6340,
"preview": "#!/usr/bin/env python3\n\nimport argparse\nimport datetime\nimport os\nimport pickle\nimport pprint\nimport sys\n\nimport numpy a"
},
{
"path": "examples/offline/convert_rl_unplugged_atari.py",
"chars": 9110,
"preview": "#!/usr/bin/env python3\n#\n# Adapted from\n# https://github.com/deepmind/deepmind-research/blob/master/rl_unplugged/atari.p"
},
{
"path": "examples/offline/d4rl_bcq.py",
"chars": 8361,
"preview": "#!/usr/bin/env python3\n\nimport argparse\nimport datetime\nimport os\nimport pprint\n\nimport gymnasium as gym\nimport numpy as"
},
{
"path": "examples/offline/d4rl_cql.py",
"chars": 11234,
"preview": "#!/usr/bin/env python3\n\nimport argparse\nimport datetime\nimport os\nimport pprint\n\nimport gymnasium as gym\nimport numpy as"
},
{
"path": "examples/offline/d4rl_il.py",
"chars": 6190,
"preview": "#!/usr/bin/env python3\n\nimport argparse\nimport datetime\nimport os\nimport pprint\n\nimport gymnasium as gym\nimport numpy as"
},
{
"path": "examples/offline/d4rl_td3_bc.py",
"chars": 8356,
"preview": "#!/usr/bin/env python3\n\nimport argparse\nimport datetime\nimport os\nimport pprint\n\nimport gymnasium as gym\nimport numpy as"
},
{
"path": "examples/offline/utils.py",
"chars": 1590,
"preview": "import d4rl\nimport gymnasium as gym\nimport h5py\nimport numpy as np\n\nfrom tianshou.data import ReplayBuffer\nfrom tianshou"
},
{
"path": "examples/vizdoom/.gitignore",
"chars": 13,
"preview": "_vizdoom.ini\n"
},
{
"path": "examples/vizdoom/README.md",
"chars": 4091,
"preview": "# ViZDoom\n\n[ViZDoom](https://github.com/mwydmuch/ViZDoom) is a popular RL env for a famous first-person shooting game Do"
},
{
"path": "examples/vizdoom/env.py",
"chars": 6948,
"preview": "import os\nfrom collections.abc import Sequence\nfrom typing import Any\n\nimport cv2\nimport gymnasium as gym\nimport numpy a"
},
{
"path": "examples/vizdoom/maps/D1_basic.cfg",
"chars": 873,
"preview": "# Lines starting with # are treated as comments (or with whitespaces+#).\n# It doesn't matter if you use capital letters "
},
{
"path": "examples/vizdoom/maps/D2_navigation.cfg",
"chars": 878,
"preview": "# Lines starting with # are treated as comments (or with whitespaces+#).\n# It doesn't matter if you use capital letters "
},
{
"path": "examples/vizdoom/maps/D3_battle.cfg",
"chars": 936,
"preview": "# Lines starting with # are treated as comments (or with whitespaces+#).\n# It doesn't matter if you use capital letters "
},
{
"path": "examples/vizdoom/maps/D4_battle2.cfg",
"chars": 938,
"preview": "# Lines starting with # are treated as comments (or with whitespaces+#).\n# It doesn't matter if you use capital letters "
},
{
"path": "examples/vizdoom/maps/README.md",
"chars": 152,
"preview": "D1-D4 maps are from https://github.com/intel-isl/DirectFuturePrediction/\n\nMore maps and cfgs: https://github.com/mwydmuc"
},
{
"path": "examples/vizdoom/maps/spectator.py",
"chars": 2403,
"preview": "#!/usr/bin/env python3\n\n#####################################################################\n# This script presents SPE"
},
{
"path": "examples/vizdoom/replay.py",
"chars": 1081,
"preview": "# import cv2\nimport os\nimport sys\nimport time\n\nimport tqdm\nimport vizdoom as vzd\n\n\ndef main(\n cfg_path: str = os.path"
},
{
"path": "examples/vizdoom/vizdoom_c51.py",
"chars": 8670,
"preview": "import argparse\nimport datetime\nimport os\nimport pprint\nimport sys\n\nimport numpy as np\nimport torch\nfrom env import make"
},
{
"path": "examples/vizdoom/vizdoom_ppo.py",
"chars": 11067,
"preview": "import argparse\nimport datetime\nimport os\nimport pprint\nimport sys\n\nimport numpy as np\nimport torch\nfrom env import make"
},
{
"path": "pyproject.toml",
"chars": 9449,
"preview": "[build-system]\nrequires = [\"poetry-core>=1.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n\n[tool.poetry]\nname = \"tiansh"
},
{
"path": "test/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "test/base/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "test/base/env.py",
"chars": 9072,
"preview": "import random\nimport time\nfrom copy import deepcopy\nfrom typing import Any, Literal\n\nimport gymnasium as gym\nimport netw"
},
{
"path": "test/base/test_action_space_sampling.py",
"chars": 1412,
"preview": "import gymnasium as gym\n\nfrom tianshou.env import DummyVectorEnv, ShmemVectorEnv, SubprocVectorEnv\n\n\ndef test_gym_env_ac"
},
{
"path": "test/base/test_batch.py",
"chars": 38550,
"preview": "import copy\nimport pickle\nfrom itertools import starmap\nfrom typing import Any, cast\n\nimport networkx as nx\nimport numpy"
},
{
"path": "test/base/test_buffer.py",
"chars": 51130,
"preview": "import os\nimport pickle\nimport tempfile\nfrom typing import cast\n\nimport h5py\nimport numpy as np\nimport numpy.typing as n"
},
{
"path": "test/base/test_collector.py",
"chars": 35514,
"preview": "from collections.abc import Callable, Sequence\nfrom typing import Any\n\nimport gymnasium as gym\nimport numpy as np\nimport"
},
{
"path": "test/base/test_env.py",
"chars": 15811,
"preview": "import sys\nimport time\nfrom collections.abc import Callable\nfrom typing import Any, Literal\n\nimport gymnasium as gym\nimp"
},
{
"path": "test/base/test_env_finite.py",
"chars": 9175,
"preview": "# see issue #322 for detail\n\nimport copy\nfrom collections import Counter\nfrom collections.abc import Callable, Iterator,"
},
{
"path": "test/base/test_logger.py",
"chars": 2941,
"preview": "from typing import Literal\n\nimport numpy as np\nimport pytest\nfrom torch.utils.tensorboard import SummaryWriter\n\nfrom tia"
},
{
"path": "test/base/test_policy.py",
"chars": 5069,
"preview": "import gymnasium as gym\nimport numpy as np\nimport pytest\nimport torch\nfrom torch.distributions import Categorical, Distr"
},
{
"path": "test/base/test_returns.py",
"chars": 11560,
"preview": "from typing import cast\n\nimport numpy as np\nimport torch\n\nfrom tianshou.algorithm import Algorithm\nfrom tianshou.data im"
},
{
"path": "test/base/test_stats.py",
"chars": 3379,
"preview": "from typing import cast\n\nimport numpy as np\nimport pytest\nimport torch\nfrom torch.distributions import Categorical, Norm"
},
{
"path": "test/base/test_utils.py",
"chars": 8709,
"preview": "from typing import cast\n\nimport numpy as np\nimport pytest\nimport torch\nimport torch.distributions as dist\nfrom gymnasium"
},
{
"path": "test/continuous/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "test/continuous/test_ddpg.py",
"chars": 6058,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensorboard import S"
},
{
"path": "test/continuous/test_npg.py",
"chars": 6674,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch import nn\nfrom torch.distr"
},
{
"path": "test/continuous/test_ppo.py",
"chars": 8282,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.distributions import Distr"
},
{
"path": "test/continuous/test_redq.py",
"chars": 7430,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nimport torch.nn as nn\nfrom torch.util"
},
{
"path": "test/continuous/test_sac_with_il.py",
"chars": 9243,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensorboard import S"
},
{
"path": "test/continuous/test_td3.py",
"chars": 6845,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensorboard import S"
},
{
"path": "test/continuous/test_trpo.py",
"chars": 6934,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch import nn\nfrom torch.distr"
},
{
"path": "test/determinism_test.py",
"chars": 4494,
"preview": "from argparse import Namespace\nfrom collections.abc import Callable, Sequence\nfrom pathlib import Path\nfrom typing impor"
},
{
"path": "test/discrete/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "test/discrete/test_a2c_with_il.py",
"chars": 8486,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom gymnasium.spaces import Box\nfrom"
},
{
"path": "test/discrete/test_bdqn.py",
"chars": 6450,
"preview": "import argparse\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\n\nfrom test.determinism_test import AlgorithmDet"
},
{
"path": "test/discrete/test_c51.py",
"chars": 8936,
"preview": "import argparse\nimport os\nimport pickle\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensor"
},
{
"path": "test/discrete/test_discrete_sac.py",
"chars": 6871,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensorboard import S"
},
{
"path": "test/discrete/test_dqn.py",
"chars": 6950,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensorboard import S"
},
{
"path": "test/discrete/test_drqn.py",
"chars": 6011,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensorboard import S"
},
{
"path": "test/discrete/test_fqf.py",
"chars": 7654,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensorboard import S"
},
{
"path": "test/discrete/test_iqn.py",
"chars": 7421,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensorboard import S"
},
{
"path": "test/discrete/test_ppo_discrete.py",
"chars": 7039,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensorboard import S"
},
{
"path": "test/discrete/test_qrdqn.py",
"chars": 7263,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensorboard import S"
},
{
"path": "test/discrete/test_rainbow.py",
"chars": 9852,
"preview": "import argparse\nimport os\nimport pickle\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensor"
},
{
"path": "test/discrete/test_reinforce.py",
"chars": 5446,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom gymnasium.spaces import Box\nfrom"
},
{
"path": "test/highlevel/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "test/highlevel/env_factory.py",
"chars": 480,
"preview": "from tianshou.highlevel.env import (\n EnvFactoryRegistered,\n VectorEnvType,\n)\n\n\nclass DiscreteTestEnvFactory(EnvFa"
},
{
"path": "test/highlevel/test_experiment_builder.py",
"chars": 3376,
"preview": "import pytest\n\nfrom test.highlevel.env_factory import ContinuousTestEnvFactory, DiscreteTestEnvFactory\nfrom tianshou.hig"
},
{
"path": "test/modelbased/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "test/modelbased/test_dqn_icm.py",
"chars": 7668,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensorboard import S"
},
{
"path": "test/modelbased/test_ppo_icm.py",
"chars": 7676,
"preview": "import argparse\nimport os\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom gymnasium.spaces import Box\nfrom"
},
{
"path": "test/modelbased/test_psrl.py",
"chars": 5028,
"preview": "import argparse\nimport os\n\nimport numpy as np\nimport pytest\nimport torch\nfrom torch.utils.tensorboard import SummaryWrit"
},
{
"path": "test/offline/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "test/offline/gather_cartpole_data.py",
"chars": 7362,
"preview": "import argparse\nimport os\nimport pickle\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensor"
},
{
"path": "test/offline/gather_pendulum_data.py",
"chars": 6862,
"preview": "import argparse\nimport os\nimport pickle\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensor"
},
{
"path": "test/offline/test_bcq.py",
"chars": 7846,
"preview": "import argparse\nimport datetime\nimport os\nimport pickle\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom to"
},
{
"path": "test/offline/test_cql.py",
"chars": 7876,
"preview": "import argparse\nimport datetime\nimport os\nimport pickle\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom to"
},
{
"path": "test/offline/test_discrete_bcq.py",
"chars": 7123,
"preview": "import argparse\nimport os\nimport pickle\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensor"
},
{
"path": "test/offline/test_discrete_cql.py",
"chars": 5377,
"preview": "import argparse\nimport os\nimport pickle\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensor"
},
{
"path": "test/offline/test_discrete_crr.py",
"chars": 5419,
"preview": "import argparse\nimport os\nimport pickle\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.utils.tensor"
},
{
"path": "test/offline/test_gail.py",
"chars": 9577,
"preview": "import argparse\nimport os\nimport pickle\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom torch.distribution"
},
{
"path": "test/offline/test_td3_bc.py",
"chars": 7511,
"preview": "import argparse\nimport datetime\nimport os\nimport pickle\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom to"
},
{
"path": "test/pettingzoo/pistonball.py",
"chars": 7360,
"preview": "import argparse\r\nimport os\r\nimport warnings\r\n\r\nimport gymnasium as gym\r\nimport numpy as np\r\nimport torch\r\nfrom pettingzo"
},
{
"path": "test/pettingzoo/pistonball_continuous.py",
"chars": 11480,
"preview": "import argparse\r\nimport os\r\nimport warnings\r\nfrom typing import Any\r\n\r\nimport gymnasium as gym\r\nimport numpy as np\r\nimpo"
},
{
"path": "test/pettingzoo/test_pistonball.py",
"chars": 377,
"preview": "import argparse\n\nimport pytest\nfrom pistonball import get_args, train_agent, watch\n\n\n@pytest.mark.skip(reason=\"Performan"
},
{
"path": "test/pettingzoo/test_pistonball_continuous.py",
"chars": 374,
"preview": "import argparse\n\nimport pytest\nfrom pistonball_continuous import get_args, train_agent, watch\n\n\n@pytest.mark.skip(reason"
},
{
"path": "test/pettingzoo/test_tic_tac_toe.py",
"chars": 293,
"preview": "import argparse\r\n\r\nfrom tic_tac_toe import get_args, train_agent, watch\r\n\r\n\r\ndef test_tic_tac_toe(args: argparse.Namespa"
},
{
"path": "test/pettingzoo/tic_tac_toe.py",
"chars": 9060,
"preview": "import argparse\r\nimport os\r\nfrom copy import deepcopy\r\nfrom functools import partial\r\n\r\nimport gymnasium\r\nimport numpy a"
},
{
"path": "tianshou/__init__.py",
"chars": 535,
"preview": "# isort: skip_file\n# NOTE: Import order is important to avoid circular import errors!\nfrom tianshou import data, env, ex"
},
{
"path": "tianshou/algorithm/__init__.py",
"chars": 1825,
"preview": "\"\"\"Algorithm package.\"\"\"\n# isort:skip_file\n\nfrom tianshou.algorithm.algorithm_base import Algorithm, TrainingStats\nfrom "
},
{
"path": "tianshou/algorithm/algorithm_base.py",
"chars": 51106,
"preview": "import logging\nimport time\nfrom abc import ABC, abstractmethod\nfrom collections.abc import Callable, Mapping\nfrom datacl"
},
{
"path": "tianshou/algorithm/imitation/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tianshou/algorithm/imitation/bcq.py",
"chars": 11962,
"preview": "import copy\nfrom dataclasses import dataclass\nfrom typing import Any, Literal, TypeVar, cast\n\nimport gymnasium as gym\nim"
},
{
"path": "tianshou/algorithm/imitation/cql.py",
"chars": 19492,
"preview": "from copy import deepcopy\nfrom dataclasses import dataclass\nfrom typing import cast\n\nimport numpy as np\nimport torch\nimp"
},
{
"path": "tianshou/algorithm/imitation/discrete_bcq.py",
"chars": 12140,
"preview": "import math\nfrom dataclasses import dataclass\nfrom typing import Any, cast\n\nimport gymnasium as gym\nimport numpy as np\ni"
},
{
"path": "tianshou/algorithm/imitation/discrete_cql.py",
"chars": 5386,
"preview": "from dataclasses import dataclass\n\nimport numpy as np\nimport torch\nimport torch.nn.functional as F\n\nfrom tianshou.algori"
},
{
"path": "tianshou/algorithm/imitation/discrete_crr.py",
"chars": 7431,
"preview": "from dataclasses import dataclass\nfrom typing import Literal\n\nimport numpy as np\nimport torch\nimport torch.nn.functional"
},
{
"path": "tianshou/algorithm/imitation/gail.py",
"chars": 14165,
"preview": "from dataclasses import dataclass\n\nimport numpy as np\nimport torch\nimport torch.nn.functional as F\n\nfrom tianshou.algori"
},
{
"path": "tianshou/algorithm/imitation/imitation_base.py",
"chars": 7118,
"preview": "from dataclasses import dataclass\nfrom typing import Any, Literal, cast\n\nimport gymnasium as gym\nimport numpy as np\nimpo"
},
{
"path": "tianshou/algorithm/imitation/td3_bc.py",
"chars": 6919,
"preview": "import torch\nimport torch.nn.functional as F\n\nfrom tianshou.algorithm import TD3\nfrom tianshou.algorithm.algorithm_base "
},
{
"path": "tianshou/algorithm/modelbased/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tianshou/algorithm/modelbased/icm.py",
"chars": 11547,
"preview": "import numpy as np\nimport torch\nimport torch.nn.functional as F\n\nfrom tianshou.algorithm import Algorithm\nfrom tianshou."
},
{
"path": "tianshou/algorithm/modelbased/psrl.py",
"chars": 11274,
"preview": "from dataclasses import dataclass\nfrom typing import Any, cast\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\n"
},
{
"path": "tianshou/algorithm/modelfree/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tianshou/algorithm/modelfree/a2c.py",
"chars": 15487,
"preview": "from abc import ABC\nfrom dataclasses import dataclass\nfrom typing import cast\n\nimport numpy as np\nimport torch\nimport to"
},
{
"path": "tianshou/algorithm/modelfree/bdqn.py",
"chars": 9795,
"preview": "from typing import cast\n\nimport gymnasium as gym\nimport numpy as np\nimport torch\nfrom sensai.util.helper import mark_use"
}
]
// ... and 103 more files (download for full content)
About this extraction
This page contains the full source code of the thu-ml/tianshou GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 303 files (4.9 MB), approximately 1.3M tokens, and a symbol index with 4993 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.