diff --git a/.github/workflows/docs_pages.yml b/.github/workflows/docs_pages.yml new file mode 100644 index 0000000..0f673d8 --- /dev/null +++ b/.github/workflows/docs_pages.yml @@ -0,0 +1,74 @@ +name: Documentation Pages + +on: + push: + branches: + - main + tags: + - "v*" + paths: + - doc/** + - pyproject.toml + - .github/workflows/docs_pages.yml + - pyTorchAutoForge/** + pull_request: + branches: + - main + paths: + - doc/** + - pyproject.toml + - .github/workflows/docs_pages.yml + - pyTorchAutoForge/** + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: pip + - name: Install docs dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -e ".[docs]" + - name: Build Sphinx site + if: github.event_name == 'pull_request' + run: python -m sphinx -b html doc site + - name: Build versioned Sphinx site + if: github.event_name != 'pull_request' + run: bash doc/build_versioned_docs.sh site + - name: Configure GitHub Pages + if: github.event_name != 'pull_request' + uses: actions/configure-pages@v5 + - name: Upload Pages artifact + if: github.event_name != 'pull_request' + uses: actions/upload-pages-artifact@v3 + with: + path: site + + deploy-docs: + if: github.event_name != 'pull_request' + needs: build-docs + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index d065b29..097a225 100644 --- a/.gitignore +++ b/.gitignore @@ -80,6 +80,8 @@ instance/ # Sphinx documentation docs/_build/ +doc/_build/ +site/ # PyBuilder .pybuilder/ @@ -173,3 +175,8 @@ cython_debug/ .vscode/browse.vc.db .vscode/browse.vc.db-shm .vscode/browse.vc.db-wal +CONTEXT.md + +# Documentation ignore +doc/_autoapi_templates/* +doc/_static/* \ No newline at end of file diff --git a/.readthedocs.yaml b/.readthedocs.yaml deleted file mode 100644 index e253e11..0000000 --- a/.readthedocs.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# Read the Docs configuration file -# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details - -# Required -version: 2 - -# Set the OS, Python version, and other tools you might need -build: - os: ubuntu-24.04 - tools: - python: "3.11" - -# Build documentation in the "docs/" directory with Sphinx -sphinx: - configuration: doc/conf.py - -# Optionally, but recommended, -# declare the Python requirements required to build your documentation -# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html -python: - install: - - requirements: doc/requirements.txt - - method: pip - path: . - - diff --git a/README.md b/README.md index 418c6f8..e48467e 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,24 @@ A library based on PyTorch () and designed to automate ML models development, tracking and deployment, integrated with MLflow and Optuna (, ). It also supports spiking networks libraries (WIP). Model optimization and deployment can be performed using ONNx, pyTorch facilities or TensorRT (WIP). The library aims to be compatible with Jetson Orin Nano Jetpack rev6.1. Several other functionalities and utilities for sklearn and pySR () are included (see README and documentation). +## Documentation + +Documentation is built with Sphinx, the PyData theme, and auto-generated public API pages. It is published through GitHub Pages: +. + +Local preview: + +```bash +python -m pip install -e ".[docs]" +doc/makedoc.sh -a +``` + +Local strict build: + +```bash +doc/makedoc.sh +``` + ## Some brief usage guides (WIP) ### TensorRT exporter quick usage @@ -25,21 +43,35 @@ Notes: - `PYTHON` mode requires the `tensorrt` Python package. - Default behavior avoids architecture-specific flags and is suitable for Jetson deployment workflows. -## Installation using pip +## Installation Using Pip -The suggested installation method is through pip as the others are mostly intended for development and may not be completely up-to-date with the newest release versions. -In whatever conda or virtual environment you like (preferably with a sufficiently new torch release, to install from pypi: +The package is available on PyPI. In any conda or virtual environment with a suitable PyTorch release: ```bash -pip install pyTorchAutoForge +python -m pip install pyTorchAutoForge ``` -Or from a local copy of the repository (requires `hatch` module for the build): +From a local checkout: ```bash cd pyTorchAutoforge -pip install . +python -m pip install . ``` -An automatic installation script `conda_install.sh` is provided and should work in most cases. Note that it will automatically create a new environment named **autoforge** and makes several assumptions about your environment. -Dependencies for the core modules should be installed automatically using pip. However, this is currently not fully tested. Please open related issues. +An automatic installation script `conda_install.sh` is provided for development installs. By default it uses an existing `autoforge` conda environment and installs only the core package dependencies: + +```bash +./conda_install.sh --create-env --editable +``` + +Optional extras are explicit: + +```bash +./conda_install.sh --with-test --with-docs --build-docs +``` + +Jetson/ARM installs skip x86-only dependencies through package markers. Provide board-specific PyTorch wheels when needed: + +```bash +./conda_install.sh --jetson --pytorch-url --torchvision-url +``` diff --git a/TODO b/TODO index ddeb217..cc66d7b 100644 --- a/TODO +++ b/TODO @@ -51,7 +51,7 @@ - [x] Improve backbone configuration - v0.5.0 - - [ ] Migrate documentation from Read The Docs to something more controllable and less restrictive + - [x] Migrate documentation from Read The Docs to GitHub Pages with MkDocs - [ ] [MAJOR] Restructure and expand augmentations module to correctly handle images and 1D vectors jointly - [ ] Implement 1d vector error models (from selected distributions) - [ ] Change random apply and structure of augs module for images (split based on type) @@ -74,7 +74,7 @@ - [ ] Add pySR conveniency module - [ ] Explore Hydra for configuration management from yml files - [ ] Add prototype of SHAP for CNNs - - [ ] Add prototype of model explainer for vector-to-vector regression using Captum (2 methods) + - [x] Add prototype of model explainer for vector-to-vector regression using Captum (2 methods) - [ ] Upgrade api.torch to save model "packages" with all info to run the model - [ ] Improve docstrings in src code - [ ] Implement prototype of K-fold cross-validation for training runs @@ -94,7 +94,7 @@ - [ ] Add type annotations for mypy -- [ ] Automate documentation building process +- [x] Automate documentation building process - [x] Implement CI/CD to run tests diff --git a/bash_scripts/check_torch_availability.sh b/bash_scripts/check_torch_availability.sh new file mode 100755 index 0000000..c52ad57 --- /dev/null +++ b/bash_scripts/check_torch_availability.sh @@ -0,0 +1,86 @@ +#!/bin/bash +set -euo pipefail + +# Script variables +ENV_NAME="${CONDA_ENV:-autoforge}" +CONDA_EXE="${CONDA_EXE:-conda}" +USE_CONDA=1 +REQUIRE_CUDA=0 + +# Usage guide +usage() { + cat <<'EOF' +Usage: bash_scripts/check_torch_availability.sh [options] + +Options: + -e, --env-name NAME Conda environment name (default: CONDA_ENV or autoforge) + --conda-exe PATH Conda executable (default: conda) + --no-conda Do not activate conda environment + --require-cuda Return non-zero when CUDA is unavailable + -h, --help Show this help +EOF +} + +# Parser loop +while [[ $# -gt 0 ]]; do + case "$1" in + -e|--env-name) + ENV_NAME="$2" + shift 2 + ;; + --conda-exe) + CONDA_EXE="$2" + shift 2 + ;; + --no-conda) + USE_CONDA=0 + shift + ;; + --require-cuda) + REQUIRE_CUDA=1 + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +# Source conda environment +if [[ "${USE_CONDA}" -eq 1 ]]; then + CONDA_BASE="$("${CONDA_EXE}" info --base)" + # shellcheck source=/dev/null + source "${CONDA_BASE}/etc/profile.d/conda.sh" + conda activate "${ENV_NAME}" +fi + +# Set environment variable to make CUDA errors easier to debug +export CUDA_LAUNCH_BLOCKING="${CUDA_LAUNCH_BLOCKING:-1}" +export PTAF_REQUIRE_CUDA="${REQUIRE_CUDA}" + +# Check PyTorch and CUDA availability with a python command +python -c ' +import os +import sys + +import torch + +cuda_available = torch.cuda.is_available() +print("Torch version:", torch.__version__) +print("CUDA availability:", cuda_available) + +if cuda_available: + device_index = torch.cuda.current_device() + print("Torch device props:", torch.cuda.get_device_properties(device_index)) + tensor = torch.tensor(2.0, device="cuda").fill_(3.14) + print("Tensor created on CUDA:", tensor) +elif os.environ.get("PTAF_REQUIRE_CUDA") == "1": + print("CUDA is not available", file=sys.stderr) + sys.exit(1) +' diff --git a/bash_scripts/start_mlflow_server.sh b/bash_scripts/start_mlflow_server.sh index 057c237..4401459 100755 --- a/bash_scripts/start_mlflow_server.sh +++ b/bash_scripts/start_mlflow_server.sh @@ -1,5 +1,99 @@ -# NOTE: this script only works on peterc-desktopMSI. Server is started using the same uri as pointed by MLFLOW_TRACKING_URI environment variable, therefore works as local database automatically. +#!/bin/bash +set -euo pipefail -source /home/peterc/devDir/pyTorchAutoForge/.venvTorch/bin/activate -mlflow server --workers 2 --host 0.0.0.0 --port 5000 --backend-store-uri ${MLFLOW_TRACKING_URI} --default-artifact-root ${MLFLOW_ARTIFACTS_URI} -#mlflow server --workers 2 --host 0.0.0.0 --port 8080 --backend-store-uri sqlite:///./localdb --default-artifact-root file:///localdb/artifacts +# Script variables +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +ENV_NAME="${CONDA_ENV:-autoforge}" +CONDA_EXE="${CONDA_EXE:-conda}" +USE_CONDA=1 +HOST="${MLFLOW_HOST:-0.0.0.0}" +PORT="${MLFLOW_PORT:-8080}" +WORKERS="${MLFLOW_WORKERS:-2}" +BACKEND_STORE_URI="${MLFLOW_TRACKING_URI:-sqlite:///${REPO_ROOT}/mlruns/mlflow.db}" +DEFAULT_ARTIFACT_ROOT="${MLFLOW_ARTIFACTS_URI:-file://${REPO_ROOT}/mlruns/artifacts}" + +# Usage guide +usage() { + cat <<'EOF' +Usage: bash_scripts/start_mlflow_server.sh [options] + +Options: + -e, --env-name NAME Conda environment name (default: CONDA_ENV or autoforge) + --conda-exe PATH Conda executable (default: conda) + --no-conda Do not activate conda environment + --host HOST Host bind address (default: MLFLOW_HOST or 0.0.0.0) + --port PORT Server port (default: MLFLOW_PORT or 8080) + --workers COUNT Worker count (default: MLFLOW_WORKERS or 2) + --backend-store-uri URI Backend store URI (default: MLFLOW_TRACKING_URI or local sqlite) + --artifact-root URI Artifact root URI (default: MLFLOW_ARTIFACTS_URI or local file store) + -h, --help Show this help +EOF +} + +# Parser loop +while [[ $# -gt 0 ]]; do + case "$1" in + -e|--env-name) + ENV_NAME="$2" + shift 2 + ;; + --conda-exe) + CONDA_EXE="$2" + shift 2 + ;; + --no-conda) + USE_CONDA=0 + shift + ;; + --host) + HOST="$2" + shift 2 + ;; + --port) + PORT="$2" + shift 2 + ;; + --workers) + WORKERS="$2" + shift 2 + ;; + --backend-store-uri) + BACKEND_STORE_URI="$2" + shift 2 + ;; + --artifact-root) + DEFAULT_ARTIFACT_ROOT="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +# Source conda environment +if [[ "${USE_CONDA}" -eq 1 ]]; then + CONDA_BASE="$("${CONDA_EXE}" info --base)" + # shellcheck source=/dev/null + source "${CONDA_BASE}/etc/profile.d/conda.sh" + conda activate "${ENV_NAME}" +fi + +# Make sure artifact directory exists for local file store +mkdir -p "${REPO_ROOT}/mlruns/artifacts" + +# Start MLflow server with python command +exec mlflow server \ + --workers "${WORKERS}" \ + --host "${HOST}" \ + --port "${PORT}" \ + --backend-store-uri "${BACKEND_STORE_URI}" \ + --default-artifact-root "${DEFAULT_ARTIFACT_ROOT}" diff --git a/bash_scripts/start_mlflow_ui.sh b/bash_scripts/start_mlflow_ui.sh index fb59775..b282ba2 100755 --- a/bash_scripts/start_mlflow_ui.sh +++ b/bash_scripts/start_mlflow_ui.sh @@ -1 +1,82 @@ -mlflow ui --port 8080 --backend-store-uri ${MLFLOW_TRACKING_URI} # Using secondary disk (mounted under /media) \ No newline at end of file +#!/bin/bash +set -euo pipefail + +# Script variables +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +ENV_NAME="${CONDA_ENV:-autoforge}" +CONDA_EXE="${CONDA_EXE:-conda}" +USE_CONDA=1 +HOST="${MLFLOW_UI_HOST:-127.0.0.1}" +PORT="${MLFLOW_UI_PORT:-8080}" +BACKEND_STORE_URI="${MLFLOW_TRACKING_URI:-sqlite:///${REPO_ROOT}/mlruns/mlflow.db}" + +# Usage guide +usage() { + cat <<'EOF' +Usage: bash_scripts/start_mlflow_ui.sh [options] + +Options: + -e, --env-name NAME Conda environment name (default: CONDA_ENV or autoforge) + --conda-exe PATH Conda executable (default: conda) + --no-conda Do not activate conda environment + --host HOST Host bind address (default: MLFLOW_UI_HOST or 127.0.0.1) + --port PORT UI port (default: MLFLOW_UI_PORT or 8080) + --backend-store-uri URI Backend store URI (default: MLFLOW_TRACKING_URI or local sqlite) + -h, --help Show this help +EOF +} + +# Parser loop +while [[ $# -gt 0 ]]; do + case "$1" in + -e|--env-name) + ENV_NAME="$2" + shift 2 + ;; + --conda-exe) + CONDA_EXE="$2" + shift 2 + ;; + --no-conda) + USE_CONDA=0 + shift + ;; + --host) + HOST="$2" + shift 2 + ;; + --port) + PORT="$2" + shift 2 + ;; + --backend-store-uri) + BACKEND_STORE_URI="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +# Source conda environment +if [[ "${USE_CONDA}" -eq 1 ]]; then + CONDA_BASE="$("${CONDA_EXE}" info --base)" + # shellcheck source=/dev/null + source "${CONDA_BASE}/etc/profile.d/conda.sh" + conda activate "${ENV_NAME}" +fi + +# Start MLflow UI with python command +exec mlflow ui \ + --host "${HOST}" \ + --port "${PORT}" \ + --backend-store-uri "${BACKEND_STORE_URI}" diff --git a/check_torch_availability.sh b/check_torch_availability.sh deleted file mode 100755 index eab247e..0000000 --- a/check_torch_availability.sh +++ /dev/null @@ -1,15 +0,0 @@ -env_name=$1 -if [ -z "$env_name" ]; then - echo "Usage: $0 " - exit 1 -fi - -source $(conda info --base)/etc/profile.d/conda.sh -conda activate "$env_name" -export CUDA_LAUNCH_BLOCKING=1 - -# Test python imports -python -c "import torch; print('CUDA availability:', torch.cuda.is_available()); print('Torch device props:', torch.cuda.get_device_properties(0)); print('Torch version:', torch.__version__)" - -# Try to make a tensor on CUDA -python -c "import torch; tensor = torch.tensor(2).fill_(3.14).to('cuda'); print('Tensor created on CUDA:', tensor)" \ No newline at end of file diff --git a/conda_install.sh b/conda_install.sh index 1792856..48e3f14 100755 --- a/conda_install.sh +++ b/conda_install.sh @@ -1,224 +1,358 @@ -# Default values -jetson_target=false -editable_mode=false -sudo_mode=false -venv_name="autoforge" -create_conda_env=false - -# Parse options using getopt -# NOTE: no ":" after option means no argument, ":" means required argument, "::" means optional argument -OPTIONS=j,v:,s,c,e -LONGOPTIONS=jetson_target,venv_name:,sudo_mode,create_conda_env,editable_mode - -# Parsed arguments list with getopt -PARSED=$(getopt --options ${OPTIONS} --longoptions ${LONGOPTIONS} --name "$0" -- "$@") -# TODO check if this is where I need to modify something to allow things like -B build, instead of -Bbuild - -# Check validity of input arguments -if [[ $? -ne 0 ]]; then - exit 2 -fi - -# Parse arguments -eval set -- "$PARSED" - -# Process options (change default values if needed) -while true; do - case "$1" in - -j|--jetson_target) - jetson_target=1 - echo "Jetson target selected..." - shift - ;; - -v|--venv_name) - venv_name=$2 - echo "Conda environment name: $venv_name" - shift 2 - ;; - -s|--sudo_mode) - sudo_mode=true - echo "Sudo mode requested..." - shift - ;; - - -c|--create_conda_env) - create_conda_env=true - echo "Creating and initializing conda environment..." - shift - ;; - -e|--editable_mode) - editable_mode=true - echo "Editable mode selected..." - shift - ;; - --) - shift - break - ;; - *) - echo "Not a valid option: $1" >&2 - exit 3 - ;; - esac +#!/bin/bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="${SCRIPT_DIR}" + +# Script variables +CONDA_EXE="${CONDA_EXE:-conda}" +ENV_NAME="${CONDA_ENV:-autoforge}" +PYTHON_VERSION="${PYTHON_VERSION:-3.12}" +CREATE_ENV=0 +EDITABLE=0 +BUILD_DOCS=0 +RUN_CHECK=0 +ARM_MODE=0 +USE_MAMBA=0 +INSTALL_NVIDIA_DEPLOY=0 +PYTORCH_URL="" +TORCHVISION_URL="" +TORCHVISION_SOURCE_TAG="" +EXTRAS=() + +usage() { + cat <<'EOF' +Usage: ./conda_install.sh [options] + +Default behavior: + - use an existing conda environment named "autoforge" + - install core pyTorchAutoForge package only, without optional extras + - do not create environments unless --create-env is passed + +Environment: + -n, --env-name NAME Conda environment name (default: CONDA_ENV or autoforge) + -v, --venv_name NAME Compatibility alias for --env-name + -c, --create-env Create environment if missing + --python-version VERSION Python version for new envs (default: PYTHON_VERSION or 3.12) + --conda-exe PATH Conda executable (default: conda) + --mamba Use mamba for env creation when available + +Install: + --core Core package only (default) + -e, --editable Install editable from this checkout + --wheel Install non-editable package from this checkout (default) + --extras LIST Comma-separated pyproject extras, for example test,docs,explain + --with-test Add test extra + --with-docs Add docs extra + --with-explain Add explain extra + --with-shap Add explain-shap extra + --with-captum Add explain-captum extra + --with-cuda Add cuda_all extra + --with-classical-ml Add classical-ml extra + --with-xgboost Add xgboost extra + --with-pysr Add pysr extra + --build-docs Build MkDocs after install + --check Run tests/.configuration/test_env.py after install + +ARM / Jetson: + -j, --jetson Enable ARM/Jetson-friendly mode + --jetson_target Compatibility alias for --jetson + --arm Alias for --jetson + --pytorch-url URL Install board-specific PyTorch wheel/url before PTAF + --torchvision-url URL Install board-specific torchvision wheel/url before PTAF + --torchvision-source-tag TAG + Build torchvision from source tag, for example v0.20.0 + --nvidia-deploy Install NVIDIA deploy packages from pypi.nvidia.com + +Compatibility: + -s, --sudo_mode Accepted for old callers; no system packages are installed here + -h, --help Show this help +EOF +} + +add_extra() { + local raw_extra_="$1" + local extra_="" + + IFS=',' read -ra split_extras_ <<< "${raw_extra_}" + for extra_ in "${split_extras_[@]}"; do + extra_="${extra_//[[:space:]]/}" + if [[ -n "${extra_}" ]]; then + EXTRAS+=("${extra_}") + fi + done +} + +join_extras() { + local joined_="" + local seen_="," + local extra_="" + + for extra_ in "${EXTRAS[@]}"; do + if [[ "${seen_}" == *",${extra_},"* ]]; then + continue + fi + seen_="${seen_}${extra_}," + if [[ -z "${joined_}" ]]; then + joined_="${extra_}" + else + joined_="${joined_},${extra_}" + fi + done + + printf "%s" "${joined_}" +} + +source_conda() { + local base_dir_="" + base_dir_="$("${CONDA_EXE}" info --base)" + # shellcheck source=/dev/null + source "${base_dir_}/etc/profile.d/conda.sh" +} + +conda_env_exists() { + "${CONDA_EXE}" env list | awk '{print $1}' | grep -Fxq "${ENV_NAME}" +} + +create_conda_env() { + local create_cmd_=("${CONDA_EXE}" "create" "-y" "-n" "${ENV_NAME}" "python=${PYTHON_VERSION}" "pip" "setuptools" "wheel") + + if [[ "${USE_MAMBA}" -eq 1 ]] && command -v mamba >/dev/null 2>&1; then + create_cmd_[0]="mamba" + fi + + echo "Creating conda environment '${ENV_NAME}' with Python ${PYTHON_VERSION}" + "${create_cmd_[@]}" +} + +install_url_if_requested() { + local label_="$1" + local url_="$2" + + if [[ -n "${url_}" ]]; then + echo "Installing ${label_} from explicit URL" + python -m pip install "${url_}" + fi +} + +install_torchvision_from_source() { + local tag_="$1" + local temp_dir_="" + + if [[ -z "${tag_}" ]]; then + return + fi + + temp_dir_="$(mktemp -d)" + trap 'rm -rf "${temp_dir_}"' RETURN + + echo "Building torchvision from source tag ${tag_}" + git clone --depth 1 --branch "${tag_}" https://github.com/pytorch/vision.git "${temp_dir_}/vision" + python -m pip install "${temp_dir_}/vision" +} + +detect_arm_mode() { + local machine_="" + machine_="$(uname -m)" + + case "${machine_}" in + aarch64|arm64|armv7l|armv8l) + ARM_MODE=1 + ;; + esac +} + +# Parser loop +while [[ $# -gt 0 ]]; do + case "$1" in + -n|--env-name|-v|--venv_name) + ENV_NAME="$2" + shift 2 + ;; + -c|--create-env|--create_conda_env) + CREATE_ENV=1 + shift + ;; + --python-version) + PYTHON_VERSION="$2" + shift 2 + ;; + --conda-exe) + CONDA_EXE="$2" + shift 2 + ;; + --mamba) + USE_MAMBA=1 + shift + ;; + --core) + EXTRAS=() + shift + ;; + -e|--editable|--editable_mode) + EDITABLE=1 + shift + ;; + --wheel) + EDITABLE=0 + shift + ;; + --extras) + add_extra "$2" + shift 2 + ;; + --with-test) + add_extra "test" + shift + ;; + --with-docs) + add_extra "docs" + shift + ;; + --with-explain) + add_extra "explain" + shift + ;; + --with-shap) + add_extra "explain-shap" + shift + ;; + --with-captum) + add_extra "explain-captum" + shift + ;; + --with-cuda) + add_extra "cuda_all" + shift + ;; + --with-classical-ml) + add_extra "classical-ml" + shift + ;; + --with-xgboost) + add_extra "xgboost" + shift + ;; + --with-pysr) + add_extra "pysr" + shift + ;; + --build-docs) + BUILD_DOCS=1 + shift + ;; + --check) + RUN_CHECK=1 + shift + ;; + -j|--jetson|--jetson_target|--arm) + ARM_MODE=1 + shift + ;; + --pytorch-url) + PYTORCH_URL="$2" + shift 2 + ;; + --torchvision-url) + TORCHVISION_URL="$2" + shift 2 + ;; + --torchvision-source-tag) + TORCHVISION_SOURCE_TAG="$2" + shift 2 + ;; + --nvidia-deploy) + INSTALL_NVIDIA_DEPLOY=1 + shift + ;; + -s|--sudo_mode) + echo "--sudo_mode accepted for compatibility; system package installation is not handled by this script." + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + usage >&2 + exit 2 + ;; + esac done -if [ $jetson_target = true ] && [ ! $sudo_mode = true ]; then - echo "Jetson target requires sudo mode. Please use -s option." - exit 1 +# Check architecture before sourcing conda +detect_arm_mode + +if ! command -v "${CONDA_EXE}" >/dev/null 2>&1; then + echo "Conda executable not found: ${CONDA_EXE}" >&2 + exit 1 fi -if [ $create_conda_env = true ]; then - # Create and activate conda environment - conda create -n $venv_name python=3.12 - source $(conda info --base)/etc/profile.d/conda.sh - conda activate $venv_name +# Source conda environment +source_conda + +if conda_env_exists; then + echo "Using existing conda environment '${ENV_NAME}'" +elif [[ "${CREATE_ENV}" -eq 1 ]]; then + # Create conda if target environment not found + create_conda_env else - echo "Attempt to activate existing conda environment..." - - # Check if conda environment exists else stop - if conda info --envs | grep -q "$venv_name"; then - echo "Conda environment $venv_name found. Activating it..." - conda init bash - # Activate conda environment - conda activate $venv_name - else - echo "Conda environment $venv_name does not exist. Please create it first or run this script with -c flag." + echo "Conda environment '${ENV_NAME}' does not exist. Re-run with --create-env to create it." >&2 exit 1 - fi fi -sleep 1 - -if [ $jetson_target = false ] && [ ! -f /usr/local/cuda/lib64/libcusparseLt.so ]; then - echo "libcusparseLt.so not found. Downloading and installing..." - # if not exist, download and copy to the directory - wget https://developer.download.nvidia.com/compute/cusparselt/redist/libcusparse_lt/linux-sbsa/libcusparse_lt-linux-sbsa-0.5.2.1-archive.tar.xz - tar xf libcusparse_lt-linux-sbsa-0.5.2.1-archive.tar.xz - sudo cp -a libcusparse_lt-linux-sbsa-0.5.2.1-archive/include/* /usr/local/cuda/include/ - sudo cp -a libcusparse_lt-linux-sbsa-0.5.2.1-archive/lib/* /usr/local/cuda/lib64/ - rm libcusparse_lt-linux-sbsa-0.5.2.1-archive.tar.xz - rm -r libcusparse_lt-linux-sbsa-0.5.2.1-archive +# Activate environment and basic tools setup +conda activate "${ENV_NAME}" + +python -m pip install --upgrade pip setuptools wheel build + +if [[ "${ARM_MODE}" -eq 1 ]]; then + echo "ARM/Jetson mode active. x86-only pyproject dependencies are skipped by platform markers." + if [[ -z "${PYTORCH_URL}" ]]; then + echo "No --pytorch-url provided. Use board-specific PyTorch install before GPU workflows if torch is not already available." + fi fi -if [ $jetson_target = true ]; then - - #pip install -r requirements.txt # Install dependencies - #pip install -e . # Install the package in editable mode - - # Tools for building and installing wheels - echo "Installing setuptools, twine, and build..." - pip install setuptools twine build - python3 -m ensurepip --upgrade - python3 -m pip install --upgrade pip - - # Install key modules not managed by dependencies installation for versioning reasons - echo "Installing additional key modules..." - - # Remove torch and torchvision - pip uninstall -y torch torchvision torchaudio - - # Install torch for Jetson - pip install torch https://developer.download.nvidia.com/compute/redist/jp/v61/pytorch/torch-2.5.0a0+872d972e41.nv24.08.17622132-cp310-cp310-linux_aarch64.whl - - # Build and install torchvision from source - # From guide: https://github.com/azimjaan21/jetpack-6.1-pytorch-torchvision-/blob/main/README.md - git clone https://github.com/pytorch/vision.git - cd vision - git checkout tags/v0.20.0 - python3 setup.py install - - # Clean up - cd .. - sudo rm -r vision - - #pip install norse==1.0.0 aestream tonic expelliarmus --ignore-requires-python3 # FIXME: build fails due to "CUDA20" entry - - pip install nvidia-pyindex pycuda - - # ACHTUNG: this must run correctly before torch_tensorrt - pip install "nvidia-modelopt[all]" -U --extra-index-url https://pypi.nvidia.com - - # Install torch-tensorrt from source - mkdir lib - cd lib - - # Check if submodule exists - if [ -d "TensorRT" ]; then - echo "TensorRT submodule exists" - else - git submodule add --branch release/2.5 https://github.com/pytorch/TensorRT.git # Try to use release/2.6 (latest) - fi - - cd TensorRT - git checkout release/2.5 - git pull - - # Install required python3 packages of torch-tensorrt - python3 -m pip install -r toolchains/jp_workspaces/requirements.txt # NOTE: Installs the correct version of setuptools. Do not touch it. - - cuda_version=$(nvcc --version | grep Cuda | grep release | cut -d ',' -f 2 | sed -e 's/ release //g') - export TORCH_INSTALL_PATH=$(python3 -c "import torch, os; print(os.path.dirname(torch.__file__))") - export SITE_PACKAGE_PATH=${TORCH_INSTALL_PATH::-6} - export CUDA_HOME=/usr/local/cuda-${cuda_version}/ - - # Replace the MODULE.bazel with the jetpack one # DOUBT: why needed? - cat toolchains/jp_workspaces/MODULE.bazel.tmpl | envsubst > MODULE.bazel - - # Build and install torch_tensorrt wheel file with CXX11 ABI - python3 setup.py install --use-cxx11-abi - cd ../.. - - # Finally, build pyTorchAutoForge wheel - if [ $editable_mode -eq 1 ]; then - echo "Building and installing pyTorchAutoForge in editable mode..." - pip install -e . # Install the package in editable mode - else - echo "Building and installing pyTorchAutoForge wheel..." - # Remove previous build - rm -rf dist - rm -rf build - rm -rf pyTorchAutoForge.egg-info - # Build and install - python3 -m build - pip install dist/*.whl # Install pyTorchAutoForge wheel # FIXME editable mode does not work for this - fi - +# Install PyTorch and torchvision from URLs if provided +install_url_if_requested "PyTorch" "${PYTORCH_URL}" +install_url_if_requested "torchvision" "${TORCHVISION_URL}" +install_torchvision_from_source "${TORCHVISION_SOURCE_TAG}" + +cd "${REPO_ROOT}" + +# Append extras from command line and join into CSV for pip install +extras_csv="$(join_extras)" + +# Install package with extras if provided +install_spec="." +if [[ -n "${extras_csv}" ]]; then + install_spec=".[${extras_csv}]" +fi + +if [[ "${EDITABLE}" -eq 1 ]]; then + # Install as editable + echo "Installing editable package: ${install_spec}" + python -m pip install -e "${install_spec}" else - #pip install -r requirements.txt # Install dependencies that do not cause issues... - #python3 -m pip install -r toolchains/jp_workspaces/test_requirements.txt # Required for test cases - - # Tools for building and installing wheels - echo "Installing setuptools, twine, and build..." - pip install setuptools twine build - python3 -m ensurepip --upgrade - python3 -m pip install --upgrade pip - - # Install key modules not managed by dependencies installation for versioning reasons - echo "Installing additional key modules..." - - # Build pyTorchAutoForge wheel - if [ $editable_mode -eq 1 ]; then - echo "Building and installing pyTorchAutoForge in editable mode..." - pip install -e . # Install the package in editable mode - else - echo "Building and installing pyTorchAutoForge wheel..." - # Remove previous build - rm -rf dist - rm -rf build - rm -rf pyTorchAutoForge.egg-info - # Build and install - python3 -m build - pip install dist/*.whl # Install pyTorchAutoForge wheel # FIXME editable mode does not work for this - fi - - # Install tools for model optimization and deployment - echo "Installing tools for model optimization and deployment by Nvidia..." - python3 -m pip install pycuda torch torchvision torch-tensorrt tensorrt "nvidia-modelopt[all]" -U --extra-index-url https://pypi.nvidia.com + # Normal install + echo "Installing package: ${install_spec}" + python -m pip install "${install_spec}" fi -# Check installation by printing versions in python3 -cd ./tests/.configuration/ -python3 -m test_env -cd ../.. +# Install nvidia tools from pypi.nvidia.com +if [[ "${INSTALL_NVIDIA_DEPLOY}" -eq 1 ]]; then + echo "Installing optional NVIDIA deploy packages" + python -m pip install -U --extra-index-url https://pypi.nvidia.com \ + pycuda torch-tensorrt tensorrt "nvidia-modelopt[all]" +fi +# Build documentation +if [[ "${BUILD_DOCS}" -eq 1 ]]; then + CONDA_ENV="${ENV_NAME}" "${REPO_ROOT}/doc/makedoc.sh" +fi +# Test environment setup +if [[ "${RUN_CHECK}" -eq 1 ]]; then + python "${REPO_ROOT}/tests/.configuration/test_env.py" +fi diff --git a/doc/.gitignore b/doc/.gitignore index 8a7c9b5..e288e62 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -1,8 +1,8 @@ -<<<<<<< HEAD -html -doctrees -======= -doctrees -html ->>>>>>> main -sources \ No newline at end of file +# Generated documentation outputs +_build/ +api/generated/ +doctrees/ +html/ +local_guides/ +_static/* +_auto_api_templates/* diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index 0c91a55..0000000 --- a/doc/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -# Minimal makefile for Sphinx documentation -# -clean: - rm -rf _build - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/doc/_build/doctrees/environment.pickle b/doc/_build/doctrees/environment.pickle deleted file mode 100644 index 8c958e3..0000000 Binary files a/doc/_build/doctrees/environment.pickle and /dev/null differ diff --git a/doc/_build/doctrees/index.doctree b/doc/_build/doctrees/index.doctree deleted file mode 100644 index 89a63db..0000000 Binary files a/doc/_build/doctrees/index.doctree and /dev/null differ diff --git a/doc/_build/html/.buildinfo b/doc/_build/html/.buildinfo deleted file mode 100644 index 0b037fd..0000000 --- a/doc/_build/html/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file records the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 6754983062a99de618420ede929e476b -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/doc/_build/html/.buildinfo.bak b/doc/_build/html/.buildinfo.bak deleted file mode 100644 index 2e3fa7f..0000000 --- a/doc/_build/html/.buildinfo.bak +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: b0c9dee0bc36e63a3fbbaa9645f4cd51 -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/doc/_build/html/_sources/index.rst.txt b/doc/_build/html/_sources/index.rst.txt deleted file mode 100644 index 824a089..0000000 --- a/doc/_build/html/_sources/index.rst.txt +++ /dev/null @@ -1,58 +0,0 @@ -.. pyTorchAutoForge documentation master file, created by - sphinx-quickstart on Sun Sep 22 18:03:23 2024. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to pyTorchAutoForge's documentation! -============================================ - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - -.. TODO: Add all modules here explicitly (ask GPT) - -.. API modules -.. automodule:: pyTorchAutoForge.api.torch.torchModulesIO - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: pyTorchAutoForge.api.matlab.TorchModelMATLABwrapper - :members: - :undoc-members: - :show-inheritance: - -.. Datasets module -.. automodule:: pyTorchAutoForge.datasets - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: pyTorchAutoForge.hparamsOptim - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: pyTorchAutoForge.modelBuilding.ModelAutoBuilder - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: pyTorchAutoForge.optimization - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: pyTorchAutoForge.utils - :members: - :undoc-members: - :show-inheritance: - -Indices and tables -================== -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - - diff --git a/doc/_build/html/_static/_sphinx_javascript_frameworks_compat.js b/doc/_build/html/_static/_sphinx_javascript_frameworks_compat.js deleted file mode 100644 index 8141580..0000000 --- a/doc/_build/html/_static/_sphinx_javascript_frameworks_compat.js +++ /dev/null @@ -1,123 +0,0 @@ -/* Compatability shim for jQuery and underscores.js. - * - * Copyright Sphinx contributors - * Released under the two clause BSD licence - */ - -/** - * small helper function to urldecode strings - * - * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL - */ -jQuery.urldecode = function(x) { - if (!x) { - return x - } - return decodeURIComponent(x.replace(/\+/g, ' ')); -}; - -/** - * small helper function to urlencode strings - */ -jQuery.urlencode = encodeURIComponent; - -/** - * This function returns the parsed url parameters of the - * current request. Multiple values per key are supported, - * it will always return arrays of strings for the value parts. - */ -jQuery.getQueryParameters = function(s) { - if (typeof s === 'undefined') - s = document.location.search; - var parts = s.substr(s.indexOf('?') + 1).split('&'); - var result = {}; - for (var i = 0; i < parts.length; i++) { - var tmp = parts[i].split('=', 2); - var key = jQuery.urldecode(tmp[0]); - var value = jQuery.urldecode(tmp[1]); - if (key in result) - result[key].push(value); - else - result[key] = [value]; - } - return result; -}; - -/** - * highlight a given string on a jquery object by wrapping it in - * span elements with the given class name. - */ -jQuery.fn.highlightText = function(text, className) { - function highlight(node, addItems) { - if (node.nodeType === 3) { - var val = node.nodeValue; - var pos = val.toLowerCase().indexOf(text); - if (pos >= 0 && - !jQuery(node.parentNode).hasClass(className) && - !jQuery(node.parentNode).hasClass("nohighlight")) { - var span; - var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.className = className; - } - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - node.parentNode.insertBefore(span, node.parentNode.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling)); - node.nodeValue = val.substr(0, pos); - if (isInSVG) { - var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - var bbox = node.parentElement.getBBox(); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute('class', className); - addItems.push({ - "parent": node.parentNode, - "target": rect}); - } - } - } - else if (!jQuery(node).is("button, select, textarea")) { - jQuery.each(node.childNodes, function() { - highlight(this, addItems); - }); - } - } - var addItems = []; - var result = this.each(function() { - highlight(this, addItems); - }); - for (var i = 0; i < addItems.length; ++i) { - jQuery(addItems[i].parent).before(addItems[i].target); - } - return result; -}; - -/* - * backward compatibility for jQuery.browser - * This will be supported until firefox bug is fixed. - */ -if (!jQuery.browser) { - jQuery.uaMatch = function(ua) { - ua = ua.toLowerCase(); - - var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || - /(webkit)[ \/]([\w.]+)/.exec(ua) || - /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || - /(msie) ([\w.]+)/.exec(ua) || - ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || - []; - - return { - browser: match[ 1 ] || "", - version: match[ 2 ] || "0" - }; - }; - jQuery.browser = {}; - jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; -} diff --git a/doc/_build/html/_static/basic.css b/doc/_build/html/_static/basic.css deleted file mode 100644 index 7ebbd6d..0000000 --- a/doc/_build/html/_static/basic.css +++ /dev/null @@ -1,914 +0,0 @@ -/* - * Sphinx stylesheet -- basic theme. - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -div.section::after { - display: block; - content: ''; - clear: left; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; - word-wrap: break-word; - overflow-wrap : break-word; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox form.search { - overflow: hidden; -} - -div.sphinxsidebar #searchbox input[type="text"] { - float: left; - width: 80%; - padding: 0.25em; - box-sizing: border-box; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - float: left; - width: 20%; - border-left: none; - padding: 0.25em; - box-sizing: border-box; -} - - -img { - border: 0; - max-width: 100%; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin-top: 10px; -} - -ul.search li { - padding: 5px 0; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li p.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; - margin-left: auto; - margin-right: auto; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable ul { - margin-top: 0; - margin-bottom: 0; - list-style-type: none; -} - -table.indextable > tbody > tr > td > ul { - padding-left: 0em; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- domain module index --------------------------------------------------- */ - -table.modindextable td { - padding: 2px; - border-collapse: collapse; -} - -/* -- general body styles --------------------------------------------------- */ - -div.body { - min-width: 360px; - max-width: 800px; -} - -div.body p, div.body dd, div.body li, div.body blockquote { - -moz-hyphens: auto; - -ms-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; -} - -a.headerlink { - visibility: hidden; -} - -a:visited { - color: #551A8B; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink, -caption:hover > a.headerlink, -p.caption:hover > a.headerlink, -div.code-block-caption:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, figure.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, figure.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, figure.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -img.align-default, figure.align-default, .figure.align-default { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-default { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar, -aside.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px; - background-color: #ffe; - width: 40%; - float: right; - clear: right; - overflow-x: auto; -} - -p.sidebar-title { - font-weight: bold; -} - -nav.contents, -aside.topic, -div.admonition, div.topic, blockquote { - clear: left; -} - -/* -- topics ---------------------------------------------------------------- */ - -nav.contents, -aside.topic, -div.topic { - border: 1px solid #ccc; - padding: 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- content of sidebars/topics/admonitions -------------------------------- */ - -div.sidebar > :last-child, -aside.sidebar > :last-child, -nav.contents > :last-child, -aside.topic > :last-child, -div.topic > :last-child, -div.admonition > :last-child { - margin-bottom: 0; -} - -div.sidebar::after, -aside.sidebar::after, -nav.contents::after, -aside.topic::after, -div.topic::after, -div.admonition::after, -blockquote::after { - display: block; - content: ''; - clear: both; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - margin-top: 10px; - margin-bottom: 10px; - border: 0; - border-collapse: collapse; -} - -table.align-center { - margin-left: auto; - margin-right: auto; -} - -table.align-default { - margin-left: auto; - margin-right: auto; -} - -table caption span.caption-number { - font-style: italic; -} - -table caption span.caption-text { -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -th > :first-child, -td > :first-child { - margin-top: 0px; -} - -th > :last-child, -td > :last-child { - margin-bottom: 0px; -} - -/* -- figures --------------------------------------------------------------- */ - -div.figure, figure { - margin: 0.5em; - padding: 0.5em; -} - -div.figure p.caption, figcaption { - padding: 0.3em; -} - -div.figure p.caption span.caption-number, -figcaption span.caption-number { - font-style: italic; -} - -div.figure p.caption span.caption-text, -figcaption span.caption-text { -} - -/* -- field list styles ----------------------------------------------------- */ - -table.field-list td, table.field-list th { - border: 0 !important; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -/* -- hlist styles ---------------------------------------------------------- */ - -table.hlist { - margin: 1em 0; -} - -table.hlist td { - vertical-align: top; -} - -/* -- object description styles --------------------------------------------- */ - -.sig { - font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; -} - -.sig-name, code.descname { - background-color: transparent; - font-weight: bold; -} - -.sig-name { - font-size: 1.1em; -} - -code.descname { - font-size: 1.2em; -} - -.sig-prename, code.descclassname { - background-color: transparent; -} - -.optional { - font-size: 1.3em; -} - -.sig-paren { - font-size: larger; -} - -.sig-param.n { - font-style: italic; -} - -/* C++ specific styling */ - -.sig-inline.c-texpr, -.sig-inline.cpp-texpr { - font-family: unset; -} - -.sig.c .k, .sig.c .kt, -.sig.cpp .k, .sig.cpp .kt { - color: #0033B3; -} - -.sig.c .m, -.sig.cpp .m { - color: #1750EB; -} - -.sig.c .s, .sig.c .sc, -.sig.cpp .s, .sig.cpp .sc { - color: #067D17; -} - - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -:not(li) > ol > li:first-child > :first-child, -:not(li) > ul > li:first-child > :first-child { - margin-top: 0px; -} - -:not(li) > ol > li:last-child > :last-child, -:not(li) > ul > li:last-child > :last-child { - margin-bottom: 0px; -} - -ol.simple ol p, -ol.simple ul p, -ul.simple ol p, -ul.simple ul p { - margin-top: 0; -} - -ol.simple > li:not(:first-child) > p, -ul.simple > li:not(:first-child) > p { - margin-top: 0; -} - -ol.simple p, -ul.simple p { - margin-bottom: 0; -} - -aside.footnote > span, -div.citation > span { - float: left; -} -aside.footnote > span:last-of-type, -div.citation > span:last-of-type { - padding-right: 0.5em; -} -aside.footnote > p { - margin-left: 2em; -} -div.citation > p { - margin-left: 4em; -} -aside.footnote > p:last-of-type, -div.citation > p:last-of-type { - margin-bottom: 0em; -} -aside.footnote > p:last-of-type:after, -div.citation > p:last-of-type:after { - content: ""; - clear: both; -} - -dl.field-list { - display: grid; - grid-template-columns: fit-content(30%) auto; -} - -dl.field-list > dt { - font-weight: bold; - word-break: break-word; - padding-left: 0.5em; - padding-right: 5px; -} - -dl.field-list > dd { - padding-left: 0.5em; - margin-top: 0em; - margin-left: 0em; - margin-bottom: 0em; -} - -dl { - margin-bottom: 15px; -} - -dd > :first-child { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -.sig dd { - margin-top: 0px; - margin-bottom: 0px; -} - -.sig dl { - margin-top: 0px; - margin-bottom: 0px; -} - -dl > dd:last-child, -dl > dd:last-child > :last-child { - margin-bottom: 0; -} - -dt:target, span.highlighted { - background-color: #fbe54e; -} - -rect.highlighted { - fill: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -.classifier:before { - font-style: normal; - margin: 0 0.5em; - content: ":"; - display: inline-block; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -.translated { - background-color: rgba(207, 255, 207, 0.2) -} - -.untranslated { - background-color: rgba(255, 207, 207, 0.2) -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -pre, div[class*="highlight-"] { - clear: both; -} - -span.pre { - -moz-hyphens: none; - -ms-hyphens: none; - -webkit-hyphens: none; - hyphens: none; - white-space: nowrap; -} - -div[class*="highlight-"] { - margin: 1em 0; -} - -td.linenos pre { - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - display: block; -} - -table.highlighttable tbody { - display: block; -} - -table.highlighttable tr { - display: flex; -} - -table.highlighttable td { - margin: 0; - padding: 0; -} - -table.highlighttable td.linenos { - padding-right: 0.5em; -} - -table.highlighttable td.code { - flex: 1; - overflow: hidden; -} - -.highlight .hll { - display: block; -} - -div.highlight pre, -table.highlighttable pre { - margin: 0; -} - -div.code-block-caption + div { - margin-top: 0; -} - -div.code-block-caption { - margin-top: 1em; - padding: 2px 5px; - font-size: small; -} - -div.code-block-caption code { - background-color: transparent; -} - -table.highlighttable td.linenos, -span.linenos, -div.highlight span.gp { /* gp: Generic.Prompt */ - user-select: none; - -webkit-user-select: text; /* Safari fallback only */ - -webkit-user-select: none; /* Chrome/Safari */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE10+ */ -} - -div.code-block-caption span.caption-number { - padding: 0.1em 0.3em; - font-style: italic; -} - -div.code-block-caption span.caption-text { -} - -div.literal-block-wrapper { - margin: 1em 0; -} - -code.xref, a code { - background-color: transparent; - font-weight: bold; -} - -h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -span.eqno a.headerlink { - position: absolute; - z-index: 1; -} - -div.math:hover a.headerlink { - visibility: visible; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file diff --git a/doc/_build/html/_static/css/badge_only.css b/doc/_build/html/_static/css/badge_only.css deleted file mode 100644 index 88ba55b..0000000 --- a/doc/_build/html/_static/css/badge_only.css +++ /dev/null @@ -1 +0,0 @@ -.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions .rst-other-versions .rtd-current-item{font-weight:700}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}#flyout-search-form{padding:6px} \ No newline at end of file diff --git a/doc/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff b/doc/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff deleted file mode 100644 index 6cb6000..0000000 Binary files a/doc/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff and /dev/null differ diff --git a/doc/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 b/doc/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 deleted file mode 100644 index 7059e23..0000000 Binary files a/doc/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 and /dev/null differ diff --git a/doc/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff b/doc/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff deleted file mode 100644 index f815f63..0000000 Binary files a/doc/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff and /dev/null differ diff --git a/doc/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 b/doc/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 deleted file mode 100644 index f2c76e5..0000000 Binary files a/doc/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 and /dev/null differ diff --git a/doc/_build/html/_static/css/fonts/fontawesome-webfont.eot b/doc/_build/html/_static/css/fonts/fontawesome-webfont.eot deleted file mode 100644 index e9f60ca..0000000 Binary files a/doc/_build/html/_static/css/fonts/fontawesome-webfont.eot and /dev/null differ diff --git a/doc/_build/html/_static/css/fonts/fontawesome-webfont.svg b/doc/_build/html/_static/css/fonts/fontawesome-webfont.svg deleted file mode 100644 index 855c845..0000000 --- a/doc/_build/html/_static/css/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,2671 +0,0 @@ - - - - -Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 - By ,,, -Copyright Dave Gandy 2016. All rights reserved. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/_build/html/_static/css/fonts/fontawesome-webfont.ttf b/doc/_build/html/_static/css/fonts/fontawesome-webfont.ttf deleted file mode 100644 index 35acda2..0000000 Binary files a/doc/_build/html/_static/css/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/doc/_build/html/_static/css/fonts/fontawesome-webfont.woff b/doc/_build/html/_static/css/fonts/fontawesome-webfont.woff deleted file mode 100644 index 400014a..0000000 Binary files a/doc/_build/html/_static/css/fonts/fontawesome-webfont.woff and /dev/null differ diff --git a/doc/_build/html/_static/css/fonts/fontawesome-webfont.woff2 b/doc/_build/html/_static/css/fonts/fontawesome-webfont.woff2 deleted file mode 100644 index 4d13fc6..0000000 Binary files a/doc/_build/html/_static/css/fonts/fontawesome-webfont.woff2 and /dev/null differ diff --git a/doc/_build/html/_static/css/fonts/lato-bold-italic.woff b/doc/_build/html/_static/css/fonts/lato-bold-italic.woff deleted file mode 100644 index 88ad05b..0000000 Binary files a/doc/_build/html/_static/css/fonts/lato-bold-italic.woff and /dev/null differ diff --git a/doc/_build/html/_static/css/fonts/lato-bold-italic.woff2 b/doc/_build/html/_static/css/fonts/lato-bold-italic.woff2 deleted file mode 100644 index c4e3d80..0000000 Binary files a/doc/_build/html/_static/css/fonts/lato-bold-italic.woff2 and /dev/null differ diff --git a/doc/_build/html/_static/css/fonts/lato-bold.woff b/doc/_build/html/_static/css/fonts/lato-bold.woff deleted file mode 100644 index c6dff51..0000000 Binary files a/doc/_build/html/_static/css/fonts/lato-bold.woff and /dev/null differ diff --git a/doc/_build/html/_static/css/fonts/lato-bold.woff2 b/doc/_build/html/_static/css/fonts/lato-bold.woff2 deleted file mode 100644 index bb19504..0000000 Binary files a/doc/_build/html/_static/css/fonts/lato-bold.woff2 and /dev/null differ diff --git a/doc/_build/html/_static/css/fonts/lato-normal-italic.woff b/doc/_build/html/_static/css/fonts/lato-normal-italic.woff deleted file mode 100644 index 76114bc..0000000 Binary files a/doc/_build/html/_static/css/fonts/lato-normal-italic.woff and /dev/null differ diff --git a/doc/_build/html/_static/css/fonts/lato-normal-italic.woff2 b/doc/_build/html/_static/css/fonts/lato-normal-italic.woff2 deleted file mode 100644 index 3404f37..0000000 Binary files a/doc/_build/html/_static/css/fonts/lato-normal-italic.woff2 and /dev/null differ diff --git a/doc/_build/html/_static/css/fonts/lato-normal.woff b/doc/_build/html/_static/css/fonts/lato-normal.woff deleted file mode 100644 index ae1307f..0000000 Binary files a/doc/_build/html/_static/css/fonts/lato-normal.woff and /dev/null differ diff --git a/doc/_build/html/_static/css/fonts/lato-normal.woff2 b/doc/_build/html/_static/css/fonts/lato-normal.woff2 deleted file mode 100644 index 3bf9843..0000000 Binary files a/doc/_build/html/_static/css/fonts/lato-normal.woff2 and /dev/null differ diff --git a/doc/_build/html/_static/css/theme.css b/doc/_build/html/_static/css/theme.css deleted file mode 100644 index 0f14f10..0000000 --- a/doc/_build/html/_static/css/theme.css +++ /dev/null @@ -1,4 +0,0 @@ -html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search .wy-dropdown>aactive,.wy-side-nav-search .wy-dropdown>afocus,.wy-side-nav-search>a:hover,.wy-side-nav-search>aactive,.wy-side-nav-search>afocus{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon,.wy-side-nav-search>a.icon{display:block}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.switch-menus{position:relative;display:block;margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-side-nav-search>div.switch-menus>div.language-switch,.wy-side-nav-search>div.switch-menus>div.version-switch{display:inline-block;padding:.2em}.wy-side-nav-search>div.switch-menus>div.language-switch select,.wy-side-nav-search>div.switch-menus>div.version-switch select{display:inline-block;margin-right:-2rem;padding-right:2rem;max-width:240px;text-align-last:center;background:none;border:none;border-radius:0;box-shadow:none;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-size:1em;font-weight:400;color:hsla(0,0%,100%,.3);cursor:pointer;appearance:none;-webkit-appearance:none;-moz-appearance:none}.wy-side-nav-search>div.switch-menus>div.language-switch select:active,.wy-side-nav-search>div.switch-menus>div.language-switch select:focus,.wy-side-nav-search>div.switch-menus>div.language-switch select:hover,.wy-side-nav-search>div.switch-menus>div.version-switch select:active,.wy-side-nav-search>div.switch-menus>div.version-switch select:focus,.wy-side-nav-search>div.switch-menus>div.version-switch select:hover{background:hsla(0,0%,100%,.1);color:hsla(0,0%,100%,.5)}.wy-side-nav-search>div.switch-menus>div.language-switch select option,.wy-side-nav-search>div.switch-menus>div.version-switch select option{color:#000}.wy-side-nav-search>div.switch-menus>div.language-switch:has(>select):after,.wy-side-nav-search>div.switch-menus>div.version-switch:has(>select):after{display:inline-block;width:1.5em;height:100%;padding:.1em;content:"\f0d7";font-size:1em;line-height:1.2em;font-family:FontAwesome;text-align:center;pointer-events:none;box-sizing:border-box}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions .rst-other-versions .rtd-current-item{font-weight:700}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}#flyout-search-form{padding:6px}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel,.rst-content .menuselection{font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .guilabel,.rst-content .menuselection{border:1px solid #7fbbe3;background:#e7f2fa}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/doc/_build/html/_static/custom.css b/doc/_build/html/_static/custom.css deleted file mode 100644 index 06ca476..0000000 --- a/doc/_build/html/_static/custom.css +++ /dev/null @@ -1,50 +0,0 @@ -/* Increase the width of the content area */ -.wy-nav-content { - max-width: 100%; - /* Set to your preferred width */ -} - -.wy-side-nav-search { - width: 100%; - overflow: visible; - /* Fixes the cutoff issue */ -} - -/* Optionally adjust the width of the side navigation */ -/* -.wy-nav-side { - width: 250px; - Set a custom width for the side index -} -*/ - - -/* Adjust the input field of the search bar */ -.wy-side-nav-search input { - width: 90%; - /* Adjust the width if necessary */ -} - -/* Change background color */ -body { - background-color: #f8f8f8; -} - -/* Change text color */ -p { - color: #333; -} - -/* Customize sidebar background and link colors */ -.wy-nav-side { - background-color: #2c3e50; -} - -.wy-nav-side a { - color: #ecf0f1; -} - -/* Customize main content links */ -a { - color: #2980b9; -} \ No newline at end of file diff --git a/doc/_build/html/_static/doctools.js b/doc/_build/html/_static/doctools.js deleted file mode 100644 index 0398ebb..0000000 --- a/doc/_build/html/_static/doctools.js +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Base JavaScript utilities for all Sphinx HTML documentation. - */ -"use strict"; - -const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ - "TEXTAREA", - "INPUT", - "SELECT", - "BUTTON", -]); - -const _ready = (callback) => { - if (document.readyState !== "loading") { - callback(); - } else { - document.addEventListener("DOMContentLoaded", callback); - } -}; - -/** - * Small JavaScript module for the documentation. - */ -const Documentation = { - init: () => { - Documentation.initDomainIndexTable(); - Documentation.initOnKeyListeners(); - }, - - /** - * i18n support - */ - TRANSLATIONS: {}, - PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), - LOCALE: "unknown", - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext: (string) => { - const translated = Documentation.TRANSLATIONS[string]; - switch (typeof translated) { - case "undefined": - return string; // no translation - case "string": - return translated; // translation exists - default: - return translated[0]; // (singular, plural) translation tuple exists - } - }, - - ngettext: (singular, plural, n) => { - const translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated !== "undefined") - return translated[Documentation.PLURAL_EXPR(n)]; - return n === 1 ? singular : plural; - }, - - addTranslations: (catalog) => { - Object.assign(Documentation.TRANSLATIONS, catalog.messages); - Documentation.PLURAL_EXPR = new Function( - "n", - `return (${catalog.plural_expr})` - ); - Documentation.LOCALE = catalog.locale; - }, - - /** - * helper function to focus on search bar - */ - focusSearchBar: () => { - document.querySelectorAll("input[name=q]")[0]?.focus(); - }, - - /** - * Initialise the domain index toggle buttons - */ - initDomainIndexTable: () => { - const toggler = (el) => { - const idNumber = el.id.substr(7); - const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); - if (el.src.substr(-9) === "minus.png") { - el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; - toggledRows.forEach((el) => (el.style.display = "none")); - } else { - el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; - toggledRows.forEach((el) => (el.style.display = "")); - } - }; - - const togglerElements = document.querySelectorAll("img.toggler"); - togglerElements.forEach((el) => - el.addEventListener("click", (event) => toggler(event.currentTarget)) - ); - togglerElements.forEach((el) => (el.style.display = "")); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); - }, - - initOnKeyListeners: () => { - // only install a listener if it is really needed - if ( - !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && - !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS - ) - return; - - document.addEventListener("keydown", (event) => { - // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; - // bail with special keys - if (event.altKey || event.ctrlKey || event.metaKey) return; - - if (!event.shiftKey) { - switch (event.key) { - case "ArrowLeft": - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; - - const prevLink = document.querySelector('link[rel="prev"]'); - if (prevLink && prevLink.href) { - window.location.href = prevLink.href; - event.preventDefault(); - } - break; - case "ArrowRight": - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; - - const nextLink = document.querySelector('link[rel="next"]'); - if (nextLink && nextLink.href) { - window.location.href = nextLink.href; - event.preventDefault(); - } - break; - } - } - - // some keyboard layouts may need Shift to get / - switch (event.key) { - case "/": - if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; - Documentation.focusSearchBar(); - event.preventDefault(); - } - }); - }, -}; - -// quick alias for translations -const _ = Documentation.gettext; - -_ready(Documentation.init); diff --git a/doc/_build/html/_static/documentation_options.js b/doc/_build/html/_static/documentation_options.js deleted file mode 100644 index e21c068..0000000 --- a/doc/_build/html/_static/documentation_options.js +++ /dev/null @@ -1,13 +0,0 @@ -const DOCUMENTATION_OPTIONS = { - VERSION: '0.1', - LANGUAGE: 'en', - COLLAPSE_INDEX: false, - BUILDER: 'html', - FILE_SUFFIX: '.html', - LINK_SUFFIX: '.html', - HAS_SOURCE: true, - SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false, - SHOW_SEARCH_SUMMARY: true, - ENABLE_SEARCH_SHORTCUTS: true, -}; \ No newline at end of file diff --git a/doc/_build/html/_static/file.png b/doc/_build/html/_static/file.png deleted file mode 100644 index a858a41..0000000 Binary files a/doc/_build/html/_static/file.png and /dev/null differ diff --git a/doc/_build/html/_static/fonts/Lato/lato-bold.eot b/doc/_build/html/_static/fonts/Lato/lato-bold.eot deleted file mode 100644 index 3361183..0000000 Binary files a/doc/_build/html/_static/fonts/Lato/lato-bold.eot and /dev/null differ diff --git a/doc/_build/html/_static/fonts/Lato/lato-bold.ttf b/doc/_build/html/_static/fonts/Lato/lato-bold.ttf deleted file mode 100644 index 29f691d..0000000 Binary files a/doc/_build/html/_static/fonts/Lato/lato-bold.ttf and /dev/null differ diff --git a/doc/_build/html/_static/fonts/Lato/lato-bold.woff b/doc/_build/html/_static/fonts/Lato/lato-bold.woff deleted file mode 100644 index c6dff51..0000000 Binary files a/doc/_build/html/_static/fonts/Lato/lato-bold.woff and /dev/null differ diff --git a/doc/_build/html/_static/fonts/Lato/lato-bold.woff2 b/doc/_build/html/_static/fonts/Lato/lato-bold.woff2 deleted file mode 100644 index bb19504..0000000 Binary files a/doc/_build/html/_static/fonts/Lato/lato-bold.woff2 and /dev/null differ diff --git a/doc/_build/html/_static/fonts/Lato/lato-bolditalic.eot b/doc/_build/html/_static/fonts/Lato/lato-bolditalic.eot deleted file mode 100644 index 3d41549..0000000 Binary files a/doc/_build/html/_static/fonts/Lato/lato-bolditalic.eot and /dev/null differ diff --git a/doc/_build/html/_static/fonts/Lato/lato-bolditalic.ttf b/doc/_build/html/_static/fonts/Lato/lato-bolditalic.ttf deleted file mode 100644 index f402040..0000000 Binary files a/doc/_build/html/_static/fonts/Lato/lato-bolditalic.ttf and /dev/null differ diff --git a/doc/_build/html/_static/fonts/Lato/lato-bolditalic.woff b/doc/_build/html/_static/fonts/Lato/lato-bolditalic.woff deleted file mode 100644 index 88ad05b..0000000 Binary files a/doc/_build/html/_static/fonts/Lato/lato-bolditalic.woff and /dev/null differ diff --git a/doc/_build/html/_static/fonts/Lato/lato-bolditalic.woff2 b/doc/_build/html/_static/fonts/Lato/lato-bolditalic.woff2 deleted file mode 100644 index c4e3d80..0000000 Binary files a/doc/_build/html/_static/fonts/Lato/lato-bolditalic.woff2 and /dev/null differ diff --git a/doc/_build/html/_static/fonts/Lato/lato-italic.eot b/doc/_build/html/_static/fonts/Lato/lato-italic.eot deleted file mode 100644 index 3f82642..0000000 Binary files a/doc/_build/html/_static/fonts/Lato/lato-italic.eot and /dev/null differ diff --git a/doc/_build/html/_static/fonts/Lato/lato-italic.ttf b/doc/_build/html/_static/fonts/Lato/lato-italic.ttf deleted file mode 100644 index b4bfc9b..0000000 Binary files a/doc/_build/html/_static/fonts/Lato/lato-italic.ttf and /dev/null differ diff --git a/doc/_build/html/_static/fonts/Lato/lato-italic.woff b/doc/_build/html/_static/fonts/Lato/lato-italic.woff deleted file mode 100644 index 76114bc..0000000 Binary files a/doc/_build/html/_static/fonts/Lato/lato-italic.woff and /dev/null differ diff --git a/doc/_build/html/_static/fonts/Lato/lato-italic.woff2 b/doc/_build/html/_static/fonts/Lato/lato-italic.woff2 deleted file mode 100644 index 3404f37..0000000 Binary files a/doc/_build/html/_static/fonts/Lato/lato-italic.woff2 and /dev/null differ diff --git a/doc/_build/html/_static/fonts/Lato/lato-regular.eot b/doc/_build/html/_static/fonts/Lato/lato-regular.eot deleted file mode 100644 index 11e3f2a..0000000 Binary files a/doc/_build/html/_static/fonts/Lato/lato-regular.eot and /dev/null differ diff --git a/doc/_build/html/_static/fonts/Lato/lato-regular.ttf b/doc/_build/html/_static/fonts/Lato/lato-regular.ttf deleted file mode 100644 index 74decd9..0000000 Binary files a/doc/_build/html/_static/fonts/Lato/lato-regular.ttf and /dev/null differ diff --git a/doc/_build/html/_static/fonts/Lato/lato-regular.woff b/doc/_build/html/_static/fonts/Lato/lato-regular.woff deleted file mode 100644 index ae1307f..0000000 Binary files a/doc/_build/html/_static/fonts/Lato/lato-regular.woff and /dev/null differ diff --git a/doc/_build/html/_static/fonts/Lato/lato-regular.woff2 b/doc/_build/html/_static/fonts/Lato/lato-regular.woff2 deleted file mode 100644 index 3bf9843..0000000 Binary files a/doc/_build/html/_static/fonts/Lato/lato-regular.woff2 and /dev/null differ diff --git a/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot b/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot deleted file mode 100644 index 79dc8ef..0000000 Binary files a/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot and /dev/null differ diff --git a/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf b/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf deleted file mode 100644 index df5d1df..0000000 Binary files a/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf and /dev/null differ diff --git a/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff b/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff deleted file mode 100644 index 6cb6000..0000000 Binary files a/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff and /dev/null differ diff --git a/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 b/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 deleted file mode 100644 index 7059e23..0000000 Binary files a/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 and /dev/null differ diff --git a/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot b/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot deleted file mode 100644 index 2f7ca78..0000000 Binary files a/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot and /dev/null differ diff --git a/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf b/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf deleted file mode 100644 index eb52a79..0000000 Binary files a/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf and /dev/null differ diff --git a/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff b/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff deleted file mode 100644 index f815f63..0000000 Binary files a/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff and /dev/null differ diff --git a/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 b/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 deleted file mode 100644 index f2c76e5..0000000 Binary files a/doc/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 and /dev/null differ diff --git a/doc/_build/html/_static/jquery.js b/doc/_build/html/_static/jquery.js deleted file mode 100644 index c4c6022..0000000 --- a/doc/_build/html/_static/jquery.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/doc/_build/html/_static/js/html5shiv.min.js b/doc/_build/html/_static/js/html5shiv.min.js deleted file mode 100644 index cd1c674..0000000 --- a/doc/_build/html/_static/js/html5shiv.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/** -* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed -*/ -!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/doc/_build/html/_static/js/theme.js b/doc/_build/html/_static/js/theme.js deleted file mode 100644 index 1fddb6e..0000000 --- a/doc/_build/html/_static/js/theme.js +++ /dev/null @@ -1 +0,0 @@ -!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t a.language.name.localeCompare(b.language.name)); - - const languagesHTML = ` -
-
Languages
- ${languages - .map( - (translation) => ` -
- ${translation.language.code} -
- `, - ) - .join("\n")} -
- `; - return languagesHTML; - } - - function renderVersions(config) { - if (!config.versions.active.length) { - return ""; - } - const versionsHTML = ` -
-
Versions
- ${config.versions.active - .map( - (version) => ` -
- ${version.slug} -
- `, - ) - .join("\n")} -
- `; - return versionsHTML; - } - - function renderDownloads(config) { - if (!Object.keys(config.versions.current.downloads).length) { - return ""; - } - const downloadsNameDisplay = { - pdf: "PDF", - epub: "Epub", - htmlzip: "HTML", - }; - - const downloadsHTML = ` -
-
Downloads
- ${Object.entries(config.versions.current.downloads) - .map( - ([name, url]) => ` -
- ${downloadsNameDisplay[name]} -
- `, - ) - .join("\n")} -
- `; - return downloadsHTML; - } - - document.addEventListener("readthedocs-addons-data-ready", function (event) { - const config = event.detail.data(); - - const flyout = ` -
- - Read the Docs - v: ${config.versions.current.slug} - - -
-
- ${renderLanguages(config)} - ${renderVersions(config)} - ${renderDownloads(config)} -
-
On Read the Docs
-
- Project Home -
-
- Builds -
-
- Downloads -
-
-
-
Search
-
-
- -
-
-
-
- - Hosted by Read the Docs - -
-
- `; - - // Inject the generated flyout into the body HTML element. - document.body.insertAdjacentHTML("beforeend", flyout); - - // Trigger the Read the Docs Addons Search modal when clicking on the "Search docs" input from inside the flyout. - document - .querySelector("#flyout-search-form") - .addEventListener("focusin", () => { - const event = new CustomEvent("readthedocs-search-show"); - document.dispatchEvent(event); - }); - }) -} - -if (themeLanguageSelector || themeVersionSelector) { - function onSelectorSwitch(event) { - const option = event.target.selectedIndex; - const item = event.target.options[option]; - window.location.href = item.dataset.url; - } - - document.addEventListener("readthedocs-addons-data-ready", function (event) { - const config = event.detail.data(); - - const versionSwitch = document.querySelector( - "div.switch-menus > div.version-switch", - ); - if (themeVersionSelector) { - let versions = config.versions.active; - if (config.versions.current.hidden || config.versions.current.type === "external") { - versions.unshift(config.versions.current); - } - const versionSelect = ` - - `; - - versionSwitch.innerHTML = versionSelect; - versionSwitch.firstElementChild.addEventListener("change", onSelectorSwitch); - } - - const languageSwitch = document.querySelector( - "div.switch-menus > div.language-switch", - ); - - if (themeLanguageSelector) { - if (config.projects.translations.length) { - // Add the current language to the options on the selector - let languages = config.projects.translations.concat( - config.projects.current, - ); - languages = languages.sort((a, b) => - a.language.name.localeCompare(b.language.name), - ); - - const languageSelect = ` - - `; - - languageSwitch.innerHTML = languageSelect; - languageSwitch.firstElementChild.addEventListener("change", onSelectorSwitch); - } - else { - languageSwitch.remove(); - } - } - }); -} - -document.addEventListener("readthedocs-addons-data-ready", function (event) { - // Trigger the Read the Docs Addons Search modal when clicking on "Search docs" input from the topnav. - document - .querySelector("[role='search'] input") - .addEventListener("focusin", () => { - const event = new CustomEvent("readthedocs-search-show"); - document.dispatchEvent(event); - }); -}); \ No newline at end of file diff --git a/doc/_build/html/_static/language_data.js b/doc/_build/html/_static/language_data.js deleted file mode 100644 index c7fe6c6..0000000 --- a/doc/_build/html/_static/language_data.js +++ /dev/null @@ -1,192 +0,0 @@ -/* - * This script contains the language-specific data used by searchtools.js, - * namely the list of stopwords, stemmer, scorer and splitter. - */ - -var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; - - -/* Non-minified version is copied as a separate JS file, if available */ - -/** - * Porter Stemmer - */ -var Stemmer = function() { - - var step2list = { - ational: 'ate', - tional: 'tion', - enci: 'ence', - anci: 'ance', - izer: 'ize', - bli: 'ble', - alli: 'al', - entli: 'ent', - eli: 'e', - ousli: 'ous', - ization: 'ize', - ation: 'ate', - ator: 'ate', - alism: 'al', - iveness: 'ive', - fulness: 'ful', - ousness: 'ous', - aliti: 'al', - iviti: 'ive', - biliti: 'ble', - logi: 'log' - }; - - var step3list = { - icate: 'ic', - ative: '', - alize: 'al', - iciti: 'ic', - ical: 'ic', - ful: '', - ness: '' - }; - - var c = "[^aeiou]"; // consonant - var v = "[aeiouy]"; // vowel - var C = c + "[^aeiouy]*"; // consonant sequence - var V = v + "[aeiou]*"; // vowel sequence - - var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 - var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 - var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 - var s_v = "^(" + C + ")?" + v; // vowel in stem - - this.stemWord = function (w) { - var stem; - var suffix; - var firstch; - var origword = w; - - if (w.length < 3) - return w; - - var re; - var re2; - var re3; - var re4; - - firstch = w.substr(0,1); - if (firstch == "y") - w = firstch.toUpperCase() + w.substr(1); - - // Step 1a - re = /^(.+?)(ss|i)es$/; - re2 = /^(.+?)([^s])s$/; - - if (re.test(w)) - w = w.replace(re,"$1$2"); - else if (re2.test(w)) - w = w.replace(re2,"$1$2"); - - // Step 1b - re = /^(.+?)eed$/; - re2 = /^(.+?)(ed|ing)$/; - if (re.test(w)) { - var fp = re.exec(w); - re = new RegExp(mgr0); - if (re.test(fp[1])) { - re = /.$/; - w = w.replace(re,""); - } - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1]; - re2 = new RegExp(s_v); - if (re2.test(stem)) { - w = stem; - re2 = /(at|bl|iz)$/; - re3 = new RegExp("([^aeiouylsz])\\1$"); - re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re2.test(w)) - w = w + "e"; - else if (re3.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - else if (re4.test(w)) - w = w + "e"; - } - } - - // Step 1c - re = /^(.+?)y$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(s_v); - if (re.test(stem)) - w = stem + "i"; - } - - // Step 2 - re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step2list[suffix]; - } - - // Step 3 - re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step3list[suffix]; - } - - // Step 4 - re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; - re2 = /^(.+?)(s|t)(ion)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - if (re.test(stem)) - w = stem; - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1] + fp[2]; - re2 = new RegExp(mgr1); - if (re2.test(stem)) - w = stem; - } - - // Step 5 - re = /^(.+?)e$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - re2 = new RegExp(meq1); - re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) - w = stem; - } - re = /ll$/; - re2 = new RegExp(mgr1); - if (re.test(w) && re2.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - - // and turn initial Y back to y - if (firstch == "y") - w = firstch.toLowerCase() + w.substr(1); - return w; - } -} - diff --git a/doc/_build/html/_static/minus.png b/doc/_build/html/_static/minus.png deleted file mode 100644 index d96755f..0000000 Binary files a/doc/_build/html/_static/minus.png and /dev/null differ diff --git a/doc/_build/html/_static/plus.png b/doc/_build/html/_static/plus.png deleted file mode 100644 index 7107cec..0000000 Binary files a/doc/_build/html/_static/plus.png and /dev/null differ diff --git a/doc/_build/html/_static/pygments.css b/doc/_build/html/_static/pygments.css deleted file mode 100644 index 84ab303..0000000 --- a/doc/_build/html/_static/pygments.css +++ /dev/null @@ -1,75 +0,0 @@ -pre { line-height: 125%; } -td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -.highlight .hll { background-color: #ffffcc } -.highlight { background: #f8f8f8; } -.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ -.highlight .err { border: 1px solid #FF0000 } /* Error */ -.highlight .k { color: #008000; font-weight: bold } /* Keyword */ -.highlight .o { color: #666666 } /* Operator */ -.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ -.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #9C6500 } /* Comment.Preproc */ -.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ -.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ -.highlight .gd { color: #A00000 } /* Generic.Deleted */ -.highlight .ge { font-style: italic } /* Generic.Emph */ -.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ -.highlight .gr { color: #E40000 } /* Generic.Error */ -.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #008400 } /* Generic.Inserted */ -.highlight .go { color: #717171 } /* Generic.Output */ -.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ -.highlight .gs { font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #0044DD } /* Generic.Traceback */ -.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #008000 } /* Keyword.Pseudo */ -.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #B00040 } /* Keyword.Type */ -.highlight .m { color: #666666 } /* Literal.Number */ -.highlight .s { color: #BA2121 } /* Literal.String */ -.highlight .na { color: #687822 } /* Name.Attribute */ -.highlight .nb { color: #008000 } /* Name.Builtin */ -.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ -.highlight .no { color: #880000 } /* Name.Constant */ -.highlight .nd { color: #AA22FF } /* Name.Decorator */ -.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ -.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #0000FF } /* Name.Function */ -.highlight .nl { color: #767600 } /* Name.Label */ -.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ -.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #19177C } /* Name.Variable */ -.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ -.highlight .w { color: #bbbbbb } /* Text.Whitespace */ -.highlight .mb { color: #666666 } /* Literal.Number.Bin */ -.highlight .mf { color: #666666 } /* Literal.Number.Float */ -.highlight .mh { color: #666666 } /* Literal.Number.Hex */ -.highlight .mi { color: #666666 } /* Literal.Number.Integer */ -.highlight .mo { color: #666666 } /* Literal.Number.Oct */ -.highlight .sa { color: #BA2121 } /* Literal.String.Affix */ -.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ -.highlight .sc { color: #BA2121 } /* Literal.String.Char */ -.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ -.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ -.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ -.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ -.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ -.highlight .sx { color: #008000 } /* Literal.String.Other */ -.highlight .sr { color: #A45A77 } /* Literal.String.Regex */ -.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ -.highlight .ss { color: #19177C } /* Literal.String.Symbol */ -.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ -.highlight .fm { color: #0000FF } /* Name.Function.Magic */ -.highlight .vc { color: #19177C } /* Name.Variable.Class */ -.highlight .vg { color: #19177C } /* Name.Variable.Global */ -.highlight .vi { color: #19177C } /* Name.Variable.Instance */ -.highlight .vm { color: #19177C } /* Name.Variable.Magic */ -.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/doc/_build/html/_static/searchtools.js b/doc/_build/html/_static/searchtools.js deleted file mode 100644 index 2c774d1..0000000 --- a/doc/_build/html/_static/searchtools.js +++ /dev/null @@ -1,632 +0,0 @@ -/* - * Sphinx JavaScript utilities for the full-text search. - */ -"use strict"; - -/** - * Simple result scoring code. - */ -if (typeof Scorer === "undefined") { - var Scorer = { - // Implement the following function to further tweak the score for each result - // The function takes a result array [docname, title, anchor, descr, score, filename] - // and returns the new score. - /* - score: result => { - const [docname, title, anchor, descr, score, filename, kind] = result - return score - }, - */ - - // query matches the full name of an object - objNameMatch: 11, - // or matches in the last dotted part of the object name - objPartialMatch: 6, - // Additive scores depending on the priority of the object - objPrio: { - 0: 15, // used to be importantResults - 1: 5, // used to be objectResults - 2: -5, // used to be unimportantResults - }, - // Used when the priority is not in the mapping. - objPrioDefault: 0, - - // query found in title - title: 15, - partialTitle: 7, - // query found in terms - term: 5, - partialTerm: 2, - }; -} - -// Global search result kind enum, used by themes to style search results. -class SearchResultKind { - static get index() { return "index"; } - static get object() { return "object"; } - static get text() { return "text"; } - static get title() { return "title"; } -} - -const _removeChildren = (element) => { - while (element && element.lastChild) element.removeChild(element.lastChild); -}; - -/** - * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping - */ -const _escapeRegExp = (string) => - string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string - -const _displayItem = (item, searchTerms, highlightTerms) => { - const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; - const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; - const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; - const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; - const contentRoot = document.documentElement.dataset.content_root; - - const [docName, title, anchor, descr, score, _filename, kind] = item; - - let listItem = document.createElement("li"); - // Add a class representing the item's type: - // can be used by a theme's CSS selector for styling - // See SearchResultKind for the class names. - listItem.classList.add(`kind-${kind}`); - let requestUrl; - let linkUrl; - if (docBuilder === "dirhtml") { - // dirhtml builder - let dirname = docName + "/"; - if (dirname.match(/\/index\/$/)) - dirname = dirname.substring(0, dirname.length - 6); - else if (dirname === "index/") dirname = ""; - requestUrl = contentRoot + dirname; - linkUrl = requestUrl; - } else { - // normal html builders - requestUrl = contentRoot + docName + docFileSuffix; - linkUrl = docName + docLinkSuffix; - } - let linkEl = listItem.appendChild(document.createElement("a")); - linkEl.href = linkUrl + anchor; - linkEl.dataset.score = score; - linkEl.innerHTML = title; - if (descr) { - listItem.appendChild(document.createElement("span")).innerHTML = - " (" + descr + ")"; - // highlight search terms in the description - if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js - highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); - } - else if (showSearchSummary) - fetch(requestUrl) - .then((responseData) => responseData.text()) - .then((data) => { - if (data) - listItem.appendChild( - Search.makeSearchSummary(data, searchTerms, anchor) - ); - // highlight search terms in the summary - if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js - highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); - }); - Search.output.appendChild(listItem); -}; -const _finishSearch = (resultCount) => { - Search.stopPulse(); - Search.title.innerText = _("Search Results"); - if (!resultCount) - Search.status.innerText = Documentation.gettext( - "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." - ); - else - Search.status.innerText = Documentation.ngettext( - "Search finished, found one page matching the search query.", - "Search finished, found ${resultCount} pages matching the search query.", - resultCount, - ).replace('${resultCount}', resultCount); -}; -const _displayNextItem = ( - results, - resultCount, - searchTerms, - highlightTerms, -) => { - // results left, load the summary and display it - // this is intended to be dynamic (don't sub resultsCount) - if (results.length) { - _displayItem(results.pop(), searchTerms, highlightTerms); - setTimeout( - () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), - 5 - ); - } - // search finished, update title and status message - else _finishSearch(resultCount); -}; -// Helper function used by query() to order search results. -// Each input is an array of [docname, title, anchor, descr, score, filename, kind]. -// Order the results by score (in opposite order of appearance, since the -// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. -const _orderResultsByScoreThenName = (a, b) => { - const leftScore = a[4]; - const rightScore = b[4]; - if (leftScore === rightScore) { - // same score: sort alphabetically - const leftTitle = a[1].toLowerCase(); - const rightTitle = b[1].toLowerCase(); - if (leftTitle === rightTitle) return 0; - return leftTitle > rightTitle ? -1 : 1; // inverted is intentional - } - return leftScore > rightScore ? 1 : -1; -}; - -/** - * Default splitQuery function. Can be overridden in ``sphinx.search`` with a - * custom function per language. - * - * The regular expression works by splitting the string on consecutive characters - * that are not Unicode letters, numbers, underscores, or emoji characters. - * This is the same as ``\W+`` in Python, preserving the surrogate pair area. - */ -if (typeof splitQuery === "undefined") { - var splitQuery = (query) => query - .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) - .filter(term => term) // remove remaining empty strings -} - -/** - * Search Module - */ -const Search = { - _index: null, - _queued_query: null, - _pulse_status: -1, - - htmlToText: (htmlString, anchor) => { - const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); - for (const removalQuery of [".headerlink", "script", "style"]) { - htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() }); - } - if (anchor) { - const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`); - if (anchorContent) return anchorContent.textContent; - - console.warn( - `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.` - ); - } - - // if anchor not specified or not found, fall back to main content - const docContent = htmlElement.querySelector('[role="main"]'); - if (docContent) return docContent.textContent; - - console.warn( - "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template." - ); - return ""; - }, - - init: () => { - const query = new URLSearchParams(window.location.search).get("q"); - document - .querySelectorAll('input[name="q"]') - .forEach((el) => (el.value = query)); - if (query) Search.performSearch(query); - }, - - loadIndex: (url) => - (document.body.appendChild(document.createElement("script")).src = url), - - setIndex: (index) => { - Search._index = index; - if (Search._queued_query !== null) { - const query = Search._queued_query; - Search._queued_query = null; - Search.query(query); - } - }, - - hasIndex: () => Search._index !== null, - - deferQuery: (query) => (Search._queued_query = query), - - stopPulse: () => (Search._pulse_status = -1), - - startPulse: () => { - if (Search._pulse_status >= 0) return; - - const pulse = () => { - Search._pulse_status = (Search._pulse_status + 1) % 4; - Search.dots.innerText = ".".repeat(Search._pulse_status); - if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); - }; - pulse(); - }, - - /** - * perform a search for something (or wait until index is loaded) - */ - performSearch: (query) => { - // create the required interface elements - const searchText = document.createElement("h2"); - searchText.textContent = _("Searching"); - const searchSummary = document.createElement("p"); - searchSummary.classList.add("search-summary"); - searchSummary.innerText = ""; - const searchList = document.createElement("ul"); - searchList.setAttribute("role", "list"); - searchList.classList.add("search"); - - const out = document.getElementById("search-results"); - Search.title = out.appendChild(searchText); - Search.dots = Search.title.appendChild(document.createElement("span")); - Search.status = out.appendChild(searchSummary); - Search.output = out.appendChild(searchList); - - const searchProgress = document.getElementById("search-progress"); - // Some themes don't use the search progress node - if (searchProgress) { - searchProgress.innerText = _("Preparing search..."); - } - Search.startPulse(); - - // index already loaded, the browser was quick! - if (Search.hasIndex()) Search.query(query); - else Search.deferQuery(query); - }, - - _parseQuery: (query) => { - // stem the search terms and add them to the correct list - const stemmer = new Stemmer(); - const searchTerms = new Set(); - const excludedTerms = new Set(); - const highlightTerms = new Set(); - const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); - splitQuery(query.trim()).forEach((queryTerm) => { - const queryTermLower = queryTerm.toLowerCase(); - - // maybe skip this "word" - // stopwords array is from language_data.js - if ( - stopwords.indexOf(queryTermLower) !== -1 || - queryTerm.match(/^\d+$/) - ) - return; - - // stem the word - let word = stemmer.stemWord(queryTermLower); - // select the correct list - if (word[0] === "-") excludedTerms.add(word.substr(1)); - else { - searchTerms.add(word); - highlightTerms.add(queryTermLower); - } - }); - - if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js - localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) - } - - // console.debug("SEARCH: searching for:"); - // console.info("required: ", [...searchTerms]); - // console.info("excluded: ", [...excludedTerms]); - - return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; - }, - - /** - * execute search (requires search index to be loaded) - */ - _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => { - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const titles = Search._index.titles; - const allTitles = Search._index.alltitles; - const indexEntries = Search._index.indexentries; - - // Collect multiple result groups to be sorted separately and then ordered. - // Each is an array of [docname, title, anchor, descr, score, filename, kind]. - const normalResults = []; - const nonMainIndexResults = []; - - _removeChildren(document.getElementById("search-progress")); - - const queryLower = query.toLowerCase().trim(); - for (const [title, foundTitles] of Object.entries(allTitles)) { - if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { - for (const [file, id] of foundTitles) { - const score = Math.round(Scorer.title * queryLower.length / title.length); - const boost = titles[file] === title ? 1 : 0; // add a boost for document titles - normalResults.push([ - docNames[file], - titles[file] !== title ? `${titles[file]} > ${title}` : title, - id !== null ? "#" + id : "", - null, - score + boost, - filenames[file], - SearchResultKind.title, - ]); - } - } - } - - // search for explicit entries in index directives - for (const [entry, foundEntries] of Object.entries(indexEntries)) { - if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { - for (const [file, id, isMain] of foundEntries) { - const score = Math.round(100 * queryLower.length / entry.length); - const result = [ - docNames[file], - titles[file], - id ? "#" + id : "", - null, - score, - filenames[file], - SearchResultKind.index, - ]; - if (isMain) { - normalResults.push(result); - } else { - nonMainIndexResults.push(result); - } - } - } - } - - // lookup as object - objectTerms.forEach((term) => - normalResults.push(...Search.performObjectSearch(term, objectTerms)) - ); - - // lookup as search terms in fulltext - normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms)); - - // let the scorer override scores with a custom scoring function - if (Scorer.score) { - normalResults.forEach((item) => (item[4] = Scorer.score(item))); - nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); - } - - // Sort each group of results by score and then alphabetically by name. - normalResults.sort(_orderResultsByScoreThenName); - nonMainIndexResults.sort(_orderResultsByScoreThenName); - - // Combine the result groups in (reverse) order. - // Non-main index entries are typically arbitrary cross-references, - // so display them after other results. - let results = [...nonMainIndexResults, ...normalResults]; - - // remove duplicate search results - // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept - let seen = new Set(); - results = results.reverse().reduce((acc, result) => { - let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); - if (!seen.has(resultStr)) { - acc.push(result); - seen.add(resultStr); - } - return acc; - }, []); - - return results.reverse(); - }, - - query: (query) => { - const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query); - const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms); - - // for debugging - //Search.lastresults = results.slice(); // a copy - // console.info("search results:", Search.lastresults); - - // print the results - _displayNextItem(results, results.length, searchTerms, highlightTerms); - }, - - /** - * search for object names - */ - performObjectSearch: (object, objectTerms) => { - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const objects = Search._index.objects; - const objNames = Search._index.objnames; - const titles = Search._index.titles; - - const results = []; - - const objectSearchCallback = (prefix, match) => { - const name = match[4] - const fullname = (prefix ? prefix + "." : "") + name; - const fullnameLower = fullname.toLowerCase(); - if (fullnameLower.indexOf(object) < 0) return; - - let score = 0; - const parts = fullnameLower.split("."); - - // check for different match types: exact matches of full name or - // "last name" (i.e. last dotted part) - if (fullnameLower === object || parts.slice(-1)[0] === object) - score += Scorer.objNameMatch; - else if (parts.slice(-1)[0].indexOf(object) > -1) - score += Scorer.objPartialMatch; // matches in last name - - const objName = objNames[match[1]][2]; - const title = titles[match[0]]; - - // If more than one term searched for, we require other words to be - // found in the name/title/description - const otherTerms = new Set(objectTerms); - otherTerms.delete(object); - if (otherTerms.size > 0) { - const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); - if ( - [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) - ) - return; - } - - let anchor = match[3]; - if (anchor === "") anchor = fullname; - else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; - - const descr = objName + _(", in ") + title; - - // add custom score for some objects according to scorer - if (Scorer.objPrio.hasOwnProperty(match[2])) - score += Scorer.objPrio[match[2]]; - else score += Scorer.objPrioDefault; - - results.push([ - docNames[match[0]], - fullname, - "#" + anchor, - descr, - score, - filenames[match[0]], - SearchResultKind.object, - ]); - }; - Object.keys(objects).forEach((prefix) => - objects[prefix].forEach((array) => - objectSearchCallback(prefix, array) - ) - ); - return results; - }, - - /** - * search for full-text terms in the index - */ - performTermsSearch: (searchTerms, excludedTerms) => { - // prepare search - const terms = Search._index.terms; - const titleTerms = Search._index.titleterms; - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const titles = Search._index.titles; - - const scoreMap = new Map(); - const fileMap = new Map(); - - // perform the search on the required terms - searchTerms.forEach((word) => { - const files = []; - const arr = [ - { files: terms[word], score: Scorer.term }, - { files: titleTerms[word], score: Scorer.title }, - ]; - // add support for partial matches - if (word.length > 2) { - const escapedWord = _escapeRegExp(word); - if (!terms.hasOwnProperty(word)) { - Object.keys(terms).forEach((term) => { - if (term.match(escapedWord)) - arr.push({ files: terms[term], score: Scorer.partialTerm }); - }); - } - if (!titleTerms.hasOwnProperty(word)) { - Object.keys(titleTerms).forEach((term) => { - if (term.match(escapedWord)) - arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); - }); - } - } - - // no match but word was a required one - if (arr.every((record) => record.files === undefined)) return; - - // found search word in contents - arr.forEach((record) => { - if (record.files === undefined) return; - - let recordFiles = record.files; - if (recordFiles.length === undefined) recordFiles = [recordFiles]; - files.push(...recordFiles); - - // set score for the word in each file - recordFiles.forEach((file) => { - if (!scoreMap.has(file)) scoreMap.set(file, {}); - scoreMap.get(file)[word] = record.score; - }); - }); - - // create the mapping - files.forEach((file) => { - if (!fileMap.has(file)) fileMap.set(file, [word]); - else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word); - }); - }); - - // now check if the files don't contain excluded terms - const results = []; - for (const [file, wordList] of fileMap) { - // check if all requirements are matched - - // as search terms with length < 3 are discarded - const filteredTermCount = [...searchTerms].filter( - (term) => term.length > 2 - ).length; - if ( - wordList.length !== searchTerms.size && - wordList.length !== filteredTermCount - ) - continue; - - // ensure that none of the excluded terms is in the search result - if ( - [...excludedTerms].some( - (term) => - terms[term] === file || - titleTerms[term] === file || - (terms[term] || []).includes(file) || - (titleTerms[term] || []).includes(file) - ) - ) - break; - - // select one (max) score for the file. - const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); - // add result to the result list - results.push([ - docNames[file], - titles[file], - "", - null, - score, - filenames[file], - SearchResultKind.text, - ]); - } - return results; - }, - - /** - * helper function to return a node containing the - * search summary for a given text. keywords is a list - * of stemmed words. - */ - makeSearchSummary: (htmlText, keywords, anchor) => { - const text = Search.htmlToText(htmlText, anchor); - if (text === "") return null; - - const textLower = text.toLowerCase(); - const actualStartPosition = [...keywords] - .map((k) => textLower.indexOf(k.toLowerCase())) - .filter((i) => i > -1) - .slice(-1)[0]; - const startWithContext = Math.max(actualStartPosition - 120, 0); - - const top = startWithContext === 0 ? "" : "..."; - const tail = startWithContext + 240 < text.length ? "..." : ""; - - let summary = document.createElement("p"); - summary.classList.add("context"); - summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; - - return summary; - }, -}; - -_ready(Search.init); diff --git a/doc/_build/html/_static/sphinx_highlight.js b/doc/_build/html/_static/sphinx_highlight.js deleted file mode 100644 index 8a96c69..0000000 --- a/doc/_build/html/_static/sphinx_highlight.js +++ /dev/null @@ -1,154 +0,0 @@ -/* Highlighting utilities for Sphinx HTML documentation. */ -"use strict"; - -const SPHINX_HIGHLIGHT_ENABLED = true - -/** - * highlight a given string on a node by wrapping it in - * span elements with the given class name. - */ -const _highlight = (node, addItems, text, className) => { - if (node.nodeType === Node.TEXT_NODE) { - const val = node.nodeValue; - const parent = node.parentNode; - const pos = val.toLowerCase().indexOf(text); - if ( - pos >= 0 && - !parent.classList.contains(className) && - !parent.classList.contains("nohighlight") - ) { - let span; - - const closestNode = parent.closest("body, svg, foreignObject"); - const isInSVG = closestNode && closestNode.matches("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.classList.add(className); - } - - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - const rest = document.createTextNode(val.substr(pos + text.length)); - parent.insertBefore( - span, - parent.insertBefore( - rest, - node.nextSibling - ) - ); - node.nodeValue = val.substr(0, pos); - /* There may be more occurrences of search term in this node. So call this - * function recursively on the remaining fragment. - */ - _highlight(rest, addItems, text, className); - - if (isInSVG) { - const rect = document.createElementNS( - "http://www.w3.org/2000/svg", - "rect" - ); - const bbox = parent.getBBox(); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute("class", className); - addItems.push({ parent: parent, target: rect }); - } - } - } else if (node.matches && !node.matches("button, select, textarea")) { - node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); - } -}; -const _highlightText = (thisNode, text, className) => { - let addItems = []; - _highlight(thisNode, addItems, text, className); - addItems.forEach((obj) => - obj.parent.insertAdjacentElement("beforebegin", obj.target) - ); -}; - -/** - * Small JavaScript module for the documentation. - */ -const SphinxHighlight = { - - /** - * highlight the search words provided in localstorage in the text - */ - highlightSearchWords: () => { - if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight - - // get and clear terms from localstorage - const url = new URL(window.location); - const highlight = - localStorage.getItem("sphinx_highlight_terms") - || url.searchParams.get("highlight") - || ""; - localStorage.removeItem("sphinx_highlight_terms") - url.searchParams.delete("highlight"); - window.history.replaceState({}, "", url); - - // get individual terms from highlight string - const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); - if (terms.length === 0) return; // nothing to do - - // There should never be more than one element matching "div.body" - const divBody = document.querySelectorAll("div.body"); - const body = divBody.length ? divBody[0] : document.querySelector("body"); - window.setTimeout(() => { - terms.forEach((term) => _highlightText(body, term, "highlighted")); - }, 10); - - const searchBox = document.getElementById("searchbox"); - if (searchBox === null) return; - searchBox.appendChild( - document - .createRange() - .createContextualFragment( - '" - ) - ); - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords: () => { - document - .querySelectorAll("#searchbox .highlight-link") - .forEach((el) => el.remove()); - document - .querySelectorAll("span.highlighted") - .forEach((el) => el.classList.remove("highlighted")); - localStorage.removeItem("sphinx_highlight_terms") - }, - - initEscapeListener: () => { - // only install a listener if it is really needed - if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; - - document.addEventListener("keydown", (event) => { - // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; - // bail with special keys - if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; - if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { - SphinxHighlight.hideSearchWords(); - event.preventDefault(); - } - }); - }, -}; - -_ready(() => { - /* Do not call highlightSearchWords() when we are on the search page. - * It will highlight words from the *previous* search query. - */ - if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); - SphinxHighlight.initEscapeListener(); -}); diff --git a/doc/_build/html/genindex.html b/doc/_build/html/genindex.html deleted file mode 100644 index 858fe71..0000000 --- a/doc/_build/html/genindex.html +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - Index — pyTorchAutoForge 0.1 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- - -

Index

- -
- -
- - -
-
-
- -
- -
-

© Copyright 2024, Pietro Califano.

-
- - Built with Sphinx using a - theme - provided by Read the Docs. - - -
-
-
-
-
- - - - \ No newline at end of file diff --git a/doc/_build/html/index.html b/doc/_build/html/index.html deleted file mode 100644 index 409eb8f..0000000 --- a/doc/_build/html/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - - - Welcome to pyTorchAutoForge’s documentation! — pyTorchAutoForge 0.1 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

Welcome to pyTorchAutoForge’s documentation!

-
-
-
-
-

Indices and tables

- -
- - -
-
-
- -
- -
-

© Copyright 2024, Pietro Califano.

-
- - Built with Sphinx using a - theme - provided by Read the Docs. - - -
-
-
-
-
- - - - \ No newline at end of file diff --git a/doc/_build/html/objects.inv b/doc/_build/html/objects.inv deleted file mode 100644 index 5ecc6fa..0000000 Binary files a/doc/_build/html/objects.inv and /dev/null differ diff --git a/doc/_build/html/py-modindex.html b/doc/_build/html/py-modindex.html deleted file mode 100644 index 51617da..0000000 --- a/doc/_build/html/py-modindex.html +++ /dev/null @@ -1,155 +0,0 @@ - - - - - - Python Module Index — pyTorchAutoForge 0.1 documentation - - - - - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- - -

Python Module Index

- -
- p -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
- p
- pyTorchAutoForge -
    - pyTorchAutoForge.api.matlab.TorchModelMATLABwrapper -
    - pyTorchAutoForge.api.torch.torchModulesIO -
    - pyTorchAutoForge.datasets -
    - pyTorchAutoForge.hparamsOptim -
    - pyTorchAutoForge.modelBuilding.ModelAutoBuilder -
    - pyTorchAutoForge.optimization -
    - pyTorchAutoForge.utils -
- - -
-
-
- -
- -
-

© Copyright 2024, Pietro Califano.

-
- - Built with Sphinx using a - theme - provided by Read the Docs. - - -
-
-
-
-
- - - - \ No newline at end of file diff --git a/doc/_build/html/search.html b/doc/_build/html/search.html deleted file mode 100644 index a44eeb4..0000000 --- a/doc/_build/html/search.html +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - Search — pyTorchAutoForge 0.1 documentation - - - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- - - - -
- -
- -
-
-
- -
- -
-

© Copyright 2024, Pietro Califano.

-
- - Built with Sphinx using a - theme - provided by Read the Docs. - - -
-
-
-
-
- - - - - - - - - \ No newline at end of file diff --git a/doc/_build/html/searchindex.js b/doc/_build/html/searchindex.js deleted file mode 100644 index dc9dad2..0000000 --- a/doc/_build/html/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({"alltitles": {"Indices and tables": [[0, "indices-and-tables"]], "Welcome to pyTorchAutoForge\u2019s documentation!": [[0, null]]}, "docnames": ["index"], "envversion": {"sphinx": 64, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"index": 0, "modul": 0, "page": 0, "search": 0}, "titles": ["Welcome to pyTorchAutoForge\u2019s documentation!"], "titleterms": {"": 0, "document": 0, "indic": 0, "pytorchautoforg": 0, "tabl": 0, "welcom": 0}}) \ No newline at end of file diff --git a/doc/_static/ptaf_doc_theme.css b/doc/_static/ptaf_doc_theme.css deleted file mode 100644 index dc0af92..0000000 --- a/doc/_static/ptaf_doc_theme.css +++ /dev/null @@ -1,59 +0,0 @@ -/* Increase the width of the content area */ -.wy-nav-content { - max-width: 100%; - /* Set to your preferred width */ -} - -.wy-side-nav-search { - width: 100%; - overflow: visible; - /* Fixes the cutoff issue */ -} - -/* Optionally adjust the width of the side navigation */ -/* -.wy-nav-side { - width: 250px; - Set a custom width for the side index -} -*/ - -/* Adjust the input field of the search bar */ -.wy-side-nav-search input { - width: 90%; - /* Adjust the width if necessary */ -} - -/* Apply a dark background with light text */ -body { - background-color: #121212; - color: #e0e0e0; -} - -/* Change text color */ -p { - color: #256103; -} - -/* Customize sidebar background and link colors */ -.wy-nav-side { - background-color: #024463; -} - -.wy-nav-side a { - color: #026694; -} - -/* Modify navigation bar */ -.wy-nav-top { - background-color: #d46000; -} - -/* Customize main content links */ -a { - color: #72049e; -} - -a:hover { - color: #3700b3; -} \ No newline at end of file diff --git a/doc/api/index.md b/doc/api/index.md new file mode 100644 index 0000000..cc73007 --- /dev/null +++ b/doc/api/index.md @@ -0,0 +1,35 @@ +# API Reference + +Public API pages are generated from source with `sphinx-autoapi`. +Google-style docstrings are parsed through `sphinx.ext.napoleon`. + +## Main Entry Points + +- {mod}`pyTorchAutoForge.model_building`: neural-network blocks, backbones, model assembly, and model mutation utilities. +- {mod}`pyTorchAutoForge.datasets`: dataset containers, labels, image augmentations, and vector error models. +- {mod}`pyTorchAutoForge.optimization`: training-manager utilities and loss functions. +- {mod}`pyTorchAutoForge.evaluation`: model evaluation, plotting, profiling, and explainability entry points. +- {mod}`pyTorchAutoForge.evaluation.explainability`: typed SHAP/Captum explainer subsystem. +- {mod}`pyTorchAutoForge.api`: ONNX, TensorRT, Torch, MATLAB, TCP, runtime, and MLflow integration surfaces. +- {mod}`pyTorchAutoForge.monitoring`: run logging helpers. +- {mod}`pyTorchAutoForge.utils`: device, conversion, timing, and general utility helpers. + +## Explainability Shortcuts + +- {class}`pyTorchAutoForge.evaluation.explainability.ModelExplainer` +- {class}`pyTorchAutoForge.evaluation.explainability.ExplainerConfig` +- {class}`pyTorchAutoForge.evaluation.explainability.ExplainerRequest` +- {class}`pyTorchAutoForge.evaluation.explainability.ShapExplainerConfig` +- {class}`pyTorchAutoForge.evaluation.explainability.CaptumIntegratedGradientsConfig` +- {class}`pyTorchAutoForge.evaluation.explainability.ExplainerResult` + +See [](../model_explainer.md) for runnable usage. + +## Generated API Tree + +```{toctree} +:maxdepth: 2 + +module_index +generated/pyTorchAutoForge/index +``` diff --git a/doc/api/module_index.md b/doc/api/module_index.md new file mode 100644 index 0000000..a5abb20 --- /dev/null +++ b/doc/api/module_index.md @@ -0,0 +1,115 @@ +# Module Index + +This page links generated module pages in the current public documentation +surface. AutoAPI still owns the generated pages; this page is only a curated +index for scanning. + +## Package Root + +- {mod}`pyTorchAutoForge` + +## Runtime And External APIs + +- {mod}`pyTorchAutoForge.api` +- {mod}`pyTorchAutoForge.api.matlab` +- {mod}`pyTorchAutoForge.api.matlab.TorchModelMATLABwrapper` +- {mod}`pyTorchAutoForge.api.mlflow` +- {mod}`pyTorchAutoForge.api.mlflow.mlflow_api` +- {mod}`pyTorchAutoForge.api.onnx` +- {mod}`pyTorchAutoForge.api.onnx.ModelHandlerONNx` +- {mod}`pyTorchAutoForge.api.onnx.OnnxRuntimeApi` +- {mod}`pyTorchAutoForge.api.runtime` +- {mod}`pyTorchAutoForge.api.runtime.ModelRuntimeApi` +- {mod}`pyTorchAutoForge.api.tcp` +- {mod}`pyTorchAutoForge.api.tcp.tcpServerPy` +- {mod}`pyTorchAutoForge.api.tcp.tcp_socket_server` +- {mod}`pyTorchAutoForge.api.tcp.tcp_torchModel_eval` +- {mod}`pyTorchAutoForge.api.tensorrt` +- {mod}`pyTorchAutoForge.api.tensorrt.TRTengineExporter` +- {mod}`pyTorchAutoForge.api.tensorrt.TensorrtRuntimeApi` +- {mod}`pyTorchAutoForge.api.torch` +- {mod}`pyTorchAutoForge.api.torch.torchModulesIO` + +## Datasets + +- {mod}`pyTorchAutoForge.datasets` +- {mod}`pyTorchAutoForge.datasets.AugmentationsBaseClasses` +- {mod}`pyTorchAutoForge.datasets.AugmentationsManager` +- {mod}`pyTorchAutoForge.datasets.DataloaderIndex` +- {mod}`pyTorchAutoForge.datasets.DatasetClasses` +- {mod}`pyTorchAutoForge.datasets.ImagesAugmentation` +- {mod}`pyTorchAutoForge.datasets.LabelsClasses` +- {mod}`pyTorchAutoForge.datasets.auxiliary_functions` +- {mod}`pyTorchAutoForge.datasets.noise_models` +- {mod}`pyTorchAutoForge.datasets.noise_models.FrameCameraErrorModels` +- {mod}`pyTorchAutoForge.datasets.noise_models.GeometricAugs` +- {mod}`pyTorchAutoForge.datasets.noise_models.IntensityAugs` +- {mod}`pyTorchAutoForge.datasets.noise_models.NoiseErrorsAugs` +- {mod}`pyTorchAutoForge.datasets.noise_models.OpticsErrorsAugs` +- {mod}`pyTorchAutoForge.datasets.vector_error_models` +- {mod}`pyTorchAutoForge.datasets.vector_error_models.VectorErrorsBaseClasses` + +## Evaluation And Explainability + +- {mod}`pyTorchAutoForge.evaluation` +- {mod}`pyTorchAutoForge.evaluation.ModelEvaluator` +- {mod}`pyTorchAutoForge.evaluation.ModelExplainer` +- {mod}`pyTorchAutoForge.evaluation.ModelProfiler` +- {mod}`pyTorchAutoForge.evaluation.ResultsPlotter` +- {mod}`pyTorchAutoForge.evaluation.explainability` +- {mod}`pyTorchAutoForge.evaluation.explainability.adapters` +- {mod}`pyTorchAutoForge.evaluation.explainability.adapters.captum_adapter` +- {mod}`pyTorchAutoForge.evaluation.explainability.adapters.shap_adapter` +- {mod}`pyTorchAutoForge.evaluation.explainability.configs` +- {mod}`pyTorchAutoForge.evaluation.explainability.explainer` +- {mod}`pyTorchAutoForge.evaluation.explainability.method_configs` +- {mod}`pyTorchAutoForge.evaluation.explainability.predictors` +- {mod}`pyTorchAutoForge.evaluation.explainability.registry` +- {mod}`pyTorchAutoForge.evaluation.explainability.results` +- {mod}`pyTorchAutoForge.evaluation.explainability.schemas` +- {mod}`pyTorchAutoForge.evaluation.explainability.targets` + +## Model Building + +- {mod}`pyTorchAutoForge.model_building` +- {mod}`pyTorchAutoForge.model_building.ModelAssembler` +- {mod}`pyTorchAutoForge.model_building.ModelAutoBuilder` +- {mod}`pyTorchAutoForge.model_building.ModelMutator` +- {mod}`pyTorchAutoForge.model_building.backbones` +- {mod}`pyTorchAutoForge.model_building.backbones.base_backbones` +- {mod}`pyTorchAutoForge.model_building.backbones.efficient_net` +- {mod}`pyTorchAutoForge.model_building.backbones.image_processing_operators` +- {mod}`pyTorchAutoForge.model_building.backbones.input_adapters` +- {mod}`pyTorchAutoForge.model_building.backbones.spatial_features_operators` +- {mod}`pyTorchAutoForge.model_building.convolutionalBlocks` +- {mod}`pyTorchAutoForge.model_building.fullyConnectedBlocks` +- {mod}`pyTorchAutoForge.model_building.modelBuildingBlocks` +- {mod}`pyTorchAutoForge.model_building.poolingBlocks` + +## Optimization And Experiments + +- {mod}`pyTorchAutoForge.optimization` +- {mod}`pyTorchAutoForge.optimization.ModelTrainingManager` +- {mod}`pyTorchAutoForge.optimization.lossFunctionsClasses` +- {mod}`pyTorchAutoForge.hparams_optim` +- {mod}`pyTorchAutoForge.hparams_optim.ModelHparamsOptimizer` +- {mod}`pyTorchAutoForge.hparams_optim.OptunaStudyAnalyzer` +- {mod}`pyTorchAutoForge.hparams_optim.optuna_auxiliary` +- {mod}`pyTorchAutoForge.hparams_optim.seedNewOptunaStudy` +- {mod}`pyTorchAutoForge.monitoring` +- {mod}`pyTorchAutoForge.monitoring.run_loggers` + +## Setup And Utilities + +- {mod}`pyTorchAutoForge.setup` +- {mod}`pyTorchAutoForge.setup.AutoForgeInit` +- {mod}`pyTorchAutoForge.setup.BaseConfigClass` +- {mod}`pyTorchAutoForge.utils` +- {mod}`pyTorchAutoForge.utils.DeviceManager` +- {mod}`pyTorchAutoForge.utils.LossLandscapeVisualizer` +- {mod}`pyTorchAutoForge.utils.argument_parsers` +- {mod}`pyTorchAutoForge.utils.context_management` +- {mod}`pyTorchAutoForge.utils.conversion_utils` +- {mod}`pyTorchAutoForge.utils.rename_images` +- {mod}`pyTorchAutoForge.utils.timing_utils` +- {mod}`pyTorchAutoForge.utils.utils` diff --git a/doc/_static/ptaf_logo_small.jpg b/doc/assets/ptaf_logo_small.jpg similarity index 100% rename from doc/_static/ptaf_logo_small.jpg rename to doc/assets/ptaf_logo_small.jpg diff --git a/doc/build_versioned_docs.sh b/doc/build_versioned_docs.sh new file mode 100755 index 0000000..aa717f2 --- /dev/null +++ b/doc/build_versioned_docs.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +OUTPUT_DIR="${1:-site}" +BASE_URL="${PTAF_DOC_BASE_URL:-https://petercalifano.github.io/pyTorchAutoForge}" +SWITCHER_JSON_URL="${PTAF_DOC_SWITCHER_JSON_URL:-${BASE_URL}/_static/switcher.json}" + +cd "${REPO_ROOT}" + +rm -rf "${OUTPUT_DIR}" +mkdir -p "${OUTPUT_DIR}/_static" + +build_docs_() { + local source_root_="$1" + local output_name_="$2" + local version_name_="$3" + + if [ ! -f "${source_root_}/doc/conf.py" ]; then + echo "Skipping ${version_name_}: doc/conf.py not found" + return 0 + fi + + if [ "${version_name_}" != "stable" ] && [ ! -f "${source_root_}/doc/_static/switcher.json" ]; then + echo "Skipping ${version_name_}: versioned docs config not found" + return 0 + fi + + echo "Building docs ${version_name_} -> ${OUTPUT_DIR}/${output_name_}" + if ! PTAF_DOC_BASE_URL="${BASE_URL}/${output_name_}/" \ + PTAF_DOC_VERSION="${version_name_}" \ + PTAF_DOC_SWITCHER_JSON_URL="${SWITCHER_JSON_URL}" \ + python -m sphinx -b html "${source_root_}/doc" "${OUTPUT_DIR}/${output_name_}"; then + echo "Skipping ${version_name_}: Sphinx build failed" + rm -rf "${OUTPUT_DIR:?}/${output_name_}" + fi +} + +build_docs_ "${REPO_ROOT}" "stable" "stable" + +TEMP_WORKTREE_ROOT="$(mktemp -d)" +cleanup_() { + find "${TEMP_WORKTREE_ROOT}" -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null | + while IFS= read -r -d '' worktree_dir_; do + git worktree remove --force "${worktree_dir_}" >/dev/null 2>&1 || true + done + rm -rf "${TEMP_WORKTREE_ROOT}" +} +trap cleanup_ EXIT + +while IFS= read -r tag_name_; do + [ -n "${tag_name_}" ] || continue + + output_name_="${tag_name_//\//-}" + worktree_dir_="${TEMP_WORKTREE_ROOT}/${output_name_}" + + git worktree add --detach --quiet "${worktree_dir_}" "${tag_name_}" + build_docs_ "${worktree_dir_}" "${output_name_}" "${tag_name_}" + git worktree remove --force "${worktree_dir_}" >/dev/null +done < <(git tag --list 'v*' --sort=-v:refname) + +python - "${OUTPUT_DIR}" "${BASE_URL}" <<'PY' +from __future__ import annotations + +import json +from pathlib import Path +import sys + +output_dir_ = Path(sys.argv[1]) +base_url_ = sys.argv[2].rstrip("/") + +entries_: list[dict[str, str]] = [] + +stable_dir_ = output_dir_ / "stable" +if (stable_dir_ / "index.html").exists(): + entries_.append( + { + "name": "stable", + "version": "stable", + "url": f"{base_url_}/stable/", + } + ) + +for version_dir_ in sorted(output_dir_.iterdir(), reverse=True): + if not version_dir_.is_dir(): + continue + version_name_ = version_dir_.name + if version_name_ in {"_static", "stable"}: + continue + if not (version_dir_ / "index.html").exists(): + continue + entries_.append( + { + "name": version_name_, + "version": version_name_, + "url": f"{base_url_}/{version_name_}/", + } + ) + +(output_dir_ / "_static").mkdir(parents=True, exist_ok=True) +(output_dir_ / "_static" / "switcher.json").write_text( + json.dumps(entries_, indent=2) + "\n", + encoding="utf-8", +) + +(output_dir_ / ".nojekyll").write_text("", encoding="utf-8") +(output_dir_ / "index.html").write_text( + """ + + + + + + pyTorchAutoForge Documentation + + +

Open stable documentation

+ + +""", + encoding="utf-8", +) +PY diff --git a/doc/conf.py b/doc/conf.py index 090b7e3..8bc3b13 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,111 +1,155 @@ -# Configuration file for the Sphinx documentation builder. -# -# For the full list of built-in configuration values, see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -import os, sys, runpy -sys.path.insert(0, os.path.abspath('../pyTorchAutoForge/')) # Add project root to path - -# Determine the path to the _version.py file -version_file_path = os.path.join(os.path.dirname(__file__), '..', '_version.py') - -# Execute the _version.py file and retrieve the __version__ variable -version_info = runpy.run_path(version_file_path) - -# Only mock when on RTD -on_rtd = os.environ.get('READTHEDOCS') == 'True' - -if on_rtd: - - from unittest.mock import MagicMock - - MOCK_MODULES = [ - 'pycuda', # if needed - 'pycuda.driver', - 'pycuda.autoinit', - 'pynvml', - 'pynvml.nvmlInit', - 'pynvml.nvmlDeviceGetHandleByIndex', - 'pynvml.nvmlDeviceGetMemoryInfo', - 'pynvml.nvmlShutdown', - 'yaml', # Added for YAML file parsing - ] - for mod_name in MOCK_MODULES: - sys.modules[mod_name] = MagicMock() - - autodoc_mock_imports = MOCK_MODULES - -# -- Project information ----------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information - -project = 'pyTorchAutoForge' -copyright = '2025, Pietro Califano' -author = 'Pietro Califano' -email = 'petercalifano.gs@gmail.com' -version: str = version_info['__version__'] # Major.Minor.Patch - -# -- General configuration --------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration +from __future__ import annotations -extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.napoleon", - "sphinx.ext.autosummary", - "sphinx.ext.viewcode", - "sphinx.ext.intersphinx", - "sphinx.ext.githubpages", - 'sphinx_rtd_theme' -] +import os +from pathlib import Path +import sys +import logging -templates_path = ['_templates'] -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +REPO_ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(REPO_ROOT)) -# -- Options for HTML output ------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -html_theme = 'sphinx_rtd_theme' +class _AutoapiPlaceholderWarningFilter(logging.Filter): + def filter(self, record: logging.LogRecord) -> bool: + return "Unknown type: placeholder" not in record.getMessage() -# Path to the logo image -html_logo = "_static/ptaf_logo_small.jpg" -html_theme_options = { - #'analytics_id': 'G-XXXXXXXXXX', # Provided by Google in your dashboard - #'analytics_anonymize_ip': False, - 'logo_only': False, - 'display_version': True, - #'prev_next_buttons_location': 'bottom', - #'style_external_links': False, - #'vcs_pageview_mode': '', - #'style_nav_header_background': '#2980B9', - # Toc options - #'collapse_navigation': True, - #'sticky_navigation': True, - #'navigation_depth': 4, - #'includehidden': True, - #'titles_only': False -} +logging.getLogger("autoapi._mapper").addFilter(_AutoapiPlaceholderWarningFilter()) + +try: + from autoapi._mapper import Mapper -# Add custom static files (such as style sheets) -html_static_path = ['_static'] + _AUTOAPI_CREATE_CLASS = Mapper.create_class -# Add custom CSS + def _CreateClassSkippingPlaceholders(self: Mapper, data: dict, options: object = None): + if data.get("type") == "placeholder": + return + yield from _AUTOAPI_CREATE_CLASS(self, data, options=options) + Mapper.create_class = _CreateClassSkippingPlaceholders +except Exception: + pass -def setup(app): - app.add_css_file('ptaf_doc_theme.css') +project = "pyTorchAutoForge" +author = "Pietro Califano" +copyright = "2026, Pietro Califano" +extensions = [ + "myst_parser", + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.intersphinx", + "sphinx.ext.napoleon", + "sphinx.ext.todo", + "sphinx.ext.viewcode", + "sphinx_copybutton", + "autoapi.extension", +] -# -- Options for autodoc extensions ----------------------------------------------------- -napoleon_google_docstring = True # Enable Google-style -napoleon_numpy_docstring = True # Enable NumPy-style -napoleon_include_init_with_doc = False # Don't include __init__ docstring -napoleon_use_param = True # Use :param: for function params -napoleon_use_rtype = True # Use :rtype: for return type +source_suffix = { + ".rst": "restructuredtext", + ".md": "markdown", +} +master_doc = "index" +exclude_patterns = [ + "_build", + "_autoapi_templates", + "_autoapi_templates/**", + "Thumbs.db", + ".DS_Store", + "developments", + "developments/**", + "local_guides", + "local_guides/**", +] +suppress_warnings = [ + "autoapi", + "autoapi.python_import_resolution", + "docutils", + "toc.not_included", +] -#autodoc_member_order = 'bysource' -autosummary_generate = True # Generate .rst files for all modules +myst_enable_extensions = [ + "colon_fence", + "deflist", +] -# Interspinx mapping for cross-referencing -intersphinx_mapping = {"python": ("https://docs.python.org/3", None)} +autodoc_typehints = "description" +autodoc_member_order = "bysource" +autosummary_generate = True + +napoleon_google_docstring = True +napoleon_numpy_docstring = True +napoleon_include_init_with_doc = False +napoleon_include_private_with_doc = False +napoleon_include_special_with_doc = False +napoleon_use_admonition_for_examples = False +napoleon_use_admonition_for_notes = False +napoleon_use_admonition_for_references = False +napoleon_use_ivar = False +napoleon_use_param = True +napoleon_use_rtype = True +napoleon_preprocess_types = True + +autoapi_type = "python" +autoapi_dirs = [str(REPO_ROOT / "pyTorchAutoForge")] +autoapi_root = "api/generated" +autoapi_template_dir = "_autoapi_templates" +autoapi_add_toctree_entry = False +autoapi_keep_files = False +autoapi_member_order = "bysource" +autoapi_options = [ + "members", + "undoc-members", + "show-inheritance", + "show-module-summary", +] +autoapi_ignore = [ + "*/.deprecated/*", + "*/.experimental/*", + "*/.experimental.py", + "*/api/telegram/*", + "*/extra/*", + "*/programs/*", + "*/tensorboard/*", + "*/model_building/factories/*", + "*/utils/pytest_test.py", + "*/utils/test_fixtures/*", +] +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), + "numpy": ("https://numpy.org/doc/stable/", None), + "torch": ("https://docs.pytorch.org/docs/stable/", None), + "sklearn": ("https://scikit-learn.org/stable/", None), +} +html_theme = "pydata_sphinx_theme" +html_logo = "assets/ptaf_logo_small.jpg" +html_static_path = ["_static"] +html_title = "pyTorchAutoForge" +html_baseurl = os.environ.get("PTAF_DOC_BASE_URL", "") +doc_version = os.environ.get("PTAF_DOC_VERSION", "stable") +switcher_json_url = os.environ.get("PTAF_DOC_SWITCHER_JSON_URL", "_static/switcher.json") +html_theme_options = { + "show_toc_level": 2, + "navbar_align": "left", + "navigation_depth": 3, + "collapse_navigation": False, + "switcher": { + "json_url": switcher_json_url, + "version_match": doc_version, + }, + "navbar_end": [ + "theme-switcher", + "version-switcher", + "navbar-icon-links", + ], + "icon_links": [ + { + "name": "GitHub", + "url": "https://github.com/PeterCalifano/pyTorchAutoForge", + "icon": "fa-brands fa-github", + }, + ], +} diff --git a/doc/datasets.md b/doc/datasets.md new file mode 100644 index 0000000..845dfb5 --- /dev/null +++ b/doc/datasets.md @@ -0,0 +1,101 @@ +# Datasets + +```{admonition} Codex-generated note +:class: note + +This guide is partially generated by Codex and should be reviewed against the current source before treating examples as authoritative. +``` + +## When to use this module + +Use {mod}`pyTorchAutoForge.datasets` for data containers and preprocessing pieces that are shared across PTAF training, evaluation, and augmentation flows. Stable surfaces include dataloader indexing, image-label dataset containers, label serialization, image augmentation helpers, and keyed vector error models. + +Prefer this module when: + +- train, validation, and optional test dataloaders should move together through the pipeline; +- labels need a typed container with YAML-compatible keys; +- image augmentations must update geometric labels consistently; +- synthetic error models should perturb a known 1D label layout. + +## Minimal example + +```python +import torch +from torch.utils.data import DataLoader, TensorDataset + +from pyTorchAutoForge.datasets import DataloaderIndex, LabelsContainer +from pyTorchAutoForge.datasets.LabelsClasses import PTAF_Datakey +from pyTorchAutoForge.datasets.vector_error_models.VectorErrorsBaseClasses import ( + DistributionType, + UniformParams, + Vector1dErrorModel, + Vector1dErrorStackModel, +) + +features_ = torch.arange(12, dtype=torch.float32).reshape(6, 2) +targets_ = torch.zeros(6, 1) + +train_loader_ = DataLoader(TensorDataset(features_[:4], targets_[:4]), batch_size=2) +valid_loader_ = DataLoader(TensorDataset(features_[4:], targets_[4:]), batch_size=2) +loader_index_ = DataloaderIndex(train_loader_, valid_loader_) + +labels_ = LabelsContainer() +labels_.geometric.bound_box_coordinates = (10.0, 20.0, 30.0, 40.0) + +bbox_error_ = Vector1dErrorModel( + variable_name="bbox_bias", + shape=(4,), + error_type=DistributionType.UNIFORM, + parameters=UniformParams(low=0.0, high=0.0), + target_keys=(PTAF_Datakey.BBOX_XYWH,), +) +error_stack_ = Vector1dErrorStackModel( + keys=(PTAF_Datakey.BBOX_XYWH,), + error_models=(bbox_error_,), +) + +corrected_bbox_, applied_error_ = error_stack_.apply( + torch.tensor(labels_.BBOX_XYWH), + return_error=True, +) + +print(len(loader_index_.getTrainLoader().dataset), len(loader_index_.getValidationLoader().dataset)) +print(corrected_bbox_.tolist()) +print(applied_error_.abs().sum().item()) +``` + +Expected output: + +```text +4 2 +[10.0, 20.0, 30.0, 40.0] +0.0 +``` + +## Common workflow + +1. Build or load image/label datasets, then wrap train and validation loaders in `DataloaderIndex`. +2. Store labels in `LabelsContainer` when YAML serialization or PTAF datakey access is needed. +3. Configure `AugmentationConfig` with Kornia `DataKey` values before using `ImageAugmentationsHelper`. +4. Request transform metadata from image augmentations when downstream labels must be updated from the composed geometric transform. +5. Use `Vector1dErrorStackModel` for synthetic vector perturbations where each slice is owned by a `PTAF_Datakey`. + +## API links + +- {mod}`pyTorchAutoForge.datasets` +- {mod}`pyTorchAutoForge.datasets.DatasetClasses` +- {mod}`pyTorchAutoForge.datasets.DataloaderIndex` +- {mod}`pyTorchAutoForge.datasets.LabelsClasses` +- {mod}`pyTorchAutoForge.datasets.ImagesAugmentation` +- {mod}`pyTorchAutoForge.datasets.vector_error_models.VectorErrorsBaseClasses` +- {class}`pyTorchAutoForge.datasets.DataloaderIndex.DataloaderIndex` +- {class}`pyTorchAutoForge.datasets.LabelsClasses.LabelsContainer` +- {class}`pyTorchAutoForge.datasets.ImagesAugmentation.AugmentationConfig` +- {class}`pyTorchAutoForge.datasets.ImagesAugmentation.ImageAugmentationsHelper` +- {class}`pyTorchAutoForge.datasets.vector_error_models.VectorErrorsBaseClasses.Vector1dErrorStackModel` + +## Notes and limitations + +- Dataset loaders still depend on project-specific folder layout and label conventions. Validate paths and datakeys before long data scans. +- Image augmentation examples may require Kornia-compatible image/keypoint shapes. Start with no-op probabilities, then enable one transform at a time. +- Vector error models assume the last tensor dimension matches the configured keyed layout. diff --git a/doc/developments/explainability_module.md b/doc/developments/explainability_module.md new file mode 100644 index 0000000..48f9239 --- /dev/null +++ b/doc/developments/explainability_module.md @@ -0,0 +1,199 @@ +# PTAF Explainability Module Implementation Plan + +Scope: replace the current `ModelExplainer` prototype with a typed, modular explainability subsystem. Current implementation target is CPU-friendly Torch tabular models with SHAP and Captum adapters. Future targets include ONNX Runtime, TensorRT, optimized/quantized engines, recurrent models, spiking models, image inputs, sequence inputs, event inputs, and optional custom C++/CUDA acceleration. + +## Stage 0 - Repository, Docs, External Usage Inventory + +- [x] Treat `doc/` as canonical documentation folder. +- [x] Confirm `doc/` is the canonical documentation source folder. +- [x] Confirm active documentation config lives in `.github/workflows/docs_pages.yml` and `doc/conf.py`. +- [x] Create this implementation plan at `doc/developments/explainability_module.md`. +- [x] Keep old `doc/developments/model_explainer.md` as historical design context until final cleanup decision. +- [x] Search PTAF for old explainer API references. +- [x] Search local workspace for `mlgears`; no matching checkout was found under checked workspace paths, so no external upgrade can be applied in this implementation pass. +- [x] Re-run API usage search after implementation and before final validation. + +## Stage 1 - Typed Core Package + +- [x] Create `pyTorchAutoForge/evaluation/explainability/`. +- [x] Add `schemas.py` with: + - [x] `ExplainerTaskType` + - [x] `ExplainerBackendType` + - [x] `ExplainerMethod` + - [x] `ExplainerInputSpec` + - [x] `ExplainerOutputSpec` +- [x] Add `method_configs.py` with typed method config dataclasses: + - [x] `ExplainerBaselineConfig` + - [x] `ShapExplainerConfig` + - [x] `CaptumIntegratedGradientsConfig` + - [x] `CaptumSaliencyConfig` + - [x] `CaptumGradientShapConfig` +- [x] Add validation for all config objects before backend library calls. +- [x] Add `resolve_method_name` and avoid all free-form `method_kwargs` style APIs. +- [x] Use names that explicitly belong to explainer subsystem and avoid ambiguous public names like `InputSpec`, `OutputSpec`, `TargetSpec`, `BaselineConfig`, and `PredictorProtocol`. + +Validation gate: + +- [x] `conda run -n autoforge pytest -q tests/evaluation/explainability/test_method_configs.py` + +## Stage 2 - Request, Target, Result Objects + +- [x] Add `targets.py` with `ExplainerTargetSpec`. +- [x] Add `configs.py` with `ExplainerConfig` and `ExplainerRequest`. +- [x] Add `results.py` with `ExplainerResult`. +- [x] Keep constructors side-effect-light. +- [x] Create output folders only in `ExplainerResult.save`. +- [x] Store arrays in `.npz` and metadata in `.json`. +- [x] Do not serialize native SHAP/Captum raw objects by default. +- [x] Keep `raw` available in memory for native backend objects. + +Validation gate: + +- [x] `conda run -n autoforge pytest -q tests/evaluation/explainability/test_target_spec.py tests/evaluation/explainability/test_explanation_result.py` + +## Stage 3 - Predictors + +- [x] Add `predictors.py` with: + - [x] `ExplainerPredictorProtocol` + - [x] `TorchExplainerPredictor` + - [x] `CallableExplainerPredictor` +- [x] Support tensor, tuple/list tensor, and dict tensor inputs for Torch prediction. +- [x] Preserve model training/eval state around prediction. +- [x] Provide `predict_with_grad` for gradient explainers. +- [x] Add TODO for runtime-backed predictors wrapping `ModelRuntimeApi` for future ONNX Runtime and TensorRT black-box explainers. + +Validation gate: + +- [x] `conda run -n autoforge pytest -q tests/evaluation/explainability/test_torch_predictor.py` + +## Stage 4 - Registry And Dispatch + +- [x] Add `registry.py` with: + - [x] `ExplainerAdapterProtocol` + - [x] `ExplainerRegistry` + - [x] `DEFAULT_EXPLAINER_REGISTRY` + - [x] `register_default_explainers` +- [x] Add `explainer.py` with `ModelExplainer.explain`. +- [x] Resolve method from typed method config, not from arbitrary string options. +- [x] Instantiate adapters lazily through registry. +- [x] Leave `auto_plot` as no-op metadata warning for now. +- [x] Save outputs through `ExplainerResult.save` only when `save_outputs=True`. + +Validation gate: + +- [x] `conda run -n autoforge pytest -q tests/evaluation/explainability/test_explainer_registry.py` + +## Stage 5 - SHAP Adapter + +- [x] Add `adapters/shap_adapter.py`. +- [x] Lazy import `shap` inside adapter methods. +- [x] Support tabular Torch tensor, NumPy array, and pandas DataFrame inputs. +- [x] Infer DataFrame feature names when explicit `ExplainerInputSpec.feature_names` is absent. +- [x] Use explicit background data when supplied. +- [x] Sample background from inputs when missing, capped by `max_background_samples`. +- [x] Use `TorchExplainerPredictor` or callable predictor. +- [x] Apply `ExplainerTargetSpec` when target selection is simple and clear. +- [x] Return normalized `ExplainerResult`. +- [x] Keep native SHAP explanation in `raw["shap_explanation"]`. +- [x] Metadata includes method, backend, algorithm, link, background size, seed. + +Validation gate: + +- [x] `conda run -n autoforge pytest -q tests/evaluation/explainability/test_shap_adapter.py` + +## Stage 6 - Captum Adapter + +- [x] Add `adapters/captum_adapter.py`. +- [x] Lazy import `captum` inside adapter methods. +- [x] Support simple Torch tensor inputs for first implementation. +- [x] Implement: + - [x] Integrated Gradients + - [x] Saliency + - [x] GradientShap +- [x] Implement typed baseline strategies: + - [x] zero + - [x] mean + - [x] constant + - [x] tensor + - [x] background +- [x] Use `ExplainerTargetSpec.target_index` where Captum supports it. +- [x] Preserve convergence deltas only for methods that return them. +- [x] Return normalized `ExplainerResult`. + +Validation gate: + +- [x] `conda run -n autoforge pytest -q tests/evaluation/explainability/test_captum_adapter.py` + +## Stage 7 - Exports, Facade, Dependency Metadata + +- [x] Update `pyTorchAutoForge/evaluation/explainability/__init__.py`. +- [x] Update `pyTorchAutoForge/evaluation/__init__.py`. +- [x] Replace `pyTorchAutoForge/evaluation/ModelExplainer.py` with lightweight re-export facade. +- [x] Stop exporting old `ModelExplainerHelper`, `CaptumExplainMethods`, and `ShapExplainMethods`. +- [x] Add optional extras in `pyproject.toml`: + - [x] `explain-shap` + - [x] `explain-captum` + - [x] `explain` +- [x] Ensure package import has no import-time dependency on SHAP, Captum, or TensorRT. + +Validation gate: + +- [x] `conda run -n autoforge pytest -q tests/api/test_import_safety.py tests/evaluation/explainability` + +## Stage 8 - Runnable CPU Example + +- [x] Add `examples/example_ModelExplainer.py`. +- [x] Use deterministic `DummyTestModel`. +- [x] Run SHAP block only when SHAP is installed. +- [x] Run Captum Integrated Gradients block only when Captum is installed. +- [x] Print result shapes and metadata. +- [x] Keep example CPU-only and short. + +Validation gate: + +- [x] `conda run -n autoforge python examples/example_ModelExplainer.py` + +## Stage 9 - Performance Work For Later Stages + +- [ ] Add timing metadata hooks around adapter execution once functional API stabilizes. +- [ ] Profile SHAP perturbation cost by sample count, feature count, and background size. +- [ ] Profile Captum gradient methods by input size, target count, and integration step count. +- [ ] Add batch-size tuning for SHAP model wrapper calls. +- [ ] Add vectorized target selection for multi-output Torch predictions. +- [ ] Add runtime-backed black-box perturbation adapter that wraps `ModelRuntimeApi`. +- [ ] Add ONNX Runtime predictor wrapper before any ONNX-specific attribution claims. +- [ ] Add TensorRT predictor wrapper before any TensorRT-specific attribution claims. +- [ ] Prototype custom C++/CUDA direct kernels only after profiling identifies a hot path that Python/Captum/SHAP cannot handle. +- [ ] Candidate custom acceleration areas: + - [ ] baseline tensor generation + - [ ] perturbation mask application + - [ ] batched feature ablations + - [ ] aggregation of attribution statistics + - [ ] event/sequence temporal window selection +- [ ] Keep C++/CUDA extensions optional with CPU fallback and parity tests. + +## Stage 10 - Final Validation + +- [x] `conda run -n autoforge pytest -q tests/evaluation/explainability` +- [x] `conda run -n autoforge python examples/example_ModelExplainer.py` +- [x] `conda run -n autoforge pytest -q tests/api/test_import_safety.py` +- [x] `conda run -n autoforge pytest -q tests/api/onnx/test_ModelHandlerONNx.py tests/api/tensorrt/test_TRTengineExporter.py` +- [x] Confirm no public `method_kwargs` field exists. +- [x] Confirm no arbitrary method option dictionary exists in public explainer API. +- [x] Confirm method options are typed dataclasses. +- [x] Confirm invalid options fail in validation. +- [x] Confirm old ONNX/TensorRT runtime behavior is not changed. + +## Non-Goals In First Implementation + +- [ ] Full image explainability. +- [ ] Full sequence explainability. +- [ ] Full event explainability. +- [ ] Full recurrent state policy. +- [ ] Full spiking attribution. +- [ ] Full ONNX Runtime attribution. +- [ ] Full TensorRT attribution. +- [ ] Plot/report generation. +- [ ] MLflow logging. +- [ ] Native SHAP/Captum raw object serialization. +- [ ] Refactors to unrelated modules. diff --git a/doc/developments/model_explainer.md b/doc/developments/model_explainer.md new file mode 100644 index 0000000..c72fe9c --- /dev/null +++ b/doc/developments/model_explainer.md @@ -0,0 +1,173 @@ +# Model Explainer Implementation Plan + +Status date: 2026-05-17 + +Scope: feature-level model explanation in PTAF for PyTorch models, first for tabular/vector inputs and then image/CNN workflows. Main public entry point remains `ModelExplainerHelper`, with typed configuration and result containers added around the existing helper-style API. + +## Current Status + +- [x] Existing prototype reviewed for integration risks. +- [x] Heavy optional backend imports moved out of module import path. +- [x] `pyTorchAutoForge.evaluation` namespace converted to lazy exports. +- [x] Captum call contract fixed for target selection. +- [x] Basic SHAP tabular path stabilized with deterministic background selection. +- [x] Focused regression tests added for import laziness, Captum call shape, SHAP background selection, and result serialization. +- [x] Real Captum and SHAP tests added in `autoforge`. +- [x] Full default test suite passes in `autoforge`. + +## Stage 1 - API Contract And Integration Baseline + +- [x] Keep `ModelExplainerHelper` as backward-compatible public entry point. +- [x] Keep `explain_features()` return shape compatible with old dict consumers. +- [x] Add typed result objects: + - [x] `FeatureAttributionStats` + - [x] `CaptumExplainerResult` + - [x] `ShapExplainerResult` +- [x] Add `ModelExplainerConfig` for new configuration fields. +- [x] Preserve legacy `features_names` argument. +- [x] Add preferred `feature_names` argument. +- [x] Keep `CaptumExplainMethods` and `ShapExplainMethods` exported. +- [ ] Add deprecation warning for `features_names` after downstream callers are checked. +- [ ] Decide whether `task_type` should remain accepted but unused, or become part of backend selection. +- [ ] Add API reference examples in the Sphinx API page. + +## Stage 2 - Import Safety And Optional Dependencies + +- [x] Remove top-level `captum`, `shap`, `torch`, `seaborn`, and `matplotlib.pyplot` imports from `ModelExplainer.py`. +- [x] Load `torch` only when helper instance is created. +- [x] Load `captum.attr` only when Captum backend is selected. +- [x] Load `shap` only when SHAP backend is selected. +- [x] Load plot packages only when plotting is requested. +- [x] Make `pyTorchAutoForge.evaluation.__init__` lazy. +- [x] Add import-safety regression tests. +- [ ] Split backend dependency errors by backend: + - [ ] Captum missing should not mention SHAP. + - [ ] SHAP missing should not mention Captum. +- [ ] Consider moving `shap` and `captum` to optional extras if package install weight becomes too high. + +## Stage 3 - Captum Backend + +- [x] Support `IntegratedGradients`. +- [x] Support `Saliency`. +- [x] Support `GradientShap`. +- [x] Pass target index as `target=...`. +- [x] Return convergence delta when backend supports it. +- [x] Add default zero baseline for `GradientShap`. +- [x] Allow configured baseline for Integrated Gradients / Gradient SHAP. +- [x] Convert tensor attributions to numpy result arrays. +- [x] Compute signed mean, absolute mean, standard deviation, quantiles, and min/max. +- [x] Flatten non-batch feature dimensions for stats. +- [x] Add regression tests using fake Captum backend. +- [x] Run real Captum Integrated Gradients analytic test in `autoforge`. +- [x] Run real Captum Saliency analytic test in `autoforge`. +- [x] Add real Captum unit tests gated by installed dependency marker. +- [ ] Add image/CNN Captum attribution mode: + - [ ] input validation for `NCHW`. + - [ ] per-channel aggregation. + - [ ] pixel/heatmap output. + - [ ] optional layer attribution. +- [ ] Add support for `LayerIntegratedGradients`. +- [ ] Add support for `GuidedBackprop` or equivalent image-oriented method. +- [ ] Add batch-size control for large attribution jobs. + +## Stage 4 - SHAP Backend + +- [x] Support tabular/vector SHAP path. +- [x] Validate SHAP input as `(num_samples, num_features)`. +- [x] Use deterministic background sample selection. +- [x] Add configurable background fraction. +- [x] Add configurable background max sample count. +- [x] Forward PyTorch model through numpy wrapper under `torch.no_grad()`. +- [x] Normalize scalar/vector outputs to `(num_samples, num_outputs)`. +- [x] Preserve feature and output names. +- [x] Add fake-SHAP regression test for background selection and output shape. +- [x] Add `shap` to project dependencies because backend is public. +- [x] Install/verify SHAP in active development environment. +- [x] Add real SHAP smoke test for vector-to-vector regression. +- [x] Add real SHAP smoke test for scalar regression. +- [ ] Add classifier-logit vs probability mode decision. +- [ ] Add warning if model is in training mode before explainer switches to eval. +- [ ] Add explicit unsupported error for image SHAP until CNN path is designed. + +## Stage 5 - Artifacts And Plots + +- [x] Replace pandas/PyTables `.h5` write path with compressed `.npz`. +- [x] Save SHAP values, base values, data, feature names, and output names. +- [x] Return artifact path in result object. +- [x] Save Captum feature-importance plot when `auto_plot=True`. +- [x] Use seaborn for Captum bar plot when available. +- [x] Fall back to matplotlib when seaborn is unavailable. +- [x] Fix SHAP multi-output plot bookkeeping so figures from all outputs are retained. +- [x] Close figures by default after saving. +- [ ] Add configurable artifact format enum: + - [ ] `NPZ` + - [ ] `HDF5` + - [ ] `CSV_SUMMARY` +- [ ] Add summary CSV export for Captum stats. +- [ ] Add stable file naming scheme for repeated runs. +- [ ] Add optional MLflow artifact logging hook. +- [ ] Add plot tests that verify files exist without checking visual content. + +## Stage 6 - Tests And CI Coverage + +- [x] Restore `tests/evaluation/test_ModelExplainer.py` from empty placeholder into real tests. +- [x] Test lazy import of explainer backend dependencies. +- [x] Test lazy import of evaluation namespace. +- [x] Test feature-stat flattening. +- [x] Test Captum `target` keyword behavior. +- [x] Test Captum default baseline behavior for Gradient SHAP. +- [x] Test SHAP deterministic background selection. +- [x] Test `.npz` artifact content for SHAP fake backend. +- [x] Test feature-name validation. +- [x] Run targeted evaluation/import safety suite. +- [x] Run full default pytest suite and record unrelated failures. +- [x] Add dependency-marked real Captum tests. +- [x] Add dependency-marked real SHAP tests. +- [ ] Add no-plot mode test for headless CI. +- [ ] Add plot-save smoke test with matplotlib `Agg`. +- [x] Add shape tests for multi-output regression. +- [x] Add shape tests for single-output regression. +- [ ] Add classification target tests. + +## Stage 7 - Examples And Documentation + +- [ ] Add runnable example for Captum vector regression. +- [ ] Add runnable example for Captum vector classification. +- [ ] Add runnable example for SHAP vector regression. +- [ ] Add example output snippets in docs. +- [ ] Add explainer API page to Sphinx navigation. +- [ ] Add docs section explaining backend dependency requirements. +- [ ] Add docs section explaining artifact outputs. +- [ ] Add docs section explaining known limitations: + - [ ] SHAP currently tabular/vector only. + - [ ] image/CNN attribution planned but not complete. + - [ ] results are explanation artifacts, not ONNX-exportable runtime code. + +## Stage 8 - Advanced Roadmap + +- [ ] Add image/CNN attribution support. +- [ ] Add layer-wise Captum explainers. +- [ ] Add segmentation explanation workflow. +- [ ] Add multi-input model support. +- [ ] Add model wrapper support for PTAF runtime APIs. +- [ ] Add MLflow integration for explainer artifacts. +- [ ] Add explainer report generator. +- [ ] Add dataset sampling utility for explanation cohorts. +- [ ] Add feature-group attribution support. +- [ ] Add uncertainty-aware attribution summaries for dropout ensembles. + +## Validation Ledger + +- [x] `python3 -m py_compile pyTorchAutoForge/evaluation/ModelExplainer.py pyTorchAutoForge/evaluation/__init__.py tests/evaluation/test_ModelExplainer.py` +- [x] `conda run -n autoforge pytest -q tests/evaluation/test_ModelExplainer.py` +- [x] `conda run -n autoforge pytest -q tests/api/test_import_safety.py tests/evaluation/test_ResultsPlotter.py tests/evaluation/test_ModelProfiler.py tests/evaluation/test_ModelExplainer.py` +- [x] Real Captum Integrated Gradients analytic test in `autoforge`. +- [x] Real Captum Saliency analytic test in `autoforge`. +- [x] Real SHAP scalar-regression additivity test in `autoforge`. +- [x] Real SHAP vector-regression additivity test in `autoforge`. +- [x] `conda run -n autoforge pytest -q` + +## Known Open Issues + +- [ ] `pyproject.toml` now lists `shap`; lock/install scripts may need matching update if this repo treats them as source-of-truth. +- [ ] Current image/CNN explainer tasks remain planned, not implemented. diff --git a/doc/evaluation.md b/doc/evaluation.md new file mode 100644 index 0000000..825b217 --- /dev/null +++ b/doc/evaluation.md @@ -0,0 +1,88 @@ +# Evaluation + +```{admonition} Codex-generated note +:class: note + +This guide is partially generated by Codex and should be reviewed against the current source before treating examples as authoritative. +``` + +## When to use this module + +Use {mod}`pyTorchAutoForge.evaluation` after a model has produced predictions and targets. The stable guide-level surface covers regression evaluation, prediction-error plots, profiling helpers, and the separate explainability subsystem. + +Prefer this module when: + +- residual statistics should be computed from a validation dataloader; +- prediction errors should be plotted with Matplotlib or Seaborn; +- a quick Torch profiler trace is needed for model inference; +- SHAP or Captum explanations should be run through the typed explainer API. + +## Minimal example + +```python +from pathlib import Path +from tempfile import TemporaryDirectory + +import matplotlib +matplotlib.use("Agg") +import numpy as np + +from pyTorchAutoForge.evaluation import ResultsPlotterConfig, ResultsPlotterHelper +from pyTorchAutoForge.evaluation.ResultsPlotter import backend_module + +stats_ = { + "prediction_err": np.array([[0.1, -0.2], [0.0, 0.3], [-0.1, 0.1]]), + "mean_prediction_err": np.array([0.0, 0.0667]), +} + +with TemporaryDirectory() as tmp_dir_: + plotter_ = ResultsPlotterHelper( + stats=stats_, + backend_module_=backend_module.SEABORN, + config=ResultsPlotterConfig( + save_figs=True, + entriesNames=["x", "y"], + units=["m", "m"], + output_folder=str(Path(tmp_dir_) / "plots"), + ), + ) + plotter_.histPredictionErrors() + + print(Path(plotter_.output_folder, "prediction_errors_all_components.png").is_file()) + print(stats_["prediction_err"].shape) +``` + +Expected output, ignoring informational output-folder logs: + +```text +True +(3, 2) +``` + +## Common workflow + +1. Evaluate a regression model with `ModelEvaluator` when predictions, targets, losses, and residual statistics should be computed from a dataloader. +2. Pass saved or computed stats into `ResultsPlotterHelper` for prediction-error histograms. +3. Profile candidate models with `ModelProfilerHelper` before export or deployment. +4. Use [](model_explainer.md) for SHAP/Captum feature-attribution workflows. +5. Keep plotting backends headless in CI by setting Matplotlib to `Agg` or by saving figures directly. + +## API links + +- {mod}`pyTorchAutoForge.evaluation` +- {mod}`pyTorchAutoForge.evaluation.ModelEvaluator` +- {mod}`pyTorchAutoForge.evaluation.ResultsPlotter` +- {mod}`pyTorchAutoForge.evaluation.ModelProfiler` +- {mod}`pyTorchAutoForge.evaluation.explainability` +- {class}`pyTorchAutoForge.evaluation.ModelEvaluator.ModelEvaluator` +- {class}`pyTorchAutoForge.evaluation.ModelEvaluator.ModelEvaluatorConfig` +- {class}`pyTorchAutoForge.evaluation.ResultsPlotter.ResultsPlotterHelper` +- {class}`pyTorchAutoForge.evaluation.ResultsPlotter.ResultsPlotterConfig` +- {class}`pyTorchAutoForge.evaluation.ModelProfiler.ModelProfilerHelper` +- {class}`pyTorchAutoForge.evaluation.explainability.ModelExplainer` + +## Notes and limitations + +- `ResultsPlotterHelper.histPredictionErrors()` expects `prediction_err` in the stats dictionary and uses `mean_prediction_err` when present. +- `ModelProfilerHelper` prints profiler tables and can write Chrome trace files; keep paths temporary in examples and tests. +- Explainability has optional dependencies. Install the explainability extra before running SHAP or Captum workflows. diff --git a/doc/getting_started.md b/doc/getting_started.md new file mode 100644 index 0000000..286b5c9 --- /dev/null +++ b/doc/getting_started.md @@ -0,0 +1,143 @@ +# Getting Started + +This page covers first install, local development setup, and basic validation. + +## Install From PyPI + +Use this path when you only need the released library: + +```bash +python -m pip install pyTorchAutoForge +``` + +Optional feature groups are explicit: + +```bash +python -m pip install "pyTorchAutoForge[explain]" +python -m pip install "pyTorchAutoForge[classical-ml]" +``` + +`explain` installs SHAP and Captum support. `classical-ml` installs optional XGBoost and PySR wrappers. + +## Install From Source + +Use this path for local development or when testing unreleased changes: + +```bash +git clone git@github.com:PeterCalifano/pyTorchAutoForge.git +cd pyTorchAutoForge +python -m pip install -e . +``` + +Install development extras: + +```bash +python -m pip install -e ".[test,docs]" +``` + +Install documentation and explainer extras together: + +```bash +python -m pip install -e ".[test,docs,explain]" +``` + +## Conda Development Environment + +Recommended local environment name: `autoforge`. + +```bash +conda create -n autoforge python=3.12 +conda activate autoforge +python -m pip install --upgrade pip +python -m pip install -e ".[test,docs]" +``` + +Repository scripts default to `autoforge` when no conda environment is active. To use a different active environment: + +```bash +conda activate my_ptaf_env +bash doc/makedoc.sh +``` + +To force a specific environment: + +```bash +CONDA_ENV=my_ptaf_env bash doc/makedoc.sh +``` + +## Verify Install + +Run a minimal import check: + +```bash +python - <<'PY' +import pyTorchAutoForge + +print("pyTorchAutoForge import ok") +PY +``` + +Expected output: + +```text +pyTorchAutoForge import ok +``` + +Check PyTorch availability separately: + +```bash +python - <<'PY' +import torch + +print(torch.__version__) +print("CUDA available:", torch.cuda.is_available()) +PY +``` + +## Run Tests + +Default local test run: + +```bash +./run_tests.sh -- -q +``` + +Use another conda environment: + +```bash +./run_tests.sh -e my_ptaf_env -- -q +``` + +Slow, GPU, and visual tests are opt-in: + +```bash +./run_tests.sh -- --run-slow -q +./run_tests.sh -- --run-gpu -q +./run_tests.sh -- --run-visual -q +``` + +## Build Documentation + +Build the Sphinx site: + +```bash +bash doc/makedoc.sh +``` + +Serve locally: + +```bash +bash doc/makedoc.sh -a +``` + +Open: + +```text +http://127.0.0.1:8000 +``` + +## Next Steps + +- Use the API reference for model-building, datasets, optimization, evaluation, and deployment modules. +- Use examples under `examples/` for runnable workflows. +- Install optional extras only for the backends you need. diff --git a/doc/index.md b/doc/index.md new file mode 100644 index 0000000..4a15329 --- /dev/null +++ b/doc/index.md @@ -0,0 +1,48 @@ +# pyTorchAutoForge + +```{image} assets/ptaf_logo_small.jpg +:alt: pyTorchAutoForge logo +:width: 180px +``` + +`pyTorchAutoForge` is a PyTorch-oriented toolbox for model construction, training support, experiment monitoring, and deployment through ONNX and TensorRT workflows. + +```{warning} +Public APIs are still evolving. Prefer pinned releases for production work. +``` + +## Core Areas + +- Model-building blocks, backbones, adapters, and export-oriented pooling layers. +- Dataset containers, label containers, image augmentation utilities, and vector error models. +- Training-manager utilities, MLflow/Optuna integration, and monitoring helpers. +- Runtime and export APIs for PyTorch, ONNX, and TensorRT deployment paths. + +```{toctree} +:caption: User Guide +:maxdepth: 2 + +getting_started +model_building +datasets +runtime_export +training_monitoring +evaluation +model_explainer +utilities +``` + +```{toctree} +:caption: Reference +:maxdepth: 2 + +api/index +``` + +```{toctree} +:caption: Development +:maxdepth: 2 + +testing +roadmap +``` diff --git a/doc/index.rst b/doc/index.rst deleted file mode 100644 index 4ec4bde..0000000 --- a/doc/index.rst +++ /dev/null @@ -1,39 +0,0 @@ -.. pyTorchAutoForge documentation master file, created by - sphinx-quickstart on Sun Sep 22 18:03:23 2024. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to pyTorchAutoForge's documentation! -============================================ - -.. image:: _static/ptaf_logo_small.jpg - :alt: ptaf_logo_small - :align: center - :width: 200px - -.. toctree:: - :maxdepth: 4 - :caption: Contents: - -.. Add sources. - -.. autosummary:: - :toctree: sources/ - :recursive: - - pyTorchAutoForge - pyTorchAutoForge.datasets - pyTorchAutoForge.evaluation - pyTorchAutoForge.hparams_optim - pyTorchAutoForge.optimization - pyTorchAutoForge.setup - pyTorchAutoForge.utils -.. pyTorchAutoForge.api - -Indices and tables -================== -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - - diff --git a/doc/install_sphinx.sh b/doc/install_sphinx.sh deleted file mode 100644 index f98db0a..0000000 --- a/doc/install_sphinx.sh +++ /dev/null @@ -1 +0,0 @@ -pip install sphinx sphinx-autobuild sphinx_rtd_theme sphinxcontrib-matlabdomain --break-system-packages \ No newline at end of file diff --git a/doc/make.bat b/doc/make.bat deleted file mode 100644 index 32bb245..0000000 --- a/doc/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -if "%1" == "" goto help - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/doc/makedoc.sh b/doc/makedoc.sh index e80a35e..6c10015 100755 --- a/doc/makedoc.sh +++ b/doc/makedoc.sh @@ -1,25 +1,78 @@ #!/bin/bash -source ~/miniconda3/etc/profile.d/conda.sh -conda activate autoforge +set -euo pipefail -while getopts "a,i:,o:,p:" opt; do +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +cd "${REPO_ROOT}" + +CONDA_EXE="${CONDA_EXE:-conda}" +CONDA_BASE="$("${CONDA_EXE}" info --base)" +# shellcheck source=/dev/null +source "${CONDA_BASE}/etc/profile.d/conda.sh" + +if [ -n "${CONDA_ENV:-}" ]; then + conda activate "${CONDA_ENV}" +elif [ -n "${CONDA_DEFAULT_ENV:-}" ]; then + echo "Using active conda env: ${CONDA_DEFAULT_ENV}" +else + conda activate autoforge +fi + +ensure_docs_dependencies_() { + python - <<'PY' +import importlib.util +import sys + +required_modules_ = ( + "sphinx", + "pydata_sphinx_theme", + "myst_parser", + "autoapi", + "sphinx_copybutton", +) +missing_modules_ = [ + module_name_ + for module_name_ in required_modules_ + if importlib.util.find_spec(module_name_) is None +] +if missing_modules_: + print("Missing docs modules: " + ", ".join(missing_modules_)) + sys.exit(1) +PY +} + +if ! ensure_docs_dependencies_; then + echo "Installing Sphinx documentation dependencies into conda env: ${CONDA_DEFAULT_ENV}" + python -m pip install -r doc/requirements.txt + ensure_docs_dependencies_ +fi + +SERVE=0 +STRICT=0 +HOST="127.0.0.1" +PORT="8000" + +while getopts "ash:p:" opt; do case $opt in - a) AUTOBUILD=1 ;; - i) INPUT="${OPTARG:-doc}" ;; - o) OUTPUT="${OPTARG:-test_autodoc}" ;; - p) PORT="${OPTARG:-8000}" ;; + a) SERVE=1 ;; + s) STRICT=1 ;; + h) HOST="$OPTARG" ;; + p) PORT="$OPTARG" ;; *) echo "Invalid option"; exit 1 ;; esac done -# Set default values if not provided -AUTOBUILD=${AUTOBUILD:-0} -INPUT=${INPUT:-doc} -OUTPUT=${OUTPUT:-test_autodoc} -PORT=${PORT:-8000} +SPHINX_ARGS=(-b html doc site) +if [ "$STRICT" -eq 1 ]; then + SPHINX_ARGS=(-W --keep-going "${SPHINX_ARGS[@]}") +fi + +rm -rf site -if [ -n "$AUTOBUILD" ]; then - sphinx-autobuild "$INPUT" "$OUTPUT" --port "$PORT" --open-browser +if [ "$SERVE" -eq 1 ]; then + python -m sphinx "${SPHINX_ARGS[@]}" + cd site + python -m http.server "${PORT}" --bind "${HOST}" else - make html + python -m sphinx "${SPHINX_ARGS[@]}" fi diff --git a/doc/model_building.md b/doc/model_building.md new file mode 100644 index 0000000..0abaa5d --- /dev/null +++ b/doc/model_building.md @@ -0,0 +1,91 @@ +# Model Building + +```{admonition} Codex-generated note +:class: note + +This guide is partially generated by Codex and should be reviewed against the current source before treating examples as authoritative. +``` + +## When to use this module + +Use {mod}`pyTorchAutoForge.model_building` when the model architecture should be built from reusable Torch blocks rather than hand-written layers. The stable surface covers convolutional and fully connected blocks, export-aware pooling layers, backbone adapters, and multi-head regressors. + +Prefer these utilities when: + +- model components need consistent initialization, activation, pooling, and regularization options; +- pooling must stay friendly to ONNX export paths; +- a shared feature tensor feeds more than one prediction head; +- a backbone or adapter needs to be assembled from existing PTAF building blocks. + +## Minimal example + +```python +import torch + +from pyTorchAutoForge.model_building import ( + MultiHeadRegressor, + TemplateFullyConnectedNet, + TemplateFullyConnectedNetConfig, +) +from pyTorchAutoForge.model_building.poolingBlocks import CustomAdaptiveAvgPool2d + +torch.manual_seed(0) + +feature_model_ = TemplateFullyConnectedNet( + TemplateFullyConnectedNetConfig( + out_channels_sizes=[8], + input_layer_size=4, + output_layer_size=8, + activ_type="relu", + regularization_layer_type="none", + ) +) + +heads_ = torch.nn.ModuleList( + [ + torch.nn.Linear(8, 2), + torch.nn.Linear(8, 1), + ] +) +regressor_ = torch.nn.Sequential(feature_model_, MultiHeadRegressor(heads_)).eval() + +output_ = regressor_(torch.ones(3, 4)) +pooled_ = CustomAdaptiveAvgPool2d((1, 1))(torch.ones(3, 2, 4, 4)) + +print(tuple(output_.shape)) +print(tuple(pooled_.shape)) +``` + +Expected output: + +```text +(3, 3) +(3, 2, 1, 1) +``` + +## Common workflow + +1. Pick the smallest layer abstraction that matches the architecture: block, block stack, template network, backbone, or adapter. +2. Use dataclass-like config objects for template models so constructor arguments are explicit and serializable. +3. Add task heads through `MultiHeadRegressor` when one feature representation serves several outputs. +4. Use PTAF pooling layers when adaptive pooling needs to survive ONNX export checks. +5. Validate with one CPU forward pass before moving to training or export. + +## API links + +- {mod}`pyTorchAutoForge.model_building` +- {mod}`pyTorchAutoForge.model_building.convolutionalBlocks` +- {mod}`pyTorchAutoForge.model_building.fullyConnectedBlocks` +- {mod}`pyTorchAutoForge.model_building.modelBuildingBlocks` +- {mod}`pyTorchAutoForge.model_building.poolingBlocks` +- {mod}`pyTorchAutoForge.model_building.backbones` +- {class}`pyTorchAutoForge.model_building.modelBuildingBlocks.TemplateFullyConnectedNet` +- {class}`pyTorchAutoForge.model_building.modelBuildingBlocks.TemplateFullyConnectedNetConfig` +- {class}`pyTorchAutoForge.model_building.ModelAutoBuilder.MultiHeadRegressor` +- {class}`pyTorchAutoForge.model_building.poolingBlocks.CustomAdaptiveAvgPool2d` + +## Notes and limitations + +- `ModelAssembler` and `ModelAutoBuilder` still contain experimental or incomplete paths; prefer tested blocks, template networks, adapters, and backbones for new guide-level work. +- Tensor shapes should be checked with real sample tensors before export. Some blocks have dynamic behavior that is easiest to validate by running `torch.onnx.export` on the target architecture. +- Factories under `model_building.factories` are implementation helpers and are not treated as tutorial entry points. diff --git a/doc/model_explainer.md b/doc/model_explainer.md new file mode 100644 index 0000000..2d4444f --- /dev/null +++ b/doc/model_explainer.md @@ -0,0 +1,167 @@ +# Model Explainer + +`pyTorchAutoForge.evaluation.explainability` provides typed explainability +requests and dispatches them to SHAP or Captum adapters. + +Install optional explainability dependencies first: + +```bash +python -m pip install "pyTorchAutoForge[explain]" +``` + +## Captum Integrated Gradients + +Use Captum methods when you have a differentiable Torch model and tensor inputs. + +```python +import torch + +from pyTorchAutoForge.evaluation.explainability import ( + CaptumIntegratedGradientsConfig, + ExplainerBackendType, + ExplainerConfig, + ExplainerInputSpec, + ExplainerOutputSpec, + ExplainerRequest, + ExplainerTaskType, + ExplainerTargetSpec, + ModelExplainer, +) + +model = torch.nn.Sequential( + torch.nn.Linear(3, 8), + torch.nn.ReLU(), + torch.nn.Linear(8, 1), +) + +inputs = torch.tensor( + [ + [0.2, 0.4, 0.8], + [0.5, 0.1, 0.3], + ], + dtype=torch.float32, +) + +request = ExplainerRequest( + model=model, + inputs=inputs, + config=ExplainerConfig( + method_config=CaptumIntegratedGradientsConfig(n_steps=16), + backend=ExplainerBackendType.TORCH, + device="cpu", + ), + input_spec=ExplainerInputSpec( + feature_names=("mass", "velocity", "temperature"), + ), + output_spec=ExplainerOutputSpec( + task_type=ExplainerTaskType.REGRESSION, + output_names=("score",), + ), + target_spec=ExplainerTargetSpec(target_index=0), +) + +result = ModelExplainer().explain(request) + +print(result.values.shape) +print(result.method) +``` + +Expected output shape: + +```text +torch.Size([2, 3]) +captum.integrated_gradients +``` + +## SHAP Tabular Explanation + +Use SHAP when you need black-box, tabular explanations. Torch models and Python +callables are supported. + +```python +import torch + +from pyTorchAutoForge.evaluation.explainability import ( + ExplainerBackendType, + ExplainerConfig, + ExplainerInputSpec, + ExplainerOutputSpec, + ExplainerRequest, + ExplainerTaskType, + ModelExplainer, + ShapExplainerConfig, +) + +model = torch.nn.Sequential( + torch.nn.Linear(3, 1), +) + +inputs = torch.tensor( + [ + [0.2, 0.4, 0.8], + [0.5, 0.1, 0.3], + [0.9, 0.6, 0.2], + ], + dtype=torch.float32, +) + +request = ExplainerRequest( + model=model, + inputs=inputs, + background_data=inputs, + config=ExplainerConfig( + method_config=ShapExplainerConfig(max_background_samples=3), + backend=ExplainerBackendType.TORCH, + ), + input_spec=ExplainerInputSpec( + feature_names=("mass", "velocity", "temperature"), + ), + output_spec=ExplainerOutputSpec( + task_type=ExplainerTaskType.REGRESSION, + output_names=("score",), + ), +) + +result = ModelExplainer().explain(request) + +print(result.values.shape) +print(result.method) +``` + +Expected output: + +```text +(3, 3) +shap +``` + +## Saving Results + +Set `save_outputs=True` to write portable artifacts: + +```python +request.config.save_outputs = True +request.config.output_folder = "explainer_outputs" +result = ModelExplainer().explain(request) + +print(result.metadata["saved_artifacts"]) +``` + +Saved files: + +- `explanation.npz`: arrays such as values, data, predictions, and names. +- `explanation.json`: method, backend, metadata, and raw object type names. + +## Public API + +- {class}`pyTorchAutoForge.evaluation.explainability.ModelExplainer` +- {class}`pyTorchAutoForge.evaluation.explainability.ExplainerConfig` +- {class}`pyTorchAutoForge.evaluation.explainability.ExplainerRequest` +- {class}`pyTorchAutoForge.evaluation.explainability.ExplainerInputSpec` +- {class}`pyTorchAutoForge.evaluation.explainability.ExplainerOutputSpec` +- {class}`pyTorchAutoForge.evaluation.explainability.ExplainerTargetSpec` +- {class}`pyTorchAutoForge.evaluation.explainability.ShapExplainerConfig` +- {class}`pyTorchAutoForge.evaluation.explainability.CaptumIntegratedGradientsConfig` +- {class}`pyTorchAutoForge.evaluation.explainability.CaptumSaliencyConfig` +- {class}`pyTorchAutoForge.evaluation.explainability.CaptumGradientShapConfig` +- {class}`pyTorchAutoForge.evaluation.explainability.ExplainerResult` diff --git a/doc/requirements.txt b/doc/requirements.txt index 3dbe87f..6c35e66 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,6 +1,5 @@ sphinx -sphinx_rtd_theme -numpy -pyyaml -matplotlib -torch \ No newline at end of file +pydata-sphinx-theme +myst-parser +sphinx-autoapi +sphinx-copybutton diff --git a/doc/roadmap.md b/doc/roadmap.md new file mode 100644 index 0000000..c4e01b4 --- /dev/null +++ b/doc/roadmap.md @@ -0,0 +1,15 @@ +# Development Roadmap + +## Active Infrastructure Work + +- GitHub Pages documentation with Sphinx and the PyData theme. +- Test markers for slow, GPU, visual, integration, and export paths. +- Coverage reporting through `pytest-cov` and existing coverage config. +- Better split between runnable examples and pytest modules. + +## Near-Term Test Cleanup + +- Replace external dataset tests with temp-fixture datasets where possible. +- Keep expensive external-data tests under `slow` and `integration` markers. +- Convert TCP server smoke checks to bounded ephemeral-port tests. +- Add seeded edge-case checks for augmentation geometry and dataset selection. diff --git a/doc/runtime_export.md b/doc/runtime_export.md new file mode 100644 index 0000000..ab43834 --- /dev/null +++ b/doc/runtime_export.md @@ -0,0 +1,98 @@ +# Runtime And Export + +```{admonition} Codex-generated note +:class: note + +This guide is partially generated by Codex and should be reviewed against the current source before treating examples as authoritative. +``` + +## When to use this module + +Use {mod}`pyTorchAutoForge.api` when models need to cross the boundary from Python training code into saved Torch artifacts, ONNX files, runtime sessions, MATLAB wrappers, or deployment backends. The stable path starts with Torch I/O and ONNX export; the generic runtime facade then delegates inference to ONNX Runtime or TensorRT-specific backends. + +Prefer this module when: + +- checkpoints should be saved with a consistent naming convention; +- ONNX export should share path handling, validation flags, and backend selection; +- inference code needs one facade that can switch between ONNX and TensorRT; +- TensorRT is an optional deployment backend rather than a required local dependency. + +## Minimal example + +```python +from pathlib import Path +from tempfile import TemporaryDirectory + +import torch + +from pyTorchAutoForge.api.onnx import ModelHandlerONNx +from pyTorchAutoForge.api.torch import AutoForgeModuleSaveMode, LoadModel, SaveModel + +model_ = torch.nn.Linear(2, 1).eval() +example_input_ = torch.ones(1, 2) + +with TemporaryDirectory() as tmp_dir_: + model_base_ = Path(tmp_dir_) / "linear" + SaveModel( + model=model_, + model_filename=model_base_, + save_mode=AutoForgeModuleSaveMode.MODEL_STATE_DICT, + target_device=torch.device("cpu"), + ) + loaded_model_ = LoadModel( + model=torch.nn.Linear(2, 1), + model_filename=str(model_base_) + "_statedict", + load_strict=True, + ) + + onnx_path_ = Path(tmp_dir_) / "linear.onnx" + handler_ = ModelHandlerONNx( + model=loaded_model_.eval(), + dummy_input_sample=example_input_, + onnx_export_path=str(onnx_path_), + opset_version=13, + run_export_validation=False, + generate_report=False, + run_onnx_simplify=False, + ) + exported_path_ = handler_.export_onnx(backend="legacy") + + print((model_base_.parent / "linear_statedict.pth").is_file()) + print(Path(exported_path_).suffix) +``` + +Expected output, ignoring informational save/export logs: + +```text +True +.onnx +``` + +## Common workflow + +1. Save Torch models through `SaveModel` using a save mode that matches the downstream consumer. +2. Export ONNX through `ModelHandlerONNx.export_onnx`, with validation enabled for release artifacts. +3. Use `OnnxRuntimeApi` directly for ONNX-only inference or `ModelRuntimeApi` when backend dispatch should be configured at call time. +4. Treat TensorRT export and runtime as optional: use it when local `tensorrt`, CUDA, and builder tooling are available. +5. Keep ONNX input/output names and dynamic axes aligned with model call signatures before sending artifacts to external runtimes. + +## API links + +- {mod}`pyTorchAutoForge.api` +- {mod}`pyTorchAutoForge.api.torch` +- {mod}`pyTorchAutoForge.api.onnx` +- {mod}`pyTorchAutoForge.api.runtime` +- {mod}`pyTorchAutoForge.api.tensorrt` +- {func}`pyTorchAutoForge.api.torch.torchModulesIO.SaveModel` +- {func}`pyTorchAutoForge.api.torch.torchModulesIO.LoadModel` +- {class}`pyTorchAutoForge.api.onnx.ModelHandlerONNx.ModelHandlerONNx` +- {class}`pyTorchAutoForge.api.onnx.OnnxRuntimeApi.OnnxRuntimeApi` +- {class}`pyTorchAutoForge.api.runtime.ModelRuntimeApi.ModelRuntimeApi` +- {class}`pyTorchAutoForge.api.tensorrt.TRTengineExporter.TRTengineExporter` +- {class}`pyTorchAutoForge.api.tensorrt.TensorrtRuntimeApi.TensorrtRuntimeApi` + +## Notes and limitations + +- TensorRT APIs require local CUDA/TensorRT installation and suitable engine-building tools. Keep TensorRT examples optional in portable docs. +- ONNX export behavior depends on installed Torch and ONNX versions. For release artifacts, test both export and runtime inference with representative inputs. +- TCP, MATLAB, and MLflow API surfaces are documented in the generated API reference, but they are not expanded into this runtime/export guide. diff --git a/doc/sources/pyTorchAutoForge.datasets.rst b/doc/sources/pyTorchAutoForge.datasets.rst deleted file mode 100644 index 7f2a0c0..0000000 --- a/doc/sources/pyTorchAutoForge.datasets.rst +++ /dev/null @@ -1,15 +0,0 @@ -pyTorchAutoForge.datasets -========================= - -.. automodule:: pyTorchAutoForge.datasets - - -.. rubric:: Modules - -.. autosummary:: - :toctree: - :recursive: - - DataAugmentation - DataloaderIndex - DatasetClasses diff --git a/doc/sources/pyTorchAutoForge.evaluation.rst b/doc/sources/pyTorchAutoForge.evaluation.rst deleted file mode 100644 index 8db47d2..0000000 --- a/doc/sources/pyTorchAutoForge.evaluation.rst +++ /dev/null @@ -1,15 +0,0 @@ -pyTorchAutoForge.evaluation -=========================== - -.. automodule:: pyTorchAutoForge.evaluation - - -.. rubric:: Modules - -.. autosummary:: - :toctree: - :recursive: - - ModelEvaluator - ModelProfiler - ResultsPlotter diff --git a/doc/sources/pyTorchAutoForge.hparams_optim.rst b/doc/sources/pyTorchAutoForge.hparams_optim.rst deleted file mode 100644 index 0d3d6dd..0000000 --- a/doc/sources/pyTorchAutoForge.hparams_optim.rst +++ /dev/null @@ -1,15 +0,0 @@ -pyTorchAutoForge.hparams\_optim -=============================== - -.. automodule:: pyTorchAutoForge.hparams_optim - - -.. rubric:: Modules - -.. autosummary:: - :toctree: - :recursive: - - ModelHparamsOptimizer - OptunaStudyAnalyzer - optuna_auxiliary diff --git a/doc/sources/pyTorchAutoForge.optimization.rst b/doc/sources/pyTorchAutoForge.optimization.rst deleted file mode 100644 index 20435ec..0000000 --- a/doc/sources/pyTorchAutoForge.optimization.rst +++ /dev/null @@ -1,14 +0,0 @@ -pyTorchAutoForge.optimization -============================= - -.. automodule:: pyTorchAutoForge.optimization - - -.. rubric:: Modules - -.. autosummary:: - :toctree: - :recursive: - - ModelTrainingManager - lossFunctionsClasses diff --git a/doc/sources/pyTorchAutoForge.setup.rst b/doc/sources/pyTorchAutoForge.setup.rst deleted file mode 100644 index d939d91..0000000 --- a/doc/sources/pyTorchAutoForge.setup.rst +++ /dev/null @@ -1,14 +0,0 @@ -pyTorchAutoForge.setup -====================== - -.. automodule:: pyTorchAutoForge.setup - - -.. rubric:: Modules - -.. autosummary:: - :toctree: - :recursive: - - AutoForgeInit - BaseConfigClass diff --git a/doc/sources/pyTorchAutoForge.utils.rst b/doc/sources/pyTorchAutoForge.utils.rst deleted file mode 100644 index fcd8661..0000000 --- a/doc/sources/pyTorchAutoForge.utils.rst +++ /dev/null @@ -1,19 +0,0 @@ -pyTorchAutoForge.utils -====================== - -.. automodule:: pyTorchAutoForge.utils - - -.. rubric:: Modules - -.. autosummary:: - :toctree: - :recursive: - - ArgsParser - DeviceManager - LossLandscapeVisualizer - conversion_utils - pytest_test - timing_utils - utils diff --git a/doc/testing.md b/doc/testing.md new file mode 100644 index 0000000..af94d3e --- /dev/null +++ b/doc/testing.md @@ -0,0 +1,44 @@ +# Testing + +This page is for contributors and maintainers. It documents PTAF test commands, +marker policy, and coverage checks; user-facing module workflows live in the +User Guide. + +## Default Test Run + +```bash +./run_tests.sh -- -q +``` + +`run_tests.sh` defaults to `autoforge`. Override when needed: + +```bash +./run_tests.sh -e other_env -- -q +``` + +## Marker Policy + +Default collection skips tests marked `slow`, `gpu`, or `visual`. + +Use explicit flags when needed: + +```bash +./run_tests.sh -- --run-slow -q +./run_tests.sh -- --run-gpu -q +./run_tests.sh -- --run-visual -q +``` + +Markers: + +- `unit`: fast local unit test. +- `integration`: spans multiple subsystems or external-style behavior. +- `slow`: intentionally excluded from default runs. +- `gpu`: requires usable CUDA kernels, not just CUDA discovery. +- `visual`: plotting or visual-inspection-oriented test. +- `export`: model export or serialization path. + +## Coverage + +```bash +./run_tests.sh -- --cov=pyTorchAutoForge --cov-report=term-missing:skip-covered +``` diff --git a/doc/training_monitoring.md b/doc/training_monitoring.md new file mode 100644 index 0000000..b12e444 --- /dev/null +++ b/doc/training_monitoring.md @@ -0,0 +1,116 @@ +# Training And Monitoring + +```{admonition} Codex-generated note +:class: note + +This guide is partially generated by Codex and should be reviewed against the current source before treating examples as authoritative. +``` + +## When to use this module + +Use {mod}`pyTorchAutoForge.optimization` and {mod}`pyTorchAutoForge.monitoring` when training code needs PTAF configuration objects, dataloader plumbing, checkpoint controls, loss helpers, and run loggers. The training manager owns the train/validation loop, while monitoring loggers fan out run parameters and metrics to MLflow, TensorBoard-compatible event files, or custom logger objects. + +Prefer these modules when: + +- a model should train from a reusable `ModelTrainingManagerConfig`; +- train/validation dataloaders are already grouped by `DataloaderIndex`; +- logs should be written through one interface regardless of backend; +- MLflow setup should be kept separate from model/training definitions. + +## Minimal example + +```python +import torch +from torch.utils.data import DataLoader, TensorDataset + +from pyTorchAutoForge.datasets import DataloaderIndex +from pyTorchAutoForge.monitoring import CompositeRunLogger +from pyTorchAutoForge.optimization import ( + ModelTrainingManager, + ModelTrainingManagerConfig, + TaskType, +) + + +class SpyLogger: + def __init__(self) -> None: + self.epochs = [] + + def log_run_params(self, params_dict, **kwargs) -> None: + self.params = dict(params_dict) + + def log_epoch_data(self, epoch_num, train_metrics, **kwargs) -> None: + self.epochs.append(epoch_num) + + +features_ = torch.randn(8, 3) +targets_ = torch.randn(8, 1) +train_loader_ = DataLoader(TensorDataset(features_[:6], targets_[:6]), batch_size=2) +valid_loader_ = DataLoader(TensorDataset(features_[6:], targets_[6:]), batch_size=2) +loader_index_ = DataloaderIndex(train_loader_, valid_loader_) + +model_ = torch.nn.Linear(3, 1) +optimizer_ = torch.optim.SGD(model_.parameters(), lr=1e-2) +config_ = ModelTrainingManagerConfig( + tasktype=TaskType.REGRESSION, + batch_size=2, + num_of_epochs=1, + initial_lr=1e-2, + optimizer=optimizer_, + mlflow_logging=False, + label_scaling_factors=torch.ones(1), + device="cpu", +) +trainer_ = ModelTrainingManager( + model=model_, + lossFcn=torch.nn.MSELoss(), + config=config_, + dataLoaderIndex=loader_index_, +) + +spy_logger_ = SpyLogger() +logger_ = CompositeRunLogger(spy_logger_) +logger_.log_run_params({"batch_size": trainer_.batch_size}) +logger_.log_epoch_data(epoch_num=trainer_.current_epoch, train_metrics={"loss": 0.0}) + +print(trainer_.num_of_epochs) +print(trainer_.trainingDataloader.batch_size) +print(spy_logger_.epochs) +``` + +Expected output, ignoring trainer setup logs: + +```text +1 +2 +[0] +``` + +## Common workflow + +1. Build Torch datasets and collect loaders in `DataloaderIndex`. +2. Define model, loss, optimizer, scheduler, and `ModelTrainingManagerConfig`. +3. Instantiate `ModelTrainingManager`, attach dataloaders, and run `trainAndValidate()` for real training jobs. +4. Use `CompositeRunLogger` when multiple monitoring backends should receive the same metrics. +5. Configure MLflow explicitly through {mod}`pyTorchAutoForge.api.mlflow` helpers before enabling MLflow logging. + +## API links + +- {mod}`pyTorchAutoForge.optimization` +- {mod}`pyTorchAutoForge.monitoring` +- {mod}`pyTorchAutoForge.api.mlflow` +- {mod}`pyTorchAutoForge.hparams_optim` +- {class}`pyTorchAutoForge.optimization.ModelTrainingManager.ModelTrainingManager` +- {class}`pyTorchAutoForge.optimization.ModelTrainingManager.ModelTrainingManagerConfig` +- {class}`pyTorchAutoForge.optimization.ModelTrainingManager.TaskType` +- {func}`pyTorchAutoForge.optimization.ModelTrainingManager.FreezeModel` +- {class}`pyTorchAutoForge.monitoring.run_loggers.CompositeRunLogger` +- {class}`pyTorchAutoForge.monitoring.run_loggers.MlflowRunLogger` +- {class}`pyTorchAutoForge.monitoring.run_loggers.TensorBoardRunLogger` +- {func}`pyTorchAutoForge.api.mlflow.mlflow_api.SetupMlflowTrackingSession` + +## Notes and limitations + +- Training manager examples here stop before a full optimization loop to keep docs lightweight. Use tests and project examples for full epoch-level behavior. +- MLflow and TensorBoard logging depend on optional packages and local filesystem/server setup. +- Hyperparameter and Optuna utilities remain API-linked for now; they are not promoted to a task guide here. diff --git a/doc/utilities.md b/doc/utilities.md new file mode 100644 index 0000000..a4eab3a --- /dev/null +++ b/doc/utilities.md @@ -0,0 +1,83 @@ +# Utilities + +```{admonition} Codex-generated note +:class: note + +This guide is partially generated by Codex and should be reviewed against the current source before treating examples as authoritative. +``` + +## When to use this module + +Use {mod}`pyTorchAutoForge.utils` for small cross-cutting helpers that support model code, dataset code, timing checks, device selection, conversion, and argument parsing. These helpers are useful around the core workflows, but they should not hide task-specific logic. + +Prefer this module when: + +- tensors and arrays need consistent conversion at API boundaries; +- quick timing checks should average repeated calls; +- model parameter size or trainable parameter counts are needed; +- a device selector should centralize CPU/CUDA/MPS decisions. + +## Minimal example + +```python +import numpy as np +import torch + +from pyTorchAutoForge.utils import ( + AddZerosPadding, + ComputeModelParamsStorageSize, + numpy_to_torch, + timeit_averaged_, + torch_to_numpy, +) + +array_ = np.array([1.0, 2.0], dtype=np.float32) +tensor_ = numpy_to_torch(array_) +doubled_ = torch_to_numpy(tensor_ * 2.0) + +model_ = torch.nn.Linear(2, 1) +elapsed_sec_ = timeit_averaged_(lambda value_: value_.sum().item(), 2, tensor_) + +print(doubled_.tolist()) +print(AddZerosPadding(7, "3")) +print(ComputeModelParamsStorageSize(model_) > 0.0) +print(elapsed_sec_ >= 0.0) +``` + +Expected output: + +```text +[2.0, 4.0] +007 +True +True +``` + +## Common workflow + +1. Use conversion helpers at module boundaries instead of duplicating tensor/NumPy conversion logic. +2. Use device helpers when user code should choose CPU, CUDA, MPS, or multi-device behavior consistently. +3. Wrap expensive smoke checks with timing helpers when comparing candidate implementations. +4. Keep context-management helpers close to operations that may block or time out. +5. Promote repeated task logic into a domain module once it becomes more than a general utility. + +## API links + +- {mod}`pyTorchAutoForge.utils` +- {mod}`pyTorchAutoForge.utils.DeviceManager` +- {mod}`pyTorchAutoForge.utils.conversion_utils` +- {mod}`pyTorchAutoForge.utils.timing_utils` +- {mod}`pyTorchAutoForge.utils.context_management` +- {mod}`pyTorchAutoForge.utils.argument_parsers` +- {func}`pyTorchAutoForge.utils.conversion_utils.torch_to_numpy` +- {func}`pyTorchAutoForge.utils.conversion_utils.numpy_to_torch` +- {func}`pyTorchAutoForge.utils.timing_utils.timeit_averaged` +- {func}`pyTorchAutoForge.utils.timing_utils.timeit_averaged_` +- {class}`pyTorchAutoForge.utils.DeviceManager.DeviceManager` +- {class}`pyTorchAutoForge.utils.context_management.TimeoutException` + +## Notes and limitations + +- Some utilities predate current naming and type-hint conventions. Prefer stable exported helpers over test fixtures or one-off scripts. +- `GetDevice()` is a legacy simple selector; use `GetDeviceMulti()` or `DeviceManager` when richer hardware choice matters. +- Utilities should remain thin. If a helper starts owning dataset, model, or runtime semantics, move that logic to the owning module. diff --git a/environment-ci.yml b/environment-ci.yml index 2cd4f5c..44bd627 100644 --- a/environment-ci.yml +++ b/environment-ci.yml @@ -3,9 +3,11 @@ channels: - conda-forge - defaults dependencies: - - python=3.11 + - python=3.12 - flake8 - pip - setuptools - wheel - pytest + - coverage + - pytest-cov diff --git a/install_all.sh b/install_all.sh deleted file mode 100755 index a8131bf..0000000 --- a/install_all.sh +++ /dev/null @@ -1,12 +0,0 @@ -echo "Hello! Installation script begins. This is going to install pytorch-autoforge with all the dependencies in a virtual env and build its documentation." -echo -e "Starting in 1 second...\n\n" -sleep 1 -sudo apt install python3.11 python3.11-venv # Install python3-venv -python3.11 -m venv .venvTorch # Create virtual environment -source .venvTorch/bin/activate # Activate virtual environment -pip install -e . --require-virtualenv # Install the package -#pip install -e -r requirements.txt --require-virtualenv # Install dependencies - -# Install sphinx and theme, and build the documentation -pip install sphinx sphinx_rtd_theme --require-virtualenv -make -C docs html \ No newline at end of file diff --git a/install_submodule.sh b/install_submodule.sh deleted file mode 100755 index b8979c5..0000000 --- a/install_submodule.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -cd $SCRIPT_DIR - -echo "Hello! Installation script begins. This is going to install pytorch-autoforge with all the dependencies in a virtual env and build its documentation." -echo -e "Starting in 1 second...\n\n" -sleep 1 -sudo apt install python3.11 python3.11-venv # Install python3-venv - -cd ../.. # Go to the root directory of the project -python3.11 -m venv .venvTorch # Create virtual environment -source .venvTorch/bin/activate # Activate virtual environment -pip install -r $SCRIPT_DIR/requirements.txt --require-virtualenv # Install dependencies -pip install $SCRIPT_DIR --require-virtualenv -e # Install the package in editable mode -cd $SCRIPT_DIR - -# Install sphinx and theme, and build the documentation -pip install sphinx sphinx_rtd_theme --require-virtualenv -make -C docs html \ No newline at end of file diff --git a/install_with_venv.sh b/install_with_venv.sh deleted file mode 100755 index ea36972..0000000 --- a/install_with_venv.sh +++ /dev/null @@ -1,202 +0,0 @@ -# Default values -jetson_target=0 -editable_mode=1 -sudo_mode=0 -venv_name=".venvTorch" - -# Parse options using getopt -# NOTE: no ":" after option means no argument, ":" means required argument, "::" means optional argument -OPTIONS=j,v:,s -LONGOPTIONS=jetson_target,venv_name:,sudo_mode - -# Parsed arguments list with getopt -PARSED=$(getopt --options ${OPTIONS} --longoptions ${LONGOPTIONS} --name "$0" -- "$@") -# TODO check if this is where I need to modify something to allow things like -B build, instead of -Bbuild - -# Check validity of input arguments -if [[ $? -ne 0 ]]; then - exit 2 -fi - -# Parse arguments -eval set -- "$PARSED" - -# Process options (change default values if needed) -while true; do - case "$1" in - -j|--jetson_target) - jetson_target=1 - echo "Jetson target selected..." - shift - ;; - -v|--venv_name) - venv_name=$2 - echo "Virtual environment name: $venv_name" - shift 2 - ;; - -s|--sudo_mode) - sudo_mode=1 - echo "Sudo mode requested..." - shift - ;; - --) - shift - break - ;; - *) - echo "Not a valid option: $1" >&2 - exit 3 - ;; - esac -done - -if [ $jetson_target -eq 1 ] && [ ! $sudo_mode -eq 1 ]; then - echo "Jetson target requires sudo mode. Please use -s option." - exit 1 -fi - -if [ $sudo_mode -eq 1 ]; then - echo "Running in sudo mode..." - sudo apt install python3.11 python3.11-venv # Install python3-venv -fi - - -if [ $jetson_target -eq 1 ] && [ ! -f /usr/local/cuda/lib64/libcusparseLt.so ]; then - echo "libcusparseLt.so not found. Downloading and installing..." - # if not exist, download and copy to the directory - wget https://developer.download.nvidia.com/compute/cusparselt/redist/libcusparse_lt/linux-sbsa/libcusparse_lt-linux-sbsa-0.5.2.1-archive.tar.xz - tar xf libcusparse_lt-linux-sbsa-0.5.2.1-archive.tar.xz - sudo cp -a libcusparse_lt-linux-sbsa-0.5.2.1-archive/include/* /usr/local/cuda/include/ - sudo cp -a libcusparse_lt-linux-sbsa-0.5.2.1-archive/lib/* /usr/local/cuda/lib64/ - rm libcusparse_lt-linux-sbsa-0.5.2.1-archive.tar.xz - rm -r libcusparse_lt-linux-sbsa-0.5.2.1-archive -fi - -if [ $jetson_target -eq 1 ]; then - - # Create virtualenv for Jetson - python3 -m venv $venv_name --system-site-packages # Create virtual environment - source $venv_name/bin/activate # Activate virtual environment - - #pip install -r requirements.txt --require-virtualenv # Install dependencies - #pip install -e . --require-virtualenv # Install the package in editable mode - - # Tools for building and installing wheels - echo "Installing setuptools, twine, and build..." - pip install setuptools twine build --require-virtualenv - python3 -m ensurepip --upgrade --require-virtualenv - python3 -m pip install --upgrade pip --require-virtualenv - - # Install key modules not managed by dependencies installation for versioning reasons - echo "Installing additional key modules..." - - # Remove torch and torchvision - pip uninstall -y torch torchvision torchaudio - - # Install torch for Jetson - pip install torch https://developer.download.nvidia.com/compute/redist/jp/v61/pytorch/torch-2.5.0a0+872d972e41.nv24.08.17622132-cp310-cp310-linux_aarch64.whl --require-virtualenv - - # Build and install torchvision from source - # From guide: https://github.com/azimjaan21/jetpack-6.1-pytorch-torchvision-/blob/main/README.md - git clone https://github.com/pytorch/vision.git - cd vision - git checkout tags/v0.20.0 - python3 setup.py install - - # Clean up - cd .. - sudo rm -r vision - - # Try to build torch-tensorrt - source $venv_name/bin/activate # Activate virtual environment - - #pip install norse==1.0.0 aestream tonic expelliarmus --ignore-requires-python3 --require-virtualenv # FIXME: build fails due to "CUDA20" entry - - pip install nvidia-pyindex pycuda --require-virtualenv - - # ACHTUNG: this must run correctly before torch_tensorrt - pip install "nvidia-modelopt[all]" -U --extra-index-url https://pypi.nvidia.com - - # Install torch-tensorrt from source - mkdir lib - cd lib - - # Check if submodule exists - if [ -d "TensorRT" ]; then - echo "TensorRT submodule exists" - else - git submodule add --branch release/2.5 https://github.com/pytorch/TensorRT.git # Try to use release/2.6 (latest) - fi - - cd TensorRT - git checkout release/2.5 - git pull - - # Install required python3 packages of torch-tensorrt - python3 -m pip install -r toolchains/jp_workspaces/requirements.txt # NOTE: Installs the correct version of setuptools. Do not touch it. - - cuda_version=$(nvcc --version | grep Cuda | grep release | cut -d ',' -f 2 | sed -e 's/ release //g') - export TORCH_INSTALL_PATH=$(python3 -c "import torch, os; print(os.path.dirname(torch.__file__))") - export SITE_PACKAGE_PATH=${TORCH_INSTALL_PATH::-6} - export CUDA_HOME=/usr/local/cuda-${cuda_version}/ - - # Replace the MODULE.bazel with the jetpack one # DOUBT: why needed? - cat toolchains/jp_workspaces/MODULE.bazel.tmpl | envsubst > MODULE.bazel - - # Build and install torch_tensorrt wheel file with CXX11 ABI - python3 setup.py install --use-cxx11-abi - cd ../.. - - # Finally, build pyTorchAutoForge wheel - if [ "$editable_mode" = true ]; then - echo "Building and installing pyTorchAutoForge in editable mode..." - pip install -e . --require-virtualenv # Install the package in editable mode - else - echo "Building and installing pyTorchAutoForge wheel..." - python3 -m build - pip install -e dist/*.whl --require-virtualenv # Install pyTorchAutoForge wheel - fi - -else - - # Create virtualenv for other targets - python3 -m venv $venv_name # Create virtual environment - source $venv_name/bin/activate # Activate virtual environment - - #pip install -r requirements.txt --require-virtualenv # Install dependencies that do not cause issues... - #python3 -m pip install -r toolchains/jp_workspaces/test_requirements.txt # Required for test cases - - # Tools for building and installing wheels - echo "Installing setuptools, twine, and build..." - pip install setuptools twine build --require-virtualenv - python3 -m ensurepip --upgrade --require-virtualenv - python3 -m pip install --upgrade pip --require-virtualenv - - # Install key modules not managed by dependencies installation for versioning reasons - echo "Installing additional key modules..." - pip install norse tonic aestream expelliarmus --require-virtualenv - - pip install pynvml --require-virtualenv # Install pynvml for GPU monitoring - - # Build pyTorchAutoForge wheel - if [ "$editable_mode" = true ]; then - echo "Building and installing pyTorchAutoForge in editable mode..." - pip install -e . --require-virtualenv # Install the package in editable mode - else - echo "Building and installing pyTorchAutoForge wheel..." - python3 -m build - pip install -e dist/*.whl --require-virtualenv # Install pyTorchAutoForge wheel - fi - - # Install tools for model optimization and deployment - echo "Installing tools for model optimization and deployment by Nvidia..." - python3 -m pip install pycuda torch torchvision torch-tensorrt tensorrt "nvidia-modelopt[all]" -U --extra-index-url https://pypi.nvidia.com - fi - - deactivate # Deactivate virtual environment if any - source $venv_name/bin/activate # Activate virtual environment - # Check installation by printing versions in python3 - python3 -m tests/.configuration/test_env - - - diff --git a/pyproject.toml b/pyproject.toml index 0479bc0..a747d9b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pyTorchAutoForge" -description='PyTorchAutoForge library is based on raw PyTorch and designed to automate DNN development, model tracking and deployment, tightly integrated with MLflow and Optuna. It supports Spiking networks libraries (WIP). Deployment can be performed using ONNX, pyTorch facilities or TensorRT (WIP). The library is designed to be compatible with Jetson Orin Nano Jetpack rev6.1, with bash script to automatically configure virtualenv.' +description='PyTorchAutoForge library is based on raw PyTorch and designed to automate DNN development, model tracking and deployment, tightly integrated with MLflow and Optuna. It supports Spiking networks libraries (WIP). Deployment can be performed using ONNX, pyTorch facilities or TensorRT (WIP). The library is designed to be compatible with Jetson Orin Nano Jetpack rev6.1, with a bash script to configure conda-based development environments.' readme = "README.md" requires-python = ">=3.10" dynamic = ["version"] @@ -16,7 +16,6 @@ dependencies = [ "torch-tb-profiler", "scikit-learn<=1.6.1", "dacite", - "captum", "scipy", "numpy", "onnx", @@ -34,7 +33,7 @@ dependencies = [ "colorama", "msgpack", "torchinfo", - "torch>=2.7,<2.10.0; platform_machine == 'x86_64'", # Only for x86_64. Jetson requires nvidia version + "torch>=2.7,<2.12; platform_machine == 'x86_64'", # Only for x86_64. Jetson requires nvidia version "torchvision; platform_machine == 'x86_64'", # Only for x86_64. Jetson requires nvidia version "norse; platform_machine == 'x86_64'", "tonic; platform_machine == 'x86_64'", @@ -57,6 +56,17 @@ classifiers=[ cuda_all = [ "pynvml; platform_machine == 'x86_64'", ] +test = [ + "coverage[toml]", + "pytest-cov", +] +docs = [ + "sphinx", + "pydata-sphinx-theme", + "myst-parser", + "sphinx-autoapi", + "sphinx-copybutton", +] [tool.hatch.build.targets.wheel] packages = ["pyTorchAutoForge"] @@ -72,6 +82,14 @@ addopts = """--ignore=lib/ --disable-warnings """ console_output_style = "count" +markers = [ + "unit: fast test with no external service, large dataset, GPU, or long-running export requirement", + "integration: test spanning multiple subsystems or external-style runtime behavior", + "slow: test intentionally excluded from default runs unless --run-slow is passed", + "gpu: test requiring a usable CUDA runtime, excluded unless --run-gpu is passed", + "visual: plotting or visual-inspection-oriented test, excluded unless --run-visual is passed", + "export: model export or runtime-serialization test", +] # Specify folders to ignore norecursedirs = ["*deprecated*", "*data", "*cache", "**/site-packages", "dist", "build"] diff --git a/release_to_pypi.sh b/release_to_pypi.sh index 899d9f0..9e1c8d1 100755 --- a/release_to_pypi.sh +++ b/release_to_pypi.sh @@ -1,21 +1,101 @@ #!/bin/bash -source ~/miniconda3/etc/profile.d/conda.sh -conda activate autoforge +set -euo pipefail -# Clear dist folder -if [ -d "dist" ]; then - rm -r dist +# Script variables +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="${SCRIPT_DIR}" + +CONDA_EXE="${CONDA_EXE:-conda}" +ENV_NAME="${CONDA_ENV:-autoforge}" +REPOSITORY="pypi" +DIST_DIR="dist" +CLEAN=1 +UPLOAD=1 + +# Usage guide +usage() { + cat <<'EOF' +Usage: ./release_to_pypi.sh [options] + +Builds package artifacts, runs twine check, then uploads to PyPI by default. + +Options: + -e, --env-name NAME Conda environment name (default: CONDA_ENV or autoforge) + --conda-exe PATH Conda executable (default: conda) + -r, --repository NAME Twine repository name (default: pypi) + --dist-dir PATH Distribution output directory (default: dist) + --no-clean Keep existing dist and egg-info folders + --skip-upload Build and check only + -h, --help Show this help +EOF +} + +# Parser loop +while [[ $# -gt 0 ]]; do + case "$1" in + -e|--env-name) + ENV_NAME="$2" + shift 2 + ;; + --conda-exe) + CONDA_EXE="$2" + shift 2 + ;; + -r|--repository) + REPOSITORY="$2" + shift 2 + ;; + --dist-dir) + DIST_DIR="$2" + shift 2 + ;; + --no-clean) + CLEAN=0 + shift + ;; + --skip-upload) + UPLOAD=0 + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +if ! command -v "${CONDA_EXE}" >/dev/null 2>&1; then + echo "Conda executable not found: ${CONDA_EXE}" >&2 + exit 1 fi -# Remove egg-info folder -if [ -d "*.egg-info" ]; then - rm -r *.egg-info +# Source conda environment +CONDA_BASE="$("${CONDA_EXE}" info --base)" +# shellcheck source=/dev/null +source "${CONDA_BASE}/etc/profile.d/conda.sh" +conda activate "${ENV_NAME}" + +cd "${REPO_ROOT}" + +# Clean dist and egg-info if requested (default: yes) +if [[ "${CLEAN}" -eq 1 ]]; then + rm -rf "${DIST_DIR}" + find . -maxdepth 1 -type d -name "*.egg-info" -exec rm -rf {} + fi -# Build latest wheel -python -m build +# Build and check artifacts +python -m pip install --upgrade build twine +python -m build --outdir "${DIST_DIR}" +python -m twine check "${DIST_DIR}"/* -if [ -d "dist" ]; then - twine check dist/* - twine upload --repository pypi dist/* #-u __token__ -p $PYPI_TOKEN --verbose -fi \ No newline at end of file +# Upload to PyPI if requested (default: yes) +if [[ "${UPLOAD}" -eq 1 ]]; then + python -m twine upload --repository "${REPOSITORY}" "${DIST_DIR}"/* +else + echo "Upload skipped. Artifacts are in ${DIST_DIR}." +fi diff --git a/requirements.txt b/requirements.txt index 6637d2d..1b2db59 100644 --- a/requirements.txt +++ b/requirements.txt @@ -245,15 +245,6 @@ smmap<=5.0.1 sniffio<=1.3.1 snowballstemmer<=2.2.0 soupsieve<=2.5 -Sphinx<=7.4.7 -sphinx-rtd-theme<=2.0.0 -sphinxcontrib-applehelp<=2.0.0 -sphinxcontrib-devhelp<=2.0.0 -sphinxcontrib-htmlhelp<=2.1.0 -sphinxcontrib-jquery<=4.1 -sphinxcontrib-jsmath<=1.0.1 -sphinxcontrib-qthelp<=2.0.0 -sphinxcontrib-serializinghtml<=2.0.0 SQLAlchemy<=2.0.35 sqlparse<=0.5.1 sympy<=1.13.2 @@ -293,4 +284,4 @@ wrapt<=1.16.0 xdg<=5 #xkit<=0.0.0 yacs<=0.1.8 -zipp<=3.20.2 \ No newline at end of file +zipp<=3.20.2