diff --git a/_modules/QEfficient/peft/auto.html b/_modules/QEfficient/peft/auto.html index 22de0e143..7ffff5112 100644 --- a/_modules/QEfficient/peft/auto.html +++ b/_modules/QEfficient/peft/auto.html @@ -219,6 +219,10 @@

Source code for QEfficient.peft.auto

         mhash = mhash.hexdigest()[:16]
         return mhash
 
+    @property
+    def get_model_config(self) -> dict:
+        return self.model.get_base_model().config.__dict__
+
 
[docs] def load_adapter(self, model_id: str, adapter_name: str): """Loads a new adapter from huggingface hub or local path diff --git a/_modules/QEfficient/peft/lora/auto.html b/_modules/QEfficient/peft/lora/auto.html index 7c4e600f5..994aa253c 100644 --- a/_modules/QEfficient/peft/lora/auto.html +++ b/_modules/QEfficient/peft/lora/auto.html @@ -202,6 +202,10 @@

Source code for QEfficient.peft.lora.auto

         mhash = mhash.hexdigest()[:16]
         return mhash
 
+    @property
+    def get_model_config(self) -> dict:
+        return self.model.model.config.__dict__
+
 
[docs] def download_adapter( self, adapter_model_id: str, diff --git a/_modules/QEfficient/transformers/models/modeling_auto.html b/_modules/QEfficient/transformers/models/modeling_auto.html index 9f70b4a71..9f109ad4e 100644 --- a/_modules/QEfficient/transformers/models/modeling_auto.html +++ b/_modules/QEfficient/transformers/models/modeling_auto.html @@ -164,8 +164,6 @@

Source code for QEfficient.transformers.models.modeling_auto

from QEfficient.utils.cache import to_hashable from QEfficient.utils.logging_utils import logger -MODELS_WITH_ACCURACY_ISSUE_FOR_MXFP6 = ["MllamaForConditionalGeneration"] - class QEFFTransformersBase(QEFFBaseModel): """ @@ -343,6 +341,10 @@

Source code for QEfficient.transformers.models.modeling_auto

mhash = mhash.hexdigest()[:16] return mhash + @property + def get_model_config(self) -> dict: + return self.model.config.__dict__ +
[docs] def export(self, export_dir: Optional[str] = None) -> str: """ Exports the model to ``ONNX`` format using ``torch.onnx.export``. @@ -504,7 +506,13 @@

Source code for QEfficient.transformers.models.modeling_auto

class QEffVisionEncoderForTextImageToTextModel(QEFFBaseModel): - _pytorch_transforms = [AwqToMatmulNbitsTransform, GPTQToMatmulNbitsTransform, CustomOpsTransform, KVCacheTransform] + _pytorch_transforms = [ + AwqToMatmulNbitsTransform, + GPTQToMatmulNbitsTransform, + CustomOpsTransform, + KVCacheTransform, + KVCacheModuleMethodMapperTransform, + ] _onnx_transforms = [FP16ClipTransform, SplitTensorsTransform] def __init__(self, model: nn.modules): @@ -555,6 +563,10 @@

Source code for QEfficient.transformers.models.modeling_auto

mname = mname[4:] return mname + @property + def get_model_config(self) -> dict: + return self.model.model.vision_model.config.__dict__ + class QEffCausalLMForTextImageToTextModel(QEFFBaseModel): _pytorch_transforms = [ @@ -568,6 +580,7 @@

Source code for QEfficient.transformers.models.modeling_auto

def __init__(self, model): super().__init__(model) + self.model = model.get_qeff_language_decoder() def export(self, inputs, output_names, dynamic_axes, export_dir=None): return self._export(inputs, output_names, dynamic_axes, export_dir) @@ -613,10 +626,13 @@

Source code for QEfficient.transformers.models.modeling_auto

mname = mname[4:] return mname + @property + def get_model_config(self) -> dict: + return self.model.language_model.config.__dict__ + class _QEffAutoModelForImageTextToTextDualQPC: _hf_auto_class = AutoModelForImageTextToText - UNSUPPORTED_MODELS = ["LlavaForConditionalGeneration", "InternVLChatModel"] def __init__( self, @@ -627,8 +643,6 @@

Source code for QEfficient.transformers.models.modeling_auto

raise NotImplementedError("Continuous batching is not supported for image-text-to-text models yet.") self.model = model self.config = model.config - if self.model_name in self.UNSUPPORTED_MODELS: - raise NotImplementedError(f"kv_offload is not yet supported for {self.model.__class__.__name__}") self.vision_model = QEffVisionEncoderForTextImageToTextModel(model) self.lang_model = QEffCausalLMForTextImageToTextModel(model) @@ -677,6 +691,7 @@

Source code for QEfficient.transformers.models.modeling_auto

) self.lang_model.export(inputs["lang"], output_names["lang"], dynamic_axes["lang"], export_dir) + return self.onnx_path def compile( self, @@ -739,17 +754,12 @@

Source code for QEfficient.transformers.models.modeling_auto

): self.export() - if mxfp6_matmul and self.model_name in MODELS_WITH_ACCURACY_ISSUE_FOR_MXFP6: - logger.warning( - "Due to accuracy issues of vision model fixing it's precision to fp16, while language model will be compiled for mxfp6" - ) - self.vision_model._compile( compile_dir, compile_only=True, specializations=specializations["vision"], convert_to_fp16=True, - mxfp6_matmul=False, + mxfp6_matmul=mxfp6_matmul, mdp_ts_num_devices=num_devices, aic_num_cores=num_cores, custom_io=custom_io_vision, @@ -759,12 +769,12 @@

Source code for QEfficient.transformers.models.modeling_auto

custom_io_lang = {} # Inputs for output_name in output_names["lang"]: - if output_name.startswith("past_"): + if output_name.endswith("_RetainedState"): custom_io_lang[output_name[: -len("_RetainedState")]] = kv_cache_dtype # outputs for output_name in output_names["lang"]: - if output_name.startswith("past_"): + if output_name.endswith("_RetainedState"): custom_io_lang[output_name] = kv_cache_dtype self.lang_model._compile( @@ -779,6 +789,7 @@

Source code for QEfficient.transformers.models.modeling_auto

custom_io=custom_io_lang, **compiler_options, ) + return self.qpc_path def generate( self, @@ -918,7 +929,6 @@

Source code for QEfficient.transformers.models.modeling_auto

lang_inputs["input_ids"] = outputs["logits"].argmax(2) lang_inputs["position_ids"] += 1 generated_ids[:, num_token] = lang_inputs["input_ids"].squeeze(1) - if streamer: streamer.put(lang_inputs["input_ids"][0]) @@ -999,7 +1009,7 @@

Source code for QEfficient.transformers.models.modeling_auto

inputs = self.model.get_dummy_inputs() dynamic_axes = self.model.get_onnx_dynamic_axes() output_names = self.model.get_output_names() - self._export(inputs, output_names, dynamic_axes, export_dir=export_dir) + return self._export(inputs, output_names, dynamic_axes, export_dir=export_dir) def compile( self, @@ -1058,11 +1068,6 @@

Source code for QEfficient.transformers.models.modeling_auto

if output_name.endswith("_RetainedState"): custom_io[output_name] = kv_cache_dtype - if self.model_name in MODELS_WITH_ACCURACY_ISSUE_FOR_MXFP6 and mxfp6_matmul: - logger.warning( - f"It is advised to use fp16 precision during compilation for {self.model.__class__.__name__} to avoid accuracy issues, got mxfp6_matmul=True" - ) - self._compile( onnx_path, compile_dir, @@ -1249,26 +1254,81 @@

Source code for QEfficient.transformers.models.modeling_auto

mname = mname[4:] return mname + @property + def get_model_config(self) -> dict: + return self.model.config.__dict__ +
[docs]class QEFFAutoModelForImageTextToText: """ - A factory class for creating QEFFAutoModelForImageTextToText instances with for single and Dual QPC approach + The QEFFAutoModelForImageTextToText class is used to work with multimodal language models from the HuggingFace hub. + While you can initialize the class directly, it's best to use the ``from_pretrained`` method for this purpose. This class supports both single and dual QPC approaches. Attributes: _hf_auto_class (class): The Hugging Face AutoModel class for ImageTextToText models. + + ``Mandatory`` Args: + :pretrained_model_name_or_path (str): Model card name from HuggingFace or local path to model directory. + + ``Optional`` Args: + :kv_offload (bool): Flag to toggle between single and dual QPC approaches. If set to False, the Single QPC approach will be used; otherwise, the dual QPC approach will be applied. Defaults to True. + + .. code-block:: python + import requests + from PIL import Image + from transformers import AutoProcessor, TextStreamer + + from QEfficient import QEFFAutoModelForImageTextToText + + # Add HuggingFace Token to access the model + HF_TOKEN = "" + model_name = "meta-llama/Llama-3.2-11B-Vision-Instruct" + query = "Describe this image." + image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/0052a70beed5bf71b92610a43a52df6d286cd5f3/diffusers/rabbit.jpg" + + ## STEP - 1 Load the Processor and Model, and kv_offload=True/False for dual and single qpc + processor = AutoProcessor.from_pretrained(model_name, token=token) + model = QEFFAutoModelForImageTextToText.from_pretrained(model_name, token=token, attn_implementation="eager", kv_offload=False) + + ## STEP - 2 Export & Compile the Model + model.compile( + prefill_seq_len=32, + ctx_len=512, + img_size=560, + num_cores=16, + num_devices=1, + mxfp6_matmul=False, + ) + + ## STEP - 3 Load and process the inputs for Inference + image = Image.open(requests.get(image_url, stream=True).raw) + messages = [ + { + "role": "user", + "content": [ + {"type": "image"}, + {"type": "text", "text": query}, + ], + } + ] + input_text = [processor.apply_chat_template(messages, add_generation_prompt=True)] + inputs = processor( + text=input_text, + images=image, + return_tensors="pt", + add_special_tokens=False, + padding="max_length", + max_length=prefill_seq_len, + ) + + ## STEP - 4 Run Inference on the compiled model + streamer = TextStreamer(processor.tokenizer) + model.generate(inputs=inputs, streamer=streamer, generation_len=generation_len) + """ _hf_auto_class = AutoModelForImageTextToText - def __new__(self, model: nn.Module, kv_offload: Optional[bool] = None, **kwargs): - if model.config.architectures[0] in MODELS_WITH_ACCURACY_ISSUE_FOR_MXFP6 and not kv_offload: - # For models with mxfp6 accuracy issue, we will use kv_offload=True by default - if kv_offload is None: - kv_offload = True - else: - logger.warning(f"Advised to use kv_offload=True for {model.__class__.__name__}") - elif kv_offload is None: - kv_offload = False - + def __new__(self, model: nn.Module, kv_offload: Optional[bool] = True, **kwargs): if kv_offload: return _QEffAutoModelForImageTextToTextDualQPC(model, **kwargs) else: @@ -1379,7 +1439,7 @@

Source code for QEfficient.transformers.models.modeling_auto

return mname def __repr__(self) -> str: - return self.__class__.__name__ + "\n" + self.model.__repr__ + return self.__class__.__name__ + "\n" + self.model.__repr__() @classmethod @with_replaced_quantizers @@ -1450,6 +1510,10 @@

Source code for QEfficient.transformers.models.modeling_auto

mhash = mhash.hexdigest()[:16] return mhash + @property + def get_model_config(self) -> dict: + return self.model.config.__dict__ +
[docs] def export(self, export_dir: Optional[str] = None) -> str: """ Exports the model to ``ONNX`` format using ``torch.onnx.export``. @@ -1760,6 +1824,10 @@

Source code for QEfficient.transformers.models.modeling_auto

mhash = mhash.hexdigest()[:16] return mhash + @property + def get_model_config(self) -> dict: + return self.model.config.__dict__ +
[docs] def export(self, export_dir: Optional[str] = None) -> str: """ Exports the model to ``ONNX`` format using ``torch.onnx.export``. @@ -1773,20 +1841,26 @@

Source code for QEfficient.transformers.models.modeling_auto

inputs = self.model.get_dummy_inputs() dynamic_axes = self.model.get_onnx_dynamic_axes() output_names = self.model.get_output_names() - self._export(inputs, output_names, dynamic_axes, export_dir=export_dir)
+ return self._export(inputs, output_names, dynamic_axes, export_dir=export_dir)
[docs] def compile( self, onnx_path: Optional[str] = None, compile_dir: Optional[str] = None, *, - encoder_ctx_len: int = 1500, - decoder_ctx_len: int = 150, - feature_len: int = 3000, + prefill_seq_len: Optional[int] = 1, + encoder_ctx_len: Optional[int] = None, + ctx_len: int = 150, + full_batch_size: Optional[int] = None, + kv_cache_batch_size: Optional[int] = None, batch_size: int = 1, num_devices: int = 1, num_cores: int = 16, # FIXME: Make this mandatory arg mxfp6_matmul: bool = False, + mxint8_kv_cache: bool = False, + num_speculative_tokens: Optional[int] = None, + enable_qnn: bool = False, + qnn_config: Optional[str] = None, **compiler_options, ) -> str: """ @@ -1797,19 +1871,41 @@

Source code for QEfficient.transformers.models.modeling_auto

``Optional`` Args: :onnx_path (str, optional): Path to pre-exported onnx model. :compile_dir (str, optional): Path for saving the qpc generated. - :seq_len (int, optional): The length of the prompt should be less that ``seq_len``. ``Defaults to 32``. + :encoder_ctx_len (int, optional): The maximum length of context for encoder, based on the AutoProcessor output. ``Defaults to checking config, if None in config then 1500`` + :ctx_len (int, optional): The maximum length of context to keep for decoding. ``Defaults to 150``. :batch_size (int, optional): Batch size. ``Defaults to 1``. :num_devices (int): Number of devices the model needs to be compiled for. Defaults to 1. :num_cores (int): Number of cores used to compile the model. :mxfp6_matmul (bool, optional): Whether to use ``mxfp6`` compression for weights. ``Defaults to False``. :aic_enable_depth_first (bool, optional): Enables DFS with default memory size. ``Defaults to False``. - :allow_mxint8_mdp_io (bool, optional): Allows MXINT8 compression of MDP IO traffic. ``Defaults to False.`` + + Other args are not yet implemented for AutoModelForSpeechSeq2Seq Returns: :str: Path of the compiled ``qpc`` package. """ - specializations = self.model.get_specializations(batch_size, encoder_ctx_len, decoder_ctx_len, feature_len) + specializations, compiler_options = self.model.get_specializations( + batch_size, + encoder_ctx_len, + ctx_len, + **compiler_options, + ) - self._compile( + if full_batch_size: + logger.warning("Continuous batching is not yet enabled for AutoModelForSpeechSeq2Seq") + + if kv_cache_batch_size: + logger.warning("Prefix caching is not yet enabled for AutoModelForSpeechSeq2Seq") + + if mxint8_kv_cache: + logger.warning("mxint8 cache is not yet enabled for AutoModelForSpeechSeq2Seq") + + if num_speculative_tokens: + logger.warning("Speculative decoding is not yet enabled for AutoModelForSpeechSeq2Seq") + + if enable_qnn or qnn_config: + logger.warning("QNN compile is not yet enabled for AutoModelForSpeechSeq2Seq") + + return self._compile( onnx_path, compile_dir, compile_only=True, @@ -1827,7 +1923,6 @@

Source code for QEfficient.transformers.models.modeling_auto

inputs: torch.Tensor, generation_len: int, streamer: Optional[TextStreamer] = None, - enable_debug_logs: bool = False, device_ids: List[int] = None, ) -> Union[torch.Tensor, np.ndarray]: """ @@ -1836,9 +1931,8 @@

Source code for QEfficient.transformers.models.modeling_auto

``Mandatory`` Args: :processor: autoprocessor to process inputs and decode logits - :inputs (np.ndarray): inputs to run the execution. + :inputs (torch.Tensor): inputs to run the execution. :generation_len (int): length upto which to generate - :sample_rate (int): sampling rate at which input audio is stored in inputs (needed for processor) :device_id (List[int]): Ids of devices for running the qpc pass as [0] in case of normal model / [0, 1, 2, 3] in case of tensor slicing model Returns: :dict: Output from the ``AI_100`` or ``PyTorch`` runtime. @@ -1849,9 +1943,20 @@

Source code for QEfficient.transformers.models.modeling_auto

inputs = self.auto_correct_inputs(inputs) if self.qpc_session is None: - self.qpc_session = QAICInferenceSession(str(self.qpc_path), device_ids, enable_debug_logs=enable_debug_logs) + self.qpc_session = QAICInferenceSession(str(self.qpc_path), device_ids) self.batch_size = self.qpc_session.bindings[0].dims[0] + inputs["input_features"] = inputs["input_features"].numpy().astype(np.float32) + + # add start token id and initial position ids to inputs + seq_len = 1 + inputs["decoder_input_ids"] = ( + torch.ones((self.batch_size, seq_len), dtype=torch.int64) * self.model.config.decoder_start_token_id + ).numpy() + inputs["decoder_position_ids"] = ( + torch.arange(seq_len, dtype=torch.int64).view(1, seq_len).repeat(self.batch_size, 1).numpy() + ) + self.qpc_session.skip_buffers( [x for x in self.qpc_session.input_names + self.qpc_session.output_names if x.startswith("past_")] ) diff --git a/genindex.html b/genindex.html index ef155130b..94405b7c4 100644 --- a/genindex.html +++ b/genindex.html @@ -480,8 +480,9 @@

U

+ + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Docs

+

This directory contains the instructions for building static html documentations based on sphinx.

+
+

Build the docs

+

Install the packages required for building documentation:

+
 pip install -r docs/requirements.txt
+
+
+

And then, change directory to docs folder to build the docs.

+
cd docs/
+# To build docs specific to branch
+sphinx-build -M html . build
+# [Optional] To build docs for all the supporting branches
+sphinx-multiversion . build
+
+
+
+
+

Preview the docs locally

+
cd build/html
+python -m http.server
+
+
+

You can visit the page with your web browser with url http://localhost:8080.

+
+
+ + +
+
+ +
+
+
+
+
+ + Version: release/v1.19 + + +
+ Versions +
+
main
+
release/v1.18
+
release/v1.19
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/_images/Cloud_AI_100.png b/source/release/v1.19/_images/Cloud_AI_100.png new file mode 100644 index 000000000..54ab44309 Binary files /dev/null and b/source/release/v1.19/_images/Cloud_AI_100.png differ diff --git a/source/release/v1.19/_modules/QEfficient/compile/compile_helper.html b/source/release/v1.19/_modules/QEfficient/compile/compile_helper.html new file mode 100644 index 000000000..04173939d --- /dev/null +++ b/source/release/v1.19/_modules/QEfficient/compile/compile_helper.html @@ -0,0 +1,380 @@ + + + + + + QEfficient.compile.compile_helper — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for QEfficient.compile.compile_helper

+# -----------------------------------------------------------------------------
+#
+# Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# -----------------------------------------------------------------------------
+
+import json
+import os
+import shutil
+import subprocess
+import warnings
+from typing import List, Optional, Tuple
+
+from QEfficient.compile.qnn_compiler import compile as qnn_compile
+from QEfficient.utils.logging_utils import logger
+
+
+def create_and_dump_specializations(
+    batch_size: int, prompt_len: int, ctx_len: int, path: str, full_batch_size: Optional[int] = None
+):
+    # Create specialization file.
+    specializations = {
+        "specializations": [
+            {
+                "batch_size": str(batch_size),
+                "seq_len": str(prompt_len),
+                "ctx_len": str(ctx_len),
+            },
+            {"batch_size": str(batch_size), "seq_len": "1", "ctx_len": str(ctx_len)},
+        ]
+    }
+    # If continuous batching is enabled by proving full_batch_size we need to add FBS to the specialization file and update the batch size of decoder part to FBS
+    if full_batch_size is not None:
+        specializations["specializations"][0]["full_batch_size"] = str(full_batch_size)
+        specializations["specializations"][1]["full_batch_size"] = str(full_batch_size)
+        specializations["specializations"][1]["batch_size"] = str(full_batch_size)
+
+    # To handle repetative input in specializations when prompt_len is 1
+    if prompt_len == 1 and full_batch_size is None:
+        specializations["specializations"].pop()
+
+    # Dump
+    with open(path, "w") as file:
+        json.dump(specializations, file, indent=4)
+
+
+def compile_kv_model_on_cloud_ai_100(
+    onnx_path: str,
+    specializations_json: str,
+    num_cores: int,
+    base_path: str,
+    mxfp6: bool,
+    custom_io_path: str,
+    aic_enable_depth_first: bool,
+    allow_mxint8_mdp_io: bool,
+    mos: int = -1,
+    device_group: Optional[List[int]] = None,
+    **kwargs,
+) -> Tuple[bool, str]:
+    warnings.warn(
+        "\033[93mUse `QEFFAutoModelForCausalLM.compile` instead, this method will be removed soon.\033[0m",
+        DeprecationWarning,
+        stacklevel=2,
+    )
+    if kwargs:
+        # FIXME
+        raise NotImplementedError("Can't handle extra compilation args now!")
+    aic_binary_dir = os.path.join(base_path, "qpcs")
+
+    if os.path.isdir(aic_binary_dir):
+        shutil.rmtree(aic_binary_dir)
+
+    if not os.path.isfile(specializations_json):
+        raise FileNotFoundError(f"Please use 'QEfficient.compile', as {specializations_json} file was not found")
+    if not os.path.isfile(custom_io_path):
+        raise FileNotFoundError(f"{custom_io_path} file was not found!")
+    command = [
+        "/opt/qti-aic/exec/qaic-exec",
+        f"-m={onnx_path}",
+        "-aic-hw",
+        "-aic-hw-version=2.0",
+        f"-network-specialization-config={specializations_json}",
+        "-convert-to-fp16",
+        "-retained-state",
+        f"-aic-num-cores={num_cores}",
+        f"-custom-IO-list-file={custom_io_path}",
+        "-compile-only",
+        f"-aic-binary-dir={aic_binary_dir}",
+    ]
+    if mxfp6:
+        command.append("-mxfp6-matmul")
+    if mos > 0:
+        command.append(f"-mos={mos}")
+    if aic_enable_depth_first:
+        command.append("-aic-enable-depth-first")
+    if allow_mxint8_mdp_io:
+        command.append("-allow-mxint8-mdp-io")
+    if device_group is not None and len(device_group) > 1:
+        mdp_ts_config = {
+            "connections": [{"devices": list(range(len(device_group))), "type": "p2p"}],
+            "partitions": [
+                {
+                    "name": "Partition0",
+                    "devices": [{"deviceId": device, "numCores": num_cores} for device in range(len(device_group))],
+                }
+            ],
+        }
+        mdp_ts_config_path = os.path.join(base_path, "mdp_ts_config.json")
+        with open(mdp_ts_config_path, "w") as file:
+            json.dump(mdp_ts_config, file, indent=4)
+        command.append(f"-mdp-load-partition-config={mdp_ts_config_path}")
+    print("Running AI 100 compiler:", " ".join(command))
+    result = subprocess.run(command, capture_output=True, text=True)
+    if result.returncode != 0:
+        raise RuntimeError(f"Compilation Failed!!\n\nSTDOUT\n{result.stdout}\n\nSTDERR\n{result.stderr}")
+
+    print("\n===================== Compilation Done! =====================\n")
+    return result.returncode == 0, aic_binary_dir
+
+
+
[docs]def compile( + onnx_path: str, + qpc_path: str, + num_cores: int, + device_group: Optional[List[int]] = None, # FIXME: use num_devices instead + aic_enable_depth_first: bool = False, + mos: int = -1, + batch_size: int = 1, + prompt_len: int = 32, + ctx_len: int = 128, + mxfp6: bool = True, + mxint8: bool = False, + custom_io_file_path: Optional[str] = None, + full_batch_size: Optional[int] = None, + allow_mxint8_mdp_io: Optional[bool] = False, + enable_qnn: Optional[bool] = False, + qnn_config: Optional[str] = None, + **kwargs, +) -> str: + """ + Compiles the given ``ONNX`` model using Cloud AI 100 platform SDK compiler and saves the compiled ``qpc`` package at ``qpc_path``. + Generates tensor-slicing configuration if multiple devices are passed in ``device_group``. + + This function will be deprecated soon and will be replaced by ``QEFFAutoModelForCausalLM.compile``. + + ``Mandatory`` Args: + :onnx_path (str): Generated ``ONNX`` Model Path. + :qpc_path (str): Path for saving compiled qpc binaries. + :num_cores (int): Number of cores to compile the model on. + ``Optional`` Args: + :device_group (List[int]): Used for finding the number of devices to compile for. ``Defaults to None.`` + :aic_enable_depth_first (bool): Enables ``DFS`` with default memory size. ``Defaults to False.`` + :mos (int): Effort level to reduce the on-chip memory. ``Defaults to -1.`` + :batch_size (int): Batch size to compile the model for. ``Defaults to 1.`` + :full_batch_size (int): Set full batch size to enable continuous batching mode. ``Default to None`` + :prompt_len (int): Prompt length for the model to compile. ``Defaults to 32`` + :ctx_len (int): Maximum context length to compile the model. ``Defaults to 128`` + :mxfp6 (bool): Enable compilation for ``MXFP6`` precision. ``Defaults to True.`` + :mxint8 (bool): Compress Present/Past KV to ``MXINT8`` using ``CustomIO`` config. ``Defaults to False.`` + :custom_io_file_path (str): Path to ``customIO`` file (formatted as a string). ``Defaults to None.`` + :allow_mxint8_mdp_io (bool): Allows MXINT8 compression of MDP IO traffic ``Defaults to False.`` + :enable_qnn (bool): Enables QNN Compilation. ``Defaults to False.`` + :qnn_config (str): Path of QNN Config parameters file. ``Defaults to None.`` + + Returns: + :str: Path to compiled ``qpc`` package. + """ + if full_batch_size and batch_size != 1: + raise ValueError("Only either batch_size or full_batch_size should be greater than one") + + os.makedirs(qpc_path, exist_ok=True) + specialization_json_path = os.path.join(qpc_path, "specializations.json") + + create_and_dump_specializations( + batch_size=batch_size, + prompt_len=prompt_len, + ctx_len=ctx_len, + path=specialization_json_path, + full_batch_size=full_batch_size, + ) + + if enable_qnn: + qpc_path = qnn_compile( + onnx_path=onnx_path, + qpc_base_path=qpc_path, + num_cores=num_cores, + batch_size=batch_size, + prompt_len=prompt_len, + ctx_len=ctx_len, + mxfp6=mxfp6, + mxint8=mxint8, + allow_mxint8_mdp_io=allow_mxint8_mdp_io, + aic_enable_depth_first=aic_enable_depth_first, + mos=mos, + device_group=device_group, + full_batch_size=full_batch_size, + qnn_config=qnn_config, + ) + logger.info(f"QNN Compiled QPC files can be found here: {qpc_path}") + else: + # Select the customIO config based on the mx flag. + custom_io_file_name = "custom_io_int8.yaml" if mxint8 else "custom_io_fp16.yaml" + + if custom_io_file_path is None: + custom_io_file_path = os.path.join(os.path.dirname(onnx_path), custom_io_file_name) + + if not os.path.isfile(custom_io_file_path): + raise FileNotFoundError( + f"Custom IO file {custom_io_file_name} is not present at the expected path {custom_io_file_path}. Please pass the correct file path or rerun infer/export API" + ) + + _, qpc_path = compile_kv_model_on_cloud_ai_100( + onnx_path=onnx_path, + specializations_json=specialization_json_path, + num_cores=num_cores, + custom_io_path=custom_io_file_path, + base_path=qpc_path, + mxfp6=mxfp6, + aic_enable_depth_first=aic_enable_depth_first, + allow_mxint8_mdp_io=allow_mxint8_mdp_io, + mos=mos, + device_group=device_group, + ) + logger.info(f"Compiled QPC files can be found here: {qpc_path}") + return qpc_path
+
+ +
+
+ +
+
+
+
+
+ + Version: Main + + +
+ Versions +
+
main
+
release/v1.18
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/_modules/QEfficient/exporter/export_hf_to_cloud_ai_100.html b/source/release/v1.19/_modules/QEfficient/exporter/export_hf_to_cloud_ai_100.html new file mode 100644 index 000000000..e8ed0175b --- /dev/null +++ b/source/release/v1.19/_modules/QEfficient/exporter/export_hf_to_cloud_ai_100.html @@ -0,0 +1,580 @@ + + + + + + QEfficient.exporter.export_hf_to_cloud_ai_100 — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for QEfficient.exporter.export_hf_to_cloud_ai_100

+# -----------------------------------------------------------------------------
+#
+# Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# -----------------------------------------------------------------------------
+
+import os
+import shutil
+import warnings
+from typing import Optional, Tuple, Union
+
+import torch
+from transformers import PreTrainedTokenizer, PreTrainedTokenizerFast
+
+from QEfficient.base.common import QEFFCommonLoader
+from QEfficient.base.modeling_qeff import QEFFBaseModel
+from QEfficient.exporter.export_utils import export_onnx, fix_onnx_fp16, generate_input_files, run_model_on_ort
+from QEfficient.transformers.models.modeling_auto import QEFFAutoModelForCausalLM
+from QEfficient.utils import load_hf_tokenizer
+from QEfficient.utils.constants import QEFF_MODELS_DIR, Constants
+from QEfficient.utils.generate_inputs import InputHandler
+from QEfficient.utils.logging_utils import logger
+
+
+
[docs]def convert_to_cloud_bertstyle( + model_name: str, + qeff_model: QEFFAutoModelForCausalLM, + tokenizer: Union[PreTrainedTokenizer, PreTrainedTokenizerFast], + onnx_dir_path: str, + seq_len: int, +) -> str: + """ + API to convert model to Bertstyle approach. + Bertstyle Approach: + 1. No Prefill/Decode separably compiled. + 2. No KV retention logic. + 3. KV is every time computed for all the tokens until EOS/max_length. + + ``Mandatory`` Args: + :model_name (str): Hugging Face Model Card name, Example: `gpt2`. + :qeff_model (QEFFAutoModelForCausalLM): Transformed KV torch model to be used. + :tokenizer (Union[PreTrainedTokenizer, PreTrainedTokenizerFast]): Model tokenizer. + :onnx_dir_path (str): Path to save exported ONNX file. + :seq_len (int): The length of the sequence. + + Returns: + :str: Path of exported ``ONNX`` file. + """ + if os.path.exists(onnx_dir_path): + logger.warning(f"Overriding {onnx_dir_path}") + shutil.rmtree(onnx_dir_path) + + # Decide path for saving exported ONNX files. + model_name = export_bertstyle_model_to_onnx(model_name, qeff_model.model, tokenizer, onnx_dir_path, seq_len) # type: ignore + + # return the model path for automation. + return os.path.join(onnx_dir_path, f"{model_name}.onnx")
+ + +def export_bertstyle_model_to_onnx(model_name, model, tokenizer, onnx_dir_path, seq_len) -> str: + model_base_name = model_name.replace("/", "_") + "_bertstyle" + os.makedirs(onnx_dir_path, exist_ok=True) + + input_str = Constants.INPUT_STR + # Preprocess inputs + if seq_len > 0: + inputs = tokenizer( + input_str, + return_tensors="pt", + padding="max_length", + max_length=seq_len, + ) + else: + inputs = tokenizer(input_str, return_tensors="pt") + if model.config.is_encoder_decoder: + if "token_type_ids" in inputs: + inputs.pop("token_type_ids") + inputs["decoder_input_ids"] = torch.full((1, 1), model.generation_config.decoder_start_token_id) + + # Run PyTorch inference + try: + pt_outputs = model(**inputs) + output_names = list(pt_outputs.keys()) + except Exception as e: + print(f"Model {model_name} Execution failed in pytorch:%s", e) + + # Add pkv into output_names + pkv = tuple([(key.detach(), value.detach()) for key, value in pt_outputs.past_key_values]) + pkv_idx = output_names.index("past_key_values") + key_value_names = [f"past_{x}.{i}" for i in range(len(pkv)) for x in ["key", "value"]] + output_names[pkv_idx : pkv_idx + 1] = [x for x in key_value_names] + + pt_outputs = dict(pt_outputs) + pkv_out = pt_outputs.pop("past_key_values") + for i, (key, value) in enumerate(pkv_out): + pt_outputs[f"past_key.{i}"] = key + pt_outputs[f"past_value.{i}"] = value + + # Export the model to Onnx. + try: + model_name = export_onnx( + pt_model=model, + inputs=inputs, + output_names=output_names, + gen_models_path=onnx_dir_path, + model_base_name=model_base_name, + ) + except Exception as e: + print(f"Model {model_name} failed to export in Onnx:%s", e) + + # Run onnxrt inference + input_names, ort_outputs = run_model_on_ort( + onnx_path=os.path.join(onnx_dir_path, f"{model_name}.onnx"), + inputs=inputs, + output_names=output_names, + pt_outputs=pt_outputs, + ) + + # Fix onnx for fp16 + # Clip the values to fp16 ranges to avoid over/under flow in AI 100 + model_name = fix_onnx_fp16( + inputs=inputs, + output_names=output_names, + ort_outputs=ort_outputs, + gen_models_path=onnx_dir_path, + model_base_name=model_name, + pt_outputs=pt_outputs, + ) + + # Generate inputFiles + # todo(ochougul):rename to bert_style_input_list.txt + input_list_file = os.path.join(onnx_dir_path, "input_list.txt") + generate_input_files( + input_files_path=os.path.join(onnx_dir_path, "inputFiles"), + input_names=input_names, + inputs=inputs, + input_list_file=input_list_file, + ) + + return model_name + + +
[docs]def convert_to_cloud_kvstyle( + model_name: str, + qeff_model: QEFFAutoModelForCausalLM, + tokenizer: Union[PreTrainedTokenizer, PreTrainedTokenizerFast], + onnx_dir_path: str, + seq_len: int, +) -> str: + """ + API to convert model with kv retention and export to ONNX. + KV Style Approach- + 1. This architecture is particularly suitable for auto-regressive tasks. + 2. where sequence generation involves processing one token at a time. + 3. And contextual information from earlier tokens is crucial for predicting the next token. + 4. The inclusion of a kV cache enhances the efficiency of the decoding process, making it more computationally efficient. + + ``Mandatory`` Args: + :model_name (str): Hugging Face Model Card name, Example: `gpt2`. + :qeff_model (QEFFAutoModelForCausalLM): Transformed KV torch model to be used. + :tokenizer (Union[PreTrainedTokenizer, PreTrainedTokenizerFast]): Model tokenizer. + :onnx_dir_path (str): Path to save exported ONNX file. + :seq_len (int): The length of the sequence. + + Returns: + :str: Path of exported ``ONNX`` file. + """ + if os.path.exists(onnx_dir_path): + logger.warning(f"Overriding {onnx_dir_path}") + shutil.rmtree(onnx_dir_path) + + if not qeff_model.is_transformed: + raise Exception(f"please pass the {qeff_model.__class__.__name__} after transform API") + + # Decide path for saving exported ONNX files. + model_name = export_kvstyle_transformed_model_to_onnx( + model_name, qeff_model.model, tokenizer, onnx_dir_path, seq_len + ) # type: ignore + + # return the model path for automation. + return os.path.join(onnx_dir_path, f"{model_name}.onnx")
+ + +def export_kvstyle_transformed_model_to_onnx( + model_name: str, + transformed_model: torch.nn.Module, + tokenizer: Union[PreTrainedTokenizer, PreTrainedTokenizerFast], + onnx_dir_path: str, + seq_len: int, + full_batch_size: Optional[int] = None, +) -> str: + # Disabling requires_grad on all parameters + for _, p in enumerate(transformed_model.parameters()): + p.requires_grad_(False) + + if seq_len <= 0: + raise ValueError(f"Need seq_len to be greater than zero, got seq_len={seq_len}") + + # Preprocess inputs + # Build inputs for prefill + input_handler = InputHandler( + batch_size=len(Constants.INPUT_STR), + tokenizer=tokenizer, + config=transformed_model.config, + prompt=Constants.INPUT_STR, + prompt_len=Constants.PROMPT_LEN, + ctx_len=seq_len, + full_batch_size=full_batch_size, + ) + + inputs = input_handler.prepare_pytorch_inputs() + pt_outputs = transformed_model(**inputs) + output_names = list(pt_outputs.keys()) + + # Raise error if expected outputs are not present + if "logits" not in output_names: + raise KeyError("logits not found in output") + if "past_key_values" not in output_names: + raise KeyError("past_key_values not found in output") + + # Build inputs for next iteration from outputs + # Build inputs for decode + inputs = input_handler.update_pytorch_inputs(inputs, pt_outputs) + # To avoid issues in onnx export + inputs["position_ids"] = torch.full((full_batch_size if full_batch_size else 1, 1), seq_len - 1) + + # Run PyTorch inference with past + pt_outputs = transformed_model(**inputs) + output_names = list(pt_outputs.keys()) + + # Add pkv into output_names + pkv = inputs["past_key_values"] + pkv_idx = output_names.index("past_key_values") + key_value_names = [f"past_{x}.{i}" for i in range(len(pkv)) for x in ["key", "value"]] + output_names[pkv_idx : pkv_idx + 1] = [x + "_RetainedState" for x in key_value_names] + + # Replace nested past_key_values outputs with separate KV tensors + pt_outputs = dict(pt_outputs) + pkv_out = pt_outputs.pop("past_key_values") + for i, (key, value) in enumerate(pkv_out): + pt_outputs[f"past_key.{i}_RetainedState"] = key + pt_outputs[f"past_value.{i}_RetainedState"] = value + + model_base_name = model_name.replace("/", "_") + "_kv" + os.makedirs(onnx_dir_path, exist_ok=True) + + # Export and simplify ONNX model + model_name = export_onnx( + pt_model=transformed_model, + inputs=inputs, + output_names=output_names, + gen_models_path=onnx_dir_path, + model_base_name=model_base_name, + ) + + # Replace nested past_key_values inputs with separate KV tensors + inputs.pop("past_key_values") + for i, (key, value) in enumerate(pkv): + inputs[f"past_key.{i}"] = key + inputs[f"past_value.{i}"] = value + + # Run onnxrt inference + input_names, ort_outputs = run_model_on_ort( + onnx_path=os.path.join(onnx_dir_path, f"{model_name}.onnx"), + inputs=inputs, + output_names=output_names, + pt_outputs=pt_outputs, + ) + + model_name = fix_onnx_fp16( + inputs=inputs, + output_names=output_names, + ort_outputs=ort_outputs, + gen_models_path=onnx_dir_path, + model_base_name=model_name, + pt_outputs=pt_outputs, + ) + + # Generate custom-IO files for fp16 and int8 kv + with open(os.path.join(onnx_dir_path, "custom_io_fp16.yaml"), "w") as fp: + fp.write("# Model Inputs\n\n") + for input_name in key_value_names: + fp.write(f" - IOName: {input_name}\n Precision: float16\n\n") + inputs[input_name] = inputs[input_name].to(torch.float16) + fp.write("# Model Outputs\n\n") + for output_name in key_value_names: + fp.write(f" - IOName: {output_name}_RetainedState\n Precision: float16\n\n") + + with open(os.path.join(onnx_dir_path, "custom_io_int8.yaml"), "w") as fp: + fp.write("# Model Inputs\n\n") + for input_name in key_value_names: + fp.write(f" - IOName: {input_name}\n Precision: mxint8\n\n") + fp.write("# Model Outputs\n\n") + for output_name in key_value_names: + fp.write(f" - IOName: {output_name}_RetainedState\n Precision: mxint8\n\n") + + # Generate inputFiles + input_list_file = os.path.join(onnx_dir_path, "input_list.txt") + generate_input_files( + input_files_path=os.path.join(onnx_dir_path, "inputFiles"), + input_names=input_names, + inputs=inputs, + input_list_file=input_list_file, + ) + + return model_name + + +def export_lm_model_for_cloud( + model_name: str, + qeff_model: QEFFAutoModelForCausalLM, + tokenizer: Union[PreTrainedTokenizer, PreTrainedTokenizerFast], + onnx_dir_path: str, + seq_length: int, + full_batch_size: Optional[int] = None, +) -> str: + if os.path.exists(onnx_dir_path): + logger.warning(f"Overriding {onnx_dir_path}") + shutil.rmtree(onnx_dir_path) + + model_name = export_kvstyle_transformed_model_to_onnx( + model_name=model_name, + transformed_model=qeff_model.model, + tokenizer=tokenizer, + onnx_dir_path=onnx_dir_path, + seq_len=seq_length, + full_batch_size=full_batch_size, + ) + return os.path.join(onnx_dir_path, f"{model_name}.onnx") + + +
[docs]def qualcomm_efficient_converter( + model_name: str, + model_kv: QEFFBaseModel = None, # type: ignore + local_model_dir: Optional[str] = None, + tokenizer: Optional[Union[PreTrainedTokenizer, PreTrainedTokenizerFast]] = None, + cache_dir: Optional[str] = None, + onnx_dir_path: Optional[str] = None, + hf_token: Optional[str] = None, + seq_length: int = Constants.SEQ_LEN, + kv: bool = True, + form_factor: str = "cloud", + full_batch_size: Optional[int] = None, +) -> Tuple[str, str]: + """ + This method is an alias for ``QEfficient.export``. + + Usage 1: This method can be used by passing ``model_name`` and ``local_model_dir`` or ``cache_dir`` if required for loading from local dir. + This will download the model from ``HuggingFace`` and export it to ``ONNX`` graph and returns generated files path check below. + + Usage 2: You can pass ``model_name`` and ``model_kv`` as an object of ``QEfficient.QEFFAutoModelForCausalLM``, In this case will directly export the ``model_kv.model`` to ``ONNX`` + + We will be deprecating this function and it will be replaced by ``QEFFAutoModelForCausalLM.export``. + + ``Mandatory`` Args: + :model_name (str): The name of the model to be used. + ``Optional`` Args: + :model_kv (torch.nn.Module): Transformed ``KV torch model`` to be used. ``Defaults to None``. + :local_model_dir (str): Path of local model. ``Defaults to None``. + :tokenizer (Union[PreTrainedTokenizer, PreTrainedTokenizerFast]): Model tokenizer. ``Defaults to None``. + :cache_dir (str): Path of the ``cache`` directory. ``Defaults to None``. + :onnx_dir_path (str): Path to store ``ONNX`` file. ``Defaults to None``. + :hf_token (str): HuggingFace token to access gated models. ``Defaults is None``. + :seq_len (int): The length of the sequence. ``Defaults is 128``. + :kv (bool): If false, it will export to Bert style. ``Defaults is True``. + :form_factor (str): Form factor of the hardware, currently only ``cloud`` is accepted. ``Defaults to cloud``. + + Returns: + :Tuple[str, str]: Path to Base ``ONNX`` dir and path to generated ``ONNX`` model + + .. code-block:: python + + import QEfficient + base_path, onnx_model_path = QEfficient.export(model_name="gpt2") + + """ + warnings.warn( + "\033[93m`qualcomm_efficient_converter` method will be deprecated soon, use `QEFFAutoModelForCausalLM.export` instead\033[0m", + DeprecationWarning, + stacklevel=2, + ) + + # Get model_kv first + model_kv = ( + model_kv + if model_kv + else QEFFCommonLoader.from_pretrained( + pretrained_model_name_or_path=(local_model_dir if local_model_dir else model_name), + hf_token=hf_token, + cache_dir=cache_dir, + full_batch_size=full_batch_size, + ) + ) + + if onnx_dir_path is None: + model_card_dir = os.path.join(QEFF_MODELS_DIR, str(model_name)) + onnx_dir_path = os.path.join(model_card_dir, "onnx") + os.makedirs(onnx_dir_path, exist_ok=True) + + # Load tokenizer if not passed + tokenizer = ( + tokenizer + if tokenizer + else load_hf_tokenizer( + pretrained_model_name_or_path=(local_model_dir if local_model_dir else model_name), + hf_token=hf_token, + cache_dir=cache_dir, + ) + ) + + if form_factor == "cloud": + generated_onnx_model_path = export_lm_model_for_cloud( + model_name=model_name, + qeff_model=model_kv, + tokenizer=tokenizer, + onnx_dir_path=onnx_dir_path, + seq_length=seq_length, + full_batch_size=full_batch_size, + ) + return onnx_dir_path, generated_onnx_model_path + else: + # [TODO]: Apply the class transformation to make changes for the KV models in edge use cases + # model = QEfficient.transform(model_hf, type="Transformers", form_factor="edge") + # model.eval() + raise NotImplementedError("Oops! Reached too far!!")
+
+ +
+
+ +
+
+
+
+
+ + Version: Main + + +
+ Versions +
+
main
+
release/v1.18
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/_modules/QEfficient/generation/text_generation_inference.html b/source/release/v1.19/_modules/QEfficient/generation/text_generation_inference.html new file mode 100644 index 000000000..6959d114a --- /dev/null +++ b/source/release/v1.19/_modules/QEfficient/generation/text_generation_inference.html @@ -0,0 +1,1279 @@ + + + + + + QEfficient.generation.text_generation_inference — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for QEfficient.generation.text_generation_inference

+# -----------------------------------------------------------------------------
+#
+# Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# -----------------------------------------------------------------------------
+
+import json
+import os
+from collections import deque
+from dataclasses import dataclass
+from time import perf_counter
+from typing import Dict, List, Optional, Tuple, Union
+
+import numpy as np
+import transformers
+from transformers import PreTrainedTokenizer, PreTrainedTokenizerFast
+
+from QEfficient.generation.cloud_infer import QAICInferenceSession
+from QEfficient.utils import padding_check_and_fix
+from QEfficient.utils.logging_utils import logger
+
+
+
[docs]@dataclass +class PerfMetrics: + """ + Holds all performance metrics + + Args: + :prefill_time (float): Time for prefilling. + :decode_perf (float): Decoding performance. + :total_perf (float): Total performance. + :total_time (float): Total time. + """ + + prefill_time: float + decode_perf: float + total_perf: float + total_time: float
+ + +
[docs]@dataclass +class CloudAI100ExecInfo: + """ + Holds all the information about Cloud AI 100 execution + + Args: + :batch_size (int): Batch size of the QPC compilation. + :generated_texts (Union[List[List[str]], List[str]]): Generated text(s). + :generated_ids (Union[List[np.ndarray], np.ndarray]): Generated IDs. + :perf_metrics (PerfMetrics): Performance metrics. + """ + + batch_size: int + generated_texts: Union[List[str], List[List[str]]] + generated_ids: Union[List[np.ndarray], np.ndarray] + perf_metrics: PerfMetrics + + def __repr__(self): + return f"Average Prefill time a.k.a TTFT is= {round(self.perf_metrics.prefill_time, 2)} sec\ + \nDecode is= {round(self.perf_metrics.decode_perf * self.batch_size, 2)} tokens/sec\ + \nTotal is= {round(self.perf_metrics.total_perf * self.batch_size, 2)} tokens/sec\ + \nTotal (E2E) inference time is= {round(self.perf_metrics.total_time, 2)} tokens/sec"
+ + +
[docs]@dataclass +class CloudAI100ExecInfoNew: + batch_size: int + generated_ids: Union[List[np.ndarray], np.ndarray] + perf_metrics: PerfMetrics + + def __repr__(self): + return f"Average Prefill time a.k.a TTFT is= {round(self.perf_metrics.prefill_time, 2)} sec\ + \nDecode is= {round(self.perf_metrics.decode_perf * self.batch_size, 2)} token/sec\ + \nTotal is= {round(self.perf_metrics.total_perf * self.batch_size, 2)} token/sec\ + \nTotal (E2E) inference time is= {round(self.perf_metrics.total_time, 2)} sec"
+ + +io_files = [] + + +def write_io_files( + inputs: Dict[str, np.ndarray], + outputs: Dict[str, np.ndarray], + write_io_dir: str, + write_io_subdir: str, + write_io_name: str, + include_dims: bool = False, + reset: bool = False, +): + global io_files + if reset: + io_files = [] + io = [] + os.makedirs(f"{write_io_dir}/{write_io_subdir}", exist_ok=True) + for iname, i_array in inputs.items(): + i_array.tofile(f"{write_io_dir}/{write_io_subdir}/{iname}.raw") + i_spec = { + "path": f"{write_io_subdir}/{iname}.raw", + "io-direction": "in", + "elem-size": i_array.itemsize, + "map-to": iname, + } + if include_dims: + i_spec["dims"] = i_array.shape + io.append(i_spec) + for o_name, o_array in outputs.items(): + o_array.tofile(f"{write_io_dir}/{write_io_subdir}/{o_name}.raw") + o_spec = { + "path": f"{write_io_subdir}/{o_name}.raw", + "io-direction": "out", + "elem-size": o_array.itemsize, + "map-to": o_name, + } + if include_dims or o_name.endswith("_RetainedState"): + o_spec["dims"] = o_array.shape + io.append(o_spec) + io_files.append(io) + with open(f"{write_io_dir}/{write_io_name}.json", "w") as fp: + json.dump({"IO-files": io_files}, fp, indent=True) + + +def latency_stats_bertstyle( + model_name: str, + qpc_path: str, + seq_len: int, + prompt: str, + device_id: Optional[List[int]] = None, +): + """ + Function to execute Bertstyle ONNX model on Cloud AI 100. + + Args: + :model_name (str): Hugging Face Model Card name, Example: gpt2. + :qpc_path (str): Path to save generated binary file after compilation. + :seq_len (int): Sequence length. + :prompt (str): Sample prompt for the model text generation. + :device_id (List[int]): Device Ids to be used for compilation. If devices > 1, it enables multiple card setup. + """ + session = QAICInferenceSession(qpc_path, device_id) + tokenizer = transformers.AutoTokenizer.from_pretrained(model_name, padding_side="left") + padding_check_and_fix(tokenizer) # Check and fix tokenizer viability + inputs = tokenizer(prompt, return_tensors="np", max_length=seq_len, padding="max_length") + next_token_id = inputs["input_ids"][0, -1] + cur_len = inputs["attention_mask"].sum().item() + print(prompt, end=" ", flush=True) + init_len = cur_len + start = perf_counter() + while next_token_id != tokenizer.eos_token_id and cur_len <= seq_len: + outputs = session.run(inputs) + logits = outputs["logits"] + next_token_id = logits[0, -1].argmax().item() + inputs["input_ids"] = np.concatenate( + [ + inputs["input_ids"][:, 1:], + np.ones((1, 1), dtype=np.int64) * next_token_id, + ], + 1, + ) + inputs["attention_mask"] = np.concatenate([inputs["attention_mask"][:, 1:], np.ones((1, 1), dtype=np.int64)], 1) + print(tokenizer.decode(next_token_id), end=" ", flush=True) + cur_len += 1 + end = perf_counter() + print() + print(round((cur_len - init_len) / (end - start), 2), "tok/s") + + +
[docs]def get_compilation_dims(qpc_path: str) -> Tuple[int, int, Optional[int]]: + """ + Function to fetch compilation dimensions from specializations.json. + Uses qpc path to compute path to specializations.json. + + Args: + qpc_path (str): Path to directory comprising generated binary file after compilation. + + Returns: + :tuple: compilation batch size, compilation context length, compilation full batch size + """ + qpc_base_path = os.path.dirname(os.path.normpath(qpc_path)) + specialization_file_path = os.path.join(qpc_base_path, "specializations.json") + logger.info(f"specialization_file_path : {specialization_file_path}") + + if os.path.exists(specialization_file_path): + with open(specialization_file_path, "r") as file: + data = json.load(file) + else: + raise FileNotFoundError(f"expected specializations.json file at path, {qpc_base_path}") + + compilation_batch_size = int(data["specializations"][0]["batch_size"]) + compilation_ctx_len = int(data["specializations"][0]["ctx_len"]) + if compilation_fbs := data["specializations"][0].get("full_batch_size", None): + compilation_fbs = int(compilation_fbs) + return compilation_batch_size, compilation_ctx_len, compilation_fbs
+ + +def get_input_prompts(prompt: str, prompts_txt_file_path: str) -> List[str]: + if prompt is None and prompts_txt_file_path is None: + raise ValueError("Please pass at least one argument either using --prompt or --prompts_txt_file_path") + if prompts_txt_file_path is not None: + if prompt is not None: + logger.warning("Found inputs passed using txt file as well as CLI, taking inputs from given txt file") + prompt = read_prompts_txt_file(prompts_txt_file_path) + if isinstance(prompt, str): + prompt = [prompt] + return prompt + + +
[docs]def fix_prompts(prompt: List[str], batch_size: int, full_batch_size: int = None): + """ + Adjusts the list of prompts to match the required batch size. + + ``Mandatory`` Args: + prompt (List[str]): List of input prompts. + batch_size (int): The batch size to process at a time. + + ``Optional`` Args: + full_batch_size (Optional[int]): The full batch size if different from batch_size. + + Returns: + List[str]: Adjusted list of prompts. + """ + exec_batch_size = full_batch_size if full_batch_size is not None else batch_size + + if len(prompt) < exec_batch_size: + logger.warning("Number of prompts are less than batch size/full batch size, repeating to required batch size") + prompt = (prompt * (exec_batch_size // len(prompt) + 1))[:exec_batch_size] + elif full_batch_size is None and len(prompt) % batch_size != 0: + logger.warning( + "Number of prompts are not multiple of batch size, dropping last incomplete batch from given input prompts" + ) + prompt = prompt[: batch_size * (len(prompt) // batch_size)] + + return prompt
+ + +
[docs]def fix_prompt_to_lora_id_mapping(prompt_to_lora_id_mapping: List[int], batch_size: int, full_batch_size: int = None): + """ + Adjusts the list of prompt_to_lora_id_mapping to match the required batch size. + + ``Mandatory`` Args: + prompt_to_lora_id_mapping (Optional[List[int]]): Mapping to associate prompts with their respective LoRA adapter. + batch_size (int): The batch size to process at a time. + + ``Optional`` Args: + full_batch_size (Optional[int]): The full batch size if different from batch_size. + + Returns: + List[int]: Adjusted list of prompt_to_lora_id_mapping. + """ + exec_batch_size = full_batch_size if full_batch_size is not None else batch_size + + if len(prompt_to_lora_id_mapping) < exec_batch_size: + logger.warning( + "Prompt_to_lora_id_mapping are less than batch size/full batch size, repeating to required batch size" + ) + prompt_to_lora_id_mapping = ( + prompt_to_lora_id_mapping * (exec_batch_size // len(prompt_to_lora_id_mapping) + 1) + )[:exec_batch_size] + elif full_batch_size is None and len(prompt_to_lora_id_mapping) % batch_size != 0: + logger.warning( + "prompt_to_lora_id_mapping are not multiple of batch size, dropping last incomplete batch from given input prompts" + ) + prompt_to_lora_id_mapping = prompt_to_lora_id_mapping[ + : batch_size * (len(prompt_to_lora_id_mapping) // batch_size) + ] + + return prompt_to_lora_id_mapping
+ + +def read_prompts_txt_file(prompts_txt_file_path: str): + prompt = [] + with open(prompts_txt_file_path, "r") as file: + for line in file: + prompt.append(line.strip()) + return prompt + + +def print_latency_stats_kv(prompt, exec_info, automation: bool = False): + if automation: + print("input=", prompt) + print("output=", exec_info.generated_texts) + print(exec_info) + return + print("\n========================= Performance Stats =========================") + if exec_info.batch_size > 1: + print("Batch Performance : \n") + print(exec_info) + print("=====================================================================") + + +
[docs]def calculate_latency(total_decoded_tokens, loop_start, start, end, decode_pause_time=0): + """ + Method will calculate the latency metrics using the time loops and based on the total decoded token count. + + Args: + :total_decoded_tokens (int): Number of tokens generated in decode stage. + :loop_start (float): Start time of decode loop. + :start (float): Start time. + :end (float): End time. + :decode_pause_time (float): Total decode pause time in continuous batching decode stage. + + Returns: + :tuple: prefill time, decode performance, total performance, total time + """ + prefill_time = loop_start - start + decode_pause_time + decode_perf = (total_decoded_tokens) / (end - loop_start - decode_pause_time) + total_perf = (total_decoded_tokens) / (end - start) + total_time = end - start + return prefill_time, decode_perf, total_perf, total_time
+ + +
[docs]def cloud_ai_100_exec_kv( + tokenizer: Union[PreTrainedTokenizer, PreTrainedTokenizerFast], + qpc_path: str, + prompt: Optional[str] = None, + prompts_txt_file_path: Optional[str] = None, + device_id: Optional[List[int]] = None, + generation_len: Optional[int] = None, + enable_debug_logs: bool = False, + stream: bool = True, + write_io_dir: Optional[str] = None, + automation=False, + prompt_to_lora_id_mapping: Optional[List[int]] = None, + is_tlm: bool = False, +): + """ + This method generates output until ``eos`` or ``generation_len`` by executing the compiled ``qpc`` on ``Cloud AI 100`` Hardware cards. + This is a sequential execution based on the ``batch_size`` of the compiled model and the number of prompts passed. + If the number of prompts cannot be divided by the ``batch_size``, the last unfulfilled batch will be dropped. + + ``Mandatory`` Args: + :tokenizer (Union[PreTrainedTokenizer, PreTrainedTokenizerFast]): Model tokenizer. + :qpc_path (str): Path to the saved generated binary file after compilation. + + ``Optional`` Args: + :prompt (str): Sample prompt for the model text generation. ``Defaults to None``. + :prompts_txt_file_path (str): Path of the prompt text file. ``Defaults to None``. + :generation_len (int): Maximum context length for the model during compilation. ``Defaults to None``. + :device_id (List[int]): Device IDs to be used for execution. If ``len(device_id) > 1``, it enables multiple card setup. If ``None``, auto-device-picker will be used. ``Defaults to None``. + :enable_debug_logs (bool): If True, it enables debugging logs. ``Defaults to False``. + :stream (bool): If True, enable streamer, which returns tokens one by one as the model generates them. ``Defaults to True``. + :Write_io_dir (str): Path to write the input and output files. ``Defaults to None``. + :automation (bool): If true, it prints input, output, and performance stats. ``Defaults to False``. + :prompt_to_lora_id_mapping (List[int]): Mapping to associate prompts with their respective LoRA adapter. + + Returns: + :CloudAI100ExecInfo: Object holding execution output and performance details. + + .. code-block:: python + + import transformers + import QEfficient + base_path, onnx_model_path = QEfficient.export(model_name="gpt2") + qpc_path = QEfficient.compile(onnx_path=onnx_model_path, qpc_path=os.path.join(base_path, "qpc"), num_cores=14, device_group=[0]) + tokenizer = transformers.AutoTokenizer.from_pretrained("gpt2") + exec_info = QEfficient.cloud_ai_100_exec_kv(tokenizer=tokenizer, qpc_path=qpc_path, prompt="Hi there!!", device_id=[0]) + + """ + batch_size, ctx_len, full_batch_size = get_compilation_dims(qpc_path) + prompt: List[str] = get_input_prompts(prompt, prompts_txt_file_path) + prompt = fix_prompts(prompt, batch_size, full_batch_size) + if prompt_to_lora_id_mapping is not None: + prompt_to_lora_id_mapping = fix_prompt_to_lora_id_mapping( + prompt_to_lora_id_mapping, batch_size, full_batch_size + ) + generate_text = TextGeneration( + tokenizer=tokenizer, + qpc_path=qpc_path, + device_id=device_id, + ctx_len=ctx_len, + enable_debug_logs=enable_debug_logs, + write_io_dir=write_io_dir, + full_batch_size=full_batch_size, + is_tlm=is_tlm, + ) + if full_batch_size is None: + exec_info = [ + generate_text.generate(prompt[i : i + batch_size], generation_len, stream, prompt_to_lora_id_mapping) + for i in range(0, len(prompt), batch_size) + ] + prefill_time = np.average([info.perf_metrics.prefill_time for info in exec_info]) + decode_perf = np.average([info.perf_metrics.decode_perf for info in exec_info]) + total_perf = np.average([info.perf_metrics.total_perf for info in exec_info]) + total_time = np.average([info.perf_metrics.total_time for info in exec_info]) + generated_texts = [info.generated_texts for info in exec_info] + generated_ids = [info.generated_ids for info in exec_info] + + exec_info = CloudAI100ExecInfo( + batch_size=batch_size, + generated_texts=generated_texts, + generated_ids=generated_ids, + perf_metrics=PerfMetrics(prefill_time, decode_perf, total_perf, total_time), + ) + else: + exec_info = generate_text.generate( + prompt=prompt, generation_len=generation_len, prompt_to_lora_id_mapping=prompt_to_lora_id_mapping + ) + + print_latency_stats_kv(prompt, exec_info=exec_info, automation=automation) + return exec_info
+ + +class QEffTextGenerationBase: + def __init__( + self, + tokenizer: Union[PreTrainedTokenizer, PreTrainedTokenizerFast], + qpc_path: str, + full_batch_size: Optional[int] = None, + ctx_len: Optional[int] = None, + device_id: Optional[List[int]] = None, + enable_debug_logs: bool = False, + write_io_dir: Optional[str] = None, + is_tlm: Optional[int] = None, + ) -> None: + self._ctx_len = ctx_len + self._write_io_dir = write_io_dir + self.is_tlm = is_tlm + + # Load QPC + self._session = QAICInferenceSession(qpc_path, device_id, enable_debug_logs=enable_debug_logs) + + # Fetch the variables from the QPC + self._vocab_size = self._fetch_vocab_size() # Fetch Vocab size + self.batch_size, self._prefill_seq_len = self._fetch_batch_size_prefill_seq_len() + self._decode_seq_len = self._fetch_decode_seq_len() + self.full_batch_size = ( + full_batch_size if full_batch_size else self._fetch_full_batch_size() + ) # Check and fetch full batch size if CB is enabled + + # Initialize the storage variables. + self.batch_index = None + + # Variables to be re-initialized for every run + # These parameters will be initialized in initialize_lora_id_mapping method + self._prompt_to_lora_id_mapping_prefill = None + self._prompt_to_lora_id_mapping_decode = None + # These parameters will be initialized to np arrays in initialize_decode_inputs method + self.generated_ids = None + self.decode_input_ids = None + self.decode_pos_ids = None + self.generation_len = None + + self.tokenizer = tokenizer + self._set_tokenizer_params() # set tokenizer params + # Skip inputs/outputs + self._session.skip_buffers( + [x for x in self._session.input_names + self._session.output_names if x.startswith("past_")] + ) + + def _set_tokenizer_params(self): + """ + Sets the tokenizer parameters for the model. + """ + if self.tokenizer.padding_side != "right": + logger.warning("Please use padding_side='right' while initializing the tokenizer") + self.tokenizer.padding_side = "right" + if self.tokenizer.pad_token_id is None: + self.tokenizer.pad_token_id = self.tokenizer.eos_token_id + + def _fetch_full_batch_size( + self, + ): + """ + Fetches the full batch size from the session's bindings or allowed shapes. + + Returns: + full_batch_size: The full batch size fetched from the session's bindings or allowed shapes. If "batch_index" is not + in the session's binding index map, full_batch_size will be None. + + """ + full_batch_size = None + if "batch_index" in self._session.binding_index_map: + if self._session.allowed_shapes: + full_batch_size, _ = [ + x[self._session.binding_index_map["batch_index"]][1][0] for x in self._session.allowed_shapes + ] + else: + full_batch_size, _ = self._session.bindings[self._session.binding_index_map["batch_index"]].dims + return full_batch_size + + def _fetch_batch_size_prefill_seq_len( + self, + ): + """ + Fetches the batch size and prefill sequence length from the session's bindings or allowed shapes. + + Returns: + batch_size: The batch size fetched from the session's bindings or allowed shapes. + prefill_seq_len: The prefill sequence length fetched from the session's bindings or allowed shapes. + """ + if self._session.allowed_shapes: + batch_size = max( + [x[self._session.binding_index_map["input_ids"]][1][0] for x in self._session.allowed_shapes] + ) + prefill_seq_len = max( + [x[self._session.binding_index_map["input_ids"]][1][1] for x in self._session.allowed_shapes] + ) + else: + batch_size, prefill_seq_len = self._session.bindings[self._session.binding_index_map["input_ids"]].dims + return batch_size, prefill_seq_len + + def _fetch_decode_seq_len( + self, + ): + """ + Fetches the decode sequence length from the session's bindings or allowed shapes. + + Returns: + decode_seq_len: The decode sequence length fetched from the session's bindings or allowed shapes. + """ + decode_seq_len = None + if self._session.allowed_shapes: + decode_seq_len = min( + [x[self._session.binding_index_map["input_ids"]][1][1] for x in self._session.allowed_shapes] + ) + return decode_seq_len + + def _fetch_vocab_size( + self, + ): + """ + Fetches the vocabulary size from the session's allowed shapes. + Returns: + vocab_size: The vocabulary size fetched from the session's allowed shapes. + """ + if self._session.allowed_shapes: + return [x[self._session.binding_index_map["logits"]] for x in self._session.allowed_shapes][0][1][2] + + return self._session.bindings[self._session.binding_index_map["logits"]].dims[2] + + def _fetch_generation_len(self, generation_len, max_gen_len): + """ + Fetches the generation length for the model. + Args: + generation_len: The generation length provided. If None, the method uses max_gen_len. + max_gen_len: The maximum allowed generation length. + + Returns: + generation_len: The final generation length, which is either the provided generation_len (if it is not None and not greater than max_gen_len) or max_gen_len. + """ + + if generation_len is None: + if self._ctx_len is None: + raise ValueError("At least one of ctx_len or generation_len is needed") + generation_len = max_gen_len + elif generation_len > max_gen_len: + logger.warning( + "Passed generation_len is greater than allowed length. " + "Make sure this model supports sliding window, such as Mistral" + ) + if generation_len <= 0: + raise ValueError("generation length should be greater than zero") + return generation_len + + def prepare_decode_inputs(self): + """ + This function creates the decode inputs. + + Returns: + dict: The decode inputs. + """ + batch_size = self.full_batch_size if self.full_batch_size is not None else self.batch_size + decode_inputs = {} + if self.is_tlm: + position_ids = np.full((batch_size, self._decode_seq_len), -1, dtype=np.int64) + position_ids[:, -1] = self.decode_pos_ids.flatten() + input_ids = np.zeros((batch_size, self._decode_seq_len), dtype=np.int64) + input_ids[:, -1] = self.decode_input_ids.flatten() + decode_inputs["input_ids"] = input_ids + decode_inputs["position_ids"] = position_ids + decode_inputs["num_logits_to_keep"] = np.zeros((self._decode_seq_len, 1)) + else: + decode_inputs["input_ids"] = self.decode_input_ids + decode_inputs["position_ids"] = self.decode_pos_ids + if self.batch_index is not None: + decode_inputs["batch_index"] = self.batch_index + + if self._prompt_to_lora_id_mapping_decode: + if self.full_batch_size: + first_batch_lora_ids = [self._prompt_to_lora_id_mapping_decode[i] for i in range(self.full_batch_size)] + decode_inputs["lora_ids"] = np.array(first_batch_lora_ids, dtype=np.int64).reshape( + self.full_batch_size, 1 + ) + else: + batch_lora_ids = [self._prompt_to_lora_id_mapping_decode.popleft() for i in range(self.batch_size)] + decode_inputs["lora_ids"] = np.array(batch_lora_ids, dtype=np.int64).reshape(self.batch_size, 1) + + return decode_inputs + + def _fetch_next_token_id(self, outputs): + """ + Fetches the next token ID from the model's output logits. + The method identifies the token with the highest probability using argmax along the last dimension. + Args: + outputs (dict): A dictionary containing the model's output logits. The key "logits" should map to a numpy array of shape (batch_size, sequence_length, vocab_size) or (batch_size, vocab_size). + + Returns: + numpy.ndarray: An array of the next token IDs for each sequence in the batch. + """ + logits = outputs["logits"] + if len(logits.shape) == 2: + logits = np.expand_dims(logits, 1) + + # Get output token + next_token_id = logits.argmax(2) + return next_token_id + + def initialize_decode_inputs(self, num_prompts, execution_batch_size, max_gen_length): + """ + Initialize np arrays for storing the prefill output for all the decode batch size. + """ + self.generated_ids = np.full((num_prompts, max_gen_length), self.tokenizer.pad_token_id) + self.decode_input_ids = np.zeros((execution_batch_size, 1), np.int64) + self.decode_pos_ids = np.zeros((execution_batch_size, 1), np.int64) + self.generation_len = np.zeros((execution_batch_size, 1), np.int64) + + def initialize_lora_id_mapping(self, prompt_to_lora_id_mapping): + """ + Initializes the LoRA ID mapping for prefill and decode phases. + + Args: + prompt_to_lora_id_mapping (list): An iterable containing the mapping of prompts to LoRA IDs. + + Sets: + self._prompt_to_lora_id_mapping_prefill (deque): A deque containing the prompt to LoRA ID mapping for the prefill phase. + self._prompt_to_lora_id_mapping_decode (iterable or deque): The prompt to LoRA ID mapping for the decode phase. If full_batch_size is set, it uses the original iterable; otherwise, it converts it to a deque. + """ + self._prompt_to_lora_id_mapping_prefill = deque(prompt_to_lora_id_mapping) + if self.full_batch_size: + self._prompt_to_lora_id_mapping_decode = prompt_to_lora_id_mapping + else: + self._prompt_to_lora_id_mapping_decode = deque(prompt_to_lora_id_mapping) + + def update_decode_input(self, outputs, position_ids, generation_len, decode_batch_id=None): + """ + Updates the decode input with the generated values. + Args: + outputs (dict): The outputs of the model. + position_ids (array): The position IDs. + generation_len (int): The generation length. + decode_batch_id (int, optional): The decode batch ID. If None, all values are updated. Defaults to None. + + Returns: + next_token_id (array): The next token ID. + """ + next_token_id = self._fetch_next_token_id(outputs) + + # Store the generated values. + self.decode_input_ids[decode_batch_id or slice(None)] = next_token_id + self.decode_pos_ids[decode_batch_id or slice(None)] = position_ids + self.generated_ids[decode_batch_id or slice(None), 0] = next_token_id.squeeze(1) + self.generation_len[decode_batch_id or slice(None)] = generation_len + return next_token_id + + def run_prefill_for_all_inputs(self, prompt_queue, generation_len): + """ + Runs prefill for all inputs in the prompt queue and updates the decode input. + + Method iterates over the full batch size and for each decode batch ID, it pops the next prompt from the queue. It then runs prefill for the next prompt and updates the decode input with the outputs. + + Args: + prompt_queue (deque): The queue of prompts. + generation_len (int): The generation length. + + """ + for decode_batch_id in range(self.full_batch_size): + next_prompt = prompt_queue.popleft() + + # run prefill for num_chunks + outputs, position_ids, generation_len = self.run_prefill( + next_prompt, generation_len, decode_batch_id=np.array(decode_batch_id, dtype=np.int64).reshape(1, 1) + ) + + _ = self.update_decode_input(outputs, position_ids, generation_len, decode_batch_id) + + def run_prefill(self, prompt, generation_len, prefill_logit_bs=1, decode_batch_id=None): + """ + Runs prefill for a given prompt and generation length. + + This method tokenize the prompt and calculates the padded length and number of chunks. Calculates the + maximum generation length and fetches the generation length. If a batch index for prefill is provided, it sets the batch index in the inputs. The method then runs prefill for each chunk and updates the inputs and outputs. + + Args: + prompt (str): The prompt for which to run prefill. + generation_len (int): The generation length. + prefill_logit_bs (int, optional): The prefill logit batch size. Defaults to 1. + + Returns: + outputs (dict): The outputs of the prefill. + position_ids (array): The position IDs. + generation_len (int): The generation length. + """ + # Run prefill + inputs = self.tokenizer(prompt, return_tensors="np", padding=True) + position_ids = inputs["attention_mask"].sum(1, keepdims=True) + padded_len = inputs["input_ids"].shape[1] + num_chunks = -(padded_len // -self._prefill_seq_len) # ceil divide without float + padded_len = num_chunks * self._prefill_seq_len # Convert to a multiple of prompt_len + + # Initialize variables specific to request + # Calculate the max generation length. + max_gen_len = self._ctx_len - position_ids.max() + generation_len = self._fetch_generation_len(generation_len, max_gen_len) + + # Set the prefill logic buffer + logits_out_placeholder = np.zeros((prefill_logit_bs, 1, self._vocab_size), dtype=np.float32) + self._session.set_buffers({"logits": logits_out_placeholder}) + + inputs = self.tokenizer(prompt, return_tensors="np", padding="max_length", max_length=padded_len) + inputs["position_ids"] = np.where(inputs.pop("attention_mask"), np.arange(padded_len), -1) + inputs.pop("token_type_ids", None) + + if decode_batch_id is not None: + inputs["batch_index"] = decode_batch_id + if self.is_tlm: + inputs["num_logits_to_keep"] = np.zeros((1, 1)) + + if self._prompt_to_lora_id_mapping_prefill: + if self.full_batch_size: + inputs["lora_ids"] = np.array( + self._prompt_to_lora_id_mapping_prefill.popleft(), dtype=np.int64 + ).reshape(1, 1) + else: + batch_lora_ids = [self._prompt_to_lora_id_mapping_prefill.popleft() for i in range(self.batch_size)] + inputs["lora_ids"] = np.array(batch_lora_ids, dtype=np.int64).reshape(self.batch_size, 1) + + for i in range(num_chunks): + chunk_inputs = inputs.copy() + chunk_inputs["input_ids"] = inputs["input_ids"][ + :, i * self._prefill_seq_len : (i + 1) * self._prefill_seq_len + ] + chunk_inputs["position_ids"] = inputs["position_ids"][ + :, i * self._prefill_seq_len : (i + 1) * self._prefill_seq_len + ] + outputs = self._session.run(chunk_inputs) + if self._write_io_dir is not None: + write_io_files(inputs, outputs, self._write_io_dir, "prefill", "aic_batch_io", True, False) + return ( + outputs, + position_ids, + generation_len, + ) + + def run_continuous_batching_decode(self, prompt_queue, generation_len): + """ + Runs continuous batching decode for the given prompt queue and generation length. + + Method sets up the initial conditions for decoding and preparing the decode inputs. Then enters a loop that continues as long as there are prompts in the queue or any decoding is ongoing. In each iteration of the loop, it runs the session with the current decode inputs, prepares the inputs for the next iteration and updates the decode inputs. If a prompt has been fully decoded, it runs prefill for the next prompt in the queue if available. + + Args: + prompt_queue (deque): The queue of prompts to be decoded. + generation_len (int): The generation length. + + """ + + # Set logits placeholder for decode + logits_out_placeholder = np.zeros( + (self.full_batch_size, self._decode_seq_len, self._vocab_size), dtype=np.float32 + ) + self._session.set_buffers({"logits": logits_out_placeholder}) + # Generate flag for tracking progress for each batch ID + current_decode_ongoing = np.full((self.full_batch_size, 1), True) + + # Generate an array for maintaining the tokens generated in each batch ID + generated_id_current_index = np.ones((self.full_batch_size, 1), np.int64) + + # Generate a batch ID map for mapping the batch ID if input > full_batch_size. + # This ID map will be used for storing all generated tokens + batch_id_map = {i: i for i in range(self.full_batch_size)} + decode_pause_time = 0 + # Prepare decode inputs inputs. + decode_inputs = self.prepare_decode_inputs() + + while prompt_queue or current_decode_ongoing.any(): + outputs = self._session.run(decode_inputs) + + # Prepare inputs for next iteration + logits = outputs["logits"] + if len(logits.shape) == 2: + logits = np.expand_dims(logits, 1) + next_token_id = logits.argmax(2) + + for decode_batch_id in range(self.full_batch_size): + if ( + next_token_id[decode_batch_id, -1] == self.tokenizer.eos_token_id + or generated_id_current_index[decode_batch_id] >= self.generation_len[decode_batch_id] + ): + if prompt_queue: + start = perf_counter() + # run prefill for next prompt input. + outputs, position_ids, generation_len = self.run_prefill( + prompt_queue.popleft(), + generation_len, + decode_batch_id=np.array(decode_batch_id, dtype=np.int64).reshape(1, 1), + ) + + new_token_id = self.update_decode_input(outputs, position_ids, generation_len, decode_batch_id) + + batch_id_map[decode_batch_id] = max(batch_id_map.values()) + 1 + self.generated_ids[batch_id_map[decode_batch_id], 0] = new_token_id.squeeze(1) + generated_id_current_index[decode_batch_id] = 1 + + self._session.set_buffers({"logits": logits_out_placeholder}) + decode_pause_time += perf_counter() - start + + if self._prompt_to_lora_id_mapping_decode: + decode_inputs["lora_ids"][decode_batch_id] = self._prompt_to_lora_id_mapping_decode[ + batch_id_map[decode_batch_id] + ] + + else: + current_decode_ongoing[decode_batch_id] = False + else: + # If the generated sequence is valid and within generation len prepare for next decode + decode_inputs["input_ids"][decode_batch_id, -1] = next_token_id[decode_batch_id, -1] + decode_inputs["position_ids"][decode_batch_id, -1] += 1 + self.generated_ids[batch_id_map[decode_batch_id], generated_id_current_index[decode_batch_id]] = ( + next_token_id[decode_batch_id, -1] + ) + + generated_id_current_index[decode_batch_id] += 1 + + return decode_pause_time + + def run_decode(self, decode_inputs, generation_len, streamer: Optional[transformers.TextStreamer] = None): + """ + Default method for running decode. Executes the decoding process for a given set of inputs and a specified generation length. + + Enters a loop that continues until all sequences are finished or the maximum generation length is reached. In each iteration, it runs the session with the decode inputs, prepares the inputs for the next iteration and checks if all sequences are finished. + + Args: + decode_inputs (dict): The initial inputs for decoding. This should be a dictionary containing 'input_ids' and 'position_ids'. + generation_len (int): Max allowed length for generating tokens. The decoding process will be terminated when generation length is reached. + streamer (transformers.TextStreamer): TextStreamer object to print decoded tokens to console. + Returns: + num_token (int): The number of tokens processed in the decoding process. + """ + if self.is_tlm: + logits_out_placeholder = np.zeros( + (self.batch_size, self._decode_seq_len, self._vocab_size), dtype=np.float32 + ) + self._session.set_buffers({"logits": logits_out_placeholder}) + finished_sequences = decode_inputs["input_ids"] == self.tokenizer.eos_token_id + num_token = 0 + for num_token in range(1, generation_len): + if streamer: + streamer.put(decode_inputs["input_ids"][0]) + outputs = self._session.run(decode_inputs) + + if self._write_io_dir is not None: + write_io_files(decode_inputs, outputs, self._write_io_dir, "decode", "aic_batch_io", True, False) + self._write_io_dir = None + + # Prepare inputs for next iteration + decode_inputs["input_ids"] = outputs["logits"].argmax(2) + decode_inputs["position_ids"][:, -1] += 1 + self.generated_ids[:, num_token] = decode_inputs["input_ids"][:, -1] + finished_sequences |= decode_inputs["input_ids"] == self.tokenizer.eos_token_id + + if finished_sequences.all(): + break + return num_token + + def generate_decode_stream(self, decode_inputs, generation_len): + """ + Generator method for yielding decode tokens. Executes the decoding process for a given set of inputs and a specified generation length. + + Enters a loop that continues until all sequences are finished or the maximum generation length is reached. In each iteration, it runs the session with the decode inputs, prepares the inputs for the next iteration and checks if all sequences are finished. + + Args: + decode_inputs (dict): The initial inputs for decoding. This should be a dictionary containing 'input_ids' and 'position_ids'. + generation_len (int): Max allowed length for generating tokens. The decoding process will be terminated when generation length is reached. + + Yields: + token_id (int): The token generated in the decoding process. + """ + finished_sequences = decode_inputs["input_ids"] == self.tokenizer.eos_token_id + for num_token in range(1, generation_len): + yield decode_inputs["input_ids"] + outputs = self._session.run(decode_inputs) + + if self._write_io_dir is not None: + write_io_files(decode_inputs, outputs, self._write_io_dir, "decode", "aic_batch_io", True, False) + self._write_io_dir = None + + # Prepare inputs for next iteration + decode_inputs["input_ids"] = outputs["logits"].argmax(2) + decode_inputs["position_ids"] += 1 + self.generated_ids[:, num_token] = decode_inputs["input_ids"].squeeze(1) + finished_sequences |= decode_inputs["input_ids"] == self.tokenizer.eos_token_id + + if finished_sequences.all(): + break + yield decode_inputs["input_ids"] # yield the last token + + +class TextGeneration: + def __init__( + self, + tokenizer: Union[PreTrainedTokenizer, PreTrainedTokenizerFast], + qpc_path: str, + full_batch_size: Optional[int] = None, + ctx_len: Optional[int] = None, + device_id: Optional[List[int]] = None, + enable_debug_logs: bool = False, + write_io_dir: Optional[str] = None, + is_tlm: bool = False, + ) -> None: + self._qaic_model = QEffTextGenerationBase( + tokenizer, qpc_path, full_batch_size, ctx_len, device_id, enable_debug_logs, write_io_dir, is_tlm + ) + self._full_batch_size = self._qaic_model.full_batch_size + self._tokenizer = self._qaic_model.tokenizer + self._ctx_len = ctx_len + self._perf_metrics = None + self._prompt_queue = None + self._text_streamer = None + + @property + def perf_metrics(self): + return self._perf_metrics + + def _setup_model_execution_inputs( + self, + prompt: List[str], + generation_len: Optional[int] = None, + prompt_to_lora_id_mapping: Optional[List[int]] = None, + ): + """ + This method should be called to set/reset inputs + Args: + :prompt (List[str]): prompts for the model text generation + :generation_len (Optional[int], optional): Number of tokens to be generated. + :prompt_to_lora_id_mapping (Optional[List[int]], optional): Mapping to associate prompts with their respective LoRA adapter. + """ + execution_batch_size = ( + self._full_batch_size if self._full_batch_size is not None else self._qaic_model.batch_size + ) + max_gen_length = self._ctx_len if not generation_len else max(self._ctx_len, generation_len) + + # Create a prompt queue. + self._prompt_queue = deque(prompt) + # Initialize np arrays for storing the prefill output for all the decode batch size. + num_prompts = len(self._prompt_queue) + + if prompt_to_lora_id_mapping: + self._qaic_model.initialize_lora_id_mapping(prompt_to_lora_id_mapping) + + self._qaic_model.initialize_decode_inputs(num_prompts, execution_batch_size, max_gen_length) + + def _regular_model_execution( + self, + prompt: List[str], + generation_len: Optional[int] = None, + stream: Optional[bool] = True, + prompt_to_lora_id_mapping: Optional[List[int]] = None, + ): + """ + Executes the model in regular mode. + This method runs the prefill, prepares the decode inputs, and then runs the decode. The generated texts are decoded and optionally streamed. Latency metrics are calculated and returned. + Args: + :prompt (List[str]): The list of prompts for the model. + :generation_len (Optional[int], optional): The generation length. + :stream (Optional[bool], optional): Boolean flag to enable stream output to console. + :prompt_to_lora_id_mapping (Optional[List[int]], optional): Mapping to associate prompts with their respective LoRA adapter. + + Returns: + :tuple: A tuple containing performance metrics and generated texts. + + """ + self._setup_model_execution_inputs(prompt, generation_len, prompt_to_lora_id_mapping) + if stream and self._text_streamer is None: + self._text_streamer = transformers.TextStreamer(self._tokenizer) + start = perf_counter() + outputs, position_ids, generation_len = self._qaic_model.run_prefill( + prompt, generation_len, prefill_logit_bs=self._qaic_model.batch_size + ) + self._qaic_model.update_decode_input(outputs, position_ids, generation_len) + + decode_inputs = self._qaic_model.prepare_decode_inputs() + + loop_start = perf_counter() # Start decode loop timer + num_token = self._qaic_model.run_decode(decode_inputs, generation_len, self._text_streamer) + end = perf_counter() + generated_texts = self._tokenizer.batch_decode(self._qaic_model.generated_ids, skip_special_tokens=True) + + total_decode_tokens = num_token + prefill_time, decode_perf, total_perf, total_time = calculate_latency( + total_decode_tokens, loop_start, start, end + ) + self._perf_metrics = PerfMetrics(prefill_time, decode_perf, total_perf, total_time) + return self._perf_metrics, generated_texts + + def _continuous_batching_execution( + self, + prompt: List[str], + generation_len: Optional[int] = None, + prompt_to_lora_id_mapping: Optional[List[int]] = None, + ): + """ + Executes the model using continuous batching. + This method handles the execution of the model when continuous batching is enabled. It runs the prefill step for all inputs, performs continuous batching decode, and then decodes the generated texts. The texts are optionally streamed. Latency metrics are calculated and returned. + + Args: + :prompt (List[str]): The list of prompts for the model. + :generation_len (Optional[int], optional): The generation length. + :prompt_to_lora_id_mapping (Optional[List[int]], optional): Mapping to associate prompts with their respective LoRA adapter. + + Returns: + :tuple: A tuple containing performance metrics and generated texts. + """ + self._setup_model_execution_inputs(prompt, generation_len, prompt_to_lora_id_mapping) + self._qaic_model.batch_index = np.arange(self._full_batch_size).reshape(-1, 1) + start = perf_counter() + self._qaic_model.run_prefill_for_all_inputs(self._prompt_queue, generation_len) + + loop_start = perf_counter() # Start decode loop timer + decode_pause_time = self._qaic_model.run_continuous_batching_decode(self._prompt_queue, generation_len) + end = perf_counter() + + generated_texts = self._tokenizer.batch_decode(self._qaic_model.generated_ids, skip_special_tokens=True) + + total_decode_tokens = sum( + np.sum(self._qaic_model.generated_ids[i] != self._tokenizer.pad_token_id) - 1 for i in range(len(prompt)) + ) + prefill_time, decode_perf, total_perf, total_time = calculate_latency( + total_decode_tokens, loop_start, start, end, decode_pause_time + ) + prefill_time /= len(prompt) # Average prefill time for continuous batching + self._perf_metrics = PerfMetrics(prefill_time, decode_perf, total_perf, total_time) + return self._perf_metrics, generated_texts + + def generate_stream_tokens( + self, + prompt: List[str], + generation_len: Optional[int] = None, + prompt_to_lora_id_mapping: Optional[List[int]] = None, + ): + """ + Executes the model for a given list of prompts and a specified generation length. + This method runs the prefill, prepares the decode inputs, and then runs the decode. The tokens are decoded and streamed as they are generated. Latency metrics are calculated and can be retrieved + after all tokens are streamed. + + Args: + :prompt (List[str]): The list of prompts for the model. + :generation_len (Optional[int], optional): The generation length. + :prompt_to_lora_id_mapping (Optional[List[int]], optional): Mapping to associate prompts with their respective LoRA adapter. + + Yields: + :list: A list containing decoded tokens corresponding to each index of batch size. + + """ + if self._full_batch_size is not None: + raise NotImplementedError("Streaming tokens is currently unavailable for continuous batch execution.") + self._setup_model_execution_inputs(prompt, generation_len, prompt_to_lora_id_mapping) + start = perf_counter() + outputs, position_ids, generation_len = self._qaic_model.run_prefill( + prompt, generation_len, prefill_logit_bs=self._qaic_model.batch_size + ) + self._qaic_model.update_decode_input(outputs, position_ids, generation_len) + + decode_inputs = self._qaic_model.prepare_decode_inputs() + + loop_start = perf_counter() # Start decode loop timer + num_token = 0 + for token_id in self._qaic_model.generate_decode_stream(decode_inputs, generation_len): + decoded_tokens = [] + for idx in range(self._qaic_model.batch_size): + decoded_tokens.append(self._tokenizer.decode(token_id[idx], skip_special_tokens=True)) + yield decoded_tokens + num_token += 1 + end = perf_counter() + + total_decode_tokens = num_token + prefill_time, decode_perf, total_perf, total_time = calculate_latency( + total_decode_tokens, loop_start, start, end + ) + self._perf_metrics = PerfMetrics(prefill_time, decode_perf, total_perf, total_time) + + def generate( + self, + prompt: List[str], + generation_len: Optional[int] = None, + stream: bool = True, + prompt_to_lora_id_mapping: Optional[List[int]] = None, + ): + """ + Executes the model for a given list of prompts and a specified generation length. + + Args: + prompt (List[str]): The list of prompts for the model. + generation_len (Optional[int], optional): The generation length. + stream (Optional[bool], optional): Boolean flag to enable stream output to console. + prompt_to_lora_id_mapping (Optional[List[int]], optional): Mapping to associate prompts with their respective LoRA adapter. + Returns: + latency_stats (tuple): A tuple containing the generated texts, performance metrics. + """ + + if self._full_batch_size is not None: + logger.warning("Streamer is currently unavailable for continuous batch execution.") + perf_metrics, generated_texts = self._continuous_batching_execution( + prompt, generation_len, prompt_to_lora_id_mapping + ) + else: + if stream: + print("\nPrompt : " + prompt[0] + "\nCompletion :", flush=True, end="") + perf_metrics, generated_texts = self._regular_model_execution( + prompt, generation_len, stream, prompt_to_lora_id_mapping + ) + + if stream: + stream_start = 0 if self._full_batch_size else 1 + stream_end = len(prompt) if self._full_batch_size else self._qaic_model.batch_size + for i in range(stream_start, stream_end): + print("\n" + "-" * 20) + print("\nPrompt : ", prompt[i]) + print("Completion : ", generated_texts[i]) + + latency_stats = CloudAI100ExecInfo( + batch_size=1 if self._full_batch_size else self._qaic_model.batch_size, + generated_texts=generated_texts, + generated_ids=self._qaic_model.generated_ids, + perf_metrics=perf_metrics, + ) + return latency_stats +
+ +
+
+ +
+
+
+
+
+ + Version: Main + + +
+ Versions +
+
main
+
release/v1.18
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/_modules/QEfficient/peft/auto.html b/source/release/v1.19/_modules/QEfficient/peft/auto.html new file mode 100644 index 000000000..7ffff5112 --- /dev/null +++ b/source/release/v1.19/_modules/QEfficient/peft/auto.html @@ -0,0 +1,523 @@ + + + + + + QEfficient.peft.auto — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for QEfficient.peft.auto

+# -----------------------------------------------------------------------------
+#
+# Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# ----------------------------------------------------------------------------
+
+import hashlib
+import logging
+import warnings
+from typing import List, Optional, Union
+
+import numpy as np
+import torch
+from peft import AutoPeftModelForCausalLM, PeftConfig, PeftModelForCausalLM, load_peft_weights
+from torch import nn
+from transformers import GenerationConfig, StoppingCriteria, StoppingCriteriaList
+from transformers.generation.streamers import BaseStreamer
+
+from QEfficient.base.modeling_qeff import QEFFBaseModel
+from QEfficient.base.onnx_transforms import FP16ClipTransform, OnnxTransform, SplitTensorsTransform
+from QEfficient.base.pytorch_transforms import PytorchTransform
+from QEfficient.generation.cloud_infer import QAICInferenceSession
+from QEfficient.peft.lora import QEffAutoLoraModelForCausalLM
+from QEfficient.peft.onnx_transforms import AdapterWeightsToInputsTransform
+from QEfficient.peft.pytorch_transforms import PeftModelInputsTransform
+from QEfficient.transformers.models.pytorch_transforms import CustomOpsTransform, KVCacheTransform
+from QEfficient.utils import constants
+from QEfficient.utils._utils import get_padding_shape_from_config
+from QEfficient.utils.cache import to_hashable
+
+logger = logging.getLogger(__name__)
+
+
+
[docs]class QEffAutoPeftModelForCausalLM(QEFFBaseModel): + """ + QEff class for loading models with PEFT adapters (Only LoRA is supported currently). + Once exported and compiled for an adapter, the same can be utilized for another adapter with same base model and adapter config. + + Args: + :model (nn.Module): PyTorch model + + .. code-block:: python + + from QEfficient import QEffAutoPeftModelForCausalLM + + m = QEffAutoPeftModelForCausalLM.from_pretrained("predibase/magicoder", "magicoder") + m.export() + m.compile(prefill_seq_len=32, ctx_len=1024) + + inputs = ... # A coding prompt + outputs = m.generate(**inputs) + + inputs = ... # A math prompt + m.load_adapter("predibase/gsm8k", "gsm8k") + m.set_adapter("gsm8k") + outputs = m.generate(**inputs) + """ + + _pytorch_transforms: List[PytorchTransform] = [CustomOpsTransform, KVCacheTransform, PeftModelInputsTransform] + _onnx_transforms: List[OnnxTransform] = [FP16ClipTransform, AdapterWeightsToInputsTransform, SplitTensorsTransform] + _hf_auto_class = AutoPeftModelForCausalLM + + def __init__(self, model: nn.Module): + if not isinstance(model, PeftModelForCausalLM): + raise TypeError(f"Required pytorch module of type PeftModel, got {type(model)}") + + if model.active_peft_config.peft_type != "LORA": + raise NotImplementedError("Only LoRA models are supported") + + super().__init__(model) + + self.num_layers = model.config.num_hidden_layers + self.exported_peft_config = None + self.adapter_weights = { + adapter_name: { + name.replace(f".{adapter_name}.weight", ".weight"): param.detach().numpy().astype("float16") + for name, param in model.named_parameters() + if name.endswith(f".{adapter_name}.weight") + } + for adapter_name in model.peft_config + } + + def __repr__(self) -> str: + return self.__class__.__name__ + "\n" + self.model.__repr__() + + @property + def model_name(self) -> str: + mname = self.model.get_base_model().__class__.__name__ + "-lora" + if mname.startswith("QEff"): + mname = mname[4:] + return mname + + @property + def model_hash(self) -> str: + # NOTE: model_config.to_diff_dict() has "_name_or_path" attribute which is the model card name or path. + # Using same card name will result in same hash. But, using a relative path for one run and + # absolute path for another run will result in different hash. + # The added complexity to resolve different paths to same location is not worth pursuing. + # Instead, advise the user to always provide same relative paths or absolute paths for local models. + + # Compute the hash with: model_config, peft_config, transforms + mhash = hashlib.sha256() + mhash.update(to_hashable(self.model.get_base_model().config.to_diff_dict())) + mhash.update(to_hashable(self.model.active_peft_config.to_dict())) + mhash.update(to_hashable(self._transform_names())) + mhash = mhash.hexdigest()[:16] + return mhash + + @property + def get_model_config(self) -> dict: + return self.model.get_base_model().config.__dict__ + +
[docs] def load_adapter(self, model_id: str, adapter_name: str): + """Loads a new adapter from huggingface hub or local path + + Args: + :model_id (str): Adapter model ID from huggingface hub or local path + :adapter_name (str): Adapter name to be used to set this adapter as current + """ + self.model.load_adapter(model_id, adapter_name) + self.adapter_weights[adapter_name] = { + k: v.numpy().astype("float16") for k, v in load_peft_weights(model_id).items() + }
+ + @property + def active_adapter(self) -> str: + "Currently active adapter to be used for inference" + return self.model.active_adapter + +
[docs] def set_adapter(self, adapter_name: str): + "Sets active adapter from one of the loaded adapters" + if self.exported_peft_config is not None and self.exported_peft_config != self.model.peft_config[adapter_name]: + raise ValueError( + "Unable to activate incompatible adapter. " + "Use an adapter compatible with export-time adapter " + "or re-export with this adapter" + ) + self.model.set_adapter(adapter_name)
+ + def disable_adapter(self): + # TODO: Set zero tensors as adapter weights + raise NotImplementedError("Disabling adapters not supported currently") + + @classmethod + def _from_pretrained(cls, pretrained_name_or_path: str, *args, **kwargs): + # Base class + model = cls._hf_auto_class.from_pretrained(pretrained_name_or_path, *args, **kwargs) + return cls(model) + +
[docs] @classmethod + def from_pretrained(cls, pretrained_name_or_path: str, *args, **kwargs): + """ + Args: + :pretrained_name_or_path (str): Model card name from huggingface or local path to model directory. + :finite_adapters (bool): set True to enable finite adapter mode with QEffAutoLoraModelForCausalLM class. Please refer to QEffAutoLoraModelForCausalLM for API specification. + :adapter_name (str): Name used to identify loaded adapter. + :args, kwargs: Additional arguments to pass to peft.AutoPeftModelForCausalLM. + """ + if kwargs.get("full_batch_size"): + raise NotImplementedError("Continuous batching currently not supported for PEFT models") + if kwargs.get("use_cache") is False: + warnings.warn("Overriding to use_cache=True") + kwargs["use_cache"] = True + + if kwargs.pop("finite_adapters", False): # initialize through finite_adapters class + obj = QEffAutoLoraModelForCausalLM.from_pretrained( + pretrained_model_name_or_path=PeftConfig.from_pretrained( + pretrained_name_or_path + ).base_model_name_or_path, + **kwargs, + ) + if adapter_name := kwargs.pop("adapter_name", None): + obj.load_adapter(pretrained_name_or_path, adapter_name=adapter_name) + return obj + if len(args) == 0 or not isinstance(list(args)[0], str): + raise TypeError("Required adapter name argument in string format") + obj.load_adapter(pretrained_name_or_path, list(args)[0]) + else: + obj = cls._from_pretrained(pretrained_name_or_path, *args, **kwargs) + return obj
+ +
[docs] def export(self, export_dir: Optional[str] = None) -> str: + self.exported_peft_config = self.model.active_peft_config + + example_shape = (constants.ONNX_EXPORT_EXAMPLE_BATCH_SIZE, constants.ONNX_EXPORT_EXAMPLE_SEQ_LEN) + kv_cache_shape = get_padding_shape_from_config(self.model.config, *example_shape) + example_inputs = { + "input_ids": torch.zeros(example_shape, dtype=torch.int64), + "position_ids": torch.arange(example_shape[1], dtype=torch.int64).view(example_shape), + "past_key_values": [[] for _ in range(self.num_layers)], + } + dynamic_axes = { + "input_ids": {0: "batch_size", 1: "seq_len"}, + "position_ids": {0: "batch_size", 1: "seq_len"}, + } + output_names = ["logits"] + for i in range(self.num_layers): + for kv in ["key", "value"]: + example_inputs["past_key_values"][i].append(torch.zeros(kv_cache_shape, dtype=torch.float32)) + dynamic_axes[f"past_{kv}.{i}"] = {0: "batch_size", 2: "ctx_len"} + output_names.append(f"past_{kv}.{i}_RetainedState") + + return self._export( + example_inputs, + output_names, + dynamic_axes, + export_kwargs={"do_constant_folding": False}, # To avoid merging adapter weights with base weights + onnx_transform_kwargs={"adapter_name": self.model.active_adapter}, + export_dir=export_dir, + )
+ +
[docs] def compile( + self, + onnx_path: Optional[str] = None, + compile_dir: Optional[str] = None, + *, + batch_size: int = 1, + prefill_seq_len: int, + ctx_len: int, + num_devices: int = 1, + num_cores: int = 16, + mxfp6_matmul: bool = False, + mxint8_kv_cache: bool = False, + **compiler_options, + ) -> str: + # Specializations + specializations = [ + {"batch_size": batch_size, "seq_len": prefill_seq_len, "ctx_len": ctx_len}, + {"batch_size": batch_size, "seq_len": 1, "ctx_len": ctx_len}, + ] + + # Custom IO + custom_io = {} + kv_cache_dtype = "mxint8" if mxint8_kv_cache else "float16" + for suffix in ["", "_RetainedState"]: + for i in range(self.num_layers): + for kv in ["key", "value"]: + custom_io[f"past_{kv}.{i}{suffix}"] = kv_cache_dtype + for weight_name in self.adapter_weights[self.active_adapter]: + custom_io[f"{weight_name}{suffix}"] = "float16" + + return self._compile( + onnx_path, + compile_dir, + compile_only=True, + retained_state=True, + specializations=specializations, + convert_to_fp16=True, + mxfp6_matmul=mxfp6_matmul, + custom_io=custom_io, + mdp_ts_num_devices=num_devices, + aic_num_cores=num_cores, + **compiler_options, + )
+ +
[docs] def generate( + self, + inputs: Optional[Union[torch.Tensor, np.ndarray]] = None, + device_ids: Optional[List[int]] = None, + generation_config: Optional[GenerationConfig] = None, + stopping_criteria: Optional[StoppingCriteria] = None, + streamer: Optional[BaseStreamer] = None, + **kwargs, + ) -> np.ndarray: + """ + Generate tokens from compiled binary. This method takes same parameters as HuggingFace transformers model.generate() method. + + Args: + :inputs: input_ids + :generation_config: Merge this generation_config with model-specific for the current generation. + :stopping_criteria: Pass custom stopping_criteria to stop at a specific point in generation. + :streamer: Streamer to put the generated tokens into. + :kwargs: Additional parameters for generation_config or to be passed to the model while generating. + """ + # Initialize session + if self.qpc_session is None: + if self.qpc_path is None: + raise FileNotFoundError("Please compile the model with `model.compile(...)`") + self.qpc_session = QAICInferenceSession(str(self.qpc_path), device_ids) + + # Skip buffers + retained_buffers = [x for x in self.qpc_session.output_names if x.endswith("_RetainedState")] + self.qpc_session.skip_buffers([x[: -len("_RetainedState")] for x in retained_buffers]) + self.qpc_session.skip_buffers(retained_buffers) + + generation_config = generation_config or self.model.generation_config + generation_config, model_kwargs = self.model._prepare_generation_config(generation_config, **kwargs) + self.model._prepare_special_tokens(generation_config) + if generation_config.do_sample: + raise NotImplementedError("do_sample=True not supported currently") + if generation_config.num_beams > 1: + raise NotImplementedError("num_beams>1 not supported currently") + if generation_config.max_new_tokens is None or generation_config.max_new_tokens <= 0: + raise ValueError("Required max_new_tokens>0 value in generation_config") + + stopping_criteria = stopping_criteria or StoppingCriteriaList() + stopping_criteria = self.model._get_stopping_criteria(generation_config, stopping_criteria) + + if inputs is not None: + inputs = {"input_ids": inputs} + else: + inputs = {} + inputs.update(model_kwargs) + inputs = {k: v.numpy() if isinstance(v, torch.Tensor) else v for k, v in inputs.items()} + + batch_size = max( + [x[self.qpc_session.binding_index_map["input_ids"]][1][0] for x in self.qpc_session.allowed_shapes] + + [self.qpc_session.bindings[self.qpc_session.binding_index_map["input_ids"]].dims[0]] + ) + passed_batch_size = inputs["input_ids"].shape[0] + if passed_batch_size != batch_size: + raise ValueError(f"Model compiled for batch_size: {batch_size}, but passed batch_size: {passed_batch_size}") + + prefill_seq_len = max( + [x[self.qpc_session.binding_index_map["input_ids"]][1][1] for x in self.qpc_session.allowed_shapes] + + [self.qpc_session.bindings[self.qpc_session.binding_index_map["input_ids"]].dims[1]] + ) + + input_len = inputs["input_ids"].shape[1] + num_chunks = -(input_len // -prefill_seq_len) # Ceil divide without float + padded_len = num_chunks * prefill_seq_len # Convert to a multiple of prompt_len + inputs["input_ids"] = np.concatenate( + [inputs["input_ids"], np.zeros((batch_size, padded_len - input_len), dtype=inputs["input_ids"].dtype)], 1 + ) + next_position_ids = inputs.pop("attention_mask").sum(1, keepdims=True) + inputs["position_ids"] = np.arange(padded_len).reshape(1, -1) + inputs["position_ids"] = np.where(inputs["position_ids"] < next_position_ids, inputs["position_ids"], -1) + generated_ids = np.zeros((batch_size, generation_config.max_new_tokens), dtype="int64") + if streamer: + streamer.put(inputs["input_ids"][:, :input_len]) + + # Set adapter weights + self.qpc_session.set_buffers(self.adapter_weights[self.active_adapter]) + + # Run prefill + for i in range(num_chunks): + chunk_inputs = inputs.copy() + chunk_inputs["input_ids"] = inputs["input_ids"][:, i * prefill_seq_len : (i + 1) * prefill_seq_len] + chunk_inputs["position_ids"] = inputs["position_ids"][:, i * prefill_seq_len : (i + 1) * prefill_seq_len] + outputs = self.qpc_session.run(chunk_inputs) + + # Get first token + inputs["input_ids"] = outputs["logits"].argmax(2) + inputs["position_ids"] = next_position_ids + generated_ids[:, 0] = inputs["input_ids"].squeeze(1) + if streamer: + streamer.put(inputs["input_ids"]) + + # Skip adapter weights + self.qpc_session.skip_buffers(list(self.adapter_weights[self.active_adapter])) + + # Decode loop + for num_token in range(1, generation_config.max_new_tokens): + if stopping_criteria(torch.from_numpy(inputs["input_ids"]), torch.from_numpy(outputs["logits"])).all(): + break + + outputs = self.qpc_session.run(inputs) + + # Prepare inputs for next iteration + inputs["input_ids"] = outputs["logits"].argmax(2) + inputs["position_ids"] += 1 + generated_ids[:, num_token] = inputs["input_ids"].squeeze(1) + if streamer: + streamer.put(inputs["input_ids"]) + + if streamer: + streamer.end() + return generated_ids
+
+ +
+
+ +
+
+
+
+
+ + Version: Main + + +
+ Versions +
+
main
+
release/v1.18
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/_modules/QEfficient/peft/lora/auto.html b/source/release/v1.19/_modules/QEfficient/peft/lora/auto.html new file mode 100644 index 000000000..994aa253c --- /dev/null +++ b/source/release/v1.19/_modules/QEfficient/peft/lora/auto.html @@ -0,0 +1,545 @@ + + + + + + QEfficient.peft.lora.auto — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for QEfficient.peft.lora.auto

+# -----------------------------------------------------------------------------
+#
+# Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# ----------------------------------------------------------------------------
+
+import hashlib
+from pathlib import Path
+from typing import List, Optional, Union
+
+import torch
+import torch.nn as nn
+from peft import PeftConfig, load_peft_weights
+from transformers import PreTrainedTokenizer, PreTrainedTokenizerFast
+
+import QEfficient
+from QEfficient import QEFFAutoModelForCausalLM
+from QEfficient.peft.lora.pytorch_transforms import LoraModelInputsTransform, TargetModulesTransform
+from QEfficient.utils import constants, get_padding_shape_from_config
+from QEfficient.utils.cache import to_hashable
+from QEfficient.utils.logging_utils import logger
+
+
+
[docs]class QEffAutoLoraModelForCausalLM(QEFFAutoModelForCausalLM): + """ + QEff class for loading models with multiple LoRA adapters. Currently only Mistral and Llama model are supported. + Once exported and compiled, the qpc can perform mixed batch inference with provided `prompt_to_adapter_mapping`. + + Args: + :model (nn.Module): PyTorch model + :continuous_batching (bool): Weather this model will be used for continuous batching in future. If this is not set True here, the model can not be exported/compiled for continuous batching later. + + .. code-block:: python + + from QEfficient.peft.lora import QEffAutoLoraModelForCausalLM + + m = QEffAutoPeftModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1") + m.load_adapter("predibase/gsm8k", "gsm8k") + m.load_adapter("predibase/magicoder", "magicoder") + m.compile(num_cores=16, device_group=[0]) + + prompts=["code prompt", "math prompt", "generic"] + m.generate(prompts, device_group=[0], prompt_to_adapter_mapping=["magicoder","gsm8k_id","base"]) + + """ + + def __init__(self, model: nn.Module, continuous_batching: bool = False, **kwargs) -> None: + super().__init__(model, continuous_batching) + if self.model.__class__.__name__ not in ["QEffMistralForCausalLM", "QEffLlamaForCausalLM"]: + raise NotImplementedError( + f"Only QEffMistralForCausalLM and QEffLlamaForCausalLM model are supported but get {self.model.__class__.__name__}" + ) + + self.adapter_weights = {} + self.adapter_configs = {} + self.active_adapter_to_id = {} + + self.lora_rank = 0 + self.target_modules_for_all_adapters = [] + + def __repr__(self) -> str: + return self.__class__.__name__ + "\n" + self.model.__repr__() + + @property + def model_hash(self) -> str: + mhash = hashlib.sha256() + + # should use model config here + mhash.update(to_hashable(self.model.model.config.to_diff_dict())) + + # create active adapter config dict + active_adapter_configs = {} + for adpt in self.active_adapter_to_id.keys(): + active_adapter_configs[adpt] = self.adapter_configs[adpt].to_dict() + mhash.update(to_hashable(active_adapter_configs)) + + # create active adapter weight dict + active_adapter_weights = {} + for adpt in self.active_adapter_to_id.keys(): + active_adapter_weights[adpt] = {key: value.tolist() for key, value in self.adapter_weights[adpt].items()} + mhash.update(to_hashable(active_adapter_weights)) + + # ensure model will be exported again if order of adapters changes + mhash.update(to_hashable(self.active_adapter_to_id)) + + # noncb & cb should have different onnx & qpc + mhash.update(to_hashable({"continuous_batching": self.continuous_batching})) + + mhash = mhash.hexdigest()[:16] + return mhash + + @property + def get_model_config(self) -> dict: + return self.model.model.config.__dict__ + +
[docs] def download_adapter( + self, + adapter_model_id: str, + adapter_name: str, + adapter_weight: Optional[dict] = None, + adapter_config: Optional[PeftConfig] = None, + ): + """ + Loads a new adapter from huggingface hub or local path into CPU cache + + ``Mandatory`` Args: + :adapter_model_id (str): Adapter model ID from huggingface hub or local path + :adapter_name (str): Adapter name to be used to downloaded this adapter + ``Optional`` Args: + :adapter_weight (dict): Adapter weight tensors in dictionary format + :adapter_config (PeftConfig): Adapter config in the format of PeftConfig + """ + + # check if adapter name already loaded + if (adapter_name in self.adapter_weights.keys()) and (adapter_name in self.adapter_configs.keys()): + logger.warning(f"{adapter_name} has been loaded. Skip download.") + else: + if adapter_weight and adapter_config: # if sufficiently get adapter weight and adpater config + self.adapter_weights[adapter_name] = adapter_weight + self.adapter_configs[adapter_name] = adapter_config + else: # donwload with adapter_model_id + self.adapter_weights[adapter_name] = { + k: v.numpy().astype("float16") for k, v in load_peft_weights(adapter_model_id).items() + } + self.adapter_configs[adapter_name] = PeftConfig.from_pretrained(adapter_model_id)
+ +
[docs] def load_adapter( + self, + adapter_model_id: str, + adapter_name: str, + adapter_weight: Optional[dict] = None, + adapter_config: Optional[PeftConfig] = None, + ): + """ + Load adapter into CPU cache and set it as active + + ``Mandatory`` Args: + :adapter_model_id (str): Adapter model ID from huggingface hub or local path + :adapter_name (str): Adapter name to be used to load this adapter + ``Optional`` Args: + :adapter_weight (dict): Adapter weight tensors in dictionary format + :adapter_config (PeftConfig): Adapter config in the format of PeftConfig + """ + + # check if adapter name already exist and activated + if adapter_name in self.active_adapter_to_id.keys(): + logger.warning(f"{adapter_name} exists and activated. Please provide a different adapter_name.") + else: + self.download_adapter(adapter_model_id, adapter_name, adapter_weight, adapter_config) + + # starting from the second adapter_name, check if adapters has same target module and rank + if list(self.adapter_configs.values())[0] and ( + self.adapter_configs[adapter_name].target_modules + != list(self.adapter_configs.values())[0].target_modules + ): + raise ValueError( + f"{adapter_name} must have same target_modules as {list(self.adapter_configs.keys())[0]}" + ) + if list(self.adapter_configs.values())[0] and ( + self.adapter_configs[adapter_name].r != list(self.adapter_configs.values())[0].r + ): + raise ValueError(f"{adapter_name} must have same rank as {list(self.adapter_configs.keys())[0]}") + + # set active adapter id to current max if adapter_name is new + if adapter_name not in self.active_adapter_to_id.keys(): + self.active_adapter_to_id[adapter_name] = len(self.active_adapter_to_id) + 1 # reserve 0 for base + + return self.active_adapter_to_id[adapter_name]
+ +
[docs] def unload_adapter(self, adapter_name: str): + """ + Deactivate adpater and remove it from CPU cache + + ``Mandatory`` Args: + :adapter_name (str): Adapter name to be unloaded + """ + + # step1: remove from active list if it's there + if adapter_name not in self.active_adapter_to_id.keys(): + logger.info(f"Adapter name {adapter_name} is not set active yet") + return False + + self.active_adapter_to_id.pop(adapter_name) + + # renumbering of active adapter id + for index, (key, value) in enumerate(self.active_adapter_to_id.items()): + self.active_adapter_to_id[key] = index + 1 + + logger.warning(f"Deleting {adapter_name} from active adapters.") + if self.onnx_path or self.qpc_path: + logger.warning("Please redo compile_and_export() to reflect the active adapters changes.") + self.onnx_path = None + self.qpc_path = None + + # step2: delete from cache + if adapter_name in self.adapter_weights.keys() and adapter_name in self.adapter_configs.keys(): + self.adapter_weights.pop(adapter_name) + self.adapter_configs.pop(adapter_name) + logger.warning(f"Unloading {adapter_name} from CPU cache.") + + return True
+ + def set_adapter(self, adapter_name: str): + raise NotImplementedError("Set adapter is not supported in finite_adapters mode") + + def _load_adapter_weights_to_model(self): + "Loads adapter weights to the model's multilora layer in a stacked format" + + num_hidden_layers = len(self.model.model.layers) + for i in range(num_hidden_layers): + for target_module in self.target_modules_for_all_adapters: + # stack all adapters weights + a_tensor_list = list(range(len(self.active_adapter_to_id) + 1)) + b_tensor_list = list(range(len(self.active_adapter_to_id) + 1)) + s_tensor_list = list(range(len(self.active_adapter_to_id) + 1)) + + for lora_name, lora_id in self.active_adapter_to_id.items(): + if target_module in ["q_proj", "k_proj", "v_proj", "o_proj"]: + a_tensor_list[lora_id] = torch.from_numpy( + self.adapter_weights[lora_name][ + f"base_model.model.model.layers.{i}.self_attn.{target_module}.lora_A.weight" + ] + ) + b_tensor_list[lora_id] = torch.from_numpy( + self.adapter_weights[lora_name][ + f"base_model.model.model.layers.{i}.self_attn.{target_module}.lora_B.weight" + ] + ) + else: + raise NotImplementedError("Target module not supported!!") + + s_tensor_list[lora_id] = torch.tensor( + self.adapter_configs[lora_name].lora_alpha / self.adapter_configs[lora_name].r, + dtype=torch.float16, + ) + + # dummy zero tensor for base model + a_tensor_list[0] = torch.zeros_like(a_tensor_list[1]) + b_tensor_list[0] = torch.zeros_like(b_tensor_list[1]) + s_tensor_list[0] = torch.zeros_like(s_tensor_list[1]) + + # stack weight tensors + stacked_lora_a = ( + torch.stack(a_tensor_list, dim=0).unsqueeze(1).transpose(2, 3) + ) # <num_loras, 1, in_feature, r> + stacked_lora_b = ( + torch.stack(b_tensor_list, dim=0).unsqueeze(1).transpose(2, 3) + ) # <num_loras, 1, r, out_feature> + stacked_lora_s = ( + torch.stack(s_tensor_list, dim=0).unsqueeze(1).unsqueeze(2).unsqueeze(3) + ) # <num_loras, 1, 1, 1> + + # stored weight to corresponding ops + if target_module == "q_proj": + module = self.model.model.layers[i].self_attn.q_proj + elif target_module == "k_proj": + module = self.model.model.layers[i].self_attn.k_proj + elif target_module == "v_proj": + module = self.model.model.layers[i].self_attn.v_proj + elif target_module == "o_proj": + module = self.model.model.layers[i].self_attn.o_proj + else: + raise NotImplementedError("Target module not supported!!") + + module.lora_a_weights.copy_(stacked_lora_a) + module.lora_b_weights.copy_(stacked_lora_b) + module.lora_scalings.copy_(stacked_lora_s) + + def _init_adapter_model(self): + "Initialize the fixed lora model with multiple adapter weigths standby" + + # set lora rank + self.lora_rank = list(self.adapter_configs.values())[0].r + + # do the module replacement + _, transformed = LoraModelInputsTransform.apply(self.model) + + self.target_modules_for_all_adapters = list(self.adapter_configs.values())[0].target_modules + _, transformed = TargetModulesTransform.apply( + self.model, self.target_modules_for_all_adapters, self.lora_rank, len(self.active_adapter_to_id) + ) + + # load_weight to model + self._load_adapter_weights_to_model() + +
[docs] def export(self, export_dir: Optional[str] = None) -> str: + """ + Exports the model to ``ONNX`` format using ``torch.onnx.export``. + We currently don't support exporting non-transformed models. Please refer to the ``convert_to_cloud_bertstyle`` function in the **Low-Level API** for a legacy function that supports this." + + ``Optional`` Args: + does not any arguments. + + Returns: + :str: Path of the generated ``ONNX`` graph. + """ + + # initialize the adapter model + if len(self.active_adapter_to_id) == 0: + raise ValueError( + "Please use load_adapter() to add at least one adapter; otherwise, refer to QEFFAutoModelForCausalLM for base model usage" + ) + + self._init_adapter_model() + + bs = constants.ONNX_EXPORT_EXAMPLE_BATCH_SIZE + seq_len = constants.ONNX_EXPORT_EXAMPLE_SEQ_LEN + fbs = constants.ONNX_EXPORT_EXAMPLE_FBS + kv_cache_shape = get_padding_shape_from_config( + self.model.config, fbs if self.continuous_batching else bs, seq_len + ) + example_inputs = { + "input_ids": torch.zeros((bs, seq_len), dtype=torch.int64), + "position_ids": torch.arange(seq_len, dtype=torch.int64).view(bs, seq_len), + "past_key_values": [[] for _ in range(self.num_layers)], + "lora_ids": torch.zeros(bs, dtype=torch.int64).view(bs, 1), + } + dynamic_axes = { + "input_ids": {0: "batch_size", 1: "seq_len"}, + "position_ids": {0: "batch_size", 1: "seq_len"}, + "lora_ids": {0: "batch_size"}, + } + output_names = ["logits"] + for i in range(self.num_layers): + for kv in ["key", "value"]: + example_inputs["past_key_values"][i].append(torch.zeros(kv_cache_shape, dtype=torch.float32)) + dynamic_axes[f"past_{kv}.{i}"] = { + 0: "full_batch_size" if self.continuous_batching else "batch_size", + 2: "ctx_len", + } + output_names.append(f"past_{kv}.{i}_RetainedState") + + if self.continuous_batching: + example_inputs["batch_index"] = torch.arange(bs).view(bs, 1) + dynamic_axes["batch_index"] = {0: "batch_size"} + + return self._export( + example_inputs, + output_names, + dynamic_axes, + export_dir=export_dir, + )
+ +
[docs] def generate( + self, + tokenizer: Union[PreTrainedTokenizerFast, PreTrainedTokenizer], + prompts: List[str], + prompt_to_adapter_mapping: List[str] = None, + device_id: Optional[List[int]] = None, + runtime: Optional[str] = "AI_100", + **kwargs, + ): + """ + This method generates output until ``eos`` or ``generation_len`` by executing the compiled ``qpc`` on ``Cloud AI 100`` Hardware cards. + This is a sequential execution based on the ``batch_size`` of the compiled model and the number of prompts passed. + If the number of prompts cannot be divided by the ``batch_size``, the last unfulfilled batch will be dropped. + + ``Mandatory`` Args: + :tokenizer (PreTrainedTokenizerFast or PreTrainedTokenizer): The tokenizer used in the inference + :prompts (List[str]): List of prompts to run the execution. + :prompt_to_adapter_mapping (List[str]): The sequence of the adapter names will be matched with sequence of prompts and corresponding adapters will be used for the prompts."base" for base model (no adapter). + ``optional`` Args: + :device_id (List[int]): Device IDs to be used for execution. If ``len(device_id) > 1``, it enables multiple card setup. If ``None``, auto-device-picker will be used. ``Defaults to None``. + :runtime (str, optional): Only ``AI_100`` runtime is supported as of now; ``ONNXRT`` and ``PyTorch`` coming soon. Defaults to "AI_100". + + """ + if runtime != "AI_100": + raise ValueError("Only AI_100 runtime is supported right now via generate API") + if not isinstance(self.qpc_path, Path): + raise TypeError("Please run compile API first!") + generation_len = kwargs.pop("generation_len", None) + + if not prompt_to_adapter_mapping: + prompt_to_adapter_mapping = ["base" for _ in range(len(prompts))] + + if len(prompt_to_adapter_mapping) != len(prompts): + raise RuntimeError( + f"Number of prompts should match number of prompt_to_adapter_mapping, got len(prompts) = {len(prompts)}, len(prompt_to_adapter_mapping) = {len(prompt_to_adapter_mapping)}" + ) + + return QEfficient.cloud_ai_100_exec_kv( + tokenizer, + self.qpc_path, + prompt=prompts, + device_id=device_id, + generation_len=generation_len, + prompt_to_lora_id_mapping=[ + self.active_adapter_to_id[name] if name != "base" else 0 for name in prompt_to_adapter_mapping + ], + )
+
+ +
+
+ +
+
+
+
+
+ + Version: Main + + +
+ Versions +
+
main
+
release/v1.18
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/_modules/QEfficient/transformers/models/modeling_auto.html b/source/release/v1.19/_modules/QEfficient/transformers/models/modeling_auto.html new file mode 100644 index 000000000..5b310132d --- /dev/null +++ b/source/release/v1.19/_modules/QEfficient/transformers/models/modeling_auto.html @@ -0,0 +1,1963 @@ + + + + + + QEfficient.transformers.models.modeling_auto — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for QEfficient.transformers.models.modeling_auto

+# -----------------------------------------------------------------------------
+#
+# Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# ----------------------------------------------------------------------------
+
+import hashlib
+import warnings
+from pathlib import Path
+from time import perf_counter
+from typing import List, Optional, Union
+
+import numpy as np
+import torch
+import torch.nn as nn
+from transformers import (
+    AutoModel,
+    AutoModelForCausalLM,
+    AutoModelForImageTextToText,
+    AutoModelForSpeechSeq2Seq,
+    PreTrainedTokenizer,
+    PreTrainedTokenizerFast,
+    TextStreamer,
+)
+
+import QEfficient
+from QEfficient.base.modeling_qeff import QEFFBaseModel
+from QEfficient.base.onnx_transforms import FP16ClipTransform, SplitTensorsTransform
+from QEfficient.generation.cloud_infer import QAICInferenceSession
+from QEfficient.generation.text_generation_inference import (
+    CloudAI100ExecInfoNew,
+    PerfMetrics,
+    calculate_latency,
+    get_compilation_dims,
+)
+from QEfficient.transformers.models.pytorch_transforms import (
+    CustomOpsTransform,
+    KVCacheModuleMethodMapperTransform,
+    KVCacheTransform,
+    SpDTransform,
+    VlmKVOffloadTransform,
+    VlmNoKVOffloadTransform,
+)
+from QEfficient.transformers.quantizers.auto import QEFF_AUTO_QUANTIZATION_CONFIG_MAPPING, with_replaced_quantizers
+from QEfficient.transformers.quantizers.quant_transforms import (
+    AwqToMatmulNbitsTransform,
+    FP8DeQuantLinearToLinearTransform,
+    GPTQToMatmulNbitsTransform,
+)
+from QEfficient.utils import constants, get_padding_shape_from_config
+from QEfficient.utils.cache import to_hashable
+from QEfficient.utils.logging_utils import logger
+
+
+class QEFFTransformersBase(QEFFBaseModel):
+    """
+    Parent class for models QEFF provides from transformers i.e. (AutoModel, AutoModelForCausalLM, AutoModelForAudioClassification etc.) from transformers/models/modeling_auto.py file.
+    """
+
+    _hf_auto_class: type
+
+    def __init__(self, model: nn.Module) -> None:
+        if (
+            hasattr(model, "config")
+            and hasattr(model.config, "quantization_config")
+            and not isinstance(model.config.quantization_config, tuple(QEFF_AUTO_QUANTIZATION_CONFIG_MAPPING.values()))
+        ):
+            raise AssertionError("Please use `from_pretrained` method to load quantized models")
+
+        super().__init__(model)
+
+    def __repr__(self) -> str:
+        return self.__class__.__name__ + "\n" + self.model.__repr__()
+
+    @classmethod
+    @with_replaced_quantizers
+    def from_pretrained(cls, pretrained_model_name_or_path: str, is_tlm: bool = False, *args, **kwargs):
+        if kwargs.get("attn_implementation", None) not in {None, "eager"}:
+            logger.warning('Updating attn_implementation="eager"')
+
+        if kwargs.get("low_cpu_mem_usage", None):
+            logger.warning("Updating low_cpu_mem_usage=False")
+
+        kwargs.update({"attn_implementation": "eager", "low_cpu_mem_usage": False})
+
+        model = cls._hf_auto_class.from_pretrained(pretrained_model_name_or_path, *args, **kwargs)
+        return cls(model, is_tlm=is_tlm)
+
+    @property
+    def model_name(self) -> str:
+        mname = self.model.__class__.__name__
+        if mname.startswith("QEff") or mname.startswith("QEFF"):
+            mname = mname[4:]
+        return mname
+
+
+class MultimodalUtilityMixin:
+    def __new__(cls, *args, **kwargs):
+        if cls is MultimodalUtilityMixin:
+            raise TypeError(f"only children of '{cls.__name__}' may be instantiated")
+        return object.__new__(cls)
+
+    def auto_correct_inputs(self, inputs):
+        checked = True
+        inputs_info = self.model.get_inputs_info()
+        for valid_input_info in inputs_info:
+            if valid_input_info.name not in inputs:
+                checked = False
+                break
+            if inputs[valid_input_info.name].dtype != valid_input_info.datatype:
+                checked = False
+                break
+
+        if not checked:
+            err_str: str = (
+                "Expected following input names and shapes to be passed\n"
+                + "\n".join([val.__repr__() for val in inputs_info])
+                + "\ngot"
+                + f"{[(k, v.shape, v.dtype) for k, v in inputs.items()]}"
+            )
+
+            raise RuntimeError(err_str)
+
+        return {k: v for k, v in inputs.items() if k in [iinfo.name for iinfo in inputs_info]}
+
+
+
[docs]class QEFFAutoModel(QEFFTransformersBase): + """ + The QEFFAutoModel class is designed for manipulating any transformer model from the HuggingFace hub. + Although it is possible to initialize the class directly, we highly recommend using the ``from_pretrained`` method for initialization. + + ``Mandatory`` Args: + :model (nn.Module): PyTorch model + + .. code-block:: python + + from QEfficient import QEFFAutoModel + from transformers import AutoTokenizer + + # Initialize the model using from_pretrained similar to transformers.AutoModel. + model = QEFFAutoModel.from_pretrained("model_name") + + # Now you can directly compile the model for Cloud AI 100 + model.compile(num_cores=16) # Considering you have a Cloud AI 100 SKU + + #prepare input + tokenizer = AutoTokenizer.from_pretrained(model_name) + inputs = tokenizer("My name is", return_tensors="pt") + + # You can now execute the model + model.generate(inputs) + """ + + _hf_auto_class = AutoModel + _pytorch_transforms = [CustomOpsTransform, AwqToMatmulNbitsTransform, GPTQToMatmulNbitsTransform] + _onnx_transforms = [FP16ClipTransform, SplitTensorsTransform] + + def __init__(self, model: nn.Module, **kwargs): + super().__init__(model) + self.model.config.use_cache = True + self.num_layers = model.config.num_hidden_layers + + @classmethod + @with_replaced_quantizers + def from_pretrained(cls, pretrained_model_name_or_path, *args, **kwargs): + """ + This method serves as the easiest entry point into using QEfficient. The interface is designed to be similar to transformers.AutoModel. + Once the model is initialized, you can use other methods such as export, compile, and generate on the same object. + + This API can also be used as exception for VLM model since transformers support loading InternChatVL models via AutoModel API we support it via AutoModelForCausalLM API + Args: + :pretrained_name_or_path (str): Model card name from HuggingFace or local path to model directory. + :args, kwargs: Additional arguments to pass to transformers.AutoModel. + + .. code-block:: python + + from QEfficient import QEFFAutoModel + from transformers import AutoTokenizer + + # Initialize the model using from_pretrained similar to transformers.AutoModel. + model = QEFFAutoModel.from_pretrained("model_name") + + # Now you can directly compile the model for Cloud AI 100 + model.compile(num_cores=16) # Considering you have a Cloud AI 100 SKU + + #prepare input + tokenizer = AutoTokenizer.from_pretrained(model_name) + inputs = tokenizer("My name is", return_tensors="pt") + + # You can now execute the model + model.generate(inputs) + """ + if kwargs.get("attn_implementation", None) not in {None, "eager"}: + logger.warning('Updating attn_implementation="eager"') + + if kwargs.get("low_cpu_mem_usage", None): + logger.warning("Updating low_cpu_mem_usage=False") + + kwargs.update({"attn_implementation": "eager", "low_cpu_mem_usage": False, "add_pooling_layer": False}) + try: + model = cls._hf_auto_class.from_pretrained(pretrained_model_name_or_path, *args, **kwargs) + warnings.warn("Removing pooling layer from the model if exist") + except TypeError: + kwargs.pop("add_pooling_layer", None) + model = cls._hf_auto_class.from_pretrained(pretrained_model_name_or_path, *args, **kwargs) + + # This is support models that should be classified to in a different auto class but transformers load them via this class + kv_offload = kwargs.pop("kv_offload", None) + if model.__class__.__name__ in MISCLASSIFIED_CAUSAL_LM_TO_QEFF_AUTO_CLASS_MAP: + return MISCLASSIFIED_CAUSAL_LM_TO_QEFF_AUTO_CLASS_MAP[model.__class__.__name__]( + model, kv_offload=kv_offload + ) + + return cls(model) + + @property + def model_hash(self) -> str: + # NOTE: model_config.to_diff_dict() has "_name_or_path" attribute which is the model card name or path. + # Using same card name will result in same hash. But, using a relative path for one run and + # absolute path for another run will result in different hash. + # The added complexity to resolve different paths to same location is not worth pursuing. + # Instead, advise the user to always provide same relative paths or absolute paths for local models. + + # Compute the hash with: model_config, transforms + mhash = hashlib.sha256() + mhash.update(to_hashable(self.model.config.to_diff_dict())) + mhash.update(to_hashable(self._transform_names())) + mhash = mhash.hexdigest()[:16] + return mhash + + @property + def get_model_config(self) -> dict: + return self.model.config.__dict__ + +
[docs] def export(self, export_dir: Optional[str] = None) -> str: + """ + Exports the model to ``ONNX`` format using ``torch.onnx.export``. + + ``Optional`` Args: + :export_dir (str, optional): The directory path to store ONNX-graph. + + Returns: + :str: Path of the generated ``ONNX`` graph. + """ + bs = constants.ONNX_EXPORT_EXAMPLE_BATCH_SIZE + seq_len = constants.ONNX_EXPORT_EXAMPLE_SEQ_LEN + + example_inputs = { + "input_ids": torch.zeros((bs, seq_len), dtype=torch.int64), + "attention_mask": torch.ones((bs, seq_len), dtype=torch.int64), + } + + dynamic_axes = {"input_ids": {0: "batch_size", 1: "seq_len"}, "attention_mask": {0: "batch_size", 1: "seq_len"}} + + output_names = ["output"] + + return self._export( + example_inputs, + output_names, + dynamic_axes, + export_dir=export_dir, + )
+ +
[docs] def compile( + self, + onnx_path: Optional[str] = None, + compile_dir: Optional[str] = None, + *, + seq_len: int = 32, + batch_size: int = 1, + num_devices: int = 1, + num_cores: int = 16, # FIXME: Make this mandatory arg + mxfp6_matmul: bool = False, + **compiler_options, + ) -> str: + """ + This method compiles the exported ``ONNX`` model using the Cloud AI 100 Platform SDK compiler binary found at ``/opt/qti-aic/exec/qaic-exec`` and generates a ``qpc`` package. + If the model has not been exported yet, this method will handle the export process. + You can pass any other arguments that the `qaic-exec` takes as extra kwargs. + + ``Optional`` Args: + :onnx_path (str, optional): Path to pre-exported onnx model. + :compile_dir (str, optional): Path for saving the qpc generated. + :seq_len (int, optional): The length of the prompt should be less that ``seq_len``. ``Defaults to 32``. + :batch_size (int, optional): Batch size. ``Defaults to 1``. + :num_devices (int): Number of devices the model needs to be compiled for. Defaults to 1. + :num_cores (int): Number of cores used to compile the model. + :mxfp6_matmul (bool, optional): Whether to use ``mxfp6`` compression for weights. ``Defaults to False``. + :aic_enable_depth_first (bool, optional): Enables DFS with default memory size. ``Defaults to False``. + :allow_mxint8_mdp_io (bool, optional): Allows MXINT8 compression of MDP IO traffic. ``Defaults to False.`` + Returns: + :str: Path of the compiled ``qpc`` package. + """ + + specializations = [ + {"batch_size": batch_size, "seq_len": seq_len}, + ] + + return self._compile( + onnx_path, + compile_dir, + compile_only=True, + specializations=specializations, + convert_to_fp16=True, + mxfp6_matmul=mxfp6_matmul, + mdp_ts_num_devices=num_devices, + aic_num_cores=num_cores, + **compiler_options, + )
+ +
[docs] def generate( + self, + inputs: torch.Tensor, + device_ids: List[int] = None, + runtime_ai100: bool = True, + ) -> Union[torch.Tensor, np.ndarray]: + """ + This method generates output by executing PyTorch runtime or the compiled ``qpc`` on ``Cloud AI 100`` Hardware cards. + ``Mandatory`` Args: + :inputs (Union[torch.Tensor, np.ndarray]): inputs to run the execution. + ``optional`` Args: + :device_id (List[int]): Ids of devices for running the qpc pass as [0] in case of normal model / [0, 1, 2, 3] in case of tensor slicing model + :runtime_ai100 (bool, optional): ``AI_100`` and ``PyTorch`` runtime is supported as of now. Defaults to ``True`` for ``AI_100`` runtime. + Returns: + :dict: Output from the ``AI_100`` or ``PyTorch`` runtime. + """ + # AI_100 runtime + if runtime_ai100: + if not isinstance(self.qpc_path, Path): + raise TypeError("Please run compile API first!") + + return self.cloud_ai_100_feature_generate(inputs=inputs, device_ids=device_ids) + # PyTorch runtime + else: + return self.pytorch_feature_generate(model=self.model, inputs=inputs)
+ +
[docs] def cloud_ai_100_feature_generate( + self, + inputs: torch.Tensor, + device_ids: List[int] = [0], + ) -> np.ndarray: + """ + Generates features with list of prompts using AI 100 runtime. + + ``Mandatory`` Args: + :inputs (Union[torch.Tensor, np.ndarray]): inputs to run the execution. + ``Optional`` Args: + device_ids (List[int], optional): A list of device IDs to use for the session. Defaults to [0]. + + Returns: + np.ndarray: A list of dictionaries containing the generated output features. + """ + + if self.qpc_session is None: + self.qpc_session = QAICInferenceSession(str(self.qpc_path), device_ids) + self.batch_size = self.qpc_session.bindings[0].dims[0] + self.seq_len = self.qpc_session.bindings[0].dims[1] + # Prepare input + input_ids_len = inputs["input_ids"].shape[1] + input_ids = np.array( + torch.nn.functional.pad(inputs["input_ids"], (0, self.seq_len - inputs["input_ids"].size(1)), "constant", 0) + ) + attention_mask = np.array( + torch.nn.functional.pad( + inputs["attention_mask"], (0, self.seq_len - inputs["attention_mask"].size(1)), "constant", 0 + ) + ) + + inputs = dict(input_ids=input_ids, attention_mask=attention_mask) + + outputs = { + "output": np.random.randn(self.batch_size, self.seq_len, self.qpc_session.bindings[2].dims[2]).astype( + np.float32 + ), + } + self.qpc_session.set_buffers(outputs) + outputs = self.qpc_session.run(inputs) + outputs = outputs["output"][:, :input_ids_len, :] + return outputs
+ +
[docs] def pytorch_feature_generate(self, model, inputs: Union[torch.Tensor, np.ndarray]) -> List[torch.Tensor]: + """ + Generates features from a list of text prompts using a PyTorch model. + + ``Mandatory`` Args: + :model: The transformed PyTorch model used for generating features. + :inputs (Union[torch.Tensor, np.ndarray]): inputs to run the execution. + + Returns: + torch.Tensor: A list of output features generated by the model for each prompt. + """ + return model(**inputs)
+ + +class QEffVisionEncoderForTextImageToTextModel(QEFFBaseModel): + _pytorch_transforms = [ + AwqToMatmulNbitsTransform, + GPTQToMatmulNbitsTransform, + CustomOpsTransform, + KVCacheTransform, + KVCacheModuleMethodMapperTransform, + ] + _onnx_transforms = [FP16ClipTransform, SplitTensorsTransform] + + def __init__(self, model: nn.modules): + super().__init__(model) + self.model = model.get_qeff_vision_encoder() + + def export(self, inputs, output_names, dynamic_axes, export_dir=None): + return self._export(inputs, output_names, dynamic_axes, export_dir) + + def compile( + self, + compile_dir, + compile_only, + specializations, + convert_to_fp16, + mxfp6_matmul, + mdp_ts_num_devices, + aic_num_cores, + custom_io, + **compiler_options, + ) -> str: + return self._compile( + compile_dir=compile_dir, + compile_only=compile_only, + specializations=specializations, + convert_to_fp16=convert_to_fp16, + mxfp6_matmul=mxfp6_matmul, + mdp_ts_num_devices=mdp_ts_num_devices, + aic_num_cores=aic_num_cores, + custom_io=custom_io, + **compiler_options, + ) + + @property + def model_hash(self) -> str: + # Compute the hash with: model_config, continuous_batching, transforms + mhash = hashlib.sha256() + mhash.update(to_hashable(self.model.model.config.to_diff_dict())) + mhash.update(to_hashable(self._transform_names())) + mhash.update(to_hashable({"QEffVisionEncoderForTextImageToTextModel": True})) + mhash = mhash.hexdigest()[:16] + return mhash + + @property + def model_name(self) -> str: + mname = self.model.__class__.__name__ + if mname.startswith("QEff") or mname.startswith("QEFF"): + mname = mname[4:] + return mname + + @property + def get_model_config(self) -> dict: + return self.model.model.vision_model.config.__dict__ + + +class QEffCausalLMForTextImageToTextModel(QEFFBaseModel): + _pytorch_transforms = [ + AwqToMatmulNbitsTransform, + GPTQToMatmulNbitsTransform, + CustomOpsTransform, + KVCacheTransform, + VlmKVOffloadTransform, + ] + _onnx_transforms = [FP16ClipTransform, SplitTensorsTransform] + + def __init__(self, model): + super().__init__(model) + self.model = model.get_qeff_language_decoder() + + def export(self, inputs, output_names, dynamic_axes, export_dir=None): + return self._export(inputs, output_names, dynamic_axes, export_dir) + + def compile( + self, + compile_dir, + compile_only, + specializations, + convert_to_fp16, + mxfp6_matmul, + mdp_ts_num_devices, + aic_num_cores, + custom_io, + **compiler_options, + ) -> str: + return self._compile( + compile_dir=compile_dir, + compile_only=compile_only, + specializations=specializations, + convert_to_fp16=convert_to_fp16, + mxfp6_matmul=mxfp6_matmul, + mdp_ts_num_devices=mdp_ts_num_devices, + aic_num_cores=aic_num_cores, + custom_io=custom_io, + **compiler_options, + ) + + @property + def model_hash(self) -> str: + # Compute the hash with: model_config, continuous_batching, transforms + mhash = hashlib.sha256() + mhash.update(to_hashable(self.model.config.to_diff_dict())) + mhash.update(to_hashable(self._transform_names())) + mhash.update(to_hashable({"QEffCausalLMForTextImageToTextModel": True})) + mhash = mhash.hexdigest()[:16] + return mhash + + @property + def model_name(self) -> str: + mname = self.model.__class__.__name__ + if mname.startswith("QEff") or mname.startswith("QEFF"): + mname = mname[4:] + return mname + + @property + def get_model_config(self) -> dict: + return self.model.language_model.config.__dict__ + + +class _QEffAutoModelForImageTextToTextDualQPC: + _hf_auto_class = AutoModelForImageTextToText + + def __init__( + self, + model: nn.Module, + **kwargs, + ): + if kwargs.pop("full_batch_size", None): + raise NotImplementedError("Continuous batching is not supported for image-text-to-text models yet.") + self.model = model + self.config = model.config + self.vision_model = QEffVisionEncoderForTextImageToTextModel(model) + self.lang_model = QEffCausalLMForTextImageToTextModel(model) + + self.input_shapes, self.output_names = None, None + + @property + def model_name(self) -> str: + mname = self.model.__class__.__name__ + if mname.startswith("QEff") or mname.startswith("QEFF"): + mname = mname[4:] + return mname + + @classmethod + def from_pretrained(cls, pretrained_model_name_or_path: str, **kwargs): + if kwargs.get("attn_implementation", None) not in {None, "eager"}: + logger.warning('Updating attn_implementation="eager"') + + if kwargs.get("low_cpu_mem_usage", None): + logger.warning("Updating low_cpu_mem_usage=False") + + kwargs.update({"attn_implementation": "eager", "low_cpu_mem_usage": False}) + model = cls._hf_auto_class.from_pretrained(pretrained_model_name_or_path, **kwargs) + return cls(model, **kwargs) + + @property + def onnx_path(self): + return [self.vision_model.onnx_path, self.lang_model.onnx_path] + + @property + def qpc_path(self): + return [self.vision_model.qpc_path, self.lang_model.qpc_path] + + def export( + self, + export_dir: Optional[str] = None, + **kwargs, + ) -> str: + inputs = self.model.get_dummy_inputs(kv_offload=True) + dynamic_axes = self.model.get_onnx_dynamic_axes(kv_offload=True) + output_names = self.model.get_output_names(kv_offload=True) + self.vision_model.export( + inputs["vision"], + output_names["vision"], + dynamic_axes["vision"], + export_dir, + ) + + self.lang_model.export(inputs["lang"], output_names["lang"], dynamic_axes["lang"], export_dir) + + def compile( + self, + img_size: Optional[int] = None, + vision_onnx_path: Optional[str] = None, + lang_onnx_path: Optional[str] = None, + compile_dir: Optional[str] = None, + *, + prefill_seq_len: Optional[int] = None, + ctx_len: Optional[int] = None, + batch_size: int = 1, + full_batch_size: Optional[int] = None, + kv_cache_batch_size: Optional[int] = None, + num_devices: int = 1, + num_cores: int = 16, # FIXME: Make this mandatory arg + mxfp6_matmul: bool = False, + mxint8_kv_cache: bool = False, + num_speculative_tokens: Optional[int] = None, + enable_qnn: bool = False, + qnn_config: Optional[str] = None, + **compiler_options, + ) -> str: + if ( + any( + param is not None + for param in [full_batch_size, kv_cache_batch_size, num_speculative_tokens, qnn_config] + ) + or enable_qnn + ): + raise ValueError( + f"Expected 'full_batch_size', 'kv_cache_batch_size', 'num_speculative_tokens', and 'qnn_config' to be None, and 'enable_qnn' to be False but got: " + f"full_batch_size={full_batch_size}, kv_cache_batch_size={kv_cache_batch_size}, num_speculative_tokens={num_speculative_tokens}, " + f"enable_qnn={enable_qnn}, qnn_config={qnn_config}" + ) + + output_names = self.model.get_output_names(kv_offload=True) + + specializations, compiler_options = self.model.get_specializations( + batch_size=batch_size, + prefill_seq_len=prefill_seq_len, + ctx_len=ctx_len, + img_size=img_size, + kv_offload=True, + **compiler_options, + ) + + custom_io_vision = {} + kv_cache_dtype = "mxint8" if mxint8_kv_cache else "float16" + custom_io_vision["pixel_values"] = kv_cache_dtype + for output_name in output_names["vision"]: + custom_io_vision[output_name] = kv_cache_dtype + + if vision_onnx_path: + self.vision_model.onnx_path = vision_onnx_path + if lang_onnx_path: + self.lang_model.onnx_path = lang_onnx_path + + if (self.vision_model.onnx_path is None and vision_onnx_path is None) or ( + self.lang_model.onnx_path is None and lang_onnx_path is None + ): + self.export() + + self.vision_model._compile( + compile_dir, + compile_only=True, + specializations=specializations["vision"], + convert_to_fp16=True, + mxfp6_matmul=mxfp6_matmul, + mdp_ts_num_devices=num_devices, + aic_num_cores=num_cores, + custom_io=custom_io_vision, + **compiler_options, + ) + + custom_io_lang = {} + # Inputs + for output_name in output_names["lang"]: + if output_name.endswith("_RetainedState"): + custom_io_lang[output_name[: -len("_RetainedState")]] = kv_cache_dtype + + # outputs + for output_name in output_names["lang"]: + if output_name.endswith("_RetainedState"): + custom_io_lang[output_name] = kv_cache_dtype + + self.lang_model._compile( + compile_dir, + compile_only=True, + retained_state=True, + specializations=specializations["lang"], + convert_to_fp16=True, + mxfp6_matmul=mxfp6_matmul, + mdp_ts_num_devices=num_devices, + aic_num_cores=num_cores, + custom_io=custom_io_lang, + **compiler_options, + ) + + def generate( + self, + inputs: torch.Tensor, + streamer: Optional[TextStreamer] = None, + device_ids: List[int] = None, + runtime_ai100: bool = True, + generation_len: Optional[int] = None, + ) -> Union[torch.Tensor, np.ndarray]: + """ + This method generates output by executing PyTorch runtime or the compiled ``qpc`` on ``Cloud AI 100`` Hardware cards. + ``Mandatory`` Args: + :inputs (Union[torch.Tensor, np.ndarray]): inputs to run the execution. + ``optional`` Args: + :device_id (List[int]): Ids of devices for running the qpc pass as [0] in case of normal model / [0, 1, 2, 3] in case of tensor slicing model + :runtime_ai100 (bool, optional): ``AI_100`` and ``PyTorch`` runtime is supported as of now. Defaults to ``True`` for ``AI_100`` runtime. + Returns: + :dict: Output from the ``AI_100`` or ``PyTorch`` runtime. + """ + if not runtime_ai100: + raise NotImplementedError("PyTorch execution is not supported yet for this model!") + + return self.kv_offload_generate( + inputs=inputs, device_ids=device_ids, streamer=streamer, generation_len=generation_len + ) + + def kv_offload_generate( + self, + inputs: List[str] = None, + streamer: Optional[TextStreamer] = None, + device_ids: List[int] = None, + generation_len: int = None, + ): + lang_session = QAICInferenceSession(self.lang_model.qpc_path, device_ids, activate=False) + + vision_session = QAICInferenceSession(self.vision_model.qpc_path, device_ids) + + batch_size, ctx_len, fbs = get_compilation_dims(self.lang_model.qpc_path) + + pad_token_id = 1 + + # Skip inputs/outputs + lang_session.skip_buffers( + [x for x in lang_session.input_names + lang_session.output_names if x.startswith("past_")] + ) + + # Read prompt and ctx len from session + batch_size = max( + [x[lang_session.binding_index_map["input_ids"]][1][0] for x in lang_session.allowed_shapes] + + [lang_session.bindings[lang_session.binding_index_map["input_ids"]].dims[0]] + ) + + prefill_seq_len = max( + [x[lang_session.binding_index_map["input_ids"]][1][1] for x in lang_session.allowed_shapes] + + [lang_session.bindings[lang_session.binding_index_map["input_ids"]].dims[1]] + ) + + input_len = inputs["attention_mask"].sum(1, keepdims=True) + input_ids_length = inputs["input_ids"].shape[1] + num_chunks = -(input_ids_length // -prefill_seq_len) # ceil divide without float + padded_len = num_chunks * prefill_seq_len # Convert to a multiple of prompt_len + + if generation_len is None: + generation_len = ctx_len - input_len.max() + assert generation_len > 0, "generation length should be greater than zero" + generated_ids = np.full((batch_size, generation_len + 1), pad_token_id) + + # Prepare inputs for prefill + prefill_start = perf_counter() + + inputs["input_ids"] = torch.nn.functional.pad( + inputs["input_ids"], + (0, padded_len - input_ids_length), + "constant", + 1, + ) + inputs["attention_mask"] = torch.nn.functional.pad( + inputs["attention_mask"], (0, padded_len - input_ids_length), "constant", 0 + ) + if "cross_attention_mask" in inputs: + inputs["cross_attention_mask"] = torch.nn.functional.pad( + inputs["cross_attention_mask"], (0, 0, 0, 0, 0, padded_len - input_ids_length) + ) + + for k, v in inputs.items(): + inputs[k] = np.array(v) + + vision_inputs = { + k: v for k, v in inputs.items() if k in {"pixel_values", "aspect_ratio_ids", "aspect_ratio_mask"} + } + + vision_inputs["pixel_values"] = vision_inputs["pixel_values"].astype("float16") + vision_outputs = vision_session.run(vision_inputs) + + lang_inputs = {k: v for k, v in inputs.items() if k not in vision_inputs} + lang_inputs["position_ids"] = np.where( + lang_inputs.pop("attention_mask"), np.arange(padded_len), -1 + ) # Need to use -1 as position_ids for invalid tokens + + vision_session.deactivate() + lang_session.activate() + + lang_session.set_buffers(vision_outputs) + + # Run prefill + for i in range(num_chunks): + chunk_inputs = lang_inputs.copy() + chunk_inputs["input_ids"] = lang_inputs["input_ids"][:, i * prefill_seq_len : (i + 1) * prefill_seq_len] + chunk_inputs["position_ids"] = lang_inputs["position_ids"][ + :, i * prefill_seq_len : (i + 1) * prefill_seq_len + ] + outputs = lang_session.run(chunk_inputs) + + prefill_time = perf_counter() - prefill_start + # Skip inputs/outputs again + lang_session.skip_buffers( + [x for x in lang_session.input_names + lang_session.output_names if x.startswith("past_")] + ) + + # Get first token + lang_inputs["input_ids"] = outputs["logits"].argmax(2) + lang_inputs["position_ids"] = input_len.numpy() + if "cross_attention_mask" in lang_inputs: + bs, _, num_images, img_tiles = lang_inputs["cross_attention_mask"].shape + lang_inputs["cross_attention_mask"] = torch.ones((bs, 1, num_images, img_tiles), dtype=torch.int64).numpy() + generated_ids[:, 0] = lang_inputs["input_ids"].squeeze(1) + + if streamer: + streamer.put(lang_inputs["input_ids"][0]) + + # Decode loop + decode_start = perf_counter() + for num_token in range(1, generation_len): + outputs = lang_session.run(lang_inputs) + + # Prepare inputs for next iteration + lang_inputs["input_ids"] = outputs["logits"].argmax(2) + lang_inputs["position_ids"] += 1 + generated_ids[:, num_token] = lang_inputs["input_ids"].squeeze(1) + if streamer: + streamer.put(lang_inputs["input_ids"][0]) + + decode_end = perf_counter() + if streamer: + streamer.end() + + decode_perf = (num_token - 1) / (decode_end - decode_start) + total_time = decode_end - prefill_start + total_perf = num_token / total_time + + return CloudAI100ExecInfoNew( + batch_size=batch_size, + generated_ids=generated_ids, + perf_metrics=PerfMetrics( + prefill_time=prefill_time, decode_perf=decode_perf, total_perf=total_perf, total_time=total_time + ), + ) + + +class _QEFFAutoModelForImageTextToTextSingleQPC(QEFFTransformersBase, MultimodalUtilityMixin): + _hf_auto_class = AutoModelForImageTextToText + _pytorch_transforms = [ + AwqToMatmulNbitsTransform, + GPTQToMatmulNbitsTransform, + CustomOpsTransform, + KVCacheTransform, + KVCacheModuleMethodMapperTransform, + VlmNoKVOffloadTransform, + ] + _onnx_transforms = [FP16ClipTransform, SplitTensorsTransform] + + def __init__( + self, + model: nn.Module, + **kwargs, + ): + if kwargs.pop("full_batch_size", None): + raise NotImplementedError("Continuous batching is not supported for image-text-to-text models yet.") + super().__init__(model) + + # to handle internvl models + if hasattr(self.model.config, "llm_config") and hasattr(self.model.config, "vision_config"): + self.model.config.llm_config.use_cache = True + self.model.config.llm_config._attn_implementation = "eager" + self.model.config.vision_config.use_flash_attn = "false" + else: + self.model.config.text_config.use_cache = True + + @classmethod + def from_pretrained( + cls, + pretrained_model_name_or_path, + *args, + **kwargs, + ): + if kwargs.get("attn_implementation", None) not in {None, "eager"}: + logger.warning('Updating attn_implementation="eager"') + + if kwargs.get("low_cpu_mem_usage", None): + logger.warning("Updating low_cpu_mem_usage=False") + + kwargs.update({"attn_implementation": "eager", "low_cpu_mem_usage": False}) + from transformers import AutoConfig + + config = AutoConfig.from_pretrained(pretrained_model_name_or_path, trust_remote_code=True) + config._attn_implementation = "eager" + config.vision_config.use_flash_attn = "false" + model = cls._hf_auto_class.from_pretrained(pretrained_model_name_or_path, config, *args, **kwargs) + + return cls(model, **kwargs) + + def export( + self, + export_dir: Optional[str] = None, + **kwargs, + ) -> str: + inputs = self.model.get_dummy_inputs() + dynamic_axes = self.model.get_onnx_dynamic_axes() + output_names = self.model.get_output_names() + self._export(inputs, output_names, dynamic_axes, export_dir=export_dir) + + def compile( + self, + onnx_path: Optional[str] = None, + img_size: Optional[int] = None, + compile_dir: Optional[str] = None, + *, + prefill_seq_len: Optional[int] = None, + ctx_len: Optional[int] = None, + batch_size: int = 1, + full_batch_size: Optional[int] = None, + kv_cache_batch_size: Optional[int] = None, + num_devices: int = 1, + num_cores: int = 16, # FIXME: Make this mandatory arg + mxfp6_matmul: bool = False, + mxint8_kv_cache: bool = False, + num_speculative_tokens: Optional[int] = None, + enable_qnn: bool = False, + qnn_config: Optional[str] = None, + **compiler_options, + ) -> str: + if ( + any( + param is not None + for param in [full_batch_size, kv_cache_batch_size, num_speculative_tokens, qnn_config] + ) + or enable_qnn + ): + raise ValueError( + f"Expected 'full_batch_size', 'kv_cache_batch_size', 'num_speculative_tokens', and 'qnn_config' to be None, and 'enable_qnn' to be False but got: " + f"full_batch_size={full_batch_size}, kv_cache_batch_size={kv_cache_batch_size}, num_speculative_tokens={num_speculative_tokens}, " + f"enable_qnn={enable_qnn}, qnn_config={qnn_config}" + ) + + output_names = self.model.get_output_names() + + # Get specializations from modelling file + # TODO: expose this via the auto class as well + specializations, compiler_options = self.model.get_specializations( + batch_size=batch_size, + prefill_seq_len=prefill_seq_len, + ctx_len=ctx_len, + img_size=img_size, + **compiler_options, + ) + + custom_io = {} + kv_cache_dtype = "mxint8" if mxint8_kv_cache else "float16" + # inputs + for input_name in output_names: + if input_name.endswith("_RetainedState"): + custom_io[input_name[: -len("_RetainedState")]] = kv_cache_dtype + + # outputs + for output_name in output_names: + if output_name.endswith("_RetainedState"): + custom_io[output_name] = kv_cache_dtype + + self._compile( + onnx_path, + compile_dir, + compile_only=True, + retained_state=True, + specializations=specializations, + convert_to_fp16=True, + mxfp6_matmul=mxfp6_matmul, + custom_io=custom_io, + mdp_ts_num_devices=num_devices, + aic_num_cores=num_cores, + **compiler_options, + ) + return self.qpc_path + + def get_onnx_dynamic_axes(self): + return self.model.get_onnx_dynamic_axes() + + def generate( + self, + inputs: torch.Tensor, + streamer: Optional[TextStreamer] = None, + device_ids: List[int] = None, + runtime_ai100: bool = True, + generation_len: Optional[int] = None, + ) -> Union[torch.Tensor, np.ndarray]: + """ + This method generates output by executing PyTorch runtime or the compiled ``qpc`` on ``Cloud AI 100`` Hardware cards. + ``Mandatory`` Args: + :inputs (Union[torch.Tensor, np.ndarray]): inputs to run the execution. + ``optional`` Args: + :device_id (List[int]): Ids of devices for running the qpc pass as [0] in case of normal model / [0, 1, 2, 3] in case of tensor slicing model + :runtime_ai100 (bool, optional): ``AI_100`` and ``PyTorch`` runtime is supported as of now. Defaults to ``True`` for ``AI_100`` runtime. + Returns: + :dict: Output from the ``AI_100`` or ``PyTorch`` runtime. + """ + if not runtime_ai100: + raise NotImplementedError("PyTorch execution is not supported yet for this model!") + + return self.cloud_ai_100_generate( + inputs=inputs, device_ids=device_ids, generation_len=generation_len, streamer=streamer + ) + + def cloud_ai_100_generate( + self, + inputs: torch.Tensor, + device_ids: List[int], + enable_debug_logs: bool = False, + generation_len: int = None, + streamer: Optional[TextStreamer] = None, + ) -> np.ndarray: + inputs = self.auto_correct_inputs(inputs) + qpc_session = QAICInferenceSession( + self.qpc_path, device_ids, enable_debug_logs=enable_debug_logs, activate=False + ) + + batch_size, ctx_len, fbs = get_compilation_dims(self.qpc_path) + + pad_token_id = 1 + + # Skip inputs/outputs + qpc_session.skip_buffers( + [ + x + for x in qpc_session.input_names + qpc_session.output_names + if x.startswith("past_") or x.endswith("_RetainedState") + ] + ) + + # Read prompt and ctx len from session + batch_size = max( + [x[qpc_session.binding_index_map["input_ids"]][1][0] for x in qpc_session.allowed_shapes] + + [qpc_session.bindings[qpc_session.binding_index_map["input_ids"]].dims[0]] + ) + + prefill_seq_len = max( + [x[qpc_session.binding_index_map["input_ids"]][1][1] for x in qpc_session.allowed_shapes] + + [qpc_session.bindings[qpc_session.binding_index_map["input_ids"]].dims[1]] + ) + + input_len = inputs["attention_mask"].sum(1, keepdims=True) + input_ids_length = inputs["input_ids"].shape[1] + + num_chunks = -(input_ids_length // -prefill_seq_len) # ceil divide without float + + padded_len = num_chunks * prefill_seq_len # Convert to a multiple of prompt_len + if generation_len is None: + generation_len = ctx_len - input_len.max() + + assert generation_len > 0, "generation length should be greater than zero" + generated_ids = np.full((batch_size, generation_len + 1), pad_token_id) + + # Prepare inputs for prefill + prefill_start = perf_counter() + + inputs["input_ids"] = torch.nn.functional.pad( + inputs["input_ids"], + (0, padded_len - input_ids_length), + "constant", + 1, + ) + inputs["attention_mask"] = torch.nn.functional.pad( + inputs["attention_mask"], (0, padded_len - input_ids_length), "constant", 0 + ) + if "cross_attention_mask" in inputs: + inputs["cross_attention_mask"] = torch.nn.functional.pad( + inputs["cross_attention_mask"], (0, 0, 0, 0, 0, padded_len - input_ids_length) + ) + for k, v in inputs.items(): + inputs[k] = np.array(v) + + if "pixel_values_RetainedState" in qpc_session.output_names: + inputs["pixel_values"] = inputs["pixel_values"].astype("float16") + + inputs["position_ids"] = np.where(inputs.pop("attention_mask"), np.arange(padded_len), -1) + + qpc_session.activate() + + # Run prefill + + for i in range(num_chunks): + chunk_inputs = inputs.copy() + chunk_inputs["input_ids"] = inputs["input_ids"][:, i * prefill_seq_len : (i + 1) * prefill_seq_len] + chunk_inputs["position_ids"] = inputs["position_ids"][:, i * prefill_seq_len : (i + 1) * prefill_seq_len] + outputs = qpc_session.run(chunk_inputs) + + prefill_time = perf_counter() - prefill_start + # Get first token + inputs["input_ids"] = outputs["logits"].argmax(2) + inputs["position_ids"] = input_len.numpy() + + if "cross_attention_mask" in inputs: + bs, _, num_images, img_tiles = inputs["cross_attention_mask"].shape + inputs["cross_attention_mask"] = torch.ones((bs, 1, num_images, img_tiles), dtype=torch.int64).numpy() + + generated_ids[:, 0] = inputs["input_ids"].squeeze(1) + if streamer: + streamer.put(inputs["input_ids"][0]) + + if "pixel_values_RetainedState" in qpc_session.output_names: + qpc_session.skip_buffers(["pixel_values"]) + inputs.pop("pixel_values") + + # Decode loop + decode_start = perf_counter() + for num_token in range(1, generation_len): + outputs = qpc_session.run(inputs) + # Prepare inputs for next iteration + inputs["input_ids"] = outputs["logits"].argmax(2) + inputs["position_ids"] += 1 + generated_ids[:, num_token] = inputs["input_ids"].squeeze(1) + if streamer: + streamer.put(inputs["input_ids"][0]) + + decode_end = perf_counter() + if streamer: + streamer.end() + + decode_perf = (num_token - 1) / (decode_end - decode_start) + total_time = decode_end - prefill_start + total_perf = num_token / total_time + + return CloudAI100ExecInfoNew( + batch_size=batch_size, + generated_ids=generated_ids, + perf_metrics=PerfMetrics( + prefill_time=prefill_time, decode_perf=decode_perf, total_perf=total_perf, total_time=total_time + ), + ) + + @property + def model_hash(self) -> str: + mhash = hashlib.sha256() + mhash.update(to_hashable(self.model.config.to_diff_dict())) + mhash.update(to_hashable(self._transform_names())) + mhash.update(to_hashable({"QEFFAutoModelForImageTextToText1QPC": True})) + mhash = mhash.hexdigest()[:16] + return mhash + + @property + def model_name(self) -> str: + mname = self.model.__class__.__name__ + if mname.startswith("QEff") or mname.startswith("QEFF"): + mname = mname[4:] + return mname + + @property + def get_model_config(self) -> dict: + return self.model.config.__dict__ + + +
[docs]class QEFFAutoModelForImageTextToText: + """ + A factory class for creating QEFFAutoModelForImageTextToText instances with for single and Dual QPC approach + Attributes: + _hf_auto_class (class): The Hugging Face AutoModel class for ImageTextToText models. + """ + + _hf_auto_class = AutoModelForImageTextToText + + def __new__(self, model: nn.Module, kv_offload: Optional[bool] = True, **kwargs): + if kv_offload: + return _QEffAutoModelForImageTextToTextDualQPC(model, **kwargs) + else: + return _QEFFAutoModelForImageTextToTextSingleQPC(model, **kwargs) + + @classmethod + @with_replaced_quantizers + def from_pretrained(cls, pretrained_model_name_or_path: str, kv_offload: Optional[bool] = None, **kwargs): + """Used to load models supported by transformers.AutoModelForImageTextToText for Cloud AI 100. + + Args: + pretrained_model_name_or_path (str): Path or model card name on HuggingFace + kv_offload (Optional[bool], optional): Should the KV of vision encoder be offloaded to CPU and use Two QPC. Defaults to None. + + Returns: + _type_: _description_ + """ + # TODO: add a check to see if kv_offload is allowed for given model by loading the config and checking architecture or type of config here. + if kwargs.get("attn_implementation", None) not in {None, "eager"}: + logger.warning('Updating attn_implementation="eager"') + + if kwargs.get("low_cpu_mem_usage", None): + logger.warning("Updating low_cpu_mem_usage=False") + + kwargs.update({"attn_implementation": "eager", "low_cpu_mem_usage": False}) + model = cls._hf_auto_class.from_pretrained(pretrained_model_name_or_path, **kwargs) + return cls(model, kv_offload=kv_offload, **kwargs)
+ + +MISCLASSIFIED_CAUSAL_LM_TO_QEFF_AUTO_CLASS_MAP = {"InternVLChatModel": QEFFAutoModelForImageTextToText} + + +
[docs]class QEFFAutoModelForCausalLM(QEFFBaseModel): + """ + The QEFF class is designed for manipulating any causal language model from the HuggingFace hub. + Although it is possible to initialize the class directly, we highly recommend using the ``from_pretrained`` method for initialization. + + ``Mandatory`` Args: + :model (nn.Module): PyTorch model + :continuous_batching (bool): Weather this model will be used for continuous batching in future. If this is not set True here, the model can not be exported/compiled for continuous batching later. + :is_tlm (bool): Whether this is a Speculative Decoding Target Language Model. If set to True, `num_logits_to_keep` input array will have to be fed to control the number of returned logits during prefill/decode. + :enable_qnn (bool): Enables QNN Compilation path for the model. + + + .. code-block:: python + + from QEfficient import QEFFAutoModelForCausalLM + from transformers import AutoTokenizer + + model_name = "gpt2" + model = QEFFAutoModelForCausalLM.from_pretrained(model_name, num_hidden_layers=2) + model.compile(prefill_seq_len=128, ctx_len=256, num_cores=16, num_devices=1) + + tokenizer = AutoTokenizer.from_pretrained(model_name) + model.generate(prompts=["Hi there!!"], tokenizer=tokenizer) + """ + + _hf_auto_class = AutoModelForCausalLM + _pytorch_transforms = [ + AwqToMatmulNbitsTransform, + GPTQToMatmulNbitsTransform, + FP8DeQuantLinearToLinearTransform, + CustomOpsTransform, + KVCacheTransform, + ] + _onnx_transforms = [FP16ClipTransform, SplitTensorsTransform] + + def __init__( + self, + model: nn.Module, + continuous_batching: bool = False, + is_tlm: bool = False, + enable_qnn: bool = False, + **kwargs, + ): + model_class_name = model.__class__.__name__ + if not (model_class_name.endswith("ForCausalLM") or model_class_name.endswith("LMHeadModel")): + raise TypeError(f"Required pytorch module for CausalLM or LMHeadModel, got {model_class_name}") + + # TODO: remove from version 1.20 + if kwargs.pop("full_batch_size", None): + continuous_batching = True + warnings.warn( + "full_batch_size argument is deprecated. Use continuous_batching=True instead.", DeprecationWarning, 2 + ) + if hasattr(model.config, "quantization_config") and not isinstance( + model.config.quantization_config, tuple(QEFF_AUTO_QUANTIZATION_CONFIG_MAPPING.values()) + ): + logger.warning( + "Please use `from_pretrained` method to load quantized models, might give unexpected results" + ) + + super().__init__(model) + + # Set use_cache=True to get KV values as output during ONNX export + self.model.config.use_cache = True + self.num_layers = model.config.num_hidden_layers + self.continuous_batching = continuous_batching + + if is_tlm: + # TODO: It is possible to always apply this transform and make value of indices as last indices by default in PyTorch + self.model, transformed = SpDTransform.apply(self.model) + self.is_tlm = is_tlm + + self.enable_qnn = enable_qnn + + @property + def model_name(self) -> str: + mname = self.model.__class__.__name__ + if mname.startswith("QEff") or mname.startswith("QEFF"): + mname = mname[4:] + return mname + + def __repr__(self) -> str: + return self.__class__.__name__ + "\n" + self.model.__repr__ + + @classmethod + @with_replaced_quantizers + def from_pretrained( + cls, + pretrained_model_name_or_path, + continuous_batching: bool = False, + is_tlm: bool = False, + enable_qnn: bool = False, + *args, + **kwargs, + ): + """ + This method serves as the easiest entry point into using QEfficient. The interface is designed to be similar to transformers.AutoModelForCausalLM. + Once the model is initialized, you can use other methods such as export, compile, and generate on the same object. + + This API can also be used as exception for VLM model since transformers support loading InternChatVL models via AutoModel API we support it via AutoModelForCausalLM API + Args: + :pretrained_name_or_path (str): Model card name from HuggingFace or local path to model directory. + :continuous_batching (bool): Whether this model will be used for continuous batching in future. If this is not set True here, the model can not be exported/compiled for continuous batching later. + :is_tlm (bool): Whether this is a Speculative Decoding Target Language Model. If set to True, `num_logits_to_keep` input array will have to be fed to control the number of returned logits during prefill/decode. + :enable_qnn (bool): Enables QNN Compilation path for the model. + :args, kwargs: Additional arguments to pass to transformers.AutoModelForCausalLM. + + .. code-block:: python + + from QEfficient import QEFFAutoModelForCausalLM + from transformers import AutoTokenizer + + # Initialize the model using from_pretrained similar to transformers.AutoModelForCausalLM + model_name = "gpt2" + model = QEFFAutoModelForCausalLM.from_pretrained(model_name) + + # Now you can directly compile the model for Cloud AI 100 + model.compile(num_cores=16) # Considering you have a Cloud AI 100 Standard SKU + + # You can now execute the model + tokenizer = AutoTokenizer.from_pretrained(model_name) + model.generate(prompts=["Hi there!!"], tokenizer=tokenizer) + """ + if kwargs.pop("full_batch_size", None): + continuous_batching = True + warnings.warn( + "full_batch_size argument is deprecated. Use continuous_batching=True instead.", DeprecationWarning, 2 + ) + + if kwargs.get("attn_implementation", None) not in {None, "eager"}: + logger.warning('Updating attn_implementation="eager"') + + if kwargs.get("low_cpu_mem_usage", None): + logger.warning("Updating low_cpu_mem_usage=False") + + kv_offload = kwargs.pop("kv_offload", None) + + kwargs.update({"attn_implementation": "eager", "low_cpu_mem_usage": False}) + + model = cls._hf_auto_class.from_pretrained(pretrained_model_name_or_path, *args, **kwargs) + + # This is support models that should be classified to in a different auto class but transformers load them via this class + + if model.__class__.__name__ in MISCLASSIFIED_CAUSAL_LM_TO_QEFF_AUTO_CLASS_MAP: + return MISCLASSIFIED_CAUSAL_LM_TO_QEFF_AUTO_CLASS_MAP[model.__class__.__name__]( + model, kv_offload=kv_offload + ) + + return cls(model, is_tlm=is_tlm, continuous_batching=continuous_batching, enable_qnn=enable_qnn) + + @property + def model_hash(self) -> str: + # Compute the hash with: model_config, continuous_batching, transforms + mhash = hashlib.sha256() + mhash.update(to_hashable(self.model.config.to_diff_dict())) + mhash.update(to_hashable({"continuous_batching": self.continuous_batching})) + mhash.update(to_hashable({"is_tlm": self.is_tlm})) + mhash.update(to_hashable(self._transform_names())) + mhash = mhash.hexdigest()[:16] + return mhash + + @property + def get_model_config(self) -> dict: + return self.model.config.__dict__ + +
[docs] def export(self, export_dir: Optional[str] = None) -> str: + """ + Exports the model to ``ONNX`` format using ``torch.onnx.export``. + + ``Optional`` Args: + :export_dir (str, optional): The directory path to store ONNX-graph. + + Returns: + :str: Path of the generated ``ONNX`` graph. + """ + bs: int = constants.ONNX_EXPORT_EXAMPLE_BATCH_SIZE + seq_len: int = constants.ONNX_EXPORT_EXAMPLE_SEQ_LEN + fbs = constants.ONNX_EXPORT_EXAMPLE_FBS + kv_cache_shape = get_padding_shape_from_config( + self.model.config, fbs if self.continuous_batching else bs, seq_len + ) + example_inputs = { + "input_ids": torch.zeros((bs, seq_len), dtype=torch.int64), + "position_ids": torch.arange(seq_len, dtype=torch.int64).view(1, seq_len).repeat(bs, 1), + "past_key_values": [[] for _ in range(self.num_layers)], + } + dynamic_axes = { + "input_ids": {0: "batch_size", 1: "seq_len"}, + "position_ids": {0: "batch_size", 1: "seq_len"}, + } + if len(kv_cache_shape) == 3: # For GPTBigCode arch the pkv is 3d + pkv_dynamic_axes = { + 0: "full_batch_size" if self.continuous_batching else "batch_size", + 1: "ctx_len", + } + else: # pkv is 4d + pkv_dynamic_axes = { + 0: "full_batch_size" if self.continuous_batching else "batch_size", + 2: "ctx_len", + } + output_names = ["logits"] + + for i in range(self.num_layers): + for kv in ["key", "value"]: + example_inputs["past_key_values"][i].append(torch.zeros(kv_cache_shape, dtype=torch.float32)) + dynamic_axes[f"past_{kv}.{i}"] = pkv_dynamic_axes + output_names.append(f"past_{kv}.{i}_RetainedState") + + if self.continuous_batching: + example_inputs["batch_index"] = torch.arange(bs).view(bs, 1) + dynamic_axes["batch_index"] = {0: "batch_size"} + + if self.is_tlm: + nlk = constants.ONNX_EXPORT_EXAMPLE_NLK # Number of Logits to Keep + example_inputs["num_logits_to_keep"] = torch.arange(nlk).view(nlk, 1) + dynamic_axes["num_logits_to_keep"] = {0: "num_logits_to_keep"} + + return self._export( + example_inputs, + output_names, + dynamic_axes, + export_dir=export_dir, + )
+ +
[docs] def compile( + self, + onnx_path: Optional[str] = None, + compile_dir: Optional[str] = None, + *, + prefill_seq_len: int = 32, + ctx_len: int = 128, + batch_size: int = 1, + full_batch_size: Optional[int] = None, + kv_cache_batch_size: Optional[int] = None, + num_devices: int = 1, + num_cores: int = 16, # FIXME: Make this mandatory arg + mxfp6_matmul: bool = False, + mxint8_kv_cache: bool = False, + num_speculative_tokens: Optional[int] = None, + enable_qnn: bool = False, + qnn_config: Optional[str] = None, + **compiler_options, + ) -> str: + """ + This method compiles the exported ``ONNX`` model using the Cloud AI 100 Platform SDK compiler binary found at ``/opt/qti-aic/exec/qaic-exec`` and generates a ``qpc`` package. + If the model has not been exported yet, this method will handle the export process. + You can pass any other arguments that the `qaic-exec` takes as extra kwargs. + + ``Optional`` Args: + :onnx_path (str, optional): Path to pre-exported onnx model. + :compile_dir (str, optional): Path for saving the qpc generated. + :num_cores (int): Number of cores used to compile the model. + :num_devices (int): Number of devices the model needs to be compiled for. Defaults to 1. + :batch_size (int, optional): Batch size. ``Defaults to 1``. + :prefill_seq_len (int, optional): The length of the Prefill prompt should be less that ``prefill_seq_len``. ``Defaults to 32``. + :ctx_len (int, optional): Maximum ``ctx`` that the compiled model can remember. ``Defaults to 128``. + :full_batch_size (int, optional): Continuous batching batch size. + :mxfp6_matmul (bool, optional): Whether to use ``mxfp6`` compression for weights. ``Defaults to False``. + :mxint8_kv_cache (bool, optional): Whether to use ``mxint8`` compression for KV cache. ``Defaults to False``. + :num_speculative_tokens (int, optional): Number of speculative tokens to take as input for Speculative Decoding Target Language Model. + :mos (int, optional): Effort level to reduce on-chip memory. Defaults to -1, meaning no effort. ``Defaults to -1``. + :aic_enable_depth_first (bool, optional): Enables DFS with default memory size. ``Defaults to False``. + :enable_qnn (bool): Enables QNN Compilation. ``Defaults to False.`` + :qnn_config (str): Path of QNN Config parameters file. ``Defaults to None.`` + + Returns: + :str: Path of the compiled ``qpc`` package. + """ + if self.is_tlm: + # assert num_speculative_tokens cfg is acceptable if defined + if num_speculative_tokens is None: + raise TypeError("missing required argument `num_speculative_tokens` as `is_tlm` is True.") + if not isinstance(num_speculative_tokens, int) and num_speculative_tokens < 2: + ValueError( + f"`num_speculative_tokens` arg should be an integer greater than 1, got {num_speculative_tokens}" + ) + num_logits_to_keep = num_speculative_tokens + 1 + if prefill_seq_len < num_logits_to_keep: + raise ValueError( + f"sequence length ({prefill_seq_len}) must be at least `num_speculative_tokens+1` ({num_logits_to_keep})" + ) + + if self.continuous_batching and full_batch_size is None: + raise TypeError("missing required argument: 'full_batch_size'") + + if kv_cache_batch_size and not full_batch_size: + raise ValueError( + "Prefix caching is enabled only for continuous batching as of now. Please pass `full_batch_size` argument and make sure you pass `continuous_batching=True` in the `from_pretrained` call" + ) + + kv_cache_batch_size = ( + kv_cache_batch_size if kv_cache_batch_size else (full_batch_size if full_batch_size else batch_size) + ) + # Define prefill specialization + prefill_specialization = { + # Prefill is always run with single BS for continuous batching. + "batch_size": 1 if self.continuous_batching else batch_size, + "seq_len": prefill_seq_len, + "ctx_len": ctx_len, + # TODO: should be renamed to kv_cache_batch_size in specialization too + } + prefill_specialization.update({"num_logits_to_keep": 1}) if self.is_tlm else ... + if self.continuous_batching: + prefill_specialization.update({"full_batch_size": kv_cache_batch_size}) + else: + prefill_specialization.update({"batch_size": kv_cache_batch_size}) + prefill_specialization.update({"full_batch_exec_size": full_batch_size}) if full_batch_size else ... + specializations = [ + prefill_specialization, + ] + + # Skip decode specialization if we are not in continuous batching and prefill_seq_len=1 as this repeats prefill specialization + if prefill_seq_len != 1 or self.continuous_batching: + decode_specialization = { + "batch_size": full_batch_size if self.continuous_batching else batch_size, + "seq_len": num_speculative_tokens + 1 if self.is_tlm else 1, + "ctx_len": ctx_len, + } + if self.continuous_batching: + decode_specialization.update({"full_batch_size": kv_cache_batch_size}) + else: + decode_specialization.update({"batch_size": kv_cache_batch_size}) + decode_specialization.update({"num_logits_to_keep": num_speculative_tokens + 1}) if self.is_tlm else ... + specializations.append(decode_specialization) + + if enable_qnn: + if compiler_options: + logger.warning("Extra arguments to QNN compilation are supported via qnn_config.json only") + + qpc_path = self._qnn_compile( + onnx_path, + compile_dir, + specializations=specializations, + prefill_seq_len=prefill_seq_len, + ctx_len=ctx_len, + batch_size=batch_size, + full_batch_size=full_batch_size, + mdp_ts_num_devices=num_devices, + num_cores=num_cores, + mxfp6_matmul=mxfp6_matmul, + mxint8_kv_cache=mxint8_kv_cache, + qnn_config=qnn_config, + kv_cache_batch_size=kv_cache_batch_size, + ) + else: + # Custom IO + custom_io = {} + kv_cache_dtype = "mxint8" if mxint8_kv_cache else "float16" + for suffix in ["", "_RetainedState"]: + for i in range(self.num_layers): + for kv in ["key", "value"]: + custom_io[f"past_{kv}.{i}{suffix}"] = kv_cache_dtype + + qpc_path = self._compile( + onnx_path, + compile_dir, + compile_only=True, + retained_state=True, + specializations=specializations, + convert_to_fp16=True, + mxfp6_matmul=mxfp6_matmul, + custom_io=custom_io, + mdp_ts_num_devices=num_devices, + num_speculative_tokens=num_speculative_tokens, + aic_num_cores=num_cores, + **compiler_options, + ) + return qpc_path
+ + # FIXME: Update this method to match with transformers AutoModelForCausalLM.generate +
[docs] def generate( + self, + tokenizer: Union[PreTrainedTokenizerFast, PreTrainedTokenizer], + prompts: List[str], + device_id: List[int] = None, + runtime_ai100: bool = True, + **kwargs, + ): + """ + This method generates output until ``eos`` or ``generation_len`` by executing the compiled ``qpc`` on ``Cloud AI 100`` Hardware cards. + This is a sequential execution based on the ``batch_size`` of the compiled model and the number of prompts passed. + If the number of prompts cannot be divided by the ``batch_size``, the last unfulfilled batch will be dropped. + + ``Mandatory`` Args: + :tokenizer (Union[PreTrainedTokenizerFast, PreTrainedTokenizer]): Pass tokenizer of the model. + :prompts (List[str]): List of prompts to run the execution. + + ``optional`` Args: + :device_id (List[int]): Ids of devices for running the qpc pass as [0] in case of normal model / [0, 1, 2, 3] in case of tensor slicing model + :runtime_ai100 (bool, optional): ``AI_100`` and ``PyTorch`` runtime is supported as of now. Defaults to ``True`` for ``AI_100`` runtime. + + """ + if runtime_ai100: + if not isinstance(self.qpc_path, Path): + raise TypeError("Please run compile API first!") + generation_len = kwargs.pop("generation_len", None) + return QEfficient.cloud_ai_100_exec_kv( + tokenizer, + self.qpc_path, + prompt=prompts, + device_id=device_id, + generation_len=generation_len, + is_tlm=self.is_tlm, + ) + else: + raise NotImplementedError("Only AI_100 runtime is supported right now via generate API")
+ + +
[docs]class QEFFAutoModelForSpeechSeq2Seq(QEFFTransformersBase, MultimodalUtilityMixin): + """ + The QEFFAutoModelForSpeechSeq2Seq class is designed for transformers models with a sequence-to-sequence speech-to-text modeling head, including Whisper and other Encoder-Decoder speech models. + Although it is possible to initialize the class directly, we highly recommend using the ``from_pretrained`` method for initialization. + + ``Mandatory`` Args: + :model (nn.Module): PyTorch model + + .. code-block:: python + + from QEfficient import QEFFAutoModelForSpeechSeq2Seq + from processors import AutoProcessor + + # Initialize the model using from_pretrained similar to transformers.AutoModelForSpeechSeq2Seq. + model = QEFFAutoModelForSpeechSeq2Seq.from_pretrained("model_name") + + # Now you can directly compile the model for Cloud AI 100 + model.compile(num_cores=16, device_group=[0]) # Considering you have a Cloud AI 100 SKU + + #prepare inputs + processor = AutoProcessor.from_pretrained(model_name) + input_audio, sample_rate = [...] # audio data loaded in via some external audio package, such as librosa or soundfile + input_features = ( + processor(data, sampling_rate=sample_rate, return_tensors="pt").input_features.numpy().astype(np.float32) + ) + decoder_input_ids = ( + torch.ones((batch_size, 1), dtype=torch.int64) * model.model.config.decoder_start_token_id + ).numpy() + decoder_position_ids = torch.arange(1, dtype=torch.int64).view(1, 1).repeat(batch_size, 1).numpy() + inputs = dict( + input_features=input_features, + decoder_input_ids=decoder_input_ids, + decoder_position_ids=decoder_position_ids, + ) + + # You can now execute the model + model.generate(inputs, generation_len=150) + """ + + _hf_auto_class = AutoModelForSpeechSeq2Seq + _pytorch_transforms = [CustomOpsTransform, AwqToMatmulNbitsTransform, GPTQToMatmulNbitsTransform, KVCacheTransform] + _onnx_transforms = [FP16ClipTransform, SplitTensorsTransform] + + def __init__(self, model: nn.Module, **kwargs): + model_class_name = model.__class__.__name__ + if not (model_class_name.endswith("ForConditionalGeneration")): + raise TypeError(f"Required pytorch module with ForConditionalGeneration, got {model_class_name}") + + super().__init__(model) + self.model.config.use_cache = True + self.num_layers = model.config.num_hidden_layers + + @property + def model_hash(self) -> str: + # NOTE: model_config.to_diff_dict() has "_name_or_path" attribute which is the model card name or path. + # Using same card name will result in same hash. But, using a relative path for one run and + # absolute path for another run will result in different hash. + # The added complexity to resolve different paths to same location is not worth pursuing. + # Instead, advise the user to always provide same relative paths or absolute paths for local models. + + # Compute the hash with: model_config, transforms + mhash = hashlib.sha256() + mhash.update(to_hashable(self.model.config.to_diff_dict())) + mhash.update(to_hashable(self._transform_names())) + mhash = mhash.hexdigest()[:16] + return mhash + + @property + def get_model_config(self) -> dict: + return self.model.config.__dict__ + +
[docs] def export(self, export_dir: Optional[str] = None) -> str: + """ + Exports the model to ``ONNX`` format using ``torch.onnx.export``. + + ``Optional`` Args: + :export_dir (str, optional): The directory path to store ONNX-graph. + + Returns: + :str: Path of the generated ``ONNX`` graph. + """ + inputs = self.model.get_dummy_inputs() + dynamic_axes = self.model.get_onnx_dynamic_axes() + output_names = self.model.get_output_names() + self._export(inputs, output_names, dynamic_axes, export_dir=export_dir)
+ +
[docs] def compile( + self, + onnx_path: Optional[str] = None, + compile_dir: Optional[str] = None, + *, + encoder_ctx_len: int = 1500, + decoder_ctx_len: int = 150, + feature_len: int = 3000, + batch_size: int = 1, + num_devices: int = 1, + num_cores: int = 16, # FIXME: Make this mandatory arg + mxfp6_matmul: bool = False, + **compiler_options, + ) -> str: + """ + This method compiles the exported ``ONNX`` model using the Cloud AI 100 Platform SDK compiler binary found at ``/opt/qti-aic/exec/qaic-exec`` and generates a ``qpc`` package. + If the model has not been exported yet, this method will handle the export process. + You can pass any other arguments that the `qaic-exec` takes as extra kwargs. + + ``Optional`` Args: + :onnx_path (str, optional): Path to pre-exported onnx model. + :compile_dir (str, optional): Path for saving the qpc generated. + :seq_len (int, optional): The length of the prompt should be less that ``seq_len``. ``Defaults to 32``. + :batch_size (int, optional): Batch size. ``Defaults to 1``. + :num_devices (int): Number of devices the model needs to be compiled for. Defaults to 1. + :num_cores (int): Number of cores used to compile the model. + :mxfp6_matmul (bool, optional): Whether to use ``mxfp6`` compression for weights. ``Defaults to False``. + :aic_enable_depth_first (bool, optional): Enables DFS with default memory size. ``Defaults to False``. + :allow_mxint8_mdp_io (bool, optional): Allows MXINT8 compression of MDP IO traffic. ``Defaults to False.`` + Returns: + :str: Path of the compiled ``qpc`` package. + """ + specializations = self.model.get_specializations(batch_size, encoder_ctx_len, decoder_ctx_len, feature_len) + + self._compile( + onnx_path, + compile_dir, + compile_only=True, + retained_state=True, + specializations=specializations, + convert_to_fp16=True, + mxfp6_matmul=mxfp6_matmul, + mdp_ts_num_devices=num_devices, + aic_num_cores=num_cores, + **compiler_options, + )
+ +
[docs] def generate( + self, + inputs: torch.Tensor, + generation_len: int, + streamer: Optional[TextStreamer] = None, + enable_debug_logs: bool = False, + device_ids: List[int] = None, + ) -> Union[torch.Tensor, np.ndarray]: + """ + This method generates output until ``endoftranscript`` or ``generation_len`` by executing the compiled ``qpc`` on ``Cloud AI 100`` Hardware cards. + This is a sequential execution based on the ``batch_size`` of the compiled model and the number of audio tensor passed. + + ``Mandatory`` Args: + :processor: autoprocessor to process inputs and decode logits + :inputs (np.ndarray): inputs to run the execution. + :generation_len (int): length upto which to generate + :sample_rate (int): sampling rate at which input audio is stored in inputs (needed for processor) + :device_id (List[int]): Ids of devices for running the qpc pass as [0] in case of normal model / [0, 1, 2, 3] in case of tensor slicing model + Returns: + :dict: Output from the ``AI_100`` or ``PyTorch`` runtime. + """ + if not isinstance(self.qpc_path, Path): + raise TypeError("Please run compile API first!") + + inputs = self.auto_correct_inputs(inputs) + + if self.qpc_session is None: + self.qpc_session = QAICInferenceSession(str(self.qpc_path), device_ids, enable_debug_logs=enable_debug_logs) + self.batch_size = self.qpc_session.bindings[0].dims[0] + + self.qpc_session.skip_buffers( + [x for x in self.qpc_session.input_names + self.qpc_session.output_names if x.startswith("past_")] + ) + + outputs = { + "logits": np.random.randn(self.batch_size, 1, self.model.config.vocab_size).astype(np.float32), + } + self.qpc_session.set_buffers(outputs) + + # encoder run + start = perf_counter() + outputs = self.qpc_session.run(inputs) + + # array to hold generated tokens + generated_ids = np.full((self.batch_size, generation_len + 1), self.model.config.eos_token_id) + generated_ids[:, 0] = [self.model.config.decoder_start_token_id] + logits = outputs["logits"] + next_token = logits.argmax(-1) + generated_ids[:, 1] = next_token.squeeze(1) + + if streamer: + streamer.put(next_token) + + inputs["input_features"] = np.zeros((self.batch_size, self.model.config.num_mel_bins, 1)).astype(np.float32) + + loop_start = perf_counter() + for num_tokens in range(generation_len): + outputs = self.qpc_session.run(inputs) + logits = outputs["logits"] + next_token = logits.argmax(-1) + generated_ids[:, num_tokens + 1] = next_token.squeeze(1) + + if next_token[0][0] == self.model.config.eos_token_id: + break + + inputs["decoder_input_ids"] = next_token + inputs["decoder_position_ids"] += 1 + + if streamer: + streamer.put(next_token) + end = perf_counter() + + prefill_time, decode_perf, total_perf, total_time = calculate_latency(num_tokens, loop_start, start, end) + + return CloudAI100ExecInfoNew( + batch_size=self.batch_size, + generated_ids=generated_ids, + perf_metrics=PerfMetrics(prefill_time, decode_perf, total_perf, total_time), + )
+
+ +
+
+ +
+
+
+
+
+ + Version: Main + + +
+ Versions +
+
main
+
release/v1.18
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/_modules/QEfficient/utils/device_utils.html b/source/release/v1.19/_modules/QEfficient/utils/device_utils.html new file mode 100644 index 000000000..0add7b706 --- /dev/null +++ b/source/release/v1.19/_modules/QEfficient/utils/device_utils.html @@ -0,0 +1,237 @@ + + + + + + QEfficient.utils.device_utils — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for QEfficient.utils.device_utils

+# -----------------------------------------------------------------------------
+#
+# Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# -----------------------------------------------------------------------------
+
+import math
+import subprocess
+
+from QEfficient.utils.constants import Constants
+from QEfficient.utils.logging_utils import logger
+
+
+
[docs]def get_available_device_id(): + """ + API to check available device id. + + Return: + :int: Available device id. + """ + + device_id = 0 + result = None + + # FIXME: goes into infinite loop when user doesn't have permission and the command gives permission denied. + # To reproduce change the ownership of available devices. + while 1: + command = ["/opt/qti-aic/tools/qaic-util", "-q", "-d", f"{device_id}"] + try: + result = subprocess.run(command, capture_output=True, text=True) + except OSError: + logger.warning("Not a Cloud AI 100 device, Command not found", command) + return None + if result: + if "Status:Error" in result.stdout: + device_id += 1 + elif "Status:Ready" in result.stdout: + logger.info("device is available.") + return [device_id] + elif "Failed to find requested device ID" in result.stdout: + logger.warning("Failed to find requested device ID") + return None
+ + +def is_qpc_size_gt_32gb(params: int, mxfp6: bool) -> bool: + if mxfp6: + qpc_size = math.ceil((params * 1) / Constants.GB) + else: + qpc_size = math.ceil((params * 2) / Constants.GB) + + logger.warning(f"Approximate QPC size is: {qpc_size} GB") + num_devices = math.ceil(qpc_size / Constants.MAX_QPC_LIMIT) + logger.warning(f"Number of Devices required: {num_devices}") + return qpc_size > Constants.MAX_QPC_LIMIT + + +def is_multi_qranium_setup_available(): + result = None + command = ["/opt/qti-aic/tools/qaic-util", "-q"] + try: + result = subprocess.run(command, stdout=subprocess.PIPE, universal_newlines=True) + filtered_result = subprocess.run( + ["grep", "Device Capabilities"], input=result.stdout, stdout=subprocess.PIPE, text=True + ) + except OSError: + print("Command not found", command) + return None + + lines = filtered_result.stdout.split("\n") + + # to count the number of devices in MQ enabled set up + hybridboot_mdp_count = 0 + for line in lines: + if ("HybridBoot+" in line) and ("MDP+" in line): + hybridboot_mdp_count = hybridboot_mdp_count + 1 + + if hybridboot_mdp_count > 0: + print("No: of Devices with MQ enabled available: ", hybridboot_mdp_count) + return True + else: + print("Device in MQ set up not available") + return False +
+ +
+
+ +
+
+
+
+
+ + Version: Main + + +
+ Versions +
+
main
+
release/v1.18
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/_modules/QEfficient/utils/generate_inputs.html b/source/release/v1.19/_modules/QEfficient/utils/generate_inputs.html new file mode 100644 index 000000000..45cef6d77 --- /dev/null +++ b/source/release/v1.19/_modules/QEfficient/utils/generate_inputs.html @@ -0,0 +1,354 @@ + + + + + + QEfficient.utils.generate_inputs — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +

Source code for QEfficient.utils.generate_inputs

+# -----------------------------------------------------------------------------
+#
+# Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# -----------------------------------------------------------------------------
+
+import numpy as np
+import torch
+
+from QEfficient.utils import get_num_layers_from_config, get_padding_shape_from_config, padding_check_and_fix
+
+
+
[docs]class InputHandler: + def __init__(self, batch_size, tokenizer, config, prompt, prompt_len, ctx_len, full_batch_size): + """ + Initialization + + ``Mandatory`` Args: + :batch_size (int): Number of prompts to run in one batch. + :tokenizer (Union[PreTrainedTokenizer, PreTrainedTokenizerFast]): Pass model tokenizer. + :config (AutoConfig): From pretrained model. + :prompt (List[str]): String to used as input prompt for the model. + :prompt_len (int): Prompt length for the model to compile. + :ctx_len (int): Maximum context length to compile the model. + :full_batch_size (int): Continuous batching batch size + """ + # check and fix tokenizer viability + padding_check_and_fix(tokenizer) + self.tokenizer = tokenizer + self.prompt = prompt + self.prompt_len = prompt_len + self.ctx_len = ctx_len + self.full_batch_size = full_batch_size + self.n_layer = get_num_layers_from_config(config) + self.padding_shape = get_padding_shape_from_config( + config=config, batch_size=full_batch_size if full_batch_size else batch_size, seq_len=ctx_len + ) + +
[docs] def prepare_pytorch_inputs(self): + """ + Function responsible for creating Prefill stage tensor inputs for PyTorch model. + + Return: + :Dict: input_ids, position_ids, past_key_values + """ + + inputs = self.tokenizer( + self.prompt, + return_tensors="pt", + padding=True, + ) + input_ids = inputs["input_ids"] + batch_size, input_len = input_ids.shape + inputs.pop("attention_mask") + inputs.pop("token_type_ids", None) + position_ids = torch.arange(input_len).view(1, -1) + inputs["input_ids"] = torch.concat( + [ + input_ids, + torch.ones((batch_size, self.prompt_len - input_len), dtype=torch.int64) + * (self.tokenizer.pad_token_id), + ], + 1, + ) + inputs["position_ids"] = torch.concat( + [ + position_ids, + torch.ones((batch_size, self.prompt_len - input_len), dtype=torch.int64) * (-1), + ], + 1, + ) + + if self.full_batch_size: + inputs["input_ids"] = input_ids + inputs["position_ids"] = torch.arange(input_len).view(1, input_len) + inputs["batch_index"] = torch.arange(1).view(-1, 1) + + past_key_values = [] + for i in range(self.n_layer): + past_key = torch.zeros((self.padding_shape), dtype=torch.float32) + past_value = torch.zeros((self.padding_shape), dtype=torch.float32) + pkv = (past_key, past_value) + past_key_values.append(pkv) + inputs["past_key_values"] = tuple(past_key_values) + + return inputs
+ +
[docs] def update_pytorch_inputs(self, inputs, pt_outputs): + """ + Function responsible for updating Prefill stage inputs to create decode stage inputs for PyTorch model. + + ``Mandatory`` Args: + :inputs (Dict): Pytorch inputs from previous iteration + :pt_outputs (Dict): Pytorch outputs from previous iteration + + Return: + :Dict: Updated input_ids, position_ids and past_key_values + """ + updated_inputs = {} + if self.full_batch_size: + batch_index = torch.arange(1).view(-1, 1) + + input_ids = pt_outputs.logits.detach().argmax(2) + updated_inputs["input_ids"] = torch.full((self.full_batch_size, 1), self.tokenizer.pad_token_id) + updated_inputs["input_ids"][batch_index.view(-1)] = input_ids + + position_ids = inputs["position_ids"].max(1, keepdim=True).values + 1 + updated_inputs["position_ids"] = torch.full((self.full_batch_size, 1), 0) + updated_inputs["position_ids"][batch_index.view(-1)] = position_ids + + updated_inputs["batch_index"] = torch.arange(self.full_batch_size).view(-1, 1) + + else: + updated_inputs["input_ids"] = pt_outputs["logits"].argmax(-1).reshape(-1, 1) + updated_inputs["position_ids"] = inputs["position_ids"].max(1, keepdim=True).values + 1 + + updated_inputs["past_key_values"] = tuple( + [(key.detach(), value.detach()) for key, value in pt_outputs["past_key_values"]] + ) + + return updated_inputs
+ +
[docs] def prepare_ort_inputs(self): + """ + Function responsible for creating Prefill stage numpy inputs for ONNX model to be run on ONNXRT. + + Return: + :Dict: input_ids, position_ids, past_key_values + """ + + inputs = self.tokenizer( + self.prompt, + return_tensors="np", + padding=True, + ) + input_ids = inputs["input_ids"] + batch_size, input_len = input_ids.shape + inputs.pop("attention_mask") + inputs.pop("token_type_ids", None) + position_ids = np.arange(input_len).reshape(1, -1) + inputs["input_ids"] = np.concatenate( + [input_ids, np.full((batch_size, self.prompt_len - input_len), self.tokenizer.pad_token_id)], + axis=1, + ).astype(np.int64) + inputs["position_ids"] = np.concatenate( + [position_ids, np.full((batch_size, self.prompt_len - input_len), -1)], + axis=1, + ).astype(np.int64) + + for i in range(self.n_layer): + inputs["past_key." + str(i)] = np.zeros((self.padding_shape), dtype=np.float32) + inputs["past_value." + str(i)] = np.zeros((self.padding_shape), dtype=np.float32) + + return inputs
+ +
[docs] def update_ort_inputs(self, inputs, ort_outputs): + """ + Function responsible for updating Prefill stage inputs to create inputs for decode stage inputs for ONNX model to be run on ONNXRT. + + ``Mandatory`` Args: + :inputs (Dict): NumPy inputs of Onnx model from previous iteration + :ort_outputs (Dict): Numpy outputs of Onnx model from previous iteration + + Return: + :Dict: Updated input_ids, position_ids and past_key_values + """ + + updated_inputs = {} + updated_inputs["input_ids"] = ort_outputs["logits"].argmax(-1) + updated_inputs["position_ids"] = np.max(inputs["position_ids"], axis=1, keepdims=True) + 1 + for i in range(self.n_layer): + updated_inputs["past_key." + str(i)] = ort_outputs["past_key_values"][i * 2] + updated_inputs["past_value." + str(i)] = ort_outputs["past_key_values"][i * 2 + 1] + + return updated_inputs
+ +
[docs] def update_ort_outputs(self, ort_outputs): + """ + Function responsible for updating ONNXRT session outputs. + + ``Mandatory`` Args: + :ort_outputs (Dict): Numpy outputs of Onnx model from current iteration + + Return: + updated_outputs (Dict): Updated past_key_values, logits + """ + + present_key_values = [] + for i in range(self.n_layer): + if "past_key." + str(i) + "_RetainedState" in ort_outputs: + present_key_values.append(ort_outputs["past_key." + str(i) + "_RetainedState"]) + if "past_value." + str(i) + "_RetainedState" in ort_outputs: + present_key_values.append(ort_outputs["past_value." + str(i) + "_RetainedState"]) + + outputs = {} + outputs["past_key_values"] = present_key_values + outputs["logits"] = ort_outputs["logits"] + + return outputs
+
+ +
+
+ +
+
+
+
+
+ + Version: Main + + +
+ Versions +
+
main
+
release/v1.18
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/_modules/QEfficient/utils/run_utils.html b/source/release/v1.19/_modules/QEfficient/utils/run_utils.html new file mode 100644 index 000000000..c3f578ba3 --- /dev/null +++ b/source/release/v1.19/_modules/QEfficient/utils/run_utils.html @@ -0,0 +1,399 @@ + + + + + + QEfficient.utils.run_utils — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

Source code for QEfficient.utils.run_utils

+# -----------------------------------------------------------------------------
+#
+# Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# -----------------------------------------------------------------------------
+
+import os
+
+import numpy as np
+import onnx
+import onnxruntime
+import torch
+
+from QEfficient.generation.text_generation_inference import TextGeneration
+from QEfficient.utils.generate_inputs import InputHandler
+
+
+# TODO: Deprecate this class and encourage the use of `QeffAutoModel...` classes
+
[docs]class ApiRunner: + """ + ApiRunner class is responsible for running: + --------- + + 1. HuggingFace ``PyTorch`` model + 2. Transformed KV Pytorch Model + 3. ``ONNX`` model on ONNXRT + 4. ``ONNX`` model on Cloud AI 100 + """ + + def __init__(self, batch_size, tokenizer, config, prompt, prompt_len, ctx_len, full_batch_size=None): + """ + Initialization + + Args: + :batch_size (int): Number of prompts to run in one batch. + :tokenizer (Union[PreTrainedTokenizer, PreTrainedTokenizerFast]): Pass model tokenizer. + :config (AutoConfig): From pretrained model. + :prompt (List[str]): Input prompt for running the model. + :prompt_len (int): Prompt length to compile the model. + :ctx_len (int): Maximum context length to compile the model. + """ + self.input_handler = InputHandler( + batch_size=batch_size, + tokenizer=tokenizer, + config=config, + prompt=prompt, + prompt_len=prompt_len, + ctx_len=ctx_len, + full_batch_size=full_batch_size, + ) + + self.gen_len = self.input_handler.ctx_len - self.input_handler.prompt_len + +
[docs] @torch.no_grad() + def run_hf_model_on_pytorch_CB(self, model_hf): + """ + Function responsible for running HuggingFace ``PyTorch`` model and return the output tokens + + ``Mandatory`` Args: + :model_hf (torch.nn.module): Original ``PyTorch`` model + + Return: + :numpy.ndarray: Generated output tokens + """ + input_ids = [ + self.input_handler.tokenizer.encode(prompt, return_tensors="pt") for prompt in self.input_handler.prompt + ] + + generated_ids = [] + + for idx, inp_ids in enumerate(input_ids): + gen_ids = inp_ids.clone() + for _ in range(self.gen_len): + outputs = model_hf(input_ids=gen_ids) + logits = outputs.logits[:, -1, :] + predicted_token_id = torch.argmax(logits, dim=-1) + gen_ids = torch.cat([gen_ids, predicted_token_id.unsqueeze(-1)], dim=-1) + + gen_ids = gen_ids.detach().numpy() + gen_ids = gen_ids[:, inp_ids.shape[1] :] + generated_ids.append(gen_ids) + + generated_texts = [ + self.input_handler.tokenizer.decode(gen_ids.squeeze().tolist(), skip_special_tokens=True) + for gen_ids in generated_ids + ] + print("Original HF Model Outputs (Torch CPU): \n") + print("Prompt:", repr(self.input_handler.prompt)) + print("Completion:", repr(generated_texts)) + return generated_ids
+ +
[docs] @torch.no_grad() + def run_hf_model_on_pytorch(self, model_hf): + """ + Function responsible for running HuggingFace ``PyTorch`` model and return the output tokens + + ``Mandatory`` Args: + :model_hf (torch.nn.module): Original ``PyTorch`` model + + Return: + :numpy.ndarray: Generated output tokens + """ + input_ids = self.input_handler.tokenizer.encode(self.input_handler.prompt[0], return_tensors="pt") + + input_ids_len = len(input_ids[0]) + + for _ in range(self.gen_len): + outputs = model_hf(input_ids) + logits = outputs.logits[:, -1, :] + predicted_token_id = torch.argmax(logits, dim=-1) + input_ids = torch.cat([input_ids, predicted_token_id.unsqueeze(1)], dim=-1) + + generated_ids = input_ids[0][input_ids_len:].detach().numpy() + generated_text = self.input_handler.tokenizer.decode(generated_ids, skip_special_tokens=True) + print("Original HF Model Outputs (Torch CPU): \n") + print("Prompt:", repr(self.input_handler.prompt)) + print("Completion:", repr(generated_text)) + return generated_ids
+ +
[docs] def run_kv_model_on_pytorch(self, model): + """ + Function responsible for running KV ``PyTorch`` model and return the output tokens + + ``Mandatory`` Args: + :model (torch.nn.module): Transformed ``PyTorch`` model + + Return: + :numpy.ndarray: Generated output tokens + """ + + generated_ids = [] + inputs = self.input_handler.prepare_pytorch_inputs() + + pt_outputs = model(**inputs) + for _ in range(1, self.gen_len): + generated_ids.append(pt_outputs["logits"].argmax(-1).reshape(-1, 1)) + inputs = self.input_handler.update_pytorch_inputs(inputs, pt_outputs) + pt_outputs = model(**inputs) + + generated_ids.append(pt_outputs["logits"].argmax(-1).reshape(-1, 1)) + generated_ids = np.concatenate(generated_ids, axis=1) + predicted_string = self.input_handler.tokenizer.batch_decode(generated_ids, skip_special_tokens=True) + print("QEff Transformed HF Model Outputs (Torch CPU): \n") + print("Prompt:", repr(self.input_handler.prompt)) + print("Completion:", repr(predicted_string)) + return generated_ids
+ +
[docs] def run_ort_session(self, inputs, session) -> dict: + """ + Function responsible for running onnxrt session with given inputs and passing retained state outputs to be used for next iteration inputs + + ``Mandatory`` Args: + :inputs (Dict): + :session (onnxruntime.capi.onnxruntime_inference_collection.InferenceSession): + + Return: + :Dict: Numpy outputs of Onnx model + """ + output_names = [x.name for x in session.get_outputs()] + session_input_names = [x.name for x in session.get_inputs()] + session_inputs = {} + for inp_name in session_input_names: + if inp_name in inputs.keys(): + session_inputs[inp_name] = inputs[inp_name] + outputs_data = session.run(output_names, session_inputs) + ort_outputs = dict(zip(output_names, outputs_data)) + return ort_outputs
+ +
[docs] def run_kv_model_on_ort(self, model_path, is_tlm=False): + """ + Function responsible for running ``ONNX`` model on onnxruntime and return the output tokens + + ``Mandatory`` Args: + :model_path (str): Path to the Onnx model. + + Return: + :numpy.ndarray: Generated output tokens + """ + + # Replace invalid index value for INT32 max to 0 using add_initializer + m = onnx.load(model_path, load_external_data=False) + # NOTE: OrtValue objects should be kept around until the session is run, hence this dict is required + added_initializers = {} + for node in m.graph.node: + if node.op_type == "Constant": + np_tensor = onnx.numpy_helper.to_array(node.attribute[0].t, os.path.dirname(model_path)) + if len(np_tensor.shape) == 0 and np_tensor.item() == 2147483647: + added_initializers[node.output[0]] = onnxruntime.OrtValue.ortvalue_from_numpy( + np.array(0, np_tensor.dtype) + ) + + session_options = onnxruntime.SessionOptions() + for name, value in added_initializers.items(): + session_options.add_initializer(name, value) + session = onnxruntime.InferenceSession(model_path, session_options) + + generated_ids = [] + inputs = self.input_handler.prepare_ort_inputs() + if is_tlm: + nltk = np.zeros((1, 1), dtype=np.int64) + inputs["num_logits_to_keep"] = nltk + ort_outputs = self.run_ort_session(inputs, session) + ort_outputs = self.input_handler.update_ort_outputs(ort_outputs) + + for _ in range(1, self.gen_len): + generated_ids.append(ort_outputs["logits"].argmax(-1).reshape(-1, 1)) + inputs = self.input_handler.update_ort_inputs(inputs, ort_outputs) + if is_tlm: + inputs["num_logits_to_keep"] = nltk + ort_outputs = self.run_ort_session(inputs, session) + ort_outputs = self.input_handler.update_ort_outputs(ort_outputs) + + generated_ids.append(ort_outputs["logits"].argmax(-1).reshape(-1, 1)) + generated_ids = np.concatenate(generated_ids, axis=1) + predicted_string = self.input_handler.tokenizer.batch_decode(generated_ids, skip_special_tokens=True) + print("QEff Transformed Onnx Model Outputs (OnnxRuntime CPU): \n") + print("Prompt:", repr(self.input_handler.prompt)) + print("Completion:", repr(predicted_string)) + return generated_ids
+ +
[docs] def run_kv_model_on_cloud_ai_100(self, qpc_path, device_group=None): + """ + Function responsible for running ``ONNX`` model on Cloud AI 100 and return the output tokens + + ``Mandatory`` Args: + :qpc_path (str): path to qpc generated after compilation + :device_group (List[int]): Device Ids to be used for compilation. if len(device_group) > 1. Multiple Card setup is enabled. + + Return: + :numpy.ndarray: Generated output tokens + """ + execinfo = TextGeneration( + tokenizer=self.input_handler.tokenizer, + qpc_path=qpc_path, + device_id=device_group, + ctx_len=self.input_handler.ctx_len, + full_batch_size=self.input_handler.full_batch_size, + ).generate(prompt=self.input_handler.prompt, generation_len=self.gen_len, stream=False) + + predicted_string = self.input_handler.tokenizer.batch_decode(execinfo.generated_ids, skip_special_tokens=True) + print("QEff Transformed Model Outputs (Cloud AI 100): \n") + print("Prompt:", repr(self.input_handler.prompt)) + print("Completion:", repr(predicted_string)) + return execinfo.generated_ids
+
+ +
+
+ +
+
+
+
+
+ + Version: Main + + +
+ Versions +
+
main
+
release/v1.18
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/_modules/index.html b/source/release/v1.19/_modules/index.html new file mode 100644 index 000000000..47dca2ec2 --- /dev/null +++ b/source/release/v1.19/_modules/index.html @@ -0,0 +1,162 @@ + + + + + + Overview: module code — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+
+ + Version: Main + + +
+ Versions +
+
main
+
release/v1.18
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/_static/_sphinx_javascript_frameworks_compat.js b/source/release/v1.19/_static/_sphinx_javascript_frameworks_compat.js new file mode 100644 index 000000000..81415803e --- /dev/null +++ b/source/release/v1.19/_static/_sphinx_javascript_frameworks_compat.js @@ -0,0 +1,123 @@ +/* Compatability shim for jQuery and underscores.js. + * + * Copyright Sphinx contributors + * Released under the two clause BSD licence + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} diff --git a/source/release/v1.19/_static/basic.css b/source/release/v1.19/_static/basic.css new file mode 100644 index 000000000..cfc60b86c --- /dev/null +++ b/source/release/v1.19/_static/basic.css @@ -0,0 +1,921 @@ +/* + * 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; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/source/release/v1.19/_static/css/badge_only.css b/source/release/v1.19/_static/css/badge_only.css new file mode 100644 index 000000000..c718cee44 --- /dev/null +++ b/source/release/v1.19/_static/css/badge_only.css @@ -0,0 +1 @@ +.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} \ No newline at end of file diff --git a/source/release/v1.19/_static/css/fonts/Roboto-Slab-Bold.woff b/source/release/v1.19/_static/css/fonts/Roboto-Slab-Bold.woff new file mode 100644 index 000000000..6cb600001 Binary files /dev/null and b/source/release/v1.19/_static/css/fonts/Roboto-Slab-Bold.woff differ diff --git a/source/release/v1.19/_static/css/fonts/Roboto-Slab-Bold.woff2 b/source/release/v1.19/_static/css/fonts/Roboto-Slab-Bold.woff2 new file mode 100644 index 000000000..7059e2314 Binary files /dev/null and b/source/release/v1.19/_static/css/fonts/Roboto-Slab-Bold.woff2 differ diff --git a/source/release/v1.19/_static/css/fonts/Roboto-Slab-Regular.woff b/source/release/v1.19/_static/css/fonts/Roboto-Slab-Regular.woff new file mode 100644 index 000000000..f815f63f9 Binary files /dev/null and b/source/release/v1.19/_static/css/fonts/Roboto-Slab-Regular.woff differ diff --git a/source/release/v1.19/_static/css/fonts/Roboto-Slab-Regular.woff2 b/source/release/v1.19/_static/css/fonts/Roboto-Slab-Regular.woff2 new file mode 100644 index 000000000..f2c76e5bd Binary files /dev/null and b/source/release/v1.19/_static/css/fonts/Roboto-Slab-Regular.woff2 differ diff --git a/source/release/v1.19/_static/css/fonts/fontawesome-webfont.eot b/source/release/v1.19/_static/css/fonts/fontawesome-webfont.eot new file mode 100644 index 000000000..e9f60ca95 Binary files /dev/null and b/source/release/v1.19/_static/css/fonts/fontawesome-webfont.eot differ diff --git a/source/release/v1.19/_static/css/fonts/fontawesome-webfont.svg b/source/release/v1.19/_static/css/fonts/fontawesome-webfont.svg new file mode 100644 index 000000000..855c845e5 --- /dev/null +++ b/source/release/v1.19/_static/css/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/release/v1.19/_static/css/fonts/fontawesome-webfont.ttf b/source/release/v1.19/_static/css/fonts/fontawesome-webfont.ttf new file mode 100644 index 000000000..35acda2fa Binary files /dev/null and b/source/release/v1.19/_static/css/fonts/fontawesome-webfont.ttf differ diff --git a/source/release/v1.19/_static/css/fonts/fontawesome-webfont.woff b/source/release/v1.19/_static/css/fonts/fontawesome-webfont.woff new file mode 100644 index 000000000..400014a4b Binary files /dev/null and b/source/release/v1.19/_static/css/fonts/fontawesome-webfont.woff differ diff --git a/source/release/v1.19/_static/css/fonts/fontawesome-webfont.woff2 b/source/release/v1.19/_static/css/fonts/fontawesome-webfont.woff2 new file mode 100644 index 000000000..4d13fc604 Binary files /dev/null and b/source/release/v1.19/_static/css/fonts/fontawesome-webfont.woff2 differ diff --git a/source/release/v1.19/_static/css/fonts/lato-bold-italic.woff b/source/release/v1.19/_static/css/fonts/lato-bold-italic.woff new file mode 100644 index 000000000..88ad05b9f Binary files /dev/null and b/source/release/v1.19/_static/css/fonts/lato-bold-italic.woff differ diff --git a/source/release/v1.19/_static/css/fonts/lato-bold-italic.woff2 b/source/release/v1.19/_static/css/fonts/lato-bold-italic.woff2 new file mode 100644 index 000000000..c4e3d804b Binary files /dev/null and b/source/release/v1.19/_static/css/fonts/lato-bold-italic.woff2 differ diff --git a/source/release/v1.19/_static/css/fonts/lato-bold.woff b/source/release/v1.19/_static/css/fonts/lato-bold.woff new file mode 100644 index 000000000..c6dff51f0 Binary files /dev/null and b/source/release/v1.19/_static/css/fonts/lato-bold.woff differ diff --git a/source/release/v1.19/_static/css/fonts/lato-bold.woff2 b/source/release/v1.19/_static/css/fonts/lato-bold.woff2 new file mode 100644 index 000000000..bb195043c Binary files /dev/null and b/source/release/v1.19/_static/css/fonts/lato-bold.woff2 differ diff --git a/source/release/v1.19/_static/css/fonts/lato-normal-italic.woff b/source/release/v1.19/_static/css/fonts/lato-normal-italic.woff new file mode 100644 index 000000000..76114bc03 Binary files /dev/null and b/source/release/v1.19/_static/css/fonts/lato-normal-italic.woff differ diff --git a/source/release/v1.19/_static/css/fonts/lato-normal-italic.woff2 b/source/release/v1.19/_static/css/fonts/lato-normal-italic.woff2 new file mode 100644 index 000000000..3404f37e2 Binary files /dev/null and b/source/release/v1.19/_static/css/fonts/lato-normal-italic.woff2 differ diff --git a/source/release/v1.19/_static/css/fonts/lato-normal.woff b/source/release/v1.19/_static/css/fonts/lato-normal.woff new file mode 100644 index 000000000..ae1307ff5 Binary files /dev/null and b/source/release/v1.19/_static/css/fonts/lato-normal.woff differ diff --git a/source/release/v1.19/_static/css/fonts/lato-normal.woff2 b/source/release/v1.19/_static/css/fonts/lato-normal.woff2 new file mode 100644 index 000000000..3bf984332 Binary files /dev/null and b/source/release/v1.19/_static/css/fonts/lato-normal.woff2 differ diff --git a/source/release/v1.19/_static/css/theme.css b/source/release/v1.19/_static/css/theme.css new file mode 100644 index 000000000..19a446a0e --- /dev/null +++ b/source/release/v1.19/_static/css/theme.css @@ -0,0 +1,4 @@ +html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search>a:hover{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel,.rst-content .menuselection{font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .guilabel,.rst-content .menuselection{border:1px solid #7fbbe3;background:#e7f2fa}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/source/release/v1.19/_static/doctools.js b/source/release/v1.19/_static/doctools.js new file mode 100644 index 000000000..d06a71d75 --- /dev/null +++ b/source/release/v1.19/_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/source/release/v1.19/_static/documentation_options.js b/source/release/v1.19/_static/documentation_options.js new file mode 100644 index 000000000..6fdbd7dc1 --- /dev/null +++ b/source/release/v1.19/_static/documentation_options.js @@ -0,0 +1,14 @@ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), + VERSION: 'main', + 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/source/release/v1.19/_static/file.png b/source/release/v1.19/_static/file.png new file mode 100644 index 000000000..a858a410e Binary files /dev/null and b/source/release/v1.19/_static/file.png differ diff --git a/source/release/v1.19/_static/jquery.js b/source/release/v1.19/_static/jquery.js new file mode 100644 index 000000000..c4c6022f2 --- /dev/null +++ b/source/release/v1.19/_static/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/source/release/v1.19/_static/js/html5shiv.min.js b/source/release/v1.19/_static/js/html5shiv.min.js new file mode 100644 index 000000000..cd1c674f5 --- /dev/null +++ b/source/release/v1.19/_static/js/html5shiv.min.js @@ -0,0 +1,4 @@ +/** +* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed +*/ +!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/source/release/v1.19/_static/js/theme.js b/source/release/v1.19/_static/js/theme.js new file mode 100644 index 000000000..1fddb6ee4 --- /dev/null +++ b/source/release/v1.19/_static/js/theme.js @@ -0,0 +1 @@ +!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0 + 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/source/release/v1.19/_static/minus.png b/source/release/v1.19/_static/minus.png new file mode 100644 index 000000000..d96755fda Binary files /dev/null and b/source/release/v1.19/_static/minus.png differ diff --git a/source/release/v1.19/_static/my_theme.css b/source/release/v1.19/_static/my_theme.css new file mode 100644 index 000000000..00a18c905 --- /dev/null +++ b/source/release/v1.19/_static/my_theme.css @@ -0,0 +1,3 @@ +.wy-nav-content { + max-width: 1200px !important; +} \ No newline at end of file diff --git a/source/release/v1.19/_static/plus.png b/source/release/v1.19/_static/plus.png new file mode 100644 index 000000000..7107cec93 Binary files /dev/null and b/source/release/v1.19/_static/plus.png differ diff --git a/source/release/v1.19/_static/pygments.css b/source/release/v1.19/_static/pygments.css new file mode 100644 index 000000000..6f8b210a1 --- /dev/null +++ b/source/release/v1.19/_static/pygments.css @@ -0,0 +1,75 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #F00 } /* Error */ +.highlight .k { color: #008000; font-weight: bold } /* Keyword */ +.highlight .o { color: #666 } /* Operator */ +.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #9C6500 } /* Comment.Preproc */ +.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.highlight .gr { color: #E40000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #008400 } /* Generic.Inserted */ +.highlight .go { color: #717171 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #04D } /* Generic.Traceback */ +.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #008000 } /* Keyword.Pseudo */ +.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #B00040 } /* Keyword.Type */ +.highlight .m { color: #666 } /* Literal.Number */ +.highlight .s { color: #BA2121 } /* Literal.String */ +.highlight .na { color: #687822 } /* Name.Attribute */ +.highlight .nb { color: #008000 } /* Name.Builtin */ +.highlight .nc { color: #00F; font-weight: bold } /* Name.Class */ +.highlight .no { color: #800 } /* Name.Constant */ +.highlight .nd { color: #A2F } /* Name.Decorator */ +.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #00F } /* Name.Function */ +.highlight .nl { color: #767600 } /* Name.Label */ +.highlight .nn { color: #00F; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #19177C } /* Name.Variable */ +.highlight .ow { color: #A2F; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #BBB } /* Text.Whitespace */ +.highlight .mb { color: #666 } /* Literal.Number.Bin */ +.highlight .mf { color: #666 } /* Literal.Number.Float */ +.highlight .mh { color: #666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666 } /* Literal.Number.Oct */ +.highlight .sa { color: #BA2121 } /* Literal.String.Affix */ +.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ +.highlight .sc { color: #BA2121 } /* Literal.String.Char */ +.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ +.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ +.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #A45A77 } /* Literal.String.Regex */ +.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ +.highlight .ss { color: #19177C } /* Literal.String.Symbol */ +.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #00F } /* Name.Function.Magic */ +.highlight .vc { color: #19177C } /* Name.Variable.Class */ +.highlight .vg { color: #19177C } /* Name.Variable.Global */ +.highlight .vi { color: #19177C } /* Name.Variable.Instance */ +.highlight .vm { color: #19177C } /* Name.Variable.Magic */ +.highlight .il { color: #666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/source/release/v1.19/_static/searchtools.js b/source/release/v1.19/_static/searchtools.js new file mode 100644 index 000000000..97d56a74d --- /dev/null +++ b/source/release/v1.19/_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/source/release/v1.19/_static/sphinx_highlight.js b/source/release/v1.19/_static/sphinx_highlight.js new file mode 100644 index 000000000..aae669d7e --- /dev/null +++ b/source/release/v1.19/_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/source/release/v1.19/genindex.html b/source/release/v1.19/genindex.html new file mode 100644 index 000000000..eeec1d0c4 --- /dev/null +++ b/source/release/v1.19/genindex.html @@ -0,0 +1,495 @@ + + + + + + Index — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + +

Index

+ +
+ A + | C + | D + | E + | F + | G + | I + | L + | M + | P + | Q + | R + | S + | U + +
+

A

+ + + +
+ +

C

+ + + +
+ +

D

+ + +
+ +

E

+ + +
+ +

F

+ + + +
+ +

G

+ + + +
+ +

I

+ + +
+ +

L

+ + +
+ +

M

+ + +
+ +

P

+ + + +
+ +

Q

+ + + +
+ +

R

+ + + +
+ +

S

+ + +
+ +

U

+ + + +
+ + + +
+
+ +
+
+
+
+
+ + Version: release/v1.19 + + +
+ Versions +
+
main
+
release/v1.18
+
release/v1.19
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/index.html b/source/release/v1.19/index.html new file mode 100644 index 000000000..c4096eae9 --- /dev/null +++ b/source/release/v1.19/index.html @@ -0,0 +1,338 @@ + + + + + + + Welcome to Efficient-Transformers Documentation! — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Welcome to Efficient-Transformers Documentation!

+ + + +
+

Upgrade Efficient-Transformers

+ +
+ + + + +
+ + +
+
+ +
+
+
+
+
+ + Version: release/v1.19 + + +
+ Versions +
+
main
+
release/v1.18
+
release/v1.19
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/objects.inv b/source/release/v1.19/objects.inv new file mode 100644 index 000000000..caea96e9a Binary files /dev/null and b/source/release/v1.19/objects.inv differ diff --git a/source/release/v1.19/py-modindex.html b/source/release/v1.19/py-modindex.html new file mode 100644 index 000000000..b5ebc54b3 --- /dev/null +++ b/source/release/v1.19/py-modindex.html @@ -0,0 +1,229 @@ + + + + + + Python Module Index — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + +

Python Module Index

+ +
+ q +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ q
+ QEfficient +
    + QEfficient.cloud.execute.main +
    + QEfficient.cloud.export.main +
    + QEfficient.cloud.finetune.main +
    + QEfficient.cloud.infer.main +
    + QEfficient.compile.compile_helper +
    + QEfficient.compile.compile_helper.compile +
    + QEfficient.exporter.export_hf_to_cloud_ai_100 +
    + QEfficient.generation.text_generation_inference +
    + QEfficient.utils.device_utils +
    + QEfficient.utils.generate_inputs +
    + QEfficient.utils.run_utils +
+ + +
+
+ +
+
+
+
+
+ + Version: release/v1.19 + + +
+ Versions +
+
main
+
release/v1.18
+
release/v1.19
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/search.html b/source/release/v1.19/search.html new file mode 100644 index 000000000..cd8a9cfff --- /dev/null +++ b/source/release/v1.19/search.html @@ -0,0 +1,174 @@ + + + + + + Search — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + + + + + +
+ + Version: release/v1.19 + + +
+ Versions +
+
main
+
release/v1.18
+
release/v1.19
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/source/release/v1.19/searchindex.js b/source/release/v1.19/searchindex.js new file mode 100644 index 000000000..d72c5ab0e --- /dev/null +++ b/source/release/v1.19/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"docnames": ["README", "index", "source/blogs", "source/cli_api", "source/finetune", "source/installation", "source/introduction", "source/python_api", "source/quick_start", "source/reference", "source/upgrade", "source/validate"], "filenames": ["README.md", "index.md", "source/blogs.md", "source/cli_api.md", "source/finetune.md", "source/installation.md", "source/introduction.md", "source/python_api.md", "source/quick_start.md", "source/reference.md", "source/upgrade.md", "source/validate.md"], "titles": ["Docs", "Welcome to Efficient-Transformers Documentation!", "Train anywhere, Infer on Qualcomm Cloud AI 100", "Command Line Interface Use (CLI)", "Finetune Infra", "Pre-requisites", "Introduction Qualcomm efficient-transformers library", "Python API", "Quick Start", "Qualcomm Cloud AI home", "Using GitHub Repository", "Validated Models"], "terms": {"thi": [0, 3, 4, 6, 7, 8, 10], "directori": [0, 4, 5, 7, 8], "contain": [0, 7, 8], "instruct": [0, 6, 8, 11], "static": 0, "html": 0, "document": [0, 6], "base": [0, 1, 6, 7, 11], "sphinx": 0, "instal": [0, 10], "packag": [0, 3, 7], "requir": [0, 5, 7, 8, 10], "pip": [0, 4, 10], "r": [0, 11], "txt": [0, 3, 8], "And": [0, 7], "chang": [0, 6, 8], "folder": [0, 8], "cd": 0, "To": [0, 4, 6, 8], "specif": [0, 1, 4, 7], "branch": 0, "m": [0, 3, 4, 7, 8, 10], "option": [0, 3, 7, 8], "all": [0, 7], "support": [0, 1, 5, 6, 7, 11], "multivers": 0, "python": [0, 1, 3, 4, 5, 10], "http": [0, 4, 8, 10], "server": 0, "you": [0, 4, 5, 7, 8], "can": [0, 4, 5, 6, 7, 8], "visit": [0, 4], "page": [0, 7], "your": [0, 7, 8], "web": 0, "browser": 0, "url": 0, "localhost": 0, "8080": 0, "introduct": 1, "qualcomm": 1, "librari": [1, 4, 5, 8], "valid": [1, 8, 10], "model": [1, 3, 4, 5, 6, 7, 10], "text": [1, 3, 7, 8], "onli": [1, 6, 7, 8], "languag": [1, 7, 8], "gener": [1, 3, 4, 7, 8], "task": [1, 7, 8], "embed": [1, 8], "multimod": 1, "vision": [1, 8], "imag": 1, "audio": [1, 7], "come": [1, 6, 7], "soon": [1, 3, 6, 7], "pre": [1, 6, 7, 8], "requisit": 1, "1": [1, 3, 4, 6, 7, 11], "download": [1, 3, 4, 7], "app": [1, 3], "sdk": [1, 3, 7, 8, 10], "2": [1, 4, 6, 7, 11], "saniti": 1, "check": [1, 3, 7, 8], "us": [1, 4, 5, 6, 7, 8], "github": [1, 4], "repositori": [1, 4], "quick": 1, "featur": [1, 7], "qpc": [1, 3, 7], "storag": 1, "command": [1, 4, 5, 11], "line": [1, 4, 5, 7], "interfac": 1, "qeffici": [1, 4, 5, 7, 10], "execut": [1, 5, 6], "multi": [1, 5, 11], "qranium": 1, "continu": [1, 3, 6, 7], "batch": [1, 3, 6, 7], "qnn": [1, 3, 7], "compil": [1, 6], "api": 1, "optim": [1, 6], "export": [1, 4, 6], "one": [1, 7], "3": [1, 4, 6, 7, 10, 11], "draft": 1, "specul": [1, 6, 7], "decod": [1, 6, 7], "cli": [1, 4, 8], "high": [1, 8], "level": [1, 3, 4, 8], "qeffautomodelforcausallm": [1, 3, 6, 8, 11], "qeffautomodel": [1, 11], "qeffautopeftmodelforcausallm": 1, "qeffautoloramodelforcausallm": 1, "qeffautomodelforimagetexttotext": 1, "qeffautomodelforspeechseq2seq": [1, 8, 11], "qualcomm_efficient_convert": [1, 7, 8], "cloudai100execinfo": [1, 7], "cloudai100execinfonew": [1, 7], "perfmetr": [1, 7], "calculate_lat": [1, 7], "cloud_ai_100_exec_kv": [1, 7], "fix_prompt_to_lora_id_map": [1, 7], "fix_prompt": [1, 7], "get_compilation_dim": [1, 7], "low": [1, 8], "convert_to_cloud_kvstyl": 1, "convert_to_cloud_bertstyl": 1, "util": [1, 5, 8], "get_available_device_id": [1, 7], "inputhandl": [1, 7], "apirunn": 1, "infra": 1, "dataset": [1, 8], "detail": [1, 7, 8], "usag": [1, 6, 7, 8], "visual": 1, "train": [1, 6], "anywher": [1, 6], "how": [1, 6], "quadrupl": 1, "llm": [1, 6, 8], "perform": [1, 6, 7, 8], "spd": 1, "microsc": 1, "mx": 1, "format": [1, 3, 7, 8], "power": [1, 8], "acceler": [1, 4, 8], "larg": [1, 8, 11], "2x": 1, "introduc": 1, "One": 1, "infinit": 1, "possibl": [1, 7], "home": 1, "user": [1, 6, 8], "guid": 1, "ocp": 1, "click": 2, "here": [2, 6, 7], "bash": [3, 8], "termin": [3, 8], "els": [3, 8], "zsh": [3, 8], "device_group": [3, 7, 8], "should": [3, 7, 8], "singl": [3, 7, 8], "quot": [3, 8], "e": [3, 8], "g": [3, 8], "0": [3, 4, 7, 8], "given": [3, 7], "config": [3, 4, 7, 8], "alreadi": [3, 4, 8], "exist": [3, 5, 8], "doe": [3, 7], "jump": [3, 8], "onnx": [3, 6, 7, 8], "file": [3, 4, 7, 8], "true": [3, 4, 7, 8], "hf": [3, 8, 11], "cach": [3, 7, 8], "start": [3, 5, 7], "transform": [3, 4, 7, 10, 11], "4": [3, 4, 6], "mandatori": [3, 7], "arg": [3, 7], "model_nam": [3, 4, 7, 8], "str": [3, 7], "hug": [3, 7], "face": [3, 7], "card": [3, 5, 6, 7, 8], "name": [3, 7, 8], "exampl": [3, 4, 6, 7, 8], "gpt2": [3, 7, 8, 11], "num_cor": [3, 7, 8], "int": [3, 7, 8], "number": [3, 4, 7, 8], "core": [3, 7], "list": [3, 7], "devic": [3, 4, 5, 6, 7, 8], "id": [3, 7], "If": [3, 5, 7, 8], "len": [3, 7], "multipl": [3, 7, 8], "setup": [3, 7], "i": [3, 4, 5, 6, 8], "enabl": [3, 4, 5, 6, 7, 8], "default": [3, 7, 8], "none": [3, 7], "prompt": [3, 7, 8], "sampl": [3, 6, 7, 8], "prompts_txt_file_path": [3, 7, 8], "path": [3, 5, 6, 7, 8], "input": [3, 7, 8], "aic_enable_depth_first": [3, 7, 8], "bool": [3, 7], "df": [3, 7], "memori": [3, 7, 8], "size": [3, 7, 8], "fals": [3, 7], "mo": [3, 7, 8], "effort": [3, 7, 8], "reduc": [3, 7, 8], "chip": [3, 7], "batch_siz": [3, 7, 8], "full_batch_s": [3, 7, 8], "set": [3, 4, 7, 8], "full": [3, 7], "mode": [3, 4, 7], "prompt_len": [3, 7, 8], "length": [3, 7, 8], "32": [3, 7, 8], "ctx_len": [3, 7, 8], "maximum": [3, 7, 8], "context": [3, 7, 8], "128": [3, 7, 8], "generation_len": [3, 7], "token": [3, 7, 8], "mxfp6": [3, 7, 8], "precis": [3, 6, 7, 8], "mxint8": [3, 7], "compress": [3, 7], "present": [3, 7, 8], "past": [3, 7], "kv": [3, 7, 8], "customio": [3, 7, 8], "local_model_dir": [3, 7], "custom": [3, 7, 8], "weight": [3, 6, 7], "cache_dir": [3, 7, 8], "dir": [3, 7, 8], "where": [3, 7, 8], "huggingfac": [3, 6, 7, 8], "ar": [3, 4, 5, 6, 7, 8], "store": [3, 5, 7, 8], "hf_token": [3, 7], "login": 3, "access": [3, 7, 8], "privat": [3, 4], "repo": [3, 10], "allow_mxint8_mdp_io": [3, 7], "allow": [3, 6, 7, 8], "mdp": [3, 7], "io": [3, 7], "traffic": [3, 7], "enable_qnn": [3, 7, 8], "qnn_config": [3, 7, 8], "paramet": [3, 4, 7, 8], "helper": 3, "function": [3, 6, 7], "run": [3, 4, 5, 8], "ai": [3, 5, 6, 7, 11], "100": [3, 5, 6, 7], "platform": [3, 5, 7, 8], "qpc_path": [3, 7, 8], "binari": [3, 7, 8], "after": [3, 5, 7], "constant": [3, 8], "save": [3, 7, 8], "tensor": [3, 7], "slice": [3, 7], "configur": [3, 4, 7, 8], "pass": [3, 7, 8], "deprec": [3, 7], "replac": [3, 6, 7], "onnx_path": [3, 7, 8], "find": [3, 7], "custom_io_file_path": [3, 7], "string": [3, 7, 8], "return": [3, 6, 7], "qaic": [3, 5, 7, 8], "provid": [4, 6, 7, 8], "infrastructur": 4, "differ": [4, 7, 8], "hardwar": [4, 7, 8], "same": [4, 7, 8, 10], "gpu": 4, "flag": [4, 7, 8], "torch": [4, 7, 8], "cuda": 4, "along": [4, 8], "pytorch": [4, 7, 8], "eager": [4, 8], "For": [4, 6, 8], "com": [4, 10], "quic": [4, 10], "effici": [4, 7, 8, 10], "torch_qaic": 4, "assum": 4, "opt": [4, 5, 7], "qti": [4, 5, 7], "aic": [4, 5, 7], "integr": [4, 5, 7, 8], "py310": 4, "cp310": 4, "linux_x86_64": 4, "whl": 4, "env": [4, 10], "variabl": [4, 8], "hf_datasets_trust_remote_cod": 4, "get": 4, "hw": [4, 6], "trace": 4, "debug": [4, 7], "log": [4, 7], "qaic_device_log_level": 4, "qaic_debug": 4, "understand": 4, "cpu": [4, 7], "fallback": 4, "op": 4, "alpaca": 4, "link": 4, "place": 4, "under": [4, 5, 8], "make": [4, 6, 7, 8], "sure": [4, 8], "updat": [4, 6, 7], "accordingli": 4, "wget": 4, "c": [4, 5], "raw": 4, "githubusercont": 4, "tatsu": 4, "lab": 4, "stanford_alpaca": 4, "ref": 4, "head": [4, 7, 8], "main": 4, "alpaca_data": 4, "json": [4, 7, 8], "p": 4, "grammar": 4, "datasets_grammar": 4, "cloud": [4, 5, 6, 7], "meta": [4, 6, 8, 11], "llama": [4, 6, 7, 8, 11], "1b": [4, 6, 8, 11], "also": [4, 8], "variou": [4, 8], "more": [4, 6, 7, 8], "checkout": [4, 8], "py": 4, "below": [4, 7, 8], "peft": [4, 6, 7, 8], "output_dir": [4, 8], "sam": [4, 8], "num_epoch": [4, 8], "context_length": [4, 8], "256": [4, 7, 8], "qaic_visible_devic": 4, "torchrun": 4, "nproc": 4, "per": 4, "node": [4, 7], "enable_ddp": 4, "dist_backend": 4, "qccl": 4, "worker": 4, "local": [4, 6, 7, 8], "tensorboard": 4, "insid": 4, "date": 4, "time": [4, 7, 8], "stamp": 4, "visualis": 4, "data": [4, 7], "logdir": 4, "bind_al": 4, "system": 5, "linux": 5, "o": [5, 7, 8], "ubuntu": 5, "rhel": 5, "aw": 5, "shard": 5, "uninstal": 5, "sudo": 5, "sh": 5, "script": [5, 8], "root": 5, "permiss": 5, "qeff": [5, 7, 8, 11], "sourc": [5, 6, 7, 10], "dev": 5, "bin": [5, 10], "activ": [5, 7, 8, 10], "On": 5, "success": [5, 8], "content": 5, "exec": [5, 7], "version": [5, 7], "follow": [5, 8], "tool": 5, "appli": 5, "chmod": 5, "x": 5, "hexagon_tool": 5, "abov": [5, 8], "method": [5, 7], "correctli": 5, "import": [5, 7, 8], "print": [5, 7, 8], "__version__": 5, "successfulli": 5, "good": 5, "go": 5, "ahead": [5, 8], "deploi": 5, "infer": [6, 7], "develop": [6, 8], "centric": 6, "toolchain": 6, "reimplement": 6, "block": [6, 8], "which": [6, 7, 8], "highli": [6, 7], "we": [6, 7, 8], "wide": 6, "rang": 6, "architectur": [6, 7, 8, 11], "easi": 6, "deploy": 6, "need": [6, 7, 8], "from": [6, 7, 8], "take": [6, 7, 8], "care": 6, "": [6, 7], "implement": 6, "other": [6, 7, 8], "comprehens": 6, "inspir": 6, "upon": [6, 8], "typic": 6, "retent": [6, 7], "intermedi": 6, "state": [6, 7, 8], "read": 6, "graph": [6, 7, 8], "kei": [6, 8], "oper": 6, "lower": 6, "some": [6, 7], "mathemat": 6, "equival": 6, "backend": [6, 8], "handl": [6, 7, 8], "underflow": 6, "overflow": [6, 8], "patcher": 6, "modul": [6, 7], "map": [6, 7], "origin": [6, 7, 8], "applic": [6, 7, 8], "demo": [6, 8], "notebook": [6, 8], "unit": 6, "test": 6, "templat": 6, "latest": 6, "new": [6, 7, 8], "popular": 6, "01": 6, "2025": 6, "fp8": [6, 8], "ad": [6, 7, 8], "ibm": [6, 11], "granit": [6, 11], "11": 6, "2024": 6, "finit": [6, 7, 8], "adapt": [6, 7, 8, 11], "mix": [6, 7, 8], "tlm": [6, 8], "than": 6, "logit": [6, 7], "dure": [6, 7, 8], "70b": [6, 8, 11], "3b": [6, 11], "09": 6, "awq": [6, 8], "gptq": [6, 8], "bit": 6, "quantiz": [6, 8], "now": [6, 7, 8], "guardian": [6, 11], "gemma": [6, 11], "famili": [6, 11], "codegemma": [6, 11], "8b": [6, 8, 11], "20b": [6, 11], "code": [6, 7, 8, 11], "8k": [6, 11], "starcoder1": [6, 11], "15b": [6, 11], "08": 6, "techniqu": [6, 8], "jai": [6, 11], "13b": [6, 11], "chat": [6, 8, 11], "7b": [6, 7, 11], "06": 6, "gpt": [6, 11], "j": [6, 11], "6b": [6, 11], "qwen2": [6, 11], "5b": [6, 11], "starcoder2": [6, 11], "phi3": 6, "mini": [6, 11], "4k": [6, 11], "codestr": [6, 11], "22b": [6, 11], "v0": [6, 7, 11], "vicuna": [6, 11], "v1": [6, 8, 11], "5": [6, 11], "05": 6, "mixtral": [6, 11], "8x7b": [6, 11], "mistral": [6, 7, 11], "04": 6, "initi": [6, 7, 8], "releas": 6, "seamless": [6, 8], "give": 7, "an": [7, 8], "overview": 7, "about": 7, "might": 7, "modeling_auto": 7, "continuous_batch": 7, "is_tlm": [7, 8], "kwarg": 7, "The": [7, 8], "design": [7, 8], "manipul": 7, "ani": [7, 8], "causal": 7, "hub": 7, "although": 7, "directli": [7, 8], "recommend": 7, "from_pretrain": [7, 8], "nn": 7, "weather": 7, "futur": [7, 8], "later": 7, "whether": 7, "target": [7, 8], "num_logits_to_keep": 7, "arrai": [7, 8], "have": [7, 8, 10], "fed": [7, 8], "control": [7, 8], "prefil": [7, 8], "autotoken": 7, "num_hidden_lay": 7, "prefill_seq_len": 7, "16": [7, 8], "num_devic": 7, "hi": 7, "export_dir": 7, "compile_dir": 7, "kv_cache_batch_s": 7, "mxfp6_matmul": 7, "mxint8_kv_cach": 7, "num_speculative_token": [7, 8], "compiler_opt": 7, "found": [7, 8], "ha": 7, "been": [7, 8, 10], "yet": 7, "process": [7, 8], "argument": [7, 8], "extra": [7, 8], "less": 7, "ctx": 7, "rememb": 7, "mean": 7, "pretrainedtokenizerfast": 7, "pretrainedtoken": 7, "device_id": 7, "runtime_ai100": 7, "output": 7, "until": 7, "eo": 7, "sequenti": 7, "cannot": 7, "divid": [7, 8], "last": 7, "unfulfil": 7, "drop": 7, "union": 7, "case": [7, 8], "normal": 7, "ai_100": 7, "runtim": [7, 8], "similar": [7, 8], "automodel": 7, "consid": 7, "sku": 7, "prepar": 7, "my": [7, 8], "return_tensor": 7, "pt": 7, "seq_len": 7, "ndarrai": 7, "np": [7, 8], "dict": 7, "cloud_ai_100_feature_gener": 7, "A": [7, 8], "session": [7, 8], "dictionari": 7, "pytorch_feature_gener": 7, "each": 7, "auto": [7, 11], "load": 7, "lora": [7, 8], "current": 7, "onc": [7, 8], "anoth": 7, "predibas": 7, "magicod": 7, "1024": 7, "math": 7, "load_adapt": 7, "gsm8k": 7, "set_adapt": 7, "model_id": 7, "adapter_nam": 7, "properti": 7, "active_adapt": 7, "classmethod": 7, "pretrained_name_or_path": 7, "finite_adapt": 7, "pleas": [7, 8], "refer": [7, 8], "identifi": 7, "addit": 7, "autopeftmodelforcausallm": 7, "specifi": [7, 8], "suffix": 7, "hash": 7, "correspond": [7, 8], "ai100": 7, "avoid": 7, "reus": [7, 8], "matmul": 7, "faster": [7, 8], "param": [7, 8], "convert": 7, "aic_num_cor": 7, "num": 7, "convert_to_fp16": 7, "fp16": [7, 8], "alloc": 7, "sequenc": [7, 8], "chunk": 7, "accord": 7, "space": 7, "generation_config": 7, "generationconfig": 7, "stopping_criteria": 7, "stoppingcriteria": 7, "streamer": 7, "basestream": 7, "input_id": 7, "merg": 7, "stop": 7, "point": 7, "put": 7, "while": [7, 8], "prompt_to_adapter_map": 7, "mistralai": [7, 11], "gsm8k_id": 7, "download_adapt": 7, "adapter_model_id": 7, "adapter_weight": 7, "adapter_config": 7, "peftconfig": 7, "unload_adapt": 7, "deactiv": 7, "adpat": 7, "remov": 7, "unload": 7, "don": [7, 8], "t": [7, 8], "non": 7, "legaci": 7, "match": 7, "picker": 7, "onnxrt": 7, "kv_offload": 7, "work": [8, 10], "best": [], "purpos": [], "both": 8, "dual": 7, "approach": [7, 8], "attribut": 7, "_hf_auto_class": 7, "imagetexttotext": 7, "pretrained_model_name_or_path": [], "toggl": [], "between": [], "otherwis": [], "speech": [7, 8, 11], "includ": [7, 8], "whisper": [7, 11], "encod": 7, "processor": 7, "autoprocessor": 7, "automodelforspeechseq2seq": 7, "input_audio": 7, "sample_r": 7, "via": [7, 8], "extern": 7, "librosa": 7, "soundfil": 7, "input_featur": 7, "sampling_r": 7, "numpi": 7, "astyp": 7, "float32": 7, "decoder_input_id": 7, "ones": 7, "dtype": 7, "int64": 7, "decoder_start_token_id": 7, "decoder_position_id": 7, "arang": 7, "view": 7, "repeat": 7, "150": 7, "encoder_ctx_len": 7, "1500": 7, "keep": [], "textstream": 7, "endoftranscript": 7, "upto": 7, "export_hf_to_cloud_ai_100": 7, "model_kv": 7, "qeffbasemodel": 7, "onnx_dir_path": 7, "seq_length": 7, "form_factor": 7, "tupl": 7, "alia": 7, "object": [7, 8], "In": 7, "gate": 7, "bert": [7, 11], "style": 7, "form": 7, "factor": 7, "accept": 7, "base_path": 7, "onnx_model_path": 7, "sinc": 7, "19": 7, "instead": [7, 8], "compile_help": 7, "join": [7, 8], "14": [7, 8], "text_generation_infer": 7, "generated_text": 7, "generated_id": 7, "perf_metr": 7, "hold": 7, "inform": 7, "metric": 7, "prefill_tim": 7, "float": 7, "decode_perf": 7, "total_perf": 7, "total_tim": 7, "total": 7, "total_decoded_token": 7, "loop_start": 7, "end": [7, 8], "decode_pause_tim": 7, "calcul": [7, 8], "latenc": [7, 8], "loop": 7, "count": 7, "stage": [7, 8], "paus": 7, "enable_debug_log": 7, "stream": 7, "write_io_dir": 7, "autom": 7, "prompt_to_lora_id_map": 7, "them": [7, 8], "write": 7, "stat": [7, 8], "associ": 7, "respect": 7, "exec_info": 7, "adjust": 7, "fetch": 7, "dimens": 7, "special": [7, 8], "comput": [7, 8], "compris": 7, "qeff_model": [7, 8], "particularli": [7, 8], "suitabl": 7, "regress": 7, "involv": 7, "contextu": 7, "earlier": 7, "crucial": 7, "predict": [7, 8], "next": 7, "inclus": 7, "enhanc": [7, 8], "computation": 7, "bertstyl": 7, "No": 7, "separ": [7, 8], "logic": 7, "everi": 7, "max_length": 7, "device_util": 7, "avail": [7, 8], "generate_input": 7, "prepare_ort_input": 7, "creat": [7, 8, 10], "position_id": 7, "past_key_valu": 7, "prepare_pytorch_input": 7, "update_ort_input": 7, "ort_output": 7, "previou": 7, "iter": 7, "update_ort_output": 7, "updated_output": 7, "update_pytorch_input": 7, "pt_output": 7, "run_util": 7, "run_hf_model_on_pytorch": 7, "model_hf": 7, "run_hf_model_on_pytorch_cb": 7, "run_kv_model_on_cloud_ai_100": 7, "run_kv_model_on_ort": 7, "model_path": 7, "onnxruntim": [7, 8], "run_kv_model_on_pytorch": 7, "run_ort_sess": 7, "retain": 7, "capi": 7, "onnxruntime_inference_collect": 7, "inferencesess": 7, "wa": 8, "goal": 8, "onboard": 8, "straightforward": 8, "leverag": 8, "complet": 8, "achiev": 8, "abstract": 8, "awai": 8, "complex": 8, "offer": 8, "simpler": 8, "thei": 8, "re": 8, "ideal": 8, "prototyp": 8, "technologi": 8, "want": 8, "minim": 8, "granular": 8, "when": 8, "necessari": 8, "impact": 8, "upcom": 8, "increas": 8, "better": 8, "long": 8, "swift": 8, "overhead": 8, "valu": 8, "pair": 8, "lead": 8, "improv": 8, "throughput": 8, "attent": 8, "progress": 8, "cost": 8, "rag": 8, "automodelforimagetexttotext": 8, "class": [8, 11], "advanc": 8, "facilit": 8, "significantli": 8, "speed": 8, "share": 8, "prefix": 8, "redund": 8, "lookup": 8, "up": 8, "overlap": 8, "part": 8, "without": 8, "lose": 8, "qualiti": 8, "fine": 8, "tune": 8, "rank": 8, "vector": 8, "retriev": 8, "preliminari": 8, "verifi": 8, "lorax": 8, "At": 8, "within": 8, "cpp": 8, "inferenc": 8, "flexibl": 8, "dynam": 8, "request": 8, "ensur": 8, "resourc": 8, "serv": 8, "yield": 8, "perplex": 8, "evalu": 8, "comparison": 8, "across": 8, "replic": 8, "modifi": 8, "By": 8, "program": 8, "readi": 8, "qeff_cach": 8, "environ": 8, "qeff_hom": 8, "its": 8, "xdg_cache_hom": 8, "note": 8, "rerout": 8, "entir": 8, "neither": 8, "nor": 8, "e2": 8, "model_card": 8, "doc": 8, "It": 8, "skip": 8, "second": 8, "automat": [8, 11], "creation": 8, "out": 8, "help": 8, "pipe": 8, "symbol": 8, "flat": 8, "earth": 8, "theori": 8, "belief": 8, "sun": 8, "rise": 8, "lot": 8, "first": 8, "precompil": 8, "qpc_16cores_1bs_32pl_128cl_1devices_mxfp6": 8, "predefin": 8, "pipelin": 8, "subsect": 8, "mq": 8, "just": 8, "group": 8, "fly": 8, "soc": 8, "salesforc": 8, "codegen": 8, "2b": [8, 11], "mono": 8, "def": 8, "fibonacci": 8, "n": 8, "step": 8, "model_card_nam": 8, "pick": 8, "qpc_16cores_1bs_32pl_128cl_2devices_mxfp6": 8, "binary_search": 8, "k": 8, "disabl": 8, "like": 8, "again": 8, "full_batch_size_valu": 8, "regular": 8, "wai": 8, "tinyllama": 8, "tinyllama_v1": 8, "qnn_sdk_root": 8, "qnn_sdk_folder": 8, "add": 8, "overrid": 8, "With": 8, "qpc_qnn_16cores_1bs_32pl_128cl_1devices_mxfp6": 8, "framework": 8, "variat": 8, "automodelforcausallm": 8, "co": [8, 11], "xl": 8, "lib": 8, "generated_qpc_path": 8, "qnn_config_file_path": 8, "advantag": 8, "fall": 8, "rais": 8, "issu": 8, "troubl": 8, "uncom": 8, "appropri": 8, "transformers_cach": 8, "mnt": 8, "workspac": 8, "hf_cach": 8, "root_dir": 8, "dirnam": 8, "abspath": 8, "tmp": 8, "locat": 8, "f": 8, "clip": 8, "v": 8, "Then": 8, "yaml": 8, "benchmark": 8, "tok": 8, "sec": 8, "post": 8, "greedi": 8, "small": [8, 11], "dlm": 8, "autoregress": 8, "what": 8, "would": 8, "benefici": 8, "phase": 8, "bound": 8, "thu": 8, "our": 8, "tlm_name": 8, "dlm_name": 8, "instanti": 8, "becaus": 8, "slight": 8, "defin": 8, "actual": 8, "As": 8, "warn": 10, "compat": 10, "upgrad": 10, "mai": 10, "result": 10, "certain": 10, "becom": 10, "incompat": 10, "virtual": 10, "10": 10, "python3": 10, "venv": 10, "qeff_env": 10, "u": 10, "clone": 10, "git": 10, "repres": 11, "cb": 11, "falconforcausallm": 11, "falcon": 11, "tiiuae": 11, "40b": 11, "gemmaforcausallm": 11, "googl": 11, "9b": 11, "27b": 11, "gptbigcodeforcausallm": 11, "bigcod": 11, "starcod": 11, "gptjforcausallm": 11, "eleutherai": 11, "gpt2lmheadmodel": 11, "openai": 11, "commun": 11, "graniteforcausallm": 11, "internvlchatmodel": 11, "intern": 11, "vl": 11, "opengvlab": 11, "internvl2_5": 11, "llamaforcausallm": 11, "codellama": 11, "34b": 11, "deepseek": 11, "r1": 11, "distil": 11, "inceptionai": 11, "lmsy": 11, "delta": 11, "mistralforcausallm": 11, "mixtralforcausallm": 11, "mptforcausallm": 11, "mpt": 11, "mosaicml": 11, "phi3forcausallm": 11, "phi": 11, "microsoft": 11, "qwenforcausallm": 11, "qwen": 11, "32b": 11, "bertmodel": 11, "baai": 11, "bge": 11, "en": 11, "e5": 11, "v2": 11, "llamamodel": 11, "intfloat": 11, "mpnetformaskedlm": 11, "mpnet": 11, "sentenc": 11, "qa": 11, "mistralmodel": 11, "nomicbertmodel": 11, "nomicbert": 11, "nomic": 11, "emb": 11, "qwen2forcausallm": 11, "stella_en_1": 11, "5b_v5": 11, "robertamodel": [], "roberta": 11, "30m": [], "english": [], "125m": [], "xlmrobertaforsequenceclassif": 11, "xlm": 11, "rerank": 11, "m3bge": 11, "m3": 11, "xlmrobertamodel": [], "107m": [], "multilingu": [], "278m": [], "qeffautomodelimagetexttotext": 11, "llavaforconditionalgener": 11, "llava": 11, "mllamaforconditionalgener": 11, "11b": 11, "90b": 11, "recognit": 11, "transcript": 11, "tini": 11, "medium": 11, "v3": 11, "turbo": 11, "baichuanforcausallm": 11, "baichuan2": 11, "baichuan": 11, "inc": 11, "cohereforcausallm": 11, "cohereforai": 11, "c4ai": 11, "v01": 11, "dbrxforcausallm": 11, "dbrx": 11, "databrick": 11, "factori": 7, "instanc": 7, "decoder_ctx_len": 7, "feature_len": 7, "3000": 7, "rate": 7}, "objects": {"QEfficient.cloud.execute": [[3, 0, 0, "-", "main"]], "QEfficient.cloud.export": [[3, 0, 0, "-", "main"]], "QEfficient.cloud.finetune": [[3, 0, 0, "-", "main"]], "QEfficient.cloud.infer": [[3, 0, 0, "-", "main"]], "QEfficient.compile": [[7, 0, 0, "-", "compile_helper"]], "QEfficient.compile.compile_helper": [[7, 1, 1, "", "compile"], [3, 0, 0, "-", "compile"]], "QEfficient.exporter": [[7, 0, 0, "-", "export_hf_to_cloud_ai_100"]], "QEfficient.exporter.export_hf_to_cloud_ai_100": [[7, 1, 1, "", "convert_to_cloud_bertstyle"], [7, 1, 1, "", "convert_to_cloud_kvstyle"], [7, 1, 1, "", "qualcomm_efficient_converter"]], "QEfficient.generation": [[7, 0, 0, "-", "text_generation_inference"]], "QEfficient.generation.text_generation_inference": [[7, 2, 1, "", "CloudAI100ExecInfo"], [7, 2, 1, "", "CloudAI100ExecInfoNew"], [7, 2, 1, "", "PerfMetrics"], [7, 1, 1, "", "calculate_latency"], [7, 1, 1, "", "cloud_ai_100_exec_kv"], [7, 1, 1, "", "fix_prompt_to_lora_id_mapping"], [7, 1, 1, "", "fix_prompts"], [7, 1, 1, "", "get_compilation_dims"]], "QEfficient.peft.auto": [[7, 2, 1, "", "QEffAutoPeftModelForCausalLM"]], "QEfficient.peft.auto.QEffAutoPeftModelForCausalLM": [[7, 3, 1, "", "active_adapter"], [7, 4, 1, "", "compile"], [7, 4, 1, "", "export"], [7, 4, 1, "", "from_pretrained"], [7, 4, 1, "", "generate"], [7, 4, 1, "", "load_adapter"], [7, 4, 1, "", "set_adapter"]], "QEfficient.peft.lora.auto": [[7, 2, 1, "", "QEffAutoLoraModelForCausalLM"]], "QEfficient.peft.lora.auto.QEffAutoLoraModelForCausalLM": [[7, 4, 1, "", "download_adapter"], [7, 4, 1, "", "export"], [7, 4, 1, "", "generate"], [7, 4, 1, "", "load_adapter"], [7, 4, 1, "", "unload_adapter"]], "QEfficient.transformers.models.modeling_auto": [[7, 2, 1, "", "QEFFAutoModel"], [7, 2, 1, "", "QEFFAutoModelForCausalLM"], [7, 2, 1, "", "QEFFAutoModelForImageTextToText"], [7, 2, 1, "", "QEFFAutoModelForSpeechSeq2Seq"]], "QEfficient.transformers.models.modeling_auto.QEFFAutoModel": [[7, 4, 1, "", "cloud_ai_100_feature_generate"], [7, 4, 1, "", "compile"], [7, 4, 1, "", "export"], [7, 4, 1, "", "generate"], [7, 4, 1, "", "pytorch_feature_generate"]], "QEfficient.transformers.models.modeling_auto.QEFFAutoModelForCausalLM": [[7, 4, 1, "", "compile"], [7, 4, 1, "", "export"], [7, 4, 1, "", "generate"]], "QEfficient.transformers.models.modeling_auto.QEFFAutoModelForSpeechSeq2Seq": [[7, 4, 1, "", "compile"], [7, 4, 1, "", "export"], [7, 4, 1, "", "generate"]], "QEfficient.utils": [[7, 0, 0, "-", "device_utils"], [7, 0, 0, "-", "generate_inputs"], [7, 0, 0, "-", "run_utils"]], "QEfficient.utils.device_utils": [[7, 1, 1, "", "get_available_device_id"]], "QEfficient.utils.generate_inputs": [[7, 2, 1, "", "InputHandler"]], "QEfficient.utils.generate_inputs.InputHandler": [[7, 4, 1, "", "prepare_ort_inputs"], [7, 4, 1, "", "prepare_pytorch_inputs"], [7, 4, 1, "", "update_ort_inputs"], [7, 4, 1, "", "update_ort_outputs"], [7, 4, 1, "", "update_pytorch_inputs"]], "QEfficient.utils.run_utils": [[7, 2, 1, "", "ApiRunner"]], "QEfficient.utils.run_utils.ApiRunner": [[7, 4, 1, "", "run_hf_model_on_pytorch"], [7, 4, 1, "", "run_hf_model_on_pytorch_CB"], [7, 4, 1, "", "run_kv_model_on_cloud_ai_100"], [7, 4, 1, "", "run_kv_model_on_ort"], [7, 4, 1, "", "run_kv_model_on_pytorch"], [7, 4, 1, "", "run_ort_session"]]}, "objtypes": {"0": "py:module", "1": "py:function", "2": "py:class", "3": "py:property", "4": "py:method"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "function", "Python function"], "2": ["py", "class", "Python class"], "3": ["py", "property", "Python property"], "4": ["py", "method", "Python method"]}, "titleterms": {"doc": 0, "build": 0, "preview": 0, "local": 0, "welcom": 1, "effici": [1, 2, 5, 6], "transform": [1, 2, 5, 6, 8], "document": 1, "get": 1, "start": [1, 8], "instal": [1, 4, 5], "upgrad": 1, "infer": [1, 2, 3, 8], "cloud": [1, 2, 3, 8, 9], "ai": [1, 2, 8, 9], "100": [1, 2, 8], "qaic": [1, 4], "finetun": [1, 3, 4, 8], "blog": 1, "refer": [1, 9], "train": [2, 4], "anywher": 2, "qualcomm": [2, 6, 9], "how": 2, "quadrupl": 2, "llm": 2, "decod": [2, 8], "perform": 2, "specul": [2, 8], "spd": 2, "microsc": [2, 9], "mx": [2, 9], "format": [2, 9], "power": 2, "acceler": 2, "larg": 2, "languag": [2, 11], "model": [2, 8, 11], "sdk": [2, 5, 9], "2x": 2, "us": [2, 3, 10], "introduc": 2, "One": 2, "api": [2, 7, 8, 9], "infinit": 2, "possibl": 2, "command": [3, 8], "line": [3, 8], "interfac": [3, 8], "cli": 3, "qeffici": [3, 8], "execut": [3, 7, 8], "compil": [3, 7, 8], "export": [3, 7, 8], "infra": 4, "dataset": 4, "detail": 4, "usag": 4, "singl": 4, "soc": 4, "distribut": 4, "ddp": 4, "visual": 4, "pre": 5, "requisit": 5, "small": 5, "1": [5, 8], "download": [5, 8, 9], "app": 5, "2": [5, 8], "saniti": 5, "check": 5, "introduct": 6, "librari": 6, "python": [7, 8], "high": 7, "level": 7, "qeffautomodelforcausallm": 7, "qeffautomodel": 7, "qeffautopeftmodelforcausallm": 7, "qeffautoloramodelforcausallm": 7, "qeffautomodelforimagetexttotext": 7, "qeffautomodelforspeechseq2seq": 7, "low": 7, "convert_to_cloud_kvstyl": 7, "convert_to_cloud_bertstyl": 7, "util": 7, "apirunn": 7, "class": 7, "i": 7, "respons": 7, "run": 7, "quick": 8, "support": 8, "featur": 8, "qpc": 8, "storag": 8, "multi": 8, "qranium": 8, "continu": 8, "batch": 8, "qnn": 8, "optim": 8, "one": 8, "3": 8, "draft": 8, "base": 8, "home": 9, "user": 9, "guid": 9, "ocp": 9, "specif": 9, "github": 10, "repositori": 10, "valid": 11, "text": 11, "onli": 11, "gener": 11, "task": 11, "embed": 11, "multimod": 11, "vision": 11, "imag": 11, "audio": 11, "come": 11, "soon": 11}, "envversion": {"sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx.ext.todo": 2, "sphinx.ext.viewcode": 1, "sphinx": 58}, "alltitles": {"Docs": [[0, "docs"]], "Build the docs": [[0, "build-the-docs"]], "Preview the docs locally": [[0, "preview-the-docs-locally"]], "Welcome to Efficient-Transformers Documentation!": [[1, "welcome-to-efficient-transformers-documentation"]], "Getting Started": [[1, null]], "Installation": [[1, null], [4, "installation"], [5, "installation"]], "Upgrade Efficient-Transformers": [[1, null]], "Inference on Cloud AI 100": [[1, null]], "QAIC Finetune": [[1, null]], "Blogs": [[1, null]], "Reference": [[1, null]], "Train anywhere, Infer on Qualcomm Cloud AI 100": [[2, "train-anywhere-infer-on-qualcomm-cloud-ai-100"]], "How to Quadruple LLM Decoding Performance with Speculative Decoding (SpD) and Microscaling (MX) Formats on Qualcomm\u00ae Cloud AI 100": [[2, "how-to-quadruple-llm-decoding-performance-with-speculative-decoding-spd-and-microscaling-mx-formats-on-qualcomm-cloud-ai-100"]], "Power-efficient acceleration for large language models \u2013 Qualcomm Cloud AI SDK": [[2, "power-efficient-acceleration-for-large-language-models-qualcomm-cloud-ai-sdk"]], "Qualcomm Cloud AI 100 Accelerates Large Language Model Inference by ~2x Using Microscaling (Mx) Formats": [[2, "qualcomm-cloud-ai-100-accelerates-large-language-model-inference-by-2x-using-microscaling-mx-formats"]], "Qualcomm Cloud AI Introduces Efficient Transformers: One API, Infinite Possibilities": [[2, "qualcomm-cloud-ai-introduces-efficient-transformers-one-api-infinite-possibilities"]], "Command Line Interface Use (CLI)": [[3, "command-line-interface-use-cli"]], "QEfficient.cloud.infer": [[3, "module-QEfficient.cloud.infer.main"], [8, "qefficient-cloud-infer"]], "QEfficient.cloud.execute": [[3, "module-QEfficient.cloud.execute.main"], [8, "qefficient-cloud-execute"]], "QEfficient.cloud.compile": [[3, "qefficient-cloud-compile"]], "QEfficient.cloud.export": [[3, "qefficient-cloud-export"]], "QEfficient.cloud.finetune": [[3, "qefficient-cloud-finetune"], [8, "qefficient-cloud-finetune"]], "Finetune Infra": [[4, "finetune-infra"]], "Finetuning": [[4, "finetuning"]], "Dataset Details": [[4, "dataset-details"]], "Usage": [[4, "usage"]], "Single SOC finetuning on QAIC": [[4, "single-soc-finetuning-on-qaic"]], "Distributed training(DDP) on QAIC": [[4, "distributed-training-ddp-on-qaic"]], "Visualization": [[4, "visualization"]], "Pre-requisites": [[5, "pre-requisites"]], " 1. Download Apps SDK": [[5, "download-apps-sdk"]], " 2. Install Efficient-Transformers": [[5, "install-efficient-transformers"]], "Sanity Check": [[5, "sanity-check"]], "Introduction Qualcomm efficient-transformers library": [[6, "introduction-qualcomm-efficient-transformers-library"]], "Qualcomm Cloud AI home": [[9, "qualcomm-cloud-ai-home"]], "Qualcomm Cloud AI SDK download": [[9, "qualcomm-cloud-ai-sdk-download"]], "Qualcomm Cloud AI API reference": [[9, "qualcomm-cloud-ai-api-reference"]], "User Guide": [[9, "user-guide"]], "OCP Microscaling Formats (MX) Specification": [[9, "ocp-microscaling-formats-mx-specification"]], "Using GitHub Repository": [[10, "using-github-repository"]], "Python API": [[7, "python-api"], [8, "python-api"]], "High Level API": [[7, "high-level-api"]], "QEFFAutoModelForCausalLM": [[7, "qeffautomodelforcausallm"]], "QEFFAutoModel": [[7, "qeffautomodel"]], "QEffAutoPeftModelForCausalLM": [[7, "qeffautopeftmodelforcausallm"]], "QEffAutoLoraModelForCausalLM": [[7, "qeffautoloramodelforcausallm"]], "QEFFAutoModelForImageTextToText": [[7, "qeffautomodelforimagetexttotext"]], "QEFFAutoModelForSpeechSeq2Seq": [[7, "qeffautomodelforspeechseq2seq"]], "export": [[7, "module-QEfficient.exporter.export_hf_to_cloud_ai_100"]], "compile": [[7, "module-QEfficient.compile.compile_helper"]], "Execute": [[7, "module-QEfficient.generation.text_generation_inference"]], "Low Level API": [[7, "low-level-api"]], "convert_to_cloud_kvstyle": [[7, "module-QEfficient.exporter.export_hf_to_cloud_ai_100"]], "convert_to_cloud_bertstyle": [[7, "module-QEfficient.exporter.export_hf_to_cloud_ai_100"]], "utils": [[7, "module-QEfficient.utils.device_utils"]], "ApiRunner class is responsible for running:": [[7, "apirunner-class-is-responsible-for-running"]], "Quick Start": [[8, "quick-start"]], "Supported Features": [[8, "supported-features"]], "Transformed models and QPC storage": [[8, "transformed-models-and-qpc-storage"]], "Command Line Interface": [[8, "command-line-interface"]], "Multi-Qranium Inference": [[8, "multi-qranium-inference"]], "Continuous Batching": [[8, "continuous-batching"]], "QNN Compilation": [[8, "qnn-compilation"]], "1. Model download and Optimize for Cloud AI 100": [[8, "model-download-and-optimize-for-cloud-ai-100"]], "2. Export and Compile with one API": [[8, "export-and-compile-with-one-api"]], "3. Execute": [[8, "execute"]], "Draft-Based Speculative Decoding": [[8, "draft-based-speculative-decoding"]], "Validated Models": [[11, "validated-models"]], "Text-only Language Models": [[11, "text-only-language-models"]], "Text Generation Task": [[11, "text-generation-task"]], "Embedding Models": [[11, "embedding-models"]], "Text Embedding Task": [[11, "text-embedding-task"]], "Multimodal Language Models": [[11, "multimodal-language-models"]], "Vision-Language Models (Text + Image Generation)": [[11, "vision-language-models-text-image-generation"]], "Audio Models": [[11, "audio-models"]], "Models Coming Soon": [[11, "models-coming-soon"]]}, "indexentries": {"apirunner (class in qefficient.utils.run_utils)": [[7, "QEfficient.utils.run_utils.ApiRunner"]], "cloudai100execinfo (class in qefficient.generation.text_generation_inference)": [[7, "QEfficient.generation.text_generation_inference.CloudAI100ExecInfo"]], "cloudai100execinfonew (class in qefficient.generation.text_generation_inference)": [[7, "QEfficient.generation.text_generation_inference.CloudAI100ExecInfoNew"]], "inputhandler (class in qefficient.utils.generate_inputs)": [[7, "QEfficient.utils.generate_inputs.InputHandler"]], "perfmetrics (class in qefficient.generation.text_generation_inference)": [[7, "QEfficient.generation.text_generation_inference.PerfMetrics"]], "qeffautomodel (class in qefficient.transformers.models.modeling_auto)": [[7, "QEfficient.transformers.models.modeling_auto.QEFFAutoModel"]], "qeffautomodelforcausallm (class in qefficient.transformers.models.modeling_auto)": [[7, "QEfficient.transformers.models.modeling_auto.QEFFAutoModelForCausalLM"]], "qeffautomodelforimagetexttotext (class in qefficient.transformers.models.modeling_auto)": [[7, "QEfficient.transformers.models.modeling_auto.QEFFAutoModelForImageTextToText"]], "qeffautomodelforspeechseq2seq (class in qefficient.transformers.models.modeling_auto)": [[7, "QEfficient.transformers.models.modeling_auto.QEFFAutoModelForSpeechSeq2Seq"]], "qeffautoloramodelforcausallm (class in qefficient.peft.lora.auto)": [[7, "QEfficient.peft.lora.auto.QEffAutoLoraModelForCausalLM"]], "qeffautopeftmodelforcausallm (class in qefficient.peft.auto)": [[7, "QEfficient.peft.auto.QEffAutoPeftModelForCausalLM"]], "qefficient.compile.compile_helper": [[7, "module-QEfficient.compile.compile_helper"]], "qefficient.exporter.export_hf_to_cloud_ai_100": [[7, "module-QEfficient.exporter.export_hf_to_cloud_ai_100"]], "qefficient.generation.text_generation_inference": [[7, "module-QEfficient.generation.text_generation_inference"]], "qefficient.utils.device_utils": [[7, "module-QEfficient.utils.device_utils"]], "qefficient.utils.generate_inputs": [[7, "module-QEfficient.utils.generate_inputs"]], "qefficient.utils.run_utils": [[7, "module-QEfficient.utils.run_utils"]], "active_adapter (qefficient.peft.auto.qeffautopeftmodelforcausallm property)": [[7, "QEfficient.peft.auto.QEffAutoPeftModelForCausalLM.active_adapter"]], "calculate_latency() (in module qefficient.generation.text_generation_inference)": [[7, "QEfficient.generation.text_generation_inference.calculate_latency"]], "cloud_ai_100_exec_kv() (in module qefficient.generation.text_generation_inference)": [[7, "QEfficient.generation.text_generation_inference.cloud_ai_100_exec_kv"]], "cloud_ai_100_feature_generate() (qefficient.transformers.models.modeling_auto.qeffautomodel method)": [[7, "QEfficient.transformers.models.modeling_auto.QEFFAutoModel.cloud_ai_100_feature_generate"]], "compile() (qefficient.peft.auto.qeffautopeftmodelforcausallm method)": [[7, "QEfficient.peft.auto.QEffAutoPeftModelForCausalLM.compile"]], "compile() (qefficient.transformers.models.modeling_auto.qeffautomodel method)": [[7, "QEfficient.transformers.models.modeling_auto.QEFFAutoModel.compile"]], "compile() (qefficient.transformers.models.modeling_auto.qeffautomodelforcausallm method)": [[7, "QEfficient.transformers.models.modeling_auto.QEFFAutoModelForCausalLM.compile"]], "compile() (qefficient.transformers.models.modeling_auto.qeffautomodelforspeechseq2seq method)": [[7, "QEfficient.transformers.models.modeling_auto.QEFFAutoModelForSpeechSeq2Seq.compile"]], "compile() (in module qefficient.compile.compile_helper)": [[7, "QEfficient.compile.compile_helper.compile"]], "convert_to_cloud_bertstyle() (in module qefficient.exporter.export_hf_to_cloud_ai_100)": [[7, "QEfficient.exporter.export_hf_to_cloud_ai_100.convert_to_cloud_bertstyle"]], "convert_to_cloud_kvstyle() (in module qefficient.exporter.export_hf_to_cloud_ai_100)": [[7, "QEfficient.exporter.export_hf_to_cloud_ai_100.convert_to_cloud_kvstyle"]], "download_adapter() (qefficient.peft.lora.auto.qeffautoloramodelforcausallm method)": [[7, "QEfficient.peft.lora.auto.QEffAutoLoraModelForCausalLM.download_adapter"]], "export() (qefficient.peft.auto.qeffautopeftmodelforcausallm method)": [[7, "QEfficient.peft.auto.QEffAutoPeftModelForCausalLM.export"]], "export() (qefficient.peft.lora.auto.qeffautoloramodelforcausallm method)": [[7, "QEfficient.peft.lora.auto.QEffAutoLoraModelForCausalLM.export"]], "export() (qefficient.transformers.models.modeling_auto.qeffautomodel method)": [[7, "QEfficient.transformers.models.modeling_auto.QEFFAutoModel.export"]], "export() (qefficient.transformers.models.modeling_auto.qeffautomodelforcausallm method)": [[7, "QEfficient.transformers.models.modeling_auto.QEFFAutoModelForCausalLM.export"]], "export() (qefficient.transformers.models.modeling_auto.qeffautomodelforspeechseq2seq method)": [[7, "QEfficient.transformers.models.modeling_auto.QEFFAutoModelForSpeechSeq2Seq.export"]], "fix_prompt_to_lora_id_mapping() (in module qefficient.generation.text_generation_inference)": [[7, "QEfficient.generation.text_generation_inference.fix_prompt_to_lora_id_mapping"]], "fix_prompts() (in module qefficient.generation.text_generation_inference)": [[7, "QEfficient.generation.text_generation_inference.fix_prompts"]], "from_pretrained() (qefficient.peft.auto.qeffautopeftmodelforcausallm class method)": [[7, "QEfficient.peft.auto.QEffAutoPeftModelForCausalLM.from_pretrained"]], "generate() (qefficient.peft.auto.qeffautopeftmodelforcausallm method)": [[7, "QEfficient.peft.auto.QEffAutoPeftModelForCausalLM.generate"]], "generate() (qefficient.peft.lora.auto.qeffautoloramodelforcausallm method)": [[7, "QEfficient.peft.lora.auto.QEffAutoLoraModelForCausalLM.generate"]], "generate() (qefficient.transformers.models.modeling_auto.qeffautomodel method)": [[7, "QEfficient.transformers.models.modeling_auto.QEFFAutoModel.generate"]], "generate() (qefficient.transformers.models.modeling_auto.qeffautomodelforcausallm method)": [[7, "QEfficient.transformers.models.modeling_auto.QEFFAutoModelForCausalLM.generate"]], "generate() (qefficient.transformers.models.modeling_auto.qeffautomodelforspeechseq2seq method)": [[7, "QEfficient.transformers.models.modeling_auto.QEFFAutoModelForSpeechSeq2Seq.generate"]], "get_available_device_id() (in module qefficient.utils.device_utils)": [[7, "QEfficient.utils.device_utils.get_available_device_id"]], "get_compilation_dims() (in module qefficient.generation.text_generation_inference)": [[7, "QEfficient.generation.text_generation_inference.get_compilation_dims"]], "load_adapter() (qefficient.peft.auto.qeffautopeftmodelforcausallm method)": [[7, "QEfficient.peft.auto.QEffAutoPeftModelForCausalLM.load_adapter"]], "load_adapter() (qefficient.peft.lora.auto.qeffautoloramodelforcausallm method)": [[7, "QEfficient.peft.lora.auto.QEffAutoLoraModelForCausalLM.load_adapter"]], "module": [[7, "module-QEfficient.compile.compile_helper"], [7, "module-QEfficient.exporter.export_hf_to_cloud_ai_100"], [7, "module-QEfficient.generation.text_generation_inference"], [7, "module-QEfficient.utils.device_utils"], [7, "module-QEfficient.utils.generate_inputs"], [7, "module-QEfficient.utils.run_utils"]], "prepare_ort_inputs() (qefficient.utils.generate_inputs.inputhandler method)": [[7, "QEfficient.utils.generate_inputs.InputHandler.prepare_ort_inputs"]], "prepare_pytorch_inputs() (qefficient.utils.generate_inputs.inputhandler method)": [[7, "QEfficient.utils.generate_inputs.InputHandler.prepare_pytorch_inputs"]], "pytorch_feature_generate() (qefficient.transformers.models.modeling_auto.qeffautomodel method)": [[7, "QEfficient.transformers.models.modeling_auto.QEFFAutoModel.pytorch_feature_generate"]], "qualcomm_efficient_converter() (in module qefficient.exporter.export_hf_to_cloud_ai_100)": [[7, "QEfficient.exporter.export_hf_to_cloud_ai_100.qualcomm_efficient_converter"]], "run_hf_model_on_pytorch() (qefficient.utils.run_utils.apirunner method)": [[7, "QEfficient.utils.run_utils.ApiRunner.run_hf_model_on_pytorch"]], "run_hf_model_on_pytorch_cb() (qefficient.utils.run_utils.apirunner method)": [[7, "QEfficient.utils.run_utils.ApiRunner.run_hf_model_on_pytorch_CB"]], "run_kv_model_on_cloud_ai_100() (qefficient.utils.run_utils.apirunner method)": [[7, "QEfficient.utils.run_utils.ApiRunner.run_kv_model_on_cloud_ai_100"]], "run_kv_model_on_ort() (qefficient.utils.run_utils.apirunner method)": [[7, "QEfficient.utils.run_utils.ApiRunner.run_kv_model_on_ort"]], "run_kv_model_on_pytorch() (qefficient.utils.run_utils.apirunner method)": [[7, "QEfficient.utils.run_utils.ApiRunner.run_kv_model_on_pytorch"]], "run_ort_session() (qefficient.utils.run_utils.apirunner method)": [[7, "QEfficient.utils.run_utils.ApiRunner.run_ort_session"]], "set_adapter() (qefficient.peft.auto.qeffautopeftmodelforcausallm method)": [[7, "QEfficient.peft.auto.QEffAutoPeftModelForCausalLM.set_adapter"]], "unload_adapter() (qefficient.peft.lora.auto.qeffautoloramodelforcausallm method)": [[7, "QEfficient.peft.lora.auto.QEffAutoLoraModelForCausalLM.unload_adapter"]], "update_ort_inputs() (qefficient.utils.generate_inputs.inputhandler method)": [[7, "QEfficient.utils.generate_inputs.InputHandler.update_ort_inputs"]], "update_ort_outputs() (qefficient.utils.generate_inputs.inputhandler method)": [[7, "QEfficient.utils.generate_inputs.InputHandler.update_ort_outputs"]], "update_pytorch_inputs() (qefficient.utils.generate_inputs.inputhandler method)": [[7, "QEfficient.utils.generate_inputs.InputHandler.update_pytorch_inputs"]]}}) \ No newline at end of file diff --git a/source/release/v1.19/source/blogs.html b/source/release/v1.19/source/blogs.html new file mode 100644 index 000000000..b27334ca1 --- /dev/null +++ b/source/release/v1.19/source/blogs.html @@ -0,0 +1,180 @@ + + + + + + + Train anywhere, Infer on Qualcomm Cloud AI 100 — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Train anywhere, Infer on Qualcomm Cloud AI 100

+

Click here

+
+
+

How to Quadruple LLM Decoding Performance with Speculative Decoding (SpD) and Microscaling (MX) Formats on Qualcomm® Cloud AI 100

+

Click here

+
+
+

Power-efficient acceleration for large language models – Qualcomm Cloud AI SDK

+

Click here

+
+
+

Qualcomm Cloud AI 100 Accelerates Large Language Model Inference by ~2x Using Microscaling (Mx) Formats

+

click here

+
+
+

Qualcomm Cloud AI Introduces Efficient Transformers: One API, Infinite Possibilities

+

click here

+
+ + +
+
+ +
+
+
+
+
+ + Version: release/v1.19 + + +
+ Versions +
+
main
+
release/v1.18
+
release/v1.19
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/source/cli_api.html b/source/release/v1.19/source/cli_api.html new file mode 100644 index 000000000..d4f6047ad --- /dev/null +++ b/source/release/v1.19/source/cli_api.html @@ -0,0 +1,415 @@ + + + + + + + Command Line Interface Use (CLI) — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Command Line Interface Use (CLI)

+
+

Note

+

Use bash terminal, else if using ZSH terminal then device_groupshould be in single quotes e.g. '--device_group [0]'

+
+
+

QEfficient.cloud.infer

+
    +
  1. Check if compiled qpc for given config already exists, if it does jump to execute, else

  2. +
  3. Check if exported ONNX file already exists, if true, jump to compilation -> execution, else

  4. +
  5. Check if HF model exists in cache, if true, start transform -> export -> compilation -> execution, else,

  6. +
+

4. Download HF model -> transform -> export -> compile -> execute +Mandatory Args:

+
+
+
model_name (str):
+

Hugging Face Model Card name, Example: gpt2

+
+
num_cores (int):
+

Number of cores to compile model on.

+
+
+
+
+
Optional Args:
+
device_group (List[int]):
+

Device Ids to be used for compilation. If len(device_group) > 1, multiple Card setup is enabled. Defaults to None.

+
+
prompt (str):
+

Sample prompt for the model text generation. Defaults to None.

+
+
prompts_txt_file_path (str):
+

Path to txt file for multiple input prompts. Defaults to None.

+
+
aic_enable_depth_first (bool):
+

Enables DFS with default memory size. Defaults to False.

+
+
mos (int):
+

Effort level to reduce the on-chip memory. Defaults to 1.

+
+
batch_size (int):
+

Batch size to compile the model for. Defaults to 1.

+
+
full_batch_size (int):
+

Set full batch size to enable continuous batching mode. Default to None

+
+
prompt_len (int):
+

Prompt length for the model to compile. Defaults to 32.

+
+
ctx_len (int):
+

Maximum context length to compile the model. Defaults to 128.

+
+
generation_len (int):
+

Number of tokens to be generated. Defaults to False.

+
+
mxfp6 (bool):
+

Enable compilation for MXFP6 precision. Defaults to False.

+
+
mxint8 (bool):
+

Compress Present/Past KV to MXINT8 using CustomIO config. Defaults to False.

+
+
local_model_dir (str):
+

Path to custom model weights and config files. Defaults to None.

+
+
cache_dir (str):
+

Cache dir where downloaded HuggingFace files are stored. Defaults to None.

+
+
hf_token (str):
+

HuggingFace login token to access private repos. Defaults to None.

+
+
allow_mxint8_mdp_io (bool):
+

Allows MXINT8 compression of MDP IO traffic. Defaults to False.

+
+
enable_qnn (bool):
+

Enables QNN Compilation. Defaults to False.

+
+
qnn_config (str):
+

Path of QNN Config parameters file. Defaults to None.

+
+
+
+
+
python -m QEfficient.cloud.infer OPTIONS
+
+
+
+
+

QEfficient.cloud.execute

+

Helper function used by execute CLI app to run the Model on Cloud AI 100 Platform.

+
+
Mandatory Args:
+
model_name (str):
+

Hugging Face Model Card name, Example: gpt2.

+
+
qpc_path (str):
+

Path to the generated binary after compilation.

+
+
+
+
Optional Args:
+
device_group (List[int]):
+

Device Ids to be used for compilation. if len(device_group) > 1. Multiple Card setup is enabled.``Defaults to None.``

+
+
local_model_dir (str):
+

Path to custom model weights and config files. Defaults to None.

+
+
prompt (str):
+

Sample prompt for the model text generation. Defaults to None.

+
+
prompts_txt_file_path (str):
+

Path to txt file for multiple input prompts. Defaults to None.

+
+
generation_len (int):
+

Number of tokens to be generated. Defaults to None.

+
+
cache_dir (str):
+

Cache dir where downloaded HuggingFace files are stored. Defaults to Constants.CACHE_DIR.

+
+
hf_token (str):
+

HuggingFace login token to access private repos. Defaults to None.

+
+
full_batch_size (int):
+

Set full batch size to enable continuous batching mode. Defaults to None.

+
+
+
+
+
python -m QEfficient.cloud.execute OPTIONS
+
+
+
+
+

QEfficient.cloud.compile

+
+

Compiles the given ONNX model using Cloud AI 100 platform SDK compiler and saves the compiled qpc package at qpc_path. +Generates tensor-slicing configuration if multiple devices are passed in device_group.

+

This function will be deprecated soon and will be replaced by QEFFAutoModelForCausalLM.compile.

+
+
Mandatory Args:
+
onnx_path (str):
+

Generated ONNX Model Path.

+
+
qpc_path (str):
+

Path for saving compiled qpc binaries.

+
+
num_cores (int):
+

Number of cores to compile the model on.

+
+
+
+
Optional Args:
+
device_group (List[int]):
+

Used for finding the number of devices to compile for. Defaults to None.

+
+
aic_enable_depth_first (bool):
+

Enables DFS with default memory size. Defaults to False.

+
+
mos (int):
+

Effort level to reduce the on-chip memory. Defaults to -1.

+
+
batch_size (int):
+

Batch size to compile the model for. Defaults to 1.

+
+
full_batch_size (int):
+

Set full batch size to enable continuous batching mode. Default to None

+
+
prompt_len (int):
+

Prompt length for the model to compile. Defaults to 32

+
+
ctx_len (int):
+

Maximum context length to compile the model. Defaults to 128

+
+
mxfp6 (bool):
+

Enable compilation for MXFP6 precision. Defaults to True.

+
+
mxint8 (bool):
+

Compress Present/Past KV to MXINT8 using CustomIO config. Defaults to False.

+
+
custom_io_file_path (str):
+

Path to customIO file (formatted as a string). Defaults to None.

+
+
allow_mxint8_mdp_io (bool):
+

Allows MXINT8 compression of MDP IO traffic Defaults to False.

+
+
enable_qnn (bool):
+

Enables QNN Compilation. Defaults to False.

+
+
qnn_config (str):
+

Path of QNN Config parameters file. Defaults to None.

+
+
+
+
Returns:
+
str:
+

Path to compiled qpc package.

+
+
+
+
+
python -m QEfficient.cloud.compile OPTIONS
+
+
+
+
+
+

QEfficient.cloud.export

+
+

Helper function used by export CLI app for exporting to ONNX Model.

+
+
Mandatory Args:
+
model_name (str):
+

Hugging Face Model Card name, Example: gpt2.

+
+
+
+
Optional Args:
+
cache_dir (str):
+

Cache dir where downloaded HuggingFace files are stored. Defaults to None.

+
+
hf_token (str):
+

HuggingFace login token to access private repos. Defaults to None.

+
+
local_model_dir (str):
+

Path to custom model weights and config files. Defaults to None.

+
+
full_batch_size (int):
+

Set full batch size to enable continuous batching mode. Defaults to None.

+
+
+
+
+
python -m QEfficient.cloud.export OPTIONS
+
+
+
+
+
+

QEfficient.cloud.finetune

+
+

Helper function to finetune the model on QAic.

+
python -m QEfficient.cloud.finetune OPTIONS
+
+
+
+
+
+ + +
+
+ +
+
+
+
+
+ + Version: release/v1.19 + + +
+ Versions +
+
main
+
release/v1.18
+
release/v1.19
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/source/finetune.html b/source/release/v1.19/source/finetune.html new file mode 100644 index 000000000..510b6a14b --- /dev/null +++ b/source/release/v1.19/source/finetune.html @@ -0,0 +1,233 @@ + + + + + + + Finetune Infra — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Finetune Infra

+

This repository provides the infrastructure for finetuning models using different hardware accelerators such as QAIC. +Same CLI can be used to run Finetuning on gpu by setting the device flag.(for finetuning on gpu, install torch specific to cuda)

+
+

Installation

+

Same as QEfficient along with QAIC PyTorch Eager mode.

+

For QEfficient Library : https://github.com/quic/efficient-transformers

+

For torch_qaic, assuming QEfficient is already installed,

+
pip install /opt/qti-aic/integrations/torch_qaic/py310/torch_qaic-0.1.0-cp310-cp310-linux_x86_64.whl
+
+
+
+
+

Finetuning

+

Export the ENV variables to download and enable private datasets

+
export HF_DATASETS_TRUST_REMOTE_CODE=True
+
+
+

Export the ENV variables to get the device and HW traces and debugging logs

+
export QAIC_DEVICE_LOG_LEVEL=0 # For Device level logs
+export QAIC_DEBUG=1 # To understand the CPU fallback ops
+
+
+
+
+

Dataset Details

+

To download the Alpaca dataset, visit this link. Download the dataset and place it under the dataset directory. Make sure to update the training configuration accordingly.

+
wget -c https://raw.githubusercontent.com/tatsu-lab/stanford_alpaca/refs/heads/main/alpaca_data.json -P dataset/
+
+
+

To download the grammar dataset, visit this link. Download the dataset and place it under the datasets_grammar directory. Make sure to update the training configuration accordingly.

+
+
+

Usage

+
+

Single SOC finetuning on QAIC

+
python -m QEfficient.cloud.finetune --device qaic:0 --model_name "meta-llama/Llama-3.2-1B"
+
+
+

Also, you can configure various training parameters, for more details, checkout: QEfficient/finetune/configs/training.py, Below is example command line

+
python -m QEfficient.cloud.finetune --device qaic:0 --use-peft --output_dir ./meta-sam --num_epochs 2 --context_length 256 
+
+
+
+
+

Distributed training(DDP) on QAIC

+
QAIC_VISIBLE_DEVICES=0,1,2,3 torchrun --nproc-per-node 4 -m QEfficient.cloud.finetune --device qaic --enable_ddp --dist_backend qccl --num_epochs 2  --model_name "meta-llama/Llama-3.2-1B"
+
+
+

**nproc-per-node is number of workers(QAIC devices) running locally.

+
+
+
+

Visualization

+

Tensorboard logs are generated inside runs/ directory with date and time stamp. +to visualise the data,

+
tensorboard --logdir runs/<file> --bind_all
+
+
+
+
+ + +
+
+ +
+
+
+
+
+ + Version: release/v1.19 + + +
+ Versions +
+
main
+
release/v1.18
+
release/v1.19
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/source/installation.html b/source/release/v1.19/source/installation.html new file mode 100644 index 000000000..00e0294a7 --- /dev/null +++ b/source/release/v1.19/source/installation.html @@ -0,0 +1,216 @@ + + + + + + + Pre-requisites — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Pre-requisites

+

System Requirements:

+
    +
  1. Supported Linux OS - Ubuntu, RHEL and AWS Linux

  2. +
  3. Cloud AI 100 Platform SDK installed

  4. +
  5. SDK Pre-requisites

  6. +
  7. Multi-device support enabled for model sharding

  8. +
+
+
+

Installation

+
+

1. Download Apps SDK

+ +
+
+

2. Install Efficient-Transformers

+

Uninstall existing Apps SDK

+
sudo ./uninstall.sh
+
+
+

Run the install.sh script as root or with sudo to install with root permissions.

+
sudo ./install.sh --enable-qeff
+source  /opt/qti-aic/dev/python/qeff/bin/activate
+
+
+

On successful installation, the contents are stored to the /opt/qti-aic path under the dev and exec directories:

+
dev exec integrations scripts
+
+
+

Check the Apps SDK version with the following command

+
sudo /opt/qti-aic/tools/qaic-version-util --apps
+
+
+

Apply chmod commands

+
sudo chmod a+x /opt/qti-aic/dev/hexagon_tools/bin/*
+sudo chmod a+x /opt/qti-aic/exec/*
+
+
+
+
+
+

Sanity Check

+

After above installation methods, you can check if QEfficient is installed correctly by using

+
python -c "import QEfficient; print(QEfficient.__version__)"
+
+
+

If the above line executes successfully, you are good to go ahead and start deploying models on Cloud AI 100 cards using QEfficient library.

+
+ + +
+
+ +
+
+
+
+
+ + Version: release/v1.19 + + +
+ Versions +
+
main
+
release/v1.18
+
release/v1.19
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/source/introduction.html b/source/release/v1.19/source/introduction.html new file mode 100644 index 000000000..cbe2af547 --- /dev/null +++ b/source/release/v1.19/source/introduction.html @@ -0,0 +1,218 @@ + + + + + + + Introduction Qualcomm efficient-transformers library — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +

alt text

+
+

Introduction Qualcomm efficient-transformers library

+

Train anywhere, Infer on Qualcomm Cloud AI with a Developer-centric Toolchain

+

This library provides reimplemented blocks of LLMs which are used to make the models functional and highly performant on Qualcomm Cloud AI 100. +We support wide range of models architectures, for easy efficient deployment on Cloud AI 100 cards. Users only need to provide model card from HuggingFace or Path to the local model and the library will take care of transforming model to it’s efficient implementation for Cloud AI 100.

+

For other models, there is comprehensive documentation to inspire upon the changes needed and How-To(s).

+

Typically for LLMs, the library provides:

+
    +
  1. Reimplemented blocks from Transformers which enable efficient on-device retention of intermediate states. read more here

  2. +
  3. Graph transformations to enable execution of key operations in lower precision

  4. +
  5. Graph transformations to replace some operations to other mathematically equivalent operations that are efficient/supported on HW backend

  6. +
  7. Handling for underflow and overflows in lower precision

  8. +
  9. Patcher modules to map weights of original model’s operations to updated model’s operations

  10. +
  11. Exporter module to export the model source into a ONNX Graph.

  12. +
  13. Sample example applications and demo notebooks

  14. +
  15. Unit test templates.

  16. +
+

Latest news :

+ +
+More + +
+
+ + +
+
+ +
+
+
+
+
+ + Version: release/v1.19 + + +
+ Versions +
+
main
+
release/v1.18
+
release/v1.19
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/source/python_api.html b/source/release/v1.19/source/python_api.html new file mode 100644 index 000000000..fafe36ae8 --- /dev/null +++ b/source/release/v1.19/source/python_api.html @@ -0,0 +1,1712 @@ + + + + + + + Python API — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Python API

+

This page give you an overview about the all the APIs that you might need to integrate the QEfficient into your python applications.

+
+

High Level API

+
+

QEFFAutoModelForCausalLM

+
+
+class QEfficient.transformers.models.modeling_auto.QEFFAutoModelForCausalLM(model: Module, continuous_batching: bool = False, is_tlm: bool = False, enable_qnn: bool = False, **kwargs)[source]
+

The QEFF class is designed for manipulating any causal language model from the HuggingFace hub. +Although it is possible to initialize the class directly, we highly recommend using the from_pretrained method for initialization.

+
+
Mandatory Args:
+
model (nn.Module):
+

PyTorch model

+
+
continuous_batching (bool):
+

Weather this model will be used for continuous batching in future. If this is not set True here, the model can not be exported/compiled for continuous batching later.

+
+
is_tlm (bool):
+

Whether this is a Speculative Decoding Target Language Model. If set to True, num_logits_to_keep input array will have to be fed to control the number of returned logits during prefill/decode.

+
+
enable_qnn (bool):
+

Enables QNN Compilation path for the model.

+
+
+
+
+
from QEfficient import QEFFAutoModelForCausalLM
+from transformers import AutoTokenizer
+
+model_name = "gpt2"
+model = QEFFAutoModelForCausalLM.from_pretrained(model_name, num_hidden_layers=2)
+model.compile(prefill_seq_len=128, ctx_len=256, num_cores=16, num_devices=1)
+
+tokenizer = AutoTokenizer.from_pretrained(model_name)
+model.generate(prompts=["Hi there!!"], tokenizer=tokenizer)
+
+
+
+
+export(export_dir: str | None = None) str[source]
+

Exports the model to ONNX format using torch.onnx.export.

+
+
Optional Args:
+
export_dir (str, optional):
+

The directory path to store ONNX-graph.

+
+
+
+
Returns:
+
str:
+

Path of the generated ONNX graph.

+
+
+
+
+
+ +
+
+compile(onnx_path: str | None = None, compile_dir: str | None = None, *, prefill_seq_len: int = 32, ctx_len: int = 128, batch_size: int = 1, full_batch_size: int | None = None, kv_cache_batch_size: int | None = None, num_devices: int = 1, num_cores: int = 16, mxfp6_matmul: bool = False, mxint8_kv_cache: bool = False, num_speculative_tokens: int | None = None, enable_qnn: bool = False, qnn_config: str | None = None, **compiler_options) str[source]
+

This method compiles the exported ONNX model using the Cloud AI 100 Platform SDK compiler binary found at /opt/qti-aic/exec/qaic-exec and generates a qpc package. +If the model has not been exported yet, this method will handle the export process. +You can pass any other arguments that the qaic-exec takes as extra kwargs.

+
+
Optional Args:
+
onnx_path (str, optional):
+

Path to pre-exported onnx model.

+
+
compile_dir (str, optional):
+

Path for saving the qpc generated.

+
+
num_cores (int):
+

Number of cores used to compile the model.

+
+
num_devices (int):
+

Number of devices the model needs to be compiled for. Defaults to 1.

+
+
batch_size (int, optional):
+

Batch size. Defaults to 1.

+
+
prefill_seq_len (int, optional):
+

The length of the Prefill prompt should be less that prefill_seq_len. Defaults to 32.

+
+
ctx_len (int, optional):
+

Maximum ctx that the compiled model can remember. Defaults to 128.

+
+
full_batch_size (int, optional):
+

Continuous batching batch size.

+
+
mxfp6_matmul (bool, optional):
+

Whether to use mxfp6 compression for weights. Defaults to False.

+
+
mxint8_kv_cache (bool, optional):
+

Whether to use mxint8 compression for KV cache. Defaults to False.

+
+
num_speculative_tokens (int, optional):
+

Number of speculative tokens to take as input for Speculative Decoding Target Language Model.

+
+
mos (int, optional):
+

Effort level to reduce on-chip memory. Defaults to -1, meaning no effort. Defaults to -1.

+
+
aic_enable_depth_first (bool, optional):
+

Enables DFS with default memory size. Defaults to False.

+
+
enable_qnn (bool):
+

Enables QNN Compilation. Defaults to False.

+
+
qnn_config (str):
+

Path of QNN Config parameters file. Defaults to None.

+
+
+
+
Returns:
+
str:
+

Path of the compiled qpc package.

+
+
+
+
+
+ +
+
+generate(tokenizer: PreTrainedTokenizerFast | PreTrainedTokenizer, prompts: List[str], device_id: List[int] | None = None, runtime_ai100: bool = True, **kwargs)[source]
+

This method generates output until eos or generation_len by executing the compiled qpc on Cloud AI 100 Hardware cards. +This is a sequential execution based on the batch_size of the compiled model and the number of prompts passed. +If the number of prompts cannot be divided by the batch_size, the last unfulfilled batch will be dropped.

+
+
Mandatory Args:
+
tokenizer (Union[PreTrainedTokenizerFast, PreTrainedTokenizer]):
+

Pass tokenizer of the model.

+
+
prompts (List[str]):
+

List of prompts to run the execution.

+
+
+
+
optional Args:
+
device_id (List[int]):
+

Ids of devices for running the qpc pass as [0] in case of normal model / [0, 1, 2, 3] in case of tensor slicing model

+
+
runtime_ai100 (bool, optional):
+

AI_100 and PyTorch runtime is supported as of now. Defaults to True for AI_100 runtime.

+
+
+
+
+
+ +
+ +
+
+

QEFFAutoModel

+
+
+class QEfficient.transformers.models.modeling_auto.QEFFAutoModel(model: Module, **kwargs)[source]
+

The QEFFAutoModel class is designed for manipulating any transformer model from the HuggingFace hub. +Although it is possible to initialize the class directly, we highly recommend using the from_pretrained method for initialization.

+
+
Mandatory Args:
+
model (nn.Module):
+

PyTorch model

+
+
+
+
+
from QEfficient import QEFFAutoModel
+from transformers import AutoTokenizer
+
+# Initialize the model using from_pretrained similar to transformers.AutoModel.
+model = QEFFAutoModel.from_pretrained("model_name")
+
+# Now you can directly compile the model for Cloud AI 100
+model.compile(num_cores=16)  # Considering you have a Cloud AI 100 SKU
+
+#prepare input
+tokenizer = AutoTokenizer.from_pretrained(model_name)
+inputs = tokenizer("My name is", return_tensors="pt")
+
+# You can now execute the model
+model.generate(inputs)
+
+
+
+
+export(export_dir: str | None = None) str[source]
+

Exports the model to ONNX format using torch.onnx.export.

+
+
Optional Args:
+
export_dir (str, optional):
+

The directory path to store ONNX-graph.

+
+
+
+
Returns:
+
str:
+

Path of the generated ONNX graph.

+
+
+
+
+
+ +
+
+compile(onnx_path: str | None = None, compile_dir: str | None = None, *, seq_len: int = 32, batch_size: int = 1, num_devices: int = 1, num_cores: int = 16, mxfp6_matmul: bool = False, **compiler_options) str[source]
+

This method compiles the exported ONNX model using the Cloud AI 100 Platform SDK compiler binary found at /opt/qti-aic/exec/qaic-exec and generates a qpc package. +If the model has not been exported yet, this method will handle the export process. +You can pass any other arguments that the qaic-exec takes as extra kwargs.

+
+
Optional Args:
+
onnx_path (str, optional):
+

Path to pre-exported onnx model.

+
+
compile_dir (str, optional):
+

Path for saving the qpc generated.

+
+
seq_len (int, optional):
+

The length of the prompt should be less that seq_len. Defaults to 32.

+
+
batch_size (int, optional):
+

Batch size. Defaults to 1.

+
+
num_devices (int):
+

Number of devices the model needs to be compiled for. Defaults to 1.

+
+
num_cores (int):
+

Number of cores used to compile the model.

+
+
mxfp6_matmul (bool, optional):
+

Whether to use mxfp6 compression for weights. Defaults to False.

+
+
aic_enable_depth_first (bool, optional):
+

Enables DFS with default memory size. Defaults to False.

+
+
allow_mxint8_mdp_io (bool, optional):
+

Allows MXINT8 compression of MDP IO traffic. Defaults to False.

+
+
+
+
Returns:
+
str:
+

Path of the compiled qpc package.

+
+
+
+
+
+ +
+
+generate(inputs: Tensor, device_ids: List[int] | None = None, runtime_ai100: bool = True) Tensor | ndarray[source]
+

This method generates output by executing PyTorch runtime or the compiled qpc on Cloud AI 100 Hardware cards. +Mandatory Args:

+
+
+
inputs (Union[torch.Tensor, np.ndarray]):
+

inputs to run the execution.

+
+
+
+
+
optional Args:
+
device_id (List[int]):
+

Ids of devices for running the qpc pass as [0] in case of normal model / [0, 1, 2, 3] in case of tensor slicing model

+
+
runtime_ai100 (bool, optional):
+

AI_100 and PyTorch runtime is supported as of now. Defaults to True for AI_100 runtime.

+
+
+
+
Returns:
+
dict:
+

Output from the AI_100 or PyTorch runtime.

+
+
+
+
+
+ +
+
+cloud_ai_100_feature_generate(inputs: Tensor, device_ids: List[int] = [0]) ndarray[source]
+

Generates features with list of prompts using AI 100 runtime.

+
+
Mandatory Args:
+
inputs (Union[torch.Tensor, np.ndarray]):
+

inputs to run the execution.

+
+
+
+
Optional Args:

device_ids (List[int], optional): A list of device IDs to use for the session. Defaults to [0].

+
+
Returns:

np.ndarray: A list of dictionaries containing the generated output features.

+
+
+
+ +
+
+pytorch_feature_generate(model, inputs: Tensor | ndarray) List[Tensor][source]
+

Generates features from a list of text prompts using a PyTorch model.

+
+
Mandatory Args:
+
model:
+

The transformed PyTorch model used for generating features.

+
+
inputs (Union[torch.Tensor, np.ndarray]):
+

inputs to run the execution.

+
+
+
+
Returns:

torch.Tensor: A list of output features generated by the model for each prompt.

+
+
+
+ +
+ +
+
+

QEffAutoPeftModelForCausalLM

+
+
+class QEfficient.peft.auto.QEffAutoPeftModelForCausalLM(model: Module)[source]
+

QEff class for loading models with PEFT adapters (Only LoRA is supported currently). +Once exported and compiled for an adapter, the same can be utilized for another adapter with same base model and adapter config.

+
+
Args:
+
model (nn.Module):
+

PyTorch model

+
+
+
+
+
from QEfficient import QEffAutoPeftModelForCausalLM
+
+m = QEffAutoPeftModelForCausalLM.from_pretrained("predibase/magicoder", "magicoder")
+m.export()
+m.compile(prefill_seq_len=32, ctx_len=1024)
+
+inputs = ...  # A coding prompt
+outputs = m.generate(**inputs)
+
+inputs = ...  # A math prompt
+m.load_adapter("predibase/gsm8k", "gsm8k")
+m.set_adapter("gsm8k")
+outputs = m.generate(**inputs)
+
+
+
+
+load_adapter(model_id: str, adapter_name: str)[source]
+

Loads a new adapter from huggingface hub or local path

+
+
Args:
+
model_id (str):
+

Adapter model ID from huggingface hub or local path

+
+
adapter_name (str):
+

Adapter name to be used to set this adapter as current

+
+
+
+
+
+ +
+
+property active_adapter: str
+

Currently active adapter to be used for inference

+
+ +
+
+set_adapter(adapter_name: str)[source]
+

Sets active adapter from one of the loaded adapters

+
+ +
+
+classmethod from_pretrained(pretrained_name_or_path: str, *args, **kwargs)[source]
+
+
Args:
+
pretrained_name_or_path (str):
+

Model card name from huggingface or local path to model directory.

+
+
finite_adapters (bool):
+

set True to enable finite adapter mode with QEffAutoLoraModelForCausalLM class. Please refer to QEffAutoLoraModelForCausalLM for API specification.

+
+
adapter_name (str):
+

Name used to identify loaded adapter.

+
+
args, kwargs:
+

Additional arguments to pass to peft.AutoPeftModelForCausalLM.

+
+
+
+
+
+ +
+
+export(export_dir: str | None = None) str[source]
+

Exports the model to ONNX format using torch.onnx.export.

+
+
Args:
+
export_dir (str):
+

Specify the export directory. The export_dir will be suffixed with a hash corresponding to current model.

+
+
+
+
Returns:
+
Path:
+

Path of the generated ONNX file.

+
+
+
+
+
+ +
+
+compile(onnx_path: str | None = None, compile_dir: str | None = None, *, batch_size: int = 1, prefill_seq_len: int, ctx_len: int, num_devices: int = 1, num_cores: int = 16, mxfp6_matmul: bool = False, mxint8_kv_cache: bool = False, **compiler_options) str[source]
+

Compile the exported onnx to run on AI100. +If the model has not been exported yet, this method will handle the export process.

+
+
Args:
+
onnx_path (str):
+

Onnx file to compile

+
+
compile_dir (str):
+

Directory path to compile the qpc. A suffix is added to the directory path to avoid reusing same qpc for different parameters.

+
+
num_devices (int):
+

Number of devices to compile for. Defaults to 1.

+
+
num_cores (int):
+

Number of cores to utilize in each device Defaults to 16.

+
+
mxfp6_matmul (bool):
+

Use MXFP6 to compress weights for MatMul nodes to run faster on device. Defaults to False.

+
+
mxint8_kv_cache (bool):
+

Use MXINT8 to compress KV-cache on device to access and update KV-cache faster. Defaults to False.

+
+
compiler_options:
+

Pass any compiler option as input. Any flag that is supported by qaic-exec can be passed. Params are converted to flags as below: +- aic_num_cores=16 -> -aic-num-cores=16 +- convert_to_fp16=True -> -convert-to-fp16

+
+
+
+
QEFFAutoModelForCausalLM Args:
+
full_batch_size (int):
+

Full batch size to allocate cache lines.

+
+
batch_size (int):
+

Batch size to compile for. Defaults to 1.

+
+
prefill_seq_len (int):
+

Prefill sequence length to compile for. Prompt will be chunked according to this length.

+
+
ctx_len (int):
+

Context length to allocate space for KV-cache tensors.

+
+
+
+
Returns:
+
str:
+

Path of the compiled qpc package.

+
+
+
+
+
+ +
+
+generate(inputs: Tensor | ndarray | None = None, device_ids: List[int] | None = None, generation_config: GenerationConfig | None = None, stopping_criteria: StoppingCriteria | None = None, streamer: BaseStreamer | None = None, **kwargs) ndarray[source]
+

Generate tokens from compiled binary. This method takes same parameters as HuggingFace transformers model.generate() method.

+
+
Args:
+
inputs:
+

input_ids

+
+
generation_config:
+

Merge this generation_config with model-specific for the current generation.

+
+
stopping_criteria:
+

Pass custom stopping_criteria to stop at a specific point in generation.

+
+
streamer:
+

Streamer to put the generated tokens into.

+
+
kwargs:
+

Additional parameters for generation_config or to be passed to the model while generating.

+
+
+
+
+
+ +
+ +
+
+

QEffAutoLoraModelForCausalLM

+
+
+class QEfficient.peft.lora.auto.QEffAutoLoraModelForCausalLM(model: Module, continuous_batching: bool = False, **kwargs)[source]
+

QEff class for loading models with multiple LoRA adapters. Currently only Mistral and Llama model are supported. +Once exported and compiled, the qpc can perform mixed batch inference with provided prompt_to_adapter_mapping.

+
+
Args:
+
model (nn.Module):
+

PyTorch model

+
+
continuous_batching (bool):
+

Weather this model will be used for continuous batching in future. If this is not set True here, the model can not be exported/compiled for continuous batching later.

+
+
+
+
+
from QEfficient.peft.lora import QEffAutoLoraModelForCausalLM
+
+m = QEffAutoPeftModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1")
+m.load_adapter("predibase/gsm8k", "gsm8k")
+m.load_adapter("predibase/magicoder", "magicoder")
+m.compile(num_cores=16, device_group=[0])
+
+prompts=["code prompt", "math prompt", "generic"]
+m.generate(prompts, device_group=[0], prompt_to_adapter_mapping=["magicoder","gsm8k_id","base"])
+
+
+
+
+download_adapter(adapter_model_id: str, adapter_name: str, adapter_weight: dict | None = None, adapter_config: PeftConfig | None = None)[source]
+

Loads a new adapter from huggingface hub or local path into CPU cache

+
+
Mandatory Args:
+
adapter_model_id (str):
+

Adapter model ID from huggingface hub or local path

+
+
adapter_name (str):
+

Adapter name to be used to downloaded this adapter

+
+
+
+
Optional Args:
+
adapter_weight (dict):
+

Adapter weight tensors in dictionary format

+
+
adapter_config (PeftConfig):
+

Adapter config in the format of PeftConfig

+
+
+
+
+
+ +
+
+load_adapter(adapter_model_id: str, adapter_name: str, adapter_weight: dict | None = None, adapter_config: PeftConfig | None = None)[source]
+

Load adapter into CPU cache and set it as active

+
+
Mandatory Args:
+
adapter_model_id (str):
+

Adapter model ID from huggingface hub or local path

+
+
adapter_name (str):
+

Adapter name to be used to load this adapter

+
+
+
+
Optional Args:
+
adapter_weight (dict):
+

Adapter weight tensors in dictionary format

+
+
adapter_config (PeftConfig):
+

Adapter config in the format of PeftConfig

+
+
+
+
+
+ +
+
+unload_adapter(adapter_name: str)[source]
+

Deactivate adpater and remove it from CPU cache

+
+
Mandatory Args:
+
adapter_name (str):
+

Adapter name to be unloaded

+
+
+
+
+
+ +
+
+export(export_dir: str | None = None) str[source]
+

Exports the model to ONNX format using torch.onnx.export. +We currently don’t support exporting non-transformed models. Please refer to the convert_to_cloud_bertstyle function in the Low-Level API for a legacy function that supports this.”

+
+
Optional Args:

does not any arguments.

+
+
Returns:
+
str:
+

Path of the generated ONNX graph.

+
+
+
+
+
+ +
+
+generate(tokenizer: PreTrainedTokenizerFast | PreTrainedTokenizer, prompts: List[str], prompt_to_adapter_mapping: List[str] | None = None, device_id: List[int] | None = None, runtime: str | None = 'AI_100', **kwargs)[source]
+

This method generates output until eos or generation_len by executing the compiled qpc on Cloud AI 100 Hardware cards. +This is a sequential execution based on the batch_size of the compiled model and the number of prompts passed. +If the number of prompts cannot be divided by the batch_size, the last unfulfilled batch will be dropped.

+
+
Mandatory Args:
+
tokenizer (PreTrainedTokenizerFast or PreTrainedTokenizer):
+

The tokenizer used in the inference

+
+
prompts (List[str]):
+

List of prompts to run the execution.

+
+
prompt_to_adapter_mapping (List[str]):
+

The sequence of the adapter names will be matched with sequence of prompts and corresponding adapters will be used for the prompts.”base” for base model (no adapter).

+
+
+
+
optional Args:
+
device_id (List[int]):
+

Device IDs to be used for execution. If len(device_id) > 1, it enables multiple card setup. If None, auto-device-picker will be used. Defaults to None.

+
+
runtime (str, optional):
+

Only AI_100 runtime is supported as of now; ONNXRT and PyTorch coming soon. Defaults to “AI_100”.

+
+
+
+
+
+ +
+ +
+
+

QEFFAutoModelForImageTextToText

+
+
+class QEfficient.transformers.models.modeling_auto.QEFFAutoModelForImageTextToText(model: Module, kv_offload: bool | None = True, **kwargs)[source]
+

A factory class for creating QEFFAutoModelForImageTextToText instances with for single and Dual QPC approach +Attributes:

+
+

_hf_auto_class (class): The Hugging Face AutoModel class for ImageTextToText models.

+
+
+ +
+
+

QEFFAutoModelForSpeechSeq2Seq

+
+
+class QEfficient.transformers.models.modeling_auto.QEFFAutoModelForSpeechSeq2Seq(*args, **kwargs)[source]
+

The QEFFAutoModelForSpeechSeq2Seq class is designed for transformers models with a sequence-to-sequence speech-to-text modeling head, including Whisper and other Encoder-Decoder speech models. +Although it is possible to initialize the class directly, we highly recommend using the from_pretrained method for initialization.

+
+
Mandatory Args:
+
model (nn.Module):
+

PyTorch model

+
+
+
+
+
from QEfficient import QEFFAutoModelForSpeechSeq2Seq
+from processors import AutoProcessor
+
+# Initialize the model using from_pretrained similar to transformers.AutoModelForSpeechSeq2Seq.
+model = QEFFAutoModelForSpeechSeq2Seq.from_pretrained("model_name")
+
+# Now you can directly compile the model for Cloud AI 100
+model.compile(num_cores=16, device_group=[0])  # Considering you have a Cloud AI 100 SKU
+
+#prepare inputs
+processor = AutoProcessor.from_pretrained(model_name)
+input_audio, sample_rate = [...] # audio data loaded in via some external audio package, such as librosa or soundfile
+input_features = (
+    processor(data, sampling_rate=sample_rate, return_tensors="pt").input_features.numpy().astype(np.float32)
+)
+decoder_input_ids = (
+    torch.ones((batch_size, 1), dtype=torch.int64) * model.model.config.decoder_start_token_id
+).numpy()
+decoder_position_ids = torch.arange(1, dtype=torch.int64).view(1, 1).repeat(batch_size, 1).numpy()
+inputs = dict(
+    input_features=input_features,
+    decoder_input_ids=decoder_input_ids,
+    decoder_position_ids=decoder_position_ids,
+)
+
+# You can now execute the model
+model.generate(inputs, generation_len=150)
+
+
+
+
+export(export_dir: str | None = None) str[source]
+

Exports the model to ONNX format using torch.onnx.export.

+

Optional Args: +:export_dir (str, optional): The directory path to store ONNX-graph.

+
+
Returns:
+
str:
+

Path of the generated ONNX graph.

+
+
+
+
+
+ +
+
+compile(onnx_path: str | None = None, compile_dir: str | None = None, *, encoder_ctx_len: int = 1500, decoder_ctx_len: int = 150, feature_len: int = 3000, batch_size: int = 1, num_devices: int = 1, num_cores: int = 16, mxfp6_matmul: bool = False, **compiler_options) str[source]
+

This method compiles the exported ONNX model using the Cloud AI 100 Platform SDK compiler binary found at /opt/qti-aic/exec/qaic-exec and generates a qpc package. +If the model has not been exported yet, this method will handle the export process. +You can pass any other arguments that the qaic-exec takes as extra kwargs.

+
+
Optional Args:
+
onnx_path (str, optional):
+

Path to pre-exported onnx model.

+
+
compile_dir (str, optional):
+

Path for saving the qpc generated.

+
+
seq_len (int, optional):
+

The length of the prompt should be less that seq_len. Defaults to 32.

+
+
batch_size (int, optional):
+

Batch size. Defaults to 1.

+
+
num_devices (int):
+

Number of devices the model needs to be compiled for. Defaults to 1.

+
+
num_cores (int):
+

Number of cores used to compile the model.

+
+
mxfp6_matmul (bool, optional):
+

Whether to use mxfp6 compression for weights. Defaults to False.

+
+
aic_enable_depth_first (bool, optional):
+

Enables DFS with default memory size. Defaults to False.

+
+
allow_mxint8_mdp_io (bool, optional):
+

Allows MXINT8 compression of MDP IO traffic. Defaults to False.

+
+
+
+
Returns:
+
str:
+

Path of the compiled qpc package.

+
+
+
+
+
+ +
+
+generate(inputs: Tensor, generation_len: int, streamer: TextStreamer | None = None, enable_debug_logs: bool = False, device_ids: List[int] | None = None) Tensor | ndarray[source]
+

This method generates output until endoftranscript or generation_len by executing the compiled qpc on Cloud AI 100 Hardware cards. +This is a sequential execution based on the batch_size of the compiled model and the number of audio tensor passed.

+
+
Mandatory Args:
+
processor:
+

autoprocessor to process inputs and decode logits

+
+
inputs (np.ndarray):
+

inputs to run the execution.

+
+
generation_len (int):
+

length upto which to generate

+
+
sample_rate (int):
+

sampling rate at which input audio is stored in inputs (needed for processor)

+
+
device_id (List[int]):
+

Ids of devices for running the qpc pass as [0] in case of normal model / [0, 1, 2, 3] in case of tensor slicing model

+
+
+
+
Returns:
+
dict:
+

Output from the AI_100 or PyTorch runtime.

+
+
+
+
+
+ +
+ +
+
+

export

+
+
+QEfficient.exporter.export_hf_to_cloud_ai_100.qualcomm_efficient_converter(model_name: str, model_kv: QEFFBaseModel | None = None, local_model_dir: str | None = None, tokenizer: PreTrainedTokenizer | PreTrainedTokenizerFast | None = None, cache_dir: str | None = None, onnx_dir_path: str | None = None, hf_token: str | None = None, seq_length: int = 32, kv: bool = True, form_factor: str = 'cloud', full_batch_size: int | None = None) Tuple[str, str][source]
+

This method is an alias for QEfficient.export.

+

Usage 1: This method can be used by passing model_name and local_model_dir or cache_dir if required for loading from local dir. +This will download the model from HuggingFace and export it to ONNX graph and returns generated files path check below.

+

Usage 2: You can pass model_name and model_kv as an object of QEfficient.QEFFAutoModelForCausalLM, In this case will directly export the model_kv.model to ONNX

+

We will be deprecating this function and it will be replaced by QEFFAutoModelForCausalLM.export.

+
+
Mandatory Args:
+
model_name (str):
+

The name of the model to be used.

+
+
+
+
Optional Args:
+
model_kv (torch.nn.Module):
+

Transformed KV torch model to be used. Defaults to None.

+
+
local_model_dir (str):
+

Path of local model. Defaults to None.

+
+
tokenizer (Union[PreTrainedTokenizer, PreTrainedTokenizerFast]):
+

Model tokenizer. Defaults to None.

+
+
cache_dir (str):
+

Path of the cache directory. Defaults to None.

+
+
onnx_dir_path (str):
+

Path to store ONNX file. Defaults to None.

+
+
hf_token (str):
+

HuggingFace token to access gated models. Defaults is None.

+
+
seq_len (int):
+

The length of the sequence. Defaults is 128.

+
+
kv (bool):
+

If false, it will export to Bert style. Defaults is True.

+
+
form_factor (str):
+

Form factor of the hardware, currently only cloud is accepted. Defaults to cloud.

+
+
+
+
Returns:
+
Tuple[str, str]:
+

Path to Base ONNX dir and path to generated ONNX model

+
+
+
+
+
import QEfficient
+base_path, onnx_model_path = QEfficient.export(model_name="gpt2")
+
+
+
+ +
+

Deprecated since version This: function will be deprecated in version 1.19, please use QEFFAutoModelForCausalLM.export instead

+
+
+
+

compile

+
+
+QEfficient.compile.compile_helper.compile(onnx_path: str, qpc_path: str, num_cores: int, device_group: List[int] | None = None, aic_enable_depth_first: bool = False, mos: int = -1, batch_size: int = 1, prompt_len: int = 32, ctx_len: int = 128, mxfp6: bool = True, mxint8: bool = False, custom_io_file_path: str | None = None, full_batch_size: int | None = None, allow_mxint8_mdp_io: bool | None = False, enable_qnn: bool | None = False, qnn_config: str | None = None, **kwargs) str[source]
+

Compiles the given ONNX model using Cloud AI 100 platform SDK compiler and saves the compiled qpc package at qpc_path. +Generates tensor-slicing configuration if multiple devices are passed in device_group.

+

This function will be deprecated soon and will be replaced by QEFFAutoModelForCausalLM.compile.

+
+
Mandatory Args:
+
onnx_path (str):
+

Generated ONNX Model Path.

+
+
qpc_path (str):
+

Path for saving compiled qpc binaries.

+
+
num_cores (int):
+

Number of cores to compile the model on.

+
+
+
+
Optional Args:
+
device_group (List[int]):
+

Used for finding the number of devices to compile for. Defaults to None.

+
+
aic_enable_depth_first (bool):
+

Enables DFS with default memory size. Defaults to False.

+
+
mos (int):
+

Effort level to reduce the on-chip memory. Defaults to -1.

+
+
batch_size (int):
+

Batch size to compile the model for. Defaults to 1.

+
+
full_batch_size (int):
+

Set full batch size to enable continuous batching mode. Default to None

+
+
prompt_len (int):
+

Prompt length for the model to compile. Defaults to 32

+
+
ctx_len (int):
+

Maximum context length to compile the model. Defaults to 128

+
+
mxfp6 (bool):
+

Enable compilation for MXFP6 precision. Defaults to True.

+
+
mxint8 (bool):
+

Compress Present/Past KV to MXINT8 using CustomIO config. Defaults to False.

+
+
custom_io_file_path (str):
+

Path to customIO file (formatted as a string). Defaults to None.

+
+
allow_mxint8_mdp_io (bool):
+

Allows MXINT8 compression of MDP IO traffic Defaults to False.

+
+
enable_qnn (bool):
+

Enables QNN Compilation. Defaults to False.

+
+
qnn_config (str):
+

Path of QNN Config parameters file. Defaults to None.

+
+
+
+
Returns:
+
str:
+

Path to compiled qpc package.

+
+
+
+
+
+ +
import QEfficient
+base_path, onnx_model_path = QEfficient.export(model_name="gpt2")
+qpc_path = QEfficient.compile(onnx_path=onnx_model_path, qpc_path=os.path.join(base_path, "qpc"), num_cores=14, device_group=[0])
+
+
+
+

Deprecated since version This: function will be deprecated in version 1.19, please use QEFFAutoModelForCausalLM.compile instead

+
+
+
+

Execute

+
+
+class QEfficient.generation.text_generation_inference.CloudAI100ExecInfo(batch_size: int, generated_texts: List[str] | List[List[str]], generated_ids: List[ndarray] | ndarray, perf_metrics: PerfMetrics)[source]
+

Bases: object

+

Holds all the information about Cloud AI 100 execution

+
+
Args:
+
batch_size (int):
+

Batch size of the QPC compilation.

+
+
generated_texts (Union[List[List[str]], List[str]]):
+

Generated text(s).

+
+
generated_ids (Union[List[np.ndarray], np.ndarray]):
+

Generated IDs.

+
+
perf_metrics (PerfMetrics):
+

Performance metrics.

+
+
+
+
+
+ +
+
+class QEfficient.generation.text_generation_inference.CloudAI100ExecInfoNew(batch_size: int, generated_ids: List[numpy.ndarray] | numpy.ndarray, perf_metrics: QEfficient.generation.text_generation_inference.PerfMetrics)[source]
+

Bases: object

+
+ +
+
+class QEfficient.generation.text_generation_inference.PerfMetrics(prefill_time: float, decode_perf: float, total_perf: float, total_time: float)[source]
+

Bases: object

+

Holds all performance metrics

+
+
Args:
+
prefill_time (float):
+

Time for prefilling.

+
+
decode_perf (float):
+

Decoding performance.

+
+
total_perf (float):
+

Total performance.

+
+
total_time (float):
+

Total time.

+
+
+
+
+
+ +
+
+QEfficient.generation.text_generation_inference.calculate_latency(total_decoded_tokens, loop_start, start, end, decode_pause_time=0)[source]
+

Method will calculate the latency metrics using the time loops and based on the total decoded token count.

+
+
Args:
+
total_decoded_tokens (int):
+

Number of tokens generated in decode stage.

+
+
loop_start (float):
+

Start time of decode loop.

+
+
start (float):
+

Start time.

+
+
end (float):
+

End time.

+
+
decode_pause_time (float):
+

Total decode pause time in continuous batching decode stage.

+
+
+
+
+

Returns: +:tuple: prefill time, decode performance, total performance, total time

+
+ +
+
+QEfficient.generation.text_generation_inference.cloud_ai_100_exec_kv(tokenizer: PreTrainedTokenizer | PreTrainedTokenizerFast, qpc_path: str, prompt: str | None = None, prompts_txt_file_path: str | None = None, device_id: List[int] | None = None, generation_len: int | None = None, enable_debug_logs: bool = False, stream: bool = True, write_io_dir: str | None = None, automation=False, prompt_to_lora_id_mapping: List[int] | None = None, is_tlm: bool = False)[source]
+

This method generates output until eos or generation_len by executing the compiled qpc on Cloud AI 100 Hardware cards. +This is a sequential execution based on the batch_size of the compiled model and the number of prompts passed. +If the number of prompts cannot be divided by the batch_size, the last unfulfilled batch will be dropped.

+
+
Mandatory Args:
+
tokenizer (Union[PreTrainedTokenizer, PreTrainedTokenizerFast]):
+

Model tokenizer.

+
+
qpc_path (str):
+

Path to the saved generated binary file after compilation.

+
+
+
+
Optional Args:
+
prompt (str):
+

Sample prompt for the model text generation. Defaults to None.

+
+
prompts_txt_file_path (str):
+

Path of the prompt text file. Defaults to None.

+
+
generation_len (int):
+

Maximum context length for the model during compilation. Defaults to None.

+
+
device_id (List[int]):
+

Device IDs to be used for execution. If len(device_id) > 1, it enables multiple card setup. If None, auto-device-picker will be used. Defaults to None.

+
+
enable_debug_logs (bool):
+

If True, it enables debugging logs. Defaults to False.

+
+
stream (bool):
+

If True, enable streamer, which returns tokens one by one as the model generates them. Defaults to True.

+
+
Write_io_dir (str):
+

Path to write the input and output files. Defaults to None.

+
+
automation (bool):
+

If true, it prints input, output, and performance stats. Defaults to False.

+
+
prompt_to_lora_id_mapping (List[int]):
+

Mapping to associate prompts with their respective LoRA adapter.

+
+
+
+
Returns:
+
CloudAI100ExecInfo:
+

Object holding execution output and performance details.

+
+
+
+
+
import transformers
+import QEfficient
+base_path, onnx_model_path = QEfficient.export(model_name="gpt2")
+qpc_path = QEfficient.compile(onnx_path=onnx_model_path, qpc_path=os.path.join(base_path, "qpc"), num_cores=14, device_group=[0])
+tokenizer = transformers.AutoTokenizer.from_pretrained("gpt2")
+exec_info = QEfficient.cloud_ai_100_exec_kv(tokenizer=tokenizer, qpc_path=qpc_path, prompt="Hi there!!", device_id=[0])
+
+
+
+ +
+
+QEfficient.generation.text_generation_inference.fix_prompt_to_lora_id_mapping(prompt_to_lora_id_mapping: List[int], batch_size: int, full_batch_size: int | None = None)[source]
+

Adjusts the list of prompt_to_lora_id_mapping to match the required batch size.

+
+
Mandatory Args:

prompt_to_lora_id_mapping (Optional[List[int]]): Mapping to associate prompts with their respective LoRA adapter. +batch_size (int): The batch size to process at a time.

+
+
Optional Args:

full_batch_size (Optional[int]): The full batch size if different from batch_size.

+
+
Returns:

List[int]: Adjusted list of prompt_to_lora_id_mapping.

+
+
+
+ +
+
+QEfficient.generation.text_generation_inference.fix_prompts(prompt: List[str], batch_size: int, full_batch_size: int | None = None)[source]
+

Adjusts the list of prompts to match the required batch size.

+
+
Mandatory Args:

prompt (List[str]): List of input prompts. +batch_size (int): The batch size to process at a time.

+
+
Optional Args:

full_batch_size (Optional[int]): The full batch size if different from batch_size.

+
+
Returns:

List[str]: Adjusted list of prompts.

+
+
+
+ +
+
+QEfficient.generation.text_generation_inference.get_compilation_dims(qpc_path: str) Tuple[int, int, int | None][source]
+

Function to fetch compilation dimensions from specializations.json. +Uses qpc path to compute path to specializations.json.

+
+
Args:

qpc_path (str): Path to directory comprising generated binary file after compilation.

+
+
+

Returns: +:tuple: compilation batch size, compilation context length, compilation full batch size

+
+ +
+
+
+

Low Level API

+
+

convert_to_cloud_kvstyle

+
+
+QEfficient.exporter.export_hf_to_cloud_ai_100.convert_to_cloud_kvstyle(model_name: str, qeff_model: QEFFAutoModelForCausalLM, tokenizer: PreTrainedTokenizer | PreTrainedTokenizerFast, onnx_dir_path: str, seq_len: int) str[source]
+

API to convert model with kv retention and export to ONNX. +KV Style Approach-

+
+
    +
  1. This architecture is particularly suitable for auto-regressive tasks.

  2. +
  3. where sequence generation involves processing one token at a time.

  4. +
  5. And contextual information from earlier tokens is crucial for predicting the next token.

  6. +
  7. The inclusion of a kV cache enhances the efficiency of the decoding process, making it more computationally efficient.

  8. +
+
+
+
Mandatory Args:
+
model_name (str):
+

Hugging Face Model Card name, Example: gpt2.

+
+
qeff_model (QEFFAutoModelForCausalLM):
+

Transformed KV torch model to be used.

+
+
tokenizer (Union[PreTrainedTokenizer, PreTrainedTokenizerFast]):
+

Model tokenizer.

+
+
onnx_dir_path (str):
+

Path to save exported ONNX file.

+
+
seq_len (int):
+

The length of the sequence.

+
+
+
+
Returns:
+
str:
+

Path of exported ONNX file.

+
+
+
+
+
+ +
+
+

convert_to_cloud_bertstyle

+
+
+QEfficient.exporter.export_hf_to_cloud_ai_100.convert_to_cloud_bertstyle(model_name: str, qeff_model: QEFFAutoModelForCausalLM, tokenizer: PreTrainedTokenizer | PreTrainedTokenizerFast, onnx_dir_path: str, seq_len: int) str[source]
+

API to convert model to Bertstyle approach. +Bertstyle Approach:

+
+
    +
  1. No Prefill/Decode separably compiled.

  2. +
  3. No KV retention logic.

  4. +
  5. KV is every time computed for all the tokens until EOS/max_length.

  6. +
+
+
+
Mandatory Args:
+
model_name (str):
+

Hugging Face Model Card name, Example: gpt2.

+
+
qeff_model (QEFFAutoModelForCausalLM):
+

Transformed KV torch model to be used.

+
+
tokenizer (Union[PreTrainedTokenizer, PreTrainedTokenizerFast]):
+

Model tokenizer.

+
+
onnx_dir_path (str):
+

Path to save exported ONNX file.

+
+
seq_len (int):
+

The length of the sequence.

+
+
+
+
Returns:
+
str:
+

Path of exported ONNX file.

+
+
+
+
+
+ +
+
+

utils

+
+
+QEfficient.utils.device_utils.get_available_device_id()[source]
+

API to check available device id.

+
+
Return:
+
int:
+

Available device id.

+
+
+
+
+
+ +
+
+class QEfficient.utils.generate_inputs.InputHandler(batch_size, tokenizer, config, prompt, prompt_len, ctx_len, full_batch_size)[source]
+

Bases: object

+
+
+prepare_ort_inputs()[source]
+

Function responsible for creating Prefill stage numpy inputs for ONNX model to be run on ONNXRT.

+
+
Return:
+
Dict:
+

input_ids, position_ids, past_key_values

+
+
+
+
+
+ +
+
+prepare_pytorch_inputs()[source]
+

Function responsible for creating Prefill stage tensor inputs for PyTorch model.

+
+
Return:
+
Dict:
+

input_ids, position_ids, past_key_values

+
+
+
+
+
+ +
+
+update_ort_inputs(inputs, ort_outputs)[source]
+

Function responsible for updating Prefill stage inputs to create inputs for decode stage inputs for ONNX model to be run on ONNXRT.

+
+
Mandatory Args:
+
inputs (Dict):
+

NumPy inputs of Onnx model from previous iteration

+
+
ort_outputs (Dict):
+

Numpy outputs of Onnx model from previous iteration

+
+
+
+
Return:
+
Dict:
+

Updated input_ids, position_ids and past_key_values

+
+
+
+
+
+ +
+
+update_ort_outputs(ort_outputs)[source]
+

Function responsible for updating ONNXRT session outputs.

+
+
Mandatory Args:
+
ort_outputs (Dict):
+

Numpy outputs of Onnx model from current iteration

+
+
+
+
Return:

updated_outputs (Dict): Updated past_key_values, logits

+
+
+
+ +
+
+update_pytorch_inputs(inputs, pt_outputs)[source]
+

Function responsible for updating Prefill stage inputs to create decode stage inputs for PyTorch model.

+
+
Mandatory Args:
+
inputs (Dict):
+

Pytorch inputs from previous iteration

+
+
pt_outputs (Dict):
+

Pytorch outputs from previous iteration

+
+
+
+
Return:
+
Dict:
+

Updated input_ids, position_ids and past_key_values

+
+
+
+
+
+ +
+ +
+
+class QEfficient.utils.run_utils.ApiRunner(batch_size, tokenizer, config, prompt, prompt_len, ctx_len, full_batch_size=None)[source]
+

Bases: object

+
+

ApiRunner class is responsible for running:

+
    +
  1. HuggingFace PyTorch model

  2. +
  3. Transformed KV Pytorch Model

  4. +
  5. ONNX model on ONNXRT

  6. +
  7. ONNX model on Cloud AI 100

  8. +
+
+
+run_hf_model_on_pytorch(model_hf)[source]
+

Function responsible for running HuggingFace PyTorch model and return the output tokens

+
+
Mandatory Args:
+
model_hf (torch.nn.module):
+

Original PyTorch model

+
+
+
+
Return:
+
numpy.ndarray:
+

Generated output tokens

+
+
+
+
+
+ +
+
+run_hf_model_on_pytorch_CB(model_hf)[source]
+

Function responsible for running HuggingFace PyTorch model and return the output tokens

+
+
Mandatory Args:
+
model_hf (torch.nn.module):
+

Original PyTorch model

+
+
+
+
Return:
+
numpy.ndarray:
+

Generated output tokens

+
+
+
+
+
+ +
+
+run_kv_model_on_cloud_ai_100(qpc_path, device_group=None)[source]
+

Function responsible for running ONNX model on Cloud AI 100 and return the output tokens

+
+
Mandatory Args:
+
qpc_path (str):
+

path to qpc generated after compilation

+
+
device_group (List[int]):
+

Device Ids to be used for compilation. if len(device_group) > 1. Multiple Card setup is enabled.

+
+
+
+
Return:
+
numpy.ndarray:
+

Generated output tokens

+
+
+
+
+
+ +
+
+run_kv_model_on_ort(model_path, is_tlm=False)[source]
+

Function responsible for running ONNX model on onnxruntime and return the output tokens

+
+
Mandatory Args:
+
model_path (str):
+

Path to the Onnx model.

+
+
+
+
Return:
+
numpy.ndarray:
+

Generated output tokens

+
+
+
+
+
+ +
+
+run_kv_model_on_pytorch(model)[source]
+

Function responsible for running KV PyTorch model and return the output tokens

+

Mandatory Args: +:model (torch.nn.module): Transformed PyTorch model

+
+
Return:
+
numpy.ndarray:
+

Generated output tokens

+
+
+
+
+
+ +
+
+run_ort_session(inputs, session) dict[source]
+

Function responsible for running onnxrt session with given inputs and passing retained state outputs to be used for next iteration inputs

+
+
Mandatory Args:
+
inputs (Dict):
+

+
session (onnxruntime.capi.onnxruntime_inference_collection.InferenceSession):
+

+
+
+
Return:
+
Dict:
+

Numpy outputs of Onnx model

+
+
+
+
+
+ +
+
+ +
+
+
+ + +
+
+ +
+
+
+
+
+ + Version: release/v1.19 + + +
+ Versions +
+
main
+
release/v1.18
+
release/v1.19
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/source/quick_start.html b/source/release/v1.19/source/quick_start.html new file mode 100644 index 000000000..8dfce7e43 --- /dev/null +++ b/source/release/v1.19/source/quick_start.html @@ -0,0 +1,469 @@ + + + + + + + Quick Start — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Quick Start

+

QEfficient Library was designed with one goal:

+

To make onboarding of models inference straightforward for any Transformer architecture, while leveraging the complete power of Cloud AI platform

+

To achieve this, we have 2 levels of APIs, with different levels of abstraction.

+
    +
  1. Command line interface abstracts away complex details, offering a simpler interface. They’re ideal for quick development and prototyping. If you’re new to a technology or want to minimize coding effort.

  2. +
  3. Python high level APIs offer more granular control, ideal for when customization is necessary.

  4. +
+
+

Supported Features

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Feature

Impact

Context Length Specializations (upcoming)

Increases the maximum context length that models can handle, allowing for better performance on tasks requiring long sequences of text.

Swift KV (upcoming)

Reduces computational overhead during inference by optimizing key-value pair processing, leading to improved throughput.

Block Attention (in progress)

Reduces inference latency and computational cost by dividing context into blocks and reusing key-value states, particularly useful in RAG.

Vision Language Model

Provides support for the AutoModelForImageTextToText class from the transformers library, enabling advanced vision-language tasks. Refer sample script for more details.

Speech Sequence to Sequence Model

Provides support for the QEFFAutoModelForSpeechSeq2Seq Facilitates speech-to-text sequence models. Refer sample script for more details.

Support for FP8 Execution

Enables execution with FP8 precision, significantly improving performance and reducing memory usage for computational tasks.

Prefill caching

Enhances inference speed by caching key-value pairs for shared prefixes, reducing redundant computations and improving efficiency.

Prompt-Lookup Decoding

Speeds up text generation by using overlapping parts of the input prompt and the generated text, making the process faster without losing quality. Refer sample script for more details.

PEFT LoRA support

Enables parameter-efficient fine-tuning using low-rank adaptation techniques, reducing the computational and memory requirements for fine-tuning large models. Refer sample script for more details.

QNN support

Enables compilation using QNN SDK, making Qeff adaptable for various backends in the future.

Embedding model support

Facilitates the generation of vector embeddings for retrieval tasks.

Speculative Decoding

Accelerates text generation by using a draft model to generate preliminary predictions, which are then verified by the target model, reducing latency and improving efficiency. Refer sample script for more details.

Finite lorax

Users can activate multiple LoRA adapters and compile them with the base model. At runtime, they can specify which prompt should use which adapter, enabling mixed adapter usage within the same batch. Refer sample script for more details.

Python and CPP Inferencing API support

Provides flexibility while running inference with Qeff and enabling integration with various applications and improving accessibility for developers. Refer sample script for more details.

Continuous batching

Optimizes throughput and latency by dynamically batching requests, ensuring efficient use of computational resources.

AWQ and GPTQ support

Supports advanced quantization techniques, improving model efficiency and performance on AI 100.

Support serving successive requests in same session

An API that yields tokens as they are generated, facilitating seamless integration with various applications and enhancing accessibility for developers.

Perplexity calculation

A script for computing the perplexity of a model, allowing for the evaluation of model performance and comparison across different models and datasets. Refer sample script for more details.

KV Heads Replication Script

A sample script for replicating key-value (KV) heads for the Llama-3-8B-Instruct model, running inference with the original model, replicating KV heads, validating changes, and exporting the modified model to ONNX format. Refer sample script for more details.

+
+
+

Transformed models and QPC storage

+

By default, the library exported models and Qaic Program Container (QPC) files, which are compiled and inference-ready model binaries generated by the compiler, are stored in ~/.cache/qeff_cache. You can customize this storage path using the following environment variables:

+
    +
  1. QEFF_HOME: If this variable is set, its path will be used for storing models and QPC files.

  2. +
  3. XDG_CACHE_HOME: If QEFF_HOME is not set but XDG_CACHE_HOME is provided, this path will be used instead. Note that setting XDG_CACHE_HOME will reroute the entire ~/.cache directory to the specified folder, including HF models.

  4. +
  5. Default: If neither QEFF_HOME nor XDG_CACHE_HOME are set, the default path ~/.cache/qeff_cache will be used.

  6. +
+
+
+

Command Line Interface

+
+

Note

+

Use bash terminal, else if using ZSH terminal then device_groupshould be in single quotes e.g. '--device_group [0]'

+
+
+

QEfficient.cloud.infer

+

This is the single e2e CLI API, which takes model_card name as input along with other compilation arguments. Check Infer API doc for more details.

+
    +
  • HuggingFace model files Download → Optimize for Cloud AI 100 → Export to ONNX → Compile on Cloud AI 100 → Execute

  • +
  • It skips the export/compile stage based if ONNX or qpc files are found. If you use infer second time with different compilation arguments, it will automatically skip ONNX model creation and directly jump to compile stage.

  • +
+
# Check out the options using the help
+python -m QEfficient.cloud.infer --help
+python -m QEfficient.cloud.infer --model_name gpt2 --batch_size 1 --prompt_len 32 --ctx_len 128 --mxfp6 --num_cores 16 --device_group [0] --prompt "My name is" --mos 1 --aic_enable_depth_first
+
+
+

If executing for batch size>1, +You can pass input prompts in single string but separate with pipe (|) symbol”. Example below

+
python -m QEfficient.cloud.infer --model_name gpt2 --batch_size 3 --prompt_len 32 --ctx_len 128 --num_cores 16 --device_group [0] --prompt "My name is|The flat earth
+theory is the belief that|The sun rises from" --mxfp6 --mos 1 --aic_enable_depth_first
+
+
+

You can also pass path of txt file with input prompts when you want to run inference on lot of prompts, Example below, sample txt file(prompts.txt) is present in examples folder.

+
python -m QEfficient.cloud.infer --model_name gpt2 --batch_size 3 --prompt_len 32 --ctx_len 128 --num_cores 16 --device_group [0] --prompts_txt_file_path examples/prompts.txt --mxfp6 --mos 1 --aic_enable_depth_first
+
+
+
+
+

QEfficient.cloud.execute

+

You can first run infer API and then use execute to run the pre-compiled model on Cloud AI 100 cards. +Once we have compiled the QPC, we can now use the precompiled QPC in execute API to run for different prompts. Make sure to pass same --device_group as used during infer. Refer Execute API doc for more details.

+
python -m QEfficient.cloud.execute --model_name gpt2 --qpc_path qeff_models/gpt2/qpc_16cores_1BS_32PL_128CL_1devices_mxfp6/qpcs --prompt "Once upon a time in" --device_group [0]
+
+
+
+
+

QEfficient.cloud.finetune

+

You can run the finetune with set of predefined existing datasets on QAIC using the eager pipeline

+
python -m QEfficient.cloud.finetune --device qaic:0 --use-peft --output_dir ./meta-sam --num_epochs 2 --context_length 256 
+
+
+

For more details on finetune, checkout the subsection.

+
+
+

Multi-Qranium Inference

+

You can also enable MQ, just based on the number of devices. Based on the --device-group as input it will create TS config on the fly. If --device-group [0,1] it will create TS config for 2 devices and use it for compilation, if --device-group [0] then TS compilation is skipped and single soc execution is enabled.

+
python -m QEfficient.cloud.infer --model_name Salesforce/codegen-2B-mono --batch_size 1 --prompt_len 32 --ctx_len 128 --mxfp6 --num_cores 16 --device-group [0,1] --prompt "def fibonacci(n):" --mos 2 --aic_enable_depth_first
+
+
+

Above step will save the qpc files under efficient-transformers/qeff_models/{model_card_name}, you can use the execute API to run for different prompts. This will automatically pick the pre-compiled qpc files.

+
python -m QEfficient.cloud.execute --model_name Salesforce/codegen-2B-mono --qpc-path qeff_models/Salesforce/codegen-2B-mono/qpc_16cores_1BS_32PL_128CL_2devices_mxfp6/qpcs --prompt "def binary_search(array: np.array, k: int):" --device-group [0,1]
+
+
+

To disable MQ, just pass single soc like below, below step will compile the model again and reuse the ONNX file as only compilation argument are different from above commands.

+
python -m QEfficient.cloud.infer --model_name gpt2 --batch_size 1 --prompt_len 32 --ctx_len 128 --mxfp6 --num_cores 16 --device-group [0] --prompt "My name is" --mos 1 --aic_enable_depth_first
+
+
+
+
+

Continuous Batching

+

Users can compile a model utilizing the continuous batching feature by specifying full_batch_size <full_batch_size_value> in the infer and compiler APIs. If full_batch_size is not provided, the model will be compiled in the regular way.

+

When enabling continuous batching, batch size should not be specified.

+

Users can leverage multi-Qranium and other supported features along with continuous batching.

+
python -m QEfficient.cloud.infer --model_name TinyLlama/TinyLlama_v1.1 --prompt_len 32 --ctx_len 128 --num_cores 16 --device_group [0] --prompt "My name is|The flat earth
+theory is the belief that|The sun rises from" --mxfp6 --mos 1 --aic_enable_depth_first --full_batch_size 3
+
+
+
+
+

QNN Compilation

+

Users can compile a model with QNN SDK by following the steps below:

+
    +
  • Set QNN SDK Path: export $QNN_SDK_ROOT=/path/to/qnn_sdk_folder

  • +
  • Enabled QNN by passing enable_qnn flag, add –enable_qnn in the cli command.

  • +
  • An optional config file can be passed to override the default parameters.

  • +
+

CLI Inference Command

+

Without QNN Config

+
python -m QEfficient.cloud.infer --model_name gpt2 --batch_size 1 --prompt_len 32 --ctx_len 128 --mxfp6 --num_cores 16 --device_group [0] --prompt "My name is" --mos 1 --aic_enable_depth_first --enable_qnn
+
+
+

With QNN Config

+
python -m QEfficient.cloud.infer --model_name gpt2 --batch_size 1 --prompt_len 32 --ctx_len 128 --mxfp6 --num_cores 16 --device_group [0] --prompt "My name is" --mos 1 --aic_enable_depth_first --enable_qnn QEfficient/compile/qnn_config.json
+
+
+

CLI Compile Command

+

Users can also use compile API to compile pre exported onnx models using QNN SDK.

+

Without QNN Config

+
python -m QEfficient.cloud.compile --onnx_path <path to gpt2 onnx file> --qpc-path <path to save qpc files> --batch_size 1 --prompt_len 32 --ctx_len 128 --mxfp6 --num_cores 16 --device_group [0] --prompt "My name is" --mos 1 --aic_enable_depth_first --enable_qnn
+
+
+

With QNN Config

+
python -m QEfficient.cloud.compile --onnx_path <path to gpt2 onnx file> --qpc-path <path to save qpc files> --batch_size 1 --prompt_len 32 --ctx_len 128 --mxfp6 --num_cores 16 --device_group [0] --prompt "My name is" --mos 1 --aic_enable_depth_first --enable_qnn QEfficient/compile/qnn_config.json
+
+
+

CLI Execute Command

+

Once we have compiled the QPC using infer or compile API, we can now use the precompiled QPC in execute API to run for different prompts.

+

Make sure to pass same --device_group as used during infer. Refer Execute API doc for more details.

+
python -m QEfficient.cloud.execute --model_name gpt2 --qpc_path qeff_models/gpt2/qpc_qnn_16cores_1BS_32PL_128CL_1devices_mxfp6/qpcs --prompt "Once upon a time in" --device_group [0]
+
+
+

QNN Compilation via Python API

+

Users can also use python API to export, compile and execute onnx models using QNN SDK.

+
# We can now export the modified models to ONNX framework
+# This will generate single ONNX Model for both Prefill and Decode Variations which are optimized for
+# Cloud AI 100 Platform.
+from QEfficient import QEFFAutoModelForCausalLM as AutoModelForCausalLM
+
+# Model-Card name (This is HF Model Card name) : https://huggingface.co/gpt2-xl
+model_name = "gpt2"  # Similar, we can change model name and generate corresponding models, if we have added the support in the lib.
+
+qeff_model = AutoModelForCausalLM.from_pretrained(model_name)
+
+generated_qpc_path = qeff_model.compile(
+    num_cores=14,
+    mxfp6=True,
+    enable_qnn=True,
+    qnn_config = qnn_config_file_path # QNN compilation configuration is passed.
+)
+
+qeff_model.generate(prompts=["My name is"])
+
+
+

Users can also take advantage of features like multi-Qranium inference and continuous batching with QNN SDK Compilation.

+
+
+
+

Python API

+
+

1. Model download and Optimize for Cloud AI 100

+

If your models falls into the model architectures that are already supported, Below steps should work fine. +Please raise an issue, in case of trouble.

+
# Initiate the Original Transformer model
+# import os
+
+from QEfficient import QEFFAutoModelForCausalLM as AutoModelForCausalLM
+
+# Please uncomment and use appropriate Cache Directory for transformers, in case you don't want to use default ~/.cache dir.
+# os.environ["TRANSFORMERS_CACHE"] = "/local/mnt/workspace/hf_cache"
+
+# ROOT_DIR = os.path.dirname(os.path.abspath(""))
+# CACHE_DIR = os.path.join(ROOT_DIR, "tmp") #, you can use a different location for just one model by passing this param as cache_dir in below API.
+
+# Model-Card name (This is HF Model Card name) : https://huggingface.co/gpt2-xl
+model_name = "gpt2"  # Similar, we can change model name and generate corresponding models, if we have added the support in the lib.
+
+qeff_model = AutoModelForCausalLM.from_pretrained(model_name)
+print(f"{model_name} optimized for AI 100 \n", qeff_model)
+
+
+
+
+

2. Export and Compile with one API

+

Use the qualcomm_efficient_converter API to export the KV transformed Model to ONNX and Verify on Torch.

+
# We can now export the modified models to ONNX framework
+# This will generate single ONNX Model for both Prefill and Decode Variations which are optimized for
+# Cloud AI 100 Platform.
+
+# While generating the ONNX model, this will clip the overflow constants to fp16
+# Verify the model on ONNXRuntime vs Pytorch
+
+# Then generate inputs and customio yaml file required for compilation.
+# Compile the model for provided compilation arguments
+# Please use platform SDk to Check num_cores for your card.
+
+generated_qpc_path = qeff_model.compile(
+    num_cores=14,
+    mxfp6=True,
+)
+
+
+
+
+

3. Execute

+

Benchmark the model on Cloud AI 100, run the infer API to print tokens and tok/sec

+
# post compilation, we can print the latency stats for the kv models, We provide API to print token and Latency stats on AI 100
+# We need the compiled prefill and decode qpc to compute the token generated, This is based on Greedy Sampling Approach
+
+qeff_model.generate(prompts=["My name is"])
+
+
+

End to End demo examples for various models are available in notebooks directory. Please check them out.

+
+
+

Draft-Based Speculative Decoding

+

Draft-based speculative decoding is a technique where a small Draft Language Model (DLM) makes num_speculative_tokens autoregressive speculations ahead of the Target Language Model (TLM). The objective is to predict what the TLM would have predicted if it would have been used instead of the DLM. This approach is beneficial when the autoregressive decode phase of the TLM is memory bound and thus, we can leverage the extra computing resources of our hardware by batching the speculations of the DLM as an input to TLM to validate the speculations.

+

To export and compile both DLM/TLM, add corresponding is_tlm and num_speculative_tokens for TLM and export DLM as you would any other QEfficient LLM model:

+
tlm_name = "meta-llama/Llama-2-70b-chat-hf"
+dlm_name = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
+k = 3 # DLM will make `k` speculations
+tlm = AutoModelForCausalLM.from_pretrained(tlm_name, is_tlm=True)
+dlm = AutoModelForCausalLM.from_pretrained(dlm_name)
+tlm.compile(num_speculative_tokens=k)
+dlm.compile()
+
+
+

The is_tlm flag is fed during the instantiation of the model because slight changes to the ONNX graph are required. Once complete, the user can specify num_speculative_tokens to define the actual number of speculations that the TLM will take as input during the decode phase. As for the DLM, no new changes are required at the ONNX or compile level.

+
+
+
+ + +
+
+ +
+
+
+
+
+ + Version: release/v1.19 + + +
+ Versions +
+
main
+
release/v1.18
+
release/v1.19
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/source/reference.html b/source/release/v1.19/source/reference.html new file mode 100644 index 000000000..ad0e4920d --- /dev/null +++ b/source/release/v1.19/source/reference.html @@ -0,0 +1,174 @@ + + + + + + + Qualcomm Cloud AI home — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + + + +
+ + Version: release/v1.19 + + +
+ Versions +
+
main
+
release/v1.18
+
release/v1.19
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/source/upgrade.html b/source/release/v1.19/source/upgrade.html new file mode 100644 index 000000000..a52addba0 --- /dev/null +++ b/source/release/v1.19/source/upgrade.html @@ -0,0 +1,174 @@ + + + + + + + Using GitHub Repository — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Using GitHub Repository

+

Warning: Efficient Transformers have been validated to work with the same compatible SDK. Upgrading this may result in certain models becoming incompatible.

+
# Create Python virtual env and activate it. (Required Python 3.10)
+
+python3.10 -m venv qeff_env
+source qeff_env/bin/activate
+pip install -U pip
+
+# Clone and Install the QEfficient Repo.
+pip install git+https://github.com/quic/efficient-transformers
+
+
+
+ + +
+
+ +
+
+
+
+
+ + Version: release/v1.19 + + +
+ Versions +
+
main
+
release/v1.18
+
release/v1.19
+
+
+
+ + + \ No newline at end of file diff --git a/source/release/v1.19/source/validate.html b/source/release/v1.19/source/validate.html new file mode 100644 index 000000000..b4296cbfc --- /dev/null +++ b/source/release/v1.19/source/validate.html @@ -0,0 +1,435 @@ + + + + + + + Validated Models — efficient-transformers main documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Validated Models

+
+

Text-only Language Models

+
+

Text Generation Task

+

QEff Auto Class: QEFFAutoModelForCausalLM

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Architecture

Model Family

Representative Models

CB Support

FalconForCausalLM

Falcon

tiiuae/falcon-40b

✔️

GemmaForCausalLM

CodeGemma

google/codegemma-2b
google/codegemma-7b

✔️

Gemma

google/gemma-2b
google/gemma-7b
google/gemma-2-2b
google/gemma-2-9b
google/gemma-2-27b

✔️

GPTBigCodeForCausalLM

Starcoder1.5

bigcode/starcoder

✔️

Starcoder2

bigcode/starcoder2-15b

✔️

GPTJForCausalLM

GPT-J

EleutherAI/gpt-j-6b

✔️

GPT2LMHeadModel

GPT-2

openai-community/gpt2

✔️

GraniteForCausalLM

Granite 3.1

ibm-granite/granite-3.1-8b-instruct
ibm-granite/granite-guardian-3.1-8b

✔️

Granite 20B

ibm-granite/granite-20b-code-base-8k
ibm-granite/granite-20b-code-instruct-8k

✔️

InternVLChatModel

Intern-VL

OpenGVLab/InternVL2_5-1B

LlamaForCausalLM

CodeLlama

codellama/CodeLlama-7b-hf
codellama/CodeLlama-13b-hf
codellama/CodeLlama-34b-hf

✔️

DeepSeek-R1-Distill-Llama

deepseek-ai/DeepSeek-R1-Distill-Llama-70B

✔️

InceptionAI-Adapted

inceptionai/jais-adapted-7b
inceptionai/jais-adapted-13b-chat
inceptionai/jais-adapted-70b

✔️

Llama 3.3

meta-llama/Llama-3.3-70B-Instruct

✔️

Llama 3.2

meta-llama/Llama-3.2-1B
meta-llama/Llama-3.2-3B

✔️

Llama 3.1

meta-llama/Llama-3.1-8B
meta-llama/Llama-3.1-70B

✔️

Llama 3

meta-llama/Meta-Llama-3-8B
meta-llama/Meta-Llama-3-70B

✔️

Llama 2

meta-llama/Llama-2-7b-chat-hf
meta-llama/Llama-2-13b-chat-hf
meta-llama/Llama-2-70b-chat-hf

✔️

Vicuna

lmsys/vicuna-13b-delta-v0
lmsys/vicuna-13b-v1.3
lmsys/vicuna-13b-v1.5

✔️

MistralForCausalLM

Mistral

mistralai/Mistral-7B-Instruct-v0.1

✔️

MixtralForCausalLM

Codestral
Mixtral

mistralai/Codestral-22B-v0.1
mistralai/Mixtral-8x7B-v0.1

✔️

MPTForCausalLM

MPT

mosaicml/mpt-7b

✔️

Phi3ForCausalLM

Phi-3, Phi-3.5

microsoft/Phi-3-mini-4k-instruct

✔️

QwenForCausalLM

DeepSeek-R1-Distill-Qwen

DeepSeek-R1-Distill-Qwen-32B

✔️

Qwen2, Qwen2.5

Qwen/Qwen2-1.5B-Instruct

✔️

+
+
+
+

Embedding Models

+
+

Text Embedding Task

+

QEff Auto Class: QEFFAutoModel

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Architecture

Model Family

Representative Models

BertModel

BERT-based

BAAI/bge-base-en-v1.5
BAAI/bge-large-en-v1.5
BAAI/bge-small-en-v1.5
e5-large-v2

LlamaModel

Llama-based

intfloat/e5-mistral-7b-instruct

Qwen2ForCausalLM

Qwen2

stella_en_1.5B_v5

XLMRobertaForSequenceClassification

XLM-RoBERTa

bge-reranker-v2-m3bge-reranker-v2-m3

MPNetForMaskedLM

MPNet

sentence-transformers/multi-qa-mpnet-base-cos-v1

NomicBertModel

NomicBERT

nomic-embed-text-v1.5

MistralModel

Mistral

e5-mistral-7b-instruct

+
+
+
+

Multimodal Language Models

+
+

Vision-Language Models (Text + Image Generation)

+

QEff Auto Class: QEFFAutoModelImageTextToText

+ + + + + + + + + + + + + + + + + +

Architecture

Model Family

Representative Models

LlavaForConditionalGeneration

LLaVA-1.5

llava-hf/llava-1.5-7b-hf

MllamaForConditionalGeneration

Llama 3.2

meta-llama/Llama-3.2-11B-Vision Instruct
meta-llama/Llama-3.2-90B-Vision

+
+
+

Audio Models

+

(Automatic Speech Recognition) - Transcription Task +QEff Auto Class: QEFFAutoModelForSpeechSeq2Seq

+ + + + + + + + + + + + + +

Architecture

Model Family

Representative Models

Whisper

Whisper

openai/whisper-tiny
openai/whisper-base
openai/whisper-small
openai/whisper-medium
openai/whisper-large
openai/whisper-large-v3-turbo

+
+
+
+
+

Models Coming Soon

+ + + + + + + + + + + + + + + + + + + + + +

Architecture

Model Family

Representative Models

BaichuanForCausalLM

Baichuan2

baichuan-inc/Baichuan2-7B-Base

CohereForCausalLM

Command-R

CohereForAI/c4ai-command-r-v01

DbrxForCausalLM

DBRX

databricks/dbrx-base

+
+ + +
+
+ +
+
+
+
+
+ + Version: release/v1.19 + + +
+ Versions +
+
main
+
release/v1.18
+
release/v1.19
+
+
+
+ + + \ No newline at end of file diff --git a/source/upgrade.html b/source/upgrade.html index 82c92b32a..85d1de4d1 100644 --- a/source/upgrade.html +++ b/source/upgrade.html @@ -161,6 +161,7 @@

Using GitHub Repository
main
release/v1.18
+
release/v1.19