From 693736bdd64f8815c139c4f68ffe609dd202f40a Mon Sep 17 00:00:00 2001 From: justinfu Date: Thu, 19 Oct 2023 22:01:20 +0000 Subject: [PATCH] Documentation v0.1.0 --- .../waymax/agents/actor_core/index.rst.txt | 177 + .../waymax/agents/agent_builder/index.rst.txt | 34 + .../agents/constant_speed/index.rst.txt | 68 + .../waymax/agents/expert/index.rst.txt | 77 + _sources/autoapi/waymax/agents/index.rst.txt | 407 ++ .../waymax/agents/sim_agent/index.rst.txt | 98 + .../waypoint_following_agent/index.rst.txt | 267 ++ _sources/autoapi/waymax/config/index.rst.txt | 647 +++ .../dataloader/dataloader_utils/index.rst.txt | 116 + .../autoapi/waymax/dataloader/index.rst.txt | 237 ++ .../dataloader/womd_dataloader/index.rst.txt | 69 + .../dataloader/womd_factories/index.rst.txt | 123 + .../womd_factories_internal/index.rst.txt | 6 + .../dataloader/womd_utils/index.rst.txt | 167 + .../waymax/datatypes/action/index.rst.txt | 135 + .../waymax/datatypes/array/index.rst.txt | 91 + .../waymax/datatypes/constant/index.rst.txt | 24 + .../autoapi/waymax/datatypes/index.rst.txt | 1753 ++++++++ .../datatypes/object_state/index.rst.txt | 386 ++ .../datatypes/observation/index.rst.txt | 404 ++ .../waymax/datatypes/operations/index.rst.txt | 200 + .../waymax/datatypes/roadgraph/index.rst.txt | 325 ++ .../waymax/datatypes/route/index.rst.txt | 140 + .../datatypes/simulator_state/index.rst.txt | 192 + .../datatypes/traffic_lights/index.rst.txt | 174 + .../dynamics/abstract_dynamics/index.rst.txt | 157 + .../dynamics/bicycle_model/index.rst.txt | 145 + .../waymax/dynamics/delta/index.rst.txt | 122 + .../waymax/dynamics/discretizer/index.rst.txt | 147 + .../autoapi/waymax/dynamics/index.rst.txt | 402 ++ .../dynamics/state_dynamics/index.rst.txt | 63 + .../env/abstract_environment/index.rst.txt | 165 + .../waymax/env/base_environment/index.rst.txt | 159 + .../autoapi/waymax/env/errors/index.rst.txt | 30 + _sources/autoapi/waymax/env/index.rst.txt | 790 ++++ .../planning_agent_environment/index.rst.txt | 191 + .../autoapi/waymax/env/rollout/index.rst.txt | 189 + .../autoapi/waymax/env/typedefs/index.rst.txt | 46 + .../env/wrappers/brax_wrapper/index.rst.txt | 223 + .../env/wrappers/dm_env_wrapper/index.rst.txt | 112 + .../autoapi/waymax/env/wrappers/index.rst.txt | 16 + _sources/autoapi/waymax/index.rst.txt | 37 + .../metrics/abstract_metric/index.rst.txt | 63 + .../waymax/metrics/comfort/index.rst.txt | 88 + .../waymax/metrics/imitation/index.rst.txt | 62 + _sources/autoapi/waymax/metrics/index.rst.txt | 297 ++ .../metrics/metric_factory/index.rst.txt | 37 + .../waymax/metrics/overlap/index.rst.txt | 55 + .../waymax/metrics/roadgraph/index.rst.txt | 127 + .../waymax/metrics/route/index.rst.txt | 108 + .../abstract_reward_function/index.rst.txt | 47 + _sources/autoapi/waymax/rewards/index.rst.txt | 80 + .../linear_combination_reward/index.rst.txt | 65 + .../waymax/utils/geometry/index.rst.txt | 185 + _sources/autoapi/waymax/utils/index.rst.txt | 16 + .../waymax/utils/test_utils/index.rst.txt | 135 + .../waymax/visualization/color/index.rst.txt | 26 + .../waymax/visualization/index.rst.txt | 123 + .../waymax/visualization/utils/index.rst.txt | 135 + .../waymax/visualization/viz/index.rst.txt | 149 + .../notebooks/data_demo.ipynb.txt | 136 + .../notebooks/datatypes_demo.ipynb.txt | 300 ++ .../notebooks/multi_actors_demo.ipynb.txt | 272 ++ .../wosac_submission_via_waymax.ipynb.txt | 348 ++ .../notebooks/data_demo.ipynb.txt | 136 + .../notebooks/datatypes_demo.ipynb.txt | 300 ++ .../notebooks/multi_actors_demo.ipynb.txt | 272 ++ .../wosac_submission_via_waymax.ipynb.txt | 348 ++ _sources/getting_started.rst.txt | 11 + _sources/index.rst.txt | 62 + _sources/notebooks/data_demo.ipynb.txt | 136 + _sources/notebooks/datatypes_demo.ipynb.txt | 300 ++ .../notebooks/multi_actors_demo.ipynb.txt | 272 ++ .../wosac_submission_via_waymax.ipynb.txt | 348 ++ ...e.1e8bd061cd6da7fc9cf755528e8ffc24.min.css | 1 + _sphinx_design_static/design-tabs.js | 27 + _static/basic.css | 903 ++++ _static/debug.css | 69 + ...e.1e8bd061cd6da7fc9cf755528e8ffc24.min.css | 1 + _static/design-tabs.js | 27 + _static/doctools.js | 156 + _static/documentation_options.js | 14 + _static/file.png | Bin 0 -> 286 bytes _static/graphviz.css | 19 + _static/language_data.js | 199 + _static/minus.png | Bin 0 -> 90 bytes ...69c37c29e427902b24a333a5f9fcb2f0b3ac41.css | 2342 +++++++++++ _static/plus.png | Bin 0 -> 90 bytes _static/pygments.css | 258 ++ _static/scripts/furo-extensions.js | 0 _static/scripts/furo.js | 3 + _static/scripts/furo.js.LICENSE.txt | 7 + _static/scripts/furo.js.map | 1 + _static/searchtools.js | 566 +++ _static/skeleton.css | 296 ++ _static/sphinx_highlight.js | 144 + _static/styles/furo-extensions.css | 2 + _static/styles/furo-extensions.css.map | 1 + _static/styles/furo.css | 2 + _static/styles/furo.css.map | 1 + autoapi/waymax/agents/actor_core/index.html | 602 +++ .../waymax/agents/agent_builder/index.html | 390 ++ .../waymax/agents/constant_speed/index.html | 442 ++ autoapi/waymax/agents/expert/index.html | 455 +++ autoapi/waymax/agents/index.html | 925 +++++ autoapi/waymax/agents/sim_agent/index.html | 478 +++ .../waypoint_following_agent/index.html | 742 ++++ autoapi/waymax/config/index.html | 1221 ++++++ .../dataloader/dataloader_utils/index.html | 503 +++ autoapi/waymax/dataloader/index.html | 690 ++++ .../dataloader/womd_dataloader/index.html | 442 ++ .../dataloader/womd_factories/index.html | 533 +++ .../womd_factories_internal/index.html | 334 ++ .../waymax/dataloader/womd_utils/index.html | 597 +++ autoapi/waymax/datatypes/action/index.html | 522 +++ autoapi/waymax/datatypes/array/index.html | 472 +++ autoapi/waymax/datatypes/constant/index.html | 371 ++ autoapi/waymax/datatypes/index.html | 2823 +++++++++++++ .../waymax/datatypes/object_state/index.html | 882 ++++ .../waymax/datatypes/observation/index.html | 937 +++++ .../waymax/datatypes/operations/index.html | 653 +++ autoapi/waymax/datatypes/roadgraph/index.html | 786 ++++ autoapi/waymax/datatypes/route/index.html | 534 +++ .../datatypes/simulator_state/index.html | 618 +++ .../datatypes/traffic_lights/index.html | 575 +++ .../dynamics/abstract_dynamics/index.html | 533 +++ .../waymax/dynamics/bicycle_model/index.html | 525 +++ autoapi/waymax/dynamics/delta/index.html | 501 +++ .../waymax/dynamics/discretizer/index.html | 530 +++ autoapi/waymax/dynamics/index.html | 900 ++++ .../waymax/dynamics/state_dynamics/index.html | 414 ++ .../env/abstract_environment/index.html | 569 +++ .../waymax/env/base_environment/index.html | 573 +++ autoapi/waymax/env/errors/index.html | 375 ++ autoapi/waymax/env/index.html | 1458 +++++++ .../env/planning_agent_environment/index.html | 605 +++ autoapi/waymax/env/rollout/index.html | 604 +++ autoapi/waymax/env/typedefs/index.html | 407 ++ .../env/wrappers/brax_wrapper/index.html | 656 +++ .../env/wrappers/dm_env_wrapper/index.html | 505 +++ autoapi/waymax/env/wrappers/index.html | 362 ++ autoapi/waymax/index.html | 430 ++ .../waymax/metrics/abstract_metric/index.html | 433 ++ autoapi/waymax/metrics/comfort/index.html | 458 +++ autoapi/waymax/metrics/imitation/index.html | 427 ++ autoapi/waymax/metrics/index.html | 752 ++++ .../waymax/metrics/metric_factory/index.html | 396 ++ autoapi/waymax/metrics/overlap/index.html | 418 ++ autoapi/waymax/metrics/roadgraph/index.html | 520 +++ autoapi/waymax/metrics/route/index.html | 481 +++ .../abstract_reward_function/index.html | 406 ++ autoapi/waymax/rewards/index.html | 451 ++ .../linear_combination_reward/index.html | 436 ++ autoapi/waymax/utils/geometry/index.html | 632 +++ autoapi/waymax/utils/index.html | 362 ++ autoapi/waymax/utils/test_utils/index.html | 555 +++ autoapi/waymax/visualization/color/index.html | 377 ++ autoapi/waymax/visualization/index.html | 525 +++ autoapi/waymax/visualization/utils/index.html | 525 +++ autoapi/waymax/visualization/viz/index.html | 572 +++ .../jupyter_execute/notebooks/data_demo.html | 371 ++ .../notebooks/datatypes_demo.html | 501 +++ .../notebooks/multi_actors_demo.html | 493 +++ .../wosac_submission_via_waymax.html | 586 +++ .../jupyter_execute/notebooks/data_demo.html | 371 ++ .../notebooks/datatypes_demo.html | 501 +++ .../notebooks/multi_actors_demo.html | 493 +++ .../wosac_submission_via_waymax.html | 586 +++ genindex.html | 3614 +++++++++++++++++ getting_started.html | 341 ++ index.html | 379 ++ notebooks/data_demo.html | 371 ++ notebooks/datatypes_demo.html | 501 +++ notebooks/multi_actors_demo.html | 493 +++ notebooks/wosac_submission_via_waymax.html | 586 +++ objects.inv | Bin 0 -> 10270 bytes py-modindex.html | 684 ++++ search.html | 323 ++ searchindex.js | 1 + 179 files changed, 66784 insertions(+) create mode 100644 _sources/autoapi/waymax/agents/actor_core/index.rst.txt create mode 100644 _sources/autoapi/waymax/agents/agent_builder/index.rst.txt create mode 100644 _sources/autoapi/waymax/agents/constant_speed/index.rst.txt create mode 100644 _sources/autoapi/waymax/agents/expert/index.rst.txt create mode 100644 _sources/autoapi/waymax/agents/index.rst.txt create mode 100644 _sources/autoapi/waymax/agents/sim_agent/index.rst.txt create mode 100644 _sources/autoapi/waymax/agents/waypoint_following_agent/index.rst.txt create mode 100644 _sources/autoapi/waymax/config/index.rst.txt create mode 100644 _sources/autoapi/waymax/dataloader/dataloader_utils/index.rst.txt create mode 100644 _sources/autoapi/waymax/dataloader/index.rst.txt create mode 100644 _sources/autoapi/waymax/dataloader/womd_dataloader/index.rst.txt create mode 100644 _sources/autoapi/waymax/dataloader/womd_factories/index.rst.txt create mode 100644 _sources/autoapi/waymax/dataloader/womd_factories_internal/index.rst.txt create mode 100644 _sources/autoapi/waymax/dataloader/womd_utils/index.rst.txt create mode 100644 _sources/autoapi/waymax/datatypes/action/index.rst.txt create mode 100644 _sources/autoapi/waymax/datatypes/array/index.rst.txt create mode 100644 _sources/autoapi/waymax/datatypes/constant/index.rst.txt create mode 100644 _sources/autoapi/waymax/datatypes/index.rst.txt create mode 100644 _sources/autoapi/waymax/datatypes/object_state/index.rst.txt create mode 100644 _sources/autoapi/waymax/datatypes/observation/index.rst.txt create mode 100644 _sources/autoapi/waymax/datatypes/operations/index.rst.txt create mode 100644 _sources/autoapi/waymax/datatypes/roadgraph/index.rst.txt create mode 100644 _sources/autoapi/waymax/datatypes/route/index.rst.txt create mode 100644 _sources/autoapi/waymax/datatypes/simulator_state/index.rst.txt create mode 100644 _sources/autoapi/waymax/datatypes/traffic_lights/index.rst.txt create mode 100644 _sources/autoapi/waymax/dynamics/abstract_dynamics/index.rst.txt create mode 100644 _sources/autoapi/waymax/dynamics/bicycle_model/index.rst.txt create mode 100644 _sources/autoapi/waymax/dynamics/delta/index.rst.txt create mode 100644 _sources/autoapi/waymax/dynamics/discretizer/index.rst.txt create mode 100644 _sources/autoapi/waymax/dynamics/index.rst.txt create mode 100644 _sources/autoapi/waymax/dynamics/state_dynamics/index.rst.txt create mode 100644 _sources/autoapi/waymax/env/abstract_environment/index.rst.txt create mode 100644 _sources/autoapi/waymax/env/base_environment/index.rst.txt create mode 100644 _sources/autoapi/waymax/env/errors/index.rst.txt create mode 100644 _sources/autoapi/waymax/env/index.rst.txt create mode 100644 _sources/autoapi/waymax/env/planning_agent_environment/index.rst.txt create mode 100644 _sources/autoapi/waymax/env/rollout/index.rst.txt create mode 100644 _sources/autoapi/waymax/env/typedefs/index.rst.txt create mode 100644 _sources/autoapi/waymax/env/wrappers/brax_wrapper/index.rst.txt create mode 100644 _sources/autoapi/waymax/env/wrappers/dm_env_wrapper/index.rst.txt create mode 100644 _sources/autoapi/waymax/env/wrappers/index.rst.txt create mode 100644 _sources/autoapi/waymax/index.rst.txt create mode 100644 _sources/autoapi/waymax/metrics/abstract_metric/index.rst.txt create mode 100644 _sources/autoapi/waymax/metrics/comfort/index.rst.txt create mode 100644 _sources/autoapi/waymax/metrics/imitation/index.rst.txt create mode 100644 _sources/autoapi/waymax/metrics/index.rst.txt create mode 100644 _sources/autoapi/waymax/metrics/metric_factory/index.rst.txt create mode 100644 _sources/autoapi/waymax/metrics/overlap/index.rst.txt create mode 100644 _sources/autoapi/waymax/metrics/roadgraph/index.rst.txt create mode 100644 _sources/autoapi/waymax/metrics/route/index.rst.txt create mode 100644 _sources/autoapi/waymax/rewards/abstract_reward_function/index.rst.txt create mode 100644 _sources/autoapi/waymax/rewards/index.rst.txt create mode 100644 _sources/autoapi/waymax/rewards/linear_combination_reward/index.rst.txt create mode 100644 _sources/autoapi/waymax/utils/geometry/index.rst.txt create mode 100644 _sources/autoapi/waymax/utils/index.rst.txt create mode 100644 _sources/autoapi/waymax/utils/test_utils/index.rst.txt create mode 100644 _sources/autoapi/waymax/visualization/color/index.rst.txt create mode 100644 _sources/autoapi/waymax/visualization/index.rst.txt create mode 100644 _sources/autoapi/waymax/visualization/utils/index.rst.txt create mode 100644 _sources/autoapi/waymax/visualization/viz/index.rst.txt create mode 100644 _sources/build/jupyter_execute/build/jupyter_execute/notebooks/data_demo.ipynb.txt create mode 100644 _sources/build/jupyter_execute/build/jupyter_execute/notebooks/datatypes_demo.ipynb.txt create mode 100644 _sources/build/jupyter_execute/build/jupyter_execute/notebooks/multi_actors_demo.ipynb.txt create mode 100644 _sources/build/jupyter_execute/build/jupyter_execute/notebooks/wosac_submission_via_waymax.ipynb.txt create mode 100644 _sources/build/jupyter_execute/notebooks/data_demo.ipynb.txt create mode 100644 _sources/build/jupyter_execute/notebooks/datatypes_demo.ipynb.txt create mode 100644 _sources/build/jupyter_execute/notebooks/multi_actors_demo.ipynb.txt create mode 100644 _sources/build/jupyter_execute/notebooks/wosac_submission_via_waymax.ipynb.txt create mode 100644 _sources/getting_started.rst.txt create mode 100644 _sources/index.rst.txt create mode 100644 _sources/notebooks/data_demo.ipynb.txt create mode 100644 _sources/notebooks/datatypes_demo.ipynb.txt create mode 100644 _sources/notebooks/multi_actors_demo.ipynb.txt create mode 100644 _sources/notebooks/wosac_submission_via_waymax.ipynb.txt create mode 100644 _sphinx_design_static/design-style.1e8bd061cd6da7fc9cf755528e8ffc24.min.css create mode 100644 _sphinx_design_static/design-tabs.js create mode 100644 _static/basic.css create mode 100644 _static/debug.css create mode 100644 _static/design-style.1e8bd061cd6da7fc9cf755528e8ffc24.min.css create mode 100644 _static/design-tabs.js create mode 100644 _static/doctools.js create mode 100644 _static/documentation_options.js create mode 100644 _static/file.png create mode 100644 _static/graphviz.css create mode 100644 _static/language_data.js create mode 100644 _static/minus.png create mode 100644 _static/mystnb.4510f1fc1dee50b3e5859aac5469c37c29e427902b24a333a5f9fcb2f0b3ac41.css create mode 100644 _static/plus.png create mode 100644 _static/pygments.css create mode 100644 _static/scripts/furo-extensions.js create mode 100644 _static/scripts/furo.js create mode 100644 _static/scripts/furo.js.LICENSE.txt create mode 100644 _static/scripts/furo.js.map create mode 100644 _static/searchtools.js create mode 100644 _static/skeleton.css create mode 100644 _static/sphinx_highlight.js create mode 100644 _static/styles/furo-extensions.css create mode 100644 _static/styles/furo-extensions.css.map create mode 100644 _static/styles/furo.css create mode 100644 _static/styles/furo.css.map create mode 100644 autoapi/waymax/agents/actor_core/index.html create mode 100644 autoapi/waymax/agents/agent_builder/index.html create mode 100644 autoapi/waymax/agents/constant_speed/index.html create mode 100644 autoapi/waymax/agents/expert/index.html create mode 100644 autoapi/waymax/agents/index.html create mode 100644 autoapi/waymax/agents/sim_agent/index.html create mode 100644 autoapi/waymax/agents/waypoint_following_agent/index.html create mode 100644 autoapi/waymax/config/index.html create mode 100644 autoapi/waymax/dataloader/dataloader_utils/index.html create mode 100644 autoapi/waymax/dataloader/index.html create mode 100644 autoapi/waymax/dataloader/womd_dataloader/index.html create mode 100644 autoapi/waymax/dataloader/womd_factories/index.html create mode 100644 autoapi/waymax/dataloader/womd_factories_internal/index.html create mode 100644 autoapi/waymax/dataloader/womd_utils/index.html create mode 100644 autoapi/waymax/datatypes/action/index.html create mode 100644 autoapi/waymax/datatypes/array/index.html create mode 100644 autoapi/waymax/datatypes/constant/index.html create mode 100644 autoapi/waymax/datatypes/index.html create mode 100644 autoapi/waymax/datatypes/object_state/index.html create mode 100644 autoapi/waymax/datatypes/observation/index.html create mode 100644 autoapi/waymax/datatypes/operations/index.html create mode 100644 autoapi/waymax/datatypes/roadgraph/index.html create mode 100644 autoapi/waymax/datatypes/route/index.html create mode 100644 autoapi/waymax/datatypes/simulator_state/index.html create mode 100644 autoapi/waymax/datatypes/traffic_lights/index.html create mode 100644 autoapi/waymax/dynamics/abstract_dynamics/index.html create mode 100644 autoapi/waymax/dynamics/bicycle_model/index.html create mode 100644 autoapi/waymax/dynamics/delta/index.html create mode 100644 autoapi/waymax/dynamics/discretizer/index.html create mode 100644 autoapi/waymax/dynamics/index.html create mode 100644 autoapi/waymax/dynamics/state_dynamics/index.html create mode 100644 autoapi/waymax/env/abstract_environment/index.html create mode 100644 autoapi/waymax/env/base_environment/index.html create mode 100644 autoapi/waymax/env/errors/index.html create mode 100644 autoapi/waymax/env/index.html create mode 100644 autoapi/waymax/env/planning_agent_environment/index.html create mode 100644 autoapi/waymax/env/rollout/index.html create mode 100644 autoapi/waymax/env/typedefs/index.html create mode 100644 autoapi/waymax/env/wrappers/brax_wrapper/index.html create mode 100644 autoapi/waymax/env/wrappers/dm_env_wrapper/index.html create mode 100644 autoapi/waymax/env/wrappers/index.html create mode 100644 autoapi/waymax/index.html create mode 100644 autoapi/waymax/metrics/abstract_metric/index.html create mode 100644 autoapi/waymax/metrics/comfort/index.html create mode 100644 autoapi/waymax/metrics/imitation/index.html create mode 100644 autoapi/waymax/metrics/index.html create mode 100644 autoapi/waymax/metrics/metric_factory/index.html create mode 100644 autoapi/waymax/metrics/overlap/index.html create mode 100644 autoapi/waymax/metrics/roadgraph/index.html create mode 100644 autoapi/waymax/metrics/route/index.html create mode 100644 autoapi/waymax/rewards/abstract_reward_function/index.html create mode 100644 autoapi/waymax/rewards/index.html create mode 100644 autoapi/waymax/rewards/linear_combination_reward/index.html create mode 100644 autoapi/waymax/utils/geometry/index.html create mode 100644 autoapi/waymax/utils/index.html create mode 100644 autoapi/waymax/utils/test_utils/index.html create mode 100644 autoapi/waymax/visualization/color/index.html create mode 100644 autoapi/waymax/visualization/index.html create mode 100644 autoapi/waymax/visualization/utils/index.html create mode 100644 autoapi/waymax/visualization/viz/index.html create mode 100644 build/jupyter_execute/build/jupyter_execute/notebooks/data_demo.html create mode 100644 build/jupyter_execute/build/jupyter_execute/notebooks/datatypes_demo.html create mode 100644 build/jupyter_execute/build/jupyter_execute/notebooks/multi_actors_demo.html create mode 100644 build/jupyter_execute/build/jupyter_execute/notebooks/wosac_submission_via_waymax.html create mode 100644 build/jupyter_execute/notebooks/data_demo.html create mode 100644 build/jupyter_execute/notebooks/datatypes_demo.html create mode 100644 build/jupyter_execute/notebooks/multi_actors_demo.html create mode 100644 build/jupyter_execute/notebooks/wosac_submission_via_waymax.html create mode 100644 genindex.html create mode 100644 getting_started.html create mode 100644 index.html create mode 100644 notebooks/data_demo.html create mode 100644 notebooks/datatypes_demo.html create mode 100644 notebooks/multi_actors_demo.html create mode 100644 notebooks/wosac_submission_via_waymax.html create mode 100644 objects.inv create mode 100644 py-modindex.html create mode 100644 search.html create mode 100644 searchindex.js diff --git a/_sources/autoapi/waymax/agents/actor_core/index.rst.txt b/_sources/autoapi/waymax/agents/actor_core/index.rst.txt new file mode 100644 index 0000000..65de72a --- /dev/null +++ b/_sources/autoapi/waymax/agents/actor_core/index.rst.txt @@ -0,0 +1,177 @@ +:py:mod:`waymax.agents.actor_core` +================================== + +.. py:module:: waymax.agents.actor_core + +.. autoapi-nested-parse:: + + Abstract definition of a Waymax actor for use at inference-time. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.agents.actor_core.WaymaxActorOutput + waymax.agents.actor_core.WaymaxActorCore + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.agents.actor_core.register_actor_core + waymax.agents.actor_core.actor_core_factory + waymax.agents.actor_core.merge_actions + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.agents.actor_core.ActorState + waymax.agents.actor_core.Params + waymax.agents.actor_core._ActorCore + + +.. py:data:: ActorState + + + +.. py:data:: Params + + + +.. py:class:: WaymaxActorOutput + + + Output of the Waymax actor including an action and its internal state. + + .. attribute:: actor_state + + Internal state for whatever the agent needs to keep as its + state. This can be recurrent embeddings or accounting information. + + .. attribute:: action + + Action of shape (..., num_objects) predicted by the Waymax actor at + the most recent simulation step given the inputs in the `select_action` + function of `WaymaxActorCore`. + + .. attribute:: is_controlled + + A binary indicator of shape (..., num_objects) representing + which objects are controlled by the actor. + + .. py:attribute:: actor_state + :type: ActorState + + + + .. py:attribute:: action + :type: waymax.datatypes.Action + + + + .. py:attribute:: is_controlled + :type: jax.Array + + + + .. py:method:: validate() + + Validates shapes. + + + +.. py:class:: WaymaxActorCore + + + Bases: :py:obj:`abc.ABC` + + Interface that defines actor functionality for inference. + + .. py:property:: name + :type: str + :abstractmethod: + + Name of the agent used for inspection and logging. + + .. py:method:: init(rng: jax.Array, state: waymax.datatypes.SimulatorState) -> ActorState + :abstractmethod: + + Initializes the actor's internal state. + + ActorState is a generic type which can contain anything that the agent + needs to pass through to the next call, e.g. for recurrent state or + batch normalization. The `init` function takes a random key to help + randomize initialization and the initial timestep. + + :param rng: A random key. + :param state: The initial simulator state. + + :returns: The actor's initial state. + + + .. py:method:: select_action(params: Params, state: waymax.datatypes.SimulatorState, actor_state: ActorState, rng: jax.Array) -> WaymaxActorOutput + :abstractmethod: + + Selects an action given the current simulator state. + + :param params: Actor parameters, e.g. neural network weights. + :param state: The current simulator state. + :param actor_state: The actor state, e.g. recurrent state or batch normalization. + :param rng: A random key. + + :returns: An actor output containing the next action and actor state. + + + +.. py:data:: _ActorCore + + + +.. py:function:: register_actor_core(actor_core_cls: type[_ActorCore]) -> type[_ActorCore] + + Registers an ActorCore class as a PyTree node. + + +.. py:function:: actor_core_factory(init: Callable[[jax.Array, waymax.datatypes.SimulatorState], ActorState], select_action: Callable[[Params, waymax.datatypes.SimulatorState, ActorState, jax.Array], WaymaxActorOutput], name: str = 'WaymaxActorCore') -> WaymaxActorCore + + Creates a WaymaxActorCore from pure functions. + + :param init: A function that initializes the actor's internal state. This is a + generic type which can contain anything that the agent needs to pass + through to the next call. The `init` function takes a random key to help + randomize initialization and the initial timestep. It should return its + specific internal state. + :param select_action: A function that selects an action given the current simulator + state of the environment, the previous actor state and an optional random + key. Returns the action and the updated internal actor state. + :param name: Name of the agent used for inspection and logging. + + :returns: An actor core instance defined by init and select_action. + + +.. py:function:: merge_actions(actor_outputs: Sequence[WaymaxActorOutput]) -> waymax.datatypes.Action + + Combines multiple actor_outputs into one action instance. + + :param actor_outputs: A sequence of WaymaxActorOutput to be combined, each + corresponds to a different actor. Note different actor should not be + controlling the same object (i.e. is_controlled flags from different + actors should be disjoint). Note all actors must use the same dynamics + model. + + :returns: An `Action` consists of information from all actor outputs. + + diff --git a/_sources/autoapi/waymax/agents/agent_builder/index.rst.txt b/_sources/autoapi/waymax/agents/agent_builder/index.rst.txt new file mode 100644 index 0000000..8d81744 --- /dev/null +++ b/_sources/autoapi/waymax/agents/agent_builder/index.rst.txt @@ -0,0 +1,34 @@ +:py:mod:`waymax.agents.agent_builder` +===================================== + +.. py:module:: waymax.agents.agent_builder + +.. autoapi-nested-parse:: + + Waymax sim agent builder functions. + + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.agents.agent_builder.create_sim_agents_from_config + + + +.. py:function:: create_sim_agents_from_config(config: waymax.config.SimAgentConfig) -> waymax.agents.actor_core.WaymaxActorCore + + Constructs sim agent WaymaxActorCore objects from a config. + + :param config: Waymax sim agent config specifying agent type and controlled + objects' type. + + :returns: Constructed sim agents. + + diff --git a/_sources/autoapi/waymax/agents/constant_speed/index.rst.txt b/_sources/autoapi/waymax/agents/constant_speed/index.rst.txt new file mode 100644 index 0000000..0556115 --- /dev/null +++ b/_sources/autoapi/waymax/agents/constant_speed/index.rst.txt @@ -0,0 +1,68 @@ +:py:mod:`waymax.agents.constant_speed` +====================================== + +.. py:module:: waymax.agents.constant_speed + +.. autoapi-nested-parse:: + + Constant speed agents. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.agents.constant_speed.ConstantSpeedPolicy + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.agents.constant_speed.create_constant_speed_actor + + + +.. py:function:: create_constant_speed_actor(dynamics_model: waymax.dynamics.DynamicsModel, is_controlled_func: Callable[[waymax.datatypes.SimulatorState], jax.Array], speed: Optional[float] = None) -> waymax.agents.actor_core.WaymaxActorCore + + Creates an actor with constant speed without changing objects' heading. + + Note the difference against ConstantSpeedPolicy is that an actor requires + input of a dynamics model, while a policy does not (it assumes to use + StateDynamics). + + :param dynamics_model: The dynamics model the actor is using that defines the + action output by the actor. + :param is_controlled_func: Defines which objects are controlled by this actor. + :param speed: Speed of the actor, if None, speed from previous step is used. + + :returns: An statelss actor that drives the controlled objects with constant speed. + + +.. py:class:: ConstantSpeedPolicy(speed: float = 0.0) + + + Bases: :py:obj:`waymax.agents.waypoint_following_agent.WaypointFollowingPolicy` + + A policy that maintains a constant speed for all sim agents. + + .. py:method:: update_speed(state: waymax.datatypes.SimulatorState, dt: float = 0.1) -> tuple[jax.Array, jax.Array] + + Sets the speed for each agent in the current sim step to a constant. + + :param state: The simulator state of shape (...). + :param dt: Delta between timesteps of the simulator state. + + :returns: A (..., num_objects) float array of constant speeds. + valids: A (..., num_objects) bool array of valids. + :rtype: speeds + + + diff --git a/_sources/autoapi/waymax/agents/expert/index.rst.txt b/_sources/autoapi/waymax/agents/expert/index.rst.txt new file mode 100644 index 0000000..355feeb --- /dev/null +++ b/_sources/autoapi/waymax/agents/expert/index.rst.txt @@ -0,0 +1,77 @@ +:py:mod:`waymax.agents.expert` +============================== + +.. py:module:: waymax.agents.expert + +.. autoapi-nested-parse:: + + Expert actor which returns an action inferred from logged data. + + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.agents.expert.infer_expert_action + waymax.agents.expert.create_expert_actor + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.agents.expert._EXPERT_NAME + waymax.agents.expert._IS_SDC_FUNC + + +.. py:data:: _EXPERT_NAME + :value: 'expert' + + + +.. py:data:: _IS_SDC_FUNC + + + +.. py:function:: infer_expert_action(simulator_state: waymax.datatypes.SimulatorState, dynamics_model: waymax.dynamics.DynamicsModel) -> waymax.datatypes.Action + + Infers an action from sim_traj[timestep] to log_traj[timestep + 1]. + + :param simulator_state: State of the simulator at the current timestep. Will use + the `sim_trajectory` and `log_trajectory` fields to calculate an action. + :param dynamics_model: Dynamics model whose `inverse` function will be used to + infer the expert action given the logged states. + + :returns: + + Action that will take the agent from sim_traj[timestep] to + log_traj[timestep + 1]. + + +.. py:function:: create_expert_actor(dynamics_model: waymax.dynamics.DynamicsModel, is_controlled_func: Callable[[waymax.datatypes.SimulatorState], jax.Array] = _IS_SDC_FUNC) -> waymax.agents.actor_core.WaymaxActorCore + + Creates an expert agent using the WaymaxActorCore interface. + + This agent infers an action from the `expert` by inferring an action using + the logged data. It does this by calling the `inverse` function on the passed + in `dynamics` parameter. It will return an action in the format returned by + the `dynamics` parameter. + + :param dynamics_model: Dynamics model whose `inverse` function will be used to + infer the expert action given the logged states. + :param is_controlled_func: A function that maps state to a controlled objects mask + of shape (..., num_objects). + + :returns: A Stateless Waymax actor which returns an `expert` action for all controlled + objects (defined by is_controlled_func) by inferring the best-fit action + given the logged state. + + diff --git a/_sources/autoapi/waymax/agents/index.rst.txt b/_sources/autoapi/waymax/agents/index.rst.txt new file mode 100644 index 0000000..d4a0fcf --- /dev/null +++ b/_sources/autoapi/waymax/agents/index.rst.txt @@ -0,0 +1,407 @@ +:py:mod:`waymax.agents` +======================= + +.. py:module:: waymax.agents + +.. autoapi-nested-parse:: + + Waymax agent interfaces and sim agent implementations. + + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + actor_core/index.rst + agent_builder/index.rst + constant_speed/index.rst + expert/index.rst + sim_agent/index.rst + waypoint_following_agent/index.rst + + +Package Contents +---------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.agents.WaymaxActorCore + waymax.agents.WaymaxActorOutput + waymax.agents.FrozenSimPolicy + waymax.agents.SimAgentActor + waymax.agents.IDMRoutePolicy + waymax.agents.WaypointFollowingPolicy + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.agents.actor_core_factory + waymax.agents.merge_actions + waymax.agents.create_sim_agents_from_config + waymax.agents.create_constant_speed_actor + waymax.agents.create_expert_actor + + + +.. py:function:: actor_core_factory(init: Callable[[jax.Array, waymax.datatypes.SimulatorState], ActorState], select_action: Callable[[Params, waymax.datatypes.SimulatorState, ActorState, jax.Array], WaymaxActorOutput], name: str = 'WaymaxActorCore') -> WaymaxActorCore + + Creates a WaymaxActorCore from pure functions. + + :param init: A function that initializes the actor's internal state. This is a + generic type which can contain anything that the agent needs to pass + through to the next call. The `init` function takes a random key to help + randomize initialization and the initial timestep. It should return its + specific internal state. + :param select_action: A function that selects an action given the current simulator + state of the environment, the previous actor state and an optional random + key. Returns the action and the updated internal actor state. + :param name: Name of the agent used for inspection and logging. + + :returns: An actor core instance defined by init and select_action. + + +.. py:function:: merge_actions(actor_outputs: Sequence[WaymaxActorOutput]) -> waymax.datatypes.Action + + Combines multiple actor_outputs into one action instance. + + :param actor_outputs: A sequence of WaymaxActorOutput to be combined, each + corresponds to a different actor. Note different actor should not be + controlling the same object (i.e. is_controlled flags from different + actors should be disjoint). Note all actors must use the same dynamics + model. + + :returns: An `Action` consists of information from all actor outputs. + + +.. py:class:: WaymaxActorCore + + + Bases: :py:obj:`abc.ABC` + + Interface that defines actor functionality for inference. + + .. py:property:: name + :type: str + :abstractmethod: + + Name of the agent used for inspection and logging. + + .. py:method:: init(rng: jax.Array, state: waymax.datatypes.SimulatorState) -> ActorState + :abstractmethod: + + Initializes the actor's internal state. + + ActorState is a generic type which can contain anything that the agent + needs to pass through to the next call, e.g. for recurrent state or + batch normalization. The `init` function takes a random key to help + randomize initialization and the initial timestep. + + :param rng: A random key. + :param state: The initial simulator state. + + :returns: The actor's initial state. + + + .. py:method:: select_action(params: Params, state: waymax.datatypes.SimulatorState, actor_state: ActorState, rng: jax.Array) -> WaymaxActorOutput + :abstractmethod: + + Selects an action given the current simulator state. + + :param params: Actor parameters, e.g. neural network weights. + :param state: The current simulator state. + :param actor_state: The actor state, e.g. recurrent state or batch normalization. + :param rng: A random key. + + :returns: An actor output containing the next action and actor state. + + + +.. py:class:: WaymaxActorOutput + + + Output of the Waymax actor including an action and its internal state. + + .. attribute:: actor_state + + Internal state for whatever the agent needs to keep as its + state. This can be recurrent embeddings or accounting information. + + .. attribute:: action + + Action of shape (..., num_objects) predicted by the Waymax actor at + the most recent simulation step given the inputs in the `select_action` + function of `WaymaxActorCore`. + + .. attribute:: is_controlled + + A binary indicator of shape (..., num_objects) representing + which objects are controlled by the actor. + + .. py:attribute:: actor_state + :type: ActorState + + + + .. py:attribute:: action + :type: waymax.datatypes.Action + + + + .. py:attribute:: is_controlled + :type: jax.Array + + + + .. py:method:: validate() + + Validates shapes. + + + +.. py:function:: create_sim_agents_from_config(config: waymax.config.SimAgentConfig) -> waymax.agents.actor_core.WaymaxActorCore + + Constructs sim agent WaymaxActorCore objects from a config. + + :param config: Waymax sim agent config specifying agent type and controlled + objects' type. + + :returns: Constructed sim agents. + + +.. py:function:: create_constant_speed_actor(dynamics_model: waymax.dynamics.DynamicsModel, is_controlled_func: Callable[[waymax.datatypes.SimulatorState], jax.Array], speed: Optional[float] = None) -> waymax.agents.actor_core.WaymaxActorCore + + Creates an actor with constant speed without changing objects' heading. + + Note the difference against ConstantSpeedPolicy is that an actor requires + input of a dynamics model, while a policy does not (it assumes to use + StateDynamics). + + :param dynamics_model: The dynamics model the actor is using that defines the + action output by the actor. + :param is_controlled_func: Defines which objects are controlled by this actor. + :param speed: Speed of the actor, if None, speed from previous step is used. + + :returns: An statelss actor that drives the controlled objects with constant speed. + + +.. py:function:: create_expert_actor(dynamics_model: waymax.dynamics.DynamicsModel, is_controlled_func: Callable[[waymax.datatypes.SimulatorState], jax.Array] = _IS_SDC_FUNC) -> waymax.agents.actor_core.WaymaxActorCore + + Creates an expert agent using the WaymaxActorCore interface. + + This agent infers an action from the `expert` by inferring an action using + the logged data. It does this by calling the `inverse` function on the passed + in `dynamics` parameter. It will return an action in the format returned by + the `dynamics` parameter. + + :param dynamics_model: Dynamics model whose `inverse` function will be used to + infer the expert action given the logged states. + :param is_controlled_func: A function that maps state to a controlled objects mask + of shape (..., num_objects). + + :returns: A Stateless Waymax actor which returns an `expert` action for all controlled + objects (defined by is_controlled_func) by inferring the best-fit action + given the logged state. + + +.. py:class:: FrozenSimPolicy(is_controlled_func: Optional[Callable[[waymax.datatypes.SimulatorState], jax.Array]] = None) + + + Bases: :py:obj:`SimAgentActor` + + A sim agent policy that does not update object trajectories. + + This class is primarily intended to be used for testing or debugging purposes. + + .. py:method:: update_trajectory(state: waymax.datatypes.SimulatorState) -> waymax.datatypes.TrajectoryUpdate + + Returns the current sim trajectory as the next update. + + + +.. py:class:: SimAgentActor(is_controlled_func: Optional[Callable[[waymax.datatypes.SimulatorState], jax.Array]] = None) + + + Bases: :py:obj:`waymax.agents.actor_core.WaymaxActorCore` + + Base class for simulated agents. + + Subclasses should implement the `update_trajectory` method. As SimAgentActor + outputs TrajectoryUpdate actions, it is primarily intended to be used with + the StateDynamics dynamics model. + + .. py:property:: name + :type: str + + Name of the agent used for inspection and logging. + + .. py:method:: update_trajectory(state: waymax.datatypes.SimulatorState) -> waymax.datatypes.TrajectoryUpdate + :abstractmethod: + + Updates the trajectory for all simulated agents. + + :param state: The current simulator state. + + :returns: A trajectory update of shape (..., num_objects, num_timesteps=1) that + contains the updated positions and velocities for all simulated agents + for the next timestep. + + + .. py:method:: init(rng: jax.Array, state: waymax.datatypes.SimulatorState) + + Returns an empty initial state. + + + .. py:method:: select_action(params: waymax.agents.actor_core.Params, state: waymax.datatypes.SimulatorState, actor_state: Any, rng: jax.Array) -> waymax.agents.actor_core.WaymaxActorOutput + + Selects an action given the current simulator state. + + :param params: Actor parameters, e.g. neural network weights. + :param state: The current simulator state. + :param actor_state: The actor state, e.g. recurrent state or batch normalization. + :param rng: A random key. + + :returns: An actor output containing the next action and actor state. + + + +.. py:class:: IDMRoutePolicy(is_controlled_func: Optional[Callable[[waymax.datatypes.SimulatorState], jax.Array]] = None, desired_vel: float = 30.0, min_spacing: float = 2.0, safe_time_headway: float = 2.0, max_accel: float = 2.0, max_deccel: float = 4.0, delta: float = 4.0, max_lookahead: int = 10, additional_lookahead_points: int = 10, additional_lookahead_distance: float = 10.0) + + + Bases: :py:obj:`WaypointFollowingPolicy` + + A policy implementing the intelligent driver model (IDM). + + This policy uses IDM to compute the acceleration and velocities for the + agent while it follows its own logged future. + + .. py:method:: update_speed(state: waymax.datatypes.SimulatorState, dt: float = _DEFAULT_TIME_DELTA) -> tuple[jax.Array, jax.Array] + + Returns the new speed for each agent in the current simulation step. + + :param state: The simulator state of shape (...). + :param dt: Delta between timesteps of the simulator state. + + :returns: A (..., num_objects) float array of speeds. + valids: A (..., num_objects) bool array of valids. + :rtype: speeds + + + .. py:method:: _get_accel(log_waypoints: waymax.datatypes.Trajectory, cur_position: jax.Array, cur_speed: jax.Array, obj_curr_traj: waymax.datatypes.Trajectory) -> jax.Array + + Computes vehicle accelerations according to IDM for a single vehicle. + + Note log_waypoints and obj_curr_traj contain the same set of objects, thus + need to remove collision against oneself when computing pairwise collision. + + :param log_waypoints: A trajectory of the agents' future of shape (..., + num_objects, num_timesteps). + :param cur_position: Current positions for the agents of shape (..., num_objects, + 3). + :param cur_speed: Current speeds for the agents of shape (..., num_objects). + :param obj_curr_traj: Trajectory containing the state for all current objects of + shape (..., num_objects, num_timesteps=1). + + :returns: + + A vector of all vehicles' accelerations after solving for them of shape + (..., num_objects). + + + .. py:method:: _compute_lead_velocity(future_speeds: jax.Array, collisions_per_agent: jax.Array, future_speeds_valid: Optional[jax.Array] = None) -> jax.Array + + Computes the velocity of the object at the closest collision. + + :param future_speeds: Future speeds per agent of shape (..., num_objects, + num_timesteps). + :param collisions_per_agent: Future collision indications of shape (..., + num_objects, num_timesteps). + :param future_speeds_valid: Boolean mask for future speeds of shape (..., + num_objects, num_timesteps). + + :returns: + + An array containing the velocity of the colliding object at the + closest collision of shape (...). + + + .. py:method:: _compute_lead_distance(agent_future: jax.Array, collision_indicator: jax.Array, agent_future_valid: Optional[jax.Array] = None, current_position: Optional[jax.Array] = None, use_arclength=False) -> jax.Array + + Computes the distance between the agent and the nearest collision. + + :param agent_future: Agent's future positions {x, y, z} of shape (..., + num_timesteps, 3). + :param collision_indicator: Collision indications of shape (..., num_timesteps). + :param agent_future_valid: Boolean mask for agent's future positions of shape + (..., num_timesteps). + :param current_position: Array of the vehicle's current positions {x, y, z} of + shape (..., 1, 3). If None, will use the first element of agent_future + as the current position. + :param use_arclength: Whether to use arclength for computing collisions. + Arclength is more accurate but is not robust to futures with mixed + valids. + + :returns: An array of distances to the agent's closest collision of shape (...). + + + +.. py:class:: WaypointFollowingPolicy(is_controlled_func: Optional[Callable[[waymax.datatypes.SimulatorState], jax.Array]] = None) + + + Bases: :py:obj:`waymax.agents.sim_agent.SimAgentActor` + + A base class for all waypoint-following sim agents. + + The WaypointFollowingPolicy will force sim agents to travel along a + pre-defined path (the agent's future in the log trajectory). The behavior + of the vehicle is determined by setting its speed via the update_speed() + method, which will update the velocity of the vehicle. + + .. py:method:: update_trajectory(state: waymax.datatypes.SimulatorState) -> waymax.datatypes.TrajectoryUpdate + + Returns a trajectory update of shape (..., num_objects, 1). + + + .. py:method:: _get_next_trajectory_by_projection(log_traj: waymax.datatypes.Trajectory, cur_sim_traj: waymax.datatypes.Trajectory, new_speed: jax.Array, new_speed_valid: jax.Array, dt: float = _DEFAULT_TIME_DELTA) -> waymax.datatypes.Trajectory + + Computes the next trajectory. + + :param log_traj: Logged trajectory for the simulation of shape (..., num_objects, + num_timesteps). + :param cur_sim_traj: Current simulated trajectory for the simulation of shape + (..., num_objects, num_timesteps=1). + :param new_speed: Updated speed for the agents after solving for velocity of + shape (..., num_objects). + :param new_speed_valid: Updated validity for the speed updates of the agents + after (..., num_objects). + :param dt: Delta between timesteps of the simulator state. + + :returns: + + The next Trajectory projected onto log_traj of shape + (..., num_objects, num_timesteps=1). + + + .. py:method:: update_speed(state: waymax.datatypes.SimulatorState, dt: float = _DEFAULT_TIME_DELTA) -> tuple[jax.Array, jax.Array] + :abstractmethod: + + Updates the speed for each agent in the current simulation step. + + :param state: The simulator state of shape (...). + :param dt: Delta between timesteps of the simulator state. + + :returns: A (..., num_objects) float array of speeds. + valids: A (..., num_objects) bool array of valids. + :rtype: speeds + + + diff --git a/_sources/autoapi/waymax/agents/sim_agent/index.rst.txt b/_sources/autoapi/waymax/agents/sim_agent/index.rst.txt new file mode 100644 index 0000000..5d1d638 --- /dev/null +++ b/_sources/autoapi/waymax/agents/sim_agent/index.rst.txt @@ -0,0 +1,98 @@ +:py:mod:`waymax.agents.sim_agent` +================================= + +.. py:module:: waymax.agents.sim_agent + +.. autoapi-nested-parse:: + + Base class for Waymax sim agents. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.agents.sim_agent.SimAgentActor + waymax.agents.sim_agent.FrozenSimPolicy + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.agents.sim_agent._DEFAULT_CONTROL_FUNC + + +.. py:data:: _DEFAULT_CONTROL_FUNC + + + +.. py:class:: SimAgentActor(is_controlled_func: Optional[Callable[[waymax.datatypes.SimulatorState], jax.Array]] = None) + + + Bases: :py:obj:`waymax.agents.actor_core.WaymaxActorCore` + + Base class for simulated agents. + + Subclasses should implement the `update_trajectory` method. As SimAgentActor + outputs TrajectoryUpdate actions, it is primarily intended to be used with + the StateDynamics dynamics model. + + .. py:property:: name + :type: str + + Name of the agent used for inspection and logging. + + .. py:method:: update_trajectory(state: waymax.datatypes.SimulatorState) -> waymax.datatypes.TrajectoryUpdate + :abstractmethod: + + Updates the trajectory for all simulated agents. + + :param state: The current simulator state. + + :returns: A trajectory update of shape (..., num_objects, num_timesteps=1) that + contains the updated positions and velocities for all simulated agents + for the next timestep. + + + .. py:method:: init(rng: jax.Array, state: waymax.datatypes.SimulatorState) + + Returns an empty initial state. + + + .. py:method:: select_action(params: waymax.agents.actor_core.Params, state: waymax.datatypes.SimulatorState, actor_state: Any, rng: jax.Array) -> waymax.agents.actor_core.WaymaxActorOutput + + Selects an action given the current simulator state. + + :param params: Actor parameters, e.g. neural network weights. + :param state: The current simulator state. + :param actor_state: The actor state, e.g. recurrent state or batch normalization. + :param rng: A random key. + + :returns: An actor output containing the next action and actor state. + + + +.. py:class:: FrozenSimPolicy(is_controlled_func: Optional[Callable[[waymax.datatypes.SimulatorState], jax.Array]] = None) + + + Bases: :py:obj:`SimAgentActor` + + A sim agent policy that does not update object trajectories. + + This class is primarily intended to be used for testing or debugging purposes. + + .. py:method:: update_trajectory(state: waymax.datatypes.SimulatorState) -> waymax.datatypes.TrajectoryUpdate + + Returns the current sim trajectory as the next update. + + + diff --git a/_sources/autoapi/waymax/agents/waypoint_following_agent/index.rst.txt b/_sources/autoapi/waymax/agents/waypoint_following_agent/index.rst.txt new file mode 100644 index 0000000..5bc9ecf --- /dev/null +++ b/_sources/autoapi/waymax/agents/waypoint_following_agent/index.rst.txt @@ -0,0 +1,267 @@ +:py:mod:`waymax.agents.waypoint_following_agent` +================================================ + +.. py:module:: waymax.agents.waypoint_following_agent + +.. autoapi-nested-parse:: + + Implementations for waypoint following sim agents. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.agents.waypoint_following_agent.WaypointFollowingPolicy + waymax.agents.waypoint_following_agent.IDMRoutePolicy + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.agents.waypoint_following_agent._find_reference_traj_from_log_traj + waymax.agents.waypoint_following_agent._project_to_a_trajectory + waymax.agents.waypoint_following_agent._compute_arclengths + waymax.agents.waypoint_following_agent._add_headway_waypoints + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.agents.waypoint_following_agent._DEFAULT_LEAD_DISTANCE + waymax.agents.waypoint_following_agent._DEFAULT_LEAD_VELOCITY + waymax.agents.waypoint_following_agent._MINIMUM_LEAD_DISTANCE + waymax.agents.waypoint_following_agent._DEFAULT_TIME_DELTA + waymax.agents.waypoint_following_agent._REACHED_END_OF_TRAJECTORY_THRESHOLD + waymax.agents.waypoint_following_agent._DISTANCE_TO_REF_THRESHOLD + waymax.agents.waypoint_following_agent._STATIC_SPEED_THRESHOLD + + +.. py:data:: _DEFAULT_LEAD_DISTANCE + + + +.. py:data:: _DEFAULT_LEAD_VELOCITY + + + +.. py:data:: _MINIMUM_LEAD_DISTANCE + :value: 0.1 + + + +.. py:data:: _DEFAULT_TIME_DELTA + :value: 0.1 + + + +.. py:data:: _REACHED_END_OF_TRAJECTORY_THRESHOLD + :value: 0.05 + + + +.. py:data:: _DISTANCE_TO_REF_THRESHOLD + :value: 5.0 + + + +.. py:data:: _STATIC_SPEED_THRESHOLD + :value: 1.0 + + + +.. py:class:: WaypointFollowingPolicy(is_controlled_func: Optional[Callable[[waymax.datatypes.SimulatorState], jax.Array]] = None) + + + Bases: :py:obj:`waymax.agents.sim_agent.SimAgentActor` + + A base class for all waypoint-following sim agents. + + The WaypointFollowingPolicy will force sim agents to travel along a + pre-defined path (the agent's future in the log trajectory). The behavior + of the vehicle is determined by setting its speed via the update_speed() + method, which will update the velocity of the vehicle. + + .. py:method:: update_trajectory(state: waymax.datatypes.SimulatorState) -> waymax.datatypes.TrajectoryUpdate + + Returns a trajectory update of shape (..., num_objects, 1). + + + .. py:method:: _get_next_trajectory_by_projection(log_traj: waymax.datatypes.Trajectory, cur_sim_traj: waymax.datatypes.Trajectory, new_speed: jax.Array, new_speed_valid: jax.Array, dt: float = _DEFAULT_TIME_DELTA) -> waymax.datatypes.Trajectory + + Computes the next trajectory. + + :param log_traj: Logged trajectory for the simulation of shape (..., num_objects, + num_timesteps). + :param cur_sim_traj: Current simulated trajectory for the simulation of shape + (..., num_objects, num_timesteps=1). + :param new_speed: Updated speed for the agents after solving for velocity of + shape (..., num_objects). + :param new_speed_valid: Updated validity for the speed updates of the agents + after (..., num_objects). + :param dt: Delta between timesteps of the simulator state. + + :returns: + + The next Trajectory projected onto log_traj of shape + (..., num_objects, num_timesteps=1). + + + .. py:method:: update_speed(state: waymax.datatypes.SimulatorState, dt: float = _DEFAULT_TIME_DELTA) -> tuple[jax.Array, jax.Array] + :abstractmethod: + + Updates the speed for each agent in the current simulation step. + + :param state: The simulator state of shape (...). + :param dt: Delta between timesteps of the simulator state. + + :returns: A (..., num_objects) float array of speeds. + valids: A (..., num_objects) bool array of valids. + :rtype: speeds + + + +.. py:class:: IDMRoutePolicy(is_controlled_func: Optional[Callable[[waymax.datatypes.SimulatorState], jax.Array]] = None, desired_vel: float = 30.0, min_spacing: float = 2.0, safe_time_headway: float = 2.0, max_accel: float = 2.0, max_deccel: float = 4.0, delta: float = 4.0, max_lookahead: int = 10, additional_lookahead_points: int = 10, additional_lookahead_distance: float = 10.0) + + + Bases: :py:obj:`WaypointFollowingPolicy` + + A policy implementing the intelligent driver model (IDM). + + This policy uses IDM to compute the acceleration and velocities for the + agent while it follows its own logged future. + + .. py:method:: update_speed(state: waymax.datatypes.SimulatorState, dt: float = _DEFAULT_TIME_DELTA) -> tuple[jax.Array, jax.Array] + + Returns the new speed for each agent in the current simulation step. + + :param state: The simulator state of shape (...). + :param dt: Delta between timesteps of the simulator state. + + :returns: A (..., num_objects) float array of speeds. + valids: A (..., num_objects) bool array of valids. + :rtype: speeds + + + .. py:method:: _get_accel(log_waypoints: waymax.datatypes.Trajectory, cur_position: jax.Array, cur_speed: jax.Array, obj_curr_traj: waymax.datatypes.Trajectory) -> jax.Array + + Computes vehicle accelerations according to IDM for a single vehicle. + + Note log_waypoints and obj_curr_traj contain the same set of objects, thus + need to remove collision against oneself when computing pairwise collision. + + :param log_waypoints: A trajectory of the agents' future of shape (..., + num_objects, num_timesteps). + :param cur_position: Current positions for the agents of shape (..., num_objects, + 3). + :param cur_speed: Current speeds for the agents of shape (..., num_objects). + :param obj_curr_traj: Trajectory containing the state for all current objects of + shape (..., num_objects, num_timesteps=1). + + :returns: + + A vector of all vehicles' accelerations after solving for them of shape + (..., num_objects). + + + .. py:method:: _compute_lead_velocity(future_speeds: jax.Array, collisions_per_agent: jax.Array, future_speeds_valid: Optional[jax.Array] = None) -> jax.Array + + Computes the velocity of the object at the closest collision. + + :param future_speeds: Future speeds per agent of shape (..., num_objects, + num_timesteps). + :param collisions_per_agent: Future collision indications of shape (..., + num_objects, num_timesteps). + :param future_speeds_valid: Boolean mask for future speeds of shape (..., + num_objects, num_timesteps). + + :returns: + + An array containing the velocity of the colliding object at the + closest collision of shape (...). + + + .. py:method:: _compute_lead_distance(agent_future: jax.Array, collision_indicator: jax.Array, agent_future_valid: Optional[jax.Array] = None, current_position: Optional[jax.Array] = None, use_arclength=False) -> jax.Array + + Computes the distance between the agent and the nearest collision. + + :param agent_future: Agent's future positions {x, y, z} of shape (..., + num_timesteps, 3). + :param collision_indicator: Collision indications of shape (..., num_timesteps). + :param agent_future_valid: Boolean mask for agent's future positions of shape + (..., num_timesteps). + :param current_position: Array of the vehicle's current positions {x, y, z} of + shape (..., 1, 3). If None, will use the first element of agent_future + as the current position. + :param use_arclength: Whether to use arclength for computing collisions. + Arclength is more accurate but is not robust to futures with mixed + valids. + + :returns: An array of distances to the agent's closest collision of shape (...). + + + +.. py:function:: _find_reference_traj_from_log_traj(xyz: jax.Array, traj: waymax.datatypes.Trajectory, num_pts: int) -> waymax.datatypes.Trajectory + + Finds sub-trajectory for given position xyz. + + :param xyz: Position of the agent of shape (..., 3). + :param traj: Full trajectory for a given agent (..., T). + :param num_pts: The number of waypoints to retrieve for future Trajectory. + + :returns: Trajectory with (..., T_out=num_pts). + + +.. py:function:: _project_to_a_trajectory(xy: jax.Array, traj: waymax.datatypes.Trajectory, extrapolate_traj: bool = False) -> tuple[jax.Array, jax.Array, jax.Array] + + Projects points on to a Trajectory. + + :param xy: Current xy position of the agent of shape (..., 1, 2). + :param traj: Full trajectory of the agent of shape (..., num_timesteps). + :param extrapolate_traj: Whether to extrapolate a projection beyond the final + waypoint in direction of the final waypoint. + + :returns: Updated xy with shape (..., 1, 2). + Updated yaw with shape (..., 1). + Boolean indicating whether the agent reached the last waypoint + with shape (..., 1). + + +.. py:function:: _compute_arclengths(waypoints: jax.Array, valid: Optional[jax.Array] = None) -> jax.Array + + Helper function for computing the arclengths of a series of waypoints. + + +.. py:function:: _add_headway_waypoints(traj: waymax.datatypes.Trajectory, distance: float = 2.0, num_points: int = 10) -> waymax.datatypes.Trajectory + + Adds additional waypoints after a trajectory for collision detection. + + This function adds `num_points` additional waypoints, spaced evenly over + `distance`, to the end of a series of waypoints following the same heading as + the final timestep. + The use case is to add additional waypoints for collision detection, since + we only check for collisions along future waypoints. This is especially + useful for vehicles that move slowly or are parked in the logs and do not + have many future logged waypoints. + + :param traj: A trajectory of waypoints of shape (...., num_timesteps). + :param distance: Distance over which to add points. + :param num_points: Number of points to add. + + :returns: + + A trajectory of shape (..., num_timesteps+N) containing augmented waypoints, + where N is num_points. + + diff --git a/_sources/autoapi/waymax/config/index.rst.txt b/_sources/autoapi/waymax/config/index.rst.txt new file mode 100644 index 0000000..d10c84c --- /dev/null +++ b/_sources/autoapi/waymax/config/index.rst.txt @@ -0,0 +1,647 @@ +:py:mod:`waymax.config` +======================= + +.. py:module:: waymax.config + +.. autoapi-nested-parse:: + + Configs for Waymax Environments. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.config.CoordinateFrame + waymax.config.DataFormat + waymax.config.DatasetConfig + waymax.config.ObservationConfig + waymax.config.MetricsConfig + waymax.config.LinearCombinationRewardConfig + waymax.config.ObjectType + waymax.config.SimAgentType + waymax.config.SimAgentConfig + waymax.config.EnvironmentConfig + waymax.config.WaymaxConfig + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.config.WOD_1_0_0_TRAINING + waymax.config.WOD_1_0_0_VALIDATION + waymax.config.WOD_1_1_0_TRAINING + waymax.config.WOD_1_1_0_VALIDATION + waymax.config.WOD_1_2_0_TEST + + +.. py:class:: CoordinateFrame(*args, **kwds) + + + Bases: :py:obj:`enum.Enum` + + Coordinate system used for data. + + .. py:attribute:: SDC + :value: 'SDC' + + + + .. py:attribute:: OBJECT + :value: 'OBJECT' + + + + .. py:attribute:: GLOBAL + :value: 'GLOBAL' + + + + +.. py:class:: DataFormat(*args, **kwds) + + + Bases: :py:obj:`enum.Enum` + + Data format for serialized records. + + .. py:attribute:: TFRECORD + :value: 'TFRECORD' + + + + +.. py:class:: DatasetConfig + + + Configs for dataset/dataloader. + + .. attribute:: path + + Path/pattern for data, supporting sharded files with @. + + .. attribute:: data_format + + The format of data in `path`, string from + womd_dataloader.DataFormat. + + .. attribute:: repeat + + Number of times to repeat the dataset. Set None to repeat + infinitely. + + .. attribute:: batch_dims + + Batch dimensions in tuple format. Can be empty as (). + + .. attribute:: shuffle_seed + + Random seed. Set None to disable shuffle. + + .. attribute:: shuffle_buffer_size + + Buffer size for shuffling. + + .. attribute:: num_shards + + Number of shards for parallel loading, no effect on data + returned. + + .. attribute:: deterministic + + Whether to use deterministic parallel processing. + + .. attribute:: include_sdc_paths + + Whether to include all valid future paths for SDC + according to roadgraph connectivity from its starting position. + + .. attribute:: aggregate_timesteps + + Whether to aggregate keys from tf examples, need to set + to True for parsing SimulatorState afterwards. + + .. attribute:: max_num_rg_points + + Max number of roadgraph points in data. + + .. attribute:: max_num_objects + + Max number of total objects in the scene. Set None to + include all objects from the data. + + .. attribute:: num_paths + + Number of roadgraph traversal paths. Must be specified if + include_sdc_paths is True. + + .. attribute:: num_points_per_path + + Number of points per roadgraph traversal path. Must be + specified if include_sdc_paths is True. + + .. attribute:: drop_remainder + + Argument for tf.data.Dataset.batch. Set True to drop + remainder if the last batch does not contain enough examples. Note + training should not be affected since it is looping over all data for + multiple epochs. For evaluation, it should be set to False to include all + examples. + + .. attribute:: tf_data_service_address + + A string or a tuple indicating how to connect to + the tf.data service. Used as the `service` argument for + tf.data.experimental.service.distribute in dataloader. + + .. attribute:: distributed + + If True, the generated data batch will have an extra leading + axis corresponding to the number of available devices. This is needed when + the returned data is consumed by a pmap function. + + .. attribute:: batch_by_scenario + + If True, one example in a returned batch is the entire + scenario containing all objects; if False, the dataset will treat + individual object trajectories as a training example rather than an entire + scenario. + + .. py:attribute:: path + :type: str + + + + .. py:attribute:: data_format + :type: DataFormat + + + + .. py:attribute:: repeat + :type: Optional[int] + + + + .. py:attribute:: batch_dims + :type: tuple[int, Ellipsis] + :value: () + + + + .. py:attribute:: shuffle_seed + :type: Optional[int] + + + + .. py:attribute:: shuffle_buffer_size + :type: int + :value: 1000 + + + + .. py:attribute:: num_shards + :type: int + :value: 4 + + + + .. py:attribute:: deterministic + :type: bool + :value: True + + + + .. py:attribute:: include_sdc_paths + :type: bool + :value: False + + + + .. py:attribute:: aggregate_timesteps + :type: bool + :value: True + + + + .. py:attribute:: max_num_rg_points + :type: int + :value: 30000 + + + + .. py:attribute:: max_num_objects + :type: Optional[int] + + + + .. py:attribute:: num_paths + :type: Optional[int] + + + + .. py:attribute:: num_points_per_path + :type: Optional[int] + + + + .. py:attribute:: drop_remainder + :type: bool + :value: True + + + + .. py:attribute:: tf_data_service_address + :type: Optional[str] + + + + .. py:attribute:: distributed + :type: bool + :value: False + + + + .. py:attribute:: batch_by_scenario + :type: bool + :value: True + + + + +.. py:class:: ObservationConfig + + + Config for the built-in Waymax Observation function. + + .. attribute:: num_steps + + Number of trajectory history steps for observation. + + .. attribute:: roadgraph_top_k + + Number of closest roadgraph elements to get for + observation. + + .. attribute:: coordinate_frame + + What coordinate frame the observation is using. + + .. py:attribute:: num_steps + :type: int + :value: 1 + + + + .. py:attribute:: roadgraph_top_k + :type: int + :value: 2000 + + + + .. py:attribute:: coordinate_frame + :type: CoordinateFrame + + + + +.. py:class:: MetricsConfig + + + Config for the built-in Waymax Metrics functions. + + .. attribute:: run_log_divergence + + Whether log_divergence metric will be computed in the + `step` function. + + .. attribute:: run_overlap + + Whether overlap metric will be computed in the `step` function. + + .. attribute:: run_offroad + + Whether offroad metric will be computed in the `step` function. + + .. attribute:: run_sdc_wrongway + + Whether wrong-way metric will be computed for SDC in the + `step` function. Note this is only for single-agent env currently since + there is no route for sim-agents in data. + + .. attribute:: run_sdc_progression + + Whether progression metric will be computed for SDC in + the `step` function. Note this is only for single-agent env currently + since there is no route for sim-agents in data. + + .. attribute:: run_sdc_off_route + + Whether the off-route metric will be computed for SDC in + the `step` function. Note this is only for single-agent env currently + since there is no route for sim-agents in data. + + .. attribute:: run_sdc_kinematic_infeasibility + + Whether the kinematics infeasibility metric + will be computed for SDC in the `step` function. Note this is only for + single-agent env currently since other agents may have different dynamics + and cannot be evaluated using the current kinematics infeasibility metrics + + .. py:attribute:: run_log_divergence + :type: bool + :value: True + + + + .. py:attribute:: run_overlap + :type: bool + :value: True + + + + .. py:attribute:: run_offroad + :type: bool + :value: True + + + + .. py:attribute:: run_sdc_wrongway + :type: bool + :value: False + + + + .. py:attribute:: run_sdc_progression + :type: bool + :value: False + + + + .. py:attribute:: run_sdc_off_route + :type: bool + :value: False + + + + .. py:attribute:: run_sdc_kinematic_infeasibility + :type: bool + :value: False + + + + +.. py:class:: LinearCombinationRewardConfig + + + Config listing all metrics and their corresponding weights. + + .. attribute:: rewards + + Dictionary of metric names to floats indicating the weight of each + metric to create a reward of a linear combination. Valid metric names are + taken from the MetricConfig and removing 'run_'. For example, to create a + reward using the progression metric, the name would have to be + 'sdc_progression', since 'run_sdc_progression' is used in the config + above. + + .. py:attribute:: rewards + :type: dict[str, float] + + + + +.. py:class:: ObjectType(*args, **kwds) + + + Bases: :py:obj:`enum.Enum` + + Types of objects that can be controlled by Waymax. + + .. py:attribute:: SDC + :value: 'SDC' + + + + .. py:attribute:: MODELED + :value: 'MODELED' + + + + .. py:attribute:: VALID + :value: 'VALID' + + + + .. py:attribute:: NON_SDC + :value: 'NON_SDC' + + + + +.. py:class:: SimAgentType(*args, **kwds) + + + Bases: :py:obj:`enum.Enum` + + Types of sim agents that can be used by Waymax. + + .. py:attribute:: IDM + :value: 'IDM' + + + + +.. py:class:: SimAgentConfig + + + Config for sim agents. + + .. attribute:: agent_type + + What sim agent algorithm to use. + + .. attribute:: controlled_objects + + Which objects the sim agent should control. + + .. py:attribute:: agent_type + :type: SimAgentType + + + + .. py:attribute:: controlled_objects + :type: ObjectType + + + + +.. py:class:: EnvironmentConfig + + + Configs for Waymax Environments. + + .. attribute:: max_num_objects + + Max number of objects in the scene, should be consistent + with DatasetConfig.max_num_objects. + + .. attribute:: init_steps + + Initial/warmup steps taken for the environment. For WOMD, this + includes 10 warmup steps and 1 for the current step + + .. attribute:: controlled_object + + What type of objects are controlled. + + .. attribute:: compute_reward + + Whether to compute the reward. Computing the reward is + expensive and unnecessary for cases like rollout data generation. + + .. attribute:: allow_new_objects_after_warmup + + Whether to allow new objects to appear after + environment warmup. If this is set to `False`, all non-controlled objects + that are invalid in the log at timestep `t=warmup` will be forever invalid + in the simulation. This means that objects that appear out of occlusion + after `t=warmup` will still be invalid as if they never appeared. If this + is set to `True`, agents will be able to appear in the future simulated + state if they appeared in the future logged state. Note when set to True, + the environment could expect users to control objects even before their + first appearance, users can either ignore or simply provide any invalid + action for those objects. + + .. attribute:: observation + + Optional config for the provided observation function found at + waymax/data/observation.py. + + .. attribute:: metrics + + Specifies which built-in Waymax metrics to run when calling + `env.metrics(...)`. + + .. attribute:: rewards + + Specifies the metrics and weights to create a reward as a linear + combination of metrics. + + .. attribute:: sim_agents + + Configurations for sim agents used to control non + user-controlled objects. Sim agents are applied in the order of that they + are specified (if multiple sim agents control the same object, only the + last sim agent will be applied for that object). + + .. py:attribute:: max_num_objects + :type: int + :value: 128 + + + + .. py:attribute:: init_steps + :type: int + :value: 11 + + + + .. py:attribute:: controlled_object + :type: ObjectType + + + + .. py:attribute:: compute_reward + :type: bool + :value: True + + + + .. py:attribute:: allow_new_objects_after_warmup + :type: bool + :value: True + + + + .. py:attribute:: observation + :type: Optional[ObservationConfig] + + + + .. py:attribute:: metrics + :type: MetricsConfig + + + + .. py:attribute:: rewards + :type: LinearCombinationRewardConfig + + + + .. py:attribute:: sim_agents + :type: Optional[Sequence[SimAgentConfig]] + + + + .. py:method:: __post_init__() + + + +.. py:class:: WaymaxConfig + + + Top level config for Waymax. + + .. attribute:: data_config + + Data related configurations, including how to parse and load + the data. + + .. attribute:: env_config + + Configurations about the environment itself, observation, and + metrics. + + .. py:attribute:: data_config + :type: DatasetConfig + + + + .. py:attribute:: env_config + :type: EnvironmentConfig + + + + .. py:method:: __post_init__() + + + +.. py:data:: WOD_1_0_0_TRAINING + + + +.. py:data:: WOD_1_0_0_VALIDATION + + + +.. py:data:: WOD_1_1_0_TRAINING + + + +.. py:data:: WOD_1_1_0_VALIDATION + + + +.. py:data:: WOD_1_2_0_TEST + + + diff --git a/_sources/autoapi/waymax/dataloader/dataloader_utils/index.rst.txt b/_sources/autoapi/waymax/dataloader/dataloader_utils/index.rst.txt new file mode 100644 index 0000000..1624caf --- /dev/null +++ b/_sources/autoapi/waymax/dataloader/dataloader_utils/index.rst.txt @@ -0,0 +1,116 @@ +:py:mod:`waymax.dataloader.dataloader_utils` +============================================ + +.. py:module:: waymax.dataloader.dataloader_utils + +.. autoapi-nested-parse:: + + Util functions for general dataloading. + + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.dataloader.dataloader_utils.generate_sharded_filenames + waymax.dataloader.dataloader_utils.tf_examples_dataset + waymax.dataloader.dataloader_utils.get_data_generator + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.dataloader.dataloader_utils.T + waymax.dataloader.dataloader_utils.AUTOTUNE + + +.. py:data:: T + + + +.. py:data:: AUTOTUNE + + + +.. py:function:: generate_sharded_filenames(path: str) -> Sequence[str] + + Returns the filenames of individual sharded files. + + A sharded file is a set of files of the format filename-XXXXX-of-YYYYY, + where XXXXX is a placeholder for the index of the shard, and YYYYY is the + total number of shards. These files are collectively referred to by a + sharded path filename@YYYYY. + + For example, the sharded path `myfile@100` refers to the set of files + - myfile-00000-of-00100 + - myfile-00001-of-00100 + - ... + - myfile-00098-of-00100 + - myfile-00099-of-00100 + + :param path: A path to a sharded file, with format `filename@shards`, where shards + is an integer denoting the number of total shards. + + :returns: An iterator through the complete set of filenames that the path refers to, + with each filename having the format `filename-XXXXX-of-YYYYY` + + +.. py:function:: tf_examples_dataset(path: str, data_format: waymax.config.DataFormat, preprocess_fn: Callable[[bytes], dict[str, tensorflow.Tensor]], shuffle_seed: Optional[int] = None, shuffle_buffer_size: int = 100, repeat: Optional[int] = None, batch_dims: Sequence[int] = (), num_shards: int = 1, deterministic: bool = True, drop_remainder: bool = True, tf_data_service_address: Optional[str] = None, batch_by_scenario: bool = True) -> tensorflow.data.Dataset + + Returns a dataset of Open Motion dataset TFExamples. + + Each TFExample contains data for the trajectory of all objects, the roadgraph, + and traffic light states. See https://waymo.com/open/data/motion/tfexample + for the data format definition. + + :param path: The path to the dataset. + :param data_format: Data format of the dataset. + :param preprocess_fn: Function for parsing and preprocessing individual examples. + :param shuffle_seed: Seed for shuffling. If left default (None), will not shuffle + the dataset. + :param shuffle_buffer_size: The size of the shuffle buffer. + :param repeat: Number of times to repeat the dataset. Default (None) will repeat + infinitely. + :param batch_dims: List of size of batch dimensions. Multiple batch dimension can + be used to provide inputs for multiple devices. E.g. + [jax.local_device_count(), batch_size_per_device]. + :param num_shards: Number of shards for parallel loading, no effect on data + returned. + :param deterministic: Whether to use deterministic parallel processing. + :param drop_remainder: Arg for tf.data.Dataset.batch. Set True to drop remainder if + the last batch does not contains enough examples. + :param tf_data_service_address: Set to use tf data service. + :param batch_by_scenario: If True, one example in a returned batch is the entire + scenario containing all objects; if False, the dataset will treat + individual object trajectories as a training example rather than an entire + scenario. + + :returns: A tf.data.Dataset of Open Motion Dataset tf.Example elements. + + +.. py:function:: get_data_generator(config: waymax.config.DatasetConfig, preprocess_fn: Optional[Callable[[bytes], dict[str, tensorflow.Tensor | dict[str, tensorflow.Tensor]]]], postprocess_fn: Optional[Callable[[dict[str, jax.Array]], T]] = None) -> Iterator[T] + + Iterator that yields the desired object returned by postprocess_fn. + + It parses data using preprocess_fn and returns a generator of data whose data + structure is defined by postprocess_fn function. + + :param config: config for dataset and preprocessing. + :param preprocess_fn: preprocess the serialized data into a dictionary of str to tf + Tensor. + :param postprocess_fn: a function that converts dict of jnp array to desired data + class. Note for distributed training, this function will be pmap-ed and + executed in the main process. + + :Yields: Iterator of desired data class. + + diff --git a/_sources/autoapi/waymax/dataloader/index.rst.txt b/_sources/autoapi/waymax/dataloader/index.rst.txt new file mode 100644 index 0000000..c86769a --- /dev/null +++ b/_sources/autoapi/waymax/dataloader/index.rst.txt @@ -0,0 +1,237 @@ +:py:mod:`waymax.dataloader` +=========================== + +.. py:module:: waymax.dataloader + +.. autoapi-nested-parse:: + + Libraries for loading data in Waymax. + + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + dataloader_utils/index.rst + womd_dataloader/index.rst + womd_factories/index.rst + womd_factories_internal/index.rst + womd_utils/index.rst + + +Package Contents +---------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.dataloader.get_data_generator + waymax.dataloader.tf_examples_dataset + waymax.dataloader.preprocess_serialized_womd_data + waymax.dataloader.preprocess_womd_example + waymax.dataloader.simulator_state_generator + waymax.dataloader.object_metadata_from_womd_dict + waymax.dataloader.paths_from_womd_dict + waymax.dataloader.roadgraph_from_womd_dict + waymax.dataloader.simulator_state_from_womd_dict + waymax.dataloader.traffic_lights_from_womd_dict + waymax.dataloader.trajectory_from_womd_dict + waymax.dataloader.simulator_state_to_womd_dict + + + +.. py:function:: get_data_generator(config: waymax.config.DatasetConfig, preprocess_fn: Optional[Callable[[bytes], dict[str, tensorflow.Tensor | dict[str, tensorflow.Tensor]]]], postprocess_fn: Optional[Callable[[dict[str, jax.Array]], T]] = None) -> Iterator[T] + + Iterator that yields the desired object returned by postprocess_fn. + + It parses data using preprocess_fn and returns a generator of data whose data + structure is defined by postprocess_fn function. + + :param config: config for dataset and preprocessing. + :param preprocess_fn: preprocess the serialized data into a dictionary of str to tf + Tensor. + :param postprocess_fn: a function that converts dict of jnp array to desired data + class. Note for distributed training, this function will be pmap-ed and + executed in the main process. + + :Yields: Iterator of desired data class. + + +.. py:function:: tf_examples_dataset(path: str, data_format: waymax.config.DataFormat, preprocess_fn: Callable[[bytes], dict[str, tensorflow.Tensor]], shuffle_seed: Optional[int] = None, shuffle_buffer_size: int = 100, repeat: Optional[int] = None, batch_dims: Sequence[int] = (), num_shards: int = 1, deterministic: bool = True, drop_remainder: bool = True, tf_data_service_address: Optional[str] = None, batch_by_scenario: bool = True) -> tensorflow.data.Dataset + + Returns a dataset of Open Motion dataset TFExamples. + + Each TFExample contains data for the trajectory of all objects, the roadgraph, + and traffic light states. See https://waymo.com/open/data/motion/tfexample + for the data format definition. + + :param path: The path to the dataset. + :param data_format: Data format of the dataset. + :param preprocess_fn: Function for parsing and preprocessing individual examples. + :param shuffle_seed: Seed for shuffling. If left default (None), will not shuffle + the dataset. + :param shuffle_buffer_size: The size of the shuffle buffer. + :param repeat: Number of times to repeat the dataset. Default (None) will repeat + infinitely. + :param batch_dims: List of size of batch dimensions. Multiple batch dimension can + be used to provide inputs for multiple devices. E.g. + [jax.local_device_count(), batch_size_per_device]. + :param num_shards: Number of shards for parallel loading, no effect on data + returned. + :param deterministic: Whether to use deterministic parallel processing. + :param drop_remainder: Arg for tf.data.Dataset.batch. Set True to drop remainder if + the last batch does not contains enough examples. + :param tf_data_service_address: Set to use tf data service. + :param batch_by_scenario: If True, one example in a returned batch is the entire + scenario containing all objects; if False, the dataset will treat + individual object trajectories as a training example rather than an entire + scenario. + + :returns: A tf.data.Dataset of Open Motion Dataset tf.Example elements. + + +.. py:function:: preprocess_serialized_womd_data(serialized: bytes, config: waymax.config.DatasetConfig) -> dict[str, tensorflow.Tensor] + + Parses serialized tf example into tf Tensor dict. + + +.. py:function:: preprocess_womd_example(example: dict[str, tensorflow.Tensor], aggregate_timesteps: bool, max_num_objects: Optional[int] = None) -> dict[str, tensorflow.Tensor] + + Preprocesses dict of tf tensors, keyed by str. + + +.. py:function:: simulator_state_generator(config: waymax.config.DatasetConfig) -> Iterator[waymax.datatypes.simulator_state.SimulatorState] + + Wrapper for SimulatorState iterator. + + This is the high level api for Waymax data loading that takes Waymax data + config and outputs generator of SimulatorState. + + :param config: dataset config. + + :returns: A SimulatorState iterator. + + +.. py:function:: object_metadata_from_womd_dict(example: dict[str, jax.Array]) -> waymax.datatypes.object_state.ObjectMetadata + + Constructs object metadata from an Open Motion TFExample dictionary. + + :param example: Mapping from feature name to array for data read from WOMD + `tf.Example`'s in the format of specified in the WOMD website + (https://waymo.com/open/data/motion/tfexample). + + :returns: + + Metadata for the objects in the scene read from the WOMD data example of + shape (..., num_objects). + + +.. py:function:: paths_from_womd_dict(example: dict[str, jax.Array], prefix_key: str = 'path_samples') -> waymax.datatypes.route.Paths + + Constructs paths from an Open Motion TFExample dictionary. + + :param example: Mapping from feature name to array for data read from WOMD, with + extra key/values pairs for route related data. + :param prefix_key: Keys for indexing route data. + + :returns: Paths with shape (..., num_paths, num_points_per_path). + + +.. py:function:: roadgraph_from_womd_dict(example: dict[str, jax.Array], prefix_key: str = 'roadgraph_samples') -> waymax.datatypes.roadgraph.RoadgraphPoints + + Constructs a point map from an Open Motion TFExample dictionary. + + :param example: Dictionary which contains data from the WOMD tf.Examples with + optional extra batch dimensions. These are defined at + https://waymo.com/open/data/motion/tfexample. + :param prefix_key: Prefix key for the roadgraph type in the example. + + :returns: RoadgraphPoints from the `example` scenario with shape (..., num_points). + + +.. py:function:: simulator_state_from_womd_dict(example: dict[str, jax.Array], include_sdc_paths: bool = False, time_key: str = 'all') -> waymax.datatypes.simulator_state.SimulatorState + + Constructs a simulator state from an aggregated WOMD data dict. + + :param example: Mapping from feature name to array for data read from WOMD + `tf.Example`'s in the format of specified in the WOMD website + (https://waymo.com/open/data/motion/tfexample). The data, however, can be + augmented with different time keys. We do this in our data loader, by + aggregating all timesteps into an `all` key prefix. + :param include_sdc_paths: Whether to include sdc based route paths when + constructing the simulator state. This is useful for representing the + route of the agent. A similar parameter must be used in the data loader to + get consistent behavior. + :param time_key: Time step information to gather from `example` (`past`, `current`, + `future`, `all`). + + :returns: + + A simulator state constructed at the initial timestep to spec given the + parameters. + + :raises ValueError: If a `time_key` parameter is passed in that is not one of + (`past`, `current`, `future`, `all`). + + +.. py:function:: traffic_lights_from_womd_dict(example: dict[str, jax.Array], time_key: str = 'all') -> waymax.datatypes.traffic_lights.TrafficLights + + Constructs a traffic light state from WOMD TFExample dictionary. + + :param example: Mapping from feature name to array for data read from WOMD + `tf.Example`'s in the format of specified in the WOMD website + (https://waymo.com/open/data/motion/tfexample). The data, however, can be + augmented with different time keys. We do this in our data loader, by + aggregating all timesteps into an `all` key. + :param time_key: Time step information to gather from traffic light (`past`, + `current`, `future`, `all`). + + :returns: Traffic lights from the `example` scenario with shape (..., + num_traffic_lights, num_timesteps). + + :raises ValueError: If `time_key` is not part of the accepted values. + + +.. py:function:: trajectory_from_womd_dict(example: dict[str, jax.Array], time_key: str = 'all') -> waymax.datatypes.object_state.Trajectory + + Constructs a trajectory from an Open Motion TFExample dictionary. + + :param example: Mapping from feature name to array for data read from WOMD + `tf.Example`'s in the format of specified in the WOMD website + (https://waymo.com/open/data/motion/tfexample). + :param time_key: Key which represents what time dimension to access from the state + fields in the example: (i.e. past, current, future, all). + + :returns: Trajectory of all objects from the `example` scenario with shape (..., + num_objects, num_timesteps). + + +.. py:function:: simulator_state_to_womd_dict(state: waymax.datatypes.simulator_state.SimulatorState, feature_description: dict[str, tensorflow.io.FixedLenFeature], validate: bool = False) -> dict[str, jax.Array] + + Converts a simulator state into the WOMD tensor format. + + See https://waymo.com/open/data/motion/tfexample for the tf.Example format + which will be returned from this function. + Note: This function is compatible with `jax2tf`. + + :param state: State of the simulator from the environment. Should contain at least + num_history + 1 elements in the time dimension for all temporal components + of the simulated trajectory. + :param feature_description: Feature description expected out of the dictionary. + This is used to understand the shape of the fields expected such as number + of agents and amount of history. + :param validate: Validate whether the simulation has progressed far enough to + ensure that an adequate amount of history is present. + + :returns: A dictionary matching fields as if they were read from the WOMD dataset. + + :raises ValueError: If `validate` is set to `True` and the number of history stored + in the observations is not `num_history` + 1. + + diff --git a/_sources/autoapi/waymax/dataloader/womd_dataloader/index.rst.txt b/_sources/autoapi/waymax/dataloader/womd_dataloader/index.rst.txt new file mode 100644 index 0000000..cad7c16 --- /dev/null +++ b/_sources/autoapi/waymax/dataloader/womd_dataloader/index.rst.txt @@ -0,0 +1,69 @@ +:py:mod:`waymax.dataloader.womd_dataloader` +=========================================== + +.. py:module:: waymax.dataloader.womd_dataloader + +.. autoapi-nested-parse:: + + Waymax data loading functions. + + WOMD represents the data we typically use for simulation in our environment. See + https://waymo.com/open/data/motion/tfexample for definitions on many of the data + fields mentioned in this file. + + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.dataloader.womd_dataloader.preprocess_serialized_womd_data + waymax.dataloader.womd_dataloader.preprocess_womd_example + waymax.dataloader.womd_dataloader.simulator_state_generator + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.dataloader.womd_dataloader.T + waymax.dataloader.womd_dataloader.AUTOTUNE + + +.. py:data:: T + + + +.. py:data:: AUTOTUNE + + + +.. py:function:: preprocess_serialized_womd_data(serialized: bytes, config: waymax.config.DatasetConfig) -> dict[str, tensorflow.Tensor] + + Parses serialized tf example into tf Tensor dict. + + +.. py:function:: preprocess_womd_example(example: dict[str, tensorflow.Tensor], aggregate_timesteps: bool, max_num_objects: Optional[int] = None) -> dict[str, tensorflow.Tensor] + + Preprocesses dict of tf tensors, keyed by str. + + +.. py:function:: simulator_state_generator(config: waymax.config.DatasetConfig) -> Iterator[waymax.datatypes.simulator_state.SimulatorState] + + Wrapper for SimulatorState iterator. + + This is the high level api for Waymax data loading that takes Waymax data + config and outputs generator of SimulatorState. + + :param config: dataset config. + + :returns: A SimulatorState iterator. + + diff --git a/_sources/autoapi/waymax/dataloader/womd_factories/index.rst.txt b/_sources/autoapi/waymax/dataloader/womd_factories/index.rst.txt new file mode 100644 index 0000000..e3ac2f8 --- /dev/null +++ b/_sources/autoapi/waymax/dataloader/womd_factories/index.rst.txt @@ -0,0 +1,123 @@ +:py:mod:`waymax.dataloader.womd_factories` +========================================== + +.. py:module:: waymax.dataloader.womd_factories + +.. autoapi-nested-parse:: + + Factory functions for converting tensors to Waymax internal data structures. + + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.dataloader.womd_factories.object_metadata_from_womd_dict + waymax.dataloader.womd_factories.roadgraph_from_womd_dict + waymax.dataloader.womd_factories.paths_from_womd_dict + waymax.dataloader.womd_factories.simulator_state_from_womd_dict + waymax.dataloader.womd_factories.trajectory_from_womd_dict + waymax.dataloader.womd_factories.traffic_lights_from_womd_dict + + + +.. py:function:: object_metadata_from_womd_dict(example: dict[str, jax.Array]) -> waymax.datatypes.object_state.ObjectMetadata + + Constructs object metadata from an Open Motion TFExample dictionary. + + :param example: Mapping from feature name to array for data read from WOMD + `tf.Example`'s in the format of specified in the WOMD website + (https://waymo.com/open/data/motion/tfexample). + + :returns: + + Metadata for the objects in the scene read from the WOMD data example of + shape (..., num_objects). + + +.. py:function:: roadgraph_from_womd_dict(example: dict[str, jax.Array], prefix_key: str = 'roadgraph_samples') -> waymax.datatypes.roadgraph.RoadgraphPoints + + Constructs a point map from an Open Motion TFExample dictionary. + + :param example: Dictionary which contains data from the WOMD tf.Examples with + optional extra batch dimensions. These are defined at + https://waymo.com/open/data/motion/tfexample. + :param prefix_key: Prefix key for the roadgraph type in the example. + + :returns: RoadgraphPoints from the `example` scenario with shape (..., num_points). + + +.. py:function:: paths_from_womd_dict(example: dict[str, jax.Array], prefix_key: str = 'path_samples') -> waymax.datatypes.route.Paths + + Constructs paths from an Open Motion TFExample dictionary. + + :param example: Mapping from feature name to array for data read from WOMD, with + extra key/values pairs for route related data. + :param prefix_key: Keys for indexing route data. + + :returns: Paths with shape (..., num_paths, num_points_per_path). + + +.. py:function:: simulator_state_from_womd_dict(example: dict[str, jax.Array], include_sdc_paths: bool = False, time_key: str = 'all') -> waymax.datatypes.simulator_state.SimulatorState + + Constructs a simulator state from an aggregated WOMD data dict. + + :param example: Mapping from feature name to array for data read from WOMD + `tf.Example`'s in the format of specified in the WOMD website + (https://waymo.com/open/data/motion/tfexample). The data, however, can be + augmented with different time keys. We do this in our data loader, by + aggregating all timesteps into an `all` key prefix. + :param include_sdc_paths: Whether to include sdc based route paths when + constructing the simulator state. This is useful for representing the + route of the agent. A similar parameter must be used in the data loader to + get consistent behavior. + :param time_key: Time step information to gather from `example` (`past`, `current`, + `future`, `all`). + + :returns: + + A simulator state constructed at the initial timestep to spec given the + parameters. + + :raises ValueError: If a `time_key` parameter is passed in that is not one of + (`past`, `current`, `future`, `all`). + + +.. py:function:: trajectory_from_womd_dict(example: dict[str, jax.Array], time_key: str = 'all') -> waymax.datatypes.object_state.Trajectory + + Constructs a trajectory from an Open Motion TFExample dictionary. + + :param example: Mapping from feature name to array for data read from WOMD + `tf.Example`'s in the format of specified in the WOMD website + (https://waymo.com/open/data/motion/tfexample). + :param time_key: Key which represents what time dimension to access from the state + fields in the example: (i.e. past, current, future, all). + + :returns: Trajectory of all objects from the `example` scenario with shape (..., + num_objects, num_timesteps). + + +.. py:function:: traffic_lights_from_womd_dict(example: dict[str, jax.Array], time_key: str = 'all') -> waymax.datatypes.traffic_lights.TrafficLights + + Constructs a traffic light state from WOMD TFExample dictionary. + + :param example: Mapping from feature name to array for data read from WOMD + `tf.Example`'s in the format of specified in the WOMD website + (https://waymo.com/open/data/motion/tfexample). The data, however, can be + augmented with different time keys. We do this in our data loader, by + aggregating all timesteps into an `all` key. + :param time_key: Time step information to gather from traffic light (`past`, + `current`, `future`, `all`). + + :returns: Traffic lights from the `example` scenario with shape (..., + num_traffic_lights, num_timesteps). + + :raises ValueError: If `time_key` is not part of the accepted values. + + diff --git a/_sources/autoapi/waymax/dataloader/womd_factories_internal/index.rst.txt b/_sources/autoapi/waymax/dataloader/womd_factories_internal/index.rst.txt new file mode 100644 index 0000000..561139f --- /dev/null +++ b/_sources/autoapi/waymax/dataloader/womd_factories_internal/index.rst.txt @@ -0,0 +1,6 @@ +:py:mod:`waymax.dataloader.womd_factories_internal` +=================================================== + +.. py:module:: waymax.dataloader.womd_factories_internal + + diff --git a/_sources/autoapi/waymax/dataloader/womd_utils/index.rst.txt b/_sources/autoapi/waymax/dataloader/womd_utils/index.rst.txt new file mode 100644 index 0000000..2db73f3 --- /dev/null +++ b/_sources/autoapi/waymax/dataloader/womd_utils/index.rst.txt @@ -0,0 +1,167 @@ +:py:mod:`waymax.dataloader.womd_utils` +====================================== + +.. py:module:: waymax.dataloader.womd_utils + +.. autoapi-nested-parse:: + + General settings and utility functions specifically for the WOMD data. + + These functions are mainly intended to be iternal into the Waymax data library. + + WOMD (go/womd) represents the data we typically use for simulation in our + environment. See https://waymo.com/open/data/motion/tfexample for definitions on + many of the data fields mentioned in this file. + + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.dataloader.womd_utils.aggregate_time_tensors + waymax.dataloader.womd_utils.get_features_description + waymax.dataloader.womd_utils.simulator_state_to_womd_dict_tensorflow + waymax.dataloader.womd_utils.simulator_state_to_womd_dict + waymax.dataloader.womd_utils._roadgraph_to_dict + waymax.dataloader.womd_utils._trajectory_to_dict + waymax.dataloader.womd_utils._traffic_light_to_dict + waymax.dataloader.womd_utils._object_metadata_to_dict + waymax.dataloader.womd_utils._get_invalid_future_trajectory + waymax.dataloader.womd_utils._get_invalid_future_traffic_light + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.dataloader.womd_utils.DEFAULT_FLOAT + waymax.dataloader.womd_utils.DEFAULT_INT + waymax.dataloader.womd_utils.DEFAULT_BOOL + waymax.dataloader.womd_utils._TF_TO_JNP_DTYPE + waymax.dataloader.womd_utils.TL_TIMESTAMP_STEP_AXIS + waymax.dataloader.womd_utils.TL_STEP_AXIS + + +.. py:data:: DEFAULT_FLOAT + + + +.. py:data:: DEFAULT_INT + + + +.. py:data:: DEFAULT_BOOL + :value: False + + + +.. py:data:: _TF_TO_JNP_DTYPE + + + +.. py:data:: TL_TIMESTAMP_STEP_AXIS + + + +.. py:data:: TL_STEP_AXIS + + + +.. py:function:: aggregate_time_tensors(decoded_tensors: dict[str, tensorflow.Tensor]) -> dict[str, tensorflow.Tensor] + + Combines all past/current/future fields into an 'all' field. + + Note the original past/current/future keys are removed in the returned dict. + + :param decoded_tensors: input dict of tensors keyed by string. + + :returns: `past/current/future` are + merged into `all`. + :rtype: A new dict of tensors keyed by updated string + + +.. py:function:: get_features_description(max_num_objects: int = 128, max_num_rg_points: int = 30000, include_sdc_paths: bool = False, num_paths: Optional[int] = 45, num_points_per_path: Optional[int] = 800, num_tls: Optional[int] = 16) -> dict[str, tensorflow.io.FixedLenFeature] + + Returns a dictionary of all features to be extracted. + + :param max_num_objects: Max number of objects. + :param max_num_rg_points: Max number of sampled roadgraph points. + :param include_sdc_paths: Whether to include roadgraph traversal paths for the SDC. + :param num_paths: Optional number of SDC paths. Must be defined if + `include_sdc_paths` is True. + :param num_points_per_path: Optional number of points per SDC path. Must be defined + if `include_sdc_paths` is True. + :param num_tls: Maximum number of traffic lights. + + :returns: Dictionary of all features to be extracted. + + :raises ValueError: If `include_sdc_paths` is True but either `num_paths` or + `num_points_per_path` is None. + + +.. py:function:: simulator_state_to_womd_dict_tensorflow(state: waymax.datatypes.simulator_state.SimulatorState, feature_description: dict[str, tensorflow.io.FixedLenFeature], validate: bool = False) -> dict[str, tensorflow.Tensor] + + Tensorflow version of the simulator state to WOMD dict converter. + + +.. py:function:: simulator_state_to_womd_dict(state: waymax.datatypes.simulator_state.SimulatorState, feature_description: dict[str, tensorflow.io.FixedLenFeature], validate: bool = False) -> dict[str, jax.Array] + + Converts a simulator state into the WOMD tensor format. + + See https://waymo.com/open/data/motion/tfexample for the tf.Example format + which will be returned from this function. + Note: This function is compatible with `jax2tf`. + + :param state: State of the simulator from the environment. Should contain at least + num_history + 1 elements in the time dimension for all temporal components + of the simulated trajectory. + :param feature_description: Feature description expected out of the dictionary. + This is used to understand the shape of the fields expected such as number + of agents and amount of history. + :param validate: Validate whether the simulation has progressed far enough to + ensure that an adequate amount of history is present. + + :returns: A dictionary matching fields as if they were read from the WOMD dataset. + + :raises ValueError: If `validate` is set to `True` and the number of history stored + in the observations is not `num_history` + 1. + + +.. py:function:: _roadgraph_to_dict(rg: waymax.datatypes.roadgraph.RoadgraphPoints, prefix: str = 'roadgraph_samples') -> dict[str, jax.Array] + + Gets the roadgrpah mpdata from the simulator state. + + +.. py:function:: _trajectory_to_dict(trajectory: waymax.datatypes.object_state.Trajectory, time_prefix: str) -> dict[str, jax.Array] + + Generates the mpdata fields for Trajectory data. + + +.. py:function:: _traffic_light_to_dict(tls: waymax.datatypes.traffic_lights.TrafficLights, time_prefix: str, timestamp_micros: jax.Array) -> dict[str, jax.Array] + + Generates the corresponding mpdata for TrafficLights fields. + + +.. py:function:: _object_metadata_to_dict(metadata: waymax.datatypes.object_state.ObjectMetadata) -> dict[str, jax.Array] + + Converts object metadata to the original tf.Example format. + + +.. py:function:: _get_invalid_future_trajectory(feature_description: dict[str, tensorflow.io.FixedLenFeature]) -> dict[str, jax.Array] + + Gets an invalid trajectory representing future. + + +.. py:function:: _get_invalid_future_traffic_light(feature_description: dict[str, tensorflow.io.FixedLenFeature]) -> dict[str, jax.Array] + + Gets an invalid traffic light representing future. + + diff --git a/_sources/autoapi/waymax/datatypes/action/index.rst.txt b/_sources/autoapi/waymax/datatypes/action/index.rst.txt new file mode 100644 index 0000000..216722f --- /dev/null +++ b/_sources/autoapi/waymax/datatypes/action/index.rst.txt @@ -0,0 +1,135 @@ +:py:mod:`waymax.datatypes.action` +================================= + +.. py:module:: waymax.datatypes.action + +.. autoapi-nested-parse:: + + Dataclass definitions for dynamics models. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.action.Action + waymax.datatypes.action.TrajectoryUpdate + + + + +.. py:class:: Action + + + Raw actions tensor and validity mask. + + .. attribute:: data + + Action array for all agents in the scene of shape (..., num_objects, + dim). + + .. attribute:: valid + + Whether or not an action is valid for a given agent of shape (..., + num_objects, 1). + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The tensor shape of actions. + + .. py:attribute:: data + :type: jax.Array + + + + .. py:attribute:: valid + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: validate() -> None + + Validates shape and type. + + + +.. py:class:: TrajectoryUpdate + + + A datastructure holding the controllable parts of a Trajectory. + + The TrajectoryUpdate class contains the fields that a dynamics model is + allowed to update (pose and velocity). Remaining fields, such as object + dimensions and timestamps, are computed using common code + (see `update_state_with_dynamics_trajectory`). + + As all dynamics produce a TrajectoryUpdate (via the `compute_update` method), + the TrajectoryUpdate serves as an intermediate update format that is common + to all dynamics models. This allows handling of multiple agents using + heterogeneous dynamics models. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The tensor shape of actions. + + .. py:attribute:: x + :type: jax.Array + + + + .. py:attribute:: y + :type: jax.Array + + + + .. py:attribute:: yaw + :type: jax.Array + + + + .. py:attribute:: vel_x + :type: jax.Array + + + + .. py:attribute:: vel_y + :type: jax.Array + + + + .. py:attribute:: valid + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: validate() -> None + + Validates shape and type. + + + .. py:method:: as_action() -> Action + + Returns this trajectory update as a 5D Action for StateDynamics. + + :returns: An action data structure with data of shape (..., 5) containing + x, y, yaw, vel_x, and vel_y. + + + diff --git a/_sources/autoapi/waymax/datatypes/array/index.rst.txt b/_sources/autoapi/waymax/datatypes/array/index.rst.txt new file mode 100644 index 0000000..0db8647 --- /dev/null +++ b/_sources/autoapi/waymax/datatypes/array/index.rst.txt @@ -0,0 +1,91 @@ +:py:mod:`waymax.datatypes.array` +================================ + +.. py:module:: waymax.datatypes.array + +.. autoapi-nested-parse:: + + Array data structures. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.array.MaskedArray + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.array.PyTree + + +.. py:data:: PyTree + + + +.. py:class:: MaskedArray + + + A dataclass holding values and a validity mask. + + .. attribute:: value + + A valid.shape + (...) array of values. + + .. attribute:: valid + + A boolean validity mask. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The Array prefix shape of the value. + + .. py:attribute:: value + :type: jax.Array + + + + .. py:attribute:: valid + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: validate() -> None + + Validates shape and type. + + + .. py:method:: create_and_validate(value: jax.Array, valid: jax.Array) -> MaskedArray + :classmethod: + + Creates an instance of the class. + + + .. py:method:: masked_value(fill_value: Any = 0.0) -> jax.Array + + Returns `value` where invalid elements are replaced with `fill_value`. + + :param fill_value: Value with which to replace invalid elements. Must be castable + to the dtype of `value`. + + :returns: `value` where invalid elements are replaced with `fill_value`. + + + diff --git a/_sources/autoapi/waymax/datatypes/constant/index.rst.txt b/_sources/autoapi/waymax/datatypes/constant/index.rst.txt new file mode 100644 index 0000000..b4edac0 --- /dev/null +++ b/_sources/autoapi/waymax/datatypes/constant/index.rst.txt @@ -0,0 +1,24 @@ +:py:mod:`waymax.datatypes.constant` +=================================== + +.. py:module:: waymax.datatypes.constant + +.. autoapi-nested-parse:: + + Predefined constant values. + + + +Module Contents +--------------- + +.. py:data:: TIME_INTERVAL + :value: 0.1 + + + +.. py:data:: TIMESTEP_MICROS_INTERVAL + :value: 100000 + + + diff --git a/_sources/autoapi/waymax/datatypes/index.rst.txt b/_sources/autoapi/waymax/datatypes/index.rst.txt new file mode 100644 index 0000000..45c7f21 --- /dev/null +++ b/_sources/autoapi/waymax/datatypes/index.rst.txt @@ -0,0 +1,1753 @@ +:py:mod:`waymax.datatypes` +========================== + +.. py:module:: waymax.datatypes + +.. autoapi-nested-parse:: + + Data structures and helper operations for Waymax. + + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + action/index.rst + array/index.rst + constant/index.rst + object_state/index.rst + observation/index.rst + operations/index.rst + roadgraph/index.rst + route/index.rst + simulator_state/index.rst + traffic_lights/index.rst + + +Package Contents +---------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.Action + waymax.datatypes.TrajectoryUpdate + waymax.datatypes.MaskedArray + waymax.datatypes.ObjectMetadata + waymax.datatypes.ObjectTypeIds + waymax.datatypes.Trajectory + waymax.datatypes.ObjectPose2D + waymax.datatypes.Observation + waymax.datatypes.MapElementIds + waymax.datatypes.RoadgraphPoints + waymax.datatypes.Paths + waymax.datatypes.SimulatorState + waymax.datatypes.TrafficLights + waymax.datatypes.TrafficLightStates + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.fill_invalid_trajectory + waymax.datatypes.observation_from_state + waymax.datatypes.sdc_observation_from_state + waymax.datatypes.transform_observation + waymax.datatypes.transform_roadgraph_points + waymax.datatypes.transform_traffic_lights + waymax.datatypes.transform_trajectory + waymax.datatypes.compare_all_leaf_nodes + waymax.datatypes.dynamic_index + waymax.datatypes.dynamic_slice + waymax.datatypes.make_invalid_data + waymax.datatypes.masked_mean + waymax.datatypes.select_by_onehot + waymax.datatypes.update_by_mask + waymax.datatypes.update_by_slice_in_dim + waymax.datatypes.filter_topk_roadgraph_points + waymax.datatypes.is_road_edge + waymax.datatypes.get_control_mask + waymax.datatypes.update_state_by_log + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.PyTree + waymax.datatypes.TIME_INTERVAL + waymax.datatypes.TIMESTEP_MICROS_INTERVAL + waymax.datatypes.dynamic_update_slice_in_dim + + +.. py:class:: Action + + + Raw actions tensor and validity mask. + + .. attribute:: data + + Action array for all agents in the scene of shape (..., num_objects, + dim). + + .. attribute:: valid + + Whether or not an action is valid for a given agent of shape (..., + num_objects, 1). + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The tensor shape of actions. + + .. py:attribute:: data + :type: jax.Array + + + + .. py:attribute:: valid + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: validate() -> None + + Validates shape and type. + + + +.. py:class:: TrajectoryUpdate + + + A datastructure holding the controllable parts of a Trajectory. + + The TrajectoryUpdate class contains the fields that a dynamics model is + allowed to update (pose and velocity). Remaining fields, such as object + dimensions and timestamps, are computed using common code + (see `update_state_with_dynamics_trajectory`). + + As all dynamics produce a TrajectoryUpdate (via the `compute_update` method), + the TrajectoryUpdate serves as an intermediate update format that is common + to all dynamics models. This allows handling of multiple agents using + heterogeneous dynamics models. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The tensor shape of actions. + + .. py:attribute:: x + :type: jax.Array + + + + .. py:attribute:: y + :type: jax.Array + + + + .. py:attribute:: yaw + :type: jax.Array + + + + .. py:attribute:: vel_x + :type: jax.Array + + + + .. py:attribute:: vel_y + :type: jax.Array + + + + .. py:attribute:: valid + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: validate() -> None + + Validates shape and type. + + + .. py:method:: as_action() -> Action + + Returns this trajectory update as a 5D Action for StateDynamics. + + :returns: An action data structure with data of shape (..., 5) containing + x, y, yaw, vel_x, and vel_y. + + + +.. py:class:: MaskedArray + + + A dataclass holding values and a validity mask. + + .. attribute:: value + + A valid.shape + (...) array of values. + + .. attribute:: valid + + A boolean validity mask. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The Array prefix shape of the value. + + .. py:attribute:: value + :type: jax.Array + + + + .. py:attribute:: valid + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: validate() -> None + + Validates shape and type. + + + .. py:method:: create_and_validate(value: jax.Array, valid: jax.Array) -> MaskedArray + :classmethod: + + Creates an instance of the class. + + + .. py:method:: masked_value(fill_value: Any = 0.0) -> jax.Array + + Returns `value` where invalid elements are replaced with `fill_value`. + + :param fill_value: Value with which to replace invalid elements. Must be castable + to the dtype of `value`. + + :returns: `value` where invalid elements are replaced with `fill_value`. + + + +.. py:data:: PyTree + + + +.. py:data:: TIME_INTERVAL + :value: 0.1 + + + +.. py:data:: TIMESTEP_MICROS_INTERVAL + :value: 100000 + + + +.. py:function:: fill_invalid_trajectory(traj: Trajectory) -> Trajectory + + Fills a trajectory with invalid values. + + An invalid value is -1 for numerical fields and False for booleans. + + :param traj: Trajectory to fill. + + :returns: A new trajectory with invalid values. + + +.. py:class:: ObjectMetadata + + + Time-independent object metadata. + + All arrays are of shape (..., num_objects). + + .. attribute:: ids + + A unique integer id for each object which is consistent over time of + data type int32. + + .. attribute:: object_types + + An integer representing each different class of object + (Unset=0, Vehicle=1, Pedestrian=2, Cyclist=3, Other=4) of data type int32. + This definition is from Waymo Open Motion Dataset (WOMD). + + .. attribute:: is_sdc + + Binary mask of data type bool representing whether an object + represents the sdc or some other object. + + .. attribute:: is_modeled + + Whether a specific object is one designated by WOMD to be + predicted of data type bool. + + .. attribute:: is_valid + + Whether an object is valid at any part of the run segment of data + type bool. + + .. attribute:: objects_of_interest + + A vector of type bool to indicate which objects in the + scene corresponding to the first dimension of the object tensors have + interactive behavior. Up to 2 objects will be selected. The objects in + this list form an interactive group. + + .. attribute:: is_controlled + + Whether an object will be controlled by external agents in an + environment. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The Array shape of the metadata. + + .. py:property:: num_objects + :type: int + + The number of objects in metadata. + + .. py:attribute:: ids + :type: jax.Array + + + + .. py:attribute:: object_types + :type: jax.Array + + + + .. py:attribute:: is_sdc + :type: jax.Array + + + + .. py:attribute:: is_modeled + :type: jax.Array + + + + .. py:attribute:: is_valid + :type: jax.Array + + + + .. py:attribute:: objects_of_interest + :type: jax.Array + + + + .. py:attribute:: is_controlled + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: validate() + + Validates shape and type. + + + +.. py:class:: ObjectTypeIds + + + Bases: :py:obj:`enum.IntEnum` + + Ids for different map elements to be mapped into a tensor. + + These integers represent the ID of these specific types as defined in: + https://waymo.com/open/data/motion/tfexample. + + .. py:attribute:: UNSET + :value: 0 + + + + .. py:attribute:: VEHICLE + :value: 1 + + + + .. py:attribute:: PEDESTRIAN + :value: 2 + + + + .. py:attribute:: CYCLIST + :value: 3 + + + + .. py:attribute:: OTHER + :value: 4 + + + + +.. py:class:: Trajectory + + + Data structure representing a trajectory. + + The shapes of all objects are of shape (..., num_objects, num_timesteps). + + .. attribute:: x + + The x coordinate of each object at each time step of data type float32. + + .. attribute:: y + + The y coordinate of each object at each time step of data type float32. + + .. attribute:: z + + The z coordinate of each object at each time step of data type float32. + + .. attribute:: vel_x + + The x component of the object velocity at each time step of data type + float32. + + .. attribute:: vel_y + + The y component of the object velocity at each time step of data type + float32. + + .. attribute:: yaw + + Counter-clockwise yaw in top-down view (rotation about the Z axis from + a unit X vector to the object direction vector) of shape of data type + float32. + + .. attribute:: valid + + Validity bit for all object at all times steps of data type bool. + + .. attribute:: timestamp_micros + + A timestamp in microseconds for each time step of data + type int32. + + .. attribute:: length + + The length of each object at each time step of data type float32. + Note for each object, its length is fixed for all time steps. + + .. attribute:: width + + The width of each object at each time step of data type float32. Note + for each object, its width is fixed for all time steps. + + .. attribute:: height + + The height of each object at each time step of data type float32. + Note for each object, its height is fixed for all time steps. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The Array shape of this trajectory. + + .. py:property:: num_objects + :type: int + + The number of objects included in this trajectory per example. + + .. py:property:: num_timesteps + :type: int + + The length of this trajectory in time. + + .. py:property:: xy + :type: jax.Array + + Stacked xy location. + + .. py:property:: xyz + :type: jax.Array + + Stacked xyz location. + + .. py:property:: vel_xy + :type: jax.Array + + Stacked xy velocity. + + .. py:property:: speed + :type: jax.Array + + Speed on x-y plane. + + .. py:property:: vel_yaw + :type: jax.Array + + Angle of the velocity on x-y plane. + + .. py:property:: bbox_corners + :type: jax.Array + + Corners of the bounding box spanning the object's shape. + + :returns: + + Box corners' (x, y) coordinates spanning the object of shape + (..., num_objects, num_timesteps, 4, 2). The 4 corners start from the + objects' front right corner and go counter-clockwise. + + .. py:attribute:: x + :type: jax.Array + + + + .. py:attribute:: y + :type: jax.Array + + + + .. py:attribute:: z + :type: jax.Array + + + + .. py:attribute:: vel_x + :type: jax.Array + + + + .. py:attribute:: vel_y + :type: jax.Array + + + + .. py:attribute:: yaw + :type: jax.Array + + + + .. py:attribute:: valid + :type: jax.Array + + + + .. py:attribute:: timestamp_micros + :type: jax.Array + + + + .. py:attribute:: length + :type: jax.Array + + + + .. py:attribute:: width + :type: jax.Array + + + + .. py:attribute:: height + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: stack_fields(field_names: collections.abc.Sequence[str]) -> jax.Array + + Returns a concatenated version of a set of field names for Trajectory. + + + .. py:method:: zeros(shape: collections.abc.Sequence[int]) -> Trajectory + :classmethod: + + Creates a Trajectory containing zeros of the specified shape. + + + .. py:method:: validate() + + Validates shape and type. + + + +.. py:class:: ObjectPose2D + + + Object 2D pose that can be used for coordinate transformation. + + The pose information is stored with two format + (see details at https://en.wikipedia.org/wiki/Pose_(computer_vision)): + 1) the position (original_xy) and orientation (original_yaw); 2) the + transformation matrix (matrix) with the rotation angle explicitly saved as + delta_yaw. + + Note since this is a general data structure, the pose could be using arbitrary + coordinate system. + + Example usage: + Assuming the pose is using C coordinate system, applying matrix[..., i, :, :] + over an object with center (x, y) and yaw in C coordinate system gives its + position in i's local coordinate system. The object's yaw in i's local + coordinate system is yaw + delta_yaw. + This is done by translation of -original_xy followed by counter-clockwise + rotation of original_yaw. + + .. attribute:: original_xy + + (..., num_objects, 2), the coordinates of each object's center + in original coordinate systems. + + .. attribute:: original_yaw + + (..., num_objects), object's yaw in original coordinate + systems. + + .. attribute:: matrix + + (..., num_objects, 3, 3), transformation matrix where `matrix[..., + i, :, :]` is the transformation from original coordinates to object `i` + centric coordinates. + + .. attribute:: delta_yaw + + (..., num_objects), rotation angles used to transform yaw in + original coordinate system to object-center coordinate system. Note this + is the negative of original_yaw. + + .. attribute:: valid + + (..., num_objects), valid or not. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The Array shape. + + .. py:property:: num_objects + :type: int + + The number of objects. + + .. py:property:: rotation_matrix + :type: jax.Array + + 2D rotation matrix. + + .. py:property:: translation_vector + :type: jax.Array + + 2D translation vector. + + .. py:attribute:: original_xy + :type: jax.Array + + + + .. py:attribute:: original_yaw + :type: jax.Array + + + + .. py:attribute:: matrix + :type: jax.Array + + + + .. py:attribute:: delta_yaw + :type: jax.Array + + + + .. py:attribute:: valid + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: from_center_and_yaw(xy: jax.Array, yaw: jax.Array, valid: Optional[jax.Array] = None) -> ObjectPose2D + :classmethod: + + Initializes pose from center location and yaw. + + :param xy: (..., num_objects, 2), 2D coordinates of objects' center in arbitrary + coordinate system. + :param yaw: (..., num_objects), objects' yaw in same coordinate system as xy. + :param valid: (..., num_objects), boolean mask for validity. + + :returns: A ObjectPose2D with shape (..., num_objects). + + + .. py:method:: from_transformation(matrix: jax.Array, delta_yaw: jax.Array, valid: Optional[jax.Array] = None) -> ObjectPose2D + :classmethod: + + Init pose from transformation matrix and delta_yaw. + + :param matrix: (..., num_objects, 3, 3), 2D homogenous transformation matrix. + :param delta_yaw: (..., num_objects), transformation angles used together with + matrix for coordinate transformation (i.e. rotation applied by the + transformation matrix). + :param valid: (..., num_objects), boolean mask for validity. + + :returns: A ObjectPose2D with shape (..., num_objects). + + + .. py:method:: validate() + + Validates shape and type. + + + +.. py:class:: Observation + + + Observation at a single simulation step. + + num_observations: number of objects that will have an observation/view over + other objects/map elements. + + The observation can include a fixed number of history information. Note we + support multi-agent observation by default: for each object (in axis + num_observations), we compute its view of all other objects (in axis + num_objects), roadgraph points, and traffic lights. The coordinates used for + each object (num_observations) are centered at its location defined by pose2d. + + .. attribute:: trajectory + + Time-dependent information, in object-centric coordinates + defined by pose2d of shape (..., num_observations, num_objects, + num_timesteps). + + .. attribute:: is_ego + + Binary mask of shape (..., num_observations, num_objects). It + represents which object in num_objects is the observer itself. + + .. attribute:: pose2d + + Poses for all objects, used for transformation of shape (..., + num_observations). + + .. attribute:: metadata + + Time-independent information of shape (..., num_observations, + num_objects). + + .. attribute:: roadgraph_static_points + + Top-k (k=num_points) nearest static roadgraph + points of shape (..., num_observations, num_points). + + .. attribute:: traffic_lights + + Current state of the traffic lights in the log of shape + (..., num_observations, num_traffic_lights, num_timesteps). + + .. attribute:: sdc_paths + + SDC roadgraph traversal paths. Only valid for SDC agents of shape + (..., num_observations, num_paths, num_points_per_path). + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + (..., num_observations). + + :type: The longest common prefix shape of all attributes + + .. py:property:: batch_dims + :type: tuple[int, Ellipsis] + + Batch dimensions. + + .. py:property:: num_objects + :type: int + + The number of objects. + + .. py:property:: valid + :type: jax.Array + + Whether the observation is valid, (..., num_observations). + + .. py:attribute:: trajectory + :type: waymax.datatypes.object_state.Trajectory + + + + .. py:attribute:: is_ego + :type: jax.Array + + + + .. py:attribute:: pose2d + :type: ObjectPose2D + + + + .. py:attribute:: metadata + :type: waymax.datatypes.object_state.ObjectMetadata + + + + .. py:attribute:: roadgraph_static_points + :type: waymax.datatypes.roadgraph.RoadgraphPoints + + + + .. py:attribute:: traffic_lights + :type: Observation.traffic_lights + + + + .. py:attribute:: sdc_paths + :type: Optional[waymax.datatypes.route.Paths] + + + + .. py:method:: for_obj(idx: int) -> Observation + + Observation from idx-th object's point of view over all objects. + + :param idx: The index of the object. + + :returns: A Observation with shape (..., 1). + + + .. py:method:: validate() + + Validates shape and type. + + + +.. py:function:: observation_from_state(state: waymax.datatypes.simulator_state.SimulatorState, obs_num_steps: int = 1, roadgraph_top_k: int = 1000, coordinate_frame: waymax.config.CoordinateFrame = config.CoordinateFrame.GLOBAL) -> Observation + + Constructs Observation from SimulatorState for all agents (jit-able). + + :param state: A SimulatorState, with entirely variable shape (...). + :param obs_num_steps: Number of steps history included in observation. Last + timestep is state.timestep. + :param roadgraph_top_k: Number of topk roadgraph observed by each object. + :param coordinate_frame: Which coordinate frame the returned observation is using. + + :returns: Observation at current timestep from given simulator state, with shape (..., + num_objects). + + +.. py:function:: sdc_observation_from_state(state: waymax.datatypes.simulator_state.SimulatorState, obs_num_steps: int = 1, roadgraph_top_k: int = 1000, coordinate_frame: waymax.config.CoordinateFrame = config.CoordinateFrame.SDC) -> Observation + + Constructs Observation from SimulatorState for SDC only (jit-able). + + :param state: a SimulatorState, with shape (...) + :param obs_num_steps: number of steps history included in observation. Last + timestep is state.timestep. + :param roadgraph_top_k: number of topk roadgraph observed by each object. + :param coordinate_frame: which coordinate frame the returned observation is using. + + :returns: SDC Observation at current timestep from given simulator state, with shape + (..., 1), where the last object dimension is 1 as there is only one SDC. It + is not sequeezed to be consistent with multi-agent cases and compatible for + other utils fnctions. + + +.. py:function:: transform_observation(observation: Observation, pose2d: ObjectPose2D) -> Observation + + Transforms a Observation into coordinates specified by pose2d. + + :param observation: Has shape (..., num_observations) + :param pose2d: Has shape (..., num_observations) + + :returns: Transformed observation in local coordinates per-observation defined by the + pose. + + +.. py:function:: transform_roadgraph_points(roadgraph_points: waymax.datatypes.roadgraph.RoadgraphPoints, pose2d: ObjectPose2D) -> waymax.datatypes.roadgraph.RoadgraphPoints + + Transform a RoadgraphPoints into coordinates specified by pose2d. + + :param roadgraph_points: A RoadgraphPoints with shape (..., num_observations, + num_points). + :param pose2d: A ObjectPose2D with shape (..., num_observations). + + :returns: A transformed RoadgraphPoints in local coordinates per-observation defined + by the pose. + + +.. py:function:: transform_traffic_lights(tls: waymax.datatypes.traffic_lights.TrafficLights, pose2d: ObjectPose2D) -> waymax.datatypes.traffic_lights.TrafficLights + + Transforms a TrafficLightStates into coordinates specified by pose2d. + + :param tls: A TrafficLightStates with shape (..., num_observations, + num_traffic_lights, num_timesteps). + :param pose2d: A ObjectPose2D with shape (..., num_observations). + + :returns: Transformed TrafficLightStates in local coordinates per-observation defined + by the pose. + + +.. py:function:: transform_trajectory(traj: waymax.datatypes.object_state.Trajectory, pose2d: ObjectPose2D) -> waymax.datatypes.object_state.Trajectory + + Transforms a Trajectory into coordinates specified by pose2d. + + :param traj: A Trajectory with shape (..., num_observations, num_objects, + num_timesteps) in coordinate system same as the one used by + original_xy/yaw in pose2d. + :param pose2d: A ObjectPose2D with shape (..., num_observations). + + :returns: A transformed trajectory in local coordinates per-observation defined by the + pose. + + +.. py:function:: compare_all_leaf_nodes(first: PyTree, second: PyTree, compare_func: Callable[[jax.Array, jax.Array], bool] = jnp.array_equal) -> bool + + Checks if all leaf nodes are consistent via compare_func. + + The default behaviour (with compare_func as jnp.array_equal) is to + compare if two PyTree are equal (i.e. all leaf nodes of array are equal). + One can also use compare_func as jnp.allclose to give some tolerance + for numerical difference. + + :param first: A PyTree for comparison. + :param second: A PyTree for comparison. + :param compare_func: A function comparing nodes between two input arrays. + + :returns: True if inputs PyTrees are consistent by func. + + +.. py:function:: dynamic_index(inputs: PyTree, index: ArrayLike, axis: int = 0, keepdims: bool = True) -> PyTree + + Wraps dynamic_index_in_dim on a PyTree. + + :param inputs: Object where each element in it is to be indexed. + :param index: Element number to index into each array in the tree. + :param axis: Axis of each array to perform the indexing in. + :param keepdims: Whether or not to keep the same rank as `inputs`. If this is set + to `True` then the return value will be such that `.shape[axis]` == 1. + + :returns: + + Array tree where every element of the tree has been indexed at the same + specified axis. + + +.. py:function:: dynamic_slice(inputs: PyTree, start_index: ArrayLike, slice_size: int, axis: int = 0) -> PyTree + + Wraps dynamic_slice_in_dim on a PyTree. + + :param inputs: Object where each element in its tree is to be sliced. + :param start_index: Start index of slicing function. + :param slice_size: Size of slice applied to `inputs` starting from `start_index` to + `start_index` + `slice_size`. + :param axis: Axis of each array to perform the slicing in. + + :returns: + + Array tree which has been sliced along an axis which maintains the same + rank as `inputs`. + + +.. py:data:: dynamic_update_slice_in_dim + + + +.. py:function:: make_invalid_data(data: jax.Array) -> jax.Array + + Returns a tensor of invalid values with identical shape to data. + + Invalid values are defined as False for booleans, and -1 for numerical values. + + :param data: Tensor to invalidate. + + :returns: A tensor of invalid values of the same shape and dtype as data. + + +.. py:function:: masked_mean(x: jax.Array, valid: jax.Array, axis: Axis = 0, invalid_value: float = -1.0) -> jax.Array + + Calculates the mean of the array removing invalid values. + + :param x: Input to the masked mean function. + :param valid: Boolean array with the same shape as x which indicates which values + should be included in the mean. + :param axis: Axis to reduce along. + :param invalid_value: If there is no valid fields, the value will be replaced by + this invalid value. + + :returns: Array representing the mean of the array of all valid values. + + +.. py:function:: select_by_onehot(data: PyTree, selection: jax.Array, keepdims: bool = False) -> PyTree + + Selects data using a one-hot vector. + + :param data: A pytree with leaf shapes (..., N, ...). + :param selection: A one-hot vector with shape (..., N). + :param keepdims: Whether to leave a (1,) dimension on the selected axis. + + :returns: A pytree with leaf shapes (..., 1) if keepdims=True. + A pytree with leaf shapes (..., ) if keepdims=False. + + +.. py:function:: update_by_mask(inputs: PyTree, updates: PyTree, mask: jax.Array) -> PyTree + + Updates inputs by updates (with same struct) for masked values. + + Uses `jax.tree_util` to update each field as such: + inputs.field = jnp.where(mask, updates.field, inputs.field) + + :param inputs: Represents the data to be updated and returned. + :param updates: Represents the data that is passed in to update all fields. It is + assumed that the `updates` and the `inputs` have the same structure. These + fields must be broadcastable to the same shape as `inputs` after applying + the mask. + :param mask: Mask that represents which elements of the `inputs` array tree to + update with the `updates` field. Shape must be broadcastable to the leaf + nodes of inputs and updates. + + :returns: + + Updated tree array of the same structure as `inputs` which has had all its + fields updated with the corresponding fields in `updates` conditioned on + whether `mask` requests it. + + +.. py:function:: update_by_slice_in_dim(inputs: PyTree, updates: PyTree, inputs_start_idx: ArrayLike, updates_start_idx: Optional[ArrayLike] = None, slice_size: Optional[int] = None, axis: int = 0) -> PyTree + + Updates a slice in inputs using slices from updates. + + It replaces inputs[inputs_start_idx:inputs_start_idx+slice_size] by + updates[updates_start_idx:updates_start_idx+slice_size] for specified axis + only. + + If updates_start_idx and slice_size are both None, the behavior of this + function will default to dynamic_update_slice_in_dim. + + :param inputs: Represents the data to be updated and returned. + :param updates: Represents the data that is passed in to update all fields. It is + assumed that the `updates` and the `inputs` have the same structure. + :param inputs_start_idx: starting index in inputs. + :param updates_start_idx: starting index in updates. + :param slice_size: the slice size used for update. If slice size is None, uses the + entire array and updates_start_idx will be ignored. + :param axis: Axis of each array to perform the slicing in. + + :returns: A PyTree with same structure as inputs. + + +.. py:function:: filter_topk_roadgraph_points(roadgraph: RoadgraphPoints, reference_points: jax.Array, topk: int) -> RoadgraphPoints + + Returns the topk closest roadgraph points to a reference point. + + If `topk` is larger than the number of points, exception will be raised. + + :param roadgraph: Roadgraph information to filter, (..., num_points). + :param reference_points: A tensor of shape (..., 2) - the reference point used to + measure distance. + :param topk: Number of points to keep. + + :returns: + + Roadgraph data structure that has been filtered to only contain the `topk` + closest points to a reference point. + + +.. py:function:: is_road_edge(types: jax.Array) -> jax.Array + + Determines which map elements in a tensor are road edges. + + :param types: An array of integer values with each integer value representing a + unique map type. These integers are based on a schema defined in + https://waymo.com/open/data/motion/tfexample. This is of shape (..., + num_points). + + :returns: A bool array where an element is true if the map element is a road edge. + + +.. py:class:: MapElementIds + + + Bases: :py:obj:`enum.IntEnum` + + Ids for different map elements to be mapped into a tensor. + + These integers represent the ID of these specific types as defined in: + https://waymo.com/open/data/motion/tfexample. + + .. py:attribute:: LANE_UNDEFINED + :value: 0 + + + + .. py:attribute:: LANE_FREEWAY + :value: 1 + + + + .. py:attribute:: LANE_SURFACE_STREET + :value: 2 + + + + .. py:attribute:: LANE_BIKE_LANE + :value: 3 + + + + .. py:attribute:: ROAD_LINE_UNKNOWN + :value: 5 + + + + .. py:attribute:: ROAD_LINE_BROKEN_SINGLE_WHITE + :value: 6 + + + + .. py:attribute:: ROAD_LINE_SOLID_SINGLE_WHITE + :value: 7 + + + + .. py:attribute:: ROAD_LINE_SOLID_DOUBLE_WHITE + :value: 8 + + + + .. py:attribute:: ROAD_LINE_BROKEN_SINGLE_YELLOW + :value: 9 + + + + .. py:attribute:: ROAD_LINE_BROKEN_DOUBLE_YELLOW + :value: 10 + + + + .. py:attribute:: ROAD_LINE_SOLID_SINGLE_YELLOW + :value: 11 + + + + .. py:attribute:: ROAD_LINE_SOLID_DOUBLE_YELLOW + :value: 12 + + + + .. py:attribute:: ROAD_LINE_PASSING_DOUBLE_YELLOW + :value: 13 + + + + .. py:attribute:: ROAD_EDGE_UNKNOWN + :value: 14 + + + + .. py:attribute:: ROAD_EDGE_BOUNDARY + :value: 15 + + + + .. py:attribute:: ROAD_EDGE_MEDIAN + :value: 16 + + + + .. py:attribute:: STOP_SIGN + :value: 17 + + + + .. py:attribute:: CROSSWALK + :value: 18 + + + + .. py:attribute:: SPEED_BUMP + :value: 19 + + + + .. py:attribute:: UNKNOWN + + + + +.. py:class:: RoadgraphPoints + + + Data structure representing roadgraph points. + + It holds the coordinates of the sampled map data points. Note all attributes + have shape (..., num_points), where num_points is the + number of road graph points. + + .. attribute:: x + + X coordinate of positions of the sampled map data points of dtype + float32. + + .. attribute:: y + + Y coordinate of positions of the sampled map data points of dtype + float32. + + .. attribute:: z + + Z coordinate of positions of the sampled map data points of dtype + float32. + + .. attribute:: dir_x + + X coordinate of a unit direction vector for each map feature sample + point of dtype float32. + + .. attribute:: dir_y + + Y coordinate of a unit direction vector for each map feature sample + point of dtype float32. + + .. attribute:: dir_z + + Z coordinate of a unit direction vector for each map feature sample + point of dtype float32. + + .. attribute:: types + + A unique integer for each combination of map feature type and + properties of dtype int32. See the table in the Value Range column of + website: https://waymo.com/open/data/motion/tfexample. + + .. attribute:: ids + + A unique Integer ID for the vector map feature each sample is from of + dtype int32. + + .. attribute:: valid + + A valid flag for each map sample point of dtype bool. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The Array shape of the RoadgraphPoints. + + .. py:property:: num_points + :type: int + + The number of points included in this RoadgraphPoints per example. + + .. py:property:: xy + :type: jax.Array + + Stacked xy location for all points. + + .. py:property:: xyz + :type: jax.Array + + Stacked xyz location for all points. + + .. py:property:: dir_xy + :type: jax.Array + + Stacked xy direction for all points. + + .. py:property:: dir_xyz + :type: jax.Array + + Stacked xy direction for all points. + + .. py:attribute:: x + :type: jax.Array + + + + .. py:attribute:: y + :type: jax.Array + + + + .. py:attribute:: z + :type: jax.Array + + + + .. py:attribute:: dir_x + :type: jax.Array + + + + .. py:attribute:: dir_y + :type: jax.Array + + + + .. py:attribute:: dir_z + :type: jax.Array + + + + .. py:attribute:: types + :type: jax.Array + + + + .. py:attribute:: ids + :type: jax.Array + + + + .. py:attribute:: valid + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: validate() + + Validates shape and type. + + + +.. py:class:: Paths + + + Data structure for a set of paths represented by roadgraph points. + + A `path` consists of a set of roadgraph points (usually along the lane center) + and represents where an object can legally traverse giving its starting point. + + .. attribute:: x + + Path coordinate x, shape is (..., num_paths, num_points_per_path) and + dtype is float32. + + .. attribute:: y + + Path coordinate y, shape is (..., num_paths, num_points_per_path) and + dtype is float32. + + .. attribute:: z + + Path coordinate z, shape is (..., num_paths, num_points_per_path) and + dtype is float32. + + .. attribute:: ids + + IDs representing which lane the points belong to, shape is (..., + num_paths, num_points_per_path) and dtype is int32. + + .. attribute:: valid + + Validity flag, shape is (..., num_paths, num_points_per_path) and + dtype is bool. + + .. attribute:: arc_length + + Represents the arc length for each point from the starting point + along the path with shape (..., num_paths, num_points_per_path) and dtype + is float32. + + .. attribute:: on_route + + Flag for each path representing whether it is on the road route + corresponding to the logged trajectory, shape is (..., num_paths, 1) and + the dtype is bool. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The Array shape of the routes. + + .. py:property:: num_points_per_path + :type: int + + The number of points included in the paths per example. + + .. py:property:: num_paths + :type: int + + The number of paths included in the example. + + .. py:property:: xy + :type: jax.Array + + Stacked xy location for all points. + + .. py:property:: xyz + :type: jax.Array + + Stacked xyz location for all points. + + .. py:attribute:: x + :type: jax.Array + + + + .. py:attribute:: y + :type: jax.Array + + + + .. py:attribute:: z + :type: jax.Array + + + + .. py:attribute:: ids + :type: jax.Array + + + + .. py:attribute:: valid + :type: jax.Array + + + + .. py:attribute:: arc_length + :type: jax.Array + + + + .. py:attribute:: on_route + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: validate() -> None + + Validates shape and type. + + + +.. py:function:: get_control_mask(metadata: waymax.datatypes.object_state.ObjectMetadata, obj_type: waymax.config.ObjectType) -> jax.Array + + Returns binary mask for selected object type. + + :param metadata: An ObjectMetadata, having shape (..., num_objects). + :param obj_type: Represents which type of objects should be selected. + + :returns: A binary mask with shape (..., num_objects). + + +.. py:class:: SimulatorState + + + A dataclass holding the simulator state, all data in global coordinates. + + .. attribute:: sim_trajectory + + Simulated trajectory for all objects of shape (..., + num_objects, num_timesteps). The number of timesteps is the same as in the + log, but future trajectory points that have not been simulated will be + marked invalid. + + .. attribute:: log_trajectory + + Logged trajectory for all objects of shape (..., + num_objects, num_timesteps). + + .. attribute:: log_traffic_light + + Logged traffic light information for the entire run + segment of shape (..., num_traffic_lights, num_timesteps). + + .. attribute:: object_metadata + + Metadata for all objects of shape (..., num_objects). + + .. attribute:: timestep + + The current simulation timestep index of shape (...). Note that + sim_trajectory at `timestep` is the last executed step by the simulator. + + .. attribute:: sdc_paths + + Paths for SDC, representing where the SDC can drive of shape + (..., num_paths, num_points_per_path). + + .. attribute:: roadgraph_points + + A optional RoadgraphPoints holding subsampled roadgraph + points of shape (..., num_points). + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + Shape is defined as the most common prefix shape of all attributes. + + .. py:property:: batch_dims + :type: tuple[int, Ellipsis] + + Batch dimensions. + + .. py:property:: num_objects + :type: int + + The number of objects included in this trajectory per example. + + .. py:property:: is_done + :type: bool + + Returns whether the simulation is at the end of the logged history. + + .. py:property:: remaining_timesteps + :type: int + + Returns the number of remaining timesteps in the episode. + + .. py:property:: current_sim_trajectory + :type: waymax.datatypes.object_state.Trajectory + + Returns the trajectory corresponding to the current sim state. + + .. py:property:: current_log_trajectory + :type: waymax.datatypes.object_state.Trajectory + + Returns the trajectory corresponding to the current sim state. + + .. py:attribute:: sim_trajectory + :type: waymax.datatypes.object_state.Trajectory + + + + .. py:attribute:: log_trajectory + :type: waymax.datatypes.object_state.Trajectory + + + + .. py:attribute:: log_traffic_light + :type: waymax.datatypes.traffic_lights.TrafficLights + + + + .. py:attribute:: object_metadata + :type: waymax.datatypes.object_state.ObjectMetadata + + + + .. py:attribute:: timestep + :type: jax.typing.ArrayLike + + + + .. py:attribute:: sdc_paths + :type: Optional[waymax.datatypes.route.Paths] + + + + .. py:attribute:: roadgraph_points + :type: Optional[waymax.datatypes.roadgraph.RoadgraphPoints] + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: validate() + + Validates shape and type. + + + +.. py:function:: update_state_by_log(state: SimulatorState, num_steps: int) -> SimulatorState + + Advances SimulatorState by num_steps using logged data. + + +.. py:class:: TrafficLights + + + Data structure representing the dynamic traffic light state information. + + All attributes have shape (..., num_traffic_lights, num_timesteps). + + .. attribute:: x + + The X coordinate of the stop light position of dtype float32. + + .. attribute:: y + + The Y coordinate of the stop light position of dtype float32. + + .. attribute:: z + + The Z coordinate of the stop light position. This point is at the + beginning of the lane segment controlled by the traffic signal of dtype + float32. + + .. attribute:: state + + The state of each traffic light at each time step of dtype int32. See + above enum for integer values of all traffic lights states. + + .. attribute:: lane_ids + + which lane it controls. + + .. attribute:: valid + + A valid flag for all elements of features traffic_light.XX. If set to + True, the element is populated with valid data. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The tensor shape of the traffic lights. + + .. py:property:: num_traffic_lights + :type: int + + The number of points included in this traffic light per example. + + .. py:property:: num_timesteps + :type: int + + The number of timesteps included in this traffic light per example. + + .. py:property:: xy + :type: jax.Array + + Stacked xy location for all points. + + .. py:attribute:: x + :type: jax.Array + + + + .. py:attribute:: y + :type: jax.Array + + + + .. py:attribute:: z + :type: jax.Array + + + + .. py:attribute:: state + :type: jax.Array + + + + .. py:attribute:: lane_ids + :type: jax.Array + + + + .. py:attribute:: valid + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: validate() + + Validates shape and type. + + + +.. py:class:: TrafficLightStates + + + Bases: :py:obj:`enum.IntEnum` + + Represents the integer values for all Traffic Light State values. + + .. py:attribute:: UNKNOWN + :value: 0 + + + + .. py:attribute:: ARROW_STOP + :value: 1 + + + + .. py:attribute:: ARROW_CAUTION + :value: 2 + + + + .. py:attribute:: ARROW_GO + :value: 3 + + + + .. py:attribute:: STOP + :value: 4 + + + + .. py:attribute:: CAUTION + :value: 5 + + + + .. py:attribute:: GO + :value: 6 + + + + .. py:attribute:: FLASHING_STOP + :value: 7 + + + + .. py:attribute:: FLASHING_CAUTION + :value: 8 + + + + diff --git a/_sources/autoapi/waymax/datatypes/object_state/index.rst.txt b/_sources/autoapi/waymax/datatypes/object_state/index.rst.txt new file mode 100644 index 0000000..34eb302 --- /dev/null +++ b/_sources/autoapi/waymax/datatypes/object_state/index.rst.txt @@ -0,0 +1,386 @@ +:py:mod:`waymax.datatypes.object_state` +======================================= + +.. py:module:: waymax.datatypes.object_state + +.. autoapi-nested-parse:: + + Data structures for trajectory and metadata information for scene objects. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.object_state.ObjectTypeIds + waymax.datatypes.object_state.ObjectMetadata + waymax.datatypes.object_state.Trajectory + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.object_state.fill_invalid_trajectory + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.object_state._INVALID_FLOAT_VALUE + waymax.datatypes.object_state._INVALID_INT_VALUE + + +.. py:data:: _INVALID_FLOAT_VALUE + + + +.. py:data:: _INVALID_INT_VALUE + + + +.. py:class:: ObjectTypeIds + + + Bases: :py:obj:`enum.IntEnum` + + Ids for different map elements to be mapped into a tensor. + + These integers represent the ID of these specific types as defined in: + https://waymo.com/open/data/motion/tfexample. + + .. py:attribute:: UNSET + :value: 0 + + + + .. py:attribute:: VEHICLE + :value: 1 + + + + .. py:attribute:: PEDESTRIAN + :value: 2 + + + + .. py:attribute:: CYCLIST + :value: 3 + + + + .. py:attribute:: OTHER + :value: 4 + + + + +.. py:class:: ObjectMetadata + + + Time-independent object metadata. + + All arrays are of shape (..., num_objects). + + .. attribute:: ids + + A unique integer id for each object which is consistent over time of + data type int32. + + .. attribute:: object_types + + An integer representing each different class of object + (Unset=0, Vehicle=1, Pedestrian=2, Cyclist=3, Other=4) of data type int32. + This definition is from Waymo Open Motion Dataset (WOMD). + + .. attribute:: is_sdc + + Binary mask of data type bool representing whether an object + represents the sdc or some other object. + + .. attribute:: is_modeled + + Whether a specific object is one designated by WOMD to be + predicted of data type bool. + + .. attribute:: is_valid + + Whether an object is valid at any part of the run segment of data + type bool. + + .. attribute:: objects_of_interest + + A vector of type bool to indicate which objects in the + scene corresponding to the first dimension of the object tensors have + interactive behavior. Up to 2 objects will be selected. The objects in + this list form an interactive group. + + .. attribute:: is_controlled + + Whether an object will be controlled by external agents in an + environment. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The Array shape of the metadata. + + .. py:property:: num_objects + :type: int + + The number of objects in metadata. + + .. py:attribute:: ids + :type: jax.Array + + + + .. py:attribute:: object_types + :type: jax.Array + + + + .. py:attribute:: is_sdc + :type: jax.Array + + + + .. py:attribute:: is_modeled + :type: jax.Array + + + + .. py:attribute:: is_valid + :type: jax.Array + + + + .. py:attribute:: objects_of_interest + :type: jax.Array + + + + .. py:attribute:: is_controlled + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: validate() + + Validates shape and type. + + + +.. py:class:: Trajectory + + + Data structure representing a trajectory. + + The shapes of all objects are of shape (..., num_objects, num_timesteps). + + .. attribute:: x + + The x coordinate of each object at each time step of data type float32. + + .. attribute:: y + + The y coordinate of each object at each time step of data type float32. + + .. attribute:: z + + The z coordinate of each object at each time step of data type float32. + + .. attribute:: vel_x + + The x component of the object velocity at each time step of data type + float32. + + .. attribute:: vel_y + + The y component of the object velocity at each time step of data type + float32. + + .. attribute:: yaw + + Counter-clockwise yaw in top-down view (rotation about the Z axis from + a unit X vector to the object direction vector) of shape of data type + float32. + + .. attribute:: valid + + Validity bit for all object at all times steps of data type bool. + + .. attribute:: timestamp_micros + + A timestamp in microseconds for each time step of data + type int32. + + .. attribute:: length + + The length of each object at each time step of data type float32. + Note for each object, its length is fixed for all time steps. + + .. attribute:: width + + The width of each object at each time step of data type float32. Note + for each object, its width is fixed for all time steps. + + .. attribute:: height + + The height of each object at each time step of data type float32. + Note for each object, its height is fixed for all time steps. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The Array shape of this trajectory. + + .. py:property:: num_objects + :type: int + + The number of objects included in this trajectory per example. + + .. py:property:: num_timesteps + :type: int + + The length of this trajectory in time. + + .. py:property:: xy + :type: jax.Array + + Stacked xy location. + + .. py:property:: xyz + :type: jax.Array + + Stacked xyz location. + + .. py:property:: vel_xy + :type: jax.Array + + Stacked xy velocity. + + .. py:property:: speed + :type: jax.Array + + Speed on x-y plane. + + .. py:property:: vel_yaw + :type: jax.Array + + Angle of the velocity on x-y plane. + + .. py:property:: bbox_corners + :type: jax.Array + + Corners of the bounding box spanning the object's shape. + + :returns: + + Box corners' (x, y) coordinates spanning the object of shape + (..., num_objects, num_timesteps, 4, 2). The 4 corners start from the + objects' front right corner and go counter-clockwise. + + .. py:attribute:: x + :type: jax.Array + + + + .. py:attribute:: y + :type: jax.Array + + + + .. py:attribute:: z + :type: jax.Array + + + + .. py:attribute:: vel_x + :type: jax.Array + + + + .. py:attribute:: vel_y + :type: jax.Array + + + + .. py:attribute:: yaw + :type: jax.Array + + + + .. py:attribute:: valid + :type: jax.Array + + + + .. py:attribute:: timestamp_micros + :type: jax.Array + + + + .. py:attribute:: length + :type: jax.Array + + + + .. py:attribute:: width + :type: jax.Array + + + + .. py:attribute:: height + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: stack_fields(field_names: collections.abc.Sequence[str]) -> jax.Array + + Returns a concatenated version of a set of field names for Trajectory. + + + .. py:method:: zeros(shape: collections.abc.Sequence[int]) -> Trajectory + :classmethod: + + Creates a Trajectory containing zeros of the specified shape. + + + .. py:method:: validate() + + Validates shape and type. + + + +.. py:function:: fill_invalid_trajectory(traj: Trajectory) -> Trajectory + + Fills a trajectory with invalid values. + + An invalid value is -1 for numerical fields and False for booleans. + + :param traj: Trajectory to fill. + + :returns: A new trajectory with invalid values. + + diff --git a/_sources/autoapi/waymax/datatypes/observation/index.rst.txt b/_sources/autoapi/waymax/datatypes/observation/index.rst.txt new file mode 100644 index 0000000..b228c5e --- /dev/null +++ b/_sources/autoapi/waymax/datatypes/observation/index.rst.txt @@ -0,0 +1,404 @@ +:py:mod:`waymax.datatypes.observation` +====================================== + +.. py:module:: waymax.datatypes.observation + +.. autoapi-nested-parse:: + + Datastructure and helper functions for Waymax Observation functions. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.observation.ObjectPose2D + waymax.datatypes.observation.Observation + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.observation.transform_trajectory + waymax.datatypes.observation.transform_roadgraph_points + waymax.datatypes.observation.transform_traffic_lights + waymax.datatypes.observation.transform_observation + waymax.datatypes.observation.combine_two_object_pose_2d + waymax.datatypes.observation.global_observation_from_state + waymax.datatypes.observation._tree_expand_and_repeat + waymax.datatypes.observation.observation_from_state + waymax.datatypes.observation.sdc_observation_from_state + + + +.. py:class:: ObjectPose2D + + + Object 2D pose that can be used for coordinate transformation. + + The pose information is stored with two format + (see details at https://en.wikipedia.org/wiki/Pose_(computer_vision)): + 1) the position (original_xy) and orientation (original_yaw); 2) the + transformation matrix (matrix) with the rotation angle explicitly saved as + delta_yaw. + + Note since this is a general data structure, the pose could be using arbitrary + coordinate system. + + Example usage: + Assuming the pose is using C coordinate system, applying matrix[..., i, :, :] + over an object with center (x, y) and yaw in C coordinate system gives its + position in i's local coordinate system. The object's yaw in i's local + coordinate system is yaw + delta_yaw. + This is done by translation of -original_xy followed by counter-clockwise + rotation of original_yaw. + + .. attribute:: original_xy + + (..., num_objects, 2), the coordinates of each object's center + in original coordinate systems. + + .. attribute:: original_yaw + + (..., num_objects), object's yaw in original coordinate + systems. + + .. attribute:: matrix + + (..., num_objects, 3, 3), transformation matrix where `matrix[..., + i, :, :]` is the transformation from original coordinates to object `i` + centric coordinates. + + .. attribute:: delta_yaw + + (..., num_objects), rotation angles used to transform yaw in + original coordinate system to object-center coordinate system. Note this + is the negative of original_yaw. + + .. attribute:: valid + + (..., num_objects), valid or not. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The Array shape. + + .. py:property:: num_objects + :type: int + + The number of objects. + + .. py:property:: rotation_matrix + :type: jax.Array + + 2D rotation matrix. + + .. py:property:: translation_vector + :type: jax.Array + + 2D translation vector. + + .. py:attribute:: original_xy + :type: jax.Array + + + + .. py:attribute:: original_yaw + :type: jax.Array + + + + .. py:attribute:: matrix + :type: jax.Array + + + + .. py:attribute:: delta_yaw + :type: jax.Array + + + + .. py:attribute:: valid + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: from_center_and_yaw(xy: jax.Array, yaw: jax.Array, valid: Optional[jax.Array] = None) -> ObjectPose2D + :classmethod: + + Initializes pose from center location and yaw. + + :param xy: (..., num_objects, 2), 2D coordinates of objects' center in arbitrary + coordinate system. + :param yaw: (..., num_objects), objects' yaw in same coordinate system as xy. + :param valid: (..., num_objects), boolean mask for validity. + + :returns: A ObjectPose2D with shape (..., num_objects). + + + .. py:method:: from_transformation(matrix: jax.Array, delta_yaw: jax.Array, valid: Optional[jax.Array] = None) -> ObjectPose2D + :classmethod: + + Init pose from transformation matrix and delta_yaw. + + :param matrix: (..., num_objects, 3, 3), 2D homogenous transformation matrix. + :param delta_yaw: (..., num_objects), transformation angles used together with + matrix for coordinate transformation (i.e. rotation applied by the + transformation matrix). + :param valid: (..., num_objects), boolean mask for validity. + + :returns: A ObjectPose2D with shape (..., num_objects). + + + .. py:method:: validate() + + Validates shape and type. + + + +.. py:class:: Observation + + + Observation at a single simulation step. + + num_observations: number of objects that will have an observation/view over + other objects/map elements. + + The observation can include a fixed number of history information. Note we + support multi-agent observation by default: for each object (in axis + num_observations), we compute its view of all other objects (in axis + num_objects), roadgraph points, and traffic lights. The coordinates used for + each object (num_observations) are centered at its location defined by pose2d. + + .. attribute:: trajectory + + Time-dependent information, in object-centric coordinates + defined by pose2d of shape (..., num_observations, num_objects, + num_timesteps). + + .. attribute:: is_ego + + Binary mask of shape (..., num_observations, num_objects). It + represents which object in num_objects is the observer itself. + + .. attribute:: pose2d + + Poses for all objects, used for transformation of shape (..., + num_observations). + + .. attribute:: metadata + + Time-independent information of shape (..., num_observations, + num_objects). + + .. attribute:: roadgraph_static_points + + Top-k (k=num_points) nearest static roadgraph + points of shape (..., num_observations, num_points). + + .. attribute:: traffic_lights + + Current state of the traffic lights in the log of shape + (..., num_observations, num_traffic_lights, num_timesteps). + + .. attribute:: sdc_paths + + SDC roadgraph traversal paths. Only valid for SDC agents of shape + (..., num_observations, num_paths, num_points_per_path). + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + (..., num_observations). + + :type: The longest common prefix shape of all attributes + + .. py:property:: batch_dims + :type: tuple[int, Ellipsis] + + Batch dimensions. + + .. py:property:: num_objects + :type: int + + The number of objects. + + .. py:property:: valid + :type: jax.Array + + Whether the observation is valid, (..., num_observations). + + .. py:attribute:: trajectory + :type: waymax.datatypes.object_state.Trajectory + + + + .. py:attribute:: is_ego + :type: jax.Array + + + + .. py:attribute:: pose2d + :type: ObjectPose2D + + + + .. py:attribute:: metadata + :type: waymax.datatypes.object_state.ObjectMetadata + + + + .. py:attribute:: roadgraph_static_points + :type: waymax.datatypes.roadgraph.RoadgraphPoints + + + + .. py:attribute:: traffic_lights + :type: Observation.traffic_lights + + + + .. py:attribute:: sdc_paths + :type: Optional[waymax.datatypes.route.Paths] + + + + .. py:method:: for_obj(idx: int) -> Observation + + Observation from idx-th object's point of view over all objects. + + :param idx: The index of the object. + + :returns: A Observation with shape (..., 1). + + + .. py:method:: validate() + + Validates shape and type. + + + +.. py:function:: transform_trajectory(traj: waymax.datatypes.object_state.Trajectory, pose2d: ObjectPose2D) -> waymax.datatypes.object_state.Trajectory + + Transforms a Trajectory into coordinates specified by pose2d. + + :param traj: A Trajectory with shape (..., num_observations, num_objects, + num_timesteps) in coordinate system same as the one used by + original_xy/yaw in pose2d. + :param pose2d: A ObjectPose2D with shape (..., num_observations). + + :returns: A transformed trajectory in local coordinates per-observation defined by the + pose. + + +.. py:function:: transform_roadgraph_points(roadgraph_points: waymax.datatypes.roadgraph.RoadgraphPoints, pose2d: ObjectPose2D) -> waymax.datatypes.roadgraph.RoadgraphPoints + + Transform a RoadgraphPoints into coordinates specified by pose2d. + + :param roadgraph_points: A RoadgraphPoints with shape (..., num_observations, + num_points). + :param pose2d: A ObjectPose2D with shape (..., num_observations). + + :returns: A transformed RoadgraphPoints in local coordinates per-observation defined + by the pose. + + +.. py:function:: transform_traffic_lights(tls: waymax.datatypes.traffic_lights.TrafficLights, pose2d: ObjectPose2D) -> waymax.datatypes.traffic_lights.TrafficLights + + Transforms a TrafficLightStates into coordinates specified by pose2d. + + :param tls: A TrafficLightStates with shape (..., num_observations, + num_traffic_lights, num_timesteps). + :param pose2d: A ObjectPose2D with shape (..., num_observations). + + :returns: Transformed TrafficLightStates in local coordinates per-observation defined + by the pose. + + +.. py:function:: transform_observation(observation: Observation, pose2d: ObjectPose2D) -> Observation + + Transforms a Observation into coordinates specified by pose2d. + + :param observation: Has shape (..., num_observations) + :param pose2d: Has shape (..., num_observations) + + :returns: Transformed observation in local coordinates per-observation defined by the + pose. + + +.. py:function:: combine_two_object_pose_2d(src_pose: ObjectPose2D, dst_pose: ObjectPose2D) -> ObjectPose2D + + Combines two ObjectPose2D as inverse(src_pose) plus dst_pose. + + Applying transformation using the returned pose is equivalent to applying + transformation first with inverse(src_pose) and then dst_pose. Note as data + transformation is much more expensive than computing the combined pose, it's + more efficient to apply one data transformation with the combined pose instead + of applying multiple transformations with multiple poses. + + :param src_pose: The source pose. + :param dst_pose: The destination/target pose. + + :returns: The combined pose. + + +.. py:function:: global_observation_from_state(state: waymax.datatypes.simulator_state.SimulatorState, obs_num_steps: int = 1, num_obj: int = 1) -> Observation + + Generates observation in global coordinates. + + :param state: Has shape (...,). + :param obs_num_steps: Number of observation steps for trajectories and traffic + lights state. + :param num_obj: Used to tile the global observation for multiple objects. + + :returns: Observation with shape (..., num_obj). Note the same observation in + global coordinates is tiled for num_obj when num_obj is larger than 1. + + +.. py:function:: _tree_expand_and_repeat(tree: Any, repeats: int, axis: int) -> waymax.datatypes.array.PyTree + + +.. py:function:: observation_from_state(state: waymax.datatypes.simulator_state.SimulatorState, obs_num_steps: int = 1, roadgraph_top_k: int = 1000, coordinate_frame: waymax.config.CoordinateFrame = config.CoordinateFrame.GLOBAL) -> Observation + + Constructs Observation from SimulatorState for all agents (jit-able). + + :param state: A SimulatorState, with entirely variable shape (...). + :param obs_num_steps: Number of steps history included in observation. Last + timestep is state.timestep. + :param roadgraph_top_k: Number of topk roadgraph observed by each object. + :param coordinate_frame: Which coordinate frame the returned observation is using. + + :returns: Observation at current timestep from given simulator state, with shape (..., + num_objects). + + +.. py:function:: sdc_observation_from_state(state: waymax.datatypes.simulator_state.SimulatorState, obs_num_steps: int = 1, roadgraph_top_k: int = 1000, coordinate_frame: waymax.config.CoordinateFrame = config.CoordinateFrame.SDC) -> Observation + + Constructs Observation from SimulatorState for SDC only (jit-able). + + :param state: a SimulatorState, with shape (...) + :param obs_num_steps: number of steps history included in observation. Last + timestep is state.timestep. + :param roadgraph_top_k: number of topk roadgraph observed by each object. + :param coordinate_frame: which coordinate frame the returned observation is using. + + :returns: SDC Observation at current timestep from given simulator state, with shape + (..., 1), where the last object dimension is 1 as there is only one SDC. It + is not sequeezed to be consistent with multi-agent cases and compatible for + other utils fnctions. + + diff --git a/_sources/autoapi/waymax/datatypes/operations/index.rst.txt b/_sources/autoapi/waymax/datatypes/operations/index.rst.txt new file mode 100644 index 0000000..dfc1211 --- /dev/null +++ b/_sources/autoapi/waymax/datatypes/operations/index.rst.txt @@ -0,0 +1,200 @@ +:py:mod:`waymax.datatypes.operations` +===================================== + +.. py:module:: waymax.datatypes.operations + +.. autoapi-nested-parse:: + + Common data operations made to work on PyTree data structures. + + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.operations.update_by_mask + waymax.datatypes.operations._vmap_over_batch_dims + waymax.datatypes.operations.dynamic_slice + waymax.datatypes.operations.dynamic_index + waymax.datatypes.operations.update_by_slice_in_dim + waymax.datatypes.operations.compare_all_leaf_nodes + waymax.datatypes.operations.select_by_onehot + waymax.datatypes.operations.make_invalid_data + waymax.datatypes.operations.masked_mean + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.operations.ArrayLike + waymax.datatypes.operations.F + waymax.datatypes.operations.PyTree + waymax.datatypes.operations.Axis + waymax.datatypes.operations.dynamic_update_slice_in_dim + + +.. py:data:: ArrayLike + + + +.. py:data:: F + + + +.. py:data:: PyTree + + + +.. py:data:: Axis + + + +.. py:function:: update_by_mask(inputs: PyTree, updates: PyTree, mask: jax.Array) -> PyTree + + Updates inputs by updates (with same struct) for masked values. + + Uses `jax.tree_util` to update each field as such: + inputs.field = jnp.where(mask, updates.field, inputs.field) + + :param inputs: Represents the data to be updated and returned. + :param updates: Represents the data that is passed in to update all fields. It is + assumed that the `updates` and the `inputs` have the same structure. These + fields must be broadcastable to the same shape as `inputs` after applying + the mask. + :param mask: Mask that represents which elements of the `inputs` array tree to + update with the `updates` field. Shape must be broadcastable to the leaf + nodes of inputs and updates. + + :returns: + + Updated tree array of the same structure as `inputs` which has had all its + fields updated with the corresponding fields in `updates` conditioned on + whether `mask` requests it. + + +.. py:function:: _vmap_over_batch_dims(func: F, batch_element: ArrayLike) -> F + + Apply vmap to a function based on the number of batch dimensions. + + +.. py:function:: dynamic_slice(inputs: PyTree, start_index: ArrayLike, slice_size: int, axis: int = 0) -> PyTree + + Wraps dynamic_slice_in_dim on a PyTree. + + :param inputs: Object where each element in its tree is to be sliced. + :param start_index: Start index of slicing function. + :param slice_size: Size of slice applied to `inputs` starting from `start_index` to + `start_index` + `slice_size`. + :param axis: Axis of each array to perform the slicing in. + + :returns: + + Array tree which has been sliced along an axis which maintains the same + rank as `inputs`. + + +.. py:function:: dynamic_index(inputs: PyTree, index: ArrayLike, axis: int = 0, keepdims: bool = True) -> PyTree + + Wraps dynamic_index_in_dim on a PyTree. + + :param inputs: Object where each element in it is to be indexed. + :param index: Element number to index into each array in the tree. + :param axis: Axis of each array to perform the indexing in. + :param keepdims: Whether or not to keep the same rank as `inputs`. If this is set + to `True` then the return value will be such that `.shape[axis]` == 1. + + :returns: + + Array tree where every element of the tree has been indexed at the same + specified axis. + + +.. py:function:: update_by_slice_in_dim(inputs: PyTree, updates: PyTree, inputs_start_idx: ArrayLike, updates_start_idx: Optional[ArrayLike] = None, slice_size: Optional[int] = None, axis: int = 0) -> PyTree + + Updates a slice in inputs using slices from updates. + + It replaces inputs[inputs_start_idx:inputs_start_idx+slice_size] by + updates[updates_start_idx:updates_start_idx+slice_size] for specified axis + only. + + If updates_start_idx and slice_size are both None, the behavior of this + function will default to dynamic_update_slice_in_dim. + + :param inputs: Represents the data to be updated and returned. + :param updates: Represents the data that is passed in to update all fields. It is + assumed that the `updates` and the `inputs` have the same structure. + :param inputs_start_idx: starting index in inputs. + :param updates_start_idx: starting index in updates. + :param slice_size: the slice size used for update. If slice size is None, uses the + entire array and updates_start_idx will be ignored. + :param axis: Axis of each array to perform the slicing in. + + :returns: A PyTree with same structure as inputs. + + +.. py:data:: dynamic_update_slice_in_dim + + + +.. py:function:: compare_all_leaf_nodes(first: PyTree, second: PyTree, compare_func: Callable[[jax.Array, jax.Array], bool] = jnp.array_equal) -> bool + + Checks if all leaf nodes are consistent via compare_func. + + The default behaviour (with compare_func as jnp.array_equal) is to + compare if two PyTree are equal (i.e. all leaf nodes of array are equal). + One can also use compare_func as jnp.allclose to give some tolerance + for numerical difference. + + :param first: A PyTree for comparison. + :param second: A PyTree for comparison. + :param compare_func: A function comparing nodes between two input arrays. + + :returns: True if inputs PyTrees are consistent by func. + + +.. py:function:: select_by_onehot(data: PyTree, selection: jax.Array, keepdims: bool = False) -> PyTree + + Selects data using a one-hot vector. + + :param data: A pytree with leaf shapes (..., N, ...). + :param selection: A one-hot vector with shape (..., N). + :param keepdims: Whether to leave a (1,) dimension on the selected axis. + + :returns: A pytree with leaf shapes (..., 1) if keepdims=True. + A pytree with leaf shapes (..., ) if keepdims=False. + + +.. py:function:: make_invalid_data(data: jax.Array) -> jax.Array + + Returns a tensor of invalid values with identical shape to data. + + Invalid values are defined as False for booleans, and -1 for numerical values. + + :param data: Tensor to invalidate. + + :returns: A tensor of invalid values of the same shape and dtype as data. + + +.. py:function:: masked_mean(x: jax.Array, valid: jax.Array, axis: Axis = 0, invalid_value: float = -1.0) -> jax.Array + + Calculates the mean of the array removing invalid values. + + :param x: Input to the masked mean function. + :param valid: Boolean array with the same shape as x which indicates which values + should be included in the mean. + :param axis: Axis to reduce along. + :param invalid_value: If there is no valid fields, the value will be replaced by + this invalid value. + + :returns: Array representing the mean of the array of all valid values. + + diff --git a/_sources/autoapi/waymax/datatypes/roadgraph/index.rst.txt b/_sources/autoapi/waymax/datatypes/roadgraph/index.rst.txt new file mode 100644 index 0000000..3ffc1c5 --- /dev/null +++ b/_sources/autoapi/waymax/datatypes/roadgraph/index.rst.txt @@ -0,0 +1,325 @@ +:py:mod:`waymax.datatypes.roadgraph` +==================================== + +.. py:module:: waymax.datatypes.roadgraph + +.. autoapi-nested-parse:: + + Roadgraph based datastructures for Waymax based on WOMD. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.roadgraph.MapElementIds + waymax.datatypes.roadgraph.RoadgraphPoints + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.roadgraph.filter_topk_roadgraph_points + waymax.datatypes.roadgraph.is_road_edge + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.roadgraph.PyTree + + +.. py:data:: PyTree + + + +.. py:class:: MapElementIds + + + Bases: :py:obj:`enum.IntEnum` + + Ids for different map elements to be mapped into a tensor. + + These integers represent the ID of these specific types as defined in: + https://waymo.com/open/data/motion/tfexample. + + .. py:attribute:: LANE_UNDEFINED + :value: 0 + + + + .. py:attribute:: LANE_FREEWAY + :value: 1 + + + + .. py:attribute:: LANE_SURFACE_STREET + :value: 2 + + + + .. py:attribute:: LANE_BIKE_LANE + :value: 3 + + + + .. py:attribute:: ROAD_LINE_UNKNOWN + :value: 5 + + + + .. py:attribute:: ROAD_LINE_BROKEN_SINGLE_WHITE + :value: 6 + + + + .. py:attribute:: ROAD_LINE_SOLID_SINGLE_WHITE + :value: 7 + + + + .. py:attribute:: ROAD_LINE_SOLID_DOUBLE_WHITE + :value: 8 + + + + .. py:attribute:: ROAD_LINE_BROKEN_SINGLE_YELLOW + :value: 9 + + + + .. py:attribute:: ROAD_LINE_BROKEN_DOUBLE_YELLOW + :value: 10 + + + + .. py:attribute:: ROAD_LINE_SOLID_SINGLE_YELLOW + :value: 11 + + + + .. py:attribute:: ROAD_LINE_SOLID_DOUBLE_YELLOW + :value: 12 + + + + .. py:attribute:: ROAD_LINE_PASSING_DOUBLE_YELLOW + :value: 13 + + + + .. py:attribute:: ROAD_EDGE_UNKNOWN + :value: 14 + + + + .. py:attribute:: ROAD_EDGE_BOUNDARY + :value: 15 + + + + .. py:attribute:: ROAD_EDGE_MEDIAN + :value: 16 + + + + .. py:attribute:: STOP_SIGN + :value: 17 + + + + .. py:attribute:: CROSSWALK + :value: 18 + + + + .. py:attribute:: SPEED_BUMP + :value: 19 + + + + .. py:attribute:: UNKNOWN + + + + +.. py:class:: RoadgraphPoints + + + Data structure representing roadgraph points. + + It holds the coordinates of the sampled map data points. Note all attributes + have shape (..., num_points), where num_points is the + number of road graph points. + + .. attribute:: x + + X coordinate of positions of the sampled map data points of dtype + float32. + + .. attribute:: y + + Y coordinate of positions of the sampled map data points of dtype + float32. + + .. attribute:: z + + Z coordinate of positions of the sampled map data points of dtype + float32. + + .. attribute:: dir_x + + X coordinate of a unit direction vector for each map feature sample + point of dtype float32. + + .. attribute:: dir_y + + Y coordinate of a unit direction vector for each map feature sample + point of dtype float32. + + .. attribute:: dir_z + + Z coordinate of a unit direction vector for each map feature sample + point of dtype float32. + + .. attribute:: types + + A unique integer for each combination of map feature type and + properties of dtype int32. See the table in the Value Range column of + website: https://waymo.com/open/data/motion/tfexample. + + .. attribute:: ids + + A unique Integer ID for the vector map feature each sample is from of + dtype int32. + + .. attribute:: valid + + A valid flag for each map sample point of dtype bool. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The Array shape of the RoadgraphPoints. + + .. py:property:: num_points + :type: int + + The number of points included in this RoadgraphPoints per example. + + .. py:property:: xy + :type: jax.Array + + Stacked xy location for all points. + + .. py:property:: xyz + :type: jax.Array + + Stacked xyz location for all points. + + .. py:property:: dir_xy + :type: jax.Array + + Stacked xy direction for all points. + + .. py:property:: dir_xyz + :type: jax.Array + + Stacked xy direction for all points. + + .. py:attribute:: x + :type: jax.Array + + + + .. py:attribute:: y + :type: jax.Array + + + + .. py:attribute:: z + :type: jax.Array + + + + .. py:attribute:: dir_x + :type: jax.Array + + + + .. py:attribute:: dir_y + :type: jax.Array + + + + .. py:attribute:: dir_z + :type: jax.Array + + + + .. py:attribute:: types + :type: jax.Array + + + + .. py:attribute:: ids + :type: jax.Array + + + + .. py:attribute:: valid + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: validate() + + Validates shape and type. + + + +.. py:function:: filter_topk_roadgraph_points(roadgraph: RoadgraphPoints, reference_points: jax.Array, topk: int) -> RoadgraphPoints + + Returns the topk closest roadgraph points to a reference point. + + If `topk` is larger than the number of points, exception will be raised. + + :param roadgraph: Roadgraph information to filter, (..., num_points). + :param reference_points: A tensor of shape (..., 2) - the reference point used to + measure distance. + :param topk: Number of points to keep. + + :returns: + + Roadgraph data structure that has been filtered to only contain the `topk` + closest points to a reference point. + + +.. py:function:: is_road_edge(types: jax.Array) -> jax.Array + + Determines which map elements in a tensor are road edges. + + :param types: An array of integer values with each integer value representing a + unique map type. These integers are based on a schema defined in + https://waymo.com/open/data/motion/tfexample. This is of shape (..., + num_points). + + :returns: A bool array where an element is true if the map element is a road edge. + + diff --git a/_sources/autoapi/waymax/datatypes/route/index.rst.txt b/_sources/autoapi/waymax/datatypes/route/index.rst.txt new file mode 100644 index 0000000..de84592 --- /dev/null +++ b/_sources/autoapi/waymax/datatypes/route/index.rst.txt @@ -0,0 +1,140 @@ +:py:mod:`waymax.datatypes.route` +================================ + +.. py:module:: waymax.datatypes.route + +.. autoapi-nested-parse:: + + Customized route paths data structure for Waymax datatypes. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.route.Paths + + + + +.. py:class:: Paths + + + Data structure for a set of paths represented by roadgraph points. + + A `path` consists of a set of roadgraph points (usually along the lane center) + and represents where an object can legally traverse giving its starting point. + + .. attribute:: x + + Path coordinate x, shape is (..., num_paths, num_points_per_path) and + dtype is float32. + + .. attribute:: y + + Path coordinate y, shape is (..., num_paths, num_points_per_path) and + dtype is float32. + + .. attribute:: z + + Path coordinate z, shape is (..., num_paths, num_points_per_path) and + dtype is float32. + + .. attribute:: ids + + IDs representing which lane the points belong to, shape is (..., + num_paths, num_points_per_path) and dtype is int32. + + .. attribute:: valid + + Validity flag, shape is (..., num_paths, num_points_per_path) and + dtype is bool. + + .. attribute:: arc_length + + Represents the arc length for each point from the starting point + along the path with shape (..., num_paths, num_points_per_path) and dtype + is float32. + + .. attribute:: on_route + + Flag for each path representing whether it is on the road route + corresponding to the logged trajectory, shape is (..., num_paths, 1) and + the dtype is bool. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The Array shape of the routes. + + .. py:property:: num_points_per_path + :type: int + + The number of points included in the paths per example. + + .. py:property:: num_paths + :type: int + + The number of paths included in the example. + + .. py:property:: xy + :type: jax.Array + + Stacked xy location for all points. + + .. py:property:: xyz + :type: jax.Array + + Stacked xyz location for all points. + + .. py:attribute:: x + :type: jax.Array + + + + .. py:attribute:: y + :type: jax.Array + + + + .. py:attribute:: z + :type: jax.Array + + + + .. py:attribute:: ids + :type: jax.Array + + + + .. py:attribute:: valid + :type: jax.Array + + + + .. py:attribute:: arc_length + :type: jax.Array + + + + .. py:attribute:: on_route + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: validate() -> None + + Validates shape and type. + + + diff --git a/_sources/autoapi/waymax/datatypes/simulator_state/index.rst.txt b/_sources/autoapi/waymax/datatypes/simulator_state/index.rst.txt new file mode 100644 index 0000000..b554939 --- /dev/null +++ b/_sources/autoapi/waymax/datatypes/simulator_state/index.rst.txt @@ -0,0 +1,192 @@ +:py:mod:`waymax.datatypes.simulator_state` +========================================== + +.. py:module:: waymax.datatypes.simulator_state + +.. autoapi-nested-parse:: + + All customized data structures for Waymax. + + Note the shape of a data classes here is defined as the most common prefix shape + among all attributes. + The validate function is implemented separately instead of as __post_init__, to + have better support with jax utils. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.simulator_state.SimulatorState + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.simulator_state.update_state_by_log + waymax.datatypes.simulator_state.get_control_mask + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.simulator_state.ArrayLike + waymax.datatypes.simulator_state.PyTree + + +.. py:data:: ArrayLike + + + +.. py:data:: PyTree + + + +.. py:class:: SimulatorState + + + A dataclass holding the simulator state, all data in global coordinates. + + .. attribute:: sim_trajectory + + Simulated trajectory for all objects of shape (..., + num_objects, num_timesteps). The number of timesteps is the same as in the + log, but future trajectory points that have not been simulated will be + marked invalid. + + .. attribute:: log_trajectory + + Logged trajectory for all objects of shape (..., + num_objects, num_timesteps). + + .. attribute:: log_traffic_light + + Logged traffic light information for the entire run + segment of shape (..., num_traffic_lights, num_timesteps). + + .. attribute:: object_metadata + + Metadata for all objects of shape (..., num_objects). + + .. attribute:: timestep + + The current simulation timestep index of shape (...). Note that + sim_trajectory at `timestep` is the last executed step by the simulator. + + .. attribute:: sdc_paths + + Paths for SDC, representing where the SDC can drive of shape + (..., num_paths, num_points_per_path). + + .. attribute:: roadgraph_points + + A optional RoadgraphPoints holding subsampled roadgraph + points of shape (..., num_points). + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + Shape is defined as the most common prefix shape of all attributes. + + .. py:property:: batch_dims + :type: tuple[int, Ellipsis] + + Batch dimensions. + + .. py:property:: num_objects + :type: int + + The number of objects included in this trajectory per example. + + .. py:property:: is_done + :type: bool + + Returns whether the simulation is at the end of the logged history. + + .. py:property:: remaining_timesteps + :type: int + + Returns the number of remaining timesteps in the episode. + + .. py:property:: current_sim_trajectory + :type: waymax.datatypes.object_state.Trajectory + + Returns the trajectory corresponding to the current sim state. + + .. py:property:: current_log_trajectory + :type: waymax.datatypes.object_state.Trajectory + + Returns the trajectory corresponding to the current sim state. + + .. py:attribute:: sim_trajectory + :type: waymax.datatypes.object_state.Trajectory + + + + .. py:attribute:: log_trajectory + :type: waymax.datatypes.object_state.Trajectory + + + + .. py:attribute:: log_traffic_light + :type: waymax.datatypes.traffic_lights.TrafficLights + + + + .. py:attribute:: object_metadata + :type: waymax.datatypes.object_state.ObjectMetadata + + + + .. py:attribute:: timestep + :type: jax.typing.ArrayLike + + + + .. py:attribute:: sdc_paths + :type: Optional[waymax.datatypes.route.Paths] + + + + .. py:attribute:: roadgraph_points + :type: Optional[waymax.datatypes.roadgraph.RoadgraphPoints] + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: validate() + + Validates shape and type. + + + +.. py:function:: update_state_by_log(state: SimulatorState, num_steps: int) -> SimulatorState + + Advances SimulatorState by num_steps using logged data. + + +.. py:function:: get_control_mask(metadata: waymax.datatypes.object_state.ObjectMetadata, obj_type: waymax.config.ObjectType) -> jax.Array + + Returns binary mask for selected object type. + + :param metadata: An ObjectMetadata, having shape (..., num_objects). + :param obj_type: Represents which type of objects should be selected. + + :returns: A binary mask with shape (..., num_objects). + + diff --git a/_sources/autoapi/waymax/datatypes/traffic_lights/index.rst.txt b/_sources/autoapi/waymax/datatypes/traffic_lights/index.rst.txt new file mode 100644 index 0000000..a5505b7 --- /dev/null +++ b/_sources/autoapi/waymax/datatypes/traffic_lights/index.rst.txt @@ -0,0 +1,174 @@ +:py:mod:`waymax.datatypes.traffic_lights` +========================================= + +.. py:module:: waymax.datatypes.traffic_lights + +.. autoapi-nested-parse:: + + Custom data structures for traffic light signals in Wayamx. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.datatypes.traffic_lights.TrafficLightStates + waymax.datatypes.traffic_lights.TrafficLights + + + + +.. py:class:: TrafficLightStates + + + Bases: :py:obj:`enum.IntEnum` + + Represents the integer values for all Traffic Light State values. + + .. py:attribute:: UNKNOWN + :value: 0 + + + + .. py:attribute:: ARROW_STOP + :value: 1 + + + + .. py:attribute:: ARROW_CAUTION + :value: 2 + + + + .. py:attribute:: ARROW_GO + :value: 3 + + + + .. py:attribute:: STOP + :value: 4 + + + + .. py:attribute:: CAUTION + :value: 5 + + + + .. py:attribute:: GO + :value: 6 + + + + .. py:attribute:: FLASHING_STOP + :value: 7 + + + + .. py:attribute:: FLASHING_CAUTION + :value: 8 + + + + +.. py:class:: TrafficLights + + + Data structure representing the dynamic traffic light state information. + + All attributes have shape (..., num_traffic_lights, num_timesteps). + + .. attribute:: x + + The X coordinate of the stop light position of dtype float32. + + .. attribute:: y + + The Y coordinate of the stop light position of dtype float32. + + .. attribute:: z + + The Z coordinate of the stop light position. This point is at the + beginning of the lane segment controlled by the traffic signal of dtype + float32. + + .. attribute:: state + + The state of each traffic light at each time step of dtype int32. See + above enum for integer values of all traffic lights states. + + .. attribute:: lane_ids + + which lane it controls. + + .. attribute:: valid + + A valid flag for all elements of features traffic_light.XX. If set to + True, the element is populated with valid data. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + The tensor shape of the traffic lights. + + .. py:property:: num_traffic_lights + :type: int + + The number of points included in this traffic light per example. + + .. py:property:: num_timesteps + :type: int + + The number of timesteps included in this traffic light per example. + + .. py:property:: xy + :type: jax.Array + + Stacked xy location for all points. + + .. py:attribute:: x + :type: jax.Array + + + + .. py:attribute:: y + :type: jax.Array + + + + .. py:attribute:: z + :type: jax.Array + + + + .. py:attribute:: state + :type: jax.Array + + + + .. py:attribute:: lane_ids + :type: jax.Array + + + + .. py:attribute:: valid + :type: jax.Array + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + .. py:method:: validate() + + Validates shape and type. + + + diff --git a/_sources/autoapi/waymax/dynamics/abstract_dynamics/index.rst.txt b/_sources/autoapi/waymax/dynamics/abstract_dynamics/index.rst.txt new file mode 100644 index 0000000..cd96fcd --- /dev/null +++ b/_sources/autoapi/waymax/dynamics/abstract_dynamics/index.rst.txt @@ -0,0 +1,157 @@ +:py:mod:`waymax.dynamics.abstract_dynamics` +=========================================== + +.. py:module:: waymax.dynamics.abstract_dynamics + +.. autoapi-nested-parse:: + + Library for different object dynamics model for Waymax. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.dynamics.abstract_dynamics.DynamicsModel + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.dynamics.abstract_dynamics.apply_trajectory_update_to_state + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.dynamics.abstract_dynamics.CONTROLLABLE_FIELDS + + +.. py:data:: CONTROLLABLE_FIELDS + :value: ['x', 'y', 'yaw', 'vel_x', 'vel_y'] + + + +.. py:class:: DynamicsModel + + + Bases: :py:obj:`abc.ABC` + + Object dynamics base class. + + .. py:method:: action_spec() -> dm_env.specs.BoundedArray + :abstractmethod: + + Returns the dm_env bounded action spec. + + + .. py:method:: forward(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory, reference_trajectory: waymax.datatypes.Trajectory, is_controlled: jax.Array, timestep: int, allow_object_injection: bool = False) -> waymax.datatypes.Trajectory + + Updates a simulated trajectory to the next timestep given an update. + + :param action: Actions to be applied to the trajectory to produce updates at the + next timestep of shape (..., num_objects). + :param trajectory: Simulated trajectory up to the current timestep. This + trajectory will be updated by this function updated with the trajectory + update. It is expected that this trajectory will have been updated up to + `timestep`. This is of shape: (..., num_objects, num_timesteps). + :param reference_trajectory: Default trajectory for all objects over the entire + run segment. Certain fields such as valid are optionally taken from this + trajectory. This is of shape: (..., num_objects, num_timesteps). + :param is_controlled: Boolean array specifying which objects are to be controlled + by the trajectory update of shape (..., num_objects). + :param timestep: Timestep of the current simulation. + :param allow_object_injection: Whether to allow new objects to enter the scene. + If this is set to False, all objects that are not valid at the current + timestep will not be valid at the next timestep and vice versa. + + :returns: + + Updated trajectory given update from a dynamics model at `timestep` + 1 of + shape (..., num_objects, num_timesteps). + + + .. py:method:: compute_update(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory) -> waymax.datatypes.TrajectoryUpdate + :abstractmethod: + + Applies actions to current trajectory to produce next timestep update. + + Only the fields updated as part of the `TrajectoryUpdate` are allowed to be + updated by the dynamics models. All other fields in `Trajectory` are to be + updated by the simulator. + + :param action: Actions to be applied to the trajectory to produce updates at the + next timestep of shape (..., num_objects, dim). + :param trajectory: Current trajectory for all agents being controlled, of shape + (..., num_objects, num_timesteps=1). The actions will be applied to this + trajectory to produce the update. + + :returns: + + Updated fields of the trajectory after applying the actions for the given + dynamics model of shape (..., num_objects). + + + .. py:method:: inverse(trajectory: waymax.datatypes.Trajectory, metadata: waymax.datatypes.ObjectMetadata, timestep: int) -> waymax.datatypes.Action + :abstractmethod: + + Computes actions converting traj[timestep] to traj[timestep+1]. + + :param trajectory: Full trajectory to compute the inverse actions from of shape + (..., num_objects, num_timesteps). This trajectory is for the entire + simulation so that dynamics models can use optimization techniques to + find the best fitting actions. + :param metadata: Metadata describing the types of all objects in the scene, of + shape (..., num_objects). + :param timestep: Current timestep of the simulation. + + :returns: + + Action which will take a set of objects from trajectory[timestep] to + trajectory[timestep + 1] of shape (..., num_objects, dim). + + + +.. py:function:: apply_trajectory_update_to_state(trajectory_update: waymax.datatypes.TrajectoryUpdate, sim_trajectory: waymax.datatypes.Trajectory, reference_trajectory: waymax.datatypes.Trajectory, is_controlled: jax.Array, timestep: int, allow_object_injection: bool = False) -> waymax.datatypes.Trajectory + + Applies a TrajectoryUpdate to the sim trajectory at the next timestep. + + When applying a dynamics update, the trajectory will be updated with the + most recent updates in the trajectory for controlled objects after a dynamics + update. Fields that are not part of the trajectory update (such as length, + width, height, valid, etc.) may not be updated in this function. + + For objects not in is_controlled, reference_trajectory is used. + For objects in is_controlled, but not valid in trajectory_update, fall back to + constant speed behaviour. + + :param trajectory_update: Updated trajectory fields for all objects after the + dynamics update of shape (..., num_objects, num_timesteps=1). + :param sim_trajectory: Simulated trajectory up to the current timestep. This + trajectory will be modified using the trajectory_update. It is expected + that this trajectory will have been updated up to `timestep`. This is of + shape (..., num_objects, num_timesteps). + :param reference_trajectory: Default trajectory for all objects over the entire run + segment. Certain fields such as valid are optionally taken from this + trajectory. This is of shape: (..., num_objects, num_timesteps). + :param is_controlled: Boolean array specifying which objects are to be controlled + by the trajectory update of shape (..., num_objects). + :param timestep: Timestep of the current simulation. + :param allow_object_injection: Whether to allow new objects to enter the scene. If + this is set to False, all objects that are not valid at the current + timestep will not be valid at the next timestep and visa versa. + + :returns: Updated trajectory given update from a dynamics model at `timestep` + 1. + + diff --git a/_sources/autoapi/waymax/dynamics/bicycle_model/index.rst.txt b/_sources/autoapi/waymax/dynamics/bicycle_model/index.rst.txt new file mode 100644 index 0000000..7558ece --- /dev/null +++ b/_sources/autoapi/waymax/dynamics/bicycle_model/index.rst.txt @@ -0,0 +1,145 @@ +:py:mod:`waymax.dynamics.bicycle_model` +======================================= + +.. py:module:: waymax.dynamics.bicycle_model + +.. autoapi-nested-parse:: + + Implementation of the bicycle (acceleration, steering) dynamics model. + + The bicycle model uses acceleration and curvature as the action space, with + the max/min bounds on acceleration and curvature tuned to minimize error + on the Waymo Open Motion dataset. Unlike the Ackermann steering model, + this dynamics model has an analytical inverse that can be used to compute + expert actions from logged behavior. + + + This action space always uses the [-1.0, 1.0] as the range for acceleration + and steering commands to be consistent with other RL training pipeline since + many algorithms' hyperparameters are tuned based on this assumption. The actual + acceleration and steering command range can still be specified by `max_accel` + and `max_steering` in the class definition function. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.dynamics.bicycle_model.InvertibleBicycleModel + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.dynamics.bicycle_model.compute_inverse + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.dynamics.bicycle_model.DynamicsModel + waymax.dynamics.bicycle_model._SPEED_LIMIT + + +.. py:data:: DynamicsModel + + + +.. py:data:: _SPEED_LIMIT + :value: 0.6 + + + +.. py:function:: compute_inverse(traj: waymax.datatypes.Trajectory, timestep: jax.typing.ArrayLike, dt: float = 0.1, estimate_yaw_with_velocity: bool = True) -> waymax.datatypes.Action + + Runs inverse dynamics model to infer actions for specified timestep. + + Inverse dynamics: + accel = (new_vel - vel) / dt + steering = (new_yaw - yaw) / (speed * dt + 1/2 * accel * dt ** 2) + + :param traj: A Trajectory used to infer actions of shape (..., num_objects, + num_timesteps). + :param timestep: Index of time for actions. + :param dt: The time step length used in the simulator. + :param estimate_yaw_with_velocity: Whether to use the yaw recorded in `traj` for + estimating the inverse action or use the yaw estimated from velocities. It + is recommended to set this to True, as using the estimated yaw is + generally less noisy than using the yaw directly recorded in the + trajectory. + + :returns: + + An Action that converts traj[timestep] to traj[timestep+1] of shape + (..., num_objects, dim=2). + + +.. py:class:: InvertibleBicycleModel(dt: float = 0.1, max_accel: float = 6.0, max_steering: float = 0.3, normalize_actions: bool = False) + + + Bases: :py:obj:`DynamicsModel` + + Dynamics model using acceleration and steering curvature for control. + + .. py:method:: action_spec() -> dm_env.specs.BoundedArray + + Action spec for the acceleration steering continuous action space. + + + .. py:method:: _clip_values(action_array: jax.Array) -> jax.Array + + Clip action values to be within the allowable ranges. + + + .. py:method:: compute_update(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory) -> waymax.datatypes.TrajectoryUpdate + + Computes the pose and velocity updates at timestep. + + Forward dynamics: + new_x = x + vel_x * t + 1/2 * accel * cos(yaw) * t ** 2 + new_y = y + vel_y * t + 1/2 * accel * sin(yaw) * t ** 2 + new_yaw = yaw + steering * (speed * t + 1/2 * accel * t ** 2) + new_vel = vel + accel * t + + :param action: Actions of shape (..., num_objects) containing acceleration and + steering controls. + :param trajectory: Trajectory to be updated. Has shape of (..., num_objects, + num_timesteps=1). + + :returns: + + The trajectory update for timestep of shape + (..., num_objects, num_timesteps=1). + + + .. py:method:: inverse(trajectory: waymax.datatypes.Trajectory, metadata: waymax.datatypes.ObjectMetadata, timestep: int) -> waymax.datatypes.Action + + Runs inverse dynamics model to infer actions for specified timestep. + + Inverse dynamics: + accel = (new_vel - vel) / dt + steering = (new_yaw - yaw) / (speed * dt + 1/2 * accel * dt ** 2) + + :param trajectory: A Trajectory used to infer actions (..., num_objects, + num_timesteps), + :param metadata: Object metadata for the trajectory of shape (..., num_objects). + :param timestep: Index of time for actions. + + :returns: + + An Action that converts traj[timestep] to traj[timestep+1] of shape + (..., num_objects, dim=2). + + + diff --git a/_sources/autoapi/waymax/dynamics/delta/index.rst.txt b/_sources/autoapi/waymax/dynamics/delta/index.rst.txt new file mode 100644 index 0000000..fd77997 --- /dev/null +++ b/_sources/autoapi/waymax/dynamics/delta/index.rst.txt @@ -0,0 +1,122 @@ +:py:mod:`waymax.dynamics.delta` +=============================== + +.. py:module:: waymax.dynamics.delta + +.. autoapi-nested-parse:: + + Library for different delta action space object dynamics models for Waymax. + + Delta dynamics modeled can be applied in global coordinates or in the local + (object) coordinate frame. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.dynamics.delta.DeltaGlobal + waymax.dynamics.delta.DeltaLocal + + + + +.. py:class:: DeltaGlobal(dt: float = 0.1, max_dx: float = 6.0, max_dy: float = 6.0, max_dyaw: float = jnp.pi) + + + Bases: :py:obj:`waymax.dynamics.abstract_dynamics.DynamicsModel` + + Dynamics model operating in global coordinates. + + Deltas (displacements) are computed in the global coordinate frame, + regardless of the orientation of the objects. + + .. py:method:: action_spec() -> dm_env.specs.BoundedArray + + Action spec for the delta global action space. + + + .. py:method:: compute_update(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory) -> waymax.datatypes.TrajectoryUpdate + + Computes the pose and velocity updates at timestep. + + :param action: Actions to take. Has shape (..., num_objects). + :param trajectory: Trajectory to be updated. Has shape of (..., num_objects, + num_timesteps=1). + + :returns: + + The trajectory update for timestep of shape + (..., num_objects, num_timesteps=1). + + + .. py:method:: _clip_values(action: jax.Array) -> jax.Array + + Clips action values to the action spec. + + :param action: A tensor of shape (..., 3) containing x, y, and yaw values. + + :returns: A tensor of shape (..., 3) containing action values clipped to the + minimum/maximum bounds. + + + .. py:method:: inverse(trajectory: waymax.datatypes.Trajectory, metadata: waymax.datatypes.ObjectMetadata, timestep: int) -> waymax.datatypes.Action + + Runs inverse dynamics model to infer actions for specified timestep. + + :param trajectory: A Trajectory used to infer actions of shape (..., num_objects, + num_timesteps). + :param metadata: Object metadata for the trajectory of shape (..., num_objects). + :param timestep: Index of time for actions. + + :returns: + + An Action that converts traj[timestep] to traj[timestep+1] of shape + (..., num_objects, dim=2). + + + +.. py:class:: DeltaLocal(dt: float = 0.1, max_dx: float = 6.0, max_dy: float = 6.0, max_dyaw: float = jnp.pi) + + + Bases: :py:obj:`DeltaGlobal` + + Dynamics model operating in global coordinates. + + Actions are defined as (dx, dy, dyaw) in local coordinates. + + .. py:method:: compute_update(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory) -> waymax.datatypes.TrajectoryUpdate + + Converts to global actions and calls DeltaGlobal.compute_update. + + :param action: Actions to take. Has shape (..., num_objects). + :param trajectory: Trajectory to be updated. Has shape of (..., num_objects, + num_timesteps=1). + + :returns: + + The trajectory update for timestep of shape + (..., num_objects, num_timesteps=1). + + + .. py:method:: inverse(trajectory: waymax.datatypes.Trajectory, metadata: waymax.datatypes.ObjectMetadata, timestep: int) -> waymax.datatypes.Action + + Calls DeltaGlobal's inverse and converts to local coordinates. + + :param trajectory: A Trajectory used to infer actions of shape (..., num_objects, + num_timesteps=1). + :param metadata: Object metadata for the trajectory of shape (..., num_objects). + :param timestep: index of time for actions. + + :returns: + + An Action that converts traj[timestep] to traj[timestep+1] of shape + (..., num_objects, dim=2). + + + diff --git a/_sources/autoapi/waymax/dynamics/discretizer/index.rst.txt b/_sources/autoapi/waymax/dynamics/discretizer/index.rst.txt new file mode 100644 index 0000000..a03662a --- /dev/null +++ b/_sources/autoapi/waymax/dynamics/discretizer/index.rst.txt @@ -0,0 +1,147 @@ +:py:mod:`waymax.dynamics.discretizer` +===================================== + +.. py:module:: waymax.dynamics.discretizer + +.. autoapi-nested-parse:: + + Library for discretizing continuous values and discretizing wrappers. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.dynamics.discretizer.Discretizer + waymax.dynamics.discretizer.DiscreteActionSpaceWrapper + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.dynamics.discretizer.inverse_action_by_search + + + +.. py:class:: Discretizer(min_value: jax.Array, max_value: jax.Array, bins: jax.Array) + + + Discretizes continuous values into a 1-dimensional array. + + The total number of discrete actions is equal to the product of (all + bins + 1). We add +1 to be inclusive of boundaries of the min and max values. + If the continuous value has a shape of (..., 3), and 3 bins are used + with bin sizes [2, 3, 4], then there will be a total of 60 discrete actions + (3 * 4 * 5). + + .. py:method:: discretize(values: jax.Array) -> jax.Array + + Discretizes a continuous batched n-d vector of values to 1d indices. + + :param values: Vector of continuous values of shape (..., num_vector_elements) to + discretize. + + :returns: + + Discretized values in a tensor of shape (..., 1) with maximum + value self._max_discrete_idx. + + + .. py:method:: make_continuous(indices_1d: jax.Array) -> jax.Array + + Takes a discretized matrix and converts it back to continuous values. + + :param indices_1d: Discrete matrix of shape (..., 1) to convert back to + continuous matrices of shape (..., num_vector_elements). + + :returns: + + Continuous values of shape (..., num_vector_elements) corresponding to the + value discretized by `indices_1d`. + + + +.. py:class:: DiscreteActionSpaceWrapper(dynamics_model: waymax.dynamics.abstract_dynamics.DynamicsModel, bins: jax.Array, bin_inverse: bool = True) + + + Bases: :py:obj:`waymax.dynamics.abstract_dynamics.DynamicsModel` + + Discrete action version of any dynamics model. + + .. py:method:: action_spec() -> dm_env.specs.BoundedArray + + Action spec for discrete dynamics model. + + + .. py:method:: compute_update(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory) -> waymax.datatypes.TrajectoryUpdate + + Computes the pose and velocity updates. + + This function converts the discrete action into a continuous action and + then runs the underlying continuous action space. + + :param action: Actions to take. Has shape (..., num_objects). + :param trajectory: Trajectory to be updated. Has shape of (..., num_objects, + num_timesteps=1). + + :returns: + + The trajectory update for timestep of shape + (..., num_objects, num_timesteps=1). + + + .. py:method:: inverse(trajectory: waymax.datatypes.Trajectory, metadata: waymax.datatypes.ObjectMetadata, timestep: int) -> waymax.datatypes.Action + + Calculates the inverse to generate the best fit discrete action. + + If `self._bin_inverse` is False, a discrete optimization algorithm is + performed to produce the best fit action by searching over the discrete + action space. If `self._bin_inverse` is True, we discretize the continuous + inverse. This might be noisier than `self._bin_inverse` = False. + + :param trajectory: A Trajectory used to infer actions of shape (..., num_objects, + num_timesteps=1). + :param metadata: Object metadata for the trajectory of shape (..., num_objects). + :param timestep: index of time for actions. + + :returns: + + An Action that converts traj[timestep] to traj[timestep+1] of shape + (..., num_objects, dim=2). + + + +.. py:function:: inverse_action_by_search(traj: waymax.datatypes.Trajectory, metadata: waymax.datatypes.ObjectMetadata, timestep: int, continuous_dynamics: waymax.dynamics.abstract_dynamics.DynamicsModel, all_continuous_actions: jax.Array, all_discrete_actions: jax.Array, discrete_invalid_placeholder: jax.Array) -> waymax.datatypes.Action + + Compute the inverse action that best fits a trajectory. + + This inverse method uses a discrete optimization algorithm to produce the + best fit action by searching over the discrete action space. + + :param traj: A Trajectory used to infer actions of shape (..., num_objects, + num_timesteps=1). + :param metadata: Object metadata for the trajectory of shape (..., num_objects). + :param timestep: index of time for actions. + :param continuous_dynamics: A continuous-space dynamics model. + :param all_continuous_actions: A tensor containing of all possible continuous + actions of shape (all_actions, dim_action). Each continuous value + corresponds to one bin of the discretized space. + :param all_discrete_actions: A tensor containing the indices of all possible + discrete actions of shape (all_actions, 1) + :param discrete_invalid_placeholder: The default action value of shape (..., 1) + used as a placeholder for actions that are invalid. + + :returns: + + An Action that converts traj[timestep] to traj[timestep+1] of shape + (..., num_objects, dim=2). + + diff --git a/_sources/autoapi/waymax/dynamics/index.rst.txt b/_sources/autoapi/waymax/dynamics/index.rst.txt new file mode 100644 index 0000000..7bd5ac0 --- /dev/null +++ b/_sources/autoapi/waymax/dynamics/index.rst.txt @@ -0,0 +1,402 @@ +:py:mod:`waymax.dynamics` +========================= + +.. py:module:: waymax.dynamics + +.. autoapi-nested-parse:: + + Implementations of vehicle dynamics models. + + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + abstract_dynamics/index.rst + bicycle_model/index.rst + delta/index.rst + discretizer/index.rst + state_dynamics/index.rst + + +Package Contents +---------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.dynamics.DynamicsModel + waymax.dynamics.InvertibleBicycleModel + waymax.dynamics.DeltaGlobal + waymax.dynamics.DeltaLocal + waymax.dynamics.DiscreteActionSpaceWrapper + waymax.dynamics.Discretizer + waymax.dynamics.StateDynamics + + + + +.. py:class:: DynamicsModel + + + Bases: :py:obj:`abc.ABC` + + Object dynamics base class. + + .. py:method:: action_spec() -> dm_env.specs.BoundedArray + :abstractmethod: + + Returns the dm_env bounded action spec. + + + .. py:method:: forward(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory, reference_trajectory: waymax.datatypes.Trajectory, is_controlled: jax.Array, timestep: int, allow_object_injection: bool = False) -> waymax.datatypes.Trajectory + + Updates a simulated trajectory to the next timestep given an update. + + :param action: Actions to be applied to the trajectory to produce updates at the + next timestep of shape (..., num_objects). + :param trajectory: Simulated trajectory up to the current timestep. This + trajectory will be updated by this function updated with the trajectory + update. It is expected that this trajectory will have been updated up to + `timestep`. This is of shape: (..., num_objects, num_timesteps). + :param reference_trajectory: Default trajectory for all objects over the entire + run segment. Certain fields such as valid are optionally taken from this + trajectory. This is of shape: (..., num_objects, num_timesteps). + :param is_controlled: Boolean array specifying which objects are to be controlled + by the trajectory update of shape (..., num_objects). + :param timestep: Timestep of the current simulation. + :param allow_object_injection: Whether to allow new objects to enter the scene. + If this is set to False, all objects that are not valid at the current + timestep will not be valid at the next timestep and vice versa. + + :returns: + + Updated trajectory given update from a dynamics model at `timestep` + 1 of + shape (..., num_objects, num_timesteps). + + + .. py:method:: compute_update(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory) -> waymax.datatypes.TrajectoryUpdate + :abstractmethod: + + Applies actions to current trajectory to produce next timestep update. + + Only the fields updated as part of the `TrajectoryUpdate` are allowed to be + updated by the dynamics models. All other fields in `Trajectory` are to be + updated by the simulator. + + :param action: Actions to be applied to the trajectory to produce updates at the + next timestep of shape (..., num_objects, dim). + :param trajectory: Current trajectory for all agents being controlled, of shape + (..., num_objects, num_timesteps=1). The actions will be applied to this + trajectory to produce the update. + + :returns: + + Updated fields of the trajectory after applying the actions for the given + dynamics model of shape (..., num_objects). + + + .. py:method:: inverse(trajectory: waymax.datatypes.Trajectory, metadata: waymax.datatypes.ObjectMetadata, timestep: int) -> waymax.datatypes.Action + :abstractmethod: + + Computes actions converting traj[timestep] to traj[timestep+1]. + + :param trajectory: Full trajectory to compute the inverse actions from of shape + (..., num_objects, num_timesteps). This trajectory is for the entire + simulation so that dynamics models can use optimization techniques to + find the best fitting actions. + :param metadata: Metadata describing the types of all objects in the scene, of + shape (..., num_objects). + :param timestep: Current timestep of the simulation. + + :returns: + + Action which will take a set of objects from trajectory[timestep] to + trajectory[timestep + 1] of shape (..., num_objects, dim). + + + +.. py:class:: InvertibleBicycleModel(dt: float = 0.1, max_accel: float = 6.0, max_steering: float = 0.3, normalize_actions: bool = False) + + + Bases: :py:obj:`DynamicsModel` + + Dynamics model using acceleration and steering curvature for control. + + .. py:method:: action_spec() -> dm_env.specs.BoundedArray + + Action spec for the acceleration steering continuous action space. + + + .. py:method:: _clip_values(action_array: jax.Array) -> jax.Array + + Clip action values to be within the allowable ranges. + + + .. py:method:: compute_update(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory) -> waymax.datatypes.TrajectoryUpdate + + Computes the pose and velocity updates at timestep. + + Forward dynamics: + new_x = x + vel_x * t + 1/2 * accel * cos(yaw) * t ** 2 + new_y = y + vel_y * t + 1/2 * accel * sin(yaw) * t ** 2 + new_yaw = yaw + steering * (speed * t + 1/2 * accel * t ** 2) + new_vel = vel + accel * t + + :param action: Actions of shape (..., num_objects) containing acceleration and + steering controls. + :param trajectory: Trajectory to be updated. Has shape of (..., num_objects, + num_timesteps=1). + + :returns: + + The trajectory update for timestep of shape + (..., num_objects, num_timesteps=1). + + + .. py:method:: inverse(trajectory: waymax.datatypes.Trajectory, metadata: waymax.datatypes.ObjectMetadata, timestep: int) -> waymax.datatypes.Action + + Runs inverse dynamics model to infer actions for specified timestep. + + Inverse dynamics: + accel = (new_vel - vel) / dt + steering = (new_yaw - yaw) / (speed * dt + 1/2 * accel * dt ** 2) + + :param trajectory: A Trajectory used to infer actions (..., num_objects, + num_timesteps), + :param metadata: Object metadata for the trajectory of shape (..., num_objects). + :param timestep: Index of time for actions. + + :returns: + + An Action that converts traj[timestep] to traj[timestep+1] of shape + (..., num_objects, dim=2). + + + +.. py:class:: DeltaGlobal(dt: float = 0.1, max_dx: float = 6.0, max_dy: float = 6.0, max_dyaw: float = jnp.pi) + + + Bases: :py:obj:`waymax.dynamics.abstract_dynamics.DynamicsModel` + + Dynamics model operating in global coordinates. + + Deltas (displacements) are computed in the global coordinate frame, + regardless of the orientation of the objects. + + .. py:method:: action_spec() -> dm_env.specs.BoundedArray + + Action spec for the delta global action space. + + + .. py:method:: compute_update(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory) -> waymax.datatypes.TrajectoryUpdate + + Computes the pose and velocity updates at timestep. + + :param action: Actions to take. Has shape (..., num_objects). + :param trajectory: Trajectory to be updated. Has shape of (..., num_objects, + num_timesteps=1). + + :returns: + + The trajectory update for timestep of shape + (..., num_objects, num_timesteps=1). + + + .. py:method:: _clip_values(action: jax.Array) -> jax.Array + + Clips action values to the action spec. + + :param action: A tensor of shape (..., 3) containing x, y, and yaw values. + + :returns: A tensor of shape (..., 3) containing action values clipped to the + minimum/maximum bounds. + + + .. py:method:: inverse(trajectory: waymax.datatypes.Trajectory, metadata: waymax.datatypes.ObjectMetadata, timestep: int) -> waymax.datatypes.Action + + Runs inverse dynamics model to infer actions for specified timestep. + + :param trajectory: A Trajectory used to infer actions of shape (..., num_objects, + num_timesteps). + :param metadata: Object metadata for the trajectory of shape (..., num_objects). + :param timestep: Index of time for actions. + + :returns: + + An Action that converts traj[timestep] to traj[timestep+1] of shape + (..., num_objects, dim=2). + + + +.. py:class:: DeltaLocal(dt: float = 0.1, max_dx: float = 6.0, max_dy: float = 6.0, max_dyaw: float = jnp.pi) + + + Bases: :py:obj:`DeltaGlobal` + + Dynamics model operating in global coordinates. + + Actions are defined as (dx, dy, dyaw) in local coordinates. + + .. py:method:: compute_update(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory) -> waymax.datatypes.TrajectoryUpdate + + Converts to global actions and calls DeltaGlobal.compute_update. + + :param action: Actions to take. Has shape (..., num_objects). + :param trajectory: Trajectory to be updated. Has shape of (..., num_objects, + num_timesteps=1). + + :returns: + + The trajectory update for timestep of shape + (..., num_objects, num_timesteps=1). + + + .. py:method:: inverse(trajectory: waymax.datatypes.Trajectory, metadata: waymax.datatypes.ObjectMetadata, timestep: int) -> waymax.datatypes.Action + + Calls DeltaGlobal's inverse and converts to local coordinates. + + :param trajectory: A Trajectory used to infer actions of shape (..., num_objects, + num_timesteps=1). + :param metadata: Object metadata for the trajectory of shape (..., num_objects). + :param timestep: index of time for actions. + + :returns: + + An Action that converts traj[timestep] to traj[timestep+1] of shape + (..., num_objects, dim=2). + + + +.. py:class:: DiscreteActionSpaceWrapper(dynamics_model: waymax.dynamics.abstract_dynamics.DynamicsModel, bins: jax.Array, bin_inverse: bool = True) + + + Bases: :py:obj:`waymax.dynamics.abstract_dynamics.DynamicsModel` + + Discrete action version of any dynamics model. + + .. py:method:: action_spec() -> dm_env.specs.BoundedArray + + Action spec for discrete dynamics model. + + + .. py:method:: compute_update(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory) -> waymax.datatypes.TrajectoryUpdate + + Computes the pose and velocity updates. + + This function converts the discrete action into a continuous action and + then runs the underlying continuous action space. + + :param action: Actions to take. Has shape (..., num_objects). + :param trajectory: Trajectory to be updated. Has shape of (..., num_objects, + num_timesteps=1). + + :returns: + + The trajectory update for timestep of shape + (..., num_objects, num_timesteps=1). + + + .. py:method:: inverse(trajectory: waymax.datatypes.Trajectory, metadata: waymax.datatypes.ObjectMetadata, timestep: int) -> waymax.datatypes.Action + + Calculates the inverse to generate the best fit discrete action. + + If `self._bin_inverse` is False, a discrete optimization algorithm is + performed to produce the best fit action by searching over the discrete + action space. If `self._bin_inverse` is True, we discretize the continuous + inverse. This might be noisier than `self._bin_inverse` = False. + + :param trajectory: A Trajectory used to infer actions of shape (..., num_objects, + num_timesteps=1). + :param metadata: Object metadata for the trajectory of shape (..., num_objects). + :param timestep: index of time for actions. + + :returns: + + An Action that converts traj[timestep] to traj[timestep+1] of shape + (..., num_objects, dim=2). + + + +.. py:class:: Discretizer(min_value: jax.Array, max_value: jax.Array, bins: jax.Array) + + + Discretizes continuous values into a 1-dimensional array. + + The total number of discrete actions is equal to the product of (all + bins + 1). We add +1 to be inclusive of boundaries of the min and max values. + If the continuous value has a shape of (..., 3), and 3 bins are used + with bin sizes [2, 3, 4], then there will be a total of 60 discrete actions + (3 * 4 * 5). + + .. py:method:: discretize(values: jax.Array) -> jax.Array + + Discretizes a continuous batched n-d vector of values to 1d indices. + + :param values: Vector of continuous values of shape (..., num_vector_elements) to + discretize. + + :returns: + + Discretized values in a tensor of shape (..., 1) with maximum + value self._max_discrete_idx. + + + .. py:method:: make_continuous(indices_1d: jax.Array) -> jax.Array + + Takes a discretized matrix and converts it back to continuous values. + + :param indices_1d: Discrete matrix of shape (..., 1) to convert back to + continuous matrices of shape (..., num_vector_elements). + + :returns: + + Continuous values of shape (..., num_vector_elements) corresponding to the + value discretized by `indices_1d`. + + + +.. py:class:: StateDynamics + + + Bases: :py:obj:`waymax.dynamics.abstract_dynamics.DynamicsModel` + + Dynamics model for setting state in global coordinates. + + .. py:method:: action_spec() -> dm_env.specs.BoundedArray + + Action spec for the delta global action space. + + + .. py:method:: compute_update(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory) -> waymax.datatypes.TrajectoryUpdate + + Computes the pose and velocity updates at timestep. + + This dynamics will directly set the next x, y, yaw, vel_x, and vel_y based + on the action. + + :param action: Actions to take. Has shape (..., num_objects). + :param trajectory: Trajectory to be updated. Has shape of (..., num_objects, + num_timesteps=1). + + :returns: The trajectory update for timestep. + + + .. py:method:: inverse(trajectory: waymax.datatypes.Trajectory, metadata: waymax.datatypes.ObjectMetadata, timestep: int) -> waymax.datatypes.Action + + Runs inverse dynamics model to infer actions for specified timestep. + + :param trajectory: A Trajectory used to infer actions of shape (..., num_objects, + num_timesteps). + :param metadata: Object metadata for the trajectory. + :param timestep: Index of time for actions. + + :returns: An Action that converts traj[timestep] to traj[timestep+1]. + + + diff --git a/_sources/autoapi/waymax/dynamics/state_dynamics/index.rst.txt b/_sources/autoapi/waymax/dynamics/state_dynamics/index.rst.txt new file mode 100644 index 0000000..ae4e089 --- /dev/null +++ b/_sources/autoapi/waymax/dynamics/state_dynamics/index.rst.txt @@ -0,0 +1,63 @@ +:py:mod:`waymax.dynamics.state_dynamics` +======================================== + +.. py:module:: waymax.dynamics.state_dynamics + +.. autoapi-nested-parse:: + + Dynamics model for setting state in global coordinates. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.dynamics.state_dynamics.StateDynamics + + + + +.. py:class:: StateDynamics + + + Bases: :py:obj:`waymax.dynamics.abstract_dynamics.DynamicsModel` + + Dynamics model for setting state in global coordinates. + + .. py:method:: action_spec() -> dm_env.specs.BoundedArray + + Action spec for the delta global action space. + + + .. py:method:: compute_update(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory) -> waymax.datatypes.TrajectoryUpdate + + Computes the pose and velocity updates at timestep. + + This dynamics will directly set the next x, y, yaw, vel_x, and vel_y based + on the action. + + :param action: Actions to take. Has shape (..., num_objects). + :param trajectory: Trajectory to be updated. Has shape of (..., num_objects, + num_timesteps=1). + + :returns: The trajectory update for timestep. + + + .. py:method:: inverse(trajectory: waymax.datatypes.Trajectory, metadata: waymax.datatypes.ObjectMetadata, timestep: int) -> waymax.datatypes.Action + + Runs inverse dynamics model to infer actions for specified timestep. + + :param trajectory: A Trajectory used to infer actions of shape (..., num_objects, + num_timesteps). + :param metadata: Object metadata for the trajectory. + :param timestep: Index of time for actions. + + :returns: An Action that converts traj[timestep] to traj[timestep+1]. + + + diff --git a/_sources/autoapi/waymax/env/abstract_environment/index.rst.txt b/_sources/autoapi/waymax/env/abstract_environment/index.rst.txt new file mode 100644 index 0000000..3be2eb9 --- /dev/null +++ b/_sources/autoapi/waymax/env/abstract_environment/index.rst.txt @@ -0,0 +1,165 @@ +:py:mod:`waymax.env.abstract_environment` +========================================= + +.. py:module:: waymax.env.abstract_environment + +.. autoapi-nested-parse:: + + Abstract interface for the Waymax environment. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.env.abstract_environment.AbstractEnvironment + + + + +.. py:class:: AbstractEnvironment + + + Bases: :py:obj:`abc.ABC` + + A stateless environment interface for Waymax. + + .. py:method:: reset(scenario: waymax.env.typedefs.GenericScenario) -> waymax.env.typedefs.GenericState + :abstractmethod: + + Initializes a simulation state. + + This method allows the environment to perform optional postprocessing + on the state before the episode begins. By default this method is a + no-op. + + :param scenario: Scenario used to generate the initial state. + + :returns: The initialized simulation state. + + + .. py:method:: step(state: waymax.env.typedefs.GenericState, actions: waymax.env.typedefs.GenericAction) -> waymax.env.typedefs.GenericState + :abstractmethod: + + Advances the simulation by one timestep. + + :param state: The current state of the simulator. + :param actions: Action to apply to the state to produce the updated simulator + state. + + :returns: The next simulation state after taking an action. + + + .. py:method:: reward(state: waymax.env.typedefs.GenericState, action: waymax.env.typedefs.GenericAction) -> jax.Array + :abstractmethod: + + Computes the reward for a transition. + + :param state: The state used to compute the reward. + :param action: The action applied to state. + + :returns: A (..., num_objects) tensor of rewards. + + + .. py:method:: metrics(state: waymax.env.typedefs.GenericState) -> waymax.env.typedefs.Metrics + :abstractmethod: + + Computes a set of metrics which score a given simulator state. + + :param state: The state used to compute the metrics. + + :returns: + + A mapping from metric name to metrics which evaluate a simulator state at + state.timestep where all of the metrics are of shape (..., num_objects). + + + .. py:method:: observe(state: waymax.env.typedefs.GenericState) -> waymax.env.typedefs.Observation + :abstractmethod: + + Computes the observation of the simulator for the actor. + + :param state: The state used to compute the observation. + + :returns: + + An observation of the simulator state for the given timestep of shape + (...). + + + .. py:method:: action_spec() -> waymax.env.typedefs.GenericAction + :abstractmethod: + + Returns the action specs of the environment without batch dimension. + + :returns: + + The action specs represented as a PyTree where the leaves + are instances of specs.Array. + + + .. py:method:: reward_spec() -> dm_env.specs.Array + :abstractmethod: + + Returns the reward specs of the environment without batch dimension. + + + .. py:method:: discount_spec() -> dm_env.specs.BoundedArray + :abstractmethod: + + Returns the discount specs of the environment without batch dimension. + + + .. py:method:: observation_spec() -> waymax.env.typedefs.PyTree + :abstractmethod: + + Returns the observation specs of the environment without batch dimension. + + :returns: + + The observation specs represented as a PyTree where the + leaves are instances of specs.Array. + + + .. py:method:: termination(state: waymax.env.typedefs.GenericState) -> jax.Array + + Returns whether the current state is an episode termination. + + A termination marks the end of an episode where the cost-to-go from + this state is 0. + + The equivalent step type in DMEnv is dm_env.termination. + + :param state: The current simulator state. + + :returns: + + A boolean (...) tensor indicating whether the current state is the end of + an episode as a termination. + + + .. py:method:: truncation(state: waymax.env.typedefs.GenericState) -> jax.Array + + Returns whether the current state should truncate the episode. + + A truncation denotes that an episode has ended due to reaching the step + limit of an episode. In these cases dynamic programming methods (e.g. + Q-learning) should still compute cost-to-go assuming the episode will + continue running. + + The equivalent step type in DMEnv is dm_env.truncation. + + :param state: The current simulator state. + + :returns: + + A boolean (...) tensor indicating whether the current state is the end of + an episode as a truncation. + + + diff --git a/_sources/autoapi/waymax/env/base_environment/index.rst.txt b/_sources/autoapi/waymax/env/base_environment/index.rst.txt new file mode 100644 index 0000000..c5a0262 --- /dev/null +++ b/_sources/autoapi/waymax/env/base_environment/index.rst.txt @@ -0,0 +1,159 @@ +:py:mod:`waymax.env.base_environment` +===================================== + +.. py:module:: waymax.env.base_environment + +.. autoapi-nested-parse:: + + Core class definitions for MultiAgentEnvironment. + + This environment is designed to work with multiple objects (autonomous driving + vehicle and other objects). + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.env.base_environment.BaseEnvironment + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.env.base_environment._get_control_mask + waymax.env.base_environment._multi_agent_reward_spec + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.env.base_environment.MultiAgentEnvironment + + +.. py:class:: BaseEnvironment(dynamics_model: waymax.dynamics.DynamicsModel, config: waymax.config.EnvironmentConfig) + + + Bases: :py:obj:`waymax.env.abstract_environment.AbstractEnvironment` + + Waymax environment for multi-agent scenarios. + + .. py:property:: dynamics + :type: waymax.dynamics.DynamicsModel + + + .. py:method:: metrics(state: waymax.datatypes.SimulatorState) -> waymax.env.typedefs.Metrics + + Computes metrics (lower is better) from state. + + + .. py:method:: reset(state: waymax.datatypes.SimulatorState) -> waymax.datatypes.SimulatorState + + Initializes the simulation state. + + This initializer sets the initial timestep and fills the initial simulation + trajectory with invalid values. + + :param state: An uninitialized state of shape (...). + + :returns: The initialized simulation state of shape (...). + + + .. py:method:: observe(state: waymax.datatypes.SimulatorState) -> waymax.env.typedefs.Observation + + Computes the observation for the given simulation state. + + Here we assume that the default observation is just the simulator state. We + leave this for the user to override in order to provide a user-specific + observation function. A user can use this to move some of their model + specific post-processing into the environment rollout in the actor nodes. If + they want this post-processing on the accelertor, they can keep this the + same and implement it on the learner side. We provide some helper functions + at datatypes.observation.py to help write your own observation functions. + + :param state: Current state of the simulator of shape (...). + + :returns: Simulator state as an observation without modifications of shape (...). + + + .. py:method:: step(state: waymax.datatypes.SimulatorState, action: waymax.datatypes.Action) -> waymax.datatypes.SimulatorState + + Advances simulation by one timestep using the dynamics model. + + :param state: The current state of the simulator of shape (...). + :param action: The action to apply, of shape (..., num_objects). The + actions.valid field is used to denote which objects are being controlled + - objects whose valid is False will fallback to default behavior + specified by self.dynamics. + + :returns: The next simulation state after taking an action of shape (...). + + + .. py:method:: reward(state: waymax.datatypes.SimulatorState, action: waymax.datatypes.Action) -> jax.Array + + Computes the reward for a transition. + + :param state: The state used to compute the reward at state.timestep of shape + (...). + :param action: The action applied to state of shape (..., num_objects, dim). + + :returns: An array of rewards of shape (..., num_objects). + + + .. py:method:: action_spec() -> waymax.datatypes.Action + + Returns the action specs of the environment without batch dimension. + + :returns: + + The action specs represented as a PyTree where the leaves + are instances of specs.Array. + + + .. py:method:: reward_spec() -> dm_env.specs.Array + + Returns the reward specs of the environment without batch dimension. + + + .. py:method:: discount_spec() -> dm_env.specs.BoundedArray + + Returns the discount specs of the environment without batch dimension. + + + .. py:method:: observation_spec() -> waymax.env.typedefs.Observation + :abstractmethod: + + Returns the observation specs of the environment without batch dimension. + + :returns: + + The observation specs represented as a PyTree where the + leaves are instances of specs.Array. + + + +.. py:function:: _get_control_mask(state: waymax.datatypes.SimulatorState, config: waymax.config.EnvironmentConfig) -> jax.Array + + Gets the control mask for a multi-agent environment. + + +.. py:function:: _multi_agent_reward_spec(config: waymax.config.EnvironmentConfig) -> dm_env.specs.Array + + Gets the reward spec for a multi-agent environment. + + +.. py:data:: MultiAgentEnvironment + + + diff --git a/_sources/autoapi/waymax/env/errors/index.rst.txt b/_sources/autoapi/waymax/env/errors/index.rst.txt new file mode 100644 index 0000000..64a78fc --- /dev/null +++ b/_sources/autoapi/waymax/env/errors/index.rst.txt @@ -0,0 +1,30 @@ +:py:mod:`waymax.env.errors` +=========================== + +.. py:module:: waymax.env.errors + +.. autoapi-nested-parse:: + + Error messages for waymax. + + + +Module Contents +--------------- + +.. py:exception:: EpisodeAlreadyFinishedError + + + Bases: :py:obj:`RuntimeError` + + Error thrown when attempting to advance an episode that is finished. + + +.. py:exception:: SimulationNotInitializedError + + + Bases: :py:obj:`RuntimeError` + + Error thrown when attempting to advance an episode before reset. + + diff --git a/_sources/autoapi/waymax/env/index.rst.txt b/_sources/autoapi/waymax/env/index.rst.txt new file mode 100644 index 0000000..22f9833 --- /dev/null +++ b/_sources/autoapi/waymax/env/index.rst.txt @@ -0,0 +1,790 @@ +:py:mod:`waymax.env` +==================== + +.. py:module:: waymax.env + +.. autoapi-nested-parse:: + + Reinforcement learning environment interfaces. + + + +Subpackages +----------- +.. toctree:: + :titlesonly: + :maxdepth: 3 + + wrappers/index.rst + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + abstract_environment/index.rst + base_environment/index.rst + errors/index.rst + planning_agent_environment/index.rst + rollout/index.rst + typedefs/index.rst + + +Package Contents +---------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.env.AbstractEnvironment + waymax.env.BaseEnvironment + waymax.env.PlanningAgentDynamics + waymax.env.PlanningAgentEnvironment + waymax.env.RolloutOutput + waymax.env.BraxWrapper + waymax.env.DMEnvWrapper + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.env.rollout + waymax.env.rollout_log_by_expert_sdc + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.env.MultiAgentEnvironment + waymax.env.Metrics + waymax.env.Observation + waymax.env.ObservationFn + waymax.env.PyTree + waymax.env.RewardFn + + +.. py:class:: AbstractEnvironment + + + Bases: :py:obj:`abc.ABC` + + A stateless environment interface for Waymax. + + .. py:method:: reset(scenario: waymax.env.typedefs.GenericScenario) -> waymax.env.typedefs.GenericState + :abstractmethod: + + Initializes a simulation state. + + This method allows the environment to perform optional postprocessing + on the state before the episode begins. By default this method is a + no-op. + + :param scenario: Scenario used to generate the initial state. + + :returns: The initialized simulation state. + + + .. py:method:: step(state: waymax.env.typedefs.GenericState, actions: waymax.env.typedefs.GenericAction) -> waymax.env.typedefs.GenericState + :abstractmethod: + + Advances the simulation by one timestep. + + :param state: The current state of the simulator. + :param actions: Action to apply to the state to produce the updated simulator + state. + + :returns: The next simulation state after taking an action. + + + .. py:method:: reward(state: waymax.env.typedefs.GenericState, action: waymax.env.typedefs.GenericAction) -> jax.Array + :abstractmethod: + + Computes the reward for a transition. + + :param state: The state used to compute the reward. + :param action: The action applied to state. + + :returns: A (..., num_objects) tensor of rewards. + + + .. py:method:: metrics(state: waymax.env.typedefs.GenericState) -> waymax.env.typedefs.Metrics + :abstractmethod: + + Computes a set of metrics which score a given simulator state. + + :param state: The state used to compute the metrics. + + :returns: + + A mapping from metric name to metrics which evaluate a simulator state at + state.timestep where all of the metrics are of shape (..., num_objects). + + + .. py:method:: observe(state: waymax.env.typedefs.GenericState) -> waymax.env.typedefs.Observation + :abstractmethod: + + Computes the observation of the simulator for the actor. + + :param state: The state used to compute the observation. + + :returns: + + An observation of the simulator state for the given timestep of shape + (...). + + + .. py:method:: action_spec() -> waymax.env.typedefs.GenericAction + :abstractmethod: + + Returns the action specs of the environment without batch dimension. + + :returns: + + The action specs represented as a PyTree where the leaves + are instances of specs.Array. + + + .. py:method:: reward_spec() -> dm_env.specs.Array + :abstractmethod: + + Returns the reward specs of the environment without batch dimension. + + + .. py:method:: discount_spec() -> dm_env.specs.BoundedArray + :abstractmethod: + + Returns the discount specs of the environment without batch dimension. + + + .. py:method:: observation_spec() -> waymax.env.typedefs.PyTree + :abstractmethod: + + Returns the observation specs of the environment without batch dimension. + + :returns: + + The observation specs represented as a PyTree where the + leaves are instances of specs.Array. + + + .. py:method:: termination(state: waymax.env.typedefs.GenericState) -> jax.Array + + Returns whether the current state is an episode termination. + + A termination marks the end of an episode where the cost-to-go from + this state is 0. + + The equivalent step type in DMEnv is dm_env.termination. + + :param state: The current simulator state. + + :returns: + + A boolean (...) tensor indicating whether the current state is the end of + an episode as a termination. + + + .. py:method:: truncation(state: waymax.env.typedefs.GenericState) -> jax.Array + + Returns whether the current state should truncate the episode. + + A truncation denotes that an episode has ended due to reaching the step + limit of an episode. In these cases dynamic programming methods (e.g. + Q-learning) should still compute cost-to-go assuming the episode will + continue running. + + The equivalent step type in DMEnv is dm_env.truncation. + + :param state: The current simulator state. + + :returns: + + A boolean (...) tensor indicating whether the current state is the end of + an episode as a truncation. + + + +.. py:class:: BaseEnvironment(dynamics_model: waymax.dynamics.DynamicsModel, config: waymax.config.EnvironmentConfig) + + + Bases: :py:obj:`waymax.env.abstract_environment.AbstractEnvironment` + + Waymax environment for multi-agent scenarios. + + .. py:property:: dynamics + :type: waymax.dynamics.DynamicsModel + + + .. py:method:: metrics(state: waymax.datatypes.SimulatorState) -> waymax.env.typedefs.Metrics + + Computes metrics (lower is better) from state. + + + .. py:method:: reset(state: waymax.datatypes.SimulatorState) -> waymax.datatypes.SimulatorState + + Initializes the simulation state. + + This initializer sets the initial timestep and fills the initial simulation + trajectory with invalid values. + + :param state: An uninitialized state of shape (...). + + :returns: The initialized simulation state of shape (...). + + + .. py:method:: observe(state: waymax.datatypes.SimulatorState) -> waymax.env.typedefs.Observation + + Computes the observation for the given simulation state. + + Here we assume that the default observation is just the simulator state. We + leave this for the user to override in order to provide a user-specific + observation function. A user can use this to move some of their model + specific post-processing into the environment rollout in the actor nodes. If + they want this post-processing on the accelertor, they can keep this the + same and implement it on the learner side. We provide some helper functions + at datatypes.observation.py to help write your own observation functions. + + :param state: Current state of the simulator of shape (...). + + :returns: Simulator state as an observation without modifications of shape (...). + + + .. py:method:: step(state: waymax.datatypes.SimulatorState, action: waymax.datatypes.Action) -> waymax.datatypes.SimulatorState + + Advances simulation by one timestep using the dynamics model. + + :param state: The current state of the simulator of shape (...). + :param action: The action to apply, of shape (..., num_objects). The + actions.valid field is used to denote which objects are being controlled + - objects whose valid is False will fallback to default behavior + specified by self.dynamics. + + :returns: The next simulation state after taking an action of shape (...). + + + .. py:method:: reward(state: waymax.datatypes.SimulatorState, action: waymax.datatypes.Action) -> jax.Array + + Computes the reward for a transition. + + :param state: The state used to compute the reward at state.timestep of shape + (...). + :param action: The action applied to state of shape (..., num_objects, dim). + + :returns: An array of rewards of shape (..., num_objects). + + + .. py:method:: action_spec() -> waymax.datatypes.Action + + Returns the action specs of the environment without batch dimension. + + :returns: + + The action specs represented as a PyTree where the leaves + are instances of specs.Array. + + + .. py:method:: reward_spec() -> dm_env.specs.Array + + Returns the reward specs of the environment without batch dimension. + + + .. py:method:: discount_spec() -> dm_env.specs.BoundedArray + + Returns the discount specs of the environment without batch dimension. + + + .. py:method:: observation_spec() -> waymax.env.typedefs.Observation + :abstractmethod: + + Returns the observation specs of the environment without batch dimension. + + :returns: + + The observation specs represented as a PyTree where the + leaves are instances of specs.Array. + + + +.. py:data:: MultiAgentEnvironment + + + +.. py:exception:: EpisodeAlreadyFinishedError + + + Bases: :py:obj:`RuntimeError` + + Error thrown when attempting to advance an episode that is finished. + + +.. py:exception:: SimulationNotInitializedError + + + Bases: :py:obj:`RuntimeError` + + Error thrown when attempting to advance an episode before reset. + + +.. py:class:: PlanningAgentDynamics(multi_agent_dynamics: waymax.dynamics.DynamicsModel) + + + Bases: :py:obj:`waymax.dynamics.DynamicsModel` + + A dynamics wrapper for converting multi-agent dynamics to single-agent. + + .. py:method:: action_spec() -> dm_env.specs.BoundedArray + + Action spec of the action containing the bounds. + + + .. py:method:: compute_update(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory) -> waymax.datatypes.TrajectoryUpdate + + Computes the pose and velocity updates at timestep. + + + .. py:method:: forward(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory, log_trajectory: waymax.datatypes.Trajectory, is_controlled: jax.Array, timestep: int, allow_new_objects: bool = True) -> waymax.datatypes.Trajectory + + Updates a simulated trajectory to the next timestep given an update. + + Runs the forward model for the planning agent by taking in a single object's + action and tiling it for all others and then running the wrapped action. + + :param action: Actions to be applied to the trajectory to produce updates at the + next timestep of shape (..., dim). + :param trajectory: Simulated trajectory up to the current timestep. This + trajectory will be updated by this function updated with the trajectory + update. It is expected that this trajectory will have been updated up to + `timestep`. This is of shape: (..., num_objects, num_timesteps). + :param log_trajectory: Logged trajectory for all objects over the entire run + segment. Certain fields such as valid are optionally taken from this + trajectory. This is of shape: (..., num_objects, num_timesteps). + :param is_controlled: Boolean array specifying which objects are to be controlled + by the trajectory update of shape (..., num_objects). + :param timestep: Timestep of the current simulation. + :param allow_new_objects: Whether to allow new objects to enter the secene. If + this is set to False, all objects that are not valid at the current + timestep will not be valid at the next timestep and visa versa. + + :returns: + + Updated trajectory given update from a dynamics model at `timestep` + 1 + of shape (..., num_objects, num_timesteps). + + + .. py:method:: inverse(trajectory: waymax.datatypes.Trajectory, metadata: waymax.datatypes.ObjectMetadata, timestep: int) -> waymax.datatypes.Action + + Computes actions converting traj[timestep] to traj[timestep+1]. + + Runs the wrapped dynamics inverse and slices out the sdc's action + specifically. + + :param trajectory: Full trajectory to compute the inverse actions from of shape + (..., num_objects, num_timesteps). This trajectory is for the entire + simulation so that dynamics models can use sophisticated otpimization + techniques to find the best fitting actions. + :param metadata: Metadata on all objects in the scene which contains information + about what types of objects are in the scene of shape (..., + num_objects). + :param timestep: Current timestpe of the simulation. + + :returns: + + Action which will take a set of objects from trajectory[timestep] to + trajectory[timestep + 1] of shape (..., num_objects, dim). + + + +.. py:class:: PlanningAgentEnvironment(dynamics_model: waymax.dynamics.DynamicsModel, config: waymax.config.EnvironmentConfig, sim_agent_actors: Sequence[waymax.agents.actor_core.WaymaxActorCore] = ()) + + + Bases: :py:obj:`waymax.env.base_environment.BaseEnvironment` + + An environment wrapper allowing for controlling a single agent. + + The PlanningAgentEnvironment inherits from a multi-agent BaseEnvironment + to build a single-agent environment by returning only the observations and + rewards corresponding to the ego-agent (i.e. ADV). + + Note that while the action and reward no longer have an obj dimension as + expected for a single agent env, the observation retains the obj dimension + set to 1 to conform with the observation datastructure. + + .. py:method:: metrics(state: waymax.datatypes.SimulatorState) -> waymax.env.typedefs.Metrics + + Computes the metrics for the single agent wrapper. + + The metrics to be computed are based on those specified by the configuration + passed into the environment. This runs metrics that may be specific to the + planning agent case. + + :param state: State of simulation to compute the metrics for. This will compute + metrics for the timestep corresponding to `state.timestep` of shape + (...). + + :returns: + + Dictionary from metric name to metrics.MetricResult which represents the + metrics calculated at `state.timestep`. All metrics assumed to be shaped + (..., num_objects=1) unless specified in the metrics implementation. + + + .. py:method:: reward(state: waymax.datatypes.SimulatorState, action: waymax.datatypes.Action) -> jax.Array + + Computes the reward for a transition. + + :param state: State of simulation to compute the metrics for. This will compute + reward for the timestep corresponding to `state.timestep` of shape + (...). + :param action: The action applied for the state. + + :returns: A float (...) tensor of rewards for the single agent. + + + .. py:method:: action_spec() -> waymax.datatypes.Action + + Returns the action specs of the environment without batch dimension. + + :returns: + + The action specs represented as a PyTree where the leaves + are instances of specs.Array. + + + .. py:method:: step(state: waymax.datatypes.SimulatorState, action: waymax.datatypes.Action) -> waymax.datatypes.SimulatorState + + Advances simulation by one timestep using the dynamics model. + + :param state: The current state of the simulator of shape (...). + :param action: The action to apply, of shape (..., num_objects). The + actions.valid field is used to denote which objects are being controlled + - objects whose valid is False will fallback to default behavior + specified by self.dynamics. + + :returns: The next simulation state after taking an action of shape (...). + + + .. py:method:: reward_spec() -> dm_env.specs.Array + + Specify the reward spec as just for one object. + + + +.. py:function:: rollout(scenario: waymax.env.typedefs.GenericScenario, actor: waymax.agents.actor_core.WaymaxActorCore, env: waymax.env.abstract_environment.AbstractEnvironment, rng: jax.Array, rollout_num_steps: int = 1, actor_params: Optional[waymax.agents.actor_core.Params] = None) -> RolloutOutput + + Performs a rollout from the beginning of a run segment. + + + :param scenario: initial SimulatorState to start the rollout of shape (...). + :param actor: The action function used to select actions during the rollout. + :param env: A stateless Waymax environment used for computing steps, observations, + and rewards. + :param rng: Random key used for generate stochastic actions if needed. + :param rollout_num_steps: number of rollout steps. + :param actor_params: Parameters used by actor to select actions. It can be None if + the actor does not require parameters. + + :returns: + + Stacked rollout output of shape (rollout_num_steps + 1, ...) from the + simulator when taking an action given the action_fn. There is one extra in + the time dimension compared to `rollout_num_steps`. This is because we + prepend the initial timetep to the `timestep` field and append an invalid + action into the `action` field. + + +.. py:function:: rollout_log_by_expert_sdc(scenario: waymax.env.typedefs.GenericScenario, env: waymax.env.abstract_environment.AbstractEnvironment, dynamics_model: waymax.dynamics.DynamicsModel, rollout_num_steps: int = 1) -> RolloutOutput + + Rollouts state using logged expert actions specified by dynamics_model. + + +.. py:class:: RolloutOutput + + + Rollout output datatypes.structure for using as output of rollout function. + + .. attribute:: action + + Action produced by a functional corresponding to `ActionFuncType` + which after taking by calling some `environment.step(action)` produces the + `timestep` information. This is aggregated over a number of timesteps and + so the shape is (num_timesteps, ..., num_objects, action_dim). The `...` + of the shapes correspond to any kind of prefix for batching that might be + applied. + + .. attribute:: state + + Temporally aggregated information of the output of the simulation + after calling `environment.step(action)`. This information represents the + important information from the simulation aggregated through the rollout + of shape (num_timesteps, ...). The first element of `state` corresponds to + the initial simulation state. + + .. attribute:: observation + + Temporally aggregated information of the output of the + simulation after calling `observe(environment.step(action))`. This + information represents the observation of the agent of the simulator + state aggregated through the rollout of shape (num_timesteps, ...). The + first element of `observation` corresponds to the initial simulation + state. + + .. attribute:: metrics + + Mapping from metric name to metric which contains metrics computed + on the simulator states aggregated in time of shape (num_timestpes, ...). + These functions are defined in the `env.metrics(state)` function. As this + is a mapping, these metrics could be empty if the environment decides not + to produce metrics. This could be due to speed reasons during the rollout. + + .. attribute:: reward + + Scalar value of shape (num_timesteps, ..., num_objects) which + represents the reward achieved at a certain simulator state at the given + `state.timestep`. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + Returns the shape prefix for the rollout type. + + .. py:attribute:: action + :type: waymax.env.typedefs.GenericAction + + + + .. py:attribute:: state + :type: waymax.env.typedefs.GenericState + + + + .. py:attribute:: observation + :type: waymax.env.typedefs.Observation + + + + .. py:attribute:: metrics + :type: waymax.env.typedefs.Metrics + + + + .. py:attribute:: reward + :type: jax.Array + + + + .. py:method:: validate() + + Validates the shape prefix of the actions and timesteps. + + + +.. py:data:: Metrics + + + +.. py:data:: Observation + + + +.. py:data:: ObservationFn + + + +.. py:data:: PyTree + + + +.. py:data:: RewardFn + + + +.. py:class:: BraxWrapper(wrapped_env: waymax.env.base_environment.BaseEnvironment) + + + Brax-like interface wrapper for the Waymax environment. + + .. py:property:: dynamics + :type: waymax.dynamics.DynamicsModel + + + .. py:method:: metrics(state: waymax.datatypes.SimulatorState) -> waymax.env.typedefs.Metrics + + Computes metrics (lower is better) from state. + + + .. py:method:: reset(state: waymax.datatypes.SimulatorState) -> TimeStep + + Resets the environment and initializes the simulation state. + + This initializer sets the initial timestep and fills the initial simulation + trajectory with invalid values. + + :param state: An uninitialized state. + + :returns: The initialized simulation state. + + + .. py:method:: observe(state: waymax.datatypes.SimulatorState) -> waymax.env.typedefs.Observation + + Computes the observation for the given simulation state. + + + .. py:method:: step(timestep: TimeStep, action: waymax.datatypes.Action) -> TimeStep + + Advances simulation by one timestep using the dynamics model. + + :param timestep: The timestep containing the current state. + :param action: The action to apply, of shape (..., num_objects). The + actions.valid field is used to denote which objects are being controlled + - objects whose valid is False will fallback to default behavior + specified by self.dynamics. + + :returns: The timestep corresponding to the transition taken. + + + .. py:method:: reward(state: waymax.datatypes.SimulatorState, action: waymax.datatypes.Action) -> jax.Array + + Computes the reward for a transition. + + :param state: The state used to compute the reward at state.timestep. + :param action: The action applied to state. + + :returns: A (..., num_objects) tensor of rewards. + + + .. py:method:: termination(state: waymax.datatypes.SimulatorState) -> jax.Array + + Returns whether the current state is an episode termination. + + A termination marks the end of an episode where the cost-to-go from + this state is 0. + + The equivalent step type in DMEnv is dm_env.termination. + + :param state: The current simulator state. + + :returns: + + A boolean (...) tensor indicating whether the current state is the end + of an episode as a termination. + + + .. py:method:: truncation(state: waymax.datatypes.SimulatorState) -> jax.Array + + Returns whether the current state should truncate the episode. + + A truncation denotes that an episode has ended due to reaching the step + limit of an episode. In these cases dynamic programming methods (e.g. + Q-learning) should still compute cost-to-go assuming the episode will + continue running. + + The equivalent step type in DMEnv is dm_env.truncation. + + :param state: The current simulator state. + + :returns: + + A boolean (...) tensor indicating whether the current state is the end of + an episode as a truncation. + + + .. py:method:: action_spec() -> waymax.datatypes.Action + + Action spec of the environment. + + + .. py:method:: reward_spec() -> dm_env.specs.Array + + Reward spec of the environment. + + + .. py:method:: discount_spec() -> dm_env.specs.BoundedArray + + Discount spec of the environment. + + + .. py:method:: observation_spec() -> waymax.env.typedefs.PyTree + + Observation spec of the environment. + + + +.. py:class:: DMEnvWrapper(data_generator: Iterator[waymax.datatypes.SimulatorState], stateless_env: waymax.env.abstract_environment.AbstractEnvironment, squeeze_scalar_actions: bool = True) + + + Bases: :py:obj:`dm_env.Environment` + + A stateful environment wrapper implementing the DMEnv interface. + + .. py:property:: config + :type: waymax.config.EnvironmentConfig + + + .. py:property:: simulation_state + :type: waymax.datatypes.SimulatorState + + The current simulation state. + + .. py:property:: stateless_env + :type: waymax.env.abstract_environment.AbstractEnvironment + + The underlying stateless Waymax environment. + + .. py:method:: observe(state: waymax.datatypes.SimulatorState) -> waymax.env.typedefs.Observation + + Runs the stateless environment observation function. + + + .. py:method:: reset() -> dm_env.TimeStep + + Resets the environment and returns the initial TimeStep. + + + .. py:method:: step(action: jax.Array) -> dm_env.TimeStep + + Advances the state given an action. + + :param action: An action with shape compatible with self.action_spec() + + :returns: + + The TimeStep corresponding to the transition taken by applying + action to the current state. + + :raises SimulationNotInitializedError: If reset() has not been called before + this method is called. + :raises EpisodeAlreadyFinishedError: If this method is called after an episode + has been terminated or truncated. + + + .. py:method:: action_spec() -> dm_env.specs.BoundedArray + + The action specs of this environment, without batch dimension. + + + .. py:method:: discount_spec() -> dm_env.specs.BoundedArray + + The discount specs of this environment, without batch dimension. + + + .. py:method:: observation_spec() -> waymax.env.typedefs.PyTree + + The observation specs of this environment, without batch dimension. + + + .. py:method:: reward_spec() -> dm_env.specs.Array + + The reward specs of this environment, without batch dimension. + + + diff --git a/_sources/autoapi/waymax/env/planning_agent_environment/index.rst.txt b/_sources/autoapi/waymax/env/planning_agent_environment/index.rst.txt new file mode 100644 index 0000000..49159d9 --- /dev/null +++ b/_sources/autoapi/waymax/env/planning_agent_environment/index.rst.txt @@ -0,0 +1,191 @@ +:py:mod:`waymax.env.planning_agent_environment` +=============================================== + +.. py:module:: waymax.env.planning_agent_environment + +.. autoapi-nested-parse:: + + Waymax environment for tasks relating to Planning for the ADV. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.env.planning_agent_environment.PlanningAgentDynamics + waymax.env.planning_agent_environment.PlanningAgentEnvironment + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.env.planning_agent_environment._initialized_overlap + + + +.. py:class:: PlanningAgentDynamics(multi_agent_dynamics: waymax.dynamics.DynamicsModel) + + + Bases: :py:obj:`waymax.dynamics.DynamicsModel` + + A dynamics wrapper for converting multi-agent dynamics to single-agent. + + .. py:method:: action_spec() -> dm_env.specs.BoundedArray + + Action spec of the action containing the bounds. + + + .. py:method:: compute_update(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory) -> waymax.datatypes.TrajectoryUpdate + + Computes the pose and velocity updates at timestep. + + + .. py:method:: forward(action: waymax.datatypes.Action, trajectory: waymax.datatypes.Trajectory, log_trajectory: waymax.datatypes.Trajectory, is_controlled: jax.Array, timestep: int, allow_new_objects: bool = True) -> waymax.datatypes.Trajectory + + Updates a simulated trajectory to the next timestep given an update. + + Runs the forward model for the planning agent by taking in a single object's + action and tiling it for all others and then running the wrapped action. + + :param action: Actions to be applied to the trajectory to produce updates at the + next timestep of shape (..., dim). + :param trajectory: Simulated trajectory up to the current timestep. This + trajectory will be updated by this function updated with the trajectory + update. It is expected that this trajectory will have been updated up to + `timestep`. This is of shape: (..., num_objects, num_timesteps). + :param log_trajectory: Logged trajectory for all objects over the entire run + segment. Certain fields such as valid are optionally taken from this + trajectory. This is of shape: (..., num_objects, num_timesteps). + :param is_controlled: Boolean array specifying which objects are to be controlled + by the trajectory update of shape (..., num_objects). + :param timestep: Timestep of the current simulation. + :param allow_new_objects: Whether to allow new objects to enter the secene. If + this is set to False, all objects that are not valid at the current + timestep will not be valid at the next timestep and visa versa. + + :returns: + + Updated trajectory given update from a dynamics model at `timestep` + 1 + of shape (..., num_objects, num_timesteps). + + + .. py:method:: inverse(trajectory: waymax.datatypes.Trajectory, metadata: waymax.datatypes.ObjectMetadata, timestep: int) -> waymax.datatypes.Action + + Computes actions converting traj[timestep] to traj[timestep+1]. + + Runs the wrapped dynamics inverse and slices out the sdc's action + specifically. + + :param trajectory: Full trajectory to compute the inverse actions from of shape + (..., num_objects, num_timesteps). This trajectory is for the entire + simulation so that dynamics models can use sophisticated otpimization + techniques to find the best fitting actions. + :param metadata: Metadata on all objects in the scene which contains information + about what types of objects are in the scene of shape (..., + num_objects). + :param timestep: Current timestpe of the simulation. + + :returns: + + Action which will take a set of objects from trajectory[timestep] to + trajectory[timestep + 1] of shape (..., num_objects, dim). + + + +.. py:class:: PlanningAgentEnvironment(dynamics_model: waymax.dynamics.DynamicsModel, config: waymax.config.EnvironmentConfig, sim_agent_actors: Sequence[waymax.agents.actor_core.WaymaxActorCore] = ()) + + + Bases: :py:obj:`waymax.env.base_environment.BaseEnvironment` + + An environment wrapper allowing for controlling a single agent. + + The PlanningAgentEnvironment inherits from a multi-agent BaseEnvironment + to build a single-agent environment by returning only the observations and + rewards corresponding to the ego-agent (i.e. ADV). + + Note that while the action and reward no longer have an obj dimension as + expected for a single agent env, the observation retains the obj dimension + set to 1 to conform with the observation datastructure. + + .. py:method:: metrics(state: waymax.datatypes.SimulatorState) -> waymax.env.typedefs.Metrics + + Computes the metrics for the single agent wrapper. + + The metrics to be computed are based on those specified by the configuration + passed into the environment. This runs metrics that may be specific to the + planning agent case. + + :param state: State of simulation to compute the metrics for. This will compute + metrics for the timestep corresponding to `state.timestep` of shape + (...). + + :returns: + + Dictionary from metric name to metrics.MetricResult which represents the + metrics calculated at `state.timestep`. All metrics assumed to be shaped + (..., num_objects=1) unless specified in the metrics implementation. + + + .. py:method:: reward(state: waymax.datatypes.SimulatorState, action: waymax.datatypes.Action) -> jax.Array + + Computes the reward for a transition. + + :param state: State of simulation to compute the metrics for. This will compute + reward for the timestep corresponding to `state.timestep` of shape + (...). + :param action: The action applied for the state. + + :returns: A float (...) tensor of rewards for the single agent. + + + .. py:method:: action_spec() -> waymax.datatypes.Action + + Returns the action specs of the environment without batch dimension. + + :returns: + + The action specs represented as a PyTree where the leaves + are instances of specs.Array. + + + .. py:method:: step(state: waymax.datatypes.SimulatorState, action: waymax.datatypes.Action) -> waymax.datatypes.SimulatorState + + Advances simulation by one timestep using the dynamics model. + + :param state: The current state of the simulator of shape (...). + :param action: The action to apply, of shape (..., num_objects). The + actions.valid field is used to denote which objects are being controlled + - objects whose valid is False will fallback to default behavior + specified by self.dynamics. + + :returns: The next simulation state after taking an action of shape (...). + + + .. py:method:: reward_spec() -> dm_env.specs.Array + + Specify the reward spec as just for one object. + + + +.. py:function:: _initialized_overlap(log_trajectory: waymax.datatypes.Trajectory) -> jax.Array + + Return a mask for objects initialized in a overlap state. + + This function returns a boolean mask indicating if an object is in a + overlap state at timestep 0 in the logged trajectory. This function + can be used to prune out certain objects that are initialized in an + overlap, such as articulated buses and pedestrians in a PUDO situation. + + :param log_trajectory: A trajectory of shape (..., num_objects, num_timesteps). + + :returns: A [..., objects] boolean tensor of overlap masks. + + diff --git a/_sources/autoapi/waymax/env/rollout/index.rst.txt b/_sources/autoapi/waymax/env/rollout/index.rst.txt new file mode 100644 index 0000000..fad4c2c --- /dev/null +++ b/_sources/autoapi/waymax/env/rollout/index.rst.txt @@ -0,0 +1,189 @@ +:py:mod:`waymax.env.rollout` +============================ + +.. py:module:: waymax.env.rollout + +.. autoapi-nested-parse:: + + Utilities for fast jittable rollout of environments in Waymax. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.env.rollout.RolloutCarry + waymax.env.rollout.RolloutOutput + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.env.rollout.rollout + waymax.env.rollout.rollout_log_by_expert_sdc + + + +.. py:class:: RolloutCarry + + + Rollout output that is carried between iterations of the rollout. + + .. attribute:: state + + Current state of the simulator after `state.timestep` steps. + + .. attribute:: observation + + Observation of the simulator state from the environment which + is called by a given observation function meant to be the input of the + actor's model. + + .. attribute:: rng + + Random key which represents the key for randomizing actions and + initializing parameters for a Waymax actor. + + .. attribute:: actor_state + + Internal state for whatever the agent needs to keep as its + state. This can be recurrent embeddings or accounting information. + + .. py:attribute:: state + :type: waymax.env.typedefs.GenericState + + + + .. py:attribute:: observation + :type: waymax.env.typedefs.Observation + + + + .. py:attribute:: rng + :type: jax.Array + + + + .. py:attribute:: actor_state + :type: waymax.env.typedefs.PyTree + + + + +.. py:class:: RolloutOutput + + + Rollout output datatypes.structure for using as output of rollout function. + + .. attribute:: action + + Action produced by a functional corresponding to `ActionFuncType` + which after taking by calling some `environment.step(action)` produces the + `timestep` information. This is aggregated over a number of timesteps and + so the shape is (num_timesteps, ..., num_objects, action_dim). The `...` + of the shapes correspond to any kind of prefix for batching that might be + applied. + + .. attribute:: state + + Temporally aggregated information of the output of the simulation + after calling `environment.step(action)`. This information represents the + important information from the simulation aggregated through the rollout + of shape (num_timesteps, ...). The first element of `state` corresponds to + the initial simulation state. + + .. attribute:: observation + + Temporally aggregated information of the output of the + simulation after calling `observe(environment.step(action))`. This + information represents the observation of the agent of the simulator + state aggregated through the rollout of shape (num_timesteps, ...). The + first element of `observation` corresponds to the initial simulation + state. + + .. attribute:: metrics + + Mapping from metric name to metric which contains metrics computed + on the simulator states aggregated in time of shape (num_timestpes, ...). + These functions are defined in the `env.metrics(state)` function. As this + is a mapping, these metrics could be empty if the environment decides not + to produce metrics. This could be due to speed reasons during the rollout. + + .. attribute:: reward + + Scalar value of shape (num_timesteps, ..., num_objects) which + represents the reward achieved at a certain simulator state at the given + `state.timestep`. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + Returns the shape prefix for the rollout type. + + .. py:attribute:: action + :type: waymax.env.typedefs.GenericAction + + + + .. py:attribute:: state + :type: waymax.env.typedefs.GenericState + + + + .. py:attribute:: observation + :type: waymax.env.typedefs.Observation + + + + .. py:attribute:: metrics + :type: waymax.env.typedefs.Metrics + + + + .. py:attribute:: reward + :type: jax.Array + + + + .. py:method:: validate() + + Validates the shape prefix of the actions and timesteps. + + + +.. py:function:: rollout(scenario: waymax.env.typedefs.GenericScenario, actor: waymax.agents.actor_core.WaymaxActorCore, env: waymax.env.abstract_environment.AbstractEnvironment, rng: jax.Array, rollout_num_steps: int = 1, actor_params: Optional[waymax.agents.actor_core.Params] = None) -> RolloutOutput + + Performs a rollout from the beginning of a run segment. + + + :param scenario: initial SimulatorState to start the rollout of shape (...). + :param actor: The action function used to select actions during the rollout. + :param env: A stateless Waymax environment used for computing steps, observations, + and rewards. + :param rng: Random key used for generate stochastic actions if needed. + :param rollout_num_steps: number of rollout steps. + :param actor_params: Parameters used by actor to select actions. It can be None if + the actor does not require parameters. + + :returns: + + Stacked rollout output of shape (rollout_num_steps + 1, ...) from the + simulator when taking an action given the action_fn. There is one extra in + the time dimension compared to `rollout_num_steps`. This is because we + prepend the initial timetep to the `timestep` field and append an invalid + action into the `action` field. + + +.. py:function:: rollout_log_by_expert_sdc(scenario: waymax.env.typedefs.GenericScenario, env: waymax.env.abstract_environment.AbstractEnvironment, dynamics_model: waymax.dynamics.DynamicsModel, rollout_num_steps: int = 1) -> RolloutOutput + + Rollouts state using logged expert actions specified by dynamics_model. + + diff --git a/_sources/autoapi/waymax/env/typedefs/index.rst.txt b/_sources/autoapi/waymax/env/typedefs/index.rst.txt new file mode 100644 index 0000000..a26e2c8 --- /dev/null +++ b/_sources/autoapi/waymax/env/typedefs/index.rst.txt @@ -0,0 +1,46 @@ +:py:mod:`waymax.env.typedefs` +============================= + +.. py:module:: waymax.env.typedefs + +.. autoapi-nested-parse:: + + Manages types specific to Waymax environments used in this library. + + + +Module Contents +--------------- + +.. py:data:: PyTree + + + +.. py:data:: GenericScenario + + + +.. py:data:: GenericAction + + + +.. py:data:: GenericState + + + +.. py:data:: Observation + + + +.. py:data:: ObservationFn + + + +.. py:data:: RewardFn + + + +.. py:data:: Metrics + + + diff --git a/_sources/autoapi/waymax/env/wrappers/brax_wrapper/index.rst.txt b/_sources/autoapi/waymax/env/wrappers/brax_wrapper/index.rst.txt new file mode 100644 index 0000000..e7018c1 --- /dev/null +++ b/_sources/autoapi/waymax/env/wrappers/brax_wrapper/index.rst.txt @@ -0,0 +1,223 @@ +:py:mod:`waymax.env.wrappers.brax_wrapper` +========================================== + +.. py:module:: waymax.env.wrappers.brax_wrapper + +.. autoapi-nested-parse:: + + Library for wrapping Waymax environments in a Brax-like interface. + + For more information on the Brax interface see: https://github.com/google/brax. + + The Waymax/Brax interface primarily differs from the Google/Brax interface in + the reset function. Because Waymax uses data to instantiate a new episode, + the reset function requires a SimulatorState argument, whereas the Google/Brax + interface requires only a random key. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.env.wrappers.brax_wrapper.TimeStep + waymax.env.wrappers.brax_wrapper.BraxWrapper + + + + +.. py:class:: TimeStep + + + Container class for Waymax transitions. + + .. attribute:: state + + The current simulation state of shape (...). + + .. attribute:: observation + + The current observation of shape (..,). + + .. attribute:: reward + + The reward obtained in the current transition of shape (..., + num_objects). + + .. attribute:: done + + A boolean array denoting the end of an episode of shape (...). + + .. attribute:: discount + + An array of discount values of shape (...). + + .. attribute:: metrics + + Optional dictionary of metrics. + + .. attribute:: info + + Optional dictionary of arbitrary logging information. + + .. py:property:: shape + :type: tuple[int, Ellipsis] + + Shape of TimeStep. + + .. py:attribute:: state + :type: waymax.datatypes.SimulatorState + + + + .. py:attribute:: observation + :type: waymax.env.typedefs.Observation + + + + .. py:attribute:: reward + :type: jax.Array + + + + .. py:attribute:: done + :type: jax.Array + + + + .. py:attribute:: discount + :type: jax.Array + + + + .. py:attribute:: metrics + :type: waymax.env.typedefs.Metrics + + + + .. py:attribute:: info + :type: dict[str, Any] + + + + .. py:method:: __eq__(other: Any) -> bool + + Return self==value. + + + +.. py:class:: BraxWrapper(wrapped_env: waymax.env.base_environment.BaseEnvironment) + + + Brax-like interface wrapper for the Waymax environment. + + .. py:property:: dynamics + :type: waymax.dynamics.DynamicsModel + + + .. py:method:: metrics(state: waymax.datatypes.SimulatorState) -> waymax.env.typedefs.Metrics + + Computes metrics (lower is better) from state. + + + .. py:method:: reset(state: waymax.datatypes.SimulatorState) -> TimeStep + + Resets the environment and initializes the simulation state. + + This initializer sets the initial timestep and fills the initial simulation + trajectory with invalid values. + + :param state: An uninitialized state. + + :returns: The initialized simulation state. + + + .. py:method:: observe(state: waymax.datatypes.SimulatorState) -> waymax.env.typedefs.Observation + + Computes the observation for the given simulation state. + + + .. py:method:: step(timestep: TimeStep, action: waymax.datatypes.Action) -> TimeStep + + Advances simulation by one timestep using the dynamics model. + + :param timestep: The timestep containing the current state. + :param action: The action to apply, of shape (..., num_objects). The + actions.valid field is used to denote which objects are being controlled + - objects whose valid is False will fallback to default behavior + specified by self.dynamics. + + :returns: The timestep corresponding to the transition taken. + + + .. py:method:: reward(state: waymax.datatypes.SimulatorState, action: waymax.datatypes.Action) -> jax.Array + + Computes the reward for a transition. + + :param state: The state used to compute the reward at state.timestep. + :param action: The action applied to state. + + :returns: A (..., num_objects) tensor of rewards. + + + .. py:method:: termination(state: waymax.datatypes.SimulatorState) -> jax.Array + + Returns whether the current state is an episode termination. + + A termination marks the end of an episode where the cost-to-go from + this state is 0. + + The equivalent step type in DMEnv is dm_env.termination. + + :param state: The current simulator state. + + :returns: + + A boolean (...) tensor indicating whether the current state is the end + of an episode as a termination. + + + .. py:method:: truncation(state: waymax.datatypes.SimulatorState) -> jax.Array + + Returns whether the current state should truncate the episode. + + A truncation denotes that an episode has ended due to reaching the step + limit of an episode. In these cases dynamic programming methods (e.g. + Q-learning) should still compute cost-to-go assuming the episode will + continue running. + + The equivalent step type in DMEnv is dm_env.truncation. + + :param state: The current simulator state. + + :returns: + + A boolean (...) tensor indicating whether the current state is the end of + an episode as a truncation. + + + .. py:method:: action_spec() -> waymax.datatypes.Action + + Action spec of the environment. + + + .. py:method:: reward_spec() -> dm_env.specs.Array + + Reward spec of the environment. + + + .. py:method:: discount_spec() -> dm_env.specs.BoundedArray + + Discount spec of the environment. + + + .. py:method:: observation_spec() -> waymax.env.typedefs.PyTree + + Observation spec of the environment. + + + diff --git a/_sources/autoapi/waymax/env/wrappers/dm_env_wrapper/index.rst.txt b/_sources/autoapi/waymax/env/wrappers/dm_env_wrapper/index.rst.txt new file mode 100644 index 0000000..cdda5e4 --- /dev/null +++ b/_sources/autoapi/waymax/env/wrappers/dm_env_wrapper/index.rst.txt @@ -0,0 +1,112 @@ +:py:mod:`waymax.env.wrappers.dm_env_wrapper` +============================================ + +.. py:module:: waymax.env.wrappers.dm_env_wrapper + +.. autoapi-nested-parse:: + + Library for wrapping Waymax environments in a DMEnv interface. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.env.wrappers.dm_env_wrapper.DMEnvWrapper + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.env.wrappers.dm_env_wrapper.make_sdc_dm_environment + + + +.. py:class:: DMEnvWrapper(data_generator: Iterator[waymax.datatypes.SimulatorState], stateless_env: waymax.env.abstract_environment.AbstractEnvironment, squeeze_scalar_actions: bool = True) + + + Bases: :py:obj:`dm_env.Environment` + + A stateful environment wrapper implementing the DMEnv interface. + + .. py:property:: config + :type: waymax.config.EnvironmentConfig + + + .. py:property:: simulation_state + :type: waymax.datatypes.SimulatorState + + The current simulation state. + + .. py:property:: stateless_env + :type: waymax.env.abstract_environment.AbstractEnvironment + + The underlying stateless Waymax environment. + + .. py:method:: observe(state: waymax.datatypes.SimulatorState) -> waymax.env.typedefs.Observation + + Runs the stateless environment observation function. + + + .. py:method:: reset() -> dm_env.TimeStep + + Resets the environment and returns the initial TimeStep. + + + .. py:method:: step(action: jax.Array) -> dm_env.TimeStep + + Advances the state given an action. + + :param action: An action with shape compatible with self.action_spec() + + :returns: + + The TimeStep corresponding to the transition taken by applying + action to the current state. + + :raises SimulationNotInitializedError: If reset() has not been called before + this method is called. + :raises EpisodeAlreadyFinishedError: If this method is called after an episode + has been terminated or truncated. + + + .. py:method:: action_spec() -> dm_env.specs.BoundedArray + + The action specs of this environment, without batch dimension. + + + .. py:method:: discount_spec() -> dm_env.specs.BoundedArray + + The discount specs of this environment, without batch dimension. + + + .. py:method:: observation_spec() -> waymax.env.typedefs.PyTree + + The observation specs of this environment, without batch dimension. + + + .. py:method:: reward_spec() -> dm_env.specs.Array + + The reward specs of this environment, without batch dimension. + + + +.. py:function:: make_sdc_dm_environment(dynamics_model: waymax.dynamics.DynamicsModel, data_config: waymax.config.DatasetConfig, env_config: waymax.config.EnvironmentConfig) -> DMEnvWrapper + + Makes a DM environment for controlling SDC only. + + :param dynamics_model: A dynamics model used to transit state of the environment. + :param data_config: Config for dataset, see details in config.DatasetConfig + :param env_config: Config for environment, see details in config.EnvironmentConfig. + + :returns: The single agent (SDC) Waymax DM environment that has not been reset. + + diff --git a/_sources/autoapi/waymax/env/wrappers/index.rst.txt b/_sources/autoapi/waymax/env/wrappers/index.rst.txt new file mode 100644 index 0000000..81fe42b --- /dev/null +++ b/_sources/autoapi/waymax/env/wrappers/index.rst.txt @@ -0,0 +1,16 @@ +:py:mod:`waymax.env.wrappers` +============================= + +.. py:module:: waymax.env.wrappers + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + brax_wrapper/index.rst + dm_env_wrapper/index.rst + + diff --git a/_sources/autoapi/waymax/index.rst.txt b/_sources/autoapi/waymax/index.rst.txt new file mode 100644 index 0000000..8f8e598 --- /dev/null +++ b/_sources/autoapi/waymax/index.rst.txt @@ -0,0 +1,37 @@ +:py:mod:`waymax` +================ + +.. py:module:: waymax + +.. autoapi-nested-parse:: + + Waymax root directory. + + + +Subpackages +----------- +.. toctree:: + :titlesonly: + :maxdepth: 3 + + agents/index.rst + dataloader/index.rst + datatypes/index.rst + dynamics/index.rst + env/index.rst + metrics/index.rst + rewards/index.rst + utils/index.rst + visualization/index.rst + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + config/index.rst + + diff --git a/_sources/autoapi/waymax/metrics/abstract_metric/index.rst.txt b/_sources/autoapi/waymax/metrics/abstract_metric/index.rst.txt new file mode 100644 index 0000000..1e2d46d --- /dev/null +++ b/_sources/autoapi/waymax/metrics/abstract_metric/index.rst.txt @@ -0,0 +1,63 @@ +:py:mod:`waymax.metrics.abstract_metric` +======================================== + +.. py:module:: waymax.metrics.abstract_metric + +.. autoapi-nested-parse:: + + Simulator abstract metric definition. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.metrics.abstract_metric.MetricResult + waymax.metrics.abstract_metric.AbstractMetric + + + + +.. py:class:: MetricResult + + + Bases: :py:obj:`waymax.datatypes.MaskedArray` + + A metric result holding metric values and a validity mask. + + .. attribute:: value + + A (...) float32 array of values. + + .. attribute:: valid + + A (...) boolean validity mask. + + .. py:method:: validate() -> None + + Validates shape and type. + + + +.. py:class:: AbstractMetric + + + Abstract class for simulator metrics. + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState) -> MetricResult + :abstractmethod: + + Computes a per-timestep, per-object metric from a SimulatorState. + + :param simulator_state: The current simulator state of shape (...). + + :returns: A MetricResult containing the metric result and validity mask of shape + (..., num_objects). + + + diff --git a/_sources/autoapi/waymax/metrics/comfort/index.rst.txt b/_sources/autoapi/waymax/metrics/comfort/index.rst.txt new file mode 100644 index 0000000..19f15f7 --- /dev/null +++ b/_sources/autoapi/waymax/metrics/comfort/index.rst.txt @@ -0,0 +1,88 @@ +:py:mod:`waymax.metrics.comfort` +================================ + +.. py:module:: waymax.metrics.comfort + +.. autoapi-nested-parse:: + + Metrics relating to comfort. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.metrics.comfort.KinematicsInfeasibilityMetric + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.metrics.comfort._KIM_EPSILON + + +.. py:data:: _KIM_EPSILON + :value: 0.001 + + + +.. py:class:: KinematicsInfeasibilityMetric(dt: float = 0.1, max_acc: float = 10.4, max_steering: float = 0.3) + + + Bases: :py:obj:`waymax.metrics.abstract_metric.AbstractMetric` + + Kinematics infeasibility metric. + + The metric uses continuous acceleration, steering inverse dynamics model to + estimate the required acceleration and steering curvature to complete the + transition. This metric returns 1 if the magnitude of the estimated + acceleration and/or steering curvature exceeds the max feasibility magnitude, + otherwise returns 0.0. + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState) -> waymax.metrics.abstract_metric.MetricResult + + Computes the kinematics infeasibility metric. + + The metrics detects whether the acceleration, steering curvature at step + simulator_state.timestep violates the allowed magnitude ranges. The + acceleration, steering curvature corresponds to the transition from + simulator_state.timestep-1 to simulator_state.timestep. The value of + simulator_state.timestep means that all states are updated up to the step + of simulator_state.timestep and simulator_state.timestep+1 is still invalid. + Therefore, we need to test whether the transition from + simulator_state.timestep-1 to simulator_state.timestep is kinematically + feasible. + + :param simulator_state: The current simulator state of shape of shape (...). + + :returns: + + A (..., num_objects) MetricResult containing the metric result, with + values in either 1 or 0, where 1.0 indicates that the current step + violates the kinematic infeasibility metrics, and 0.0 indicates that the + current step does not violate the kinematic infeasibility metrics. + + + .. py:method:: compute_kinematics_infeasibility(traj: waymax.datatypes.Trajectory, timestep: jax.Array) -> waymax.metrics.abstract_metric.MetricResult + + Computes the kinematics infeasibility metric. + + + :param traj: The sim_trajectory of all steps, of shape (..., num_objects, + num_timesteps). + :param timestep: The current simulator timestep at which the kinematics + infeasibility metric is calculated at. + + :returns: A (..., num_objects) MetricResult containing the metric result. + + + diff --git a/_sources/autoapi/waymax/metrics/imitation/index.rst.txt b/_sources/autoapi/waymax/metrics/imitation/index.rst.txt new file mode 100644 index 0000000..472aa4e --- /dev/null +++ b/_sources/autoapi/waymax/metrics/imitation/index.rst.txt @@ -0,0 +1,62 @@ +:py:mod:`waymax.metrics.imitation` +================================== + +.. py:module:: waymax.metrics.imitation + +.. autoapi-nested-parse:: + + Metrics relating to imitation. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.metrics.imitation.LogDivergenceMetric + + + + +.. py:class:: LogDivergenceMetric + + + Bases: :py:obj:`waymax.metrics.abstract_metric.AbstractMetric` + + Log divergence metric. + + This metric returns the L2 distance between the controlled object's XY + location and its position in the logged history at the same timestep. + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState) -> waymax.metrics.abstract_metric.MetricResult + + Computes log divergence by fetching correct arguments. + + :param simulator_state: The current simulator state. + + :returns: A (..., num_objects) MetricResult containing the metric result, with L2 + values representing the distance of each object from the log to the + simulated trajectory at the current timestep. + + + .. py:method:: compute_log_divergence(object_xy: jax.Array, log_xy: jax.Array) -> jax.Array + :classmethod: + + Computes the L2 distance between `object_xy` and `log_xy`. + + :param object_xy: XY coordinates of current vehicles for the current timestep, of + shape (..., num_objects, num_timesteps, 2). + :param log_xy: XY coordinates of logged vehicles for the current timestep, of + shape (..., num_objects, num_timesteps, 2). + + :returns: + + A (..., num_objects, num_timesteps) array containing the metric result of + the same shape as the input trajectories. + + + diff --git a/_sources/autoapi/waymax/metrics/index.rst.txt b/_sources/autoapi/waymax/metrics/index.rst.txt new file mode 100644 index 0000000..417ec5a --- /dev/null +++ b/_sources/autoapi/waymax/metrics/index.rst.txt @@ -0,0 +1,297 @@ +:py:mod:`waymax.metrics` +======================== + +.. py:module:: waymax.metrics + +.. autoapi-nested-parse:: + + Metrics for agent evaluation. + + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + abstract_metric/index.rst + comfort/index.rst + imitation/index.rst + metric_factory/index.rst + overlap/index.rst + roadgraph/index.rst + route/index.rst + + +Package Contents +---------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.metrics.AbstractMetric + waymax.metrics.MetricResult + waymax.metrics.LogDivergenceMetric + waymax.metrics.OverlapMetric + waymax.metrics.OffroadMetric + waymax.metrics.WrongWayMetric + waymax.metrics.OffRouteMetric + waymax.metrics.ProgressionMetric + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.metrics.run_metrics + + + +.. py:class:: AbstractMetric + + + Abstract class for simulator metrics. + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState) -> MetricResult + :abstractmethod: + + Computes a per-timestep, per-object metric from a SimulatorState. + + :param simulator_state: The current simulator state of shape (...). + + :returns: A MetricResult containing the metric result and validity mask of shape + (..., num_objects). + + + +.. py:class:: MetricResult + + + Bases: :py:obj:`waymax.datatypes.MaskedArray` + + A metric result holding metric values and a validity mask. + + .. attribute:: value + + A (...) float32 array of values. + + .. attribute:: valid + + A (...) boolean validity mask. + + .. py:method:: validate() -> None + + Validates shape and type. + + + +.. py:class:: LogDivergenceMetric + + + Bases: :py:obj:`waymax.metrics.abstract_metric.AbstractMetric` + + Log divergence metric. + + This metric returns the L2 distance between the controlled object's XY + location and its position in the logged history at the same timestep. + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState) -> waymax.metrics.abstract_metric.MetricResult + + Computes log divergence by fetching correct arguments. + + :param simulator_state: The current simulator state. + + :returns: A (..., num_objects) MetricResult containing the metric result, with L2 + values representing the distance of each object from the log to the + simulated trajectory at the current timestep. + + + .. py:method:: compute_log_divergence(object_xy: jax.Array, log_xy: jax.Array) -> jax.Array + :classmethod: + + Computes the L2 distance between `object_xy` and `log_xy`. + + :param object_xy: XY coordinates of current vehicles for the current timestep, of + shape (..., num_objects, num_timesteps, 2). + :param log_xy: XY coordinates of logged vehicles for the current timestep, of + shape (..., num_objects, num_timesteps, 2). + + :returns: + + A (..., num_objects, num_timesteps) array containing the metric result of + the same shape as the input trajectories. + + + +.. py:function:: run_metrics(simulator_state: waymax.datatypes.SimulatorState, metrics_config: waymax.config.MetricsConfig) -> dict[str, waymax.metrics.abstract_metric.MetricResult] + + Runs all metrics with config flags set to True. + + :param simulator_state: The current simulator state of shape (...). + :param metrics_config: Waymax metrics config. + + :returns: + + A dictionary of metric names mapping to metric result arrays where each + metric is of shape (..., num_objects). + + +.. py:class:: OverlapMetric + + + Bases: :py:obj:`waymax.metrics.abstract_metric.AbstractMetric` + + Overlap metric. + + This metric returns 1.0 if an object's bounding box is overlapping with + that of another object. + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState) -> waymax.metrics.abstract_metric.MetricResult + + Computes a per-timestep, per-object metric from a SimulatorState. + + :param simulator_state: The current simulator state of shape (...). + + :returns: A MetricResult containing the metric result and validity mask of shape + (..., num_objects). + + + .. py:method:: compute_overlap(current_traj: waymax.datatypes.Trajectory) -> waymax.metrics.abstract_metric.MetricResult + + Computes the overlap metric. + + :param current_traj: Trajectory object containing current states of shape (..., + num_objects, num_timesteps=1). + + :returns: A (..., num_objects) MetricResult. + + + +.. py:class:: OffroadMetric + + + Bases: :py:obj:`waymax.metrics.abstract_metric.AbstractMetric` + + Offroad metric. + + This metric returns 1.0 if the object is offroad. + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState) -> waymax.metrics.abstract_metric.MetricResult + + Computes the offroad metric. + + :param simulator_state: Updated simulator state to calculate metrics for. Will + compute the offroad metric for timestep `simulator_state.timestep`. + + :returns: + + An array containing the metric result of the same shape as the input + trajectories. The shape is (..., num_objects). + + + +.. py:class:: WrongWayMetric + + + Bases: :py:obj:`waymax.metrics.abstract_metric.AbstractMetric` + + Wrong-way metric for SDC. + + This metric checks if SDC is driving into wrong driving the wrong way or path. + It first computes the distance to the closest roadgraph point in all valid + paths that the SDC can drive along from its starting position. If the distance + is larger than the threhold WRONG_WAY_THRES, it's considered wrong-way and + returns the distance; otherwise, it's driving on the legal lanes, and returns + 0.0. + + .. py:attribute:: WRONG_WAY_THRES + :value: 3.5 + + + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState) -> waymax.metrics.abstract_metric.MetricResult + + Computes a per-timestep, per-object metric from a SimulatorState. + + :param simulator_state: The current simulator state of shape (...). + + :returns: A MetricResult containing the metric result and validity mask of shape + (..., num_objects). + + + +.. py:class:: OffRouteMetric + + + Bases: :py:obj:`waymax.metrics.abstract_metric.AbstractMetric` + + Off-route metric for the SDC. + + The SDC is considered off-route either if 1) it is farther than + MAX_DISTANCE_TO_ROUTE_PATH from the closest on-route path, or 2) it is farther + from the closest on-route path than the closest off-route path by + MAX_RELATIVE_DISTANCE_TO_OFF_ROUTE_PATH. + + If the SDC is on-route, the SDC trajectory is completely invalid, or there are + no valid on-route paths, it returns zero. + + If the SDC is off-route, this metric returns the distance to the closest valid + on-route path. If there are no valid on-route paths, it returns the distance + to the closest valid off-route path. + + .. py:attribute:: MAX_DISTANCE_TO_ROUTE_PATH + :value: 5 + + + + .. py:attribute:: MAX_RELATIVE_DISTANCE_TO_OFF_ROUTE_PATH + :value: 2 + + + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState) -> waymax.metrics.abstract_metric.MetricResult + + Computes the off route metric. + + :param simulator_state: The current simulator state of shape (....). + + :returns: A (...) MetricResult containing the metric result described above. + + :raises ValueError: If `simulator_state.sdc_paths` is undefined. + + + +.. py:class:: ProgressionMetric + + + Bases: :py:obj:`waymax.metrics.abstract_metric.AbstractMetric` + + Route progression metric for SDC. + + This metric returns a non-negative value representing the progression + towards the final logged position from the initial logged position along the + route. It first finds the closest on-route path to the SDC's current xy + position according to Euclidean distance and uses that as the reference path + to compute the progress (`arc_length`) relative to the logged SDC's initial + and final xy positions. + + If the SDC trajectory is invalid or there are no valid on-route paths, it + returns zero. + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState) -> waymax.metrics.abstract_metric.MetricResult + + Computes the progression metric. + + :param simulator_state: The current simulator state of shape. + + :returns: A (...) MetricResult containing the metric result described above. + + :raises ValueError: If `simulator_state.sdc_paths` is undefined. + + + diff --git a/_sources/autoapi/waymax/metrics/metric_factory/index.rst.txt b/_sources/autoapi/waymax/metrics/metric_factory/index.rst.txt new file mode 100644 index 0000000..e912039 --- /dev/null +++ b/_sources/autoapi/waymax/metrics/metric_factory/index.rst.txt @@ -0,0 +1,37 @@ +:py:mod:`waymax.metrics.metric_factory` +======================================= + +.. py:module:: waymax.metrics.metric_factory + +.. autoapi-nested-parse:: + + Utility function that runs all metrics according to an environment config. + + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.metrics.metric_factory.run_metrics + + + +.. py:function:: run_metrics(simulator_state: waymax.datatypes.SimulatorState, metrics_config: waymax.config.MetricsConfig) -> dict[str, waymax.metrics.abstract_metric.MetricResult] + + Runs all metrics with config flags set to True. + + :param simulator_state: The current simulator state of shape (...). + :param metrics_config: Waymax metrics config. + + :returns: + + A dictionary of metric names mapping to metric result arrays where each + metric is of shape (..., num_objects). + + diff --git a/_sources/autoapi/waymax/metrics/overlap/index.rst.txt b/_sources/autoapi/waymax/metrics/overlap/index.rst.txt new file mode 100644 index 0000000..dfd19a8 --- /dev/null +++ b/_sources/autoapi/waymax/metrics/overlap/index.rst.txt @@ -0,0 +1,55 @@ +:py:mod:`waymax.metrics.overlap` +================================ + +.. py:module:: waymax.metrics.overlap + +.. autoapi-nested-parse:: + + Metrics relating to overlaps. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.metrics.overlap.OverlapMetric + + + + +.. py:class:: OverlapMetric + + + Bases: :py:obj:`waymax.metrics.abstract_metric.AbstractMetric` + + Overlap metric. + + This metric returns 1.0 if an object's bounding box is overlapping with + that of another object. + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState) -> waymax.metrics.abstract_metric.MetricResult + + Computes a per-timestep, per-object metric from a SimulatorState. + + :param simulator_state: The current simulator state of shape (...). + + :returns: A MetricResult containing the metric result and validity mask of shape + (..., num_objects). + + + .. py:method:: compute_overlap(current_traj: waymax.datatypes.Trajectory) -> waymax.metrics.abstract_metric.MetricResult + + Computes the overlap metric. + + :param current_traj: Trajectory object containing current states of shape (..., + num_objects, num_timesteps=1). + + :returns: A (..., num_objects) MetricResult. + + + diff --git a/_sources/autoapi/waymax/metrics/roadgraph/index.rst.txt b/_sources/autoapi/waymax/metrics/roadgraph/index.rst.txt new file mode 100644 index 0000000..05ed2ae --- /dev/null +++ b/_sources/autoapi/waymax/metrics/roadgraph/index.rst.txt @@ -0,0 +1,127 @@ +:py:mod:`waymax.metrics.roadgraph` +================================== + +.. py:module:: waymax.metrics.roadgraph + +.. autoapi-nested-parse:: + + Metrics functions relating to roadgraph. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.metrics.roadgraph.WrongWayMetric + waymax.metrics.roadgraph.OffroadMetric + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.metrics.roadgraph.is_offroad + waymax.metrics.roadgraph.compute_signed_distance_to_nearest_road_edge_point + + + +.. py:class:: WrongWayMetric + + + Bases: :py:obj:`waymax.metrics.abstract_metric.AbstractMetric` + + Wrong-way metric for SDC. + + This metric checks if SDC is driving into wrong driving the wrong way or path. + It first computes the distance to the closest roadgraph point in all valid + paths that the SDC can drive along from its starting position. If the distance + is larger than the threhold WRONG_WAY_THRES, it's considered wrong-way and + returns the distance; otherwise, it's driving on the legal lanes, and returns + 0.0. + + .. py:attribute:: WRONG_WAY_THRES + :value: 3.5 + + + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState) -> waymax.metrics.abstract_metric.MetricResult + + Computes a per-timestep, per-object metric from a SimulatorState. + + :param simulator_state: The current simulator state of shape (...). + + :returns: A MetricResult containing the metric result and validity mask of shape + (..., num_objects). + + + +.. py:class:: OffroadMetric + + + Bases: :py:obj:`waymax.metrics.abstract_metric.AbstractMetric` + + Offroad metric. + + This metric returns 1.0 if the object is offroad. + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState) -> waymax.metrics.abstract_metric.MetricResult + + Computes the offroad metric. + + :param simulator_state: Updated simulator state to calculate metrics for. Will + compute the offroad metric for timestep `simulator_state.timestep`. + + :returns: + + An array containing the metric result of the same shape as the input + trajectories. The shape is (..., num_objects). + + + +.. py:function:: is_offroad(trajectory: waymax.datatypes.Trajectory, roadgraph_points: waymax.datatypes.RoadgraphPoints) -> jax.Array + + Checks if the given trajectory is offroad. + + This determines the signed distance between each bounding box corner and the + closest road edge (median or boundary). If the distance is negative, then the + trajectory is onroad else offroad. + + :param trajectory: Agent trajectories to test to see if they are on or off road of + shape (..., num_objects). The bounding boxes derived from center and shape + of the trajectory will be used to determine if any point in the box is + offroad. + :param roadgraph_points: All of the roadgraph points in the run segment of shape + (..., num_points). Roadgraph points of type `ROAD_EDGE_BOUNDARY` and + `ROAD_EDGE_MEDIAN` are used to do the check. + + :returns: a bool array with the shape (..., num_objects). The value is + True if the bbox is offroad. + :rtype: agent_mask + + +.. py:function:: compute_signed_distance_to_nearest_road_edge_point(query_points: jax.Array, roadgraph_points: waymax.datatypes.RoadgraphPoints, z_stretch: float = 2.0) -> jax.Array + + Computes the signed distance from a set of queries to roadgraph points. + + :param query_points: A set of query points for the metric of shape + (num_query_points, 3). + :param roadgraph_points: A set of roadgraph points of shape (num_points). + :param z_stretch: Tolerance in the z dimension which determines how close to + associate points in the roadgraph. This is used to fix problems with + overpasses. + + :returns: + + Signed distances of the query points with the closest road edge points of + shape (num_query_points). If the value is negative, it means that the + actor is on the correct side of the road, if it is positive, it is + considered `offroad`. + + diff --git a/_sources/autoapi/waymax/metrics/route/index.rst.txt b/_sources/autoapi/waymax/metrics/route/index.rst.txt new file mode 100644 index 0000000..7c9efd6 --- /dev/null +++ b/_sources/autoapi/waymax/metrics/route/index.rst.txt @@ -0,0 +1,108 @@ +:py:mod:`waymax.metrics.route` +============================== + +.. py:module:: waymax.metrics.route + +.. autoapi-nested-parse:: + + Metrics relating to route. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.metrics.route.ProgressionMetric + waymax.metrics.route.OffRouteMetric + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.metrics.route.FULL_PROGRESS_VALUE + + +.. py:data:: FULL_PROGRESS_VALUE + :value: 1.0 + + + +.. py:class:: ProgressionMetric + + + Bases: :py:obj:`waymax.metrics.abstract_metric.AbstractMetric` + + Route progression metric for SDC. + + This metric returns a non-negative value representing the progression + towards the final logged position from the initial logged position along the + route. It first finds the closest on-route path to the SDC's current xy + position according to Euclidean distance and uses that as the reference path + to compute the progress (`arc_length`) relative to the logged SDC's initial + and final xy positions. + + If the SDC trajectory is invalid or there are no valid on-route paths, it + returns zero. + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState) -> waymax.metrics.abstract_metric.MetricResult + + Computes the progression metric. + + :param simulator_state: The current simulator state of shape. + + :returns: A (...) MetricResult containing the metric result described above. + + :raises ValueError: If `simulator_state.sdc_paths` is undefined. + + + +.. py:class:: OffRouteMetric + + + Bases: :py:obj:`waymax.metrics.abstract_metric.AbstractMetric` + + Off-route metric for the SDC. + + The SDC is considered off-route either if 1) it is farther than + MAX_DISTANCE_TO_ROUTE_PATH from the closest on-route path, or 2) it is farther + from the closest on-route path than the closest off-route path by + MAX_RELATIVE_DISTANCE_TO_OFF_ROUTE_PATH. + + If the SDC is on-route, the SDC trajectory is completely invalid, or there are + no valid on-route paths, it returns zero. + + If the SDC is off-route, this metric returns the distance to the closest valid + on-route path. If there are no valid on-route paths, it returns the distance + to the closest valid off-route path. + + .. py:attribute:: MAX_DISTANCE_TO_ROUTE_PATH + :value: 5 + + + + .. py:attribute:: MAX_RELATIVE_DISTANCE_TO_OFF_ROUTE_PATH + :value: 2 + + + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState) -> waymax.metrics.abstract_metric.MetricResult + + Computes the off route metric. + + :param simulator_state: The current simulator state of shape (....). + + :returns: A (...) MetricResult containing the metric result described above. + + :raises ValueError: If `simulator_state.sdc_paths` is undefined. + + + diff --git a/_sources/autoapi/waymax/rewards/abstract_reward_function/index.rst.txt b/_sources/autoapi/waymax/rewards/abstract_reward_function/index.rst.txt new file mode 100644 index 0000000..ec3f161 --- /dev/null +++ b/_sources/autoapi/waymax/rewards/abstract_reward_function/index.rst.txt @@ -0,0 +1,47 @@ +:py:mod:`waymax.rewards.abstract_reward_function` +================================================= + +.. py:module:: waymax.rewards.abstract_reward_function + +.. autoapi-nested-parse:: + + Abstract reward function definition. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.rewards.abstract_reward_function.AbstractRewardFunction + + + + +.. py:class:: AbstractRewardFunction + + + Bases: :py:obj:`abc.ABC` + + Abstract class that ensures a certain reward function design. + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState, action: waymax.datatypes.Action, agent_mask: jax.Array) -> jax.Array + :abstractmethod: + + Computes rewards for every agent given a sim state and action. + + :param simulator_state: State of the Waymax environment. + :param action: Action taken to control the agent(s) (..., num_objects, + action_space). + :param agent_mask: Binary mask indicating which agent inputs are valid (..., + num_objects). + + :returns: An array of rewards, where there is one reward per agent + (..., num_objects). + + + diff --git a/_sources/autoapi/waymax/rewards/index.rst.txt b/_sources/autoapi/waymax/rewards/index.rst.txt new file mode 100644 index 0000000..9f01527 --- /dev/null +++ b/_sources/autoapi/waymax/rewards/index.rst.txt @@ -0,0 +1,80 @@ +:py:mod:`waymax.rewards` +======================== + +.. py:module:: waymax.rewards + +.. autoapi-nested-parse:: + + Reward functions. + + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + abstract_reward_function/index.rst + linear_combination_reward/index.rst + + +Package Contents +---------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.rewards.AbstractRewardFunction + waymax.rewards.LinearCombinationReward + + + + +.. py:class:: AbstractRewardFunction + + + Bases: :py:obj:`abc.ABC` + + Abstract class that ensures a certain reward function design. + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState, action: waymax.datatypes.Action, agent_mask: jax.Array) -> jax.Array + :abstractmethod: + + Computes rewards for every agent given a sim state and action. + + :param simulator_state: State of the Waymax environment. + :param action: Action taken to control the agent(s) (..., num_objects, + action_space). + :param agent_mask: Binary mask indicating which agent inputs are valid (..., + num_objects). + + :returns: An array of rewards, where there is one reward per agent + (..., num_objects). + + + +.. py:class:: LinearCombinationReward(config: waymax.config.LinearCombinationRewardConfig) + + + Bases: :py:obj:`waymax.rewards.abstract_reward_function.AbstractRewardFunction` + + Reward function that performs a linear combination of metrics. + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState, action: waymax.datatypes.Action, agent_mask: jax.Array) -> jax.Array + + Computes the reward as a linear combination of metrics. + + :param simulator_state: State of the Waymax environment. + :param action: Action taken to control the agent(s) (..., num_objects, + action_space). + :param agent_mask: Binary mask indicating which agent inputs are valid (..., + num_objects). + + :returns: An array of rewards, where there is one reward per agent + (..., num_objects). + + + diff --git a/_sources/autoapi/waymax/rewards/linear_combination_reward/index.rst.txt b/_sources/autoapi/waymax/rewards/linear_combination_reward/index.rst.txt new file mode 100644 index 0000000..906c673 --- /dev/null +++ b/_sources/autoapi/waymax/rewards/linear_combination_reward/index.rst.txt @@ -0,0 +1,65 @@ +:py:mod:`waymax.rewards.linear_combination_reward` +================================================== + +.. py:module:: waymax.rewards.linear_combination_reward + +.. autoapi-nested-parse:: + + Reward functions for the Waymax environment. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.rewards.linear_combination_reward.LinearCombinationReward + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.rewards.linear_combination_reward._validate_reward_metrics + waymax.rewards.linear_combination_reward._linear_config_to_metric_config + + + +.. py:class:: LinearCombinationReward(config: waymax.config.LinearCombinationRewardConfig) + + + Bases: :py:obj:`waymax.rewards.abstract_reward_function.AbstractRewardFunction` + + Reward function that performs a linear combination of metrics. + + .. py:method:: compute(simulator_state: waymax.datatypes.SimulatorState, action: waymax.datatypes.Action, agent_mask: jax.Array) -> jax.Array + + Computes the reward as a linear combination of metrics. + + :param simulator_state: State of the Waymax environment. + :param action: Action taken to control the agent(s) (..., num_objects, + action_space). + :param agent_mask: Binary mask indicating which agent inputs are valid (..., + num_objects). + + :returns: An array of rewards, where there is one reward per agent + (..., num_objects). + + + +.. py:function:: _validate_reward_metrics(config: waymax.config.LinearCombinationRewardConfig) + + Checks that all metrics in the RewardConfigs are valid. + + +.. py:function:: _linear_config_to_metric_config(config: waymax.config.LinearCombinationRewardConfig) -> waymax.config.MetricsConfig + + Converts a LinearCombinationRewardConfig into a MetricsConfig. + + diff --git a/_sources/autoapi/waymax/utils/geometry/index.rst.txt b/_sources/autoapi/waymax/utils/geometry/index.rst.txt new file mode 100644 index 0000000..022e2e2 --- /dev/null +++ b/_sources/autoapi/waymax/utils/geometry/index.rst.txt @@ -0,0 +1,185 @@ +:py:mod:`waymax.utils.geometry` +=============================== + +.. py:module:: waymax.utils.geometry + +.. autoapi-nested-parse:: + + JIT-able util functions on array (data_struct agnostic). + + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.utils.geometry.rotation_matrix_2d + waymax.utils.geometry.rotate_points + waymax.utils.geometry.pose_from_xy_yaw + waymax.utils.geometry._transform_points + waymax.utils.geometry.transform_points + waymax.utils.geometry.transform_yaw + waymax.utils.geometry.transform_trajectories + waymax.utils.geometry.transform_direction + waymax.utils.geometry.corners_from_bbox + waymax.utils.geometry.corners_from_bboxes + waymax.utils.geometry.has_overlap + waymax.utils.geometry.compute_pairwise_overlaps + waymax.utils.geometry.wrap_yaws + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.utils.geometry.EPS + + +.. py:data:: EPS + :value: 1e-10 + + + +.. py:function:: rotation_matrix_2d(angle: jax.Array) -> jax.Array + + Returns a 2D rotation matrix. + + If an angle with batched dimensions is given, the result will be + a batched rotation matrix with the same leading batch dimensions as angle. + E.g. if angle is of shape (3, 7), the result will be of shape (3, 7, 2, 2) + + :param angle: Angle to rotate by. The positive direction is counter-clockwise. + + :returns: A 2x2 2D rotation matrix corresponding to angle. + + +.. py:function:: rotate_points(matrix: jax.Array, points: jax.Array) -> jax.Array + + Rotates points by a rotation matrix. + + :param matrix: Matrix specifying the rotation to apply shape (prefix, dof, dof). + :param points: Points to rotate given `matrix` of shape (prefix, ..., dof). + + :returns: A rotated set of points of shape (prefix, ..., dof). + + +.. py:function:: pose_from_xy_yaw(xy: jax.Array, yaw: jax.Array) -> tuple[jax.Array, jax.Array] + + Gets 2D transformation matrix and delta yaw with any prefix shape. + + Applying the transformation using returned values will rotate + counter-clockwise by yaw, and then translate by xy. + Example: a unit vector at xy with direction equals yaw will be at (0, 0) + with direction equals 0 after the transformation. + + :param xy: XY positions of points of shape (..., 2). + :param yaw: Orientation in radians with shape (...). + + :returns: Transformation matrix and delta yaw. Note it is used as matmul(pose_matrix, + pts). + + +.. py:function:: _transform_points(matrix: jax.Array, pts: jax.Array) -> jax.Array + + Helper function for transform_points. + + +.. py:function:: transform_points(pose_matrix: jax.Array, pts: jax.Array) -> jax.Array + + Transforms points into new coordinates with any prefix shape. + + :param pose_matrix: Matrix representing the transformation into the frame of some + pose of shape (prefix, dof+1, dof+1). + :param pts: Points to translate of shape (prefix, ..., dof). + + :returns: Points transformed by the `pose_matrix` of shape (prefix, ..., dof). + + +.. py:function:: transform_yaw(pose_yaw: jax.Array, yaw: jax.Array) -> jax.Array + + Transforms yaw with any prefix shape. + + +.. py:function:: transform_trajectories(traj: jax.Array, pose_matrix: jax.Array, pose_yaw: jax.Array) -> jax.Array + + Transforms trajectories by given pose with any prefix shape. + + :param traj: jax.Array of shape (prefix_shape, ..., dof), where prefix_shape can be + any dimensions, dof must be either 5 or 7. Dof 5 represents [x, y, length, + width, yaw], and 7 represents [x, y, length, width, yaw, vel_x, vel_y] + :param pose_matrix: Matrix representing the transformation into the frame of some + pose of shape (prefix_shape, 3, 3). + :param pose_yaw: Rotation angle of the transformation of shape (prefix_shape). + + :returns: Transformed trajectories with same shape as inputs traj. + + +.. py:function:: transform_direction(pose_matrix: jax.Array, pts_dir: jax.Array) -> jax.Array + + Transforms direction with any prefix shape. + + :param pose_matrix: Matrix representing the transformation into the frame of some + pose of shape (prefix_shape, 3, 3). + :param pts_dir: Vector direction to transform of shape (prefix_shape, ..., 2). + + :returns: Transformed direction. + + +.. py:function:: corners_from_bbox(bbox: jax.Array) -> jax.Array + + Computes corners for one 5 dof bbox. + + +.. py:function:: corners_from_bboxes(bboxes: jax.Array) -> jax.Array + + Computes corners for 5 dof bboxes with any prefix shape. + + +.. py:function:: has_overlap(bboxes_a: jax.Array, bboxes_b: jax.Array) -> jax.Array + + Checks if 5 dof bboxes (with any prefix shape) overlap with each other. + + It does a 1:1 comparison of equivalent batch indices. + + The algorithm first computes bboxes_a's projection on bboxes_b's axes and + check if there is an overlap between the projection. It then computes + bboxes_b's projection on bboxes_a's axes and check overlap. Two bboxes are + overlapped if and only if there is overlap in both steps. + + :param bboxes_a: Bounding boxes of the above format of shape (..., 5). The last + dimension represents [x, y, length, width, yaw]. + :param bboxes_b: Bounding boxes of the above format of shape (..., 5). + + :returns: + + Boolean array which specifies whether `bboxes_a` and `bboxes_b` overlap each + other of shape (...). + + +.. py:function:: compute_pairwise_overlaps(traj: jax.Array) -> jax.Array + + Computes an overlap mask among all agent pairs for all steps. + + 5 dof trajectories have [x, y, length, width, yaw] for last dimension. + + :param traj: Bounding boxes of the above format of shape (..., num_objects, + num_timesteps, 5). + + :returns: + + Boolean array of shape (..., num_objects, num_objects) which denotes whether + any of the objects in the trajectory are in overlap. + + +.. py:function:: wrap_yaws(yaws: jax.Array | tensorflow.Tensor) -> jax.Array | tensorflow.Tensor + + Wraps yaw angles between pi and -pi radians. + + diff --git a/_sources/autoapi/waymax/utils/index.rst.txt b/_sources/autoapi/waymax/utils/index.rst.txt new file mode 100644 index 0000000..8739ac4 --- /dev/null +++ b/_sources/autoapi/waymax/utils/index.rst.txt @@ -0,0 +1,16 @@ +:py:mod:`waymax.utils` +====================== + +.. py:module:: waymax.utils + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + geometry/index.rst + test_utils/index.rst + + diff --git a/_sources/autoapi/waymax/utils/test_utils/index.rst.txt b/_sources/autoapi/waymax/utils/test_utils/index.rst.txt new file mode 100644 index 0000000..1a07201 --- /dev/null +++ b/_sources/autoapi/waymax/utils/test_utils/index.rst.txt @@ -0,0 +1,135 @@ +:py:mod:`waymax.utils.test_utils` +================================= + +.. py:module:: waymax.utils.test_utils + +.. autoapi-nested-parse:: + + Helper functions and utilities to provide test states for Waymax. + + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.utils.test_utils.make_test_dataset + waymax.utils.test_utils.simulated_trajectory_no_overlap + waymax.utils.test_utils.simulated_trajectory_with_overlap + waymax.utils.test_utils.simulator_state_with_overlap + waymax.utils.test_utils.create_metadata + waymax.utils.test_utils.create_invalid_traffic_lights + waymax.utils.test_utils.simulator_state_with_offroad + waymax.utils.test_utils.simulator_state_without_offroad + waymax.utils.test_utils.create_test_map_element + waymax.utils.test_utils.create_test_trajectory_from_position + waymax.utils.test_utils.make_zeros_state + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.utils.test_utils.ROUTE_DATA_PATH + waymax.utils.test_utils.ROUTE_NUM_PATHS + waymax.utils.test_utils.ROUTE_NUM_POINTS_PER_PATH + + +.. py:data:: ROUTE_DATA_PATH + + + +.. py:data:: ROUTE_NUM_PATHS + :value: 30 + + + +.. py:data:: ROUTE_NUM_POINTS_PER_PATH + :value: 200 + + + +.. py:function:: make_test_dataset(batch_dims: tuple[int, Ellipsis] = (), **kwargs) + + Create a test dataset using ROUTE_DATA_PATH. + + +.. py:function:: simulated_trajectory_no_overlap() -> waymax.datatypes.Trajectory + + Creates a simulated trajectory where there are no object overlaps. + + +.. py:function:: simulated_trajectory_with_overlap() -> waymax.datatypes.Trajectory + + Creates a simulated trajectory where the SDC has overlaps. + + +.. py:function:: simulator_state_with_overlap() -> waymax.datatypes.SimulatorState + + Creates a simulator state where the SDC has overlaps. + + +.. py:function:: create_metadata(num_agents: int) -> waymax.datatypes.ObjectMetadata + + Returns a sample metadata object. + + +.. py:function:: create_invalid_traffic_lights() -> waymax.datatypes.TrafficLights + + Creates invalid traffic lights with shape (). + + +.. py:function:: simulator_state_with_offroad() -> waymax.datatypes.SimulatorState + + Creates a simulator state with the SDC off of the road. + + +.. py:function:: simulator_state_without_offroad() -> waymax.datatypes.SimulatorState + + Creates a simulator state with the SDC off of the road. + + +.. py:function:: create_test_map_element(element_type: waymax.datatypes.MapElementIds, position: collections.abc.Sequence[float], direction: collections.abc.Sequence[float], repeat: int = 1) -> waymax.datatypes.RoadgraphPoints + + Creates a test map element given specification in arguments. + + :param element_type: Type of map element to create. See + `waymax/open_motion_data/constants.py` for the list of map elements. + :param position: 3D position of the map element in the global coordinate system in + meters. + :param direction: 3D direction of the map elemnt (i.e. direction of lane) in the + global coordinate system in meters. + :param repeat: How many times to repeat the map element. + + :returns: + + A map element with `repeat` number of versions that contains the fields + specified in the arguments. + + :raises ValueError: If the provided `position` does not have three elements. + :raises ValueError: If the provided `direction` does not have three elements. + + +.. py:function:: create_test_trajectory_from_position(position: collections.abc.Sequence[float] = (1.0, 1.0, 1.0)) -> waymax.datatypes.Trajectory + + Creates a trajectory with default metadata fields at a position. + + :param position: Position of the trajectory point in global coordinates. + + :returns: Filled trajectory with a single point at `position`. + + :raises If `position` does not have three elements.: + + +.. py:function:: make_zeros_state(config: waymax.config.DatasetConfig) -> waymax.datatypes.simulator_state.SimulatorState + + Returns a SimulatorState containing zeros. + + diff --git a/_sources/autoapi/waymax/visualization/color/index.rst.txt b/_sources/autoapi/waymax/visualization/color/index.rst.txt new file mode 100644 index 0000000..7ca0581 --- /dev/null +++ b/_sources/autoapi/waymax/visualization/color/index.rst.txt @@ -0,0 +1,26 @@ +:py:mod:`waymax.visualization.color` +==================================== + +.. py:module:: waymax.visualization.color + +.. autoapi-nested-parse:: + + Defines the color for different components. + + + +Module Contents +--------------- + +.. py:data:: TRAFFIC_LIGHT_COLORS + + + +.. py:data:: ROAD_GRAPH_COLORS + + + +.. py:data:: COLOR_DICT + + + diff --git a/_sources/autoapi/waymax/visualization/index.rst.txt b/_sources/autoapi/waymax/visualization/index.rst.txt new file mode 100644 index 0000000..995f225 --- /dev/null +++ b/_sources/autoapi/waymax/visualization/index.rst.txt @@ -0,0 +1,123 @@ +:py:mod:`waymax.visualization` +============================== + +.. py:module:: waymax.visualization + +.. autoapi-nested-parse:: + + Matplotlib-based Waymax visualization tool. + + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + color/index.rst + utils/index.rst + viz/index.rst + + +Package Contents +---------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.visualization.plot_observation + waymax.visualization.plot_roadgraph_points + waymax.visualization.plot_simulator_state + waymax.visualization.plot_single_agent_brax_timestep + waymax.visualization.plot_traffic_light_signals_as_points + waymax.visualization.plot_trajectory + + + +.. py:function:: plot_observation(obs: waymax.datatypes.Observation, obj_idx: int, viz_config: Optional[dict[str, Any]] = None, batch_idx: int = -1, highlight_obj: waymax.config.ObjectType = waymax_config.ObjectType.SDC) -> numpy.ndarray + + Plots np array image for an Observation. + + :param obs: An Observation instance, with shape (..., obs_A), where obs_A + represents the number of objects that have observation view over things + including other objects, roadgraph, and traffic lights. + :param obj_idx: The object index in obs_A. + :param viz_config: Dict for optional config. + :param batch_idx: Optional batch index. + :param highlight_obj: Represents the type of objects that will be highlighted with + `color.COLOR_DICT['controlled']` color. + + :returns: np image. + + +.. py:function:: plot_roadgraph_points(ax: matplotlib.axes.Axes, rg_pts: waymax.datatypes.RoadgraphPoints, verbose: bool = False) -> None + + Plots road graph as points. + + :param ax: matplotlib axes. + :param rg_pts: a RoadgraphPoints with shape (1,) + :param verbose: print roadgraph points count if set to True. + + +.. py:function:: plot_simulator_state(state: waymax.datatypes.SimulatorState, use_log_traj: bool = True, viz_config: Optional[dict[str, Any]] = None, batch_idx: int = -1, highlight_obj: waymax.config.ObjectType = waymax_config.ObjectType.SDC) -> numpy.ndarray + + Plots np array image for SimulatorState. + + :param state: A SimulatorState instance. + :param use_log_traj: Set True to use logged trajectory, o/w uses simulated + trajectory. + :param viz_config: dict for optional config. + :param batch_idx: optional batch index. + :param highlight_obj: Represents the type of objects that will be highlighted with + `color.COLOR_DICT['controlled']` color. + + :returns: np image. + + +.. py:function:: plot_single_agent_brax_timestep(waymax_ts: waymax.env.wrappers.brax_wrapper.TimeStep, use_log_traj: bool = False, viz_config: Optional[dict[str, Any]] = None, batch_idx: int = -1) -> numpy.ndarray + + Plots np array image for Brax TimeStep with metrics. + + Currently only for single-agent env outputs. + + :param waymax_ts: Timestep returned from Waymax env step or reset. + :param use_log_traj: Set True to use logged trajectory, o/w uses simulated + trajectory. + :param viz_config: dict for optional config. + :param batch_idx: optional batch index. + + :returns: np image. + + +.. py:function:: plot_traffic_light_signals_as_points(ax: matplotlib.axes.Axes, tls: waymax.datatypes.TrafficLights, timestep: int = 0, verbose: bool = False) -> None + + Plots traffic lights for timestep. + + :param ax: matplotlib axes. + :param tls: a TrafficLightStates to show. + :param timestep: draw traffi lights at this given timestep. + :param verbose: print traffic lights count if set to True. + + +.. py:function:: plot_trajectory(ax: matplotlib.axes.Axes, traj: waymax.datatypes.Trajectory, is_controlled: numpy.ndarray, time_idx: Optional[int] = None, indices: Optional[numpy.ndarray] = None) -> None + + Plots a Trajectory with different color for controlled and context. + + Plots the full bounding_boxes only for time_idx step, overlap is + highlighted. + + Notation: A: number of agents; T: numbe of time steps; 5 degree of freedom: + center x, center y, length, width, yaw. + + :param ax: matplotlib axes. + :param traj: a Trajectory with shape (A, T). + :param is_controlled: binary mask for controlled object, shape (A,). + :param time_idx: step index to highlight bbox, -1 for last step. Default(None) for + not showing bbox. + :param indices: ids to show for each agents if not None, shape (A,). + + diff --git a/_sources/autoapi/waymax/visualization/utils/index.rst.txt b/_sources/autoapi/waymax/visualization/utils/index.rst.txt new file mode 100644 index 0000000..c87feb3 --- /dev/null +++ b/_sources/autoapi/waymax/visualization/utils/index.rst.txt @@ -0,0 +1,135 @@ +:py:mod:`waymax.visualization.utils` +==================================== + +.. py:module:: waymax.visualization.utils + +.. autoapi-nested-parse:: + + General visualization functions for non-waymax data using matplotlib. + + Note there is no batch dimension, and should not rely on any customized data + structure. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + waymax.visualization.utils.VizConfig + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.visualization.utils.init_fig_ax_via_size + waymax.visualization.utils.init_fig_ax + waymax.visualization.utils.center_at_xy + waymax.visualization.utils.img_from_fig + waymax.visualization.utils.save_img_as_png + waymax.visualization.utils.get_n_colors + waymax.visualization.utils.plot_numpy_bounding_boxes + + + +.. py:class:: VizConfig + + + Config for visualization. + + .. py:attribute:: front_x + :type: float + :value: 75.0 + + + + .. py:attribute:: back_x + :type: float + :value: 75.0 + + + + .. py:attribute:: front_y + :type: float + :value: 75.0 + + + + .. py:attribute:: back_y + :type: float + :value: 75.0 + + + + .. py:attribute:: px_per_meter + :type: float + :value: 4.0 + + + + .. py:attribute:: show_agent_id + :type: bool + :value: True + + + + .. py:attribute:: center_agent_idx + :type: int + + + + .. py:attribute:: verbose + :type: bool + :value: True + + + + +.. py:function:: init_fig_ax_via_size(x_px: float, y_px: float) -> tuple[matplotlib.figure.Figure, matplotlib.axes.Axes] + + Initializes a figure with given size in pixel. + + +.. py:function:: init_fig_ax(vis_config: VizConfig = VizConfig()) -> tuple[matplotlib.figure.Figure, matplotlib.axes.Axes] + + Initializes a figure with vis_config. + + +.. py:function:: center_at_xy(ax: matplotlib.axes.Axes, xy: numpy.ndarray, vis_config: VizConfig = VizConfig()) -> None + + +.. py:function:: img_from_fig(fig: matplotlib.figure.Figure) -> numpy.ndarray + + Returns a [H, W, 3] uint8 np image from fig.canvas.tostring_rgb(). + + +.. py:function:: save_img_as_png(img: numpy.ndarray, filename: str = '/tmp/img.png') + + Saves np image to disk. + + +.. py:function:: get_n_colors(num_color: int = 10, max_hue: float = 1.0, saturation: float = 1.0, lightness: float = 1.0) -> numpy.ndarray + + Gets n different colors. + + +.. py:function:: plot_numpy_bounding_boxes(ax: matplotlib.axes.Axes, bboxes: numpy.ndarray, color: numpy.ndarray, alpha: Optional[float] = 1.0, as_center_pts: bool = False) -> None + + Plots multiple bounding boxes. + + :param ax: Fig handles. + :param bboxes: Shape (num_bbox, 5), with last dimension as (x, y, length, width, + yaw). + :param color: Shape (3,), represents RGB color for drawing. + :param alpha: Alpha value for drawing, i.e. 0 means fully transparent. + :param as_center_pts: If set to True, bboxes will be drawn as center points, + instead of full bboxes. + + diff --git a/_sources/autoapi/waymax/visualization/viz/index.rst.txt b/_sources/autoapi/waymax/visualization/viz/index.rst.txt new file mode 100644 index 0000000..ea3685c --- /dev/null +++ b/_sources/autoapi/waymax/visualization/viz/index.rst.txt @@ -0,0 +1,149 @@ +:py:mod:`waymax.visualization.viz` +================================== + +.. py:module:: waymax.visualization.viz + +.. autoapi-nested-parse:: + + Visualization functions for Waymax data structures. + + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + waymax.visualization.viz._plot_bounding_boxes + waymax.visualization.viz._index_pytree + waymax.visualization.viz.plot_trajectory + waymax.visualization.viz.plot_roadgraph_points + waymax.visualization.viz.plot_traffic_light_signals_as_points + waymax.visualization.viz._plot_path_points + waymax.visualization.viz.plot_simulator_state + waymax.visualization.viz.plot_observation + waymax.visualization.viz.plot_single_agent_brax_timestep + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + waymax.visualization.viz._RoadGraphShown + waymax.visualization.viz._RoadGraphDefaultColor + + +.. py:data:: _RoadGraphShown + :value: (1, 2, 3, 15, 16, 17, 18, 19) + + + +.. py:data:: _RoadGraphDefaultColor + :value: (0.9, 0.9, 0.9) + + + +.. py:function:: _plot_bounding_boxes(ax: matplotlib.axes.Axes, traj_5dof: numpy.ndarray, time_idx: int, is_controlled: numpy.ndarray, valid: numpy.ndarray) -> None + + Helper function to plot multiple bounding boxes across time. + + +.. py:function:: _index_pytree(inputs: Any, idx: int) -> Any + + Helper function to get idx-th example in a batch. + + +.. py:function:: plot_trajectory(ax: matplotlib.axes.Axes, traj: waymax.datatypes.Trajectory, is_controlled: numpy.ndarray, time_idx: Optional[int] = None, indices: Optional[numpy.ndarray] = None) -> None + + Plots a Trajectory with different color for controlled and context. + + Plots the full bounding_boxes only for time_idx step, overlap is + highlighted. + + Notation: A: number of agents; T: numbe of time steps; 5 degree of freedom: + center x, center y, length, width, yaw. + + :param ax: matplotlib axes. + :param traj: a Trajectory with shape (A, T). + :param is_controlled: binary mask for controlled object, shape (A,). + :param time_idx: step index to highlight bbox, -1 for last step. Default(None) for + not showing bbox. + :param indices: ids to show for each agents if not None, shape (A,). + + +.. py:function:: plot_roadgraph_points(ax: matplotlib.axes.Axes, rg_pts: waymax.datatypes.RoadgraphPoints, verbose: bool = False) -> None + + Plots road graph as points. + + :param ax: matplotlib axes. + :param rg_pts: a RoadgraphPoints with shape (1,) + :param verbose: print roadgraph points count if set to True. + + +.. py:function:: plot_traffic_light_signals_as_points(ax: matplotlib.axes.Axes, tls: waymax.datatypes.TrafficLights, timestep: int = 0, verbose: bool = False) -> None + + Plots traffic lights for timestep. + + :param ax: matplotlib axes. + :param tls: a TrafficLightStates to show. + :param timestep: draw traffi lights at this given timestep. + :param verbose: print traffic lights count if set to True. + + +.. py:function:: _plot_path_points(ax: matplotlib.axes.Axes, paths: waymax.datatypes.Paths) -> None + + Plots on/off route paths. + + +.. py:function:: plot_simulator_state(state: waymax.datatypes.SimulatorState, use_log_traj: bool = True, viz_config: Optional[dict[str, Any]] = None, batch_idx: int = -1, highlight_obj: waymax.config.ObjectType = waymax_config.ObjectType.SDC) -> numpy.ndarray + + Plots np array image for SimulatorState. + + :param state: A SimulatorState instance. + :param use_log_traj: Set True to use logged trajectory, o/w uses simulated + trajectory. + :param viz_config: dict for optional config. + :param batch_idx: optional batch index. + :param highlight_obj: Represents the type of objects that will be highlighted with + `color.COLOR_DICT['controlled']` color. + + :returns: np image. + + +.. py:function:: plot_observation(obs: waymax.datatypes.Observation, obj_idx: int, viz_config: Optional[dict[str, Any]] = None, batch_idx: int = -1, highlight_obj: waymax.config.ObjectType = waymax_config.ObjectType.SDC) -> numpy.ndarray + + Plots np array image for an Observation. + + :param obs: An Observation instance, with shape (..., obs_A), where obs_A + represents the number of objects that have observation view over things + including other objects, roadgraph, and traffic lights. + :param obj_idx: The object index in obs_A. + :param viz_config: Dict for optional config. + :param batch_idx: Optional batch index. + :param highlight_obj: Represents the type of objects that will be highlighted with + `color.COLOR_DICT['controlled']` color. + + :returns: np image. + + +.. py:function:: plot_single_agent_brax_timestep(waymax_ts: waymax.env.wrappers.brax_wrapper.TimeStep, use_log_traj: bool = False, viz_config: Optional[dict[str, Any]] = None, batch_idx: int = -1) -> numpy.ndarray + + Plots np array image for Brax TimeStep with metrics. + + Currently only for single-agent env outputs. + + :param waymax_ts: Timestep returned from Waymax env step or reset. + :param use_log_traj: Set True to use logged trajectory, o/w uses simulated + trajectory. + :param viz_config: dict for optional config. + :param batch_idx: optional batch index. + + :returns: np image. + + diff --git a/_sources/build/jupyter_execute/build/jupyter_execute/notebooks/data_demo.ipynb.txt b/_sources/build/jupyter_execute/build/jupyter_execute/notebooks/data_demo.ipynb.txt new file mode 100644 index 0000000..3e80b3c --- /dev/null +++ b/_sources/build/jupyter_execute/build/jupyter_execute/notebooks/data_demo.ipynb.txt @@ -0,0 +1,136 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "rNYRA6k8Qfyo" + }, + "source": [ + "# Scenario Data Loading\n", + "\n", + "This tutorial demonstrates how to load scenario data from the Waymo Open Motion Dataset (WOMD) using the Waymax dataloader." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MtgRcYqmtMwD" + }, + "outputs": [], + "source": [ + "%%capture\n", + "import numpy as np\n", + "import mediapy\n", + "from tqdm import tqdm\n", + "import dataclasses\n", + "\n", + "from waymax import config as _config\n", + "from waymax import dataloader\n", + "from waymax import datatypes\n", + "from waymax import visualization" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0o2sAapxRMAT" + }, + "source": [ + "\n", + "We first create a dataset config, using the default configs provided in the `waymax.config` module. In particular, `config.WOD_1_1_0_TRAINING` is a pre-defined configuration that points to version 1.1.0 of the Waymo Open Dataset.\n", + "\n", + "The data config contains a number of options to configure how and where the dataset is loaded from. By default, the `WOD_1_1_0_TRAINING` loads up to 128 objects (e.g. vehicles, pedestrians) per scenario. Here, we can save memory and compute by loading only the first 32 objects stored in the scenario.\n", + "\n", + "We use the `dataloader.simulator_state_generator` function to create an iterator\n", + "through Open Motion Dataset scenarios. Calling next on the iterator will retrieve the first scenario in the dataset.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "dkJwTuSLr0gh" + }, + "outputs": [], + "source": [ + "config = dataclasses.replace(_config.WOD_1_1_0_TRAINING, max_num_objects=32)\n", + "data_iter = dataloader.simulator_state_generator(config=config)\n", + "scenario = next(data_iter)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "q1xyeYpLR8J6" + }, + "source": [ + "Next, we can plot the initial state of this scenario. We use a matplotlib-based visualization available in the `waymax.visualization` package." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "OY3-OOArsFcU" + }, + "outputs": [], + "source": [ + "# Using logged trajectory\n", + "img = visualization.plot_simulator_state(scenario, use_log_traj=True)\n", + "mediapy.show_image(img)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "H0Z15epRSC23" + }, + "source": [ + "The Waymo Open Motion Dataset consists of 9-second trajectory snippets. We can visualize the entire logged trajectory as a video as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "06SjvXdRrV3N" + }, + "outputs": [], + "source": [ + "imgs = []\n", + "\n", + "state = scenario\n", + "for _ in range(scenario.remaining_timesteps):\n", + " state = datatypes.update_state_by_log(state, num_steps=1)\n", + " imgs.append(visualization.plot_simulator_state(state, use_log_traj=True))\n", + "\n", + "mediapy.show_video(imgs, fps=10)" + ] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "private_outputs": true, + "provenance": [ + { + "file_id": "14w5MbrMNLsOsLuD5kXy5-rrNO3ZgsHat", + "timestamp": 1678404744504 + } + ] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/_sources/build/jupyter_execute/build/jupyter_execute/notebooks/datatypes_demo.ipynb.txt b/_sources/build/jupyter_execute/build/jupyter_execute/notebooks/datatypes_demo.ipynb.txt new file mode 100644 index 0000000..d18cf0a --- /dev/null +++ b/_sources/build/jupyter_execute/build/jupyter_execute/notebooks/datatypes_demo.ipynb.txt @@ -0,0 +1,300 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "0IwlAjlibuUE" + }, + "source": [ + "# Understanding and Manipulating Data in Waymax\n", + "\n", + "This tutorial covers data structures in Waymax." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1NTUsEy_btuU" + }, + "outputs": [], + "source": [ + "%%capture\n", + "import dataclasses\n", + "import jax\n", + "from jax import numpy as jnp\n", + "from matplotlib import pyplot as plt\n", + "\n", + "from waymax import config as _config\n", + "from waymax import dataloader\n", + "from waymax import datatypes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "2pcSuBb9cFZN" + }, + "outputs": [], + "source": [ + "# Load example data.\n", + "config = dataclasses.replace(_config.WOD_1_1_0_VALIDATION, max_num_objects=32)\n", + "data_iter = dataloader.simulator_state_generator(config=config)\n", + "scenario = next(data_iter)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GZAFy24xcLQc" + }, + "source": [ + "## JAX-based data structures\n", + "\n", + "The key property with all data structures in Waymax is that all data is *immutable*. This is a design decision that is inherited from JAX and enables code written using Waymax to be compatible with the powerful functional transforms in JAX, such as `jit`, `vmap`, etc. While efficiency may be a concern with immutable data structures, wrapping your function in `jax.jit` will allow JAX to optimize and replace your operations with in-place operations wherever possible, avoiding the need for excessive data copying.\n", + "\n", + "Additionally, all datastructures in Waymax are implemented as dataclasses. This allows convenient named access to fields, and allows simple nesting of data structures that is easy to manipulate with tree-based operations (such as those in `jax.tree_util`).\n", + "\n", + "The first example we will cover is the `datatypes.Trajectory` data structure, which holds the pose information for all objects. The scenario that we loaded contains a trajectory containing the logged behavior for all agents under the `scenario.log_trajectory` attribute." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "sPMaI3ShdPwB" + }, + "outputs": [], + "source": [ + "log_trajectory = scenario.log_trajectory\n", + "\n", + "# Number of objects stored in this trajectory.\n", + "print('Number of objects:', log_trajectory.num_objects)\n", + "print('Number of timesteps:', log_trajectory.num_timesteps)\n", + "print('Trajectory shape (num_objects, num_timesteps):', log_trajectory.shape)\n", + "print('XYZ positions (num_objects, num_timesteps, 3):', log_trajectory.xyz.shape)\n", + "print('XY velocities (num_objects, num_timesteps, 2):', log_trajectory.vel_xy.shape)\n", + "print('Yaw (num_objects, num_timesteps):', log_trajectory.yaw.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nsKEO7GCelBK" + }, + "source": [ + "The `datatypes` module contains some helper methods that automatically map over datastructures. We can use `datatypes.dynamic_slice` to select out the trajectory belonging to a particular object or at a particular timestep. These operations, as with all JAX operations, will return new copies of the object they are modifying. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "k6cWGuVRdlQk" + }, + "outputs": [], + "source": [ + "# Slice by time. Select the trajectory at timestep 23.\n", + "traj_t23 = datatypes.dynamic_slice(log_trajectory, start_index=23, slice_size=1, axis=-1)\n", + "print('XYZ positions (num_objects, 1, 3):', traj_t23.xyz.shape)\n", + "\n", + "# Slice by object. Select the trajectory for object 15.\n", + "traj_obj15 = datatypes.dynamic_slice(log_trajectory, start_index=15, slice_size=1, axis=-2)\n", + "print('XYZ positions (1, num_timesteps, 3):', traj_obj15.xyz.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LXrPjhzPf7EJ" + }, + "source": [ + "Of course, JAX functions from the core library also work on Waymax data structures. The `tree_map` function is particularly useful for working with dataclasses, and will apply a single function to all fields in the data structure (recursively if there are nested data structures)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "shVRPmToe9oY" + }, + "outputs": [], + "source": [ + "def max_along_time(x: jax.Array) -> jax.Array:\n", + " return jnp.max(x, axis=-1, keepdims=True)\n", + "\n", + "max_trajectory = jax.tree_util.tree_map(max_along_time, log_trajectory)\n", + "print('XYZ positions (num_objects, 1, 3):', max_trajectory.xyz.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mlPkVLA7Mcx-" + }, + "source": [ + "To modify the values of the data structure, we can use `dataclasses.replace` to replace entire fields, and `Array.at[idx].set(value)` to selectively modify individual values. For example, to set the all yaws for object 1 to zero, we can use the following code snippet:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Z5napP9gMutj" + }, + "outputs": [], + "source": [ + "zeroed_traj = dataclasses.replace(\n", + " log_trajectory, \n", + " yaw=log_trajectory.yaw.at[1].set(0.0)\n", + ")\n", + "\n", + "# Should be the original values.\n", + "print('Yaws for object 0, timesteps 0 to 5:', zeroed_traj.yaw[0, 0:5])\n", + "\n", + "# Should be now set to 0.\n", + "print('Yaws for object 1, timesteps 0 to 5:', zeroed_traj.yaw[1, 0:5])\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "E7DZS_BKhZLj" + }, + "source": [ + "# Other important data structures\n", + "\n", + "We will now cover the remaining important data structures that are stored in a scenario.\n", + "\n", + "The `datatypes.RoadgraphPoints` data structure holds all static information regarding the road and environment. This includes all lanes markers, road edges, stop signs, speed bumps, and crosswalks. \n", + "- The `x`, `y`, and `z` attributes define the spatial coordinates of the points.\n", + "- The `type` attribute is an integer that defines what type of point (lane, edge, stop sign, etc.) the point is. See `roadgraph_samples/type` of the [Waymo Open Motion Dataset](https://waymo.com/open/data/motion/tfexample) for definitions of which value corresponds to what type of point.\n", + "- The `dir_x` and `dir_y` attributes define the orientation of the points. Lane points will orient in the forward direction of the lane. Edge points are oriented such that the inside of the road is always on the port side (left if facing forward) of the point.\n", + "- The `id` field is a unique identifier for each contiguous lane. Lanes end if there is an intersection or reach the edge of the map." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "k9XQmInAh4vo" + }, + "outputs": [], + "source": [ + "# Plot the roadgraph, with colors corresponding to the road type.\n", + "rg_points = scenario.roadgraph_points\n", + "\n", + "where_valid = rg_points.valid\n", + "plt.scatter(\n", + " x = rg_points.x[where_valid],\n", + " y = rg_points.y[where_valid],\n", + " s=0.1,\n", + " c = rg_points.types[where_valid]\n", + ")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2MdtujKbjahp" + }, + "source": [ + "The `datatypes.TrafficLights` structure holds time-varying information regarding the color and position of the traffic lights.\n", + "- The `x`, `y`, and `z` attributes define the spatial location of the light.\n", + "- The `state` attribute defines what color the light is at a particular instance in time.\n", + "- The `lane_ids` attribute tells what lanes the traffic light is controlling. These can be cross-referenced with the `RoadgraphPoints.ids` field." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "zse6OxgaiEbH" + }, + "outputs": [], + "source": [ + "traffic_lights = scenario.log_traffic_light\n", + "\n", + "print('Traffic Light States (num_lights, num_timesteps):', traffic_lights.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QY-rhD1Qkr-C" + }, + "source": [ + "Finally, `datatypes.ObjectMetadata` holds \n", + "- The `object_types` attribute defines whether the object is a vehicle, pedestrian, or cyclist.\n", + "- The `ids` attribute assigns a unique ID to each object.\n", + "- The `is_sdc` attribute defines whether the object is the ego-vehicle (or self-driving car).\n", + "- The `is_modeled` attribute marks whether the object's behavior is meant to be predicted as part of the Waymo Open Motion dataset.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Dohh8d9Rkh70" + }, + "outputs": [], + "source": [ + "metadata = scenario.object_metadata\n", + "print('All object IDS:', metadata.ids)\n", + "\n", + "# Color-code object trajectory by whether it is the SDC or not.\n", + "# The SDC trajectory in the center is shown in blue, and all other trajectories\n", + "# are shown in red.\n", + "flat_trajectory = jax.tree_util.tree_map(lambda x: jnp.reshape(x, [-1]), log_trajectory)\n", + "colors = jnp.zeros(log_trajectory.shape, dtype=jnp.int32).at[metadata.is_sdc].set(1)\n", + "colors = jnp.reshape(colors, [-1])\n", + "\n", + "where_valid = flat_trajectory.valid\n", + "plt.scatter(\n", + " x=flat_trajectory.x[where_valid],\n", + " y=flat_trajectory.y[where_valid],\n", + " s=0.5,\n", + " c=colors[where_valid],\n", + " cmap='RdYlBu'\n", + ")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "hmNQVj-clfIr" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "private_outputs": true, + "provenance": [ + { + "file_id": "1Ee2coesuhg82e7E9HmZxafZ7raD_ug5q", + "timestamp": 1683520258687 + } + ] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/_sources/build/jupyter_execute/build/jupyter_execute/notebooks/multi_actors_demo.ipynb.txt b/_sources/build/jupyter_execute/build/jupyter_execute/notebooks/multi_actors_demo.ipynb.txt new file mode 100644 index 0000000..7ec65e6 --- /dev/null +++ b/_sources/build/jupyter_execute/build/jupyter_execute/notebooks/multi_actors_demo.ipynb.txt @@ -0,0 +1,272 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "qg5s5R7AT8Fj" + }, + "source": [ + "# Multi-agent Simulation\n", + "\n", + "This tutorial demonstrates how to run a simple closed-loop simulation with multiple pre-defined sim agents." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MtgRcYqmtMwD" + }, + "outputs": [], + "source": [ + "import jax\n", + "from jax import numpy as jnp\n", + "import numpy as np\n", + "import mediapy\n", + "from tqdm import tqdm\n", + "import dataclasses\n", + "\n", + "from waymax import config as _config\n", + "from waymax import dataloader\n", + "from waymax import datatypes\n", + "from waymax import dynamics\n", + "from waymax import env as _env\n", + "from waymax import agents\n", + "from waymax import visualization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "dkJwTuSLr0gh" + }, + "outputs": [], + "source": [ + "# Config dataset:\n", + "max_num_objects = 32\n", + "\n", + "config = dataclasses.replace(_config.WOD_1_0_0_VALIDATION, max_num_objects=max_num_objects)\n", + "data_iter = dataloader.simulator_state_generator(config=config)\n", + "scenario = next(data_iter)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DINmUYg7y-jI" + }, + "source": [ + "## Initializing and Running the Simulator\n", + "\n", + "Waymax uses a Gym-like interface for running closed-loop simulation. \n", + "\n", + "The `env.MultiAgentEnvironment` class defines a stateless simulation interface with the two key methods:\n", + "- The `reset` method initializes and returns the first simulation state.\n", + "- The `step` method transitions the simulation and takes as arguments a state and an action and outputs the next state.\n", + "\n", + "Crucially, the `MultiAgentEnvironment` does not hold any simulation state itself, and the `reset` and `step` functions have no side effects. This allows us to use functional transforms from JAX, such as using jit compilation to optimize the compuation. It also allows the user to arbitrarily branch and restart simulation from any state, or save the simulation by simply serializing and saving the state object.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "x7u2dqPCtdeq" + }, + "outputs": [], + "source": [ + "# Config the multi-agent environment:\n", + "init_steps = 11\n", + "\n", + "# Set the dynamics model the environment is using.\n", + "# Note each actor interacting with the environment needs to provide action\n", + "# compatible with this dynamics model.\n", + "dynamics_model = dynamics.StateDynamics()\n", + "\n", + "# Expect users to control all valid object in the scene.\n", + "env = _env.MultiAgentEnvironment(\n", + " dynamics_model=dynamics_model,\n", + " config=dataclasses.replace(\n", + " _config.EnvironmentConfig(),\n", + " max_num_objects=max_num_objects,\n", + " controlled_object=_config.ObjectType.VALID,\n", + " ),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "876iboHbYx2H" + }, + "source": [ + "We now create a set of sim agents to run in simulation. By default, the behavior of an object that is not controlled is to replay the behavior stored in the dataset (log playback).\n", + "\n", + "For each sim agent, we define the algorithm (such as IDM), and specify which objects the agent controls via the `is_controlled_func`, which is required to return a boolean mask marking which objects are being controlled.\n", + "\n", + "The IDM agent we use in this example is the `IDMRoutePolicy`, which follows the spatial trajectory stored in the logs, but adjusts the speed profile based on the IDM rule, which will stop or speed up according to the distance between the vehicle and any objects in front of the vehicle. For the remaining agents, we set them to use a constant speed policy which will follow the logged route with a fixed, constant speed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "IfCHlgJzghUS" + }, + "outputs": [], + "source": [ + "# Setup a few actors, see visualization below for how each actor behaves.\n", + "\n", + "# An actor that doesn't move, controlling all objects with index > 4\n", + "obj_idx = jnp.arange(max_num_objects)\n", + "static_actor = agents.create_constant_speed_actor(\n", + " speed=0.0,\n", + " dynamics_model=dynamics_model,\n", + " is_controlled_func=lambda state: obj_idx > 4,\n", + ")\n", + "\n", + "# IDM actor/policy controlling both object 0 and 1.\n", + "# Note IDM policy is an actor hard-coded to use dynamics.StateDynamics().\n", + "actor_0 = agents.IDMRoutePolicy(\n", + " is_controlled_func=lambda state: (obj_idx == 0) | (obj_idx == 1)\n", + ")\n", + "\n", + "# Constant speed actor with predefined fixed speed controlling object 2.\n", + "actor_1 = agents.create_constant_speed_actor(\n", + " speed=5.0,\n", + " dynamics_model=dynamics_model,\n", + " is_controlled_func=lambda state: obj_idx == 2,\n", + ")\n", + "\n", + "# Exper/log actor controlling objects 3 and 4.\n", + "actor_2 = agents.create_expert_actor(\n", + " dynamics_model=dynamics_model,\n", + " is_controlled_func=lambda state: (obj_idx == 3) | (obj_idx == 4),\n", + ")\n", + "\n", + "actors = [static_actor, actor_0, actor_1, actor_2]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JvV82vOXWbI0" + }, + "source": [ + "We can (optionally) jit the step and select action functions to speed up computation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "YmzEJy2LUwe5" + }, + "outputs": [], + "source": [ + "jit_step = jax.jit(env.step)\n", + "jit_select_action_list = [jax.jit(actor.select_action) for actor in actors]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iBeI2mdIUdTw" + }, + "source": [ + "We can now write a for loop to all of these agents in simulation together.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "06SjvXdRrV3N" + }, + "outputs": [], + "source": [ + "states = [env.reset(scenario)]\n", + "for _ in range(states[0].remaining_timesteps):\n", + " current_state = states[-1]\n", + "\n", + " outputs = [\n", + " jit_select_action({}, current_state, None, None)\n", + " for jit_select_action in jit_select_action_list\n", + " ]\n", + " action = agents.merge_actions(outputs)\n", + " next_state = jit_step(current_state, action)\n", + "\n", + " states.append(next_state)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "s6TmqhLRGc_7" + }, + "source": [ + "## Visualization of simulation.\n", + "\n", + "We can now visualize the result of the simulation loop.\n", + "\n", + "On the left side:\n", + "- Objects 5, 6, and 7 (controlled by static_actor) remain static.\n", + "- Objects 3 and 4 controlled by log playback, and collide with objects 5 and 6.\n", + "\n", + "On the right side:\n", + "- Object 2 controlled by actor_1 is moving at constant speed 5m/s (i.e. slower than log in this case).\n", + "- Object 0 and 1, controlled by the IDM agent, follow the log in the beginning, but object 1 slows down when approaching object 2." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "cYWjd4dE1bB2" + }, + "outputs": [], + "source": [ + "imgs = []\n", + "for state in states:\n", + " imgs.append(visualization.plot_simulator_state(state, use_log_traj=False))\n", + "mediapy.show_video(imgs, fps=10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "liRaNVbE1gWb" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "name": "multi_actors_demo.ipynb", + "private_outputs": true, + "provenance": [ + { + "file_id": "14w5MbrMNLsOsLuD5kXy5-rrNO3ZgsHat", + "timestamp": 1678404744504 + } + ] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/_sources/build/jupyter_execute/build/jupyter_execute/notebooks/wosac_submission_via_waymax.ipynb.txt b/_sources/build/jupyter_execute/build/jupyter_execute/notebooks/wosac_submission_via_waymax.ipynb.txt new file mode 100644 index 0000000..d04c1c3 --- /dev/null +++ b/_sources/build/jupyter_execute/build/jupyter_execute/notebooks/wosac_submission_via_waymax.ipynb.txt @@ -0,0 +1,348 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "DH236BT5wcH7" + }, + "source": [ + "# Waymo Open Sim Agents Challenge Submission\n", + "\n", + "This tutorial covers how to use Waymax to create a Waymo Open Sim Agents Challenge (WOSAC) submission.\n", + "\n", + "Please also refer to the [WOSAC submission notebook](https://github.com/waymo-research/waymo-open-dataset/blob/master/tutorial/tutorial_sim_agents.ipynb) for additional reference and for setting up a submission without Waymax." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "n-sW1wrcvHys" + }, + "outputs": [], + "source": [ + "!pip install waymo-open-dataset-tf-2-11-0==1.6.0\n", + "\n", + "import os\n", + "import jax\n", + "from jax import random\n", + "from jax import numpy as jnp\n", + "import tensorflow as tf\n", + "\n", + "from waymo_open_dataset.protos import sim_agents_submission_pb2\n", + "from waymax import agents\n", + "from waymax import config as _config\n", + "from waymax import dynamics\n", + "from waymax import dataloader\n", + "from waymax import datatypes\n", + "from waymax import env as _env\n", + "\n", + "CURRENT_TIME_INDEX = 10\n", + "N_SIMULATION_STEPS = 80\n", + "N_ROLLOUTS = 32" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9YTPFzmMwu-F" + }, + "source": [ + "## Dataloader\n", + "\n", + "To load data for a WOSAC submission, we write a custom dataloader that processes the scenario IDs. These are normally discarded in the default Waymax dataloader as they are not used during simulation and JAX does not have native support for string data. The scenario ID is stored in the field `scenario/id` as described in the [`tf.Example` spec](https://waymo.com/open/data/motion/tfexample).\n", + "\n", + "This custom dataloader defines a preprocessor `_preprocess` that decodes the scenario ID into an array of bytes, and a postprocessor `_postprocess` that converts those bytes into the string scenario ID. The actual scenario data is processed in the same way as the default dataloader in Waymax." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JJAfGGSF74Ym" + }, + "outputs": [], + "source": [ + "data_config = _config.WOD_1_2_0_TEST\n", + "\n", + "# Write a custom dataloader that loads scenario IDs.\n", + "def _preprocess(serialized: bytes) -> dict[str, tf.Tensor]:\n", + " womd_features = dataloader.womd_utils.get_features_description(\n", + " include_sdc_paths=data_config.include_sdc_paths,\n", + " max_num_rg_points=data_config.max_num_rg_points,\n", + " num_paths=data_config.num_paths,\n", + " num_points_per_path=data_config.num_points_per_path,\n", + " )\n", + " womd_features['scenario/id'] = tf.io.FixedLenFeature([1], tf.string)\n", + "\n", + " deserialized = tf.io.parse_example(serialized, womd_features)\n", + " parsed_id = deserialized.pop('scenario/id')\n", + " deserialized['scenario/id'] = tf.io.decode_raw(parsed_id, tf.uint8)\n", + "\n", + " return dataloader.preprocess_womd_example(\n", + " deserialized,\n", + " aggregate_timesteps=data_config.aggregate_timesteps,\n", + " max_num_objects=data_config.max_num_objects,\n", + " )\n", + "\n", + "def _postprocess(example: dict[str, tf.Tensor]):\n", + " scenario = dataloader.simulator_state_from_womd_dict(example)\n", + " scenario_id = example['scenario/id']\n", + " return scenario_id, scenario\n", + "\n", + "def decode_bytes(data_iter):\n", + " for scenario_id, scenario in data_iter:\n", + " scenario_id = scenario_id.tobytes().decode('utf-8')\n", + " yield scenario_id, scenario\n", + "\n", + "data_iter = decode_bytes(dataloader.get_data_generator(\n", + " data_config, _preprocess, _postprocess\n", + "))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hXQk4wG8xmTs" + }, + "source": [ + "## Environment and Agent Configuration\n", + "\n", + "The following code initializes the environment and sim agent used for simulation. In this example, we use a constant speed actor which will maintain the course and speed that the agent has at the initial timestep.\n", + "\n", + "WOSAC evaluates metrics on all agents valid at the initial timestep. Therefore, the `is_controlled` field is set to all valid agents at the 11th timestep.\n", + "\n", + "Other configurations related to the agent and environment are customizable. This includes the dynamics model (here, we use the `InvertibleBicycleModel`) and the type of sim agent to evaluate." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "XnvXA1z1wwqQ" + }, + "outputs": [], + "source": [ + "env_config = _config.EnvironmentConfig(\n", + " # Ensure that the sim agent can control all valid objects.\n", + " controlled_object=_config.ObjectType.VALID\n", + ")\n", + "\n", + "dynamics_model = dynamics.InvertibleBicycleModel()\n", + "env = _env.MultiAgentEnvironment(\n", + " dynamics_model=dynamics_model,\n", + " config=env_config,\n", + ")\n", + "\n", + "agent = agents.create_constant_speed_actor(\n", + " dynamics_model=dynamics_model,\n", + " # Controlled objects are those valid at t=0.\n", + " is_controlled_func=lambda state: state.log_trajectory.valid[..., CURRENT_TIME_INDEX]\n", + ")\n", + "\n", + "jit_step = jax.jit(env.step)\n", + "jit_select_action = jax.jit(agent.select_action)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jGI9vfXFyBEo" + }, + "source": [ + "## Generating Rollouts\n", + "\n", + "We can now define a function that will rollout the environment and agent to generate trajectories. The WOSAC submission format consists of multiple protobufs defined in `sim_agents_submission_pb2`. These consist of (copied from the [WOSAC submission notebook](https://github.com/waymo-research/waymo-open-dataset/blob/master/tutorial/tutorial_sim_agents.ipynb)):\n", + "\n", + "- `SimulatedTrajectory` contains one trajectory for a single object, with the fields we need to simulate (x, y, z, heading).\n", + "- `JointScene` is a set of all the object trajectories from a single simulation, describing one of the possible rollouts.\n", + "- `ScenarioRollouts` is a collection of all the parallel simulations for a single initial Scenario.\n", + "- `SimAgentsChallengeSubmission` is used to package submissions for multiple Scenarios (e.g. for the whole testing dataset).\n", + "\n", + "Here, we will write a function `generate_scenario_rollout` that generates a `ScenarioRollouts` protobuf from a single input scenario. By default, WOSAC requires 32 rollouts per scenario. Our actor is deterministic so all 32 rollouts will be identical, but we still generate these rollouts to provide an accurate example of a proper submission.\n", + "\n", + "We also provide a utility function `validate_scenario_rollout` to help ensure that the scenario rollouts have the correct format before uploading.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "-hHb4wXa6Jo-" + }, + "outputs": [], + "source": [ + "def validate_scenario_rollout(scenario_rollouts: sim_agents_submission_pb2.ScenarioRollouts,\n", + " scenario: datatypes.SimulatorState):\n", + " \"\"\"Verifies if scenario_rollouts has correct formatting.\"\"\"\n", + " valid_sim_agents = scenario.log_trajectory.valid[..., CURRENT_TIME_INDEX]\n", + " sim_agent_id_idxs = jnp.where(valid_sim_agents)[0]\n", + " sim_agent_ids = scenario.object_metadata.ids[sim_agent_id_idxs].tolist()\n", + "\n", + " if len(scenario_rollouts.joint_scenes) != N_ROLLOUTS:\n", + " raise ValueError('Incorrect number of parallel simulations. '\n", + " f'(Actual: {len(scenario_rollouts.joint_scenes)}, '\n", + " f'Expected: {N_ROLLOUTS})')\n", + "\n", + " def _raise_if_wrong_length(trajectory, field_name, expected_length):\n", + " if len(getattr(trajectory, field_name)) != expected_length:\n", + " raise ValueError(f'Invalid {field_name} tensor length '\n", + " f'(actual: {len(getattr(trajectory, field_name))}, '\n", + " f'expected: {expected_length})')\n", + "\n", + " for joint_scene in scenario_rollouts.joint_scenes:\n", + " simulated_ids = []\n", + " for simulated_trajectory in joint_scene.simulated_trajectories:\n", + " # Check the length of each of the simulated fields.\n", + " _raise_if_wrong_length(simulated_trajectory, 'center_x', N_SIMULATION_STEPS)\n", + " _raise_if_wrong_length(simulated_trajectory, 'center_y', N_SIMULATION_STEPS)\n", + " _raise_if_wrong_length(simulated_trajectory, 'center_z', N_SIMULATION_STEPS)\n", + " _raise_if_wrong_length(simulated_trajectory, 'heading', N_SIMULATION_STEPS)\n", + " # Check that each object ID is present in the original WOMD scenario.\n", + " if simulated_trajectory.object_id not in sim_agent_ids:\n", + " raise ValueError(\n", + " f'Object {simulated_trajectory.object_id} is not a sim agent.')\n", + " simulated_ids.append(simulated_trajectory.object_id)\n", + " # Check that all of the required objects/agents are simulated.\n", + " missing_agents = set(sim_agent_ids) - set(simulated_ids)\n", + " if missing_agents:\n", + " raise ValueError(\n", + " f'Sim agents {missing_agents} are missing from the simulation.')\n", + "\n", + "\n", + "def generate_scenario_rollout(\n", + " scenario_id: str,\n", + " scenario: datatypes.SimulatorState) -> sim_agents_submission_pb2.ScenarioRollouts:\n", + " \"\"\"Simulate 32 rollouts and return a ScenarioRollouts protobuf.\"\"\"\n", + " joint_scenes = []\n", + " key = random.PRNGKey(0)\n", + " for _ in range(N_ROLLOUTS):\n", + " initial_state = current_state = env.reset(scenario)\n", + " # Controlled objects are those valid at t=0.\n", + " is_controlled = scenario.log_trajectory.valid[..., CURRENT_TIME_INDEX]\n", + "\n", + " # Run the sim agent for 80 steps.\n", + " for _ in (range(initial_state.remaining_timesteps)):\n", + " key, actor_key = random.split(key, 2)\n", + " actor_output = jit_select_action({}, current_state, None, actor_key)\n", + " next_state = jit_step(current_state, actor_output.action)\n", + " current_state = next_state\n", + "\n", + " # Write out result\n", + " final_trajectory = current_state.sim_trajectory\n", + " object_ids = current_state.object_metadata.ids # Shape (n_objects,)\n", + " object_ids = jnp.where(is_controlled, object_ids, -1)\n", + "\n", + " simulated_trajectories = []\n", + " for i, object_id in enumerate(object_ids):\n", + " if object_id != -1:\n", + " simulated_trajectory = sim_agents_submission_pb2.SimulatedTrajectory(\n", + " center_x=final_trajectory.x[i, env_config.init_steps:],\n", + " center_y=final_trajectory.y[i, env_config.init_steps:],\n", + " center_z=final_trajectory.z[i, env_config.init_steps:],\n", + " heading=final_trajectory.yaw[i, env_config.init_steps:],\n", + " object_id=object_id,\n", + " )\n", + " simulated_trajectories.append(simulated_trajectory)\n", + " joint_scene = sim_agents_submission_pb2.JointScene(\n", + " simulated_trajectories=simulated_trajectories\n", + " )\n", + " joint_scenes.append(joint_scene)\n", + "\n", + " scenario_rollouts = sim_agents_submission_pb2.ScenarioRollouts(\n", + " scenario_id=scenario_id, joint_scenes=joint_scenes\n", + " )\n", + " validate_scenario_rollout(scenario_rollouts, scenario)\n", + " return scenario_rollouts" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XPlK3Qp_yD8K" + }, + "source": [ + "## Generating the Submission\n", + "\n", + "We are now ready to generate the submission file. Because the data is potentially large (over the 2GB maximum size for a protobuf), we process the data in a streaming fashion and write out results incrementally. The testing set of Waymo Open Motion Dataset v1.2.0 has 44926 segments -- this step may take a significant amount of time if the rollout generation time is long.\n", + "\n", + "After we process all of the data, we zip the individual shards to create a zip file ready for submission. Please refer to the Open dataset website for further instructions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "mjOZoUVYT0OW" + }, + "outputs": [], + "source": [ + "OUTPUT_ROOT_DIRECTORY = '/tmp/waymo_sim_agents/'\n", + "os.makedirs(OUTPUT_ROOT_DIRECTORY, exist_ok=True)\n", + "output_filenames = []\n", + "scenario_rollouts = []\n", + "\n", + "for i, (scenario_id, scenario) in enumerate(data_iter):\n", + " scenario_rollouts.append(generate_scenario_rollout(scenario_id, scenario))\n", + "\n", + " if i % 5 == 0 and i > 0:\n", + " shard_suffix = '.%d' % i\n", + " shard_submission = sim_agents_submission_pb2.SimAgentsChallengeSubmission(\n", + " scenario_rollouts=scenario_rollouts,\n", + " submission_type=sim_agents_submission_pb2.SimAgentsChallengeSubmission.SIM_AGENTS_SUBMISSION,\n", + " account_name='your_account@test.com',\n", + " unique_method_name='waymax_sim_agents_tutorial',\n", + " authors=['test'],\n", + " affiliation='waymo',\n", + " description='Submission from the Waymax - Sim Agents tutorial',\n", + " method_link='https://waymo.com/open/'\n", + " )\n", + " scenario_rollouts = []\n", + " output_filename = f'submission.binproto{shard_suffix}'\n", + " with open(os.path.join(OUTPUT_ROOT_DIRECTORY, output_filename), 'wb') as f:\n", + " f.write(shard_submission.SerializeToString())\n", + " output_filenames.append(output_filename)\n", + "\n", + "# Once we have created all the shards, we can package them directly into a\n", + "# tar.gz archive, ready for submission.\n", + "with tarfile.open(\n", + " os.path.join(OUTPUT_ROOT_DIRECTORY, 'submission.tar.gz'), 'w:gz') as tar:\n", + " for output_filename in output_filenames:\n", + " tar.add(os.path.join(OUTPUT_ROOT_DIRECTORY, output_filename),\n", + " arcname=output_filename)" + ] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "private_outputs": true, + "provenance": [ + { + "file_id": "165MIhRtVwtmR9ontFYJe6suV6CLHerJI", + "timestamp": 1695771240550 + }, + { + "file_id": "1Y3eSCA7LCGrCJ672zHeBnStJKTEorl5z", + "timestamp": 1695771147053 + }, + { + "file_id": "1l1iYQbLAGQ1vv-13AriC2bp78LPv3LH2", + "timestamp": 1695668356011 + } + ] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/_sources/build/jupyter_execute/notebooks/data_demo.ipynb.txt b/_sources/build/jupyter_execute/notebooks/data_demo.ipynb.txt new file mode 100644 index 0000000..3e80b3c --- /dev/null +++ b/_sources/build/jupyter_execute/notebooks/data_demo.ipynb.txt @@ -0,0 +1,136 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "rNYRA6k8Qfyo" + }, + "source": [ + "# Scenario Data Loading\n", + "\n", + "This tutorial demonstrates how to load scenario data from the Waymo Open Motion Dataset (WOMD) using the Waymax dataloader." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MtgRcYqmtMwD" + }, + "outputs": [], + "source": [ + "%%capture\n", + "import numpy as np\n", + "import mediapy\n", + "from tqdm import tqdm\n", + "import dataclasses\n", + "\n", + "from waymax import config as _config\n", + "from waymax import dataloader\n", + "from waymax import datatypes\n", + "from waymax import visualization" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0o2sAapxRMAT" + }, + "source": [ + "\n", + "We first create a dataset config, using the default configs provided in the `waymax.config` module. In particular, `config.WOD_1_1_0_TRAINING` is a pre-defined configuration that points to version 1.1.0 of the Waymo Open Dataset.\n", + "\n", + "The data config contains a number of options to configure how and where the dataset is loaded from. By default, the `WOD_1_1_0_TRAINING` loads up to 128 objects (e.g. vehicles, pedestrians) per scenario. Here, we can save memory and compute by loading only the first 32 objects stored in the scenario.\n", + "\n", + "We use the `dataloader.simulator_state_generator` function to create an iterator\n", + "through Open Motion Dataset scenarios. Calling next on the iterator will retrieve the first scenario in the dataset.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "dkJwTuSLr0gh" + }, + "outputs": [], + "source": [ + "config = dataclasses.replace(_config.WOD_1_1_0_TRAINING, max_num_objects=32)\n", + "data_iter = dataloader.simulator_state_generator(config=config)\n", + "scenario = next(data_iter)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "q1xyeYpLR8J6" + }, + "source": [ + "Next, we can plot the initial state of this scenario. We use a matplotlib-based visualization available in the `waymax.visualization` package." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "OY3-OOArsFcU" + }, + "outputs": [], + "source": [ + "# Using logged trajectory\n", + "img = visualization.plot_simulator_state(scenario, use_log_traj=True)\n", + "mediapy.show_image(img)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "H0Z15epRSC23" + }, + "source": [ + "The Waymo Open Motion Dataset consists of 9-second trajectory snippets. We can visualize the entire logged trajectory as a video as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "06SjvXdRrV3N" + }, + "outputs": [], + "source": [ + "imgs = []\n", + "\n", + "state = scenario\n", + "for _ in range(scenario.remaining_timesteps):\n", + " state = datatypes.update_state_by_log(state, num_steps=1)\n", + " imgs.append(visualization.plot_simulator_state(state, use_log_traj=True))\n", + "\n", + "mediapy.show_video(imgs, fps=10)" + ] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "private_outputs": true, + "provenance": [ + { + "file_id": "14w5MbrMNLsOsLuD5kXy5-rrNO3ZgsHat", + "timestamp": 1678404744504 + } + ] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/_sources/build/jupyter_execute/notebooks/datatypes_demo.ipynb.txt b/_sources/build/jupyter_execute/notebooks/datatypes_demo.ipynb.txt new file mode 100644 index 0000000..d18cf0a --- /dev/null +++ b/_sources/build/jupyter_execute/notebooks/datatypes_demo.ipynb.txt @@ -0,0 +1,300 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "0IwlAjlibuUE" + }, + "source": [ + "# Understanding and Manipulating Data in Waymax\n", + "\n", + "This tutorial covers data structures in Waymax." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1NTUsEy_btuU" + }, + "outputs": [], + "source": [ + "%%capture\n", + "import dataclasses\n", + "import jax\n", + "from jax import numpy as jnp\n", + "from matplotlib import pyplot as plt\n", + "\n", + "from waymax import config as _config\n", + "from waymax import dataloader\n", + "from waymax import datatypes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "2pcSuBb9cFZN" + }, + "outputs": [], + "source": [ + "# Load example data.\n", + "config = dataclasses.replace(_config.WOD_1_1_0_VALIDATION, max_num_objects=32)\n", + "data_iter = dataloader.simulator_state_generator(config=config)\n", + "scenario = next(data_iter)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GZAFy24xcLQc" + }, + "source": [ + "## JAX-based data structures\n", + "\n", + "The key property with all data structures in Waymax is that all data is *immutable*. This is a design decision that is inherited from JAX and enables code written using Waymax to be compatible with the powerful functional transforms in JAX, such as `jit`, `vmap`, etc. While efficiency may be a concern with immutable data structures, wrapping your function in `jax.jit` will allow JAX to optimize and replace your operations with in-place operations wherever possible, avoiding the need for excessive data copying.\n", + "\n", + "Additionally, all datastructures in Waymax are implemented as dataclasses. This allows convenient named access to fields, and allows simple nesting of data structures that is easy to manipulate with tree-based operations (such as those in `jax.tree_util`).\n", + "\n", + "The first example we will cover is the `datatypes.Trajectory` data structure, which holds the pose information for all objects. The scenario that we loaded contains a trajectory containing the logged behavior for all agents under the `scenario.log_trajectory` attribute." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "sPMaI3ShdPwB" + }, + "outputs": [], + "source": [ + "log_trajectory = scenario.log_trajectory\n", + "\n", + "# Number of objects stored in this trajectory.\n", + "print('Number of objects:', log_trajectory.num_objects)\n", + "print('Number of timesteps:', log_trajectory.num_timesteps)\n", + "print('Trajectory shape (num_objects, num_timesteps):', log_trajectory.shape)\n", + "print('XYZ positions (num_objects, num_timesteps, 3):', log_trajectory.xyz.shape)\n", + "print('XY velocities (num_objects, num_timesteps, 2):', log_trajectory.vel_xy.shape)\n", + "print('Yaw (num_objects, num_timesteps):', log_trajectory.yaw.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nsKEO7GCelBK" + }, + "source": [ + "The `datatypes` module contains some helper methods that automatically map over datastructures. We can use `datatypes.dynamic_slice` to select out the trajectory belonging to a particular object or at a particular timestep. These operations, as with all JAX operations, will return new copies of the object they are modifying. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "k6cWGuVRdlQk" + }, + "outputs": [], + "source": [ + "# Slice by time. Select the trajectory at timestep 23.\n", + "traj_t23 = datatypes.dynamic_slice(log_trajectory, start_index=23, slice_size=1, axis=-1)\n", + "print('XYZ positions (num_objects, 1, 3):', traj_t23.xyz.shape)\n", + "\n", + "# Slice by object. Select the trajectory for object 15.\n", + "traj_obj15 = datatypes.dynamic_slice(log_trajectory, start_index=15, slice_size=1, axis=-2)\n", + "print('XYZ positions (1, num_timesteps, 3):', traj_obj15.xyz.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LXrPjhzPf7EJ" + }, + "source": [ + "Of course, JAX functions from the core library also work on Waymax data structures. The `tree_map` function is particularly useful for working with dataclasses, and will apply a single function to all fields in the data structure (recursively if there are nested data structures)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "shVRPmToe9oY" + }, + "outputs": [], + "source": [ + "def max_along_time(x: jax.Array) -> jax.Array:\n", + " return jnp.max(x, axis=-1, keepdims=True)\n", + "\n", + "max_trajectory = jax.tree_util.tree_map(max_along_time, log_trajectory)\n", + "print('XYZ positions (num_objects, 1, 3):', max_trajectory.xyz.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mlPkVLA7Mcx-" + }, + "source": [ + "To modify the values of the data structure, we can use `dataclasses.replace` to replace entire fields, and `Array.at[idx].set(value)` to selectively modify individual values. For example, to set the all yaws for object 1 to zero, we can use the following code snippet:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Z5napP9gMutj" + }, + "outputs": [], + "source": [ + "zeroed_traj = dataclasses.replace(\n", + " log_trajectory, \n", + " yaw=log_trajectory.yaw.at[1].set(0.0)\n", + ")\n", + "\n", + "# Should be the original values.\n", + "print('Yaws for object 0, timesteps 0 to 5:', zeroed_traj.yaw[0, 0:5])\n", + "\n", + "# Should be now set to 0.\n", + "print('Yaws for object 1, timesteps 0 to 5:', zeroed_traj.yaw[1, 0:5])\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "E7DZS_BKhZLj" + }, + "source": [ + "# Other important data structures\n", + "\n", + "We will now cover the remaining important data structures that are stored in a scenario.\n", + "\n", + "The `datatypes.RoadgraphPoints` data structure holds all static information regarding the road and environment. This includes all lanes markers, road edges, stop signs, speed bumps, and crosswalks. \n", + "- The `x`, `y`, and `z` attributes define the spatial coordinates of the points.\n", + "- The `type` attribute is an integer that defines what type of point (lane, edge, stop sign, etc.) the point is. See `roadgraph_samples/type` of the [Waymo Open Motion Dataset](https://waymo.com/open/data/motion/tfexample) for definitions of which value corresponds to what type of point.\n", + "- The `dir_x` and `dir_y` attributes define the orientation of the points. Lane points will orient in the forward direction of the lane. Edge points are oriented such that the inside of the road is always on the port side (left if facing forward) of the point.\n", + "- The `id` field is a unique identifier for each contiguous lane. Lanes end if there is an intersection or reach the edge of the map." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "k9XQmInAh4vo" + }, + "outputs": [], + "source": [ + "# Plot the roadgraph, with colors corresponding to the road type.\n", + "rg_points = scenario.roadgraph_points\n", + "\n", + "where_valid = rg_points.valid\n", + "plt.scatter(\n", + " x = rg_points.x[where_valid],\n", + " y = rg_points.y[where_valid],\n", + " s=0.1,\n", + " c = rg_points.types[where_valid]\n", + ")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2MdtujKbjahp" + }, + "source": [ + "The `datatypes.TrafficLights` structure holds time-varying information regarding the color and position of the traffic lights.\n", + "- The `x`, `y`, and `z` attributes define the spatial location of the light.\n", + "- The `state` attribute defines what color the light is at a particular instance in time.\n", + "- The `lane_ids` attribute tells what lanes the traffic light is controlling. These can be cross-referenced with the `RoadgraphPoints.ids` field." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "zse6OxgaiEbH" + }, + "outputs": [], + "source": [ + "traffic_lights = scenario.log_traffic_light\n", + "\n", + "print('Traffic Light States (num_lights, num_timesteps):', traffic_lights.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QY-rhD1Qkr-C" + }, + "source": [ + "Finally, `datatypes.ObjectMetadata` holds \n", + "- The `object_types` attribute defines whether the object is a vehicle, pedestrian, or cyclist.\n", + "- The `ids` attribute assigns a unique ID to each object.\n", + "- The `is_sdc` attribute defines whether the object is the ego-vehicle (or self-driving car).\n", + "- The `is_modeled` attribute marks whether the object's behavior is meant to be predicted as part of the Waymo Open Motion dataset.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Dohh8d9Rkh70" + }, + "outputs": [], + "source": [ + "metadata = scenario.object_metadata\n", + "print('All object IDS:', metadata.ids)\n", + "\n", + "# Color-code object trajectory by whether it is the SDC or not.\n", + "# The SDC trajectory in the center is shown in blue, and all other trajectories\n", + "# are shown in red.\n", + "flat_trajectory = jax.tree_util.tree_map(lambda x: jnp.reshape(x, [-1]), log_trajectory)\n", + "colors = jnp.zeros(log_trajectory.shape, dtype=jnp.int32).at[metadata.is_sdc].set(1)\n", + "colors = jnp.reshape(colors, [-1])\n", + "\n", + "where_valid = flat_trajectory.valid\n", + "plt.scatter(\n", + " x=flat_trajectory.x[where_valid],\n", + " y=flat_trajectory.y[where_valid],\n", + " s=0.5,\n", + " c=colors[where_valid],\n", + " cmap='RdYlBu'\n", + ")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "hmNQVj-clfIr" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "private_outputs": true, + "provenance": [ + { + "file_id": "1Ee2coesuhg82e7E9HmZxafZ7raD_ug5q", + "timestamp": 1683520258687 + } + ] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/_sources/build/jupyter_execute/notebooks/multi_actors_demo.ipynb.txt b/_sources/build/jupyter_execute/notebooks/multi_actors_demo.ipynb.txt new file mode 100644 index 0000000..7ec65e6 --- /dev/null +++ b/_sources/build/jupyter_execute/notebooks/multi_actors_demo.ipynb.txt @@ -0,0 +1,272 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "qg5s5R7AT8Fj" + }, + "source": [ + "# Multi-agent Simulation\n", + "\n", + "This tutorial demonstrates how to run a simple closed-loop simulation with multiple pre-defined sim agents." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MtgRcYqmtMwD" + }, + "outputs": [], + "source": [ + "import jax\n", + "from jax import numpy as jnp\n", + "import numpy as np\n", + "import mediapy\n", + "from tqdm import tqdm\n", + "import dataclasses\n", + "\n", + "from waymax import config as _config\n", + "from waymax import dataloader\n", + "from waymax import datatypes\n", + "from waymax import dynamics\n", + "from waymax import env as _env\n", + "from waymax import agents\n", + "from waymax import visualization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "dkJwTuSLr0gh" + }, + "outputs": [], + "source": [ + "# Config dataset:\n", + "max_num_objects = 32\n", + "\n", + "config = dataclasses.replace(_config.WOD_1_0_0_VALIDATION, max_num_objects=max_num_objects)\n", + "data_iter = dataloader.simulator_state_generator(config=config)\n", + "scenario = next(data_iter)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DINmUYg7y-jI" + }, + "source": [ + "## Initializing and Running the Simulator\n", + "\n", + "Waymax uses a Gym-like interface for running closed-loop simulation. \n", + "\n", + "The `env.MultiAgentEnvironment` class defines a stateless simulation interface with the two key methods:\n", + "- The `reset` method initializes and returns the first simulation state.\n", + "- The `step` method transitions the simulation and takes as arguments a state and an action and outputs the next state.\n", + "\n", + "Crucially, the `MultiAgentEnvironment` does not hold any simulation state itself, and the `reset` and `step` functions have no side effects. This allows us to use functional transforms from JAX, such as using jit compilation to optimize the compuation. It also allows the user to arbitrarily branch and restart simulation from any state, or save the simulation by simply serializing and saving the state object.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "x7u2dqPCtdeq" + }, + "outputs": [], + "source": [ + "# Config the multi-agent environment:\n", + "init_steps = 11\n", + "\n", + "# Set the dynamics model the environment is using.\n", + "# Note each actor interacting with the environment needs to provide action\n", + "# compatible with this dynamics model.\n", + "dynamics_model = dynamics.StateDynamics()\n", + "\n", + "# Expect users to control all valid object in the scene.\n", + "env = _env.MultiAgentEnvironment(\n", + " dynamics_model=dynamics_model,\n", + " config=dataclasses.replace(\n", + " _config.EnvironmentConfig(),\n", + " max_num_objects=max_num_objects,\n", + " controlled_object=_config.ObjectType.VALID,\n", + " ),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "876iboHbYx2H" + }, + "source": [ + "We now create a set of sim agents to run in simulation. By default, the behavior of an object that is not controlled is to replay the behavior stored in the dataset (log playback).\n", + "\n", + "For each sim agent, we define the algorithm (such as IDM), and specify which objects the agent controls via the `is_controlled_func`, which is required to return a boolean mask marking which objects are being controlled.\n", + "\n", + "The IDM agent we use in this example is the `IDMRoutePolicy`, which follows the spatial trajectory stored in the logs, but adjusts the speed profile based on the IDM rule, which will stop or speed up according to the distance between the vehicle and any objects in front of the vehicle. For the remaining agents, we set them to use a constant speed policy which will follow the logged route with a fixed, constant speed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "IfCHlgJzghUS" + }, + "outputs": [], + "source": [ + "# Setup a few actors, see visualization below for how each actor behaves.\n", + "\n", + "# An actor that doesn't move, controlling all objects with index > 4\n", + "obj_idx = jnp.arange(max_num_objects)\n", + "static_actor = agents.create_constant_speed_actor(\n", + " speed=0.0,\n", + " dynamics_model=dynamics_model,\n", + " is_controlled_func=lambda state: obj_idx > 4,\n", + ")\n", + "\n", + "# IDM actor/policy controlling both object 0 and 1.\n", + "# Note IDM policy is an actor hard-coded to use dynamics.StateDynamics().\n", + "actor_0 = agents.IDMRoutePolicy(\n", + " is_controlled_func=lambda state: (obj_idx == 0) | (obj_idx == 1)\n", + ")\n", + "\n", + "# Constant speed actor with predefined fixed speed controlling object 2.\n", + "actor_1 = agents.create_constant_speed_actor(\n", + " speed=5.0,\n", + " dynamics_model=dynamics_model,\n", + " is_controlled_func=lambda state: obj_idx == 2,\n", + ")\n", + "\n", + "# Exper/log actor controlling objects 3 and 4.\n", + "actor_2 = agents.create_expert_actor(\n", + " dynamics_model=dynamics_model,\n", + " is_controlled_func=lambda state: (obj_idx == 3) | (obj_idx == 4),\n", + ")\n", + "\n", + "actors = [static_actor, actor_0, actor_1, actor_2]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JvV82vOXWbI0" + }, + "source": [ + "We can (optionally) jit the step and select action functions to speed up computation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "YmzEJy2LUwe5" + }, + "outputs": [], + "source": [ + "jit_step = jax.jit(env.step)\n", + "jit_select_action_list = [jax.jit(actor.select_action) for actor in actors]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iBeI2mdIUdTw" + }, + "source": [ + "We can now write a for loop to all of these agents in simulation together.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "06SjvXdRrV3N" + }, + "outputs": [], + "source": [ + "states = [env.reset(scenario)]\n", + "for _ in range(states[0].remaining_timesteps):\n", + " current_state = states[-1]\n", + "\n", + " outputs = [\n", + " jit_select_action({}, current_state, None, None)\n", + " for jit_select_action in jit_select_action_list\n", + " ]\n", + " action = agents.merge_actions(outputs)\n", + " next_state = jit_step(current_state, action)\n", + "\n", + " states.append(next_state)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "s6TmqhLRGc_7" + }, + "source": [ + "## Visualization of simulation.\n", + "\n", + "We can now visualize the result of the simulation loop.\n", + "\n", + "On the left side:\n", + "- Objects 5, 6, and 7 (controlled by static_actor) remain static.\n", + "- Objects 3 and 4 controlled by log playback, and collide with objects 5 and 6.\n", + "\n", + "On the right side:\n", + "- Object 2 controlled by actor_1 is moving at constant speed 5m/s (i.e. slower than log in this case).\n", + "- Object 0 and 1, controlled by the IDM agent, follow the log in the beginning, but object 1 slows down when approaching object 2." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "cYWjd4dE1bB2" + }, + "outputs": [], + "source": [ + "imgs = []\n", + "for state in states:\n", + " imgs.append(visualization.plot_simulator_state(state, use_log_traj=False))\n", + "mediapy.show_video(imgs, fps=10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "liRaNVbE1gWb" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "name": "multi_actors_demo.ipynb", + "private_outputs": true, + "provenance": [ + { + "file_id": "14w5MbrMNLsOsLuD5kXy5-rrNO3ZgsHat", + "timestamp": 1678404744504 + } + ] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/_sources/build/jupyter_execute/notebooks/wosac_submission_via_waymax.ipynb.txt b/_sources/build/jupyter_execute/notebooks/wosac_submission_via_waymax.ipynb.txt new file mode 100644 index 0000000..d04c1c3 --- /dev/null +++ b/_sources/build/jupyter_execute/notebooks/wosac_submission_via_waymax.ipynb.txt @@ -0,0 +1,348 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "DH236BT5wcH7" + }, + "source": [ + "# Waymo Open Sim Agents Challenge Submission\n", + "\n", + "This tutorial covers how to use Waymax to create a Waymo Open Sim Agents Challenge (WOSAC) submission.\n", + "\n", + "Please also refer to the [WOSAC submission notebook](https://github.com/waymo-research/waymo-open-dataset/blob/master/tutorial/tutorial_sim_agents.ipynb) for additional reference and for setting up a submission without Waymax." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "n-sW1wrcvHys" + }, + "outputs": [], + "source": [ + "!pip install waymo-open-dataset-tf-2-11-0==1.6.0\n", + "\n", + "import os\n", + "import jax\n", + "from jax import random\n", + "from jax import numpy as jnp\n", + "import tensorflow as tf\n", + "\n", + "from waymo_open_dataset.protos import sim_agents_submission_pb2\n", + "from waymax import agents\n", + "from waymax import config as _config\n", + "from waymax import dynamics\n", + "from waymax import dataloader\n", + "from waymax import datatypes\n", + "from waymax import env as _env\n", + "\n", + "CURRENT_TIME_INDEX = 10\n", + "N_SIMULATION_STEPS = 80\n", + "N_ROLLOUTS = 32" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9YTPFzmMwu-F" + }, + "source": [ + "## Dataloader\n", + "\n", + "To load data for a WOSAC submission, we write a custom dataloader that processes the scenario IDs. These are normally discarded in the default Waymax dataloader as they are not used during simulation and JAX does not have native support for string data. The scenario ID is stored in the field `scenario/id` as described in the [`tf.Example` spec](https://waymo.com/open/data/motion/tfexample).\n", + "\n", + "This custom dataloader defines a preprocessor `_preprocess` that decodes the scenario ID into an array of bytes, and a postprocessor `_postprocess` that converts those bytes into the string scenario ID. The actual scenario data is processed in the same way as the default dataloader in Waymax." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JJAfGGSF74Ym" + }, + "outputs": [], + "source": [ + "data_config = _config.WOD_1_2_0_TEST\n", + "\n", + "# Write a custom dataloader that loads scenario IDs.\n", + "def _preprocess(serialized: bytes) -> dict[str, tf.Tensor]:\n", + " womd_features = dataloader.womd_utils.get_features_description(\n", + " include_sdc_paths=data_config.include_sdc_paths,\n", + " max_num_rg_points=data_config.max_num_rg_points,\n", + " num_paths=data_config.num_paths,\n", + " num_points_per_path=data_config.num_points_per_path,\n", + " )\n", + " womd_features['scenario/id'] = tf.io.FixedLenFeature([1], tf.string)\n", + "\n", + " deserialized = tf.io.parse_example(serialized, womd_features)\n", + " parsed_id = deserialized.pop('scenario/id')\n", + " deserialized['scenario/id'] = tf.io.decode_raw(parsed_id, tf.uint8)\n", + "\n", + " return dataloader.preprocess_womd_example(\n", + " deserialized,\n", + " aggregate_timesteps=data_config.aggregate_timesteps,\n", + " max_num_objects=data_config.max_num_objects,\n", + " )\n", + "\n", + "def _postprocess(example: dict[str, tf.Tensor]):\n", + " scenario = dataloader.simulator_state_from_womd_dict(example)\n", + " scenario_id = example['scenario/id']\n", + " return scenario_id, scenario\n", + "\n", + "def decode_bytes(data_iter):\n", + " for scenario_id, scenario in data_iter:\n", + " scenario_id = scenario_id.tobytes().decode('utf-8')\n", + " yield scenario_id, scenario\n", + "\n", + "data_iter = decode_bytes(dataloader.get_data_generator(\n", + " data_config, _preprocess, _postprocess\n", + "))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hXQk4wG8xmTs" + }, + "source": [ + "## Environment and Agent Configuration\n", + "\n", + "The following code initializes the environment and sim agent used for simulation. In this example, we use a constant speed actor which will maintain the course and speed that the agent has at the initial timestep.\n", + "\n", + "WOSAC evaluates metrics on all agents valid at the initial timestep. Therefore, the `is_controlled` field is set to all valid agents at the 11th timestep.\n", + "\n", + "Other configurations related to the agent and environment are customizable. This includes the dynamics model (here, we use the `InvertibleBicycleModel`) and the type of sim agent to evaluate." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "XnvXA1z1wwqQ" + }, + "outputs": [], + "source": [ + "env_config = _config.EnvironmentConfig(\n", + " # Ensure that the sim agent can control all valid objects.\n", + " controlled_object=_config.ObjectType.VALID\n", + ")\n", + "\n", + "dynamics_model = dynamics.InvertibleBicycleModel()\n", + "env = _env.MultiAgentEnvironment(\n", + " dynamics_model=dynamics_model,\n", + " config=env_config,\n", + ")\n", + "\n", + "agent = agents.create_constant_speed_actor(\n", + " dynamics_model=dynamics_model,\n", + " # Controlled objects are those valid at t=0.\n", + " is_controlled_func=lambda state: state.log_trajectory.valid[..., CURRENT_TIME_INDEX]\n", + ")\n", + "\n", + "jit_step = jax.jit(env.step)\n", + "jit_select_action = jax.jit(agent.select_action)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jGI9vfXFyBEo" + }, + "source": [ + "## Generating Rollouts\n", + "\n", + "We can now define a function that will rollout the environment and agent to generate trajectories. The WOSAC submission format consists of multiple protobufs defined in `sim_agents_submission_pb2`. These consist of (copied from the [WOSAC submission notebook](https://github.com/waymo-research/waymo-open-dataset/blob/master/tutorial/tutorial_sim_agents.ipynb)):\n", + "\n", + "- `SimulatedTrajectory` contains one trajectory for a single object, with the fields we need to simulate (x, y, z, heading).\n", + "- `JointScene` is a set of all the object trajectories from a single simulation, describing one of the possible rollouts.\n", + "- `ScenarioRollouts` is a collection of all the parallel simulations for a single initial Scenario.\n", + "- `SimAgentsChallengeSubmission` is used to package submissions for multiple Scenarios (e.g. for the whole testing dataset).\n", + "\n", + "Here, we will write a function `generate_scenario_rollout` that generates a `ScenarioRollouts` protobuf from a single input scenario. By default, WOSAC requires 32 rollouts per scenario. Our actor is deterministic so all 32 rollouts will be identical, but we still generate these rollouts to provide an accurate example of a proper submission.\n", + "\n", + "We also provide a utility function `validate_scenario_rollout` to help ensure that the scenario rollouts have the correct format before uploading.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "-hHb4wXa6Jo-" + }, + "outputs": [], + "source": [ + "def validate_scenario_rollout(scenario_rollouts: sim_agents_submission_pb2.ScenarioRollouts,\n", + " scenario: datatypes.SimulatorState):\n", + " \"\"\"Verifies if scenario_rollouts has correct formatting.\"\"\"\n", + " valid_sim_agents = scenario.log_trajectory.valid[..., CURRENT_TIME_INDEX]\n", + " sim_agent_id_idxs = jnp.where(valid_sim_agents)[0]\n", + " sim_agent_ids = scenario.object_metadata.ids[sim_agent_id_idxs].tolist()\n", + "\n", + " if len(scenario_rollouts.joint_scenes) != N_ROLLOUTS:\n", + " raise ValueError('Incorrect number of parallel simulations. '\n", + " f'(Actual: {len(scenario_rollouts.joint_scenes)}, '\n", + " f'Expected: {N_ROLLOUTS})')\n", + "\n", + " def _raise_if_wrong_length(trajectory, field_name, expected_length):\n", + " if len(getattr(trajectory, field_name)) != expected_length:\n", + " raise ValueError(f'Invalid {field_name} tensor length '\n", + " f'(actual: {len(getattr(trajectory, field_name))}, '\n", + " f'expected: {expected_length})')\n", + "\n", + " for joint_scene in scenario_rollouts.joint_scenes:\n", + " simulated_ids = []\n", + " for simulated_trajectory in joint_scene.simulated_trajectories:\n", + " # Check the length of each of the simulated fields.\n", + " _raise_if_wrong_length(simulated_trajectory, 'center_x', N_SIMULATION_STEPS)\n", + " _raise_if_wrong_length(simulated_trajectory, 'center_y', N_SIMULATION_STEPS)\n", + " _raise_if_wrong_length(simulated_trajectory, 'center_z', N_SIMULATION_STEPS)\n", + " _raise_if_wrong_length(simulated_trajectory, 'heading', N_SIMULATION_STEPS)\n", + " # Check that each object ID is present in the original WOMD scenario.\n", + " if simulated_trajectory.object_id not in sim_agent_ids:\n", + " raise ValueError(\n", + " f'Object {simulated_trajectory.object_id} is not a sim agent.')\n", + " simulated_ids.append(simulated_trajectory.object_id)\n", + " # Check that all of the required objects/agents are simulated.\n", + " missing_agents = set(sim_agent_ids) - set(simulated_ids)\n", + " if missing_agents:\n", + " raise ValueError(\n", + " f'Sim agents {missing_agents} are missing from the simulation.')\n", + "\n", + "\n", + "def generate_scenario_rollout(\n", + " scenario_id: str,\n", + " scenario: datatypes.SimulatorState) -> sim_agents_submission_pb2.ScenarioRollouts:\n", + " \"\"\"Simulate 32 rollouts and return a ScenarioRollouts protobuf.\"\"\"\n", + " joint_scenes = []\n", + " key = random.PRNGKey(0)\n", + " for _ in range(N_ROLLOUTS):\n", + " initial_state = current_state = env.reset(scenario)\n", + " # Controlled objects are those valid at t=0.\n", + " is_controlled = scenario.log_trajectory.valid[..., CURRENT_TIME_INDEX]\n", + "\n", + " # Run the sim agent for 80 steps.\n", + " for _ in (range(initial_state.remaining_timesteps)):\n", + " key, actor_key = random.split(key, 2)\n", + " actor_output = jit_select_action({}, current_state, None, actor_key)\n", + " next_state = jit_step(current_state, actor_output.action)\n", + " current_state = next_state\n", + "\n", + " # Write out result\n", + " final_trajectory = current_state.sim_trajectory\n", + " object_ids = current_state.object_metadata.ids # Shape (n_objects,)\n", + " object_ids = jnp.where(is_controlled, object_ids, -1)\n", + "\n", + " simulated_trajectories = []\n", + " for i, object_id in enumerate(object_ids):\n", + " if object_id != -1:\n", + " simulated_trajectory = sim_agents_submission_pb2.SimulatedTrajectory(\n", + " center_x=final_trajectory.x[i, env_config.init_steps:],\n", + " center_y=final_trajectory.y[i, env_config.init_steps:],\n", + " center_z=final_trajectory.z[i, env_config.init_steps:],\n", + " heading=final_trajectory.yaw[i, env_config.init_steps:],\n", + " object_id=object_id,\n", + " )\n", + " simulated_trajectories.append(simulated_trajectory)\n", + " joint_scene = sim_agents_submission_pb2.JointScene(\n", + " simulated_trajectories=simulated_trajectories\n", + " )\n", + " joint_scenes.append(joint_scene)\n", + "\n", + " scenario_rollouts = sim_agents_submission_pb2.ScenarioRollouts(\n", + " scenario_id=scenario_id, joint_scenes=joint_scenes\n", + " )\n", + " validate_scenario_rollout(scenario_rollouts, scenario)\n", + " return scenario_rollouts" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XPlK3Qp_yD8K" + }, + "source": [ + "## Generating the Submission\n", + "\n", + "We are now ready to generate the submission file. Because the data is potentially large (over the 2GB maximum size for a protobuf), we process the data in a streaming fashion and write out results incrementally. The testing set of Waymo Open Motion Dataset v1.2.0 has 44926 segments -- this step may take a significant amount of time if the rollout generation time is long.\n", + "\n", + "After we process all of the data, we zip the individual shards to create a zip file ready for submission. Please refer to the Open dataset website for further instructions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "mjOZoUVYT0OW" + }, + "outputs": [], + "source": [ + "OUTPUT_ROOT_DIRECTORY = '/tmp/waymo_sim_agents/'\n", + "os.makedirs(OUTPUT_ROOT_DIRECTORY, exist_ok=True)\n", + "output_filenames = []\n", + "scenario_rollouts = []\n", + "\n", + "for i, (scenario_id, scenario) in enumerate(data_iter):\n", + " scenario_rollouts.append(generate_scenario_rollout(scenario_id, scenario))\n", + "\n", + " if i % 5 == 0 and i > 0:\n", + " shard_suffix = '.%d' % i\n", + " shard_submission = sim_agents_submission_pb2.SimAgentsChallengeSubmission(\n", + " scenario_rollouts=scenario_rollouts,\n", + " submission_type=sim_agents_submission_pb2.SimAgentsChallengeSubmission.SIM_AGENTS_SUBMISSION,\n", + " account_name='your_account@test.com',\n", + " unique_method_name='waymax_sim_agents_tutorial',\n", + " authors=['test'],\n", + " affiliation='waymo',\n", + " description='Submission from the Waymax - Sim Agents tutorial',\n", + " method_link='https://waymo.com/open/'\n", + " )\n", + " scenario_rollouts = []\n", + " output_filename = f'submission.binproto{shard_suffix}'\n", + " with open(os.path.join(OUTPUT_ROOT_DIRECTORY, output_filename), 'wb') as f:\n", + " f.write(shard_submission.SerializeToString())\n", + " output_filenames.append(output_filename)\n", + "\n", + "# Once we have created all the shards, we can package them directly into a\n", + "# tar.gz archive, ready for submission.\n", + "with tarfile.open(\n", + " os.path.join(OUTPUT_ROOT_DIRECTORY, 'submission.tar.gz'), 'w:gz') as tar:\n", + " for output_filename in output_filenames:\n", + " tar.add(os.path.join(OUTPUT_ROOT_DIRECTORY, output_filename),\n", + " arcname=output_filename)" + ] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "private_outputs": true, + "provenance": [ + { + "file_id": "165MIhRtVwtmR9ontFYJe6suV6CLHerJI", + "timestamp": 1695771240550 + }, + { + "file_id": "1Y3eSCA7LCGrCJ672zHeBnStJKTEorl5z", + "timestamp": 1695771147053 + }, + { + "file_id": "1l1iYQbLAGQ1vv-13AriC2bp78LPv3LH2", + "timestamp": 1695668356011 + } + ] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/_sources/getting_started.rst.txt b/_sources/getting_started.rst.txt new file mode 100644 index 0000000..322975b --- /dev/null +++ b/_sources/getting_started.rst.txt @@ -0,0 +1,11 @@ +.. _getting-started : + +Tutorials +========= + +The following tutorials are also available as IPython notebooks on the Github repository. + +- :doc:`notebooks/data_demo` +- :doc:`notebooks/datatypes_demo` +- :doc:`notebooks/multi_actors_demo` +- :doc:`notebooks/wosac_submission_via_waymax` \ No newline at end of file diff --git a/_sources/index.rst.txt b/_sources/index.rst.txt new file mode 100644 index 0000000..e900ca5 --- /dev/null +++ b/_sources/index.rst.txt @@ -0,0 +1,62 @@ +Waymax: An accelerated simulator for autonomous driving research +================================================================ + +Waymax is a lightweight, multi-agent, `JAX`_-based simulator for autonomous driving research based on the Waymo Open Motion Dataset. + +.. grid:: 2 + :margin: 0 + :padding: 0 + :gutter: 2 + + .. grid-item-card:: :material-regular:`rocket_launch;2em` Getting Started + :link: getting-started + :link-type: ref + :class-card: sd-border-0 + + .. grid-item-card:: :material-regular:`library_books;2em` API Documentation + :link: autoapi/waymax/index + :link-type: doc + :class-card: sd-border-0 + + +Installation +------------ +.. code-block:: bash + + pip install git+https://github.com/waymo-research/waymax.git@main#egg=waymo-waymax + +For additional installation options, see the `Install Guide`_ in the project README. + + +.. toctree:: + :hidden: + :maxdepth: 1 + + getting_started + + +.. toctree:: + :hidden: + :maxdepth: 2 + :caption: API Documentation + + autoapi/waymax/agents/index + autoapi/waymax/dataloader/index + autoapi/waymax/datatypes/index + autoapi/waymax/env/index + autoapi/waymax/metrics/index + autoapi/waymax/rewards/index + autoapi/waymax/utils/index + autoapi/waymax/visualization/index + autoapi/waymax/config/index + +.. toctree:: + :hidden: + :maxdepth: 1 + :caption: Further Resources + + genindex + modindex + +.. _JAX: https://github.com/google/jax +.. _Install Guide: https://github.com/waymo-research/waymax#installation diff --git a/_sources/notebooks/data_demo.ipynb.txt b/_sources/notebooks/data_demo.ipynb.txt new file mode 100644 index 0000000..1bcfb35 --- /dev/null +++ b/_sources/notebooks/data_demo.ipynb.txt @@ -0,0 +1,136 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "rNYRA6k8Qfyo" + }, + "source": [ + "# Scenario Data Loading\n", + "\n", + "This tutorial demonstrates how to load scenario data from the Waymo Open Motion Dataset (WOMD) using the Waymax dataloader." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MtgRcYqmtMwD" + }, + "outputs": [], + "source": [ + "%%capture\n", + "import numpy as np\n", + "import mediapy\n", + "from tqdm import tqdm\n", + "import dataclasses\n", + "\n", + "from waymax import config as _config\n", + "from waymax import dataloader\n", + "from waymax import datatypes\n", + "from waymax import visualization" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0o2sAapxRMAT" + }, + "source": [ + "\n", + "We first create a dataset config, using the default configs provided in the `waymax.config` module. In particular, `config.WOD_1_1_0_TRAINING` is a pre-defined configuration that points to version 1.1.0 of the Waymo Open Dataset.\n", + "\n", + "The data config contains a number of options to configure how and where the dataset is loaded from. By default, the `WOD_1_1_0_TRAINING` loads up to 128 objects (e.g. vehicles, pedestrians) per scenario. Here, we can save memory and compute by loading only the first 32 objects stored in the scenario.\n", + "\n", + "We use the `dataloader.simulator_state_generator` function to create an iterator\n", + "through Open Motion Dataset scenarios. Calling next on the iterator will retrieve the first scenario in the dataset.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "dkJwTuSLr0gh" + }, + "outputs": [], + "source": [ + "config = dataclasses.replace(_config.WOD_1_1_0_TRAINING, max_num_objects=32)\n", + "data_iter = dataloader.simulator_state_generator(config=config)\n", + "scenario = next(data_iter)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "q1xyeYpLR8J6" + }, + "source": [ + "Next, we can plot the initial state of this scenario. We use a matplotlib-based visualization available in the `waymax.visualization` package." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "OY3-OOArsFcU" + }, + "outputs": [], + "source": [ + "# Using logged trajectory\n", + "img = visualization.plot_simulator_state(scenario, use_log_traj=True)\n", + "mediapy.show_image(img)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "H0Z15epRSC23" + }, + "source": [ + "The Waymo Open Motion Dataset consists of 9-second trajectory snippets. We can visualize the entire logged trajectory as a video as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "06SjvXdRrV3N" + }, + "outputs": [], + "source": [ + "imgs = []\n", + "\n", + "state = scenario\n", + "for _ in range(scenario.remaining_timesteps):\n", + " state = datatypes.update_state_by_log(state, num_steps=1)\n", + " imgs.append(visualization.plot_simulator_state(state, use_log_traj=True))\n", + "\n", + "mediapy.show_video(imgs, fps=10)" + ] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "private_outputs": true, + "provenance": [ + { + "file_id": "14w5MbrMNLsOsLuD5kXy5-rrNO3ZgsHat", + "timestamp": 1678404744504 + } + ] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/_sources/notebooks/datatypes_demo.ipynb.txt b/_sources/notebooks/datatypes_demo.ipynb.txt new file mode 100644 index 0000000..bb084ec --- /dev/null +++ b/_sources/notebooks/datatypes_demo.ipynb.txt @@ -0,0 +1,300 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "0IwlAjlibuUE" + }, + "source": [ + "# Understanding and Manipulating Data in Waymax\n", + "\n", + "This tutorial covers data structures in Waymax." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1NTUsEy_btuU" + }, + "outputs": [], + "source": [ + "%%capture\n", + "import dataclasses\n", + "import jax\n", + "from jax import numpy as jnp\n", + "from matplotlib import pyplot as plt\n", + "\n", + "from waymax import config as _config\n", + "from waymax import dataloader\n", + "from waymax import datatypes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "2pcSuBb9cFZN" + }, + "outputs": [], + "source": [ + "# Load example data.\n", + "config = dataclasses.replace(_config.WOD_1_1_0_VALIDATION, max_num_objects=32)\n", + "data_iter = dataloader.simulator_state_generator(config=config)\n", + "scenario = next(data_iter)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GZAFy24xcLQc" + }, + "source": [ + "## JAX-based data structures\n", + "\n", + "The key property with all data structures in Waymax is that all data is *immutable*. This is a design decision that is inherited from JAX and enables code written using Waymax to be compatible with the powerful functional transforms in JAX, such as `jit`, `vmap`, etc. While efficiency may be a concern with immutable data structures, wrapping your function in `jax.jit` will allow JAX to optimize and replace your operations with in-place operations wherever possible, avoiding the need for excessive data copying.\n", + "\n", + "Additionally, all datastructures in Waymax are implemented as dataclasses. This allows convenient named access to fields, and allows simple nesting of data structures that is easy to manipulate with tree-based operations (such as those in `jax.tree_util`).\n", + "\n", + "The first example we will cover is the `datatypes.Trajectory` data structure, which holds the pose information for all objects. The scenario that we loaded contains a trajectory containing the logged behavior for all agents under the `scenario.log_trajectory` attribute." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "sPMaI3ShdPwB" + }, + "outputs": [], + "source": [ + "log_trajectory = scenario.log_trajectory\n", + "\n", + "# Number of objects stored in this trajectory.\n", + "print('Number of objects:', log_trajectory.num_objects)\n", + "print('Number of timesteps:', log_trajectory.num_timesteps)\n", + "print('Trajectory shape (num_objects, num_timesteps):', log_trajectory.shape)\n", + "print('XYZ positions (num_objects, num_timesteps, 3):', log_trajectory.xyz.shape)\n", + "print('XY velocities (num_objects, num_timesteps, 2):', log_trajectory.vel_xy.shape)\n", + "print('Yaw (num_objects, num_timesteps):', log_trajectory.yaw.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nsKEO7GCelBK" + }, + "source": [ + "The `datatypes` module contains some helper methods that automatically map over datastructures. We can use `datatypes.dynamic_slice` to select out the trajectory belonging to a particular object or at a particular timestep. These operations, as with all JAX operations, will return new copies of the object they are modifying. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "k6cWGuVRdlQk" + }, + "outputs": [], + "source": [ + "# Slice by time. Select the trajectory at timestep 23.\n", + "traj_t23 = datatypes.dynamic_slice(log_trajectory, start_index=23, slice_size=1, axis=-1)\n", + "print('XYZ positions (num_objects, 1, 3):', traj_t23.xyz.shape)\n", + "\n", + "# Slice by object. Select the trajectory for object 15.\n", + "traj_obj15 = datatypes.dynamic_slice(log_trajectory, start_index=15, slice_size=1, axis=-2)\n", + "print('XYZ positions (1, num_timesteps, 3):', traj_obj15.xyz.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LXrPjhzPf7EJ" + }, + "source": [ + "Of course, JAX functions from the core library also work on Waymax data structures. The `tree_map` function is particularly useful for working with dataclasses, and will apply a single function to all fields in the data structure (recursively if there are nested data structures)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "shVRPmToe9oY" + }, + "outputs": [], + "source": [ + "def max_along_time(x: jax.Array) -\u003e jax.Array:\n", + " return jnp.max(x, axis=-1, keepdims=True)\n", + "\n", + "max_trajectory = jax.tree_util.tree_map(max_along_time, log_trajectory)\n", + "print('XYZ positions (num_objects, 1, 3):', max_trajectory.xyz.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mlPkVLA7Mcx-" + }, + "source": [ + "To modify the values of the data structure, we can use `dataclasses.replace` to replace entire fields, and `Array.at[idx].set(value)` to selectively modify individual values. For example, to set the all yaws for object 1 to zero, we can use the following code snippet:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Z5napP9gMutj" + }, + "outputs": [], + "source": [ + "zeroed_traj = dataclasses.replace(\n", + " log_trajectory, \n", + " yaw=log_trajectory.yaw.at[1].set(0.0)\n", + ")\n", + "\n", + "# Should be the original values.\n", + "print('Yaws for object 0, timesteps 0 to 5:', zeroed_traj.yaw[0, 0:5])\n", + "\n", + "# Should be now set to 0.\n", + "print('Yaws for object 1, timesteps 0 to 5:', zeroed_traj.yaw[1, 0:5])\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "E7DZS_BKhZLj" + }, + "source": [ + "# Other important data structures\n", + "\n", + "We will now cover the remaining important data structures that are stored in a scenario.\n", + "\n", + "The `datatypes.RoadgraphPoints` data structure holds all static information regarding the road and environment. This includes all lanes markers, road edges, stop signs, speed bumps, and crosswalks. \n", + "- The `x`, `y`, and `z` attributes define the spatial coordinates of the points.\n", + "- The `type` attribute is an integer that defines what type of point (lane, edge, stop sign, etc.) the point is. See `roadgraph_samples/type` of the [Waymo Open Motion Dataset](https://waymo.com/open/data/motion/tfexample) for definitions of which value corresponds to what type of point.\n", + "- The `dir_x` and `dir_y` attributes define the orientation of the points. Lane points will orient in the forward direction of the lane. Edge points are oriented such that the inside of the road is always on the port side (left if facing forward) of the point.\n", + "- The `id` field is a unique identifier for each contiguous lane. Lanes end if there is an intersection or reach the edge of the map." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "k9XQmInAh4vo" + }, + "outputs": [], + "source": [ + "# Plot the roadgraph, with colors corresponding to the road type.\n", + "rg_points = scenario.roadgraph_points\n", + "\n", + "where_valid = rg_points.valid\n", + "plt.scatter(\n", + " x = rg_points.x[where_valid],\n", + " y = rg_points.y[where_valid],\n", + " s=0.1,\n", + " c = rg_points.types[where_valid]\n", + ")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2MdtujKbjahp" + }, + "source": [ + "The `datatypes.TrafficLights` structure holds time-varying information regarding the color and position of the traffic lights.\n", + "- The `x`, `y`, and `z` attributes define the spatial location of the light.\n", + "- The `state` attribute defines what color the light is at a particular instance in time.\n", + "- The `lane_ids` attribute tells what lanes the traffic light is controlling. These can be cross-referenced with the `RoadgraphPoints.ids` field." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "zse6OxgaiEbH" + }, + "outputs": [], + "source": [ + "traffic_lights = scenario.log_traffic_light\n", + "\n", + "print('Traffic Light States (num_lights, num_timesteps):', traffic_lights.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QY-rhD1Qkr-C" + }, + "source": [ + "Finally, `datatypes.ObjectMetadata` holds \n", + "- The `object_types` attribute defines whether the object is a vehicle, pedestrian, or cyclist.\n", + "- The `ids` attribute assigns a unique ID to each object.\n", + "- The `is_sdc` attribute defines whether the object is the ego-vehicle (or self-driving car).\n", + "- The `is_modeled` attribute marks whether the object's behavior is meant to be predicted as part of the Waymo Open Motion dataset.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Dohh8d9Rkh70" + }, + "outputs": [], + "source": [ + "metadata = scenario.object_metadata\n", + "print('All object IDS:', metadata.ids)\n", + "\n", + "# Color-code object trajectory by whether it is the SDC or not.\n", + "# The SDC trajectory in the center is shown in blue, and all other trajectories\n", + "# are shown in red.\n", + "flat_trajectory = jax.tree_util.tree_map(lambda x: jnp.reshape(x, [-1]), log_trajectory)\n", + "colors = jnp.zeros(log_trajectory.shape, dtype=jnp.int32).at[metadata.is_sdc].set(1)\n", + "colors = jnp.reshape(colors, [-1])\n", + "\n", + "where_valid = flat_trajectory.valid\n", + "plt.scatter(\n", + " x=flat_trajectory.x[where_valid],\n", + " y=flat_trajectory.y[where_valid],\n", + " s=0.5,\n", + " c=colors[where_valid],\n", + " cmap='RdYlBu'\n", + ")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "hmNQVj-clfIr" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "private_outputs": true, + "provenance": [ + { + "file_id": "1Ee2coesuhg82e7E9HmZxafZ7raD_ug5q", + "timestamp": 1683520258687 + } + ] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/_sources/notebooks/multi_actors_demo.ipynb.txt b/_sources/notebooks/multi_actors_demo.ipynb.txt new file mode 100644 index 0000000..77e3384 --- /dev/null +++ b/_sources/notebooks/multi_actors_demo.ipynb.txt @@ -0,0 +1,272 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "qg5s5R7AT8Fj" + }, + "source": [ + "# Multi-agent Simulation\n", + "\n", + "This tutorial demonstrates how to run a simple closed-loop simulation with multiple pre-defined sim agents." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MtgRcYqmtMwD" + }, + "outputs": [], + "source": [ + "import jax\n", + "from jax import numpy as jnp\n", + "import numpy as np\n", + "import mediapy\n", + "from tqdm import tqdm\n", + "import dataclasses\n", + "\n", + "from waymax import config as _config\n", + "from waymax import dataloader\n", + "from waymax import datatypes\n", + "from waymax import dynamics\n", + "from waymax import env as _env\n", + "from waymax import agents\n", + "from waymax import visualization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "dkJwTuSLr0gh" + }, + "outputs": [], + "source": [ + "# Config dataset:\n", + "max_num_objects = 32\n", + "\n", + "config = dataclasses.replace(_config.WOD_1_0_0_VALIDATION, max_num_objects=max_num_objects)\n", + "data_iter = dataloader.simulator_state_generator(config=config)\n", + "scenario = next(data_iter)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DINmUYg7y-jI" + }, + "source": [ + "## Initializing and Running the Simulator\n", + "\n", + "Waymax uses a Gym-like interface for running closed-loop simulation. \n", + "\n", + "The `env.MultiAgentEnvironment` class defines a stateless simulation interface with the two key methods:\n", + "- The `reset` method initializes and returns the first simulation state.\n", + "- The `step` method transitions the simulation and takes as arguments a state and an action and outputs the next state.\n", + "\n", + "Crucially, the `MultiAgentEnvironment` does not hold any simulation state itself, and the `reset` and `step` functions have no side effects. This allows us to use functional transforms from JAX, such as using jit compilation to optimize the compuation. It also allows the user to arbitrarily branch and restart simulation from any state, or save the simulation by simply serializing and saving the state object.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "x7u2dqPCtdeq" + }, + "outputs": [], + "source": [ + "# Config the multi-agent environment:\n", + "init_steps = 11\n", + "\n", + "# Set the dynamics model the environment is using.\n", + "# Note each actor interacting with the environment needs to provide action\n", + "# compatible with this dynamics model.\n", + "dynamics_model = dynamics.StateDynamics()\n", + "\n", + "# Expect users to control all valid object in the scene.\n", + "env = _env.MultiAgentEnvironment(\n", + " dynamics_model=dynamics_model,\n", + " config=dataclasses.replace(\n", + " _config.EnvironmentConfig(),\n", + " max_num_objects=max_num_objects,\n", + " controlled_object=_config.ObjectType.VALID,\n", + " ),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "876iboHbYx2H" + }, + "source": [ + "We now create a set of sim agents to run in simulation. By default, the behavior of an object that is not controlled is to replay the behavior stored in the dataset (log playback).\n", + "\n", + "For each sim agent, we define the algorithm (such as IDM), and specify which objects the agent controls via the `is_controlled_func`, which is required to return a boolean mask marking which objects are being controlled.\n", + "\n", + "The IDM agent we use in this example is the `IDMRoutePolicy`, which follows the spatial trajectory stored in the logs, but adjusts the speed profile based on the IDM rule, which will stop or speed up according to the distance between the vehicle and any objects in front of the vehicle. For the remaining agents, we set them to use a constant speed policy which will follow the logged route with a fixed, constant speed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "IfCHlgJzghUS" + }, + "outputs": [], + "source": [ + "# Setup a few actors, see visualization below for how each actor behaves.\n", + "\n", + "# An actor that doesn't move, controlling all objects with index \u003e 4\n", + "obj_idx = jnp.arange(max_num_objects)\n", + "static_actor = agents.create_constant_speed_actor(\n", + " speed=0.0,\n", + " dynamics_model=dynamics_model,\n", + " is_controlled_func=lambda state: obj_idx \u003e 4,\n", + ")\n", + "\n", + "# IDM actor/policy controlling both object 0 and 1.\n", + "# Note IDM policy is an actor hard-coded to use dynamics.StateDynamics().\n", + "actor_0 = agents.IDMRoutePolicy(\n", + " is_controlled_func=lambda state: (obj_idx == 0) | (obj_idx == 1)\n", + ")\n", + "\n", + "# Constant speed actor with predefined fixed speed controlling object 2.\n", + "actor_1 = agents.create_constant_speed_actor(\n", + " speed=5.0,\n", + " dynamics_model=dynamics_model,\n", + " is_controlled_func=lambda state: obj_idx == 2,\n", + ")\n", + "\n", + "# Exper/log actor controlling objects 3 and 4.\n", + "actor_2 = agents.create_expert_actor(\n", + " dynamics_model=dynamics_model,\n", + " is_controlled_func=lambda state: (obj_idx == 3) | (obj_idx == 4),\n", + ")\n", + "\n", + "actors = [static_actor, actor_0, actor_1, actor_2]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JvV82vOXWbI0" + }, + "source": [ + "We can (optionally) jit the step and select action functions to speed up computation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "YmzEJy2LUwe5" + }, + "outputs": [], + "source": [ + "jit_step = jax.jit(env.step)\n", + "jit_select_action_list = [jax.jit(actor.select_action) for actor in actors]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iBeI2mdIUdTw" + }, + "source": [ + "We can now write a for loop to all of these agents in simulation together.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "06SjvXdRrV3N" + }, + "outputs": [], + "source": [ + "states = [env.reset(scenario)]\n", + "for _ in range(states[0].remaining_timesteps):\n", + " current_state = states[-1]\n", + "\n", + " outputs = [\n", + " jit_select_action({}, current_state, None, None)\n", + " for jit_select_action in jit_select_action_list\n", + " ]\n", + " action = agents.merge_actions(outputs)\n", + " next_state = jit_step(current_state, action)\n", + "\n", + " states.append(next_state)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "s6TmqhLRGc_7" + }, + "source": [ + "## Visualization of simulation.\n", + "\n", + "We can now visualize the result of the simulation loop.\n", + "\n", + "On the left side:\n", + "- Objects 5, 6, and 7 (controlled by static_actor) remain static.\n", + "- Objects 3 and 4 controlled by log playback, and collide with objects 5 and 6.\n", + "\n", + "On the right side:\n", + "- Object 2 controlled by actor_1 is moving at constant speed 5m/s (i.e. slower than log in this case).\n", + "- Object 0 and 1, controlled by the IDM agent, follow the log in the beginning, but object 1 slows down when approaching object 2." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "cYWjd4dE1bB2" + }, + "outputs": [], + "source": [ + "imgs = []\n", + "for state in states:\n", + " imgs.append(visualization.plot_simulator_state(state, use_log_traj=False))\n", + "mediapy.show_video(imgs, fps=10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "liRaNVbE1gWb" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "name": "multi_actors_demo.ipynb", + "private_outputs": true, + "provenance": [ + { + "file_id": "14w5MbrMNLsOsLuD5kXy5-rrNO3ZgsHat", + "timestamp": 1678404744504 + } + ] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/_sources/notebooks/wosac_submission_via_waymax.ipynb.txt b/_sources/notebooks/wosac_submission_via_waymax.ipynb.txt new file mode 100644 index 0000000..5ef1012 --- /dev/null +++ b/_sources/notebooks/wosac_submission_via_waymax.ipynb.txt @@ -0,0 +1,348 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "DH236BT5wcH7" + }, + "source": [ + "# Waymo Open Sim Agents Challenge Submission\n", + "\n", + "This tutorial covers how to use Waymax to create a Waymo Open Sim Agents Challenge (WOSAC) submission.\n", + "\n", + "Please also refer to the [WOSAC submission notebook](https://github.com/waymo-research/waymo-open-dataset/blob/master/tutorial/tutorial_sim_agents.ipynb) for additional reference and for setting up a submission without Waymax." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "n-sW1wrcvHys" + }, + "outputs": [], + "source": [ + "!pip install waymo-open-dataset-tf-2-11-0==1.6.0\n", + "\n", + "import os\n", + "import jax\n", + "from jax import random\n", + "from jax import numpy as jnp\n", + "import tensorflow as tf\n", + "\n", + "from waymo_open_dataset.protos import sim_agents_submission_pb2\n", + "from waymax import agents\n", + "from waymax import config as _config\n", + "from waymax import dynamics\n", + "from waymax import dataloader\n", + "from waymax import datatypes\n", + "from waymax import env as _env\n", + "\n", + "CURRENT_TIME_INDEX = 10\n", + "N_SIMULATION_STEPS = 80\n", + "N_ROLLOUTS = 32" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9YTPFzmMwu-F" + }, + "source": [ + "## Dataloader\n", + "\n", + "To load data for a WOSAC submission, we write a custom dataloader that processes the scenario IDs. These are normally discarded in the default Waymax dataloader as they are not used during simulation and JAX does not have native support for string data. The scenario ID is stored in the field `scenario/id` as described in the [`tf.Example` spec](https://waymo.com/open/data/motion/tfexample).\n", + "\n", + "This custom dataloader defines a preprocessor `_preprocess` that decodes the scenario ID into an array of bytes, and a postprocessor `_postprocess` that converts those bytes into the string scenario ID. The actual scenario data is processed in the same way as the default dataloader in Waymax." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JJAfGGSF74Ym" + }, + "outputs": [], + "source": [ + "data_config = _config.WOD_1_2_0_TEST\n", + "\n", + "# Write a custom dataloader that loads scenario IDs.\n", + "def _preprocess(serialized: bytes) -\u003e dict[str, tf.Tensor]:\n", + " womd_features = dataloader.womd_utils.get_features_description(\n", + " include_sdc_paths=data_config.include_sdc_paths,\n", + " max_num_rg_points=data_config.max_num_rg_points,\n", + " num_paths=data_config.num_paths,\n", + " num_points_per_path=data_config.num_points_per_path,\n", + " )\n", + " womd_features['scenario/id'] = tf.io.FixedLenFeature([1], tf.string)\n", + "\n", + " deserialized = tf.io.parse_example(serialized, womd_features)\n", + " parsed_id = deserialized.pop('scenario/id')\n", + " deserialized['scenario/id'] = tf.io.decode_raw(parsed_id, tf.uint8)\n", + "\n", + " return dataloader.preprocess_womd_example(\n", + " deserialized,\n", + " aggregate_timesteps=data_config.aggregate_timesteps,\n", + " max_num_objects=data_config.max_num_objects,\n", + " )\n", + "\n", + "def _postprocess(example: dict[str, tf.Tensor]):\n", + " scenario = dataloader.simulator_state_from_womd_dict(example)\n", + " scenario_id = example['scenario/id']\n", + " return scenario_id, scenario\n", + "\n", + "def decode_bytes(data_iter):\n", + " for scenario_id, scenario in data_iter:\n", + " scenario_id = scenario_id.tobytes().decode('utf-8')\n", + " yield scenario_id, scenario\n", + "\n", + "data_iter = decode_bytes(dataloader.get_data_generator(\n", + " data_config, _preprocess, _postprocess\n", + "))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hXQk4wG8xmTs" + }, + "source": [ + "## Environment and Agent Configuration\n", + "\n", + "The following code initializes the environment and sim agent used for simulation. In this example, we use a constant speed actor which will maintain the course and speed that the agent has at the initial timestep.\n", + "\n", + "WOSAC evaluates metrics on all agents valid at the initial timestep. Therefore, the `is_controlled` field is set to all valid agents at the 11th timestep.\n", + "\n", + "Other configurations related to the agent and environment are customizable. This includes the dynamics model (here, we use the `InvertibleBicycleModel`) and the type of sim agent to evaluate." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "XnvXA1z1wwqQ" + }, + "outputs": [], + "source": [ + "env_config = _config.EnvironmentConfig(\n", + " # Ensure that the sim agent can control all valid objects.\n", + " controlled_object=_config.ObjectType.VALID\n", + ")\n", + "\n", + "dynamics_model = dynamics.InvertibleBicycleModel()\n", + "env = _env.MultiAgentEnvironment(\n", + " dynamics_model=dynamics_model,\n", + " config=env_config,\n", + ")\n", + "\n", + "agent = agents.create_constant_speed_actor(\n", + " dynamics_model=dynamics_model,\n", + " # Controlled objects are those valid at t=0.\n", + " is_controlled_func=lambda state: state.log_trajectory.valid[..., CURRENT_TIME_INDEX]\n", + ")\n", + "\n", + "jit_step = jax.jit(env.step)\n", + "jit_select_action = jax.jit(agent.select_action)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jGI9vfXFyBEo" + }, + "source": [ + "## Generating Rollouts\n", + "\n", + "We can now define a function that will rollout the environment and agent to generate trajectories. The WOSAC submission format consists of multiple protobufs defined in `sim_agents_submission_pb2`. These consist of (copied from the [WOSAC submission notebook](https://github.com/waymo-research/waymo-open-dataset/blob/master/tutorial/tutorial_sim_agents.ipynb)):\n", + "\n", + "- `SimulatedTrajectory` contains one trajectory for a single object, with the fields we need to simulate (x, y, z, heading).\n", + "- `JointScene` is a set of all the object trajectories from a single simulation, describing one of the possible rollouts.\n", + "- `ScenarioRollouts` is a collection of all the parallel simulations for a single initial Scenario.\n", + "- `SimAgentsChallengeSubmission` is used to package submissions for multiple Scenarios (e.g. for the whole testing dataset).\n", + "\n", + "Here, we will write a function `generate_scenario_rollout` that generates a `ScenarioRollouts` protobuf from a single input scenario. By default, WOSAC requires 32 rollouts per scenario. Our actor is deterministic so all 32 rollouts will be identical, but we still generate these rollouts to provide an accurate example of a proper submission.\n", + "\n", + "We also provide a utility function `validate_scenario_rollout` to help ensure that the scenario rollouts have the correct format before uploading.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "-hHb4wXa6Jo-" + }, + "outputs": [], + "source": [ + "def validate_scenario_rollout(scenario_rollouts: sim_agents_submission_pb2.ScenarioRollouts,\n", + " scenario: datatypes.SimulatorState):\n", + " \"\"\"Verifies if scenario_rollouts has correct formatting.\"\"\"\n", + " valid_sim_agents = scenario.log_trajectory.valid[..., CURRENT_TIME_INDEX]\n", + " sim_agent_id_idxs = jnp.where(valid_sim_agents)[0]\n", + " sim_agent_ids = scenario.object_metadata.ids[sim_agent_id_idxs].tolist()\n", + "\n", + " if len(scenario_rollouts.joint_scenes) != N_ROLLOUTS:\n", + " raise ValueError('Incorrect number of parallel simulations. '\n", + " f'(Actual: {len(scenario_rollouts.joint_scenes)}, '\n", + " f'Expected: {N_ROLLOUTS})')\n", + "\n", + " def _raise_if_wrong_length(trajectory, field_name, expected_length):\n", + " if len(getattr(trajectory, field_name)) != expected_length:\n", + " raise ValueError(f'Invalid {field_name} tensor length '\n", + " f'(actual: {len(getattr(trajectory, field_name))}, '\n", + " f'expected: {expected_length})')\n", + "\n", + " for joint_scene in scenario_rollouts.joint_scenes:\n", + " simulated_ids = []\n", + " for simulated_trajectory in joint_scene.simulated_trajectories:\n", + " # Check the length of each of the simulated fields.\n", + " _raise_if_wrong_length(simulated_trajectory, 'center_x', N_SIMULATION_STEPS)\n", + " _raise_if_wrong_length(simulated_trajectory, 'center_y', N_SIMULATION_STEPS)\n", + " _raise_if_wrong_length(simulated_trajectory, 'center_z', N_SIMULATION_STEPS)\n", + " _raise_if_wrong_length(simulated_trajectory, 'heading', N_SIMULATION_STEPS)\n", + " # Check that each object ID is present in the original WOMD scenario.\n", + " if simulated_trajectory.object_id not in sim_agent_ids:\n", + " raise ValueError(\n", + " f'Object {simulated_trajectory.object_id} is not a sim agent.')\n", + " simulated_ids.append(simulated_trajectory.object_id)\n", + " # Check that all of the required objects/agents are simulated.\n", + " missing_agents = set(sim_agent_ids) - set(simulated_ids)\n", + " if missing_agents:\n", + " raise ValueError(\n", + " f'Sim agents {missing_agents} are missing from the simulation.')\n", + "\n", + "\n", + "def generate_scenario_rollout(\n", + " scenario_id: str,\n", + " scenario: datatypes.SimulatorState) -\u003e sim_agents_submission_pb2.ScenarioRollouts:\n", + " \"\"\"Simulate 32 rollouts and return a ScenarioRollouts protobuf.\"\"\"\n", + " joint_scenes = []\n", + " key = random.PRNGKey(0)\n", + " for _ in range(N_ROLLOUTS):\n", + " initial_state = current_state = env.reset(scenario)\n", + " # Controlled objects are those valid at t=0.\n", + " is_controlled = scenario.log_trajectory.valid[..., CURRENT_TIME_INDEX]\n", + "\n", + " # Run the sim agent for 80 steps.\n", + " for _ in (range(initial_state.remaining_timesteps)):\n", + " key, actor_key = random.split(key, 2)\n", + " actor_output = jit_select_action({}, current_state, None, actor_key)\n", + " next_state = jit_step(current_state, actor_output.action)\n", + " current_state = next_state\n", + "\n", + " # Write out result\n", + " final_trajectory = current_state.sim_trajectory\n", + " object_ids = current_state.object_metadata.ids # Shape (n_objects,)\n", + " object_ids = jnp.where(is_controlled, object_ids, -1)\n", + "\n", + " simulated_trajectories = []\n", + " for i, object_id in enumerate(object_ids):\n", + " if object_id != -1:\n", + " simulated_trajectory = sim_agents_submission_pb2.SimulatedTrajectory(\n", + " center_x=final_trajectory.x[i, env_config.init_steps:],\n", + " center_y=final_trajectory.y[i, env_config.init_steps:],\n", + " center_z=final_trajectory.z[i, env_config.init_steps:],\n", + " heading=final_trajectory.yaw[i, env_config.init_steps:],\n", + " object_id=object_id,\n", + " )\n", + " simulated_trajectories.append(simulated_trajectory)\n", + " joint_scene = sim_agents_submission_pb2.JointScene(\n", + " simulated_trajectories=simulated_trajectories\n", + " )\n", + " joint_scenes.append(joint_scene)\n", + "\n", + " scenario_rollouts = sim_agents_submission_pb2.ScenarioRollouts(\n", + " scenario_id=scenario_id, joint_scenes=joint_scenes\n", + " )\n", + " validate_scenario_rollout(scenario_rollouts, scenario)\n", + " return scenario_rollouts" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XPlK3Qp_yD8K" + }, + "source": [ + "## Generating the Submission\n", + "\n", + "We are now ready to generate the submission file. Because the data is potentially large (over the 2GB maximum size for a protobuf), we process the data in a streaming fashion and write out results incrementally. The testing set of Waymo Open Motion Dataset v1.2.0 has 44926 segments -- this step may take a significant amount of time if the rollout generation time is long.\n", + "\n", + "After we process all of the data, we zip the individual shards to create a zip file ready for submission. Please refer to the Open dataset website for further instructions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "mjOZoUVYT0OW" + }, + "outputs": [], + "source": [ + "OUTPUT_ROOT_DIRECTORY = '/tmp/waymo_sim_agents/'\n", + "os.makedirs(OUTPUT_ROOT_DIRECTORY, exist_ok=True)\n", + "output_filenames = []\n", + "scenario_rollouts = []\n", + "\n", + "for i, (scenario_id, scenario) in enumerate(data_iter):\n", + " scenario_rollouts.append(generate_scenario_rollout(scenario_id, scenario))\n", + "\n", + " if i % 5 == 0 and i \u003e 0:\n", + " shard_suffix = '.%d' % i\n", + " shard_submission = sim_agents_submission_pb2.SimAgentsChallengeSubmission(\n", + " scenario_rollouts=scenario_rollouts,\n", + " submission_type=sim_agents_submission_pb2.SimAgentsChallengeSubmission.SIM_AGENTS_SUBMISSION,\n", + " account_name='your_account@test.com',\n", + " unique_method_name='waymax_sim_agents_tutorial',\n", + " authors=['test'],\n", + " affiliation='waymo',\n", + " description='Submission from the Waymax - Sim Agents tutorial',\n", + " method_link='https://waymo.com/open/'\n", + " )\n", + " scenario_rollouts = []\n", + " output_filename = f'submission.binproto{shard_suffix}'\n", + " with open(os.path.join(OUTPUT_ROOT_DIRECTORY, output_filename), 'wb') as f:\n", + " f.write(shard_submission.SerializeToString())\n", + " output_filenames.append(output_filename)\n", + "\n", + "# Once we have created all the shards, we can package them directly into a\n", + "# tar.gz archive, ready for submission.\n", + "with tarfile.open(\n", + " os.path.join(OUTPUT_ROOT_DIRECTORY, 'submission.tar.gz'), 'w:gz') as tar:\n", + " for output_filename in output_filenames:\n", + " tar.add(os.path.join(OUTPUT_ROOT_DIRECTORY, output_filename),\n", + " arcname=output_filename)" + ] + } + ], + "metadata": { + "colab": { + "last_runtime": { + "build_target": "", + "kind": "local" + }, + "private_outputs": true, + "provenance": [ + { + "file_id": "165MIhRtVwtmR9ontFYJe6suV6CLHerJI", + "timestamp": 1695771240550 + }, + { + "file_id": "1Y3eSCA7LCGrCJ672zHeBnStJKTEorl5z", + "timestamp": 1695771147053 + }, + { + "file_id": "1l1iYQbLAGQ1vv-13AriC2bp78LPv3LH2", + "timestamp": 1695668356011 + } + ] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/_sphinx_design_static/design-style.1e8bd061cd6da7fc9cf755528e8ffc24.min.css b/_sphinx_design_static/design-style.1e8bd061cd6da7fc9cf755528e8ffc24.min.css new file mode 100644 index 0000000..eb19f69 --- /dev/null +++ b/_sphinx_design_static/design-style.1e8bd061cd6da7fc9cf755528e8ffc24.min.css @@ -0,0 +1 @@ +.sd-bg-primary{background-color:var(--sd-color-primary) !important}.sd-bg-text-primary{color:var(--sd-color-primary-text) !important}button.sd-bg-primary:focus,button.sd-bg-primary:hover{background-color:var(--sd-color-primary-highlight) !important}a.sd-bg-primary:focus,a.sd-bg-primary:hover{background-color:var(--sd-color-primary-highlight) !important}.sd-bg-secondary{background-color:var(--sd-color-secondary) !important}.sd-bg-text-secondary{color:var(--sd-color-secondary-text) !important}button.sd-bg-secondary:focus,button.sd-bg-secondary:hover{background-color:var(--sd-color-secondary-highlight) !important}a.sd-bg-secondary:focus,a.sd-bg-secondary:hover{background-color:var(--sd-color-secondary-highlight) !important}.sd-bg-success{background-color:var(--sd-color-success) !important}.sd-bg-text-success{color:var(--sd-color-success-text) !important}button.sd-bg-success:focus,button.sd-bg-success:hover{background-color:var(--sd-color-success-highlight) !important}a.sd-bg-success:focus,a.sd-bg-success:hover{background-color:var(--sd-color-success-highlight) !important}.sd-bg-info{background-color:var(--sd-color-info) !important}.sd-bg-text-info{color:var(--sd-color-info-text) !important}button.sd-bg-info:focus,button.sd-bg-info:hover{background-color:var(--sd-color-info-highlight) !important}a.sd-bg-info:focus,a.sd-bg-info:hover{background-color:var(--sd-color-info-highlight) !important}.sd-bg-warning{background-color:var(--sd-color-warning) !important}.sd-bg-text-warning{color:var(--sd-color-warning-text) !important}button.sd-bg-warning:focus,button.sd-bg-warning:hover{background-color:var(--sd-color-warning-highlight) !important}a.sd-bg-warning:focus,a.sd-bg-warning:hover{background-color:var(--sd-color-warning-highlight) !important}.sd-bg-danger{background-color:var(--sd-color-danger) !important}.sd-bg-text-danger{color:var(--sd-color-danger-text) !important}button.sd-bg-danger:focus,button.sd-bg-danger:hover{background-color:var(--sd-color-danger-highlight) !important}a.sd-bg-danger:focus,a.sd-bg-danger:hover{background-color:var(--sd-color-danger-highlight) !important}.sd-bg-light{background-color:var(--sd-color-light) !important}.sd-bg-text-light{color:var(--sd-color-light-text) !important}button.sd-bg-light:focus,button.sd-bg-light:hover{background-color:var(--sd-color-light-highlight) !important}a.sd-bg-light:focus,a.sd-bg-light:hover{background-color:var(--sd-color-light-highlight) !important}.sd-bg-muted{background-color:var(--sd-color-muted) !important}.sd-bg-text-muted{color:var(--sd-color-muted-text) !important}button.sd-bg-muted:focus,button.sd-bg-muted:hover{background-color:var(--sd-color-muted-highlight) !important}a.sd-bg-muted:focus,a.sd-bg-muted:hover{background-color:var(--sd-color-muted-highlight) !important}.sd-bg-dark{background-color:var(--sd-color-dark) !important}.sd-bg-text-dark{color:var(--sd-color-dark-text) !important}button.sd-bg-dark:focus,button.sd-bg-dark:hover{background-color:var(--sd-color-dark-highlight) !important}a.sd-bg-dark:focus,a.sd-bg-dark:hover{background-color:var(--sd-color-dark-highlight) !important}.sd-bg-black{background-color:var(--sd-color-black) !important}.sd-bg-text-black{color:var(--sd-color-black-text) !important}button.sd-bg-black:focus,button.sd-bg-black:hover{background-color:var(--sd-color-black-highlight) !important}a.sd-bg-black:focus,a.sd-bg-black:hover{background-color:var(--sd-color-black-highlight) !important}.sd-bg-white{background-color:var(--sd-color-white) !important}.sd-bg-text-white{color:var(--sd-color-white-text) !important}button.sd-bg-white:focus,button.sd-bg-white:hover{background-color:var(--sd-color-white-highlight) !important}a.sd-bg-white:focus,a.sd-bg-white:hover{background-color:var(--sd-color-white-highlight) !important}.sd-text-primary,.sd-text-primary>p{color:var(--sd-color-primary) !important}a.sd-text-primary:focus,a.sd-text-primary:hover{color:var(--sd-color-primary-highlight) !important}.sd-text-secondary,.sd-text-secondary>p{color:var(--sd-color-secondary) !important}a.sd-text-secondary:focus,a.sd-text-secondary:hover{color:var(--sd-color-secondary-highlight) !important}.sd-text-success,.sd-text-success>p{color:var(--sd-color-success) !important}a.sd-text-success:focus,a.sd-text-success:hover{color:var(--sd-color-success-highlight) !important}.sd-text-info,.sd-text-info>p{color:var(--sd-color-info) !important}a.sd-text-info:focus,a.sd-text-info:hover{color:var(--sd-color-info-highlight) !important}.sd-text-warning,.sd-text-warning>p{color:var(--sd-color-warning) !important}a.sd-text-warning:focus,a.sd-text-warning:hover{color:var(--sd-color-warning-highlight) !important}.sd-text-danger,.sd-text-danger>p{color:var(--sd-color-danger) !important}a.sd-text-danger:focus,a.sd-text-danger:hover{color:var(--sd-color-danger-highlight) !important}.sd-text-light,.sd-text-light>p{color:var(--sd-color-light) !important}a.sd-text-light:focus,a.sd-text-light:hover{color:var(--sd-color-light-highlight) !important}.sd-text-muted,.sd-text-muted>p{color:var(--sd-color-muted) !important}a.sd-text-muted:focus,a.sd-text-muted:hover{color:var(--sd-color-muted-highlight) !important}.sd-text-dark,.sd-text-dark>p{color:var(--sd-color-dark) !important}a.sd-text-dark:focus,a.sd-text-dark:hover{color:var(--sd-color-dark-highlight) !important}.sd-text-black,.sd-text-black>p{color:var(--sd-color-black) !important}a.sd-text-black:focus,a.sd-text-black:hover{color:var(--sd-color-black-highlight) !important}.sd-text-white,.sd-text-white>p{color:var(--sd-color-white) !important}a.sd-text-white:focus,a.sd-text-white:hover{color:var(--sd-color-white-highlight) !important}.sd-outline-primary{border-color:var(--sd-color-primary) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-primary:focus,a.sd-outline-primary:hover{border-color:var(--sd-color-primary-highlight) !important}.sd-outline-secondary{border-color:var(--sd-color-secondary) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-secondary:focus,a.sd-outline-secondary:hover{border-color:var(--sd-color-secondary-highlight) !important}.sd-outline-success{border-color:var(--sd-color-success) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-success:focus,a.sd-outline-success:hover{border-color:var(--sd-color-success-highlight) !important}.sd-outline-info{border-color:var(--sd-color-info) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-info:focus,a.sd-outline-info:hover{border-color:var(--sd-color-info-highlight) !important}.sd-outline-warning{border-color:var(--sd-color-warning) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-warning:focus,a.sd-outline-warning:hover{border-color:var(--sd-color-warning-highlight) !important}.sd-outline-danger{border-color:var(--sd-color-danger) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-danger:focus,a.sd-outline-danger:hover{border-color:var(--sd-color-danger-highlight) !important}.sd-outline-light{border-color:var(--sd-color-light) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-light:focus,a.sd-outline-light:hover{border-color:var(--sd-color-light-highlight) !important}.sd-outline-muted{border-color:var(--sd-color-muted) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-muted:focus,a.sd-outline-muted:hover{border-color:var(--sd-color-muted-highlight) !important}.sd-outline-dark{border-color:var(--sd-color-dark) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-dark:focus,a.sd-outline-dark:hover{border-color:var(--sd-color-dark-highlight) !important}.sd-outline-black{border-color:var(--sd-color-black) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-black:focus,a.sd-outline-black:hover{border-color:var(--sd-color-black-highlight) !important}.sd-outline-white{border-color:var(--sd-color-white) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-white:focus,a.sd-outline-white:hover{border-color:var(--sd-color-white-highlight) !important}.sd-bg-transparent{background-color:transparent !important}.sd-outline-transparent{border-color:transparent !important}.sd-text-transparent{color:transparent !important}.sd-p-0{padding:0 !important}.sd-pt-0,.sd-py-0{padding-top:0 !important}.sd-pr-0,.sd-px-0{padding-right:0 !important}.sd-pb-0,.sd-py-0{padding-bottom:0 !important}.sd-pl-0,.sd-px-0{padding-left:0 !important}.sd-p-1{padding:.25rem !important}.sd-pt-1,.sd-py-1{padding-top:.25rem !important}.sd-pr-1,.sd-px-1{padding-right:.25rem !important}.sd-pb-1,.sd-py-1{padding-bottom:.25rem !important}.sd-pl-1,.sd-px-1{padding-left:.25rem !important}.sd-p-2{padding:.5rem !important}.sd-pt-2,.sd-py-2{padding-top:.5rem !important}.sd-pr-2,.sd-px-2{padding-right:.5rem !important}.sd-pb-2,.sd-py-2{padding-bottom:.5rem !important}.sd-pl-2,.sd-px-2{padding-left:.5rem !important}.sd-p-3{padding:1rem !important}.sd-pt-3,.sd-py-3{padding-top:1rem !important}.sd-pr-3,.sd-px-3{padding-right:1rem !important}.sd-pb-3,.sd-py-3{padding-bottom:1rem !important}.sd-pl-3,.sd-px-3{padding-left:1rem !important}.sd-p-4{padding:1.5rem !important}.sd-pt-4,.sd-py-4{padding-top:1.5rem !important}.sd-pr-4,.sd-px-4{padding-right:1.5rem !important}.sd-pb-4,.sd-py-4{padding-bottom:1.5rem !important}.sd-pl-4,.sd-px-4{padding-left:1.5rem !important}.sd-p-5{padding:3rem !important}.sd-pt-5,.sd-py-5{padding-top:3rem !important}.sd-pr-5,.sd-px-5{padding-right:3rem !important}.sd-pb-5,.sd-py-5{padding-bottom:3rem !important}.sd-pl-5,.sd-px-5{padding-left:3rem !important}.sd-m-auto{margin:auto !important}.sd-mt-auto,.sd-my-auto{margin-top:auto !important}.sd-mr-auto,.sd-mx-auto{margin-right:auto !important}.sd-mb-auto,.sd-my-auto{margin-bottom:auto !important}.sd-ml-auto,.sd-mx-auto{margin-left:auto !important}.sd-m-0{margin:0 !important}.sd-mt-0,.sd-my-0{margin-top:0 !important}.sd-mr-0,.sd-mx-0{margin-right:0 !important}.sd-mb-0,.sd-my-0{margin-bottom:0 !important}.sd-ml-0,.sd-mx-0{margin-left:0 !important}.sd-m-1{margin:.25rem !important}.sd-mt-1,.sd-my-1{margin-top:.25rem !important}.sd-mr-1,.sd-mx-1{margin-right:.25rem !important}.sd-mb-1,.sd-my-1{margin-bottom:.25rem !important}.sd-ml-1,.sd-mx-1{margin-left:.25rem !important}.sd-m-2{margin:.5rem !important}.sd-mt-2,.sd-my-2{margin-top:.5rem !important}.sd-mr-2,.sd-mx-2{margin-right:.5rem !important}.sd-mb-2,.sd-my-2{margin-bottom:.5rem !important}.sd-ml-2,.sd-mx-2{margin-left:.5rem !important}.sd-m-3{margin:1rem !important}.sd-mt-3,.sd-my-3{margin-top:1rem !important}.sd-mr-3,.sd-mx-3{margin-right:1rem !important}.sd-mb-3,.sd-my-3{margin-bottom:1rem !important}.sd-ml-3,.sd-mx-3{margin-left:1rem !important}.sd-m-4{margin:1.5rem !important}.sd-mt-4,.sd-my-4{margin-top:1.5rem !important}.sd-mr-4,.sd-mx-4{margin-right:1.5rem !important}.sd-mb-4,.sd-my-4{margin-bottom:1.5rem !important}.sd-ml-4,.sd-mx-4{margin-left:1.5rem !important}.sd-m-5{margin:3rem !important}.sd-mt-5,.sd-my-5{margin-top:3rem !important}.sd-mr-5,.sd-mx-5{margin-right:3rem !important}.sd-mb-5,.sd-my-5{margin-bottom:3rem !important}.sd-ml-5,.sd-mx-5{margin-left:3rem !important}.sd-w-25{width:25% !important}.sd-w-50{width:50% !important}.sd-w-75{width:75% !important}.sd-w-100{width:100% !important}.sd-w-auto{width:auto !important}.sd-h-25{height:25% !important}.sd-h-50{height:50% !important}.sd-h-75{height:75% !important}.sd-h-100{height:100% !important}.sd-h-auto{height:auto !important}.sd-d-none{display:none !important}.sd-d-inline{display:inline !important}.sd-d-inline-block{display:inline-block !important}.sd-d-block{display:block !important}.sd-d-grid{display:grid !important}.sd-d-flex-row{display:-ms-flexbox !important;display:flex !important;flex-direction:row !important}.sd-d-flex-column{display:-ms-flexbox !important;display:flex !important;flex-direction:column !important}.sd-d-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}@media(min-width: 576px){.sd-d-sm-none{display:none !important}.sd-d-sm-inline{display:inline !important}.sd-d-sm-inline-block{display:inline-block !important}.sd-d-sm-block{display:block !important}.sd-d-sm-grid{display:grid !important}.sd-d-sm-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-sm-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 768px){.sd-d-md-none{display:none !important}.sd-d-md-inline{display:inline !important}.sd-d-md-inline-block{display:inline-block !important}.sd-d-md-block{display:block !important}.sd-d-md-grid{display:grid !important}.sd-d-md-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-md-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 992px){.sd-d-lg-none{display:none !important}.sd-d-lg-inline{display:inline !important}.sd-d-lg-inline-block{display:inline-block !important}.sd-d-lg-block{display:block !important}.sd-d-lg-grid{display:grid !important}.sd-d-lg-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-lg-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 1200px){.sd-d-xl-none{display:none !important}.sd-d-xl-inline{display:inline !important}.sd-d-xl-inline-block{display:inline-block !important}.sd-d-xl-block{display:block !important}.sd-d-xl-grid{display:grid !important}.sd-d-xl-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-xl-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}.sd-align-major-start{justify-content:flex-start !important}.sd-align-major-end{justify-content:flex-end !important}.sd-align-major-center{justify-content:center !important}.sd-align-major-justify{justify-content:space-between !important}.sd-align-major-spaced{justify-content:space-evenly !important}.sd-align-minor-start{align-items:flex-start !important}.sd-align-minor-end{align-items:flex-end !important}.sd-align-minor-center{align-items:center !important}.sd-align-minor-stretch{align-items:stretch !important}.sd-text-justify{text-align:justify !important}.sd-text-left{text-align:left !important}.sd-text-right{text-align:right !important}.sd-text-center{text-align:center !important}.sd-font-weight-light{font-weight:300 !important}.sd-font-weight-lighter{font-weight:lighter !important}.sd-font-weight-normal{font-weight:400 !important}.sd-font-weight-bold{font-weight:700 !important}.sd-font-weight-bolder{font-weight:bolder !important}.sd-font-italic{font-style:italic !important}.sd-text-decoration-none{text-decoration:none !important}.sd-text-lowercase{text-transform:lowercase !important}.sd-text-uppercase{text-transform:uppercase !important}.sd-text-capitalize{text-transform:capitalize !important}.sd-text-wrap{white-space:normal !important}.sd-text-nowrap{white-space:nowrap !important}.sd-text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-fs-1,.sd-fs-1>p{font-size:calc(1.375rem + 1.5vw) !important;line-height:unset !important}.sd-fs-2,.sd-fs-2>p{font-size:calc(1.325rem + 0.9vw) !important;line-height:unset !important}.sd-fs-3,.sd-fs-3>p{font-size:calc(1.3rem + 0.6vw) !important;line-height:unset !important}.sd-fs-4,.sd-fs-4>p{font-size:calc(1.275rem + 0.3vw) !important;line-height:unset !important}.sd-fs-5,.sd-fs-5>p{font-size:1.25rem !important;line-height:unset !important}.sd-fs-6,.sd-fs-6>p{font-size:1rem !important;line-height:unset !important}.sd-border-0{border:0 solid !important}.sd-border-top-0{border-top:0 solid !important}.sd-border-bottom-0{border-bottom:0 solid !important}.sd-border-right-0{border-right:0 solid !important}.sd-border-left-0{border-left:0 solid !important}.sd-border-1{border:1px solid !important}.sd-border-top-1{border-top:1px solid !important}.sd-border-bottom-1{border-bottom:1px solid !important}.sd-border-right-1{border-right:1px solid !important}.sd-border-left-1{border-left:1px solid !important}.sd-border-2{border:2px solid !important}.sd-border-top-2{border-top:2px solid !important}.sd-border-bottom-2{border-bottom:2px solid !important}.sd-border-right-2{border-right:2px solid !important}.sd-border-left-2{border-left:2px solid !important}.sd-border-3{border:3px solid !important}.sd-border-top-3{border-top:3px solid !important}.sd-border-bottom-3{border-bottom:3px solid !important}.sd-border-right-3{border-right:3px solid !important}.sd-border-left-3{border-left:3px solid !important}.sd-border-4{border:4px solid !important}.sd-border-top-4{border-top:4px solid !important}.sd-border-bottom-4{border-bottom:4px solid !important}.sd-border-right-4{border-right:4px solid !important}.sd-border-left-4{border-left:4px solid !important}.sd-border-5{border:5px solid !important}.sd-border-top-5{border-top:5px solid !important}.sd-border-bottom-5{border-bottom:5px solid !important}.sd-border-right-5{border-right:5px solid !important}.sd-border-left-5{border-left:5px solid !important}.sd-rounded-0{border-radius:0 !important}.sd-rounded-1{border-radius:.2rem !important}.sd-rounded-2{border-radius:.3rem !important}.sd-rounded-3{border-radius:.5rem !important}.sd-rounded-pill{border-radius:50rem !important}.sd-rounded-circle{border-radius:50% !important}.shadow-none{box-shadow:none !important}.sd-shadow-sm{box-shadow:0 .125rem .25rem var(--sd-color-shadow) !important}.sd-shadow-md{box-shadow:0 .5rem 1rem var(--sd-color-shadow) !important}.sd-shadow-lg{box-shadow:0 1rem 3rem var(--sd-color-shadow) !important}@keyframes sd-slide-from-left{0%{transform:translateX(-100%)}100%{transform:translateX(0)}}@keyframes sd-slide-from-right{0%{transform:translateX(200%)}100%{transform:translateX(0)}}@keyframes sd-grow100{0%{transform:scale(0);opacity:.5}100%{transform:scale(1);opacity:1}}@keyframes sd-grow50{0%{transform:scale(0.5);opacity:.5}100%{transform:scale(1);opacity:1}}@keyframes sd-grow50-rot20{0%{transform:scale(0.5) rotateZ(-20deg);opacity:.5}75%{transform:scale(1) rotateZ(5deg);opacity:1}95%{transform:scale(1) rotateZ(-1deg);opacity:1}100%{transform:scale(1) rotateZ(0);opacity:1}}.sd-animate-slide-from-left{animation:1s ease-out 0s 1 normal none running sd-slide-from-left}.sd-animate-slide-from-right{animation:1s ease-out 0s 1 normal none running sd-slide-from-right}.sd-animate-grow100{animation:1s ease-out 0s 1 normal none running sd-grow100}.sd-animate-grow50{animation:1s ease-out 0s 1 normal none running sd-grow50}.sd-animate-grow50-rot20{animation:1s ease-out 0s 1 normal none running sd-grow50-rot20}.sd-badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.sd-badge:empty{display:none}a.sd-badge{text-decoration:none}.sd-btn .sd-badge{position:relative;top:-1px}.sd-btn{background-color:transparent;border:1px solid transparent;border-radius:.25rem;cursor:pointer;display:inline-block;font-weight:400;font-size:1rem;line-height:1.5;padding:.375rem .75rem;text-align:center;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;vertical-align:middle;user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none}.sd-btn:hover{text-decoration:none}@media(prefers-reduced-motion: reduce){.sd-btn{transition:none}}.sd-btn-primary,.sd-btn-outline-primary:hover,.sd-btn-outline-primary:focus{color:var(--sd-color-primary-text) !important;background-color:var(--sd-color-primary) !important;border-color:var(--sd-color-primary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-primary:hover,.sd-btn-primary:focus{color:var(--sd-color-primary-text) !important;background-color:var(--sd-color-primary-highlight) !important;border-color:var(--sd-color-primary-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-primary{color:var(--sd-color-primary) !important;border-color:var(--sd-color-primary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-secondary,.sd-btn-outline-secondary:hover,.sd-btn-outline-secondary:focus{color:var(--sd-color-secondary-text) !important;background-color:var(--sd-color-secondary) !important;border-color:var(--sd-color-secondary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-secondary:hover,.sd-btn-secondary:focus{color:var(--sd-color-secondary-text) !important;background-color:var(--sd-color-secondary-highlight) !important;border-color:var(--sd-color-secondary-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-secondary{color:var(--sd-color-secondary) !important;border-color:var(--sd-color-secondary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-success,.sd-btn-outline-success:hover,.sd-btn-outline-success:focus{color:var(--sd-color-success-text) !important;background-color:var(--sd-color-success) !important;border-color:var(--sd-color-success) !important;border-width:1px !important;border-style:solid !important}.sd-btn-success:hover,.sd-btn-success:focus{color:var(--sd-color-success-text) !important;background-color:var(--sd-color-success-highlight) !important;border-color:var(--sd-color-success-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-success{color:var(--sd-color-success) !important;border-color:var(--sd-color-success) !important;border-width:1px !important;border-style:solid !important}.sd-btn-info,.sd-btn-outline-info:hover,.sd-btn-outline-info:focus{color:var(--sd-color-info-text) !important;background-color:var(--sd-color-info) !important;border-color:var(--sd-color-info) !important;border-width:1px !important;border-style:solid !important}.sd-btn-info:hover,.sd-btn-info:focus{color:var(--sd-color-info-text) !important;background-color:var(--sd-color-info-highlight) !important;border-color:var(--sd-color-info-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-info{color:var(--sd-color-info) !important;border-color:var(--sd-color-info) !important;border-width:1px !important;border-style:solid !important}.sd-btn-warning,.sd-btn-outline-warning:hover,.sd-btn-outline-warning:focus{color:var(--sd-color-warning-text) !important;background-color:var(--sd-color-warning) !important;border-color:var(--sd-color-warning) !important;border-width:1px !important;border-style:solid !important}.sd-btn-warning:hover,.sd-btn-warning:focus{color:var(--sd-color-warning-text) !important;background-color:var(--sd-color-warning-highlight) !important;border-color:var(--sd-color-warning-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-warning{color:var(--sd-color-warning) !important;border-color:var(--sd-color-warning) !important;border-width:1px !important;border-style:solid !important}.sd-btn-danger,.sd-btn-outline-danger:hover,.sd-btn-outline-danger:focus{color:var(--sd-color-danger-text) !important;background-color:var(--sd-color-danger) !important;border-color:var(--sd-color-danger) !important;border-width:1px !important;border-style:solid !important}.sd-btn-danger:hover,.sd-btn-danger:focus{color:var(--sd-color-danger-text) !important;background-color:var(--sd-color-danger-highlight) !important;border-color:var(--sd-color-danger-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-danger{color:var(--sd-color-danger) !important;border-color:var(--sd-color-danger) !important;border-width:1px !important;border-style:solid !important}.sd-btn-light,.sd-btn-outline-light:hover,.sd-btn-outline-light:focus{color:var(--sd-color-light-text) !important;background-color:var(--sd-color-light) !important;border-color:var(--sd-color-light) !important;border-width:1px !important;border-style:solid !important}.sd-btn-light:hover,.sd-btn-light:focus{color:var(--sd-color-light-text) !important;background-color:var(--sd-color-light-highlight) !important;border-color:var(--sd-color-light-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-light{color:var(--sd-color-light) !important;border-color:var(--sd-color-light) !important;border-width:1px !important;border-style:solid !important}.sd-btn-muted,.sd-btn-outline-muted:hover,.sd-btn-outline-muted:focus{color:var(--sd-color-muted-text) !important;background-color:var(--sd-color-muted) !important;border-color:var(--sd-color-muted) !important;border-width:1px !important;border-style:solid !important}.sd-btn-muted:hover,.sd-btn-muted:focus{color:var(--sd-color-muted-text) !important;background-color:var(--sd-color-muted-highlight) !important;border-color:var(--sd-color-muted-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-muted{color:var(--sd-color-muted) !important;border-color:var(--sd-color-muted) !important;border-width:1px !important;border-style:solid !important}.sd-btn-dark,.sd-btn-outline-dark:hover,.sd-btn-outline-dark:focus{color:var(--sd-color-dark-text) !important;background-color:var(--sd-color-dark) !important;border-color:var(--sd-color-dark) !important;border-width:1px !important;border-style:solid !important}.sd-btn-dark:hover,.sd-btn-dark:focus{color:var(--sd-color-dark-text) !important;background-color:var(--sd-color-dark-highlight) !important;border-color:var(--sd-color-dark-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-dark{color:var(--sd-color-dark) !important;border-color:var(--sd-color-dark) !important;border-width:1px !important;border-style:solid !important}.sd-btn-black,.sd-btn-outline-black:hover,.sd-btn-outline-black:focus{color:var(--sd-color-black-text) !important;background-color:var(--sd-color-black) !important;border-color:var(--sd-color-black) !important;border-width:1px !important;border-style:solid !important}.sd-btn-black:hover,.sd-btn-black:focus{color:var(--sd-color-black-text) !important;background-color:var(--sd-color-black-highlight) !important;border-color:var(--sd-color-black-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-black{color:var(--sd-color-black) !important;border-color:var(--sd-color-black) !important;border-width:1px !important;border-style:solid !important}.sd-btn-white,.sd-btn-outline-white:hover,.sd-btn-outline-white:focus{color:var(--sd-color-white-text) !important;background-color:var(--sd-color-white) !important;border-color:var(--sd-color-white) !important;border-width:1px !important;border-style:solid !important}.sd-btn-white:hover,.sd-btn-white:focus{color:var(--sd-color-white-text) !important;background-color:var(--sd-color-white-highlight) !important;border-color:var(--sd-color-white-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-white{color:var(--sd-color-white) !important;border-color:var(--sd-color-white) !important;border-width:1px !important;border-style:solid !important}.sd-stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.sd-hide-link-text{font-size:0}.sd-octicon,.sd-material-icon{display:inline-block;fill:currentColor;vertical-align:middle}.sd-avatar-xs{border-radius:50%;object-fit:cover;object-position:center;width:1rem;height:1rem}.sd-avatar-sm{border-radius:50%;object-fit:cover;object-position:center;width:3rem;height:3rem}.sd-avatar-md{border-radius:50%;object-fit:cover;object-position:center;width:5rem;height:5rem}.sd-avatar-lg{border-radius:50%;object-fit:cover;object-position:center;width:7rem;height:7rem}.sd-avatar-xl{border-radius:50%;object-fit:cover;object-position:center;width:10rem;height:10rem}.sd-avatar-inherit{border-radius:50%;object-fit:cover;object-position:center;width:inherit;height:inherit}.sd-avatar-initial{border-radius:50%;object-fit:cover;object-position:center;width:initial;height:initial}.sd-card{background-clip:border-box;background-color:var(--sd-color-card-background);border:1px solid var(--sd-color-card-border);border-radius:.25rem;color:var(--sd-color-card-text);display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;position:relative;word-wrap:break-word}.sd-card>hr{margin-left:0;margin-right:0}.sd-card-hover:hover{border-color:var(--sd-color-card-border-hover);transform:scale(1.01)}.sd-card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem 1rem}.sd-card-title{margin-bottom:.5rem}.sd-card-subtitle{margin-top:-0.25rem;margin-bottom:0}.sd-card-text:last-child{margin-bottom:0}.sd-card-link:hover{text-decoration:none}.sd-card-link+.card-link{margin-left:1rem}.sd-card-header{padding:.5rem 1rem;margin-bottom:0;background-color:var(--sd-color-card-header);border-bottom:1px solid var(--sd-color-card-border)}.sd-card-header:first-child{border-radius:calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0}.sd-card-footer{padding:.5rem 1rem;background-color:var(--sd-color-card-footer);border-top:1px solid var(--sd-color-card-border)}.sd-card-footer:last-child{border-radius:0 0 calc(0.25rem - 1px) calc(0.25rem - 1px)}.sd-card-header-tabs{margin-right:-0.5rem;margin-bottom:-0.5rem;margin-left:-0.5rem;border-bottom:0}.sd-card-header-pills{margin-right:-0.5rem;margin-left:-0.5rem}.sd-card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem;border-radius:calc(0.25rem - 1px)}.sd-card-img,.sd-card-img-bottom,.sd-card-img-top{width:100%}.sd-card-img,.sd-card-img-top{border-top-left-radius:calc(0.25rem - 1px);border-top-right-radius:calc(0.25rem - 1px)}.sd-card-img,.sd-card-img-bottom{border-bottom-left-radius:calc(0.25rem - 1px);border-bottom-right-radius:calc(0.25rem - 1px)}.sd-cards-carousel{width:100%;display:flex;flex-wrap:nowrap;-ms-flex-direction:row;flex-direction:row;overflow-x:hidden;scroll-snap-type:x mandatory}.sd-cards-carousel.sd-show-scrollbar{overflow-x:auto}.sd-cards-carousel:hover,.sd-cards-carousel:focus{overflow-x:auto}.sd-cards-carousel>.sd-card{flex-shrink:0;scroll-snap-align:start}.sd-cards-carousel>.sd-card:not(:last-child){margin-right:3px}.sd-card-cols-1>.sd-card{width:90%}.sd-card-cols-2>.sd-card{width:45%}.sd-card-cols-3>.sd-card{width:30%}.sd-card-cols-4>.sd-card{width:22.5%}.sd-card-cols-5>.sd-card{width:18%}.sd-card-cols-6>.sd-card{width:15%}.sd-card-cols-7>.sd-card{width:12.8571428571%}.sd-card-cols-8>.sd-card{width:11.25%}.sd-card-cols-9>.sd-card{width:10%}.sd-card-cols-10>.sd-card{width:9%}.sd-card-cols-11>.sd-card{width:8.1818181818%}.sd-card-cols-12>.sd-card{width:7.5%}.sd-container,.sd-container-fluid,.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container-xl{margin-left:auto;margin-right:auto;padding-left:var(--sd-gutter-x, 0.75rem);padding-right:var(--sd-gutter-x, 0.75rem);width:100%}@media(min-width: 576px){.sd-container-sm,.sd-container{max-width:540px}}@media(min-width: 768px){.sd-container-md,.sd-container-sm,.sd-container{max-width:720px}}@media(min-width: 992px){.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container{max-width:960px}}@media(min-width: 1200px){.sd-container-xl,.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container{max-width:1140px}}.sd-row{--sd-gutter-x: 1.5rem;--sd-gutter-y: 0;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-top:calc(var(--sd-gutter-y) * -1);margin-right:calc(var(--sd-gutter-x) * -0.5);margin-left:calc(var(--sd-gutter-x) * -0.5)}.sd-row>*{box-sizing:border-box;flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--sd-gutter-x) * 0.5);padding-left:calc(var(--sd-gutter-x) * 0.5);margin-top:var(--sd-gutter-y)}.sd-col{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-auto>*{flex:0 0 auto;width:auto}.sd-row-cols-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}@media(min-width: 576px){.sd-col-sm{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-sm-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-sm-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-sm-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-sm-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-sm-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-sm-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-sm-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-sm-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-sm-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-sm-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-sm-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-sm-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-sm-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 768px){.sd-col-md{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-md-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-md-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-md-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-md-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-md-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-md-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-md-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-md-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-md-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-md-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-md-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-md-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-md-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 992px){.sd-col-lg{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-lg-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-lg-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-lg-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-lg-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-lg-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-lg-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-lg-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-lg-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-lg-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-lg-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-lg-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-lg-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-lg-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 1200px){.sd-col-xl{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-xl-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-xl-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-xl-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-xl-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-xl-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-xl-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-xl-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-xl-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-xl-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-xl-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-xl-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-xl-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-xl-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}.sd-col-auto{flex:0 0 auto;-ms-flex:0 0 auto;width:auto}.sd-col-1{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}.sd-col-2{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-col-3{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-col-4{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-col-5{flex:0 0 auto;-ms-flex:0 0 auto;width:41.6666666667%}.sd-col-6{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-col-7{flex:0 0 auto;-ms-flex:0 0 auto;width:58.3333333333%}.sd-col-8{flex:0 0 auto;-ms-flex:0 0 auto;width:66.6666666667%}.sd-col-9{flex:0 0 auto;-ms-flex:0 0 auto;width:75%}.sd-col-10{flex:0 0 auto;-ms-flex:0 0 auto;width:83.3333333333%}.sd-col-11{flex:0 0 auto;-ms-flex:0 0 auto;width:91.6666666667%}.sd-col-12{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-g-0,.sd-gy-0{--sd-gutter-y: 0}.sd-g-0,.sd-gx-0{--sd-gutter-x: 0}.sd-g-1,.sd-gy-1{--sd-gutter-y: 0.25rem}.sd-g-1,.sd-gx-1{--sd-gutter-x: 0.25rem}.sd-g-2,.sd-gy-2{--sd-gutter-y: 0.5rem}.sd-g-2,.sd-gx-2{--sd-gutter-x: 0.5rem}.sd-g-3,.sd-gy-3{--sd-gutter-y: 1rem}.sd-g-3,.sd-gx-3{--sd-gutter-x: 1rem}.sd-g-4,.sd-gy-4{--sd-gutter-y: 1.5rem}.sd-g-4,.sd-gx-4{--sd-gutter-x: 1.5rem}.sd-g-5,.sd-gy-5{--sd-gutter-y: 3rem}.sd-g-5,.sd-gx-5{--sd-gutter-x: 3rem}@media(min-width: 576px){.sd-col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-sm-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-sm-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-sm-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-sm-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-sm-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-sm-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-sm-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-sm-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-sm-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-sm-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-sm-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-sm-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-sm-0,.sd-gy-sm-0{--sd-gutter-y: 0}.sd-g-sm-0,.sd-gx-sm-0{--sd-gutter-x: 0}.sd-g-sm-1,.sd-gy-sm-1{--sd-gutter-y: 0.25rem}.sd-g-sm-1,.sd-gx-sm-1{--sd-gutter-x: 0.25rem}.sd-g-sm-2,.sd-gy-sm-2{--sd-gutter-y: 0.5rem}.sd-g-sm-2,.sd-gx-sm-2{--sd-gutter-x: 0.5rem}.sd-g-sm-3,.sd-gy-sm-3{--sd-gutter-y: 1rem}.sd-g-sm-3,.sd-gx-sm-3{--sd-gutter-x: 1rem}.sd-g-sm-4,.sd-gy-sm-4{--sd-gutter-y: 1.5rem}.sd-g-sm-4,.sd-gx-sm-4{--sd-gutter-x: 1.5rem}.sd-g-sm-5,.sd-gy-sm-5{--sd-gutter-y: 3rem}.sd-g-sm-5,.sd-gx-sm-5{--sd-gutter-x: 3rem}}@media(min-width: 768px){.sd-col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-md-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-md-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-md-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-md-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-md-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-md-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-md-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-md-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-md-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-md-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-md-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-md-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-md-0,.sd-gy-md-0{--sd-gutter-y: 0}.sd-g-md-0,.sd-gx-md-0{--sd-gutter-x: 0}.sd-g-md-1,.sd-gy-md-1{--sd-gutter-y: 0.25rem}.sd-g-md-1,.sd-gx-md-1{--sd-gutter-x: 0.25rem}.sd-g-md-2,.sd-gy-md-2{--sd-gutter-y: 0.5rem}.sd-g-md-2,.sd-gx-md-2{--sd-gutter-x: 0.5rem}.sd-g-md-3,.sd-gy-md-3{--sd-gutter-y: 1rem}.sd-g-md-3,.sd-gx-md-3{--sd-gutter-x: 1rem}.sd-g-md-4,.sd-gy-md-4{--sd-gutter-y: 1.5rem}.sd-g-md-4,.sd-gx-md-4{--sd-gutter-x: 1.5rem}.sd-g-md-5,.sd-gy-md-5{--sd-gutter-y: 3rem}.sd-g-md-5,.sd-gx-md-5{--sd-gutter-x: 3rem}}@media(min-width: 992px){.sd-col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-lg-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-lg-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-lg-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-lg-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-lg-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-lg-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-lg-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-lg-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-lg-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-lg-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-lg-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-lg-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-lg-0,.sd-gy-lg-0{--sd-gutter-y: 0}.sd-g-lg-0,.sd-gx-lg-0{--sd-gutter-x: 0}.sd-g-lg-1,.sd-gy-lg-1{--sd-gutter-y: 0.25rem}.sd-g-lg-1,.sd-gx-lg-1{--sd-gutter-x: 0.25rem}.sd-g-lg-2,.sd-gy-lg-2{--sd-gutter-y: 0.5rem}.sd-g-lg-2,.sd-gx-lg-2{--sd-gutter-x: 0.5rem}.sd-g-lg-3,.sd-gy-lg-3{--sd-gutter-y: 1rem}.sd-g-lg-3,.sd-gx-lg-3{--sd-gutter-x: 1rem}.sd-g-lg-4,.sd-gy-lg-4{--sd-gutter-y: 1.5rem}.sd-g-lg-4,.sd-gx-lg-4{--sd-gutter-x: 1.5rem}.sd-g-lg-5,.sd-gy-lg-5{--sd-gutter-y: 3rem}.sd-g-lg-5,.sd-gx-lg-5{--sd-gutter-x: 3rem}}@media(min-width: 1200px){.sd-col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-xl-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-xl-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-xl-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-xl-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-xl-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-xl-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-xl-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-xl-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-xl-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-xl-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-xl-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-xl-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-xl-0,.sd-gy-xl-0{--sd-gutter-y: 0}.sd-g-xl-0,.sd-gx-xl-0{--sd-gutter-x: 0}.sd-g-xl-1,.sd-gy-xl-1{--sd-gutter-y: 0.25rem}.sd-g-xl-1,.sd-gx-xl-1{--sd-gutter-x: 0.25rem}.sd-g-xl-2,.sd-gy-xl-2{--sd-gutter-y: 0.5rem}.sd-g-xl-2,.sd-gx-xl-2{--sd-gutter-x: 0.5rem}.sd-g-xl-3,.sd-gy-xl-3{--sd-gutter-y: 1rem}.sd-g-xl-3,.sd-gx-xl-3{--sd-gutter-x: 1rem}.sd-g-xl-4,.sd-gy-xl-4{--sd-gutter-y: 1.5rem}.sd-g-xl-4,.sd-gx-xl-4{--sd-gutter-x: 1.5rem}.sd-g-xl-5,.sd-gy-xl-5{--sd-gutter-y: 3rem}.sd-g-xl-5,.sd-gx-xl-5{--sd-gutter-x: 3rem}}.sd-flex-row-reverse{flex-direction:row-reverse !important}details.sd-dropdown{position:relative}details.sd-dropdown .sd-summary-title{font-weight:700;padding-right:3em !important;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}details.sd-dropdown:hover{cursor:pointer}details.sd-dropdown .sd-summary-content{cursor:default}details.sd-dropdown summary{list-style:none;padding:1em}details.sd-dropdown summary .sd-octicon.no-title{vertical-align:middle}details.sd-dropdown[open] summary .sd-octicon.no-title{visibility:hidden}details.sd-dropdown summary::-webkit-details-marker{display:none}details.sd-dropdown summary:focus{outline:none}details.sd-dropdown .sd-summary-icon{margin-right:.5em}details.sd-dropdown .sd-summary-icon svg{opacity:.8}details.sd-dropdown summary:hover .sd-summary-up svg,details.sd-dropdown summary:hover .sd-summary-down svg{opacity:1;transform:scale(1.1)}details.sd-dropdown .sd-summary-up svg,details.sd-dropdown .sd-summary-down svg{display:block;opacity:.6}details.sd-dropdown .sd-summary-up,details.sd-dropdown .sd-summary-down{pointer-events:none;position:absolute;right:1em;top:1em}details.sd-dropdown[open]>.sd-summary-title .sd-summary-down{visibility:hidden}details.sd-dropdown:not([open])>.sd-summary-title .sd-summary-up{visibility:hidden}details.sd-dropdown:not([open]).sd-card{border:none}details.sd-dropdown:not([open])>.sd-card-header{border:1px solid var(--sd-color-card-border);border-radius:.25rem}details.sd-dropdown.sd-fade-in[open] summary~*{-moz-animation:sd-fade-in .5s ease-in-out;-webkit-animation:sd-fade-in .5s ease-in-out;animation:sd-fade-in .5s ease-in-out}details.sd-dropdown.sd-fade-in-slide-down[open] summary~*{-moz-animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out;-webkit-animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out;animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out}.sd-col>.sd-dropdown{width:100%}.sd-summary-content>.sd-tab-set:first-child{margin-top:0}@keyframes sd-fade-in{0%{opacity:0}100%{opacity:1}}@keyframes sd-slide-down{0%{transform:translate(0, -10px)}100%{transform:translate(0, 0)}}.sd-tab-set{border-radius:.125rem;display:flex;flex-wrap:wrap;margin:1em 0;position:relative}.sd-tab-set>input{opacity:0;position:absolute}.sd-tab-set>input:checked+label{border-color:var(--sd-color-tabs-underline-active);color:var(--sd-color-tabs-label-active)}.sd-tab-set>input:checked+label+.sd-tab-content{display:block}.sd-tab-set>input:not(:checked)+label:hover{color:var(--sd-color-tabs-label-hover);border-color:var(--sd-color-tabs-underline-hover)}.sd-tab-set>input:focus+label{outline-style:auto}.sd-tab-set>input:not(.focus-visible)+label{outline:none;-webkit-tap-highlight-color:transparent}.sd-tab-set>label{border-bottom:.125rem solid transparent;margin-bottom:0;color:var(--sd-color-tabs-label-inactive);border-color:var(--sd-color-tabs-underline-inactive);cursor:pointer;font-size:var(--sd-fontsize-tabs-label);font-weight:700;padding:1em 1.25em .5em;transition:color 250ms;width:auto;z-index:1}html .sd-tab-set>label:hover{color:var(--sd-color-tabs-label-active)}.sd-col>.sd-tab-set{width:100%}.sd-tab-content{box-shadow:0 -0.0625rem var(--sd-color-tabs-overline),0 .0625rem var(--sd-color-tabs-underline);display:none;order:99;padding-bottom:.75rem;padding-top:.75rem;width:100%}.sd-tab-content>:first-child{margin-top:0 !important}.sd-tab-content>:last-child{margin-bottom:0 !important}.sd-tab-content>.sd-tab-set{margin:0}.sd-sphinx-override,.sd-sphinx-override *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.sd-sphinx-override p{margin-top:0}:root{--sd-color-primary: #0071bc;--sd-color-secondary: #6c757d;--sd-color-success: #28a745;--sd-color-info: #17a2b8;--sd-color-warning: #f0b37e;--sd-color-danger: #dc3545;--sd-color-light: #f8f9fa;--sd-color-muted: #6c757d;--sd-color-dark: #212529;--sd-color-black: black;--sd-color-white: white;--sd-color-primary-highlight: #0060a0;--sd-color-secondary-highlight: #5c636a;--sd-color-success-highlight: #228e3b;--sd-color-info-highlight: #148a9c;--sd-color-warning-highlight: #cc986b;--sd-color-danger-highlight: #bb2d3b;--sd-color-light-highlight: #d3d4d5;--sd-color-muted-highlight: #5c636a;--sd-color-dark-highlight: #1c1f23;--sd-color-black-highlight: black;--sd-color-white-highlight: #d9d9d9;--sd-color-primary-text: #fff;--sd-color-secondary-text: #fff;--sd-color-success-text: #fff;--sd-color-info-text: #fff;--sd-color-warning-text: #212529;--sd-color-danger-text: #fff;--sd-color-light-text: #212529;--sd-color-muted-text: #fff;--sd-color-dark-text: #fff;--sd-color-black-text: #fff;--sd-color-white-text: #212529;--sd-color-shadow: rgba(0, 0, 0, 0.15);--sd-color-card-border: rgba(0, 0, 0, 0.125);--sd-color-card-border-hover: hsla(231, 99%, 66%, 1);--sd-color-card-background: transparent;--sd-color-card-text: inherit;--sd-color-card-header: transparent;--sd-color-card-footer: transparent;--sd-color-tabs-label-active: hsla(231, 99%, 66%, 1);--sd-color-tabs-label-hover: hsla(231, 99%, 66%, 1);--sd-color-tabs-label-inactive: hsl(0, 0%, 66%);--sd-color-tabs-underline-active: hsla(231, 99%, 66%, 1);--sd-color-tabs-underline-hover: rgba(178, 206, 245, 0.62);--sd-color-tabs-underline-inactive: transparent;--sd-color-tabs-overline: rgb(222, 222, 222);--sd-color-tabs-underline: rgb(222, 222, 222);--sd-fontsize-tabs-label: 1rem} diff --git a/_sphinx_design_static/design-tabs.js b/_sphinx_design_static/design-tabs.js new file mode 100644 index 0000000..36b38cf --- /dev/null +++ b/_sphinx_design_static/design-tabs.js @@ -0,0 +1,27 @@ +var sd_labels_by_text = {}; + +function ready() { + const li = document.getElementsByClassName("sd-tab-label"); + for (const label of li) { + syncId = label.getAttribute("data-sync-id"); + if (syncId) { + label.onclick = onLabelClick; + if (!sd_labels_by_text[syncId]) { + sd_labels_by_text[syncId] = []; + } + sd_labels_by_text[syncId].push(label); + } + } +} + +function onLabelClick() { + // Activate other inputs with the same sync id. + syncId = this.getAttribute("data-sync-id"); + for (label of sd_labels_by_text[syncId]) { + if (label === this) continue; + label.previousElementSibling.checked = true; + } + window.localStorage.setItem("sphinx-design-last-tab", syncId); +} + +document.addEventListener("DOMContentLoaded", ready, false); diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 0000000..7577acb --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,903 @@ +/* + * 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; +} + +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; +} + +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; +} + +/* -- 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/_static/debug.css b/_static/debug.css new file mode 100644 index 0000000..74d4aec --- /dev/null +++ b/_static/debug.css @@ -0,0 +1,69 @@ +/* + 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/_static/design-style.1e8bd061cd6da7fc9cf755528e8ffc24.min.css b/_static/design-style.1e8bd061cd6da7fc9cf755528e8ffc24.min.css new file mode 100644 index 0000000..eb19f69 --- /dev/null +++ b/_static/design-style.1e8bd061cd6da7fc9cf755528e8ffc24.min.css @@ -0,0 +1 @@ +.sd-bg-primary{background-color:var(--sd-color-primary) !important}.sd-bg-text-primary{color:var(--sd-color-primary-text) !important}button.sd-bg-primary:focus,button.sd-bg-primary:hover{background-color:var(--sd-color-primary-highlight) !important}a.sd-bg-primary:focus,a.sd-bg-primary:hover{background-color:var(--sd-color-primary-highlight) !important}.sd-bg-secondary{background-color:var(--sd-color-secondary) !important}.sd-bg-text-secondary{color:var(--sd-color-secondary-text) !important}button.sd-bg-secondary:focus,button.sd-bg-secondary:hover{background-color:var(--sd-color-secondary-highlight) !important}a.sd-bg-secondary:focus,a.sd-bg-secondary:hover{background-color:var(--sd-color-secondary-highlight) !important}.sd-bg-success{background-color:var(--sd-color-success) !important}.sd-bg-text-success{color:var(--sd-color-success-text) !important}button.sd-bg-success:focus,button.sd-bg-success:hover{background-color:var(--sd-color-success-highlight) !important}a.sd-bg-success:focus,a.sd-bg-success:hover{background-color:var(--sd-color-success-highlight) !important}.sd-bg-info{background-color:var(--sd-color-info) !important}.sd-bg-text-info{color:var(--sd-color-info-text) !important}button.sd-bg-info:focus,button.sd-bg-info:hover{background-color:var(--sd-color-info-highlight) !important}a.sd-bg-info:focus,a.sd-bg-info:hover{background-color:var(--sd-color-info-highlight) !important}.sd-bg-warning{background-color:var(--sd-color-warning) !important}.sd-bg-text-warning{color:var(--sd-color-warning-text) !important}button.sd-bg-warning:focus,button.sd-bg-warning:hover{background-color:var(--sd-color-warning-highlight) !important}a.sd-bg-warning:focus,a.sd-bg-warning:hover{background-color:var(--sd-color-warning-highlight) !important}.sd-bg-danger{background-color:var(--sd-color-danger) !important}.sd-bg-text-danger{color:var(--sd-color-danger-text) !important}button.sd-bg-danger:focus,button.sd-bg-danger:hover{background-color:var(--sd-color-danger-highlight) !important}a.sd-bg-danger:focus,a.sd-bg-danger:hover{background-color:var(--sd-color-danger-highlight) !important}.sd-bg-light{background-color:var(--sd-color-light) !important}.sd-bg-text-light{color:var(--sd-color-light-text) !important}button.sd-bg-light:focus,button.sd-bg-light:hover{background-color:var(--sd-color-light-highlight) !important}a.sd-bg-light:focus,a.sd-bg-light:hover{background-color:var(--sd-color-light-highlight) !important}.sd-bg-muted{background-color:var(--sd-color-muted) !important}.sd-bg-text-muted{color:var(--sd-color-muted-text) !important}button.sd-bg-muted:focus,button.sd-bg-muted:hover{background-color:var(--sd-color-muted-highlight) !important}a.sd-bg-muted:focus,a.sd-bg-muted:hover{background-color:var(--sd-color-muted-highlight) !important}.sd-bg-dark{background-color:var(--sd-color-dark) !important}.sd-bg-text-dark{color:var(--sd-color-dark-text) !important}button.sd-bg-dark:focus,button.sd-bg-dark:hover{background-color:var(--sd-color-dark-highlight) !important}a.sd-bg-dark:focus,a.sd-bg-dark:hover{background-color:var(--sd-color-dark-highlight) !important}.sd-bg-black{background-color:var(--sd-color-black) !important}.sd-bg-text-black{color:var(--sd-color-black-text) !important}button.sd-bg-black:focus,button.sd-bg-black:hover{background-color:var(--sd-color-black-highlight) !important}a.sd-bg-black:focus,a.sd-bg-black:hover{background-color:var(--sd-color-black-highlight) !important}.sd-bg-white{background-color:var(--sd-color-white) !important}.sd-bg-text-white{color:var(--sd-color-white-text) !important}button.sd-bg-white:focus,button.sd-bg-white:hover{background-color:var(--sd-color-white-highlight) !important}a.sd-bg-white:focus,a.sd-bg-white:hover{background-color:var(--sd-color-white-highlight) !important}.sd-text-primary,.sd-text-primary>p{color:var(--sd-color-primary) !important}a.sd-text-primary:focus,a.sd-text-primary:hover{color:var(--sd-color-primary-highlight) !important}.sd-text-secondary,.sd-text-secondary>p{color:var(--sd-color-secondary) !important}a.sd-text-secondary:focus,a.sd-text-secondary:hover{color:var(--sd-color-secondary-highlight) !important}.sd-text-success,.sd-text-success>p{color:var(--sd-color-success) !important}a.sd-text-success:focus,a.sd-text-success:hover{color:var(--sd-color-success-highlight) !important}.sd-text-info,.sd-text-info>p{color:var(--sd-color-info) !important}a.sd-text-info:focus,a.sd-text-info:hover{color:var(--sd-color-info-highlight) !important}.sd-text-warning,.sd-text-warning>p{color:var(--sd-color-warning) !important}a.sd-text-warning:focus,a.sd-text-warning:hover{color:var(--sd-color-warning-highlight) !important}.sd-text-danger,.sd-text-danger>p{color:var(--sd-color-danger) !important}a.sd-text-danger:focus,a.sd-text-danger:hover{color:var(--sd-color-danger-highlight) !important}.sd-text-light,.sd-text-light>p{color:var(--sd-color-light) !important}a.sd-text-light:focus,a.sd-text-light:hover{color:var(--sd-color-light-highlight) !important}.sd-text-muted,.sd-text-muted>p{color:var(--sd-color-muted) !important}a.sd-text-muted:focus,a.sd-text-muted:hover{color:var(--sd-color-muted-highlight) !important}.sd-text-dark,.sd-text-dark>p{color:var(--sd-color-dark) !important}a.sd-text-dark:focus,a.sd-text-dark:hover{color:var(--sd-color-dark-highlight) !important}.sd-text-black,.sd-text-black>p{color:var(--sd-color-black) !important}a.sd-text-black:focus,a.sd-text-black:hover{color:var(--sd-color-black-highlight) !important}.sd-text-white,.sd-text-white>p{color:var(--sd-color-white) !important}a.sd-text-white:focus,a.sd-text-white:hover{color:var(--sd-color-white-highlight) !important}.sd-outline-primary{border-color:var(--sd-color-primary) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-primary:focus,a.sd-outline-primary:hover{border-color:var(--sd-color-primary-highlight) !important}.sd-outline-secondary{border-color:var(--sd-color-secondary) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-secondary:focus,a.sd-outline-secondary:hover{border-color:var(--sd-color-secondary-highlight) !important}.sd-outline-success{border-color:var(--sd-color-success) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-success:focus,a.sd-outline-success:hover{border-color:var(--sd-color-success-highlight) !important}.sd-outline-info{border-color:var(--sd-color-info) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-info:focus,a.sd-outline-info:hover{border-color:var(--sd-color-info-highlight) !important}.sd-outline-warning{border-color:var(--sd-color-warning) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-warning:focus,a.sd-outline-warning:hover{border-color:var(--sd-color-warning-highlight) !important}.sd-outline-danger{border-color:var(--sd-color-danger) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-danger:focus,a.sd-outline-danger:hover{border-color:var(--sd-color-danger-highlight) !important}.sd-outline-light{border-color:var(--sd-color-light) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-light:focus,a.sd-outline-light:hover{border-color:var(--sd-color-light-highlight) !important}.sd-outline-muted{border-color:var(--sd-color-muted) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-muted:focus,a.sd-outline-muted:hover{border-color:var(--sd-color-muted-highlight) !important}.sd-outline-dark{border-color:var(--sd-color-dark) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-dark:focus,a.sd-outline-dark:hover{border-color:var(--sd-color-dark-highlight) !important}.sd-outline-black{border-color:var(--sd-color-black) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-black:focus,a.sd-outline-black:hover{border-color:var(--sd-color-black-highlight) !important}.sd-outline-white{border-color:var(--sd-color-white) !important;border-style:solid !important;border-width:1px !important}a.sd-outline-white:focus,a.sd-outline-white:hover{border-color:var(--sd-color-white-highlight) !important}.sd-bg-transparent{background-color:transparent !important}.sd-outline-transparent{border-color:transparent !important}.sd-text-transparent{color:transparent !important}.sd-p-0{padding:0 !important}.sd-pt-0,.sd-py-0{padding-top:0 !important}.sd-pr-0,.sd-px-0{padding-right:0 !important}.sd-pb-0,.sd-py-0{padding-bottom:0 !important}.sd-pl-0,.sd-px-0{padding-left:0 !important}.sd-p-1{padding:.25rem !important}.sd-pt-1,.sd-py-1{padding-top:.25rem !important}.sd-pr-1,.sd-px-1{padding-right:.25rem !important}.sd-pb-1,.sd-py-1{padding-bottom:.25rem !important}.sd-pl-1,.sd-px-1{padding-left:.25rem !important}.sd-p-2{padding:.5rem !important}.sd-pt-2,.sd-py-2{padding-top:.5rem !important}.sd-pr-2,.sd-px-2{padding-right:.5rem !important}.sd-pb-2,.sd-py-2{padding-bottom:.5rem !important}.sd-pl-2,.sd-px-2{padding-left:.5rem !important}.sd-p-3{padding:1rem !important}.sd-pt-3,.sd-py-3{padding-top:1rem !important}.sd-pr-3,.sd-px-3{padding-right:1rem !important}.sd-pb-3,.sd-py-3{padding-bottom:1rem !important}.sd-pl-3,.sd-px-3{padding-left:1rem !important}.sd-p-4{padding:1.5rem !important}.sd-pt-4,.sd-py-4{padding-top:1.5rem !important}.sd-pr-4,.sd-px-4{padding-right:1.5rem !important}.sd-pb-4,.sd-py-4{padding-bottom:1.5rem !important}.sd-pl-4,.sd-px-4{padding-left:1.5rem !important}.sd-p-5{padding:3rem !important}.sd-pt-5,.sd-py-5{padding-top:3rem !important}.sd-pr-5,.sd-px-5{padding-right:3rem !important}.sd-pb-5,.sd-py-5{padding-bottom:3rem !important}.sd-pl-5,.sd-px-5{padding-left:3rem !important}.sd-m-auto{margin:auto !important}.sd-mt-auto,.sd-my-auto{margin-top:auto !important}.sd-mr-auto,.sd-mx-auto{margin-right:auto !important}.sd-mb-auto,.sd-my-auto{margin-bottom:auto !important}.sd-ml-auto,.sd-mx-auto{margin-left:auto !important}.sd-m-0{margin:0 !important}.sd-mt-0,.sd-my-0{margin-top:0 !important}.sd-mr-0,.sd-mx-0{margin-right:0 !important}.sd-mb-0,.sd-my-0{margin-bottom:0 !important}.sd-ml-0,.sd-mx-0{margin-left:0 !important}.sd-m-1{margin:.25rem !important}.sd-mt-1,.sd-my-1{margin-top:.25rem !important}.sd-mr-1,.sd-mx-1{margin-right:.25rem !important}.sd-mb-1,.sd-my-1{margin-bottom:.25rem !important}.sd-ml-1,.sd-mx-1{margin-left:.25rem !important}.sd-m-2{margin:.5rem !important}.sd-mt-2,.sd-my-2{margin-top:.5rem !important}.sd-mr-2,.sd-mx-2{margin-right:.5rem !important}.sd-mb-2,.sd-my-2{margin-bottom:.5rem !important}.sd-ml-2,.sd-mx-2{margin-left:.5rem !important}.sd-m-3{margin:1rem !important}.sd-mt-3,.sd-my-3{margin-top:1rem !important}.sd-mr-3,.sd-mx-3{margin-right:1rem !important}.sd-mb-3,.sd-my-3{margin-bottom:1rem !important}.sd-ml-3,.sd-mx-3{margin-left:1rem !important}.sd-m-4{margin:1.5rem !important}.sd-mt-4,.sd-my-4{margin-top:1.5rem !important}.sd-mr-4,.sd-mx-4{margin-right:1.5rem !important}.sd-mb-4,.sd-my-4{margin-bottom:1.5rem !important}.sd-ml-4,.sd-mx-4{margin-left:1.5rem !important}.sd-m-5{margin:3rem !important}.sd-mt-5,.sd-my-5{margin-top:3rem !important}.sd-mr-5,.sd-mx-5{margin-right:3rem !important}.sd-mb-5,.sd-my-5{margin-bottom:3rem !important}.sd-ml-5,.sd-mx-5{margin-left:3rem !important}.sd-w-25{width:25% !important}.sd-w-50{width:50% !important}.sd-w-75{width:75% !important}.sd-w-100{width:100% !important}.sd-w-auto{width:auto !important}.sd-h-25{height:25% !important}.sd-h-50{height:50% !important}.sd-h-75{height:75% !important}.sd-h-100{height:100% !important}.sd-h-auto{height:auto !important}.sd-d-none{display:none !important}.sd-d-inline{display:inline !important}.sd-d-inline-block{display:inline-block !important}.sd-d-block{display:block !important}.sd-d-grid{display:grid !important}.sd-d-flex-row{display:-ms-flexbox !important;display:flex !important;flex-direction:row !important}.sd-d-flex-column{display:-ms-flexbox !important;display:flex !important;flex-direction:column !important}.sd-d-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}@media(min-width: 576px){.sd-d-sm-none{display:none !important}.sd-d-sm-inline{display:inline !important}.sd-d-sm-inline-block{display:inline-block !important}.sd-d-sm-block{display:block !important}.sd-d-sm-grid{display:grid !important}.sd-d-sm-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-sm-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 768px){.sd-d-md-none{display:none !important}.sd-d-md-inline{display:inline !important}.sd-d-md-inline-block{display:inline-block !important}.sd-d-md-block{display:block !important}.sd-d-md-grid{display:grid !important}.sd-d-md-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-md-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 992px){.sd-d-lg-none{display:none !important}.sd-d-lg-inline{display:inline !important}.sd-d-lg-inline-block{display:inline-block !important}.sd-d-lg-block{display:block !important}.sd-d-lg-grid{display:grid !important}.sd-d-lg-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-lg-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}@media(min-width: 1200px){.sd-d-xl-none{display:none !important}.sd-d-xl-inline{display:inline !important}.sd-d-xl-inline-block{display:inline-block !important}.sd-d-xl-block{display:block !important}.sd-d-xl-grid{display:grid !important}.sd-d-xl-flex{display:-ms-flexbox !important;display:flex !important}.sd-d-xl-inline-flex{display:-ms-inline-flexbox !important;display:inline-flex !important}}.sd-align-major-start{justify-content:flex-start !important}.sd-align-major-end{justify-content:flex-end !important}.sd-align-major-center{justify-content:center !important}.sd-align-major-justify{justify-content:space-between !important}.sd-align-major-spaced{justify-content:space-evenly !important}.sd-align-minor-start{align-items:flex-start !important}.sd-align-minor-end{align-items:flex-end !important}.sd-align-minor-center{align-items:center !important}.sd-align-minor-stretch{align-items:stretch !important}.sd-text-justify{text-align:justify !important}.sd-text-left{text-align:left !important}.sd-text-right{text-align:right !important}.sd-text-center{text-align:center !important}.sd-font-weight-light{font-weight:300 !important}.sd-font-weight-lighter{font-weight:lighter !important}.sd-font-weight-normal{font-weight:400 !important}.sd-font-weight-bold{font-weight:700 !important}.sd-font-weight-bolder{font-weight:bolder !important}.sd-font-italic{font-style:italic !important}.sd-text-decoration-none{text-decoration:none !important}.sd-text-lowercase{text-transform:lowercase !important}.sd-text-uppercase{text-transform:uppercase !important}.sd-text-capitalize{text-transform:capitalize !important}.sd-text-wrap{white-space:normal !important}.sd-text-nowrap{white-space:nowrap !important}.sd-text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-fs-1,.sd-fs-1>p{font-size:calc(1.375rem + 1.5vw) !important;line-height:unset !important}.sd-fs-2,.sd-fs-2>p{font-size:calc(1.325rem + 0.9vw) !important;line-height:unset !important}.sd-fs-3,.sd-fs-3>p{font-size:calc(1.3rem + 0.6vw) !important;line-height:unset !important}.sd-fs-4,.sd-fs-4>p{font-size:calc(1.275rem + 0.3vw) !important;line-height:unset !important}.sd-fs-5,.sd-fs-5>p{font-size:1.25rem !important;line-height:unset !important}.sd-fs-6,.sd-fs-6>p{font-size:1rem !important;line-height:unset !important}.sd-border-0{border:0 solid !important}.sd-border-top-0{border-top:0 solid !important}.sd-border-bottom-0{border-bottom:0 solid !important}.sd-border-right-0{border-right:0 solid !important}.sd-border-left-0{border-left:0 solid !important}.sd-border-1{border:1px solid !important}.sd-border-top-1{border-top:1px solid !important}.sd-border-bottom-1{border-bottom:1px solid !important}.sd-border-right-1{border-right:1px solid !important}.sd-border-left-1{border-left:1px solid !important}.sd-border-2{border:2px solid !important}.sd-border-top-2{border-top:2px solid !important}.sd-border-bottom-2{border-bottom:2px solid !important}.sd-border-right-2{border-right:2px solid !important}.sd-border-left-2{border-left:2px solid !important}.sd-border-3{border:3px solid !important}.sd-border-top-3{border-top:3px solid !important}.sd-border-bottom-3{border-bottom:3px solid !important}.sd-border-right-3{border-right:3px solid !important}.sd-border-left-3{border-left:3px solid !important}.sd-border-4{border:4px solid !important}.sd-border-top-4{border-top:4px solid !important}.sd-border-bottom-4{border-bottom:4px solid !important}.sd-border-right-4{border-right:4px solid !important}.sd-border-left-4{border-left:4px solid !important}.sd-border-5{border:5px solid !important}.sd-border-top-5{border-top:5px solid !important}.sd-border-bottom-5{border-bottom:5px solid !important}.sd-border-right-5{border-right:5px solid !important}.sd-border-left-5{border-left:5px solid !important}.sd-rounded-0{border-radius:0 !important}.sd-rounded-1{border-radius:.2rem !important}.sd-rounded-2{border-radius:.3rem !important}.sd-rounded-3{border-radius:.5rem !important}.sd-rounded-pill{border-radius:50rem !important}.sd-rounded-circle{border-radius:50% !important}.shadow-none{box-shadow:none !important}.sd-shadow-sm{box-shadow:0 .125rem .25rem var(--sd-color-shadow) !important}.sd-shadow-md{box-shadow:0 .5rem 1rem var(--sd-color-shadow) !important}.sd-shadow-lg{box-shadow:0 1rem 3rem var(--sd-color-shadow) !important}@keyframes sd-slide-from-left{0%{transform:translateX(-100%)}100%{transform:translateX(0)}}@keyframes sd-slide-from-right{0%{transform:translateX(200%)}100%{transform:translateX(0)}}@keyframes sd-grow100{0%{transform:scale(0);opacity:.5}100%{transform:scale(1);opacity:1}}@keyframes sd-grow50{0%{transform:scale(0.5);opacity:.5}100%{transform:scale(1);opacity:1}}@keyframes sd-grow50-rot20{0%{transform:scale(0.5) rotateZ(-20deg);opacity:.5}75%{transform:scale(1) rotateZ(5deg);opacity:1}95%{transform:scale(1) rotateZ(-1deg);opacity:1}100%{transform:scale(1) rotateZ(0);opacity:1}}.sd-animate-slide-from-left{animation:1s ease-out 0s 1 normal none running sd-slide-from-left}.sd-animate-slide-from-right{animation:1s ease-out 0s 1 normal none running sd-slide-from-right}.sd-animate-grow100{animation:1s ease-out 0s 1 normal none running sd-grow100}.sd-animate-grow50{animation:1s ease-out 0s 1 normal none running sd-grow50}.sd-animate-grow50-rot20{animation:1s ease-out 0s 1 normal none running sd-grow50-rot20}.sd-badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.sd-badge:empty{display:none}a.sd-badge{text-decoration:none}.sd-btn .sd-badge{position:relative;top:-1px}.sd-btn{background-color:transparent;border:1px solid transparent;border-radius:.25rem;cursor:pointer;display:inline-block;font-weight:400;font-size:1rem;line-height:1.5;padding:.375rem .75rem;text-align:center;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;vertical-align:middle;user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none}.sd-btn:hover{text-decoration:none}@media(prefers-reduced-motion: reduce){.sd-btn{transition:none}}.sd-btn-primary,.sd-btn-outline-primary:hover,.sd-btn-outline-primary:focus{color:var(--sd-color-primary-text) !important;background-color:var(--sd-color-primary) !important;border-color:var(--sd-color-primary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-primary:hover,.sd-btn-primary:focus{color:var(--sd-color-primary-text) !important;background-color:var(--sd-color-primary-highlight) !important;border-color:var(--sd-color-primary-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-primary{color:var(--sd-color-primary) !important;border-color:var(--sd-color-primary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-secondary,.sd-btn-outline-secondary:hover,.sd-btn-outline-secondary:focus{color:var(--sd-color-secondary-text) !important;background-color:var(--sd-color-secondary) !important;border-color:var(--sd-color-secondary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-secondary:hover,.sd-btn-secondary:focus{color:var(--sd-color-secondary-text) !important;background-color:var(--sd-color-secondary-highlight) !important;border-color:var(--sd-color-secondary-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-secondary{color:var(--sd-color-secondary) !important;border-color:var(--sd-color-secondary) !important;border-width:1px !important;border-style:solid !important}.sd-btn-success,.sd-btn-outline-success:hover,.sd-btn-outline-success:focus{color:var(--sd-color-success-text) !important;background-color:var(--sd-color-success) !important;border-color:var(--sd-color-success) !important;border-width:1px !important;border-style:solid !important}.sd-btn-success:hover,.sd-btn-success:focus{color:var(--sd-color-success-text) !important;background-color:var(--sd-color-success-highlight) !important;border-color:var(--sd-color-success-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-success{color:var(--sd-color-success) !important;border-color:var(--sd-color-success) !important;border-width:1px !important;border-style:solid !important}.sd-btn-info,.sd-btn-outline-info:hover,.sd-btn-outline-info:focus{color:var(--sd-color-info-text) !important;background-color:var(--sd-color-info) !important;border-color:var(--sd-color-info) !important;border-width:1px !important;border-style:solid !important}.sd-btn-info:hover,.sd-btn-info:focus{color:var(--sd-color-info-text) !important;background-color:var(--sd-color-info-highlight) !important;border-color:var(--sd-color-info-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-info{color:var(--sd-color-info) !important;border-color:var(--sd-color-info) !important;border-width:1px !important;border-style:solid !important}.sd-btn-warning,.sd-btn-outline-warning:hover,.sd-btn-outline-warning:focus{color:var(--sd-color-warning-text) !important;background-color:var(--sd-color-warning) !important;border-color:var(--sd-color-warning) !important;border-width:1px !important;border-style:solid !important}.sd-btn-warning:hover,.sd-btn-warning:focus{color:var(--sd-color-warning-text) !important;background-color:var(--sd-color-warning-highlight) !important;border-color:var(--sd-color-warning-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-warning{color:var(--sd-color-warning) !important;border-color:var(--sd-color-warning) !important;border-width:1px !important;border-style:solid !important}.sd-btn-danger,.sd-btn-outline-danger:hover,.sd-btn-outline-danger:focus{color:var(--sd-color-danger-text) !important;background-color:var(--sd-color-danger) !important;border-color:var(--sd-color-danger) !important;border-width:1px !important;border-style:solid !important}.sd-btn-danger:hover,.sd-btn-danger:focus{color:var(--sd-color-danger-text) !important;background-color:var(--sd-color-danger-highlight) !important;border-color:var(--sd-color-danger-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-danger{color:var(--sd-color-danger) !important;border-color:var(--sd-color-danger) !important;border-width:1px !important;border-style:solid !important}.sd-btn-light,.sd-btn-outline-light:hover,.sd-btn-outline-light:focus{color:var(--sd-color-light-text) !important;background-color:var(--sd-color-light) !important;border-color:var(--sd-color-light) !important;border-width:1px !important;border-style:solid !important}.sd-btn-light:hover,.sd-btn-light:focus{color:var(--sd-color-light-text) !important;background-color:var(--sd-color-light-highlight) !important;border-color:var(--sd-color-light-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-light{color:var(--sd-color-light) !important;border-color:var(--sd-color-light) !important;border-width:1px !important;border-style:solid !important}.sd-btn-muted,.sd-btn-outline-muted:hover,.sd-btn-outline-muted:focus{color:var(--sd-color-muted-text) !important;background-color:var(--sd-color-muted) !important;border-color:var(--sd-color-muted) !important;border-width:1px !important;border-style:solid !important}.sd-btn-muted:hover,.sd-btn-muted:focus{color:var(--sd-color-muted-text) !important;background-color:var(--sd-color-muted-highlight) !important;border-color:var(--sd-color-muted-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-muted{color:var(--sd-color-muted) !important;border-color:var(--sd-color-muted) !important;border-width:1px !important;border-style:solid !important}.sd-btn-dark,.sd-btn-outline-dark:hover,.sd-btn-outline-dark:focus{color:var(--sd-color-dark-text) !important;background-color:var(--sd-color-dark) !important;border-color:var(--sd-color-dark) !important;border-width:1px !important;border-style:solid !important}.sd-btn-dark:hover,.sd-btn-dark:focus{color:var(--sd-color-dark-text) !important;background-color:var(--sd-color-dark-highlight) !important;border-color:var(--sd-color-dark-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-dark{color:var(--sd-color-dark) !important;border-color:var(--sd-color-dark) !important;border-width:1px !important;border-style:solid !important}.sd-btn-black,.sd-btn-outline-black:hover,.sd-btn-outline-black:focus{color:var(--sd-color-black-text) !important;background-color:var(--sd-color-black) !important;border-color:var(--sd-color-black) !important;border-width:1px !important;border-style:solid !important}.sd-btn-black:hover,.sd-btn-black:focus{color:var(--sd-color-black-text) !important;background-color:var(--sd-color-black-highlight) !important;border-color:var(--sd-color-black-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-black{color:var(--sd-color-black) !important;border-color:var(--sd-color-black) !important;border-width:1px !important;border-style:solid !important}.sd-btn-white,.sd-btn-outline-white:hover,.sd-btn-outline-white:focus{color:var(--sd-color-white-text) !important;background-color:var(--sd-color-white) !important;border-color:var(--sd-color-white) !important;border-width:1px !important;border-style:solid !important}.sd-btn-white:hover,.sd-btn-white:focus{color:var(--sd-color-white-text) !important;background-color:var(--sd-color-white-highlight) !important;border-color:var(--sd-color-white-highlight) !important;border-width:1px !important;border-style:solid !important}.sd-btn-outline-white{color:var(--sd-color-white) !important;border-color:var(--sd-color-white) !important;border-width:1px !important;border-style:solid !important}.sd-stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.sd-hide-link-text{font-size:0}.sd-octicon,.sd-material-icon{display:inline-block;fill:currentColor;vertical-align:middle}.sd-avatar-xs{border-radius:50%;object-fit:cover;object-position:center;width:1rem;height:1rem}.sd-avatar-sm{border-radius:50%;object-fit:cover;object-position:center;width:3rem;height:3rem}.sd-avatar-md{border-radius:50%;object-fit:cover;object-position:center;width:5rem;height:5rem}.sd-avatar-lg{border-radius:50%;object-fit:cover;object-position:center;width:7rem;height:7rem}.sd-avatar-xl{border-radius:50%;object-fit:cover;object-position:center;width:10rem;height:10rem}.sd-avatar-inherit{border-radius:50%;object-fit:cover;object-position:center;width:inherit;height:inherit}.sd-avatar-initial{border-radius:50%;object-fit:cover;object-position:center;width:initial;height:initial}.sd-card{background-clip:border-box;background-color:var(--sd-color-card-background);border:1px solid var(--sd-color-card-border);border-radius:.25rem;color:var(--sd-color-card-text);display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;position:relative;word-wrap:break-word}.sd-card>hr{margin-left:0;margin-right:0}.sd-card-hover:hover{border-color:var(--sd-color-card-border-hover);transform:scale(1.01)}.sd-card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem 1rem}.sd-card-title{margin-bottom:.5rem}.sd-card-subtitle{margin-top:-0.25rem;margin-bottom:0}.sd-card-text:last-child{margin-bottom:0}.sd-card-link:hover{text-decoration:none}.sd-card-link+.card-link{margin-left:1rem}.sd-card-header{padding:.5rem 1rem;margin-bottom:0;background-color:var(--sd-color-card-header);border-bottom:1px solid var(--sd-color-card-border)}.sd-card-header:first-child{border-radius:calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0}.sd-card-footer{padding:.5rem 1rem;background-color:var(--sd-color-card-footer);border-top:1px solid var(--sd-color-card-border)}.sd-card-footer:last-child{border-radius:0 0 calc(0.25rem - 1px) calc(0.25rem - 1px)}.sd-card-header-tabs{margin-right:-0.5rem;margin-bottom:-0.5rem;margin-left:-0.5rem;border-bottom:0}.sd-card-header-pills{margin-right:-0.5rem;margin-left:-0.5rem}.sd-card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem;border-radius:calc(0.25rem - 1px)}.sd-card-img,.sd-card-img-bottom,.sd-card-img-top{width:100%}.sd-card-img,.sd-card-img-top{border-top-left-radius:calc(0.25rem - 1px);border-top-right-radius:calc(0.25rem - 1px)}.sd-card-img,.sd-card-img-bottom{border-bottom-left-radius:calc(0.25rem - 1px);border-bottom-right-radius:calc(0.25rem - 1px)}.sd-cards-carousel{width:100%;display:flex;flex-wrap:nowrap;-ms-flex-direction:row;flex-direction:row;overflow-x:hidden;scroll-snap-type:x mandatory}.sd-cards-carousel.sd-show-scrollbar{overflow-x:auto}.sd-cards-carousel:hover,.sd-cards-carousel:focus{overflow-x:auto}.sd-cards-carousel>.sd-card{flex-shrink:0;scroll-snap-align:start}.sd-cards-carousel>.sd-card:not(:last-child){margin-right:3px}.sd-card-cols-1>.sd-card{width:90%}.sd-card-cols-2>.sd-card{width:45%}.sd-card-cols-3>.sd-card{width:30%}.sd-card-cols-4>.sd-card{width:22.5%}.sd-card-cols-5>.sd-card{width:18%}.sd-card-cols-6>.sd-card{width:15%}.sd-card-cols-7>.sd-card{width:12.8571428571%}.sd-card-cols-8>.sd-card{width:11.25%}.sd-card-cols-9>.sd-card{width:10%}.sd-card-cols-10>.sd-card{width:9%}.sd-card-cols-11>.sd-card{width:8.1818181818%}.sd-card-cols-12>.sd-card{width:7.5%}.sd-container,.sd-container-fluid,.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container-xl{margin-left:auto;margin-right:auto;padding-left:var(--sd-gutter-x, 0.75rem);padding-right:var(--sd-gutter-x, 0.75rem);width:100%}@media(min-width: 576px){.sd-container-sm,.sd-container{max-width:540px}}@media(min-width: 768px){.sd-container-md,.sd-container-sm,.sd-container{max-width:720px}}@media(min-width: 992px){.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container{max-width:960px}}@media(min-width: 1200px){.sd-container-xl,.sd-container-lg,.sd-container-md,.sd-container-sm,.sd-container{max-width:1140px}}.sd-row{--sd-gutter-x: 1.5rem;--sd-gutter-y: 0;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-top:calc(var(--sd-gutter-y) * -1);margin-right:calc(var(--sd-gutter-x) * -0.5);margin-left:calc(var(--sd-gutter-x) * -0.5)}.sd-row>*{box-sizing:border-box;flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--sd-gutter-x) * 0.5);padding-left:calc(var(--sd-gutter-x) * 0.5);margin-top:var(--sd-gutter-y)}.sd-col{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-auto>*{flex:0 0 auto;width:auto}.sd-row-cols-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}@media(min-width: 576px){.sd-col-sm{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-sm-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-sm-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-sm-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-sm-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-sm-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-sm-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-sm-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-sm-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-sm-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-sm-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-sm-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-sm-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-sm-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 768px){.sd-col-md{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-md-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-md-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-md-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-md-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-md-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-md-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-md-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-md-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-md-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-md-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-md-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-md-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-md-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 992px){.sd-col-lg{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-lg-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-lg-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-lg-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-lg-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-lg-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-lg-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-lg-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-lg-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-lg-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-lg-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-lg-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-lg-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-lg-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}@media(min-width: 1200px){.sd-col-xl{flex:1 0 0%;-ms-flex:1 0 0%}.sd-row-cols-xl-auto{flex:1 0 auto;-ms-flex:1 0 auto;width:100%}.sd-row-cols-xl-1>*{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-row-cols-xl-2>*{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-row-cols-xl-3>*{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-row-cols-xl-4>*{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-row-cols-xl-5>*{flex:0 0 auto;-ms-flex:0 0 auto;width:20%}.sd-row-cols-xl-6>*{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-row-cols-xl-7>*{flex:0 0 auto;-ms-flex:0 0 auto;width:14.2857142857%}.sd-row-cols-xl-8>*{flex:0 0 auto;-ms-flex:0 0 auto;width:12.5%}.sd-row-cols-xl-9>*{flex:0 0 auto;-ms-flex:0 0 auto;width:11.1111111111%}.sd-row-cols-xl-10>*{flex:0 0 auto;-ms-flex:0 0 auto;width:10%}.sd-row-cols-xl-11>*{flex:0 0 auto;-ms-flex:0 0 auto;width:9.0909090909%}.sd-row-cols-xl-12>*{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}}.sd-col-auto{flex:0 0 auto;-ms-flex:0 0 auto;width:auto}.sd-col-1{flex:0 0 auto;-ms-flex:0 0 auto;width:8.3333333333%}.sd-col-2{flex:0 0 auto;-ms-flex:0 0 auto;width:16.6666666667%}.sd-col-3{flex:0 0 auto;-ms-flex:0 0 auto;width:25%}.sd-col-4{flex:0 0 auto;-ms-flex:0 0 auto;width:33.3333333333%}.sd-col-5{flex:0 0 auto;-ms-flex:0 0 auto;width:41.6666666667%}.sd-col-6{flex:0 0 auto;-ms-flex:0 0 auto;width:50%}.sd-col-7{flex:0 0 auto;-ms-flex:0 0 auto;width:58.3333333333%}.sd-col-8{flex:0 0 auto;-ms-flex:0 0 auto;width:66.6666666667%}.sd-col-9{flex:0 0 auto;-ms-flex:0 0 auto;width:75%}.sd-col-10{flex:0 0 auto;-ms-flex:0 0 auto;width:83.3333333333%}.sd-col-11{flex:0 0 auto;-ms-flex:0 0 auto;width:91.6666666667%}.sd-col-12{flex:0 0 auto;-ms-flex:0 0 auto;width:100%}.sd-g-0,.sd-gy-0{--sd-gutter-y: 0}.sd-g-0,.sd-gx-0{--sd-gutter-x: 0}.sd-g-1,.sd-gy-1{--sd-gutter-y: 0.25rem}.sd-g-1,.sd-gx-1{--sd-gutter-x: 0.25rem}.sd-g-2,.sd-gy-2{--sd-gutter-y: 0.5rem}.sd-g-2,.sd-gx-2{--sd-gutter-x: 0.5rem}.sd-g-3,.sd-gy-3{--sd-gutter-y: 1rem}.sd-g-3,.sd-gx-3{--sd-gutter-x: 1rem}.sd-g-4,.sd-gy-4{--sd-gutter-y: 1.5rem}.sd-g-4,.sd-gx-4{--sd-gutter-x: 1.5rem}.sd-g-5,.sd-gy-5{--sd-gutter-y: 3rem}.sd-g-5,.sd-gx-5{--sd-gutter-x: 3rem}@media(min-width: 576px){.sd-col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-sm-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-sm-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-sm-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-sm-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-sm-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-sm-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-sm-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-sm-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-sm-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-sm-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-sm-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-sm-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-sm-0,.sd-gy-sm-0{--sd-gutter-y: 0}.sd-g-sm-0,.sd-gx-sm-0{--sd-gutter-x: 0}.sd-g-sm-1,.sd-gy-sm-1{--sd-gutter-y: 0.25rem}.sd-g-sm-1,.sd-gx-sm-1{--sd-gutter-x: 0.25rem}.sd-g-sm-2,.sd-gy-sm-2{--sd-gutter-y: 0.5rem}.sd-g-sm-2,.sd-gx-sm-2{--sd-gutter-x: 0.5rem}.sd-g-sm-3,.sd-gy-sm-3{--sd-gutter-y: 1rem}.sd-g-sm-3,.sd-gx-sm-3{--sd-gutter-x: 1rem}.sd-g-sm-4,.sd-gy-sm-4{--sd-gutter-y: 1.5rem}.sd-g-sm-4,.sd-gx-sm-4{--sd-gutter-x: 1.5rem}.sd-g-sm-5,.sd-gy-sm-5{--sd-gutter-y: 3rem}.sd-g-sm-5,.sd-gx-sm-5{--sd-gutter-x: 3rem}}@media(min-width: 768px){.sd-col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-md-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-md-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-md-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-md-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-md-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-md-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-md-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-md-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-md-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-md-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-md-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-md-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-md-0,.sd-gy-md-0{--sd-gutter-y: 0}.sd-g-md-0,.sd-gx-md-0{--sd-gutter-x: 0}.sd-g-md-1,.sd-gy-md-1{--sd-gutter-y: 0.25rem}.sd-g-md-1,.sd-gx-md-1{--sd-gutter-x: 0.25rem}.sd-g-md-2,.sd-gy-md-2{--sd-gutter-y: 0.5rem}.sd-g-md-2,.sd-gx-md-2{--sd-gutter-x: 0.5rem}.sd-g-md-3,.sd-gy-md-3{--sd-gutter-y: 1rem}.sd-g-md-3,.sd-gx-md-3{--sd-gutter-x: 1rem}.sd-g-md-4,.sd-gy-md-4{--sd-gutter-y: 1.5rem}.sd-g-md-4,.sd-gx-md-4{--sd-gutter-x: 1.5rem}.sd-g-md-5,.sd-gy-md-5{--sd-gutter-y: 3rem}.sd-g-md-5,.sd-gx-md-5{--sd-gutter-x: 3rem}}@media(min-width: 992px){.sd-col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-lg-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-lg-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-lg-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-lg-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-lg-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-lg-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-lg-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-lg-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-lg-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-lg-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-lg-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-lg-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-lg-0,.sd-gy-lg-0{--sd-gutter-y: 0}.sd-g-lg-0,.sd-gx-lg-0{--sd-gutter-x: 0}.sd-g-lg-1,.sd-gy-lg-1{--sd-gutter-y: 0.25rem}.sd-g-lg-1,.sd-gx-lg-1{--sd-gutter-x: 0.25rem}.sd-g-lg-2,.sd-gy-lg-2{--sd-gutter-y: 0.5rem}.sd-g-lg-2,.sd-gx-lg-2{--sd-gutter-x: 0.5rem}.sd-g-lg-3,.sd-gy-lg-3{--sd-gutter-y: 1rem}.sd-g-lg-3,.sd-gx-lg-3{--sd-gutter-x: 1rem}.sd-g-lg-4,.sd-gy-lg-4{--sd-gutter-y: 1.5rem}.sd-g-lg-4,.sd-gx-lg-4{--sd-gutter-x: 1.5rem}.sd-g-lg-5,.sd-gy-lg-5{--sd-gutter-y: 3rem}.sd-g-lg-5,.sd-gx-lg-5{--sd-gutter-x: 3rem}}@media(min-width: 1200px){.sd-col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.sd-col-xl-1{-ms-flex:0 0 auto;flex:0 0 auto;width:8.3333333333%}.sd-col-xl-2{-ms-flex:0 0 auto;flex:0 0 auto;width:16.6666666667%}.sd-col-xl-3{-ms-flex:0 0 auto;flex:0 0 auto;width:25%}.sd-col-xl-4{-ms-flex:0 0 auto;flex:0 0 auto;width:33.3333333333%}.sd-col-xl-5{-ms-flex:0 0 auto;flex:0 0 auto;width:41.6666666667%}.sd-col-xl-6{-ms-flex:0 0 auto;flex:0 0 auto;width:50%}.sd-col-xl-7{-ms-flex:0 0 auto;flex:0 0 auto;width:58.3333333333%}.sd-col-xl-8{-ms-flex:0 0 auto;flex:0 0 auto;width:66.6666666667%}.sd-col-xl-9{-ms-flex:0 0 auto;flex:0 0 auto;width:75%}.sd-col-xl-10{-ms-flex:0 0 auto;flex:0 0 auto;width:83.3333333333%}.sd-col-xl-11{-ms-flex:0 0 auto;flex:0 0 auto;width:91.6666666667%}.sd-col-xl-12{-ms-flex:0 0 auto;flex:0 0 auto;width:100%}.sd-g-xl-0,.sd-gy-xl-0{--sd-gutter-y: 0}.sd-g-xl-0,.sd-gx-xl-0{--sd-gutter-x: 0}.sd-g-xl-1,.sd-gy-xl-1{--sd-gutter-y: 0.25rem}.sd-g-xl-1,.sd-gx-xl-1{--sd-gutter-x: 0.25rem}.sd-g-xl-2,.sd-gy-xl-2{--sd-gutter-y: 0.5rem}.sd-g-xl-2,.sd-gx-xl-2{--sd-gutter-x: 0.5rem}.sd-g-xl-3,.sd-gy-xl-3{--sd-gutter-y: 1rem}.sd-g-xl-3,.sd-gx-xl-3{--sd-gutter-x: 1rem}.sd-g-xl-4,.sd-gy-xl-4{--sd-gutter-y: 1.5rem}.sd-g-xl-4,.sd-gx-xl-4{--sd-gutter-x: 1.5rem}.sd-g-xl-5,.sd-gy-xl-5{--sd-gutter-y: 3rem}.sd-g-xl-5,.sd-gx-xl-5{--sd-gutter-x: 3rem}}.sd-flex-row-reverse{flex-direction:row-reverse !important}details.sd-dropdown{position:relative}details.sd-dropdown .sd-summary-title{font-weight:700;padding-right:3em !important;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}details.sd-dropdown:hover{cursor:pointer}details.sd-dropdown .sd-summary-content{cursor:default}details.sd-dropdown summary{list-style:none;padding:1em}details.sd-dropdown summary .sd-octicon.no-title{vertical-align:middle}details.sd-dropdown[open] summary .sd-octicon.no-title{visibility:hidden}details.sd-dropdown summary::-webkit-details-marker{display:none}details.sd-dropdown summary:focus{outline:none}details.sd-dropdown .sd-summary-icon{margin-right:.5em}details.sd-dropdown .sd-summary-icon svg{opacity:.8}details.sd-dropdown summary:hover .sd-summary-up svg,details.sd-dropdown summary:hover .sd-summary-down svg{opacity:1;transform:scale(1.1)}details.sd-dropdown .sd-summary-up svg,details.sd-dropdown .sd-summary-down svg{display:block;opacity:.6}details.sd-dropdown .sd-summary-up,details.sd-dropdown .sd-summary-down{pointer-events:none;position:absolute;right:1em;top:1em}details.sd-dropdown[open]>.sd-summary-title .sd-summary-down{visibility:hidden}details.sd-dropdown:not([open])>.sd-summary-title .sd-summary-up{visibility:hidden}details.sd-dropdown:not([open]).sd-card{border:none}details.sd-dropdown:not([open])>.sd-card-header{border:1px solid var(--sd-color-card-border);border-radius:.25rem}details.sd-dropdown.sd-fade-in[open] summary~*{-moz-animation:sd-fade-in .5s ease-in-out;-webkit-animation:sd-fade-in .5s ease-in-out;animation:sd-fade-in .5s ease-in-out}details.sd-dropdown.sd-fade-in-slide-down[open] summary~*{-moz-animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out;-webkit-animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out;animation:sd-fade-in .5s ease-in-out,sd-slide-down .5s ease-in-out}.sd-col>.sd-dropdown{width:100%}.sd-summary-content>.sd-tab-set:first-child{margin-top:0}@keyframes sd-fade-in{0%{opacity:0}100%{opacity:1}}@keyframes sd-slide-down{0%{transform:translate(0, -10px)}100%{transform:translate(0, 0)}}.sd-tab-set{border-radius:.125rem;display:flex;flex-wrap:wrap;margin:1em 0;position:relative}.sd-tab-set>input{opacity:0;position:absolute}.sd-tab-set>input:checked+label{border-color:var(--sd-color-tabs-underline-active);color:var(--sd-color-tabs-label-active)}.sd-tab-set>input:checked+label+.sd-tab-content{display:block}.sd-tab-set>input:not(:checked)+label:hover{color:var(--sd-color-tabs-label-hover);border-color:var(--sd-color-tabs-underline-hover)}.sd-tab-set>input:focus+label{outline-style:auto}.sd-tab-set>input:not(.focus-visible)+label{outline:none;-webkit-tap-highlight-color:transparent}.sd-tab-set>label{border-bottom:.125rem solid transparent;margin-bottom:0;color:var(--sd-color-tabs-label-inactive);border-color:var(--sd-color-tabs-underline-inactive);cursor:pointer;font-size:var(--sd-fontsize-tabs-label);font-weight:700;padding:1em 1.25em .5em;transition:color 250ms;width:auto;z-index:1}html .sd-tab-set>label:hover{color:var(--sd-color-tabs-label-active)}.sd-col>.sd-tab-set{width:100%}.sd-tab-content{box-shadow:0 -0.0625rem var(--sd-color-tabs-overline),0 .0625rem var(--sd-color-tabs-underline);display:none;order:99;padding-bottom:.75rem;padding-top:.75rem;width:100%}.sd-tab-content>:first-child{margin-top:0 !important}.sd-tab-content>:last-child{margin-bottom:0 !important}.sd-tab-content>.sd-tab-set{margin:0}.sd-sphinx-override,.sd-sphinx-override *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.sd-sphinx-override p{margin-top:0}:root{--sd-color-primary: #0071bc;--sd-color-secondary: #6c757d;--sd-color-success: #28a745;--sd-color-info: #17a2b8;--sd-color-warning: #f0b37e;--sd-color-danger: #dc3545;--sd-color-light: #f8f9fa;--sd-color-muted: #6c757d;--sd-color-dark: #212529;--sd-color-black: black;--sd-color-white: white;--sd-color-primary-highlight: #0060a0;--sd-color-secondary-highlight: #5c636a;--sd-color-success-highlight: #228e3b;--sd-color-info-highlight: #148a9c;--sd-color-warning-highlight: #cc986b;--sd-color-danger-highlight: #bb2d3b;--sd-color-light-highlight: #d3d4d5;--sd-color-muted-highlight: #5c636a;--sd-color-dark-highlight: #1c1f23;--sd-color-black-highlight: black;--sd-color-white-highlight: #d9d9d9;--sd-color-primary-text: #fff;--sd-color-secondary-text: #fff;--sd-color-success-text: #fff;--sd-color-info-text: #fff;--sd-color-warning-text: #212529;--sd-color-danger-text: #fff;--sd-color-light-text: #212529;--sd-color-muted-text: #fff;--sd-color-dark-text: #fff;--sd-color-black-text: #fff;--sd-color-white-text: #212529;--sd-color-shadow: rgba(0, 0, 0, 0.15);--sd-color-card-border: rgba(0, 0, 0, 0.125);--sd-color-card-border-hover: hsla(231, 99%, 66%, 1);--sd-color-card-background: transparent;--sd-color-card-text: inherit;--sd-color-card-header: transparent;--sd-color-card-footer: transparent;--sd-color-tabs-label-active: hsla(231, 99%, 66%, 1);--sd-color-tabs-label-hover: hsla(231, 99%, 66%, 1);--sd-color-tabs-label-inactive: hsl(0, 0%, 66%);--sd-color-tabs-underline-active: hsla(231, 99%, 66%, 1);--sd-color-tabs-underline-hover: rgba(178, 206, 245, 0.62);--sd-color-tabs-underline-inactive: transparent;--sd-color-tabs-overline: rgb(222, 222, 222);--sd-color-tabs-underline: rgb(222, 222, 222);--sd-fontsize-tabs-label: 1rem} diff --git a/_static/design-tabs.js b/_static/design-tabs.js new file mode 100644 index 0000000..36b38cf --- /dev/null +++ b/_static/design-tabs.js @@ -0,0 +1,27 @@ +var sd_labels_by_text = {}; + +function ready() { + const li = document.getElementsByClassName("sd-tab-label"); + for (const label of li) { + syncId = label.getAttribute("data-sync-id"); + if (syncId) { + label.onclick = onLabelClick; + if (!sd_labels_by_text[syncId]) { + sd_labels_by_text[syncId] = []; + } + sd_labels_by_text[syncId].push(label); + } + } +} + +function onLabelClick() { + // Activate other inputs with the same sync id. + syncId = this.getAttribute("data-sync-id"); + for (label of sd_labels_by_text[syncId]) { + if (label === this) continue; + label.previousElementSibling.checked = true; + } + window.localStorage.setItem("sphinx-design-last-tab", syncId); +} + +document.addEventListener("DOMContentLoaded", ready, false); diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 0000000..d06a71d --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * 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/_static/documentation_options.js b/_static/documentation_options.js new file mode 100644 index 0000000..b57ae3b --- /dev/null +++ b/_static/documentation_options.js @@ -0,0 +1,14 @@ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), + VERSION: '', + 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/_static/file.png b/_static/file.png new file mode 100644 index 0000000000000000000000000000000000000000..a858a410e4faa62ce324d814e4b816fff83a6fb3 GIT binary patch literal 286 zcmV+(0pb3MP)s`hMrGg#P~ix$^RISR_I47Y|r1 z_CyJOe}D1){SET-^Amu_i71Lt6eYfZjRyw@I6OQAIXXHDfiX^GbOlHe=Ae4>0m)d(f|Me07*qoM6N<$f}vM^LjV8( literal 0 HcmV?d00001 diff --git a/_static/graphviz.css b/_static/graphviz.css new file mode 100644 index 0000000..8d81c02 --- /dev/null +++ b/_static/graphviz.css @@ -0,0 +1,19 @@ +/* + * graphviz.css + * ~~~~~~~~~~~~ + * + * Sphinx stylesheet -- graphviz extension. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +img.graphviz { + border: 0; + max-width: 100%; +} + +object.graphviz { + max-width: 100%; +} diff --git a/_static/language_data.js b/_static/language_data.js new file mode 100644 index 0000000..250f566 --- /dev/null +++ b/_static/language_data.js @@ -0,0 +1,199 @@ +/* + * 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/_static/minus.png b/_static/minus.png new file mode 100644 index 0000000000000000000000000000000000000000..d96755fdaf8bb2214971e0db9c1fd3077d7c419d GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu=nj kDsEF_5m^0CR;1wuP-*O&G^0G}KYk!hp00i_>zopr08q^qX#fBK literal 0 HcmV?d00001 diff --git a/_static/mystnb.4510f1fc1dee50b3e5859aac5469c37c29e427902b24a333a5f9fcb2f0b3ac41.css b/_static/mystnb.4510f1fc1dee50b3e5859aac5469c37c29e427902b24a333a5f9fcb2f0b3ac41.css new file mode 100644 index 0000000..3356631 --- /dev/null +++ b/_static/mystnb.4510f1fc1dee50b3e5859aac5469c37c29e427902b24a333a5f9fcb2f0b3ac41.css @@ -0,0 +1,2342 @@ +/* Variables */ +:root { + --mystnb-source-bg-color: #f7f7f7; + --mystnb-stdout-bg-color: #fcfcfc; + --mystnb-stderr-bg-color: #fdd; + --mystnb-traceback-bg-color: #fcfcfc; + --mystnb-source-border-color: #ccc; + --mystnb-source-margin-color: green; + --mystnb-stdout-border-color: #f7f7f7; + --mystnb-stderr-border-color: #f7f7f7; + --mystnb-traceback-border-color: #ffd6d6; + --mystnb-hide-prompt-opacity: 70%; + --mystnb-source-border-radius: .4em; + --mystnb-source-border-width: 1px; +} + +/* Whole cell */ +div.container.cell { + padding-left: 0; + margin-bottom: 1em; +} + +/* Removing all background formatting so we can control at the div level */ +.cell_input div.highlight, +.cell_output pre, +.cell_input pre, +.cell_output .output { + border: none; + box-shadow: none; +} + +.cell_output .output pre, +.cell_input pre { + margin: 0px; +} + +/* Input cells */ +div.cell div.cell_input, +div.cell details.above-input>summary { + padding-left: 0em; + padding-right: 0em; + border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid; + background-color: var(--mystnb-source-bg-color); + border-left-color: var(--mystnb-source-margin-color); + border-left-width: medium; + border-radius: var(--mystnb-source-border-radius); +} + +div.cell_input>div, +div.cell_output div.output>div.highlight { + margin: 0em !important; + border: none !important; +} + +/* All cell outputs */ +.cell_output { + padding-left: 1em; + padding-right: 0em; + margin-top: 1em; +} + +/* Text outputs from cells */ +.cell_output .output.text_plain, +.cell_output .output.traceback, +.cell_output .output.stream, +.cell_output .output.stderr { + margin-top: 1em; + margin-bottom: 0em; + box-shadow: none; +} + +.cell_output .output.text_plain, +.cell_output .output.stream { + background: var(--mystnb-stdout-bg-color); + border: 1px solid var(--mystnb-stdout-border-color); +} + +.cell_output .output.stderr { + background: var(--mystnb-stderr-bg-color); + border: 1px solid var(--mystnb-stderr-border-color); +} + +.cell_output .output.traceback { + background: var(--mystnb-traceback-bg-color); + border: 1px solid var(--mystnb-traceback-border-color); +} + +/* Collapsible cell content */ +div.cell details.above-input div.cell_input { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-top: var(--mystnb-source-border-width) var(--mystnb-source-border-color) dashed; +} + +div.cell div.cell_input.above-output-prompt { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +div.cell details.above-input>summary { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + border-bottom: var(--mystnb-source-border-width) var(--mystnb-source-border-color) dashed; + padding-left: 1em; + margin-bottom: 0; +} + +div.cell details.above-output>summary { + background-color: var(--mystnb-source-bg-color); + padding-left: 1em; + padding-right: 0em; + border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid; + border-radius: var(--mystnb-source-border-radius); + border-left-color: var(--mystnb-source-margin-color); + border-left-width: medium; +} + +div.cell details.below-input>summary { + background-color: var(--mystnb-source-bg-color); + padding-left: 1em; + padding-right: 0em; + border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid; + border-top: none; + border-bottom-left-radius: var(--mystnb-source-border-radius); + border-bottom-right-radius: var(--mystnb-source-border-radius); + border-left-color: var(--mystnb-source-margin-color); + border-left-width: medium; +} + +div.cell details.hide>summary>span { + opacity: var(--mystnb-hide-prompt-opacity); +} + +div.cell details.hide[open]>summary>span.collapsed { + display: none; +} + +div.cell details.hide:not([open])>summary>span.expanded { + display: none; +} + +@keyframes collapsed-fade-in { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} +div.cell details.hide[open]>summary~* { + -moz-animation: collapsed-fade-in 0.3s ease-in-out; + -webkit-animation: collapsed-fade-in 0.3s ease-in-out; + animation: collapsed-fade-in 0.3s ease-in-out; +} + +/* Math align to the left */ +.cell_output .MathJax_Display { + text-align: left !important; +} + +/* Pandas tables. Pulled from the Jupyter / nbsphinx CSS */ +div.cell_output table { + border: none; + border-collapse: collapse; + border-spacing: 0; + color: black; + font-size: 1em; + table-layout: fixed; +} + +div.cell_output thead { + border-bottom: 1px solid black; + vertical-align: bottom; +} + +div.cell_output tr, +div.cell_output th, +div.cell_output td { + text-align: right; + vertical-align: middle; + padding: 0.5em 0.5em; + line-height: normal; + white-space: normal; + max-width: none; + border: none; +} + +div.cell_output th { + font-weight: bold; +} + +div.cell_output tbody tr:nth-child(odd) { + background: #f5f5f5; +} + +div.cell_output tbody tr:hover { + background: rgba(66, 165, 245, 0.2); +} + +/** source code line numbers **/ +span.linenos { + opacity: 0.5; +} + +/* Inline text from `paste` operation */ + +span.pasted-text { + font-weight: bold; +} + +span.pasted-inline img { + max-height: 2em; +} + +tbody span.pasted-inline img { + max-height: none; +} + +/* Font colors for translated ANSI escape sequences +Color values are copied from Jupyter Notebook +https://github.com/jupyter/notebook/blob/52581f8eda9b319eb0390ac77fe5903c38f81e3e/notebook/static/notebook/less/ansicolors.less#L14-L21 +Background colors from +https://nbsphinx.readthedocs.io/en/latest/code-cells.html#ANSI-Colors +*/ +div.highlight .-Color-Bold { + font-weight: bold; +} + +div.highlight .-Color[class*=-Black] { + color: #3E424D +} + +div.highlight .-Color[class*=-Red] { + color: #E75C58 +} + +div.highlight .-Color[class*=-Green] { + color: #00A250 +} + +div.highlight .-Color[class*=-Yellow] { + color: #DDB62B +} + +div.highlight .-Color[class*=-Blue] { + color: #208FFB +} + +div.highlight .-Color[class*=-Magenta] { + color: #D160C4 +} + +div.highlight .-Color[class*=-Cyan] { + color: #60C6C8 +} + +div.highlight .-Color[class*=-White] { + color: #C5C1B4 +} + +div.highlight .-Color[class*=-BGBlack] { + background-color: #3E424D +} + +div.highlight .-Color[class*=-BGRed] { + background-color: #E75C58 +} + +div.highlight .-Color[class*=-BGGreen] { + background-color: #00A250 +} + +div.highlight .-Color[class*=-BGYellow] { + background-color: #DDB62B +} + +div.highlight .-Color[class*=-BGBlue] { + background-color: #208FFB +} + +div.highlight .-Color[class*=-BGMagenta] { + background-color: #D160C4 +} + +div.highlight .-Color[class*=-BGCyan] { + background-color: #60C6C8 +} + +div.highlight .-Color[class*=-BGWhite] { + background-color: #C5C1B4 +} + +/* Font colors for 8-bit ANSI */ + +div.highlight .-Color[class*=-C0] { + color: #000000 +} + +div.highlight .-Color[class*=-BGC0] { + background-color: #000000 +} + +div.highlight .-Color[class*=-C1] { + color: #800000 +} + +div.highlight .-Color[class*=-BGC1] { + background-color: #800000 +} + +div.highlight .-Color[class*=-C2] { + color: #008000 +} + +div.highlight .-Color[class*=-BGC2] { + background-color: #008000 +} + +div.highlight .-Color[class*=-C3] { + color: #808000 +} + +div.highlight .-Color[class*=-BGC3] { + background-color: #808000 +} + +div.highlight .-Color[class*=-C4] { + color: #000080 +} + +div.highlight .-Color[class*=-BGC4] { + background-color: #000080 +} + +div.highlight .-Color[class*=-C5] { + color: #800080 +} + +div.highlight .-Color[class*=-BGC5] { + background-color: #800080 +} + +div.highlight .-Color[class*=-C6] { + color: #008080 +} + +div.highlight .-Color[class*=-BGC6] { + background-color: #008080 +} + +div.highlight .-Color[class*=-C7] { + color: #C0C0C0 +} + +div.highlight .-Color[class*=-BGC7] { + background-color: #C0C0C0 +} + +div.highlight .-Color[class*=-C8] { + color: #808080 +} + +div.highlight .-Color[class*=-BGC8] { + background-color: #808080 +} + +div.highlight .-Color[class*=-C9] { + color: #FF0000 +} + +div.highlight .-Color[class*=-BGC9] { + background-color: #FF0000 +} + +div.highlight .-Color[class*=-C10] { + color: #00FF00 +} + +div.highlight .-Color[class*=-BGC10] { + background-color: #00FF00 +} + +div.highlight .-Color[class*=-C11] { + color: #FFFF00 +} + +div.highlight .-Color[class*=-BGC11] { + background-color: #FFFF00 +} + +div.highlight .-Color[class*=-C12] { + color: #0000FF +} + +div.highlight .-Color[class*=-BGC12] { + background-color: #0000FF +} + +div.highlight .-Color[class*=-C13] { + color: #FF00FF +} + +div.highlight .-Color[class*=-BGC13] { + background-color: #FF00FF +} + +div.highlight .-Color[class*=-C14] { + color: #00FFFF +} + +div.highlight .-Color[class*=-BGC14] { + background-color: #00FFFF +} + +div.highlight .-Color[class*=-C15] { + color: #FFFFFF +} + +div.highlight .-Color[class*=-BGC15] { + background-color: #FFFFFF +} + +div.highlight .-Color[class*=-C16] { + color: #000000 +} + +div.highlight .-Color[class*=-BGC16] { + background-color: #000000 +} + +div.highlight .-Color[class*=-C17] { + color: #00005F +} + +div.highlight .-Color[class*=-BGC17] { + background-color: #00005F +} + +div.highlight .-Color[class*=-C18] { + color: #000087 +} + +div.highlight .-Color[class*=-BGC18] { + background-color: #000087 +} + +div.highlight .-Color[class*=-C19] { + color: #0000AF +} + +div.highlight .-Color[class*=-BGC19] { + background-color: #0000AF +} + +div.highlight .-Color[class*=-C20] { + color: #0000D7 +} + +div.highlight .-Color[class*=-BGC20] { + background-color: #0000D7 +} + +div.highlight .-Color[class*=-C21] { + color: #0000FF +} + +div.highlight .-Color[class*=-BGC21] { + background-color: #0000FF +} + +div.highlight .-Color[class*=-C22] { + color: #005F00 +} + +div.highlight .-Color[class*=-BGC22] { + background-color: #005F00 +} + +div.highlight .-Color[class*=-C23] { + color: #005F5F +} + +div.highlight .-Color[class*=-BGC23] { + background-color: #005F5F +} + +div.highlight .-Color[class*=-C24] { + color: #005F87 +} + +div.highlight .-Color[class*=-BGC24] { + background-color: #005F87 +} + +div.highlight .-Color[class*=-C25] { + color: #005FAF +} + +div.highlight .-Color[class*=-BGC25] { + background-color: #005FAF +} + +div.highlight .-Color[class*=-C26] { + color: #005FD7 +} + +div.highlight .-Color[class*=-BGC26] { + background-color: #005FD7 +} + +div.highlight .-Color[class*=-C27] { + color: #005FFF +} + +div.highlight .-Color[class*=-BGC27] { + background-color: #005FFF +} + +div.highlight .-Color[class*=-C28] { + color: #008700 +} + +div.highlight .-Color[class*=-BGC28] { + background-color: #008700 +} + +div.highlight .-Color[class*=-C29] { + color: #00875F +} + +div.highlight .-Color[class*=-BGC29] { + background-color: #00875F +} + +div.highlight .-Color[class*=-C30] { + color: #008787 +} + +div.highlight .-Color[class*=-BGC30] { + background-color: #008787 +} + +div.highlight .-Color[class*=-C31] { + color: #0087AF +} + +div.highlight .-Color[class*=-BGC31] { + background-color: #0087AF +} + +div.highlight .-Color[class*=-C32] { + color: #0087D7 +} + +div.highlight .-Color[class*=-BGC32] { + background-color: #0087D7 +} + +div.highlight .-Color[class*=-C33] { + color: #0087FF +} + +div.highlight .-Color[class*=-BGC33] { + background-color: #0087FF +} + +div.highlight .-Color[class*=-C34] { + color: #00AF00 +} + +div.highlight .-Color[class*=-BGC34] { + background-color: #00AF00 +} + +div.highlight .-Color[class*=-C35] { + color: #00AF5F +} + +div.highlight .-Color[class*=-BGC35] { + background-color: #00AF5F +} + +div.highlight .-Color[class*=-C36] { + color: #00AF87 +} + +div.highlight .-Color[class*=-BGC36] { + background-color: #00AF87 +} + +div.highlight .-Color[class*=-C37] { + color: #00AFAF +} + +div.highlight .-Color[class*=-BGC37] { + background-color: #00AFAF +} + +div.highlight .-Color[class*=-C38] { + color: #00AFD7 +} + +div.highlight .-Color[class*=-BGC38] { + background-color: #00AFD7 +} + +div.highlight .-Color[class*=-C39] { + color: #00AFFF +} + +div.highlight .-Color[class*=-BGC39] { + background-color: #00AFFF +} + +div.highlight .-Color[class*=-C40] { + color: #00D700 +} + +div.highlight .-Color[class*=-BGC40] { + background-color: #00D700 +} + +div.highlight .-Color[class*=-C41] { + color: #00D75F +} + +div.highlight .-Color[class*=-BGC41] { + background-color: #00D75F +} + +div.highlight .-Color[class*=-C42] { + color: #00D787 +} + +div.highlight .-Color[class*=-BGC42] { + background-color: #00D787 +} + +div.highlight .-Color[class*=-C43] { + color: #00D7AF +} + +div.highlight .-Color[class*=-BGC43] { + background-color: #00D7AF +} + +div.highlight .-Color[class*=-C44] { + color: #00D7D7 +} + +div.highlight .-Color[class*=-BGC44] { + background-color: #00D7D7 +} + +div.highlight .-Color[class*=-C45] { + color: #00D7FF +} + +div.highlight .-Color[class*=-BGC45] { + background-color: #00D7FF +} + +div.highlight .-Color[class*=-C46] { + color: #00FF00 +} + +div.highlight .-Color[class*=-BGC46] { + background-color: #00FF00 +} + +div.highlight .-Color[class*=-C47] { + color: #00FF5F +} + +div.highlight .-Color[class*=-BGC47] { + background-color: #00FF5F +} + +div.highlight .-Color[class*=-C48] { + color: #00FF87 +} + +div.highlight .-Color[class*=-BGC48] { + background-color: #00FF87 +} + +div.highlight .-Color[class*=-C49] { + color: #00FFAF +} + +div.highlight .-Color[class*=-BGC49] { + background-color: #00FFAF +} + +div.highlight .-Color[class*=-C50] { + color: #00FFD7 +} + +div.highlight .-Color[class*=-BGC50] { + background-color: #00FFD7 +} + +div.highlight .-Color[class*=-C51] { + color: #00FFFF +} + +div.highlight .-Color[class*=-BGC51] { + background-color: #00FFFF +} + +div.highlight .-Color[class*=-C52] { + color: #5F0000 +} + +div.highlight .-Color[class*=-BGC52] { + background-color: #5F0000 +} + +div.highlight .-Color[class*=-C53] { + color: #5F005F +} + +div.highlight .-Color[class*=-BGC53] { + background-color: #5F005F +} + +div.highlight .-Color[class*=-C54] { + color: #5F0087 +} + +div.highlight .-Color[class*=-BGC54] { + background-color: #5F0087 +} + +div.highlight .-Color[class*=-C55] { + color: #5F00AF +} + +div.highlight .-Color[class*=-BGC55] { + background-color: #5F00AF +} + +div.highlight .-Color[class*=-C56] { + color: #5F00D7 +} + +div.highlight .-Color[class*=-BGC56] { + background-color: #5F00D7 +} + +div.highlight .-Color[class*=-C57] { + color: #5F00FF +} + +div.highlight .-Color[class*=-BGC57] { + background-color: #5F00FF +} + +div.highlight .-Color[class*=-C58] { + color: #5F5F00 +} + +div.highlight .-Color[class*=-BGC58] { + background-color: #5F5F00 +} + +div.highlight .-Color[class*=-C59] { + color: #5F5F5F +} + +div.highlight .-Color[class*=-BGC59] { + background-color: #5F5F5F +} + +div.highlight .-Color[class*=-C60] { + color: #5F5F87 +} + +div.highlight .-Color[class*=-BGC60] { + background-color: #5F5F87 +} + +div.highlight .-Color[class*=-C61] { + color: #5F5FAF +} + +div.highlight .-Color[class*=-BGC61] { + background-color: #5F5FAF +} + +div.highlight .-Color[class*=-C62] { + color: #5F5FD7 +} + +div.highlight .-Color[class*=-BGC62] { + background-color: #5F5FD7 +} + +div.highlight .-Color[class*=-C63] { + color: #5F5FFF +} + +div.highlight .-Color[class*=-BGC63] { + background-color: #5F5FFF +} + +div.highlight .-Color[class*=-C64] { + color: #5F8700 +} + +div.highlight .-Color[class*=-BGC64] { + background-color: #5F8700 +} + +div.highlight .-Color[class*=-C65] { + color: #5F875F +} + +div.highlight .-Color[class*=-BGC65] { + background-color: #5F875F +} + +div.highlight .-Color[class*=-C66] { + color: #5F8787 +} + +div.highlight .-Color[class*=-BGC66] { + background-color: #5F8787 +} + +div.highlight .-Color[class*=-C67] { + color: #5F87AF +} + +div.highlight .-Color[class*=-BGC67] { + background-color: #5F87AF +} + +div.highlight .-Color[class*=-C68] { + color: #5F87D7 +} + +div.highlight .-Color[class*=-BGC68] { + background-color: #5F87D7 +} + +div.highlight .-Color[class*=-C69] { + color: #5F87FF +} + +div.highlight .-Color[class*=-BGC69] { + background-color: #5F87FF +} + +div.highlight .-Color[class*=-C70] { + color: #5FAF00 +} + +div.highlight .-Color[class*=-BGC70] { + background-color: #5FAF00 +} + +div.highlight .-Color[class*=-C71] { + color: #5FAF5F +} + +div.highlight .-Color[class*=-BGC71] { + background-color: #5FAF5F +} + +div.highlight .-Color[class*=-C72] { + color: #5FAF87 +} + +div.highlight .-Color[class*=-BGC72] { + background-color: #5FAF87 +} + +div.highlight .-Color[class*=-C73] { + color: #5FAFAF +} + +div.highlight .-Color[class*=-BGC73] { + background-color: #5FAFAF +} + +div.highlight .-Color[class*=-C74] { + color: #5FAFD7 +} + +div.highlight .-Color[class*=-BGC74] { + background-color: #5FAFD7 +} + +div.highlight .-Color[class*=-C75] { + color: #5FAFFF +} + +div.highlight .-Color[class*=-BGC75] { + background-color: #5FAFFF +} + +div.highlight .-Color[class*=-C76] { + color: #5FD700 +} + +div.highlight .-Color[class*=-BGC76] { + background-color: #5FD700 +} + +div.highlight .-Color[class*=-C77] { + color: #5FD75F +} + +div.highlight .-Color[class*=-BGC77] { + background-color: #5FD75F +} + +div.highlight .-Color[class*=-C78] { + color: #5FD787 +} + +div.highlight .-Color[class*=-BGC78] { + background-color: #5FD787 +} + +div.highlight .-Color[class*=-C79] { + color: #5FD7AF +} + +div.highlight .-Color[class*=-BGC79] { + background-color: #5FD7AF +} + +div.highlight .-Color[class*=-C80] { + color: #5FD7D7 +} + +div.highlight .-Color[class*=-BGC80] { + background-color: #5FD7D7 +} + +div.highlight .-Color[class*=-C81] { + color: #5FD7FF +} + +div.highlight .-Color[class*=-BGC81] { + background-color: #5FD7FF +} + +div.highlight .-Color[class*=-C82] { + color: #5FFF00 +} + +div.highlight .-Color[class*=-BGC82] { + background-color: #5FFF00 +} + +div.highlight .-Color[class*=-C83] { + color: #5FFF5F +} + +div.highlight .-Color[class*=-BGC83] { + background-color: #5FFF5F +} + +div.highlight .-Color[class*=-C84] { + color: #5FFF87 +} + +div.highlight .-Color[class*=-BGC84] { + background-color: #5FFF87 +} + +div.highlight .-Color[class*=-C85] { + color: #5FFFAF +} + +div.highlight .-Color[class*=-BGC85] { + background-color: #5FFFAF +} + +div.highlight .-Color[class*=-C86] { + color: #5FFFD7 +} + +div.highlight .-Color[class*=-BGC86] { + background-color: #5FFFD7 +} + +div.highlight .-Color[class*=-C87] { + color: #5FFFFF +} + +div.highlight .-Color[class*=-BGC87] { + background-color: #5FFFFF +} + +div.highlight .-Color[class*=-C88] { + color: #870000 +} + +div.highlight .-Color[class*=-BGC88] { + background-color: #870000 +} + +div.highlight .-Color[class*=-C89] { + color: #87005F +} + +div.highlight .-Color[class*=-BGC89] { + background-color: #87005F +} + +div.highlight .-Color[class*=-C90] { + color: #870087 +} + +div.highlight .-Color[class*=-BGC90] { + background-color: #870087 +} + +div.highlight .-Color[class*=-C91] { + color: #8700AF +} + +div.highlight .-Color[class*=-BGC91] { + background-color: #8700AF +} + +div.highlight .-Color[class*=-C92] { + color: #8700D7 +} + +div.highlight .-Color[class*=-BGC92] { + background-color: #8700D7 +} + +div.highlight .-Color[class*=-C93] { + color: #8700FF +} + +div.highlight .-Color[class*=-BGC93] { + background-color: #8700FF +} + +div.highlight .-Color[class*=-C94] { + color: #875F00 +} + +div.highlight .-Color[class*=-BGC94] { + background-color: #875F00 +} + +div.highlight .-Color[class*=-C95] { + color: #875F5F +} + +div.highlight .-Color[class*=-BGC95] { + background-color: #875F5F +} + +div.highlight .-Color[class*=-C96] { + color: #875F87 +} + +div.highlight .-Color[class*=-BGC96] { + background-color: #875F87 +} + +div.highlight .-Color[class*=-C97] { + color: #875FAF +} + +div.highlight .-Color[class*=-BGC97] { + background-color: #875FAF +} + +div.highlight .-Color[class*=-C98] { + color: #875FD7 +} + +div.highlight .-Color[class*=-BGC98] { + background-color: #875FD7 +} + +div.highlight .-Color[class*=-C99] { + color: #875FFF +} + +div.highlight .-Color[class*=-BGC99] { + background-color: #875FFF +} + +div.highlight .-Color[class*=-C100] { + color: #878700 +} + +div.highlight .-Color[class*=-BGC100] { + background-color: #878700 +} + +div.highlight .-Color[class*=-C101] { + color: #87875F +} + +div.highlight .-Color[class*=-BGC101] { + background-color: #87875F +} + +div.highlight .-Color[class*=-C102] { + color: #878787 +} + +div.highlight .-Color[class*=-BGC102] { + background-color: #878787 +} + +div.highlight .-Color[class*=-C103] { + color: #8787AF +} + +div.highlight .-Color[class*=-BGC103] { + background-color: #8787AF +} + +div.highlight .-Color[class*=-C104] { + color: #8787D7 +} + +div.highlight .-Color[class*=-BGC104] { + background-color: #8787D7 +} + +div.highlight .-Color[class*=-C105] { + color: #8787FF +} + +div.highlight .-Color[class*=-BGC105] { + background-color: #8787FF +} + +div.highlight .-Color[class*=-C106] { + color: #87AF00 +} + +div.highlight .-Color[class*=-BGC106] { + background-color: #87AF00 +} + +div.highlight .-Color[class*=-C107] { + color: #87AF5F +} + +div.highlight .-Color[class*=-BGC107] { + background-color: #87AF5F +} + +div.highlight .-Color[class*=-C108] { + color: #87AF87 +} + +div.highlight .-Color[class*=-BGC108] { + background-color: #87AF87 +} + +div.highlight .-Color[class*=-C109] { + color: #87AFAF +} + +div.highlight .-Color[class*=-BGC109] { + background-color: #87AFAF +} + +div.highlight .-Color[class*=-C110] { + color: #87AFD7 +} + +div.highlight .-Color[class*=-BGC110] { + background-color: #87AFD7 +} + +div.highlight .-Color[class*=-C111] { + color: #87AFFF +} + +div.highlight .-Color[class*=-BGC111] { + background-color: #87AFFF +} + +div.highlight .-Color[class*=-C112] { + color: #87D700 +} + +div.highlight .-Color[class*=-BGC112] { + background-color: #87D700 +} + +div.highlight .-Color[class*=-C113] { + color: #87D75F +} + +div.highlight .-Color[class*=-BGC113] { + background-color: #87D75F +} + +div.highlight .-Color[class*=-C114] { + color: #87D787 +} + +div.highlight .-Color[class*=-BGC114] { + background-color: #87D787 +} + +div.highlight .-Color[class*=-C115] { + color: #87D7AF +} + +div.highlight .-Color[class*=-BGC115] { + background-color: #87D7AF +} + +div.highlight .-Color[class*=-C116] { + color: #87D7D7 +} + +div.highlight .-Color[class*=-BGC116] { + background-color: #87D7D7 +} + +div.highlight .-Color[class*=-C117] { + color: #87D7FF +} + +div.highlight .-Color[class*=-BGC117] { + background-color: #87D7FF +} + +div.highlight .-Color[class*=-C118] { + color: #87FF00 +} + +div.highlight .-Color[class*=-BGC118] { + background-color: #87FF00 +} + +div.highlight .-Color[class*=-C119] { + color: #87FF5F +} + +div.highlight .-Color[class*=-BGC119] { + background-color: #87FF5F +} + +div.highlight .-Color[class*=-C120] { + color: #87FF87 +} + +div.highlight .-Color[class*=-BGC120] { + background-color: #87FF87 +} + +div.highlight .-Color[class*=-C121] { + color: #87FFAF +} + +div.highlight .-Color[class*=-BGC121] { + background-color: #87FFAF +} + +div.highlight .-Color[class*=-C122] { + color: #87FFD7 +} + +div.highlight .-Color[class*=-BGC122] { + background-color: #87FFD7 +} + +div.highlight .-Color[class*=-C123] { + color: #87FFFF +} + +div.highlight .-Color[class*=-BGC123] { + background-color: #87FFFF +} + +div.highlight .-Color[class*=-C124] { + color: #AF0000 +} + +div.highlight .-Color[class*=-BGC124] { + background-color: #AF0000 +} + +div.highlight .-Color[class*=-C125] { + color: #AF005F +} + +div.highlight .-Color[class*=-BGC125] { + background-color: #AF005F +} + +div.highlight .-Color[class*=-C126] { + color: #AF0087 +} + +div.highlight .-Color[class*=-BGC126] { + background-color: #AF0087 +} + +div.highlight .-Color[class*=-C127] { + color: #AF00AF +} + +div.highlight .-Color[class*=-BGC127] { + background-color: #AF00AF +} + +div.highlight .-Color[class*=-C128] { + color: #AF00D7 +} + +div.highlight .-Color[class*=-BGC128] { + background-color: #AF00D7 +} + +div.highlight .-Color[class*=-C129] { + color: #AF00FF +} + +div.highlight .-Color[class*=-BGC129] { + background-color: #AF00FF +} + +div.highlight .-Color[class*=-C130] { + color: #AF5F00 +} + +div.highlight .-Color[class*=-BGC130] { + background-color: #AF5F00 +} + +div.highlight .-Color[class*=-C131] { + color: #AF5F5F +} + +div.highlight .-Color[class*=-BGC131] { + background-color: #AF5F5F +} + +div.highlight .-Color[class*=-C132] { + color: #AF5F87 +} + +div.highlight .-Color[class*=-BGC132] { + background-color: #AF5F87 +} + +div.highlight .-Color[class*=-C133] { + color: #AF5FAF +} + +div.highlight .-Color[class*=-BGC133] { + background-color: #AF5FAF +} + +div.highlight .-Color[class*=-C134] { + color: #AF5FD7 +} + +div.highlight .-Color[class*=-BGC134] { + background-color: #AF5FD7 +} + +div.highlight .-Color[class*=-C135] { + color: #AF5FFF +} + +div.highlight .-Color[class*=-BGC135] { + background-color: #AF5FFF +} + +div.highlight .-Color[class*=-C136] { + color: #AF8700 +} + +div.highlight .-Color[class*=-BGC136] { + background-color: #AF8700 +} + +div.highlight .-Color[class*=-C137] { + color: #AF875F +} + +div.highlight .-Color[class*=-BGC137] { + background-color: #AF875F +} + +div.highlight .-Color[class*=-C138] { + color: #AF8787 +} + +div.highlight .-Color[class*=-BGC138] { + background-color: #AF8787 +} + +div.highlight .-Color[class*=-C139] { + color: #AF87AF +} + +div.highlight .-Color[class*=-BGC139] { + background-color: #AF87AF +} + +div.highlight .-Color[class*=-C140] { + color: #AF87D7 +} + +div.highlight .-Color[class*=-BGC140] { + background-color: #AF87D7 +} + +div.highlight .-Color[class*=-C141] { + color: #AF87FF +} + +div.highlight .-Color[class*=-BGC141] { + background-color: #AF87FF +} + +div.highlight .-Color[class*=-C142] { + color: #AFAF00 +} + +div.highlight .-Color[class*=-BGC142] { + background-color: #AFAF00 +} + +div.highlight .-Color[class*=-C143] { + color: #AFAF5F +} + +div.highlight .-Color[class*=-BGC143] { + background-color: #AFAF5F +} + +div.highlight .-Color[class*=-C144] { + color: #AFAF87 +} + +div.highlight .-Color[class*=-BGC144] { + background-color: #AFAF87 +} + +div.highlight .-Color[class*=-C145] { + color: #AFAFAF +} + +div.highlight .-Color[class*=-BGC145] { + background-color: #AFAFAF +} + +div.highlight .-Color[class*=-C146] { + color: #AFAFD7 +} + +div.highlight .-Color[class*=-BGC146] { + background-color: #AFAFD7 +} + +div.highlight .-Color[class*=-C147] { + color: #AFAFFF +} + +div.highlight .-Color[class*=-BGC147] { + background-color: #AFAFFF +} + +div.highlight .-Color[class*=-C148] { + color: #AFD700 +} + +div.highlight .-Color[class*=-BGC148] { + background-color: #AFD700 +} + +div.highlight .-Color[class*=-C149] { + color: #AFD75F +} + +div.highlight .-Color[class*=-BGC149] { + background-color: #AFD75F +} + +div.highlight .-Color[class*=-C150] { + color: #AFD787 +} + +div.highlight .-Color[class*=-BGC150] { + background-color: #AFD787 +} + +div.highlight .-Color[class*=-C151] { + color: #AFD7AF +} + +div.highlight .-Color[class*=-BGC151] { + background-color: #AFD7AF +} + +div.highlight .-Color[class*=-C152] { + color: #AFD7D7 +} + +div.highlight .-Color[class*=-BGC152] { + background-color: #AFD7D7 +} + +div.highlight .-Color[class*=-C153] { + color: #AFD7FF +} + +div.highlight .-Color[class*=-BGC153] { + background-color: #AFD7FF +} + +div.highlight .-Color[class*=-C154] { + color: #AFFF00 +} + +div.highlight .-Color[class*=-BGC154] { + background-color: #AFFF00 +} + +div.highlight .-Color[class*=-C155] { + color: #AFFF5F +} + +div.highlight .-Color[class*=-BGC155] { + background-color: #AFFF5F +} + +div.highlight .-Color[class*=-C156] { + color: #AFFF87 +} + +div.highlight .-Color[class*=-BGC156] { + background-color: #AFFF87 +} + +div.highlight .-Color[class*=-C157] { + color: #AFFFAF +} + +div.highlight .-Color[class*=-BGC157] { + background-color: #AFFFAF +} + +div.highlight .-Color[class*=-C158] { + color: #AFFFD7 +} + +div.highlight .-Color[class*=-BGC158] { + background-color: #AFFFD7 +} + +div.highlight .-Color[class*=-C159] { + color: #AFFFFF +} + +div.highlight .-Color[class*=-BGC159] { + background-color: #AFFFFF +} + +div.highlight .-Color[class*=-C160] { + color: #D70000 +} + +div.highlight .-Color[class*=-BGC160] { + background-color: #D70000 +} + +div.highlight .-Color[class*=-C161] { + color: #D7005F +} + +div.highlight .-Color[class*=-BGC161] { + background-color: #D7005F +} + +div.highlight .-Color[class*=-C162] { + color: #D70087 +} + +div.highlight .-Color[class*=-BGC162] { + background-color: #D70087 +} + +div.highlight .-Color[class*=-C163] { + color: #D700AF +} + +div.highlight .-Color[class*=-BGC163] { + background-color: #D700AF +} + +div.highlight .-Color[class*=-C164] { + color: #D700D7 +} + +div.highlight .-Color[class*=-BGC164] { + background-color: #D700D7 +} + +div.highlight .-Color[class*=-C165] { + color: #D700FF +} + +div.highlight .-Color[class*=-BGC165] { + background-color: #D700FF +} + +div.highlight .-Color[class*=-C166] { + color: #D75F00 +} + +div.highlight .-Color[class*=-BGC166] { + background-color: #D75F00 +} + +div.highlight .-Color[class*=-C167] { + color: #D75F5F +} + +div.highlight .-Color[class*=-BGC167] { + background-color: #D75F5F +} + +div.highlight .-Color[class*=-C168] { + color: #D75F87 +} + +div.highlight .-Color[class*=-BGC168] { + background-color: #D75F87 +} + +div.highlight .-Color[class*=-C169] { + color: #D75FAF +} + +div.highlight .-Color[class*=-BGC169] { + background-color: #D75FAF +} + +div.highlight .-Color[class*=-C170] { + color: #D75FD7 +} + +div.highlight .-Color[class*=-BGC170] { + background-color: #D75FD7 +} + +div.highlight .-Color[class*=-C171] { + color: #D75FFF +} + +div.highlight .-Color[class*=-BGC171] { + background-color: #D75FFF +} + +div.highlight .-Color[class*=-C172] { + color: #D78700 +} + +div.highlight .-Color[class*=-BGC172] { + background-color: #D78700 +} + +div.highlight .-Color[class*=-C173] { + color: #D7875F +} + +div.highlight .-Color[class*=-BGC173] { + background-color: #D7875F +} + +div.highlight .-Color[class*=-C174] { + color: #D78787 +} + +div.highlight .-Color[class*=-BGC174] { + background-color: #D78787 +} + +div.highlight .-Color[class*=-C175] { + color: #D787AF +} + +div.highlight .-Color[class*=-BGC175] { + background-color: #D787AF +} + +div.highlight .-Color[class*=-C176] { + color: #D787D7 +} + +div.highlight .-Color[class*=-BGC176] { + background-color: #D787D7 +} + +div.highlight .-Color[class*=-C177] { + color: #D787FF +} + +div.highlight .-Color[class*=-BGC177] { + background-color: #D787FF +} + +div.highlight .-Color[class*=-C178] { + color: #D7AF00 +} + +div.highlight .-Color[class*=-BGC178] { + background-color: #D7AF00 +} + +div.highlight .-Color[class*=-C179] { + color: #D7AF5F +} + +div.highlight .-Color[class*=-BGC179] { + background-color: #D7AF5F +} + +div.highlight .-Color[class*=-C180] { + color: #D7AF87 +} + +div.highlight .-Color[class*=-BGC180] { + background-color: #D7AF87 +} + +div.highlight .-Color[class*=-C181] { + color: #D7AFAF +} + +div.highlight .-Color[class*=-BGC181] { + background-color: #D7AFAF +} + +div.highlight .-Color[class*=-C182] { + color: #D7AFD7 +} + +div.highlight .-Color[class*=-BGC182] { + background-color: #D7AFD7 +} + +div.highlight .-Color[class*=-C183] { + color: #D7AFFF +} + +div.highlight .-Color[class*=-BGC183] { + background-color: #D7AFFF +} + +div.highlight .-Color[class*=-C184] { + color: #D7D700 +} + +div.highlight .-Color[class*=-BGC184] { + background-color: #D7D700 +} + +div.highlight .-Color[class*=-C185] { + color: #D7D75F +} + +div.highlight .-Color[class*=-BGC185] { + background-color: #D7D75F +} + +div.highlight .-Color[class*=-C186] { + color: #D7D787 +} + +div.highlight .-Color[class*=-BGC186] { + background-color: #D7D787 +} + +div.highlight .-Color[class*=-C187] { + color: #D7D7AF +} + +div.highlight .-Color[class*=-BGC187] { + background-color: #D7D7AF +} + +div.highlight .-Color[class*=-C188] { + color: #D7D7D7 +} + +div.highlight .-Color[class*=-BGC188] { + background-color: #D7D7D7 +} + +div.highlight .-Color[class*=-C189] { + color: #D7D7FF +} + +div.highlight .-Color[class*=-BGC189] { + background-color: #D7D7FF +} + +div.highlight .-Color[class*=-C190] { + color: #D7FF00 +} + +div.highlight .-Color[class*=-BGC190] { + background-color: #D7FF00 +} + +div.highlight .-Color[class*=-C191] { + color: #D7FF5F +} + +div.highlight .-Color[class*=-BGC191] { + background-color: #D7FF5F +} + +div.highlight .-Color[class*=-C192] { + color: #D7FF87 +} + +div.highlight .-Color[class*=-BGC192] { + background-color: #D7FF87 +} + +div.highlight .-Color[class*=-C193] { + color: #D7FFAF +} + +div.highlight .-Color[class*=-BGC193] { + background-color: #D7FFAF +} + +div.highlight .-Color[class*=-C194] { + color: #D7FFD7 +} + +div.highlight .-Color[class*=-BGC194] { + background-color: #D7FFD7 +} + +div.highlight .-Color[class*=-C195] { + color: #D7FFFF +} + +div.highlight .-Color[class*=-BGC195] { + background-color: #D7FFFF +} + +div.highlight .-Color[class*=-C196] { + color: #FF0000 +} + +div.highlight .-Color[class*=-BGC196] { + background-color: #FF0000 +} + +div.highlight .-Color[class*=-C197] { + color: #FF005F +} + +div.highlight .-Color[class*=-BGC197] { + background-color: #FF005F +} + +div.highlight .-Color[class*=-C198] { + color: #FF0087 +} + +div.highlight .-Color[class*=-BGC198] { + background-color: #FF0087 +} + +div.highlight .-Color[class*=-C199] { + color: #FF00AF +} + +div.highlight .-Color[class*=-BGC199] { + background-color: #FF00AF +} + +div.highlight .-Color[class*=-C200] { + color: #FF00D7 +} + +div.highlight .-Color[class*=-BGC200] { + background-color: #FF00D7 +} + +div.highlight .-Color[class*=-C201] { + color: #FF00FF +} + +div.highlight .-Color[class*=-BGC201] { + background-color: #FF00FF +} + +div.highlight .-Color[class*=-C202] { + color: #FF5F00 +} + +div.highlight .-Color[class*=-BGC202] { + background-color: #FF5F00 +} + +div.highlight .-Color[class*=-C203] { + color: #FF5F5F +} + +div.highlight .-Color[class*=-BGC203] { + background-color: #FF5F5F +} + +div.highlight .-Color[class*=-C204] { + color: #FF5F87 +} + +div.highlight .-Color[class*=-BGC204] { + background-color: #FF5F87 +} + +div.highlight .-Color[class*=-C205] { + color: #FF5FAF +} + +div.highlight .-Color[class*=-BGC205] { + background-color: #FF5FAF +} + +div.highlight .-Color[class*=-C206] { + color: #FF5FD7 +} + +div.highlight .-Color[class*=-BGC206] { + background-color: #FF5FD7 +} + +div.highlight .-Color[class*=-C207] { + color: #FF5FFF +} + +div.highlight .-Color[class*=-BGC207] { + background-color: #FF5FFF +} + +div.highlight .-Color[class*=-C208] { + color: #FF8700 +} + +div.highlight .-Color[class*=-BGC208] { + background-color: #FF8700 +} + +div.highlight .-Color[class*=-C209] { + color: #FF875F +} + +div.highlight .-Color[class*=-BGC209] { + background-color: #FF875F +} + +div.highlight .-Color[class*=-C210] { + color: #FF8787 +} + +div.highlight .-Color[class*=-BGC210] { + background-color: #FF8787 +} + +div.highlight .-Color[class*=-C211] { + color: #FF87AF +} + +div.highlight .-Color[class*=-BGC211] { + background-color: #FF87AF +} + +div.highlight .-Color[class*=-C212] { + color: #FF87D7 +} + +div.highlight .-Color[class*=-BGC212] { + background-color: #FF87D7 +} + +div.highlight .-Color[class*=-C213] { + color: #FF87FF +} + +div.highlight .-Color[class*=-BGC213] { + background-color: #FF87FF +} + +div.highlight .-Color[class*=-C214] { + color: #FFAF00 +} + +div.highlight .-Color[class*=-BGC214] { + background-color: #FFAF00 +} + +div.highlight .-Color[class*=-C215] { + color: #FFAF5F +} + +div.highlight .-Color[class*=-BGC215] { + background-color: #FFAF5F +} + +div.highlight .-Color[class*=-C216] { + color: #FFAF87 +} + +div.highlight .-Color[class*=-BGC216] { + background-color: #FFAF87 +} + +div.highlight .-Color[class*=-C217] { + color: #FFAFAF +} + +div.highlight .-Color[class*=-BGC217] { + background-color: #FFAFAF +} + +div.highlight .-Color[class*=-C218] { + color: #FFAFD7 +} + +div.highlight .-Color[class*=-BGC218] { + background-color: #FFAFD7 +} + +div.highlight .-Color[class*=-C219] { + color: #FFAFFF +} + +div.highlight .-Color[class*=-BGC219] { + background-color: #FFAFFF +} + +div.highlight .-Color[class*=-C220] { + color: #FFD700 +} + +div.highlight .-Color[class*=-BGC220] { + background-color: #FFD700 +} + +div.highlight .-Color[class*=-C221] { + color: #FFD75F +} + +div.highlight .-Color[class*=-BGC221] { + background-color: #FFD75F +} + +div.highlight .-Color[class*=-C222] { + color: #FFD787 +} + +div.highlight .-Color[class*=-BGC222] { + background-color: #FFD787 +} + +div.highlight .-Color[class*=-C223] { + color: #FFD7AF +} + +div.highlight .-Color[class*=-BGC223] { + background-color: #FFD7AF +} + +div.highlight .-Color[class*=-C224] { + color: #FFD7D7 +} + +div.highlight .-Color[class*=-BGC224] { + background-color: #FFD7D7 +} + +div.highlight .-Color[class*=-C225] { + color: #FFD7FF +} + +div.highlight .-Color[class*=-BGC225] { + background-color: #FFD7FF +} + +div.highlight .-Color[class*=-C226] { + color: #FFFF00 +} + +div.highlight .-Color[class*=-BGC226] { + background-color: #FFFF00 +} + +div.highlight .-Color[class*=-C227] { + color: #FFFF5F +} + +div.highlight .-Color[class*=-BGC227] { + background-color: #FFFF5F +} + +div.highlight .-Color[class*=-C228] { + color: #FFFF87 +} + +div.highlight .-Color[class*=-BGC228] { + background-color: #FFFF87 +} + +div.highlight .-Color[class*=-C229] { + color: #FFFFAF +} + +div.highlight .-Color[class*=-BGC229] { + background-color: #FFFFAF +} + +div.highlight .-Color[class*=-C230] { + color: #FFFFD7 +} + +div.highlight .-Color[class*=-BGC230] { + background-color: #FFFFD7 +} + +div.highlight .-Color[class*=-C231] { + color: #FFFFFF +} + +div.highlight .-Color[class*=-BGC231] { + background-color: #FFFFFF +} + +div.highlight .-Color[class*=-C232] { + color: #080808 +} + +div.highlight .-Color[class*=-BGC232] { + background-color: #080808 +} + +div.highlight .-Color[class*=-C233] { + color: #121212 +} + +div.highlight .-Color[class*=-BGC233] { + background-color: #121212 +} + +div.highlight .-Color[class*=-C234] { + color: #1C1C1C +} + +div.highlight .-Color[class*=-BGC234] { + background-color: #1C1C1C +} + +div.highlight .-Color[class*=-C235] { + color: #262626 +} + +div.highlight .-Color[class*=-BGC235] { + background-color: #262626 +} + +div.highlight .-Color[class*=-C236] { + color: #303030 +} + +div.highlight .-Color[class*=-BGC236] { + background-color: #303030 +} + +div.highlight .-Color[class*=-C237] { + color: #3A3A3A +} + +div.highlight .-Color[class*=-BGC237] { + background-color: #3A3A3A +} + +div.highlight .-Color[class*=-C238] { + color: #444444 +} + +div.highlight .-Color[class*=-BGC238] { + background-color: #444444 +} + +div.highlight .-Color[class*=-C239] { + color: #4E4E4E +} + +div.highlight .-Color[class*=-BGC239] { + background-color: #4E4E4E +} + +div.highlight .-Color[class*=-C240] { + color: #585858 +} + +div.highlight .-Color[class*=-BGC240] { + background-color: #585858 +} + +div.highlight .-Color[class*=-C241] { + color: #626262 +} + +div.highlight .-Color[class*=-BGC241] { + background-color: #626262 +} + +div.highlight .-Color[class*=-C242] { + color: #6C6C6C +} + +div.highlight .-Color[class*=-BGC242] { + background-color: #6C6C6C +} + +div.highlight .-Color[class*=-C243] { + color: #767676 +} + +div.highlight .-Color[class*=-BGC243] { + background-color: #767676 +} + +div.highlight .-Color[class*=-C244] { + color: #808080 +} + +div.highlight .-Color[class*=-BGC244] { + background-color: #808080 +} + +div.highlight .-Color[class*=-C245] { + color: #8A8A8A +} + +div.highlight .-Color[class*=-BGC245] { + background-color: #8A8A8A +} + +div.highlight .-Color[class*=-C246] { + color: #949494 +} + +div.highlight .-Color[class*=-BGC246] { + background-color: #949494 +} + +div.highlight .-Color[class*=-C247] { + color: #9E9E9E +} + +div.highlight .-Color[class*=-BGC247] { + background-color: #9E9E9E +} + +div.highlight .-Color[class*=-C248] { + color: #A8A8A8 +} + +div.highlight .-Color[class*=-BGC248] { + background-color: #A8A8A8 +} + +div.highlight .-Color[class*=-C249] { + color: #B2B2B2 +} + +div.highlight .-Color[class*=-BGC249] { + background-color: #B2B2B2 +} + +div.highlight .-Color[class*=-C250] { + color: #BCBCBC +} + +div.highlight .-Color[class*=-BGC250] { + background-color: #BCBCBC +} + +div.highlight .-Color[class*=-C251] { + color: #C6C6C6 +} + +div.highlight .-Color[class*=-BGC251] { + background-color: #C6C6C6 +} + +div.highlight .-Color[class*=-C252] { + color: #D0D0D0 +} + +div.highlight .-Color[class*=-BGC252] { + background-color: #D0D0D0 +} + +div.highlight .-Color[class*=-C253] { + color: #DADADA +} + +div.highlight .-Color[class*=-BGC253] { + background-color: #DADADA +} + +div.highlight .-Color[class*=-C254] { + color: #E4E4E4 +} + +div.highlight .-Color[class*=-BGC254] { + background-color: #E4E4E4 +} + +div.highlight .-Color[class*=-C255] { + color: #EEEEEE +} + +div.highlight .-Color[class*=-BGC255] { + background-color: #EEEEEE +} diff --git a/_static/plus.png b/_static/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..7107cec93a979b9a5f64843235a16651d563ce2d GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu>-2 m3q%Vub%g%s<8sJhVPMczOq}xhg9DJoz~JfX=d#Wzp$Pyb1r*Kz literal 0 HcmV?d00001 diff --git a/_static/pygments.css b/_static/pygments.css new file mode 100644 index 0000000..c2e07c7 --- /dev/null +++ b/_static/pygments.css @@ -0,0 +1,258 @@ +.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/_static/scripts/furo-extensions.js b/_static/scripts/furo-extensions.js new file mode 100644 index 0000000..e69de29 diff --git a/_static/scripts/furo.js b/_static/scripts/furo.js new file mode 100644 index 0000000..cbf6487 --- /dev/null +++ b/_static/scripts/furo.js @@ -0,0 +1,3 @@ +/*! 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;function s(){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 l(){!function(){const t=document.getElementsByClassName("theme-toggle");Array.from(t).forEach((t=>{t.addEventListener("click",s)}))}(),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){t<64?document.documentElement.classList.remove("show-back-to-top"):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"),l()}))})()})(); +//# sourceMappingURL=furo.js.map \ No newline at end of file diff --git a/_static/scripts/furo.js.LICENSE.txt b/_static/scripts/furo.js.LICENSE.txt new file mode 100644 index 0000000..1632189 --- /dev/null +++ b/_static/scripts/furo.js.LICENSE.txt @@ -0,0 +1,7 @@ +/*! + * 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/_static/scripts/furo.js.map b/_static/scripts/furo.js.map new file mode 100644 index 0000000..7ed2be8 --- /dev/null +++ b/_static/scripts/furo.js.map @@ -0,0 +1 @@ +{"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,KAQjBK,EAAe,SAAUR,GAC3B,IAAIS,EAAW,EACf,GAAIT,EAAKU,aACP,KAAOV,GACLS,GAAYT,EAAKW,UACjBX,EAAOA,EAAKU,aAGhB,OAAOD,GAAY,EAAIA,EAAW,GAOhCG,EAAe,SAAUC,GACvBA,GACFA,EAASC,MAAK,SAAUC,EAAOC,GAG7B,OAFcR,EAAaO,EAAME,SACnBT,EAAaQ,EAAMC,UACF,EACxB,MA2CTC,EAAW,SAAUlB,EAAME,EAAUiB,GACvC,IAAIC,EAASpB,EAAKqB,wBACd1B,EAnCU,SAAUO,GAExB,MAA+B,mBAApBA,EAASP,OACX2B,WAAWpB,EAASP,UAItB2B,WAAWpB,EAASP,QA4Bd4B,CAAUrB,GACvB,OAAIiB,EAEAK,SAASJ,EAAOD,OAAQ,KACvB/B,EAAOqC,aAAeC,SAASC,gBAAgBC,cAG7CJ,SAASJ,EAAOS,IAAK,KAAOlC,GAOjCmC,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,eAqDzBU,EAAY,SAAUzB,EAAUX,GAClC,IAAIqC,EAAO1B,EAASA,EAAS2B,OAAS,GACtC,GAbgB,SAAUC,EAAMvC,GAChC,SAAI4B,MAAgBZ,EAASuB,EAAKxB,QAASf,GAAU,IAYjDwC,CAAYH,EAAMrC,GAAW,OAAOqC,EACxC,IAAK,IAAII,EAAI9B,EAAS2B,OAAS,EAAGG,GAAK,EAAGA,IACxC,GAAIzB,EAASL,EAAS8B,GAAG1B,QAASf,GAAW,OAAOW,EAAS8B,IAS7DC,EAAmB,SAAUC,EAAK3C,GAEpC,GAAKA,EAAST,QAAWoD,EAAIC,WAA7B,CAGA,IAAIC,EAAKF,EAAIC,WAAWE,QAAQ,MAC3BD,IAGLA,EAAGE,UAAUC,OAAOhD,EAASR,aAG7BkD,EAAiBG,EAAI7C,MAQnBiD,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,OASVoD,EAAiB,SAAUT,EAAK3C,GAElC,GAAKA,EAAST,OAAd,CAGA,IAAIsD,EAAKF,EAAIC,WAAWE,QAAQ,MAC3BD,IAGLA,EAAGE,UAAUM,IAAIrD,EAASR,aAG1B4D,EAAeP,EAAI7C,MA8LrB,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,OAKbL,EAAaC,IAMfgD,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,MAuEVuE,CAASD,EAAQtE,GAGjByD,EAAUa,GAfJb,IACFR,EAAWQ,EAASzD,GACpByD,EAAU,QAoBZe,EAAgB,SAAUvE,GAExByD,GACFxE,EAAOuF,qBAAqBf,GAI9BA,EAAUxE,EAAOwF,sBAAsBf,EAAWgB,SAOhDC,EAAgB,SAAU3E,GAExByD,GACFxE,EAAOuF,qBAAqBf,GAI9BA,EAAUxE,EAAOwF,uBAAsB,WACrChE,EAAaC,GACbgD,EAAWgB,aAoDf,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,MAQXA,EA3XS,WACX,IAAI+E,EAAS,GAOb,OANAlB,MAAMC,UAAUC,QAAQC,KAAKgB,WAAW,SAAUC,GAChD,IAAK,IAAIC,KAAOD,EAAK,CACnB,IAAKA,EAAIE,eAAeD,GAAM,OAC9BH,EAAOG,GAAOD,EAAIC,OAGfH,EAmXMK,CAAOhG,EAAUmE,GAAW,IAGvCI,EAAW0B,QAGX1B,EAAWgB,SAGXzF,EAAOoG,iBAAiB,SAAUd,GAAe,GAC7CxE,EAASN,QACXR,EAAOoG,iBAAiB,SAAUV,GAAe,GAS9CjB,GA7bA4B,CAAQvG,IAChB,QAFM,SAEN,uBCXDwG,EAA2B,GAG/B,SAASC,EAAoBC,GAE5B,IAAIC,EAAeH,EAAyBE,GAC5C,QAAqBE,IAAjBD,EACH,OAAOA,EAAaE,QAGrB,IAAIC,EAASN,EAAyBE,GAAY,CAGjDG,QAAS,IAOV,OAHAE,EAAoBL,GAAU1B,KAAK8B,EAAOD,QAASC,EAAQA,EAAOD,QAASJ,GAGpEK,EAAOD,QCpBfJ,EAAoBO,EAAKF,IACxB,IAAIG,EAASH,GAAUA,EAAOI,WAC7B,IAAOJ,EAAiB,QACxB,IAAM,EAEP,OADAL,EAAoBU,EAAEF,EAAQ,CAAEG,EAAGH,IAC5BA,GCLRR,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,MCJ3EO,EAAoBxG,EAAI,WACvB,GAA0B,iBAAf0H,WAAyB,OAAOA,WAC3C,IACC,OAAOxH,MAAQ,IAAIyH,SAAS,cAAb,GACd,MAAOC,GACR,GAAsB,iBAAX3H,OAAqB,OAAOA,QALjB,GCAxBuG,EAAoBa,EAAI,CAACrB,EAAK6B,IAAUP,OAAOzC,UAAUqB,eAAenB,KAAKiB,EAAK6B,4CCK9EC,EAAY,KACZC,EAAS,KACTC,EAAgB/H,OAAO6C,aAAeP,SAASC,gBAAgByF,UA4EnE,SAASC,IACP,MAAMC,EAAeC,aAAaC,QAAQ,UAAY,OAZxD,IAAkBC,EACH,WADGA,EAaIrI,OAAOsI,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,QAGT/F,SAASS,KAAK2F,QAAQC,MAAQN,EAC9BF,aAAaS,QAAQ,QAASP,GAC9BG,QAAQK,IAAI,cAAcR,WA4E5B,SAASlC,KART,WAEE,MAAM2C,EAAUxG,SAASyG,uBAAuB,gBAChDpE,MAAMqE,KAAKF,GAASjE,SAASoE,IAC3BA,EAAI7C,iBAAiB,QAAS6B,MAKhCiB,GA9CF,WAEE,IAAIC,EAA6B,EAC7BC,GAAU,EAEdpJ,OAAOoG,iBAAiB,UAAU,SAAUuB,GAC1CwB,EAA6BnJ,OAAOqJ,QAE/BD,IACHpJ,OAAOwF,uBAAsB,WAzDnC,IAAuB8D,IA0DDH,EA9GkC,GAAlDxG,KAAK4G,MAAMzB,EAAO7F,wBAAwBQ,KAC5CqF,EAAOjE,UAAUM,IAAI,YAErB2D,EAAOjE,UAAUC,OAAO,YAI5B,SAAmCwF,GAC7BA,EAXmB,GAYrBhH,SAASC,gBAAgBsB,UAAUC,OAAO,oBAEtCwF,EAAYvB,EACdzF,SAASC,gBAAgBsB,UAAUM,IAAI,oBAC9BmF,EAAYvB,GACrBzF,SAASC,gBAAgBsB,UAAUC,OAAO,oBAG9CiE,EAAgBuB,EAqChBE,CAA0BF,GAlC5B,SAA6BA,GACT,OAAdzB,IAKa,GAAbyB,EACFzB,EAAU4B,SAAS,EAAG,GAGtB9G,KAAKC,KAAK0G,IACV3G,KAAK4G,MAAMjH,SAASC,gBAAgBS,aAAehD,OAAOqC,aAE1DwF,EAAU4B,SAAS,EAAG5B,EAAU7E,cAGhBV,SAASoH,cAAc,oBAmBzCC,CAAoBL,GAwDdF,GAAU,KAGZA,GAAU,MAGdpJ,OAAO4J,SA8BPC,GA1BkB,OAAdhC,GAKJ,IAAI,IAAJ,CAAY,cAAe,CACzBrH,QAAQ,EACRsJ,WAAW,EACX3J,SAAU,iBACVI,OAAQ,KACN,IAAIwJ,EAAM7H,WAAW8H,iBAAiB1H,SAASC,iBAAiB0H,UAChE,OAAOnC,EAAO7F,wBAAwBiI,OAAS,GAAMH,EAAM,KA+BjEzH,SAAS8D,iBAAiB,oBAT1B,WACE9D,SAASS,KAAKW,WAAWG,UAAUC,OAAO,SAE1CgE,EAASxF,SAASoH,cAAc,UAChC7B,EAAYvF,SAASoH,cAAc,eAEnCvD","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","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/_static/searchtools.js b/_static/searchtools.js new file mode 100644 index 0000000..97d56a7 --- /dev/null +++ b/_static/searchtools.js @@ -0,0 +1,566 @@ +/* + * 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) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docUrlRoot = DOCUMENTATION_OPTIONS.URL_ROOT; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + + 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 = docUrlRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = docUrlRoot + 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 + ")"; + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms) + ); + }); + 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 +) => { + // 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); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms), + 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); + }, + + /** + * 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/_static/skeleton.css b/_static/skeleton.css new file mode 100644 index 0000000..467c878 --- /dev/null +++ b/_static/skeleton.css @@ -0,0 +1,296 @@ +/* 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/_static/sphinx_highlight.js b/_static/sphinx_highlight.js new file mode 100644 index 0000000..aae669d --- /dev/null +++ b/_static/sphinx_highlight.js @@ -0,0 +1,144 @@ +/* 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))); + parent.insertBefore( + span, + parent.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + + 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(SphinxHighlight.highlightSearchWords); +_ready(SphinxHighlight.initEscapeListener); diff --git a/_static/styles/furo-extensions.css b/_static/styles/furo-extensions.css new file mode 100644 index 0000000..bc447f2 --- /dev/null +++ b/_static/styles/furo-extensions.css @@ -0,0 +1,2 @@ +#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/_static/styles/furo-extensions.css.map b/_static/styles/furo-extensions.css.map new file mode 100644 index 0000000..9ba5637 --- /dev/null +++ b/_static/styles/furo-extensions.css.map @@ -0,0 +1 @@ +{"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/_static/styles/furo.css b/_static/styles/furo.css new file mode 100644 index 0000000..b30989d --- /dev/null +++ b/_static/styles/furo.css @@ -0,0 +1,2 @@ +/*! 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;-webkit-text-decoration-color:var(--color-link-underline);text-decoration-color:var(--color-link-underline)}a:hover{color:var(--color-link--hover);-webkit-text-decoration-color:var(--color-link-underline--hover);text-decoration-color:var(--color-link-underline--hover)}a.muted-link{color:inherit}a.muted-link:hover{color:var(--color-link);-webkit-text-decoration-color:var(--color-link-underline--hover);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)}.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:-webkit-sticky;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);-webkit-text-decoration-color:var(--color-announcement-text);text-decoration-color:var(--color-announcement-text)}.announcement-content a:hover{color:var(--color-announcement-text);-webkit-text-decoration-color:var(--color-link--hover);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:-webkit-sticky;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}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}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}.sig:not(.sig-inline) span.pre{overflow-wrap:anywhere}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);overflow-wrap:break-word;padding:.1em .2em}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:-webkit-max-content auto;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;-webkit-text-decoration-line:none;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;-webkit-text-decoration-color:var(--color-link-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/_static/styles/furo.css.map b/_static/styles/furo.css.map new file mode 100644 index 0000000..92af407 --- /dev/null +++ b/_static/styles/furo.css.map @@ -0,0 +1 @@ +{"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,sDACA,uEAGE,sDACA,gGACF,wCAGI,sBACA,yHCzEJ,2BACA,qCAGF,sEAGE,kEAGA,sHAGA,2IACE,8BACA,8BAOF,uCAEA,wEAGA,sDACA,iCAKA,CAEF,qCAEE,sDACA,gCACA,gEAKA,+CAOE,sBACA,gEAGA,GAYF,yLACA,gDAGA,mBAEA,wCACA,wCAGF,CAEE,iCAGF,wBACE,mBAIF,oBAFE,eAEF,CAJE,gBAEA,CAMA,mBACA,mBAGA,mDAIA,YACA,mBAEA,CACA,kBAGF,OAJE,kBAQA,CAJF,GACE,aAGA,IACA,mCACA,qBAEF,IACE,oBAEA,aACA,CAFA,WAEA,GAEE,oBAKJ,CAPE,gBAOF,aACE,+CAGA,UAHA,kCAGA,4BACA,GAEA,uBACA,CAHA,yBAEA,CACA,yDAGF,kDAEE,SACA,8BAEA,iEAGE,yDACA,sEAEA,iEAEE,yHAKN,kDAMA,0DAIE,CANA,oBAMA,0GAOA,aAEF,CAHE,YAGF,4HAWE,+CACE,iCAIJ,0CAGE,CALE,qCAEJ,CAHI,WAMF,SAIA,0CAIA,CANF,qCAME,mBACA,gBACA,gBAIA,+CAEE,CAIF,kDAGF,CAPI,8BAGJ,CAKE,YACF,CAbE,2BAEA,CAHA,WAYF,UAEA,yBACE,kBAIA,iEAKA,iCAGA,mDAEA,mBACF,OACE,iBAQA,0CAIA,CAPA,6DAGA,CALF,qBAEE,CAOA,qCAEE,CAGA,eAHA,sBAGA,gCAKF,qBACE,WACA,aACA,sCAEA,mBAOJ,6BASE,kCACA,CAHA,sBACA,aACA,CARA,uBAGA,gBAEA,MAIA,6BAEA,yBACA,2DAEA,sBAGA,8BACA,CANA,wBAMA,2BAEE,YACA,sBACA,WAEF,CAFE,UAEF,eAeF,kBAEE,CAhBE,qDAGA,qCAOJ,CAEI,YAEJ,CAJA,2BAEI,CAIF,eACE,qBACF,4CAIE,uBACA,sBACF,cACE,CAFA,aACF,CAEE,kBADA,kBACA,yBAGF,oCACE,6DAMF,qDAGE,CC1VY,8BDgWd,oCAEA,uDAEA,CACE,8CAIA,gCAEA,YACA,8CACA,CAEA,oCAGE,CAHF,oCAGE,mBAEA,mDADA,YADA,qBACA,WACA,sBAEE,WACA,uDAEN,eAFM,YAEN,iDAGE,uCAIA,YAGF,+CAKE,kBACA,CALA,sBAKA,mBACF,aACE,aACA,yBAEJ,YAGI,CAHJ,YAOE,SACE,CAFJ,kBACE,CAHE,gBAEJ,CAHI,iBAKA,6CAIA,aACA,YEhaJ,4BAEE,aADA,iBACA,6BAEA,kCAEA,SACA,UAIA,gCACA,CALA,SAEA,SAEA,CAJA,0EAEA,CAFA,OAKA,CAGA,mDACE,iBAGF,gCACE,CADF,UACE,aAEJ,iCACE,CADF,UAEE,wCAEA,WACA,WAFA,UAEA,6CAIA,yCACA,WAGA,WAJA,UAIA,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,kBACA,CADA,2CADF,uCACE,MACA,0DACE,yCACA,qGALJ,oCACA,uCACE,CAFF,UAEE,uEACA,+CACE,oDACA,6DANN,kCACE,kCACA,gBADA,UACA,yBACE,wDACA,cADA,UACA,qBACE,6CACA,yFALJ,sCACA,CAEE,gBACE,CAHJ,gBAGI,sBAHJ,uBACE,4DACA,4CACE,iDAJJ,2CACA,CADA,gBAEE,gBAGE,sBALJ,+BAII,iBAFF,gDACA,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,yDCpEA,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,wDAIA,uCAEE,kEAEF,CACF,6CAEE,uDAEA,oCAIF,4BACE,6BAEA,gEAEE,+CAIF,0EC9FA,sDAGE,+DCLJ,sCAGE,8BAKA,wJAIE,gBACA,yGCZF,mBAQA,2MAIA,oBAOF,wGAKE,iCAEE,CAFF,wBAEE,8GAWF,mBAEE,2GAMA,mBAEA,6HAOF,YAGA,mIAOE,gBADA,YACA,4FAOF,8BACA,uBAYA,sCAEE,CAFF,qBARA,wCAEA,CAHA,8BACA,CAFA,eACA,CAGA,mBAEA,sBAEA,kDAEA,CAEE,kCACE,6BACA,4CAMJ,kDAGA,eAIA,6CACE,mCACA,0CACA,8BAEA,sCACA,cAEF,+BACE,CAHA,eAGA,YACA,4BACA,gEAGF,0DAME,sBAFA,kBAGE,+BACA,4BAIJ,aACE,oBACA,CAFF,gBAEE,yBAEA,eACA,CApHsB,YAmHtB,CACA,sECpIF,mDACA,2FAMA,iCAGA,0FAEE,eACA,CAFF,YAEE,0BACE,8CAEF,mBAIE,qCACE,CACF,yBADE,iBACF,8BAGJ,+CAKF,aACE,wCACA,kDAEF,YAEE,CAFF,YAEE,CClCA,mFDwCA,QCzCF,UAGE,CAFA,IACA,aACA,mCAGA,eACE,kCAGA,uDAGF,mBAKA,6CAGE,CALA,mBAEF,CAGE,kCAEF,CARE,kBACA,CAFA,eASF,YAEE,mBACA,CAHF,UAGE,wCC7BJ,oBDkCE,8CAEE,iBCpCJ,iBACE,wDACA,gEASE,6CCLF,CDIE,uBACA,CALF,oBACE,4BAEF,8BCAE,2CAEE,CALJ,kCAGE,CDHF,aAGA,eACE,CAJF,uBCKI,gCAEF,gDAGA,kDAGE,iBAIF,cADF,UACE,uBAEA,iCAEA,wCAEA,6CAEA,CASE,+BASJ,CAZE,4BAGE,CATF,kCAMA,kCAYF,4BACE,2DAEA,CAHF,+BACE,CADF,qBAGE,2GAGA,wIAEE,CAFF,8EAEE,qBACA,oCAGF,6RAIA,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,kCADA,gBACA,aAGJ,sDAHI,mBAGJ,yBAYI,+VACE,sDAGA,iBAHA,2BAGA,kWAGN,iDAEE,CALI,gGAGN,CAHM,gBAKJ,yCAGF,0EACE,2EAGF,iBACE,yDAOA,0EAGF,6EAEE,iBC/EA,wDACA,4DACA,qBAEA,oDCDA,6BACA,yBACA,sBAEA,iBAGF,sNAYE,iBAEA,kBAdF,wRA8BI,kBACA,iOAkBA,aACA,4DACE,uEAEA,uVAoBA,iDAKA,ieC1EJ,4BACA,CCFF,6JAEE,iDACA,sEAIA,mDAGA,iDAOF,4DAGE,8CAEA,CAEA,kBACA,CAHA,gCAEA,CACA,eADA,cACA,oBAEE,uBAFF,kCAEE,gCAEF,kBACE,CAIA,mDAEA,CAHA,uCACA,CALF,aACE,6BAEA,CAIA,gBAJA,mCACA,CADA,gBAIA,wBACA,6CAGF,YAHE,iBAGF,gCAGA,iEACA,6CAEA,qDACA,6EACA,2EACA,8GAEA,yCAGA,uBACA,CAFA,yBACA,CACA,yDAKA,kDACE,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/variables/_layout.scss","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/_search.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\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","// Overall Layout Variables\n//\n// Because CSS variables can't be used in media queries. The fact that this\n// makes the layout non-user-configurable is a good thing.\n$content-padding: 3em;\n$content-padding--small: 1em;\n$content-width: 46em;\n$sidebar-width: 15em;\n$full-width: $content-width + 2 * ($content-padding + $sidebar-width);\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\np.admonition-title, p.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\n // Break words when they're too long\n span.pre\n overflow-wrap: anywhere\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 overflow-wrap: break-word\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","//\n// Search Page Listing\n//\nul.search\n padding-left: 0\n list-style: none\n\n li\n padding: 1rem 0\n border-bottom: 1px solid var(--color-background-border)\n\n//\n// Highlighted by links in search page\n//\n[role=main] .highlighted\n background-color: var(--color-highlighted-background)\n color: var(--color-highlighted-text)\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