diff --git a/.gitattributes b/.gitattributes index b495b445b..c3c477560 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13,3 +13,7 @@ *.grid filter=lfs diff=lfs merge=lfs -text *.nvdb filter=lfs diff=lfs merge=lfs -text *.gif filter=lfs diff=lfs merge=lfs -text + +# Exclude vendored code from project language stats +warp/native/cutlass/** linguist-vendored +tools/** linguist-vendored diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml new file mode 100644 index 000000000..7f80e9324 --- /dev/null +++ b/.github/workflows/sphinx.yml @@ -0,0 +1,30 @@ +name: Deploy Sphinx documentation to Pages + +# Runs on pushes targeting the default branch +on: + push: + branches: [github-pages] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - name: Build HTML + uses: ammaraskar/sphinx-action@master + with: + docs-folder: "docs/" + build-command: "python ../build_docs.py" + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: html-docs + path: docs/_build/html/ + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + if: github.ref == 'refs/heads/github-pages' + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: docs/_build/html diff --git a/.gitignore b/.gitignore index 32462e43d..4e9d2c85e 100644 --- a/.gitignore +++ b/.gitignore @@ -18,8 +18,7 @@ archive /_repo examples/assets/.thumbs /examples/tmp -/docs/_build/doctrees -/docs/_build/html/_static/fonts +/docs/_build /warp_lang.egg-info exts/omni.warp/omni/warp/ogn/tests/usd build/lib/ @@ -29,4 +28,5 @@ exts/omni.warp/config/extension.gen.toml /build /dist .coverage +.cache warp/tests/outputs diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000..9fe2f2c2c --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,24 @@ +stages: + - deploy + +pages: + stage: deploy + image: python:3.11-slim + before_script: + - echo -e "\\e[0Ksection_start:`date +%s`:my_first_section[collapsed=true]\\r\\e[0KSet up docs environment" + - apt-get update && apt-get install make --no-install-recommends -y + - python -m pip install --upgrade pip + - python -m pip install -r docs/requirements.txt + - echo -e "\\e[0Ksection_end:`date +%s`:my_first_section\\r\\e[0K" + script: + - cd docs && make clean + - python ../build_docs.py --no-color + after_script: + - mv docs/_build/html/ ./public/ + artifacts: + paths: + - public + rules: + - if: $CI_COMMIT_BRANCH == "master" + tags: + - pages diff --git a/CHANGELOG.md b/CHANGELOG.md index c452e8f7f..53b680d60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,15 +5,15 @@ - Fix for kernel caching when function argument types change - Fix code-gen ordering of dependent structs - Fix for `wp.Mesh` build on MGPU systems -- Fix for name clash bug with adjoint code: https://github.com/NVIDIA/warp/issues/154 +- Fix for name clash bug with adjoint code: https://github.com/NVIDIA/warp/issues/154 - Add `wp.frac()` for returning the fractional part of a floating point value - Add support for custom native CUDA snippets using `@wp.func_native` decorator - Add support for batched matmul with batch size > 2^16-1 -- Add support for tranposed CUTLASS `wp.matmul()` and additional error checking +- Add support for transposed CUTLASS `wp.matmul()` and additional error checking - Add support for quad and hex meshes in `wp.fem` -- Detect and warn when C++ runtime doesn't match compiler during build, e.g.: libstdc++.so.6: version `GLIBCXX_3.4.30' not found +- Detect and warn when C++ runtime doesn't match compiler during build, e.g.: ``libstdc++.so.6: version `GLIBCXX_3.4.30' not found`` - Documentation update for `wp.BVH` -- Documentaiton and simplified API for runtime kernel specialization `wp.Kernel` +- Documentation and simplified API for runtime kernel specialization `wp.Kernel` ## [1.0.0-beta.4] - 2023-11-01 @@ -43,7 +43,11 @@ - Fix for `wp.func` to return a default value if function does not return on all control paths - Refactor `wp.fem` support for new basis functions, decoupled function spaces - Optimizations for `wp.noise` functions, up to 10x faster in most cases -- Optimizations for `type_size_in_bytes()` used in array construction +- Optimizations for `type_size_in_bytes()` used in array construction' + +### Breaking Changes + +- To support grid-stride kernels, `wp.tid()` can no longer be called inside `wp.func` functions. ## [1.0.0-beta.2] - 2023-09-01 @@ -144,7 +148,7 @@ ## [0.9.0] - 2023-06-01 - Add support for in-place modifications to vector, matrix, and struct types inside kernels (will warn during backward pass with `wp.verbose` if using gradients) -- Add support for step-through VSCode debugging of kernel code with standalone LLVM compiler, see `wp.breakpoint()`, and `test_debug.py` +- Add support for step-through VSCode debugging of kernel code with standalone LLVM compiler, see `wp.breakpoint()`, and `walkthrough_debug.py` - Add support for default values on built-in functions - Add support for multi-valued `@wp.func` functions - Add support for `pass`, `continue`, and `break` statements @@ -181,7 +185,7 @@ ## [0.8.1] - 2023-04-13 - Fix for regression when passing flattened numeric lists as matrix arguments to kernels -- Fix for regressions when passing wp.struct types with uninitialized (None) member attributes +- Fix for regressions when passing `wp.struct` types with uninitialized (`None`) member attributes ## [0.8.0] - 2023-04-05 @@ -215,20 +219,18 @@ - Optimizations for `wp.launch()`, up to 3x faster launches in common cases - Fix `wp.randf()` conversion to float to reduce bias for uniform sampling - Fix capture of `wp.func` and `wp.constant` types from inside Python closures -- Fix for CUDA on WSL +- Fix for CUDA on WSL - Fix for matrices in structs - Fix for transpose indexing for some non-square matrices - Enable Python faulthandler by default - Update to VS2019 -Breaking Changes ----------------- +### Breaking Changes - `wp.constant` variables can now be treated as their true type, accessing the underlying value through `constant.val` is no longer supported - `wp.sim.model.ground_plane` is now a `wp.array` to support gradient, users should call `builder.set_ground_plane()` to create the ground - `wp.sim` capsule, cones, and cylinders are now aligned with the default USD up-axis - ## [0.7.2] - 2023-02-15 - Reduce test time for vec/math types @@ -248,9 +250,9 @@ Breaking Changes - Add support for slicing `wp.array` types in Python - Add `wp.from_ptr()` helper to construct arrays from an existing allocation - Add support for `break` statements in ranged-for and while loops (backward pass support currently not implemented) -- Add built-in mathematic constants, see `wp.pi`, `wp.e`, `wp.log2e`, etc +- Add built-in mathematic constants, see `wp.pi`, `wp.e`, `wp.log2e`, etc. - Add built-in conversion between degrees and radians, see `wp.degrees()`, `wp.radians()` -- Add security pop-up for Kernel Node +- Add security pop-up for Kernel Node - Improve error handling for kernel return values ## [0.6.3] - 2023-01-31 @@ -336,7 +338,7 @@ Breaking Changes - Fix for hashing of `wp.constants()` not invalidating kernels - Fix for reload when multiple `.ptx` versions are present - Improved error reporting during code-gen - + ## [0.4.3] - 2022-09-20 - Update all samples to use GPU interop path by default @@ -360,7 +362,6 @@ Breaking Changes - Fix for debug flags not being set correctly on CUDA when `wp.config.mode == "debug"`, this enables bounds checking on CUDA kernels in debug mode - Fix for code gen of functions that do not return a value - ## [0.4.0] - 2022-08-09 - Fix for FP16 conversions on GPUs without hardware support @@ -373,18 +374,15 @@ Breaking Changes - Add support for cross-module `@wp.struct` references - Support running even if CUDA initialization failed, use `wp.is_cuda_available()` to check availability - Statically linking with the CUDA runtime library to avoid deployment issues - ### Breaking Changes - Removed `wp.runtime` reference from the top-level module, as it should be considered private - ## [0.3.2] - 2022-07-19 - Remove Torch import from `__init__.py`, defer import to `wp.from_torch()`, `wp.to_torch()` - ## [0.3.1] - 2022-07-12 - Fix for marching cubes reallocation after initialization @@ -396,7 +394,6 @@ Breaking Changes - Add support for using arbitrary external CUDA contexts, see `wp.map_cuda_device()`, `wp.unmap_cuda_device()` - Add PyTorch device aliasing functions, see `wp.device_from_torch()`, `wp.device_to_torch()` - ### Breaking Changes - A CUDA device is used by default, if available (aligned with `wp.get_preferred_device()`) @@ -404,7 +401,6 @@ Breaking Changes - `wp.synchronize()` now synchronizes all devices; for finer-grained control, use `wp.synchronize_device()` - Device alias `"cuda"` now refers to the current CUDA context, rather than a specific device like `"cuda:0"` or `"cuda:1"` - ## [0.3.0] - 2022-07-08 - Add support for FP16 storage type, see `wp.float16` @@ -429,7 +425,6 @@ Breaking Changes - Fix for reload of generated CPU kernel code on Linux - Fix for example scripts to output USD at 60 timecodes per-second (better Kit compatibility) - ## [0.2.3] - 2022-06-13 - Fix for incorrect 4d array bounds checking @@ -438,7 +433,6 @@ Breaking Changes - Array gradients are now allocated along with the arrays and accessible as `wp.array.grad`, users should take care to always call `wp.Tape.zero()` to clear gradients between different invocations of `wp.Tape.backward()` - Added `wp.array.fill_()` to set all entries to a scalar value (4-byte values only currently) - ### Breaking Changes - Tape `capture` option has been removed, users can now capture tapes inside existing CUDA graphs (e.g.: inside Torch) @@ -471,7 +465,7 @@ Breaking Changes ## [0.2.1] - 2022-05-11 - Fix for unit tests in Kit -- + ## [0.2.0] - 2022-05-02 ### Warp Core @@ -509,7 +503,7 @@ Breaking Changes - Fix for URDF importer and floating base support - Add examples showing how to use differentiable forward kinematics to solve inverse kinematics - Add examples for URDF cartpole and quadruped simulation - + ### Breaking Changes - `wp.volume_sample_world()` is now replaced by `wp.volume_sample_f/i/vec()` which operate in index (local) space. Users should use `wp.volume_world_to_index()` to transform points from world space to index space before sampling. @@ -517,7 +511,6 @@ Breaking Changes - `wp.array.length` member has been removed, please use `wp.array.shape` to access array dimensions, or use `wp.array.size` to get total element count - Marking `dense_gemm()`, `dense_chol()`, etc methods as experimental until we revisit them - ## [0.1.25] - 2022-03-20 - Add support for class methods to be Warp kernels @@ -528,7 +521,6 @@ Breaking Changes - Add support for floored division on integer types - Move tests into core library so they can be run in Kit environment - ## [0.1.24] - 2022-03-03 ### Warp Core diff --git a/build_docs.py b/build_docs.py index 4bcdd57de..2f492dd39 100644 --- a/build_docs.py +++ b/build_docs.py @@ -1,38 +1,47 @@ +# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. + import os -import sys import subprocess +import sys -import warp as wp - - -wp.init() +from warp.context import export_functions_rst, export_stubs # docs # disable sphinx color output os.environ["NO_COLOR"] = "1" -with open("docs/modules/functions.rst", "w") as function_ref: - wp.print_builtins(function_ref) +base_path = os.path.dirname(os.path.realpath(__file__)) + +with open(os.path.join(base_path, "docs", "modules", "functions.rst"), "w") as function_ref: + export_functions_rst(function_ref) # run Sphinx build try: - if os.name == 'nt': - subprocess.check_output("make.bat html", cwd="docs", shell=True) + docs_folder = os.path.join(base_path, "docs") + make_html_cmd = ["make.bat" if os.name == "nt" else "make", "html"] + + if os.name == "nt" or len(sys.argv) == 1: + subprocess.check_output(make_html_cmd, cwd=docs_folder) else: - subprocess.run("make clean", cwd="docs", shell=True) - subprocess.check_output("make html", cwd="docs", shell=True) + # Sphinx options were passed via the argument list + make_html_cmd.append("-e") + sphinx_options = " ".join(sys.argv[1:]) + subprocess.check_output(make_html_cmd, cwd=docs_folder, env=dict(os.environ, SPHINXOPTS=sphinx_options)) except subprocess.CalledProcessError as e: print(e.output.decode()) raise e - # generate stubs for autocomplete -stub_file = open("warp/stubs.py", "w") -wp.export_stubs(stub_file) -stub_file.close() +with open(os.path.join(base_path, "warp", "stubs.py"), "w") as stub_file: + export_stubs(stub_file) # code formatting -subprocess.run([sys.executable, "-m", "black", "warp/stubs.py"]) +subprocess.run([sys.executable, "-m", "black", os.path.join(base_path, "warp", "stubs.py")]) print("Finished") diff --git a/build_lib.py b/build_lib.py index 1738b5f3e..ce8f7c1cb 100644 --- a/build_lib.py +++ b/build_lib.py @@ -1,3 +1,10 @@ +# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. + # This script is an 'offline' build of the core warp runtime libraries # designed to be executed as part of CI / developer workflows, not # as part of the user runtime (since it requires CUDA toolkit, etc) @@ -49,6 +56,10 @@ parser.add_argument("--no_build_llvm", dest="build_llvm", action="store_false") parser.set_defaults(build_llvm=False) +parser.add_argument( + "--llvm_source_path", type=str, help="Path to the LLVM project source code (optional, repo cloned if not set)" +) + parser.add_argument("--debug_llvm", action="store_true", help="Enable LLVM compiler code debugging, default disabled") parser.add_argument("--no_debug_llvm", dest="debug_llvm", action="store_false") parser.set_defaults(debug_llvm=False) diff --git a/build_llvm.py b/build_llvm.py index fc839bad0..1f7fc331d 100644 --- a/build_llvm.py +++ b/build_llvm.py @@ -3,68 +3,69 @@ import sys import warp -from warp.build_dll import build_dll_for_arch, run_cmd +from warp.build_dll import * # set build output path off this file base_path = os.path.dirname(os.path.realpath(__file__)) build_path = os.path.join(base_path, "warp") -llvm_project_dir = "external/llvm-project" -llvm_project_path = os.path.join(base_path, llvm_project_dir) -llvm_path = os.path.join(llvm_project_path, "llvm") +llvm_project_path = os.path.join(base_path, "external/llvm-project") llvm_build_path = os.path.join(llvm_project_path, "out/build/") llvm_install_path = os.path.join(llvm_project_path, "out/install/") + # Fetch prebuilt Clang/LLVM libraries -if os.name == "nt": - packman = "tools\\packman\\packman.cmd" - packages = {"x86_64": "15.0.7-windows-x86_64-ptx-vs142"} -else: - packman = "./tools/packman/packman" - if sys.platform == "darwin": - packages = { - "arm64": "15.0.7-darwin-aarch64-macos11", - "x86_64": "15.0.7-darwin-x86_64-macos11", - } +def fetch_prebuilt_libraries(): + if os.name == "nt": + packman = "tools\\packman\\packman.cmd" + packages = {"x86_64": "15.0.7-windows-x86_64-ptx-vs142"} else: - packages = {"x86_64": "15.0.7-linux-x86_64-ptx-gcc7.5-cxx11abi0"} - -for arch in packages: - subprocess.check_call( - [ - packman, - "install", - "-l", - f"./_build/host-deps/llvm-project/release-{arch}", - "clang+llvm-warp", - packages[arch], - ] - ) + packman = "./tools/packman/packman" + if sys.platform == "darwin": + packages = { + "aarch64": "15.0.7-darwin-aarch64-macos11", + "x86_64": "15.0.7-darwin-x86_64-macos11", + } + else: + packages = { + "aarch64": "15.0.7-linux-aarch64-gcc7.5", + "x86_64": "15.0.7-linux-x86_64-ptx-gcc7.5-cxx11abi0", + } + + for arch in packages: + subprocess.check_call( + [ + packman, + "install", + "-l", + f"./_build/host-deps/llvm-project/release-{arch}", + "clang+llvm-warp", + packages[arch], + ] + ) -def build_from_source_for_arch(args, arch): - # Install dependencies - subprocess.check_call([sys.executable, "-m", "pip", "install", "gitpython"]) - subprocess.check_call([sys.executable, "-m", "pip", "install", "cmake"]) - subprocess.check_call([sys.executable, "-m", "pip", "install", "ninja"]) +def build_from_source_for_arch(args, arch, llvm_source): + # Check out the LLVM project Git repository, unless it already exists + if not os.path.exists(llvm_source): + # Install dependencies + subprocess.check_call([sys.executable, "-m", "pip", "install", "gitpython"]) + subprocess.check_call([sys.executable, "-m", "pip", "install", "cmake"]) + subprocess.check_call([sys.executable, "-m", "pip", "install", "ninja"]) - from git import Repo + from git import Repo - repo_url = "https://github.com/llvm/llvm-project.git" + repo_url = "https://github.com/llvm/llvm-project.git" + print(f"Cloning LLVM project from {repo_url}...") - if not os.path.exists(llvm_project_path): - print("Cloning LLVM project...") shallow_clone = True # https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/ if shallow_clone: - repo = Repo.clone_from( - repo_url, to_path=llvm_project_path, single_branch=True, branch="llvmorg-15.0.7", depth=1 - ) + repo = Repo.clone_from(repo_url, to_path=llvm_source, single_branch=True, branch="llvmorg-15.0.7", depth=1) else: - repo = Repo.clone_from(repo_url, to_path=llvm_project_path) + repo = Repo.clone_from(repo_url, to_path=llvm_source) repo.git.checkout("tags/llvmorg-15.0.7", "-b", "llvm-15.0.7") - else: - print(f"Found existing {llvm_project_dir} directory") - repo = Repo(llvm_project_path) + + print(f"Using LLVM project source from {llvm_source}") # CMake supports Debug, Release, RelWithDebInfo, and MinSizeRel builds if warp.config.mode == "release": @@ -84,20 +85,24 @@ def build_from_source_for_arch(args, arch): python_bin = "python/Scripts" if sys.platform == "win32" else "python/bin" os.environ["PATH"] = os.path.join(base_path, "_build/target-deps/" + python_bin) + os.pathsep + os.environ["PATH"] - if arch == "arm64": + if arch == "aarch64": target_backend = "AArch64" else: target_backend = "X86" if sys.platform == "darwin": host_triple = f"{arch}-apple-macos11" + osx_architectures = arch # build one architecture only elif os.name == "nt": host_triple = f"{arch}-pc-windows" + osx_architectures = "" else: host_triple = f"{arch}-pc-linux" + osx_architectures = "" - build_path = os.path.join(llvm_build_path, f"{warp.config.mode}-{ arch}") - install_path = os.path.join(llvm_install_path, f"{warp.config.mode}-{ arch}") + llvm_path = os.path.join(llvm_source, "llvm") + build_path = os.path.join(llvm_build_path, f"{warp.config.mode}-{arch}") + install_path = os.path.join(llvm_install_path, f"{warp.config.mode}-{arch}") # Build LLVM and Clang cmake_gen = [ @@ -131,7 +136,7 @@ def build_from_source_for_arch(args, arch): "-D", "CMAKE_CXX_FLAGS=-D_GLIBCXX_USE_CXX11_ABI=0", # The pre-C++11 ABI is still the default on the CentOS 7 toolchain "-D", f"CMAKE_INSTALL_PREFIX={install_path}", "-D", f"LLVM_HOST_TRIPLE={host_triple}", - "-D", f"CMAKE_OSX_ARCHITECTURES={ arch}", + "-D", f"CMAKE_OSX_ARCHITECTURES={osx_architectures}", # Disable unused tools and features "-D", "CLANG_BUILD_TOOLS=FALSE", @@ -276,10 +281,22 @@ def build_from_source_for_arch(args, arch): def build_from_source(args): - build_from_source_for_arch(args, "x86_64") + print("Building Clang/LLVM from source...") + + if args.llvm_source_path is not None: + llvm_source = args.llvm_source_path + else: + llvm_source = llvm_project_path + # build for the machine's architecture + build_from_source_for_arch(args, machine_architecture(), llvm_source) + + # for Apple systems also cross-compile for building a universal binary if sys.platform == "darwin": - build_from_source_for_arch(args, "arm64") + if machine_architecture() == "x86_64": + build_from_source_for_arch(args, "aarch64", llvm_source) + else: + build_from_source_for_arch(args, "x86_64", llvm_source) # build warp-clang.dll @@ -299,7 +316,7 @@ def build_warp_clang_for_arch(args, lib_name, arch): libpath = os.path.join(install_path, "lib") else: # obtain Clang and LLVM libraries from packman - assert os.path.exists("_build/host-deps/llvm-project"), "run build.bat / build.sh" + fetch_prebuilt_libraries() libpath = os.path.join(base_path, f"_build/host-deps/llvm-project/release-{arch}/lib") for _, _, libs in os.walk(libpath): @@ -342,12 +359,12 @@ def build_warp_clang(args, lib_name): if sys.platform == "darwin": # create a universal binary by combining x86-64 and AArch64 builds build_warp_clang_for_arch(args, lib_name + "-x86_64", "x86_64") - build_warp_clang_for_arch(args, lib_name + "-arm64", "arm64") + build_warp_clang_for_arch(args, lib_name + "-aarch64", "aarch64") dylib_path = os.path.join(build_path, f"bin/{lib_name}") - run_cmd(f"lipo -create -output {dylib_path} {dylib_path}-x86_64 {dylib_path}-arm64") + run_cmd(f"lipo -create -output {dylib_path} {dylib_path}-x86_64 {dylib_path}-aarch64") os.remove(f"{dylib_path}-x86_64") - os.remove(f"{dylib_path}-arm64") + os.remove(f"{dylib_path}-aarch64") else: - build_warp_clang_for_arch(args, lib_name, "x86_64") + build_warp_clang_for_arch(args, lib_name, machine_architecture()) diff --git a/docs/_build/html/.buildinfo b/docs/_build/html/.buildinfo deleted file mode 100644 index 7e3e1db1b..000000000 --- a/docs/_build/html/.buildinfo +++ /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: 3801b32913435da995d554ffb86411be -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/_build/html/_images/compiler_pipeline.png b/docs/_build/html/_images/compiler_pipeline.png deleted file mode 100644 index f3475577b..000000000 Binary files a/docs/_build/html/_images/compiler_pipeline.png and /dev/null differ diff --git a/docs/_build/html/_images/header.png b/docs/_build/html/_images/header.png deleted file mode 100644 index 89cec360a..000000000 Binary files a/docs/_build/html/_images/header.png and /dev/null differ diff --git a/docs/_build/html/_images/joint_transforms.png b/docs/_build/html/_images/joint_transforms.png deleted file mode 100644 index d39d15336..000000000 Binary files a/docs/_build/html/_images/joint_transforms.png and /dev/null differ diff --git a/docs/_build/html/_sources/basics.rst.txt b/docs/_build/html/_sources/basics.rst.txt deleted file mode 100644 index 7402b68db..000000000 --- a/docs/_build/html/_sources/basics.rst.txt +++ /dev/null @@ -1,214 +0,0 @@ -Basics -====== - -Initialization --------------- - -Before use Warp should be explicitly initialized with the ``wp.init()`` method as follows:: - - import warp as wp - - wp.init() - -Warp will print some startup information about the compute devices available, driver versions, and the location -for any generated kernel code, e.g.: - -.. code:: bat - - Warp 1.0.0 initialized: - CUDA Toolkit: 11.8, Driver: 12.1 - Devices: - "cpu" | AMD64 Family 25 Model 33 Stepping 0, AuthenticAMD - "cuda:0" | NVIDIA GeForce RTX 4080 (sm_89) - Kernel cache: C:\Users\mmacklin\AppData\Local\NVIDIA Corporation\warp\Cache\1.0.0 - - -Kernels -------- - -In Warp, compute kernels are defined as Python functions and annotated with the ``@wp.kernel`` decorator, as follows:: - - @wp.kernel - def simple_kernel(a: wp.array(dtype=wp.vec3), - b: wp.array(dtype=wp.vec3), - c: wp.array(dtype=float)): - - # get thread index - tid = wp.tid() - - # load two vec3s - x = a[tid] - y = b[tid] - - # compute the dot product between vectors - r = wp.dot(x, y) - - # write result back to memory - c[tid] = r - -Because Warp kernels are compiled to native C++/CUDA code, all the function input arguments must be statically typed. This allows -Warp to generate fast code that executes at essentially native speeds. Because kernels may be run on either the CPU -or GPU, they cannot access arbitrary global state from the Python environment. Instead they must read and write data -through their input parameters such as arrays. - -Warp kernels functions have a 1:1 correspondence with CUDA kernels, to launch a kernel with 1024 threads, we use -:func:`wp.launch() ` as follows:: - - wp.launch(kernel=simple_kernel, # kernel to launch - dim=1024, # number of threads - inputs=[a, b, c], # parameters - device="cuda") # execution device - -Inside the kernel we can retrieve the *thread index* of the each thread using the ``wp.tid()`` builtin function:: - - # get thread index - i = wp.tid() - -Kernels can be launched with 1D, 2D, 3D, or 4D grids of threads, e.g.: to launch a 2D grid of threads to process a 1024x1024 image we could write:: - - wp.launch(kernel=compute_image, - dim=(1024, 1024), - inputs=[img], - device="cuda") - -Then, inside the kernel we can retrieve a 2D thread index as follows:: - - # get thread index - i, j = wp.tid() - - # write out a color value for each pixel - color[i, j] = wp.vec3(r, g, b) - -.. _example-cache-management: - -Example: Changing the kernel cache directory -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The following example illustrates how the location for generated and compiled -kernel code can be changed before and after calling ``wp.init()``. - -.. code:: python - - import os - - import warp as wp - - example_dir = os.path.dirname(os.path.realpath(__file__)) - - # set default cache directory before wp.init() - wp.config.kernel_cache_dir = os.path.join(example_dir, "tmp", "warpcache1") - - wp.init() - - print("+++ Current cache directory: ", wp.config.kernel_cache_dir) - - # change cache directory after wp.init() - wp.build.init_kernel_cache(os.path.join(example_dir, "tmp", "warpcache2")) - - print("+++ Current cache directory: ", wp.config.kernel_cache_dir) - - # clear kernel cache (forces fresh kernel builds every time) - wp.build.clear_kernel_cache() - - - @wp.kernel - def basic(x: wp.array(dtype=float)): - tid = wp.tid() - x[tid] = float(tid) - - - device = "cpu" - n = 10 - x = wp.zeros(n, dtype=float, device=device) - - wp.launch(kernel=basic, dim=n, inputs=[x], device=device) - print(x.numpy()) - -Arrays ------- - -Memory allocations are exposed via the ``wp.array`` type. Arrays wrap an underlying memory allocation that may live in -either host (CPU), or device (GPU) memory. Arrays are strongly typed and store a linear sequence of built-in values -(``float,``, ``int``, ``vec3``, ``matrix33``, etc). - -Arrays can be allocated similar to PyTorch:: - - # allocate an uninitialized array of vec3s - v = wp.empty(shape=n, dtype=wp.vec3, device="cuda") - - # allocate a zero-initialized array of quaternions - q = wp.zeros(shape=n, dtype=wp.quat, device="cuda") - - # allocate and initialize an array from a NumPy array - # will be automatically transferred to the specified device - a = np.ones((10, 3), dtype=np.float32) - v = wp.from_numpy(a, dtype=wp.vec3, device="cuda") - -By default, Warp arrays that are initialized from external data (e.g.: NumPy, Lists, Tuples) will create a copy the data to new memory for the -device specified. However, it is possible for arrays to alias external memory using the ``copy=False`` parameter to the -array constructor provided the input is contiguous and on the same device. See the :doc:`/modules/interoperability` -section for more details on sharing memory with external frameworks. - -To read GPU array data back to CPU memory we can use the ``array.numpy()`` method:: - - # bring data from device back to host - view = device_array.numpy() - -This will automatically synchronize with the GPU to ensure that any outstanding work has finished, and will -copy the array back to CPU memory where it is passed to NumPy. Calling ``array.numpy()`` on a CPU array will return -a zero-copy NumPy view onto the Warp data. - -User Functions --------------- - -Users can write their own functions using the ``@wp.func`` decorator, for example:: - - @wp.func - def square(x: float): - return x*x - -User functions can be called freely from within kernels inside the same module and accept arrays as inputs. - -Compilation Model ------------------ - -Warp uses a Python->C++/CUDA compilation model that generates kernel code from Python function definitions. All kernels belonging to a Python module are runtime compiled into dynamic libraries and PTX, the result is then cached between application restarts for fast startup times. - -Note that compilation is triggered on the first kernel launch for that module. Any kernels registered in the module with ``@wp.kernel`` will be included in the shared library. - -.. image:: ./img/compiler_pipeline.png - - -Language Details ----------------- - -To support GPU computation and differentiability, there are some differences from the CPython runtime. - -Built-in Types -^^^^^^^^^^^^^^ - -Warp supports a number of built-in math types similar to high-level shading languages, for example ``vec2, vec3, vec4, mat22, mat33, mat44, quat, array``. All built-in types have value semantics so that expressions such as ``a = b`` generate a copy of the variable b rather than a reference. - -Strong Typing -^^^^^^^^^^^^^ - -Unlike Python, in Warp all variables must be typed. Types are inferred from source expressions and function signatures using the Python typing extensions. All kernel parameters must be annotated with the appropriate type, for example: :: - - @wp.kernel - def simple_kernel(a: wp.array(dtype=vec3), - b: wp.array(dtype=vec3), - c: float): - -Tuple initialization is not supported, instead variables should be explicitly typed: :: - - # invalid - a = (1.0, 2.0, 3.0) - - # valid - a = wp.vec3(1.0, 2.0, 3.0) - - -Limitations and Unsupported Features -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -See :doc:`limitations` for a list of Warp limitations and unsupported features. diff --git a/docs/_build/html/_sources/configuration.rst.txt b/docs/_build/html/_sources/configuration.rst.txt deleted file mode 100644 index a19f228aa..000000000 --- a/docs/_build/html/_sources/configuration.rst.txt +++ /dev/null @@ -1,138 +0,0 @@ -Runtime Settings -================ - -Warp has settings at the global, module, and kernel level that can be used to fine-tune the compilation and verbosity -of Warp programs. In cases in which a setting can be changed at multiple levels (e.g ``enable_backward``), -the setting at the more-specific scope takes precedence. - -Global Settings ---------------- - -To change a setting, prepend ``wp.config.`` to the name of the variable and assign a value to it. -Some settings may be changed on the fly, while others need to be set prior to calling ``wp.init()`` to take effect. - -For example, the location of the user kernel cache can be changed with: - -.. code-block:: python - - import os - - import warp as wp - - example_dir = os.path.dirname(os.path.realpath(__file__)) - - # set default cache directory before wp.init() - wp.config.kernel_cache_dir = os.path.join(example_dir, "tmp", "warpcache1") - - wp.init() - - -Basic Global Settings -^^^^^^^^^^^^^^^^^^^^^ - -+--------------------+---------+-------------+--------------------------------------------------------------------------+ -| Field | Type |Default Value| Description | -+====================+=========+=============+==========================================================================+ -|``verify_fp`` | Boolean | ``False`` | If ``True``, Warp will check that inputs and outputs are finite before | -| | | | and/or after various operations. **Has performance implications.** | -+--------------------+---------+-------------+--------------------------------------------------------------------------+ -|``verify_cuda`` | Boolean | ``False`` | If ``True``, Warp will check for CUDA errors after every launch and | -| | | | memory operation. CUDA error verification cannot be used during graph | -| | | | capture. **Has performance implications.** | -+--------------------+---------+-------------+--------------------------------------------------------------------------+ -|``print_launches`` | Boolean | ``False`` | If ``True``, Warp will print details of every kernel launch to standard | -| | | | out (e.g. launch dimensions, inputs, outputs, device, etc.). | -| | | | **Has performance implications.** | -+--------------------+---------+-------------+--------------------------------------------------------------------------+ -|``mode`` | String |``"release"``| Controls whether to compile Warp kernels in debug or release mode. | -| | | | Valid choices are ``"release"`` or ``"debug"``. | -| | | | **Has performance implications.** | -+--------------------+---------+-------------+--------------------------------------------------------------------------+ -|``verbose`` | Boolean | ``False`` | If ``True``, additional information will be printed to standard out | -| | | | during code generation, compilation, etc. | -+--------------------+---------+-------------+--------------------------------------------------------------------------+ -|``quiet`` | Boolean | ``False`` | If ``True``, Warp module initialization messages will be disabled. | -| | | | This setting does not affect error messages and warnings. | -+--------------------+---------+-------------+--------------------------------------------------------------------------+ -|``kernel_cache_dir``| String | ``None`` | The path to the directory used for the user kernel cache. Subdirectories | -| | | | named ``gen`` and ``bin`` will be created in this directory. If ``None``,| -| | | | a directory will be automatically determined using | -| | | | `appdirs.user_cache_directory `_ | -| | | | | -+--------------------+---------+-------------+--------------------------------------------------------------------------+ -|``enable_backward`` | Boolean | ``True`` | If ``True``, backward passes of kernels will be compiled by default. | -| | | | Disabling this setting can reduce kernel compilation times. | -+--------------------+---------+-------------+--------------------------------------------------------------------------+ - -Advanced Global Settings -^^^^^^^^^^^^^^^^^^^^^^^^ - -+--------------------+---------+-------------+--------------------------------------------------------------------------+ -| Field | Type |Default Value| Description | -+====================+=========+=============+==========================================================================+ -|``cache_kernels`` | Boolean | ``True`` | If ``True``, kernels that have already been compiled from previous | -| | | | application launches will not be recompiled. | -+--------------------+---------+-------------+--------------------------------------------------------------------------+ -|``cuda_output`` | String | ``None`` | The preferred CUDA output format for kernels. Valid choices are ``None``,| -| | | | ``"ptx"``, and ``"cubin"``. If ``None``, a format will be determined | -| | | | automatically. | -+--------------------+---------+-------------+--------------------------------------------------------------------------+ -|``ptx_target_arch`` | Integer | 70 | The target architecture for PTX generation. | -+--------------------+---------+-------------+--------------------------------------------------------------------------+ -|``llvm_cuda`` | Boolean | ``False`` | If ``True``, Clang/LLVM will be used to compile CUDA code instead of | -| | | | NVTRC. | -+--------------------+---------+-------------+--------------------------------------------------------------------------+ - -Module Settings ---------------- - -Module-level settings to control runtime compilation and code generation may be changed by passing a dictionary of -option pairs to ``wp.set_module_options()``. - -For example, compilation of backward passes for the kernel in an entire module can be disabled with: - -.. code:: python - - wp.set_module_options({"enable_backward": False}) - -The options for a module can also be queried using ``wp.get_module_options()``. - -+--------------------+---------+-------------+--------------------------------------------------------------------------+ -| Field | Type |Default Value| Description | -+====================+=========+=============+==========================================================================+ -|``mode`` | String | Global | Controls whether to compile the module's kernels in debug or release | -| | | setting | mode by default. Valid choices are ``"release"`` or ``"debug"``. | -+--------------------+---------+-------------+--------------------------------------------------------------------------+ -|``max_unroll`` | Integer | 16 | The maximum fixed-size loop to unroll. Note that ``max_unroll`` does not | -| | | | consider the total number of iterations in nested loops. This can result | -| | | | in a large amount of automatically generated code if each nested loop is | -| | | | below the ``max_unroll`` threshold. | -+--------------------+---------+-------------+--------------------------------------------------------------------------+ -|``enable_backward`` | Boolean | Global | If ``True``, backward passes of kernels will be compiled by default. | -| | | setting | Valid choices are ``"release"`` or ``"debug"``. | -+--------------------+---------+-------------+--------------------------------------------------------------------------+ -|``fast_math`` | Boolean | ``False`` | If ``True``, CUDA kernels will be compiled with the ``--use_fast_math`` | -| | | | compiler option, which enables some fast math operations that are faster | -| | | | but less accurate. | -+--------------------+---------+-------------+--------------------------------------------------------------------------+ -|``cuda_output`` | String | ``None`` | The preferred CUDA output format for kernels. Valid choices are ``None``,| -| | | | ``"ptx"``, and ``"cubin"``. If ``None``, a format will be determined | -| | | | automatically. The module-level setting takes precedence over the global | -| | | | setting. | -+--------------------+---------+-------------+--------------------------------------------------------------------------+ - -Kernel Settings ---------------- - -``enable_backward`` is currently the only setting that can also be configured on a per-kernel level. -Backward-pass compilation can be disabled by passing an argument into the ``@wp.kernel`` decorator -as in the following example: - -.. code-block:: python - - @wp.kernel(enable_backward=False) - def scale_2( - x: wp.array(dtype=float), - y: wp.array(dtype=float), - ): - y[0] = x[0] ** 2.0 diff --git a/docs/_build/html/_sources/debugging.rst.txt b/docs/_build/html/_sources/debugging.rst.txt deleted file mode 100644 index c006bd226..000000000 --- a/docs/_build/html/_sources/debugging.rst.txt +++ /dev/null @@ -1,86 +0,0 @@ -Debugging -========= - -Printing Values ---------------- - -Often one of the best debugging methods is to simply print values from kernels. Warp supports printing all built-in -types using the ``print()`` function, e.g.:: - - v = wp.vec3(1.0, 2.0, 3.0) - - print(v) - -In addition, formatted C-style printing is available through the ``wp.printf()`` function, e.g.:: - - x = 1.0 - i = 2 - - wp.printf("A float value %f, an int value: %d", x, i) - -.. note:: Formatted printing is only available for scalar types (e.g.: ``int`` and ``float``) not vector types. - -Printing Launches ------------------ - -For complex applications it can be difficult to understand the order-of-operations that lead to a bug. To help diagnose -these issues Warp supports a simple option to print out all launches and arguments to the console:: - - wp.config.print_launches = True - - -Step-Through Debugging ----------------------- - -It is possible to attach IDE debuggers such as Visual Studio to Warp processes to step through generated kernel code. -Users should first compile the kernels in debug mode by setting:: - - wp.config.mode = "debug" - -This setting ensures that line numbers, and debug symbols are generated correctly. After launching the Python process, -the debugger should be attached, and a breakpoint inserted into the generated code. - -.. note:: Generated kernel code is not a 1:1 correspondence with the original Python code, but individual operations can still be replayed and variables inspected. - -Also see :github:`warp/tests/test_debug.py` for an example of how to debug Warp kernel code running on the CPU. - -Generated Code --------------- - -The generated code for kernels is stored in a central cache location in the user's home directory, the cache location -is printed at startup when ``wp.init()`` is called, for example: - -.. code-block:: console - - Warp 0.8.1 initialized: - CUDA Toolkit: 11.8, Driver: 11.8 - Devices: - "cpu" | AMD64 Family 25 Model 33 Stepping 0, AuthenticAMD - "cuda:0" | NVIDIA GeForce RTX 3090 (sm_86) - "cuda:1" | NVIDIA GeForce RTX 2080 Ti (sm_75) - Kernel cache: C:\Users\LukasW\AppData\Local\NVIDIA Corporation\warp\Cache\0.8.1 - -The kernel cache has ``gen`` and ``bin`` folders that contain the generated C++/CUDA code and the compiled binaries -respectively. Occasionally it can be useful to inspect the generated code for debugging / profiling. - -Bounds Checking ---------------- - -Warp will perform bounds checking in debug build configurations to ensure that all array accesses lie within the defined -shape. - -CUDA Verification ------------------ - -It is possible to generate out-of-bounds memory access violations through poorly formed kernel code or inputs. In this -case the CUDA runtime will detect the violation and put the CUDA context into an error state. Subsequent kernel launches -may silently fail which can lead to hard to diagnose issues. - -If a CUDA error is suspected a simple verification method is to enable:: - - wp.config.verify_cuda = True - -This setting will check the CUDA context after every operation to ensure that it is still valid. If an error is -encountered it will raise an exception that often helps to narrow down the problematic kernel. - -.. note:: Verifying CUDA state at each launch requires synchronizing CPU and GPU which has a significant overhead. Users should ensure this setting is only used during debugging. diff --git a/docs/_build/html/_sources/faq.rst.txt b/docs/_build/html/_sources/faq.rst.txt deleted file mode 100644 index bcb924c51..000000000 --- a/docs/_build/html/_sources/faq.rst.txt +++ /dev/null @@ -1,112 +0,0 @@ -FAQ -=== - -How does Warp relate to other Python projects for GPU programming, e.g.: Numba, Taichi, cuPy, PyTorch, etc.? ------------------------------------------------------------------------------------------------------------- - -Warp is inspired by many of these projects, and is closely related to -Numba and Taichi which both expose kernel programming to Python. These -frameworks map to traditional GPU programming models, so many of the -high-level concepts are similar, however there are some functionality -and implementation differences. - -Compared to Numba, Warp supports a smaller subset of Python, but -offering auto-differentiation of kernel programs, which is useful for -machine learning. Compared to Taichi Warp uses C++/CUDA as an -intermediate representation, which makes it convenient to implement and -expose low-level routines. In addition, we are building in -data structures to support geometry processing (meshes, sparse volumes, -point clouds, USD data) as first-class citizens that are not exposed in -other runtimes. - -Warp does not offer a full tensor-based programming model like PyTorch -and JAX, but is designed to work well with these frameworks through data -sharing mechanisms like ``__cuda_array_interface__``. For computations -that map well to tensors (e.g.: neural-network inference) it makes sense -to use these existing tools. For problems with a lot of e.g.: sparsity, -conditional logic, heterogenous workloads (like the ones we often find in -simulation and graphics), then the kernel-based programming model like -the one in Warp are often more convenient since users have control over -individual threads. - -Does Warp support all of the Python language? ---------------------------------------------- - -No, Warp supports a subset of Python that maps well to the GPU. Our goal -is to not have any performance cliffs so that users can expect -consistently good behavior from kernels that is close to native code. -Examples of unsupported concepts that don’t map well to the GPU are -dynamic types, list comprehensions, exceptions, garbage collection, etc. - -When should I call ``wp.synchronize()``? ----------------------------------------- - -One of the common sources of confusion for new users is when calls to -``wp.synchronize()`` are necessary. The answer is “almost never”! -Synchronization is quite expensive, and should generally be avoided -unless necessary. Warp naturally takes care of synchronization between -operations (e.g.: kernel launches, device memory copies). - -For example, the following requires no manual synchronization, as the -conversion to NumPy will automatically synchronize: - -.. code:: python - - # run some kernels - wp.launch(kernel_1, dim, [array_x, array_y], device="cuda") - wp.launch(kernel_2, dim, [array_y, array_z], device="cuda") - - # bring data back to host (and implicitly synchronize) - x = array_z.numpy() - -The *only* case where manual synchronization is needed is when copies -are being performed back to CPU asynchronously, e.g.: - -.. code:: python - - # copy data back to cpu from gpu, all copies will happen asynchronously to Python - wp.copy(cpu_array_1, gpu_array_1) - wp.copy(cpu_array_2, gpu_array_2) - wp.copy(cpu_array_3, gpu_array_3) - - # ensure that the copies have finished - wp.synchronize() - - # return a numpy wrapper around the cpu arrays, note there is no implicit synchronization here - a1 = cpu_array_1.numpy() - a2 = cpu_array_2.numpy() - a3 = cpu_array_3.numpy() - -What happens when you differentiate a function like ``wp.abs(x)``? ------------------------------------------------------------------- - -Non-smooth functions such as ``y=|x|`` do not have a single unique -gradient at ``x=0``, rather they have what is known as a -``subgradient``, which is formally the convex hull of directional -derivatives at that point. The way that Warp (and most -auto-differentiation frameworks) handles these points is to pick an -arbitrary gradient from this set, e.g.: for ``wp.abs()``, it will -arbitrarily choose the gradient to be 1.0 at the origin. You can find -the implementation for these functions in ``warp/native/builtin.h``. - -Most optimizers (particularly ones that exploit stochasticity), are not -sensitive to the choice of which gradient to use from the subgradient, -although there are exceptions. - -Does Warp support multi-GPU programming? ----------------------------------------- - -Yes! Since version ``0.4.0`` we support allocating, launching, and -copying between multiple GPUs in a single process. We follow the naming -conventions of PyTorch and use aliases such as ``cuda:0``, ``cuda:1``, -``cpu`` to identify individual devices. - -Should I switch to Warp over IsaacGym / PhysX? ----------------------------------------------- - -Warp is not a replacement for IsaacGym, IsaacSim, or PhysX - while Warp -does offer some physical simulation capabilities this is primarily aimed -at developers who need differentiable physics, rather than a fully -featured physics engine. Warp is also integrated with IsaacGym and is -great for performing auxiliary tasks such as reward and observation -computations for reinforcement learning. diff --git a/docs/_build/html/_sources/index.rst.txt b/docs/_build/html/_sources/index.rst.txt deleted file mode 100644 index 1c7c8f690..000000000 --- a/docs/_build/html/_sources/index.rst.txt +++ /dev/null @@ -1,192 +0,0 @@ -NVIDIA Warp Documentation -========================= - -Warp is a Python framework for writing high-performance simulation and graphics code. Warp takes -regular Python functions and JIT compiles them to efficient kernel code that can run on the CPU or GPU. - -Warp is designed for spatial computing and comes with a rich set of primitives that make it easy to write -programs for physics simulation, perception, robotics, and geometry processing. In addition, Warp kernels -are differentiable and can be used as part of machine-learning pipelines with frameworks such as PyTorch and JAX. - -Below are some examples of simulations implemented using Warp: - -.. image:: ./img/header.png - -Quickstart -========== - -The easiest way is to install Warp is from PyPi: - -.. code-block:: sh - - $ pip install warp-lang - -Pre-built binary packages for Windows, Linux and macOS are also available on the `Releases `__ page. To install in your local Python environment extract the archive and run the following command from the root directory: - -.. code-block:: sh - - $ pip install . - -Basic example -------------- - -An example first program that computes the lengths of random 3D vectors is given below:: - - import warp as wp - import numpy as np - - wp.init() - - num_points = 1024 - - @wp.kernel - def length(points: wp.array(dtype=wp.vec3), - lengths: wp.array(dtype=float)): - - # thread index - tid = wp.tid() - - # compute distance of each point from origin - lengths[tid] = wp.length(points[tid]) - - - # allocate an array of 3d points - points = wp.array(np.random.rand(num_points, 3), dtype=wp.vec3) - lengths = wp.zeros(num_points, dtype=float) - - # launch kernel - wp.launch(kernel=length, - dim=len(points), - inputs=[points, lengths]) - - print(lengths) - -Additional examples -------------------- -The `examples `__ directory in -the Github repository contains a number of scripts that show how to -implement different simulation methods using the Warp API. Most examples -will generate USD files containing time-sampled animations in the -``examples/outputs`` directory. Before running examples users should -ensure that the ``usd-core`` package is installed using: - -:: - - pip install usd-core - -USD files can be viewed or rendered inside NVIDIA -`Omniverse `__, -Pixar's UsdView, and Blender. Note that Preview in macOS is not -recommended as it has limited support for time-sampled animations. - -Built-in unit tests can be run from the command-line as follows: - -:: - - python -m warp.tests - -Omniverse ---------- - -A Warp Omniverse extension is available in the extension registry inside -Omniverse Kit or USD Composer. - -Enabling the extension will automatically install and initialize the -Warp Python module inside the Kit Python environment. Please see the -`Omniverse Warp Documentation `__ -for more details on how to use Warp in Omniverse. - - -Learn More ----------- - -Please see the following resources for additional background on Warp: - -- `GTC 2022 - Presentation `__ -- `GTC 2021 - Presentation `__ -- `SIGGRAPH Asia 2021 Differentiable Simulation - Course `__ - -The underlying technology in Warp has been used in a number of research -projects at NVIDIA including the following publications: - -- Accelerated Policy Learning with Parallel Differentiable Simulation - - Xu, J., Makoviychuk, V., Narang, Y., Ramos, F., Matusik, W., Garg, - A., & Macklin, M. - `(2022) `__ -- DiSECt: Differentiable Simulator for Robotic Cutting - Heiden, E., - Macklin, M., Narang, Y., Fox, D., Garg, A., & Ramos, F - `(2021) `__ -- gradSim: Differentiable Simulation for System Identification and - Visuomotor Control - Murthy, J. Krishna, Miles Macklin, Florian - Golemo, Vikram Voleti, Linda Petrini, Martin Weiss, Breandan - Considine et - al. `(2021) `__ - -Citing ------- - -If you use Warp in your research please use the following citation: - -.. code:: bibtex - - @misc{warp2022, - title= {Warp: A High-performance Python Framework for GPU Simulation and Graphics}, - author = {Miles Macklin}, - month = {March}, - year = {2022}, - note= {NVIDIA GPU Technology Conference (GTC)}, - howpublished = {\url{https://github.com/nvidia/warp}} - } - -License -------- - -Warp is provided under the NVIDIA Source Code License (NVSCL), please see -`LICENSE.md `_ for the full license text. - -Please contact `omniverse-license-questions@nvidia.com `_ for -commercial licensing inquires. - -Full Table of Contents ----------------------- - -.. toctree:: - :maxdepth: 2 - :caption: User's Guide - - installation - basics - modules/devices - modules/interoperability - configuration - debugging - limitations - faq - -.. toctree:: - :maxdepth: 2 - :caption: Core Reference - - modules/runtime - modules/functions - -.. toctree:: - :maxdepth: 2 - :caption: Simulation Reference - - modules/sim - modules/sparse - modules/fem - -.. toctree:: - :hidden: - :caption: Project Links - - GitHub - PyPI - Discord - -:ref:`Full Index ` diff --git a/docs/_build/html/_sources/installation.rst.txt b/docs/_build/html/_sources/installation.rst.txt deleted file mode 100644 index 2d166887f..000000000 --- a/docs/_build/html/_sources/installation.rst.txt +++ /dev/null @@ -1,93 +0,0 @@ -Installation -============ - -The easiest way is to install Warp is from `PyPi `_: - -.. code-block:: sh - - $ pip install warp-lang - -Pre-built binary packages for Windows, Linux and macOS are also available on the `Releases `__ page. To install in your local Python environment extract the archive and run the following command from the root directory: - -.. code-block:: sh - - $ pip install . - -Dependencies ------------- - -Warp supports Python versions 3.7 or later and requires `NumPy `_ to be installed. - -The following optional dependencies are required to support certain features: - -* `usd-core `_: Required for some Warp examples, ``warp.sim.parse_usd()``, and ``warp.render.UsdRenderer``. -* `JAX `_: Required for JAX interoperability (see :ref:`jax-interop`). -* `PyTorch `_: Required for PyTorch interoperability (see :ref:`pytorch-interop`). -* `NVTX for Python `_: Required to use :class:`wp.ScopedTimer(use_nvtx=True) `. - -Building the Warp documentation requires: - -* `Sphinx `_ -* `Furo `_ -* `Sphinx-copybutton `_ - -Building from source --------------------- - -For developers who want to build the library themselves the following tools are required: - -* Microsoft Visual Studio (Windows), minimum version 2019 -* GCC (Linux), minimum version 7.2 -* `CUDA Toolkit `_, minimum version 11.5 -* `Git Large File Storage `_ - -If you are cloning from Windows, please first ensure that you have -enabled “Developer Mode” in Windows settings and symlinks in Git: - -.. code-block:: console - - $ git config --global core.symlinks true - -This will ensure symlinks inside ``exts/omni.warp.core`` work upon cloning. - -After cloning the repository, users should run: - -.. code-block:: console - - $ python build_lib.py - -This will generate the ``warp.dll`` / ``warp.so`` core library -respectively. When building manually, users should ensure that their -``CUDA_PATH`` environment variable is set, otherwise Warp will be built -without CUDA support. Alternatively, the path to the CUDA Toolkit can be -passed to the build command as ``--cuda_path="..."``. After building, the -Warp package should be installed using: - -.. code-block:: console - - $ pip install -e . - -Which ensures that subsequent modifications to the library will be -reflected in the Python package. - -Conda environments ------------------- - -Some modules, such as ``usd-core``, don't support the latest Python version. -To manage running Warp and other projects on different Python versions one can -make use of an environment management system such as -`Conda `_. - -**WARNING:** When building and running Warp in a different environment, make sure -the build environment has the same C++ runtime library version, or an older -one, than the execution environment. Otherwise Warp's shared libraries may end -up looking for a newer runtime library version than the one available in the -execution environment. For example on Linux this error could occur: - -``OSError: <...>/libstdc++.so.6: version `GLIBCXX_3.4.30' not found (required by <...>/warp/warp/bin/warp.so)`` - -This can be solved by installing a newer C++ runtime version in the runtime -conda environment using ``conda install -c conda-forge libstdcxx-ng=12.1`` or -newer. Or, the build environment's C++ toolchain can be downgraded using -``conda install -c conda-forge libstdcxx-ng=8.5``. Or, one can ``activate`` or -``deactivate`` conda environments as needed for building vs. running Warp. diff --git a/docs/_build/html/_sources/limitations.rst.txt b/docs/_build/html/_sources/limitations.rst.txt deleted file mode 100644 index 5640103fc..000000000 --- a/docs/_build/html/_sources/limitations.rst.txt +++ /dev/null @@ -1,56 +0,0 @@ -Limitations -=========== - -.. currentmodule:: warp - -This section summarizes various limitations and currently unsupported features in Warp. -Requests for new features can be made at `GitHub Discussions `_, -and issues can be opened at `GitHub Issues `_. - -Unsupported Features --------------------- - -To achieve good performance on GPUs some dynamic language features are not supported: - -* Lambda functions -* List comprehensions -* Exceptions -* Recursion -* Runtime evaluation of expressions, e.g.: eval() -* Dynamic structures such as lists, sets, dictionaries, etc. - -Kernels -------- - -* Strings cannot be passed into kernels. -* Short-circuit evaluation is not supported. -* :func:`wp.atomic_add() ` does not support ``wp.int64``. -* :func:`wp.tid() ` cannot be called from user functions. -* CUDA thread blocks use a fixed size 256 threads per block. - -Arrays ------- - -* Arrays can have a maximum of four dimensions. -* Each dimension of a Warp array cannot be greater than the maximum value representable by a 32-bit signed integer, - :math:`2^{31}-1`. -* There are currently no data types that support complex numbers. - -Structs -------- - -* Structs cannot have generic members, i.e. of type ``typing.Any``. - -Volumes -------- - -* The sparse-volume *topology* cannot be changed after the tiles for the :class:`Volume` have been allocated. - -Multiple Processes ------------------- - -* A CUDA context created in the parent process cannot be used in a *forked* child process. - Use the spawn start method instead, or avoid creating CUDA contexts in the parent process. -* There can be issues with using same user kernel cache directory when running with multiple processes. - A workaround is to use a separate cache directory for every process. - See :ref:`example-cache-management` for how the cache directory may be changed. diff --git a/docs/_build/html/_sources/modules/devices.rst.txt b/docs/_build/html/_sources/modules/devices.rst.txt deleted file mode 100644 index eda532d6f..000000000 --- a/docs/_build/html/_sources/modules/devices.rst.txt +++ /dev/null @@ -1,200 +0,0 @@ -.. _devices: - -Devices -======= - -Warp assigns unique string aliases to all supported compute devices in the system. There is currently a single CPU device exposed as ``"cpu"``. Each CUDA-capable GPU gets an alias of the form ``"cuda:i"``, where ``i`` is the CUDA device ordinal. This convention should be familiar to users of other popular frameworks like PyTorch. - -It is possible to explicitly target a specific device with each Warp API call using the ``device`` argument:: - - a = wp.zeros(n, device="cpu") - wp.launch(kernel, dim=a.size, inputs=[a], device="cpu") - - b = wp.zeros(n, device="cuda:0") - wp.launch(kernel, dim=b.size, inputs=[b], device="cuda:0") - - c = wp.zeros(n, device="cuda:1") - wp.launch(kernel, dim=c.size, inputs=[c], device="cuda:1") - -.. note:: - - A Warp CUDA device (``"cuda:i"``) corresponds to the primary CUDA context of device ``i``. This is compatible with frameworks like PyTorch and other software that uses the CUDA Runtime API. It makes interoperability easy, because GPU resources like memory can be shared with Warp. - -Default Device --------------- - -To simplify writing code, Warp has the concept of **default device**. When the ``device`` argument is omitted from a Warp API call, the default device will be used. - -During Warp initialization, the default device is set to be ``"cuda:0"`` if CUDA is available. Otherwise, the default device is ``"cpu"``. - -The function ``wp.set_device()`` can be used to change the default device:: - - wp.set_device("cpu") - a = wp.zeros(n) - wp.launch(kernel, dim=a.size, inputs=[a]) - - wp.set_device("cuda:0") - b = wp.zeros(n) - wp.launch(kernel, dim=b.size, inputs=[b]) - - wp.set_device("cuda:1") - c = wp.zeros(n) - wp.launch(kernel, dim=c.size, inputs=[c]) - -.. note:: - - For CUDA devices, ``wp.set_device()`` does two things: it sets the Warp default device and it makes the device's CUDA context current. This helps to minimize the number of CUDA context switches in blocks of code targeting a single device. - -For PyTorch users, this function is similar to ``torch.cuda.set_device()``. It is still possible to specify a different device in individual API calls, like in this snippet:: - - # set default device - wp.set_device("cuda:0") - - # use default device - a = wp.zeros(n) - - # use explicit devices - b = wp.empty(n, device="cpu") - c = wp.empty(n, device="cuda:1") - - # use default device - wp.launch(kernel, dim=a.size, inputs=[a]) - - wp.copy(b, a) - wp.copy(c, a) - -Scoped Devices --------------- - -Another way to manage the default device is using ``wp.ScopedDevice`` objects. They can be arbitrarily nested and restore the previous default device on exit:: - - with wp.ScopedDevice("cpu"): - # alloc and launch on "cpu" - a = wp.zeros(n) - wp.launch(kernel, dim=a.size, inputs=[a]) - - with wp.ScopedDevice("cuda:0"): - # alloc on "cuda:0" - b = wp.zeros(n) - - with wp.ScopedDevice("cuda:1"): - # alloc and launch on "cuda:1" - c = wp.zeros(n) - wp.launch(kernel, dim=c.size, inputs=[c]) - - # launch on "cuda:0" - wp.launch(kernel, dim=b.size, inputs=[b]) - -.. note:: - - For CUDA devices, ``wp.ScopedDevice`` makes the device's CUDA context current and restores the previous CUDA context on exit. This is handy when running Warp scripts as part of a bigger pipeline, because it avoids any side effects of changing the CUDA context in the enclosed code. - -Example: Using ``wp.ScopedDevice`` with multiple GPUs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The following example shows how to allocate arrays and launch kernels on all available CUDA devices. - -.. code:: python - - import warp as wp - - wp.init() - - - @wp.kernel - def inc(a: wp.array(dtype=float)): - tid = wp.tid() - a[tid] = a[tid] + 1.0 - - - # get all CUDA devices - devices = wp.get_cuda_devices() - device_count = len(devices) - - # number of launches - iters = 1000 - - # list of arrays, one per device - arrs = [] - - # loop over all devices - for device in devices: - # use a ScopedDevice to set the target device - with wp.ScopedDevice(device): - # allocate array - a = wp.zeros(250 * 1024 * 1024, dtype=float) - arrs.append(a) - - # launch kernels - for _ in range(iters): - wp.launch(inc, dim=a.size, inputs=[a]) - - # synchronize all devices - wp.synchronize() - - # print results - for i in range(device_count): - print(f"{arrs[i].device} -> {arrs[i].numpy()}") - - -Current CUDA Device -------------------- - -Warp uses the device alias ``"cuda"`` to target the current CUDA device. This allows external code to manage the CUDA device on which to execute Warp scripts. It is analogous to the PyTorch ``"cuda"`` device, which should be familiar to Torch users and simplify interoperation. - -In this snippet, we use PyTorch to manage the current CUDA device and invoke a Warp kernel on that device:: - - def example_function(): - # create a Torch tensor on the current CUDA device - t = torch.arange(10, dtype=torch.float32, device="cuda") - - a = wp.from_torch(t) - - # launch a Warp kernel on the current CUDA device - wp.launch(kernel, dim=a.size, inputs=[a], device="cuda") - - # use Torch to set the current CUDA device and run example_function() on that device - torch.cuda.set_device(0) - example_function() - - # use Torch to change the current CUDA device and re-run example_function() on that device - torch.cuda.set_device(1) - example_function() - -.. note:: - - Using the device alias ``"cuda"`` can be problematic if the code runs in an environment where another part of the code can unpredictably change the CUDA context. Using an explicit CUDA device like ``"cuda:i"`` is recommended to avoid such issues. - -Device Synchronization ----------------------- - -CUDA kernel launches and memory operations can execute asynchronously. This allows for overlapping compute and memory operations on different devices. Warp allows synchronizing the host with outstanding asynchronous operations on a specific device:: - - wp.synchronize_device("cuda:1") - -The ``wp.synchronize_device()`` function offers more fine-grained synchronization than ``wp.synchronize()``, as the latter waits for *all* devices to complete their work. - -Custom CUDA Contexts --------------------- - -Warp is designed to work with arbitrary CUDA contexts so it can easily integrate into different workflows. - -Applications built on the CUDA Runtime API target the *primary context* of each device. The Runtime API hides CUDA context management under the hood. In Warp, device ``"cuda:i"`` represents the primary context of device ``i``, which aligns with the CUDA Runtime API. - -Applications built on the CUDA Driver API work with CUDA contexts directly and can create custom CUDA contexts on any device. Custom CUDA contexts can be created with specific affinity or interop features that benefit the application. Warp can work with these CUDA contexts as well. - -The special device alias ``"cuda"`` can be used to target the current CUDA context, whether this is a primary or custom context. - -In addition, Warp allows registering new device aliases for custom CUDA contexts, so that they can be explicitly targeted by name. If the ``CUcontext`` pointer is available, it can be used to create a new device alias like this:: - - wp.map_cuda_device("foo", ctypes.c_void_p(context_ptr)) - -Alternatively, if the custom CUDA context was made current by the application, the pointer can be omitted:: - - wp.map_cuda_device("foo") - -In either case, mapping the custom CUDA context allows us to target the context directly using the assigned alias:: - - with wp.ScopedDevice("foo"): - a = wp.zeros(n) - wp.launch(kernel, dim=a.size, inputs=[a]) diff --git a/docs/_build/html/_sources/modules/fem.rst.txt b/docs/_build/html/_sources/modules/fem.rst.txt deleted file mode 100644 index 359e42df9..000000000 --- a/docs/_build/html/_sources/modules/fem.rst.txt +++ /dev/null @@ -1,386 +0,0 @@ -warp.fem -===================== - -.. currentmodule:: warp.fem - -The ``warp.fem`` module is designed to facilitate solving physical systems described as differential -equations. For example, it can solve PDEs for diffusion, convection, fluid flow, and elasticity problems -using finite-element-based (FEM) Galerkin methods, and allows users to quickly experiment with various FEM -formulations and discretization schemes. - -Integrands ----------- - -The core functionality of the FEM toolkit is the ability to integrate constant, linear, and bilinear forms -over various domains and using arbitrary interpolation basis. - -The main mechanism is the :py:func:`.integrand` decorator, for instance: :: - - @integrand - def linear_form( - s: Sample, - v: Field, - ): - return v(s) - - - @integrand - def diffusion_form(s: Sample, u: Field, v: Field, nu: float): - return nu * wp.dot( - grad(u, s), - grad(v, s), - ) - -Integrands are normal warp kernels, meaning any usual warp function can be used. -However, they accept a few special parameters: - - - :class:`.Sample` contains information about the current integration sample point, such as the element index and coordinates in element. - - :class:`.Field` designates an abstract field, which will be replaced at call time by the actual field type: for instance a :class:`.DiscreteField`, :class:`.field.TestField` or :class:`.field.TrialField` defined over an arbitrary :class:`.FunctionSpace`. - A field `u` can be evaluated at a given sample `s` using the :func:`.inner` operator, i.e, ``inner(u, s)``, or as a shortcut using the usual call operator, ``u(s)``. - Several other operators are available, such as :func:`.grad`; see the :ref:`Operators` section. - - :class:`.Domain` designates an abstract integration domain. Several operators are also provided for domains, for example in the example below evaluating the normal at the current sample position: :: - - @integrand - def boundary_form( - s: Sample, - domain: Domain, - u: Field, - ): - nor = normal(domain, s) - return wp.dot(u(s), nor) - -Integrands cannot be used directly with :func:`warp.launch`, but must be called through :func:`.integrate` or :func:`.interpolate` instead. -The root integrand (`integrand` argument passed to :func:`integrate` or :func:`interpolate` call) will automatically get passed :class:`.Sample` and :class:`.Domain` parameters. -:class:`.Field` parameters must be passed as a dictionary in the `fields` argument of the launcher function, and all other standard Warp types arguments must be -passed as a dictionary in the `values` argument of the launcher function, for instance: :: - - integrate(diffusion_form, fields={"u": trial, "v": test}, values={"nu": viscosity}) - - -Basic workflow --------------- - -The typical steps for solving a linear PDE are as follow: - - - Define a :class:`.Geometry` (grid, mesh, etc). At the moment, 2D and 3D regular grids, triangle, quadrilateral, tetrahedron and hexahedron meshes are supported. - - Define one or more :class:`.FunctionSpace`, by equipping the geometry elements with shape functions. See :func:`.make_polynomial_space`. At the moment, continuous/discontinuous Lagrange (:math:`P_{k[d]}, Q_{k[d]}`) and Serendipity (:math:`S_k`) shape functions of order :math:`k \leq 3` are supported. - - Define an integration :class:`.GeometryDomain`, for instance the geometry's cells (:class:`.Cells`) or boundary sides (:class:`.BoundarySides`). - - Integrate linear forms to build the system's right-hand-side. Define a test function over the function space using :func:`.make_test`, - a :class:`.Quadrature` formula (or let the module choose one based on the function space degree), and call :func:`.integrate` with the linear form integrand. - The result is a :class:`warp.array` containing the integration result for each of the function space degrees of freedom. - - Integrate bilinear forms to build the system's left-hand-side. Define a trial function over the function space using :func:`.make_trial`, - then call :func:`.integrate` with the bilinear form integrand. - The result is a :class:`warp.sparse.BsrMatrix` containing the integration result for each pair of test and trial function space degrees of freedom. - Note that the trial and test functions do not have to be defined over the same function space, so that Mixed FEM is supported. - - Solve the resulting linear system using the solver of your choice - - -The following excerpt from the introductory example ``examples/fem/example_diffusion.py`` outlines this procedure: :: - - # Grid geometry - geo = Grid2D(n=50, cell_size=2) - - # Domain and function spaces - domain = Cells(geometry=geo) - scalar_space = make_polynomial_space(geo, degree=3) - - # Right-hand-side (forcing term) - test = make_test(space=scalar_space, domain=domain) - rhs = integrate(linear_form, fields={"v": test}) - - # Weakly-imposed boundary conditions on Y sides - boundary = BoundarySides(geo) - bd_test = make_test(space=scalar_space, domain=boundary) - bd_trial = make_trial(space=scalar_space, domain=boundary) - bd_matrix = integrate(y_mass_form, fields={"u": bd_trial, "v": bd_test}) - - # Diffusion form - trial = make_trial(space=scalar_space, domain=domain) - matrix = integrate(diffusion_form, fields={"u": trial, "v": test}, values={"nu": viscosity}) - - # Assemble linear system (add diffusion and boundary condition matrices) - bsr_axpy(x=bd_matrix, y=matrix, alpha=boundary_strength, beta=1) - - # Solve linear system using Conjugate Gradient - x = wp.zeros_like(rhs) - bsr_cg(matrix, b=rhs, x=x) - - -.. note:: - The :func:`.integrate` function does not check that the passed integrands are actually linear or bilinear forms; it is up to the user to ensure that they are. - To solve non-linear PDEs, one can use an iterative procedure and pass the current value of the studied function :class:`.DiscreteField` argument to the integrand, on which - arbitrary operations are permitted. However, the result of the form must remain linear in the test and trial fields. - -Introductory examples ---------------------- - -``warp.fem`` ships with a list of examples in the ``examples/fem`` directory illustrating common model problems. - - - ``example_diffusion.py``: 2D diffusion with homogenous Neumann and Dirichlet boundary conditions - * ``example_diffusion_3d.py``: 3D variant of the diffusion problem - - ``example_convection_diffusion.py``: 2D convection-diffusion using semi-Lagrangian advection - * ``example_diffusion_dg0.py``: 2D convection-diffusion using finite-volume and upwind transport - * ``example_diffusion_dg.py``: 2D convection-diffusion using Discontinuous Galerkin with upwind transport and Symmetric Interior Penalty - - ``example_stokes.py``: 2D incompressible Stokes flow using mixed :math:`P_k/P_{k-1}` or :math:`Q_k/P_{(k-1)d}` elements - - ``example_navier_stokes.py``: 2D Navier-Stokes flow using mixed :math:`P_k/P_{k-1}` elements - - ``example_mixed_elasticity.py``: 2D linear elasticity using mixed continuous/discontinuous :math:`S_k/P_{(k-1)d}` elements - - -Advanced usages ---------------- - -High-order (curved) geometries -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -It is possible to convert any :class:`.Geometry` (grids and explicit meshes) into a curved, high-order variant by deforming them -with an arbitrary-order displacement field using the :meth:`~.DiscreteField.make_deformed_geometry` method. -The process looks as follow: :: - - # Define a base geometry - base_geo = fem.Grid3D(res=resolution) - - # Define a displacement field on the base geometry - deformation_space = fem.make_polynomial_space(base_geo, degree=deformation_degree, dtype=wp.vec3) - deformation_field = deformation_space.make_field() - - # Populate the field value by interpolating an expression - fem.interpolate(deformation_field_expr, dest=deformation_field) - - # Construct the deformed geometry from the displacement field - deform_geo = deformation_field.make_deformed_geometry() - - # Define new function spaces on the deformed geometry - scalar_space = fem.make_polynomial_space(deformed_geo, degree=scalar_space_degree) - -See also ``example_deformed_geometry.py`` for a complete example. - -Particle-based quadrature -^^^^^^^^^^^^^^^^^^^^^^^^^ - -The :class:`.PicQuadrature` provides a way to define Particle-In-Cell quadratures from a set or arbitrary particles, -which can be helpful to develop MPM-like methods. -The particles are automatically bucketed to the geometry cells when the quadrature is initialized. -This is illustrated by the ``example_stokes_transfer.py`` and ``example_apic_fluid.py`` examples. - -Partitioning -^^^^^^^^^^^^ - -The FEM toolkit makes it possible to perform integration on a subset of the domain elements, -possibly re-indexing degrees of freedom so that the linear system contains the local ones only. -This is useful for distributed computation (see ``examples/fem/example_diffusion_mgpu.py``), or simply to limit the simulation domain to a subset of active cells (see ``examples/fem/example_stokes_transfer.py``). - -A partition of the simulation geometry can be defined using subclasses of :class:`.GeometryPartition` -such as :class:`.LinearGeometryPartition` or :class:`.ExplicitGeometryPartition`. - -Function spaces can then be partitioned according to the geometry partition using :func:`.make_space_partition`. -The resulting :class:`.SpacePartition` object allows translating between space-wide and partition-wide node indices, -and differentiating interior, frontier and exterior nodes. - -Memory management -^^^^^^^^^^^^^^^^^ - -Several ``warp.fem`` functions require allocating temporary buffers to perform their computations. -If such functions are called many times in a tight loop, those many allocations and de-allocations may degrade performance. -To overcome this issue, a :class:`.cache.TemporaryStore` object may be created to persist and re-use temporary allocations across calls, -either globally using :func:`set_default_temporary_store` or at a per-function granularity using the corresponding argument. - -.. _Operators: - -Operators ---------- -.. autofunction:: position(domain: Domain, s: Sample) -.. autofunction:: normal(domain: Domain, s: Sample) -.. autofunction:: lookup(domain: Domain, x) -.. autofunction:: measure(domain: Domain, s: Sample) -.. autofunction:: measure_ratio(domain: Domain, s: Sample) -.. autofunction:: deformation_gradient(domain: Domain, s: Sample) - -.. autofunction:: degree(f: Field) -.. autofunction:: inner(f: Field, s: Sample) -.. autofunction:: outer(f: Field, s: Sample) -.. autofunction:: grad(f: Field, s: Sample) -.. autofunction:: grad_outer(f: Field, s: Sample) -.. autofunction:: div(f: Field, s: Sample) -.. autofunction:: div_outer(f: Field, s: Sample) -.. autofunction:: at_node(f: Field, s: Sample) - -.. autofunction:: D(f: Field, s: Sample) -.. autofunction:: curl(f: Field, s: Sample) -.. autofunction:: jump(f: Field, s: Sample) -.. autofunction:: average(f: Field, s: Sample) -.. autofunction:: grad_jump(f: Field, s: Sample) -.. autofunction:: grad_average(f: Field, s: Sample) - -.. autofunction:: warp.fem.operator.operator - -Integration ------------ - -.. autofunction:: integrate -.. autofunction:: interpolate - -.. autofunction:: integrand - -.. class:: Sample - - Per-sample point context for evaluating fields and related operators in integrands. - -.. autoclass:: Field - -.. autoclass:: Domain - -Geometry --------- - -.. autoclass:: Grid2D - :show-inheritance: - -.. autoclass:: Trimesh2D - :show-inheritance: - -.. autoclass:: Quadmesh2D - :show-inheritance: - -.. autoclass:: Grid3D - :show-inheritance: - -.. autoclass:: Tetmesh - :show-inheritance: - -.. autoclass:: Hexmesh - :show-inheritance: - -.. autoclass:: LinearGeometryPartition - -.. autoclass:: ExplicitGeometryPartition - -.. autoclass:: Cells - :show-inheritance: - -.. autoclass:: Sides - :show-inheritance: - -.. autoclass:: BoundarySides - :show-inheritance: - -.. autoclass:: FrontierSides - :show-inheritance: - -.. autoclass:: Polynomial - :members: - -.. autoclass:: RegularQuadrature - :show-inheritance: - -.. autoclass:: NodalQuadrature - :show-inheritance: - -.. autoclass:: PicQuadrature - :show-inheritance: - -Function Spaces ---------------- - -.. autofunction:: make_polynomial_space - -.. autofunction:: make_polynomial_basis_space - -.. autofunction:: make_collocated_function_space - -.. autofunction:: make_space_partition - -.. autofunction:: make_space_restriction - -.. autoclass:: ElementBasis - :members: - -.. autoclass:: SymmetricTensorMapper - :show-inheritance: - -.. autoclass:: SkewSymmetricTensorMapper - :show-inheritance: - -Fields ------- - -.. autofunction:: make_test - -.. autofunction:: make_trial - -.. autofunction:: make_restriction - -Boundary Conditions -------------------- - -.. autofunction:: normalize_dirichlet_projector - -.. autofunction:: project_linear_system - -Memory management ------------------ - -.. autofunction:: set_default_temporary_store - -.. autofunction:: borrow_temporary - -.. autofunction:: borrow_temporary_like - - -Interfaces ----------- - -Interface classes are not meant to be constructed directly, but can be derived from extend the built-in functionality. - -.. autoclass:: Geometry - :members: cell_count, side_count, boundary_side_count - -.. autoclass:: GeometryPartition - :members: cell_count, side_count, boundary_side_count, frontier_side_count - -.. autoclass:: GeometryDomain - :members: ElementKind, element_kind, dimension, element_count - -.. autoclass:: Quadrature - :members: domain, total_point_count - -.. autoclass:: FunctionSpace - :members: dtype, topology, geometry, dimension, degree, trace, make_field - -.. autoclass:: SpaceTopology - :members: dimension, geometry, node_count, element_node_indices, trace - -.. autoclass:: BasisSpace - :members: topology, geometry, shape, node_positions - -.. autoclass:: warp.fem.space.shape.ShapeFunction - -.. autoclass:: SpacePartition - :members: node_count, owned_node_count, interior_node_count, space_node_indices - -.. autoclass:: SpaceRestriction - :members: node_count - -.. autoclass:: DofMapper - -.. autoclass:: FieldLike - -.. autoclass:: DiscreteField - :show-inheritance: - :members: dof_values, trace, make_deformed_geometry - -.. autoclass:: warp.fem.field.FieldRestriction - -.. autoclass:: warp.fem.field.SpaceField - :show-inheritance: - -.. autoclass:: warp.fem.field.TestField - :show-inheritance: - -.. autoclass:: warp.fem.field.TrialField - :show-inheritance: - -.. autoclass:: TemporaryStore - :members: clear - -.. autoclass:: warp.fem.cache.Temporary - :members: array, detach, release \ No newline at end of file diff --git a/docs/_build/html/_sources/modules/functions.rst.txt b/docs/_build/html/_sources/modules/functions.rst.txt deleted file mode 100644 index 5fe56ada3..000000000 --- a/docs/_build/html/_sources/modules/functions.rst.txt +++ /dev/null @@ -1,1992 +0,0 @@ -.. - Autogenerated File - Do not edit. Run build_docs.py to generate. - -.. functions: -.. currentmodule:: warp - -Kernel Reference -================ - -Scalar Types ------------- -.. class:: int8 -.. class:: uint8 -.. class:: int16 -.. class:: uint16 -.. class:: int32 -.. class:: uint32 -.. class:: int64 -.. class:: uint64 -.. class:: float16 -.. class:: float32 -.. class:: float64 -.. class:: bool - - -Vector Types ------------- -.. class:: vec2b -.. class:: vec2ub -.. class:: vec2s -.. class:: vec2us -.. class:: vec2i -.. class:: vec2ui -.. class:: vec2l -.. class:: vec2ul -.. class:: vec2h -.. class:: vec2f -.. class:: vec2d -.. class:: vec3b -.. class:: vec3ub -.. class:: vec3s -.. class:: vec3us -.. class:: vec3i -.. class:: vec3ui -.. class:: vec3l -.. class:: vec3ul -.. class:: vec3h -.. class:: vec3f -.. class:: vec3d -.. class:: vec4b -.. class:: vec4ub -.. class:: vec4s -.. class:: vec4us -.. class:: vec4i -.. class:: vec4ui -.. class:: vec4l -.. class:: vec4ul -.. class:: vec4h -.. class:: vec4f -.. class:: vec4d -.. class:: mat22h -.. class:: mat22f -.. class:: mat22d -.. class:: mat33h -.. class:: mat33f -.. class:: mat33d -.. class:: mat44h -.. class:: mat44f -.. class:: mat44d -.. class:: quath -.. class:: quatf -.. class:: quatd -.. class:: transformh -.. class:: transformf -.. class:: transformd -.. class:: spatial_vectorh -.. class:: spatial_vectorf -.. class:: spatial_vectord -.. class:: spatial_matrixh -.. class:: spatial_matrixf -.. class:: spatial_matrixd - -Generic Types -------------- -.. class:: Int -.. class:: Float -.. class:: Scalar -.. class:: Vector -.. class:: Matrix -.. class:: Quaternion -.. class:: Transformation -.. class:: Array - - -Scalar Math ---------------- -.. function:: min(x: Scalar, y: Scalar) -> Scalar - - Return the minimum of two scalars. - - -.. function:: min(x: Vector[Any,Scalar], y: Vector[Any,Scalar]) -> Vector[Any,Scalar] - :noindex: - :nocontentsentry: - - Return the element-wise minimum of two vectors. - - -.. function:: min(v: Vector[Any,Scalar]) -> Scalar - :noindex: - :nocontentsentry: - - Return the minimum element of a vector ``v``. - - -.. function:: max(x: Scalar, y: Scalar) -> Scalar - - Return the maximum of two scalars. - - -.. function:: max(x: Vector[Any,Scalar], y: Vector[Any,Scalar]) -> Vector[Any,Scalar] - :noindex: - :nocontentsentry: - - Return the element-wise maximum of two vectors. - - -.. function:: max(v: Vector[Any,Scalar]) -> Scalar - :noindex: - :nocontentsentry: - - Return the maximum element of a vector ``v``. - - -.. function:: clamp(x: Scalar, a: Scalar, b: Scalar) -> Scalar - - Clamp the value of ``x`` to the range [a, b]. - - -.. function:: abs(x: Scalar) -> Scalar - - Return the absolute value of ``x``. - - -.. function:: sign(x: Scalar) -> Scalar - - Return -1 if ``x`` < 0, return 1 otherwise. - - -.. function:: step(x: Scalar) -> Scalar - - Return 1.0 if ``x`` < 0.0, return 0.0 otherwise. - - -.. function:: nonzero(x: Scalar) -> Scalar - - Return 1.0 if ``x`` is not equal to zero, return 0.0 otherwise. - - -.. function:: sin(x: Float) -> Float - - Return the sine of ``x`` in radians. - - -.. function:: cos(x: Float) -> Float - - Return the cosine of ``x`` in radians. - - -.. function:: acos(x: Float) -> Float - - Return arccos of ``x`` in radians. Inputs are automatically clamped to [-1.0, 1.0]. - - -.. function:: asin(x: Float) -> Float - - Return arcsin of ``x`` in radians. Inputs are automatically clamped to [-1.0, 1.0]. - - -.. function:: sqrt(x: Float) -> Float - - Return the square root of ``x``, where ``x`` is positive. - - -.. function:: cbrt(x: Float) -> Float - - Return the cube root of ``x``. - - -.. function:: tan(x: Float) -> Float - - Return the tangent of ``x`` in radians. - - -.. function:: atan(x: Float) -> Float - - Return the arctangent of ``x`` in radians. - - -.. function:: atan2(y: Float, x: Float) -> Float - - Return the 2-argument arctangent, atan2, of the point ``(x, y)`` in radians. - - -.. function:: sinh(x: Float) -> Float - - Return the sinh of ``x``. - - -.. function:: cosh(x: Float) -> Float - - Return the cosh of ``x``. - - -.. function:: tanh(x: Float) -> Float - - Return the tanh of ``x``. - - -.. function:: degrees(x: Float) -> Float - - Convert ``x`` from radians into degrees. - - -.. function:: radians(x: Float) -> Float - - Convert ``x`` from degrees into radians. - - -.. function:: log(x: Float) -> Float - - Return the natural logarithm (base-e) of ``x``, where ``x`` is positive. - - -.. function:: log2(x: Float) -> Float - - Return the binary logarithm (base-2) of ``x``, where ``x`` is positive. - - -.. function:: log10(x: Float) -> Float - - Return the common logarithm (base-10) of ``x``, where ``x`` is positive. - - -.. function:: exp(x: Float) -> Float - - Return the value of the exponential function :math:`e^x`. - - -.. function:: pow(x: Float, y: Float) -> Float - - Return the result of ``x`` raised to power of ``y``. - - -.. function:: round(x: Float) -> Float - - Return the nearest integer value to ``x``, rounding halfway cases away from zero. - This is the most intuitive form of rounding in the colloquial sense, but can be slower than other options like :func:`warp.rint()`. - Differs from :func:`numpy.round()`, which behaves the same way as :func:`numpy.rint()`. - - -.. function:: rint(x: Float) -> Float - - Return the nearest integer value to ``x``, rounding halfway cases to nearest even integer. - It is generally faster than :func:`warp.round()`. Equivalent to :func:`numpy.rint()`. - - -.. function:: trunc(x: Float) -> Float - - Return the nearest integer that is closer to zero than ``x``. - In other words, it discards the fractional part of ``x``. - It is similar to casting ``float(int(x))``, but preserves the negative sign when x is in the range [-0.0, -1.0). - Equivalent to :func:`numpy.trunc()` and :func:`numpy.fix()`. - - -.. function:: floor(x: Float) -> Float - - Return the largest integer that is less than or equal to ``x``. - - -.. function:: ceil(x: Float) -> Float - - Return the smallest integer that is greater than or equal to ``x``. - - -.. function:: frac(x: Float) -> Float - - Retrieve the fractional part of x. - In other words, it discards the integer part of x and is equivalent to ``x - trunc(x)``. - - - - -Vector Math ---------------- -.. function:: dot(x: Vector[Any,Scalar], y: Vector[Any,Scalar]) -> Scalar - - Compute the dot product between two vectors. - - -.. function:: dot(x: Quaternion[Float], y: Quaternion[Float]) -> Scalar - :noindex: - :nocontentsentry: - - Compute the dot product between two quaternions. - - -.. function:: ddot(x: Matrix[Any,Any,Scalar], y: Matrix[Any,Any,Scalar]) -> Scalar - - Compute the double dot product between two matrices. - - -.. function:: argmin(v: Vector[Any,Scalar]) -> uint32 - - Return the index of the minimum element of a vector ``v``. [1]_ - - -.. function:: argmax(v: Vector[Any,Scalar]) -> uint32 - - Return the index of the maximum element of a vector ``v``. [1]_ - - -.. function:: outer(x: Vector[Any,Scalar], y: Vector[Any,Scalar]) -> Matrix[Any,Any,Scalar] - - Compute the outer product ``x*y^T`` for two vectors. - - -.. function:: cross(x: Vector[3,Scalar], y: Vector[3,Scalar]) -> Vector[3,Scalar] - - Compute the cross product of two 3D vectors. - - -.. function:: skew(x: Vector[3,Scalar]) - - Compute the skew-symmetric 3x3 matrix for a 3D vector ``x``. - - -.. function:: length(x: Vector[Any,Float]) -> Scalar - - Compute the length of a vector ``x``. - - -.. function:: length(x: Quaternion[Float]) -> Scalar - :noindex: - :nocontentsentry: - - Compute the length of a quaternion ``x``. - - -.. function:: length_sq(x: Vector[Any,Scalar]) -> Scalar - - Compute the squared length of a 2D vector ``x``. - - -.. function:: length_sq(x: Quaternion[Scalar]) -> Scalar - :noindex: - :nocontentsentry: - - Compute the squared length of a quaternion ``x``. - - -.. function:: normalize(x: Vector[Any,Float]) -> Vector[Any,Scalar] - - Compute the normalized value of ``x``. If ``length(x)`` is 0 then the zero vector is returned. - - -.. function:: normalize(x: Quaternion[Float]) -> Quaternion[Scalar] - :noindex: - :nocontentsentry: - - Compute the normalized value of ``x``. If ``length(x)`` is 0, then the zero quaternion is returned. - - -.. function:: transpose(m: Matrix[Any,Any,Scalar]) - - Return the transpose of the matrix ``m``. - - -.. function:: inverse(m: Matrix[2,2,Float]) -> Matrix[Any,Any,Float] - - Return the inverse of a 2x2 matrix ``m``. - - -.. function:: inverse(m: Matrix[3,3,Float]) -> Matrix[Any,Any,Float] - :noindex: - :nocontentsentry: - - Return the inverse of a 3x3 matrix ``m``. - - -.. function:: inverse(m: Matrix[4,4,Float]) -> Matrix[Any,Any,Float] - :noindex: - :nocontentsentry: - - Return the inverse of a 4x4 matrix ``m``. - - -.. function:: determinant(m: Matrix[2,2,Float]) -> Scalar - - Return the determinant of a 2x2 matrix ``m``. - - -.. function:: determinant(m: Matrix[3,3,Float]) -> Scalar - :noindex: - :nocontentsentry: - - Return the determinant of a 3x3 matrix ``m``. - - -.. function:: determinant(m: Matrix[4,4,Float]) -> Scalar - :noindex: - :nocontentsentry: - - Return the determinant of a 4x4 matrix ``m``. - - -.. function:: trace(m: Matrix[Any,Any,Scalar]) -> Scalar - - Return the trace of the matrix ``m``. - - -.. function:: diag(d: Vector[Any,Scalar]) -> Matrix[Any,Any,Scalar] - - Returns a matrix with the components of the vector ``d`` on the diagonal. - - -.. function:: get_diag(m: Matrix[Any,Any,Scalar]) -> Vector[Any,Scalar] - - Returns a vector containing the diagonal elements of the square matrix ``m``. - - -.. function:: cw_mul(x: Vector[Any,Scalar], y: Vector[Any,Scalar]) -> Vector[Any,Scalar] - - Component-wise multiplication of two 2D vectors. - - -.. function:: cw_mul(x: Matrix[Any,Any,Scalar], y: Matrix[Any,Any,Scalar]) -> Matrix[Any,Any,Scalar] - :noindex: - :nocontentsentry: - - Component-wise multiplication of two 2D vectors. - - -.. function:: cw_div(x: Vector[Any,Scalar], y: Vector[Any,Scalar]) -> Vector[Any,Scalar] - - Component-wise division of two 2D vectors. - - -.. function:: cw_div(x: Matrix[Any,Any,Scalar], y: Matrix[Any,Any,Scalar]) -> Matrix[Any,Any,Scalar] - :noindex: - :nocontentsentry: - - Component-wise division of two 2D vectors. - - -.. function:: vector(w: Vector[3,Float], v: Vector[3,Float]) - - Construct a 6D screw vector from two 3D vectors. - - -.. function:: vector(*arg_types: Scalar, length: int32, dtype: Scalar) -> Vector[Any,Scalar] - :noindex: - :nocontentsentry: - - Construct a vector of with given length and dtype. - - -.. function:: matrix(pos: Vector[3,Float], rot: Quaternion[Float], scale: Vector[3,Float]) -> Matrix[Any,Any,Float] - - Construct a 4x4 transformation matrix that applies the transformations as - Translation(pos)*Rotation(rot)*Scale(scale) when applied to column vectors, i.e.: y = (TRS)*x - - -.. function:: matrix(*arg_types: Scalar, shape: Tuple[int, int], dtype: Scalar) -> Matrix[Any,Any,Scalar] - :noindex: - :nocontentsentry: - - Construct a matrix. If the positional ``arg_types`` are not given, then matrix will be zero-initialized. - - -.. function:: identity(n: int32, dtype: Scalar) -> Matrix[Any,Any,Scalar] - - Create an identity matrix with shape=(n,n) with the type given by ``dtype``. - - -.. function:: svd3(A: Matrix[3,3,Float], U: Matrix[3,3,Float], sigma: Vector[3,Float], V: Matrix[3,3,Scalar]) -> None - - Compute the SVD of a 3x3 matrix ``A``. The singular values are returned in ``sigma``, - while the left and right basis vectors are returned in ``U`` and ``V``. - - -.. function:: qr3(A: Matrix[3,3,Float], Q: Matrix[3,3,Float], R: Matrix[3,3,Float]) -> None - - Compute the QR decomposition of a 3x3 matrix ``A``. The orthogonal matrix is returned in ``Q``, - while the upper triangular matrix is returned in ``R``. - - -.. function:: eig3(A: Matrix[3,3,Float], Q: Matrix[3,3,Float], d: Vector[3,Float]) -> None - - Compute the eigendecomposition of a 3x3 matrix ``A``. The eigenvectors are returned as the columns of ``Q``, - while the corresponding eigenvalues are returned in ``d``. - - - - -Other ---------------- -.. function:: lower_bound(arr: Array[Scalar], value: Scalar) -> int - - Search a sorted array ``arr`` for the closest element greater than or equal to ``value``. - - -.. function:: lower_bound(arr: Array[Scalar], arr_begin: int32, arr_end: int32, value: Scalar) -> int - :noindex: - :nocontentsentry: - - Search a sorted array ``arr`` in the range [arr_begin, arr_end) for the closest element greater than or equal to ``value``. - - - - -Quaternion Math ---------------- -.. function:: quaternion() -> Quaternion[Float] - - Construct a zero-initialized quaternion. Quaternions are laid out as - [ix, iy, iz, r], where ix, iy, iz are the imaginary part, and r the real part. - - -.. function:: quaternion(x: Float, y: Float, z: Float, w: Float) -> Quaternion[Float] - :noindex: - :nocontentsentry: - - Create a quaternion using the supplied components (type inferred from component type). - - -.. function:: quaternion(i: Vector[3,Float], r: Float) -> Quaternion[Float] - :noindex: - :nocontentsentry: - - Create a quaternion using the supplied vector/scalar (type inferred from scalar type). - - -.. function:: quaternion(q: Quaternion[Float]) - :noindex: - :nocontentsentry: - - Construct a quaternion of type dtype from another quaternion of a different dtype. - - -.. function:: quat_identity() -> quatf - - Construct an identity quaternion with zero imaginary part and real part of 1.0 - - -.. function:: quat_from_axis_angle(axis: Vector[3,Float], angle: Float) -> Quaternion[Scalar] - - Construct a quaternion representing a rotation of angle radians around the given axis. - - -.. function:: quat_to_axis_angle(q: Quaternion[Float], axis: Vector[3,Float], angle: Float) -> None - - Extract the rotation axis and angle radians a quaternion represents. - - -.. function:: quat_from_matrix(m: Matrix[3,3,Float]) -> Quaternion[Scalar] - - Construct a quaternion from a 3x3 matrix. - - -.. function:: quat_rpy(roll: Float, pitch: Float, yaw: Float) -> Quaternion[Scalar] - - Construct a quaternion representing a combined roll (z), pitch (x), yaw rotations (y) in radians. - - -.. function:: quat_inverse(q: Quaternion[Float]) -> Quaternion[Scalar] - - Compute quaternion conjugate. - - -.. function:: quat_rotate(q: Quaternion[Float], p: Vector[3,Float]) -> Vector[3,Scalar] - - Rotate a vector by a quaternion. - - -.. function:: quat_rotate_inv(q: Quaternion[Float], p: Vector[3,Float]) -> Vector[3,Scalar] - - Rotate a vector by the inverse of a quaternion. - - -.. function:: quat_slerp(q0: Quaternion[Float], q1: Quaternion[Float], t: Float) -> Quaternion[Scalar] - - Linearly interpolate between two quaternions. - - -.. function:: quat_to_matrix(q: Quaternion[Float]) -> Matrix[3,3,Scalar] - - Convert a quaternion to a 3x3 rotation matrix. - - - - -Transformations ---------------- -.. function:: transformation(p: Vector[3,Float], q: Quaternion[Float]) -> Transformation[Scalar] - - Construct a rigid-body transformation with translation part ``p`` and rotation ``q``. - - -.. function:: transform_identity() -> transformf - - Construct an identity transform with zero translation and identity rotation. - - -.. function:: transform_get_translation(t: Transformation[Float]) -> Vector[3,Scalar] - - Return the translational part of a transform ``t``. - - -.. function:: transform_get_rotation(t: Transformation[Float]) -> Quaternion[Scalar] - - Return the rotational part of a transform ``t``. - - -.. function:: transform_multiply(a: Transformation[Float], b: Transformation[Float]) -> Transformation[Scalar] - - Multiply two rigid body transformations together. - - -.. function:: transform_point(t: Transformation[Scalar], p: Vector[3,Scalar]) -> Vector[3,Scalar] - - Apply the transform to a point ``p`` treating the homogenous coordinate as w=1 (translation and rotation). - - -.. function:: transform_point(m: Matrix[4,4,Scalar], p: Vector[3,Scalar]) -> Vector[3,Scalar] - :noindex: - :nocontentsentry: - - Apply the transform to a point ``p`` treating the homogenous coordinate as w=1. - The transformation is applied treating ``p`` as a column vector, e.g.: ``y = M*p``. - Note this is in contrast to some libraries, notably USD, which applies transforms to row vectors, ``y^T = p^T*M^T``. - If the transform is coming from a library that uses row-vectors, then users should transpose the transformation - matrix before calling this method. - - -.. function:: transform_vector(t: Transformation[Scalar], v: Vector[3,Scalar]) -> Vector[3,Scalar] - - Apply the transform to a vector ``v`` treating the homogenous coordinate as w=0 (rotation only). - - -.. function:: transform_vector(m: Matrix[4,4,Scalar], v: Vector[3,Scalar]) -> Vector[3,Scalar] - :noindex: - :nocontentsentry: - - Apply the transform to a vector ``v`` treating the homogenous coordinate as w=0. - The transformation is applied treating ``v`` as a column vector, e.g.: ``y = M*v`` - note this is in contrast to some libraries, notably USD, which applies transforms to row vectors, ``y^T = v^T*M^T``. - If the transform is coming from a library that uses row-vectors, then users should transpose the transformation - matrix before calling this method. - - -.. function:: transform_inverse(t: Transformation[Float]) -> Transformation[Float] - - Compute the inverse of the transformation ``t``. - - - - -Spatial Math ---------------- -.. function:: spatial_adjoint(r: Matrix[3,3,Float], s: Matrix[3,3,Float]) -> Matrix[6,6,Scalar] - - Construct a 6x6 spatial inertial matrix from two 3x3 diagonal blocks. - - -.. function:: spatial_dot(a: Vector[6,Float], b: Vector[6,Float]) -> Scalar - - Compute the dot product of two 6D screw vectors. - - -.. function:: spatial_cross(a: Vector[6,Float], b: Vector[6,Float]) -> Vector[6,Float] - - Compute the cross product of two 6D screw vectors. - - -.. function:: spatial_cross_dual(a: Vector[6,Float], b: Vector[6,Float]) -> Vector[6,Float] - - Compute the dual cross product of two 6D screw vectors. - - -.. function:: spatial_top(a: Vector[6,Float]) - - Return the top (first) part of a 6D screw vector. - - -.. function:: spatial_bottom(a: Vector[6,Float]) - - Return the bottom (second) part of a 6D screw vector. - - -.. function:: spatial_jacobian(S: Array[Vector[6,Float]], joint_parents: Array[int32], joint_qd_start: Array[int32], joint_start: int32, joint_count: int32, J_start: int32, J_out: Array[Float]) -> None - - -.. function:: spatial_mass(I_s: Array[Matrix[6,6,Float]], joint_start: int32, joint_count: int32, M_start: int32, M: Array[Float]) -> None - - - - -Utility ---------------- -.. function:: mlp(weights: Array[float32], bias: Array[float32], activation: Callable, index: int32, x: Array[float32], out: Array[float32]) -> None - - Evaluate a multi-layer perceptron (MLP) layer in the form: ``out = act(weights*x + bias)``. - - :param weights: A layer's network weights with dimensions ``(m, n)``. - :param bias: An array with dimensions ``(n)``. - :param activation: A ``wp.func`` function that takes a single scalar float as input and returns a scalar float as output - :param index: The batch item to process, typically each thread will process one item in the batch, in which case - index should be ``wp.tid()`` - :param x: The feature matrix with dimensions ``(n, b)`` - :param out: The network output with dimensions ``(m, b)`` - - :note: Feature and output matrices are transposed compared to some other frameworks such as PyTorch. - All matrices are assumed to be stored in flattened row-major memory layout (NumPy default). - - -.. function:: printf() -> None - - Allows printing formatted strings using C-style format specifiers. - - -.. function:: print(value: Any) -> None - - Print variable to stdout - - -.. function:: breakpoint() -> None - - Debugger breakpoint - - -.. function:: tid() -> int - - Return the current thread index for a 1D kernel launch. Note that this is the *global* index of the thread in the range [0, dim) - where dim is the parameter passed to kernel launch. This function may not be called from user-defined Warp functions. - - -.. function:: tid() -> Tuple[int, int] - :noindex: - :nocontentsentry: - - Return the current thread indices for a 2D kernel launch. Use ``i,j = wp.tid()`` syntax to retrieve the - coordinates inside the kernel thread grid. This function may not be called from user-defined Warp functions. - - -.. function:: tid() -> Tuple[int, int, int] - :noindex: - :nocontentsentry: - - Return the current thread indices for a 3D kernel launch. Use ``i,j,k = wp.tid()`` syntax to retrieve the - coordinates inside the kernel thread grid. This function may not be called from user-defined Warp functions. - - -.. function:: tid() -> Tuple[int, int, int, int] - :noindex: - :nocontentsentry: - - Return the current thread indices for a 4D kernel launch. Use ``i,j,k,l = wp.tid()`` syntax to retrieve the - coordinates inside the kernel thread grid. This function may not be called from user-defined Warp functions. - - -.. function:: select(cond: bool, arg1: Any, arg2: Any) - - Select between two arguments, if ``cond`` is ``False`` then return ``arg1``, otherwise return ``arg2`` - - -.. function:: select(cond: bool, arg1: Any, arg2: Any) - :noindex: - :nocontentsentry: - - Select between two arguments, if ``cond`` is ``False`` then return ``arg1``, otherwise return ``arg2`` - - -.. function:: select(cond: int8, arg1: Any, arg2: Any) - :noindex: - :nocontentsentry: - - Select between two arguments, if ``cond`` is ``False`` then return ``arg1``, otherwise return ``arg2`` - - -.. function:: select(cond: uint8, arg1: Any, arg2: Any) - :noindex: - :nocontentsentry: - - Select between two arguments, if ``cond`` is ``False`` then return ``arg1``, otherwise return ``arg2`` - - -.. function:: select(cond: int16, arg1: Any, arg2: Any) - :noindex: - :nocontentsentry: - - Select between two arguments, if ``cond`` is ``False`` then return ``arg1``, otherwise return ``arg2`` - - -.. function:: select(cond: uint16, arg1: Any, arg2: Any) - :noindex: - :nocontentsentry: - - Select between two arguments, if ``cond`` is ``False`` then return ``arg1``, otherwise return ``arg2`` - - -.. function:: select(cond: int32, arg1: Any, arg2: Any) - :noindex: - :nocontentsentry: - - Select between two arguments, if ``cond`` is ``False`` then return ``arg1``, otherwise return ``arg2`` - - -.. function:: select(cond: uint32, arg1: Any, arg2: Any) - :noindex: - :nocontentsentry: - - Select between two arguments, if ``cond`` is ``False`` then return ``arg1``, otherwise return ``arg2`` - - -.. function:: select(cond: int64, arg1: Any, arg2: Any) - :noindex: - :nocontentsentry: - - Select between two arguments, if ``cond`` is ``False`` then return ``arg1``, otherwise return ``arg2`` - - -.. function:: select(cond: uint64, arg1: Any, arg2: Any) - :noindex: - :nocontentsentry: - - Select between two arguments, if ``cond`` is ``False`` then return ``arg1``, otherwise return ``arg2`` - - -.. function:: select(arr: Array[Any], arg1: Any, arg2: Any) - :noindex: - :nocontentsentry: - - Select between two arguments, if ``arr`` is null then return ``arg1``, otherwise return ``arg2`` - - -.. function:: atomic_add(a: Array[Any], i: int32, value: Any) - - Atomically add ``value`` onto ``a[i]``. - - -.. function:: atomic_add(a: Array[Any], i: int32, j: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically add ``value`` onto ``a[i,j]``. - - -.. function:: atomic_add(a: Array[Any], i: int32, j: int32, k: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically add ``value`` onto ``a[i,j,k]``. - - -.. function:: atomic_add(a: Array[Any], i: int32, j: int32, k: int32, l: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically add ``value`` onto ``a[i,j,k,l]``. - - -.. function:: atomic_add(a: FabricArray[Any], i: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically add ``value`` onto ``a[i]``. - - -.. function:: atomic_add(a: FabricArray[Any], i: int32, j: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically add ``value`` onto ``a[i,j]``. - - -.. function:: atomic_add(a: FabricArray[Any], i: int32, j: int32, k: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically add ``value`` onto ``a[i,j,k]``. - - -.. function:: atomic_add(a: FabricArray[Any], i: int32, j: int32, k: int32, l: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically add ``value`` onto ``a[i,j,k,l]``. - - -.. function:: atomic_add(a: IndexedFabricArray[Any], i: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically add ``value`` onto ``a[i]``. - - -.. function:: atomic_add(a: IndexedFabricArray[Any], i: int32, j: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically add ``value`` onto ``a[i,j]``. - - -.. function:: atomic_add(a: IndexedFabricArray[Any], i: int32, j: int32, k: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically add ``value`` onto ``a[i,j,k]``. - - -.. function:: atomic_add(a: IndexedFabricArray[Any], i: int32, j: int32, k: int32, l: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically add ``value`` onto ``a[i,j,k,l]``. - - -.. function:: atomic_sub(a: Array[Any], i: int32, value: Any) - - Atomically subtract ``value`` onto ``a[i]``. - - -.. function:: atomic_sub(a: Array[Any], i: int32, j: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically subtract ``value`` onto ``a[i,j]``. - - -.. function:: atomic_sub(a: Array[Any], i: int32, j: int32, k: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically subtract ``value`` onto ``a[i,j,k]``. - - -.. function:: atomic_sub(a: Array[Any], i: int32, j: int32, k: int32, l: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically subtract ``value`` onto ``a[i,j,k,l]``. - - -.. function:: atomic_sub(a: FabricArray[Any], i: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically subtract ``value`` onto ``a[i]``. - - -.. function:: atomic_sub(a: FabricArray[Any], i: int32, j: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically subtract ``value`` onto ``a[i,j]``. - - -.. function:: atomic_sub(a: FabricArray[Any], i: int32, j: int32, k: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically subtract ``value`` onto ``a[i,j,k]``. - - -.. function:: atomic_sub(a: FabricArray[Any], i: int32, j: int32, k: int32, l: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically subtract ``value`` onto ``a[i,j,k,l]``. - - -.. function:: atomic_sub(a: IndexedFabricArray[Any], i: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically subtract ``value`` onto ``a[i]``. - - -.. function:: atomic_sub(a: IndexedFabricArray[Any], i: int32, j: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically subtract ``value`` onto ``a[i,j]``. - - -.. function:: atomic_sub(a: IndexedFabricArray[Any], i: int32, j: int32, k: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically subtract ``value`` onto ``a[i,j,k]``. - - -.. function:: atomic_sub(a: IndexedFabricArray[Any], i: int32, j: int32, k: int32, l: int32, value: Any) - :noindex: - :nocontentsentry: - - Atomically subtract ``value`` onto ``a[i,j,k,l]``. - - -.. function:: atomic_min(a: Array[Any], i: int32, value: Any) - - Compute the minimum of ``value`` and ``a[i]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_min(a: Array[Any], i: int32, j: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the minimum of ``value`` and ``a[i,j]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_min(a: Array[Any], i: int32, j: int32, k: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the minimum of ``value`` and ``a[i,j,k]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_min(a: Array[Any], i: int32, j: int32, k: int32, l: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the minimum of ``value`` and ``a[i,j,k,l]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_min(a: FabricArray[Any], i: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the minimum of ``value`` and ``a[i]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_min(a: FabricArray[Any], i: int32, j: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the minimum of ``value`` and ``a[i,j]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_min(a: FabricArray[Any], i: int32, j: int32, k: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the minimum of ``value`` and ``a[i,j,k]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_min(a: FabricArray[Any], i: int32, j: int32, k: int32, l: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the minimum of ``value`` and ``a[i,j,k,l]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_min(a: IndexedFabricArray[Any], i: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the minimum of ``value`` and ``a[i]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_min(a: IndexedFabricArray[Any], i: int32, j: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the minimum of ``value`` and ``a[i,j]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_min(a: IndexedFabricArray[Any], i: int32, j: int32, k: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the minimum of ``value`` and ``a[i,j,k]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_min(a: IndexedFabricArray[Any], i: int32, j: int32, k: int32, l: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the minimum of ``value`` and ``a[i,j,k,l]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_max(a: Array[Any], i: int32, value: Any) - - Compute the maximum of ``value`` and ``a[i]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_max(a: Array[Any], i: int32, j: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the maximum of ``value`` and ``a[i,j]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_max(a: Array[Any], i: int32, j: int32, k: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the maximum of ``value`` and ``a[i,j,k]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_max(a: Array[Any], i: int32, j: int32, k: int32, l: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the maximum of ``value`` and ``a[i,j,k,l]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_max(a: FabricArray[Any], i: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the maximum of ``value`` and ``a[i]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_max(a: FabricArray[Any], i: int32, j: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the maximum of ``value`` and ``a[i,j]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_max(a: FabricArray[Any], i: int32, j: int32, k: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the maximum of ``value`` and ``a[i,j,k]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_max(a: FabricArray[Any], i: int32, j: int32, k: int32, l: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the maximum of ``value`` and ``a[i,j,k,l]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_max(a: IndexedFabricArray[Any], i: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the maximum of ``value`` and ``a[i]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_max(a: IndexedFabricArray[Any], i: int32, j: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the maximum of ``value`` and ``a[i,j]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_max(a: IndexedFabricArray[Any], i: int32, j: int32, k: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the maximum of ``value`` and ``a[i,j,k]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: atomic_max(a: IndexedFabricArray[Any], i: int32, j: int32, k: int32, l: int32, value: Any) - :noindex: - :nocontentsentry: - - Compute the maximum of ``value`` and ``a[i,j,k,l]`` and atomically update the array. - -Note that for vectors and matrices the operation is only atomic on a per-component basis. - - -.. function:: lerp(a: Float, b: Float, t: Float) -> Float - - Linearly interpolate two values ``a`` and ``b`` using factor ``t``, computed as ``a*(1-t) + b*t`` - - -.. function:: lerp(a: Vector[Any,Float], b: Vector[Any,Float], t: Float) -> Vector[Any,Float] - :noindex: - :nocontentsentry: - - Linearly interpolate two values ``a`` and ``b`` using factor ``t``, computed as ``a*(1-t) + b*t`` - - -.. function:: lerp(a: Matrix[Any,Any,Float], b: Matrix[Any,Any,Float], t: Float) -> Matrix[Any,Any,Float] - :noindex: - :nocontentsentry: - - Linearly interpolate two values ``a`` and ``b`` using factor ``t``, computed as ``a*(1-t) + b*t`` - - -.. function:: lerp(a: Quaternion[Float], b: Quaternion[Float], t: Float) -> Quaternion[Float] - :noindex: - :nocontentsentry: - - Linearly interpolate two values ``a`` and ``b`` using factor ``t``, computed as ``a*(1-t) + b*t`` - - -.. function:: lerp(a: Transformation[Float], b: Transformation[Float], t: Float) -> Transformation[Float] - :noindex: - :nocontentsentry: - - Linearly interpolate two values ``a`` and ``b`` using factor ``t``, computed as ``a*(1-t) + b*t`` - - -.. function:: smoothstep(edge0: Float, edge1: Float, x: Float) -> Float - - Smoothly interpolate between two values ``edge0`` and ``edge1`` using a factor ``x``, - and return a result between 0 and 1 using a cubic Hermite interpolation after clamping. - - -.. function:: expect_near(arg1: Float, arg2: Float, tolerance: Float) -> None - - Prints an error to stdout if ``arg1`` and ``arg2`` are not closer than tolerance in magnitude - - -.. function:: expect_near(arg1: vec3f, arg2: vec3f, tolerance: float32) -> None - :noindex: - :nocontentsentry: - - Prints an error to stdout if any element of ``arg1`` and ``arg2`` are not closer than tolerance in magnitude - - - - -Geometry ---------------- -.. function:: bvh_query_aabb(id: uint64, lower: vec3f, upper: vec3f) -> bvh_query_t - - Construct an axis-aligned bounding box query against a BVH object. This query can be used to iterate over all bounds - inside a BVH. Returns an object that is used to track state during BVH traversal. - - :param id: The BVH identifier - :param lower: The lower bound of the bounding box in BVH space - :param upper: The upper bound of the bounding box in BVH space - - -.. function:: bvh_query_ray(id: uint64, start: vec3f, dir: vec3f) -> bvh_query_t - - Construct a ray query against a BVH object. This query can be used to iterate over all bounds - that intersect the ray. Returns an object that is used to track state during BVH traversal. - - :param id: The BVH identifier - :param start: The start of the ray in BVH space - :param dir: The direction of the ray in BVH space - - -.. function:: bvh_query_next(query: bvh_query_t, index: int32) -> bool - - Move to the next bound returned by the query. - The index of the current bound is stored in ``index``, returns ``False`` if there are no more overlapping bound. - - -.. function:: mesh_query_point(id: uint64, point: vec3f, max_dist: float32, inside: float32, face: int32, bary_u: float32, bary_v: float32) -> bool - - Computes the closest point on the :class:`Mesh` with identifier ``id`` to the given ``point`` in space. Returns ``True`` if a point < ``max_dist`` is found. - - Identifies the sign of the distance using additional ray-casts to determine if the point is inside or outside. - This method is relatively robust, but does increase computational cost. - See below for additional sign determination methods. - - :param id: The mesh identifier - :param point: The point in space to query - :param max_dist: Mesh faces above this distance will not be considered by the query - :param inside: Returns a value < 0 if query point is inside the mesh, >=0 otherwise. - Note that mesh must be watertight for this to be robust - :param face: Returns the index of the closest face - :param bary_u: Returns the barycentric u coordinate of the closest point - :param bary_v: Returns the barycentric v coordinate of the closest point - - -.. function:: mesh_query_point_no_sign(id: uint64, point: vec3f, max_dist: float32, face: int32, bary_u: float32, bary_v: float32) -> bool - - Computes the closest point on the :class:`Mesh` with identifier ``id`` to the given ``point`` in space. Returns ``True`` if a point < ``max_dist`` is found. - - This method does not compute the sign of the point (inside/outside) which makes it faster than other point query methods. - - :param id: The mesh identifier - :param point: The point in space to query - :param max_dist: Mesh faces above this distance will not be considered by the query - :param face: Returns the index of the closest face - :param bary_u: Returns the barycentric u coordinate of the closest point - :param bary_v: Returns the barycentric v coordinate of the closest point - - -.. function:: mesh_query_furthest_point_no_sign(id: uint64, point: vec3f, min_dist: float32, face: int32, bary_u: float32, bary_v: float32) -> bool - - Computes the furthest point on the mesh with identifier `id` to the given point in space. Returns ``True`` if a point > ``min_dist`` is found. - - This method does not compute the sign of the point (inside/outside). - - :param id: The mesh identifier - :param point: The point in space to query - :param min_dist: Mesh faces below this distance will not be considered by the query - :param face: Returns the index of the furthest face - :param bary_u: Returns the barycentric u coordinate of the furthest point - :param bary_v: Returns the barycentric v coordinate of the furthest point - - -.. function:: mesh_query_point_sign_normal(id: uint64, point: vec3f, max_dist: float32, inside: float32, face: int32, bary_u: float32, bary_v: float32, epsilon: float32) -> bool - - Computes the closest point on the :class:`Mesh` with identifier ``id`` to the given ``point`` in space. Returns ``True`` if a point < ``max_dist`` is found. - - Identifies the sign of the distance (inside/outside) using the angle-weighted pseudo normal. - This approach to sign determination is robust for well conditioned meshes that are watertight and non-self intersecting. - It is also comparatively fast to compute. - - :param id: The mesh identifier - :param point: The point in space to query - :param max_dist: Mesh faces above this distance will not be considered by the query - :param inside: Returns a value < 0 if query point is inside the mesh, >=0 otherwise. - Note that mesh must be watertight for this to be robust - :param face: Returns the index of the closest face - :param bary_u: Returns the barycentric u coordinate of the closest point - :param bary_v: Returns the barycentric v coordinate of the closest point - :param epsilon: Epsilon treating distance values as equal, when locating the minimum distance vertex/face/edge, as a - fraction of the average edge length, also for treating closest point as being on edge/vertex default 1e-3 - - -.. function:: mesh_query_point_sign_winding_number(id: uint64, point: vec3f, max_dist: float32, inside: float32, face: int32, bary_u: float32, bary_v: float32, accuracy: float32, threshold: float32) -> bool - - Computes the closest point on the :class:`Mesh` with identifier ``id`` to the given point in space. Returns ``True`` if a point < ``max_dist`` is found. - - Identifies the sign using the winding number of the mesh relative to the query point. This method of sign determination is robust for poorly conditioned meshes - and provides a smooth approximation to sign even when the mesh is not watertight. This method is the most robust and accurate of the sign determination meshes - but also the most expensive. - - .. note:: The :class:`Mesh` object must be constructed with ``support_winding_number=True`` for this method to return correct results. - - :param id: The mesh identifier - :param point: The point in space to query - :param max_dist: Mesh faces above this distance will not be considered by the query - :param inside: Returns a value < 0 if query point is inside the mesh, >=0 otherwise. - Note that mesh must be watertight for this to be robust - :param face: Returns the index of the closest face - :param bary_u: Returns the barycentric u coordinate of the closest point - :param bary_v: Returns the barycentric v coordinate of the closest point - :param accuracy: Accuracy for computing the winding number with fast winding number method utilizing second-order dipole approximation, default 2.0 - :param threshold: The threshold of the winding number to be considered inside, default 0.5 - - -.. function:: mesh_query_ray(id: uint64, start: vec3f, dir: vec3f, max_t: float32, t: float32, bary_u: float32, bary_v: float32, sign: float32, normal: vec3f, face: int32) -> bool - - Computes the closest ray hit on the :class:`Mesh` with identifier ``id``, returns ``True`` if a point < ``max_t`` is found. - - :param id: The mesh identifier - :param start: The start point of the ray - :param dir: The ray direction (should be normalized) - :param max_t: The maximum distance along the ray to check for intersections - :param t: Returns the distance of the closest hit along the ray - :param bary_u: Returns the barycentric u coordinate of the closest hit - :param bary_v: Returns the barycentric v coordinate of the closest hit - :param sign: Returns a value > 0 if the hit ray hit front of the face, returns < 0 otherwise - :param normal: Returns the face normal - :param face: Returns the index of the hit face - - -.. function:: mesh_query_aabb(id: uint64, lower: vec3f, upper: vec3f) -> mesh_query_aabb_t - - Construct an axis-aligned bounding box query against a :class:`Mesh`. - This query can be used to iterate over all triangles inside a volume. - Returns an object that is used to track state during mesh traversal. - - :param id: The mesh identifier - :param lower: The lower bound of the bounding box in mesh space - :param upper: The upper bound of the bounding box in mesh space - - -.. function:: mesh_query_aabb_next(query: mesh_query_aabb_t, index: int32) -> bool - - Move to the next triangle overlapping the query bounding box. - The index of the current face is stored in ``index``, returns ``False`` if there are no more overlapping triangles. - - -.. function:: mesh_eval_position(id: uint64, face: int32, bary_u: float32, bary_v: float32) -> vec3f - - Evaluates the position on the :class:`Mesh` given a face index and barycentric coordinates. - - -.. function:: mesh_eval_velocity(id: uint64, face: int32, bary_u: float32, bary_v: float32) -> vec3f - - Evaluates the velocity on the :class:`Mesh` given a face index and barycentric coordinates. - - -.. function:: hash_grid_query(id: uint64, point: vec3f, max_dist: float32) -> hash_grid_query_t - - Construct a point query against a :class:`HashGrid`. This query can be used to iterate over all neighboring points within a fixed radius from the query point. Returns an object that is used to track state during neighbor traversal. - - -.. function:: hash_grid_query_next(query: hash_grid_query_t, index: int32) -> bool - - Move to the next point in the hash grid query. The index of the current neighbor is stored in ``index``, returns ``False`` - if there are no more neighbors. - - -.. function:: hash_grid_point_id(id: uint64, index: int32) -> int - - Return the index of a point in the :class:`HashGrid`. This can be used to reorder threads such that grid - traversal occurs in a spatially coherent order. - - Returns -1 if the :class:`HashGrid` has not been reserved. - - -.. function:: intersect_tri_tri(v0: vec3f, v1: vec3f, v2: vec3f, u0: vec3f, u1: vec3f, u2: vec3f) -> int - - Tests for intersection between two triangles (v0, v1, v2) and (u0, u1, u2) using Moller's method. Returns > 0 if triangles intersect. - - -.. function:: mesh_get(id: uint64) -> Mesh - - Retrieves the mesh given its index. [1]_ - - -.. function:: mesh_eval_face_normal(id: uint64, face: int32) -> vec3f - - Evaluates the face normal the mesh given a face index. - - -.. function:: mesh_get_point(id: uint64, index: int32) -> vec3f - - Returns the point of the mesh given a index. - - -.. function:: mesh_get_velocity(id: uint64, index: int32) -> vec3f - - Returns the velocity of the mesh given a index. - - -.. function:: mesh_get_index(id: uint64, index: int32) -> int - - Returns the point-index of the mesh given a face-vertex index. - - -.. function:: closest_point_edge_edge(p1: vec3f, q1: vec3f, p2: vec3f, q2: vec3f, epsilon: float32) -> vec3f - - Finds the closest points between two edges. Returns barycentric weights to the points on each edge, as well as the closest distance between the edges. - - :param p1: First point of first edge - :param q1: Second point of first edge - :param p2: First point of second edge - :param q2: Second point of second edge - :param epsilon: Zero tolerance for determining if points in an edge are degenerate. - :param out: vec3 output containing (s,t,d), where `s` in [0,1] is the barycentric weight for the first edge, `t` is the barycentric weight for the second edge, and `d` is the distance between the two edges at these two closest points. - - - - -Volumes ---------------- -.. function:: volume_sample_f(id: uint64, uvw: vec3f, sampling_mode: int32) -> float - - Sample the volume given by ``id`` at the volume local-space point ``uvw``. - Interpolation should be :attr:`warp.Volume.CLOSEST` or :attr:`wp.Volume.LINEAR.` - - -.. function:: volume_sample_grad_f(id: uint64, uvw: vec3f, sampling_mode: int32, grad: vec3f) -> float - - Sample the volume and its gradient given by ``id`` at the volume local-space point ``uvw``. - Interpolation should be :attr:`warp.Volume.CLOSEST` or :attr:`wp.Volume.LINEAR.` - - -.. function:: volume_lookup_f(id: uint64, i: int32, j: int32, k: int32) -> float - - Returns the value of voxel with coordinates ``i``, ``j``, ``k``. - If the voxel at this index does not exist, this function returns the background value - - -.. function:: volume_store_f(id: uint64, i: int32, j: int32, k: int32, value: float32) -> None - - Store ``value`` at the voxel with coordinates ``i``, ``j``, ``k``. - - -.. function:: volume_sample_v(id: uint64, uvw: vec3f, sampling_mode: int32) -> vec3f - - Sample the vector volume given by ``id`` at the volume local-space point ``uvw``. - Interpolation should be :attr:`warp.Volume.CLOSEST` or :attr:`wp.Volume.LINEAR.` - - -.. function:: volume_lookup_v(id: uint64, i: int32, j: int32, k: int32) -> vec3f - - Returns the vector value of voxel with coordinates ``i``, ``j``, ``k``. - If the voxel at this index does not exist, this function returns the background value. - - -.. function:: volume_store_v(id: uint64, i: int32, j: int32, k: int32, value: vec3f) -> None - - Store ``value`` at the voxel with coordinates ``i``, ``j``, ``k``. - - -.. function:: volume_sample_i(id: uint64, uvw: vec3f) -> int - - Sample the :class:`int32` volume given by ``id`` at the volume local-space point ``uvw``. - - -.. function:: volume_lookup_i(id: uint64, i: int32, j: int32, k: int32) -> int - - Returns the :class:`int32` value of voxel with coordinates ``i``, ``j``, ``k``. - If the voxel at this index does not exist, this function returns the background value. - - -.. function:: volume_store_i(id: uint64, i: int32, j: int32, k: int32, value: int32) -> None - - Store ``value`` at the voxel with coordinates ``i``, ``j``, ``k``. - - -.. function:: volume_index_to_world(id: uint64, uvw: vec3f) -> vec3f - - Transform a point ``uvw`` defined in volume index space to world space given the volume's intrinsic affine transformation. - - -.. function:: volume_world_to_index(id: uint64, xyz: vec3f) -> vec3f - - Transform a point ``xyz`` defined in volume world space to the volume's index space given the volume's intrinsic affine transformation. - - -.. function:: volume_index_to_world_dir(id: uint64, uvw: vec3f) -> vec3f - - Transform a direction ``uvw`` defined in volume index space to world space given the volume's intrinsic affine transformation. - - -.. function:: volume_world_to_index_dir(id: uint64, xyz: vec3f) -> vec3f - - Transform a direction ``xyz`` defined in volume world space to the volume's index space given the volume's intrinsic affine transformation. - - - - -Random ---------------- -.. function:: rand_init(seed: int32) -> uint32 - - Initialize a new random number generator given a user-defined seed. Returns a 32-bit integer representing the RNG state. - - -.. function:: rand_init(seed: int32, offset: int32) -> uint32 - :noindex: - :nocontentsentry: - - Initialize a new random number generator given a user-defined seed and an offset. - This alternative constructor can be useful in parallel programs, where a kernel as a whole should share a seed, - but each thread should generate uncorrelated values. In this case usage should be ``r = rand_init(seed, tid)`` - - -.. function:: randi(state: uint32) -> int - - Return a random integer in the range [0, 2^32). - - -.. function:: randi(state: uint32, min: int32, max: int32) -> int - :noindex: - :nocontentsentry: - - Return a random integer between [min, max). - - -.. function:: randf(state: uint32) -> float - - Return a random float between [0.0, 1.0). - - -.. function:: randf(state: uint32, min: float32, max: float32) -> float - :noindex: - :nocontentsentry: - - Return a random float between [min, max). - - -.. function:: randn(state: uint32) -> float - - Sample a normal distribution. - - -.. function:: sample_cdf(state: uint32, cdf: Array[float32]) -> int - - Inverse-transform sample a cumulative distribution function. - - -.. function:: sample_triangle(state: uint32) -> vec2f - - Uniformly sample a triangle. Returns sample barycentric coordinates. - - -.. function:: sample_unit_ring(state: uint32) -> vec2f - - Uniformly sample a ring in the xy plane. - - -.. function:: sample_unit_disk(state: uint32) -> vec2f - - Uniformly sample a disk in the xy plane. - - -.. function:: sample_unit_sphere_surface(state: uint32) -> vec3f - - Uniformly sample a unit sphere surface. - - -.. function:: sample_unit_sphere(state: uint32) -> vec3f - - Uniformly sample a unit sphere. - - -.. function:: sample_unit_hemisphere_surface(state: uint32) -> vec3f - - Uniformly sample a unit hemisphere surface. - - -.. function:: sample_unit_hemisphere(state: uint32) -> vec3f - - Uniformly sample a unit hemisphere. - - -.. function:: sample_unit_square(state: uint32) -> vec2f - - Uniformly sample a unit square. - - -.. function:: sample_unit_cube(state: uint32) -> vec3f - - Uniformly sample a unit cube. - - -.. function:: poisson(state: uint32, lam: float32) -> uint32 - - Generate a random sample from a Poisson distribution. - - :param state: RNG state - :param lam: The expected value of the distribution - - -.. function:: noise(state: uint32, x: float32) -> float - - Non-periodic Perlin-style noise in 1D. - - -.. function:: noise(state: uint32, xy: vec2f) -> float - :noindex: - :nocontentsentry: - - Non-periodic Perlin-style noise in 2D. - - -.. function:: noise(state: uint32, xyz: vec3f) -> float - :noindex: - :nocontentsentry: - - Non-periodic Perlin-style noise in 3D. - - -.. function:: noise(state: uint32, xyzt: vec4f) -> float - :noindex: - :nocontentsentry: - - Non-periodic Perlin-style noise in 4D. - - -.. function:: pnoise(state: uint32, x: float32, px: int32) -> float - - Periodic Perlin-style noise in 1D. - - -.. function:: pnoise(state: uint32, xy: vec2f, px: int32, py: int32) -> float - :noindex: - :nocontentsentry: - - Periodic Perlin-style noise in 2D. - - -.. function:: pnoise(state: uint32, xyz: vec3f, px: int32, py: int32, pz: int32) -> float - :noindex: - :nocontentsentry: - - Periodic Perlin-style noise in 3D. - - -.. function:: pnoise(state: uint32, xyzt: vec4f, px: int32, py: int32, pz: int32, pt: int32) -> float - :noindex: - :nocontentsentry: - - Periodic Perlin-style noise in 4D. - - -.. function:: curlnoise(state: uint32, xy: vec2f, octaves: uint32, lacunarity: float32, gain: float32) -> vec2f - - Divergence-free vector field based on the gradient of a Perlin noise function. [1]_ - - -.. function:: curlnoise(state: uint32, xyz: vec3f, octaves: uint32, lacunarity: float32, gain: float32) -> vec3f - :noindex: - :nocontentsentry: - - Divergence-free vector field based on the curl of three Perlin noise functions. [1]_ - - -.. function:: curlnoise(state: uint32, xyzt: vec4f, octaves: uint32, lacunarity: float32, gain: float32) -> vec3f - :noindex: - :nocontentsentry: - - Divergence-free vector field based on the curl of three Perlin noise functions. [1]_ - - - - -Operators ---------------- -.. function:: add(x: Scalar, y: Scalar) -> Scalar - - -.. function:: add(x: Vector[Any,Scalar], y: Vector[Any,Scalar]) -> Vector[Any,Scalar] - :noindex: - :nocontentsentry: - - -.. function:: add(x: Quaternion[Scalar], y: Quaternion[Scalar]) -> Quaternion[Scalar] - :noindex: - :nocontentsentry: - - -.. function:: add(x: Matrix[Any,Any,Scalar], y: Matrix[Any,Any,Scalar]) -> Matrix[Any,Any,Scalar] - :noindex: - :nocontentsentry: - - -.. function:: add(x: Transformation[Scalar], y: Transformation[Scalar]) -> Transformation[Scalar] - :noindex: - :nocontentsentry: - - -.. function:: sub(x: Scalar, y: Scalar) -> Scalar - - -.. function:: sub(x: Vector[Any,Scalar], y: Vector[Any,Scalar]) -> Vector[Any,Scalar] - :noindex: - :nocontentsentry: - - -.. function:: sub(x: Matrix[Any,Any,Scalar], y: Matrix[Any,Any,Scalar]) -> Matrix[Any,Any,Scalar] - :noindex: - :nocontentsentry: - - -.. function:: sub(x: Quaternion[Scalar], y: Quaternion[Scalar]) -> Quaternion[Scalar] - :noindex: - :nocontentsentry: - - -.. function:: sub(x: Transformation[Scalar], y: Transformation[Scalar]) -> Transformation[Scalar] - :noindex: - :nocontentsentry: - - -.. function:: bit_and(x: Int, y: Int) -> Int - - -.. function:: bit_or(x: Int, y: Int) -> Int - - -.. function:: bit_xor(x: Int, y: Int) -> Int - - -.. function:: lshift(x: Int, y: Int) -> Int - - -.. function:: rshift(x: Int, y: Int) -> Int - - -.. function:: invert(x: Int) -> Int - - -.. function:: mul(x: Scalar, y: Scalar) -> Scalar - - -.. function:: mul(x: Vector[Any,Scalar], y: Scalar) -> Vector[Any,Scalar] - :noindex: - :nocontentsentry: - - -.. function:: mul(x: Scalar, y: Vector[Any,Scalar]) -> Vector[Any,Scalar] - :noindex: - :nocontentsentry: - - -.. function:: mul(x: Quaternion[Scalar], y: Scalar) -> Quaternion[Scalar] - :noindex: - :nocontentsentry: - - -.. function:: mul(x: Scalar, y: Quaternion[Scalar]) -> Quaternion[Scalar] - :noindex: - :nocontentsentry: - - -.. function:: mul(x: Quaternion[Scalar], y: Quaternion[Scalar]) -> Quaternion[Scalar] - :noindex: - :nocontentsentry: - - -.. function:: mul(x: Scalar, y: Matrix[Any,Any,Scalar]) -> Matrix[Any,Any,Scalar] - :noindex: - :nocontentsentry: - - -.. function:: mul(x: Matrix[Any,Any,Scalar], y: Scalar) -> Matrix[Any,Any,Scalar] - :noindex: - :nocontentsentry: - - -.. function:: mul(x: Matrix[Any,Any,Scalar], y: Vector[Any,Scalar]) -> Vector[Any,Scalar] - :noindex: - :nocontentsentry: - - -.. function:: mul(x: Matrix[Any,Any,Scalar], y: Matrix[Any,Any,Scalar]) - :noindex: - :nocontentsentry: - - -.. function:: mul(x: Transformation[Scalar], y: Transformation[Scalar]) -> Transformation[Scalar] - :noindex: - :nocontentsentry: - - -.. function:: mul(x: Scalar, y: Transformation[Scalar]) -> Transformation[Scalar] - :noindex: - :nocontentsentry: - - -.. function:: mul(x: Transformation[Scalar], y: Scalar) -> Transformation[Scalar] - :noindex: - :nocontentsentry: - - -.. function:: mod(x: Scalar, y: Scalar) -> Scalar - - -.. function:: div(x: Scalar, y: Scalar) -> Scalar - - -.. function:: div(x: Vector[Any,Scalar], y: Scalar) -> Vector[Any,Scalar] - :noindex: - :nocontentsentry: - - -.. function:: div(x: Matrix[Any,Any,Scalar], y: Scalar) -> Matrix[Any,Any,Scalar] - :noindex: - :nocontentsentry: - - -.. function:: div(x: Quaternion[Scalar], y: Scalar) -> Quaternion[Scalar] - :noindex: - :nocontentsentry: - - -.. function:: floordiv(x: Scalar, y: Scalar) -> Scalar - - -.. function:: pos(x: Scalar) -> Scalar - - -.. function:: pos(x: Vector[Any,Scalar]) -> Vector[Any,Scalar] - :noindex: - :nocontentsentry: - - -.. function:: pos(x: Quaternion[Scalar]) -> Quaternion[Scalar] - :noindex: - :nocontentsentry: - - -.. function:: pos(x: Matrix[Any,Any,Scalar]) -> Matrix[Any,Any,Scalar] - :noindex: - :nocontentsentry: - - -.. function:: neg(x: Scalar) -> Scalar - - -.. function:: neg(x: Vector[Any,Scalar]) -> Vector[Any,Scalar] - :noindex: - :nocontentsentry: - - -.. function:: neg(x: Quaternion[Scalar]) -> Quaternion[Scalar] - :noindex: - :nocontentsentry: - - -.. function:: neg(x: Matrix[Any,Any,Scalar]) -> Matrix[Any,Any,Scalar] - :noindex: - :nocontentsentry: - - -.. function:: unot(b: bool) -> bool - - -.. function:: unot(b: int8) -> bool - :noindex: - :nocontentsentry: - - -.. function:: unot(b: uint8) -> bool - :noindex: - :nocontentsentry: - - -.. function:: unot(b: int16) -> bool - :noindex: - :nocontentsentry: - - -.. function:: unot(b: uint16) -> bool - :noindex: - :nocontentsentry: - - -.. function:: unot(b: int32) -> bool - :noindex: - :nocontentsentry: - - -.. function:: unot(b: uint32) -> bool - :noindex: - :nocontentsentry: - - -.. function:: unot(b: int64) -> bool - :noindex: - :nocontentsentry: - - -.. function:: unot(b: uint64) -> bool - :noindex: - :nocontentsentry: - - -.. function:: unot(a: Array[Any]) -> bool - :noindex: - :nocontentsentry: - - -.. rubric:: Footnotes -.. [1] Note: function gradients not implemented for backpropagation. diff --git a/docs/_build/html/_sources/modules/interoperability.rst.txt b/docs/_build/html/_sources/modules/interoperability.rst.txt deleted file mode 100644 index 5e4472ccb..000000000 --- a/docs/_build/html/_sources/modules/interoperability.rst.txt +++ /dev/null @@ -1,146 +0,0 @@ -Interoperability -================ - -Warp can interop with other Python-based frameworks such as NumPy through standard interface protocols. - -NumPy ------ - -Warp arrays may be converted to a NumPy array through the ``warp.array.numpy()`` method. When the Warp array lives on -the ``cpu`` device this will return a zero-copy view onto the underlying Warp allocation. If the array lives on a -``cuda`` device then it will first be copied back to a temporary buffer and copied to NumPy. - -Warp CPU arrays also implement the ``__array_interface__`` protocol and so can be used to construct NumPy arrays -directly:: - - w = wp.array([1.0, 2.0, 3.0], dtype=float, device="cpu") - a = np.array(w) - print(a) - > [1. 2. 3.] - -.. _pytorch-interop: - -PyTorch -------- - -Warp provides helper functions to convert arrays to/from PyTorch. Please see the ``warp.torch`` module for more details. Example usage is shown below:: - - import warp.torch - - w = wp.array([1.0, 2.0, 3.0], dtype=float, device="cpu") - - # convert to Torch tensor - t = warp.to_torch(w) - - # convert from Torch tensor - w = warp.from_torch(t) - -These helper functions allow the conversion of Warp arrays to/from PyTorch tensors without copying the underlying data. -At the same time, if available, gradient arrays and tensors are converted to/from PyTorch autograd tensors, allowing the use of Warp arrays -in PyTorch autograd computations. - -Example: Optimization using ``warp.from_torch()`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -An example usage of minimizing a loss function over an array of 2D points written in Warp via PyTorch's Adam optimizer using ``warp.from_torch`` is as follows:: - - import warp as wp - import torch - - wp.init() - - @wp.kernel() - def loss(xs: wp.array(dtype=float, ndim=2), l: wp.array(dtype=float)): - tid = wp.tid() - wp.atomic_add(l, 0, xs[tid, 0] ** 2.0 + xs[tid, 1] ** 2.0) - - # indicate requires_grad so that Warp can accumulate gradients in the grad buffers - xs = torch.randn(100, 2, requires_grad=True) - l = torch.zeros(1, requires_grad=True) - opt = torch.optim.Adam([xs], lr=0.1) - - wp_xs = wp.from_torch(xs) - wp_l = wp.from_torch(l) - - tape = wp.Tape() - with tape: - # record the loss function kernel launch on the tape - wp.launch(loss, dim=len(xs), inputs=[wp_xs], outputs=[wp_l], device=wp_xs.device) - - for i in range(500): - tape.zero() - tape.backward(loss=wp_l) # compute gradients - # now xs.grad will be populated with the gradients computed by Warp - opt.step() # update xs (and thereby wp_xs) - - # these lines are only needed for evaluating the loss - # (the optimization just needs the gradient, not the loss value) - wp_l.zero_() - wp.launch(loss, dim=len(xs), inputs=[wp_xs], outputs=[wp_l], device=wp_xs.device) - print(f"{i}\tloss: {l.item()}") - -Example: Optimization using ``warp.to_torch`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Less code is needed when we declare the optimization variables directly in Warp and use ``warp.to_torch`` to convert them to PyTorch tensors. -Here, we revisit the same example from above where now only a single conversion to a torch tensor is needed to supply Adam with the optimization variables:: - - import warp as wp - import numpy as np - import torch - - wp.init() - - @wp.kernel() - def loss(xs: wp.array(dtype=float, ndim=2), l: wp.array(dtype=float)): - tid = wp.tid() - wp.atomic_add(l, 0, xs[tid, 0] ** 2.0 + xs[tid, 1] ** 2.0) - - # initialize the optimization variables in Warp - xs = wp.array(np.random.randn(100, 2), dtype=wp.float32, requires_grad=True) - l = wp.zeros(1, dtype=wp.float32, requires_grad=True) - # just a single wp.to_torch call is needed, Adam optimizes using the Warp array gradients - opt = torch.optim.Adam([wp.to_torch(xs)], lr=0.1) - - tape = wp.Tape() - with tape: - wp.launch(loss, dim=len(xs), inputs=[xs], outputs=[l], device=xs.device) - - for i in range(500): - tape.zero() - tape.backward(loss=l) - opt.step() - - l.zero_() - wp.launch(loss, dim=len(xs), inputs=[xs], outputs=[l], device=xs.device) - print(f"{i}\tloss: {l.numpy()[0]}") - -.. automodule:: warp.torch - :members: - :undoc-members: - -CuPy/Numba ----------- - -Warp GPU arrays support the ``__cuda_array_interface__`` protocol for sharing data with other Python GPU frameworks. -Currently this is one-directional, so that Warp arrays can be used as input to any framework that also supports the -``__cuda_array_interface__`` protocol, but not the other way around. - -.. _jax-interop: - -JAX ---- - -Interoperability with JAX arrays is supported through the following methods. -Internally these use the DLPack protocol to exchange data in a zero-copy way with JAX. - -.. automodule:: warp.jax - :members: - :undoc-members: - -DLPack ------- - -.. automodule:: warp.dlpack - :members: - :undoc-members: diff --git a/docs/_build/html/_sources/modules/profiling.rst.txt b/docs/_build/html/_sources/modules/profiling.rst.txt deleted file mode 100644 index 315f72b23..000000000 --- a/docs/_build/html/_sources/modules/profiling.rst.txt +++ /dev/null @@ -1,44 +0,0 @@ -Profiling -========= - -``wp.ScopedTimer`` objects can be used to gain some basic insight into the performance of Warp applications:: - - with wp.ScopedTimer("grid build"): - self.grid.build(self.x, self.point_radius) - -This results in a printout at runtime to the standard output stream like:: - - grid build took 0.06 ms - -The ``wp.ScopedTimer`` object does not synchronize (e.g. by calling ``wp.synchronize()``) -upon exiting the ``with`` statement, so this can lead to misleading numbers if the body -of the ``with`` statement launches device kernels. - -When a ``wp.ScopedTimer`` object is passed ``use_nvtx=True`` as an argument, the timing functionality is replaced by calls -to ``nvtx.start_range()`` and ``nvtx.end_range()``:: - - with wp.ScopedTimer("grid build", use_nvtx=True, color="cyan"): - self.grid.build(self.x, self.point_radius) - -These range annotations can then be collected by a tool like `NVIDIA Nsight Systems `_ -for visualization on a timeline, e.g.:: - - nsys profile python warp_application.py - -This code snippet also demonstrates the use of the ``color`` argument to specify a color -for the range, which may be a number representing the ARGB value or a recognized string -(refer to `colors.py `_ for -additional color examples). -The `nvtx module `_ must be -installed in the Python environment for this capability to work. -An equivalent way to create an NVTX range without using ``wp.ScopedTimer`` is:: - - import nvtx - - with nvtx.annotate("grid build", color="cyan"): - self.grid.build(self.x, self.point_radius) - -This form may be more convenient if the user does not need to frequently switch -between timer and NVTX capabilities of ``wp.ScopedTimer``. - -.. autoclass:: warp.ScopedTimer diff --git a/docs/_build/html/_sources/modules/runtime.rst.txt b/docs/_build/html/_sources/modules/runtime.rst.txt deleted file mode 100644 index 766adf1c3..000000000 --- a/docs/_build/html/_sources/modules/runtime.rst.txt +++ /dev/null @@ -1,1491 +0,0 @@ -Runtime Reference -================= - -.. currentmodule:: warp - -This section describes the Warp Python runtime API, how to manage memory, launch kernels, and high-level functionality -for dealing with objects such as meshes and volumes. The APIs described in this section are intended to be used at -the *Python Scope* and run inside the CPython interpreter. For a comprehensive list of functions available at -the *Kernel Scope*, please see the :doc:`functions` section. - -Kernels -------- - -Kernels are launched with the :func:`wp.launch() ` function on a specific device (CPU/GPU):: - - wp.launch(simple_kernel, dim=1024, inputs=[a, b, c], device="cuda") - -Kernels may be launched with multi-dimensional grid bounds. In this case threads are not assigned a single index, -but a coordinate in an n-dimensional grid, e.g.:: - - wp.launch(complex_kernel, dim=(128, 128, 3), ...) - -Launches a 3D grid of threads with dimension 128 x 128 x 3. To retrieve the 3D index for each thread use the following syntax:: - - i,j,k = wp.tid() - -.. note:: - Currently kernels launched on CPU devices will be executed in serial. - Kernels launched on CUDA devices will be launched in parallel with a fixed block-size. - -.. note:: - Note that all the kernel inputs must live on the target device, or a runtime exception will be raised. - -.. autofunction:: launch - -Large Kernel Launches -##################### - -A limitation of Warp is that each dimension of the grid used to launch a kernel must be representable as a 32-bit -signed integer. Therefore, no single dimension of a grid should exceed :math:`2^{31}-1`. - -Warp also currently uses a fixed block size of 256 (CUDA) threads per block. -By default, Warp will try to process one element from the Warp grid in one CUDA thread. -This is not always possible for kernels launched with multi-dimensional grid bounds, as there are -`hardware limitations `_ -on CUDA block dimensions. - -Warp will automatically resort to using -`grid-stride loops `_ when -it is not possible for a CUDA thread to process only one element from the Warp grid -When this happens, some CUDA threads may process more than one element from the Warp grid. -Users can also set the ``max_blocks`` parameter to fine-tune the grid-striding behavior of kernels, even for kernels that are otherwise -able to process one Warp-grid element per CUDA thread. - -Runtime Kernel Specialization -############################# - -It is often desirable to specialize kernels for different types, constants, or functions. -We can achieve this through the use of runtime kernel specialization using Python closures. - -For example, we might require a variety of kernels that execute particular functions for each item in an array. -We might also want this function call to be valid for a variety of data types. Making use of closure and generics, we can generate -these kernels using a single kernel definition: :: - - def make_kernel(func, dtype): - def closure_kernel_fn(data: wp.array(dtype=dtype), out: wp.array(dtype=dtype)): - tid = wp.tid() - out[tid] = func(data[tid]) - - return wp.Kernel(closure_kernel_fn) - -In practice, we might use our kernel generator, ``make_kernel()`` as follows: :: - - @wp.func - def sqr(x: Any) -> Any: - return x * x - - @wp.func - def cube(x: Any) -> Any: - return sqr(x) * x - - sqr_float = make_kernel(sqr, wp.float32) - cube_double = make_kernel(cube, wp.float64) - - arr = [1.0, 2.0, 3.0] - N = len(arr) - - data_float = wp.array(arr, dtype=wp.float32, device=device) - data_double = wp.array(arr, dtype=wp.float64, device=device) - - out_float = wp.zeros(N, dtype=wp.float32, device=device) - out_double = wp.zeros(N, dtype=wp.float64, device=device) - - wp.launch(sqr_float, dim=N, inputs=[data_float], outputs=[out_float], device=device) - wp.launch(cube_double, dim=N, inputs=[data_double], outputs=[out_double], device=device) - -We can specialize kernel definitions over warp constants similarly. The following generates kernels that add a specified constant -to a generic-typed array value: :: - - def make_add_kernel(key, constant): - def closure_kernel_fn(data: wp.array(dtype=Any), out: wp.array(dtype=Any)): - tid = wp.tid() - out[tid] = data[tid] + constant - - return wp.Kernel(closure_kernel_fn, key=key) - - add_ones_int = make_add_kernel("add_one", wp.constant(1)) - add_ones_vec3 = make_add_kernel("add_ones_vec3", wp.constant(wp.vec3(1.0, 1.0, 1.0))) - - a = wp.zeros(2, dtype=int) - b = wp.zeros(2, dtype=wp.vec3) - - a_out = wp.zeros_like(a) - b_out = wp.zeros_like(b) - - wp.launch(add_ones_int, dim=a.size, inputs=[a], outputs=[a_out], device=device) - wp.launch(add_ones_vec3, dim=b.size, inputs=[b], outputs=[b_out], device=device) - - -Arrays ------- - -Arrays are the fundamental memory abstraction in Warp; they are created through the following global constructors: :: - - wp.empty(shape=1024, dtype=wp.vec3, device="cpu") - wp.zeros(shape=1024, dtype=float, device="cuda") - wp.full(shape=1024, value=10, dtype=int, device="cuda") - - -Arrays can also be constructed directly from ``numpy`` ndarrays as follows: :: - - r = np.random.rand(1024) - - # copy to Warp owned array - a = wp.array(r, dtype=float, device="cpu") - - # return a Warp array wrapper around the NumPy data (zero-copy) - a = wp.array(r, dtype=float, copy=False, device="cpu") - - # return a Warp copy of the array data on the GPU - a = wp.array(r, dtype=float, device="cuda") - -Note that for multi-dimensional data the ``dtype`` parameter must be specified explicitly, e.g.: :: - - r = np.random.rand((1024, 3)) - - # initialize as an array of vec3 objects - a = wp.array(r, dtype=wp.vec3, device="cuda") - -If the shapes are incompatible, an error will be raised. - - -Arrays can be moved between devices using the ``array.to()`` method: :: - - host_array = wp.array(a, dtype=float, device="cpu") - - # allocate and copy to GPU - device_array = host_array.to("cuda") - -Additionally, arrays can be copied directly between memory spaces: :: - - src_array = wp.array(a, dtype=float, device="cpu") - dest_array = wp.empty_like(host_array) - - # copy from source CPU buffer to GPU - wp.copy(dest_array, src_array) - -.. autoclass:: array - :members: - :undoc-members: - -Multi-dimensional Arrays -######################## - -Multi-dimensional arrays can be constructed by passing a tuple of sizes for each dimension, e.g.: the following constructs a 2d array of size 1024x16:: - - wp.zeros(shape=(1024, 16), dtype=float, device="cuda") - -When passing multi-dimensional arrays to kernels users must specify the expected array dimension inside the kernel signature, -e.g. to pass a 2d array to a kernel the number of dims is specified using the ``ndim=2`` parameter:: - - @wp.kernel - def test(input: wp.array(dtype=float, ndim=2)): - -Type-hint helpers are provided for common array sizes, e.g.: ``array2d()``, ``array3d()``, which are equivalent to calling ``array(..., ndim=2)```, etc. To index a multi-dimensional array use a the following kernel syntax:: - - # returns a float from the 2d array - value = input[i,j] - -To create an array slice use the following syntax, where the number of indices is less than the array dimensions:: - - # returns an 1d array slice representing a row of the 2d array - row = input[i] - -Slice operators can be concatenated, e.g.: ``s = array[i][j][k]``. Slices can be passed to ``wp.func`` user functions provided -the function also declares the expected array dimension. Currently only single-index slicing is supported. - -.. note:: - Currently Warp limits arrays to 4 dimensions maximum. This is in addition to the contained datatype, which may be 1-2 dimensional for vector and matrix types such as ``vec3``, and ``mat33``. - - -The following construction methods are provided for allocating zero-initialized and empty (non-initialized) arrays: - -.. autofunction:: zeros -.. autofunction:: zeros_like -.. autofunction:: full -.. autofunction:: full_like -.. autofunction:: empty -.. autofunction:: empty_like -.. autofunction:: copy -.. autofunction:: clone - -Matrix Multiplication -##################### - -Warp 2D array multiplication is built on NVIDIA's CUTLASS library, which enables fast matrix multiplication of large arrays on the GPU. - -If no GPU is detected, matrix multiplication falls back to Numpy's implementation on the CPU. - -Matrix multiplication is fully differentiable, and can be recorded on the tape like so:: - - tape = wp.Tape() - with tape: - wp.matmul(A, B, C, D, device=device) - wp.launch(loss_kernel, dim=(m, n), inputs=[D, loss], device=device) - - tape.backward(loss=loss) - A_grad = A.grad.numpy() - -Using the ``@`` operator (``D = A @ B``) will default to the same CUTLASS algorithm used in ``wp.matmul``. - -.. autofunction:: matmul - -Data Types ----------- - -Scalar Types -############ - -The following scalar storage types are supported for array structures: - -+---------+------------------------+ -| bool | boolean | -+---------+------------------------+ -| int8 | signed byte | -+---------+------------------------+ -| uint8 | unsigned byte | -+---------+------------------------+ -| int16 | signed short | -+---------+------------------------+ -| uint16 | unsigned short | -+---------+------------------------+ -| int32 | signed integer | -+---------+------------------------+ -| uint32 | unsigned integer | -+---------+------------------------+ -| int64 | signed long integer | -+---------+------------------------+ -| uint64 | unsigned long integer | -+---------+------------------------+ -| float32 | single precision float | -+---------+------------------------+ -| float64 | double precision float | -+---------+------------------------+ - -Warp supports ``float`` and ``int`` as aliases for ``wp.float32`` and ``wp.int32`` respectively. - -.. _vec: - -Vectors -####### - -Warp provides built-in math and geometry types for common simulation and graphics problems. -A full reference for operators and functions for these types is available in the :doc:`/modules/functions`. - -Warp supports vectors of numbers with an arbitrary length/numeric type. The built in concrete types are as follows: - -+-----------------------+------------------------------------------------+ -| vec2 vec3 vec4 | 2d, 3d, 4d vector of default precision floats | -+-----------------------+------------------------------------------------+ -| vec2f vec3f vec4f | 2d, 3d, 4d vector of single precision floats | -+-----------------------+------------------------------------------------+ -| vec2d vec3d vec4d | 2d, 3d, 4d vector of double precision floats | -+-----------------------+------------------------------------------------+ -| vec2h vec3h vec4h | 2d, 3d, 4d vector of half precision floats | -+-----------------------+------------------------------------------------+ -| vec2ub vec3ub vec4ub | 2d, 3d, 4d vector of half precision floats | -+-----------------------+------------------------------------------------+ -| spatial_vector | 6d vector of single precision floats | -+-----------------------+------------------------------------------------+ -| spatial_vectorf | 6d vector of single precision floats | -+-----------------------+------------------------------------------------+ -| spatial_vectord | 6d vector of double precision floats | -+-----------------------+------------------------------------------------+ -| spatial_vectorh | 6d vector of half precision floats | -+-----------------------+------------------------------------------------+ - -Vectors support most standard linear algebra operations, e.g.: :: - - @wp.kernel - def compute( ... ): - - # basis vectors - a = wp.vec3(1.0, 0.0, 0.0) - b = wp.vec3(0.0, 1.0, 0.0) - - # take the cross product - c = wp.cross(a, b) - - # compute - r = wp.dot(c, c) - - ... - - -It's possible to declare additional vector types with different lengths and data types. This is done in outside of kernels in *Python scope* using ``warp.types.vector()``, for example: :: - - # declare a new vector type for holding 5 double precision floats: - vec5d = wp.types.vector(length=5, dtype=wp.float64) - -Once declared, the new type can be used when allocating arrays or inside kernels: :: - - # create an array of vec5d - arr = wp.zeros(10, dtype=vec5d) - - # use inside a kernel - @wp.kernel - def compute( ... ): - - # zero initialize a custom named vector type - v = vec5d() - ... - - # component-wise initialize a named vector type - v = vec5d(wp.float64(1.0), - wp.float64(2.0), - wp.float64(3.0), - wp.float64(4.0), - wp.float64(5.0)) - ... - -In addition, it's possible to directly create *anonymously* typed instances of these vectors without declaring their type in advance. In this case the type will be inferred by the constructor arguments. For example: :: - - @wp.kernel - def compute( ... ): - - # zero initialize vector of 5 doubles: - v = wp.vector(dtype=wp.float64, length=5) - - # scalar initialize a vector of 5 doubles to the same value: - v = wp.vector(wp.float64(1.0), length=5) - - # component-wise initialize a vector of 5 doubles - v = wp.vector(wp.float64(1.0), - wp.float64(2.0), - wp.float64(3.0), - wp.float64(4.0), - wp.float64(5.0)) - - -These can be used with all the standard vector arithmetic operators, e.g.: ``+``, ``-``, scalar multiplication, and can also be transformed using matrices with compatible dimensions, potentially returning vectors with a different length. - -.. _mat: - -Matrices -######## - -Matrices with arbitrary shapes/numeric types are also supported. The built in concrete matrix types are as follows: - -+--------------------------+-----------------------------------------------------+ -| mat22 mat33 mat44 | 2,3 and 4d square matrix of default precision | -+--------------------------+-----------------------------------------------------+ -| mat22f mat33f mat44f | 2,3 and 4d square matrix of single precision floats | -+--------------------------+-----------------------------------------------------+ -| mat22d mat33d mat44d | 2,3 and 4d square matrix of double precision floats | -+--------------------------+-----------------------------------------------------+ -| mat22h mat33h mat44h | 2,3 and 4d square matrix of half precision floats | -+--------------------------+-----------------------------------------------------+ -| spatial_matrix | 6x6 matrix of single precision floats | -+--------------------------+-----------------------------------------------------+ -| spatial_matrixf | 6x6 matrix of single precision floats | -+--------------------------+-----------------------------------------------------+ -| spatial_matrixd | 6x6 matrix of double precision floats | -+--------------------------+-----------------------------------------------------+ -| spatial_matrixh | 6x6 matrix of half precision floats | -+--------------------------+-----------------------------------------------------+ - -Matrices are stored in row-major format and support most standard linear algebra operations: :: - - @wp.kernel - def compute( ... ): - - # initialize matrix - m = wp.mat22(1.0, 2.0, - 3.0, 4.0) - - # compute inverse - minv = wp.inverse(m) - - # transform vector - v = minv * wp.vec2(0.5, 0.3) - - ... - - -In a similar manner to vectors, it's possible to declare new matrix types with arbitrary shapes and data types using ``wp.types.matrix()``, for example: :: - - # declare a new 3x2 half precision float matrix type: - mat32h = wp.types.matrix(shape=(3,2), dtype=wp.float64) - - # create an array of this type - a = wp.zeros(10, dtype=mat32h) - -These can be used inside a kernel: :: - - @wp.kernel - def compute( ... ): - ... - - # initialize a mat32h matrix - m = mat32h(wp.float16(1.0), wp.float16(2.0), - wp.float16(3.0), wp.float16(4.0), - wp.float16(5.0), wp.float16(6.0)) - - # declare a 2 component half precision vector - v2 = wp.vec2h(wp.float16(1.0), wp.float16(1.0)) - - # multiply by the matrix, returning a 3 component vector: - v3 = m * v2 - ... - -It's also possible to directly create anonymously typed instances inside kernels where the type is inferred from constructor arguments as follows: :: - - @wp.kernel - def compute( ... ): - ... - - # create a 3x2 half precision matrix from components (row major ordering): - m = wp.matrix( - wp.float16(1.0), wp.float16(2.0), - wp.float16(1.0), wp.float16(2.0), - wp.float16(1.0), wp.float16(2.0), - shape=(3,2)) - - # zero initialize a 3x2 half precision matrix: - m = wp.matrix(wp.float16(0.0),shape=(3,2)) - - # create a 5x5 double precision identity matrix: - m = wp.identity(n=5, dtype=wp.float64) - -As with vectors, you can do standard matrix arithmetic with these variables, along with multiplying matrices with compatible shapes and potentially returning a matrix with a new shape. - -.. _quat: - -Quaternions -########### - -Warp supports quaternions with the layout ``i, j, k, w`` where ``w`` is the real part. Here are the built in concrete quaternion types: - -+-----------------+-----------------------------------------------+ -| quat | Default precision floating point quaternion | -+-----------------+-----------------------------------------------+ -| quatf | Single precision floating point quaternion | -+-----------------+-----------------------------------------------+ -| quatd | Double precision floating point quaternion | -+-----------------+-----------------------------------------------+ -| quath | Half precision floating point quaternion | -+-----------------+-----------------------------------------------+ - -Quaternions can be used to transform vectors as follows: :: - - @wp.kernel - def compute( ... ): - ... - - # construct a 30 degree rotation around the x-axis - q = wp.quat_from_axis_angle(wp.vec3(1.0, 0.0, 0.0), wp.degrees(30.0)) - - # rotate an axis by this quaternion - v = wp.quat_rotate(q, wp.vec3(0.0, 1.0, 0.0)) - - -As with vectors and matrices, you can declare quaternion types with an arbitrary numeric type like so: :: - - quatd = wp.types.quaternion(dtype=wp.float64) - -You can also create identity quaternion and anonymously typed instances inside a kernel like so: :: - - @wp.kernel - def compute( ... ): - ... - - # create a double precision identity quaternion: - qd = wp.quat_identity(dtype=wp.float64) - - # precision defaults to wp.float32 so this creates a single precision identity quaternion: - qf = wp.quat_identity() - - # create a half precision quaternion from components, or a vector/scalar: - qh = wp.quaternion(wp.float16(0.0), - wp.float16(0.0), - wp.float16(0.0), - wp.float16(1.0)) - - - qh = wp.quaternion( - wp.vector(wp.float16(0.0),wp.float16(0.0),wp.float16(0.0)), - wp.float16(1.0)) - -.. _transform: - -Transforms -########## - -Transforms are 7d vectors of floats representing a spatial rigid body transformation in format (p, q) where p is a 3d vector, and q is a quaternion. - -+-----------------+--------------------------------------------+ -| transform | Default precision floating point transform | -+-----------------+--------------------------------------------+ -| transformf | Single precision floating point transform | -+-----------------+--------------------------------------------+ -| transformd | Double precision floating point transform | -+-----------------+--------------------------------------------+ -| transformh | Half precision floating point transform | -+-----------------+--------------------------------------------+ - -Transforms can be constructed inside kernels from translation and rotation parts: :: - - @wp.kernel - def compute( ... ): - ... - - # create a transform from a vector/quaternion: - t = wp.transform( - wp.vec3(1.0, 2.0, 3.0), - wp.quat_from_axis_angle(0.0, 1.0, 0.0, wp.degrees(30.0))) - - # transform a point - p = wp.transform_point(t, wp.vec3(10.0, 0.5, 1.0)) - - # transform a vector (ignore translation) - p = wp.transform_vector(t, wp.vec3(10.0, 0.5, 1.0)) - - -As with vectors and matrices, you can declare transform types with an arbitrary numeric type using ``wp.types.transformation()``, for example: :: - - transformd = wp.types.transformation(dtype=wp.float64) - -You can also create identity transforms and anonymously typed instances inside a kernel like so: :: - - @wp.kernel - def compute( ... ): - - # create double precision identity transform: - qd = wp.transform_identity(dtype=wp.float64) - -Structs -####### - -Users can define custom structure types using the ``@wp.struct`` decorator as follows:: - - @wp.struct - class MyStruct: - - param1: int - param2: float - param3: wp.array(dtype=wp.vec3) - -Struct attributes must be annotated with their respective type. They can be constructed in Python scope and then passed to kernels as arguments:: - - @wp.kernel - def compute(args: MyStruct): - - tid = wp.tid() - - print(args.param1) - print(args.param2) - print(args.param3[tid]) - - # construct an instance of the struct in Python - s = MyStruct() - s.param1 = 10 - s.param2 = 2.5 - s.param3 = wp.zeros(shape=10, dtype=wp.vec3) - - # pass to our compute kernel - wp.launch(compute, dim=10, inputs=[s]) - -An array of structs can be zero-initialized as follows:: - - a = wp.zeros(shape=10, dtype=MyStruct) - -An array of structs can also be initialized from a list of struct objects:: - - a = wp.array([MyStruct(), MyStruct(), MyStruct()], dtype=MyStruct) - -Example: Using a custom struct in gradient computation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: python - - import numpy as np - - import warp as wp - - wp.init() - - - @wp.struct - class TestStruct: - x: wp.vec3 - a: wp.array(dtype=wp.vec3) - b: wp.array(dtype=wp.vec3) - - - @wp.kernel - def test_kernel(s: TestStruct): - tid = wp.tid() - - s.b[tid] = s.a[tid] + s.x - - - @wp.kernel - def loss_kernel(s: TestStruct, loss: wp.array(dtype=float)): - tid = wp.tid() - - v = s.b[tid] - wp.atomic_add(loss, 0, float(tid + 1) * (v[0] + 2.0 * v[1] + 3.0 * v[2])) - - - # create struct - ts = TestStruct() - - # set members - ts.x = wp.vec3(1.0, 2.0, 3.0) - ts.a = wp.array(np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]), dtype=wp.vec3, requires_grad=True) - ts.b = wp.zeros(2, dtype=wp.vec3, requires_grad=True) - - loss = wp.zeros(1, dtype=float, requires_grad=True) - - tape = wp.Tape() - with tape: - wp.launch(test_kernel, dim=2, inputs=[ts]) - wp.launch(loss_kernel, dim=2, inputs=[ts, loss]) - - tape.backward(loss) - - print(loss) - print(tape.gradients[ts].a) - - -Type Conversions -################ - -Warp is particularly strict regarding type conversions and does not perform *any* implicit conversion between numeric types. -The user is responsible for ensuring types for most arithmetic operators match, e.g.: ``x = float(0.0) + int(4)`` will result in an error. -This can be surprising for users that are accustomed to C-style conversions but avoids a class of common bugs that result from implicit conversions. - -.. note:: - Warp does not currently perform implicit type conversions between numeric types. - Users should explicitly cast variables to compatible types using constructors like - ``int()``, ``float()``, ``wp.float16()``, ``wp.uint8()``, etc. - -Constants ---------- - -In general, Warp kernels cannot access variables in the global Python interpreter state. One exception to this is for compile-time constants, which may be declared globally (or as class attributes) and folded into the kernel definition. - -Constants are defined using the ``wp.constant()`` function. An example is shown below:: - - TYPE_SPHERE = wp.constant(0) - TYPE_CUBE = wp.constant(1) - TYPE_CAPSULE = wp.constant(2) - - @wp.kernel - def collide(geometry: wp.array(dtype=int)): - - t = geometry[wp.tid()] - - if (t == TYPE_SPHERE): - print("sphere") - if (t == TYPE_CUBE): - print("cube") - if (t == TYPE_CAPSULE): - print("capsule") - - -.. autoclass:: constant - - -Operators ----------- - -Boolean Operators -################# - -+--------------+--------------------------------------+ -| a and b | True if a and b are True | -+--------------+--------------------------------------+ -| a or b | True if a or b is True | -+--------------+--------------------------------------+ -| not a | True if a is False, otherwise False | -+--------------+--------------------------------------+ - -.. note:: - Expressions such as ``if (a and b):`` currently do not perform short-circuit evaluation. - In this case ``b`` will also be evaluated even when ``a`` is ``False``. - Users should take care to ensure that secondary conditions are safe to evaluate (e.g.: do not index out of bounds) in all cases. - - -Comparison Operators -#################### - -+----------+---------------------------------------+ -| a > b | True if a strictly greater than b | -+----------+---------------------------------------+ -| a < b | True if a strictly less than b | -+----------+---------------------------------------+ -| a >= b | True if a greater than or equal to b | -+----------+---------------------------------------+ -| a <= b | True if a less than or equal to b | -+----------+---------------------------------------+ -| a == b | True if a equals b | -+----------+---------------------------------------+ -| a != b | True if a not equal to b | -+----------+---------------------------------------+ - -Arithmetic Operators -#################### - -+-----------+--------------------------+ -| a + b | Addition | -+-----------+--------------------------+ -| a - b | Subtraction | -+-----------+--------------------------+ -| a * b | Multiplication | -+-----------+--------------------------+ -| a / b | Floating point division | -+-----------+--------------------------+ -| a // b | Floored division | -+-----------+--------------------------+ -| a ** b | Exponentiation | -+-----------+--------------------------+ -| a % b | Modulus | -+-----------+--------------------------+ - -.. note:: - Since implicit conversions are not performed arguments types to operators should match. - Users should use type constructors, e.g.: ``float()``, ``int()``, ``wp.int64()``, etc. to cast variables - to the correct type. Also note that the multiplication expression ``a * b`` is used to represent scalar - multiplication and matrix multiplication. The ``@`` operator is not currently supported. - -Graphs ------------ - -Launching kernels from Python introduces significant additional overhead compared to C++ or native programs. -To address this, Warp exposes the concept of `CUDA graphs `_ -to allow recording large batches of kernels and replaying them with very little CPU overhead. - -To record a series of kernel launches use the :func:`wp.capture_begin() ` and -:func:`wp.capture_end() ` API as follows: - -.. code:: python - - # begin capture - wp.capture_begin() - - try: - # record launches - for i in range(100): - wp.launch(kernel=compute1, inputs=[a, b], device="cuda") - finally: - # end capture and return a graph object - graph = wp.capture_end() - -We strongly recommend the use of the the try-finally pattern when capturing graphs because -:func:`wp.capture_begin ` disables Python garbage collection so that Warp objects are not -garbage-collected during graph capture (CUDA does not allow memory to be deallocated during graph capture). -:func:`wp.capture_end ` reenables garbage collection. - -Once a graph has been constructed it can be executed: :: - - wp.capture_launch(graph) - -Note that only launch calls are recorded in the graph, any Python executed outside of the kernel code will not be recorded. -Typically it is only beneficial to use CUDA graphs when the graph will be reused or launched multiple times. - -.. autofunction:: capture_begin -.. autofunction:: capture_end -.. autofunction:: capture_launch - -Bounding Value Hierarchies (BVH) --------------------------------- - -The :class:`wp.Bvh ` class can be used to create a BVH for a group of bounding volumes. This object can then be traversed -to determine which parts are intersected by a ray using :func:`bvh_query_ray` and which parts are fully contained -within a certain bounding volume using :func:`bvh_query_aabb`. - -The following snippet demonstrates how to create a :class:`wp.Bvh ` object from 100 random bounding volumes: - -.. code:: python - - rng = np.random.default_rng(123) - - num_bounds = 100 - lowers = rng.random(size=(num_bounds, 3)) * 5.0 - uppers = lowers + rng.random(size=(num_bounds, 3)) * 5.0 - - device_lowers = wp.array(lowers, dtype=wp.vec3, device="cuda:0") - device_uppers = wp.array(uppers, dtype=wp.vec3, device="cuda:0") - - bvh = wp.Bvh(device_lowers, device_uppers) - -.. autoclass:: Bvh - :members: - -Example: BVH Ray Traversal -########################## - -An example of performing a ray traversal on the data structure is as follows: - -.. code:: python - - @wp.kernel - def bvh_query_ray( - bvh_id: wp.uint64, - start: wp.vec3, - dir: wp.vec3, - bounds_intersected: wp.array(dtype=wp.bool), - ): - query = wp.bvh_query_ray(bvh_id, start, dir) - bounds_nr = wp.int32(0) - - while wp.bvh_query_next(query, bounds_nr): - # The ray intersects the volume with index bounds_nr - bounds_intersected[bounds_nr] = True - - - bounds_intersected = wp.zeros(shape=(num_bounds), dtype=wp.bool, device="cuda:0") - query_start = wp.vec3(0.0, 0.0, 0.0) - query_dir = wp.normalize(wp.vec3(1.0, 1.0, 1.0)) - - wp.launch( - kernel=bvh_query_ray, - dim=1, - inputs=[bvh.id, query_start, query_dir, bounds_intersected], - device="cuda:0", - ) - -The Warp kernel ``bvh_query_ray`` is launched with a single thread, provided the unique :class:`uint64` -identifier of the :class:`wp.Bvh ` object, parameters describing the ray, and an array to store the results. -In ``bvh_query_ray``, :func:`wp.bvh_query_ray() ` is called once to obtain an object that is stored in the -variable ``query``. An integer is also allocated as ``bounds_nr`` to store the volume index of the traversal. -A while statement is used for the actual traversal using :func:`wp.bvh_query_next() `, -which returns ``True`` as long as there are intersecting bounds. - -Example: BVH Volume Traversal -############################# - -Similar to the ray-traversal example, we can perform volume traversal to find the volumes that are fully contained -within a specified bounding box. - -.. code:: python - - @wp.kernel - def bvh_query_aabb( - bvh_id: wp.uint64, - lower: wp.vec3, - upper: wp.vec3, - bounds_intersected: wp.array(dtype=wp.bool), - ): - query = wp.bvh_query_aabb(bvh_id, lower, upper) - bounds_nr = wp.int32(0) - - while wp.bvh_query_next(query, bounds_nr): - # The volume with index bounds_nr is fully contained - # in the (lower,upper) bounding box - bounds_intersected[bounds_nr] = True - - - bounds_intersected = wp.zeros(shape=(num_bounds), dtype=wp.bool, device="cuda:0") - query_lower = wp.vec3(4.0, 4.0, 4.0) - query_upper = wp.vec3(6.0, 6.0, 6.0) - - wp.launch( - kernel=bvh_query_aabb, - dim=1, - inputs=[bvh.id, query_lower, query_upper, bounds_intersected], - device="cuda:0", - ) - -The kernel is nearly identical to the ray-traversal example, except we obtain ``query`` using -:func:`wp.bvh_query_aabb() `. - -Meshes ------- - -Warp provides a ``wp.Mesh`` class to manage triangle mesh data. To create a mesh users provide a points, indices and optionally a velocity array:: - - mesh = wp.Mesh(points, indices, velocities) - -.. note:: - Mesh objects maintain references to their input geometry buffers. All buffers should live on the same device. - -Meshes can be passed to kernels using their ``id`` attribute which uniquely identifies the mesh by a unique ``uint64`` value. -Once inside a kernel you can perform geometric queries against the mesh such as ray-casts or closest point lookups:: - - @wp.kernel - def raycast(mesh: wp.uint64, - ray_origin: wp.array(dtype=wp.vec3), - ray_dir: wp.array(dtype=wp.vec3), - ray_hit: wp.array(dtype=wp.vec3)): - - tid = wp.tid() - - t = float(0.0) # hit distance along ray - u = float(0.0) # hit face barycentric u - v = float(0.0) # hit face barycentric v - sign = float(0.0) # hit face sign - n = wp.vec3() # hit face normal - f = int(0) # hit face index - - color = wp.vec3() - - # ray cast against the mesh - if wp.mesh_query_ray(mesh, ray_origin[tid], ray_dir[tid], 1.e+6, t, u, v, sign, n, f): - - # if we got a hit then set color to the face normal - color = n*0.5 + wp.vec3(0.5, 0.5, 0.5) - - ray_hit[tid] = color - - -Users may update mesh vertex positions at runtime simply by modifying the points buffer. -After modifying point locations users should call ``Mesh.refit()`` to rebuild the bounding volume hierarchy (BVH) structure and ensure that queries work correctly. - -.. note:: - Updating Mesh topology (indices) at runtime is not currently supported. Users should instead recreate a new Mesh object. - -.. autoclass:: Mesh - :members: - -Hash Grids ----------- - -Many particle-based simulation methods such as the Discrete Element Method (DEM), or Smoothed Particle Hydrodynamics (SPH), involve iterating over spatial neighbors to compute force interactions. Hash grids are a well-established data structure to accelerate these nearest neighbor queries, and particularly well-suited to the GPU. - -To support spatial neighbor queries Warp provides a ``HashGrid`` object that may be created as follows:: - - grid = wp.HashGrid(dim_x=128, dim_y=128, dim_z=128, device="cuda") - - grid.build(points=p, radius=r) - -``p`` is an array of ``wp.vec3`` point positions, and ``r`` is the radius to use when building the grid. -Neighbors can then be iterated over inside the kernel code using :func:`wp.hash_grid_query() ` -and :func:`wp.hash_grid_query_next() ` as follows: - -.. code:: python - - @wp.kernel - def sum(grid : wp.uint64, - points: wp.array(dtype=wp.vec3), - output: wp.array(dtype=wp.vec3), - radius: float): - - tid = wp.tid() - - # query point - p = points[tid] - - # create grid query around point - query = wp.hash_grid_query(grid, p, radius) - index = int(0) - - sum = wp.vec3() - - while(wp.hash_grid_query_next(query, index)): - - neighbor = points[index] - - # compute distance to neighbor point - dist = wp.length(p-neighbor) - if (dist <= radius): - sum += neighbor - - output[tid] = sum - -.. note:: - The ``HashGrid`` query will give back all points in *cells* that fall inside the query radius. - When there are hash conflicts it means that some points outside of query radius will be returned, and users should - check the distance themselves inside their kernels. The reason the query doesn't do the check itself for each - returned point is because it's common for kernels to compute the distance themselves, so it would redundant to - check/compute the distance twice. - - -.. autoclass:: HashGrid - :members: - -Volumes -------- - -Sparse volumes are incredibly useful for representing grid data over large domains, such as signed distance fields -(SDFs) for complex objects, or velocities for large-scale fluid flow. Warp supports reading sparse volumetric grids -stored using the `NanoVDB `_ standard. Users can access voxels directly -or use built-in closest-point or trilinear interpolation to sample grid data from world or local space. - -Volume objects can be created directly from Warp arrays containing a NanoVDB grid, from the contents of a -standard ``.nvdb`` file using :func:`load_from_nvdb() `, -or from a dense 3D NumPy array using :func:`load_from_numpy() `. - -Volumes can also be created using :func:`allocate() ` or -:func:`allocate_by_tiles() `. The values for a Volume object can be modified in a Warp -kernel using :func:`wp.volume_store_f() `, :func:`wp.volume_store_v() `, and -:func:`wp.volume_store_i() `. - -.. note:: - Warp does not currently support modifying the topology of sparse volumes at runtime. - -Below we give an example of creating a Volume object from an existing NanoVDB file:: - - # open NanoVDB file on disk - file = open("mygrid.nvdb", "rb") - - # create Volume object - volume = wp.Volume.load_from_nvdb(file, device="cpu") - -.. note:: - Files written by the NanoVDB library, commonly marked by the ``.nvdb`` extension, can contain multiple grids with - various compression methods, but a :class:`Volume` object represents a single NanoVDB grid therefore only files with - a single grid are supported. NanoVDB's uncompressed and zip-compressed file formats are supported. - -To sample the volume inside a kernel we pass a reference to it by ID, and use the built-in sampling modes:: - - @wp.kernel - def sample_grid(volume: wp.uint64, - points: wp.array(dtype=wp.vec3), - samples: wp.array(dtype=float)): - - tid = wp.tid() - - # load sample point in world-space - p = points[tid] - - # transform position to the volume's local-space - q = wp.volume_world_to_index(volume, p) - - # sample volume with trilinear interpolation - f = wp.volume_sample_f(volume, q, wp.Volume.LINEAR) - - # write result - samples[tid] = f - -.. autoclass:: Volume - :members: - :undoc-members: - -.. seealso:: `Reference `__ for the volume functions available in kernels. - -Differentiability ------------------ - -By default, Warp generates a forward and backward (adjoint) version of each kernel definition. -Buffers that participate in the chain of computation should be created with ``requires_grad=True``, for example:: - - a = wp.zeros(1024, dtype=wp.vec3, device="cuda", requires_grad=True) - -The ``wp.Tape`` class can then be used to record kernel launches, and replay them to compute the gradient of a scalar loss function with respect to the kernel inputs:: - - tape = wp.Tape() - - # forward pass - with tape: - wp.launch(kernel=compute1, inputs=[a, b], device="cuda") - wp.launch(kernel=compute2, inputs=[c, d], device="cuda") - wp.launch(kernel=loss, inputs=[d, l], device="cuda") - - # reverse pass - tape.backward(l) - -After the backward pass has completed, the gradients with respect to the inputs are available via a mapping in the :class:`wp.Tape ` object:: - - # gradient of loss with respect to input a - print(tape.gradients[a]) - -Note that gradients are accumulated on the participating buffers, so if you wish to reuse the same buffers for multiple backward passes you should first zero the gradients:: - - tape.zero() - -.. note:: - - Warp uses a source-code transformation approach to auto-differentiation. - In this approach, the backwards pass must keep a record of intermediate values computed during the forward pass. - This imposes some restrictions on what kernels can do and still be differentiable: - - * Dynamic loops should not mutate any previously declared local variable. This means the loop must be side-effect free. - A simple way to ensure this is to move the loop body into a separate function. - Static loops that are unrolled at compile time do not have this restriction and can perform any computation. - - * Kernels should not overwrite any previously used array values except to perform simple linear add/subtract operations (e.g. via :func:`wp.atomic_add() `) - -.. autoclass:: Tape - :members: - -Jacobians -######### - -To compute the Jacobian matrix :math:`J\in\mathbb{R}^{m\times n}` of a multi-valued function :math:`f: \mathbb{R}^n \to \mathbb{R}^m`, we can evaluate an entire row of the Jacobian in parallel by finding the Jacobian-vector product :math:`J^\top \mathbf{e}`. The vector :math:`\mathbf{e}\in\mathbb{R}^m` selects the indices in the output buffer to differentiate with respect to. -In Warp, instead of passing a scalar loss buffer to the ``tape.backward()`` method, we pass a dictionary ``grads`` mapping from the function output array to the selection vector :math:`\mathbf{e}` having the same type:: - - # compute the Jacobian for a function of single output - jacobian = np.empty((output_dim, input_dim), dtype=np.float32) - tape = wp.Tape() - with tape: - output_buffer = launch_kernels_to_be_differentiated(input_buffer) - for output_index in range(output_dim): - # select which row of the Jacobian we want to compute - select_index = np.zeros(output_dim) - select_index[output_index] = 1.0 - e = wp.array(select_index, dtype=wp.float32) - # pass input gradients to the output buffer to apply selection - tape.backward(grads={output_buffer: e}) - q_grad_i = tape.gradients[input_buffer] - jacobian[output_index, :] = q_grad_i.numpy() - tape.zero() - -When we run simulations independently in parallel, the Jacobian corresponding to the entire system dynamics is a block-diagonal matrix. In this case, we can compute the Jacobian in parallel for all environments by choosing a selection vector that has the output indices active for all environment copies. For example, to get the first rows of the Jacobians of all environments, :math:`\mathbf{e}=[\begin{smallmatrix}1 & 0 & 0 & \dots & 1 & 0 & 0 & \dots\end{smallmatrix}]^\top`, to compute the second rows, :math:`\mathbf{e}=[\begin{smallmatrix}0 & 1 & 0 & \dots & 0 & 1 & 0 & \dots\end{smallmatrix}]^\top`, etc.:: - - # compute the Jacobian for a function over multiple environments in parallel - jacobians = np.empty((num_envs, output_dim, input_dim), dtype=np.float32) - tape = wp.Tape() - with tape: - output_buffer = launch_kernels_to_be_differentiated(input_buffer) - for output_index in range(output_dim): - # select which row of the Jacobian we want to compute - select_index = np.zeros(output_dim) - select_index[output_index] = 1.0 - # assemble selection vector for all environments (can be precomputed) - e = wp.array(np.tile(select_index, num_envs), dtype=wp.float32) - tape.backward(grads={output_buffer: e}) - q_grad_i = tape.gradients[input_buffer] - jacobians[:, output_index, :] = q_grad_i.numpy().reshape(num_envs, input_dim) - tape.zero() - - -Custom Gradient Functions -######################### - -Warp supports custom gradient function definitions for user-defined Warp functions. -This allows users to define code that should replace the automatically generated derivatives. - -To differentiate a function :math:`h(x) = f(g(x))` that has a nested call to function :math:`g(x)`, the chain rule is evaluated in the automatic differentiation of :math:`h(x)`: - -.. math:: - - h^\prime(x) = f^\prime({\color{green}{\underset{\textrm{replay}}{g(x)}}}) {\color{blue}{\underset{\textrm{grad}}{g^\prime(x)}}} - -This implies that a function to be compatible with the autodiff engine needs to provide an implementation of its forward version -:math:`\color{green}{g(x)}`, which we refer to as "replay" function (that matches the original function definition by default), -and its derivative :math:`\color{blue}{g^\prime(x)}`, referred to as "grad". - -Both the replay and the grad implementations can be customized by the user. They are defined as follows: - -.. list-table:: Customizing the replay and grad versions of function ``myfunc`` - :widths: 100 - :header-rows: 0 - - * - Forward Function - * - .. code-block:: python - - @wp.func - def myfunc(in1: InType1, ..., inN: InTypeN) -> OutType1, ..., OutTypeM: - return out1, ..., outM - - * - Custom Replay Function - * - .. code-block:: python - - @wp.func_replay(myfunc) - def replay_myfunc(in1: InType1, ..., inN: InTypeN) -> OutType1, ..., OutTypeM: - # Custom forward computations to be executed in the backward pass of a - # function calling `myfunc` go here - # Ensure the output variables match the original forward definition - return out1, ..., outM - - * - Custom Grad Function - * - .. code-block:: python - - @wp.func_grad(myfunc) - def adj_myfunc(in1: InType1, ..., inN: InTypeN, adj_out1: OutType1, ..., adj_outM: OutTypeM): - # Custom adjoint code goes here - # Update the partial derivatives for the inputs as follows: - wp.adjoint[in1] += ... - ... - wp.adjoint[inN] += ... - -.. note:: It is currently not possible to define custom replay or grad functions for functions that - have generic arguments, e.g. ``Any`` or ``wp.array(dtype=Any)``. Replay or grad functions that - themselves use generic arguments are also not yet supported. - -Example 1: Custom Grad Function -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In the following, we define a Warp function ``safe_sqrt`` that computes the square root of a number:: - - @wp.func - def safe_sqrt(x: float): - return wp.sqrt(x) - -To evaluate this function, we define a kernel that applies ``safe_sqrt`` to an array of input values:: - - @wp.kernel - def run_safe_sqrt(xs: wp.array(dtype=float), output: wp.array(dtype=float)): - i = wp.tid() - output[i] = safe_sqrt(xs[i]) - -Calling the kernel for an array of values ``[1.0, 2.0, 0.0]`` yields the expected outputs, the gradients are finite except for the zero input:: - - xs = wp.array([1.0, 2.0, 0.0], dtype=wp.float32, requires_grad=True) - ys = wp.zeros_like(xs) - tape = wp.Tape() - with tape: - wp.launch(run_safe_sqrt, dim=len(xs), inputs=[xs], outputs=[ys]) - tape.backward(grads={ys: wp.array(np.ones(len(xs)), dtype=wp.float32)}) - print("ys ", ys) - print("xs.grad", xs.grad) - - # ys [1. 1.4142135 0. ] - # xs.grad [0.5 0.35355338 inf] - -It is often desired to catch nonfinite gradients in the computation graph as they may cause the entire gradient computation to be nonfinite. -To do so, we can define a custom gradient function that replaces the adjoint function for ``safe_sqrt`` which is automatically generated by -decorating the custom gradient code via ``@wp.func_grad(safe_sqrt)``:: - - @wp.func_grad(safe_sqrt) - def adj_safe_sqrt(x: float, adj_ret: float): - if x > 0.0: - wp.adjoint[x] += 1.0 / (2.0 * wp.sqrt(x)) * adj_ret - -.. note:: The function signature of the custom grad code consists of the input arguments of the forward function plus the adjoint variables of the - forward function outputs. To access and modify the partial derivatives of the input arguments, we use the ``wp.adjoint`` dictionary. - The keys of this dictionary are the input arguments of the forward function, and the values are the partial derivatives of the forward function - output with respect to the input argument. - - -Example 2: Custom Replay Function -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In the following, we increment an array index in each thread via :func:`wp.atomic_add() ` and compute -the square root of an input array at the incremented index:: - - @wp.kernel - def test_add(counter: wp.array(dtype=int), input: wp.array(dtype=float), output: wp.array(dtype=float)): - idx = wp.atomic_add(counter, 0, 1) - output[idx] = wp.sqrt(input[idx]) - - def main(): - dim = 16 - use_reversible_increment = False - input = wp.array(np.arange(1, dim + 1), dtype=wp.float32, requires_grad=True) - counter = wp.zeros(1, dtype=wp.int32) - thread_ids = wp.zeros(dim, dtype=wp.int32) - output = wp.zeros(dim, dtype=wp.float32, requires_grad=True) - tape = wp.Tape() - with tape: - if use_reversible_increment: - wp.launch(test_add_diff, dim, inputs=[counter, thread_ids, input], outputs=[output]) - else: - wp.launch(test_add, dim, inputs=[counter, input], outputs=[output]) - - print("counter: ", counter.numpy()) - print("thread_ids: ", thread_ids.numpy()) - print("input: ", input.numpy()) - print("output: ", output.numpy()) - - tape.backward(grads={ - output: wp.array(np.ones(dim), dtype=wp.float32) - }) - print("input.grad: ", input.grad.numpy()) - - if __name__ == "__main__": - main() - -The output of the above code is: - -.. code-block:: js - - counter: [8] - thread_ids: [0 0 0 0 0 0 0 0] - input: [1. 2. 3. 4. 5. 6. 7. 8.] - output: [1. 1.4142135 1.7320508 2. 2.236068 2.4494898 2.6457512 2.828427] - input.grad: [4. 0. 0. 0. 0. 0. 0. 0.] - -The gradient of the input is incorrect because the backward pass involving the atomic operation ``wp.atomic_add()`` does not know which thread ID corresponds -to which input value. -The index returned by the adjoint of ``wp.atomic_add()`` is always zero so that the gradient the first entry of the input array, -i.e. :math:`\frac{1}{2\sqrt{1}} = 0.5`, is accumulated ``dim`` times (hence ``input.grad[0] == 4.0`` and all other entries zero). - -To fix this, we define a new Warp function ``reversible_increment()`` with a custom *replay* definition that stores the thread ID in a separate array:: - - @wp.func - def reversible_increment( - buf: wp.array(dtype=int), - buf_index: int, - value: int, - thread_values: wp.array(dtype=int), - tid: int - ): - next_index = wp.atomic_add(buf, buf_index, value) - # store which thread ID corresponds to which index for the backward pass - thread_values[tid] = next_index - return next_index - - - @wp.func_replay(reversible_increment) - def replay_reversible_increment( - buf: wp.array(dtype=int), - buf_index: int, - value: int, - thread_values: wp.array(dtype=int), - tid: int - ): - return thread_values[tid] - - -Instead of running ``reversible_increment()``, the custom replay code in ``replay_reversible_increment()`` is now executed -during forward phase in the backward pass of the function calling ``reversible_increment()``. -We first stored the array index to each thread ID in the forward pass, and now we retrieve the array index for each thread ID in the backward pass. -That way, the backward pass can reproduce the same addition operation as in the forward pass with exactly the same operands per thread. - -.. warning:: The function signature of the custom replay code must match the forward function signature. - -To use our function we write the following kernel:: - - @wp.kernel - def test_add_diff( - counter: wp.array(dtype=int), - thread_ids: wp.array(dtype=int), - input: wp.array(dtype=float), - output: wp.array(dtype=float) - ): - tid = wp.tid() - idx = reversible_increment(counter, 0, 1, thread_ids, tid) - output[idx] = wp.sqrt(input[idx]) - -Running the ``test_add_diff`` kernel via the previous ``main`` function with ``use_reversible_increment = True``, we now compute correct gradients -for the input array: - -.. code-block:: js - - counter: [8] - thread_ids: [0 1 2 3 4 5 6 7] - input: [1. 2. 3. 4. 5. 6. 7. 8.] - output: [1. 1.4142135 1.7320508 2. 2.236068 2.4494898 2.6457512 2.828427 ] - input.grad: [0.5 0.35355338 0.28867513 0.25 0.2236068 0.20412414 0.18898225 0.17677669] - -Custom Native Functions ------------------------ - -Users may insert native C++/CUDA code in Warp kernels using ``@func_native`` decorated functions. -These accept native code as strings that get compiled after code generation, and are called within ``@wp.kernel`` functions. -For example:: - - snippet = """ - __shared__ int s[128]; - - s[tid] = d[tid]; - __syncthreads(); - d[tid] = s[N - tid - 1]; - """ - - @wp.func_native(snippet) - def reverse(d: wp.array(dtype=int), N: int, tid: int): - ... - - @wp.kernel - def reverse_kernel(d: wp.array(dtype=int), N: int): - tid = wp.tid() - reverse(d, N, tid) - - N = 128 - x = wp.array(np.arange(N, dtype=int), dtype=int, device=device) - - wp.launch(kernel=reverse_kernel, dim=N, inputs=[x, N], device=device) - -Notice the use of shared memory here: the Warp library does not expose shared memory as a feature, but the CUDA compiler will -readily accept the above snippet. This means CUDA features not exposed in Warp are still accessible in Warp scripts. -Warp kernels meant for the CPU won't be able to leverage CUDA features of course, but this same mechanism supports pure C++ snippets as well. - -Please bear in mind the following: the thread index in your snippet should be computed in a ``@wp.kernel`` and passed to your snippet, -as in the above example. This means your ``@wp.func_native`` function signature should include the variables used in your snippet, -as well as a thread index of type ``int``. The function body itself should be stubbed with ``...`` (the snippet will be inserted during compilation). - -Should you wish to record your native function on the tape and then subsequently rewind the tape, you must include an adjoint snippet -alongside your snippet as an additional input to the decorator, as in the following example:: - - snippet = """ - out[tid] = a * x[tid] + y[tid]; - """ - adj_snippet = """ - adj_a = x[tid] * adj_out[tid]; - adj_x[tid] = a * adj_out[tid]; - adj_y[tid] = adj_out[tid]; - """ - - @wp.func_native(snippet, adj_snippet) - def saxpy( - a: wp.float32, - x: wp.array(dtype=wp.float32), - y: wp.array(dtype=wp.float32), - out: wp.array(dtype=wp.float32), - tid: int, - ): - ... - - @wp.kernel - def saxpy_kernel( - a: wp.float32, - x: wp.array(dtype=wp.float32), - y: wp.array(dtype=wp.float32), - out: wp.array(dtype=wp.float32) - ): - tid = wp.tid() - saxpy(a, x, y, out, tid) - - N = 128 - a = 2.0 - x = wp.array(np.arange(N, dtype=np.float32), dtype=wp.float32, device=device, requires_grad=True) - y = wp.zeros_like(x1) - out = wp.array(np.arange(N, dtype=np.float32), dtype=wp.float32, device=device) - adj_out = wp.array(np.ones(N, dtype=np.float32), dtype=wp.float32, device=device) - - tape = wp.Tape() - - with tape: - wp.launch(kernel=saxpy_kernel, dim=N, inputs=[a, x, y], outputs=[out], device=device) - - tape.backward(grads={out: adj_out}) - -Profiling ---------- - -``wp.ScopedTimer`` objects can be used to gain some basic insight into the performance of Warp applications: - -.. code:: python - - with wp.ScopedTimer("grid build"): - self.grid.build(self.x, self.point_radius) - -This results in a printout at runtime to the standard output stream like: - -.. code:: console - - grid build took 0.06 ms - -The ``wp.ScopedTimer`` object does not synchronize (e.g. by calling ``wp.synchronize()``) -upon exiting the ``with`` statement, so this can lead to misleading numbers if the body -of the ``with`` statement launches device kernels. - -When a ``wp.ScopedTimer`` object is passed ``use_nvtx=True`` as an argument, the timing functionality is replaced by calls -to ``nvtx.start_range()`` and ``nvtx.end_range()``: - -.. code:: python - - with wp.ScopedTimer("grid build", use_nvtx=True, color="cyan"): - self.grid.build(self.x, self.point_radius) - -These range annotations can then be collected by a tool like `NVIDIA Nsight Systems `_ -for visualization on a timeline, e.g.: - -.. code:: console - - $ nsys profile python warp_application.py - -This code snippet also demonstrates the use of the ``color`` argument to specify a color -for the range, which may be a number representing the ARGB value or a recognized string -(refer to `colors.py `_ for -additional color examples). -The `nvtx module `_ must be -installed in the Python environment for this capability to work. -An equivalent way to create an NVTX range without using ``wp.ScopedTimer`` is: - -.. code:: python - - import nvtx - - with nvtx.annotate("grid build", color="cyan"): - self.grid.build(self.x, self.point_radius) - -This form may be more convenient if the user does not need to frequently switch -between timer and NVTX capabilities of ``wp.ScopedTimer``. - -.. autoclass:: warp.ScopedTimer diff --git a/docs/_build/html/_sources/modules/sim.rst.txt b/docs/_build/html/_sources/modules/sim.rst.txt deleted file mode 100644 index bec52af0b..000000000 --- a/docs/_build/html/_sources/modules/sim.rst.txt +++ /dev/null @@ -1,157 +0,0 @@ -warp.sim -==================== - -.. currentmodule:: warp.sim - -.. - .. toctree:: - :maxdepth: 2 - -Warp includes a simulation module ``warp.sim`` that includes many common physical simulation models, and integrators for explicit and implicit time-stepping. - -.. note:: The simulation model is under construction and should be expected to change rapidly, please treat this section as work in progress. - - -Model ------ - -.. autoclass:: ModelBuilder - :members: - -.. autoclass:: Model - :members: - -.. autoclass:: JointAxis - :members: - -.. autoclass:: Mesh - :members: - -.. autoclass:: SDF - :members: - -.. _Joint types: - -Joint types -^^^^^^^^^^^^^^ - -.. data:: JOINT_PRISMATIC - - Prismatic (slider) joint - -.. data:: JOINT_REVOLUTE - - Revolute (hinge) joint - -.. data:: JOINT_BALL - - Ball (spherical) joint with quaternion state representation - -.. data:: JOINT_FIXED - - Fixed (static) joint - -.. data:: JOINT_FREE - - Free (floating) joint - -.. data:: JOINT_COMPOUND - - Compound joint with 3 rotational degrees of freedom - -.. data:: JOINT_UNIVERSAL - - Universal joint with 2 rotational degrees of freedom - -.. data:: JOINT_DISTANCE - - Distance joint that keeps two bodies at a distance within its joint limits (only supported in :class:`XPBDIntegrator` at the moment) - -.. data:: JOINT_D6 - - Generic D6 joint with up to 3 translational and 3 rotational degrees of freedom - -.. _Joint modes: - -Joint modes -^^^^^^^^^^^^^^ - -Joint modes control the behavior of joint axes and can be used to implement joint position or velocity drives. - -.. data:: JOINT_MODE_LIMIT - - No target or velocity control is applied, the joint is limited to its joint limits - -.. data:: JOINT_MODE_TARGET_POSITION - - The joint is driven to a target position - -.. data:: JOINT_MODE_TARGET_VELOCITY - - The joint is driven to a target velocity - -State --------------- - -.. autoclass:: State - :members: - -.. _FK-IK: - -Forward / Inverse Kinematics ----------------------------- - -Articulated rigid-body mechanisms are kinematically described by the joints that connect the bodies as well as the relative relative transform from the parent and child body to the respective anchor frames of the joint in the parent and child body: - -.. image:: /img/joint_transforms.png - :width: 400 - :align: center - -.. list-table:: Variable names in the kernels from articulation.py - :widths: 10 90 - :header-rows: 1 - - * - Symbol - - Description - * - x_wp - - World transform of the parent body (stored at :attr:`State.body_q`) - * - x_wc - - World transform of the child body (stored at :attr:`State.body_q`) - * - x_pj - - Transform from the parent body to the joint parent anchor frame (defined by :attr:`Model.joint_X_p`) - * - x_cj - - Transform from the child body to the joint child anchor frame (defined by :attr:`Model.joint_X_c`) - * - x_j - - Joint transform from the joint parent anchor frame to the joint child anchor frame - -In the forward kinematics, the joint transform is determined by the joint coordinates (generalized joint positions :attr:`State.body_q` and velocities :attr:`State.body_qd`). -Given the parent body's world transform :math:`x_{wp}` and the joint transform :math:`x_{j}`, the child body's world transform :math:`x_{wc}` is computed as: - -.. math:: - x_{wc} = x_{wp} \cdot x_{pj} \cdot x_{j} \cdot x_{cj}^{-1}. - -.. autofunction:: eval_fk - -.. autofunction:: eval_ik - -Integrators --------------- - -.. autoclass:: SemiImplicitIntegrator - :members: - -.. autoclass:: XPBDIntegrator - :members: - -Importers --------------- - -Warp sim supports the loading of simulation models from URDF, MuJoCo (MJCF), and USD Physics files. - -.. autofunction:: parse_urdf - -.. autofunction:: parse_mjcf - -.. autofunction:: parse_usd - -.. autofunction:: resolve_usd_from_url \ No newline at end of file diff --git a/docs/_build/html/_sources/modules/sparse.rst.txt b/docs/_build/html/_sources/modules/sparse.rst.txt deleted file mode 100644 index 42fb05ba1..000000000 --- a/docs/_build/html/_sources/modules/sparse.rst.txt +++ /dev/null @@ -1,19 +0,0 @@ -warp.sparse -=============================== - -.. currentmodule:: warp.sparse - -.. - .. toctree:: - :maxdepth: 2 - -Warp includes a sparse linear algebra module ``warp.sparse`` that implements some common operations for manipulating sparse matrices that arise in simulation. - -Sparse Matrices -------------------------- - -Currently `warp.sparse` supports Block Sparse Row (BSR) matrices, the BSR format can also be used to represent Compressed Sparse Row (CSR) matrices as a special case with a 1x1 block size. - -.. automodule:: warp.sparse - :members: - diff --git a/docs/_build/html/_static/basic.css b/docs/_build/html/_static/basic.css deleted file mode 100644 index 30fee9d0f..000000000 --- a/docs/_build/html/_static/basic.css +++ /dev/null @@ -1,925 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/* -- 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: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -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/docs/_build/html/_static/check-solid.svg b/docs/_build/html/_static/check-solid.svg deleted file mode 100644 index 92fad4b5c..000000000 --- a/docs/_build/html/_static/check-solid.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/docs/_build/html/_static/clipboard.min.js b/docs/_build/html/_static/clipboard.min.js deleted file mode 100644 index 54b3c4638..000000000 --- a/docs/_build/html/_static/clipboard.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * clipboard.js v2.0.8 - * https://clipboardjs.com/ - * - * Licensed MIT © Zeno Rocha - */ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1 - - - - diff --git a/docs/_build/html/_static/copybutton.css b/docs/_build/html/_static/copybutton.css deleted file mode 100644 index f1916ec7d..000000000 --- a/docs/_build/html/_static/copybutton.css +++ /dev/null @@ -1,94 +0,0 @@ -/* Copy buttons */ -button.copybtn { - position: absolute; - display: flex; - top: .3em; - right: .3em; - width: 1.7em; - height: 1.7em; - opacity: 0; - transition: opacity 0.3s, border .3s, background-color .3s; - user-select: none; - padding: 0; - border: none; - outline: none; - border-radius: 0.4em; - /* The colors that GitHub uses */ - border: #1b1f2426 1px solid; - background-color: #f6f8fa; - color: #57606a; -} - -button.copybtn.success { - border-color: #22863a; - color: #22863a; -} - -button.copybtn svg { - stroke: currentColor; - width: 1.5em; - height: 1.5em; - padding: 0.1em; -} - -div.highlight { - position: relative; -} - -/* Show the copybutton */ -.highlight:hover button.copybtn, button.copybtn.success { - opacity: 1; -} - -.highlight button.copybtn:hover { - background-color: rgb(235, 235, 235); -} - -.highlight button.copybtn:active { - background-color: rgb(187, 187, 187); -} - -/** - * A minimal CSS-only tooltip copied from: - * https://codepen.io/mildrenben/pen/rVBrpK - * - * To use, write HTML like the following: - * - *

Short

- */ - .o-tooltip--left { - position: relative; - } - - .o-tooltip--left:after { - opacity: 0; - visibility: hidden; - position: absolute; - content: attr(data-tooltip); - padding: .2em; - font-size: .8em; - left: -.2em; - background: grey; - color: white; - white-space: nowrap; - z-index: 2; - border-radius: 2px; - transform: translateX(-102%) translateY(0); - transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); -} - -.o-tooltip--left:hover:after { - display: block; - opacity: 1; - visibility: visible; - transform: translateX(-100%) translateY(0); - transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); - transition-delay: .5s; -} - -/* By default the copy button shouldn't show up when printing a page */ -@media print { - button.copybtn { - display: none; - } -} diff --git a/docs/_build/html/_static/copybutton.js b/docs/_build/html/_static/copybutton.js deleted file mode 100644 index ff4aa329a..000000000 --- a/docs/_build/html/_static/copybutton.js +++ /dev/null @@ -1,248 +0,0 @@ -// Localization support -const messages = { - 'en': { - 'copy': 'Copy', - 'copy_to_clipboard': 'Copy to clipboard', - 'copy_success': 'Copied!', - 'copy_failure': 'Failed to copy', - }, - 'es' : { - 'copy': 'Copiar', - 'copy_to_clipboard': 'Copiar al portapapeles', - 'copy_success': '¡Copiado!', - 'copy_failure': 'Error al copiar', - }, - 'de' : { - 'copy': 'Kopieren', - 'copy_to_clipboard': 'In die Zwischenablage kopieren', - 'copy_success': 'Kopiert!', - 'copy_failure': 'Fehler beim Kopieren', - }, - 'fr' : { - 'copy': 'Copier', - 'copy_to_clipboard': 'Copier dans le presse-papier', - 'copy_success': 'Copié !', - 'copy_failure': 'Échec de la copie', - }, - 'ru': { - 'copy': 'Скопировать', - 'copy_to_clipboard': 'Скопировать в буфер', - 'copy_success': 'Скопировано!', - 'copy_failure': 'Не удалось скопировать', - }, - 'zh-CN': { - 'copy': '复制', - 'copy_to_clipboard': '复制到剪贴板', - 'copy_success': '复制成功!', - 'copy_failure': '复制失败', - }, - 'it' : { - 'copy': 'Copiare', - 'copy_to_clipboard': 'Copiato negli appunti', - 'copy_success': 'Copiato!', - 'copy_failure': 'Errore durante la copia', - } -} - -let locale = 'en' -if( document.documentElement.lang !== undefined - && messages[document.documentElement.lang] !== undefined ) { - locale = document.documentElement.lang -} - -let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT; -if (doc_url_root == '#') { - doc_url_root = ''; -} - -/** - * SVG files for our copy buttons - */ -let iconCheck = ` - ${messages[locale]['copy_success']} - - -` - -// If the user specified their own SVG use that, otherwise use the default -let iconCopy = ``; -if (!iconCopy) { - iconCopy = ` - ${messages[locale]['copy_to_clipboard']} - - - -` -} - -/** - * Set up copy/paste for code blocks - */ - -const runWhenDOMLoaded = cb => { - if (document.readyState != 'loading') { - cb() - } else if (document.addEventListener) { - document.addEventListener('DOMContentLoaded', cb) - } else { - document.attachEvent('onreadystatechange', function() { - if (document.readyState == 'complete') cb() - }) - } -} - -const codeCellId = index => `codecell${index}` - -// Clears selected text since ClipboardJS will select the text when copying -const clearSelection = () => { - if (window.getSelection) { - window.getSelection().removeAllRanges() - } else if (document.selection) { - document.selection.empty() - } -} - -// Changes tooltip text for a moment, then changes it back -// We want the timeout of our `success` class to be a bit shorter than the -// tooltip and icon change, so that we can hide the icon before changing back. -var timeoutIcon = 2000; -var timeoutSuccessClass = 1500; - -const temporarilyChangeTooltip = (el, oldText, newText) => { - el.setAttribute('data-tooltip', newText) - el.classList.add('success') - // Remove success a little bit sooner than we change the tooltip - // So that we can use CSS to hide the copybutton first - setTimeout(() => el.classList.remove('success'), timeoutSuccessClass) - setTimeout(() => el.setAttribute('data-tooltip', oldText), timeoutIcon) -} - -// Changes the copy button icon for two seconds, then changes it back -const temporarilyChangeIcon = (el) => { - el.innerHTML = iconCheck; - setTimeout(() => {el.innerHTML = iconCopy}, timeoutIcon) -} - -const addCopyButtonToCodeCells = () => { - // If ClipboardJS hasn't loaded, wait a bit and try again. This - // happens because we load ClipboardJS asynchronously. - if (window.ClipboardJS === undefined) { - setTimeout(addCopyButtonToCodeCells, 250) - return - } - - // Add copybuttons to all of our code cells - const COPYBUTTON_SELECTOR = 'div.highlight pre'; - const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR) - codeCells.forEach((codeCell, index) => { - const id = codeCellId(index) - codeCell.setAttribute('id', id) - - const clipboardButton = id => - `` - codeCell.insertAdjacentHTML('afterend', clipboardButton(id)) - }) - -function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string -} - -/** - * Removes excluded text from a Node. - * - * @param {Node} target Node to filter. - * @param {string} exclude CSS selector of nodes to exclude. - * @returns {DOMString} Text from `target` with text removed. - */ -function filterText(target, exclude) { - const clone = target.cloneNode(true); // clone as to not modify the live DOM - if (exclude) { - // remove excluded nodes - clone.querySelectorAll(exclude).forEach(node => node.remove()); - } - return clone.innerText; -} - -// Callback when a copy button is clicked. Will be passed the node that was clicked -// should then grab the text and replace pieces of text that shouldn't be used in output -function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { - var regexp; - var match; - - // Do we check for line continuation characters and "HERE-documents"? - var useLineCont = !!lineContinuationChar - var useHereDoc = !!hereDocDelim - - // create regexp to capture prompt and remaining line - if (isRegexp) { - regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') - } else { - regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') - } - - const outputLines = []; - var promptFound = false; - var gotLineCont = false; - var gotHereDoc = false; - const lineGotPrompt = []; - for (const line of textContent.split('\n')) { - match = line.match(regexp) - if (match || gotLineCont || gotHereDoc) { - promptFound = regexp.test(line) - lineGotPrompt.push(promptFound) - if (removePrompts && promptFound) { - outputLines.push(match[2]) - } else { - outputLines.push(line) - } - gotLineCont = line.endsWith(lineContinuationChar) & useLineCont - if (line.includes(hereDocDelim) & useHereDoc) - gotHereDoc = !gotHereDoc - } else if (!onlyCopyPromptLines) { - outputLines.push(line) - } else if (copyEmptyLines && line.trim() === '') { - outputLines.push(line) - } - } - - // If no lines with the prompt were found then just use original lines - if (lineGotPrompt.some(v => v === true)) { - textContent = outputLines.join('\n'); - } - - // Remove a trailing newline to avoid auto-running when pasting - if (textContent.endsWith("\n")) { - textContent = textContent.slice(0, -1) - } - return textContent -} - - -var copyTargetText = (trigger) => { - var target = document.querySelector(trigger.attributes['data-clipboard-target'].value); - - // get filtered text - let exclude = '.linenos'; - - let text = filterText(target, exclude); - return formatCopyText(text, '>>> |\\.\\.\\. |\\$ ', true, true, true, true, '', '') -} - - // Initialize with a callback so we can modify the text before copy - const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText}) - - // Update UI with error/success messages - clipboard.on('success', event => { - clearSelection() - temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success']) - temporarilyChangeIcon(event.trigger) - }) - - clipboard.on('error', event => { - temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure']) - }) -} - -runWhenDOMLoaded(addCopyButtonToCodeCells) \ No newline at end of file diff --git a/docs/_build/html/_static/copybutton_funcs.js b/docs/_build/html/_static/copybutton_funcs.js deleted file mode 100644 index dbe1aaad7..000000000 --- a/docs/_build/html/_static/copybutton_funcs.js +++ /dev/null @@ -1,73 +0,0 @@ -function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string -} - -/** - * Removes excluded text from a Node. - * - * @param {Node} target Node to filter. - * @param {string} exclude CSS selector of nodes to exclude. - * @returns {DOMString} Text from `target` with text removed. - */ -export function filterText(target, exclude) { - const clone = target.cloneNode(true); // clone as to not modify the live DOM - if (exclude) { - // remove excluded nodes - clone.querySelectorAll(exclude).forEach(node => node.remove()); - } - return clone.innerText; -} - -// Callback when a copy button is clicked. Will be passed the node that was clicked -// should then grab the text and replace pieces of text that shouldn't be used in output -export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { - var regexp; - var match; - - // Do we check for line continuation characters and "HERE-documents"? - var useLineCont = !!lineContinuationChar - var useHereDoc = !!hereDocDelim - - // create regexp to capture prompt and remaining line - if (isRegexp) { - regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') - } else { - regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') - } - - const outputLines = []; - var promptFound = false; - var gotLineCont = false; - var gotHereDoc = false; - const lineGotPrompt = []; - for (const line of textContent.split('\n')) { - match = line.match(regexp) - if (match || gotLineCont || gotHereDoc) { - promptFound = regexp.test(line) - lineGotPrompt.push(promptFound) - if (removePrompts && promptFound) { - outputLines.push(match[2]) - } else { - outputLines.push(line) - } - gotLineCont = line.endsWith(lineContinuationChar) & useLineCont - if (line.includes(hereDocDelim) & useHereDoc) - gotHereDoc = !gotHereDoc - } else if (!onlyCopyPromptLines) { - outputLines.push(line) - } else if (copyEmptyLines && line.trim() === '') { - outputLines.push(line) - } - } - - // If no lines with the prompt were found then just use original lines - if (lineGotPrompt.some(v => v === true)) { - textContent = outputLines.join('\n'); - } - - // Remove a trailing newline to avoid auto-running when pasting - if (textContent.endsWith("\n")) { - textContent = textContent.slice(0, -1) - } - return textContent -} diff --git a/docs/_build/html/_static/custom.css b/docs/_build/html/_static/custom.css deleted file mode 100644 index 52a770bbb..000000000 --- a/docs/_build/html/_static/custom.css +++ /dev/null @@ -1,37 +0,0 @@ -/* hides the TOC caption from the main page since we already have an H1 heading */ -.section .caption-text { - display: none; -} - -/* note title text in white */ -.admonition p.admonition-title { - color: white; - font-weight: 700; -} - -/* .admonition.note { - background: #e5f4d8; -} */ - -/* left align tables */ -table.docutils { - margin-left: 1em; - font-family: monospace; -} - -/* inline code snippets #E74C3C, var(--color-link), #4e9a06 */ -code.literal { - color: #4e9a06; - font-family: monospace; - font-size: var(--font-size-normal); -} - -.sidebar-brand-text { - text-align: center; - margin: 0 0; -} - - - -/* normalize size across Firefox / Chrome */ -html { font-size: 100%; } diff --git a/docs/_build/html/_static/debug.css b/docs/_build/html/_static/debug.css deleted file mode 100644 index 74d4aec33..000000000 --- a/docs/_build/html/_static/debug.css +++ /dev/null @@ -1,69 +0,0 @@ -/* - This CSS file should be overridden by the theme authors. It's - meant for debugging and developing the skeleton that this theme provides. -*/ -body { - font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, - "Apple Color Emoji", "Segoe UI Emoji"; - background: lavender; -} -.sb-announcement { - background: rgb(131, 131, 131); -} -.sb-announcement__inner { - background: black; - color: white; -} -.sb-header { - background: lightskyblue; -} -.sb-header__inner { - background: royalblue; - color: white; -} -.sb-header-secondary { - background: lightcyan; -} -.sb-header-secondary__inner { - background: cornflowerblue; - color: white; -} -.sb-sidebar-primary { - background: lightgreen; -} -.sb-main { - background: blanchedalmond; -} -.sb-main__inner { - background: antiquewhite; -} -.sb-header-article { - background: lightsteelblue; -} -.sb-article-container { - background: snow; -} -.sb-article-main { - background: white; -} -.sb-footer-article { - background: lightpink; -} -.sb-sidebar-secondary { - background: lightgoldenrodyellow; -} -.sb-footer-content { - background: plum; -} -.sb-footer-content__inner { - background: palevioletred; -} -.sb-footer { - background: pink; -} -.sb-footer__inner { - background: salmon; -} -.sb-article { - background: white; -} diff --git a/docs/_build/html/_static/doctools.js b/docs/_build/html/_static/doctools.js deleted file mode 100644 index d06a71d75..000000000 --- a/docs/_build/html/_static/doctools.js +++ /dev/null @@ -1,156 +0,0 @@ -/* - * doctools.js - * ~~~~~~~~~~~ - * - * Base JavaScript utilities for all Sphinx HTML documentation. - * - * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ -"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/docs/_build/html/_static/documentation_options.js b/docs/_build/html/_static/documentation_options.js deleted file mode 100644 index a2012ad39..000000000 --- a/docs/_build/html/_static/documentation_options.js +++ /dev/null @@ -1,13 +0,0 @@ -const DOCUMENTATION_OPTIONS = { - VERSION: '1.0.0-beta.5', - 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/docs/_build/html/_static/file.png b/docs/_build/html/_static/file.png deleted file mode 100644 index a858a410e..000000000 Binary files a/docs/_build/html/_static/file.png and /dev/null differ diff --git a/docs/_build/html/_static/language_data.js b/docs/_build/html/_static/language_data.js deleted file mode 100644 index 250f5665f..000000000 --- a/docs/_build/html/_static/language_data.js +++ /dev/null @@ -1,199 +0,0 @@ -/* - * language_data.js - * ~~~~~~~~~~~~~~~~ - * - * This script contains the language-specific data used by searchtools.js, - * namely the list of stopwords, stemmer, scorer and splitter. - * - * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -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, is 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/docs/_build/html/_static/logo-dark-mode.png b/docs/_build/html/_static/logo-dark-mode.png deleted file mode 100644 index ce0016561..000000000 Binary files a/docs/_build/html/_static/logo-dark-mode.png and /dev/null differ diff --git a/docs/_build/html/_static/logo-light-mode.png b/docs/_build/html/_static/logo-light-mode.png deleted file mode 100644 index 3f2fd3164..000000000 Binary files a/docs/_build/html/_static/logo-light-mode.png and /dev/null differ diff --git a/docs/_build/html/_static/minus.png b/docs/_build/html/_static/minus.png deleted file mode 100644 index d96755fda..000000000 Binary files a/docs/_build/html/_static/minus.png and /dev/null differ diff --git a/docs/_build/html/_static/plus.png b/docs/_build/html/_static/plus.png deleted file mode 100644 index 7107cec93..000000000 Binary files a/docs/_build/html/_static/plus.png and /dev/null differ diff --git a/docs/_build/html/_static/pygments.css b/docs/_build/html/_static/pygments.css deleted file mode 100644 index c2e07c71e..000000000 --- a/docs/_build/html/_static/pygments.css +++ /dev/null @@ -1,258 +0,0 @@ -.highlight pre { line-height: 125%; } -.highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -.highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -.highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -.highlight 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: #8f5902; font-style: italic } /* Comment */ -.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ -.highlight .g { color: #000000 } /* Generic */ -.highlight .k { color: #204a87; font-weight: bold } /* Keyword */ -.highlight .l { color: #000000 } /* Literal */ -.highlight .n { color: #000000 } /* Name */ -.highlight .o { color: #ce5c00; font-weight: bold } /* Operator */ -.highlight .x { color: #000000 } /* Other */ -.highlight .p { color: #000000; font-weight: bold } /* Punctuation */ -.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ -.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #8f5902; font-style: italic } /* Comment.Preproc */ -.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ -.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ -.highlight .gd { color: #a40000 } /* Generic.Deleted */ -.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ -.highlight .ges { color: #000000; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ -.highlight .gr { color: #ef2929 } /* Generic.Error */ -.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #00A000 } /* Generic.Inserted */ -.highlight .go { color: #000000; font-style: italic } /* Generic.Output */ -.highlight .gp { color: #8f5902 } /* Generic.Prompt */ -.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ -.highlight .kc { color: #204a87; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #204a87; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #204a87; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #204a87; font-weight: bold } /* Keyword.Pseudo */ -.highlight .kr { color: #204a87; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #204a87; font-weight: bold } /* Keyword.Type */ -.highlight .ld { color: #000000 } /* Literal.Date */ -.highlight .m { color: #0000cf; font-weight: bold } /* Literal.Number */ -.highlight .s { color: #4e9a06 } /* Literal.String */ -.highlight .na { color: #c4a000 } /* Name.Attribute */ -.highlight .nb { color: #204a87 } /* Name.Builtin */ -.highlight .nc { color: #000000 } /* Name.Class */ -.highlight .no { color: #000000 } /* Name.Constant */ -.highlight .nd { color: #5c35cc; font-weight: bold } /* Name.Decorator */ -.highlight .ni { color: #ce5c00 } /* Name.Entity */ -.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #000000 } /* Name.Function */ -.highlight .nl { color: #f57900 } /* Name.Label */ -.highlight .nn { color: #000000 } /* Name.Namespace */ -.highlight .nx { color: #000000 } /* Name.Other */ -.highlight .py { color: #000000 } /* Name.Property */ -.highlight .nt { color: #204a87; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #000000 } /* Name.Variable */ -.highlight .ow { color: #204a87; font-weight: bold } /* Operator.Word */ -.highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */ -.highlight .w { color: #f8f8f8 } /* Text.Whitespace */ -.highlight .mb { color: #0000cf; font-weight: bold } /* Literal.Number.Bin */ -.highlight .mf { color: #0000cf; font-weight: bold } /* Literal.Number.Float */ -.highlight .mh { color: #0000cf; font-weight: bold } /* Literal.Number.Hex */ -.highlight .mi { color: #0000cf; font-weight: bold } /* Literal.Number.Integer */ -.highlight .mo { color: #0000cf; font-weight: bold } /* Literal.Number.Oct */ -.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */ -.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ -.highlight .sc { color: #4e9a06 } /* Literal.String.Char */ -.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */ -.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ -.highlight .se { color: #4e9a06 } /* Literal.String.Escape */ -.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ -.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ -.highlight .sx { color: #4e9a06 } /* Literal.String.Other */ -.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ -.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ -.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ -.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ -.highlight .fm { color: #000000 } /* Name.Function.Magic */ -.highlight .vc { color: #000000 } /* Name.Variable.Class */ -.highlight .vg { color: #000000 } /* Name.Variable.Global */ -.highlight .vi { color: #000000 } /* Name.Variable.Instance */ -.highlight .vm { color: #000000 } /* Name.Variable.Magic */ -.highlight .il { color: #0000cf; font-weight: bold } /* Literal.Number.Integer.Long */ -@media not print { -body[data-theme="dark"] .highlight pre { line-height: 125%; } -body[data-theme="dark"] .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } -body[data-theme="dark"] .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } -body[data-theme="dark"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -body[data-theme="dark"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -body[data-theme="dark"] .highlight .hll { background-color: #404040 } -body[data-theme="dark"] .highlight { background: #202020; color: #d0d0d0 } -body[data-theme="dark"] .highlight .c { color: #ababab; font-style: italic } /* Comment */ -body[data-theme="dark"] .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ -body[data-theme="dark"] .highlight .esc { color: #d0d0d0 } /* Escape */ -body[data-theme="dark"] .highlight .g { color: #d0d0d0 } /* Generic */ -body[data-theme="dark"] .highlight .k { color: #6ebf26; font-weight: bold } /* Keyword */ -body[data-theme="dark"] .highlight .l { color: #d0d0d0 } /* Literal */ -body[data-theme="dark"] .highlight .n { color: #d0d0d0 } /* Name */ -body[data-theme="dark"] .highlight .o { color: #d0d0d0 } /* Operator */ -body[data-theme="dark"] .highlight .x { color: #d0d0d0 } /* Other */ -body[data-theme="dark"] .highlight .p { color: #d0d0d0 } /* Punctuation */ -body[data-theme="dark"] .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */ -body[data-theme="dark"] .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */ -body[data-theme="dark"] .highlight .cp { color: #ff3a3a; font-weight: bold } /* Comment.Preproc */ -body[data-theme="dark"] .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */ -body[data-theme="dark"] .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */ -body[data-theme="dark"] .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */ -body[data-theme="dark"] .highlight .gd { color: #d22323 } /* Generic.Deleted */ -body[data-theme="dark"] .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */ -body[data-theme="dark"] .highlight .ges { color: #d0d0d0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ -body[data-theme="dark"] .highlight .gr { color: #d22323 } /* Generic.Error */ -body[data-theme="dark"] .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */ -body[data-theme="dark"] .highlight .gi { color: #589819 } /* Generic.Inserted */ -body[data-theme="dark"] .highlight .go { color: #cccccc } /* Generic.Output */ -body[data-theme="dark"] .highlight .gp { color: #aaaaaa } /* Generic.Prompt */ -body[data-theme="dark"] .highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */ -body[data-theme="dark"] .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */ -body[data-theme="dark"] .highlight .gt { color: #d22323 } /* Generic.Traceback */ -body[data-theme="dark"] .highlight .kc { color: #6ebf26; font-weight: bold } /* Keyword.Constant */ -body[data-theme="dark"] .highlight .kd { color: #6ebf26; font-weight: bold } /* Keyword.Declaration */ -body[data-theme="dark"] .highlight .kn { color: #6ebf26; font-weight: bold } /* Keyword.Namespace */ -body[data-theme="dark"] .highlight .kp { color: #6ebf26 } /* Keyword.Pseudo */ -body[data-theme="dark"] .highlight .kr { color: #6ebf26; font-weight: bold } /* Keyword.Reserved */ -body[data-theme="dark"] .highlight .kt { color: #6ebf26; font-weight: bold } /* Keyword.Type */ -body[data-theme="dark"] .highlight .ld { color: #d0d0d0 } /* Literal.Date */ -body[data-theme="dark"] .highlight .m { color: #51b2fd } /* Literal.Number */ -body[data-theme="dark"] .highlight .s { color: #ed9d13 } /* Literal.String */ -body[data-theme="dark"] .highlight .na { color: #bbbbbb } /* Name.Attribute */ -body[data-theme="dark"] .highlight .nb { color: #2fbccd } /* Name.Builtin */ -body[data-theme="dark"] .highlight .nc { color: #71adff; text-decoration: underline } /* Name.Class */ -body[data-theme="dark"] .highlight .no { color: #40ffff } /* Name.Constant */ -body[data-theme="dark"] .highlight .nd { color: #ffa500 } /* Name.Decorator */ -body[data-theme="dark"] .highlight .ni { color: #d0d0d0 } /* Name.Entity */ -body[data-theme="dark"] .highlight .ne { color: #bbbbbb } /* Name.Exception */ -body[data-theme="dark"] .highlight .nf { color: #71adff } /* Name.Function */ -body[data-theme="dark"] .highlight .nl { color: #d0d0d0 } /* Name.Label */ -body[data-theme="dark"] .highlight .nn { color: #71adff; text-decoration: underline } /* Name.Namespace */ -body[data-theme="dark"] .highlight .nx { color: #d0d0d0 } /* Name.Other */ -body[data-theme="dark"] .highlight .py { color: #d0d0d0 } /* Name.Property */ -body[data-theme="dark"] .highlight .nt { color: #6ebf26; font-weight: bold } /* Name.Tag */ -body[data-theme="dark"] .highlight .nv { color: #40ffff } /* Name.Variable */ -body[data-theme="dark"] .highlight .ow { color: #6ebf26; font-weight: bold } /* Operator.Word */ -body[data-theme="dark"] .highlight .pm { color: #d0d0d0 } /* Punctuation.Marker */ -body[data-theme="dark"] .highlight .w { color: #666666 } /* Text.Whitespace */ -body[data-theme="dark"] .highlight .mb { color: #51b2fd } /* Literal.Number.Bin */ -body[data-theme="dark"] .highlight .mf { color: #51b2fd } /* Literal.Number.Float */ -body[data-theme="dark"] .highlight .mh { color: #51b2fd } /* Literal.Number.Hex */ -body[data-theme="dark"] .highlight .mi { color: #51b2fd } /* Literal.Number.Integer */ -body[data-theme="dark"] .highlight .mo { color: #51b2fd } /* Literal.Number.Oct */ -body[data-theme="dark"] .highlight .sa { color: #ed9d13 } /* Literal.String.Affix */ -body[data-theme="dark"] .highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */ -body[data-theme="dark"] .highlight .sc { color: #ed9d13 } /* Literal.String.Char */ -body[data-theme="dark"] .highlight .dl { color: #ed9d13 } /* Literal.String.Delimiter */ -body[data-theme="dark"] .highlight .sd { color: #ed9d13 } /* Literal.String.Doc */ -body[data-theme="dark"] .highlight .s2 { color: #ed9d13 } /* Literal.String.Double */ -body[data-theme="dark"] .highlight .se { color: #ed9d13 } /* Literal.String.Escape */ -body[data-theme="dark"] .highlight .sh { color: #ed9d13 } /* Literal.String.Heredoc */ -body[data-theme="dark"] .highlight .si { color: #ed9d13 } /* Literal.String.Interpol */ -body[data-theme="dark"] .highlight .sx { color: #ffa500 } /* Literal.String.Other */ -body[data-theme="dark"] .highlight .sr { color: #ed9d13 } /* Literal.String.Regex */ -body[data-theme="dark"] .highlight .s1 { color: #ed9d13 } /* Literal.String.Single */ -body[data-theme="dark"] .highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */ -body[data-theme="dark"] .highlight .bp { color: #2fbccd } /* Name.Builtin.Pseudo */ -body[data-theme="dark"] .highlight .fm { color: #71adff } /* Name.Function.Magic */ -body[data-theme="dark"] .highlight .vc { color: #40ffff } /* Name.Variable.Class */ -body[data-theme="dark"] .highlight .vg { color: #40ffff } /* Name.Variable.Global */ -body[data-theme="dark"] .highlight .vi { color: #40ffff } /* Name.Variable.Instance */ -body[data-theme="dark"] .highlight .vm { color: #40ffff } /* Name.Variable.Magic */ -body[data-theme="dark"] .highlight .il { color: #51b2fd } /* Literal.Number.Integer.Long */ -@media (prefers-color-scheme: dark) { -body:not([data-theme="light"]) .highlight pre { line-height: 125%; } -body:not([data-theme="light"]) .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } -body:not([data-theme="light"]) .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } -body:not([data-theme="light"]) .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -body:not([data-theme="light"]) .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -body:not([data-theme="light"]) .highlight .hll { background-color: #404040 } -body:not([data-theme="light"]) .highlight { background: #202020; color: #d0d0d0 } -body:not([data-theme="light"]) .highlight .c { color: #ababab; font-style: italic } /* Comment */ -body:not([data-theme="light"]) .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ -body:not([data-theme="light"]) .highlight .esc { color: #d0d0d0 } /* Escape */ -body:not([data-theme="light"]) .highlight .g { color: #d0d0d0 } /* Generic */ -body:not([data-theme="light"]) .highlight .k { color: #6ebf26; font-weight: bold } /* Keyword */ -body:not([data-theme="light"]) .highlight .l { color: #d0d0d0 } /* Literal */ -body:not([data-theme="light"]) .highlight .n { color: #d0d0d0 } /* Name */ -body:not([data-theme="light"]) .highlight .o { color: #d0d0d0 } /* Operator */ -body:not([data-theme="light"]) .highlight .x { color: #d0d0d0 } /* Other */ -body:not([data-theme="light"]) .highlight .p { color: #d0d0d0 } /* Punctuation */ -body:not([data-theme="light"]) .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */ -body:not([data-theme="light"]) .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */ -body:not([data-theme="light"]) .highlight .cp { color: #ff3a3a; font-weight: bold } /* Comment.Preproc */ -body:not([data-theme="light"]) .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */ -body:not([data-theme="light"]) .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */ -body:not([data-theme="light"]) .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */ -body:not([data-theme="light"]) .highlight .gd { color: #d22323 } /* Generic.Deleted */ -body:not([data-theme="light"]) .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */ -body:not([data-theme="light"]) .highlight .ges { color: #d0d0d0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ -body:not([data-theme="light"]) .highlight .gr { color: #d22323 } /* Generic.Error */ -body:not([data-theme="light"]) .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */ -body:not([data-theme="light"]) .highlight .gi { color: #589819 } /* Generic.Inserted */ -body:not([data-theme="light"]) .highlight .go { color: #cccccc } /* Generic.Output */ -body:not([data-theme="light"]) .highlight .gp { color: #aaaaaa } /* Generic.Prompt */ -body:not([data-theme="light"]) .highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */ -body:not([data-theme="light"]) .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */ -body:not([data-theme="light"]) .highlight .gt { color: #d22323 } /* Generic.Traceback */ -body:not([data-theme="light"]) .highlight .kc { color: #6ebf26; font-weight: bold } /* Keyword.Constant */ -body:not([data-theme="light"]) .highlight .kd { color: #6ebf26; font-weight: bold } /* Keyword.Declaration */ -body:not([data-theme="light"]) .highlight .kn { color: #6ebf26; font-weight: bold } /* Keyword.Namespace */ -body:not([data-theme="light"]) .highlight .kp { color: #6ebf26 } /* Keyword.Pseudo */ -body:not([data-theme="light"]) .highlight .kr { color: #6ebf26; font-weight: bold } /* Keyword.Reserved */ -body:not([data-theme="light"]) .highlight .kt { color: #6ebf26; font-weight: bold } /* Keyword.Type */ -body:not([data-theme="light"]) .highlight .ld { color: #d0d0d0 } /* Literal.Date */ -body:not([data-theme="light"]) .highlight .m { color: #51b2fd } /* Literal.Number */ -body:not([data-theme="light"]) .highlight .s { color: #ed9d13 } /* Literal.String */ -body:not([data-theme="light"]) .highlight .na { color: #bbbbbb } /* Name.Attribute */ -body:not([data-theme="light"]) .highlight .nb { color: #2fbccd } /* Name.Builtin */ -body:not([data-theme="light"]) .highlight .nc { color: #71adff; text-decoration: underline } /* Name.Class */ -body:not([data-theme="light"]) .highlight .no { color: #40ffff } /* Name.Constant */ -body:not([data-theme="light"]) .highlight .nd { color: #ffa500 } /* Name.Decorator */ -body:not([data-theme="light"]) .highlight .ni { color: #d0d0d0 } /* Name.Entity */ -body:not([data-theme="light"]) .highlight .ne { color: #bbbbbb } /* Name.Exception */ -body:not([data-theme="light"]) .highlight .nf { color: #71adff } /* Name.Function */ -body:not([data-theme="light"]) .highlight .nl { color: #d0d0d0 } /* Name.Label */ -body:not([data-theme="light"]) .highlight .nn { color: #71adff; text-decoration: underline } /* Name.Namespace */ -body:not([data-theme="light"]) .highlight .nx { color: #d0d0d0 } /* Name.Other */ -body:not([data-theme="light"]) .highlight .py { color: #d0d0d0 } /* Name.Property */ -body:not([data-theme="light"]) .highlight .nt { color: #6ebf26; font-weight: bold } /* Name.Tag */ -body:not([data-theme="light"]) .highlight .nv { color: #40ffff } /* Name.Variable */ -body:not([data-theme="light"]) .highlight .ow { color: #6ebf26; font-weight: bold } /* Operator.Word */ -body:not([data-theme="light"]) .highlight .pm { color: #d0d0d0 } /* Punctuation.Marker */ -body:not([data-theme="light"]) .highlight .w { color: #666666 } /* Text.Whitespace */ -body:not([data-theme="light"]) .highlight .mb { color: #51b2fd } /* Literal.Number.Bin */ -body:not([data-theme="light"]) .highlight .mf { color: #51b2fd } /* Literal.Number.Float */ -body:not([data-theme="light"]) .highlight .mh { color: #51b2fd } /* Literal.Number.Hex */ -body:not([data-theme="light"]) .highlight .mi { color: #51b2fd } /* Literal.Number.Integer */ -body:not([data-theme="light"]) .highlight .mo { color: #51b2fd } /* Literal.Number.Oct */ -body:not([data-theme="light"]) .highlight .sa { color: #ed9d13 } /* Literal.String.Affix */ -body:not([data-theme="light"]) .highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */ -body:not([data-theme="light"]) .highlight .sc { color: #ed9d13 } /* Literal.String.Char */ -body:not([data-theme="light"]) .highlight .dl { color: #ed9d13 } /* Literal.String.Delimiter */ -body:not([data-theme="light"]) .highlight .sd { color: #ed9d13 } /* Literal.String.Doc */ -body:not([data-theme="light"]) .highlight .s2 { color: #ed9d13 } /* Literal.String.Double */ -body:not([data-theme="light"]) .highlight .se { color: #ed9d13 } /* Literal.String.Escape */ -body:not([data-theme="light"]) .highlight .sh { color: #ed9d13 } /* Literal.String.Heredoc */ -body:not([data-theme="light"]) .highlight .si { color: #ed9d13 } /* Literal.String.Interpol */ -body:not([data-theme="light"]) .highlight .sx { color: #ffa500 } /* Literal.String.Other */ -body:not([data-theme="light"]) .highlight .sr { color: #ed9d13 } /* Literal.String.Regex */ -body:not([data-theme="light"]) .highlight .s1 { color: #ed9d13 } /* Literal.String.Single */ -body:not([data-theme="light"]) .highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */ -body:not([data-theme="light"]) .highlight .bp { color: #2fbccd } /* Name.Builtin.Pseudo */ -body:not([data-theme="light"]) .highlight .fm { color: #71adff } /* Name.Function.Magic */ -body:not([data-theme="light"]) .highlight .vc { color: #40ffff } /* Name.Variable.Class */ -body:not([data-theme="light"]) .highlight .vg { color: #40ffff } /* Name.Variable.Global */ -body:not([data-theme="light"]) .highlight .vi { color: #40ffff } /* Name.Variable.Instance */ -body:not([data-theme="light"]) .highlight .vm { color: #40ffff } /* Name.Variable.Magic */ -body:not([data-theme="light"]) .highlight .il { color: #51b2fd } /* Literal.Number.Integer.Long */ -} -} \ No newline at end of file diff --git a/docs/_build/html/_static/scripts/furo-extensions.js b/docs/_build/html/_static/scripts/furo-extensions.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/_build/html/_static/scripts/furo.js b/docs/_build/html/_static/scripts/furo.js deleted file mode 100644 index 32e7c05be..000000000 --- a/docs/_build/html/_static/scripts/furo.js +++ /dev/null @@ -1,3 +0,0 @@ -/*! For license information please see furo.js.LICENSE.txt */ -(()=>{var t={212:function(t,e,n){var o,r;r=void 0!==n.g?n.g:"undefined"!=typeof window?window:this,o=function(){return function(t){"use strict";var e={navClass:"active",contentClass:"active",nested:!1,nestedClass:"active",offset:0,reflow:!1,events:!0},n=function(t,e,n){if(n.settings.events){var o=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:n});e.dispatchEvent(o)}},o=function(t){var e=0;if(t.offsetParent)for(;t;)e+=t.offsetTop,t=t.offsetParent;return e>=0?e:0},r=function(t){t&&t.sort((function(t,e){return o(t.content)=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight,document.body.clientHeight,document.documentElement.clientHeight)},l=function(t,e){var n=t[t.length-1];if(function(t,e){return!(!s()||!c(t.content,e,!0))}(n,e))return n;for(var o=t.length-1;o>=0;o--)if(c(t[o].content,e))return t[o]},a=function(t,e){if(e.nested&&t.parentNode){var n=t.parentNode.closest("li");n&&(n.classList.remove(e.nestedClass),a(n,e))}},i=function(t,e){if(t){var o=t.nav.closest("li");o&&(o.classList.remove(e.navClass),t.content.classList.remove(e.contentClass),a(o,e),n("gumshoeDeactivate",o,{link:t.nav,content:t.content,settings:e}))}},u=function(t,e){if(e.nested){var n=t.parentNode.closest("li");n&&(n.classList.add(e.nestedClass),u(n,e))}};return function(o,c){var s,a,d,f,m,v={setup:function(){s=document.querySelectorAll(o),a=[],Array.prototype.forEach.call(s,(function(t){var e=document.getElementById(decodeURIComponent(t.hash.substr(1)));e&&a.push({nav:t,content:e})})),r(a)},detect:function(){var t=l(a,m);t?d&&t.content===d.content||(i(d,m),function(t,e){if(t){var o=t.nav.closest("li");o&&(o.classList.add(e.navClass),t.content.classList.add(e.contentClass),u(o,e),n("gumshoeActivate",o,{link:t.nav,content:t.content,settings:e}))}}(t,m),d=t):d&&(i(d,m),d=null)}},h=function(e){f&&t.cancelAnimationFrame(f),f=t.requestAnimationFrame(v.detect)},g=function(e){f&&t.cancelAnimationFrame(f),f=t.requestAnimationFrame((function(){r(a),v.detect()}))};return v.destroy=function(){d&&i(d,m),t.removeEventListener("scroll",h,!1),m.reflow&&t.removeEventListener("resize",g,!1),a=null,s=null,d=null,f=null,m=null},m=function(){var t={};return Array.prototype.forEach.call(arguments,(function(e){for(var n in e){if(!e.hasOwnProperty(n))return;t[n]=e[n]}})),t}(e,c||{}),v.setup(),v.detect(),t.addEventListener("scroll",h,!1),m.reflow&&t.addEventListener("resize",g,!1),v}}(r)}.apply(e,[]),void 0===o||(t.exports=o)}},e={};function n(o){var r=e[o];if(void 0!==r)return r.exports;var c=e[o]={exports:{}};return t[o].call(c.exports,c,c.exports,n),c.exports}n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var o in e)n.o(e,o)&&!n.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:e[o]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),(()=>{"use strict";var t=n(212),e=n.n(t),o=null,r=null,c=window.pageYOffset||document.documentElement.scrollTop;const s=64;function l(){const t=localStorage.getItem("theme")||"auto";var e;"light"!==(e=window.matchMedia("(prefers-color-scheme: dark)").matches?"auto"===t?"light":"light"==t?"dark":"auto":"auto"===t?"dark":"dark"==t?"light":"auto")&&"dark"!==e&&"auto"!==e&&(console.error(`Got invalid theme mode: ${e}. Resetting to auto.`),e="auto"),document.body.dataset.theme=e,localStorage.setItem("theme",e),console.log(`Changed to ${e} mode.`)}function a(){!function(){const t=document.getElementsByClassName("theme-toggle");Array.from(t).forEach((t=>{t.addEventListener("click",l)}))}(),function(){let t=0,e=!1;window.addEventListener("scroll",(function(n){t=window.scrollY,e||(window.requestAnimationFrame((function(){var n;n=t,0==Math.floor(r.getBoundingClientRect().top)?r.classList.add("scrolled"):r.classList.remove("scrolled"),function(t){tc&&document.documentElement.classList.remove("show-back-to-top"),c=t}(n),function(t){null!==o&&(0==t?o.scrollTo(0,0):Math.ceil(t)>=Math.floor(document.documentElement.scrollHeight-window.innerHeight)?o.scrollTo(0,o.scrollHeight):document.querySelector(".scroll-current"))}(n),e=!1})),e=!0)})),window.scroll()}(),null!==o&&new(e())(".toc-tree a",{reflow:!0,recursive:!0,navClass:"scroll-current",offset:()=>{let t=parseFloat(getComputedStyle(document.documentElement).fontSize);return r.getBoundingClientRect().height+.5*t+1}})}document.addEventListener("DOMContentLoaded",(function(){document.body.parentNode.classList.remove("no-js"),r=document.querySelector("header"),o=document.querySelector(".toc-scroll"),a()}))})()})(); -//# sourceMappingURL=furo.js.map \ No newline at end of file diff --git a/docs/_build/html/_static/scripts/furo.js.LICENSE.txt b/docs/_build/html/_static/scripts/furo.js.LICENSE.txt deleted file mode 100644 index 1632189c7..000000000 --- a/docs/_build/html/_static/scripts/furo.js.LICENSE.txt +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * gumshoejs v5.1.2 (patched by @pradyunsg) - * A simple, framework-agnostic scrollspy script. - * (c) 2019 Chris Ferdinandi - * MIT License - * http://github.com/cferdinandi/gumshoe - */ diff --git a/docs/_build/html/_static/scripts/furo.js.map b/docs/_build/html/_static/scripts/furo.js.map deleted file mode 100644 index 7b7ddb113..000000000 --- a/docs/_build/html/_static/scripts/furo.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"scripts/furo.js","mappings":";iCAAA,MAQWA,SAWS,IAAX,EAAAC,EACH,EAAAA,EACkB,oBAAXC,OACPA,OACAC,KAbS,EAAF,WACP,OAaJ,SAAUD,GACR,aAMA,IAAIE,EAAW,CAEbC,SAAU,SACVC,aAAc,SAGdC,QAAQ,EACRC,YAAa,SAGbC,OAAQ,EACRC,QAAQ,EAGRC,QAAQ,GA6BNC,EAAY,SAAUC,EAAMC,EAAMC,GAEpC,GAAKA,EAAOC,SAASL,OAArB,CAGA,IAAIM,EAAQ,IAAIC,YAAYL,EAAM,CAChCM,SAAS,EACTC,YAAY,EACZL,OAAQA,IAIVD,EAAKO,cAAcJ,EAVgB,CAWrC,EAOIK,EAAe,SAAUR,GAC3B,IAAIS,EAAW,EACf,GAAIT,EAAKU,aACP,KAAOV,GACLS,GAAYT,EAAKW,UACjBX,EAAOA,EAAKU,aAGhB,OAAOD,GAAY,EAAIA,EAAW,CACpC,EAMIG,EAAe,SAAUC,GACvBA,GACFA,EAASC,MAAK,SAAUC,EAAOC,GAG7B,OAFcR,EAAaO,EAAME,SACnBT,EAAaQ,EAAMC,UACF,EACxB,CACT,GAEJ,EAwCIC,EAAW,SAAUlB,EAAME,EAAUiB,GACvC,IAAIC,EAASpB,EAAKqB,wBACd1B,EAnCU,SAAUO,GAExB,MAA+B,mBAApBA,EAASP,OACX2B,WAAWpB,EAASP,UAItB2B,WAAWpB,EAASP,OAC7B,CA2Be4B,CAAUrB,GACvB,OAAIiB,EAEAK,SAASJ,EAAOD,OAAQ,KACvB/B,EAAOqC,aAAeC,SAASC,gBAAgBC,cAG7CJ,SAASJ,EAAOS,IAAK,KAAOlC,CACrC,EAMImC,EAAa,WACf,OACEC,KAAKC,KAAK5C,EAAOqC,YAAcrC,EAAO6C,cAnCjCF,KAAKG,IACVR,SAASS,KAAKC,aACdV,SAASC,gBAAgBS,aACzBV,SAASS,KAAKE,aACdX,SAASC,gBAAgBU,aACzBX,SAASS,KAAKP,aACdF,SAASC,gBAAgBC,aAkC7B,EAmBIU,EAAY,SAAUzB,EAAUX,GAClC,IAAIqC,EAAO1B,EAASA,EAAS2B,OAAS,GACtC,GAbgB,SAAUC,EAAMvC,GAChC,SAAI4B,MAAgBZ,EAASuB,EAAKxB,QAASf,GAAU,GAEvD,CAUMwC,CAAYH,EAAMrC,GAAW,OAAOqC,EACxC,IAAK,IAAII,EAAI9B,EAAS2B,OAAS,EAAGG,GAAK,EAAGA,IACxC,GAAIzB,EAASL,EAAS8B,GAAG1B,QAASf,GAAW,OAAOW,EAAS8B,EAEjE,EAOIC,EAAmB,SAAUC,EAAK3C,GAEpC,GAAKA,EAAST,QAAWoD,EAAIC,WAA7B,CAGA,IAAIC,EAAKF,EAAIC,WAAWE,QAAQ,MAC3BD,IAGLA,EAAGE,UAAUC,OAAOhD,EAASR,aAG7BkD,EAAiBG,EAAI7C,GAV0B,CAWjD,EAOIiD,EAAa,SAAUC,EAAOlD,GAEhC,GAAKkD,EAAL,CAGA,IAAIL,EAAKK,EAAMP,IAAIG,QAAQ,MACtBD,IAGLA,EAAGE,UAAUC,OAAOhD,EAASX,UAC7B6D,EAAMnC,QAAQgC,UAAUC,OAAOhD,EAASV,cAGxCoD,EAAiBG,EAAI7C,GAGrBJ,EAAU,oBAAqBiD,EAAI,CACjCM,KAAMD,EAAMP,IACZ5B,QAASmC,EAAMnC,QACff,SAAUA,IAjBM,CAmBpB,EAOIoD,EAAiB,SAAUT,EAAK3C,GAElC,GAAKA,EAAST,OAAd,CAGA,IAAIsD,EAAKF,EAAIC,WAAWE,QAAQ,MAC3BD,IAGLA,EAAGE,UAAUM,IAAIrD,EAASR,aAG1B4D,EAAeP,EAAI7C,GAVS,CAW9B,EA6LA,OA1JkB,SAAUsD,EAAUC,GAKpC,IACIC,EAAU7C,EAAU8C,EAASC,EAAS1D,EADtC2D,EAAa,CAUjBA,MAAmB,WAEjBH,EAAWhC,SAASoC,iBAAiBN,GAGrC3C,EAAW,GAGXkD,MAAMC,UAAUC,QAAQC,KAAKR,GAAU,SAAUjB,GAE/C,IAAIxB,EAAUS,SAASyC,eACrBC,mBAAmB3B,EAAK4B,KAAKC,OAAO,KAEjCrD,GAGLJ,EAAS0D,KAAK,CACZ1B,IAAKJ,EACLxB,QAASA,GAEb,IAGAL,EAAaC,EACf,EAKAgD,OAAoB,WAElB,IAAIW,EAASlC,EAAUzB,EAAUX,GAG5BsE,EASDb,GAAWa,EAAOvD,UAAY0C,EAAQ1C,UAG1CkC,EAAWQ,EAASzD,GAzFT,SAAUkD,EAAOlD,GAE9B,GAAKkD,EAAL,CAGA,IAAIL,EAAKK,EAAMP,IAAIG,QAAQ,MACtBD,IAGLA,EAAGE,UAAUM,IAAIrD,EAASX,UAC1B6D,EAAMnC,QAAQgC,UAAUM,IAAIrD,EAASV,cAGrC8D,EAAeP,EAAI7C,GAGnBJ,EAAU,kBAAmBiD,EAAI,CAC/BM,KAAMD,EAAMP,IACZ5B,QAASmC,EAAMnC,QACff,SAAUA,IAjBM,CAmBpB,CAqEIuE,CAASD,EAAQtE,GAGjByD,EAAUa,GAfJb,IACFR,EAAWQ,EAASzD,GACpByD,EAAU,KAchB,GAMIe,EAAgB,SAAUvE,GAExByD,GACFxE,EAAOuF,qBAAqBf,GAI9BA,EAAUxE,EAAOwF,sBAAsBf,EAAWgB,OACpD,EAMIC,EAAgB,SAAU3E,GAExByD,GACFxE,EAAOuF,qBAAqBf,GAI9BA,EAAUxE,EAAOwF,uBAAsB,WACrChE,EAAaC,GACbgD,EAAWgB,QACb,GACF,EAkDA,OA7CAhB,EAAWkB,QAAU,WAEfpB,GACFR,EAAWQ,EAASzD,GAItBd,EAAO4F,oBAAoB,SAAUN,GAAe,GAChDxE,EAASN,QACXR,EAAO4F,oBAAoB,SAAUF,GAAe,GAItDjE,EAAW,KACX6C,EAAW,KACXC,EAAU,KACVC,EAAU,KACV1D,EAAW,IACb,EAOEA,EA3XS,WACX,IAAI+E,EAAS,CAAC,EAOd,OANAlB,MAAMC,UAAUC,QAAQC,KAAKgB,WAAW,SAAUC,GAChD,IAAK,IAAIC,KAAOD,EAAK,CACnB,IAAKA,EAAIE,eAAeD,GAAM,OAC9BH,EAAOG,GAAOD,EAAIC,EACpB,CACF,IACOH,CACT,CAkXeK,CAAOhG,EAAUmE,GAAW,CAAC,GAGxCI,EAAW0B,QAGX1B,EAAWgB,SAGXzF,EAAOoG,iBAAiB,SAAUd,GAAe,GAC7CxE,EAASN,QACXR,EAAOoG,iBAAiB,SAAUV,GAAe,GAS9CjB,CACT,CAOF,CArcW4B,CAAQvG,EAChB,UAFM,SAEN,uBCXDwG,EAA2B,CAAC,EAGhC,SAASC,EAAoBC,GAE5B,IAAIC,EAAeH,EAAyBE,GAC5C,QAAqBE,IAAjBD,EACH,OAAOA,EAAaE,QAGrB,IAAIC,EAASN,EAAyBE,GAAY,CAGjDG,QAAS,CAAC,GAOX,OAHAE,EAAoBL,GAAU1B,KAAK8B,EAAOD,QAASC,EAAQA,EAAOD,QAASJ,GAGpEK,EAAOD,OACf,CCrBAJ,EAAoBO,EAAKF,IACxB,IAAIG,EAASH,GAAUA,EAAOI,WAC7B,IAAOJ,EAAiB,QACxB,IAAM,EAEP,OADAL,EAAoBU,EAAEF,EAAQ,CAAEG,EAAGH,IAC5BA,CAAM,ECLdR,EAAoBU,EAAI,CAACN,EAASQ,KACjC,IAAI,IAAInB,KAAOmB,EACXZ,EAAoBa,EAAED,EAAYnB,KAASO,EAAoBa,EAAET,EAASX,IAC5EqB,OAAOC,eAAeX,EAASX,EAAK,CAAEuB,YAAY,EAAMC,IAAKL,EAAWnB,IAE1E,ECNDO,EAAoBxG,EAAI,WACvB,GAA0B,iBAAf0H,WAAyB,OAAOA,WAC3C,IACC,OAAOxH,MAAQ,IAAIyH,SAAS,cAAb,EAChB,CAAE,MAAOC,GACR,GAAsB,iBAAX3H,OAAqB,OAAOA,MACxC,CACA,CAPuB,GCAxBuG,EAAoBa,EAAI,CAACrB,EAAK6B,IAAUP,OAAOzC,UAAUqB,eAAenB,KAAKiB,EAAK6B,4CCK9EC,EAAY,KACZC,EAAS,KACTC,EAAgB/H,OAAO6C,aAAeP,SAASC,gBAAgByF,UACnE,MAAMC,EAAmB,GA2EzB,SAASC,IACP,MAAMC,EAAeC,aAAaC,QAAQ,UAAY,OAZxD,IAAkBC,EACH,WADGA,EAaItI,OAAOuI,WAAW,gCAAgCC,QAI/C,SAAjBL,EACO,QACgB,SAAhBA,EACA,OAEA,OAIU,SAAjBA,EACO,OACgB,QAAhBA,EACA,QAEA,SA9BoB,SAATG,GAA4B,SAATA,IACzCG,QAAQC,MAAM,2BAA2BJ,yBACzCA,EAAO,QAGThG,SAASS,KAAK4F,QAAQC,MAAQN,EAC9BF,aAAaS,QAAQ,QAASP,GAC9BG,QAAQK,IAAI,cAAcR,UA0B5B,CAkDA,SAASnC,KART,WAEE,MAAM4C,EAAUzG,SAAS0G,uBAAuB,gBAChDrE,MAAMsE,KAAKF,GAASlE,SAASqE,IAC3BA,EAAI9C,iBAAiB,QAAS8B,EAAe,GAEjD,CAGEiB,GA9CF,WAEE,IAAIC,EAA6B,EAC7BC,GAAU,EAEdrJ,OAAOoG,iBAAiB,UAAU,SAAUuB,GAC1CyB,EAA6BpJ,OAAOsJ,QAE/BD,IACHrJ,OAAOwF,uBAAsB,WAzDnC,IAAuB+D,IA0DDH,EA9GkC,GAAlDzG,KAAK6G,MAAM1B,EAAO7F,wBAAwBQ,KAC5CqF,EAAOjE,UAAUM,IAAI,YAErB2D,EAAOjE,UAAUC,OAAO,YAI5B,SAAmCyF,GAC7BA,EAAYtB,EACd3F,SAASC,gBAAgBsB,UAAUC,OAAO,oBAEtCyF,EAAYxB,EACdzF,SAASC,gBAAgBsB,UAAUM,IAAI,oBAC9BoF,EAAYxB,GACrBzF,SAASC,gBAAgBsB,UAAUC,OAAO,oBAG9CiE,EAAgBwB,CAClB,CAoCEE,CAA0BF,GAlC5B,SAA6BA,GACT,OAAd1B,IAKa,GAAb0B,EACF1B,EAAU6B,SAAS,EAAG,GAGtB/G,KAAKC,KAAK2G,IACV5G,KAAK6G,MAAMlH,SAASC,gBAAgBS,aAAehD,OAAOqC,aAE1DwF,EAAU6B,SAAS,EAAG7B,EAAU7E,cAGhBV,SAASqH,cAAc,mBAc3C,CAKEC,CAAoBL,GAwDdF,GAAU,CACZ,IAEAA,GAAU,EAEd,IACArJ,OAAO6J,QACT,CA6BEC,GA1BkB,OAAdjC,GAKJ,IAAI,IAAJ,CAAY,cAAe,CACzBrH,QAAQ,EACRuJ,WAAW,EACX5J,SAAU,iBACVI,OAAQ,KACN,IAAIyJ,EAAM9H,WAAW+H,iBAAiB3H,SAASC,iBAAiB2H,UAChE,OAAOpC,EAAO7F,wBAAwBkI,OAAS,GAAMH,EAAM,CAAC,GAiBlE,CAcA1H,SAAS8D,iBAAiB,oBAT1B,WACE9D,SAASS,KAAKW,WAAWG,UAAUC,OAAO,SAE1CgE,EAASxF,SAASqH,cAAc,UAChC9B,EAAYvF,SAASqH,cAAc,eAEnCxD,GACF","sources":["webpack:///./src/furo/assets/scripts/gumshoe-patched.js","webpack:///webpack/bootstrap","webpack:///webpack/runtime/compat get default export","webpack:///webpack/runtime/define property getters","webpack:///webpack/runtime/global","webpack:///webpack/runtime/hasOwnProperty shorthand","webpack:///./src/furo/assets/scripts/furo.js"],"sourcesContent":["/*!\n * gumshoejs v5.1.2 (patched by @pradyunsg)\n * A simple, framework-agnostic scrollspy script.\n * (c) 2019 Chris Ferdinandi\n * MIT License\n * http://github.com/cferdinandi/gumshoe\n */\n\n(function (root, factory) {\n if (typeof define === \"function\" && define.amd) {\n define([], function () {\n return factory(root);\n });\n } else if (typeof exports === \"object\") {\n module.exports = factory(root);\n } else {\n root.Gumshoe = factory(root);\n }\n})(\n typeof global !== \"undefined\"\n ? global\n : typeof window !== \"undefined\"\n ? window\n : this,\n function (window) {\n \"use strict\";\n\n //\n // Defaults\n //\n\n var defaults = {\n // Active classes\n navClass: \"active\",\n contentClass: \"active\",\n\n // Nested navigation\n nested: false,\n nestedClass: \"active\",\n\n // Offset & reflow\n offset: 0,\n reflow: false,\n\n // Event support\n events: true,\n };\n\n //\n // Methods\n //\n\n /**\n * Merge two or more objects together.\n * @param {Object} objects The objects to merge together\n * @returns {Object} Merged values of defaults and options\n */\n var extend = function () {\n var merged = {};\n Array.prototype.forEach.call(arguments, function (obj) {\n for (var key in obj) {\n if (!obj.hasOwnProperty(key)) return;\n merged[key] = obj[key];\n }\n });\n return merged;\n };\n\n /**\n * Emit a custom event\n * @param {String} type The event type\n * @param {Node} elem The element to attach the event to\n * @param {Object} detail Any details to pass along with the event\n */\n var emitEvent = function (type, elem, detail) {\n // Make sure events are enabled\n if (!detail.settings.events) return;\n\n // Create a new event\n var event = new CustomEvent(type, {\n bubbles: true,\n cancelable: true,\n detail: detail,\n });\n\n // Dispatch the event\n elem.dispatchEvent(event);\n };\n\n /**\n * Get an element's distance from the top of the Document.\n * @param {Node} elem The element\n * @return {Number} Distance from the top in pixels\n */\n var getOffsetTop = function (elem) {\n var location = 0;\n if (elem.offsetParent) {\n while (elem) {\n location += elem.offsetTop;\n elem = elem.offsetParent;\n }\n }\n return location >= 0 ? location : 0;\n };\n\n /**\n * Sort content from first to last in the DOM\n * @param {Array} contents The content areas\n */\n var sortContents = function (contents) {\n if (contents) {\n contents.sort(function (item1, item2) {\n var offset1 = getOffsetTop(item1.content);\n var offset2 = getOffsetTop(item2.content);\n if (offset1 < offset2) return -1;\n return 1;\n });\n }\n };\n\n /**\n * Get the offset to use for calculating position\n * @param {Object} settings The settings for this instantiation\n * @return {Float} The number of pixels to offset the calculations\n */\n var getOffset = function (settings) {\n // if the offset is a function run it\n if (typeof settings.offset === \"function\") {\n return parseFloat(settings.offset());\n }\n\n // Otherwise, return it as-is\n return parseFloat(settings.offset);\n };\n\n /**\n * Get the document element's height\n * @private\n * @returns {Number}\n */\n var getDocumentHeight = function () {\n return Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight,\n document.body.offsetHeight,\n document.documentElement.offsetHeight,\n document.body.clientHeight,\n document.documentElement.clientHeight,\n );\n };\n\n /**\n * Determine if an element is in view\n * @param {Node} elem The element\n * @param {Object} settings The settings for this instantiation\n * @param {Boolean} bottom If true, check if element is above bottom of viewport instead\n * @return {Boolean} Returns true if element is in the viewport\n */\n var isInView = function (elem, settings, bottom) {\n var bounds = elem.getBoundingClientRect();\n var offset = getOffset(settings);\n if (bottom) {\n return (\n parseInt(bounds.bottom, 10) <\n (window.innerHeight || document.documentElement.clientHeight)\n );\n }\n return parseInt(bounds.top, 10) <= offset;\n };\n\n /**\n * Check if at the bottom of the viewport\n * @return {Boolean} If true, page is at the bottom of the viewport\n */\n var isAtBottom = function () {\n if (\n Math.ceil(window.innerHeight + window.pageYOffset) >=\n getDocumentHeight()\n )\n return true;\n return false;\n };\n\n /**\n * Check if the last item should be used (even if not at the top of the page)\n * @param {Object} item The last item\n * @param {Object} settings The settings for this instantiation\n * @return {Boolean} If true, use the last item\n */\n var useLastItem = function (item, settings) {\n if (isAtBottom() && isInView(item.content, settings, true)) return true;\n return false;\n };\n\n /**\n * Get the active content\n * @param {Array} contents The content areas\n * @param {Object} settings The settings for this instantiation\n * @return {Object} The content area and matching navigation link\n */\n var getActive = function (contents, settings) {\n var last = contents[contents.length - 1];\n if (useLastItem(last, settings)) return last;\n for (var i = contents.length - 1; i >= 0; i--) {\n if (isInView(contents[i].content, settings)) return contents[i];\n }\n };\n\n /**\n * Deactivate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var deactivateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested || !nav.parentNode) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Remove the active class\n li.classList.remove(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n deactivateNested(li, settings);\n };\n\n /**\n * Deactivate a nav and content area\n * @param {Object} items The nav item and content to deactivate\n * @param {Object} settings The settings for this instantiation\n */\n var deactivate = function (items, settings) {\n // Make sure there are items to deactivate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Remove the active class from the nav and content\n li.classList.remove(settings.navClass);\n items.content.classList.remove(settings.contentClass);\n\n // Deactivate any parent navs in a nested navigation\n deactivateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeDeactivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Activate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var activateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Add the active class\n li.classList.add(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n activateNested(li, settings);\n };\n\n /**\n * Activate a nav and content area\n * @param {Object} items The nav item and content to activate\n * @param {Object} settings The settings for this instantiation\n */\n var activate = function (items, settings) {\n // Make sure there are items to activate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Add the active class to the nav and content\n li.classList.add(settings.navClass);\n items.content.classList.add(settings.contentClass);\n\n // Activate any parent navs in a nested navigation\n activateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeActivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Create the Constructor object\n * @param {String} selector The selector to use for navigation items\n * @param {Object} options User options and settings\n */\n var Constructor = function (selector, options) {\n //\n // Variables\n //\n\n var publicAPIs = {};\n var navItems, contents, current, timeout, settings;\n\n //\n // Methods\n //\n\n /**\n * Set variables from DOM elements\n */\n publicAPIs.setup = function () {\n // Get all nav items\n navItems = document.querySelectorAll(selector);\n\n // Create contents array\n contents = [];\n\n // Loop through each item, get it's matching content, and push to the array\n Array.prototype.forEach.call(navItems, function (item) {\n // Get the content for the nav item\n var content = document.getElementById(\n decodeURIComponent(item.hash.substr(1)),\n );\n if (!content) return;\n\n // Push to the contents array\n contents.push({\n nav: item,\n content: content,\n });\n });\n\n // Sort contents by the order they appear in the DOM\n sortContents(contents);\n };\n\n /**\n * Detect which content is currently active\n */\n publicAPIs.detect = function () {\n // Get the active content\n var active = getActive(contents, settings);\n\n // if there's no active content, deactivate and bail\n if (!active) {\n if (current) {\n deactivate(current, settings);\n current = null;\n }\n return;\n }\n\n // If the active content is the one currently active, do nothing\n if (current && active.content === current.content) return;\n\n // Deactivate the current content and activate the new content\n deactivate(current, settings);\n activate(active, settings);\n\n // Update the currently active content\n current = active;\n };\n\n /**\n * Detect the active content on scroll\n * Debounced for performance\n */\n var scrollHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(publicAPIs.detect);\n };\n\n /**\n * Update content sorting on resize\n * Debounced for performance\n */\n var resizeHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(function () {\n sortContents(contents);\n publicAPIs.detect();\n });\n };\n\n /**\n * Destroy the current instantiation\n */\n publicAPIs.destroy = function () {\n // Undo DOM changes\n if (current) {\n deactivate(current, settings);\n }\n\n // Remove event listeners\n window.removeEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.removeEventListener(\"resize\", resizeHandler, false);\n }\n\n // Reset variables\n contents = null;\n navItems = null;\n current = null;\n timeout = null;\n settings = null;\n };\n\n /**\n * Initialize the current instantiation\n */\n var init = function () {\n // Merge user options into defaults\n settings = extend(defaults, options || {});\n\n // Setup variables based on the current DOM\n publicAPIs.setup();\n\n // Find the currently active content\n publicAPIs.detect();\n\n // Setup event listeners\n window.addEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.addEventListener(\"resize\", resizeHandler, false);\n }\n };\n\n //\n // Initialize and return the public APIs\n //\n\n init();\n return publicAPIs;\n };\n\n //\n // Return the Constructor\n //\n\n return Constructor;\n },\n);\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","import Gumshoe from \"./gumshoe-patched.js\";\n\n////////////////////////////////////////////////////////////////////////////////\n// Scroll Handling\n////////////////////////////////////////////////////////////////////////////////\nvar tocScroll = null;\nvar header = null;\nvar lastScrollTop = window.pageYOffset || document.documentElement.scrollTop;\nconst GO_TO_TOP_OFFSET = 64;\n\nfunction scrollHandlerForHeader() {\n if (Math.floor(header.getBoundingClientRect().top) == 0) {\n header.classList.add(\"scrolled\");\n } else {\n header.classList.remove(\"scrolled\");\n }\n}\n\nfunction scrollHandlerForBackToTop(positionY) {\n if (positionY < GO_TO_TOP_OFFSET) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n } else {\n if (positionY < lastScrollTop) {\n document.documentElement.classList.add(\"show-back-to-top\");\n } else if (positionY > lastScrollTop) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n }\n }\n lastScrollTop = positionY;\n}\n\nfunction scrollHandlerForTOC(positionY) {\n if (tocScroll === null) {\n return;\n }\n\n // top of page.\n if (positionY == 0) {\n tocScroll.scrollTo(0, 0);\n } else if (\n // bottom of page.\n Math.ceil(positionY) >=\n Math.floor(document.documentElement.scrollHeight - window.innerHeight)\n ) {\n tocScroll.scrollTo(0, tocScroll.scrollHeight);\n } else {\n // somewhere in the middle.\n const current = document.querySelector(\".scroll-current\");\n if (current == null) {\n return;\n }\n\n // https://github.com/pypa/pip/issues/9159 This breaks scroll behaviours.\n // // scroll the currently \"active\" heading in toc, into view.\n // const rect = current.getBoundingClientRect();\n // if (0 > rect.top) {\n // current.scrollIntoView(true); // the argument is \"alignTop\"\n // } else if (rect.bottom > window.innerHeight) {\n // current.scrollIntoView(false);\n // }\n }\n}\n\nfunction scrollHandler(positionY) {\n scrollHandlerForHeader();\n scrollHandlerForBackToTop(positionY);\n scrollHandlerForTOC(positionY);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Theme Toggle\n////////////////////////////////////////////////////////////////////////////////\nfunction setTheme(mode) {\n if (mode !== \"light\" && mode !== \"dark\" && mode !== \"auto\") {\n console.error(`Got invalid theme mode: ${mode}. Resetting to auto.`);\n mode = \"auto\";\n }\n\n document.body.dataset.theme = mode;\n localStorage.setItem(\"theme\", mode);\n console.log(`Changed to ${mode} mode.`);\n}\n\nfunction cycleThemeOnce() {\n const currentTheme = localStorage.getItem(\"theme\") || \"auto\";\n const prefersDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n\n if (prefersDark) {\n // Auto (dark) -> Light -> Dark\n if (currentTheme === \"auto\") {\n setTheme(\"light\");\n } else if (currentTheme == \"light\") {\n setTheme(\"dark\");\n } else {\n setTheme(\"auto\");\n }\n } else {\n // Auto (light) -> Dark -> Light\n if (currentTheme === \"auto\") {\n setTheme(\"dark\");\n } else if (currentTheme == \"dark\") {\n setTheme(\"light\");\n } else {\n setTheme(\"auto\");\n }\n }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Setup\n////////////////////////////////////////////////////////////////////////////////\nfunction setupScrollHandler() {\n // Taken from https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event\n let last_known_scroll_position = 0;\n let ticking = false;\n\n window.addEventListener(\"scroll\", function (e) {\n last_known_scroll_position = window.scrollY;\n\n if (!ticking) {\n window.requestAnimationFrame(function () {\n scrollHandler(last_known_scroll_position);\n ticking = false;\n });\n\n ticking = true;\n }\n });\n window.scroll();\n}\n\nfunction setupScrollSpy() {\n if (tocScroll === null) {\n return;\n }\n\n // Scrollspy -- highlight table on contents, based on scroll\n new Gumshoe(\".toc-tree a\", {\n reflow: true,\n recursive: true,\n navClass: \"scroll-current\",\n offset: () => {\n let rem = parseFloat(getComputedStyle(document.documentElement).fontSize);\n return header.getBoundingClientRect().height + 0.5 * rem + 1;\n },\n });\n}\n\nfunction setupTheme() {\n // Attach event handlers for toggling themes\n const buttons = document.getElementsByClassName(\"theme-toggle\");\n Array.from(buttons).forEach((btn) => {\n btn.addEventListener(\"click\", cycleThemeOnce);\n });\n}\n\nfunction setup() {\n setupTheme();\n setupScrollHandler();\n setupScrollSpy();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main entrypoint\n////////////////////////////////////////////////////////////////////////////////\nfunction main() {\n document.body.parentNode.classList.remove(\"no-js\");\n\n header = document.querySelector(\"header\");\n tocScroll = document.querySelector(\".toc-scroll\");\n\n setup();\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", main);\n"],"names":["root","g","window","this","defaults","navClass","contentClass","nested","nestedClass","offset","reflow","events","emitEvent","type","elem","detail","settings","event","CustomEvent","bubbles","cancelable","dispatchEvent","getOffsetTop","location","offsetParent","offsetTop","sortContents","contents","sort","item1","item2","content","isInView","bottom","bounds","getBoundingClientRect","parseFloat","getOffset","parseInt","innerHeight","document","documentElement","clientHeight","top","isAtBottom","Math","ceil","pageYOffset","max","body","scrollHeight","offsetHeight","getActive","last","length","item","useLastItem","i","deactivateNested","nav","parentNode","li","closest","classList","remove","deactivate","items","link","activateNested","add","selector","options","navItems","current","timeout","publicAPIs","querySelectorAll","Array","prototype","forEach","call","getElementById","decodeURIComponent","hash","substr","push","active","activate","scrollHandler","cancelAnimationFrame","requestAnimationFrame","detect","resizeHandler","destroy","removeEventListener","merged","arguments","obj","key","hasOwnProperty","extend","setup","addEventListener","factory","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","exports","module","__webpack_modules__","n","getter","__esModule","d","a","definition","o","Object","defineProperty","enumerable","get","globalThis","Function","e","prop","tocScroll","header","lastScrollTop","scrollTop","GO_TO_TOP_OFFSET","cycleThemeOnce","currentTheme","localStorage","getItem","mode","matchMedia","matches","console","error","dataset","theme","setItem","log","buttons","getElementsByClassName","from","btn","setupTheme","last_known_scroll_position","ticking","scrollY","positionY","floor","scrollHandlerForBackToTop","scrollTo","querySelector","scrollHandlerForTOC","scroll","setupScrollHandler","recursive","rem","getComputedStyle","fontSize","height"],"sourceRoot":""} \ No newline at end of file diff --git a/docs/_build/html/_static/searchtools.js b/docs/_build/html/_static/searchtools.js deleted file mode 100644 index 7918c3fab..000000000 --- a/docs/_build/html/_static/searchtools.js +++ /dev/null @@ -1,574 +0,0 @@ -/* - * searchtools.js - * ~~~~~~~~~~~~~~~~ - * - * Sphinx JavaScript utilities for the full-text search. - * - * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ -"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] = 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, - }; -} - -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] = item; - - let listItem = document.createElement("li"); - 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) - ); - // 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 = _( - `Search finished, found ${resultCount} page(s) matching the search query.` - ); -}; -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); -}; - -/** - * 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) => { - const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); - htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() }); - const docContent = htmlElement.querySelector('[role="main"]'); - if (docContent !== undefined) return docContent.textContent; - console.warn( - "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you 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.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); - }, - - /** - * execute search (requires search index to be loaded) - */ - query: (query) => { - 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; - - // 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]); - - // array of [docname, title, anchor, descr, score, filename] - let results = []; - _removeChildren(document.getElementById("search-progress")); - - const queryLower = query.toLowerCase(); - for (const [title, foundTitles] of Object.entries(allTitles)) { - if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) { - for (const [file, id] of foundTitles) { - let score = Math.round(100 * queryLower.length / title.length) - results.push([ - docNames[file], - titles[file] !== title ? `${titles[file]} > ${title}` : title, - id !== null ? "#" + id : "", - null, - score, - filenames[file], - ]); - } - } - } - - // 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] of foundEntries) { - let score = Math.round(100 * queryLower.length / entry.length) - results.push([ - docNames[file], - titles[file], - id ? "#" + id : "", - null, - score, - filenames[file], - ]); - } - } - } - - // lookup as object - objectTerms.forEach((term) => - results.push(...Search.performObjectSearch(term, objectTerms)) - ); - - // lookup as search terms in fulltext - results.push(...Search.performTermsSearch(searchTerms, excludedTerms)); - - // let the scorer override scores with a custom scoring function - if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item))); - - // now sort the results by score (in opposite order of appearance, since the - // display function below uses pop() to retrieve items) and then - // alphabetically - results.sort((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; - }); - - // 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; - }, []); - - results = results.reverse(); - - // 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]], - ]); - }; - 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); - Object.keys(terms).forEach((term) => { - if (term.match(escapedWord) && !terms[word]) - arr.push({ files: terms[term], score: Scorer.partialTerm }); - }); - Object.keys(titleTerms).forEach((term) => { - if (term.match(escapedWord) && !titleTerms[word]) - arr.push({ files: titleTerms[word], 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.get(file).indexOf(word) === -1) - fileMap.get(file).push(word); - else fileMap.set(file, [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], - ]); - } - 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) => { - const text = Search.htmlToText(htmlText); - 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/docs/_build/html/_static/skeleton.css b/docs/_build/html/_static/skeleton.css deleted file mode 100644 index 467c878c6..000000000 --- a/docs/_build/html/_static/skeleton.css +++ /dev/null @@ -1,296 +0,0 @@ -/* Some sane resets. */ -html { - height: 100%; -} - -body { - margin: 0; - min-height: 100%; -} - -/* All the flexbox magic! */ -body, -.sb-announcement, -.sb-content, -.sb-main, -.sb-container, -.sb-container__inner, -.sb-article-container, -.sb-footer-content, -.sb-header, -.sb-header-secondary, -.sb-footer { - display: flex; -} - -/* These order things vertically */ -body, -.sb-main, -.sb-article-container { - flex-direction: column; -} - -/* Put elements in the center */ -.sb-header, -.sb-header-secondary, -.sb-container, -.sb-content, -.sb-footer, -.sb-footer-content { - justify-content: center; -} -/* Put elements at the ends */ -.sb-article-container { - justify-content: space-between; -} - -/* These elements grow. */ -.sb-main, -.sb-content, -.sb-container, -article { - flex-grow: 1; -} - -/* Because padding making this wider is not fun */ -article { - box-sizing: border-box; -} - -/* The announcements element should never be wider than the page. */ -.sb-announcement { - max-width: 100%; -} - -.sb-sidebar-primary, -.sb-sidebar-secondary { - flex-shrink: 0; - width: 17rem; -} - -.sb-announcement__inner { - justify-content: center; - - box-sizing: border-box; - height: 3rem; - - overflow-x: auto; - white-space: nowrap; -} - -/* Sidebars, with checkbox-based toggle */ -.sb-sidebar-primary, -.sb-sidebar-secondary { - position: fixed; - height: 100%; - top: 0; -} - -.sb-sidebar-primary { - left: -17rem; - transition: left 250ms ease-in-out; -} -.sb-sidebar-secondary { - right: -17rem; - transition: right 250ms ease-in-out; -} - -.sb-sidebar-toggle { - display: none; -} -.sb-sidebar-overlay { - position: fixed; - top: 0; - width: 0; - height: 0; - - transition: width 0ms ease 250ms, height 0ms ease 250ms, opacity 250ms ease; - - opacity: 0; - background-color: rgba(0, 0, 0, 0.54); -} - -#sb-sidebar-toggle--primary:checked - ~ .sb-sidebar-overlay[for="sb-sidebar-toggle--primary"], -#sb-sidebar-toggle--secondary:checked - ~ .sb-sidebar-overlay[for="sb-sidebar-toggle--secondary"] { - width: 100%; - height: 100%; - opacity: 1; - transition: width 0ms ease, height 0ms ease, opacity 250ms ease; -} - -#sb-sidebar-toggle--primary:checked ~ .sb-container .sb-sidebar-primary { - left: 0; -} -#sb-sidebar-toggle--secondary:checked ~ .sb-container .sb-sidebar-secondary { - right: 0; -} - -/* Full-width mode */ -.drop-secondary-sidebar-for-full-width-content - .hide-when-secondary-sidebar-shown { - display: none !important; -} -.drop-secondary-sidebar-for-full-width-content .sb-sidebar-secondary { - display: none !important; -} - -/* Mobile views */ -.sb-page-width { - width: 100%; -} - -.sb-article-container, -.sb-footer-content__inner, -.drop-secondary-sidebar-for-full-width-content .sb-article, -.drop-secondary-sidebar-for-full-width-content .match-content-width { - width: 100vw; -} - -.sb-article, -.match-content-width { - padding: 0 1rem; - box-sizing: border-box; -} - -@media (min-width: 32rem) { - .sb-article, - .match-content-width { - padding: 0 2rem; - } -} - -/* Tablet views */ -@media (min-width: 42rem) { - .sb-article-container { - width: auto; - } - .sb-footer-content__inner, - .drop-secondary-sidebar-for-full-width-content .sb-article, - .drop-secondary-sidebar-for-full-width-content .match-content-width { - width: 42rem; - } - .sb-article, - .match-content-width { - width: 42rem; - } -} -@media (min-width: 46rem) { - .sb-footer-content__inner, - .drop-secondary-sidebar-for-full-width-content .sb-article, - .drop-secondary-sidebar-for-full-width-content .match-content-width { - width: 46rem; - } - .sb-article, - .match-content-width { - width: 46rem; - } -} -@media (min-width: 50rem) { - .sb-footer-content__inner, - .drop-secondary-sidebar-for-full-width-content .sb-article, - .drop-secondary-sidebar-for-full-width-content .match-content-width { - width: 50rem; - } - .sb-article, - .match-content-width { - width: 50rem; - } -} - -/* Tablet views */ -@media (min-width: 59rem) { - .sb-sidebar-secondary { - position: static; - } - .hide-when-secondary-sidebar-shown { - display: none !important; - } - .sb-footer-content__inner, - .drop-secondary-sidebar-for-full-width-content .sb-article, - .drop-secondary-sidebar-for-full-width-content .match-content-width { - width: 59rem; - } - .sb-article, - .match-content-width { - width: 42rem; - } -} -@media (min-width: 63rem) { - .sb-footer-content__inner, - .drop-secondary-sidebar-for-full-width-content .sb-article, - .drop-secondary-sidebar-for-full-width-content .match-content-width { - width: 63rem; - } - .sb-article, - .match-content-width { - width: 46rem; - } -} -@media (min-width: 67rem) { - .sb-footer-content__inner, - .drop-secondary-sidebar-for-full-width-content .sb-article, - .drop-secondary-sidebar-for-full-width-content .match-content-width { - width: 67rem; - } - .sb-article, - .match-content-width { - width: 50rem; - } -} - -/* Desktop views */ -@media (min-width: 76rem) { - .sb-sidebar-primary { - position: static; - } - .hide-when-primary-sidebar-shown { - display: none !important; - } - .sb-footer-content__inner, - .drop-secondary-sidebar-for-full-width-content .sb-article, - .drop-secondary-sidebar-for-full-width-content .match-content-width { - width: 59rem; - } - .sb-article, - .match-content-width { - width: 42rem; - } -} - -/* Full desktop views */ -@media (min-width: 80rem) { - .sb-article, - .match-content-width { - width: 46rem; - } - .sb-footer-content__inner, - .drop-secondary-sidebar-for-full-width-content .sb-article, - .drop-secondary-sidebar-for-full-width-content .match-content-width { - width: 63rem; - } -} - -@media (min-width: 84rem) { - .sb-article, - .match-content-width { - width: 50rem; - } - .sb-footer-content__inner, - .drop-secondary-sidebar-for-full-width-content .sb-article, - .drop-secondary-sidebar-for-full-width-content .match-content-width { - width: 67rem; - } -} - -@media (min-width: 88rem) { - .sb-footer-content__inner, - .drop-secondary-sidebar-for-full-width-content .sb-article, - .drop-secondary-sidebar-for-full-width-content .match-content-width { - width: 67rem; - } - .sb-page-width { - width: 88rem; - } -} diff --git a/docs/_build/html/_static/sphinx_highlight.js b/docs/_build/html/_static/sphinx_highlight.js deleted file mode 100644 index 8a96c69a1..000000000 --- a/docs/_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/docs/_build/html/_static/styles/furo-extensions.css b/docs/_build/html/_static/styles/furo-extensions.css deleted file mode 100644 index bc447f228..000000000 --- a/docs/_build/html/_static/styles/furo-extensions.css +++ /dev/null @@ -1,2 +0,0 @@ -#furo-sidebar-ad-placement{padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)}#furo-sidebar-ad-placement .ethical-sidebar{background:var(--color-background-secondary);border:none;box-shadow:none}#furo-sidebar-ad-placement .ethical-sidebar:hover{background:var(--color-background-hover)}#furo-sidebar-ad-placement .ethical-sidebar a{color:var(--color-foreground-primary)}#furo-sidebar-ad-placement .ethical-callout a{color:var(--color-foreground-secondary)!important}#furo-readthedocs-versions{background:transparent;display:block;position:static;width:100%}#furo-readthedocs-versions .rst-versions{background:#1a1c1e}#furo-readthedocs-versions .rst-current-version{background:var(--color-sidebar-item-background);cursor:unset}#furo-readthedocs-versions .rst-current-version:hover{background:var(--color-sidebar-item-background)}#furo-readthedocs-versions .rst-current-version .fa-book{color:var(--color-foreground-primary)}#furo-readthedocs-versions>.rst-other-versions{padding:0}#furo-readthedocs-versions>.rst-other-versions small{opacity:1}#furo-readthedocs-versions .injected .rst-versions{position:unset}#furo-readthedocs-versions:focus-within,#furo-readthedocs-versions:hover{box-shadow:0 0 0 1px var(--color-sidebar-background-border)}#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:hover .rst-current-version{background:#1a1c1e;font-size:inherit;height:auto;line-height:inherit;padding:12px;text-align:right}#furo-readthedocs-versions:focus-within .rst-current-version .fa-book,#furo-readthedocs-versions:hover .rst-current-version .fa-book{color:#fff;float:left}#furo-readthedocs-versions:focus-within .fa-caret-down,#furo-readthedocs-versions:hover .fa-caret-down{display:none}#furo-readthedocs-versions:focus-within .injected,#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:focus-within .rst-other-versions,#furo-readthedocs-versions:hover .injected,#furo-readthedocs-versions:hover .rst-current-version,#furo-readthedocs-versions:hover .rst-other-versions{display:block}#furo-readthedocs-versions:focus-within>.rst-current-version,#furo-readthedocs-versions:hover>.rst-current-version{display:none}.highlight:hover button.copybtn{color:var(--color-code-foreground)}.highlight button.copybtn{align-items:center;background-color:var(--color-code-background);border:none;color:var(--color-background-item);cursor:pointer;height:1.25em;opacity:1;right:.5rem;top:.625rem;transition:color .3s,opacity .3s;width:1.25em}.highlight button.copybtn:hover{background-color:var(--color-code-background);color:var(--color-brand-content)}.highlight button.copybtn:after{background-color:transparent;color:var(--color-code-foreground);display:none}.highlight button.copybtn.success{color:#22863a;transition:color 0ms}.highlight button.copybtn.success:after{display:block}.highlight button.copybtn svg{padding:0}body{--sd-color-primary:var(--color-brand-primary);--sd-color-primary-highlight:var(--color-brand-content);--sd-color-primary-text:var(--color-background-primary);--sd-color-shadow:rgba(0,0,0,.05);--sd-color-card-border:var(--color-card-border);--sd-color-card-border-hover:var(--color-brand-content);--sd-color-card-background:var(--color-card-background);--sd-color-card-text:var(--color-foreground-primary);--sd-color-card-header:var(--color-card-marginals-background);--sd-color-card-footer:var(--color-card-marginals-background);--sd-color-tabs-label-active:var(--color-brand-content);--sd-color-tabs-label-hover:var(--color-foreground-muted);--sd-color-tabs-label-inactive:var(--color-foreground-muted);--sd-color-tabs-underline-active:var(--color-brand-content);--sd-color-tabs-underline-hover:var(--color-foreground-border);--sd-color-tabs-underline-inactive:var(--color-background-border);--sd-color-tabs-overline:var(--color-background-border);--sd-color-tabs-underline:var(--color-background-border)}.sd-tab-content{box-shadow:0 -2px var(--sd-color-tabs-overline),0 1px var(--sd-color-tabs-underline)}.sd-card{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)}.sd-shadow-sm{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-md{box-shadow:0 .3rem .75rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-lg{box-shadow:0 .6rem 1.5rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-card-hover:hover{transform:none}.sd-cards-carousel{gap:.25rem;padding:.25rem}body{--tabs--label-text:var(--color-foreground-muted);--tabs--label-text--hover:var(--color-foreground-muted);--tabs--label-text--active:var(--color-brand-content);--tabs--label-text--active--hover:var(--color-brand-content);--tabs--label-background:transparent;--tabs--label-background--hover:transparent;--tabs--label-background--active:transparent;--tabs--label-background--active--hover:transparent;--tabs--padding-x:0.25em;--tabs--margin-x:1em;--tabs--border:var(--color-background-border);--tabs--label-border:transparent;--tabs--label-border--hover:var(--color-foreground-muted);--tabs--label-border--active:var(--color-brand-content);--tabs--label-border--active--hover:var(--color-brand-content)}[role=main] .container{max-width:none;padding-left:0;padding-right:0}.shadow.docutils{border:none;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)!important}.sphinx-bs .card{background-color:var(--color-background-secondary);color:var(--color-foreground)} -/*# sourceMappingURL=furo-extensions.css.map*/ \ No newline at end of file diff --git a/docs/_build/html/_static/styles/furo-extensions.css.map b/docs/_build/html/_static/styles/furo-extensions.css.map deleted file mode 100644 index 9ba5637f9..000000000 --- a/docs/_build/html/_static/styles/furo-extensions.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"styles/furo-extensions.css","mappings":"AAGA,2BACE,oFACA,4CAKE,6CAHA,YACA,eAEA,CACA,kDACE,yCAEF,8CACE,sCAEJ,8CACE,kDAEJ,2BAGE,uBACA,cAHA,gBACA,UAEA,CAGA,yCACE,mBAEF,gDAEE,gDADA,YACA,CACA,sDACE,gDACF,yDACE,sCAEJ,+CACE,UACA,qDACE,UAGF,mDACE,eAEJ,yEAEE,4DAEA,mHASE,mBAPA,kBAEA,YADA,oBAGA,aADA,gBAIA,CAEA,qIAEE,WADA,UACA,CAEJ,uGACE,aAEF,iUAGE,cAEF,mHACE,aC1EJ,gCACE,mCAEF,0BAKE,mBAUA,8CACA,YAFA,mCAKA,eAZA,cALA,UASA,YADA,YAYA,iCAdA,YAcA,CAEA,gCAEE,8CADA,gCACA,CAEF,gCAGE,6BADA,mCADA,YAEA,CAEF,kCAEE,cADA,oBACA,CACA,wCACE,cAEJ,8BACE,UC5CN,KAEE,6CAA8C,CAC9C,uDAAwD,CACxD,uDAAwD,CAGxD,iCAAsC,CAGtC,+CAAgD,CAChD,uDAAwD,CACxD,uDAAwD,CACxD,oDAAqD,CACrD,6DAA8D,CAC9D,6DAA8D,CAG9D,uDAAwD,CACxD,yDAA0D,CAC1D,4DAA6D,CAC7D,2DAA4D,CAC5D,8DAA+D,CAC/D,iEAAkE,CAClE,uDAAwD,CACxD,wDAAyD,CAG3D,gBACE,qFAGF,SACE,6EAEF,cACE,uFAEF,cACE,uFAEF,cACE,uFAGF,qBACE,eAEF,mBACE,WACA,eChDF,KACE,gDAAiD,CACjD,uDAAwD,CACxD,qDAAsD,CACtD,4DAA6D,CAC7D,oCAAqC,CACrC,2CAA4C,CAC5C,4CAA6C,CAC7C,mDAAoD,CACpD,wBAAyB,CACzB,oBAAqB,CACrB,6CAA8C,CAC9C,gCAAiC,CACjC,yDAA0D,CAC1D,uDAAwD,CACxD,8DAA+D,CCbjE,uBACE,eACA,eACA,gBAGF,iBACE,YACA,+EAGF,iBACE,mDACA","sources":["webpack:///./src/furo/assets/styles/extensions/_readthedocs.sass","webpack:///./src/furo/assets/styles/extensions/_copybutton.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-design.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-inline-tabs.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-panels.sass"],"sourcesContent":["// This file contains the styles used for tweaking how ReadTheDoc's embedded\n// contents would show up inside the theme.\n\n#furo-sidebar-ad-placement\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n .ethical-sidebar\n // Remove the border and box-shadow.\n border: none\n box-shadow: none\n // Manage the background colors.\n background: var(--color-background-secondary)\n &:hover\n background: var(--color-background-hover)\n // Ensure the text is legible.\n a\n color: var(--color-foreground-primary)\n\n .ethical-callout a\n color: var(--color-foreground-secondary) !important\n\n#furo-readthedocs-versions\n position: static\n width: 100%\n background: transparent\n display: block\n\n // Make the background color fit with the theme's aesthetic.\n .rst-versions\n background: rgb(26, 28, 30)\n\n .rst-current-version\n cursor: unset\n background: var(--color-sidebar-item-background)\n &:hover\n background: var(--color-sidebar-item-background)\n .fa-book\n color: var(--color-foreground-primary)\n\n > .rst-other-versions\n padding: 0\n small\n opacity: 1\n\n .injected\n .rst-versions\n position: unset\n\n &:hover,\n &:focus-within\n box-shadow: 0 0 0 1px var(--color-sidebar-background-border)\n\n .rst-current-version\n // Undo the tweaks done in RTD's CSS\n font-size: inherit\n line-height: inherit\n height: auto\n text-align: right\n padding: 12px\n\n // Match the rest of the body\n background: #1a1c1e\n\n .fa-book\n float: left\n color: white\n\n .fa-caret-down\n display: none\n\n .rst-current-version,\n .rst-other-versions,\n .injected\n display: block\n\n > .rst-current-version\n display: none\n",".highlight\n &:hover button.copybtn\n color: var(--color-code-foreground)\n\n button.copybtn\n // Make it visible\n opacity: 1\n\n // Align things correctly\n align-items: center\n\n height: 1.25em\n width: 1.25em\n\n top: 0.625rem // $code-spacing-vertical\n right: 0.5rem\n\n // Make it look better\n color: var(--color-background-item)\n background-color: var(--color-code-background)\n border: none\n\n // Change to cursor to make it obvious that you can click on it\n cursor: pointer\n\n // Transition smoothly, for aesthetics\n transition: color 300ms, opacity 300ms\n\n &:hover\n color: var(--color-brand-content)\n background-color: var(--color-code-background)\n\n &::after\n display: none\n color: var(--color-code-foreground)\n background-color: transparent\n\n &.success\n transition: color 0ms\n color: #22863a\n &::after\n display: block\n\n svg\n padding: 0\n","body\n // Colors\n --sd-color-primary: var(--color-brand-primary)\n --sd-color-primary-highlight: var(--color-brand-content)\n --sd-color-primary-text: var(--color-background-primary)\n\n // Shadows\n --sd-color-shadow: rgba(0, 0, 0, 0.05)\n\n // Cards\n --sd-color-card-border: var(--color-card-border)\n --sd-color-card-border-hover: var(--color-brand-content)\n --sd-color-card-background: var(--color-card-background)\n --sd-color-card-text: var(--color-foreground-primary)\n --sd-color-card-header: var(--color-card-marginals-background)\n --sd-color-card-footer: var(--color-card-marginals-background)\n\n // Tabs\n --sd-color-tabs-label-active: var(--color-brand-content)\n --sd-color-tabs-label-hover: var(--color-foreground-muted)\n --sd-color-tabs-label-inactive: var(--color-foreground-muted)\n --sd-color-tabs-underline-active: var(--color-brand-content)\n --sd-color-tabs-underline-hover: var(--color-foreground-border)\n --sd-color-tabs-underline-inactive: var(--color-background-border)\n --sd-color-tabs-overline: var(--color-background-border)\n --sd-color-tabs-underline: var(--color-background-border)\n\n// Tabs\n.sd-tab-content\n box-shadow: 0 -2px var(--sd-color-tabs-overline), 0 1px var(--sd-color-tabs-underline)\n\n// Shadows\n.sd-card // Have a shadow by default\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n.sd-shadow-sm\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-md\n box-shadow: 0 0.3rem 0.75rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-lg\n box-shadow: 0 0.6rem 1.5rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Cards\n.sd-card-hover:hover // Don't change scale on hover\n transform: none\n\n.sd-cards-carousel // Have a bit of gap in the carousel by default\n gap: 0.25rem\n padding: 0.25rem\n","// This file contains styles to tweak sphinx-inline-tabs to work well with Furo.\n\nbody\n --tabs--label-text: var(--color-foreground-muted)\n --tabs--label-text--hover: var(--color-foreground-muted)\n --tabs--label-text--active: var(--color-brand-content)\n --tabs--label-text--active--hover: var(--color-brand-content)\n --tabs--label-background: transparent\n --tabs--label-background--hover: transparent\n --tabs--label-background--active: transparent\n --tabs--label-background--active--hover: transparent\n --tabs--padding-x: 0.25em\n --tabs--margin-x: 1em\n --tabs--border: var(--color-background-border)\n --tabs--label-border: transparent\n --tabs--label-border--hover: var(--color-foreground-muted)\n --tabs--label-border--active: var(--color-brand-content)\n --tabs--label-border--active--hover: var(--color-brand-content)\n","// This file contains styles to tweak sphinx-panels to work well with Furo.\n\n// sphinx-panels includes Bootstrap 4, which uses .container which can conflict\n// with docutils' `.. container::` directive.\n[role=\"main\"] .container\n max-width: initial\n padding-left: initial\n padding-right: initial\n\n// Make the panels look nicer!\n.shadow.docutils\n border: none\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Make panel colors respond to dark mode\n.sphinx-bs .card\n background-color: var(--color-background-secondary)\n color: var(--color-foreground)\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/docs/_build/html/_static/styles/furo.css b/docs/_build/html/_static/styles/furo.css deleted file mode 100644 index 3d29a218f..000000000 --- a/docs/_build/html/_static/styles/furo.css +++ /dev/null @@ -1,2 +0,0 @@ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{-webkit-text-size-adjust:100%;line-height:1.15}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}@media print{.content-icon-container,.headerlink,.mobile-header,.related-pages{display:none!important}.highlight{border:.1pt solid var(--color-foreground-border)}a,blockquote,dl,ol,pre,table,ul{page-break-inside:avoid}caption,figure,h1,h2,h3,h4,h5,h6,img{page-break-after:avoid;page-break-inside:avoid}dl,ol,ul{page-break-before:avoid}}.visually-hidden{clip:rect(0,0,0,0)!important;border:0!important;height:1px!important;margin:-1px!important;overflow:hidden!important;padding:0!important;position:absolute!important;white-space:nowrap!important;width:1px!important}:-moz-focusring{outline:auto}body{--font-stack:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;--font-stack--monospace:"SFMono-Regular",Menlo,Consolas,Monaco,Liberation Mono,Lucida Console,monospace;--font-size--normal:100%;--font-size--small:87.5%;--font-size--small--2:81.25%;--font-size--small--3:75%;--font-size--small--4:62.5%;--sidebar-caption-font-size:var(--font-size--small--2);--sidebar-item-font-size:var(--font-size--small);--sidebar-search-input-font-size:var(--font-size--small);--toc-font-size:var(--font-size--small--3);--toc-font-size--mobile:var(--font-size--normal);--toc-title-font-size:var(--font-size--small--4);--admonition-font-size:0.8125rem;--admonition-title-font-size:0.8125rem;--code-font-size:var(--font-size--small--2);--api-font-size:var(--font-size--small);--header-height:calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*4);--header-padding:0.5rem;--sidebar-tree-space-above:1.5rem;--sidebar-caption-space-above:1rem;--sidebar-item-line-height:1rem;--sidebar-item-spacing-vertical:0.5rem;--sidebar-item-spacing-horizontal:1rem;--sidebar-item-height:calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*2);--sidebar-expander-width:var(--sidebar-item-height);--sidebar-search-space-above:0.5rem;--sidebar-search-input-spacing-vertical:0.5rem;--sidebar-search-input-spacing-horizontal:0.5rem;--sidebar-search-input-height:1rem;--sidebar-search-icon-size:var(--sidebar-search-input-height);--toc-title-padding:0.25rem 0;--toc-spacing-vertical:1.5rem;--toc-spacing-horizontal:1.5rem;--toc-item-spacing-vertical:0.4rem;--toc-item-spacing-horizontal:1rem;--icon-search:url('data:image/svg+xml;charset=utf-8,');--icon-pencil:url('data:image/svg+xml;charset=utf-8,');--icon-abstract:url('data:image/svg+xml;charset=utf-8,');--icon-info:url('data:image/svg+xml;charset=utf-8,');--icon-flame:url('data:image/svg+xml;charset=utf-8,');--icon-question:url('data:image/svg+xml;charset=utf-8,');--icon-warning:url('data:image/svg+xml;charset=utf-8,');--icon-failure:url('data:image/svg+xml;charset=utf-8,');--icon-spark:url('data:image/svg+xml;charset=utf-8,');--color-admonition-title--caution:#ff9100;--color-admonition-title-background--caution:rgba(255,145,0,.2);--color-admonition-title--warning:#ff9100;--color-admonition-title-background--warning:rgba(255,145,0,.2);--color-admonition-title--danger:#ff5252;--color-admonition-title-background--danger:rgba(255,82,82,.2);--color-admonition-title--attention:#ff5252;--color-admonition-title-background--attention:rgba(255,82,82,.2);--color-admonition-title--error:#ff5252;--color-admonition-title-background--error:rgba(255,82,82,.2);--color-admonition-title--hint:#00c852;--color-admonition-title-background--hint:rgba(0,200,82,.2);--color-admonition-title--tip:#00c852;--color-admonition-title-background--tip:rgba(0,200,82,.2);--color-admonition-title--important:#00bfa5;--color-admonition-title-background--important:rgba(0,191,165,.2);--color-admonition-title--note:#00b0ff;--color-admonition-title-background--note:rgba(0,176,255,.2);--color-admonition-title--seealso:#448aff;--color-admonition-title-background--seealso:rgba(68,138,255,.2);--color-admonition-title--admonition-todo:grey;--color-admonition-title-background--admonition-todo:hsla(0,0%,50%,.2);--color-admonition-title:#651fff;--color-admonition-title-background:rgba(101,31,255,.2);--icon-admonition-default:var(--icon-abstract);--color-topic-title:#14b8a6;--color-topic-title-background:rgba(20,184,166,.2);--icon-topic-default:var(--icon-pencil);--color-problematic:#b30000;--color-foreground-primary:#000;--color-foreground-secondary:#5a5c63;--color-foreground-muted:#646776;--color-foreground-border:#878787;--color-background-primary:#fff;--color-background-secondary:#f8f9fb;--color-background-hover:#efeff4;--color-background-hover--transparent:#efeff400;--color-background-border:#eeebee;--color-background-item:#ccc;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2962ff;--color-brand-content:#2a5adf;--color-api-background:var(--color-background-hover--transparent);--color-api-background-hover:var(--color-background-hover);--color-api-overall:var(--color-foreground-secondary);--color-api-name:var(--color-problematic);--color-api-pre-name:var(--color-problematic);--color-api-paren:var(--color-foreground-secondary);--color-api-keyword:var(--color-foreground-primary);--color-highlight-on-target:#ffc;--color-inline-code-background:var(--color-background-secondary);--color-highlighted-background:#def;--color-highlighted-text:var(--color-foreground-primary);--color-guilabel-background:#ddeeff80;--color-guilabel-border:#bedaf580;--color-guilabel-text:var(--color-foreground-primary);--color-admonition-background:transparent;--color-table-header-background:var(--color-background-secondary);--color-table-border:var(--color-background-border);--color-card-border:var(--color-background-secondary);--color-card-background:transparent;--color-card-marginals-background:var(--color-background-secondary);--color-header-background:var(--color-background-primary);--color-header-border:var(--color-background-border);--color-header-text:var(--color-foreground-primary);--color-sidebar-background:var(--color-background-secondary);--color-sidebar-background-border:var(--color-background-border);--color-sidebar-brand-text:var(--color-foreground-primary);--color-sidebar-caption-text:var(--color-foreground-muted);--color-sidebar-link-text:var(--color-foreground-secondary);--color-sidebar-link-text--top-level:var(--color-brand-primary);--color-sidebar-item-background:var(--color-sidebar-background);--color-sidebar-item-background--current:var( --color-sidebar-item-background );--color-sidebar-item-background--hover:linear-gradient(90deg,var(--color-background-hover--transparent) 0%,var(--color-background-hover) var(--sidebar-item-spacing-horizontal),var(--color-background-hover) 100%);--color-sidebar-item-expander-background:transparent;--color-sidebar-item-expander-background--hover:var( --color-background-hover );--color-sidebar-search-text:var(--color-foreground-primary);--color-sidebar-search-background:var(--color-background-secondary);--color-sidebar-search-background--focus:var(--color-background-primary);--color-sidebar-search-border:var(--color-background-border);--color-sidebar-search-icon:var(--color-foreground-muted);--color-toc-background:var(--color-background-primary);--color-toc-title-text:var(--color-foreground-muted);--color-toc-item-text:var(--color-foreground-secondary);--color-toc-item-text--hover:var(--color-foreground-primary);--color-toc-item-text--active:var(--color-brand-primary);--color-content-foreground:var(--color-foreground-primary);--color-content-background:transparent;--color-link:var(--color-brand-content);--color-link--hover:var(--color-brand-content);--color-link-underline:var(--color-background-border);--color-link-underline--hover:var(--color-foreground-border)}.only-light{display:block!important}html body .only-dark{display:none!important}@media not print{body[data-theme=dark]{--color-problematic:#ee5151;--color-foreground-primary:#ffffffcc;--color-foreground-secondary:#9ca0a5;--color-foreground-muted:#81868d;--color-foreground-border:#666;--color-background-primary:#131416;--color-background-secondary:#1a1c1e;--color-background-hover:#1e2124;--color-background-hover--transparent:#1e212400;--color-background-border:#303335;--color-background-item:#444;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2b8cee;--color-brand-content:#368ce2;--color-highlighted-background:#083563;--color-guilabel-background:#08356380;--color-guilabel-border:#13395f80;--color-api-keyword:var(--color-foreground-secondary);--color-highlight-on-target:#330;--color-admonition-background:#18181a;--color-card-border:var(--color-background-secondary);--color-card-background:#18181a;--color-card-marginals-background:var(--color-background-hover)}html body[data-theme=dark] .only-light{display:none!important}body[data-theme=dark] .only-dark{display:block!important}@media(prefers-color-scheme:dark){body:not([data-theme=light]){--color-problematic:#ee5151;--color-foreground-primary:#ffffffcc;--color-foreground-secondary:#9ca0a5;--color-foreground-muted:#81868d;--color-foreground-border:#666;--color-background-primary:#131416;--color-background-secondary:#1a1c1e;--color-background-hover:#1e2124;--color-background-hover--transparent:#1e212400;--color-background-border:#303335;--color-background-item:#444;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2b8cee;--color-brand-content:#368ce2;--color-highlighted-background:#083563;--color-guilabel-background:#08356380;--color-guilabel-border:#13395f80;--color-api-keyword:var(--color-foreground-secondary);--color-highlight-on-target:#330;--color-admonition-background:#18181a;--color-card-border:var(--color-background-secondary);--color-card-background:#18181a;--color-card-marginals-background:var(--color-background-hover)}html body:not([data-theme=light]) .only-light{display:none!important}body:not([data-theme=light]) .only-dark{display:block!important}}}body[data-theme=auto] .theme-toggle svg.theme-icon-when-auto,body[data-theme=dark] .theme-toggle svg.theme-icon-when-dark,body[data-theme=light] .theme-toggle svg.theme-icon-when-light{display:block}body{font-family:var(--font-stack)}code,kbd,pre,samp{font-family:var(--font-stack--monospace)}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}article{line-height:1.5}h1,h2,h3,h4,h5,h6{border-radius:.5rem;font-weight:700;line-height:1.25;margin:.5rem -.5rem;padding-left:.5rem;padding-right:.5rem}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:0}h1{font-size:2.5em;margin-bottom:1rem}h1,h2{margin-top:1.75rem}h2{font-size:2em}h3{font-size:1.5em}h4{font-size:1.25em}h5{font-size:1.125em}h6{font-size:1em}small{font-size:80%;opacity:75%}p{margin-bottom:.75rem;margin-top:.5rem}hr.docutils{background-color:var(--color-background-border);border:0;height:1px;margin:2rem 0;padding:0}.centered{text-align:center}a{color:var(--color-link);text-decoration:underline;text-decoration-color:var(--color-link-underline)}a:hover{color:var(--color-link--hover);text-decoration-color:var(--color-link-underline--hover)}a.muted-link{color:inherit}a.muted-link:hover{color:var(--color-link);text-decoration-color:var(--color-link-underline--hover)}html{overflow-x:hidden;overflow-y:scroll;scroll-behavior:smooth}.sidebar-scroll,.toc-scroll,article[role=main] *{scrollbar-color:var(--color-foreground-border) transparent;scrollbar-width:thin}.sidebar-scroll::-webkit-scrollbar,.toc-scroll::-webkit-scrollbar,article[role=main] ::-webkit-scrollbar{height:.25rem;width:.25rem}.sidebar-scroll::-webkit-scrollbar-thumb,.toc-scroll::-webkit-scrollbar-thumb,article[role=main] ::-webkit-scrollbar-thumb{background-color:var(--color-foreground-border);border-radius:.125rem}body,html{background:var(--color-background-primary);color:var(--color-foreground-primary);height:100%}article{background:var(--color-content-background);color:var(--color-content-foreground);overflow-wrap:break-word}.page{display:flex;min-height:100%}.mobile-header{background-color:var(--color-header-background);border-bottom:1px solid var(--color-header-border);color:var(--color-header-text);display:none;height:var(--header-height);width:100%;z-index:10}.mobile-header.scrolled{border-bottom:none;box-shadow:0 0 .2rem rgba(0,0,0,.1),0 .2rem .4rem rgba(0,0,0,.2)}.mobile-header .header-center a{color:var(--color-header-text);text-decoration:none}.main{display:flex;flex:1}.sidebar-drawer{background:var(--color-sidebar-background);border-right:1px solid var(--color-sidebar-background-border);box-sizing:border-box;display:flex;justify-content:flex-end;min-width:15em;width:calc(50% - 26em)}.sidebar-container,.toc-drawer{box-sizing:border-box;width:15em}.toc-drawer{background:var(--color-toc-background);padding-right:1rem}.sidebar-sticky,.toc-sticky{display:flex;flex-direction:column;height:min(100%,100vh);height:100vh;position:sticky;top:0}.sidebar-scroll,.toc-scroll{flex-grow:1;flex-shrink:1;overflow:auto;scroll-behavior:smooth}.content{display:flex;flex-direction:column;justify-content:space-between;padding:0 3em;width:46em}.icon{display:inline-block;height:1rem;width:1rem}.icon svg{height:100%;width:100%}.announcement{align-items:center;background-color:var(--color-announcement-background);color:var(--color-announcement-text);display:flex;height:var(--header-height);overflow-x:auto}.announcement+.page{min-height:calc(100% - var(--header-height))}.announcement-content{box-sizing:border-box;min-width:100%;padding:.5rem;text-align:center;white-space:nowrap}.announcement-content a{color:var(--color-announcement-text);text-decoration-color:var(--color-announcement-text)}.announcement-content a:hover{color:var(--color-announcement-text);text-decoration-color:var(--color-link--hover)}.no-js .theme-toggle-container{display:none}.theme-toggle-container{vertical-align:middle}.theme-toggle{background:transparent;border:none;cursor:pointer;padding:0}.theme-toggle svg{color:var(--color-foreground-primary);display:none;height:1rem;vertical-align:middle;width:1rem}.theme-toggle-header{float:left;padding:1rem .5rem}.nav-overlay-icon,.toc-overlay-icon{cursor:pointer;display:none}.nav-overlay-icon .icon,.toc-overlay-icon .icon{color:var(--color-foreground-secondary);height:1rem;width:1rem}.nav-overlay-icon,.toc-header-icon{align-items:center;justify-content:center}.toc-content-icon{height:1.5rem;width:1.5rem}.content-icon-container{display:flex;float:right;gap:.5rem;margin-bottom:1rem;margin-left:1rem;margin-top:1.5rem}.content-icon-container .edit-this-page svg{color:inherit;height:1rem;width:1rem}.sidebar-toggle{display:none;position:absolute}.sidebar-toggle[name=__toc]{left:20px}.sidebar-toggle:checked{left:40px}.overlay{background-color:rgba(0,0,0,.54);height:0;opacity:0;position:fixed;top:0;transition:width 0ms,height 0ms,opacity .25s ease-out;width:0}.sidebar-overlay{z-index:20}.toc-overlay{z-index:40}.sidebar-drawer{transition:left .25s ease-in-out;z-index:30}.toc-drawer{transition:right .25s ease-in-out;z-index:50}#__navigation:checked~.sidebar-overlay{height:100%;opacity:1;width:100%}#__navigation:checked~.page .sidebar-drawer{left:0;top:0}#__toc:checked~.toc-overlay{height:100%;opacity:1;width:100%}#__toc:checked~.page .toc-drawer{right:0;top:0}.back-to-top{background:var(--color-background-primary);border-radius:1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 1px 0 hsla(220,9%,46%,.502);display:none;font-size:.8125rem;left:0;margin-left:50%;padding:.5rem .75rem .5rem .5rem;position:fixed;text-decoration:none;top:1rem;transform:translateX(-50%);z-index:10}.back-to-top svg{fill:currentColor;display:inline-block;height:1rem;width:1rem}.back-to-top span{margin-left:.25rem}.show-back-to-top .back-to-top{align-items:center;display:flex}@media(min-width:97em){html{font-size:110%}}@media(max-width:82em){.toc-content-icon{display:flex}.toc-drawer{border-left:1px solid var(--color-background-muted);height:100vh;position:fixed;right:-15em;top:0}.toc-tree{border-left:none;font-size:var(--toc-font-size--mobile)}.sidebar-drawer{width:calc(50% - 18.5em)}}@media(max-width:67em){.nav-overlay-icon{display:flex}.sidebar-drawer{height:100vh;left:-15em;position:fixed;top:0;width:15em}.toc-header-icon{display:flex}.theme-toggle-content,.toc-content-icon{display:none}.theme-toggle-header{display:block}.mobile-header{align-items:center;display:flex;justify-content:space-between;position:sticky;top:0}.mobile-header .header-left,.mobile-header .header-right{display:flex;height:var(--header-height);padding:0 var(--header-padding)}.mobile-header .header-left label,.mobile-header .header-right label{height:100%;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%}.nav-overlay-icon .icon,.theme-toggle svg{height:1.25rem;width:1.25rem}:target{scroll-margin-top:var(--header-height)}.back-to-top{top:calc(var(--header-height) + .5rem)}.page{flex-direction:column;justify-content:center}.content{margin-left:auto;margin-right:auto}}@media(max-width:52em){.content{overflow-x:auto;width:100%}}@media(max-width:46em){.content{padding:0 1em}article aside.sidebar{float:none;margin:1rem 0;width:100%}}.admonition,.topic{background:var(--color-admonition-background);border-radius:.2rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1);font-size:var(--admonition-font-size);margin:1rem auto;overflow:hidden;padding:0 .5rem .5rem;page-break-inside:avoid}.admonition>:nth-child(2),.topic>:nth-child(2){margin-top:0}.admonition>:last-child,.topic>:last-child{margin-bottom:0}.admonition p.admonition-title,p.topic-title{font-size:var(--admonition-title-font-size);font-weight:500;line-height:1.3;margin:0 -.5rem .5rem;padding:.4rem .5rem .4rem 2rem;position:relative}.admonition p.admonition-title:before,p.topic-title:before{content:"";height:1rem;left:.5rem;position:absolute;width:1rem}p.admonition-title{background-color:var(--color-admonition-title-background)}p.admonition-title:before{background-color:var(--color-admonition-title);-webkit-mask-image:var(--icon-admonition-default);mask-image:var(--icon-admonition-default);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}p.topic-title{background-color:var(--color-topic-title-background)}p.topic-title:before{background-color:var(--color-topic-title);-webkit-mask-image:var(--icon-topic-default);mask-image:var(--icon-topic-default);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.admonition{border-left:.2rem solid var(--color-admonition-title)}.admonition.caution{border-left-color:var(--color-admonition-title--caution)}.admonition.caution>.admonition-title{background-color:var(--color-admonition-title-background--caution)}.admonition.caution>.admonition-title:before{background-color:var(--color-admonition-title--caution);-webkit-mask-image:var(--icon-spark);mask-image:var(--icon-spark)}.admonition.warning{border-left-color:var(--color-admonition-title--warning)}.admonition.warning>.admonition-title{background-color:var(--color-admonition-title-background--warning)}.admonition.warning>.admonition-title:before{background-color:var(--color-admonition-title--warning);-webkit-mask-image:var(--icon-warning);mask-image:var(--icon-warning)}.admonition.danger{border-left-color:var(--color-admonition-title--danger)}.admonition.danger>.admonition-title{background-color:var(--color-admonition-title-background--danger)}.admonition.danger>.admonition-title:before{background-color:var(--color-admonition-title--danger);-webkit-mask-image:var(--icon-spark);mask-image:var(--icon-spark)}.admonition.attention{border-left-color:var(--color-admonition-title--attention)}.admonition.attention>.admonition-title{background-color:var(--color-admonition-title-background--attention)}.admonition.attention>.admonition-title:before{background-color:var(--color-admonition-title--attention);-webkit-mask-image:var(--icon-warning);mask-image:var(--icon-warning)}.admonition.error{border-left-color:var(--color-admonition-title--error)}.admonition.error>.admonition-title{background-color:var(--color-admonition-title-background--error)}.admonition.error>.admonition-title:before{background-color:var(--color-admonition-title--error);-webkit-mask-image:var(--icon-failure);mask-image:var(--icon-failure)}.admonition.hint{border-left-color:var(--color-admonition-title--hint)}.admonition.hint>.admonition-title{background-color:var(--color-admonition-title-background--hint)}.admonition.hint>.admonition-title:before{background-color:var(--color-admonition-title--hint);-webkit-mask-image:var(--icon-question);mask-image:var(--icon-question)}.admonition.tip{border-left-color:var(--color-admonition-title--tip)}.admonition.tip>.admonition-title{background-color:var(--color-admonition-title-background--tip)}.admonition.tip>.admonition-title:before{background-color:var(--color-admonition-title--tip);-webkit-mask-image:var(--icon-info);mask-image:var(--icon-info)}.admonition.important{border-left-color:var(--color-admonition-title--important)}.admonition.important>.admonition-title{background-color:var(--color-admonition-title-background--important)}.admonition.important>.admonition-title:before{background-color:var(--color-admonition-title--important);-webkit-mask-image:var(--icon-flame);mask-image:var(--icon-flame)}.admonition.note{border-left-color:var(--color-admonition-title--note)}.admonition.note>.admonition-title{background-color:var(--color-admonition-title-background--note)}.admonition.note>.admonition-title:before{background-color:var(--color-admonition-title--note);-webkit-mask-image:var(--icon-pencil);mask-image:var(--icon-pencil)}.admonition.seealso{border-left-color:var(--color-admonition-title--seealso)}.admonition.seealso>.admonition-title{background-color:var(--color-admonition-title-background--seealso)}.admonition.seealso>.admonition-title:before{background-color:var(--color-admonition-title--seealso);-webkit-mask-image:var(--icon-info);mask-image:var(--icon-info)}.admonition.admonition-todo{border-left-color:var(--color-admonition-title--admonition-todo)}.admonition.admonition-todo>.admonition-title{background-color:var(--color-admonition-title-background--admonition-todo)}.admonition.admonition-todo>.admonition-title:before{background-color:var(--color-admonition-title--admonition-todo);-webkit-mask-image:var(--icon-pencil);mask-image:var(--icon-pencil)}.admonition-todo>.admonition-title{text-transform:uppercase}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd{margin-left:2rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd>:first-child{margin-top:.125rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list,dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd>:last-child{margin-bottom:.75rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list>dt{font-size:var(--font-size--small);text-transform:uppercase}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd:empty{margin-bottom:.5rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul{margin-left:-1.2rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul>li>p:nth-child(2){margin-top:0}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul>li>p+p:last-child:empty{margin-bottom:0;margin-top:0}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt{color:var(--color-api-overall)}.sig:not(.sig-inline){background:var(--color-api-background);border-radius:.25rem;font-family:var(--font-stack--monospace);font-size:var(--api-font-size);font-weight:700;margin-left:-.25rem;margin-right:-.25rem;padding:.25rem .5rem .25rem 3em;text-indent:-2.5em;transition:background .1s ease-out}.sig:not(.sig-inline):hover{background:var(--color-api-background-hover)}.sig:not(.sig-inline) a.reference .viewcode-link{font-weight:400;width:3.5rem}em.property{font-style:normal}em.property:first-child{color:var(--color-api-keyword)}.sig-name{color:var(--color-api-name)}.sig-prename{color:var(--color-api-pre-name);font-weight:400}.sig-paren{color:var(--color-api-paren)}.sig-param{font-style:normal}.versionmodified{font-style:italic}div.deprecated p,div.versionadded p,div.versionchanged p{margin-bottom:.125rem;margin-top:.125rem}.viewcode-back,.viewcode-link{float:right;text-align:right}.line-block{margin-bottom:.75rem;margin-top:.5rem}.line-block .line-block{margin-bottom:0;margin-top:0;padding-left:1rem}.code-block-caption,article p.caption,table>caption{font-size:var(--font-size--small);text-align:center}.toctree-wrapper.compound .caption,.toctree-wrapper.compound :not(.caption)>.caption-text{font-size:var(--font-size--small);margin-bottom:0;text-align:initial;text-transform:uppercase}.toctree-wrapper.compound>ul{margin-bottom:0;margin-top:0}.sig-inline,code.literal{background:var(--color-inline-code-background);border-radius:.2em;font-size:var(--font-size--small--2);padding:.1em .2em}pre.literal-block .sig-inline,pre.literal-block code.literal{font-size:inherit;padding:0}p .sig-inline,p code.literal{border:1px solid var(--color-background-border)}.sig-inline{font-family:var(--font-stack--monospace)}div[class*=" highlight-"],div[class^=highlight-]{display:flex;margin:1em 0}div[class*=" highlight-"] .table-wrapper,div[class^=highlight-] .table-wrapper,pre{margin:0;padding:0}pre{overflow:auto}article[role=main] .highlight pre{line-height:1.5}.highlight pre,pre.literal-block{font-size:var(--code-font-size);padding:.625rem .875rem}pre.literal-block{background-color:var(--color-code-background);border-radius:.2rem;color:var(--color-code-foreground);margin-bottom:1rem;margin-top:1rem}.highlight{border-radius:.2rem;width:100%}.highlight .gp,.highlight span.linenos{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.highlight .hll{display:block;margin-left:-.875rem;margin-right:-.875rem;padding-left:.875rem;padding-right:.875rem}.code-block-caption{background-color:var(--color-code-background);border-bottom:1px solid;border-radius:.25rem;border-bottom-left-radius:0;border-bottom-right-radius:0;border-color:var(--color-background-border);color:var(--color-code-foreground);display:flex;font-weight:300;padding:.625rem .875rem}.code-block-caption+div[class]{margin-top:0}.code-block-caption+div[class] pre{border-top-left-radius:0;border-top-right-radius:0}.highlighttable{display:block;width:100%}.highlighttable tbody{display:block}.highlighttable tr{display:flex}.highlighttable td.linenos{background-color:var(--color-code-background);border-bottom-left-radius:.2rem;border-top-left-radius:.2rem;color:var(--color-code-foreground);padding:.625rem 0 .625rem .875rem}.highlighttable .linenodiv{box-shadow:-.0625rem 0 var(--color-foreground-border) inset;font-size:var(--code-font-size);padding-right:.875rem}.highlighttable td.code{display:block;flex:1;overflow:hidden;padding:0}.highlighttable td.code .highlight{border-bottom-left-radius:0;border-top-left-radius:0}.highlight span.linenos{box-shadow:-.0625rem 0 var(--color-foreground-border) inset;display:inline-block;margin-right:.875rem;padding-left:0;padding-right:.875rem}.footnote-reference{font-size:var(--font-size--small--4);vertical-align:super}dl.footnote.brackets{color:var(--color-foreground-secondary);display:grid;font-size:var(--font-size--small);grid-template-columns:max-content auto}dl.footnote.brackets dt{margin:0}dl.footnote.brackets dt>.fn-backref{margin-left:.25rem}dl.footnote.brackets dt:after{content:":"}dl.footnote.brackets dt .brackets:before{content:"["}dl.footnote.brackets dt .brackets:after{content:"]"}dl.footnote.brackets dd{margin:0;padding:0 1rem}aside.footnote{color:var(--color-foreground-secondary);font-size:var(--font-size--small)}aside.footnote>span,div.citation>span{float:left;font-weight:500;padding-right:.25rem}aside.footnote>p,div.citation>p{margin-left:2rem}img{box-sizing:border-box;height:auto;max-width:100%}article .figure,article figure{border-radius:.2rem;margin:0}article .figure :last-child,article figure :last-child{margin-bottom:0}article .align-left{clear:left;float:left;margin:0 1rem 1rem}article .align-right{clear:right;float:right;margin:0 1rem 1rem}article .align-center,article .align-default{display:block;margin-left:auto;margin-right:auto;text-align:center}article table.align-default{display:table;text-align:initial}.domainindex-jumpbox,.genindex-jumpbox{border-bottom:1px solid var(--color-background-border);border-top:1px solid var(--color-background-border);padding:.25rem}.domainindex-section h2,.genindex-section h2{margin-bottom:.5rem;margin-top:.75rem}.domainindex-section ul,.genindex-section ul{margin-bottom:0;margin-top:0}ol,ul{margin-bottom:1rem;margin-top:1rem;padding-left:1.2rem}ol li>p:first-child,ul li>p:first-child{margin-bottom:.25rem;margin-top:.25rem}ol li>p:last-child,ul li>p:last-child{margin-top:.25rem}ol li>ol,ol li>ul,ul li>ol,ul li>ul{margin-bottom:.5rem;margin-top:.5rem}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}.simple li>ol,.simple li>ul,.toctree-wrapper li>ol,.toctree-wrapper li>ul{margin-bottom:0;margin-top:0}.field-list dt,.option-list dt,dl.footnote dt,dl.glossary dt,dl.simple dt,dl:not([class]) dt{font-weight:500;margin-top:.25rem}.field-list dt+dt,.option-list dt+dt,dl.footnote dt+dt,dl.glossary dt+dt,dl.simple dt+dt,dl:not([class]) dt+dt{margin-top:0}.field-list dt .classifier:before,.option-list dt .classifier:before,dl.footnote dt .classifier:before,dl.glossary dt .classifier:before,dl.simple dt .classifier:before,dl:not([class]) dt .classifier:before{content:":";margin-left:.2rem;margin-right:.2rem}.field-list dd ul,.field-list dd>p:first-child,.option-list dd ul,.option-list dd>p:first-child,dl.footnote dd ul,dl.footnote dd>p:first-child,dl.glossary dd ul,dl.glossary dd>p:first-child,dl.simple dd ul,dl.simple dd>p:first-child,dl:not([class]) dd ul,dl:not([class]) dd>p:first-child{margin-top:.125rem}.field-list dd ul,.option-list dd ul,dl.footnote dd ul,dl.glossary dd ul,dl.simple dd ul,dl:not([class]) dd ul{margin-bottom:.125rem}.math-wrapper{overflow-x:auto;width:100%}div.math{position:relative;text-align:center}div.math .headerlink,div.math:focus .headerlink{display:none}div.math:hover .headerlink{display:inline-block}div.math span.eqno{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);z-index:1}abbr[title]{cursor:help}.problematic{color:var(--color-problematic)}kbd:not(.compound){background-color:var(--color-background-secondary);border:1px solid var(--color-foreground-border);border-radius:.2rem;box-shadow:0 .0625rem 0 rgba(0,0,0,.2),inset 0 0 0 .125rem var(--color-background-primary);color:var(--color-foreground-primary);display:inline-block;font-size:var(--font-size--small--3);margin:0 .2rem;padding:0 .2rem;vertical-align:text-bottom}blockquote{background:var(--color-background-secondary);border-left:4px solid var(--color-background-border);margin-left:0;margin-right:0;padding:.5rem 1rem}blockquote .attribution{font-weight:600;text-align:right}blockquote.highlights,blockquote.pull-quote{font-size:1.25em}blockquote.epigraph,blockquote.pull-quote{border-left-width:0;border-radius:.5rem}blockquote.highlights{background:transparent;border-left-width:0}p .reference img{vertical-align:middle}p.rubric{font-size:1.125em;font-weight:700;line-height:1.25}dd p.rubric{font-size:var(--font-size--small);font-weight:inherit;line-height:inherit;text-transform:uppercase}article .sidebar{background-color:var(--color-background-secondary);border:1px solid var(--color-background-border);border-radius:.2rem;clear:right;float:right;margin-left:1rem;margin-right:0;width:30%}article .sidebar>*{padding-left:1rem;padding-right:1rem}article .sidebar>ol,article .sidebar>ul{padding-left:2.2rem}article .sidebar .sidebar-title{border-bottom:1px solid var(--color-background-border);font-weight:500;margin:0;padding:.5rem 1rem}.table-wrapper{margin-bottom:.5rem;margin-top:1rem;overflow-x:auto;padding:.2rem .2rem .75rem;width:100%}table.docutils{border-collapse:collapse;border-radius:.2rem;border-spacing:0;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)}table.docutils th{background:var(--color-table-header-background)}table.docutils td,table.docutils th{border-bottom:1px solid var(--color-table-border);border-left:1px solid var(--color-table-border);border-right:1px solid var(--color-table-border);padding:0 .25rem}table.docutils td p,table.docutils th p{margin:.25rem}table.docutils td:first-child,table.docutils th:first-child{border-left:none}table.docutils td:last-child,table.docutils th:last-child{border-right:none}table.docutils td.text-left,table.docutils th.text-left{text-align:left}table.docutils td.text-right,table.docutils th.text-right{text-align:right}table.docutils td.text-center,table.docutils th.text-center{text-align:center}:target{scroll-margin-top:.5rem}@media(max-width:67em){:target{scroll-margin-top:calc(.5rem + var(--header-height))}section>span:target{scroll-margin-top:calc(.8rem + var(--header-height))}}.headerlink{font-weight:100;-webkit-user-select:none;-moz-user-select:none;user-select:none}.code-block-caption>.headerlink,dl dt>.headerlink,figcaption p>.headerlink,h1>.headerlink,h2>.headerlink,h3>.headerlink,h4>.headerlink,h5>.headerlink,h6>.headerlink,p.caption>.headerlink,table>caption>.headerlink{margin-left:.5rem;visibility:hidden}.code-block-caption:hover>.headerlink,dl dt:hover>.headerlink,figcaption p:hover>.headerlink,h1:hover>.headerlink,h2:hover>.headerlink,h3:hover>.headerlink,h4:hover>.headerlink,h5:hover>.headerlink,h6:hover>.headerlink,p.caption:hover>.headerlink,table>caption:hover>.headerlink{visibility:visible}.code-block-caption>.toc-backref,dl dt>.toc-backref,figcaption p>.toc-backref,h1>.toc-backref,h2>.toc-backref,h3>.toc-backref,h4>.toc-backref,h5>.toc-backref,h6>.toc-backref,p.caption>.toc-backref,table>caption>.toc-backref{color:inherit;text-decoration-line:none}figure:hover>figcaption>p>.headerlink,table:hover>caption>.headerlink{visibility:visible}:target>h1:first-of-type,:target>h2:first-of-type,:target>h3:first-of-type,:target>h4:first-of-type,:target>h5:first-of-type,:target>h6:first-of-type,span:target~h1:first-of-type,span:target~h2:first-of-type,span:target~h3:first-of-type,span:target~h4:first-of-type,span:target~h5:first-of-type,span:target~h6:first-of-type{background-color:var(--color-highlight-on-target)}:target>h1:first-of-type code.literal,:target>h2:first-of-type code.literal,:target>h3:first-of-type code.literal,:target>h4:first-of-type code.literal,:target>h5:first-of-type code.literal,:target>h6:first-of-type code.literal,span:target~h1:first-of-type code.literal,span:target~h2:first-of-type code.literal,span:target~h3:first-of-type code.literal,span:target~h4:first-of-type code.literal,span:target~h5:first-of-type code.literal,span:target~h6:first-of-type code.literal{background-color:transparent}.literal-block-wrapper:target .code-block-caption,.this-will-duplicate-information-and-it-is-still-useful-here li :target,figure:target,table:target>caption{background-color:var(--color-highlight-on-target)}dt:target{background-color:var(--color-highlight-on-target)!important}.footnote-reference:target,.footnote>dt:target+dd{background-color:var(--color-highlight-on-target)}.guilabel{background-color:var(--color-guilabel-background);border:1px solid var(--color-guilabel-border);border-radius:.5em;color:var(--color-guilabel-text);font-size:.9em;padding:0 .3em}footer{display:flex;flex-direction:column;font-size:var(--font-size--small);margin-top:2rem}.bottom-of-page{align-items:center;border-top:1px solid var(--color-background-border);color:var(--color-foreground-secondary);display:flex;justify-content:space-between;line-height:1.5;margin-top:1rem;padding-bottom:1rem;padding-top:1rem}@media(max-width:46em){.bottom-of-page{flex-direction:column-reverse;gap:.25rem;text-align:center}}.bottom-of-page .left-details{font-size:var(--font-size--small)}.bottom-of-page .right-details{display:flex;flex-direction:column;gap:.25rem;text-align:right}.bottom-of-page .icons{display:flex;font-size:1rem;gap:.25rem;justify-content:flex-end}.bottom-of-page .icons a{text-decoration:none}.bottom-of-page .icons img,.bottom-of-page .icons svg{font-size:1.125rem;height:1em;width:1em}.related-pages a{align-items:center;display:flex;text-decoration:none}.related-pages a:hover .page-info .title{color:var(--color-link);text-decoration:underline;text-decoration-color:var(--color-link-underline)}.related-pages a svg.furo-related-icon,.related-pages a svg.furo-related-icon>use{color:var(--color-foreground-border);flex-shrink:0;height:.75rem;margin:0 .5rem;width:.75rem}.related-pages a.next-page{clear:right;float:right;max-width:50%;text-align:right}.related-pages a.prev-page{clear:left;float:left;max-width:50%}.related-pages a.prev-page svg{transform:rotate(180deg)}.page-info{display:flex;flex-direction:column;overflow-wrap:anywhere}.next-page .page-info{align-items:flex-end}.page-info .context{align-items:center;color:var(--color-foreground-muted);display:flex;font-size:var(--font-size--small);padding-bottom:.1rem;text-decoration:none}ul.search{list-style:none;padding-left:0}ul.search li{border-bottom:1px solid var(--color-background-border);padding:1rem 0}[role=main] .highlighted{background-color:var(--color-highlighted-background);color:var(--color-highlighted-text)}.sidebar-brand{display:flex;flex-direction:column;flex-shrink:0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-decoration:none}.sidebar-brand-text{color:var(--color-sidebar-brand-text);font-size:1.5rem;overflow-wrap:break-word}.sidebar-brand-text,.sidebar-logo-container{margin:var(--sidebar-item-spacing-vertical) 0}.sidebar-logo{display:block;margin:0 auto;max-width:100%}.sidebar-search-container{align-items:center;background:var(--color-sidebar-search-background);display:flex;margin-top:var(--sidebar-search-space-above);position:relative}.sidebar-search-container:focus-within,.sidebar-search-container:hover{background:var(--color-sidebar-search-background--focus)}.sidebar-search-container:before{background-color:var(--color-sidebar-search-icon);content:"";height:var(--sidebar-search-icon-size);left:var(--sidebar-item-spacing-horizontal);-webkit-mask-image:var(--icon-search);mask-image:var(--icon-search);position:absolute;width:var(--sidebar-search-icon-size)}.sidebar-search{background:transparent;border:none;border-bottom:1px solid var(--color-sidebar-search-border);border-top:1px solid var(--color-sidebar-search-border);box-sizing:border-box;color:var(--color-sidebar-search-foreground);padding:var(--sidebar-search-input-spacing-vertical) var(--sidebar-search-input-spacing-horizontal) var(--sidebar-search-input-spacing-vertical) calc(var(--sidebar-item-spacing-horizontal) + var(--sidebar-search-input-spacing-horizontal) + var(--sidebar-search-icon-size));width:100%;z-index:10}.sidebar-search:focus{outline:none}.sidebar-search::-moz-placeholder{font-size:var(--sidebar-search-input-font-size)}.sidebar-search::placeholder{font-size:var(--sidebar-search-input-font-size)}#searchbox .highlight-link{margin:0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0;text-align:center}#searchbox .highlight-link a{color:var(--color-sidebar-search-icon);font-size:var(--font-size--small--2)}.sidebar-tree{font-size:var(--sidebar-item-font-size);margin-bottom:var(--sidebar-item-spacing-vertical);margin-top:var(--sidebar-tree-space-above)}.sidebar-tree ul{display:flex;flex-direction:column;list-style:none;margin-bottom:0;margin-top:0;padding:0}.sidebar-tree li{margin:0;position:relative}.sidebar-tree li>ul{margin-left:var(--sidebar-item-spacing-horizontal)}.sidebar-tree .icon,.sidebar-tree .reference{color:var(--color-sidebar-link-text)}.sidebar-tree .reference{box-sizing:border-box;display:inline-block;height:100%;line-height:var(--sidebar-item-line-height);overflow-wrap:anywhere;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-decoration:none;width:100%}.sidebar-tree .reference:hover{background:var(--color-sidebar-item-background--hover)}.sidebar-tree .reference.external:after{color:var(--color-sidebar-link-text);content:url("data:image/svg+xml;charset=utf-8,%3Csvg width='12' height='12' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' stroke-width='1.5' stroke='%23607D8B' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M0 0h24v24H0z' stroke='none'/%3E%3Cpath d='M11 7H6a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2v-5M10 14 20 4M15 4h5v5'/%3E%3C/svg%3E");margin:0 .25rem;vertical-align:middle}.sidebar-tree .current-page>.reference{font-weight:700}.sidebar-tree label{align-items:center;cursor:pointer;display:flex;height:var(--sidebar-item-height);justify-content:center;position:absolute;right:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:var(--sidebar-expander-width)}.sidebar-tree .caption,.sidebar-tree :not(.caption)>.caption-text{color:var(--color-sidebar-caption-text);font-size:var(--sidebar-caption-font-size);font-weight:700;margin:var(--sidebar-caption-space-above) 0 0 0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-transform:uppercase}.sidebar-tree li.has-children>.reference{padding-right:var(--sidebar-expander-width)}.sidebar-tree .toctree-l1>.reference,.sidebar-tree .toctree-l1>label .icon{color:var(--color-sidebar-link-text--top-level)}.sidebar-tree label{background:var(--color-sidebar-item-expander-background)}.sidebar-tree label:hover{background:var(--color-sidebar-item-expander-background--hover)}.sidebar-tree .current>.reference{background:var(--color-sidebar-item-background--current)}.sidebar-tree .current>.reference:hover{background:var(--color-sidebar-item-background--hover)}.toctree-checkbox{display:none;position:absolute}.toctree-checkbox~ul{display:none}.toctree-checkbox~label .icon svg{transform:rotate(90deg)}.toctree-checkbox:checked~ul{display:block}.toctree-checkbox:checked~label .icon svg{transform:rotate(-90deg)}.toc-title-container{padding:var(--toc-title-padding);padding-top:var(--toc-spacing-vertical)}.toc-title{color:var(--color-toc-title-text);font-size:var(--toc-title-font-size);padding-left:var(--toc-spacing-horizontal);text-transform:uppercase}.no-toc{display:none}.toc-tree-container{padding-bottom:var(--toc-spacing-vertical)}.toc-tree{border-left:1px solid var(--color-background-border);font-size:var(--toc-font-size);line-height:1.3;padding-left:calc(var(--toc-spacing-horizontal) - var(--toc-item-spacing-horizontal))}.toc-tree>ul>li:first-child{padding-top:0}.toc-tree>ul>li:first-child>ul{padding-left:0}.toc-tree>ul>li:first-child>a{display:none}.toc-tree ul{list-style-type:none;margin-bottom:0;margin-top:0;padding-left:var(--toc-item-spacing-horizontal)}.toc-tree li{padding-top:var(--toc-item-spacing-vertical)}.toc-tree li.scroll-current>.reference{color:var(--color-toc-item-text--active);font-weight:700}.toc-tree .reference{color:var(--color-toc-item-text);overflow-wrap:anywhere;text-decoration:none}.toc-scroll{max-height:100vh;overflow-y:scroll}.contents:not(.this-will-duplicate-information-and-it-is-still-useful-here){background:rgba(255,0,0,.25);color:var(--color-problematic)}.contents:not(.this-will-duplicate-information-and-it-is-still-useful-here):before{content:"ERROR: Adding a table of contents in Furo-based documentation is unnecessary, and does not work well with existing styling.Add a 'this-will-duplicate-information-and-it-is-still-useful-here' class, if you want an escape hatch."}.text-align\:left>p{text-align:left}.text-align\:center>p{text-align:center}.text-align\:right>p{text-align:right} -/*# sourceMappingURL=furo.css.map*/ \ No newline at end of file diff --git a/docs/_build/html/_static/styles/furo.css.map b/docs/_build/html/_static/styles/furo.css.map deleted file mode 100644 index d1dfb109d..000000000 --- a/docs/_build/html/_static/styles/furo.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"styles/furo.css","mappings":"AAAA,2EAA2E,CAU3E,KAEE,6BAA8B,CAD9B,gBAEF,CASA,KACE,QACF,CAMA,KACE,aACF,CAOA,GACE,aAAc,CACd,cACF,CAUA,GACE,sBAAuB,CACvB,QAAS,CACT,gBACF,CAOA,IACE,+BAAiC,CACjC,aACF,CASA,EACE,4BACF,CAOA,YACE,kBAAmB,CACnB,yBAA0B,CAC1B,gCACF,CAMA,SAEE,kBACF,CAOA,cAGE,+BAAiC,CACjC,aACF,CAeA,QAEE,aAAc,CACd,aAAc,CACd,iBAAkB,CAClB,uBACF,CAEA,IACE,aACF,CAEA,IACE,SACF,CASA,IACE,iBACF,CAUA,sCAKE,mBAAoB,CACpB,cAAe,CACf,gBAAiB,CACjB,QACF,CAOA,aAEE,gBACF,CAOA,cAEE,mBACF,CAMA,gDAIE,yBACF,CAMA,wHAIE,iBAAkB,CAClB,SACF,CAMA,4GAIE,6BACF,CAMA,SACE,0BACF,CASA,OACE,qBAAsB,CACtB,aAAc,CACd,aAAc,CACd,cAAe,CACf,SAAU,CACV,kBACF,CAMA,SACE,uBACF,CAMA,SACE,aACF,CAOA,6BAEE,qBAAsB,CACtB,SACF,CAMA,kFAEE,WACF,CAOA,cACE,4BAA6B,CAC7B,mBACF,CAMA,yCACE,uBACF,CAOA,6BACE,yBAA0B,CAC1B,YACF,CASA,QACE,aACF,CAMA,QACE,iBACF,CAiBA,kBACE,YACF,CCvVA,aAcE,kEACE,uBAOF,WACE,iDAMF,gCACE,wBAEF,qCAEE,uBADA,uBACA,CAEF,SACE,wBAtBA,CCpBJ,iBAOE,6BAEA,mBANA,qBAEA,sBACA,0BAFA,oBAHA,4BAOA,6BANA,mBAOA,CAEF,gBACE,aCPF,KCGE,mHAEA,wGAGA,wBAAyB,CACzB,wBAAyB,CACzB,4BAA6B,CAC7B,yBAA0B,CAC1B,2BAA4B,CAG5B,sDAAuD,CACvD,gDAAiD,CACjD,wDAAyD,CAGzD,0CAA2C,CAC3C,gDAAiD,CACjD,gDAAiD,CAKjD,gCAAiC,CACjC,sCAAuC,CAGvC,2CAA4C,CAG5C,uCAAwC,CChCxC,+FAGA,uBAAwB,CAGxB,iCAAkC,CAClC,kCAAmC,CAEnC,+BAAgC,CAChC,sCAAuC,CACvC,sCAAuC,CACvC,qGAIA,mDAAoD,CAEpD,mCAAoC,CACpC,8CAA+C,CAC/C,gDAAiD,CACjD,kCAAmC,CACnC,6DAA8D,CAG9D,6BAA8B,CAC9B,6BAA8B,CAC9B,+BAAgC,CAChC,kCAAmC,CACnC,kCAAmC,CCPjC,ukBCYA,srCAZF,kaCVA,mLAOA,oTAWA,2UAaA,0CACA,gEACA,0CAGA,gEAUA,yCACA,+DAGA,4CACA,CACA,iEAGA,sGACA,uCACA,4DAGA,sCACA,2DAEA,4CACA,kEACA,oGACA,CAEA,0GACA,+CAGA,+MAOA,+EACA,wCAIA,4DACA,sEACA,kEACA,sEACA,gDAGA,+DACA,0CACA,gEACA,gGACA,CAGA,2DACA,qDAGA,0CACA,8CACA,oDACA,oDL7GF,iCAEA,iEAME,oCKyGA,yDAIA,sCACA,kCACA,sDAGA,0CACA,kEACA,oDAEA,sDAGA,oCACA,oEAIA,CAGA,yDAGA,qDACA,oDAGA,6DAIA,iEAGA,2DAEA,2DL9IE,4DAEA,gEAIF,gEKgGA,gFAIA,oNAOA,qDAEA,gFAIA,4DAIA,oEAMA,yEAIA,6DACA,0DAGA,uDAGA,qDAEA,wDLpII,6DAEA,yDACE,2DAMN,uCAIA,yCACE,8CAGF,sDMjDA,6DAKA,oCAIA,4CACA,kBAGF,sBAMA,2BAME,qCAGA,qCAEA,iCAEA,+BAEA,mCAEA,qCAIA,CACA,gCACA,gDAKA,kCAIA,6BAEA,0CAQA,kCAIF,8BAGE,8BACA,uCAGF,sCAKE,kCAEA,sDAGA,iCACE,CACA,2FAGA,gCACE,CACA,+DCzEJ,wCAEA,sBAEF,yDAEE,mCACA,wDAGA,2GAGA,wIACE,gDAMJ,kCAGE,6BACA,0CAGA,gEACA,8BACA,uCAKA,sCAIA,kCACA,sDACA,iCACA,sCAOA,sDAKE,gGAIE,+CAGN,sBAEE,yCAMA,0BAMA,yLAMA,aACA,MAEF,6BACE,2DAIF,wCAIE,kCAGA,SACA,kCAKA,mBAGA,CAJA,eACA,CAHF,gBAEE,CAWA,mBACA,mBACA,mDAGA,YACA,CACA,kBACA,CAEE,kBAKJ,OAPE,kBAQA,CADF,GACE,iCACA,wCAEA,wBACA,aACA,CAFA,WAEA,GACA,oBACA,CAFA,gBAEA,aACE,+CAIF,UAJE,kCAIF,WACA,iBACA,GAGA,uBACE,CAJF,yBAGA,CACE,iDACA,uCAEA,yDACE,cACA,wDAKN,yDAIE,uBAEF,kBACE,uBAEA,kDAIA,0DAGA,CAHA,oBAGA,0GAYA,aAEA,CAHA,YAGA,4HAKF,+CAGE,sBAEF,WAKE,0CAEA,CALA,qCAGA,CAJA,WAOA,SAIA,2CAJA,qCAIA,CACE,wBACA,OACA,YAEJ,gBACE,gBAIA,+CAKF,CAGE,kDAGA,CANF,8BAGE,CAGA,YAEA,CAdF,2BACE,CAHA,UAEF,CAYE,UAEA,CACA,0CACF,iEAOE,iCACA,8BAGA,wCAIA,wBAKE,0CAKF,CARE,6DAGA,CALF,qBAEE,CASA,YACA,yBAGA,CAEE,cAKN,CAPI,sBAOJ,gCAGE,qBAEA,WACA,aACA,sCAEA,mBACA,6BAGA,uEADA,qBACA,6BAIA,yBACA,qCAEE,UAEA,YACA,sBAEF,8BAGA,CAPE,aACA,WAMF,4BACE,sBACA,WAMJ,uBACE,cAYE,mBAXA,qDAKA,qCAGA,CAEA,YACA,CAHA,2BAEA,CACA,oCAEA,4CACA,uBAIA,oCAEJ,CAFI,cAIF,iBACE,CAHJ,kBAGI,yBAEA,oCAIA,qDAMF,mEAEA,CACE,8CAKA,gCAEA,qCAGA,oCAGE,sBACA,CAJF,WAEE,CAFF,eAEE,SAEA,mBACA,qCACE,aACA,CAFF,YADA,qBACA,WAEE,sBACA,kEAEN,2BAEE,iDAKA,uCAGF,CACE,0DAKA,kBACF,CAFE,sBAGA,mBACA,0BAEJ,yBAII,aADA,WACA,CAMF,UAFE,kBAEF,CAJF,gBACE,CAHE,iBAMF,6CC9ZF,yBACE,WACA,iBAEA,aAFA,iBAEA,6BAEA,kCACA,mBAKA,gCAGA,CARA,QAEA,CAGA,UALA,qBAEA,qDAGA,CALA,OAQA,4BACE,cAGF,2BACE,gCAEJ,CAHE,UAGF,8CAGE,CAHF,UAGE,wCAGA,qBACA,CAFA,UAEA,6CAGA,yCAIA,sBAHA,UAGA,kCACE,OACA,CAFF,KAEE,cAQF,0CACE,CAFF,kBACA,CACE,wEACA,CARA,YACA,CAKF,mBAFF,OAII,eACA,CAJF,iCAJE,cAGJ,CANI,oBAEA,CAKF,SAIE,2BADA,UACA,kBAGF,sCACA,CAFF,WACE,WACA,qCACE,gCACA,2EACA,sDAKJ,aACE,mDAII,CAJJ,6CAII,kEACA,iBACE,iDACA,+CACE,aACA,WADA,+BACA,uEANN,YACE,mDAEE,mBADF,0CACE,CADF,qBACE,0DACA,YACE,4DACA,sEANN,YACE,8CACA,kBADA,UACA,2CACE,2EACA,cACE,kEACA,mEANN,yBACE,4DACA,sBACE,+EAEE,iEACA,qEANN,sCACE,CAGE,iBAHF,gBAGE,qBACE,CAJJ,uBACA,gDACE,wDACA,6DAHF,2CACA,CADA,gBACA,eACE,CAGE,sBANN,8BACE,CAII,iBAFF,4DACA,WACE,YADF,uCACE,6EACA,2BANN,8CACE,kDACA,0CACE,8BACA,yFACE,sBACA,sFALJ,mEACA,sBACE,kEACA,6EACE,uCACA,kEALJ,qGAEE,kEACA,6EACE,uCACA,kEALJ,8CACA,uDACE,sEACA,2EACE,sCACA,iEALJ,mGACA,qCACE,oDACA,0DACE,6GACA,gDAGR,yDCrEA,sEACE,CACA,6GACE,gEACF,iGAIF,wFACE,qDAGA,mGAEE,2CAEF,4FACE,gCACF,wGACE,8DAEE,6FAIA,iJAKN,6GACE,gDAKF,yDACA,qCAGA,6BACA,kBACA,qDAKA,oCAEA,+DAGA,2CAGE,oDAIA,oEAEE,qBAGJ,wDAEE,uCAEF,kEAGA,8CAEA,uDAKA,oCAEA,yDAEE,gEAKF,+CC5FA,0EAGE,CACA,qDCLJ,+DAIE,sCAIA,kEACE,yBACA,2FAMA,gBACA,yGCbF,mBAOA,2MAIA,4HAYA,0DACE,8GAYF,8HAQE,mBAEA,6HAOF,YAGA,mIAME,eACA,CAFF,YAEE,4FAMJ,8BAEE,uBAYA,sCAEE,CAJF,oBAEA,CARA,wCAEA,CAHA,8BACA,CAFA,eACA,CAGA,wCAEA,CAEA,mDAIE,kCACE,6BACA,4CAKJ,kDAIA,eACE,aAGF,8BACE,uDACA,sCACA,cAEA,+BACA,CAFA,eAEA,wCAEF,YACE,iBACA,mCACA,0DAGF,qBAEE,CAFF,kBAEE,+BAIA,yCAEE,qBADA,gBACA,yBAKF,eACA,CAFF,YACE,CACA,iBACA,qDAEA,mDCvIJ,2FAOE,iCACA,CAEA,eACA,CAHA,kBAEA,CAFA,wBAGA,8BACA,eACE,CAFF,YAEE,0BACA,8CAGA,oBACE,oCAGA,kBACE,8DAEA,iBAEN,UACE,8BAIJ,+CAEE,qDAEF,kDAIE,YAEF,CAFE,YAEF,CCjCE,mFAJA,QACA,UAIE,CADF,iBACE,mCAGA,iDACE,+BAGF,wBAEA,mBAKA,6CAEF,CAHE,mBACA,CAEF,kCAIE,CARA,kBACA,CAFF,eASE,YACA,mBAGF,CAJE,UAIF,wCCjCA,oBDmCE,wBCpCJ,uCACE,8BACA,4CACA,oBAGA,2CCAA,6CAGE,CAPF,uBAIA,CDGA,gDACE,6BCVJ,CAWM,2CAEF,CAJA,kCAEE,CDJF,aCLF,gBDKE,uBCMA,gCAGA,gDAGE,wBAGJ,0BAEA,iBACE,aACF,CADE,UACF,uBACE,aACF,oBACE,YACF,4BACE,6CAMA,CAYF,6DAZE,mCAGE,iCASJ,4BAGE,4DADA,+BACA,CAFA,qBAEA,yBACE,aAEF,wBAHA,SAGA,iHACE,2DAKF,CANA,yCACE,CADF,oCAMA,uSAIA,sGACE,oDChEJ,WAEF,yBACE,QACA,eAEA,gBAEE,uCAGA,CALF,iCAKE,uCAGA,0BACA,CACA,oBACA,iCClBJ,gBACE,KAGF,qBACE,YAGF,CAHE,cAGF,gCAEE,mBACA,iEAEA,oCACA,wCAEA,sBACA,WAEA,CAFA,YAEA,8EAEA,mCAFA,iBAEA,6BAIA,wEAKA,sDAIE,CARF,mDAIA,CAIE,cAEF,8CAIA,oBAFE,iBAEF,8CAGE,eAEF,CAFE,YAEF,OAEE,kBAGJ,CAJI,eACA,CAFF,mBAKF,yCCjDE,oBACA,CAFA,iBAEA,uCAKE,iBACA,qCAGA,mBCZJ,CDWI,gBCXJ,6BAEE,eACA,sBAGA,eAEA,sBACA,oDACA,iGAMA,gBAFE,YAEF,8FAME,iJClBF,YACA,gNAUE,6BAEF,oTAcI,kBACF,gHAIA,qBACE,eACF,qDACE,kBACF,6DACE,4BCxCJ,oBAEF,qCAEI,+CAGF,uBACE,uDAGJ,oBAkBE,mDAhBA,+CAaA,CAbA,oBAaA,0FAEE,CAFF,gGAbA,+BAaA,0BAGA,mQAIA,oNAEE,iBAGJ,CAHI,gBADA,gBAIJ,8CAYI,CAZJ,wCAYI,sVACE,iCAGA,uEAHA,QAGA,qXAKJ,iDAGF,CARM,+CACE,iDAIN,CALI,gBAQN,mHACE,gBAGF,2DACE,0EAOA,0EAKA,6EC/EA,iDACA,gCACA,oDAGA,qBACA,oDCFA,cACA,eAEA,yBAGF,sBAEE,iBACA,sNAWA,iBACE,kBACA,wRAgBA,kBAEA,iOAgBA,uCACE,uEAEA,kBAEF,qUAuBE,iDAIJ,CACA,geCxFF,4BAEE,CAQA,6JACA,iDAIA,sEAGA,mDAOF,iDAGE,4DAIA,8CACA,qDAEE,eAFF,cAEE,oBAEF,uBAFE,kCAGA,eACA,iBACA,mBAIA,mDACA,CAHA,uCAEA,CAJA,0CACA,CAIA,gBAJA,gBACA,oBADA,gBAIA,wBAEJ,gBAGE,6BACA,YAHA,iBAGA,gCACA,iEAEA,6CACA,sDACA,0BADA,wBACA,0BACA,oIAIA,mBAFA,YAEA,qBACA,0CAIE,uBAEF,CAHA,yBACE,CAEF,iDACE,mFAKJ,oCACE,CANE,aAKJ,CACE,qEAIA,YAFA,WAEA,CAHA,aACA,CAEA,gBACE,4BACA,sBADA,aACA,gCAMF,oCACA,yDACA,2CAEA,qBAGE,kBAEA,CACA,mCAIF,CARE,YACA,CAOF,iCAEE,CAPA,oBACA,CAQA,oBACE,uDAEJ,sDAGA,CAHA,cAGA,0BACE,oDAIA,oCACA,4BACA,sBAGA,cAEA,oFAGA,sBAEA,yDACE,CAIA,iBAJA,wBAIA,6CAJA,6CAOA,4BAGJ,CAHI,cAGJ,yCAGA,kBACE,CAIA,iDAEA,CATA,YAEF,CACE,4CAGA,kBAIA,wEAEA,wDAIF,kCAOE,iDACA,CARF,WAIE,sCAGA,CANA,2CACA,CAMA,oEARF,iBACE,CACA,qCAMA,iBAuBE,uBAlBF,YAKA,2DALA,uDAKA,CALA,sBAiBA,4CACE,CALA,gRAIF,YACE,UAEN,uBACE,YACA,mCAOE,+CAGA,8BAGF,+CAGA,4BCjNA,SDiNA,qFCjNA,gDAGA,sCACA,qCACA,sDAIF,CAIE,kDAGA,CAPF,0CAOE,kBAEA,kDAEA,CAHA,eACA,CAFA,YACA,CADA,SAIA,mHAIE,CAGA,6CAFA,oCAeE,CAbF,yBACE,qBAEJ,CAGE,oBACA,CAEA,YAFA,2CACF,CACE,uBAEA,mFAEE,CALJ,oBACE,CAEA,UAEE,gCAGF,sDAEA,yCC7CJ,oCAGA,CD6CE,yXAQE,sCCrDJ,wCAGA,oCACE","sources":["webpack:///./node_modules/normalize.css/normalize.css","webpack:///./src/furo/assets/styles/base/_print.sass","webpack:///./src/furo/assets/styles/base/_screen-readers.sass","webpack:///./src/furo/assets/styles/base/_theme.sass","webpack:///./src/furo/assets/styles/variables/_fonts.scss","webpack:///./src/furo/assets/styles/variables/_spacing.scss","webpack:///./src/furo/assets/styles/variables/_icons.scss","webpack:///./src/furo/assets/styles/variables/_admonitions.scss","webpack:///./src/furo/assets/styles/variables/_colors.scss","webpack:///./src/furo/assets/styles/base/_typography.sass","webpack:///./src/furo/assets/styles/_scaffold.sass","webpack:///./src/furo/assets/styles/content/_admonitions.sass","webpack:///./src/furo/assets/styles/content/_api.sass","webpack:///./src/furo/assets/styles/content/_blocks.sass","webpack:///./src/furo/assets/styles/content/_captions.sass","webpack:///./src/furo/assets/styles/content/_code.sass","webpack:///./src/furo/assets/styles/content/_footnotes.sass","webpack:///./src/furo/assets/styles/content/_images.sass","webpack:///./src/furo/assets/styles/content/_indexes.sass","webpack:///./src/furo/assets/styles/content/_lists.sass","webpack:///./src/furo/assets/styles/content/_math.sass","webpack:///./src/furo/assets/styles/content/_misc.sass","webpack:///./src/furo/assets/styles/content/_rubrics.sass","webpack:///./src/furo/assets/styles/content/_sidebar.sass","webpack:///./src/furo/assets/styles/content/_tables.sass","webpack:///./src/furo/assets/styles/content/_target.sass","webpack:///./src/furo/assets/styles/content/_gui-labels.sass","webpack:///./src/furo/assets/styles/components/_footer.sass","webpack:///./src/furo/assets/styles/components/_sidebar.sass","webpack:///./src/furo/assets/styles/components/_table_of_contents.sass","webpack:///./src/furo/assets/styles/_shame.sass"],"sourcesContent":["/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */\n\n/* Document\n ========================================================================== */\n\n/**\n * 1. Correct the line height in all browsers.\n * 2. Prevent adjustments of font size after orientation changes in iOS.\n */\n\nhtml {\n line-height: 1.15; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/* Sections\n ========================================================================== */\n\n/**\n * Remove the margin in all browsers.\n */\n\nbody {\n margin: 0;\n}\n\n/**\n * Render the `main` element consistently in IE.\n */\n\nmain {\n display: block;\n}\n\n/**\n * Correct the font size and margin on `h1` elements within `section` and\n * `article` contexts in Chrome, Firefox, and Safari.\n */\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n/* Grouping content\n ========================================================================== */\n\n/**\n * 1. Add the correct box sizing in Firefox.\n * 2. Show the overflow in Edge and IE.\n */\n\nhr {\n box-sizing: content-box; /* 1 */\n height: 0; /* 1 */\n overflow: visible; /* 2 */\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\npre {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/* Text-level semantics\n ========================================================================== */\n\n/**\n * Remove the gray background on active links in IE 10.\n */\n\na {\n background-color: transparent;\n}\n\n/**\n * 1. Remove the bottom border in Chrome 57-\n * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n */\n\nabbr[title] {\n border-bottom: none; /* 1 */\n text-decoration: underline; /* 2 */\n text-decoration: underline dotted; /* 2 */\n}\n\n/**\n * Add the correct font weight in Chrome, Edge, and Safari.\n */\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/**\n * Add the correct font size in all browsers.\n */\n\nsmall {\n font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` elements from affecting the line height in\n * all browsers.\n */\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/* Embedded content\n ========================================================================== */\n\n/**\n * Remove the border on images inside links in IE 10.\n */\n\nimg {\n border-style: none;\n}\n\n/* Forms\n ========================================================================== */\n\n/**\n * 1. Change the font styles in all browsers.\n * 2. Remove the margin in Firefox and Safari.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-size: 100%; /* 1 */\n line-height: 1.15; /* 1 */\n margin: 0; /* 2 */\n}\n\n/**\n * Show the overflow in IE.\n * 1. Show the overflow in Edge.\n */\n\nbutton,\ninput { /* 1 */\n overflow: visible;\n}\n\n/**\n * Remove the inheritance of text transform in Edge, Firefox, and IE.\n * 1. Remove the inheritance of text transform in Firefox.\n */\n\nbutton,\nselect { /* 1 */\n text-transform: none;\n}\n\n/**\n * Correct the inability to style clickable types in iOS and Safari.\n */\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\n/**\n * Remove the inner border and padding in Firefox.\n */\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n border-style: none;\n padding: 0;\n}\n\n/**\n * Restore the focus styles unset by the previous rule.\n */\n\nbutton:-moz-focusring,\n[type=\"button\"]:-moz-focusring,\n[type=\"reset\"]:-moz-focusring,\n[type=\"submit\"]:-moz-focusring {\n outline: 1px dotted ButtonText;\n}\n\n/**\n * Correct the padding in Firefox.\n */\n\nfieldset {\n padding: 0.35em 0.75em 0.625em;\n}\n\n/**\n * 1. Correct the text wrapping in Edge and IE.\n * 2. Correct the color inheritance from `fieldset` elements in IE.\n * 3. Remove the padding so developers are not caught out when they zero out\n * `fieldset` elements in all browsers.\n */\n\nlegend {\n box-sizing: border-box; /* 1 */\n color: inherit; /* 2 */\n display: table; /* 1 */\n max-width: 100%; /* 1 */\n padding: 0; /* 3 */\n white-space: normal; /* 1 */\n}\n\n/**\n * Add the correct vertical alignment in Chrome, Firefox, and Opera.\n */\n\nprogress {\n vertical-align: baseline;\n}\n\n/**\n * Remove the default vertical scrollbar in IE 10+.\n */\n\ntextarea {\n overflow: auto;\n}\n\n/**\n * 1. Add the correct box sizing in IE 10.\n * 2. Remove the padding in IE 10.\n */\n\n[type=\"checkbox\"],\n[type=\"radio\"] {\n box-sizing: border-box; /* 1 */\n padding: 0; /* 2 */\n}\n\n/**\n * Correct the cursor style of increment and decrement buttons in Chrome.\n */\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n/**\n * 1. Correct the odd appearance in Chrome and Safari.\n * 2. Correct the outline style in Safari.\n */\n\n[type=\"search\"] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/**\n * Remove the inner padding in Chrome and Safari on macOS.\n */\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/**\n * 1. Correct the inability to style clickable types in iOS and Safari.\n * 2. Change font properties to `inherit` in Safari.\n */\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/* Interactive\n ========================================================================== */\n\n/*\n * Add the correct display in Edge, IE 10+, and Firefox.\n */\n\ndetails {\n display: block;\n}\n\n/*\n * Add the correct display in all browsers.\n */\n\nsummary {\n display: list-item;\n}\n\n/* Misc\n ========================================================================== */\n\n/**\n * Add the correct display in IE 10+.\n */\n\ntemplate {\n display: none;\n}\n\n/**\n * Add the correct display in IE 10.\n */\n\n[hidden] {\n display: none;\n}\n","// This file contains styles for managing print media.\n\n////////////////////////////////////////////////////////////////////////////////\n// Hide elements not relevant to print media.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n // Hide icon container.\n .content-icon-container\n display: none !important\n\n // Hide showing header links if hovering over when printing.\n .headerlink\n display: none !important\n\n // Hide mobile header.\n .mobile-header\n display: none !important\n\n // Hide navigation links.\n .related-pages\n display: none !important\n\n////////////////////////////////////////////////////////////////////////////////\n// Tweaks related to decolorization.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n // Apply a border around code which no longer have a color background.\n .highlight\n border: 0.1pt solid var(--color-foreground-border)\n\n////////////////////////////////////////////////////////////////////////////////\n// Avoid page break in some relevant cases.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n ul, ol, dl, a, table, pre, blockquote\n page-break-inside: avoid\n\n h1, h2, h3, h4, h5, h6, img, figure, caption\n page-break-inside: avoid\n page-break-after: avoid\n\n ul, ol, dl\n page-break-before: avoid\n",".visually-hidden\n position: absolute !important\n width: 1px !important\n height: 1px !important\n padding: 0 !important\n margin: -1px !important\n overflow: hidden !important\n clip: rect(0,0,0,0) !important\n white-space: nowrap !important\n border: 0 !important\n\n:-moz-focusring\n outline: auto\n","// This file serves as the \"skeleton\" of the theming logic.\n//\n// This contains the bulk of the logic for handling dark mode, color scheme\n// toggling and the handling of color-scheme-specific hiding of elements.\n\nbody\n @include fonts\n @include spacing\n @include icons\n @include admonitions\n @include default-admonition(#651fff, \"abstract\")\n @include default-topic(#14B8A6, \"pencil\")\n\n @include colors\n\n.only-light\n display: block !important\nhtml body .only-dark\n display: none !important\n\n// Ignore dark-mode hints if print media.\n@media not print\n // Enable dark-mode, if requested.\n body[data-theme=\"dark\"]\n @include colors-dark\n\n html & .only-light\n display: none !important\n .only-dark\n display: block !important\n\n // Enable dark mode, unless explicitly told to avoid.\n @media (prefers-color-scheme: dark)\n body:not([data-theme=\"light\"])\n @include colors-dark\n\n html & .only-light\n display: none !important\n .only-dark\n display: block !important\n\n//\n// Theme toggle presentation\n//\nbody[data-theme=\"auto\"]\n .theme-toggle svg.theme-icon-when-auto\n display: block\n\nbody[data-theme=\"dark\"]\n .theme-toggle svg.theme-icon-when-dark\n display: block\n\nbody[data-theme=\"light\"]\n .theme-toggle svg.theme-icon-when-light\n display: block\n","// Fonts used by this theme.\n//\n// There are basically two things here -- using the system font stack and\n// defining sizes for various elements in %ages. We could have also used `em`\n// but %age is easier to reason about for me.\n\n@mixin fonts {\n // These are adapted from https://systemfontstack.com/\n --font-stack: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,\n sans-serif, Apple Color Emoji, Segoe UI Emoji;\n --font-stack--monospace: \"SFMono-Regular\", Menlo, Consolas, Monaco,\n Liberation Mono, Lucida Console, monospace;\n\n --font-size--normal: 100%;\n --font-size--small: 87.5%;\n --font-size--small--2: 81.25%;\n --font-size--small--3: 75%;\n --font-size--small--4: 62.5%;\n\n // Sidebar\n --sidebar-caption-font-size: var(--font-size--small--2);\n --sidebar-item-font-size: var(--font-size--small);\n --sidebar-search-input-font-size: var(--font-size--small);\n\n // Table of Contents\n --toc-font-size: var(--font-size--small--3);\n --toc-font-size--mobile: var(--font-size--normal);\n --toc-title-font-size: var(--font-size--small--4);\n\n // Admonitions\n //\n // These aren't defined in terms of %ages, since nesting these is permitted.\n --admonition-font-size: 0.8125rem;\n --admonition-title-font-size: 0.8125rem;\n\n // Code\n --code-font-size: var(--font-size--small--2);\n\n // API\n --api-font-size: var(--font-size--small);\n}\n","// Spacing for various elements on the page\n//\n// If the user wants to tweak things in a certain way, they are permitted to.\n// They also have to deal with the consequences though!\n\n@mixin spacing {\n // Header!\n --header-height: calc(\n var(--sidebar-item-line-height) + 4 * #{var(--sidebar-item-spacing-vertical)}\n );\n --header-padding: 0.5rem;\n\n // Sidebar\n --sidebar-tree-space-above: 1.5rem;\n --sidebar-caption-space-above: 1rem;\n\n --sidebar-item-line-height: 1rem;\n --sidebar-item-spacing-vertical: 0.5rem;\n --sidebar-item-spacing-horizontal: 1rem;\n --sidebar-item-height: calc(\n var(--sidebar-item-line-height) + 2 *#{var(--sidebar-item-spacing-vertical)}\n );\n\n --sidebar-expander-width: var(--sidebar-item-height); // be square\n\n --sidebar-search-space-above: 0.5rem;\n --sidebar-search-input-spacing-vertical: 0.5rem;\n --sidebar-search-input-spacing-horizontal: 0.5rem;\n --sidebar-search-input-height: 1rem;\n --sidebar-search-icon-size: var(--sidebar-search-input-height);\n\n // Table of Contents\n --toc-title-padding: 0.25rem 0;\n --toc-spacing-vertical: 1.5rem;\n --toc-spacing-horizontal: 1.5rem;\n --toc-item-spacing-vertical: 0.4rem;\n --toc-item-spacing-horizontal: 1rem;\n}\n","// Expose theme icons as CSS variables.\n\n$icons: (\n // Adapted from tabler-icons\n // url: https://tablericons.com/\n \"search\":\n url('data:image/svg+xml;charset=utf-8,'),\n // Factored out from mkdocs-material on 24-Aug-2020.\n // url: https://squidfunk.github.io/mkdocs-material/reference/admonitions/\n \"pencil\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"abstract\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"info\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"flame\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"question\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"warning\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"failure\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"spark\":\n url('data:image/svg+xml;charset=utf-8,')\n);\n\n@mixin icons {\n @each $name, $glyph in $icons {\n --icon-#{$name}: #{$glyph};\n }\n}\n","// Admonitions\n\n// Structure of these is:\n// admonition-class: color \"icon-name\";\n//\n// The colors are translated into CSS variables below. The icons are\n// used directly in the main declarations to set the `mask-image` in\n// the title.\n\n// prettier-ignore\n$admonitions: (\n // Each of these has an reST directives for it.\n \"caution\": #ff9100 \"spark\",\n \"warning\": #ff9100 \"warning\",\n \"danger\": #ff5252 \"spark\",\n \"attention\": #ff5252 \"warning\",\n \"error\": #ff5252 \"failure\",\n \"hint\": #00c852 \"question\",\n \"tip\": #00c852 \"info\",\n \"important\": #00bfa5 \"flame\",\n \"note\": #00b0ff \"pencil\",\n \"seealso\": #448aff \"info\",\n \"admonition-todo\": #808080 \"pencil\"\n);\n\n@mixin default-admonition($color, $icon-name) {\n --color-admonition-title: #{$color};\n --color-admonition-title-background: #{rgba($color, 0.2)};\n\n --icon-admonition-default: var(--icon-#{$icon-name});\n}\n\n@mixin default-topic($color, $icon-name) {\n --color-topic-title: #{$color};\n --color-topic-title-background: #{rgba($color, 0.2)};\n\n --icon-topic-default: var(--icon-#{$icon-name});\n}\n\n@mixin admonitions {\n @each $name, $values in $admonitions {\n --color-admonition-title--#{$name}: #{nth($values, 1)};\n --color-admonition-title-background--#{$name}: #{rgba(\n nth($values, 1),\n 0.2\n )};\n }\n}\n","// Colors used throughout this theme.\n//\n// The aim is to give the user more control. Thus, instead of hard-coding colors\n// in various parts of the stylesheet, the approach taken is to define all\n// colors as CSS variables and reusing them in all the places.\n//\n// `colors-dark` depends on `colors` being included at a lower specificity.\n\n@mixin colors {\n --color-problematic: #b30000;\n\n // Base Colors\n --color-foreground-primary: black; // for main text and headings\n --color-foreground-secondary: #5a5c63; // for secondary text\n --color-foreground-muted: #646776; // for muted text\n --color-foreground-border: #878787; // for content borders\n\n --color-background-primary: white; // for content\n --color-background-secondary: #f8f9fb; // for navigation + ToC\n --color-background-hover: #efeff4ff; // for navigation-item hover\n --color-background-hover--transparent: #efeff400;\n --color-background-border: #eeebee; // for UI borders\n --color-background-item: #ccc; // for \"background\" items (eg: copybutton)\n\n // Announcements\n --color-announcement-background: #000000dd;\n --color-announcement-text: #eeebee;\n\n // Brand colors\n --color-brand-primary: #2962ff;\n --color-brand-content: #2a5adf;\n\n // API documentation\n --color-api-background: var(--color-background-hover--transparent);\n --color-api-background-hover: var(--color-background-hover);\n --color-api-overall: var(--color-foreground-secondary);\n --color-api-name: var(--color-problematic);\n --color-api-pre-name: var(--color-problematic);\n --color-api-paren: var(--color-foreground-secondary);\n --color-api-keyword: var(--color-foreground-primary);\n --color-highlight-on-target: #ffffcc;\n\n // Inline code background\n --color-inline-code-background: var(--color-background-secondary);\n\n // Highlighted text (search)\n --color-highlighted-background: #ddeeff;\n --color-highlighted-text: var(--color-foreground-primary);\n\n // GUI Labels\n --color-guilabel-background: #ddeeff80;\n --color-guilabel-border: #bedaf580;\n --color-guilabel-text: var(--color-foreground-primary);\n\n // Admonitions!\n --color-admonition-background: transparent;\n\n //////////////////////////////////////////////////////////////////////////////\n // Everything below this should be one of:\n // - var(...)\n // - *-gradient(...)\n // - special literal values (eg: transparent, none)\n //////////////////////////////////////////////////////////////////////////////\n\n // Tables\n --color-table-header-background: var(--color-background-secondary);\n --color-table-border: var(--color-background-border);\n\n // Cards\n --color-card-border: var(--color-background-secondary);\n --color-card-background: transparent;\n --color-card-marginals-background: var(--color-background-secondary);\n\n // Header\n --color-header-background: var(--color-background-primary);\n --color-header-border: var(--color-background-border);\n --color-header-text: var(--color-foreground-primary);\n\n // Sidebar (left)\n --color-sidebar-background: var(--color-background-secondary);\n --color-sidebar-background-border: var(--color-background-border);\n\n --color-sidebar-brand-text: var(--color-foreground-primary);\n --color-sidebar-caption-text: var(--color-foreground-muted);\n --color-sidebar-link-text: var(--color-foreground-secondary);\n --color-sidebar-link-text--top-level: var(--color-brand-primary);\n\n --color-sidebar-item-background: var(--color-sidebar-background);\n --color-sidebar-item-background--current: var(\n --color-sidebar-item-background\n );\n --color-sidebar-item-background--hover: linear-gradient(\n 90deg,\n var(--color-background-hover--transparent) 0%,\n var(--color-background-hover) var(--sidebar-item-spacing-horizontal),\n var(--color-background-hover) 100%\n );\n\n --color-sidebar-item-expander-background: transparent;\n --color-sidebar-item-expander-background--hover: var(\n --color-background-hover\n );\n\n --color-sidebar-search-text: var(--color-foreground-primary);\n --color-sidebar-search-background: var(--color-background-secondary);\n --color-sidebar-search-background--focus: var(--color-background-primary);\n --color-sidebar-search-border: var(--color-background-border);\n --color-sidebar-search-icon: var(--color-foreground-muted);\n\n // Table of Contents (right)\n --color-toc-background: var(--color-background-primary);\n --color-toc-title-text: var(--color-foreground-muted);\n --color-toc-item-text: var(--color-foreground-secondary);\n --color-toc-item-text--hover: var(--color-foreground-primary);\n --color-toc-item-text--active: var(--color-brand-primary);\n\n // Actual page contents\n --color-content-foreground: var(--color-foreground-primary);\n --color-content-background: transparent;\n\n // Links\n --color-link: var(--color-brand-content);\n --color-link--hover: var(--color-brand-content);\n --color-link-underline: var(--color-background-border);\n --color-link-underline--hover: var(--color-foreground-border);\n}\n\n@mixin colors-dark {\n --color-problematic: #ee5151;\n\n // Base Colors\n --color-foreground-primary: #ffffffcc; // for main text and headings\n --color-foreground-secondary: #9ca0a5; // for secondary text\n --color-foreground-muted: #81868d; // for muted text\n --color-foreground-border: #666666; // for content borders\n\n --color-background-primary: #131416; // for content\n --color-background-secondary: #1a1c1e; // for navigation + ToC\n --color-background-hover: #1e2124ff; // for navigation-item hover\n --color-background-hover--transparent: #1e212400;\n --color-background-border: #303335; // for UI borders\n --color-background-item: #444; // for \"background\" items (eg: copybutton)\n\n // Announcements\n --color-announcement-background: #000000dd;\n --color-announcement-text: #eeebee;\n\n // Brand colors\n --color-brand-primary: #2b8cee;\n --color-brand-content: #368ce2;\n\n // Highlighted text (search)\n --color-highlighted-background: #083563;\n\n // GUI Labels\n --color-guilabel-background: #08356380;\n --color-guilabel-border: #13395f80;\n\n // API documentation\n --color-api-keyword: var(--color-foreground-secondary);\n --color-highlight-on-target: #333300;\n\n // Admonitions\n --color-admonition-background: #18181a;\n\n // Cards\n --color-card-border: var(--color-background-secondary);\n --color-card-background: #18181a;\n --color-card-marginals-background: var(--color-background-hover);\n}\n","// This file contains the styling for making the content throughout the page,\n// including fonts, paragraphs, headings and spacing among these elements.\n\nbody\n font-family: var(--font-stack)\npre,\ncode,\nkbd,\nsamp\n font-family: var(--font-stack--monospace)\n\n// Make fonts look slightly nicer.\nbody\n -webkit-font-smoothing: antialiased\n -moz-osx-font-smoothing: grayscale\n\n// Line height from Bootstrap 4.1\narticle\n line-height: 1.5\n\n//\n// Headings\n//\nh1,\nh2,\nh3,\nh4,\nh5,\nh6\n line-height: 1.25\n font-weight: bold\n\n border-radius: 0.5rem\n margin-top: 0.5rem\n margin-bottom: 0.5rem\n margin-left: -0.5rem\n margin-right: -0.5rem\n padding-left: 0.5rem\n padding-right: 0.5rem\n\n + p\n margin-top: 0\n\nh1\n font-size: 2.5em\n margin-top: 1.75rem\n margin-bottom: 1rem\nh2\n font-size: 2em\n margin-top: 1.75rem\nh3\n font-size: 1.5em\nh4\n font-size: 1.25em\nh5\n font-size: 1.125em\nh6\n font-size: 1em\n\nsmall\n opacity: 75%\n font-size: 80%\n\n// Paragraph\np\n margin-top: 0.5rem\n margin-bottom: 0.75rem\n\n// Horizontal rules\nhr.docutils\n height: 1px\n padding: 0\n margin: 2rem 0\n background-color: var(--color-background-border)\n border: 0\n\n.centered\n text-align: center\n\n// Links\na\n text-decoration: underline\n\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline)\n\n &:hover\n color: var(--color-link--hover)\n text-decoration-color: var(--color-link-underline--hover)\n &.muted-link\n color: inherit\n &:hover\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline--hover)\n","// This file contains the styles for the overall layouting of the documentation\n// skeleton, including the responsive changes as well as sidebar toggles.\n//\n// This is implemented as a mobile-last design, which isn't ideal, but it is\n// reasonably good-enough and I got pretty tired by the time I'd finished this\n// to move the rules around to fix this. Shouldn't take more than 3-4 hours,\n// if you know what you're doing tho.\n\n// HACK: Not all browsers account for the scrollbar width in media queries.\n// This results in horizontal scrollbars in the breakpoint where we go\n// from displaying everything to hiding the ToC. We accomodate for this by\n// adding a bit of padding to the TOC drawer, disabling the horizontal\n// scrollbar and allowing the scrollbars to cover the padding.\n// https://www.456bereastreet.com/archive/201301/media_query_width_and_vertical_scrollbars/\n\n// HACK: Always having the scrollbar visible, prevents certain browsers from\n// causing the content to stutter horizontally between taller-than-viewport and\n// not-taller-than-viewport pages.\n\nhtml\n overflow-x: hidden\n overflow-y: scroll\n scroll-behavior: smooth\n\n.sidebar-scroll, .toc-scroll, article[role=main] *\n // Override Firefox scrollbar style\n scrollbar-width: thin\n scrollbar-color: var(--color-foreground-border) transparent\n\n // Override Chrome scrollbar styles\n &::-webkit-scrollbar\n width: 0.25rem\n height: 0.25rem\n &::-webkit-scrollbar-thumb\n background-color: var(--color-foreground-border)\n border-radius: 0.125rem\n\n//\n// Overalls\n//\nhtml,\nbody\n height: 100%\n color: var(--color-foreground-primary)\n background: var(--color-background-primary)\n\narticle\n color: var(--color-content-foreground)\n background: var(--color-content-background)\n overflow-wrap: break-word\n\n.page\n display: flex\n // fill the viewport for pages with little content.\n min-height: 100%\n\n.mobile-header\n width: 100%\n height: var(--header-height)\n background-color: var(--color-header-background)\n color: var(--color-header-text)\n border-bottom: 1px solid var(--color-header-border)\n\n // Looks like sub-script/super-script have this, and we need this to\n // be \"on top\" of those.\n z-index: 10\n\n // We don't show the header on large screens.\n display: none\n\n // Add shadow when scrolled\n &.scrolled\n border-bottom: none\n box-shadow: 0 0 0.2rem rgba(0, 0, 0, 0.1), 0 0.2rem 0.4rem rgba(0, 0, 0, 0.2)\n\n .header-center\n a\n color: var(--color-header-text)\n text-decoration: none\n\n.main\n display: flex\n flex: 1\n\n// Sidebar (left) also covers the entire left portion of screen.\n.sidebar-drawer\n box-sizing: border-box\n\n border-right: 1px solid var(--color-sidebar-background-border)\n background: var(--color-sidebar-background)\n\n display: flex\n justify-content: flex-end\n // These next two lines took me two days to figure out.\n width: calc((100% - #{$full-width}) / 2 + #{$sidebar-width})\n min-width: $sidebar-width\n\n// Scroll-along sidebars\n.sidebar-container,\n.toc-drawer\n box-sizing: border-box\n width: $sidebar-width\n\n.toc-drawer\n background: var(--color-toc-background)\n // See HACK described on top of this document\n padding-right: 1rem\n\n.sidebar-sticky,\n.toc-sticky\n position: sticky\n top: 0\n height: min(100%, 100vh)\n height: 100vh\n\n display: flex\n flex-direction: column\n\n.sidebar-scroll,\n.toc-scroll\n flex-grow: 1\n flex-shrink: 1\n\n overflow: auto\n scroll-behavior: smooth\n\n// Central items.\n.content\n padding: 0 $content-padding\n width: $content-width\n\n display: flex\n flex-direction: column\n justify-content: space-between\n\n.icon\n display: inline-block\n height: 1rem\n width: 1rem\n svg\n width: 100%\n height: 100%\n\n//\n// Accommodate announcement banner\n//\n.announcement\n background-color: var(--color-announcement-background)\n color: var(--color-announcement-text)\n\n height: var(--header-height)\n display: flex\n align-items: center\n overflow-x: auto\n & + .page\n min-height: calc(100% - var(--header-height))\n\n.announcement-content\n box-sizing: border-box\n padding: 0.5rem\n min-width: 100%\n white-space: nowrap\n text-align: center\n\n a\n color: var(--color-announcement-text)\n text-decoration-color: var(--color-announcement-text)\n\n &:hover\n color: var(--color-announcement-text)\n text-decoration-color: var(--color-link--hover)\n\n////////////////////////////////////////////////////////////////////////////////\n// Toggles for theme\n////////////////////////////////////////////////////////////////////////////////\n.no-js .theme-toggle-container // don't show theme toggle if there's no JS\n display: none\n\n.theme-toggle-container\n vertical-align: middle\n\n.theme-toggle\n cursor: pointer\n border: none\n padding: 0\n background: transparent\n\n.theme-toggle svg\n vertical-align: middle\n height: 1rem\n width: 1rem\n color: var(--color-foreground-primary)\n display: none\n\n.theme-toggle-header\n float: left\n padding: 1rem 0.5rem\n\n////////////////////////////////////////////////////////////////////////////////\n// Toggles for elements\n////////////////////////////////////////////////////////////////////////////////\n.toc-overlay-icon, .nav-overlay-icon\n display: none\n cursor: pointer\n\n .icon\n color: var(--color-foreground-secondary)\n height: 1rem\n width: 1rem\n\n.toc-header-icon, .nav-overlay-icon\n // for when we set display: flex\n justify-content: center\n align-items: center\n\n.toc-content-icon\n height: 1.5rem\n width: 1.5rem\n\n.content-icon-container\n float: right\n display: flex\n margin-top: 1.5rem\n margin-left: 1rem\n margin-bottom: 1rem\n gap: 0.5rem\n\n .edit-this-page svg\n color: inherit\n height: 1rem\n width: 1rem\n\n.sidebar-toggle\n position: absolute\n display: none\n// \n.sidebar-toggle[name=\"__toc\"]\n left: 20px\n.sidebar-toggle:checked\n left: 40px\n// \n\n.overlay\n position: fixed\n top: 0\n width: 0\n height: 0\n\n transition: width 0ms, height 0ms, opacity 250ms ease-out\n\n opacity: 0\n background-color: rgba(0, 0, 0, 0.54)\n.sidebar-overlay\n z-index: 20\n.toc-overlay\n z-index: 40\n\n// Keep things on top and smooth.\n.sidebar-drawer\n z-index: 30\n transition: left 250ms ease-in-out\n.toc-drawer\n z-index: 50\n transition: right 250ms ease-in-out\n\n// Show the Sidebar\n#__navigation:checked\n & ~ .sidebar-overlay\n width: 100%\n height: 100%\n opacity: 1\n & ~ .page\n .sidebar-drawer\n top: 0\n left: 0\n // Show the toc sidebar\n#__toc:checked\n & ~ .toc-overlay\n width: 100%\n height: 100%\n opacity: 1\n & ~ .page\n .toc-drawer\n top: 0\n right: 0\n\n////////////////////////////////////////////////////////////////////////////////\n// Back to top\n////////////////////////////////////////////////////////////////////////////////\n.back-to-top\n text-decoration: none\n\n display: none\n position: fixed\n left: 0\n top: 1rem\n padding: 0.5rem\n padding-right: 0.75rem\n border-radius: 1rem\n font-size: 0.8125rem\n\n background: var(--color-background-primary)\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), #6b728080 0px 0px 1px 0px\n\n z-index: 10\n\n margin-left: 50%\n transform: translateX(-50%)\n svg\n height: 1rem\n width: 1rem\n fill: currentColor\n display: inline-block\n\n span\n margin-left: 0.25rem\n\n .show-back-to-top &\n display: flex\n align-items: center\n\n////////////////////////////////////////////////////////////////////////////////\n// Responsive layouting\n////////////////////////////////////////////////////////////////////////////////\n// Make things a bit bigger on bigger screens.\n@media (min-width: $full-width + $sidebar-width)\n html\n font-size: 110%\n\n@media (max-width: $full-width)\n // Collapse \"toc\" into the icon.\n .toc-content-icon\n display: flex\n .toc-drawer\n position: fixed\n height: 100vh\n top: 0\n right: -$sidebar-width\n border-left: 1px solid var(--color-background-muted)\n .toc-tree\n border-left: none\n font-size: var(--toc-font-size--mobile)\n\n // Accomodate for a changed content width.\n .sidebar-drawer\n width: calc((100% - #{$full-width - $sidebar-width}) / 2 + #{$sidebar-width})\n\n@media (max-width: $full-width - $sidebar-width)\n // Collapse \"navigation\".\n .nav-overlay-icon\n display: flex\n .sidebar-drawer\n position: fixed\n height: 100vh\n width: $sidebar-width\n\n top: 0\n left: -$sidebar-width\n\n // Swap which icon is visible.\n .toc-header-icon\n display: flex\n .toc-content-icon, .theme-toggle-content\n display: none\n .theme-toggle-header\n display: block\n\n // Show the header.\n .mobile-header\n position: sticky\n top: 0\n display: flex\n justify-content: space-between\n align-items: center\n\n .header-left,\n .header-right\n display: flex\n height: var(--header-height)\n padding: 0 var(--header-padding)\n label\n height: 100%\n width: 100%\n user-select: none\n\n .nav-overlay-icon .icon,\n .theme-toggle svg\n height: 1.25rem\n width: 1.25rem\n\n // Add a scroll margin for the content\n :target\n scroll-margin-top: var(--header-height)\n\n // Show back-to-top below the header\n .back-to-top\n top: calc(var(--header-height) + 0.5rem)\n\n // Center the page, and accommodate for the header.\n .page\n flex-direction: column\n justify-content: center\n .content\n margin-left: auto\n margin-right: auto\n\n@media (max-width: $content-width + 2* $content-padding)\n // Content should respect window limits.\n .content\n width: 100%\n overflow-x: auto\n\n@media (max-width: $content-width)\n .content\n padding: 0 $content-padding--small\n // Don't float sidebars to the right.\n article aside.sidebar\n float: none\n width: 100%\n margin: 1rem 0\n","//\n// The design here is strongly inspired by mkdocs-material.\n.admonition, .topic\n margin: 1rem auto\n padding: 0 0.5rem 0.5rem 0.5rem\n\n background: var(--color-admonition-background)\n\n border-radius: 0.2rem\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n font-size: var(--admonition-font-size)\n\n overflow: hidden\n page-break-inside: avoid\n\n // First element should have no margin, since the title has it.\n > :nth-child(2)\n margin-top: 0\n\n // Last item should have no margin, since we'll control that w/ padding\n > :last-child\n margin-bottom: 0\n\n.admonition p.admonition-title,\np.topic-title\n position: relative\n margin: 0 -0.5rem 0.5rem\n padding-left: 2rem\n padding-right: .5rem\n padding-top: .4rem\n padding-bottom: .4rem\n\n font-weight: 500\n font-size: var(--admonition-title-font-size)\n line-height: 1.3\n\n // Our fancy icon\n &::before\n content: \"\"\n position: absolute\n left: 0.5rem\n width: 1rem\n height: 1rem\n\n// Default styles\np.admonition-title\n background-color: var(--color-admonition-title-background)\n &::before\n background-color: var(--color-admonition-title)\n mask-image: var(--icon-admonition-default)\n mask-repeat: no-repeat\n\np.topic-title\n background-color: var(--color-topic-title-background)\n &::before\n background-color: var(--color-topic-title)\n mask-image: var(--icon-topic-default)\n mask-repeat: no-repeat\n\n//\n// Variants\n//\n.admonition\n border-left: 0.2rem solid var(--color-admonition-title)\n\n @each $type, $value in $admonitions\n &.#{$type}\n border-left-color: var(--color-admonition-title--#{$type})\n > .admonition-title\n background-color: var(--color-admonition-title-background--#{$type})\n &::before\n background-color: var(--color-admonition-title--#{$type})\n mask-image: var(--icon-#{nth($value, 2)})\n\n.admonition-todo > .admonition-title\n text-transform: uppercase\n","// This file stylizes the API documentation (stuff generated by autodoc). It's\n// deeply nested due to how autodoc structures the HTML without enough classes\n// to select the relevant items.\n\n// API docs!\ndl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)\n // Tweak the spacing of all the things!\n dd\n margin-left: 2rem\n > :first-child\n margin-top: 0.125rem\n > :last-child\n margin-bottom: 0.75rem\n\n // This is used for the arguments\n .field-list\n margin-bottom: 0.75rem\n\n // \"Headings\" (like \"Parameters\" and \"Return\")\n > dt\n text-transform: uppercase\n font-size: var(--font-size--small)\n\n dd:empty\n margin-bottom: 0.5rem\n dd > ul\n margin-left: -1.2rem\n > li\n > p:nth-child(2)\n margin-top: 0\n // When the last-empty-paragraph follows a paragraph, it doesn't need\n // to augument the existing spacing.\n > p + p:last-child:empty\n margin-top: 0\n margin-bottom: 0\n\n // Colorize the elements\n > dt\n color: var(--color-api-overall)\n\n.sig:not(.sig-inline)\n font-weight: bold\n\n font-size: var(--api-font-size)\n font-family: var(--font-stack--monospace)\n\n margin-left: -0.25rem\n margin-right: -0.25rem\n padding-top: 0.25rem\n padding-bottom: 0.25rem\n padding-right: 0.5rem\n\n // These are intentionally em, to properly match the font size.\n padding-left: 3em\n text-indent: -2.5em\n\n border-radius: 0.25rem\n\n background: var(--color-api-background)\n transition: background 100ms ease-out\n\n &:hover\n background: var(--color-api-background-hover)\n\n // adjust the size of the [source] link on the right.\n a.reference\n .viewcode-link\n font-weight: normal\n width: 3.5rem\n\nem.property\n font-style: normal\n &:first-child\n color: var(--color-api-keyword)\n.sig-name\n color: var(--color-api-name)\n.sig-prename\n font-weight: normal\n color: var(--color-api-pre-name)\n.sig-paren\n color: var(--color-api-paren)\n.sig-param\n font-style: normal\n\n.versionmodified\n font-style: italic\ndiv.versionadded, div.versionchanged, div.deprecated\n p\n margin-top: 0.125rem\n margin-bottom: 0.125rem\n\n// Align the [docs] and [source] to the right.\n.viewcode-link, .viewcode-back\n float: right\n text-align: right\n",".line-block\n margin-top: 0.5rem\n margin-bottom: 0.75rem\n .line-block\n margin-top: 0rem\n margin-bottom: 0rem\n padding-left: 1rem\n","// Captions\narticle p.caption,\ntable > caption,\n.code-block-caption\n font-size: var(--font-size--small)\n text-align: center\n\n// Caption above a TOCTree\n.toctree-wrapper.compound\n .caption, :not(.caption) > .caption-text\n font-size: var(--font-size--small)\n text-transform: uppercase\n\n text-align: initial\n margin-bottom: 0\n\n > ul\n margin-top: 0\n margin-bottom: 0\n","// Inline code\ncode.literal, .sig-inline\n background: var(--color-inline-code-background)\n border-radius: 0.2em\n // Make the font smaller, and use padding to recover.\n font-size: var(--font-size--small--2)\n padding: 0.1em 0.2em\n\n pre.literal-block &\n font-size: inherit\n padding: 0\n\n p &\n border: 1px solid var(--color-background-border)\n\n.sig-inline\n font-family: var(--font-stack--monospace)\n\n// Code and Literal Blocks\n$code-spacing-vertical: 0.625rem\n$code-spacing-horizontal: 0.875rem\n\n// Wraps every literal block + line numbers.\ndiv[class*=\" highlight-\"],\ndiv[class^=\"highlight-\"]\n margin: 1em 0\n display: flex\n\n .table-wrapper\n margin: 0\n padding: 0\n\npre\n margin: 0\n padding: 0\n overflow: auto\n\n // Needed to have more specificity than pygments' \"pre\" selector. :(\n article[role=\"main\"] .highlight &\n line-height: 1.5\n\n &.literal-block,\n .highlight &\n font-size: var(--code-font-size)\n padding: $code-spacing-vertical $code-spacing-horizontal\n\n // Make it look like all the other blocks.\n &.literal-block\n margin-top: 1rem\n margin-bottom: 1rem\n\n border-radius: 0.2rem\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n\n// All code is always contained in this.\n.highlight\n width: 100%\n border-radius: 0.2rem\n\n // Make line numbers and prompts un-selectable.\n .gp, span.linenos\n user-select: none\n pointer-events: none\n\n // Expand the line-highlighting.\n .hll\n display: block\n margin-left: -$code-spacing-horizontal\n margin-right: -$code-spacing-horizontal\n padding-left: $code-spacing-horizontal\n padding-right: $code-spacing-horizontal\n\n/* Make code block captions be nicely integrated */\n.code-block-caption\n display: flex\n padding: $code-spacing-vertical $code-spacing-horizontal\n\n border-radius: 0.25rem\n border-bottom-left-radius: 0\n border-bottom-right-radius: 0\n font-weight: 300\n border-bottom: 1px solid\n\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n border-color: var(--color-background-border)\n\n + div[class]\n margin-top: 0\n pre\n border-top-left-radius: 0\n border-top-right-radius: 0\n\n// When `html_codeblock_linenos_style` is table.\n.highlighttable\n width: 100%\n display: block\n tbody\n display: block\n\n tr\n display: flex\n\n // Line numbers\n td.linenos\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n padding: $code-spacing-vertical $code-spacing-horizontal\n padding-right: 0\n border-top-left-radius: 0.2rem\n border-bottom-left-radius: 0.2rem\n\n .linenodiv\n padding-right: $code-spacing-horizontal\n font-size: var(--code-font-size)\n box-shadow: -0.0625rem 0 var(--color-foreground-border) inset\n\n // Actual code\n td.code\n padding: 0\n display: block\n flex: 1\n overflow: hidden\n\n .highlight\n border-top-left-radius: 0\n border-bottom-left-radius: 0\n\n// When `html_codeblock_linenos_style` is inline.\n.highlight\n span.linenos\n display: inline-block\n padding-left: 0\n padding-right: $code-spacing-horizontal\n margin-right: $code-spacing-horizontal\n box-shadow: -0.0625rem 0 var(--color-foreground-border) inset\n","// Inline Footnote Reference\n.footnote-reference\n font-size: var(--font-size--small--4)\n vertical-align: super\n\n// Definition list, listing the content of each note.\n// docutils <= 0.17\ndl.footnote.brackets\n font-size: var(--font-size--small)\n color: var(--color-foreground-secondary)\n\n display: grid\n grid-template-columns: max-content auto\n dt\n margin: 0\n > .fn-backref\n margin-left: 0.25rem\n\n &:after\n content: \":\"\n\n .brackets\n &:before\n content: \"[\"\n &:after\n content: \"]\"\n\n dd\n margin: 0\n padding: 0 1rem\n\n// docutils >= 0.18\naside.footnote\n font-size: var(--font-size--small)\n color: var(--color-foreground-secondary)\n\naside.footnote > span,\ndiv.citation > span\n float: left\n font-weight: 500\n padding-right: 0.25rem\n\naside.footnote > p,\ndiv.citation > p\n margin-left: 2rem\n","//\n// Figures\n//\nimg\n box-sizing: border-box\n max-width: 100%\n height: auto\n\narticle\n figure, .figure\n border-radius: 0.2rem\n\n margin: 0\n :last-child\n margin-bottom: 0\n\n .align-left\n float: left\n clear: left\n margin: 0 1rem 1rem\n\n .align-right\n float: right\n clear: right\n margin: 0 1rem 1rem\n\n .align-default,\n .align-center\n display: block\n text-align: center\n margin-left: auto\n margin-right: auto\n\n // WELL, table needs to be stylised like a table.\n table.align-default\n display: table\n text-align: initial\n",".genindex-jumpbox, .domainindex-jumpbox\n border-top: 1px solid var(--color-background-border)\n border-bottom: 1px solid var(--color-background-border)\n padding: 0.25rem\n\n.genindex-section, .domainindex-section\n h2\n margin-top: 0.75rem\n margin-bottom: 0.5rem\n ul\n margin-top: 0\n margin-bottom: 0\n","ul,\nol\n padding-left: 1.2rem\n\n // Space lists out like paragraphs\n margin-top: 1rem\n margin-bottom: 1rem\n // reduce margins within li.\n li\n > p:first-child\n margin-top: 0.25rem\n margin-bottom: 0.25rem\n\n > p:last-child\n margin-top: 0.25rem\n\n > ul,\n > ol\n margin-top: 0.5rem\n margin-bottom: 0.5rem\n\nol\n &.arabic\n list-style: decimal\n &.loweralpha\n list-style: lower-alpha\n &.upperalpha\n list-style: upper-alpha\n &.lowerroman\n list-style: lower-roman\n &.upperroman\n list-style: upper-roman\n\n// Don't space lists out when they're \"simple\" or in a `.. toctree::`\n.simple,\n.toctree-wrapper\n li\n > ul,\n > ol\n margin-top: 0\n margin-bottom: 0\n\n// Definition Lists\n.field-list,\n.option-list,\ndl:not([class]),\ndl.simple,\ndl.footnote,\ndl.glossary\n dt\n font-weight: 500\n margin-top: 0.25rem\n + dt\n margin-top: 0\n\n .classifier::before\n content: \":\"\n margin-left: 0.2rem\n margin-right: 0.2rem\n\n dd\n > p:first-child,\n ul\n margin-top: 0.125rem\n\n ul\n margin-bottom: 0.125rem\n",".math-wrapper\n width: 100%\n overflow-x: auto\n\ndiv.math\n position: relative\n text-align: center\n\n .headerlink,\n &:focus .headerlink\n display: none\n\n &:hover .headerlink\n display: inline-block\n\n span.eqno\n position: absolute\n right: 0.5rem\n top: 50%\n transform: translate(0, -50%)\n z-index: 1\n","// Abbreviations\nabbr[title]\n cursor: help\n\n// \"Problematic\" content, as identified by Sphinx\n.problematic\n color: var(--color-problematic)\n\n// Keyboard / Mouse \"instructions\"\nkbd:not(.compound)\n margin: 0 0.2rem\n padding: 0 0.2rem\n border-radius: 0.2rem\n border: 1px solid var(--color-foreground-border)\n color: var(--color-foreground-primary)\n vertical-align: text-bottom\n\n font-size: var(--font-size--small--3)\n display: inline-block\n\n box-shadow: 0 0.0625rem 0 rgba(0, 0, 0, 0.2), inset 0 0 0 0.125rem var(--color-background-primary)\n\n background-color: var(--color-background-secondary)\n\n// Blockquote\nblockquote\n border-left: 4px solid var(--color-background-border)\n background: var(--color-background-secondary)\n\n margin-left: 0\n margin-right: 0\n padding: 0.5rem 1rem\n\n .attribution\n font-weight: 600\n text-align: right\n\n &.pull-quote,\n &.highlights\n font-size: 1.25em\n\n &.epigraph,\n &.pull-quote\n border-left-width: 0\n border-radius: 0.5rem\n\n &.highlights\n border-left-width: 0\n background: transparent\n\n// Center align embedded-in-text images\np .reference img\n vertical-align: middle\n","p.rubric\n line-height: 1.25\n font-weight: bold\n font-size: 1.125em\n\n // For Numpy-style documentation that's got rubrics within it.\n // https://github.com/pradyunsg/furo/discussions/505\n dd &\n line-height: inherit\n font-weight: inherit\n\n font-size: var(--font-size--small)\n text-transform: uppercase\n","article .sidebar\n float: right\n clear: right\n width: 30%\n\n margin-left: 1rem\n margin-right: 0\n\n border-radius: 0.2rem\n background-color: var(--color-background-secondary)\n border: var(--color-background-border) 1px solid\n\n > *\n padding-left: 1rem\n padding-right: 1rem\n\n > ul, > ol // lists need additional padding, because bullets.\n padding-left: 2.2rem\n\n .sidebar-title\n margin: 0\n padding: 0.5rem 1rem\n border-bottom: var(--color-background-border) 1px solid\n\n font-weight: 500\n\n// TODO: subtitle\n// TODO: dedicated variables?\n",".table-wrapper\n width: 100%\n overflow-x: auto\n margin-top: 1rem\n margin-bottom: 0.5rem\n padding: 0.2rem 0.2rem 0.75rem\n\ntable.docutils\n border-radius: 0.2rem\n border-spacing: 0\n border-collapse: collapse\n\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n th\n background: var(--color-table-header-background)\n\n td,\n th\n // Space things out properly\n padding: 0 0.25rem\n\n // Get the borders looking just-right.\n border-left: 1px solid var(--color-table-border)\n border-right: 1px solid var(--color-table-border)\n border-bottom: 1px solid var(--color-table-border)\n\n p\n margin: 0.25rem\n\n &:first-child\n border-left: none\n &:last-child\n border-right: none\n\n // MyST-parser tables set these classes for control of column alignment\n &.text-left\n text-align: left\n &.text-right\n text-align: right\n &.text-center\n text-align: center\n",":target\n scroll-margin-top: 0.5rem\n\n@media (max-width: $full-width - $sidebar-width)\n :target\n scroll-margin-top: calc(0.5rem + var(--header-height))\n\n // When a heading is selected\n section > span:target\n scroll-margin-top: calc(0.8rem + var(--header-height))\n\n// Permalinks\n.headerlink\n font-weight: 100\n user-select: none\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\ndl dt,\np.caption,\nfigcaption p,\ntable > caption,\n.code-block-caption\n > .headerlink\n margin-left: 0.5rem\n visibility: hidden\n &:hover > .headerlink\n visibility: visible\n\n // Don't change to link-like, if someone adds the contents directive.\n > .toc-backref\n color: inherit\n text-decoration-line: none\n\n// Figure and table captions are special.\nfigure:hover > figcaption > p > .headerlink,\ntable:hover > caption > .headerlink\n visibility: visible\n\n:target >, // Regular section[id] style anchors\nspan:target ~ // Non-regular span[id] style \"extra\" anchors\n h1,\n h2,\n h3,\n h4,\n h5,\n h6\n &:nth-of-type(1)\n background-color: var(--color-highlight-on-target)\n // .headerlink\n // visibility: visible\n code.literal\n background-color: transparent\n\ntable:target > caption,\nfigure:target\n background-color: var(--color-highlight-on-target)\n\n// Inline page contents\n.this-will-duplicate-information-and-it-is-still-useful-here li :target\n background-color: var(--color-highlight-on-target)\n\n// Code block permalinks\n.literal-block-wrapper:target .code-block-caption\n background-color: var(--color-highlight-on-target)\n\n// When a definition list item is selected\n//\n// There isn't really an alternative to !important here, due to the\n// high-specificity of API documentation's selector.\ndt:target\n background-color: var(--color-highlight-on-target) !important\n\n// When a footnote reference is selected\n.footnote > dt:target + dd,\n.footnote-reference:target\n background-color: var(--color-highlight-on-target)\n",".guilabel\n background-color: var(--color-guilabel-background)\n border: 1px solid var(--color-guilabel-border)\n color: var(--color-guilabel-text)\n\n padding: 0 0.3em\n border-radius: 0.5em\n font-size: 0.9em\n","// This file contains the styles used for stylizing the footer that's shown\n// below the content.\n\nfooter\n font-size: var(--font-size--small)\n display: flex\n flex-direction: column\n\n margin-top: 2rem\n\n// Bottom of page information\n.bottom-of-page\n display: flex\n align-items: center\n justify-content: space-between\n\n margin-top: 1rem\n padding-top: 1rem\n padding-bottom: 1rem\n\n color: var(--color-foreground-secondary)\n border-top: 1px solid var(--color-background-border)\n\n line-height: 1.5\n\n @media (max-width: $content-width)\n text-align: center\n flex-direction: column-reverse\n gap: 0.25rem\n\n .left-details\n font-size: var(--font-size--small)\n\n .right-details\n display: flex\n flex-direction: column\n gap: 0.25rem\n text-align: right\n\n .icons\n display: flex\n justify-content: flex-end\n gap: 0.25rem\n font-size: 1rem\n\n a\n text-decoration: none\n\n svg,\n img\n font-size: 1.125rem\n height: 1em\n width: 1em\n\n// Next/Prev page information\n.related-pages\n a\n display: flex\n align-items: center\n\n text-decoration: none\n &:hover .page-info .title\n text-decoration: underline\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline)\n\n svg.furo-related-icon,\n svg.furo-related-icon > use\n flex-shrink: 0\n\n color: var(--color-foreground-border)\n\n width: 0.75rem\n height: 0.75rem\n margin: 0 0.5rem\n\n &.next-page\n max-width: 50%\n\n float: right\n clear: right\n text-align: right\n\n &.prev-page\n max-width: 50%\n\n float: left\n clear: left\n\n svg\n transform: rotate(180deg)\n\n.page-info\n display: flex\n flex-direction: column\n overflow-wrap: anywhere\n\n .next-page &\n align-items: flex-end\n\n .context\n display: flex\n align-items: center\n\n padding-bottom: 0.1rem\n\n color: var(--color-foreground-muted)\n font-size: var(--font-size--small)\n text-decoration: none\n","// This file contains the styles for the contents of the left sidebar, which\n// contains the navigation tree, logo, search etc.\n\n////////////////////////////////////////////////////////////////////////////////\n// Brand on top of the scrollable tree.\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-brand\n display: flex\n flex-direction: column\n flex-shrink: 0\n\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n text-decoration: none\n\n.sidebar-brand-text\n color: var(--color-sidebar-brand-text)\n overflow-wrap: break-word\n margin: var(--sidebar-item-spacing-vertical) 0\n font-size: 1.5rem\n\n.sidebar-logo-container\n margin: var(--sidebar-item-spacing-vertical) 0\n\n.sidebar-logo\n margin: 0 auto\n display: block\n max-width: 100%\n\n////////////////////////////////////////////////////////////////////////////////\n// Search\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-search-container\n display: flex\n align-items: center\n margin-top: var(--sidebar-search-space-above)\n\n position: relative\n\n background: var(--color-sidebar-search-background)\n &:hover,\n &:focus-within\n background: var(--color-sidebar-search-background--focus)\n\n &::before\n content: \"\"\n position: absolute\n left: var(--sidebar-item-spacing-horizontal)\n width: var(--sidebar-search-icon-size)\n height: var(--sidebar-search-icon-size)\n\n background-color: var(--color-sidebar-search-icon)\n mask-image: var(--icon-search)\n\n.sidebar-search\n box-sizing: border-box\n\n border: none\n border-top: 1px solid var(--color-sidebar-search-border)\n border-bottom: 1px solid var(--color-sidebar-search-border)\n\n padding-top: var(--sidebar-search-input-spacing-vertical)\n padding-bottom: var(--sidebar-search-input-spacing-vertical)\n padding-right: var(--sidebar-search-input-spacing-horizontal)\n padding-left: calc(var(--sidebar-item-spacing-horizontal) + var(--sidebar-search-input-spacing-horizontal) + var(--sidebar-search-icon-size))\n\n width: 100%\n\n color: var(--color-sidebar-search-foreground)\n background: transparent\n z-index: 10\n\n &:focus\n outline: none\n\n &::placeholder\n font-size: var(--sidebar-search-input-font-size)\n\n//\n// Hide Search Matches link\n//\n#searchbox .highlight-link\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0\n margin: 0\n text-align: center\n\n a\n color: var(--color-sidebar-search-icon)\n font-size: var(--font-size--small--2)\n\n////////////////////////////////////////////////////////////////////////////////\n// Structure/Skeleton of the navigation tree (left)\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-tree\n font-size: var(--sidebar-item-font-size)\n margin-top: var(--sidebar-tree-space-above)\n margin-bottom: var(--sidebar-item-spacing-vertical)\n\n ul\n padding: 0\n margin-top: 0\n margin-bottom: 0\n\n display: flex\n flex-direction: column\n\n list-style: none\n\n li\n position: relative\n margin: 0\n\n > ul\n margin-left: var(--sidebar-item-spacing-horizontal)\n\n .icon\n color: var(--color-sidebar-link-text)\n\n .reference\n box-sizing: border-box\n color: var(--color-sidebar-link-text)\n\n // Fill the parent.\n display: inline-block\n line-height: var(--sidebar-item-line-height)\n text-decoration: none\n\n // Don't allow long words to cause wrapping.\n overflow-wrap: anywhere\n\n height: 100%\n width: 100%\n\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n\n &:hover\n background: var(--color-sidebar-item-background--hover)\n\n // Add a nice little \"external-link\" arrow here.\n &.external::after\n content: url('data:image/svg+xml,')\n margin: 0 0.25rem\n vertical-align: middle\n color: var(--color-sidebar-link-text)\n\n // Make the current page reference bold.\n .current-page > .reference\n font-weight: bold\n\n label\n position: absolute\n top: 0\n right: 0\n height: var(--sidebar-item-height)\n width: var(--sidebar-expander-width)\n\n cursor: pointer\n user-select: none\n\n display: flex\n justify-content: center\n align-items: center\n\n .caption, :not(.caption) > .caption-text\n font-size: var(--sidebar-caption-font-size)\n color: var(--color-sidebar-caption-text)\n\n font-weight: bold\n text-transform: uppercase\n\n margin: var(--sidebar-caption-space-above) 0 0 0\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n\n // If it has children, add a bit more padding to wrap the content to avoid\n // overlapping with the