diff --git a/MANIFEST.in b/MANIFEST.in
index 7f0629d20..bd2824d7d 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,11 +1,13 @@
recursive-include gptqmodel_ext/awq *.h *.cuh *.cu *.cpp
recursive-include gptqmodel_ext/exllama *.h *.cuh *.cu *.cpp
+recursive-include gptqmodel_ext/exllamav3 *.h *.hpp *.cuh *.cu *.cpp *.c
recursive-include gptqmodel_ext/exllamav2 *.h *.cuh *.cu *.cpp
recursive-include gptqmodel_ext/exllama_eora/eora *.h *.cuh *.cu *.cpp *.py
recursive-include gptqmodel_ext/marlin *.h *.cuh *.cu *.cpp *.hpp
recursive-include gptqmodel_ext/machete *.h *.hpp *.cuh *.cu *.cpp *.py
recursive-include gptqmodel_ext/cutlass_extensions *.h *.hpp *.cuh *.cu *.cpp *.py
recursive-include gptqmodel_ext/qqq *.h *.cuh *.cu *.cpp
+recursive-include gptqmodel/exllamav3/util/hadamard_data *.txt
include licenses/*
include gptqmodel_ext/pack_block_cpu.cpp
include gptqmodel_ext/marlin/generate_kernels.py
diff --git a/README.md b/README.md
index 449c2ad8d..4576baa17 100644
--- a/README.md
+++ b/README.md
@@ -145,7 +145,7 @@ Fixed quantization of OPT and DeepSeek V2-Lite models. Fixed inference for DeepS
## What is GPT-QModel?
GPT-QModel is a production-ready LLM model compression/quantization toolkit with hw-accelerated inference support for both CPU/GPU via HF Transformers, vLLM, and SGLang.
-GPT-QModel currently supports GPTQ, AWQ, QQQ, GPTAQ, EoRa, GAR, with more quantization methods and enhancements planned.
+GPT-QModel currently supports GPTQ, AWQ, QQQ, GGUF, FP8, EXL3, GPTAQ, EoRa, and GAR, with more quantization methods and enhancements planned.
## Quantization Support
@@ -155,16 +155,21 @@ GPT-QModel is a modular design supporting multiple quantization methods and feat
|---------------------------|------------|---|---|---|---------------|
| GPTQ | ✅ | ✅ | ✅ | ✅ | ✅ |
| AWQ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| GGUF | ✅ | x | x | x | x |
+| FP8 | ✅ | x | x | x | x |
+| Exllama V3 / EXL3 | ✅ | x | x | x | x |
| EoRA | ✅ | ✅ | ✅ | ✅ | x |
| Group Aware Act Reordering | ✅ | ✅ | ✅ | ✅ | ✅ |
| QQQ | ✅ | x | x | x | x |
| Rotation | ✅ | x | x | x | x |
| GPTAQ | ✅ | ✅ | ✅ | ✅ | ✅ |
+`GGUF`, `FP8`, and `EXL3` are currently native GPT-QModel quantization/runtime paths. `vLLM` and `SGLang` integration currently targets `GPTQ` and `AWQ`.
+
## Features
* ✨ Native integration with HF [Transformers](https://github.com/huggingface/transformers), [Optimum](https://github.com/huggingface/optimum), and [Peft](https://github.com/huggingface/peft)
* 🚀 [vLLM](https://github.com/vllm-project/vllm) and [SGLang](https://github.com/sgl-project/sglang) inference integration for quantized models with format = `FORMAT.[GPTQ/AWQ]`
-* ✨ GPTQ, AWQ, and QQQ quantization format with hardware-accelerated inference kernels.
+* ✨ GPTQ, AWQ, QQQ, GGUF, FP8, and EXL3 quantization support.
* 🚀 Quantize MoE models with ease even with extreme routing activation bias via `Moe.Routing` and/or `FailSafe`.
* 🚀 Data Parallelism for 80%+ quantization speed reduction with Multi-GPU.
* 🚀 Optimized for Python >= 3.13t (free threading) with lock-free threading.
@@ -177,6 +182,15 @@ GPT-QModel is a modular design supporting multiple quantization methods and feat
* 🚀 [Microsoft/BITBLAS](https://github.com/microsoft/BitBLAS) optimized tile based inference.
* 💯 CI unit-test coverage for all supported models and kernels including post-quantization quality regression.
+## Who's Using GPT-QModel?
+
+Selected public references where teams or companies explicitly mention `GPTQModel` in documentation, integration notes, or quantized model usage. This is not an exhaustive customer list.
+
+*
Hugging Face
+*
Intel
+*
NVIDIA
+*
Alibaba Cloud
+
## Quality: GPTQ 4bit can match native BF16:
🤗 [ModelCloud quantized Vortex models on HF](https://huggingface.co/collections/ModelCloud/vortex-673743382af0a52b2a8b9fe2)
@@ -289,6 +303,76 @@ model.quantize(calibration_dataset, batch_size=1)
model.save(quant_path)
```
+#### Other Quantization Formats
+
+`GPTQ`, `AWQ`, and `EXL3` are calibration-based. `GGUF` and `FP8` are weight-only and should be quantized with `calibration=None`.
+
+##### GGUF Example: Llama 3.2 1B Instruct
+
+```py
+from gptqmodel import BACKEND, GGUFConfig, GPTQModel
+
+model_id = "meta-llama/Llama-3.2-1B-Instruct"
+quant_path = "Llama-3.2-1B-Instruct-GGUF-Q4_K_M"
+
+qcfg = GGUFConfig(
+ bits=4,
+ format="q_k_m",
+)
+
+model = GPTQModel.load(model_id, qcfg)
+model.quantize(calibration=None, backend=BACKEND.GGUF_TORCH)
+model.save(quant_path)
+```
+
+##### FP8 Example: Llama 3.2 1B Instruct
+
+```py
+from gptqmodel import BACKEND, GPTQModel, QuantizeConfig
+
+model_id = "meta-llama/Llama-3.2-1B-Instruct"
+quant_path = "Llama-3.2-1B-Instruct-FP8-E4M3"
+
+qcfg = QuantizeConfig(
+ method="fp8",
+ format="float8_e4m3fn", # or "float8_e5m2"
+ bits=8,
+ weight_scale_method="row",
+)
+
+model = GPTQModel.load(model_id, qcfg)
+model.quantize(calibration=None, backend=BACKEND.TORCH)
+model.save(quant_path)
+```
+
+##### Exllama V3 / EXL3 Example: Llama 3.2 1B Instruct
+
+```py
+from datasets import load_dataset
+from gptqmodel import BACKEND, GPTQModel, QuantizeConfig
+
+model_id = "meta-llama/Llama-3.2-1B-Instruct"
+quant_path = "Llama-3.2-1B-Instruct-EXL3"
+
+calibration_dataset = load_dataset(
+ "allenai/c4",
+ data_files="en/c4-train.00001-of-01024.json.gz",
+ split="train",
+).select(range(1024))["text"]
+
+qcfg = QuantizeConfig(
+ method="exl3",
+ format="exl3",
+ bits=4.0, # target average bits-per-weight
+ head_bits=6.0, # optional higher bitrate for attention heads / sensitive tensors
+ codebook="mcg", # one of: mcg, mul1, 3inst
+)
+
+model = GPTQModel.load(model_id, qcfg)
+model.quantize(calibration_dataset, batch_size=1, backend=BACKEND.EXLLAMA_V3)
+model.save(quant_path)
+```
+
#### MoE Quantization
Some MoE (mixture of experts) models have extremely uneven/biased routing (distribution of tokens) to the `experts` causing some expert modules to receive close-to-zero activated tokens, thus failing to complete calibration-based quantization (GPTQ/AWQ).
@@ -489,6 +573,17 @@ Models quantized by GPT-QModel are inference compatible with HF Transformers (mi
year={2023}
}
+# GGUF / llama.cpp
+@misc{ggerganov2023gguf,
+ author = {Georgi Gerganov and ggml-org contributors},
+ title = {llama.cpp and the GGUF model format},
+ publisher = {GitHub},
+ journal = {GitHub repository},
+ howpublished = {\url{https://github.com/ggml-org/llama.cpp}},
+ note = {Canonical GGUF implementation and format reference; see also \url{https://github.com/ggml-org/llama.cpp/wiki/dev-notes}},
+ year = {2023}
+}
+
# EoRA
@article{liu2024eora,
title={EoRA: Training-free Compensation for Compressed LLM with Eigenspace Low-Rank Approximation},
@@ -528,6 +623,17 @@ Models quantized by GPT-QModel are inference compatible with HF Transformers (mi
journal={arXiv preprint arXiv:2406.09904},
year={2024}
}
+
+# ExLlama V3 / EXL3
+@misc{turboderp2026exllamav3,
+ author = {turboderp and exllamav3 contributors},
+ title = {ExLlamaV3 and the EXL3 quantization format},
+ publisher = {GitHub},
+ journal = {GitHub repository},
+ howpublished = {\url{https://github.com/turboderp-org/exllamav3}},
+ note = {Project repository and EXL3 format documentation: \url{https://github.com/turboderp-org/exllamav3/blob/master/doc/exl3.md}},
+ year = {2026}
+}
```
## Quick Notes
diff --git a/examples/quantization/llama3_2_1b_gguf_q4_k_m.py b/examples/quantization/llama3_2_1b_gguf_q4_k_m.py
new file mode 100644
index 000000000..5afb35af5
--- /dev/null
+++ b/examples/quantization/llama3_2_1b_gguf_q4_k_m.py
@@ -0,0 +1,96 @@
+# SPDX-FileCopyrightText: 2026 ModelCloud.ai
+# SPDX-License-Identifier: Apache-2.0
+
+import logging
+import os
+from pathlib import Path
+
+import torch
+from transformers import AutoTokenizer
+
+from gptqmodel import BACKEND, GPTQModel
+from gptqmodel.quantization import GGUFConfig
+
+
+os.environ.setdefault("CUDA_DEVICE_ORDER", "PCI_BUS_ID")
+os.environ.setdefault("PYTORCH_ALLOC_CONF", "expandable_segments:True,max_split_size_mb:256")
+
+SOURCE_MODEL = "/monster/data/model/Llama-3.2-1B-Instruct"
+OUTPUT_DIR = "./Llama-3.2-1B-Instruct-GGUF-Q4_K_M"
+
+
+def main() -> None:
+ device = "cuda:0" if torch.cuda.is_available() else "cpu"
+
+ tokenizer = AutoTokenizer.from_pretrained(SOURCE_MODEL, use_fast=True)
+
+ qconfig = GGUFConfig(
+ bits=4,
+ format="q_k_m",
+ smoother=None,
+ offload_to_disk=True,
+ offload_to_disk_path="./gptqmodel_offload",
+ )
+
+ print("Resolved quantize config:")
+ print(f" type = {type(qconfig).__name__}")
+ print(f" format = {qconfig.format}")
+ print(f" bits = {qconfig.bits!r}")
+ print(f" bits_s = {str(qconfig.bits)}")
+
+ model = GPTQModel.from_pretrained(
+ model_id_or_path=SOURCE_MODEL,
+ quantize_config=qconfig,
+ trust_remote_code=False,
+ )
+
+ quant_log = model.quantize(
+ calibration=None,
+ tokenizer=tokenizer,
+ backend=BACKEND.GGUF_TORCH,
+ )
+ print("Quantize lifecycle keys:", list(quant_log.keys()))
+
+ out_dir = Path(OUTPUT_DIR)
+ out_dir.mkdir(parents=True, exist_ok=True)
+
+ model.save(str(out_dir))
+ tokenizer.save_pretrained(str(out_dir))
+
+ del model
+ if torch.cuda.is_available():
+ torch.cuda.empty_cache()
+
+ quantized = GPTQModel.from_quantized(
+ model_id_or_path=str(out_dir),
+ backend=BACKEND.GGUF_TORCH,
+ device=device,
+ trust_remote_code=False,
+ )
+
+ print("Inference kernel:", quantized.qlinear_kernel.__name__)
+
+ prompt = "Which city is the capital city of France?"
+ inputs = tokenizer(prompt, return_tensors="pt").to(device)
+
+ with torch.inference_mode():
+ output_ids = quantized.generate(
+ **inputs,
+ max_new_tokens=48,
+ do_sample=False,
+ )
+
+ text = tokenizer.decode(output_ids[0], skip_special_tokens=True)
+ print("\nPrompt:")
+ print(prompt)
+ print("\nGeneration:")
+ print(text)
+
+
+if __name__ == "__main__":
+ logging.basicConfig(
+ format="%(asctime)s %(levelname)s [%(name)s] %(message)s",
+ level=logging.INFO,
+ datefmt="%Y-%m-%d %H:%M:%S",
+ )
+ main()
diff --git a/gptqmodel/__init__.py b/gptqmodel/__init__.py
index 0da3fa820..cc4b683a1 100644
--- a/gptqmodel/__init__.py
+++ b/gptqmodel/__init__.py
@@ -50,7 +50,17 @@
from .models import GPTQModel, get_best_device
from .models.auto import ASCII_LOGO
-from .quantization import BaseQuantizeConfig, GPTAQConfig, QuantizeConfig
+from .quantization import (
+ AWQQuantizeConfig,
+ BaseQuantizeConfig,
+ GGUFConfig,
+ GGUFQuantizeConfig,
+ GPTAQConfig,
+ GPTQQuantizeConfig,
+ QuantizeConfig,
+ RTNQuantizeConfig,
+ WeightOnlyConfig,
+)
from .utils import BACKEND
from .utils.exllama import exllama_set_max_input_length
from .version import __version__
diff --git a/gptqmodel/adapter/adapter.py b/gptqmodel/adapter/adapter.py
index 7a0545085..35bd9d814 100644
--- a/gptqmodel/adapter/adapter.py
+++ b/gptqmodel/adapter/adapter.py
@@ -115,8 +115,11 @@ def apply(self, x: torch.Tensor, out: torch.Tensor) -> torch.Tensor:
# out = out + ((x @ self.lora_A) @ self.lora_B)
# native quantized model/eora is float16 for gptq but for training, we may load the model as bfloat16 for accuracy
- if x.dtype != self.lora_A.dtype:
- log.info.once(f"Adapter: Lora A/B auto changed from `{self.lora_A.dtype}` to `{x.dtype}` to match forward input dtype.")
+ if x.dtype != self.lora_A.dtype or x.device != self.lora_A.device:
+ log.info.once(
+ f"Adapter: Lora A/B auto changed from `{self.lora_A.dtype}` on `{self.lora_A.device}` "
+ f"to `{x.dtype}` on `{x.device}` to match forward input."
+ )
self.lora_A = self.lora_A.to(device=x.device, dtype=x.dtype)
self.lora_B = self.lora_B.to(device=x.device, dtype=x.dtype)
diff --git a/gptqmodel/exllamav3/CREDITS.md b/gptqmodel/exllamav3/CREDITS.md
new file mode 100644
index 000000000..813f27277
--- /dev/null
+++ b/gptqmodel/exllamav3/CREDITS.md
@@ -0,0 +1,13 @@
+This directory vendors the EXL3 kernel and quantizer pieces adapted from `turboderp-org/exllamav3`.
+
+Primary upstream source:
+- https://github.com/turboderp-org/exllamav3
+
+Ported components in this repo:
+- `gptqmodel/exllamav3/ext.py`
+- `gptqmodel/exllamav3/modules/quant/exl3.py`
+- `gptqmodel/exllamav3/modules/quant/exl3_lib/quantize.py`
+- `gptqmodel/exllamav3/util/*`
+- `gptqmodel_ext/exllamav3/*`
+
+The code remains self-contained inside GPTQModel and does not depend on the external `exllamav3` Python package.
diff --git a/gptqmodel/exllamav3/__init__.py b/gptqmodel/exllamav3/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/gptqmodel/exllamav3/ext.py b/gptqmodel/exllamav3/ext.py
new file mode 100644
index 000000000..8145e2fb3
--- /dev/null
+++ b/gptqmodel/exllamav3/ext.py
@@ -0,0 +1,159 @@
+# SPDX-FileCopyrightText: 2024-2025 ModelCloud.ai
+# SPDX-FileCopyrightText: 2024-2025 qubitium@modelcloud.ai
+# SPDX-License-Identifier: Apache-2.0
+# Contact: qubitium@modelcloud.ai, x.com/qubitium
+#
+# Portions of this file are adapted from turboderp-org/exllamav3.
+# Credits: TurboDerp / ExLlamaV3 contributors.
+
+from __future__ import annotations
+
+import os
+import sys
+from pathlib import Path
+
+import torch
+from torch.utils.cpp_extension import load
+
+from ..utils._extension_loader import load_extension_module
+from .util.arch_list import maybe_set_arch_list_env
+
+
+extension_name = "gptqmodel_exllamav3_kernels"
+verbose = str(os.environ.get("GPTQMODEL_EXT_VERBOSE", "")).strip().lower() not in {"", "0", "false", "off", "no"}
+ext_debug = str(os.environ.get("GPTQMODEL_EXT_DEBUG", "")).strip().lower() in {"1", "true", "on", "yes"}
+windows = os.name == "nt"
+
+
+def _find_msvc() -> str | None:
+ program_files_x64 = os.environ["ProgramW6432"]
+ program_files_x86 = os.environ["ProgramFiles(x86)"]
+ msvc_dirs = [
+ a + "\\Microsoft Visual Studio\\" + b + "\\" + c + "\\VC\\Tools\\MSVC\\"
+ for b in ["2022", "2019", "2017"]
+ for a in [program_files_x64, program_files_x86]
+ for c in ["BuildTools", "Community", "Professional", "Enterprise", "Preview"]
+ ]
+
+ for msvc_dir in msvc_dirs:
+ if not os.path.exists(msvc_dir):
+ continue
+ versions = sorted(os.listdir(msvc_dir), reverse=True)
+ for version in versions:
+ compiler_dir = msvc_dir + version + "\\bin\\Hostx64\\x64"
+ if os.path.exists(compiler_dir) and os.path.exists(compiler_dir + "\\cl.exe"):
+ return compiler_dir
+ return None
+
+
+def _ensure_windows_compiler() -> None:
+ if not windows:
+ return
+
+ import subprocess
+
+ try:
+ subprocess.check_output(["where", "/Q", "cl"])
+ except subprocess.CalledProcessError:
+ cl_path = _find_msvc()
+ if cl_path:
+ if verbose:
+ print(" -- Injected compiler path:", cl_path)
+ os.environ["path"] += ";" + cl_path
+ else:
+ print(" !! Unable to find cl.exe; EXL3 compilation will probably fail", file=sys.stderr)
+
+
+def _source_root() -> Path:
+ return Path(__file__).resolve().parents[2] / "gptqmodel_ext" / "exllamav3"
+
+
+def _source_files() -> list[str]:
+ sources_dir = _source_root()
+ source_files = [
+ "bindings.cpp",
+ "hadamard.cpp",
+ "hgemm.cu",
+ "libtorch/linear.cpp",
+ "quant/comp_units/exl3_comp_unit_1.cu",
+ "quant/comp_units/exl3_comp_unit_2.cu",
+ "quant/comp_units/exl3_comp_unit_3.cu",
+ "quant/comp_units/exl3_comp_unit_4.cu",
+ "quant/comp_units/exl3_comp_unit_5.cu",
+ "quant/comp_units/exl3_comp_unit_6.cu",
+ "quant/comp_units/exl3_comp_unit_7.cu",
+ "quant/comp_units/exl3_comp_unit_8.cu",
+ "quant/exl3_devctx.cu",
+ "quant/exl3_gemm.cu",
+ "quant/exl3_kernel_map.cu",
+ "quant/hadamard.cu",
+ "quant/pack.cu",
+ "quant/quantize.cu",
+ "quant/reconstruct.cu",
+ "quant/util.cu",
+ ]
+ return [str((sources_dir / path).resolve()) for path in source_files]
+
+
+def _build_directory() -> str | None:
+ build_root = os.environ.get("GPTQMODEL_EXT_BUILD")
+ if not build_root:
+ return None
+ return str(Path(build_root) / extension_name)
+
+
+def _load_prebuilt():
+ try:
+ return load_extension_module(extension_name, package="gptqmodel")
+ except ImportError:
+ return None
+
+
+def _load_jit():
+ _ensure_windows_compiler()
+ maybe_set_arch_list_env()
+
+ extra_cflags = ["/O2", "/std:c++17"] if windows else ["-O3", "-std=c++17"]
+ extra_cuda_cflags = [
+ "-O3",
+ "-std=c++17",
+ "-lineinfo",
+ "-U__CUDA_NO_HALF_OPERATORS__",
+ "-U__CUDA_NO_HALF_CONVERSIONS__",
+ "-U__CUDA_NO_BFLOAT16_OPERATORS__",
+ "-U__CUDA_NO_BFLOAT16_CONVERSIONS__",
+ "-U__CUDA_NO_BFLOAT162_OPERATORS__",
+ "-U__CUDA_NO_BFLOAT162_CONVERSIONS__",
+ ]
+
+ if ext_debug:
+ if windows:
+ extra_cflags.append("/Zi")
+ else:
+ extra_cflags.extend(["-ftime-report", "-DTORCH_USE_CUDA_DSA"])
+
+ if torch.version.hip:
+ extra_cuda_cflags.append("-DHIPBLAS_USE_HIP_HALF")
+
+ extra_ldflags: list[str] = []
+ if windows:
+ extra_ldflags.append("cublas.lib")
+ if sys.base_prefix != sys.prefix:
+ extra_ldflags.append(f"/LIBPATH:{os.path.join(sys.base_prefix, 'libs')}")
+
+ sources_dir = _source_root()
+ return load(
+ name=extension_name,
+ sources=_source_files(),
+ extra_include_paths=[str(sources_dir)],
+ build_directory=_build_directory(),
+ verbose=verbose,
+ extra_ldflags=extra_ldflags,
+ extra_cuda_cflags=extra_cuda_cflags,
+ extra_cflags=extra_cflags,
+ )
+
+
+exllamav3_ext = _load_prebuilt()
+if exllamav3_ext is None:
+ exllamav3_ext = _load_jit()
diff --git a/gptqmodel/exllamav3/modules/__init__.py b/gptqmodel/exllamav3/modules/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/gptqmodel/exllamav3/modules/quant/__init__.py b/gptqmodel/exllamav3/modules/quant/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/gptqmodel/exllamav3/modules/quant/exl3.py b/gptqmodel/exllamav3/modules/quant/exl3.py
new file mode 100644
index 000000000..6a74a295b
--- /dev/null
+++ b/gptqmodel/exllamav3/modules/quant/exl3.py
@@ -0,0 +1,265 @@
+# SPDX-FileCopyrightText: 2024-2025 ModelCloud.ai
+# SPDX-FileCopyrightText: 2024-2025 qubitium@modelcloud.ai
+# SPDX-License-Identifier: Apache-2.0
+# Contact: qubitium@modelcloud.ai, x.com/qubitium
+#
+# Portions of this file are adapted from turboderp-org/exllamav3.
+# Credits: TurboDerp / ExLlamaV3 contributors.
+
+from __future__ import annotations
+
+import torch
+
+from .exl3_lib.quantize import preapply_had_l, preapply_had_r, had_k, had_n
+from ...ext import exllamav3_ext as ext
+from ...util.tensor import g_tensor_cache
+
+class LinearEXL3:
+
+ quant_type: str = "exl3"
+
+ def __init__(
+ self,
+ config: object | None,
+ in_features: int,
+ out_features: int,
+ scale: torch.Tensor | None = None,
+ su: torch.Tensor | None = None,
+ sv: torch.Tensor | None = None,
+ suh: torch.Tensor | None = None,
+ svh: torch.Tensor | None = None,
+ trellis: torch.Tensor | None = None,
+ mcg: torch.Tensor | None = None,
+ mul1: torch.Tensor | None = None,
+ bias: torch.Tensor | None = None,
+ out_dtype: torch.dtype | None = None,
+ transformers_fix: bool = False,
+ key: str | None = None
+ ):
+ assert scale is None, "scale is no longer used"
+ assert su is not None or suh is not None, "either su (packed) or suh (unpacked) is required"
+ assert sv is not None or svh is not None, "either sv (packed) or svh (unpacked) is required"
+ assert trellis is not None, "trellis is required"
+ if su is not None: assert su.dtype == torch.int16, "su is wrong datatype"
+ if sv is not None: assert sv.dtype == torch.int16, "sv is wrong datatype"
+ if suh is not None: assert suh.dtype == torch.half, "suh is wrong datatype"
+ if svh is not None: assert svh.dtype == torch.half, "svh is wrong datatype"
+ assert trellis.dtype == torch.int16, "trellis is wrong datatype"
+ assert len(trellis.shape) == 3, "trellis must have dim = 3"
+
+ if bias is not None and bias.dtype == torch.float: bias = bias.to(torch.half)
+
+ self.transformers_fix = transformers_fix
+ self.key = key
+
+ # self.scale = scale.item()
+ self.su = None
+ self.sv = None
+ self.suh = suh if suh is not None else self.unpack_bf(su)
+ self.svh = svh if svh is not None else self.unpack_bf(sv)
+ self.trellis = trellis
+ self.K = trellis.shape[-1] // 16
+ self.in_features = in_features
+ self.out_features = out_features
+ self.bias = bias
+ self.swap_device = None
+ self.out_dtype = out_dtype
+
+ self.mcg_tensor = mcg
+ self.mul1_tensor = mul1
+ self.mcg = self.mcg_tensor is not None
+ self.mul1 = self.mul1_tensor is not None
+
+ self.bsz1_xh_args = (self.trellis.device, (1, self.in_features), self.out_dtype)
+ self.bc = ext.BC_LinearEXL3(
+ self.trellis,
+ self.suh,
+ self.svh,
+ self.K,
+ self.bias,
+ self.mcg,
+ self.mul1,
+ g_tensor_cache.get(*self.bsz1_xh_args)
+ )
+
+
+ def unload(self):
+ g_tensor_cache.drop(*self.bsz1_xh_args)
+
+
+ def get_tensors(self, key: str):
+ return {
+ f"{key}.{subkey}": tensor.contiguous()
+ for subkey, tensor in [
+ ("su", self.su),
+ ("sv", self.sv),
+ ("suh", self.suh),
+ ("svh", self.svh),
+ ("trellis", self.trellis),
+ ("bias", self.bias),
+ ("mcg", self.mcg_tensor),
+ ("mul1", self.mul1_tensor),
+ ] if tensor is not None
+ }
+
+
+ def forward(
+ self,
+ x: torch.Tensor,
+ params: dict,
+ out_dtype: torch.dtype | None = None,
+ ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
+
+ if "ovr" in params:
+ ovr = params["ovr"]
+ if self.key in ovr and ovr[self.key].inner is not self:
+ return ovr[self.key].forward(x, params, out_dtype)
+
+ bsz = x.numel() // x.shape[-1]
+ torch_mode = params.get("reconstruct", bsz > 32)
+
+ out_shape = x.shape[:-1] + (self.out_features,)
+ x = x.view(-1, self.in_features)
+ y = torch.empty(out_shape, dtype = out_dtype or self.out_dtype or torch.half, device = x.device)
+
+ if torch_mode:
+ y_ = y.view(x.shape[0], self.out_features)
+ xh = torch.empty_like(x)
+ ext.had_r_128(x, xh, self.suh, None, 1.0)
+ w = self.get_inner_weight_tensor()
+ ext.hgemm(xh, w, y_)
+ ext.had_r_128(y_, y_, None, self.svh, 1.0)
+ if self.bias is not None:
+ y += self.bias
+ y = y.view(out_shape)
+
+ else:
+ self.bc.run(x, y)
+
+ return y
+
+
+ def unpack_bf(self, bitfield: torch.Tensor):
+ # For some reason this operation causes a GPU assert on Transformers. Running on CPU seems to fix it
+ device = bitfield.device
+ if self.transformers_fix:
+ bitfield = bitfield.cpu()
+
+ # TODO: Maybe custom kernel for this. Only used for full reconstruct and loading old models, not during inference
+ bitfield = bitfield.view(torch.uint16).to(torch.int)
+ masks = (1 << torch.arange(16)).to(bitfield.device)
+ expanded = (bitfield.unsqueeze(-1) & masks) > 0
+ expanded = expanded.flatten()
+ expanded = torch.where(expanded, torch.tensor(-1.0, dtype = torch.float16), torch.tensor(1.0, dtype = torch.float16))
+ return expanded.contiguous().to(device)
+
+
+ def get_weight_tensor(self):
+ # suh = self.unpack_bf(self.su).unsqueeze(1)
+ suh = self.unpack_bf(self.su).unsqueeze(1) if self.su else self.suh.unsqueeze(1)
+ svh = self.unpack_bf(self.sv).unsqueeze(0) if self.sv else self.svh.unsqueeze(0)
+ w = self.get_inner_weight_tensor()
+ w = preapply_had_l(w, had_k)
+ w *= suh
+ w = preapply_had_r(w, had_n)
+ w *= svh
+ # w *= self.scale
+ return w
+
+
+ def get_inner_weight_tensor(self):
+ w = torch.empty((self.in_features, self.out_features), dtype = torch.half, device = self.trellis.device)
+ ext.reconstruct(w, self.trellis, self.K, self.mcg, self.mul1)
+ return w
+
+
+ def get_bias_tensor(self) -> torch.Tensor | None:
+ return self.bias
+
+
+ # Swap tensors to CPU (to free some space while quantizing)
+ def swap_cpu(self):
+ if self.swap_device is not None:
+ return
+ self.swap_device = self.trellis.device
+ if self.su is not None: self.su = self.su.cpu()
+ if self.sv is not None: self.sv = self.sv.cpu()
+ if self.suh is not None: self.suh = self.suh.cpu()
+ if self.svh is not None: self.svh = self.svh.cpu()
+ if self.trellis is not None: self.trellis = self.trellis.cpu()
+ if self.bias is not None: self.bias = self.bias.cpu()
+
+
+ def unswap_cpu(self):
+ if self.swap_device is None:
+ return
+ if self.su is not None: self.su = self.su.to(self.swap_device)
+ if self.sv is not None: self.sv = self.sv.to(self.swap_device)
+ if self.suh is not None: self.suh = self.suh.to(self.swap_device)
+ if self.svh is not None: self.svh = self.svh.to(self.swap_device)
+ if self.trellis is not None: self.trellis = self.trellis.to(self.swap_device)
+ if self.bias is not None: self.bias = self.bias.to(self.swap_device)
+ self.swap_device = None
+
+
+ def tp_export(self, plan, producer):
+ return {
+ "cls": LinearEXL3,
+ "in_features": self.in_features,
+ "out_features": self.out_features,
+ "suh": producer.send(self.suh),
+ "svh": producer.send(self.svh),
+ "trellis": producer.send(self.trellis),
+ "bias": producer.send(self.bias),
+ "mcg": producer.send(self.mcg_tensor),
+ "mul1": producer.send(self.mul1_tensor),
+ "out_dtype": self.out_dtype,
+ }
+
+
+ @staticmethod
+ def tp_import_split(local_context, exported, plan, split):
+ consumer = local_context["consumer"]
+ id_suh = exported["suh"]
+ id_svh = exported["svh"]
+ id_trellis = exported["trellis"]
+ id_bias = exported["bias"]
+ mcg = consumer.recv(exported["mcg"], cuda = True)
+ mul1 = consumer.recv(exported["mul1"], cuda = True)
+
+ if split is not None:
+ split_out, first, last = split
+ else:
+ split_out, first, last = True, 0, exported["out_features"]
+
+ if split_out:
+ suh = consumer.recv(id_suh, cuda = True)
+ svh = consumer.recv(id_svh, cuda = True, slice_dim = 0, first = first, last = last)
+ trellis = consumer.recv(id_trellis, cuda = True, slice_dim = 1, first = first // 16, last = last // 16)
+ bias = consumer.recv(id_bias, cuda = True, slice_dim = 0, first = first, last = last)
+ in_features = exported["in_features"]
+ out_features = last - first
+ else:
+ suh = consumer.recv(id_suh, cuda = True, slice_dim = 0, first = first, last = last)
+ svh = consumer.recv(id_svh, cuda = True)
+ trellis = consumer.recv(id_trellis, cuda = True, slice_dim = 0, first = first // 16, last = last // 16)
+ bias = consumer.recv(id_bias, cuda = True)
+ in_features = last - first
+ out_features = exported["out_features"]
+
+ module = LinearEXL3(
+ config = None,
+ in_features = in_features,
+ out_features = out_features,
+ scale = None,
+ su = None,
+ sv = None,
+ suh = suh,
+ svh = svh,
+ trellis = trellis,
+ mcg = mcg,
+ mul1 = mul1,
+ bias = bias,
+ out_dtype = exported["out_dtype"],
+ )
+ return module
diff --git a/gptqmodel/exllamav3/modules/quant/exl3_lib/__init__.py b/gptqmodel/exllamav3/modules/quant/exl3_lib/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/gptqmodel/exllamav3/modules/quant/exl3_lib/quantize.py b/gptqmodel/exllamav3/modules/quant/exl3_lib/quantize.py
new file mode 100644
index 000000000..e403867a6
--- /dev/null
+++ b/gptqmodel/exllamav3/modules/quant/exl3_lib/quantize.py
@@ -0,0 +1,1080 @@
+# SPDX-FileCopyrightText: 2024-2025 ModelCloud.ai
+# SPDX-FileCopyrightText: 2024-2025 qubitium@modelcloud.ai
+# SPDX-License-Identifier: Apache-2.0
+# Contact: qubitium@modelcloud.ai, x.com/qubitium
+#
+# Portions of this file are adapted from turboderp-org/exllamav3.
+# Credits: TurboDerp / ExLlamaV3 contributors.
+
+import math
+import threading
+from functools import lru_cache
+
+import torch
+import torch.nn.functional as F
+
+from ....ext import exllamav3_ext as ext
+from ....util.progress import ProgressBar
+from ....util.memory import free_mem
+from ....util.hadamard import get_hadamard_dt
+from ....util.tensor import save_tensor_image
+
+# Constant
+had_k, had_n = 128, 128
+codebook_scale = 1.24371088
+
+codebook_mcg_mult = 0xCBAC1FED
+codebook_mul1_mult = 0x83DCD12D
+
+@lru_cache
+def tensor_core_perm(device):
+ perm_a = [0] * 256
+ for t in range(32):
+ r0 = (t % 4) * 2
+ r1 = r0 + 1
+ r2 = r0 + 8
+ r3 = r0 + 9
+ c0 = t // 4
+ c1 = c0 + 8
+ perm_a[t * 8 + 0] = r0 * 16 + c0
+ perm_a[t * 8 + 1] = r1 * 16 + c0
+ perm_a[t * 8 + 2] = r2 * 16 + c0
+ perm_a[t * 8 + 3] = r3 * 16 + c0
+ perm_a[t * 8 + 4] = r0 * 16 + c1
+ perm_a[t * 8 + 5] = r1 * 16 + c1
+ perm_a[t * 8 + 6] = r2 * 16 + c1
+ perm_a[t * 8 + 7] = r3 * 16 + c1
+ return torch.tensor(perm_a, dtype = torch.int, device = device)
+
+
+@lru_cache
+def tensor_core_perm_i(device):
+ return torch.argsort(tensor_core_perm(device))
+
+
+@lru_cache
+def get_temp_buffers(device, K: int):
+ max_batch_size = 256
+ temp_costs = torch.zeros((max_batch_size, 2, 65536 >> K), dtype = torch.half, device = device)
+ temp_edges = torch.zeros((max_batch_size, 256, 65536 >> K), dtype = torch.short, device = device)
+ return temp_costs, temp_edges
+
+
+def quantize_tiles(tiles, quant_args: dict):
+ tiles = tiles.contiguous()
+ assert tiles.shape[1] == 256
+ assert tiles.dtype == torch.float
+
+ K = quant_args["K"]
+ mcg = "mcg" in quant_args
+ mul1 = "mul1" in quant_args
+ quantized_tiles = torch.zeros_like(tiles)
+ quantized_idx = torch.zeros_like(tiles, dtype = torch.short)
+ temp_costs, temp_edges = get_temp_buffers(tiles.device, K)
+ ext.quantize_tiles(
+ tiles,
+ quantized_tiles,
+ quantized_idx,
+ temp_costs,
+ temp_edges,
+ K,
+ mcg,
+ mul1,
+ )
+ return quantized_tiles, quantized_idx
+
+
+@lru_cache
+def get_quant_stream(device):
+ return torch.cuda.Stream(device = device)
+
+
+pinned_tiles: torch.Tensor | None = None
+pinned_q_tiles: torch.Tensor | None = None
+pinned_q_idx: torch.Tensor | None = None
+def get_pinned(num_tiles: int):
+ global pinned_tiles, pinned_q_tiles, pinned_q_idx
+ if pinned_tiles is None or pinned_tiles.shape[0] < num_tiles:
+ pinned_tiles = torch.empty((num_tiles, 256), device = "cpu", dtype = torch.float, pin_memory = True)
+ pinned_q_tiles = torch.empty((num_tiles, 256), device = "cpu", dtype = torch.float, pin_memory = True)
+ pinned_q_idx = torch.empty((num_tiles, 256), device = "cpu", dtype = torch.int16, pin_memory = True)
+ return pinned_tiles[:num_tiles, :], pinned_q_tiles[:num_tiles, :], pinned_q_idx[:num_tiles, :]
+
+
+def quantize_tiles_multigpu(tiles, quant_args: dict):
+ devices = quant_args["devices"]
+ if len(devices) == 1:
+ return quantize_tiles(tiles, quant_args)
+
+ # Get pinned buffers
+ pin_tiles, pin_q_tiles, pin_q_idx = get_pinned(tiles.shape[0])
+
+ # Copy input tiles to pinned memory. Input is always on the first device in the split
+ copy_input_event = torch.cuda.Event(blocking = False)
+ main_stream = get_quant_stream(devices[0])
+ with torch.cuda.stream(main_stream):
+ tiles = tiles.contiguous()
+ pin_tiles.copy_(tiles, non_blocking = True)
+ copy_input_event.record(main_stream)
+
+ # Create split slices for input tiles, output tiles and output indices
+ ratios = quant_args.get("device_ratios")
+ if ratios:
+ s = sum(ratios)
+ split_sizes = [tiles.shape[0] * r / s for r in ratios]
+ split_sizes = [round(s / 16) * 16 for s in split_sizes]
+ split_sizes[-1] += tiles.shape[0] - sum(split_sizes)
+ else:
+ split_sizes = [tiles.shape[0] // len(devices)] * len(devices)
+ split_sizes[-1] += tiles.shape[0] - sum(split_sizes)
+
+ # Account for negative splits (edge case if too many GPUs and/or tensor too small)
+ for i in range(len(split_sizes) - 2, -1, -1):
+ if split_sizes[i + 1] < 0:
+ split_sizes[i] += split_sizes[i + 1]
+ split_sizes[i + 1] = 0
+
+ pin_split_tiles = torch.split(pin_tiles, split_sizes)
+ pin_split_q_tiles = torch.split(pin_q_tiles, split_sizes)
+ pin_split_q_idx = torch.split(pin_q_idx, split_sizes)
+
+ slice_done_events = []
+ for i, device in enumerate(devices):
+
+ stream = get_quant_stream(device)
+ with torch.cuda.stream(stream):
+
+ # Wait for input in host memory
+ if i > 0:
+ stream.wait_event(copy_input_event)
+
+ if split_sizes[i] > 0:
+
+ # Asynchronously copy the slice from the pinned buffer to device memory
+ dev_tiles = pin_split_tiles[i].to(device, non_blocking = True)
+
+ # Preallocate output tensors on the device.
+ dev_q_tiles = torch.empty_like(dev_tiles, device = device)
+ dev_q_idx = torch.empty_like(dev_tiles, dtype = torch.short, device = device)
+
+ # Work buffers
+ K = quant_args["K"]
+ mcg = "mcg" in quant_args
+ mul1 = "mul1" in quant_args
+ temp_costs, temp_edges = get_temp_buffers(device, K)
+
+ ext.quantize_tiles(
+ dev_tiles,
+ dev_q_tiles,
+ dev_q_idx,
+ temp_costs,
+ temp_edges,
+ K,
+ mcg,
+ mul1
+ )
+
+ # Async copy back to pinned memory
+ pin_split_q_tiles[i].copy_(dev_q_tiles, non_blocking = True)
+ pin_split_q_idx[i].copy_(dev_q_idx, non_blocking = True)
+
+ # Finished slice
+ evt = torch.cuda.Event(blocking = False)
+ slice_done_events.append(evt)
+ evt.record(stream)
+
+ # Copy pinned buffers to original device
+ with torch.cuda.stream(main_stream):
+ for evt in slice_done_events:
+ main_stream.wait_event(evt)
+ q_tiles = torch.empty_like(tiles, device = devices[0])
+ q_idx = torch.empty_like(tiles, dtype = torch.short, device = devices[0])
+ q_tiles.copy_(pin_q_tiles, non_blocking = True)
+ q_idx.copy_(pin_q_idx, non_blocking = True)
+
+ return q_tiles, q_idx
+
+
+def quantize_tiles_multigpu_sync(tiles, quant_args: dict):
+ devices = quant_args["devices"]
+ if len(devices) == 1:
+ return quantize_tiles(tiles, quant_args)
+
+ tiles = tiles.contiguous()
+
+ split_sizes = [tiles.shape[0] // len(devices)] * len(devices)
+ split_sizes[-1] += tiles.shape[0] - sum(split_sizes)
+ split_tiles = torch.split(tiles, split_sizes)
+ tiles_per_device = [chunk.to(device) for chunk, device in zip(split_tiles, devices)]
+ torch.cuda.synchronize()
+
+ q_tiles_per_device = []
+ q_idx_per_device = []
+ for dev_tiles, device in zip(tiles_per_device, devices):
+ with torch.cuda.stream(get_quant_stream(device)):
+ dev_q_tiles, dev_q_idx = quantize_tiles(dev_tiles, quant_args)
+ q_tiles_per_device.append(dev_q_tiles)
+ q_idx_per_device.append(dev_q_idx)
+
+ for device in devices:
+ torch.cuda.synchronize(device)
+
+ q_tiles_per_device = [x.to(devices[0]) for x in q_tiles_per_device]
+ q_idx_per_device = [x.to(devices[0]) for x in q_idx_per_device]
+ quantized_tiles = torch.cat(q_tiles_per_device, dim = 0)
+ quantized_idx = torch.cat(q_idx_per_device, dim = 0)
+ return quantized_tiles, quantized_idx
+
+
+def preapply_had_l(x: torch.Tensor, had_dim):
+ k, n = x.shape
+ x_dtype = x.dtype
+ x = x.to(torch.float)
+ had = get_hadamard_dt(had_dim, x.device, x.dtype, 1 / math.sqrt(had_dim))
+ x = (had @ x.view(-1, had_dim, n)).view(k, n)
+ x = x.to(x_dtype)
+ return x
+
+
+def preapply_had_r(x: torch.Tensor, had_dim):
+ k, n = x.shape
+ x_dtype = x.dtype
+ x = x.to(torch.float)
+ had = get_hadamard_dt(had_dim, x.device, x.dtype, 1 / math.sqrt(had_dim))
+ x = (x.view(k, -1, had_dim) @ had).view(k, n)
+ x = x.to(x_dtype)
+ return x
+
+
+def blockwise_preapply_had_l_(x: torch.Tensor, had_dim):
+ k, n = x.shape
+ assert k % had_dim == 0
+ assert x.dtype == torch.float
+ had = get_hadamard_dt(had_dim, x.device, x.dtype, 1 / math.sqrt(had_dim))
+ num_blocks = k // had_dim
+ for i in range(num_blocks):
+ start = i * had_dim
+ end = start + had_dim
+ block = x[start:end, :] # shape (had_dim, n)
+ block_transformed = had @ block.view(had_dim, n)
+ x[start:end, :] = block_transformed
+
+
+def blockwise_preapply_had_r_(x: torch.Tensor, had_dim):
+ k, n = x.shape
+ assert n % had_dim == 0
+ assert x.dtype == torch.float
+ had = get_hadamard_dt(had_dim, x.device, x.dtype, 1 / math.sqrt(had_dim))
+ num_blocks = n // had_dim
+ for i in range(num_blocks):
+ start = i * had_dim
+ end = start + had_dim
+ block = x[:, start:end] # shape (k, had_dim)
+ block_transformed = block @ had
+ x[:, start:end] = block_transformed
+
+
+def block_ldl(H: torch.Tensor, b: int, verbose: bool):
+
+ n, _ = H.shape
+ assert (n % b == 0)
+ m = n // b
+
+ # Cholesky factorization: H = L @ L.T
+ # Try on GPU first
+ try:
+ retry_cpu = False
+ L = torch.linalg.cholesky(H)
+ # H is not needed after this, move to CPU. Then overwrite H's GPU storage with L, since we can't otherwise
+ # free up that VRAM as the tensor is referenced by the parent frame
+ H_cpu = H.cpu()
+ H.copy_(L) # VRAM copy, tiny overhead
+ L = H
+ H = H_cpu
+
+ # Fall back on CPU factorization
+ except Exception as e:
+ if e.__class__.__name__ == "OutOfMemoryError" or "CUDA out of memory" in str(e) or "HIP out of memory" in str(e):
+ retry_cpu = True
+ else:
+ raise e
+ if retry_cpu:
+ print(f" !! Out of memory on {str(H.device)}, trying CPU fallback")
+ free_mem()
+ H_cpu = H.cpu()
+ L_cpu = torch.linalg.cholesky(H_cpu)
+ # This is ugly, but overwrite H in VRAM to avoid allocating a new tensor, then replace reference with CPU copy
+ H.copy_(L_cpu)
+ del L_cpu
+ L = H
+ H = H_cpu
+
+ # Get blocks along diagonal of L: DL.shape = (m, b, b)
+ DL = torch.diagonal(L.reshape(m, b, m, b), dim1 = 0, dim2 = 2).permute(2, 0, 1)
+
+ # Compute D as D[i] = DL[i] @ DL[i].T for each diagonal block i (don't actually end up needing this)
+ # D = DL @ DL.transpose(1, 2)
+
+ # Invert each diagonal block
+ DL = torch.linalg.inv(DL)
+
+ # Multiply each block's column with its inverse
+ L = L.view(n, m, b)
+ for i in range(m):
+ L[:, i, :] = L[:, i, :] @ DL[i, :, :] # TODO: Could maybe be L[m * b:, i, :]?
+ L = L.reshape(n, n).contiguous()
+
+ # Insert block identity matrices along the diagonal.
+ # TODO: Figure out if this is necessary. Diagonal blocks should already be identities after previous step
+ L_block = L.view(m, b, m, b).permute(0, 2, 1,3)
+ dr = torch.arange(m)
+ L_block[dr, dr] = torch.stack([torch.eye(b, device = L.device, dtype = H.dtype)] * m)
+
+ return L, H # , D.to(DL.device)
+
+
+def ldlq(
+ weight: torch.Tensor,
+ L: torch.Tensor,
+ quant_args: dict,
+ pb: ProgressBar | None = None
+):
+ """
+ :param weight:
+ Input weights, shape (k, n). If device is "cpu", result is collected on CPU as well, saving a bunch of
+ VRAM but adding a little PCIe overhead and many sync points
+
+ :param L:
+ LDL decomposition of regularized H
+
+ :param quant_args:
+ dict:
+ - K: bitrate
+ - buf_size_k: buffer size for LDLQ, along k
+
+ :param pb:
+ Optional ProgressPar to update, k // 16 steps
+
+ :return:
+ tuple:
+ - quantized weight, shape (k, n)
+ - indices (unpacked), shape (k // 16, n // 16, 256), uint16_t
+ """
+
+ devices = quant_args["devices"]
+ for device in devices:
+ torch.cuda.synchronize(device)
+ main_stream = get_quant_stream(devices[0])
+ with torch.cuda.stream(main_stream):
+
+ devices = quant_args["devices"]
+ device = L.device
+ assert device == torch.device(devices[0])
+
+ buffer_device = weight.device
+ size_k, size_n = weight.shape # Row-major
+ assert size_k % 16 == 0
+ assert size_n % 128 == 0
+ tiles_k = size_k // 16
+ tiles_n = size_n // 16
+
+ buf_size_k = max(quant_args.get("buf_size_k", 128), 16)
+ assert buf_size_k % 16 == 0
+ assert size_n % buf_size_k == 0
+
+ p_row = 0
+
+ # Work buffers
+ prod_cache = torch.zeros((size_k, size_n), dtype = torch.float, device = device)
+ weight_q = torch.zeros((size_k, size_n), dtype = torch.float, device = buffer_device)
+ encoded = torch.zeros((tiles_k, tiles_n, 256), dtype = torch.short, device = buffer_device)
+
+ for j in range(size_k, 0, -buf_size_k):
+ i = j - buf_size_k
+
+ # Current span is rows i:j
+ b_weight = weight[i:j].to(device)
+ b_weight_q = weight_q[i:j] if device == buffer_device else \
+ torch.zeros_like(weight_q[i:j], device = device)
+ b_encoded = encoded[i // 16 : j // 16] if device == buffer_device else \
+ torch.zeros_like(encoded[i // 16 : j // 16], device = device)
+ b_prod_cache = prod_cache[i:j]
+ b_L = L[i:j]
+
+ # Iterate over rows of blocks in current span
+ for bj in range(buf_size_k, 0, -16):
+ bi = bj - 16
+
+ # Error so far for the current span
+ bb_err = b_weight[bj:] - b_weight_q[bj:]
+
+ # Corresponding slice of LDL decomposition of H
+ bb_L = b_L[bj:, i + bi:i + bj]
+
+ # Input tiles for quantization
+ compensation_term = b_prod_cache[bi:bj]
+ compensation_term.addmm_(bb_L.T, bb_err, alpha = 1.0, beta = 1.0)
+ rows = b_weight[bi:bj] + compensation_term
+
+ tiles = rows.reshape(16, tiles_n, 16).permute(1, 0, 2).reshape(tiles_n, 256)
+
+ # Pre-permute to tensor core layout
+ tiles = tiles[:, tensor_core_perm(device)]
+
+ # Quantize
+ quant_w, quant_i = quantize_tiles_multigpu(tiles, quant_args)
+
+ # Undo permutation on reconstructed tiles, but keep indices in tensor core layout
+ quant_w = quant_w[:, tensor_core_perm_i(device)]
+
+ # Store result
+ quant_w = quant_w.reshape(tiles_n, 16, 16).permute(1, 0, 2).reshape(16, size_n)
+ b_weight_q[bi:bj] = quant_w
+ b_encoded[bi // 16 : bj // 16] = quant_i.unsqueeze(0)
+
+ # Update progress
+ if pb:
+ p_row += 1
+ pb.update(p_row)
+
+ # Collect output
+ if device != buffer_device:
+ weight_q[i:j] = b_weight_q.to(buffer_device)
+ encoded[i // 16 : j // 16] = b_encoded.to(buffer_device)
+
+ # Cache error term for the rest of the matrix
+ b_err = b_weight - b_weight_q
+ prod_cache.addmm_(b_L.T, b_err, alpha = 1.0, beta = 1.0)
+
+ for device in devices:
+ torch.cuda.synchronize(device)
+
+ return weight_q, encoded
+
+
+def fallback_quant(
+ weight: torch.Tensor,
+ q_device: torch.Tensor,
+ quant_args: dict,
+ pb: ProgressBar | None = None
+):
+ """
+ Perform the same quantization as ldlq() but without an LDL decomposition
+
+ :param weight:
+ Input weights, shape (k, n). If device is "cpu", result is collected on CPU as well, saving a bunch of
+ VRAM but adding a little PCIe overhead and many sync points
+
+ :param q_device:
+ Target device
+
+ :param quant_args:
+ dict:
+ - K: bitrate
+ - buf_size_k: buffer size for faux-LDLQ, along k
+
+ :param pb:
+ Optional ProgressPar to update, k // 16 steps
+
+ :return:
+ tuple:
+ - quantized weight, shape (k, n)
+ - indices (unpacked), shape (k // 16, n // 16, 256), uint16_t
+ """
+
+ devices = quant_args["devices"]
+ for device in devices:
+ torch.cuda.synchronize(device)
+ main_stream = get_quant_stream(devices[0])
+ with torch.cuda.stream(main_stream):
+
+ devices = quant_args["devices"]
+ device = weight.device
+ assert device == torch.device(devices[0])
+
+ buffer_device = weight.device
+ size_k, size_n = weight.shape # Row-major
+ assert size_k % 16 == 0
+ assert size_n % 128 == 0
+ tiles_k = size_k // 16
+ tiles_n = size_n // 16
+
+ buf_size_k = max(quant_args.get("buf_size_k", 128), 16)
+ assert buf_size_k % 16 == 0
+ assert size_n % buf_size_k == 0
+
+ p_row = 0
+
+ # Work buffers
+ weight_q = torch.zeros((size_k, size_n), dtype = torch.float, device = buffer_device)
+ encoded = torch.zeros((tiles_k, tiles_n, 256), dtype = torch.short, device = buffer_device)
+
+ for j in range(size_k, 0, -buf_size_k):
+ i = j - buf_size_k
+
+ # Current span is rows i:j
+ b_weight = weight[i:j].to(device)
+ b_weight_q = weight_q[i:j] if device == buffer_device else \
+ torch.zeros_like(weight_q[i:j], device = device)
+ b_encoded = encoded[i // 16 : j // 16] if device == buffer_device else \
+ torch.zeros_like(encoded[i // 16 : j // 16], device = device)
+
+ # Iterate over rows of blocks in current span
+ for bj in range(buf_size_k, 0, -16):
+ bi = bj - 16
+
+ # Input tiles for quantization
+ rows = b_weight[bi:bj]
+ tiles = rows.reshape(16, tiles_n, 16).permute(1, 0, 2).reshape(tiles_n, 256)
+
+ # Pre-permute to tensor core layout
+ tiles = tiles[:, tensor_core_perm(device)]
+
+ # Quantize
+ quant_w, quant_i = quantize_tiles_multigpu(tiles, quant_args)
+
+ # Undo permutation on reconstructed tiles, but keep indices in tensor core layout
+ quant_w = quant_w[:, tensor_core_perm_i(device)]
+
+ # Store result
+ quant_w = quant_w.reshape(tiles_n, 16, 16).permute(1, 0, 2).reshape(16, size_n)
+ b_weight_q[bi:bj] = quant_w
+ b_encoded[bi // 16 : bj // 16] = quant_i.unsqueeze(0)
+
+ # Update progress
+ if pb:
+ p_row += 1
+ pb.update(p_row)
+
+ # Collect output
+ if device != buffer_device:
+ weight_q[i:j] = b_weight_q.to(buffer_device)
+ encoded[i // 16 : j // 16] = b_encoded.to(buffer_device)
+
+ for device in devices:
+ torch.cuda.synchronize(device)
+
+ return weight_q, encoded
+
+
+finalize_capture_H_mutex = threading.Lock()
+
+def finalize_capture_H(H_data: dict, quant_args: dict, verbose: bool):
+ with finalize_capture_H_mutex:
+
+ # Unswap H
+ if "H_swap_device" in H_data:
+ H_data["H"] = H_data["H"].to(H_data["H_swap_device"])
+ del H_data["H_swap_device"]
+
+ H = H_data["H"]
+ if H_data["finalized"]:
+ return H_data["q_fallback"], H, H_data["L"], H_data["su"], H_data["diag"]
+
+ # Mean of samples summed up during forward pass
+ # Switch to uncalibrated fallback if no input activations or diagonal is too small (few activations)
+ count = H_data["count"]
+ if count == 0:
+ q_fallback = True
+ else:
+ H /= count
+ diag_mean = torch.diag(H).mean()
+ q_fallback = diag_mean.item() < 1e-20
+
+ # Regularize diagonal
+ idx = torch.arange(H.shape[0])
+ H[idx, idx] += quant_args.get("sigma_reg", 0.025) * diag_mean
+
+ # Some tests
+ diag = H[idx, idx].clone()
+
+ if verbose:
+ print(f" - H min/max: {H.min().item():.6f} {H.max().item():.6f}")
+ print(f" - H mean/std: {H.mean().item():.6f} {H.std().item():.6f}")
+ print(f" - H diag min/max: {diag.min():.6f} {diag.max():.6f} ")
+
+ # Random sign flips for input channel, fixed for the first linear layer to quantize with this H
+ k = H.shape[0]
+ su = (torch.randn(k, device = H.device).sign() + 1e-5).sign().to(torch.float).unsqueeze(1)
+ H_data["su"] = su
+
+ # Input had
+ H *= su.T
+ blockwise_preapply_had_r_(H, had_k)
+ H *= su
+ blockwise_preapply_had_l_(H, had_k)
+
+ # Get block LDL decomposition of H, zero diagonal
+ if q_fallback:
+ L = None
+ else:
+ L, H = block_ldl(H, 16, verbose)
+ dr = torch.arange(k)
+ L[dr, dr] = 0
+
+ H_data["L"] = L
+
+ # H is no longer needed except to compute proxy error so move to CPU
+ H = H.cpu()
+ H_data["H"] = H.cpu()
+
+ H_data["finalized"] = True
+ H_data["diag"] = diag
+ H_data["q_fallback"] = q_fallback
+ return q_fallback, H, L, su, diag
+
+
+def pack_trellis(encoded: torch.Tensor, quant_args: dict) -> torch.Tensor:
+ K = quant_args["K"]
+ shape = encoded.shape
+ assert len(shape) == 3 and shape[2] == 256
+ assert encoded.dtype == torch.int16
+ packed_shape = (shape[0], shape[1], 256 * K // 16)
+ packed = torch.zeros(packed_shape, dtype = torch.int16, device = encoded.device)
+ ext.pack_trellis(packed, encoded.contiguous(), K)
+ # unpacked = torch.zeros_like(encoded)
+ # ext.unpack_trellis(unpacked, packed, K)
+ # assert torch.equal(unpacked, encoded)
+ return packed
+
+
+def pack_signs(signs: torch.Tensor, quant_args: dict) -> torch.Tensor:
+ signs = signs.half().flatten().contiguous()
+ assert signs.shape[0] % 16 == 0
+ packed = torch.zeros(signs.shape[0] // 16, dtype = torch.int16, device = signs.device)
+ ext.pack_signs(packed, signs)
+ return packed
+
+
+def g_scale_gss(
+ weight_r: torch.Tensor,
+ verbose: bool,
+ quant_args: dict,
+ width: int = 3,
+ pb: ProgressBar = None
+):
+ # Select a sample of tiles along a wrapped diagonal (sampling from every row and column of tiles, hopefully
+ # representative) and search for the global scale within given range that minimizes the direct quantization
+ # error
+ tiles = []
+ tiles_k = weight_r.shape[0] // 16
+ tiles_n = weight_r.shape[1] // 16
+ for i in range(max(tiles_k, tiles_n)):
+ for w in range(width):
+ k = (i % tiles_k) * 16
+ n = ((i + w) % tiles_n) * 16
+ tile = weight_r[k : k + 16, n : n + 16].clone()
+ tile = tile.view(256)
+ tile = tile[tensor_core_perm(weight_r.device)]
+ tiles.append(tile)
+ tiles = torch.stack(tiles)
+
+ devices = quant_args["devices"]
+ for device in devices:
+ torch.cuda.synchronize(device)
+
+ main_stream = get_quant_stream(devices[0])
+ # TODO: Figure out why Torch always initializes cuda:0 when exiting this CM, even when it's not used
+ with torch.cuda.stream(main_stream):
+
+ def test_scale(scale: float):
+ quant_w, quant_i = quantize_tiles_multigpu(tiles * scale, quant_args)
+ mse = ((quant_w / scale - tiles) ** 2).mean()
+ return mse
+
+ # Assume quantization error is a unimodal function of scale, golden section search to find minimum
+ phi = (1 + math.sqrt(5)) / 2
+ resphi = 2 - phi
+
+ a, b = 0.1, 1.9
+ tol = 0.01
+ delta1 = abs(b - a)
+
+ x1 = a + resphi * (b - a)
+ x2 = b - resphi * (b - a)
+ f1 = test_scale(x1)
+ f2 = test_scale(x2)
+ while abs(b - a) > tol:
+ # if verbose:
+ # print(f" - gss: a = {a:.6f}, b = {b:.6f}")
+ if f1 < f2:
+ b = x2
+ x2 = x1
+ f2 = f1
+ x1 = a + resphi * (b - a)
+ f1 = test_scale(x1)
+ else:
+ a = x1
+ x1 = x2
+ f1 = f2
+ x2 = b - resphi * (b - a)
+ f2 = test_scale(x2)
+ delta2 = abs(b - a)
+ if pb:
+ pb.update(100 - 100 * int(delta2 / delta1))
+
+ best_scale = (a + b) / 2
+ if verbose:
+ print(f" - gss: min = {best_scale:.6f}, mse: {(f1 + f2) / 2:.6f}")
+
+ devices = quant_args["devices"]
+ for device in devices:
+ torch.cuda.synchronize(device)
+
+ return best_scale, (f1 + f2) / 2
+
+
+def block_rms(x: torch.Tensor, dim: int, keepdim: bool = False, blocksize: int = 32):
+ # Compute blockwise x.square().mean(dim, keepdim).sqrt()
+ n = x.size(dim)
+ sq = None
+ for block in torch.split(x, blocksize, dim = dim):
+ block_sq = block.square().sum(dim = dim, keepdim = keepdim)
+ if sq is None:
+ sq = block_sq
+ else:
+ sq += block_sq
+ mean_sq = sq / n
+ return mean_sq.sqrt()
+
+
+def block_rms_n(x: torch.Tensor, dim: int = 0, blocksize: int = 32):
+ # Compute blockwise x.square().mean().sqrt()
+ n = 0
+ sq = None
+ for block in torch.split(x, blocksize, dim = dim):
+ block_sq = block.square().sum()
+ n += block.numel()
+ if sq is None:
+ sq = block_sq
+ else:
+ sq += block_sq
+ mean_sq = sq / n
+ return mean_sq.sqrt()
+
+
+def block_nmse(x: torch.Tensor, y: torch.Tensor, dim: int = 0, blocksize: int = 32):
+ # Compute blockwise (x - y).square().mean().item() / y.square().mean().item()
+ sq = None
+ diff_sq = None
+ for block_x, block_y in zip(torch.split(x, blocksize, dim = dim), torch.split(y, blocksize, dim = dim)):
+ block_sq = block_y.square().sum()
+ block_diff_sq = (block_x - block_y).square().sum()
+ if sq is None:
+ sq = block_sq
+ diff_sq = block_diff_sq
+ else:
+ sq += block_sq
+ diff_sq += block_diff_sq
+ return diff_sq.item() / (sq.item() + 1e-20)
+
+
+def regularize(
+ weight: torch.Tensor,
+ su: torch.Tensor,
+ sv: torch.Tensor,
+ quant_args: dict,
+ verbose: bool,
+ H_diag: torch.Tensor | None,
+ pb: ProgressBar | None,
+ skip_g_scale: bool = False,
+ q_fallback: bool = False
+):
+ force_out_scales = quant_args["apply_out_scales"]
+
+ # dist_ref = torch.empty((512,), dtype = torch.float, device = weight.device)
+ # dist_r = torch.empty_like(dist_ref)
+ def jsd(h1, h2):
+ m = (h1 + h2) / 2
+ eps = 1e-12
+ js = F.kl_div((h1 + eps).log(), m, reduction = "sum") + \
+ F.kl_div((h2 + eps).log(), m, reduction = "sum")
+ return js / 2
+
+ # From experiments, it seems the deciding factor in when scaling output channels is beneficial is when
+ # the input to the linear layer is very irregular. After some testing, set the cutoff at 15% of the RMS sum
+ # on 2% of the channels
+ # TODO: More science
+ if not q_fallback and H_diag is not None:
+ diag = H_diag.sqrt()
+ diag, _ = torch.sort(diag, descending = True)
+ cutoff = diag.shape[0] // 50
+ skew_factor = diag[:cutoff].sum() / diag.sum()
+ if verbose:
+ print(f" - input state skew: {skew_factor.item():.6f}")
+
+ if force_out_scales is None:
+ apply_out_scales = skew_factor.item() < 0.15
+ else:
+ apply_out_scales = force_out_scales
+
+ else:
+ apply_out_scales = True if force_out_scales is None else force_out_scales
+
+ if q_fallback:
+ apply_out_scales = force_out_scales
+
+ # Apply output scales
+ out_channel_scales = block_rms(weight, dim = 0, keepdim = True)
+ mean = out_channel_scales.mean().item()
+ if mean > 1e-30:
+ out_channel_scales /= mean
+ quant_args["zeros"] = False
+ else:
+ quant_args["zeros"] = True
+ if force_out_scales is not None:
+ apply_out_scales = True
+ zero_out_scales = out_channel_scales.abs() < 1e-30
+
+ if apply_out_scales:
+ out_channel_scales[zero_out_scales] = 0.1
+ sv = (sv * out_channel_scales + 1e-10).float()
+ if verbose:
+ out_channel_std = out_channel_scales.std().item()
+ out_channel_mean = out_channel_scales.mean().item()
+ print(f" - out ch scales std/mean: {out_channel_std:.6f} {out_channel_mean:.6f}")
+
+ # Output sign flips (and scales)
+ weight /= sv
+
+ # Force zero output channels to zero
+ sv[zero_out_scales] = 0.0
+
+ # Output hadamard transform
+ blockwise_preapply_had_r_(weight, had_n)
+
+ # Input sign flips and scales
+ in_channel_scales = block_rms(weight, dim = 1, keepdim = True)
+ in_channel_scales[in_channel_scales.abs() < 1e-30] = 0.1
+ su = (su * in_channel_scales / (-codebook_scale) + 1e-10).float() # mustn't be inplace
+ weight /= su
+ blockwise_preapply_had_l_(weight, had_k)
+
+ # Determine best scale for matrix by test quantizing a sample of tiles along a wrapped diagonal
+ if not skip_g_scale:
+ g_scale, mse_scale = g_scale_gss(weight, False, quant_args, pb = pb)
+ else:
+ g_scale = 1.0
+ weight *= g_scale
+ su /= g_scale
+
+ # ext.test_distribution(weight_os, dist_r, dist_ref, -3.8, 3.8)
+ # js_os = jsd(dist_r, dist_ref)
+
+ if verbose:
+ print(f" - su/sv std: {su.std().item():.6f} {sv.std().item():.6f}")
+ print(f" - global scale: {g_scale:.6f}")
+ print(f" - sample mse: {mse_scale.item():.6f}")
+ print(f" - apply_out_scales: {str(apply_out_scales)}")
+
+ return apply_out_scales, weight, g_scale, su, sv
+
+
+def quantize_exl3(
+ weight: torch.Tensor,
+ H_data: dict,
+ quant_args: dict,
+ return_weight_q: bool,
+ progress_str: str | None = None,
+ verbose: bool = False,
+ swap_to_device: torch.device | None = None,
+ save_reg: str = None
+):
+ """
+ :param weight:
+ Input tensor, row major shape (in_features, out_features)
+
+ :param H_data:
+ Dictionary of hessian tensor and related data, as collected by Linear wrapper class. May be reused between
+ linear layers with the same input (e.g. Q, K and V projections)
+
+ :param quant_args:
+ dict:
+ - K: bitrate
+ - seed: integer seed for random sign flips etc.
+ - sigma_reg: regularization factor
+
+ :param return_weight_q:
+ Return quantized weight
+
+ :param progress_str:
+ Show progress bar during quantization
+
+ :param verbose:
+ Dump extra stats
+
+ :param swap_to_device:
+ If input tensor is on CPU, move to this device before quantization
+
+ :param save_reg:
+ Save regularized tensor as image to the provided path
+
+ :return:
+ tuple:
+ - quantized weight
+ - proxy error: trace(err @ H @ err.T) / (W @ H @ W.T)
+ - quantized and packed tensors
+ """
+
+ progress_text = None if not progress_str else progress_str.replace("", "Preparing")
+ with (ProgressBar(progress_text, 100) as pb):
+
+ assert weight.dtype == torch.float
+ tiles_k = weight.shape[0] // 16
+
+ if "seed" in quant_args:
+ torch.manual_seed(quant_args["seed"])
+
+ devices = quant_args["devices"]
+ if weight.device != torch.device(devices[0]):
+ weight = weight.to(devices[0])
+
+ device = weight.device if swap_to_device is None else swap_to_device
+ k, n = weight.shape
+
+ # Get H, LDL decomp. and input/output sign flips
+ q_fallback, H, L, su, H_diag = finalize_capture_H(H_data, quant_args, verbose)
+ if H.is_cuda:
+ H = H.to(device)
+ if L is not None and L.is_cuda:
+ L = L.to(device)
+ if su.is_cuda:
+ su = su.to(device)
+ if H_diag.is_cuda:
+ H_diag = H_diag.to(device)
+ sv = (torch.randn(n, device = device).sign() + 1e-5).sign().to(torch.float).unsqueeze(0)
+
+ # Move stored L to CPU (if not already), move working L to device
+ if H_data["L"] is not None:
+ H_data["L"] = H_data["L"].cpu()
+ if L is not None:
+ L = L.to(device)
+
+ if swap_to_device is not None:
+ weight = weight.to(swap_to_device)
+ if verbose:
+ weight_copy = weight.cpu()
+ weight_r = weight
+ del weight
+
+ if verbose:
+ rms = block_rms_n(weight_r, dim = 0)
+ print(f" - input tensor rms: {rms:.6f}")
+
+ # Regularization
+ apply_out_scales, weight_r, g_scale, su, sv = regularize(
+ weight_r,
+ su,
+ sv,
+ quant_args,
+ verbose,
+ H_diag,
+ pb,
+ q_fallback = q_fallback
+ )
+
+ if save_reg:
+ save_tensor_image(weight_r, save_reg)
+
+ if verbose:
+ rms = weight_r.square().mean().sqrt()
+ print(f" - regularized rms: {rms:.6f}")
+
+ progress_text = None if not progress_str else progress_str.replace("", "Quantizing")
+ pb.update(0)
+ pb.new_task(progress_text, tiles_k)
+
+ # Select device for work buffers (CPU is slower for small tensors but saves a lot of VRAM on big ones)
+ # TODO: Use pynvml or mem_get_info to predict whether CPU buffer is needed
+ if weight_r.numel() > 5e8:
+ weight_r = weight_r.cpu()
+
+ # Quantize
+ if not q_fallback:
+ weight_q, encoded_q = ldlq(weight_r, L, quant_args, pb) #zxc
+ del L
+ else:
+ weight_q, encoded_q = fallback_quant(weight_r, device, quant_args, pb) # zxc
+
+ pb.update(tiles_k)
+
+ # Metrics
+ if not q_fallback:
+ try:
+ def block_trace(A, B, block_size = 1024):
+ total = 0.0
+ for j_start in range(0, B.shape[1], block_size):
+ j_end = min(j_start + block_size, B.shape[1])
+ B_block = B[:, j_start:j_end]
+ A_j_block = A[j_start:j_end, :]
+ partial = torch.einsum("ik,ij,jk->", A, B_block, A_j_block)
+ total += partial.item()
+ return total
+ E = weight_r - weight_q # may run on CPU
+ W = weight_r
+ Hd = H.to(device)
+ weight_r = None
+ E = E.to(device)
+ num = block_trace(E, Hd)
+ E = None
+ W = W.to(device)
+ den = block_trace(W, Hd)
+ W = None
+ Hd = None
+ proxy_err = num / max(den, 1e-8)
+ except torch.OutOfMemoryError:
+ weight_r = None
+ E = None
+ W = None
+ Hd = None
+ proxy_err = -1.0
+ else:
+ proxy_err = 0.0
+
+ # free_mem()
+
+ if return_weight_q or verbose:
+ weight_q = weight_q.to(device)
+ weight_q = preapply_had_l(weight_q, had_k)
+ weight_q *= su
+ weight_q = preapply_had_r(weight_q, had_n)
+ weight_q *= sv
+
+ if verbose:
+ weight = weight_copy.to(device)
+ nmse = block_nmse(weight_q, weight)
+ print(f" - quant nmse: {nmse:.6f}")
+
+ # Compile packed tensor
+ suh = su.flatten().contiguous().to(dtype = torch.half, copy = True)
+ svh = sv.flatten().contiguous().to(dtype = torch.half, copy = True)
+ trellis = pack_trellis(encoded_q.to(device), quant_args)
+
+ out_tensors = {
+ # "scale": weight_scale.to(dtype = torch.float, copy = True),
+ # "su": pack_signs(su, quant_args),
+ "suh": suh,
+ # "sv": pack_signs(sv, quant_args),
+ "svh": svh,
+ "trellis": trellis,
+ }
+
+ # Safetensors doesn't know what to do with a torch.uint32 tensor. Anyway, since the multipliers are now
+ # locked, the values in these tensors are never read, but they need to be present in the model files to
+ # indicate which codebook to use during inference, per individual tensor.
+ if quant_args.get("mcg"):
+ out_tensors.update({
+ "mcg": torch.tensor(codebook_mcg_mult, dtype = torch.uint32).view(torch.int)
+ })
+ if quant_args.get("mul1"):
+ out_tensors.update({
+ "mul1": torch.tensor(codebook_mul1_mult, dtype = torch.uint32).view(torch.int)
+ })
+
+ quant_args.update({
+ "apply_out_scales": apply_out_scales,
+ "g_scale": g_scale,
+ "q_fallback": q_fallback,
+ })
+
+ return weight_q, proxy_err, out_tensors
diff --git a/gptqmodel/exllamav3/util/__init__.py b/gptqmodel/exllamav3/util/__init__.py
new file mode 100644
index 000000000..670066470
--- /dev/null
+++ b/gptqmodel/exllamav3/util/__init__.py
@@ -0,0 +1,3 @@
+from .misc import cuda_sync_active
+
+__all__ = ["cuda_sync_active"]
diff --git a/gptqmodel/exllamav3/util/arch_list.py b/gptqmodel/exllamav3/util/arch_list.py
new file mode 100644
index 000000000..991f13130
--- /dev/null
+++ b/gptqmodel/exllamav3/util/arch_list.py
@@ -0,0 +1,40 @@
+import os
+import torch
+
+# Since Torch 2.3.0 an annoying warning is printed every time the C++ extension is loaded, unless the
+# TORCH_CUDA_ARCH_LIST variable is set. The default behavior from pytorch/torch/utils/cpp_extension.py
+# is copied in the function below, but without the warning.
+
+def maybe_set_arch_list_env():
+
+ if os.environ.get('TORCH_CUDA_ARCH_LIST', None):
+ return
+
+ if not torch.version.cuda:
+ return
+
+ arch_list = []
+ for i in range(torch.cuda.device_count()):
+ capability = torch.cuda.get_device_capability(i)
+ # Strip known NVIDIA suffixes: 'a' (accelerated) or 'f' (family)
+ supported_sm = [int(arch.split('_')[1].rstrip('af'))
+ for arch in torch.cuda.get_arch_list() if 'sm_' in arch]
+ if not supported_sm:
+ continue
+ max_supported_sm = max((sm // 10, sm % 10) for sm in supported_sm)
+ # Capability of the device may be higher than what's supported by the user's
+ # NVCC, causing compilation error. User's NVCC is expected to match the one
+ # used to build pytorch, so we use the maximum supported capability of pytorch
+ # to clamp the capability.
+ capability = min(max_supported_sm, capability)
+ arch = f'{capability[0]}.{capability[1]}'
+ if arch not in arch_list:
+ arch_list.append(arch)
+ if not arch_list:
+ return
+ arch_list = sorted(arch_list)
+ arch_list[-1] += '+PTX'
+
+ os.environ["TORCH_CUDA_ARCH_LIST"] = ";".join(arch_list)
+
+maybe_set_arch_list_env()
\ No newline at end of file
diff --git a/gptqmodel/exllamav3/util/hadamard.py b/gptqmodel/exllamav3/util/hadamard.py
new file mode 100644
index 000000000..d9609dd12
--- /dev/null
+++ b/gptqmodel/exllamav3/util/hadamard.py
@@ -0,0 +1,137 @@
+from __future__ import annotations
+import torch
+import os, glob
+from functools import lru_cache
+from ..ext import exllamav3_ext as ext
+
+had_dict: dict[int: torch.Tensor] | None = {}
+primes: set[int]
+
+def load_constants():
+ global had_dict, primes
+
+ module_dir = os.path.dirname(os.path.abspath(__file__))
+ had_dir = os.path.join(module_dir, "hadamard_data")
+ file_pattern = os.path.join(had_dir, "hadamard_*.txt")
+ files = glob.glob(file_pattern)
+ had_dict = {}
+
+ for file_path in files:
+ with open(file_path, 'r') as file:
+ lines = file.readlines()
+ lines = [line.strip() for line in lines if line.strip()]
+ dim = len(lines)
+ assert all(len(line) == dim for line in lines), "Non-square matrix in " + file_path
+ matrix = [[1 if char == '+' else -1 for char in line] for line in lines]
+ tensor = torch.tensor(matrix, dtype = torch.float16)
+ had_dict[dim] = tensor
+
+ prime_path = os.path.join(had_dir, "primes.txt")
+ with open(prime_path, "r") as f:
+ lines = f.readlines()
+ primes = set([int(line) for line in lines if line.strip()])
+
+def sylvester(h: torch.Tensor):
+ d = h.shape[0]
+ assert d == h.shape[1], "h not square"
+ s = torch.empty((d * 2, d * 2), dtype = h.dtype, device = h.device)
+ s[:d, :d] = h
+ s[:d, d:] = h
+ s[d:, :d] = h
+ s[d:, d:] = -h
+ return s
+
+def is_quadratic_residue(a: int, p: int):
+ return pow(a, (p - 1) // 2, p) == 1
+
+def paley_torch(n: int):
+ h = torch.empty((n, n), dtype = torch.half)
+ p = n - 1
+ for i in range(p):
+ for j in range(p):
+ if i == j:
+ h[i + 1][j + 1] = 1
+ else:
+ residue = (i - j) % p
+ if is_quadratic_residue(residue, p):
+ h[i + 1][j + 1] = 1
+ else:
+ h[i + 1][j + 1] = -1
+ h[0, :] = 1
+ h[:, 0] = -1
+ h[0, 0] = 1
+ return h
+
+def paley(n: int):
+ h = torch.empty((n, n), dtype = torch.half)
+ ext.had_paley(h)
+ # ref = paley_torch(n)
+ # assert torch.all(h == ref)
+ return h
+
+def paley2_torch(n: int):
+ h = torch.empty((n, n), dtype = torch.half)
+ p = n // 2 - 1
+ for i in range(n // 2):
+ i0 = 2 * i + 0
+ i1 = 2 * i + 1
+ for j in range(n // 2):
+ j0 = 2 * j + 0
+ j1 = 2 * j + 1
+ if j == i:
+ h[i0, j0] = 1
+ h[i0, j1] = -1
+ h[i1, j0] = -1
+ h[i1, j1] = -1
+ else:
+ residue = (i - j) % p
+ if i == 0 or j == 0 or is_quadratic_residue(residue, p):
+ h[i0, j0] = 1
+ h[i0, j1] = 1
+ h[i1, j0] = 1
+ h[i1, j1] = -1
+ else:
+ h[i0, j0] = -1
+ h[i0, j1] = -1
+ h[i1, j0] = -1
+ h[i1, j1] = 1
+ return h
+
+def paley2(n: int):
+ h = torch.empty((n, n), dtype = torch.half)
+ ext.had_paley2(h)
+ # ref = paley2_torch(n)
+ # assert torch.all(h == ref)
+ return h
+
+@lru_cache(maxsize = 100)
+def get_hadamard(n: int):
+ global had_dict, primes
+
+ if not had_dict:
+ load_constants()
+
+ if n in had_dict: return had_dict[n]
+
+ # Sylvester's construction
+ if n % 2 == 0:
+ s = get_hadamard(n // 2)
+ if s is not None:
+ s = sylvester(s)
+ return s
+
+ # Paley construction
+ if n % 4 == 0 and (n - 1) % 4 == 3 and (n - 1) in primes:
+ return paley(n)
+
+ # Other Paley construction
+ if n % 4 == 0 and (n // 2) - 1 in primes:
+ return paley2(n)
+
+ return None
+
+@lru_cache(maxsize = 100)
+def get_hadamard_dt(n: int, device: torch.device | str, dtype: torch.dtype, scale = 1.0):
+ had = get_hadamard(n).to(device = device, dtype = dtype, copy = True)
+ had *= scale
+ return had
diff --git a/gptqmodel/exllamav3/util/hadamard_data/hadamard_1.txt b/gptqmodel/exllamav3/util/hadamard_data/hadamard_1.txt
new file mode 100644
index 000000000..9b26e9b10
--- /dev/null
+++ b/gptqmodel/exllamav3/util/hadamard_data/hadamard_1.txt
@@ -0,0 +1 @@
++
\ No newline at end of file
diff --git a/gptqmodel/exllamav3/util/hadamard_data/hadamard_100.txt b/gptqmodel/exllamav3/util/hadamard_data/hadamard_100.txt
new file mode 100644
index 000000000..78f3a4ae5
--- /dev/null
+++ b/gptqmodel/exllamav3/util/hadamard_data/hadamard_100.txt
@@ -0,0 +1,100 @@
++-++++----++--++----++++-+++-+++---+-++-+---+++-+++-++-+++-++----++-+++-++-+--++-+-+--------+-+-++--
+-+-++++----++--++----++++++++-+++---+-++-+---+++-+-+-++-+++-++----++-+++-++-+--++-+-+--------+-+-++-
++-+-++++----++--++----++++++++-+++---+-++-+---+++-+-+-++-+++-++----++-+++-+--+--++-+-+--------+-+-++
+++-+-++++----++--++----++-+++++-+++---+-++-+---+++++-+-++-+++-++----++-+++-+--+--++-+-+--------+-+-+
++++-+-++++----++--++----++-+++++-+++---+-++-+---++-++-+-++-+++-++----++-+++++--+--++-+-+--------+-+-
+++++-+-++++----++--++----++-+++++-+++---+-++-+---++-++-+-++-+++-++----++-++-++--+--++-+-+--------+-+
+-++++-+-++++----++--++---+++-+++++-+++---+-++-+---++-++-+-++-+++-++----++-++-++--+--++-+-+--------+-
+--++++-+-++++----++--++---+++-+++++-+++---+-++-+--+++-++-+-++-+++-++----++--+-++--+--++-+-+--------+
+---++++-+-++++----++--++---+++-+++++-+++---+-++-+--+++-++-+-++-+++-++----+++-+-++--+--++-+-+--------
+----++++-+-++++----++--++---+++-+++++-+++---+-++-++-+++-++-+-++-+++-++----+-+-+-++--+--++-+-+-------
++----++++-+-++++----++--++---+++-+++++-+++---+-++-++-+++-++-+-++-+++-++------+-+-++--+--++-+-+------
+++----++++-+-++++----++---+---+++-+++++-+++---+-++-++-+++-++-+-++-+++-++------+-+-++--+--++-+-+-----
+-++----++++-+-++++----++-+-+---+++-+++++-+++---+-+--++-+++-++-+-++-+++-++------+-+-++--+--++-+-+----
+--++----++++-+-++++----++++-+---+++-+++++-+++---+----++-+++-++-+-++-+++-++------+-+-++--+--++-+-+---
++--++----++++-+-++++----+-++-+---+++-+++++-+++---+----++-+++-++-+-++-+++-++------+-+-++--+--++-+-+--
+++--++----++++-+-++++----+-++-+---+++-+++++-+++---+----++-+++-++-+-++-+++-+-------+-+-++--+--++-+-+-
+-++--++----++++-+-++++----+-++-+---+++-+++++-+++--++----++-+++-++-+-++-+++---------+-+-++--+--++-+-+
+--++--++----++++-+-++++----+-++-+---+++-+++++-+++--++----++-+++-++-+-++-++++--------+-+-++--+--++-+-
+---++--++----++++-+-++++----+-++-+---+++-+++++-++++-++----++-+++-++-+-++-++-+--------+-+-++--+--++-+
+----++--++----++++-+-+++++---+-++-+---+++-+++++-++++-++----++-+++-++-+-++-++-+--------+-+-++--+--++-
++----++--++----++++-+-+++++---+-++-+---+++-+++++-++++-++----++-+++-++-+-++--+-+--------+-+-++--+--++
+++----++--++----++++-+-+++++---+-++-+---+++-+++++--+++-++----++-+++-++-+-+++-+-+--------+-+-++--+--+
++++----++--++----++++-+-+-+++---+-++-+---+++-++++++-+++-++----++-+++-++-+-+++-+-+--------+-+-++--+--
+++++----++--++----++++-+-+-+++---+-++-+---+++-++++++-+++-++----++-+++-++-+--++-+-+--------+-+-++--+-
+-++++----++--++----++++-+++-+++---+-++-+---+++-+++-++-+++-++----++-+++-++-+--++-+-+--------+-+-++--+
+---+---+++-+--+-+++---+--+-++++----++--++----++++--++--+-+-++++++++-+-+--+++-++-+++-++----++-+++-++-
+----+---+++-+--+-+++---+--+-++++----++--++----+++++-++--+-+-++++++++-+-+--+-+-++-+++-++----++-+++-++
+-----+---+++-+--+-+++---++-+-++++----++--++----+++++-++--+-+-++++++++-+-+--+-+-++-+++-++----++-+++-+
++-----+---+++-+--+-+++---++-+-++++----++--++----++-++-++--+-+-++++++++-+-+-++-+-++-+++-++----++-+++-
+-+-----+---+++-+--+-+++--+++-+-++++----++--++----+--++-++--+-+-++++++++-+-+-++-+-++-+++-++----++-+++
+--+-----+---+++-+--+-+++-++++-+-++++----++--++----+--++-++--+-+-++++++++-+-+-++-+-++-+++-++----++-++
+---+-----+---+++-+--+-+++-++++-+-++++----++--++----+--++-++--+-+-++++++++-+++-++-+-++-+++-++----++-+
++---+-----+---+++-+--+-++--++++-+-++++----++--++--+-+--++-++--+-+-++++++++-+++-++-+-++-+++-++----++-
+++---+-----+---+++-+--+-+---++++-+-++++----++--++--+-+--++-++--+-+-++++++++-+++-++-+-++-+++-++----++
++++---+-----+---+++-+--+-----++++-+-++++----++--+++-+-+--++-++--+-+-++++++++-+++-++-+-++-+++-++----+
+-+++---+-----+---+++-+--++----++++-+-++++----++--+++-+-+--++-++--+-+-++++++++-+++-++-+-++-+++-++----
++-+++---+-----+---+++-+--++----++++-+-++++----++--+++-+-+--++-++--+-+-+++++-++-+++-++-+-++-+++-++---
+-+-+++---+-----+---+++-+--++----++++-+-++++----++-++++-+-+--++-++--+-+-++++--++-+++-++-+-++-+++-++--
+--+-+++---+-----+---+++-+--++----++++-+-++++----+++++++-+-+--++-++--+-+-+++---++-+++-++-+-++-+++-++-
++--+-+++---+-----+---+++-+--++----++++-+-++++----+++++++-+-+--++-++--+-+-++----++-+++-++-+-++-+++-++
+-+--+-+++---+-----+---+++++--++----++++-+-++++----+++++++-+-+--++-++--+-+-++----++-+++-++-+-++-+++-+
++-+--+-+++---+-----+---++-++--++----++++-+-++++---++++++++-+-+--++-++--+-+-++----++-+++-++-+-++-+++-
+++-+--+-+++---+-----+---+--++--++----++++-+-++++---++++++++-+-+--++-++--+-+-++----++-+++-++-+-++-+++
++++-+--+-+++---+-----+------++--++----++++-+-++++-+-++++++++-+-+--++-++--+-+-++----++-+++-++-+-++-++
+-+++-+--+-+++---+-----+------++--++----++++-+-++++-+-++++++++-+-+--++-++--+++-++----++-+++-++-+-++-+
+--+++-+--+-+++---+-----+-+----++--++----++++-+-++++-+-++++++++-+-+--++-++--+++-++----++-+++-++-+-++-
+---+++-+--+-+++---+-----+++----++--++----++++-+-++-+-+-++++++++-+-+--++-++--+++-++----++-+++-++-+-++
++---+++-+--+-+++---+-----+++----++--++----++++-+-+--+-+-++++++++-+-+--++-+++-+++-++----++-+++-++-+-+
+-+---+++-+--+-+++---+----++++----++--++----++++-+-+--+-+-++++++++-+-+--++-+++-+++-++----++-+++-++-+-
+--+---+++-+--+-+++---+----++++----++--++----++++-+++--+-+-++++++++-+-+--++--++-+++-++----++-+++-++-+
+-+--+---+--++++--+---+--++--++-+-+--------+-+-++--+-++++----++--++----++++----+---+++-+--+-+++---+--
++-+--+---+--++++--+---+---+--++-+-+--------+-+-++--+-++++----++--++----++++----+---+++-+--+-+++---+-
+-+-+--+---+--++++--+---+---+--++-+-+--------+-+-+++-+-++++----++--++----+++-----+---+++-+--+-+++---+
+--+-+--+---+--++++--+---++--+--++-+-+--------+-+-+++-+-++++----++--++----+++-----+---+++-+--+-+++---
++--+-+--+---+--++++--+---++--+--++-+-+--------+-+-+++-+-++++----++--++----+-+-----+---+++-+--+-+++--
+-+--+-+--+---+--++++--+---++--+--++-+-+--------+-+++++-+-++++----++--++------+-----+---+++-+--+-+++-
+--+--+-+--+---+--++++--+-+-++--+--++-+-+--------+--++++-+-++++----++--++------+-----+---+++-+--+-+++
+---+--+-+--+---+--++++--+-+-++--+--++-+-+--------+--++++-+-++++----++--++--+---+-----+---+++-+--+-++
++---+--+-+--+---+--++++--+-+-++--+--++-+-+-----------++++-+-++++----++--++-++---+-----+---+++-+--+-+
+-+---+--+-+--+---+--++++--+-+-++--+--++-+-+-----------++++-+-++++----++--+++++---+-----+---+++-+--+-
+--+---+--+-+--+---+--++++--+-+-++--+--++-+-+------+----++++-+-++++----++--+-+++---+-----+---+++-+--+
++--+---+--+-+--+---+--+++---+-+-++--+--++-+-+-----++----++++-+-++++----++--+-+++---+-----+---+++-+--
+++--+---+--+-+--+---+--++----+-+-++--+--++-+-+-----++----++++-+-++++----++--+-+++---+-----+---+++-+-
++++--+---+--+-+--+---+--+-----+-+-++--+--++-+-+-----++----++++-+-++++----++--+-+++---+-----+---+++-+
+++++--+---+--+-+--+---+--------+-+-++--+--++-+-+--+--++----++++-+-++++----++--+-+++---+-----+---+++-
+-++++--+---+--+-+--+---+--------+-+-++--+--++-+-+-++--++----++++-+-++++-----+--+-+++---+-----+---+++
+--++++--+---+--+-+--+---+--------+-+-++--+--++-+-+-++--++----++++-+-++++---+-+--+-+++---+-----+---++
++--++++--+---+--+-+--+---+--------+-+-++--+--++-+---++--++----++++-+-++++--++-+--+-+++---+-----+---+
+-+--++++--+---+--+-+--+---+--------+-+-++--+--++-+---++--++----++++-+-++++-+++-+--+-+++---+-----+---
+--+--++++--+---+--+-+--+-+-+--------+-+-++--+--++-----++--++----++++-+-++++-+++-+--+-+++---+-----+--
+---+--++++--+---+--+-+--+-+-+--------+-+-++--+--+++----++--++----++++-+-+++--+++-+--+-+++---+-----+-
++---+--++++--+---+--+-+--+-+-+--------+-+-++--+--+++----++--++----++++-+-++---+++-+--+-+++---+-----+
+-+---+--++++--+---+--+-+-++-+-+--------+-+-++--+--+++----++--++----++++-+-++---+++-+--+-+++---+-----
+--+---+--++++--+---+--+-+-++-+-+--------+-+-++--+-++++----++--++----++++-+--+---+++-+--+-+++---+----
++--+---+--++++--+---+--+---++-+-+--------+-+-++--+-++++----++--++----++++-+--+---+++-+--+-+++---+---
+-++--+-+-++++++++-+-+--++-+--+---+--++++--+---+--++++-+++---+-++-+---+++-+++-++++----++--++----++++-
++-++--+-+-++++++++-+-+--++-+--+---+--++++--+---+--++++-+++---+-++-+---+++-+-+-++++----++--++----++++
+++-++--+-+-++++++++-+-+---+-+--+---+--++++--+---+-+++++-+++---+-++-+---+++-+-+-++++----++--++----+++
+-++-++--+-+-++++++++-+-+---+-+--+---+--++++--+---+-+++++-+++---+-++-+---+++++-+-++++----++--++----++
+--++-++--+-+-++++++++-+-++--+-+--+---+--++++--+---+-+++++-+++---+-++-+---+++++-+-++++----++--++----+
++--++-++--+-+-++++++++-+--+--+-+--+---+--++++--+--++-+++++-+++---+-++-+---+++++-+-++++----++--++----
+-+--++-++--+-+-++++++++-+--+--+-+--+---+--++++--+-+++-+++++-+++---+-++-+----++++-+-++++----++--++---
++-+--++-++--+-+-++++++++----+--+-+--+---+--++++--+-+++-+++++-+++---+-++-+----++++-+-++++----++--++--
+-+-+--++-++--+-+-+++++++++---+--+-+--+---+--++++----+++-+++++-+++---+-++-+----++++-+-++++----++--++-
++-+-+--++-++--+-+-+++++++-+---+--+-+--+---+--++++----+++-+++++-+++---+-++-+----++++-+-++++----++--++
+++-+-+--++-++--+-+-++++++--+---+--+-+--+---+--+++++---+++-+++++-+++---+-++-+----++++-+-++++----++--+
++++-+-+--++-++--+-+-++++++--+---+--+-+--+---+--+++-+---+++-+++++-+++---+-++++----++++-+-++++----++--
+++++-+-+--++-++--+-+-++++++--+---+--+-+--+---+--+++-+---+++-+++++-+++---+-+-++----++++-+-++++----++-
++++++-+-+--++-++--+-+-++++++--+---+--+-+--+---+--+++-+---+++-+++++-+++---+---++----++++-+-++++----++
+++++++-+-+--++-++--+-+-++++++--+---+--+-+--+---+---++-+---+++-+++++-+++---++--++----++++-+-++++----+
++++++++-+-+--++-++--+-+-+-++++--+---+--+-+--+---+-+-++-+---+++-+++++-+++---++--++----++++-+-++++----
+++++++++-+-+--++-++--+-+---++++--+---+--+-+--+---+-+-++-+---+++-+++++-+++---++--++----++++-+-++++---
+-++++++++-+-+--++-++--+-++--++++--+---+--+-+--+-----+-++-+---+++-+++++-+++---++--++----++++-+-++++--
++-++++++++-+-+--++-++--+--+--++++--+---+--+-+--+-----+-++-+---+++-+++++-+++---++--++----++++-+-++++-
+-+-++++++++-+-+--++-++--+--+--++++--+---+--+-+--+-+---+-++-+---+++-+++++-++----++--++----++++-+-++++
++-+-++++++++-+-+--++-++-----+--++++--+---+--+-+--+++---+-++-+---+++-+++++-++----++--++----++++-+-+++
+-+-+-++++++++-+-+--++-++-+---+--++++--+---+--+-+--+++---+-++-+---+++-+++++-++----++--++----++++-+-++
+--+-+-++++++++-+-+--++-++-+---+--++++--+---+--+-+--+++---+-++-+---+++-++++++++----++--++----++++-+-+
++--+-+-++++++++-+-+--++-+--+---+--++++--+---+--+-++-+++---+-++-+---+++-++++++++----++--++----++++-+-
+++--+-+-++++++++-+-+--++-+--+---+--++++--+---+--+-++-+++---+-++-+---+++-+++-++++----++--++----++++-+
\ No newline at end of file
diff --git a/gptqmodel/exllamav3/util/hadamard_data/hadamard_116.txt b/gptqmodel/exllamav3/util/hadamard_data/hadamard_116.txt
new file mode 100644
index 000000000..5cea1241e
--- /dev/null
+++ b/gptqmodel/exllamav3/util/hadamard_data/hadamard_116.txt
@@ -0,0 +1,116 @@
+++--+--+-+++-++++-+++-+--+--+++++-++-+---++++++---+-++-++++-+---++--+-++++++-+--++---+-+++---++--+-+----+-+--++---++
++++--+--+-+++-++++-+++-+--+--+++++-++-+---++++++---+-++-++-+-+---++--+-++++++-+--++---+++++---++--+-+----+-+--++---+
+-+++--+--+-+++-++++-+++-+--+-++++++-++-+---++++++---+-++-++-+-+---++--+-++++++-+--++---+++++---++--+-+----+-+--++---
+--+++--+--+-+++-++++-+++-+--++++++++-++-+---++++++---+-++--+-+-+---++--+-++++++-+--++---+++++---++--+-+----+-+--++--
++--+++--+--+-+++-++++-+++-+---+++++++-++-+---++++++---+-++--+-+-+---++--+-++++++-+--++---+++++---++--+-+----+-+--++-
+-+--+++--+--+-+++-++++-+++-+-+-+++++++-++-+---++++++---+-+---+-+-+---++--+-++++++-+--++---+++++---++--+-+----+-+--++
+--+--+++--+--+-+++-++++-+++-+++-+++++++-++-+---++++++---+-+---+-+-+---++--+-++++++-+--++---+++++---++--+-+----+-+--+
++--+--+++--+--+-+++-++++-+++--++-+++++++-++-+---++++++---+++---+-+-+---++--+-++++++-+--++---+++++---++--+-+----+-+--
+-+--+--+++--+--+-+++-++++-++++-++-+++++++-++-+---++++++----++---+-+-+---++--+-++++++-+--++---+++++---++--+-+----+-+-
++-+--+--+++--+--+-+++-++++-++-+-++-+++++++-++-+---++++++----++---+-+-+---++--+-++++++-+--++---+++++---++--+-+----+-+
+++-+--+--+++--+--+-+++-++++-+--+-++-+++++++-++-+---++++++-+--++---+-+-+---++--+-++++++-+--++---+++++---++--+-+----+-
++++-+--+--+++--+--+-+++-++++----+-++-+++++++-++-+---++++++-+--++---+-+-+---++--+-++++++-+--++---+++++---++--+-+----+
+-+++-+--+--+++--+--+-+++-+++++---+-++-+++++++-++-+---++++++-+--++---+-+-+---++--+-++++++-+--++---+++++---++--+-+----
++-+++-+--+--+++--+--+-+++-+++++---+-++-+++++++-++-+---++++++-+--++---+-+-+---++--+-++++-+-+--++---+++++---++--+-+---
+++-+++-+--+--+++--+--+-+++-+++++---+-++-+++++++-++-+---++++++-+--++---+-+-+---++--+-+++--+-+--++---+++++---++--+-+--
++++-+++-+--+--+++--+--+-+++-+++++---+-++-+++++++-++-+---++++++-+--++---+-+-+---++--+-++---+-+--++---+++++---++--+-+-
+++++-+++-+--+--+++--+--+-+++-+++++---+-++-+++++++-++-+---++++++-+--++---+-+-+---++--+-+----+-+--++---+++++---++--+-+
+-++++-+++-+--+--+++--+--+-+++++++++---+-++-+++++++-++-+---++++++-+--++---+-+-+---++--+-+----+-+--++---+++++---++--+-
++-++++-+++-+--+--+++--+--+-++-++++++---+-++-+++++++-++-+---++++++-+--++---+-+-+---++--+-+----+-+--++---+++++---++--+
+++-++++-+++-+--+--+++--+--+-+--++++++---+-++-+++++++-++-+-+-++++++-+--++---+-+-+---++--+-+----+-+--++---+++++---++--
++++-++++-+++-+--+--+++--+--+----++++++---+-++-+++++++-++-+-+-++++++-+--++---+-+-+---++--+-+----+-+--++---+++++---++-
+-+++-++++-+++-+--+--+++--+--++---++++++---+-++-+++++++-++---+-++++++-+--++---+-+-+---++--+-+----+-+--++---+++++---++
++-+++-++++-+++-+--+--+++--+---+---++++++---+-++-+++++++-+++--+-++++++-+--++---+-+-+---++--+-+----+-+--++---+++++---+
+-+-+++-++++-+++-+--+--+++--+-+-+---++++++---+-++-+++++++-+++--+-++++++-+--++---+-+-+---++--+-+----+-+--++---+++++---
+--+-+++-++++-+++-+--+--+++--+++-+---++++++---+-++-+++++++--++--+-++++++-+--++---+-+-+---++--+-+----+-+--++---+++++--
++--+-+++-++++-+++-+--+--+++---++-+---++++++---+-++-+++++++--++--+-++++++-+--++---+-+-+---++--+-+----+-+--++---+++++-
+-+--+-+++-++++-+++-+--+--+++-+-++-+---++++++---+-++-++++++---++--+-++++++-+--++---+-+-+---++--+-+----+-+--++---+++++
+--+--+-+++-++++-+++-+--+--+++++-++-+---++++++---+-++-++++++---++--+-++++++-+--++---+-+-+---++--+-+----+-+--++---++++
++--+--+-+++-++++-+++-+--+--+++++-++-+---++++++---+-++-++++-+---++--+-++++++-+--++---+-+++---++--+-+----+-+--++---+++
+----+--+-+++------+++-+--+---++--+--+-+++-++++-+++-+--+--+---+++--++-+-++++-+-++--+++--+-+---++--+-++++++-+--++---+-
+-----+--+-+++------+++-+--+--+++--+--+-+++-++++-+++-+--+------+++--++-+-++++-+-++--+++--+-+---++--+-++++++-+--++---+
+------+--+-+++------+++-+--+--+++--+--+-+++-++++-+++-+--+------+++--++-+-++++-+-++--++++-+-+---++--+-++++++-+--++---
+-------+--+-+++------+++-+--+--+++--+--+-+++-++++-+++-+--++-----+++--++-+-++++-+-++--++-+-+-+---++--+-++++++-+--++--
++-------+--+-+++------+++-+--+--+++--+--+-+++-++++-+++-+--++-----+++--++-+-++++-+-++--+--+-+-+---++--+-++++++-+--++-
+-+-------+--+-+++------+++-+--+--+++--+--+-+++-++++-+++-+-+++-----+++--++-+-++++-+-++-----+-+-+---++--+-++++++-+--++
+--+-------+--+-+++------+++-+--+--+++--+--+-+++-++++-+++-+-+++-----+++--++-+-++++-+-++-+---+-+-+---++--+-++++++-+--+
++--+-------+--+-+++------+++-+--+--+++--+--+-+++-++++-+++---+++-----+++--++-+-++++-+-++++---+-+-+---++--+-++++++-+--
+-+--+-------+--+-+++------+++-+--+--+++--+--+-+++-++++-++++--+++-----+++--++-+-++++-+-+-++---+-+-+---++--+-++++++-+-
++-+--+-------+--+-+++------+++-+--+--+++--+--+-+++-++++-++++--+++-----+++--++-+-++++-+---++---+-+-+---++--+-++++++-+
+++-+--+-------+--+-+++------+++-+--+--+++--+--+-+++-++++-+-++--+++-----+++--++-+-++++-++--++---+-+-+---++--+-++++++-
++++-+--+-------+--+-+++------+++-+--+--+++--+--+-+++-++++-+-++--+++-----+++--++-+-++++--+--++---+-+-+---++--+-++++++
+-+++-+--+-------+--+-+++------+++-+--+--+++--+--+-+++-++++-+-++--+++-----+++--++-+-+++++-+--++---+-+-+---++--+-+++++
+--+++-+--+-------+--+-+++----+-+++-+--+--+++--+--+-+++-++++-+-++--+++-----+++--++-+-+++++-+--++---+-+-+---++--+-++++
+---+++-+--+-------+--+-+++---++-+++-+--+--+++--+--+-+++-++++-+-++--+++-----+++--++-+-+++++-+--++---+-+-+---++--+-+++
+----+++-+--+-------+--+-+++--+++-+++-+--+--+++--+--+-+++-++++-+-++--+++-----+++--++-+-+++++-+--++---+-+-+---++--+-++
+-----+++-+--+-------+--+-+++-++++-+++-+--+--+++--+--+-+++-++++-+-++--+++-----+++--++-+-+++++-+--++---+-+-+---++--+-+
+------+++-+--+-------+--+-+++-++++-+++-+--+--+++--+--+-+++-++++-+-++--+++-----+++--++-+++++++-+--++---+-+-+---++--+-
++------+++-+--+-------+--+-+++-++++-+++-+--+--+++--+--+-+++-++++-+-++--+++-----+++--++--++++++-+--++---+-+-+---++--+
+++------+++-+--+-------+--+-+++-++++-+++-+--+--+++--+--+-+-+-++++-+-++--+++-----+++--+++-++++++-+--++---+-+-+---++--
++++------+++-+--+-------+--+-+++-++++-+++-+--+--+++--+--+-+-+-++++-+-++--+++-----+++--+-+-++++++-+--++---+-+-+---++-
+-+++------+++-+--+-------+--+-+++-++++-+++-+--+--+++--+--+++-+-++++-+-++--+++-----+++----+-++++++-+--++---+-+-+---++
++-+++------+++-+--+-------+--+-+++-++++-+++-+--+--+++--+---++-+-++++-+-++--+++-----+++-+--+-++++++-+--++---+-+-+---+
+-+-+++------+++-+--+-------+--+-+++-++++-+++-+--+--+++--+---++-+-++++-+-++--+++-----+++++--+-++++++-+--++---+-+-+---
+--+-+++------+++-+--+-------+--+-+++-++++-+++-+--+--+++--++--++-+-++++-+-++--+++-----++-++--+-++++++-+--++---+-+-+--
++--+-+++------+++-+--+-------+--+-+++-++++-+++-+--+--+++--++--++-+-++++-+-++--+++-----+--++--+-++++++-+--++---+-+-+-
+-+--+-+++------+++-+--+-------+--+-+++-++++-+++-+--+--+++-+++--++-+-++++-+-++--+++--------++--+-++++++-+--++---+-+-+
+--+--+-+++------+++-+--+-------+--+-+++-++++-+++-+--+--+++-+++--++-+-++++-+-++--+++----+---++--+-++++++-+--++---+-+-
+---+--+-+++------+++-+--+----+--+--+-+++-++++-+++-+--+--++--+++--++-+-++++-+-++--+++----+---++--+-++++++-+--++---+-+
+-+-+++--++-+------+-++--+++-++++---++--+-+----+-+--++---++++--+--+-+++-++++-+++-+--+--+----+--+-+++------+++-+--+---
++-+-+++--++-+------+-++--+++-++++---++--+-+----+-+--++---++++--+--+-+++-++++-+++-+--+-------+--+-+++------+++-+--+--
+-+-+-+++--++-+------+-++--++++++++---++--+-+----+-+--++----+++--+--+-+++-++++-+++-+--+-------+--+-+++------+++-+--+-
++-+-+-+++--++-+------+-++--++-+++++---++--+-+----+-+--++----+++--+--+-+++-++++-+++-+--+-------+--+-+++------+++-+--+
+++-+-+-+++--++-+------+-++--+--+++++---++--+-+----+-+--++-+--+++--+--+-+++-++++-+++-+--+-------+--+-+++------+++-+--
++++-+-+-+++--++-+------+-++-----+++++---++--+-+----+-+--++-+--+++--+--+-+++-++++-+++-+--+-------+--+-+++------+++-+-
+-+++-+-+-+++--++-+------+-++-+---+++++---++--+-+----+-+--+--+--+++--+--+-+++-++++-+++-+--+-------+--+-+++------+++-+
+--+++-+-+-+++--++-+------+-++++---+++++---++--+-+----+-+--+--+--+++--+--+-+++-++++-+++-+--+-------+--+-+++------+++-
++--+++-+-+-+++--++-+------+-+-++---+++++---++--+-+----+-+--+--+--+++--+--+-+++-++++-+++-+--+-------+--+-+++------+++
+++--+++-+-+-+++--++-+------+---++---+++++---++--+-+----+-++-+--+--+++--+--+-+++-++++-+++-+--+-------+--+-+++------++
+-++--+++-+-+-+++--++-+------++--++---+++++---++--+-+----+-++-+--+--+++--+--+-+++-++++-+++-+--+-------+--+-+++------+
++-++--+++-+-+-+++--++-+-------+--++---+++++---++--+-+----++++-+--+--+++--+--+-+++-++++-+++-+--+-------+--+-+++------
+-+-++--+++-+-+-+++--++-+-----+-+--++---+++++---++--+-+-----+++-+--+--+++--+--+-+++-++++-+++-+--+-------+--+-+++-----
+--+-++--+++-+-+-+++--++-+-----+-+--++---+++++---++--+-+---+-+++-+--+--+++--+--+-+++-+++--+++-+--+-------+--+-+++----
+---+-++--+++-+-+-+++--++-+-----+-+--++---+++++---++--+-+--++-+++-+--+--+++--+--+-+++-++---+++-+--+-------+--+-+++---
+----+-++--+++-+-+-+++--++-+-----+-+--++---+++++---++--+-+-+++-+++-+--+--+++--+--+-+++-+----+++-+--+-------+--+-+++--
+-----+-++--+++-+-+-+++--++-+-----+-+--++---+++++---++--+-+++++-+++-+--+--+++--+--+-+++------+++-+--+-------+--+-+++-
+------+-++--+++-+-+-+++--++-++----+-+--++---+++++---++--+--++++-+++-+--+--+++--+--+-+++------+++-+--+-------+--+-+++
++------+-++--+++-+-+-+++--++--+----+-+--++---+++++---++--++-++++-+++-+--+--+++--+--+-+++------+++-+--+-------+--+-++
+-+------+-++--+++-+-+-+++--+++-+----+-+--++---+++++---++--++-++++-+++-+--+--+++--+--+-+++------+++-+--+-------+--+-+
++-+------+-++--+++-+-+-+++--+-+-+----+-+--++---+++++---++-+++-++++-+++-+--+--+++--+--+-+++------+++-+--+-------+--+-
+++-+------+-++--+++-+-+-+++----+-+----+-+--++---+++++---++-+++-++++-+++-+--+--+++--+--+-+++------+++-+--+-------+--+
+-++-+------+-++--+++-+-+-+++-+--+-+----+-+--++---+++++---++-+++-++++-+++-+--+--+++--+--+-+++------+++-+--+-------+--
+--++-+------+-++--+++-+-+-+++++--+-+----+-+--++---+++++----+-+++-++++-+++-+--+--+++--+--+-+++------+++-+--+-------+-
++--++-+------+-++--+++-+-+-++-++--+-+----+-+--++---+++++----+-+++-++++-+++-+--+--+++--+--+-+++------+++-+--+-------+
+++--++-+------+-++--+++-+-+-+--++--+-+----+-+--++---+++++-+--+-+++-++++-+++-+--+--+++--+--+-+++------+++-+--+-------
++++--++-+------+-++--+++-+-+----++--+-+----+-+--++---+++++-+--+-+++-++++-+++-+--+--+++--+--+-+++------+++-+--+------
+-+++--++-+------+-++--+++-+-++---++--+-+----+-+--++---++++--+--+-+++-++++-+++-+--+--+++--+--+-+++------+++-+--+-----
++-+++--++-+------+-++--+++-+-++---++--+-+----+-+--++---++++--+--+-+++-++++-+++-+--+--++---+--+-+++------+++-+--+----
+---+++--++-+-++++-+-++--+++---+-+++--++-+------+-++--+++-+++++-++-+---++++++---+-++-+++++--+--+-+++-++++-+++-+--+--+
+----+++--++-+-++++-+-++--+++-+-+-+++--++-+------+-++--+++-+++++-++-+---++++++---+-++-+++++--+--+-+++-++++-+++-+--+--
+-----+++--++-+-++++-+-++--+++-+-+-+++--++-+------+-++--+++++++++-++-+---++++++---+-++-+-+++--+--+-+++-++++-+++-+--+-
++-----+++--++-+-++++-+-++--+++-+-+-+++--++-+------+-++--+++++++++-++-+---++++++---+-++---+++--+--+-+++-++++-+++-+--+
+++-----+++--++-+-++++-+-++--+++-+-+-+++--++-+------+-++--+-+++++++-++-+---++++++---+-+++--+++--+--+-+++-++++-+++-+--
++++-----+++--++-+-++++-+-++--+++-+-+-+++--++-+------+-++--+-+++++++-++-+---++++++---+-+-+--+++--+--+-+++-++++-+++-+-
+-+++-----+++--++-+-++++-+-++--+++-+-+-+++--++-+------+-++-++-+++++++-++-+---++++++---+---+--+++--+--+-+++-++++-+++-+
+--+++-----+++--++-+-++++-+-++--+++-+-+-+++--++-+------+-++-++-+++++++-++-+---++++++---++--+--+++--+--+-+++-++++-+++-
++--+++-----+++--++-+-++++-+-++--+++-+-+-+++--++-+------+-++-++-+++++++-++-+---++++++----+--+--+++--+--+-+++-++++-+++
+++--+++-----+++--++-+-++++-+-++--+++-+-+-+++--++-+------+--+-++-+++++++-++-+---++++++--+-+--+--+++--+--+-+++-++++-++
+-++--+++-----+++--++-+-++++-+-++--+++-+-+-+++--++-+------+--+-++-+++++++-++-+---++++++-++-+--+--+++--+--+-+++-++++-+
++-++--+++-----+++--++-+-++++-+-++--+++-+-+-+++--++-+---------+-++-+++++++-++-+---+++++++++-+--+--+++--+--+-+++-++++-
+-+-++--+++-----+++--++-+-++++-+-++--+++-+-+-+++--++-+-----+---+-++-+++++++-++-+---+++++-+++-+--+--+++--+--+-+++-++++
++-+-++--+++-----+++--++-+-+++--+-++--+++-+-+-+++--++-+----++---+-++-+++++++-++-+---+++++-+++-+--+--+++--+--+-+++-+++
+++-+-++--+++-----+++--++-+-++---+-++--+++-+-+-+++--++-+---+++---+-++-+++++++-++-+---+++++-+++-+--+--+++--+--+-+++-++
++++-+-++--+++-----+++--++-+-+----+-++--+++-+-+-+++--++-+--++++---+-++-+++++++-++-+---+++++-+++-+--+--+++--+--+-+++-+
+++++-+-++--+++-----+++--++-+------+-++--+++-+-+-+++--++-+-+++++---+-++-+++++++-++-+---+++++-+++-+--+--+++--+--+-+++-
+-++++-+-++--+++-----+++--++-+------+-++--+++-+-+-+++--++-+++++++---+-++-+++++++-++-+----++++-+++-+--+--+++--+--+-+++
++-++++-+-++--+++-----+++--++-+------+-++--+++-+-+-+++--++--++++++---+-++-+++++++-++-+--+-++++-+++-+--+--+++--+--+-++
+-+-++++-+-++--+++-----+++--++-+------+-++--+++-+-+-+++--++--++++++---+-++-+++++++-++-+-++-++++-+++-+--+--+++--+--+-+
++-+-++++-+-++--+++-----+++--++-+------+-++--+++-+-+-+++--+---++++++---+-++-+++++++-++-++++-++++-+++-+--+--+++--+--+-
+++-+-++++-+-++--+++-----+++--++-+------+-++--+++-+-+-+++--+---++++++---+-++-+++++++-++--+++-++++-+++-+--+--+++--+--+
+-++-+-++++-+-++--+++-----+++--++-+------+-++--+++-+-+-+++--+---++++++---+-++-+++++++-+++-+++-++++-+++-+--+--+++--+--
+--++-+-++++-+-++--+++-----+++--++-+------+-++--+++-+-+-++++-+---++++++---+-++-+++++++-+-+-+++-++++-+++-+--+--+++--+-
++--++-+-++++-+-++--+++-----+++--++-+------+-++--+++-+-+-++++-+---++++++---+-++-+++++++---+-+++-++++-+++-+--+--+++--+
+++--++-+-++++-+-++--+++-----+++--++-+------+-++--+++-+-+-+-++-+---++++++---+-++-++++++++--+-+++-++++-+++-+--+--+++--
++++--++-+-++++-+-++--+++-----+++--++-+------+-++--+++-+-+-+-++-+---++++++---+-++-++++++-+--+-+++-++++-+++-+--+--+++-
+-+++--++-+-++++-+-++--+++-----+++--++-+------+-++--+++-+-+++-++-+---++++++---+-++-+++++--+--+-+++-++++-+++-+--+--+++
+--+++--++-+-++++-+-++--+++---+-+++--++-+------+-++--+++-+-+++-++-+---++++++---+-++-+++++--+--+-+++-++++-+++-+--+--++
\ No newline at end of file
diff --git a/gptqmodel/exllamav3/util/hadamard_data/hadamard_156.txt b/gptqmodel/exllamav3/util/hadamard_data/hadamard_156.txt
new file mode 100644
index 000000000..b52b200b8
--- /dev/null
+++ b/gptqmodel/exllamav3/util/hadamard_data/hadamard_156.txt
@@ -0,0 +1,156 @@
++++--+-+-----+--++----++--+-----+-+--++++++---+--++----+-+--+-+----++--+---++++++--++-+---+-+--+----+--+-+---+-++--+++---++-+-+-----+++-++-+++-----+-+-++---
+++++--+-+-----+--++----++--+-----+-+--++++++---+--++----+-+--+-+----++--+---++++++--++-+---+-+--+----+--+-+---+-++--+-+---++-+-+-----+++-++-+++-----+-+-++--
++++++--+-+-----+--++----++--+-----+-+--++++++---+--++----+-+--+-+----++--+---++++++--++-+---+-+--+----+--+-+---+-++----+---++-+-+-----+++-++-+++-----+-+-++-
+-+++++--+-+-----+--++----++--+-----+-+-+++++++---+--++----+-+--+-+----++--+----+++++--++-+---+-+--+----+--+-+---+-++----+---++-+-+-----+++-++-+++-----+-+-++
+--+++++--+-+-----+--++----++--+-----+-+-+++++++---+--++----+-+--+-+----++--+----+++++--++-+---+-+--+----+--+-+---+-+++---+---++-+-+-----+++-++-+++-----+-+-+
++--+++++--+-+-----+--++----++--+-----+---+++++++---+--++----+-+--+-+----++--+-+--+++++--++-+---+-+--+----+--+-+---+-+++---+---++-+-+-----+++-++-+++-----+-+-
+-+--+++++--+-+-----+--++----++--+-----+---+++++++---+--++----+-+--+-+----++--+++--+++++--++-+---+-+--+----+--+-+---+--++---+---++-+-+-----+++-++-+++-----+-+
++-+--+++++--+-+-----+--++----++--+-----+---+++++++---+--++----+-+--+-+----++---++--+++++--++-+---+-+--+----+--+-+---++-++---+---++-+-+-----+++-++-+++-----+-
+-+-+--+++++--+-+-----+--++----++--+-----+---+++++++---+--++----+-+--+-+----++-+-++--+++++--++-+---+-+--+----+--+-+----+-++---+---++-+-+-----+++-++-+++-----+
+--+-+--+++++--+-+-----+--++----++--+-----+---+++++++---+--++----+-+--+-+----++-+-++--+++++--++-+---+-+--+----+--+-+--+-+-++---+---++-+-+-----+++-++-+++-----
+---+-+--+++++--+-+-----+--++----++--+--+--+---+++++++---+--++----+-+--+-+----+--+-++--+++++--++-+---+-+--+----+--+-+--+-+-++---+---++-+-+-----+++-++-+++----
+----+-+--+++++--+-+-----+--++----++--+-++--+---+++++++---+--++----+-+--+-+-------+-++--+++++--++-+---+-+--+----+--+-+--+-+-++---+---++-+-+-----+++-++-+++---
+-----+-+--+++++--+-+-----+--++----++--+-++--+---+++++++---+--++----+-+--+-+---+---+-++--+++++--++-+---+-+--+----+--+----+-+-++---+---++-+-+-----+++-++-+++--
++-----+-+--+++++--+-+-----+--++----++----++--+---+++++++---+--++----+-+--+-+---+---+-++--+++++--++-+---+-+--+----+--+----+-+-++---+---++-+-+-----+++-++-+++-
+-+-----+-+--+++++--+-+-----+--++----++----++--+---+++++++---+--++----+-+--+-+-+-+---+-++--+++++--++-+---+-+--+----+-------+-+-++---+---++-+-+-----+++-++-+++
+--+-----+-+--+++++--+-+-----+--++----++----++--+---+++++++---+--++----+-+--+-+-+-+---+-++--+++++--++-+---+-+--+----+-+-----+-+-++---+---++-+-+-----+++-++-++
++--+-----+-+--+++++--+-+-----+--++----++----++--+---+++++++---+--++----+-+--+---+-+---+-++--+++++--++-+---+-+--+----+++-----+-+-++---+---++-+-+-----+++-++-+
+++--+-----+-+--+++++--+-+-----+--++-----+----++--+---+++++++---+--++----+-+--++--+-+---+-++--+++++--++-+---+-+--+----+++-----+-+-++---+---++-+-+-----+++-++-
+-++--+-----+-+--+++++--+-+-----+--++---+-+----++--+---+++++++---+--++----+-+---+--+-+---+-++--+++++--++-+---+-+--+----+++-----+-+-++---+---++-+-+-----+++-++
+--++--+-----+-+--+++++--+-+-----+--++---+-+----++--+---+++++++---+--++----+-+---+--+-+---+-++--+++++--++-+---+-+--+--+-+++-----+-+-++---+---++-+-+-----+++-+
+---++--+-----+-+--+++++--+-+-----+--++---+-+----++--+---+++++++---+--++----+-+---+--+-+---+-++--+++++--++-+---+-+--+-++-+++-----+-+-++---+---++-+-+-----+++-
+----++--+-----+-+--+++++--+-+-----+--+++--+-+----++--+---+++++++---+--++----+-----+--+-+---+-++--+++++--++-+---+-+--+-++-+++-----+-+-++---+---++-+-+-----+++
++----++--+-----+-+--+++++--+-+-----+--+-+--+-+----++--+---+++++++---+--++----++----+--+-+---+-++--+++++--++-+---+-+--+-++-+++-----+-+-++---+---++-+-+-----++
+++----++--+-----+-+--+++++--+-+-----+--+-+--+-+----++--+---+++++++---+--++-----+----+--+-+---+-++--+++++--++-+---+-+-++-++-+++-----+-+-++---+---++-+-+-----+
+-++----++--+-----+-+--+++++--+-+-----+--+-+--+-+----++--+---+++++++---+--++-----+----+--+-+---+-++--+++++--++-+---+-++++-++-+++-----+-+-++---+---++-+-+-----
+--++----++--+-----+-+--+++++--+-+-----+--+-+--+-+----++--+---+++++++---+--++--+--+----+--+-+---+-++--+++++--++-+---+--+++-++-+++-----+-+-++---+---++-+-+----
++--++----++--+-----+-+--+++++--+-+--------+-+--+-+----++--+---+++++++---+--++--+--+----+--+-+---+-++--+++++--++-+---+--+++-++-+++-----+-+-++---+---++-+-+---
+-+--++----++--+-----+-+--+++++--+-+--------+-+--+-+----++--+---+++++++---+--+++-+--+----+--+-+---+-++--+++++--++-+------+++-++-+++-----+-+-++---+---++-+-+--
+--+--++----++--+-----+-+--+++++--+-+---+----+-+--+-+----++--+---+++++++---+--+-+-+--+----+--+-+---+-++--+++++--++-+------+++-++-+++-----+-+-++---+---++-+-+-
+---+--++----++--+-----+-+--+++++--+-+--++----+-+--+-+----++--+---+++++++---+----+-+--+----+--+-+---+-++--+++++--++-+------+++-++-+++-----+-+-++---+---++-+-+
+----+--++----++--+-----+-+--+++++--+-+--++----+-+--+-+----++--+---+++++++---+----+-+--+----+--+-+---+-++--+++++--++-++-----+++-++-+++-----+-+-++---+---++-+-
+-----+--++----++--+-----+-+--+++++--+-+--++----+-+--+-+----++--+---+++++++---++---+-+--+----+--+-+---+-++--+++++--++--+-----+++-++-+++-----+-+-++---+---++-+
++-----+--++----++--+-----+-+--+++++--+-+--++----+-+--+-+----++--+---+++++++----+---+-+--+----+--+-+---+-++--+++++--+++-+-----+++-++-+++-----+-+-++---+---++-
+-+-----+--++----++--+-----+-+--+++++--+-+--++----+-+--+-+----++--+---+++++++--+-+---+-+--+----+--+-+---+-++--+++++--+-+-+-----+++-++-+++-----+-+-++---+---++
++-+-----+--++----++--+-----+-+--+++++----+--++----+-+--+-+----++--+---+++++++-++-+---+-+--+----+--+-+---+-++--+++++--+-+-+-----+++-++-+++-----+-+-++---+---+
+-+-+-----+--++----++--+-----+-+--+++++----+--++----+-+--+-+----++--+---+++++++-++-+---+-+--+----+--+-+---+-++--+++++-++-+-+-----+++-++-+++-----+-+-++---+---
+--+-+-----+--++----++--+-----+-+--++++++---+--++----+-+--+-+----++--+---++++++--++-+---+-+--+----+--+-+---+-++--+++++-++-+-+-----+++-++-+++-----+-+-++---+--
++--+-+-----+--++----++--+-----+-+--++++++---+--++----+-+--+-+----++--+---++++++--++-+---+-+--+----+--+-+---+-++--++++--++-+-+-----+++-++-+++-----+-+-++---+-
+++--+-+-----+--++----++--+-----+-+--++++++---+--++----+-+--+-+----++--+---++++++--++-+---+-+--+----+--+-+---+-++--+++---++-+-+-----+++-++-+++-----+-+-++---+
+----+++-++--++++-+-++-+-++++--++-+++---+++--+-+-----+--++----++--+-----+-+--++-+++--+-+-+++++---+--+---+++++-+-+--++++++--++-+---+-+--+----+--+-+---+-++--++
+-----+++-++--++++-+-++-+-++++--++-+++--++++--+-+-----+--++----++--+-----+-+--++-+++--+-+-+++++---+--+---+++++-+-+--++++++--++-+---+-+--+----+--+-+---+-++--+
+------+++-++--++++-+-++-+-++++--++-+++-+++++--+-+-----+--++----++--+-----+-+--++-+++--+-+-+++++---+--+---+++++-+-+--++++++--++-+---+-+--+----+--+-+---+-++--
+-------+++-++--++++-+-++-+-++++--++-+++-+++++--+-+-----+--++----++--+-----+-+-+++-+++--+-+-+++++---+--+---+++++-+-+---+++++--++-+---+-+--+----+--+-+---+-++-
++-------+++-++--++++-+-++-+-++++--++-++--+++++--+-+-----+--++----++--+-----+-+-+++-+++--+-+-+++++---+--+---+++++-+-+---+++++--++-+---+-+--+----+--+-+---+-++
+++-------+++-++--++++-+-++-+-++++--++-++--+++++--+-+-----+--++----++--+-----+---+++-+++--+-+-+++++---+--+---+++++-+-++--+++++--++-+---+-+--+----+--+-+---+-+
++++-------+++-++--++++-+-++-+-++++--++--+--+++++--+-+-----+--++----++--+-----++--+++-+++--+-+-+++++---+--+---+++++-+-++--+++++--++-+---+-+--+----+--+-+---+-
+-+++-------+++-++--++++-+-++-+-++++--+++-+--+++++--+-+-----+--++----++--+------+--+++-+++--+-+-+++++---+--+---+++++-+-++--+++++--++-+---+-+--+----+--+-+---+
++-+++-------+++-++--++++-+-++-+-++++--+-+-+--+++++--+-+-----+--++----++--+----+-+--+++-+++--+-+-+++++---+--+---+++++-+-++--+++++--++-+---+-+--+----+--+-+---
+++-+++-------+++-++--++++-+-++-+-++++----+-+--+++++--+-+-----+--++----++--+----+-+--+++-+++--+-+-+++++---+--+---+++++-+-++--+++++--++-+---+-+--+----+--+-+--
+-++-+++-------+++-++--++++-+-++-+-++++----+-+--+++++--+-+-----+--++----++--+--+-+-+--+++-+++--+-+-+++++---+--+---++++--+-++--+++++--++-+---+-+--+----+--+-+-
+--++-+++-------+++-++--++++-+-++-+-++++----+-+--+++++--+-+-----+--++----++--+-++-+-+--+++-+++--+-+-+++++---+--+---+++---+-++--+++++--++-+---+-+--+----+--+-+
++--++-+++-------+++-++--++++-+-++-+-+++-----+-+--+++++--+-+-----+--++----++--++++-+-+--+++-+++--+-+-+++++---+--+---+++---+-++--+++++--++-+---+-+--+----+--+-
+++--++-+++-------+++-++--++++-+-++-+-+++-----+-+--+++++--+-+-----+--++----++--++++-+-+--+++-+++--+-+-+++++---+--+---+-+---+-++--+++++--++-+---+-+--+----+--+
++++--++-+++-------+++-++--++++-+-++-+-+-+-----+-+--+++++--+-+-----+--++----++-+++++-+-+--+++-+++--+-+-+++++---+--+---+-+---+-++--+++++--++-+---+-+--+----+--
+++++--++-+++-------+++-++--++++-+-++-+---+-----+-+--+++++--+-+-----+--++----++-+++++-+-+--+++-+++--+-+-+++++---+--+---+-+---+-++--+++++--++-+---+-+--+----+-
+-++++--++-+++-------+++-++--++++-+-++-++--+-----+-+--+++++--+-+-----+--++----+--+++++-+-+--+++-+++--+-+-+++++---+--+---+-+---+-++--+++++--++-+---+-+--+----+
++-++++--++-+++-------+++-++--++++-+-++-++--+-----+-+--+++++--+-+-----+--++-------+++++-+-+--+++-+++--+-+-+++++---+--++--+-+---+-++--+++++--++-+---+-+--+----
+-+-++++--++-+++-------+++-++--++++-+-++-++--+-----+-+--+++++--+-+-----+--++---+---+++++-+-+--+++-+++--+-+-+++++---+---+--+-+---+-++--+++++--++-+---+-+--+---
++-+-++++--++-+++-------+++-++--++++-+-+--++--+-----+-+--+++++--+-+-----+--++---+---+++++-+-+--+++-+++--+-+-+++++---+---+--+-+---+-++--+++++--++-+---+-+--+--
+++-+-++++--++-+++-------+++-++--++++-+----++--+-----+-+--+++++--+-+-----+--++---+---+++++-+-+--+++-+++--+-+-+++++---+---+--+-+---+-++--+++++--++-+---+-+--+-
+-++-+-++++--++-+++-------+++-++--++++-+----++--+-----+-+--+++++--+-+-----+--+++--+---+++++-+-+--+++-+++--+-+-+++++-------+--+-+---+-++--+++++--++-+---+-+--+
++-++-+-++++--++-+++-------+++-++--++++-+----++--+-----+-+--+++++--+-+-----+--+-+--+---+++++-+-+--+++-+++--+-+-+++++--+----+--+-+---+-++--+++++--++-+---+-+--
+-+-++-+-++++--++-+++-------+++-++--++++++----++--+-----+-+--+++++--+-+-----+----+--+---+++++-+-+--+++-+++--+-+-+++++--+----+--+-+---+-++--+++++--++-+---+-+-
++-+-++-+-++++--++-+++-------+++-++--+++-++----++--+-----+-+--+++++--+-+-----+----+--+---+++++-+-+--+++-+++--+-+-+++++--+----+--+-+---+-++--+++++--++-+---+-+
+++-+-++-+-++++--++-+++-------+++-++--++--++----++--+-----+-+--+++++--+-+-----++---+--+---+++++-+-+--+++-+++--+-+-+++++--+----+--+-+---+-++--+++++--++-+---+-
++++-+-++-+-++++--++-+++-------+++-++--++--++----++--+-----+-+--+++++--+-+-----++---+--+---+++++-+-+--+++-+++--+-+-+++-+--+----+--+-+---+-++--+++++--++-+---+
+++++-+-++-+-++++--++-+++-------+++-++---+--++----++--+-----+-+--+++++--+-+----+++---+--+---+++++-+-+--+++-+++--+-+-+++-+--+----+--+-+---+-++--+++++--++-+---
+-++++-+-++-+-++++--++-+++-------+++-++---+--++----++--+-----+-+--+++++--+-+---++++---+--+---+++++-+-+--+++-+++--+-+-+-+-+--+----+--+-+---+-++--+++++--++-+--
+--++++-+-++-+-++++--++-+++-------+++-++---+--++----++--+-----+-+--+++++--+-+--+++++---+--+---+++++-+-+--+++-+++--+-+---+-+--+----+--+-+---+-++--+++++--++-+-
++--++++-+-++-+-++++--++-+++-------+++-+----+--++----++--+-----+-+--+++++--+-+--+++++---+--+---+++++-+-+--+++-+++--+-+---+-+--+----+--+-+---+-++--+++++--++-+
+++--++++-+-++-+-++++--++-+++-------+++------+--++----++--+-----+-+--+++++--+-++-+++++---+--+---+++++-+-+--+++-+++--+-+---+-+--+----+--+-+---+-++--+++++--++-
+-++--++++-+-++-+-++++--++-+++-------++++-----+--++----++--+-----+-+--+++++--+--+-+++++---+--+---+++++-+-+--+++-+++--+-+---+-+--+----+--+-+---+-++--+++++--++
++-++--++++-+-++-+-++++--++-+++-------++-+-----+--++----++--+-----+-+--+++++--++-+-+++++---+--+---+++++-+-+--+++-+++--+-+---+-+--+----+--+-+---+-++--+++++--+
+++-++--++++-+-++-+-++++--++-+++-------++-+-----+--++----++--+-----+-+--+++++---+-+-+++++---+--+---+++++-+-+--+++-+++-++-+---+-+--+----+--+-+---+-++--+++++--
++++-++--++++-+-++-+-++++--++-+++--------+-+-----+--++----++--+-----+-+--+++++---+-+-+++++---+--+---+++++-+-+--+++-+++-++-+---+-+--+----+--+-+---+-++--+++++-
+-+++-++--++++-+-++-+-++++--++-+++--------+-+-----+--++----++--+-----+-+--++++++--+-+-+++++---+--+---+++++-+-+--+++-++--++-+---+-+--+----+--+-+---+-++--+++++
+--+++-++--++++-+-++-+-++++--++-+++-----+--+-+-----+--++----++--+-----+-+--++++++--+-+-+++++---+--+---+++++-+-+--+++-++--++-+---+-+--+----+--+-+---+-++--++++
+---+++-++--++++-+-++-+-++++--++-+++----++--+-+-----+--++----++--+-----+-+--++++++--+-+-+++++---+--+---+++++-+-+--+++-++--++-+---+-+--+----+--+-+---+-++--+++
+---++--+-+++-+-++-++++-++-+-+++-+--++--+---++-+-+-----+++-++-+++-----+-+-++---+++--+-+-----+--++----++--+-----+-+--++----+++-++--++++-+-++-+-++++--++-+++---
+----++--+-+++-+-++-++++-++-+-+++-+--++--+---++-+-+-----+++-++-+++-----+-+-++--++++--+-+-----+--++----++--+-----+-+--+-----+++-++--++++-+-++-+-++++--++-+++--
+-----++--+-+++-+-++-++++-++-+-+++-+--++--+---++-+-+-----+++-++-+++-----+-+-++-+++++--+-+-----+--++----++--+-----+-+--------+++-++--++++-+-++-+-++++--++-+++-
++-----++--+-+++-+-++-++++-++-+-+++-+--+---+---++-+-+-----+++-++-+++-----+-+-++-+++++--+-+-----+--++----++--+-----+-+--------+++-++--++++-+-++-+-++++--++-+++
+++-----++--+-+++-+-++-++++-++-+-+++-+--+---+---++-+-+-----+++-++-+++-----+-+-+--+++++--+-+-----+--++----++--+-----+-++-------+++-++--++++-+-++-+-++++--++-++
+-++-----++--+-+++-+-++-++++-++-+-+++-+-++---+---++-+-+-----+++-++-+++-----+-+-+--+++++--+-+-----+--++----++--+-----+-++-------+++-++--++++-+-++-+-++++--++-+
+--++-----++--+-+++-+-++-++++-++-+-+++-+-++---+---++-+-+-----+++-++-+++-----+-+-+--+++++--+-+-----+--++----++--+-----++++-------+++-++--++++-+-++-+-++++--++-
++--++-----++--+-+++-+-++-++++-++-+-+++-+-++---+---++-+-+-----+++-++-+++-----+-+-+--+++++--+-+-----+--++----++--+------+++-------+++-++--++++-+-++-+-++++--++
+-+--++-----++--+-+++-+-++-++++-++-+-+++-+-++---+---++-+-+-----+++-++-+++-----+-+-+--+++++--+-+-----+--++----++--+----+-+++-------+++-++--++++-+-++-+-++++--+
++-+--++-----++--+-+++-+-++-++++-++-+-+++-+-++---+---++-+-+-----+++-++-+++-------+-+--+++++--+-+-----+--++----++--+---++-+++-------+++-++--++++-+-++-+-++++--
+++-+--++-----++--+-+++-+-++-++++-++-+-+-+-+-++---+---++-+-+-----+++-++-+++-------+-+--+++++--+-+-----+--++----++--+---++-+++-------+++-++--++++-+-++-+-++++-
++++-+--++-----++--+-+++-+-++-++++-++-+---+-+-++---+---++-+-+-----+++-++-+++-------+-+--+++++--+-+-----+--++----++--+---++-+++-------+++-++--++++-+-++-+-++++
+-+++-+--++-----++--+-+++-+-++-++++-++-+---+-+-++---+---++-+-+-----+++-++-+++-------+-+--+++++--+-+-----+--++----++--++--++-+++-------+++-++--++++-+-++-+-+++
++-+++-+--++-----++--+-+++-+-++-++++-++-----+-+-++---+---++-+-+-----+++-++-+++-+-----+-+--+++++--+-+-----+--++----++--++--++-+++-------+++-++--++++-+-++-+-++
+-+-+++-+--++-----++--+-+++-+-++-++++-++-----+-+-++---+---++-+-+-----+++-++-+++-+-----+-+--+++++--+-+-----+--++----++-+++--++-+++-------+++-++--++++-+-++-+-+
++-+-+++-+--++-----++--+-+++-+-++-++++-++-----+-+-++---+---++-+-+-----+++-++-++--+-----+-+--+++++--+-+-----+--++----++++++--++-+++-------+++-++--++++-+-++-+-
+++-+-+++-+--++-----++--+-+++-+-++-++++-++-----+-+-++---+---++-+-+-----+++-++-++--+-----+-+--+++++--+-+-----+--++----+-++++--++-+++-------+++-++--++++-+-++-+
+-++-+-+++-+--++-----++--+-+++-+-++-+++++++-----+-+-++---+---++-+-+-----+++-++-++--+-----+-+--+++++--+-+-----+--++----+-++++--++-+++-------+++-++--++++-+-++-
++-++-+-+++-+--++-----++--+-+++-+-++-+++-+++-----+-+-++---+---++-+-+-----+++-++-++--+-----+-+--+++++--+-+-----+--++----+-++++--++-+++-------+++-++--++++-+-++
+++-++-+-+++-+--++-----++--+-+++-+-++-+++-+++-----+-+-++---+---++-+-+-----+++-+--++--+-----+-+--+++++--+-+-----+--++--+-+-++++--++-+++-------+++-++--++++-+-+
++++-++-+-+++-+--++-----++--+-+++-+-++-+++-+++-----+-+-++---+---++-+-+-----+++----++--+-----+-+--+++++--+-+-----+--++-++-+-++++--++-+++-------+++-++--++++-+-
+++++-++-+-+++-+--++-----++--+-+++-+-++--++-+++-----+-+-++---+---++-+-+-----+++----++--+-----+-+--+++++--+-+-----+--++-++-+-++++--++-+++-------+++-++--++++-+
+-++++-++-+-+++-+--++-----++--+-+++-+-+++-++-+++-----+-+-++---+---++-+-+-----+++----++--+-----+-+--+++++--+-+-----+--++-++-+-++++--++-+++-------+++-++--++++-
++-++++-++-+-+++-+--++-----++--+-+++-+-+++-++-+++-----+-+-++---+---++-+-+-----+++----++--+-----+-+--+++++--+-+-----+---+-++-+-++++--++-+++-------+++-++--++++
+++-++++-++-+-+++-+--++-----++--+-+++-+-+++-++-+++-----+-+-++---+---++-+-+------++----++--+-----+-+--+++++--+-+-----+-+-+-++-+-++++--++-+++-------+++-++--+++
+-++-++++-++-+-+++-+--++-----++--+-+++-+-+++-++-+++-----+-+-++---+---++-+-+------++----++--+-----+-+--+++++--+-+-----+++-+-++-+-++++--++-+++-------+++-++--++
++-++-++++-++-+-+++-+--++-----++--+-+++---+++-++-+++-----+-+-++---+---++-+-+---+--++----++--+-----+-+--+++++--+-+-----+++-+-++-+-++++--++-+++-------+++-++--+
+-+-++-++++-++-+-+++-+--++-----++--+-+++---+++-++-+++-----+-+-++---+---++-+-+---+--++----++--+-----+-+--+++++--+-+----++++-+-++-+-++++--++-+++-------+++-++--
++-+-++-++++-++-+-+++-+--++-----++--+-++----+++-++-+++-----+-+-++---+---++-+-+---+--++----++--+-----+-+--+++++--+-+----++++-+-++-+-++++--++-+++-------+++-++-
+++-+-++-++++-++-+-+++-+--++-----++--+-+-----+++-++-+++-----+-+-++---+---++-+-+---+--++----++--+-----+-+--+++++--+-+----++++-+-++-+-++++--++-+++-------+++-++
++++-+-++-++++-++-+-+++-+--++-----++--+-+-----+++-++-+++-----+-+-++---+---++-+-----+--++----++--+-----+-+--+++++--+-+-+--++++-+-++-+-++++--++-+++-------+++-+
+-+++-+-++-++++-++-+-+++-+--++-----++--+-+-----+++-++-+++-----+-+-++---+---++-+-----+--++----++--+-----+-+--+++++--+-+++--++++-+-++-+-++++--++-+++-------+++-
++-+++-+-++-++++-++-+-+++-+--++-----++--+-+-----+++-++-+++-----+-+-++---+---++-+-----+--++----++--+-----+-+--+++++--+--++--++++-+-++-+-++++--++-+++-------+++
+-+-+++-+-++-++++-++-+-+++-+--++-----++--+-+-----+++-++-+++-----+-+-++---+---++-+-----+--++----++--+-----+-+--+++++--++-++--++++-+-++-+-++++--++-+++-------++
+--+-+++-+-++-++++-++-+-+++-+--++-----+++-+-+-----+++-++-+++-----+-+-++---+---++-+-----+--++----++--+-----+-+--+++++--++-++--++++-+-++-+-++++--++-+++-------+
++--+-+++-+-++-++++-++-+-+++-+--++-----+++-+-+-----+++-++-+++-----+-+-++---+----+-+-----+--++----++--+-----+-+--+++++-+++-++--++++-+-++-+-++++--++-+++-------
+++--+-+++-+-++-++++-++-+-+++-+--++------++-+-+-----+++-++-+++-----+-+-++---+----+-+-----+--++----++--+-----+-+--+++++-+++-++--++++-+-++-+-++++--++-+++------
+-++--+-+++-+-++-++++-++-+-+++-+--++------++-+-+-----+++-++-+++-----+-+-++---+-+--+-+-----+--++----++--+-----+-+--++++--+++-++--++++-+-++-+-++++--++-+++-----
+--++--+-+++-+-++-++++-++-+-+++-+--++------++-+-+-----+++-++-+++-----+-+-++---+++--+-+-----+--++----++--+-----+-+--+++---+++-++--++++-+-++-+-++++--++-+++----
+-+++--+-+-+++++---+--+---+++++-+-+--+++---++--+-+++-+-++-++++-++-+-+++-+--++--++++---+--++----+-+--+-+----++--+---++++++--+-+-----+--++----++--+-----+-+--++
++-+++--+-+-+++++---+--+---+++++-+-+--++----++--+-+++-+-++-++++-++-+-+++-+--++-+++++---+--++----+-+--+-+----++--+---++++++--+-+-----+--++----++--+-----+-+--+
+++-+++--+-+-+++++---+--+---+++++-+-+--+-----++--+-+++-+-++-++++-++-+-+++-+--++++++++---+--++----+-+--+-+----++--+---++++++--+-+-----+--++----++--+-----+-+--
++++-+++--+-+-+++++---+--+---+++++-+-+--+-----++--+-+++-+-++-++++-++-+-+++-+--++++++++---+--++----+-+--+-+----++--+----+++++--+-+-----+--++----++--+-----+-+-
+-+++-+++--+-+-+++++---+--+---+++++-+-+-++-----++--+-+++-+-++-++++-++-+-+++-+---+++++++---+--++----+-+--+-+----++--+----+++++--+-+-----+--++----++--+-----+-+
+--+++-+++--+-+-+++++---+--+---+++++-+-+-++-----++--+-+++-+-++-++++-++-+-+++-+---+++++++---+--++----+-+--+-+----++--+-+--+++++--+-+-----+--++----++--+-----+-
++--+++-+++--+-+-+++++---+--+---+++++-+---++-----++--+-+++-+-++-++++-++-+-+++-+---+++++++---+--++----+-+--+-+----++--+-+--+++++--+-+-----+--++----++--+-----+
+-+--+++-+++--+-+-+++++---+--+---+++++-++--++-----++--+-+++-+-++-++++-++-+-+++-+---+++++++---+--++----+-+--+-+----++--+-+--+++++--+-+-----+--++----++--+-----
++-+--+++-+++--+-+-+++++---+--+---+++++--+--++-----++--+-+++-+-++-++++-++-+-+++-+---+++++++---+--++----+-+--+-+----++--+-+--+++++--+-+-----+--++----++--+----
+-+-+--+++-+++--+-+-+++++---+--+---++++++-+--++-----++--+-+++-+-++-++++-++-+-++--+---+++++++---+--++----+-+--+-+----++--+-+--+++++--+-+-----+--++----++--+---
++-+-+--+++-+++--+-+-+++++---+--+---++++++-+--++-----++--+-+++-+-++-++++-++-+-++--+---+++++++---+--++----+-+--+-+----+---+-+--+++++--+-+-----+--++----++--+--
+++-+-+--+++-+++--+-+-+++++---+--+---++++++-+--++-----++--+-+++-+-++-++++-++-+-++--+---+++++++---+--++----+-+--+-+--------+-+--+++++--+-+-----+--++----++--+-
++++-+-+--+++-+++--+-+-+++++---+--+---++-+++-+--++-----++--+-+++-+-++-++++-++-+-++--+---+++++++---+--++----+-+--+-+--------+-+--+++++--+-+-----+--++----++--+
+++++-+-+--+++-+++--+-+-+++++---+--+---++-+++-+--++-----++--+-+++-+-++-++++-++---++--+---+++++++---+--++----+-+--+-+--+-----+-+--+++++--+-+-----+--++----++--
++++++-+-+--+++-+++--+-+-+++++---+--+----+-+++-+--++-----++--+-+++-+-++-++++-++---++--+---+++++++---+--++----+-+--+-+--+-----+-+--+++++--+-+-----+--++----++-
+-+++++-+-+--+++-+++--+-+-+++++---+--+--+-+-+++-+--++-----++--+-+++-+-++-++++-+----++--+---+++++++---+--++----+-+--+-+--+-----+-+--+++++--+-+-----+--++----++
+--+++++-+-+--+++-+++--+-+-+++++---+--+-++-+-+++-+--++-----++--+-+++-+-++-++++-+----++--+---+++++++---+--++----+-+--+-+--+-----+-+--+++++--+-+-----+--++----+
+---+++++-+-+--+++-+++--+-+-+++++---+--+-++-+-+++-+--++-----++--+-+++-+-++-++++-+----++--+---+++++++---+--++----+-+--+++--+-----+-+--+++++--+-+-----+--++----
++---+++++-+-+--+++-+++--+-+-+++++---+--+-++-+-+++-+--++-----++--+-+++-+-++-++++-+----++--+---+++++++---+--++----+-+---++--+-----+-+--+++++--+-+-----+--++---
+-+---+++++-+-+--+++-+++--+-+-+++++---+-++-++-+-+++-+--++-----++--+-+++-+-++-++-+-+----++--+---+++++++---+--++----+-+---++--+-----+-+--+++++--+-+-----+--++--
+--+---+++++-+-+--+++-+++--+-+-+++++---++++-++-+-+++-+--++-----++--+-+++-+-++-+--+-+----++--+---+++++++---+--++----+-+---++--+-----+-+--+++++--+-+-----+--++-
++--+---+++++-+-+--+++-+++--+-+-+++++---++++-++-+-+++-+--++-----++--+-+++-+-++-+--+-+----++--+---+++++++---+--++----+-----++--+-----+-+--+++++--+-+-----+--++
+-+--+---+++++-+-+--+++-+++--+-+-+++++---++++-++-+-+++-+--++-----++--+-+++-+-++-+--+-+----++--+---+++++++---+--++----++----++--+-----+-+--+++++--+-+-----+--+
+--+--+---+++++-+-+--+++-+++--+-+-+++++-+-++++-++-+-+++-+--++-----++--+-+++-+-++-+--+-+----++--+---+++++++---+--++----++----++--+-----+-+--+++++--+-+-----+--
+---+--+---+++++-+-+--+++-+++--+-+-+++++++-++++-++-+-+++-+--++-----++--+-+++-+--+-+--+-+----++--+---+++++++---+--++----++----++--+-----+-+--+++++--+-+-----+-
++---+--+---+++++-+-+--+++-+++--+-+-++++-++-++++-++-+-+++-+--++-----++--+-+++-+--+-+--+-+----++--+---+++++++---+--++----++----++--+-----+-+--+++++--+-+-----+
+++---+--+---+++++-+-+--+++-+++--+-+-++++-++-++++-++-+-+++-+--++-----++--+-+++----+-+--+-+----++--+---+++++++---+--++-+--++----++--+-----+-+--+++++--+-+-----
++++---+--+---+++++-+-+--+++-+++--+-+-++-+-++-++++-++-+-+++-+--++-----++--+-+++----+-+--+-+----++--+---+++++++---+--++-+--++----++--+-----+-+--+++++--+-+----
+++++---+--+---+++++-+-+--+++-+++--+-+-++-+-++-++++-++-+-+++-+--++-----++--+-+++----+-+--+-+----++--+---+++++++---+--+--+--++----++--+-----+-+--+++++--+-+---
++++++---+--+---+++++-+-+--+++-+++--+-+-++-+-++-++++-++-+-+++-+--++-----++--+-+++----+-+--+-+----++--+---+++++++---+-----+--++----++--+-----+-+--+++++--+-+--
+-+++++---+--+---+++++-+-+--+++-+++--+-++++-+-++-++++-++-+-+++-+--++-----++--+--++----+-+--+-+----++--+---+++++++---+-----+--++----++--+-----+-+--+++++--+-+-
++-+++++---+--+---+++++-+-+--+++-+++--+--+++-+-++-++++-++-+-+++-+--++-----++--+--++----+-+--+-+----++--+---+++++++---+-----+--++----++--+-----+-+--+++++--+-+
+-+-+++++---+--+---+++++-+-+--+++-+++--++-+++-+-++-++++-++-+-+++-+--++-----++--+--++----+-+--+-+----++--+---+++++++---+-----+--++----++--+-----+-+--+++++--+-
++-+-+++++---+--+---+++++-+-+--+++-+++---+-+++-+-++-++++-++-+-+++-+--++-----++--+--++----+-+--+-+----++--+---+++++++---+-----+--++----++--+-----+-+--+++++--+
+-+-+-+++++---+--+---+++++-+-+--+++-+++---+-+++-+-++-++++-++-+-+++-+--++-----++--+--++----+-+--+-+----++--+---+++++++-+-+-----+--++----++--+-----+-+--+++++--
+--+-+-+++++---+--+---+++++-+-+--+++-++++--+-+++-+-++-++++-++-+-+++-+--++-----+---+--++----+-+--+-+----++--+---+++++++-+-+-----+--++----++--+-----+-+--+++++-
++--+-+-+++++---+--+---+++++-+-+--+++-++++--+-+++-+-++-++++-++-+-+++-+--++-----+---+--++----+-+--+-+----++--+---++++++--+-+-----+--++----++--+-----+-+--+++++
+++--+-+-+++++---+--+---+++++-+-+--+++-+-++--+-+++-+-++-++++-++-+-+++-+--++----++---+--++----+-+--+-+----++--+---++++++--+-+-----+--++----++--+-----+-+--++++
++++--+-+-+++++---+--+---+++++-+-+--+++---++--+-+++-+-++-++++-++-+-+++-+--++---+++---+--++----+-+--+-+----++--+---++++++--+-+-----+--++----++--+-----+-+--+++
\ No newline at end of file
diff --git a/gptqmodel/exllamav3/util/hadamard_data/hadamard_172.txt b/gptqmodel/exllamav3/util/hadamard_data/hadamard_172.txt
new file mode 100644
index 000000000..bb2447efe
--- /dev/null
+++ b/gptqmodel/exllamav3/util/hadamard_data/hadamard_172.txt
@@ -0,0 +1,172 @@
++---++--++++-+-+++-++--++-+++-+-++++--++---++-++++++----+-+--++-++-++--+-+----++++++-++++-+-++--+-+-++++-+----+-++++-+-+--++-+-++++---++++-+--+--++--------++--+--+-++++---+
+-+---++--++++-+-+++-++--++-+++-+-++++--++--+++-++++++----+-+--++-++-++--+-+----++++++-++++-+-++--+-+-++++-+----+-++++-+-+--++-+-++++---++++-+--+--++--------++--+--+-++++---
+--+---++--++++-+-+++-++--++-+++-+-++++--++--+++-++++++----+-+--++-++-++--+-+----+++++++++++-+-++--+-+-++++-+----+-++++-+-+--++-+--+++---++++-+--+--++--------++--+--+-++++--
+---+---++--++++-+-+++-++--++-+++-+-++++--+++-+++-++++++----+-+--++-++-++--+-+----+++++-+++++-+-++--+-+-++++-+----+-++++-+-+--++-+--+++---++++-+--+--++--------++--+--+-++++-
++---+---++--++++-+-+++-++--++-+++-+-++++--+++-+++-++++++----+-+--++-++-++--+-+----+++++-+++++-+-++--+-+-++++-+----+-++++-+-+--++----+++---++++-+--+--++--------++--+--+-++++
+++---+---++--++++-+-+++-++--++-+++-+-++++--+++-+++-++++++----+-+--++-++-++--+-+----+++-+-+++++-+-++--+-+-++++-+----+-++++-+-+--+++---+++---++++-+--+--++--------++--+--+-+++
+-++---+---++--++++-+-+++-++--++-+++-+-++++-++++-+++-++++++----+-+--++-++-++--+-+----+++-+-+++++-+-++--+-+-++++-+----+-++++-+-+--+++---+++---++++-+--+--++--------++--+--+-++
+--++---+---++--++++-+-+++-++--++-+++-+-+++++++++-+++-++++++----+-+--++-++-++--+-+----+++-+-+++++-+-++--+-+-++++-+----+-++++-+-+--+++---+++---++++-+--+--++--------++--+--+-+
++--++---+---++--++++-+-+++-++--++-+++-+-+++++++++-+++-++++++----+-+--++-++-++--+-+-----++-+-+++++-+-++--+-+-++++-+----+-++++-+-+-++++---+++---++++-+--+--++--------++--+--+-
+++--++---+---++--++++-+-+++-++--++-+++-+-++-++++++-+++-++++++----+-+--++-++-++--+-+-----++-+-+++++-+-++--+-+-++++-+----+-++++-+-+-++++---+++---++++-+--+--++--------++--+--+
++++--++---+---++--++++-+-+++-++--++-+++-+-+--++++++-+++-++++++----+-+--++-++-++--+-+--+--++-+-+++++-+-++--+-+-++++-+----+-++++-+-+-++++---+++---++++-+--+--++--------++--+--
+++++--++---+---++--++++-+-+++-++--++-+++-+----++++++-+++-++++++----+-+--++-++-++--+-+--+--++-+-+++++-+-++--+-+-++++-+----+-++++-+-+-++++---+++---++++-+--+--++--------++--+-
+-++++--++---+---++--++++-+-+++-++--++-+++-+----++++++-+++-++++++----+-+--++-++-++--+-++-+--++-+-+++++-+-++--+-+-++++-+----+-++++---+-++++---+++---++++-+--+--++--------++--+
++-++++--++---+---++--++++-+-+++-++--++-+++-+----++++++-+++-++++++----+-+--++-++-++--+--+-+--++-+-+++++-+-++--+-+-++++-+----+-+++++--+-++++---+++---++++-+--+--++--------++--
+-+-++++--++---+---++--++++-+-+++-++--++-+++-+----++++++-+++-++++++----+-+--++-++-++--++-+-+--++-+-+++++-+-++--+-+-++++-+----+-+++-+--+-++++---+++---++++-+--+--++--------++-
++-+-++++--++---+---++--++++-+-+++-++--++-+++-+----++++++-+++-++++++----+-+--++-++-++--++-+-+--++-+-+++++-+-++--+-+-++++-+----+-++--+--+-++++---+++---++++-+--+--++--------++
+++-+-++++--++---+---++--++++-+-+++-++--++-+-+-+----++++++-+++-++++++----+-+--++-++-++-+++-+-+--++-+-+++++-+-++--+-+-++++-+----+-++--+--+-++++---+++---++++-+--+--++--------+
++++-+-++++--++---+---++--++++-+-+++-++--++---+-+----++++++-+++-++++++----+-+--++-++-++++++-+-+--++-+-+++++-+-++--+-+-++++-+----+-++--+--+-++++---+++---++++-+--+--++--------
+-+++-+-++++--++---+---++--++++-+-+++-++--+++--+-+----++++++-+++-++++++----+-+--++-++-+-++++-+-+--++-+-+++++-+-++--+-+-++++-+----+-++--+--+-++++---+++---++++-+--+--++-------
++-+++-+-++++--++---+---++--++++-+-+++-++--+++--+-+----++++++-+++-++++++----+-+--++-++-+-++++-+-+--++-+-+++++-+-++--+-+-++++-+------++--+--+-++++---+++---++++-+--+--++------
+++-+++-+-++++--++---+---++--++++-+-+++-++---++--+-+----++++++-+++-++++++----+-+--++-++-+-++++-+-+--++-+-+++++-+-++--+-+-++++-+------++--+--+-++++---+++---++++-+--+--++-----
+-++-+++-+-++++--++---+---++--++++-+-+++-++-+-++--+-+----++++++-+++-++++++----+-+--++-+--+-++++-+-+--++-+-+++++-+-++--+-+-++++-+------++--+--+-++++---+++---++++-+--+--++----
+--++-+++-+-++++--++---+---++--++++-+-+++-++++-++--+-+----++++++-+++-++++++----+-+--++----+-++++-+-+--++-+-+++++-+-++--+-+-++++-+------++--+--+-++++---+++---++++-+--+--++---
++--++-+++-+-++++--++---+---++--++++-+-+++-+-++-++--+-+----++++++-+++-++++++----+-+--++----+-++++-+-+--++-+-+++++-+-++--+-+-++++-+------++--+--+-++++---+++---++++-+--+--++--
+++--++-+++-+-++++--++---+---++--++++-+-+++-+-++-++--+-+----++++++-+++-++++++----+-+--++----+-++++-+-+--++-+-+++++-+-++--+-+-++++--------++--+--+-++++---+++---++++-+--+--++-
+-++--++-+++-+-++++--++---+---++--++++-+-+++++-++-++--+-+----++++++-+++-++++++----+-+---+----+-++++-+-+--++-+-+++++-+-++--+-+-++++--------++--+--+-++++---+++---++++-+--+--++
++-++--++-+++-+-++++--++---+---++--++++-+-++-++-++-++--+-+----++++++-+++-++++++----+-+-+-+----+-++++-+-+--++-+-+++++-+-++--+-+-++++--------++--+--+-++++---+++---++++-+--+--+
+++-++--++-+++-+-++++--++---+---++--++++-+-+--++-++-++--+-+----++++++-+++-++++++----+-+++-+----+-++++-+-+--++-+-+++++-+-++--+-+-++++--------++--+--+-++++---+++---++++-+--+--
++++-++--++-+++-+-++++--++---+---++--++++-+-+--++-++-++--+-+----++++++-+++-++++++----+-+++-+----+-++++-+-+--++-+-+++++-+-++--+-+-+-++--------++--+--+-++++---+++---++++-+--+-
+-+++-++--++-+++-+-++++--++---+---++--++++-+-+--++-++-++--+-+----++++++-+++-++++++----+++++-+----+-++++-+-+--++-+-+++++-+-++--+-+---++--------++--+--+-++++---+++---++++-+--+
++-+++-++--++-+++-+-++++--++---+---++--++++-+-+--++-++-++--+-+----++++++-+++-++++++-----++++-+----+-++++-+-+--++-+-+++++-+-++--+-++--++--------++--+--+-++++---+++---++++-+--
+-+-+++-++--++-+++-+-++++--++---+---++--++++-+-+--++-++-++--+-+----++++++-+++-++++++---+-++++-+----+-++++-+-+--++-+-+++++-+-++--+--+--++--------++--+--+-++++---+++---++++-+-
++-+-+++-++--++-+++-+-++++--++---+---++--+++--+-+--++-++-++--+-+----++++++-+++-++++++---+-++++-+----+-++++-+-+--++-+-+++++-+-++--+--+--++--------++--+--+-++++---+++---++++-+
+++-+-+++-++--++-+++-+-++++--++---+---++--++---+-+--++-++-++--+-+----++++++-+++-++++++-+-+-++++-+----+-++++-+-+--++-+-+++++-+-++--+--+--++--------++--+--+-++++---+++---++++-
++++-+-+++-++--++-+++-+-++++--++---+---++--+----+-+--++-++-++--+-+----++++++-+++-++++++-+-+-++++-+----+-++++-+-+--++-+-+++++-+-++--+--+--++--------++--+--+-++++---+++---++++
+++++-+-+++-++--++-+++-+-++++--++---+---++--+----+-+--++-++-++--+-+----++++++-+++-+++++--+-+-++++-+----+-++++-+-+--++-+-+++++-+-+++-+--+--++--------++--+--+-++++---+++---+++
+-++++-+-+++-++--++-+++-+-++++--++---+---++-++----+-+--++-++-++--+-+----++++++-+++-+++++--+-+-++++-+----+-++++-+-+--++-+-+++++-+-+++-+--+--++--------++--+--+-++++---+++---++
+--++++-+-+++-++--++-+++-+-++++--++---+---+++++----+-+--++-++-++--+-+----++++++-+++-+++++--+-+-++++-+----+-++++-+-+--++-+-+++++-+-+++-+--+--++--------++--+--+-++++---+++---+
++--++++-+-+++-++--++-+++-+-++++--++---+---+++++----+-+--++-++-++--+-+----++++++-+++-++-++--+-+-++++-+----+-++++-+-+--++-+-+++++-+++++-+--+--++--------++--+--+-++++---+++---
+++--++++-+-+++-++--++-+++-+-++++--++---+---+++++----+-+--++-++-++--+-+----++++++-+++-++-++--+-+-++++-+----+-++++-+-+--++-+-+++++--++++-+--+--++--------++--+--+-++++---+++--
+-++--++++-+-+++-++--++-+++-+-++++--++---+--++++++----+-+--++-++-++--+-+----++++++-+++--+-++--+-+-++++-+----+-++++-+-+--++-+-+++++--++++-+--+--++--------++--+--+-++++---+++-
+--++--++++-+-+++-++--++-+++-+-++++--++---+--++++++----+-+--++-++-++--+-+----++++++-++++-+-++--+-+-++++-+----+-++++-+-+--++-+-++++---++++-+--+--++--------++--+--+-++++---+++
+---++--++++-+-+++-++--++-+++-+-++++--++---++-++++++----+-+--++-++-++--+-+----++++++-++++-+-++--+-+-++++-+----+-++++-+-+--++-+-++++---++++-+--+--++--------++--+--+-++++---++
+--+------++++-+-++--+--+--++-+-++++------+-+---++--++++-+-+++-++--++-+++-+-++++--++-----+++----+-++-++--++++++++--++-++-+----+++-+++-+-++--+-+-++++-+----+-++++-+-+--++-+-++
+---+------++++-+-++--+--+--++-+-++++------+-+---++--++++-+-+++-++--++-+++-+-++++--++-----+++----+-++-++--++++++++--++-++-+----+++++++-+-++--+-+-++++-+----+-++++-+-+--++-+-+
++---+------++++-+-++--+--+--++-+-++++--------+---++--++++-+-+++-++--++-+++-+-++++--++-+---+++----+-++-++--++++++++--++-++-+----+++++++-+-++--+-+-++++-+----+-++++-+-+--++-+-
+-+---+------++++-+-++--+--+--++-+-++++--------+---++--++++-+-+++-++--++-+++-+-++++--++++---+++----+-++-++--++++++++--++-++-+----+-+++++-+-++--+-+-++++-+----+-++++-+-+--++-+
+--+---+------++++-+-++--+--+--++-+-++++----+---+---++--++++-+-+++-++--++-+++-+-++++--++++---+++----+-++-++--++++++++--++-++-+----+-+++++-+-++--+-+-++++-+----+-++++-+-+--++-
+---+---+------++++-+-++--+--+--++-+-++++---++---+---++--++++-+-+++-++--++-+++-+-++++---+++---+++----+-++-++--++++++++--++-++-+----+-+++++-+-++--+-+-++++-+----+-++++-+-+--++
+----+---+------++++-+-++--+--+--++-+-++++---++---+---++--++++-+-+++-++--++-+++-+-++++---+++---+++----+-++-++--++++++++--++-++-+--+-+-+++++-+-++--+-+-++++-+----+-++++-+-+--+
+-----+---+------++++-+-++--+--+--++-+-++++---++---+---++--++++-+-+++-++--++-+++-+-++++---+++---+++----+-++-++--++++++++--++-++-+-++-+-+++++-+-++--+-+-++++-+----+-++++-+-+--
+------+---+------++++-+-++--+--+--++-+-+++++--++---+---++--++++-+-+++-++--++-+++-+-+++----+++---+++----+-++-++--++++++++--++-++-+-++-+-+++++-+-++--+-+-++++-+----+-++++-+-+-
++------+---+------++++-+-++--+--+--++-+-+++++--++---+---++--++++-+-+++-++--++-+++-+-+++----+++---+++----+-++-++--++++++++--++-++---++-+-+++++-+-++--+-+-++++-+----+-++++-+-+
+++------+---+------++++-+-++--+--+--++-+-+++++--++---+---++--++++-+-+++-++--++-+++-+-+-+----+++---+++----+-++-++--++++++++--++-+++--++-+-+++++-+-++--+-+-++++-+----+-++++-+-
++++------+---+------++++-+-++--+--+--++-+-+++++--++---+---++--++++-+-+++-++--++-+++-+-+-+----+++---+++----+-++-++--++++++++--++-+-+--++-+-+++++-+-++--+-+-++++-+----+-++++-+
+++++------+---+------++++-+-++--+--+--++-+--++++--++---+---++--++++-+-+++-++--++-+++-+++-+----+++---+++----+-++-++--++++++++--++-+-+--++-+-+++++-+-++--+-+-++++-+----+-++++-
+-++++------+---+------++++-+-++--+--+--++-++-++++--++---+---++--++++-+-+++-++--++-+++--++-+----+++---+++----+-++-++--++++++++--++-+-+--++-+-+++++-+-++--+-+-++++-+----+-++++
++-++++------+---+------++++-+-++--+--+--++--+-++++--++---+---++--++++-+-+++-++--++-++++-++-+----+++---+++----+-++-++--++++++++--++-+-+--++-+-+++++-+-++--+-+-++++-+----+-+++
+-+-++++------+---+------++++-+-++--+--+--+++-+-++++--++---+---++--++++-+-+++-++--++-++++-++-+----+++---+++----+-++-++--++++++++--++-+-+--++-+-+++++-+-++--+-+-++++-+----+-++
++-+-++++------+---+------++++-+-++--+--+--+++-+-++++--++---+---++--++++-+-+++-++--++-+-++-++-+----+++---+++----+-++-++--++++++++-+++-+-+--++-+-+++++-+-++--+-+-++++-+----+-+
+++-+-++++------+---+------++++-+-++--+--+--+++-+-++++--++---+---++--++++-+-+++-++--++---++-++-+----+++---+++----+-++-++--++++++++++++-+-+--++-+-+++++-+-++--+-+-++++-+----+-
+-++-+-++++------+---+------++++-+-++--+--+--+++-+-++++--++---+---++--++++-+-+++-++--+++--++-++-+----+++---+++----+-++-++--+++++++-++++-+-+--++-+-+++++-+-++--+-+-++++-+----+
+--++-+-++++------+---+------++++-+-++--+--++-+++-+-++++--++---+---++--++++-+-+++-++--+++--++-++-+----+++---+++----+-++-++--+++++++-++++-+-+--++-+-+++++-+-++--+-+-++++-+----
++--++-+-++++------+---+------++++-+-++--+--++-+++-+-++++--++---+---++--++++-+-+++-++--+++--++-++-+----+++---+++----+-++-++--+++++-+-++++-+-+--++-+-+++++-+-++--+-+-++++-+---
+-+--++-+-++++------+---+------++++-+-++--+--++-+++-+-++++--++---+---++--++++-+-+++-++-++++--++-++-+----+++---+++----+-++-++--++++--+-++++-+-+--++-+-+++++-+-++--+-+-++++-+--
+--+--++-+-++++------+---+------++++-+-++--+--++-+++-+-++++--++---+---++--++++-+-+++-+++++++--++-++-+----+++---+++----+-++-++--+++---+-++++-+-+--++-+-+++++-+-++--+-+-++++-+-
++--+--++-+-++++------+---+------++++-+-++--+--++-+++-+-++++--++---+---++--++++-+-+++-+++++++--++-++-+----+++---+++----+-++-++--++----+-++++-+-+--++-+-+++++-+-++--+-+-++++-+
+-+--+--++-+-++++------+---+------++++-+-++-++--++-+++-+-++++--++---+---++--++++-+-+++-+++++++--++-++-+----+++---+++----+-++-++--++----+-++++-+-+--++-+-+++++-+-++--+-+-++++-
+--+--+--++-+-++++------+---+------++++-+-++-++--++-+++-+-++++--++---+---++--++++-+-+++++++++++--++-++-+----+++---+++----+-++-++---+----+-++++-+-+--++-+-+++++-+-++--+-+-++++
++--+--+--++-+-++++------+---+------++++-+-++-++--++-+++-+-++++--++---+---++--++++-+-++-++++++++--++-++-+----+++---+++----+-++-++-+-+----+-++++-+-+--++-+-+++++-+-++--+-+-+++
+++--+--+--++-+-++++------+---+------++++-+-++-++--++-+++-+-++++--++---+---++--++++-+-+--++++++++--++-++-+----+++---+++----+-++-++++-+----+-++++-+-+--++-+-+++++-+-++--+-+-++
+-++--+--+--++-+-++++------+---+------++++-++++-++--++-+++-+-++++--++---+---++--++++-+-+--++++++++--++-++-+----+++---+++----+-++-++++-+----+-++++-+-+--++-+-+++++-+-++--+-+-+
++-++--+--+--++-+-++++------+---+------++++--+++-++--++-+++-+-++++--++---+---++--++++-+++--++++++++--++-++-+----+++---+++----+-++-++++-+----+-++++-+-+--++-+-+++++-+-++--+-+-
+-+-++--+--+--++-+-++++------+---+------+++++-+++-++--++-+++-+-++++--++---+---++--++++--++--++++++++--++-++-+----+++---+++----+-++-++++-+----+-++++-+-+--++-+-+++++-+-++--+-+
++-+-++--+--+--++-+-++++------+---+------+++-+-+++-++--++-+++-+-++++--++---+---++--+++++-++--++++++++--++-++-+----+++---+++----+-++-++++-+----+-++++-+-+--++-+-+++++-+-++--+-
+++-+-++--+--+--++-+-++++------+---+------+++-+-+++-++--++-+++-+-++++--++---+---++--+++++-++--++++++++--++-++-+----+++---+++----+--+-++++-+----+-++++-+-+--++-+-+++++-+-++--+
++++-+-++--+--+--++-+-++++------+---+------+++-+-+++-++--++-+++-+-++++--++---+---++--++-++-++--++++++++--++-++-+----+++---+++----++-+-++++-+----+-++++-+-+--++-+-+++++-+-++--
+++++-+-++--+--+--++-+-++++------+---+------+++-+-+++-++--++-+++-+-++++--++---+---++--++-++-++--++++++++--++-++-+----+++---+++-----+-+-++++-+----+-++++-+-+--++-+-+++++-+-++-
+-++++-+-++--+--+--++-+-++++------+---+-----++++-+-+++-++--++-+++-+-++++--++---+---++---+-++-++--++++++++--++-++-+----+++---+++-----+-+-++++-+----+-++++-+-+--++-+-+++++-+-++
+--++++-+-++--+--+--++-+-++++------+---+-----++++-+-+++-++--++-+++-+-++++--++---+---++---+-++-++--++++++++--++-++-+----+++---+++--+--+-+-++++-+----+-++++-+-+--++-+-+++++-+-+
+---++++-+-++--+--+--++-+-++++------+---+-----++++-+-+++-++--++-+++-+-++++--++---+---++---+-++-++--++++++++--++-++-+----+++---+++-++--+-+-++++-+----+-++++-+-+--++-+-+++++-+-
+----++++-+-++--+--+--++-+-++++------+---+--+--++++-+-+++-++--++-+++-+-++++--++---+---+----+-++-++--++++++++--++-++-+----+++---+++-++--+-+-++++-+----+-++++-+-+--++-+-+++++-+
+-----++++-+-++--+--+--++-+-++++------+---+-++--++++-+-+++-++--++-+++-+-++++--++---+---+----+-++-++--++++++++--++-++-+----+++---+++-++--+-+-++++-+----+-++++-+-+--++-+-+++++-
+------++++-+-++--+--+--++-+-++++------+---+-++--++++-+-+++-++--++-+++-+-++++--++---+--++----+-++-++--++++++++--++-++-+----+++---+-+-++--+-+-++++-+----+-++++-+-+--++-+-+++++
++------++++-+-++--+--+--++-+-++++------+-----++--++++-+-+++-++--++-+++-+-++++--++---+-+++----+-++-++--++++++++--++-++-+----+++---+-+-++--+-+-++++-+----+-++++-+-+--++-+-++++
+-+------++++-+-++--+--+--++-+-++++------+-----++--++++-+-+++-++--++-+++-+-++++--++---+-+++----+-++-++--++++++++--++-++-+----+++--++-+-++--+-+-++++-+----+-++++-+-+--++-+-+++
+---+-+--++-+-+----+-++++-+----+-+-++--+-+--++---++++-+--+--++--------++--+--+-++++---++---++--++++-+-+++-++--++-+++-+-++++--++-----+------++++-+-++--+--+--++-+-++++------+-
+----+-+--++-+-+----+-++++-+----+-+-++--+-+-+++---++++-+--+--++--------++--+--+-++++----+---++--++++-+-+++-++--++-+++-+-++++--++-----+------++++-+-++--+--+--++-+-++++------+
+-----+-+--++-+-+----+-++++-+----+-+-++--+-+-+++---++++-+--+--++--------++--+--+-++++----+---++--++++-+-+++-++--++-+++-+-++++--++-+---+------++++-+-++--+--+--++-+-++++------
++-----+-+--++-+-+----+-++++-+----+-+-++--+---+++---++++-+--+--++--------++--+--+-++++----+---++--++++-+-+++-++--++-+++-+-++++--++-+---+------++++-+-++--+--+--++-+-++++-----
+-+-----+-+--++-+-+----+-++++-+----+-+-++--+---+++---++++-+--+--++--------++--+--+-+++++---+---++--++++-+-+++-++--++-+++-+-++++--+--+---+------++++-+-++--+--+--++-+-++++----
++-+-----+-+--++-+-+----+-++++-+----+-+-++--+---+++---++++-+--+--++--------++--+--+-+++++---+---++--++++-+-+++-++--++-+++-+-++++-----+---+------++++-+-++--+--+--++-+-++++---
+-+-+-----+-+--++-+-+----+-++++-+----+-+-++-++---+++---++++-+--+--++--------++--+--+-++-++---+---++--++++-+-+++-++--++-+++-+-++++-----+---+------++++-+-++--+--+--++-+-++++--
+--+-+-----+-+--++-+-+----+-++++-+----+-+-+++++---+++---++++-+--+--++--------++--+--+-+--++---+---++--++++-+-+++-++--++-+++-+-++++-----+---+------++++-+-++--+--+--++-+-++++-
++--+-+-----+-+--++-+-+----+-++++-+----+-+-+++++---+++---++++-+--+--++--------++--+--+-+--++---+---++--++++-+-+++-++--++-+++-+-+++------+---+------++++-+-++--+--+--++-+-++++
+++--+-+-----+-+--++-+-+----+-++++-+----+-+--++++---+++---++++-+--+--++--------++--+--+++--++---+---++--++++-+-+++-++--++-+++-+-+++------+---+------++++-+-++--+--+--++-+-+++
+-++--+-+-----+-+--++-+-+----+-++++-+----+-++-++++---+++---++++-+--+--++--------++--+--+++--++---+---++--++++-+-+++-++--++-+++-+-+++------+---+------++++-+-++--+--+--++-+-++
++-++--+-+-----+-+--++-+-+----+-++++-+----+--+-++++---+++---++++-+--+--++--------++--+-++++--++---+---++--++++-+-+++-++--++-+++-+-+++------+---+------++++-+-++--+--+--++-+-+
+-+-++--+-+-----+-+--++-+-+----+-++++-+----+--+-++++---+++---++++-+--+--++--------++--+-++++--++---+---++--++++-+-+++-++--++-+++-+++++------+---+------++++-+-++--+--+--++-+-
++-+-++--+-+-----+-+--++-+-+----+-++++-+----+--+-++++---+++---++++-+--+--++--------++--+-++++--++---+---++--++++-+-+++-++--++-+++--++++------+---+------++++-+-++--+--+--++-+
+-+-+-++--+-+-----+-+--++-+-+----+-++++-+----+--+-++++---+++---++++-+--+--++--------++--+-++++--++---+---++--++++-+-+++-++--++-++++-++++------+---+------++++-+-++--+--+--++-
+--+-+-++--+-+-----+-+--++-+-+----+-++++-+----+--+-++++---+++---++++-+--+--++--------+++-+-++++--++---+---++--++++-+-+++-++--++-++-+-++++------+---+------++++-+-++--+--+--++
+---+-+-++--+-+-----+-+--++-+-+----+-++++-+-+--+--+-++++---+++---++++-+--+--++--------+++-+-++++--++---+---++--++++-+-+++-++--++-++-+-++++------+---+------++++-+-++--+--+--+
+----+-+-++--+-+-----+-+--++-+-+----+-++++-+++--+--+-++++---+++---++++-+--+--++--------+++-+-++++--++---+---++--++++-+-+++-++--++-++-+-++++------+---+------++++-+-++--+--+--
++----+-+-++--+-+-----+-+--++-+-+----+-++++--++--+--+-++++---+++---++++-+--+--++--------+++-+-++++--++---+---++--++++-+-+++-++--++-++-+-++++------+---+------++++-+-++--+--+-
+-+----+-+-++--+-+-----+-+--++-+-+----+-++++--++--+--+-++++---+++---++++-+--+--++------+-+++-+-++++--++---+---++--++++-+-+++-++--+--++-+-++++------+---+------++++-+-++--+--+
++-+----+-+-++--+-+-----+-+--++-+-+----+-+++---++--+--+-++++---+++---++++-+--+--++-----++-+++-+-++++--++---+---++--++++-+-+++-++--+--++-+-++++------+---+------++++-+-++--+--
+++-+----+-+-++--+-+-----+-+--++-+-+----+-++----++--+--+-++++---+++---++++-+--+--++-----++-+++-+-++++--++---+---++--++++-+-+++-++--+--++-+-++++------+---+------++++-+-++--+-
++++-+----+-+-++--+-+-----+-+--++-+-+----+-+-----++--+--+-++++---+++---++++-+--+--++-----++-+++-+-++++--++---+---++--++++-+-+++-++--+--++-+-++++------+---+------++++-+-++--+
+++++-+----+-+-++--+-+-----+-+--++-+-+----+-------++--+--+-++++---+++---++++-+--+--++--+--++-+++-+-++++--++---+---++--++++-+-+++-++--+--++-+-++++------+---+------++++-+-++--
+-++++-+----+-+-++--+-+-----+-+--++-+-+----+-------++--+--+-++++---+++---++++-+--+--++-++--++-+++-+-++++--++---+---++--++++-+-+++--+--+--++-+-++++------+---+------++++-+-++-
++-++++-+----+-+-++--+-+-----+-+--++-+-+------------++--+--+-++++---+++---++++-+--+--++-++--++-+++-+-++++--++---+---++--++++-+-+++--+--+--++-+-++++------+---+------++++-+-++
+-+-++++-+----+-+-++--+-+-----+-+--++-+-+---+--------++--+--+-++++---+++---++++-+--+--++-++--++-+++-+-++++--++---+---++--++++-+-+++--+--+--++-+-++++------+---+------++++-+-+
+--+-++++-+----+-+-++--+-+-----+-+--++-+-+--++--------++--+--+-++++---+++---++++-+--+--++-++--++-+++-+-++++--++---+---++--++++-+-+++--+--+--++-+-++++------+---+------++++-+-
+---+-++++-+----+-+-++--+-+-----+-+--++-+-+--++--------++--+--+-++++---+++---++++-+--+-+++-++--++-+++-+-++++--++---+---++--++++-+--++--+--+--++-+-++++------+---+------++++-+
+----+-++++-+----+-+-++--+-+-----+-+--++-+-+--++--------++--+--+-++++---+++---++++-+--+-+++-++--++-+++-+-++++--++---+---++--++++-++-++--+--+--++-+-++++------+---+------++++-
++----+-++++-+----+-+-++--+-+-----+-+--++-+-+--++--------++--+--+-++++---+++---++++-+--+-+++-++--++-+++-+-++++--++---+---++--++++--+-++--+--+--++-+-++++------+---+------++++
+-+----+-++++-+----+-+-++--+-+-----+-+--++-+-+--++--------++--+--+-++++---+++---++++-+--+-+++-++--++-+++-+-++++--++---+---++--+++++-+-++--+--+--++-+-++++------+---+------+++
++-+----+-++++-+----+-+-++--+-+-----+-+--++---+--++--------++--+--+-++++---+++---++++-++-+-+++-++--++-+++-+-++++--++---+---++--+++++-+-++--+--+--++-+-++++------+---+------++
+-+-+----+-++++-+----+-+-++--+-+-----+-+--+++--+--++--------++--+--+-++++---+++---++++-++-+-+++-++--++-+++-+-++++--++---+---++--+++++-+-++--+--+--++-+-++++------+---+------+
++-+-+----+-++++-+----+-+-++--+-+-----+-+--+-+--+--++--------++--+--+-++++---+++---+++++++-+-+++-++--++-+++-+-++++--++---+---++--+++++-+-++--+--+--++-+-++++------+---+------
+++-+-+----+-++++-+----+-+-++--+-+-----+-+--+-+--+--++--------++--+--+-++++---+++---+++++++-+-+++-++--++-+++-+-++++--++---+---++---++++-+-++--+--+--++-+-++++------+---+-----
+-++-+-+----+-++++-+----+-+-++--+-+-----+-+-++-+--+--++--------++--+--+-++++---+++---++-++++-+-+++-++--++-+++-+-++++--++---+---++---++++-+-++--+--+--++-+-++++------+---+----
+--++-+-+----+-++++-+----+-+-++--+-+-----+-++++-+--+--++--------++--+--+-++++---+++---+--++++-+-+++-++--++-+++-+-++++--++---+---++---++++-+-++--+--+--++-+-++++------+---+---
++--++-+-+----+-++++-+----+-+-++--+-+-----+-++++-+--+--++--------++--+--+-++++---+++---+--++++-+-+++-++--++-+++-+-++++--++---+---+----++++-+-++--+--+--++-+-++++------+---+--
+-+--++-+-+----+-++++-+----+-+-++--+-+-----+-++++-+--+--++--------++--+--+-++++---+++--++--++++-+-+++-++--++-+++-+-++++--++---+--------++++-+-++--+--+--++-+-++++------+---+-
++-+--++-+-+----+-++++-+----+-+-++--+-+-------++++-+--+--++--------++--+--+-++++---+++--++--++++-+-+++-++--++-+++-+-++++--++---+--------++++-+-++--+--+--++-+-++++------+---+
+-+-+--++-+-+----+-++++-+----+-+-++--+-+-------++++-+--+--++--------++--+--+-++++---+++--++--++++-+-+++-++--++-+++-+-++++--++---+-+------++++-+-++--+--+--++-+-++++------+---
+--+-+--++-+-+----+-++++-+----+-+-++--+-+---+---++++-+--+--++--------++--+--+-++++---++---++--++++-+-+++-++--++-+++-+-++++--++---+-+------++++-+-++--+--+--++-+-++++------+--
+--+++----+-++-++--++++++++--++-++-+----+++----+-+--++-+-+----+-++++-+----+-+-++--+-+--++-++++++----+-+--++-++-++--+-+----++++++-++---++--++++-+-+++-++--++-+++-+-++++--++---
+---+++----+-++-++--++++++++--++-++-+----+++----+-+--++-+-+----+-++++-+----+-+-++--+-+-+++-++++++----+-+--++-++-++--+-+----++++++--+---++--++++-+-+++-++--++-+++-+-++++--++--
++---+++----+-++-++--++++++++--++-++-+----++-----+-+--++-+-+----+-++++-+----+-+-++--+-+-+++-++++++----+-+--++-++-++--+-+----++++++--+---++--++++-+-+++-++--++-+++-+-++++--++-
+++---+++----+-++-++--++++++++--++-++-+----++-----+-+--++-+-+----+-++++-+----+-+-++--+-+-+++-++++++----+-+--++-++-++--+-+----+++++---+---++--++++-+-+++-++--++-+++-+-++++--++
++++---+++----+-++-++--++++++++--++-++-+-----+-----+-+--++-+-+----+-++++-+----+-+-++--+++-+++-++++++----+-+--++-++-++--+-+----+++++---+---++--++++-+-+++-++--++-+++-+-++++--+
+-+++---+++----+-++-++--++++++++--++-++-+---+-+-----+-+--++-+-+----+-++++-+----+-+-++--+++-+++-++++++----+-+--++-++-++--+-+----+++++---+---++--++++-+-+++-++--++-+++-+-++++--
+--+++---+++----+-++-++--++++++++--++-++-+---+-+-----+-+--++-+-+----+-++++-+----+-+-++-++++-+++-++++++----+-+--++-++-++--+-+----++-++---+---++--++++-+-+++-++--++-+++-+-++++-
+---+++---+++----+-++-++--++++++++--++-++-+---+-+-----+-+--++-+-+----+-++++-+----+-+-+++++++-+++-++++++----+-+--++-++-++--+-+----+--++---+---++--++++-+-+++-++--++-+++-+-++++
+----+++---+++----+-++-++--++++++++--++-++-++--+-+-----+-+--++-+-+----+-++++-+----+-+-+++++++-+++-++++++----+-+--++-++-++--+-+----+--++---+---++--++++-+-+++-++--++-+++-+-+++
++----+++---+++----+-++-++--++++++++--++-++-++--+-+-----+-+--++-+-+----+-++++-+----+-+--++++++-+++-++++++----+-+--++-++-++--+-+---++--++---+---++--++++-+-+++-++--++-+++-+-++
+-+----+++---+++----+-++-++--++++++++--++-++-++--+-+-----+-+--++-+-+----+-++++-+----+-+--++++++-+++-++++++----+-+--++-++-++--+-+--+++--++---+---++--++++-+-+++-++--++-+++-+-+
++-+----+++---+++----+-++-++--++++++++--++-++-++--+-+-----+-+--++-+-+----+-++++-+----+----++++++-+++-++++++----+-+--++-++-++--+-+-++++--++---+---++--++++-+-+++-++--++-+++-+-
+++-+----+++---+++----+-++-++--++++++++--++--+-++--+-+-----+-+--++-+-+----+-++++-+----+----++++++-+++-++++++----+-+--++-++-++--+-+-++++--++---+---++--++++-+-+++-++--++-+++-+
+-++-+----+++---+++----+-++-++--++++++++--+++-+-++--+-+-----+-+--++-+-+----+-++++-+----+----++++++-+++-++++++----+-+--++-++-++--+-+-++++--++---+---++--++++-+-+++-++--++-+++-
++-++-+----+++---+++----+-++-++--++++++++--+-+-+-++--+-+-----+-+--++-+-+----+-++++-+----+----++++++-+++-++++++----+-+--++-++-++--+-+-++++--++---+---++--++++-+-+++-++--++-+++
+++-++-+----+++---+++----+-++-++--++++++++----+-+-++--+-+-----+-+--++-+-+----+-++++-+--+-+----++++++-+++-++++++----+-+--++-++-++--+-+-++++--++---+---++--++++-+-+++-++--++-++
+-++-++-+----+++---+++----+-++-++--++++++++----+-+-++--+-+-----+-+--++-+-+----+-++++-+--+-+----++++++-+++-++++++----+-+--++-++-++-++-+-++++--++---+---++--++++-+-+++-++--++-+
+--++-++-+----+++---+++----+-++-++--++++++++----+-+-++--+-+-----+-+--++-+-+----+-++++-+--+-+----++++++-+++-++++++----+-+--++-++-+++++-+-++++--++---+---++--++++-+-+++-++--++-
++--++-++-+----+++---+++----+-++-++--++++++++----+-+-++--+-+-----+-+--++-+-+----+-++++-+--+-+----++++++-+++-++++++----+-+--++-++-+-+++-+-++++--++---+---++--++++-+-+++-++--++
+++--++-++-+----+++---+++----+-++-++--++++++-+----+-+-++--+-+-----+-+--++-+-+----+-++++++--+-+----++++++-+++-++++++----+-+--++-++-+-+++-+-++++--++---+---++--++++-+-+++-++--+
++++--++-++-+----+++---+++----+-++-++--++++++-+----+-+-++--+-+-----+-+--++-+-+----+-+++-++--+-+----++++++-+++-++++++----+-+--++-++++-+++-+-++++--++---+---++--++++-+-+++-++--
+++++--++-++-+----+++---+++----+-++-++--++++++-+----+-+-++--+-+-----+-+--++-+-+----+-+++-++--+-+----++++++-+++-++++++----+-+--++-+-++-+++-+-++++--++---+---++--++++-+-+++-++-
++++++--++-++-+----+++---+++----+-++-++--++++++-+----+-+-++--+-+-----+-+--++-+-+----+-+++-++--+-+----++++++-+++-++++++----+-+--++---++-+++-+-++++--++---+---++--++++-+-+++-++
+++++++--++-++-+----+++---+++----+-++-++--++++++-+----+-+-++--+-+-----+-+--++-+-+----+--++-++--+-+----++++++-+++-++++++----+-+--+++--++-+++-+-++++--++---+---++--++++-+-+++-+
++++++++--++-++-+----+++---+++----+-++-++--+-++++-+----+-+-++--+-+-----+-+--++-+-+----++-++-++--+-+----++++++-+++-++++++----+-+--+++--++-+++-+-++++--++---+---++--++++-+-+++-
+++++++++--++-++-+----+++---+++----+-++-++--+-++++-+----+-+-++--+-+-----+-+--++-+-+----++-++-++--+-+----++++++-+++-++++++----+-+---++--++-+++-+-++++--++---+---++--++++-+-+++
+-++++++++--++-++-+----+++---+++----+-++-++--+-++++-+----+-+-++--+-+-----+-+--++-+-+----++-++-++--+-+----++++++-+++-++++++----+-+-+-++--++-+++-+-++++--++---+---++--++++-+-++
+--++++++++--++-++-+----+++---+++----+-++-++--+-++++-+----+-+-++--+-+-----+-+--++-+-+----++-++-++--+-+----++++++-+++-++++++----+-+++-++--++-+++-+-++++--++---+---++--++++-+-+
++--++++++++--++-++-+----+++---+++----+-++-+---+-++++-+----+-+-++--+-+-----+-+--++-+-+-+--++-++-++--+-+----++++++-+++-++++++----+-+++-++--++-+++-+-++++--++---+---++--++++-+-
+++--++++++++--++-++-+----+++---+++----+-++-----+-++++-+----+-+-++--+-+-----+-+--++-+-+-+--++-++-++--+-+----++++++-+++-++++++----+-+++-++--++-+++-+-++++--++---+---++--++++-+
+-++--++++++++--++-++-+----+++---+++----+-+++----+-++++-+----+-+-++--+-+-----+-+--++-+-+-+--++-++-++--+-+----++++++-+++-++++++----+-+++-++--++-+++-+-++++--++---+---++--++++-
++-++--++++++++--++-++-+----+++---+++----+-+-+----+-++++-+----+-+-++--+-+-----+-+--++-+-+-+--++-++-++--+-+----++++++-+++-++++++----+-+++-++--++-+++-+-++++--++---+---++--++++
+++-++--++++++++--++-++-+----+++---+++----+-+-+----+-++++-+----+-+-++--+-+-----+-+--++---+-+--++-++-++--+-+----++++++-+++-++++++--+-+-+++-++--++-+++-+-++++--++---+---++--+++
+-++-++--++++++++--++-++-+----+++---+++----+-+-+----+-++++-+----+-+-++--+-+-----+-+--++---+-+--++-++-++--+-+----++++++-+++-++++++-++-+-+++-++--++-+++-+-++++--++---+---++--++
++-++-++--++++++++--++-++-+----+++---+++----+-+-+----+-++++-+----+-+-++--+-+-----+-+--+----+-+--++-++-++--+-+----++++++-+++-+++++++++-+-+++-++--++-+++-+-++++--++---+---++--+
+-+-++-++--++++++++--++-++-+----+++---+++---++-+-+----+-++++-+----+-+-++--+-+-----+-+--+----+-+--++-++-++--+-+----++++++-+++-+++++++++-+-+++-++--++-+++-+-++++--++---+---++--
+--+-++-++--++++++++--++-++-+----+++---+++---++-+-+----+-++++-+----+-+-++--+-+-----+-+-++----+-+--++-++-++--+-+----++++++-+++-++++-++++-+-+++-++--++-+++-+-++++--++---+---++-
+---+-++-++--++++++++--++-++-+----+++---+++---++-+-+----+-++++-+----+-+-++--+-+-----+-++++----+-+--++-++-++--+-+----++++++-+++-+++--++++-+-+++-++--++-+++-+-++++--++---+---++
+----+-++-++--++++++++--++-++-+----+++---++++--++-+-+----+-++++-+----+-+-++--+-+-----+-++++----+-+--++-++-++--+-+----++++++-+++-+++--++++-+-+++-++--++-+++-+-++++--++---+---+
++----+-++-++--++++++++--++-++-+----+++---++-+--++-+-+----+-++++-+----+-+-++--+-+-----++++++----+-+--++-++-++--+-+----++++++-+++-+++--++++-+-+++-++--++-+++-+-++++--++---+---
+++----+-++-++--++++++++--++-++-+----+++---++-+--++-+-+----+-++++-+----+-+-++--+-+-----++++++----+-+--++-++-++--+-+----++++++-+++--++--++++-+-+++-++--++-+++-+-++++--++---+--
++++----+-++-++--++++++++--++-++-+----+++----+-+--++-+-+----+-++++-+----+-+-++--+-+-----++++++----+-+--++-++-++--+-+----++++++-+++--++--++++-+-+++-++--++-+++-+-++++--++---+-
+-+++----+-++-++--++++++++--++-++-+----+++----+-+--++-+-+----+-++++-+----+-+-++--+-+---+-++++++----+-+--++-++-++--+-+----++++++-++---++--++++-+-+++-++--++-+++-+-++++--++---+
\ No newline at end of file
diff --git a/gptqmodel/exllamav3/util/hadamard_data/hadamard_188.txt b/gptqmodel/exllamav3/util/hadamard_data/hadamard_188.txt
new file mode 100644
index 000000000..09126ef5c
--- /dev/null
+++ b/gptqmodel/exllamav3/util/hadamard_data/hadamard_188.txt
@@ -0,0 +1,188 @@
++-+----+--+----++---++++---+-+----++++++--+---++---+--++++++----+-+---+---+++--++++-++-++++-+-+--+--+-+++-----+---+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-
+++-+----+--+----++---++++---+-+----++++++--+------+--++++++----+-+---+---+++--++++-++-++++-+-+--+--+-+++-----+---+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+
+-++-+----+--+----++---++++---+-+----++++++--+----+--++++++----+-+---+---+++--++++-++-++++-+-+--+--+-+++-----+---+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+-
+--++-+----+--+----++---++++---+-+----++++++--+--+--++++++----+-+---+---+++--++++-++-++++-+-+--+--+-+++-----+---+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--
+---++-+----+--+----++---++++---+-+----++++++--++--++++++----+-+---+---+++--++++-++-++++-+-+-----+-+++-----+---+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+
++---++-+----+--+----++---++++---+-+----++++++----++++++----+-+---+---+++--++++-++-++++-+-+---+-+-+++-----+---+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+-
+-+---++-+----+--+----++---++++---+-+----++++++--++++++----+-+---+---+++--++++-++-++++-+-+---+-+-+++-----+---+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--
+--+---++-+----+--+----++---++++---+-+----++++++++++++----+-+---+---+++--++++-++-++++-+-+---+---+++-----+---+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+
++--+---++-+----+--+----++---++++---+-+----++++++++++----+-+---+---+++--++++-++-++++-+-+---+--++++-----+---+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+-
+++--+---++-+----+--+----++---++++---+-+----++++++++----+-+---+---+++--++++-++-++++-+-+---+--++++-----+---+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+-+
++++--+---++-+----+--+----++---++++---+-+----++++++----+-+---+---+++--++++-++-++++-+-+---+--++++-----+---+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+-++
+++++--+---++-+----+--+----++---++++---+-+----++++----+-+---+---+++--++++-++-++++-+-+---+--++++-----+---+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+-+++
++++++--+---++-+----+--+----++---++++---+-+----++----+-+---+---+++--++++-++-++++-+-+---+--+++++----+---+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+-+++-
+++++++--+---++-+----+--+----++---++++---+-+--------+-+---+---+++--++++-++-++++-+-+---+--++++++---+---+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+-+++--
+-++++++--+---++-+----+--+----++---++++---+-+------+-+---+---+++--++++-++-++++-+-+---+--++++++---+---+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+-+++---
+--++++++--+---++-+----+--+----++---++++---+-+----+-+---+---+++--++++-++-++++-+-+---+--++++++---+---+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+-+++----
+---++++++--+---++-+----+--+----++---++++---+-+--+-+---+---+++--++++-++-++++-+-+---+--++++++---+---+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+-+++-----
+----++++++--+---++-+----+--+----++---++++---+-++-+---+---+++--++++-++-++++-+-+---+--++++++-------+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+-+++-----+
++----++++++--+---++-+----+--+----++---++++---+--+---+---+++--++++-++-++++-+-+---+--++++++----+--+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+-+++-----+-
+-+----++++++--+---++-+----+--+----++---++++---++---+---+++--++++-++-++++-+-+---+--++++++----+--+---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+-+++-----+--
++-+----++++++--+---++-+----+--+----++---++++------+---+++--++++-++-++++-+-+---+--++++++----+-++---++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+-+++-----+---
+-+-+----++++++--+---++-+----+--+----++---++++----+---+++--++++-++-++++-+-+---+--++++++----+-+----++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+-+++-----+---+
+--+-+----++++++--+---++-+----+--+----++---++++--+---+++--++++-++-++++-+-+---+--++++++----+-+----++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+-+++-----+---+-
+---+-+----++++++--+---++-+----+--+----++---+++++---+++--++++-++-++++-+-+---+--++++++----+-+----++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+-+++-----+---+--
++---+-+----++++++--+---++-+----+--+----++---+++---+++--++++-++-++++-+-+---+--++++++----+-+---+++--+-++-+++-+-+--+---++--+--+-+++-----+---+-----++-+--+---+-+-++-+++-+--+--+-+++-----+---+---
+++---+-+----++++++--+---++-+----+--+----++---++--+++--++++-++-++++-+-+---+--++++++----+-+---+-+--+-++-+++-+-+--+---++--+--+-+++-----+---+---+-++-+--+---+-+-++-+++-+--+--+-+++-----+---+----
++++---+-+----++++++--+---++-+----+--+----++---+-+++--++++-++-++++-+-+---+--++++++----+-+---+----+-++-+++-+-+--+---++--+--+-+++-----+---+---++++-+--+---+-+-++-+++-+--+--+-+++-----+---+-----
+++++---+-+----++++++--+---++-+----+--+----++---+++--++++-++-++++-+-+---+--++++++----+-+---+----+-++-+++-+-+--+---++--+--+-+++-----+---+---++-+-+--+---+-+-++-+++-+--+--+-+++-----+---+-----+
+-++++---+-+----++++++--+---++-+----+--+----++--++--++++-++-++++-+-+---+--++++++----+-+---+---++-++-+++-+-+--+---++--+--+-+++-----+---+---++---+--+---+-+-++-+++-+--+--+-+++-----+---+-----++
+--++++---+-+----++++++--+---++-+----+--+----++-+--++++-++-++++-+-+---+--++++++----+-+---+---++-++-+++-+-+--+---++--+--+-+++-----+---+---++--++--+---+-+-++-+++-+--+--+-+++-----+---+-----++-
+---++++---+-+----++++++--+---++-+----+--+----++--++++-++-++++-+-+---+--++++++----+-+---+---+++++-+++-+-+--+---++--+--+-+++-----+---+---++--+---+---+-+-++-+++-+--+--+-+++-----+---+-----++-+
++---++++---+-+----++++++--+---++-+----+--+----+-++++-++-++++-+-+---+--++++++----+-+---+---+++-+-+++-+-+--+---++--+--+-+++-----+---+---++--+-+-+---+-+-++-+++-+--+--+-+++-----+---+-----++-+-
+++---++++---+-+----++++++--+---++-+----+--+----++++-++-++++-+-+---+--++++++----+-+---+---+++---+++-+-+--+---++--+--+-+++-----+---+---++--+-+++---+-+-++-+++-+--+--+-+++-----+---+-----++-+--
+-++---++++---+-+----++++++--+---++-+----+--+---+++-++-++++-+-+---+--++++++----+-+---+---+++--++++-+-+--+---++--+--+-+++-----+---+---++--+-++----+-+-++-+++-+--+--+-+++-----+---+-----++-+--+
+--++---++++---+-+----++++++--+---++-+----+--+--++-++-++++-+-+---+--++++++----+-+---+---+++--++++-+-+--+---++--+--+-+++-----+---+---++--+-++-+--+-+-++-+++-+--+--+-+++-----+---+-----++-+--+-
+---++---++++---+-+----++++++--+---++-+----+--+-+-++-++++-+-+---+--++++++----+-+---+---+++--++++-+-+--+---++--+--+-+++-----+---+---++--+-++-++-+-+-++-+++-+--+--+-+++-----+---+-----++-+--+--
+----++---++++---+-+----++++++--+---++-+----+--+-++-++++-+-+---+--++++++----+-+---+---+++--++++-+-+--+---++--+--+-+++-----+---+---++--+-++-++++-+-++-+++-+--+--+-+++-----+---+-----++-+--+---
++----++---++++---+-+----++++++--+---++-+----+--++-++++-+-+---+--++++++----+-+---+---+++--++++-+-+--+---++--+--+-+++-----+---+---++--+-++-+++--+-++-+++-+--+--+-+++-----+---+-----++-+--+---+
+-+----++---++++---+-+----++++++--+---++-+----+-+-++++-+-+---+--++++++----+-+---+---+++--++++-+-+--+---++--+--+-+++-----+---+---++--+-++-+++-++-++-+++-+--+--+-+++-----+---+-----++-+--+---+-
+--+----++---++++---+-+----++++++--+---++-+----+-++++-+-+---+--++++++----+-+---+---+++--++++-+++--+---++--+--+-+++-----+---+---++--+-++-+++-+--++-+++-+--+--+-+++-----+---+-----++-+--+---+-+
++--+----++---++++---+-+----++++++--+---++-+----++++-+-+---+--++++++----+-+---+---+++--++++-++---+---++--+--+-+++-----+---+---++--+-++-+++-+-+++-+++-+--+--+-+++-----+---+-----++-+--+---+-+-
+-+--+----++---++++---+-+----++++++--+---++-+---+++-+-+---+--++++++----+-+---+---+++--++++-++-+-+---++--+--+-+++-----+---+---++--+-++-+++-+-+-+-+++-+--+--+-+++-----+---+-----++-+--+---+-+-+
+--+--+----++---++++---+-+----++++++--+---++-+--++-+-+---+--++++++----+-+---+---+++--++++-++-+++---++--+--+-+++-----+---+---++--+-++-+++-+-+---+++-+--+--+-+++-----+---+-----++-+--+---+-+-++
+---+--+----++---++++---+-+----++++++--+---++-+-+-+-+---+--++++++----+-+---+---+++--++++-++-+++---++--+--+-+++-----+---+---++--+-++-+++-+-+--++++-+--+--+-+++-----+---+-----++-+--+---+-+-++-
+----+--+----++---++++---+-+----++++++--+---++-+-+-+---+--++++++----+-+---+---+++--++++-++-++++--++--+--+-+++-----+---+---++--+-++-+++-+-+--+-++-+--+--+-+++-----+---+-----++-+--+---+-+-++-+
++----+--+----++---++++---+-+----++++++--+---++-+-+---+--++++++----+-+---+---+++--++++-++-++++--++--+--+-+++-----+---+---++--+-++-+++-+-+--+--+-+--+--+-+++-----+---+-----++-+--+---+-+-++-++
+-+----+--+----++---++++---+-+----++++++--+---++-+---+--++++++----+-+---+---+++--++++-++-++++-+++--+--+-+++-----+---+---++--+-++-+++-+-+--+----+--+--+-+++-----+---+-----++-+--+---+-+-++-+++
+-+++-++------++++-+-+++-+++---++----+--+----+-++-+----+--+----++---++++---+-+----++++++--+---+---+--+-+-+++-++-+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++
++++-++------++++-+-+++-+++---++----+--+----+-+-++-+----+--+----++---++++---+-+----++++++--+-----+--+-+-+++-++-+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++-
+++-++------++++-+-+++-+++---++----+--+----+-+-+-++-+----+--+----++---++++---+-+----++++++--+---+--+-+-+++-++-+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++--
++-++------++++-+-+++-+++---++----+--+----+-+-++--++-+----+--+----++---++++---+-+----++++++--+-+--+-+-+++-++-+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---
+-++------++++-+-+++-+++---++----+--+----+-+-+++---++-+----+--+----++---++++---+-+----++++++--+--+-+-+++-++-+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+
+++------++++-+-+++-+++---++----+--+----+-+-+++-+---++-+----+--+----++---++++---+-+----++++++---+-+-+++-++-+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+-
++------++++-+-+++-+++---++----+--+----+-+-+++-+-+---++-+----+--+----++---++++---+-+----++++++-+-+-+++-++-+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+--
+------++++-+-+++-+++---++----+--+----+-+-+++-++--+---++-+----+--+----++---++++---+-+----++++++-+-+++-++-+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+--+
+-----++++-+-+++-+++---++----+--+----+-+-+++-++-+--+---++-+----+--+----++---++++---+-+----++++++-+++-++-+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+--+-
+----++++-+-+++-+++---++----+--+----+-+-+++-++--++--+---++-+----+--+----++---++++---+-+----++++-+++-++-+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+--+-+
+---++++-+-+++-+++---++----+--+----+-+-+++-++---+++--+---++-+----+--+----++---++++---+-+----++++++-++-+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+--+-+-
+--++++-+-+++-+++---++----+--+----+-+-+++-++----++++--+---++-+----+--+----++---++++---+-+----++++-++-+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+--+-+-+
+-++++-+-+++-+++---++----+--+----+-+-+++-++-----+++++--+---++-+----+--+----++---++++---+-+----++-++-+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+--+-+-++
+++++-+-+++-+++---++----+--+----+-+-+++-++------++++++--+---++-+----+--+----++---++++---+-+-----++-+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+--+-+-+++
++++-+-+++-+++---++----+--+----+-+-+++-++------+-++++++--+---++-+----+--+----++---++++---+-+---++-+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+--+-+-+++-
+++-+-+++-+++---++----+--+----+-+-+++-++------++--++++++--+---++-+----+--+----++---++++---+-+--+-+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+--+-+-+++-+
++-+-+++-+++---++----+--+----+-+-+++-++------+++---++++++--+---++-+----+--+----++---++++---+-+--+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+--+-+-+++-++
+-+-+++-+++---++----+--+----+-+-+++-++------++++----++++++--+---++-+----+--+----++---++++---+-++--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+--+-+-+++-++-
++-+++-+++---++----+--+----+-+-+++-++------++++-+----++++++--+---++-+----+--+----++---++++---+---+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+--+-+-+++-++-+
+-+++-+++---++----+--+----+-+-+++-++------++++-+-+----++++++--+---++-+----+--+----++---++++---+-+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+--+-+-+++-++-+-
++++-+++---++----+--+----+-+-+++-++------++++-+-+-+----++++++--+---++-+----+--+----++---++++---+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+--+-+-+++-++-+--
+++-+++---++----+--+----+-+-+++-++------++++-+-+-+-+----++++++--+---++-+----+--+----++---++++--++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+--+-+-+++-++-+--+
++-+++---++----+--+----+-+-+++-++------++++-+-++--+-+----++++++--+---++-+----+--+----++---++++-+++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--++---+--+-+-+++-++-+--++
+-+++---++----+--+----+-+-+++-++------++++-+-+++---+-+----++++++--+---++-+----+--+----++---++++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--+++--+---+-----+++-+--+--++---+--+-+-+++-++-+--++-
++++---++----+--+----+-+-+++-++------++++-+-+++-+---+-+----++++++--+---++-+----+--+----++---++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++++-+---+-----+++-+--+--++---+--+-+-+++-++-+--++--
+++---++----+--+----+-+-+++-++------++++-+-+++-+++---+-+----++++++--+---++-+----+--+----++---++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++++++---+-----+++-+--+--++---+--+-+-+++-++-+--++---
++---++----+--+----+-+-+++-++------++++-+-+++-+++++---+-+----++++++--+---++-+----+--+----++---++++-+++++---+-++-++-+---+--+-+-+++-++-+--+++++----+-----+++-+--+--++---+--+-+-+++-++-+--++---+
+---++----+--+----+-+-+++-++------++++-+-+++-+++++++---+-+----++++++--+---++-+----+--+----++---++-+++++---+-++-++-+---+--+-+-+++-++-+--+++++-+--+-----+++-+--+--++---+--+-+-+++-++-+--++---+-
+--++----+--+----+-+-+++-++------++++-+-+++-+++--++++---+-+----++++++--+---++-+----+--+----++--+-+++++---+-++-++-+---+--+-+-+++-++-+--+++++-++-+-----+++-+--+--++---+--+-+-+++-++-+--++---+--
+-++----+--+----+-+-+++-++------++++-+-+++-+++----++++---+-+----++++++--+---++-+----+--+----++--+++++---+-++-++-+---+--+-+-+++-++-+--+++++-++++-----+++-+--+--++---+--+-+-+++-++-+--++---+---
+++----+--+----+-+-+++-++------++++-+-+++-+++------++++---+-+----++++++--+---++-+----+--+----+++++++---+-++-++-+---+--+-+-+++-++-+--+++++-+++------+++-+--+--++---+--+-+-+++-++-+--++---+---+
++----+--+----+-+-+++-++------++++-+-+++-+++---++---++++---+-+----++++++--+---++-+----+--+----+++++---+-++-++-+---+--+-+-+++-++-+--+++++-+++-+----+++-+--+--++---+--+-+-+++-++-+--++---+---+-
+----+--+----+-+-+++-++------++++-+-+++-+++---++++---++++---+-+----++++++--+---++-+----+--+----+++---+-++-++-+---+--+-+-+++-++-+--+++++-+++-++---+++-+--+--++---+--+-+-+++-++-+--++---+---+--
+---+--+----+-+-+++-++------++++-+-+++-+++---++--++---++++---+-+----++++++--+---++-+----+--+---++---+-++-++-+---+--+-+-+++-++-+--+++++-+++-+++--+++-+--+--++---+--+-+-+++-++-+--++---+---+---
+--+--+----+-+-+++-++------++++-+-+++-+++---++----++---++++---+-+----++++++--+---++-+----+--+--+---+-++-++-+---+--+-+-+++-++-+--+++++-+++-++++-+++-+--+--++---+--+-+-+++-++-+--++---+---+----
+-+--+----+-+-+++-++------++++-+-+++-+++---++------++---++++---+-+----++++++--+---++-+----+--+----+-++-++-+---+--+-+-+++-++-+--+++++-+++-++++++++-+--+--++---+--+-+-+++-++-+--++---+---+-----
++--+----+-+-+++-++------++++-+-+++-+++---++--------++---++++---+-+----++++++--+---++-+----+--+--+-++-++-+---+--+-+-+++-++-+--+++++-+++-+++++-++-+--+--++---+--+-+-+++-++-+--++---+---+-----+
+--+----+-+-+++-++------++++-+-+++-+++---++----++----++---++++---+-+----++++++--+---++-+----+---+-++-++-+---+--+-+-+++-++-+--+++++-+++-+++++--+-+--+--++---+--+-+-+++-++-+--++---+---+-----++
+-+----+-+-+++-++------++++-+-+++-+++---++----+--+----++---++++---+-+----++++++--+---++-+----+-+-++-++-+---+--+-+-+++-++-+--+++++-+++-+++++----+--+--++---+--+-+-+++-++-+--++---+---+-----+++
++----+-+-+++-++------++++-+-+++-+++---++----+----+----++---++++---+-+----++++++--+---++-+----+-++-++-+---+--+-+-+++-++-+--+++++-+++-+++++---++--+--++---+--+-+-+++-++-+--++---+---+-----+++-
+----+-+-+++-++------++++-+-+++-+++---++----+--++--+----++---++++---+-+----++++++--+---++-+----++-++-+---+--+-+-+++-++-+--+++++-+++-+++++---+---+--++---+--+-+-+++-++-+--++---+---+-----+++-+
+---+-+-+++-++------++++-+-+++-+++---++----+--+--+--+----++---++++---+-+----++++++--+---++-+---+-++-+---+--+-+-+++-++-+--+++++-+++-+++++---+-+-+--++---+--+-+-+++-++-+--++---+---+-----+++-+-
+--+-+-+++-++------++++-+-+++-+++---++----+--+----+--+----++---++++---+-+----++++++--+---++-+---++-+---+--+-+-+++-++-+--+++++-+++-+++++---+-+++--++---+--+-+-+++-++-+--++---+---+-----+++-+--
+-+-+-+++-++------++++-+-+++-+++---++----+--+------+--+----++---++++---+-+----++++++--+---++-+-++-+---+--+-+-+++-++-+--+++++-+++-+++++---+-++---++---+--+-+-+++-++-+--++---+---+-----+++-+--+
++-+-+++-++------++++-+-+++-+++---++----+--+--------+--+----++---++++---+-+----++++++--+---++-++-+---+--+-+-+++-++-+--+++++-+++-+++++---+-++-+-++---+--+-+-+++-++-+--++---+---+-----+++-+--+-
+-+-+++-++------++++-+-+++-+++---++----+--+----++----+--+----++---++++---+-+----++++++--+---++--+---+--+-+-+++-++-+--+++++-+++-+++++---+-++-++++---+--+-+-+++-++-+--++---+---+-----+++-+--+--
++-+++-++------++++-+-+++-+++---++----+--+----+--+----+--+----++---++++---+-+----++++++--+---+++---+--+-+-+++-++-+--+++++-+++-+++++---+-++-++-+---+--+-+-+++-++-+--++---+---+-----+++-+--+--+
+-++-++-+---+++++-+++-+++--++-+--+---+-+-++-+++-+++-++-+-+---+--+-++-----+---+-----+++-+--+--+-+-+----+--+----++---++++---+-+----++++++--+---+-+----+--+----++---+++-+++-+-++++------++-+++-+
+++-++-+---+++++-+++-+++--++-+--+---+-+-++-+++--++-++-+-+---+--+-++-----+---+-----+++-+--+--+-+++-+----+--+----++---++++---+-+----++++++--+---+----+--+----++---+++-+++-+-++++------++-+++-+-
++-++-+---+++++-+++-+++--++-+--+---+-+-++-+++--++-++-+-+---+--+-++-----+---+-----+++-+--+--+-++-++-+----+--+----++---++++---+-+----++++++--+------+--+----++---+++-+++-+-++++------++-+++-+-+
+-++-+---+++++-+++-+++--++-+--+---+-+-++-+++--++-++-+-+---+--+-++-----+---+-----+++-+--+--+-+++--++-+----+--+----++---++++---+-+----++++++--+----+--+----++---+++-+++-+-++++------++-+++-+-+-
+++-+---+++++-+++-+++--++-+--+---+-+-++-+++--++-++-+-+---+--+-++-----+---+-----+++-+--+--+-+++----++-+----+--+----++---++++---+-+----++++++--+--+--+----++---+++-+++-+-++++------++-+++-+-+--
++-+---+++++-+++-+++--++-+--+---+-+-++-+++--++-++-+-+---+--+-++-----+---+-----+++-+--+--+-+++-++---++-+----+--+----++---++++---+-+----++++++---+--+----++---+++-+++-+-++++------++-+++-+-+---
+-+---+++++-+++-+++--++-+--+---+-+-++-+++--++-++-+-+---+--+-++-----+---+-----+++-+--+--+-+++-++-+---++-+----+--+----++---++++---+-+----++++++-+--+----++---+++-+++-+-++++------++-+++-+-+----
++---+++++-+++-+++--++-+--+---+-+-++-+++--++-++-+-+---+--+-++-----+---+-----+++-+--+--+-+++-++---+---++-+----+--+----++---++++---+-+----++++++--+----++---+++-+++-+-++++------++-+++-+-+----+
+---+++++-+++-+++--++-+--+---+-+-++-+++--++-++-+-+---+--+-++-----+---+-----+++-+--+--+-+++-++-++--+---++-+----+--+----++---++++---+-+----+++++-+----++---+++-+++-+-++++------++-+++-+-+----+-
+--+++++-+++-+++--++-+--+---+-+-++-+++--++-++-+-+---+--+-++-----+---+-----+++-+--+--+-+++-++-+-++--+---++-+----+--+----++---++++---+-+----+++++----++---+++-+++-+-++++------++-+++-+-+----+--
+-+++++-+++-+++--++-+--+---+-+-++-+++--++-++-+-----+--+-++-----+---+-----+++-+--+--+-+++-++-+-++++--+---++-+----+--+----++---++++---+-+----+++----++---+++-+++-+-++++------++-+++-+-+----+--+
++++++-+++-+++--++-+--+---+-+-++-+++--++-++-+-----+--+-++-----+---+-----+++-+--+--+-+++-++-+-+-++++--+---++-+----+--+----++---++++---+-+----++---++---+++-+++-+-++++------++-+++-+-+----+--+-
+++++-+++-+++--++-+--+---+-+-++-+++--++-++-+---+-+--+-++-----+---+-----+++-+--+--+-+++-++-+-+--+++++--+---++-+----+--+----++---++++---+-+----+--++---+++-+++-+-++++------++-+++-+-+----+--+--
++++-+++-+++--++-+--+---+-+-++-+++--++-++-+---+++--+-++-----+---+-----+++-+--+--+-+++-++-+-+---++++++--+---++-+----+--+----++---++++---+-+-----++---+++-+++-+-++++------++-+++-+-+----+--+---
+++-+++-+++--++-+--+---+-+-++-+++--++-++-+---+++--+-++-----+---+-----+++-+--+--+-+++-++-+-+---+-++++++--+---++-+----+--+----++---++++---+-+---++---+++-+++-+-++++------++-+++-+-+----+--+----
++-+++-+++--++-+--+---+-+-++-+++--++-++-+---++++-+-++-----+---+-----+++-+--+--+-+++-++-+-+---+---++++++--+---++-+----+--+----++---++++---+-+--+---+++-+++-+-++++------++-+++-+-+----+--+----+
+-+++-+++--++-+--+---+-+-++-+++--++-++-+---++++++-++-----+---+-----+++-+--+--+-+++-++-+-+---+-----++++++--+---++-+----+--+----++---++++---+-+----+++-+++-+-++++------++-+++-+-+----+--+----++
++++-+++--++-+--+---+-+-++-+++--++-++-+---+++++--++-----+---+-----+++-+--+--+-+++-++-+-+---+--+----++++++--+---++-+----+--+----++---++++---+-+--+++-+++-+-++++------++-+++-+-+----+--+----++-
+++-+++--++-+--+---+-+-++-+++--++-++-+---+++++-+++-----+---+-----+++-+--+--+-+++-++-+-+---+--+-+----++++++--+---++-+----+--+----++---++++---+--+++-+++-+-++++------++-+++-+-+----+--+----++--
++-+++--++-+--+---+-+-++-+++--++-++-+---+++++-+++-----+---+-----+++-+--+--+-+++-++-+-+---+--+-+-+----++++++--+---++-+----+--+----++---++++---++++-+++-+-++++------++-+++-+-+----+--+----++---
+-+++--++-+--+---+-+-++-+++--++-++-+---+++++-+++-----+---+-----+++-+--+--+-+++-++-+-+---+--+-+++-+----++++++--+---++-+----+--+----++---++++---++-+++-+-++++------++-+++-+-+----+--+----++---+
++++--++-+--+---+-+-++-+++--++-++-+---+++++-+++-----+---+-----+++-+--+--+-+++-++-+-+---+--+-++--+-+----++++++--+---++-+----+--+----++---++++--+-+++-+-++++------++-+++-+-+----+--+----++---++
+++--++-+--+---+-+-++-+++--++-++-+---+++++-+++-+---+---+-----+++-+--+--+-+++-++-+-+---+--+-++----+-+----++++++--+---++-+----+--+----++---++++--+++-+-++++------++-+++-+-+----+--+----++---+++
++--++-+--+---+-+-++-+++--++-++-+---+++++-+++-++--+---+-----+++-+--+--+-+++-++-+-+---+--+-++------+-+----++++++--+---++-+----+--+----++---+++++++-+-++++------++-+++-+-+----+--+----++---+++-
+--++-+--+---+-+-++-+++--++-++-+---+++++-+++-+++-+---+-----+++-+--+--+-+++-++-+-+---+--+-++----+---+-+----++++++--+---++-+----+--+----++---+++++-+-++++------++-+++-+-+----+--+----++---+++-+
+-++-+--+---+-+-++-+++--++-++-+---+++++-+++-+++-+---+-----+++-+--+--+-+++-++-+-+---+--+-++-----++---+-+----++++++--+---++-+----+--+----++---+++-+-++++------++-+++-+-+----+--+----++---+++-++
+++-+--+---+-+-++-+++--++-++-+---+++++-+++-+++-----+-----+++-+--+--+-+++-++-+-+---+--+-++-----++++---+-+----++++++--+---++-+----+--+----++---+-+-++++------++-+++-+-+----+--+----++---+++-+++
++-+--+---+-+-++-+++--++-++-+---+++++-+++-+++--+--+-----+++-+--+--+-+++-++-+-+---+--+-++-----+-++++---+-+----++++++--+---++-+----+--+----++---+-++++------++-+++-+-+----+--+----++---+++-+++-
+-+--+---+-+-++-+++--++-++-+---+++++-+++-+++--++-+-----+++-+--+--+-+++-++-+-+---+--+-++-----+---++++---+-+----++++++--+---++-+----+--+----++---++++------++-+++-+-+----+--+----++---+++-+++-+
++--+---+-+-++-+++--++-++-+---+++++-+++-+++--++-+-----+++-+--+--+-+++-++-+-+---+--+-++-----+-----++++---+-+----++++++--+---++-+----+--+----++-++++------++-+++-+-+----+--+----++---+++-+++-+-
+--+---+-+-++-+++--++-++-+---+++++-+++-+++--++-+-----+++-+--+--+-+++-++-+-+---+--+-++-----+---+---++++---+-+----++++++--+---++-+----+--+----+++++------++-+++-+-+----+--+----++---+++-+++-+-+
+-+---+-+-++-+++--++-++-+---+++++-+++-+++--++-+-----+++-+--+--+-+++-++-+-+---+--+-++-----+---+-+---++++---+-+----++++++--+---++-+----+--+----+++------++-+++-+-+----+--+----++---+++-+++-+-++
++---+-+-++-+++--++-++-+---+++++-+++-+++--++-+-----+++-+--+--+-+++-++-+-+---+--+-++-----+---+--++---++++---+-+----++++++--+---++-+----+--+----+------++-+++-+-+----+--+----++---+++-+++-+-+++
+---+-+-++-+++--++-++-+---+++++-+++-+++--++-+--+--+++-+--+--+-+++-++-+-+---+--+-++-----+---+----++---++++---+-+----++++++--+---++-+----+--+---------++-+++-+-+----+--+----++---+++-+++-+-++++
+--+-+-++-+++--++-++-+---+++++-+++-+++--++-+--+--+++-+--+--+-+++-++-+-+---+--+-++-----+---+------++---++++---+-+----++++++--+---++-+----+--+-------++-+++-+-+----+--+----++---+++-+++-+-++++-
+-+-+-++-+++--++-++-+---+++++-+++-+++--++-+--+--+++-+--+--+-+++-++-+-+---+--+-++-----+---+--------++---++++---+-+----++++++--+---++-+----+--+-----++-+++-+-+----+--+----++---+++-+++-+-++++--
++-+-++-+++--++-++-+---+++++-+++-+++--++-+--+---++-+--+--+-+++-++-+-+---+--+-++-----+---+-----+----++---++++---+-+----++++++--+---++-+----+--+---++-+++-+-+----+--+----++---+++-+++-+-++++---
+-+-++-+++--++-++-+---+++++-+++-+++--++-+--+---++-+--+--+-+++-++-+-+---+--+-++-----+---+-----+++----++---++++---+-+----++++++--+---++-+----+----++-+++-+-+----+--+----++---+++-+++-+-++++----
++-++-+++--++-++-+---+++++-+++-+++--++-+--+---+--+--+--+-+++-++-+-+---+--+-++-----+---+-----+++-+----++---++++---+-+----++++++--+---++-+----+--++-+++-+-+----+--+----++---+++-+++-+-++++-----
+-++-+++--++-++-+---+++++-+++-+++--++-+--+---+-++--+--+-+++-++-+-+---+--+-++-----+---+-----+++---+----++---++++---+-+----++++++--+---++-+----+++-+++-+-+----+--+----++---+++-+++-+-++++------
+++-+++--++-++-+---+++++-+++-+++--++-+--+---+-+---+--+-+++-++-+-+---+--+-++-----+---+-----+++-++--+----++---++++---+-+----++++++--+---++-+----+-+++-+-+----+--+----++---+++-+++-+-++++------+
++-+++--++-++-+---+++++-+++-+++--++-+--+---+-+-+-+--+-+++-++-+-+---+--+-++-----+---+-----+++-+--+--+----++---++++---+-+----++++++--+---++-+----+++-+-+----+--+----++---+++-+++-+-++++------++
+-+++--++-++-+---+++++-+++-+++--++-+--+---+-+-+++--+-+++-++-+-+---+--+-++-----+---+-----+++-+----+--+----++---++++---+-+----++++++--+---++-+--+++-+-+----+--+----++---+++-+++-+-++++------++-
++++--++-++-+---+++++-+++-+++--++-+--+---+-+-++---+-+++-++-+-+---+--+-++-----+---+-----+++-+--+---+--+----++---++++---+-+----++++++--+---++-+-++-+-+----+--+----++---+++-+++-+-++++------++-+
+++--++-++-+---+++++-+++-+++--++-+--+---+-+-++-+-+-+++-++-+-+---+--+-++-----+---+-----+++-+--+-----+--+----++---++++---+-+----++++++--+---++-++-+-+----+--+----++---+++-+++-+-++++------++-++
++--++-++-+---+++++-+++-+++--++-+--+---+-+-++-+++-+++-++-+-+---+--+-++-----+---+-----+++-+--+--+----+--+----++---++++---+-+----++++++--+---++--+-+----+--+----++---+++-+++-+-++++------++-+++
+--++-++-+---+++++-+++-+++--++-+--+---+-+-++-+++-+++-++-+-+---+--+-++-----+---+-----+++-+--+--+-+----+--+----++---++++---+-+----++++++--+---+++-+----+--+----++---+++-+++-+-++++------++-+++-
+-++-++-+---+++++-+++-+++++--+-++-+++-+-+--+---++++-++-+-+---+--+-++--+++-+++-+++++---+-++-++--+-++++-++-++++--+++---+---+-+----++++++--+---+-+-+----+--+----++---++++---+-+----++++++--+---+
+++-++-+---+++++-+++-+++++--+-++-+++-+-+--+---+-++-++-+-+---+--+-++--+++-+++-+++++---+-++-++--+-++++-++-++++--+++---+---+-+----++++++--+---+-+++-+----+--+----++---++++---+-+----++++++--+---
++-++-+---+++++-+++-+++++--+-++-+++-+-+--+---+-++-++-+-+---+--+-++--+++-+++-+++++---+-++-++--++++++-++-++++--+++---+---+-+----++++++--+---+-+--++-+----+--+----++---++++---+-+----++++++--+--
+-++-+---+++++-+++-+++++--+-++-+++-+-+--+---+-++-++-+-+---+--+-++--+++-+++-+++++---+-++-++--++++++-++-++++--+++---+---+-+----++++++--+---+-+-+--++-+----+--+----++---++++---+-+----++++++--+-
+++-+---+++++-+++-+++++--+-++-+++-+-+--+---+-++-++-+-+---+--+-++--+++-+++-+++++---+-++-++--+++-++-++-++++--+++---+---+-+----++++++--+---+-+-++---++-+----+--+----++---++++---+-+----++++++--+
++-+---+++++-+++-+++++--+-++-+++-+-+--+---+-++-++-+-+---+--+-++--+++-+++-+++++---+-++-++--+++-++-++-++++--+++---+---+-+----++++++--+---+-+-++++---++-+----+--+----++---++++---+-+----++++++--
+-+---+++++-+++-+++++--+-++-+++-+-+--+---+-++-++-+-+---+--+-++--+++-+++-+++++---+-++-++--+++-++-++-++++--+++---+---+-+----++++++--+---+-+-++++-+---++-+----+--+----++---++++---+-+----++++++-
++---+++++-+++-+++++--+-++-+++-+-+--+---+-++-++-+-+---+--+-++--+++-+++-+++++---+-++-++--+++-++-++-++++--+++---+---+-+----++++++--+---+-+-++++---+---++-+----+--+----++---++++---+-+----++++++
+---+++++-+++-+++++--+-++-+++-+-+--+---+-++-++-+-+---+--+-++--+++-+++-+++++---+-++-++--+++-++-++-++++--+++---+---+-+----++++++--+---+-+-++++-++--+---++-+----+--+----++---++++---+-+----+++++
+--+++++-+++-+++++--+-++-+++-+-+--+---+-++-++-+-+---+--+-++--+++-+++-+++++---+-++-++--+++-++-+--++++--+++---+---+-+----++++++--+---+-+-++++-++++--+---++-+----+--+----++---++++---+-+----++++
+-+++++-+++-+++++--+-++-+++-+-+--+---+-++-++-+-----+--+-++--+++-+++-+++++---+-++-++--+++-++-+-+++++--+++---+---+-+----++++++--+---+-+-++++-++-+++--+---++-+----+--+----++---++++---+-+----+++
++++++-+++-+++++--+-++-+++-+-+--+---+-++-++-+-----+--+-++--+++-+++-+++++---+-++-++--+++-++-+-+-+++--+++---+---+-+----++++++--+---+-+-++++-++-+++++--+---++-+----+--+----++---++++---+-+----++
+++++-+++-+++++--+-++-+++-+-+--+---+-++-++-+---+-+--+-++--+++-+++-+++++---+-++-++--+++-++-+-+--++--+++---+---+-+----++++++--+---+-+-++++-++-+++++++--+---++-+----+--+----++---++++---+-+----+
++++-+++-+++++--+-++-+++-+-+--+---+-++-++-+---+++--+-++--+++-+++-+++++---+-++-++--+++-++-+-+---+--+++---+---+-+----++++++--+---+-+-++++-++-+++++++++--+---++-+----+--+----++---++++---+-+----
+++-+++-+++++--+-++-+++-+-+--+---+-++-++-+---+++--+-++--+++-+++-+++++---+-++-++--+++-++-+-+---+--+++---+---+-+----++++++--+---+-+-++++-++-++++-++++++--+---++-+----+--+----++---++++---+-+---
++-+++-+++++--+-++-+++-+-+--+---+-++-++-+---++++-+-++--+++-+++-+++++---+-++-++--+++-++-+-+---+--+++---+---+-+----++++++--+---+-+-++++-++-++++---++++++--+---++-+----+--+----++---++++---+-+--
+-+++-+++++--+-++-+++-+-+--+---+-++-++-+---++++++-++--+++-+++-+++++---+-++-++--+++-++-+-+---+--+++---+---+-+----++++++--+---+-+-++++-++-++++-----++++++--+---++-+----+--+----++---++++---+-+-
++++-+++++--+-++-+++-+-+--+---+-++-++-+---+++++--++--+++-+++-+++++---+-++-++--+++-++-+-+---+--+++---+---+-+----++++++--+---+-+-++++-++-++++--+----++++++--+---++-+----+--+----++---++++---+-+
+++-+++++--+-++-+++-+-+--+---+-++-++-+---+++++-+++--+++-+++-+++++---+-++-++--+++-++-+-+---+--+-+---+---+-+----++++++--+---+-+-++++-++-++++--+++----++++++--+---++-+----+--+----++---++++---+-
++-+++++--+-++-+++-+-+--+---+-++-++-+---+++++-+++--+++-+++-+++++---+-++-++--+++-++-+-+---+--+-+---+---+-+----++++++--+---+-+-++++-++-++++--+++-+----++++++--+---++-+----+--+----++---++++---+
+-+++++--+-++-+++-+-+--+---+-++-++-+---+++++-+++--+++-+++-+++++---+-++-++--+++-++-+-+---+--+-++--+---+-+----++++++--+---+-+-++++-++-++++--+++-+-+----++++++--+---++-+----+--+----++---++++---
++++++--+-++-+++-+-+--+---+-++-++-+---+++++-+++--+++-+++-+++++---+-++-++--+++-++-+-+---+--+-++--+---+-+----++++++--+---+-+-++++-++-++++--+++---+-+----++++++--+---++-+----+--+----++---++++--
+++++--+-++-+++-+-+--+---+-++-++-+---+++++-+++-++++-+++-+++++---+-++-++--+++-++-+-+---+--+-++--+---+-+----++++++--+---+-+-++++-++-++++--+++-----+-+----++++++--+---++-+----+--+----++---++++-
++++--+-++-+++-+-+--+---+-++-++-+---+++++-+++-++++-+++-+++++---+-++-++--+++-++-+-+---+--+-++--+---+-+----++++++--+---+-+-++++-++-++++--+++---+---+-+----++++++--+---++-+----+--+----++---++++
+++--+-++-+++-+-+--+---+-++-++-+---+++++-+++-++++-+++-+++++---+-++-++--+++-++-+-+---+--+-++--++--+-+----++++++--+---+-+-++++-++-++++--+++---+-+---+-+----++++++--+---++-+----+--+----++---+++
++--+-++-+++-+-+--+---+-++-++-+---+++++-+++-++++-+++-+++++---+-++-++--+++-++-+-+---+--+-++--+++-+-+----++++++--+---+-+-++++-++-++++--+++---+--++---+-+----++++++--+---++-+----+--+----++---++
+--+-++-+++-+-+--+---+-++-++-+---+++++-+++-++++++++-+++++---+-++-++--+++-++-+-+---+--+-++--+++-+-+----++++++--+---+-+-++++-++-++++--+++---+---+++---+-+----++++++--+---++-+----+--+----++---+
+-+-++-+++-+-+--+---+-++-++-+---+++++-+++-+++++-++-+++++---+-++-++--+++-++-+-+---+--+-++--+++-+-+----++++++--+---+-+-++++-++-++++--+++---+---+++++---+-+----++++++--+---++-+----+--+----++---
++-++-+++-+-+--+---+-++-++-+---+++++-+++-+++++--+-+++++---+-++-++--+++-++-+-+---+--+-++--+++-+++----++++++--+---+-+-++++-++-++++--+++---+---+--++++---+-+----++++++--+---++-+----+--+----++--
+-++-+++-+-+--+---+-++-++-+---+++++-+++-+++++--+-+++++---+-++-++--+++-++-+-+---+--+-++--+++-+++----++++++--+---+-+-++++-++-++++--+++---+---+-+--++++---+-+----++++++--+---++-+----+--+----++-
+++-+++-+-+--+---+-++-++-+---+++++-+++-+++++--+-+++++---+-++-++--+++-++-+-+---+--+-++--+++-+++----++++++--+---+-+-++++-++-++++--+++---+---+-+----++++---+-+----++++++--+---++-+----+--+----++
++-+++-+-+--+---+-++-++-+---+++++-+++-+++++--+-+++++---+-++-++--+++-++-+-+---+--+-++--+++-+++-+--++++++--+---+-+-++++-++-++++--+++---+---+-+--+---++++---+-+----++++++--+---++-+----+--+----+
+-+++-+-+--+---+-++-++-+---+++++-+++-+++++--+-+++++---+-++-++--+++-++-+-+---+--+-++--+++-+++-++-++++++--+---+-+-++++-++-++++--+++---+---+-+---++---++++---+-+----++++++--+---++-+----+--+----
++++-+-+--+---+-++-++-+---+++++-+++-+++++--+-++-++---+-++-++--+++-++-+-+---+--+-++--+++-+++-+++++++++--+---+-+-++++-++-++++--+++---+---+-+-----++---++++---+-+----++++++--+---++-+----+--+---
+++-+-+--+---+-++-++-+---+++++-+++-+++++--+-++-++---+-++-++--+++-++-+-+---+--+-++--+++-+++-+++++++++--+---+-+-++++-++-++++--+++---+---+-+----+--++---++++---+-+----++++++--+---++-+----+--+--
++-+-+--+---+-++-++-+---+++++-+++-+++++--+-++-++---+-++-++--+++-++-+-+---+--+-++--+++-+++-+++++++++--+---+-+-++++-++-++++--+++---+---+-+----++---++---++++---+-+----++++++--+---++-+----+--+-
+-+-+--+---+-++-++-+---+++++-+++-+++++--+-++-+++--+-++-++--+++-++-+-+---+--+-++--+++-+++-+++++-+++--+---+-+-++++-++-++++--+++---+---+-+----+++----++---++++---+-+----++++++--+---++-+----+--+
++-+--+---+-++-++-+---+++++-+++-+++++--+-++-+++--+-++-++--+++-++-+-+---+--+-++--+++-+++-+++++--++--+---+-+-++++-++-++++--+++---+---+-+----+++++----++---++++---+-+----++++++--+---++-+----+--
+-+--+---+-++-++-+---+++++-+++-+++++--+-++-+++-++-++-++--+++-++-+-+---+--+-++--+++-+++-+++++---+--+---+-+-++++-++-++++--+++---+---+-+----+++++-+----++---++++---+-+----++++++--+---++-+----+-
++--+---+-++-++-+---+++++-+++-+++++--+-++-+++-+--++-++--+++-++-+-+---+--+-++--+++-+++-+++++---+--+---+-+-++++-++-++++--+++---+---+-+----++++++--+----++---++++---+-+----++++++--+---++-+----+
+--+---+-++-++-+---+++++-+++-+++++--+-++-+++-+-+++-++--+++-++-+-+---+--+-++--+++-+++-+++++---+--+---+-+-++++-++-++++--+++---+---+-+----++++++-+--+----++---++++---+-+----++++++--+---++-+----
+-+---+-++-++-+---+++++-+++-+++++--+-++-+++-+-+-+-++--+++-++-+-+---+--+-++--+++-+++-+++++---+-++---+-+-++++-++-++++--+++---+---+-+----++++++---+--+----++---++++---+-+----++++++--+---++-+---
++---+-++-++-+---+++++-+++-+++++--+-++-+++-+-+---++--+++-++-+-+---+--+-++--+++-+++-+++++---+-++---+-+-++++-++-++++--+++---+---+-+----++++++--+--+--+----++---++++---+-+----++++++--+---++-+--
+---+-++-++-+---+++++-+++-+++++--+-++-+++-+-+--+++--+++-++-+-+---+--+-++--+++-+++-+++++---+-++---+-+-++++-++-++++--+++---+---+-+----++++++--+----+--+----++---++++---+-+----++++++--+---++-+-
+--+-++-++-+---+++++-+++-+++++--+-++-+++-+-+--+-+--+++-++-+-+---+--+-++--+++-+++-+++++---+-++-+-+-+-++++-++-++++--+++---+---+-+----++++++--+------+--+----++---++++---+-+----++++++--+---++-+
+-+-++-++-+---+++++-+++-+++++--+-++-+++-+-+--+----+++-++-+-+---+--+-++--+++-+++-+++++---+-++-+++-+-++++-++-++++--+++---+---+-+----++++++--+---+----+--+----++---++++---+-+----++++++--+---++-
++-++-++-+---+++++-+++-+++++--+-++-+++-+-+--+----+++-++-+-+---+--+-++--+++-+++-+++++---+-++-++--+-++++-++-++++--+++---+---+-+----++++++--+---+-+----+--+----++---++++---+-+----++++++--+---++
\ No newline at end of file
diff --git a/gptqmodel/exllamav3/util/hadamard_data/hadamard_236.txt b/gptqmodel/exllamav3/util/hadamard_data/hadamard_236.txt
new file mode 100644
index 000000000..e065f73a5
--- /dev/null
+++ b/gptqmodel/exllamav3/util/hadamard_data/hadamard_236.txt
@@ -0,0 +1,236 @@
++++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++---++-+---+-++---+--++++-++++-++-+---+-++++--++--+-++++-++++---++++-++++--+---++-+---+-+++-+-+++-+----++--++-+----+-----+--+-+++-+--+++-++----+----+-+-+---+-++++--++--+-++++-++++-
+-+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++-++-+---+-++---+--++++-++++-++-+---+-++++--++--+-++++-++++---++++-++++--+---++-+---+-+++-+-+++-+----++--++-+----+--------+-+++-+--+++-++----+----+-+-+---+-++++--++--+-++++-++++-+
+--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-+++++-+---+-++---+--++++-++++-++-+---+-++++--++--+-++++-++++--+++++-++++--+---++-+---+-+++-+-+++-+----++--++-+----+--------+-+++-+--+++-++----+----+-+-+---+-++++--++--+-++++-++++-+-
++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-+++-+---+-++---+--++++-++++-++-+---+-++++--++--+-++++-++++--+++++-++++--+---++-+---+-+++-+-+++-+----++--++-+----+-------++-+++-+--+++-++----+----+-+-+---+-++++--++--+-++++-++++-+--
+++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-+++---+-++---+--++++-++++-++-+---+-++++--++--+-++++-++++--++-++-++++--+---++-+---+-+++-+-+++-+----++--++-+----+-------++-+++-+--+++-++----+----+-+-+---+-++++--++--+-++++-++++-+--+
++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-+---+-++---+--++++-++++-++-+---+-++++--++--+-++++-++++--++-++-++++--+---++-+---+-+++-+-+++-+----++--++-+----+-------++++++-+--+++-++----+----+-+-+---+-++++--++--+-++++-++++-+--+-
+++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++---+-++---+--++++-++++-++-+---+-++++--++--+-++++-++++--++-+--++++--+---++-+---+-+++-+-+++-+----++--++-+----+-------++++++-+--+++-++----+----+-+-+---+-++++--++--+-++++-++++-+--+-+
+-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-+-++---+--++++-++++-++-+---+-++++--++--+-++++-++++--++-+--++++--+---++-+---+-+++-+-+++-+----++--++-+----+-------++++-+-+--+++-++----+----+-+-+---+-++++--++--+-++++-++++-+--+-++
++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++---+--++++-++++-++-+---+-++++--++--+-++++-++++--++-+---+++--+---++-+---+-+++-+-+++-+----++--++-+----+-------++++-+-+--+++-++----+----+-+-+---+-++++--++--+-++++-++++-+--+-+++
+++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++-++---+--++++-++++-++-+---+-++++--++--+-++++-++++--++-+---+++--+---++-+---+-+++-+-+++-+----++--++-+----+-------++++-+++--+++-++----+----+-+-+---+-++++--++--+-++++-++++-+--+-+++-
++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--+++---+--++++-++++-++-+---+-++++--++--+-++++-++++--++-+---+-+--+---++-+---+-+++-+-+++-+----++--++-+----+-------++++-+++--+++-++----+----+-+-+---+-++++--++--+-++++-++++-+--+-+++-+
+++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--+---+--++++-++++-++-+---+-++++--++--+-++++-++++--++-+---+-+--+---++-+---+-+++-+-+++-+----++--++-+----+-------++++-++++-+++-++----+----+-+-+---+-++++--++--+-++++-++++-+--+-+++-+-
+-++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+----+--++++-++++-++-+---+-++++--++--+-++++-++++--++-+---+-++-+---++-+---+-+++-+-+++-+----++--++-+----+-------++++-++++-+++-++----+----+-+-+---+-++++--++--+-++++-++++-+--+-+++-+--
+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--+--++++-++++-++-+---+-++++--++--+-++++-++++--++-+---+-++-+---++-+---+-+++-+-+++-+----++--++-+----+-------++++-++++--++-++----+----+-+-+---+-++++--++--+-++++-++++-+--+-+++-+--+
++--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++----+--++++-++++-++-+---+-++++--++--+-++++-++++--++-+---+-++-----++-+---+-+++-+-+++-+----++--++-+----+-------++++-++++--++-++----+----+-+-+---+-++++--++--+-++++-++++-+--+-+++-+--++
+-+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++--+--++++-++++-++-+---+-++++--++--+-++++-++++--++-+---+-++-----++-+---+-+++-+-+++-+----++--++-+----+-------++++-++++--+--++----+----+-+-+---+-++++--++--+-++++-++++-+--+-+++-+--+++
+--+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---++++-++++-++-+---+-++++--++--+-++++-++++--++-+---+-++---+-++-+---+-+++-+-+++-+----++--++-+----+-------++++-++++--+--++----+----+-+-+---+-++++--++--+-++++-++++-+--+-+++-+--+++-
+---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++-++++-++++-++-+---+-++++--++--+-++++-++++--++-+---+-++---+-++-+---+-+++-+-+++-+----++--++-+----+-------++++-++++--+---+----+----+-+-+---+-++++--++--+-++++-++++-+--+-+++-+--+++-+
++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-+++++-++++-++-+---+-++++--++--+-++++-++++--++-+---+-++---+--+-+---+-+++-+-+++-+----++--++-+----+-------++++-++++--+---+----+----+-+-+---+-++++--++--+-++++-++++-+--+-+++-+--+++-++
+++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-+++-++++-++-+---+-++++--++--+-++++-++++--++-+---+-++---+--+-+---+-+++-+-+++-+----++--++-+----+-------++++-++++--+---++---+----+-+-+---+-++++--++--+-++++-++++-+--+-+++-+--+++-++-
+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+++-++++-++-+---+-++++--++--+-++++-++++--++-+---+-++---+--+++---+-+++-+-+++-+----++--++-+----+-------++++-++++--+---++---+----+-+-+---+-++++--++--+-++++-++++-+--+-+++-+--+++-++--
++-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++++-++-+---+-++++--++--+-++++-++++--++-+---+-++---+--+++---+-+++-+-+++-+----++--++-+----+-------++++-++++--+---++-+-+----+-+-+---+-++++--++--+-++++-++++-+--+-+++-+--+++-++---
+-+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---++++-++-+---+-++++--++--+-++++-++++--++-+---+-++---+--++++--+-+++-+-+++-+----++--++-+----+-------++++-++++--+---++-+-+----+-+-+---+-++++--++--+-++++-++++-+--+-+++-+--+++-++----
+--+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+-++++-++-+---+-++++--++--+-++++-++++--++-+---+-++---+--++++--+-+++-+-+++-+----++--++-+----+-------++++-++++--+---++-+------+-+-+---+-++++--++--+-++++-++++-+--+-+++-+--+++-++----+
+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-++++-++-+---+-++++--++--+-++++-++++--++-+---+-++---+--++++-++-+++-+-+++-+----++--++-+----+-------++++-++++--+---++-+------+-+-+---+-++++--++--+-++++-++++-+--+-+++-+--+++-++----+-
++---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-++-++-+---+-++++--++--+-++++-++++--++-+---+-++---+--++++-++-+++-+-+++-+----++--++-+----+-------++++-++++--+---++-+---+--+-+-+---+-++++--++--+-++++-++++-+--+-+++-+--+++-++----+--
+-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-+++++-++-+---+-++++--++--+-++++-++++--++-+---+-++---+--++++-++++++-+-+++-+----++--++-+----+-------++++-++++--+---++-+---+--+-+-+---+-++++--++--+-++++-++++-+--+-+++-+--+++-++----+---
++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-+++-++-+---+-++++--++--+-++++-++++--++-+---+-++---+--++++-++++++-+-+++-+----++--++-+----+-------++++-++++--+---++-+---+-++-+-+---+-++++--++--+-++++-++++-+--+-+++-+--+++-++----+----
+++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++++--++--+-++++-++++--++-+---+-++---+--++++-++++-+-+-+++-+----++--++-+----+-------++++-++++--+---++-+---+-++-+-+---+-++++--++--+-++++-++++-+--+-+++-+--+++-++----+----+
++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++-+---+-++++--++--+-++++-++++--++-+---+-++---+--++++-++++-+-+-+++-+----++--++-+----+-------++++-++++--+---++-+---+-++++-+---+-++++--++--+-++++-++++-+--+-+++-+--+++-++----+----+-
+++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+--+---+-++++--++--+-++++-++++--++-+---+-++---+--++++-++++-+++-+++-+----++--++-+----+-------++++-++++--+---++-+---+-+++--+---+-++++--++--+-++++-++++-+--+-+++-+--+++-++----+----+-+
+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---++---+-++++--++--+-++++-++++--++-+---+-++---+--++++-++++-++--+++-+----++--++-+----+-------++++-++++--+---++-+---+-+++-++---+-++++--++--+-++++-++++-+--+-+++-+--+++-++----+----+-+-
++-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+------+-++++--++--+-++++-++++--++-+---+-++---+--++++-++++-++-++++-+----++--++-+----+-------++++-++++--+---++-+---+-+++-+----+-++++--++--+-++++-++++-+--+-+++-+--+++-++----+----+-+-+
+-+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+----+-++++--++--+-++++-++++--++-+---+-++---+--++++-++++-++-+-++-+----++--++-+----+-------++++-++++--+---++-+---+-+++-+-+--+-++++--++--+-++++-++++-+--+-+++-+--+++-++----+----+-+-+-
+--+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+--+-++++--++--+-++++-++++--++-+---+-++---+--++++-++++-++-+--+-+----++--++-+----+-------++++-++++--+---++-+---+-+++-+-++-+-++++--++--+-++++-++++-+--+-+++-+--+++-++----+----+-+-+--
+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-++-++++--++--+-++++-++++--++-+---+-++---+--++++-++++-++-+----+----++--++-+----+-------++++-++++--+---++-+---+-+++-+-++++-++++--++--+-++++-++++-+--+-+++-+--+++-++----+----+-+-+---
++---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++--++++--++--+-++++-++++--++-+---+-++---+--++++-++++-++-+---++----++--++-+----+-------++++-++++--+---++-+---+-+++-+-+++--++++--++--+-++++-++++-+--+-+++-+--+++-++----+----+-+-+---+
+-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++++++--++--+-++++-++++--++-+---+-++---+--++++-++++-++-+---+-----++--++-+----+-------++++-++++--+---++-+---+-+++-+-+++-+++++--++--+-++++-++++-+--+-+++-+--+++-++----+----+-+-+---+-
++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++++--++--+-++++-++++--++-+---+-++---+--++++-++++-++-+---+-+---++--++-+----+-------++++-++++--+---++-+---+-+++-+-+++-+-+++--++--+-++++-++++-+--+-+++-+--+++-++----+----+-+-+---+-+
+++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++--++--+-++++-++++--++-+---+-++---+--++++-++++-++-+---+-++--++--++-+----+-------++++-++++--+---++-+---+-+++-+-+++-+--++--++--+-++++-++++-+--+-+++-+--+++-++----+----+-+-+---+-++
++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++--++--+-++++-++++--++-+---+-++---+--++++-++++-++-+---+-+++-++--++-+----+-------++++-++++--+---++-+---+-+++-+-+++-+---+--++--+-++++-++++-+--+-+++-+--+++-++----+----+-+-+---+-+++
+++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++----++--+-++++-++++--++-+---+-++---+--++++-++++-++-+---+-++++++--++-+----+-------++++-++++--+---++-+---+-+++-+-+++-+------++--+-++++-++++-+--+-+++-+--+++-++----+----+-+-+---+-++++
+-++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++--+-++++-++++--++-+---+-++---+--++++-++++-++-+---+-++++-+--++-+----+-------++++-++++--+---++-+---+-+++-+-+++-+----+-++--+-++++-++++-+--+-+++-+--+++-++----+----+-+-+---+-++++-
+--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++++--+-++++-++++--++-+---+-++---+--++++-++++-++-+---+-++++----++-+----+-------++++-++++--+---++-+---+-+++-+-+++-+----++++--+-++++-++++-+--+-+++-+--+++-++----+----+-+-+---+-++++--
++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--+-++++-++++--++-+---+-++---+--++++-++++-++-+---+-++++--+-++-+----+-------++++-++++--+---++-+---+-+++-+-+++-+----++-+--+-++++-++++-+--+-+++-+--+++-++----+----+-+-+---+-++++--+
+++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+----+-++++-++++--++-+---+-++---+--++++-++++-++-+---+-++++--++++-+----+-------++++-++++--+---++-+---+-+++-+-+++-+----++----+-++++-++++-+--+-+++-+--+++-++----+----+-+-+---+-++++--++
+-++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--+-++++-++++--++-+---+-++---+--++++-++++-++-+---+-++++--++-+-+----+-------++++-++++--+---++-+---+-+++-+-+++-+----++--+-+-++++-++++-+--+-+++-+--+++-++----+----+-+-+---+-++++--++-
+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-++-++++-++++--++-+---+-++---+--++++-++++-++-+---+-++++--++---+----+-------++++-++++--+---++-+---+-+++-+-+++-+----++--+++-++++-++++-+--+-+++-+--+++-++----+----+-+-+---+-++++--++--
++--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++--++++-++++--++-+---+-++---+--++++-++++-++-+---+-++++--++--++----+-------++++-++++--+---++-+---+-+++-+-+++-+----++--++--++++-++++-+--+-+++-+--+++-++----+----+-+-+---+-++++--++--+
+-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++++++-++++--++-+---+-++---+--++++-++++-++-+---+-++++--++--+-----+-------++++-++++--+---++-+---+-+++-+-+++-+----++--++-+++++-++++-+--+-+++-+--+++-++----+----+-+-+---+-++++--++--+-
++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++++-++++--++-+---+-++---+--++++-++++-++-+---+-++++--++--+-+---+-------++++-++++--+---++-+---+-+++-+-+++-+----++--++-+-+++-++++-+--+-+++-+--+++-++----+----+-+-+---+-++++--++--+-+
+++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-++++--++-+---+-++---+--++++-++++-++-+---+-++++--++--+-++--+-------++++-++++--+---++-+---+-+++-+-+++-+----++--++-+--++-++++-+--+-+++-+--+++-++----+----+-+-+---+-++++--++--+-++
++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++-++++--++-+---+-++---+--++++-++++-++-+---+-++++--++--+-+++-+-------++++-++++--+---++-+---+-+++-+-+++-+----++--++-+---+-++++-+--+-+++-+--+++-++----+----+-+-+---+-++++--++--+-+++
+++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++--++++--++-+---+-++---+--++++-++++-++-+---+-++++--++--+-+++++-------++++-++++--+---++-+---+-+++-+-+++-+----++--++-+-----++++-+--+-+++-+--+++-++----+----+-+-+---+-++++--++--+-++++
+-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++++++--++-+---+-++---+--++++-++++-++-+---+-++++--++--+-++++--------++++-++++--+---++-+---+-+++-+-+++-+----++--++-+----+++++-+--+-+++-+--+++-++----+----+-+-+---+-++++--++--+-++++-
++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++++--++-+---+-++---+--++++-++++-++-+---+-++++--++--+-++++-+------++++-++++--+---++-+---+-+++-+-+++-+----++--++-+----+-+++-+--+-+++-+--+++-++----+----+-+-+---+-++++--++--+-++++-+
+++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++--++-+---+-++---+--++++-++++-++-+---+-++++--++--+-++++-++-----++++-++++--+---++-+---+-+++-+-+++-+----++--++-+----+--++-+--+-+++-+--+++-++----+----+-+-+---+-++++--++--+-++++-++
++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++--++-+---+-++---+--++++-++++-++-+---+-++++--++--+-++++-+++----++++-++++--+---++-+---+-+++-+-+++-+----++--++-+----+---+-+--+-+++-+--+++-++----+----+-+-+---+-++++--++--+-++++-+++
+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+--++-+---+-++---+--++++-++++-++-+---+-++++--++--+-++++-++++---++++-++++--+---++-+---+-+++-+-+++-+----++--++-+----+-----+--+-+++-+--+++-++----+----+-+-+---+-++++--++--+-++++-++++
++--+-+++-+--+++-++----+----+--+-+++-+----++--++-+----+----++++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++------+----+-++--++----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++---
+--+-+++-+--+++-++----+----+--+-+++-+----++--++-+----+----++-+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++----+----+-++--++----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++----
+-+-+++-+--+++-++----+----+--+-+++-+----++--++-+----+----++---+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+----+-++--++----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-----
++-+++-+--+++-++----+----+--+-+++-+----++--++-+----+----++--+--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-+++-+----+-++--++----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++------
+-+++-+--+++-++----+----+--+-+++-+----++--++-+----+----++--+++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-+++----+-++--++----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------
++++-+--+++-++----+----+--+-+++-+----++--++-+----+----++--+-+++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-+----+-++--++----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+
+++-+--+++-++----+----+--+-+++-+----++--++-+----+----++--+-+++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++----+-++--++----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+-
++-+--+++-++----+----+--+-+++-+----++--++-+----+----++--+-++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++--+-++--++----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+--
+-+--+++-++----+----+--+-+++-+----++--++-+----+----++--+-++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--+++-+-++--++----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+---
++--+++-++----+----+--+-+++-+----++--++-+----+----++--+-+++-++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--+++-++--++----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----
+--+++-++----+----+--+-+++-+----++--++-+----+----++--+-+++-++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--+-++--++----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+
+-+++-++----+----+--+-+++-+----++--++-+----+----++--+-+++-+-++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++--++----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-
++++-++----+----+--+-+++-+----++--++-+----+----++--+-+++-+---++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+-+--++----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-+
+++-++----+----+--+-+++-+----++--++-+----+----++--+-+++-+--+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++
++-++----+----+--+-+++-+----++--++-+----+----++--+-+++-+--+++--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++----++----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++-
+-++----+----+--+-+++-+----++--++-+----+----++--+-+++-+--+++-+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++--++----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++--
+++----+----+--+-+++-+----++--++-+----+----++--+-+++-+--+++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++-+----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++--+
++----+----+--+-+++-+----++--++-+----+----++--+-+++-+--+++-+---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++----+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++--++
+----+----+--+-+++-+----++--++-+----+----++--+-+++-+--+++-+++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-+---+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++--++-
+---+----+--+-+++-+----++--++-+----+----++--+-+++-+--+++-++-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+---+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++--++--
+--+----+--+-+++-+----++--++-+----+----++--+-+++-+--+++-++---++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++--++---
+-+----+--+-+++-+----++--++-+----+----++--+-+++-+--+++-++---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++--++----
++----+--+-+++-+----++--++-+----+----++--+-+++-+--+++-++-----+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++--++----+
+----+--+-+++-+----++--++-+----+----++--+-+++-+--+++-++----+--+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++--++----+-
+---+--+-+++-+----++--++-+----+----++--+-+++-+--+++-++----+----+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++--++----+-+
+--+--+-+++-+----++--++-+----+----++--+-+++-+--+++-++----+--+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++--++----+-++
+-+--+-+++-+----++--++-+----+----++--+-+++-+--+++-++----+----+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++--++----+-+++
++--+-+++-+----++--++-+----+----++--+-+++-+--+++-++----+----+-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++--++----+-+++-
+--+-+++-+----++--++-+----+----++--+-+++-+--+++-++----+----+++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++-+-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++--++----+-+++-+
+-+-+++-+----++--++-+----+----++--+-+++-+--+++-++----+----+-+++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++--++----+-+++-+-
++-+++-+----++--++-+----+----++--+-+++-+--+++-++----+----+--++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+--++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-------+----+-++--++----+-+++-+-+
+-+++-+----++--++-+----+----++--+-+++-+--+++-++----+----+--+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+-+-+---+-++---+--++++-++++-------+----+-++--++----+-+++-+-++
++++-+----++--++-+----+----++--+-+++-+--+++-++----+----+--+-+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+-+-+---+-++---+--++++-++++-------+----+-++--++----+-+++-+-+++
+++-+----++--++-+----+----++--+-+++-+--+++-++----+----+--+-+-+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+--++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+-+++---+-++---+--++++-++++-------+----+-++--++----+-+++-+-+++-
++-+----++--++-+----+----++--+-+++-+--+++-++----+----+--+-++--+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+-+-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+-+++---+-++---+--++++-++++-------+----+-++--++----+-+++-+-+++-+
+-+----++--++-+----+----++--+-+++-+--+++-++----+----+--+-+++---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+-++++--+-++---+--++++-++++-------+----+-++--++----+-+++-+-+++-+-
++----++--++-+----+----++--+-+++-+--+++-++----+----+--+-+++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+-++++--+-++---+--++++-++++-------+----+-++--++----+-+++-+-+++-+--
+----++--++-+----+----++--+-+++-+--+++-++----+----+--+-+++-+-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--+++++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+-++++-++-++---+--++++-++++-------+----+-++--++----+-+++-+-+++-+---
+---++--++-+----+----++--+-+++-+--+++-++----+----+--+-+++-+-+-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--+++++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+-++++-++-++---+--++++-++++-------+----+-++--++----+-+++-+-+++-+---+
+--++--++-+----+----++--+-+++-+--+++-++----+----+--+-+++-+--++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--+++--+---++-+---+-++-+----+----+-++--++----+-+++-+-+-++++-+++++---+--++++-++++-------+----+-++--++----+-+++-+-+++-+---+-
+-++--++-+----+----++--+-+++-+--+++-++----+----+--+-+++-+---+++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--+--+---++-+---+-++-+----+----+-++--++----+-+++-+-+-++++-+++++---+--++++-++++-------+----+-++--++----+-+++-+-+++-+---+-+
+++--++-+----+----++--+-+++-+--+++-++----+----+--+-+++-+----++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++---+---++-+---+-++-+----+----+-++--++----+-+++-+-+-++++-++++----+--++++-++++-------+----+-++--++----+-+++-+-+++-+---+-++
++--++-+----+----++--+-+++-+--+++-++----+----+--+-+++-+----+-++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++-+---++-+---+-++-+----+----+-++--++----+-+++-+-+-++++-++++----+--++++-++++-------+----+-++--++----+-+++-+-+++-+---+-++-
+--++-+----+----++--+-+++-+--+++-++----+----+--+-+++-+----++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++---++-+---+-++-+----+----+-++--++----+-+++-+-+-++++-++++--+-+--++++-++++-------+----+-++--++----+-+++-+-+++-+---+-++--
+-++-+----+----++--+-+++-+--+++-++----+----+--+-+++-+----++-+--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--+--++-+---+-++-+----+----+-++--++----+-+++-+-+-++++-++++--+-+--++++-++++-------+----+-++--++----+-+++-+-+++-+---+-++---
+++-+----+----++--+-+++-+--+++-++----+----+--+-+++-+----++--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+---++-+---+-++-+----+----+-++--++----+-+++-+-+-++++-++++--+----++++-++++-------+----+-++--++----+-+++-+-+++-+---+-++---+
++-+----+----++--+-+++-+--+++-++----+----+--+-+++-+----++--+-++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+-++-+---+-++-+----+----+-++--++----+-+++-+-+-++++-++++--+----++++-++++-------+----+-++--++----+-+++-+-+++-+---+-++---+-
+-+----+----++--+-+++-+--+++-++----+----+--+-+++-+----++--++--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-++-+---+-++-+----+----+-++--++----+-+++-+-+-++++-++++--+---+++++-++++-------+----+-++--++----+-+++-+-+++-+---+-++---+--
++----+----++--+-+++-+--+++-++----+----+--+-+++-+----++--++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++--+---+-++-+----+----+-++--++----+-+++-+-+-++++-++++--+---+++++-++++-------+----+-++--++----+-+++-+-+++-+---+-++---+--+
+----+----++--+-+++-+--+++-++----+----+--+-+++-+----++--++-+-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-+++++---+-++-+----+----+-++--++----+-+++-+-+-++++-++++--+---++-++-++++-------+----+-++--++----+-+++-+-+++-+---+-++---+--++
+---+----++--+-+++-+--+++-++----+----+--+-+++-+----++--++-+-+-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-+++---+-++-+----+----+-++--++----+-+++-+-+-++++-++++--+---++-++-++++-------+----+-++--++----+-+++-+-+++-+---+-++---+--+++
+--+----++--+-+++-+--+++-++----+----+--+-+++-+----++--++-+--++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++--+-++-+----+----+-++--++----+-+++-+-+-++++-++++--+---++-+--++++-------+----+-++--++----+-+++-+-+++-+---+-++---+--++++
+-+----++--+-+++-+--+++-++----+----+--+-+++-+----++--++-+---+++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-+-+-++-+----+----+-++--++----+-+++-+-+-++++-++++--+---++-+--++++-------+----+-++--++----+-+++-+-+++-+---+-++---+--++++-
++----++--+-+++-+--+++-++----+----+--+-+++-+----++--++-+----++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-+-++-+----+----+-++--++----+-+++-+-+-++++-++++--+---++-+---+++-------+----+-++--++----+-+++-+-+++-+---+-++---+--++++-+
+----++--+-+++-+--+++-++----+----+--+-+++-+----++--++-+----+-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++-+----+----+-++--++----+-+++-+-+-++++-++++--+---++-+---+++-------+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++
+---++--+-+++-+--+++-++----+----+--+-+++-+----++--++-+----+-+-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--++++++-+----+----+-++--++----+-+++-+-+-++++-++++--+---++-+---+-+-------+----+-++--++----+-+++-+-+++-+---+-++---+--++++-+++
+--++--+-+++-+--+++-++----+----+--+-+++-+----++--++-+----+--++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--++++-+----+----+-++--++----+-+++-+-+-++++-++++--+---++-+---+-+-------+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++
+-++--+-+++-+--+++-++----+----+--+-+++-+----++--++-+----+---+++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--++-+----+----+-++--++----+-+++-+-+-++++-++++--+---++-+---+-++------+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++-
+++--+-+++-+--+++-++----+----+--+-+++-+----++--++-+----+----++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--++----+----+-++--++----+-+++-+-+-++++-++++--+---++-+---+-++------+----+-++--++----+-+++-+-+++-+---+-++---+--++++-++++--
+++----+----++-+++--+-+++-+---+-+---+-++++--++--+-++++-+++++++++-++++-+--++--++++-+---+-+-+----+----++-+++--+-+++-+--+-+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++------+----+-++--++----+-+++-+--+----+----++-+++--+-+++-+--++
++----+----++-+++--+-+++-+---+-+---+-++++--++--+-++++-+++++++++-++++-+--++--++++-+---+-+-+----+----++-+++--+-+++-+--+-+-+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++----+----+-++--++----+-+++-+--+----+----++-+++--+-+++-+--++-
+----+----++-+++--+-+++-+---+-+---+-++++--++--+-++++-+++++++++-++++-+--++--++++-+---+-+-+----+----++-+++--+-+++-+--+-++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+----+-++--++----+-+++-+--+----+----++-+++--+-+++-+--++--
+---+----++-+++--+-+++-+---+-+---+-++++--++--+-++++-+++++++-+-++++-+--++--++++-+---+-+-+----+----++-+++--+-+++-+--+-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-+++-+----+-++--++----+-+++-+--+----+----++-+++--+-+++-+--++---
+--+----++-+++--+-+++-+---+-+---+-++++--++--+-++++-+++++++---++++-+--++--++++-+---+-+-+----+----++-+++--+-+++-+--+-++++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-+++----+-++--++----+-+++-+--+----+----++-+++--+-+++-+--++----
+-+----++-+++--+-+++-+---+-+---+-++++--++--+-++++-+++++++---++++-+--++--++++-+---+-+-+----+----++-+++--+-+++-+--+-++++-+++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-+----+-++--++----+-+++-+--+----+----++-+++--+-+++-+--++----+
++----++-+++--+-+++-+---+-+---+-++++--++--+-++++-+++++++----+++-+--++--++++-+---+-+-+----+----++-+++--+-+++-+--+-++++-+++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++----+-++--++----+-+++-+--+----+----++-+++--+-+++-+--++----+-
+----++-+++--+-+++-+---+-+---+-++++--++--+-++++-+++++++----+++-+--++--++++-+---+-+-+----+----++-+++--+-+++-+--+-++++-++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++--+-++--++----+-+++-+--+----+----++-+++--+-+++-+--++----+--
+---++-+++--+-+++-+---+-+---+-++++--++--+-++++-+++++++----+-+-+--++--++++-+---+-+-+----+----++-+++--+-+++-+--+-++++-++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--+++-+-++--++----+-+++-+--+----+----++-+++--+-+++-+--++----+---
+--++-+++--+-+++-+---+-+---+-++++--++--+-++++-+++++++----+---+--++--++++-+---+-+-+----+----++-+++--+-+++-+--+-++++-++++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--+++-++--++----+-+++-+--+----+----++-+++--+-+++-+--++----+----
+-++-+++--+-+++-+---+-+---+-++++--++--+-++++-+++++++----+---+--++--++++-+---+-+-+----+----++-+++--+-+++-+--+-++++-++++-+++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--+-++--++----+-+++-+--+----+----++-+++--+-+++-+--++----+----+
+++-+++--+-+++-+---+-+---+-++++--++--+-++++-+++++++----+------++--++++-+---+-+-+----+----++-+++--+-+++-+--+-++++-++++-+++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++--++----+-+++-+--+----+----++-+++--+-+++-+--++----+----+-
++-+++--+-+++-+---+-+---+-++++--++--+-++++-+++++++----+----+-++--++++-+---+-+-+----+----++-+++--+-+++-+--+-++++-++++-+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+-+--++----+-+++-+--+----+----++-+++--+-+++-+--++----+----+-+
+-+++--+-+++-+---+-+---+-++++--++--+-++++-+++++++----+----++++--++++-+---+-+-+----+----++-+++--+-+++-+--+-++++-++++-+----++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++----+-+++-+--+----+----++-+++--+-+++-+--++----+----+-++
++++--+-+++-+---+-+---+-++++--++--+-++++-+++++++----+----++-+--++++-+---+-+-+----+----++-+++--+-+++-+--+-++++-++++-+--++--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++----++----+-+++-+--+----+----++-+++--+-+++-+--++----+----+-++-
+++--+-+++-+---+-+---+-++++--++--+-++++-+++++++----+----++-+--++++-+---+-+-+----+----++-+++--+-+++-+--+-++++-++++-+--++-+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++--++----+-+++-+--+----+----++-+++--+-+++-+--++----+----+-++--
++--+-+++-+---+-+---+-++++--++--+-++++-+++++++----+----++-++-++++-+---+-+-+----+----++-+++--+-+++-+--+-++++-++++-+--++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++-+----+-+++-+--+----+----++-+++--+-+++-+--++----+----+-++--+
+--+-+++-+---+-+---+-++++--++--+-++++-+++++++----+----++-+++++++-+---+-+-+----+----++-+++--+-+++-+--+-++++-++++-+--++-----+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++----+-+++-+--+----+----++-+++--+-+++-+--++----+----+-++--++
+-+-+++-+---+-+---+-++++--++--+-++++-+++++++----+----++-+++-+++-+---+-+-+----+----++-+++--+-+++-+--+-++++-++++-+--++--++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-+---+-+++-+--+----+----++-+++--+-+++-+--++----+----+-++--++-
++-+++-+---+-+---+-++++--++--+-++++-+++++++----+----++-+++--++-+---+-+-+----+----++-+++--+-+++-+--+-++++-++++-+--++--++++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+---+-+++-+--+----+----++-+++--+-+++-+--++----+----+-++--++--
+-+++-+---+-+---+-++++--++--+-++++-+++++++----+----++-+++--++-+---+-+-+----+----++-+++--+-+++-+--+-++++-++++-+--++--+++-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-+-+++-+--+----+----++-+++--+-+++-+--++----+----+-++--++---
++++-+---+-+---+-++++--++--+-++++-+++++++----+----++-+++--+--+---+-+-+----+----++-+++--+-+++-+--+-++++-++++-+--++--+++++-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-+++-+--+----+----++-+++--+-+++-+--++----+----+-++--++----
+++-+---+-+---+-++++--++--+-++++-+++++++----+----++-+++--+-++---+-+-+----+----++-+++--+-+++-+--+-++++-++++-+--++--++++--+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+++-+--+----+----++-+++--+-+++-+--++----+----+-++--++----+
++-+---+-+---+-++++--++--+-++++-+++++++----+----++-+++--+-++---+-+-+----+----++-+++--+-+++-+--+-++++-++++-+--++--++++-+--+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+-+++-+--+----+----++-+++--+-+++-+--++----+----+-++--++----+-
+-+---+-+---+-++++--++--+-++++-+++++++----+----++-+++--+-+++--+-+-+----+----++-+++--+-+++-+--+-++++-++++-+--++--++++-+----+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+++-+--+----+----++-+++--+-+++-+--++----+----+-++--++----+-+
++---+-+---+-++++--++--+-++++-+++++++----+----++-+++--+-+++--+-+-+----+----++-+++--+-+++-+--+-++++-++++-+--++--++++-+--+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+-+--+----+----++-+++--+-+++-+--++----+----+-++--++----+-++
+---+-+---+-++++--++--+-++++-+++++++----+----++-+++--+-+++-++-+-+----+----++-+++--+-+++-+--+-++++-++++-+--++--++++-+----+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+--+----+----++-+++--+-+++-+--++----+----+-++--++----+-+++
+--+-+---+-++++--++--+-++++-+++++++----+----++-+++--+-+++-+--+-+----+----++-+++--+-+++-+--+-++++-++++-+--++--++++-+---++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++--+----+----++-+++--+-+++-+--++----+----+-++--++----+-+++-
+-+-+---+-++++--++--+-++++-+++++++----+----++-+++--+-+++-+--+-+----+----++-+++--+-+++-+--+-++++-++++-+--++--++++-+---+-++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++--+----+----++-+++--+-+++-+--++----+----+-++--++----+-+++-+
++-+---+-++++--++--+-++++-+++++++----+----++-+++--+-+++-+----+----+----++-+++--+-+++-+--+-++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-+-+----+----++-+++--+-+++-+--++----+----+-++--++----+-+++-+-
+-+---+-++++--++--+-++++-+++++++----+----++-+++--+-+++-+---++----+----++-+++--+-+++-+--+-++++-++++-+--++--++++-+---+-+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-+----+----++-+++--+-+++-+--++----+----+-++--++----+-+++-+--
++---+-++++--++--+-++++-+++++++----+----++-+++--+-+++-+---+-----+----++-+++--+-+++-+--+-++++-++++-+--++--++++-+---+-+-+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+----+----++-+++--+-+++-+--++----+----+-++--++----+-+++-+--+
+---+-++++--++--+-++++-+++++++----+----++-+++--+-+++-+---+-+---+----++-+++--+-+++-+--+-++++-++++-+--++--++++-+---+-+-+-+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+------+----++-+++--+-+++-+--++----+----+-++--++----+-+++-+--+-
+--+-++++--++--+-++++-+++++++----+----++-+++--+-+++-+---+-+---+----++-+++--+-+++-+--+-++++-++++-+--++--++++-+---+-+-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+----+----++-+++--+-+++-+--++----+----+-++--++----+-+++-+--+--
+-+-++++--++--+-++++-+++++++----+----++-+++--+-+++-+---+-+---+----++-+++--+-+++-+--+-++++-++++-+--++--++++-+---+-+-+-----+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+--+----++-+++--+-+++-+--++----+----+-++--++----+-+++-+--+---
++-++++--++--+-++++-+++++++----+----++-+++--+-+++-+---+-+---+----++-+++--+-+++-+--+-++++-++++-+--++--++++-+---+-+-+-------+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-++----++-+++--+-+++-+--++----+----+-++--++----+-+++-+--+----
+-++++--++--+-++++-+++++++----+----++-+++--+-+++-+---+-+---+----++-+++--+-+++-+--+-++++-++++-+--++--++++-+---+-+-+----++---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-----++-+++--+-+++-+--++----+----+-++--++----+-+++-+--+----+
+++++--++--+-++++-+++++++----+----++-+++--+-+++-+---+-+---+----++-+++--+-+++-+--+-++++-++++-+--++--++++-+---+-+-+----+--+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++---++-+++--+-+++-+--++----+----+-++--++----+-+++-+--+----+-
++++--++--+-++++-+++++++----+----++-+++--+-+++-+---+-+---+-+--++-+++--+-+++-+--+-++++-++++-+--++--++++-+---+-+-+----+--+-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--+++--++-+++--+-+++-+--++----+----+-++--++----+-+++-+--+----+--
+++--++--+-++++-+++++++----+----++-+++--+-+++-+---+-+---+-++-++-+++--+-+++-+--+-++++-++++-+--++--++++-+---+-+-+----+---++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++-++-+++--+-+++-+--++----+----+-++--++----+-+++-+--+----+---
++--++--+-++++-+++++++----+----++-+++--+-+++-+---+-+---+-+++++-+++--+-+++-+--+-++++-++++-+--++--++++-+---+-+-+----+----+++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--+++-+++--+-+++-+--++----+----+-++--++----+-+++-+--+----+----
+--++--+-++++-+++++++----+----++-+++--+-+++-+---+-+---+-+++++-+++--+-+++-+--+-++++-++++-+--++--++++-+---+-+-+----+----+++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--+-+++--+-+++-+--++----+----+-++--++----+-+++-+--+----+----+
+-++--+-++++-+++++++----+----++-+++--+-+++-+---+-+---+-++++--+++--+-+++-+--+-++++-++++-+--++--++++-+---+-+-+----+----++-++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--+++--+-+++-+--++----+----+-++--++----+-+++-+--+----+----++
+++--+-++++-+++++++----+----++-+++--+-+++-+---+-+---+-++++--+++--+-+++-+--+-++++-++++-+--++--++++-+---+-+-+----+----++---++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--+++++--+-+++-+--++----+----+-++--++----+-+++-+--+----+----++-
++--+-++++-+++++++----+----++-+++--+-+++-+---+-+---+-++++--+++--+-+++-+--+-++++-++++-+--++--++++-+---+-+-+----+----++-++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--+++--+-+++-+--++----+----+-++--++----+-+++-+--+----+----++-+
+--+-++++-+++++++----+----++-+++--+-+++-+---+-+---+-++++--+++--+-+++-+--+-++++-++++-+--++--++++-+---+-+-+----+----++-++++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--+--+-+++-+--++----+----+-++--++----+-+++-+--+----+----++-++
+-+-++++-+++++++----+----++-+++--+-+++-+---+-+---+-++++--++---+-+++-+--+-++++-++++-+--++--++++-+---+-+-+----+----++-+++-++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+---+-+++-+--++----+----+-++--++----+-+++-+--+----+----++-+++
++-++++-+++++++----+----++-+++--+-+++-+---+-+---+-++++--++---+-+++-+--+-++++-++++-+--++--++++-+---+-+-+----+----++-+++---++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+-+-+++-+--++----+----+-++--++----+-+++-+--+----+----++-+++-
+-++++-+++++++----+----++-+++--+-+++-+---+-+---+-++++--++--++-+++-+--+-++++-++++-+--++--++++-+---+-+-+----+----++-+++--+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+-+++-+--++----+----+-++--++----+-+++-+--+----+----++-+++--
+++++-+++++++----+----++-+++--+-+++-+---+-+---+-++++--++--+--+++-+--+-++++-++++-+--++--++++-+---+-+-+----+----++-+++--+-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+++-+--++----+----+-++--++----+-+++-+--+----+----++-+++--+
++++-+++++++----+----++-+++--+-+++-+---+-+---+-++++--++--+-++++-+--+-++++-++++-+--++--++++-+---+-+-+----+----++-+++--+-+-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++++-+--++----+----+-++--++----+-+++-+--+----+----++-+++--+-
+++-+++++++----+----++-+++--+-+++-+---+-+---+-++++--++--+-++++-+--+-++++-++++-+--++--++++-+---+-+-+----+----++-+++--+-+++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++----+----+-++--++----+-+++-+--+----+----++-+++--+-+
++-+++++++----+----++-+++--+-+++-+---+-+---+-++++--++--+-++++-+--+-++++-++++-+--++--++++-+---+-+-+----+----++-+++--+-+++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++-+--++----+----+-++--++----+-+++-+--+----+----++-+++--+-++
+-+++++++----+----++-+++--+-+++-+---+-+---+-++++--++--+-++++-+--+-++++-++++-+--++--++++-+---+-+-+----+----++-+++--+-+++++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++--+--++----+----+-++--++----+-+++-+--+----+----++-+++--+-+++
++++++++----+----++-+++--+-+++-+---+-+---+-++++--++--+-++++-+--+-++++-++++-+--++--++++-+---+-+-+----+----++-+++--+-+++--++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--++++++--++----+----+-++--++----+-+++-+--+----+----++-+++--+-+++-
+++++++----+----++-+++--+-+++-+---+-+---+-++++--++--+-++++-+--+-++++-++++-+--++--++++-+---+-+-+----+----++-+++--+-+++-++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--++++--++----+----+-++--++----+-+++-+--+----+----++-+++--+-+++-+
++++++----+----++-+++--+-+++-+---+-+---+-++++--++--+-++++-++-+-++++-++++-+--++--++++-+---+-+-+----+----++-+++--+-+++-+-++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++-++----+----+-++--++----+-+++-+--+----+----++-+++--+-+++-+-
+++++----+----++-+++--+-+++-+---+-+---+-++++--++--+-++++-++++-++++-++++-+--++--++++-+---+-+-+----+----++-+++--+-+++-+--+++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--++++----+----+-++--++----+-+++-+--+----+----++-+++--+-+++-+--
++++----+----++-+++--+-+++-+---+-+---+-++++--++--+-++++-++++-++++-++++-+--++--++++-+---+-+-+----+----++-+++--+-+++-+--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--++----+----+-++--++----+-+++-+--+----+----++-+++--+-+++-+--+
+-++-+---+-++---+--++++-++++-+-+-+++-+----++--++-+----+----+++++-++++-+--++--++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--
+++-+---+-++---+--++++-++++-+-+-+++-+----++--++-+----+----+-+++-++++-+--++--++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--+-+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++-
++-+---+-++---+--++++-++++-+-+-+++-+----++--++-+----+----+-+++-++++-+--++--++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++
+-+---+-++---+--++++-++++-+-+-+++-+----++--++-+----+----+-+++-++++-+--++--++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-+++
++---+-++---+--++++-++++-+-+-+++-+----++--++-+----+----+-++--++++-+--++--++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++
+---+-++---+--++++-++++-+-+-+++-+----++--++-+----+----+-++-+++++-+--++--++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-+++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-+
+--+-++---+--++++-++++-+-+-+++-+----++--++-+----+----+-++-+-+++-+--++--++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-+++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-
+-+-++---+--++++-++++-+-+-+++-+----++--++-+----+----+-++-+--++-+--++--++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++
++-++---+--++++-++++-+-+-+++-+----++--++-+----+----+-++-+---+-+--++--++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--+++
+-++---+--++++-++++-+-+-+++-+----++--++-+----+----+-++-+---+-+--++--++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++
+++---+--++++-++++-+-+-+++-+----++--++-+----+----+-++-+---+-+--++--++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--+
++---+--++++-++++-+-+-+++-+----++--++-+----+----+-++-+---+-+--++--++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+--
+---+--++++-++++-+-+-+++-+----++--++-+----+----+-++-+---+-++-++--++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+-
+--+--++++-++++-+-+-+++-+----++--++-+----+----+-++-+---+-++-++--++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+----++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---+
+-+--++++-++++-+-+-+++-+----++--++-+----+----+-++-+---+-++--+--++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+--++--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++---
++--++++-++++-+-+-+++-+----++--++-+----+----+-++-+---+-++-----++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+--++-+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++--
+--++++-++++-+-+-+++-+----++--++-+----+----+-++-+---+-++---+-++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+--++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++-
+-++++-++++-+-+-+++-+----++--++-+----+----+-++-+---+-++---+-++++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+--++-----+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-++
+++++-++++-+-+-+++-+----++--++-+----+----+-++-+---+-++---+--+++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+--++--++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-+
++++-++++-+-+-+++-+----++--++-+----+----+-++-+---+-++---+--+++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+--++--++++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+-
+++-++++-+-+-+++-+----++--++-+----+----+-++-+---+-++---+--+++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+--++--+++-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---+
++-++++-+-+-+++-+----++--++-+----+----+-++-+---+-++---+--+++-+---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+--++--+++++-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+---
+-++++-+-+-+++-+----++--++-+----+----+-++-+---+-++---+--+++++---+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+--++--++++--+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+--
+++++-+-+-+++-+----++--++-+----+----+-++-+---+-++---+--++++----+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+--++--++++-+--+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+-
++++-+-+-+++-+----++--++-+----+----+-++-+---+-++---+--++++-+--+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+--++--++++-+----+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-+
+++-+-+-+++-+----++--++-+----+----+-++-+---+-++---+--++++-++-+-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+--++--++++-+--+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++-
++-+-+-+++-+----++--++-+----+----+-++-+---+-++---+--++++-++++-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+--++--++++-+----+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++++
+-+-+-+++-+----++--++-+----+----+-++-+---+-++---+--++++-++++-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+--++--++++-+---++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-+++
++-+-+++-+----++--++-+----+----+-++-+---+-++---+--++++-++++-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+--++--++++-+---+-++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-++
+-+-+++-+----++--++-+----+----+-++-+---+-++---+--++++-++++-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++--++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-+
++-+++-+----++--++-+----+----+-++-+---+-++---+--++++-++++-+---+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-+--++++-++++--+---++-+---+-++--++++-++++-+--++--++++-+---+-++++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+-
+-+++-+----++--++-+----+----+-++-+---+-++---+--++++-++++-+-+-+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-+--++++-++++--+---++-+---+-++--++++-++++-+--++--++++-+---+-++--++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---+
++++-+----++--++-+----+----+-++-+---+-++---+--++++-++++-+-+-+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-+---+++-++++--+---++-+---+-++--++++-++++-+--++--++++-+---+-++-++-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+---
+++-+----++--++-+----+----+-++-+---+-++---+--++++-++++-+-+-+-+++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-+---+++-++++--+---++-+---+-++--++++-++++-+--++--++++-+---+-++-++-+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+--
++-+----++--++-+----+----+-++-+---+-++---+--++++-++++-+-+-+++++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-+---+-+-++++--+---++-+---+-++--++++-++++-+--++--++++-+---+-++-+++--+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+-
+-+----++--++-+----+----+-++-+---+-++---+--++++-++++-+-+-+++++-+--+++-++----+----+++++++-++++-+--++--++++-+---+-+---+-+-++++--+---++-+---+-++--++++-++++-+--++--++++-+---+-++-++++---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-+
++----++--++-+----+----+-++-+---+-++---+--++++-++++-+-+-+++-+-+--+++-++----+----+++++++-++++-+--++--++++-+---+-+---+-++++++--+---++-+---+-++--++++-++++-+--++--++++-+---+-++-++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++-
+----++--++-+----+----+-++-+---+-++---+--++++-++++-+-+-+++-+-+--+++-++----+----+++++++-++++-+--++--++++-+---+-+---+-++++++--+---++-+---+-++--++++-++++-+--++--++++-+---+-++-++++-+-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++++
+---++--++-+----+----+-++-+---+-++---+--++++-++++-+-+-+++-+-+--+++-++----+----+++++++-++++-+--++--++++-+---+-+---+-+++-++--+---++-+---+-++--++++-++++-+--++--++++-+---+-++-++++-+++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--+++
+--++--++-+----+----+-++-+---+-++---+--++++-++++-+-+-+++-+----+++-++----+----+++++++-++++-+--++--++++-+---+-+---+-+++-++--+---++-+---+-++--++++-++++-+--++--++++-+---+-++-++++-+++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--++
+-++--++-+----+----+-++-+---+-++---+--++++-++++-+-+-+++-+----+++-++----+----+++++++-++++-+--++--++++-+---+-+---+-+++-+---+---++-+---+-++--++++-++++-+--++--++++-+---+-++-++++-+++++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--+
+++--++-+----+----+-++-+---+-++---+--++++-++++-+-+-+++-+----+++-++----+----+++++++-++++-+--++--++++-+---+-+---+-+++-+---+---++-+---+-++--++++-++++-+--++--++++-+---+-++-++++-++++-++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++--
++--++-+----+----+-++-+---+-++---+--++++-++++-+-+-+++-+----+++-++----+----+++++++-++++-+--++--++++-+---+-+---+-+++-+--++---++-+---+-++--++++-++++-+--++--++++-+---+-++-++++-++++---++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++-
+--++-+----+----+-++-+---+-++---+--++++-++++-+-+-+++-+----+++-++----+----+++++++-++++-+--++--++++-+---+-+---+-+++-+--++---++-+---+-++--++++-++++-+--++--++++-+---+-++-++++-++++--+--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--++
+-++-+----+----+-++-+---+-++---+--++++-++++-+-+-+++-+----++--++----+----+++++++-++++-+--++--++++-+---+-+---+-+++-+--+++--++-+---+-++--++++-++++-+--++--++++-+---+-++-++++-++++--+-+--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--+
+++-+----+----+-++-+---+-++---+--++++-++++-+-+-+++-+----++--++----+----+++++++-++++-+--++--++++-+---+-+---+-+++-+--+++--++-+---+-++--++++-++++-+--++--++++-+---+-++-++++-++++--+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+--
++-+----+----+-++-+---+-++---+--++++-++++-+-+-+++-+----++--++----+----+++++++-++++-+--++--++++-+---+-+---+-+++-+--+++-+++-+---+-++--++++-++++-+--++--++++-+---+-++-++++-++++--+----++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+-
+-+----+----+-++-+---+-++---+--++++-++++-+-+-+++-+----++--++----+----+++++++-++++-+--++--++++-+---+-+---+-+++-+--+++-+++-+---+-++--++++-++++-+--++--++++-+---+-++-++++-++++--+---+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-+
++----+----+-++-+---+-++---+--++++-++++-+-+-+++-+----++--++----+----+++++++-++++-+--++--++++-+---+-+---+-+++-+--+++-++--+---+-++--++++-++++-+--++--++++-+---+-++-++++-++++--+---+++--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++-
+----+----+-++-+---+-++---+--++++-++++-+-+-+++-+----++--++-+--+----+++++++-++++-+--++--++++-+---+-+---+-+++-+--+++-++--+---+-++--++++-++++-+--++--++++-+---+-++-++++-++++--+---++--+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++++
+---+----+-++-+---+-++---+--++++-++++-+-+-+++-+----++--++-+--+----+++++++-++++-+--++--++++-+---+-+---+-+++-+--+++-++------+-++--++++-++++-+--++--++++-+---+-++-++++-++++--+---++-++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-+++
+--+----+-++-+---+-++---+--++++-++++-+-+-+++-+----++--++-+--+----+++++++-++++-+--++--++++-+---+-+---+-+++-+--+++-++------+-++--++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+-++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-++
+-+----+-++-+---+-++---+--++++-++++-+-+-+++-+----++--++-+-------+++++++-++++-+--++--++++-+---+-+---+-+++-+--+++-++----+-+-++--++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+--+++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-+
++----+-++-+---+-++---+--++++-++++-+-+-+++-+----++--++-+-------+++++++-++++-+--++--++++-+---+-+---+-+++-+--+++-++----+-+-++--++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++-
+----+-++-+---+-++---+--++++-++++-+-+-+++-+----++--++-+----+--+++++++-++++-+--++--++++-+---+-+---+-+++-+--+++-++----+---++--++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++++
+---+-++-+---+-++---+--++++-++++-+-+-+++-+----++--++-+----+--+++++++-++++-+--++--++++-+---+-+---+-+++-+--+++-++----+---++--++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-+-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--++++
+--+-++-+---+-++---+--++++-++++-+-+-+++-+----++--++-+----+--+++++++-++++-+--++--++++-+---+-+---+-+++-+--+++-++----+----+--++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-+++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+++
+-+-++-+---+-++---+--++++-++++-+-+-+++-+----++--++-+----+---++++++-++++-+--++--++++-+---+-+---+-+++-+--+++-++----+----+--++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-+++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--++
++-++-+---+-++---+--++++-++++-+-+-+++-+----++--++-+----+----+++++-++++-+--++--++++-+---+-+---+-+++-+--+++-++----+----++-++++-++++-+--++--++++-+---+-++-++++-++++--+---++-+---+-++-++++-++++-+--++--++++-+---+-++++-+---+-++---+--++++-++++--+
\ No newline at end of file
diff --git a/gptqmodel/exllamav3/util/hadamard_data/hadamard_244.txt b/gptqmodel/exllamav3/util/hadamard_data/hadamard_244.txt
new file mode 100644
index 000000000..933b8bd60
--- /dev/null
+++ b/gptqmodel/exllamav3/util/hadamard_data/hadamard_244.txt
@@ -0,0 +1,244 @@
+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--++---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++
++++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+--+++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-++
+-+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+
+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-++++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-
++--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+--+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+
+-+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-++-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-
+--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--+++-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++--+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+
++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--+-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---+++++-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----
+++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--+-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---+++-+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++---
+-++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+-++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++--+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++--
+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---+---+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++-
++--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++-------+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++
+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++--+----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--++
++-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++-++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+
+-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--+++++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--
++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--+-+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++-
+++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+----+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++
++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--+-++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+-+--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-+
+++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+----++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-
+-++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++--++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+
+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-+++-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--
++--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-+-+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+-
+-+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+----++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+---+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+
+--+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+----++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---++--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-
+---+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+--+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++----+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++
+----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+--+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++--+-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------++
+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+--+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++-++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+
++-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+---------+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---+++++++++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------
+-+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+-----+---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---+++++-+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++-----
+--+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+----++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++--+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++----
+---+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+---+++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---+++---+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++---
+----+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+--++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++----+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++--
+-----+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+-+++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---+-----+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++-
+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++
++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+------++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+--+------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-++
+-+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+------++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+-++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+
+--+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+------++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-++++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-
+---+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+--+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++--+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+
+----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+--+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-+++-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--
+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-+-+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+-
++-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+---+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+
+-+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--++--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-
+--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-+++++-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++---+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++
++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-+++-+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++-+-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--+
+++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--
++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---+-++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++-
+++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++-----++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++
+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++--+--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----++
++-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++-++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+
+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-+++++++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----
++-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-+++-+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+---
+-+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++-++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++--+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+--
+--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--+++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-+---+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+-
++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--+++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+
+++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+---++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-++----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-
+-++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+--+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+
+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---++-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-
++--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+----+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++
+-+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+--+-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-++++++
+--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+-++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++
++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--++---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-++++
+--++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++-++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+----+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---
+---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-+++++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+--
++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-+-+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-
+++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++---+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+
+-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--+++--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-
++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--+-+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+
+++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++----+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--+++-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-
+-++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++-+--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--+-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++
+--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--+-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---+++
++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-+-++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+-++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++
+++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+---++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---+
+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-++--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++---
++-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+--+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++--
+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----++-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++-
++-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++-----+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--++
+-+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++---+-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--+
+--+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++--++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+--
+---+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++-+++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--+-++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+-
+----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+----++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-+
++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-+-++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++-
+++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++---++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-++
+-++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-++++++--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-+
++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-++++-+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+----++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+-
+++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++--+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+----++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---+
++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-++---+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+--+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++---
+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+--+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++--
++++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++------+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+--+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++-
+-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-+++++++-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+---------+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++++
++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-+++++-+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+-----+---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---+++++
+++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++--+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+----++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++++
++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-+++---+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+---+++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---+++
+++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-++----+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+--++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---++
++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-+-----+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+-+++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---+
+++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+---
+-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-++++++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+------++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+--
++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-++++-+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+------++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+-
+++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++--+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+------++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-+
++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-++---+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+--+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++-
+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+--+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-++
++++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++------+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-+
+-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----+++-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+-
++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----+-+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--+
+++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+------+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-+++++-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++--
+-++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+---+--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-+++-+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++-
+--++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+--++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---++
+---++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+-+++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---+
+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++---
++----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+--++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++--
+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-++-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++-
++-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++--+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++++
+-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--+++-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-+++
++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--+-+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++-++---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-++
+++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++----+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--+++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-+
+-++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++-+--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--+++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+-
+--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+---++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-+
++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-+-++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+-
+++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++---++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---+
+-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---+++--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+---
++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---+-+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+--
+++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++-----+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+-
+-++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++--+--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--++---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+---+
+-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+--++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++-
++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+-----++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++
+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+-+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+-+---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-+
++++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+---+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-
+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-++--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++---++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++
++-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++-+-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--+
+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--
++-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--+-++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++-
+-+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++---++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+----++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++
+--+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++---++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+-+--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-+
+---+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++---++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-
+----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--++++--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+--++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-+
++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--++-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-++-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+-
+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++--++-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++--+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----+
++++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++---+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--+++++-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++----
+-+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++-+-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--+++-+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++---
+--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++--+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++--
++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--+---+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++-
+++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+------+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-++
+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+--+-++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+-+----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-+
++-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+----++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++-
+-+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+-+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+------++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++++
+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++-+-+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+----+-++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-++++
++--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++---+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+---++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+++
+-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+++---+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+--+++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-++
++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------++----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-+
+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------++++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++-
++++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+-------+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++++
+-+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+-----+-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-+++++
+--+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+----++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-++++
+---+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+---+++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-+++
+----+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+--++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-++
+-----+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+-+++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-+
+------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++-
++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+------++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++++
+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+-+-+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+----+-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-++++
++++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+---+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+---++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+++
+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+--+---+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+--+++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-++
++-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+------+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-+
+-+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-+------+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--++++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++-
+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++-++-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++---+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----++
++--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++-+-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----+
+-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+----
++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-+++-++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+---
+++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++--++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++--++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+--
+-++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++-+++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-+---++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+-
+--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----+++++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-+
++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----++-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-++----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+-
+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+----++-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+--+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-+
++++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+-----+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--++-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++-
+-+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+---+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++---+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--++
+--+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+---+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++-+-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--+
+---+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-+---+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++--
+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--+-++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++-
++----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+----++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-++
+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++-+-++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+-+--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-+
++-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++++---++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++-
+-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-++++++++--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++---++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---++
++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-++++++-+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++-+-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---+
+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++---
++++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--++-++-++--++-+-+----++-+++++-++++++-+++++-++----+-+-++--++-++--
+----+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+----+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+
+-----+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+--+-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--
+------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+-+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+-
+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-++++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+---+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+
++-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+--+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-++--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--
+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-++-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++-
++-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++++--+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++
+-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---+++++-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--+
++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---+++-+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++---++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--
+++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---++--+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++---++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+-
++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++---+---+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++---++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+
+++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++-------+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--++++--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-
+-++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++--+----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--++-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+
+--++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--++-++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++--++-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-
+---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--+++++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++---+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++
++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+--+-+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++-+-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--+++
+++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+----+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++
+-++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+-+--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--+
+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++-+++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--
++--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-++--++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+--+-++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+-
+-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-+++-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+----++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+
++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+-+-+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+-+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----
+++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---+---+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++-+-+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+----
+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++---++--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++---+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+---
++-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++----+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+++---+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+--
+-+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++--+-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------++----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-
+--+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++++-++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+
+---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---+++++++++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------
++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---+++++-+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+-----
+++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++++--+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+----
++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---+++---+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+---
+++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---++----+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+--
++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---+-----+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+-
+++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+---------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+
+-++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+--+------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+++------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----
+--++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-+-++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+-+-+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+----
+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++-++++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+---+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+---
++---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-++--+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+--+---+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+--
+-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-+++-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+------+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-
++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+-+-+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-+------+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+
+++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--+---+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++-++-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--
+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++--++--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++-
++-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++---+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++
+-+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++-+-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-+++
+--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---++++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++--++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++
++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++---+-++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++-+++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-+
+++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++-----++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----+++++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-
+-++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++--+--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----++-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+
+--++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++++-++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+----++-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-
+---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-+++++++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+-----+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+
++---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-+++-+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+---+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--
+++---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-++--+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+---+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++-
++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-+---+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-+---+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++
+++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-+-----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--+
+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+-++----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--
++-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-------+--+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++-+-++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+-
+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-------++-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+++---++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+
++-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+--------+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-++++--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--
+-+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+------+-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-++-+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++-
+--+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+-----++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++
+---+-+-++++---++--+-++-+---++++++---+-++-+--++---++++-+-+----+++-+-+----+++--++-+--+-+++------+++-+--+-++--+++----+-+-+++-+--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--+++--+--++--+-+-++++--+-----+------+-----+--++++-+-+--++--+--++
\ No newline at end of file
diff --git a/gptqmodel/exllamav3/util/hadamard_data/hadamard_428.txt b/gptqmodel/exllamav3/util/hadamard_data/hadamard_428.txt
new file mode 100644
index 000000000..c31041318
--- /dev/null
+++ b/gptqmodel/exllamav3/util/hadamard_data/hadamard_428.txt
@@ -0,0 +1,428 @@
++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-
+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+----+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+
++-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+-+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+-
+-+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++------++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+-+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--
+--+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+--++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+
+---+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++--++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+---+----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--++
+----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+--------+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++
++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----+---+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++-
+++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++--+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++--
++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----+++-+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++---
+++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++--++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----+++++----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----
+-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+
++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-+---++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+-
+++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++--++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+--
+-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----+++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-+++++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++--++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+---
++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----+++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-+++++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-+++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----
+++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++-++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++-++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-+++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----+
++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----+++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--+-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-+++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++
+++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----+--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--+-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++-
+-++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+------+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--
+--++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+----+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++--+-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--+
+---++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++----++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++
+----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+---+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+-+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++----++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-
+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+-+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-+
++-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+---+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++
+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--++-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-
++-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+--++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+
+-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-
++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----+-+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+-+----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-+
+++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+------+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++
+-++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++---++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+---+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-
+--++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++--+++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--++--+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++--
+---++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++-++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++-+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++---
+----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++----
++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----++-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----
+++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----++-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+
++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----++++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-
+++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++--++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-+++----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++--++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+
+-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-
++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-+---+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+-+++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-+
+++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++--+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+--++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++
++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-+++-+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+---+-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-+++
+++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++--+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-+++++---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+-----+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++
+-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-++++++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++----+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----++++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-
++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-++++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+--+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+-++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+
+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-++-+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+--+-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-++
++++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+--+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-++++-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+----+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++
+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-++-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++--+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---++-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-
++-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+--+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-++-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+--+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+
+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--++--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+--++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-++--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-
++-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++----+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+---+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+
+-+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+-+---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-+-+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+-
+--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-++++++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+-----+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-+++++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--
++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-++++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+--+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++-++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+
+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--++-+-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++--+-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--++
++++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+--+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--++++-++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++----+--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++
+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--++--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++--++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---++--++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-
++-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++----++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+++--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+---++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+
+-+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+-+--+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-+-++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+-
+--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+----+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++++-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--
++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++-----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--+-+++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++-+-----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--+
+++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+----------+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--+++++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++-------+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++
+-++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+--------+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-++++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+----+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-
+--++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+------+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++--+++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--++---+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++--
+---++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+----+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++---++-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++--+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++---
+----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+--+-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++----+-+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--++++-+-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++----
+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-++-+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++------+----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--++++++-+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----
++-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++--+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----++----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++--+++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+
+-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---+++++++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-----+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-++++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-
++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---+++++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+---+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+-++-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+
+++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---+++-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-++--+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+--+-+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-++
++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---+-+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++
+++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---+++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-
+-++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+--++-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+
+--++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+-+-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-++
+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+-++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++
++---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-
+-+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+-+-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-+
+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++
++--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-
+-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++---++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+
++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--++--++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+-
+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+-++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+--
++++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---
+-+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++-+--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---+
+--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++
++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-+-+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++-
+++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-+---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--
+-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++---+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+
++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-++--+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+-
+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+-+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+--
++++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---
+-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++----++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+
++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--+++---++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+-
+++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++--++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+--
++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--+-++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+---
+++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----
+-++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+-+-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----+
+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++
++--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-
+-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+
++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-++++----+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-
+++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++---+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+--
++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-++--+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+---
+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+-+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+----
++++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----
+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+--++---++++-++++--++--+--+-+--+++++-++-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+--+----++++-++-++++-----+-+-++----+++-+----+---+-+-++---+-++--+++++-+----+++-++-+---++--+---+----++-+-----+-+--+++----+----++--++-++-+-++-----+-+-++++-+++-+-+--+++-+--++-----+-+++-+++-++-+---++--+---+----++-+-----+
++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+--+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-++-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--
+--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----++++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+------+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+
+-+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++-+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-
++++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++---+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++---+++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-++---+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+--
+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+--+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++--++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++--+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+---
++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--++---+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++-+-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-++++-+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+----
+----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-++++++-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----
+---+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++-+----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++--++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+
+--+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++--++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-
+-+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++---+++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-+-++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+-+----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-+
++----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+------+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++
+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-+++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--+---+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++-
+---++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+-+-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-+++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++--+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++--
+--++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+--++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-+-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--+++-+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++---
+-++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--+++++---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----
+++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----+-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++----+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+
++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----+++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+--+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+-
+--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----+++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-++-+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+--
+-++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++-++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+------++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-++++--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---
+++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++---++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+----++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++---++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+
++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--+--++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+---+--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-+-++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+-
+-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++---++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+----+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--
+++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++-+---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--+
++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-++++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++-----+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++
+-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-+++-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+--+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++-
++-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++--+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--++-+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++--
+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-++-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++--+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--++++-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---
+++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+--+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----+++--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++--++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+
++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-
+-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++-----+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+-+-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-+
+----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++--++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++---+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+---+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++
+---+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++-----+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--++++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-
+--+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++------++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+-++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+
+-+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++--------++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++-+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+--+----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-++
++--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+-------+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++
+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++-
+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+-+++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+--+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++--
++----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++--+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++---
+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-+++++-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----
+---+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+-+-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-+++-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+
+--+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+--++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-+++++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-
+-+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+---+++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+
++---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-+++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-++
+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++
+--+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+-+-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-++++
+-+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+--++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++
++-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++-
+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--
++-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--+
+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++
+++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-
++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-+-+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++----++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+
+---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+-
+--+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++-+--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-++-++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+--
+-+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++--++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---
++-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---+
+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++
+++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-
++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-+-+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+
+--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----+++---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-
+-+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++-+--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+
++++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-------+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+-
+++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+-++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+--
++++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--++--++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+---+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---
+++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++---++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+------+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+
++-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--++++----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+----+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+-
+-+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+--+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+--
++---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++--+-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+---
+---+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---+++++-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----
+--+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+-+-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---+++-+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+
+-+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+--++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---+++-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-
++---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---+-+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+
+---+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---+++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-++
+--+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+--++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+--++++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+----++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++
+-+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+----++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+-+++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-++---++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++-
++--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+------++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+++--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++--++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++--
+--+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---++---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+--+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-++++-++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++---
+-+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+---+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++++-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----
++-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+----+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+-+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++-+-+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----+
+-+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--++--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++---+-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++
++++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+--+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--++-+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-
+++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+--+-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+
++--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-++++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+--+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-++-----++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-
+--++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-++++++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++---+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+------++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+
+-++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++-+--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+------++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-
+++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++----+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+---++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+--
++-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-+-++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+---++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+---
+-+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+---++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+----
++++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++--++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-++++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--+++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----
+++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-++--++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--+++++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----+
++-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-++++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+-++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++-++-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++
+-++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-++++++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--+-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----+++
+++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++--+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--+++++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--+-++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++
++++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--+++++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-
+++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--+++-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-+
++--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--+-++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++
+--+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-
+-+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+-+++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-+
++-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++----++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++
+-+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--++--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-+++
++++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+--+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++---++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++
+++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-++++--++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++-
++++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++-++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++--
+++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-++++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-++++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++----+--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++---
++-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-++++++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-++-+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----
+-++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-++++++++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+--+-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+
+++--+++----+----++--++-++-+-++-----+--+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++--+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-+-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++----+-+++++--++-+---++-+-+---+----+-+++----++-+-+-----++++-++-++++----+-
++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+--+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++
+-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----++++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-
+++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+
++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-+---+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+---+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++---+++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-++
+++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++--+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++--++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++
++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-+++-+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-------+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++-+-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-++++
+----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-+++++-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+---------+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++
+---+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++--++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-
+--+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++--++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+
+-+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++---+----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-+-++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+-
++--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++--------+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--
+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+---+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++--++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-+++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--+
+-+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+---+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++--+-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-+++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++
++----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+---+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++---++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-+-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--+++
+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--++---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++-----++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++
+---+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-
+--+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+-++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+
+-+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+--+++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-++
++++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+------++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++
+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+-++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+----++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-
++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----++-++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+---++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+---+--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-+
+++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+----+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++
++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--+----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++-
+-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-++++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--
++-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+
+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--++
++--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++--+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++
+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----+++--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-
+-++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-+-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+
+++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--+-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-+++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++-----+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+-
++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--+-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++---+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--
+++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--+++++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++---++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++-----+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+
++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--+++++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+---++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+-
+---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--+++++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++-+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+--
+--+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++--+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---
+-+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++--+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+
++-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+--+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+-
+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-++++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++--+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+--
+++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+--+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-+++++-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---
++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-+++-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+
+++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-+++++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-
++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-+++----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-+++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+
+-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++---++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+--++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-+++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-++
++++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++---++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+----++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++
+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+-++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+----+-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-++++
++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-++++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++
+-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-++++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++-
++-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++---+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--
+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--+
++--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++---+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++
+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--++-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-
+-+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++----++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+
++++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+-
+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-++-++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+--
++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---
+-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---+
++--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++---+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++
+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--++-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-
+-++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+
+++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+---+++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----+++---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-
++-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+
+-----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++++-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-------+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+-
+----+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-+-++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-++-++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+--
+---+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++---++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++--++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+---+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---
+--+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++---++++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+------+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+
+-+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++----+++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-+----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+----+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+-
++-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+--+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+--
+-++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----++-+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++--+--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+---
+++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+--+-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---+++++--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----
++++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---+++--+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+
+++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++-+-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++-+-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+-
++---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++-----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--
+---+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+--------++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+
+--+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+--++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+------++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-
+-+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+----++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+----++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+--
++--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++-----++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+------++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+--++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+---
+--+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+-++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+----+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+---++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+----
+-+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+-++-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+------+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+-++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----
++-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-++-+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----+
+-+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----+++--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++--+-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++
++++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++--+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--++++-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-
+++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+-++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--++-++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+
++--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-++++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-
+--++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-++++-++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-+
+-++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++
+++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++---+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-
++-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-+
+-+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++---++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++
++++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++--++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++-
+++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--+-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-++++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--
++-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-++----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--+
+-++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--+++++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-----+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++
+++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++----+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++--+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++---+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++-
++++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-+--+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++--+-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--+++--+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++--
+++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++-+----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++---++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++-+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++---
++--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----
+--+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+------+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+
+-+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+----+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+-
++-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++----+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+--
+-+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+---+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++--+++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+---
++++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+-----+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-++++++++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----
+++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-++++++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+
++++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-++++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----++
+++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++--+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----++++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-++--++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++
++-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-++++-+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++-++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+-++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++-
+-++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-++++++-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--
+++-++++----+--+----+++++-+-+--++++---+-++++-+++-+-+--+++-+--++-----+-++++---+--+-+++--++-+++-++++--+-+++++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++-+-++-++--++----+----+++--+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-++-+++++-+--++++-+++-++--+++-+--+---+---+-+++++--++-+---++-+-+---+----+--+-----++-+-++-++--++----+----+++--+
+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-+++-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-
+++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++------+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+
++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----
+---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-++---+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+---+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++---
+--++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++-++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++--+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++--
+-++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++--+-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-++++-+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-------+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++-
+++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++----+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-++++++-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+---------+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++
++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++--++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-+++
+++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++
++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---+++-++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+-+----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-+
+-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+------+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-
+++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-+++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--+---+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++--++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++
++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-+++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++--+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++--+-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-+
+++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-+++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--+++-+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++---++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-
++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-+++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--+++++---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++-----++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++
+--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-+++++++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++----+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----+++
+-++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++-++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+--+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+-++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++
+++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--+-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-++-+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+--+++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----+
++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--+-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-++++--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----
+--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++---++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+-++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+----
+-+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++-+--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-+-++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+---++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+---
++--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++----+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+--
+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+-+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++-+---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--+----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-
+-+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+-+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++-----+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+
++-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+--+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-
+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--++-+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+
++--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+--+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--++++-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-
+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-++--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++--++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++
+-+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+---+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-+-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----+
++++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+---+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+-+-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-+++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----
+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--++---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+---+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++---
++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--++---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--++++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++---++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++--
+++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++--++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+-++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+---++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++-
++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--++++-++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+--+-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++
+-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+----+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+++
++-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++
+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+
++----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-++++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-
+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++
+---+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-+++
+--+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+-------++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++
+-+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+-------++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-+++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-+
++---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+-------++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+--++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-
+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+--++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+----++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++
+--+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+--++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+----+-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-++
+-+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+--++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+
++-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-
+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+
++-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-
+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-++-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++---+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+
+++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+--+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--++-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--
++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++-
+---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++
+--+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++-+--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-++
+-+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++----+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+
++-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++----+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-
+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---++-+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++---+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+
+++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+--+-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--++-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--
++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++-
+--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++
+-+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++-+++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----+
++++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----
+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-++-++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+----
++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--++-++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++--++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+---
+++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+--
++-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-+----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+-
+-+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++++-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-+
++---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++-
+---+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++++
+--+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+-+---++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---+++
+-+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+-----++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---++
++---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+-----++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+--+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-+++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---+
+---+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+-++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+--+++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+---
+--+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+-++++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+-++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+--
+-+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+--+++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-++--++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+-
++--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--+
+--+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---++--+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+--
+-+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+---+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++-+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+-
++-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+---+-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++---+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-+
+-+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--++-+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++-
++++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+--+-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+++
+++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-++-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--++
++--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-++-+++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--+
+--++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-++++++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+--+--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+-+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++--
+-++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++-++++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-++--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+---+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++-
+++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--+++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-++--++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-++
++-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--+++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++-++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+-+--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-+
+-+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--+++----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-++++++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++-
++++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-----+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-++++++--++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--+-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+++
+++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+---+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++---++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-++
++-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-++--+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++---++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++-++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-+
+-++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++---++++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++-
+++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--+-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++++
++++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-+--+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+++-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--+++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--+++
+++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++-+----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+-+-++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--+++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--++
++--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+---++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--+++++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--+
+--+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+++++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+--
+-+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+-+++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-+-++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+-
++-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++----++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+--++---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-+
+-+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+---+---++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++-
++++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+-------++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++++
+++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----+--++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++-+-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-++++
++++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++-++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++--++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+++
+++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----+++++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---+++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-++
++-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-++++-++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----+++++--+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-+
+-+-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++++-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++---+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---+++++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+-
++-++---++++-++++--++--+--+-+--+++++-+-+----+---+-+-++---+-++--+++++-+---+---+--+-+++--++-+++-++++--+-+++++-+-+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-+---++++--+-+-+++++----+--+----++++-+-+-----+-++----+---+--++---+-++-+++-+++-+-----++--+-+++--+-+-+++-++++-++-+++++--+-+--+--++--++++-++++---++--+++++-+--++++-+++-++--+++-+--+---++++-+-----++--+-+++--+-+-+++-++++-++++----++-+-+-----++++-++-++++----+-+
diff --git a/gptqmodel/exllamav3/util/hadamard_data/hadamard_52.txt b/gptqmodel/exllamav3/util/hadamard_data/hadamard_52.txt
new file mode 100644
index 000000000..72b32aa83
--- /dev/null
+++ b/gptqmodel/exllamav3/util/hadamard_data/hadamard_52.txt
@@ -0,0 +1,52 @@
++-+--++++--+-+---++++++---++-+--++--+-++----+--+----
+-+-+--++++--+-+---++++++--+++-+--++--+--+----+--+---
++-+-+--++++----+---++++++--+++-+--++--+--+----+--+--
+-+-+-+--++++----+---+++++++-+++-+--++-----+----+--+-
+--+-+-+--+++++---+---+++++-+-+++-+--++-----+----+--+
++--+-+-+--+++++---+---++++--+-+++-+--+++----+----+--
+++--+-+-+--+++++---+---++++--+-+++-+--+-+----+----+-
++++--+-+-+--+++++---+---++++--+-+++-+----+----+----+
+++++--+-+-+--+++++---+---+-++--+-+++-+-+--+----+----
+-++++--+-+-+-++++++---+-----++--+-+++-+-+--+----+---
+--++++--+-+-+-++++++---+--+--++--+-+++---+--+----+--
++--++++--+-+---++++++---+--+--++--+-+++---+--+----+-
+-+--++++--+-+---++++++---++-+--++--+-++----+--+----+
+-+++------++++-+--++++--+--++++-++-++++++-+--++--+-+
++-+++------++-+-+--++++--++-++++-++-++++++-+--++--+-
+++-+++------++-+-+--++++--++-++++-++-++-+++-+--++--+
++++-+++-------+-+-+--++++-+++-++++-++-++-+++-+--++--
+-+++-+++-------+-+-+--++++++++-++++-++--+-+++-+--++-
+--+++-+++----+--+-+-+--+++-++++-++++-++--+-+++-+--++
+---+++-+++---++--+-+-+--+++-++++-++++-++--+-+++-+--+
+----+++-+++--+++--+-+-+--+++-++++-++++-++--+-+++-+--
+-----+++-+++-++++--+-+-+---++-++++-++++-++--+-+++-+-
+------+++-+++-++++--+-+-+-+-++-++++-+++--++--+-+++-+
++------+++-++--++++--+-+-+++-++-++++-+++--++--+-+++-
+++------+++-++--++++--+-+-+++-++-++++-+-+--++--+-+++
++++------+++--+--++++--+-+++++-++-++++-+-+--++--+-++
+--+-++--++-+-+----+--+----+-+--++++--+--+++------+++
+---+-++--++-+-+----+--+----+-+--++++--++-+++------++
++---+-++--++---+----+--+--+-+-+--++++--++-+++------+
+-+---+-++--++---+----+--+--+-+-+--++++-+++-+++------
++-+---+-++--+----+----+--+--+-+-+--++++-+++-+++-----
+++-+---+-++--+----+----+--+--+-+-+--+++--+++-+++----
+-++-+---+-++--+----+----+-++--+-+-+--++---+++-+++---
+--++-+---+-++--+----+----++++--+-+-+--+----+++-+++--
++--++-+---+-++--+----+----++++--+-+-+-------+++-+++-
+++--++-+---+--+--+----+----++++--+-+-+-------+++-+++
+-++--++-+---+--+--+----+----++++--+-+-++------+++-++
++-++--++-+------+--+----+-+--++++--+-+-++------+++-+
+-+-++--++-+------+--+----+-+--++++--+-++++------+++-
+-++++-++-++++--+-++--++-+-+---++++++---+-+--++++--+-
++-++++-++-+++---+-++--++-+-+---++++++---+-+--++++--+
+++-++++-++-+++---+-++--++---+---++++++-+-+-+--++++--
++++-++++-++-+-+---+-++--++---+---++++++-+-+-+--++++-
+++++-++++-++-+-+---+-++--++---+---+++++--+-+-+--++++
+-++++-++++-++++-+---+-++--++---+---+++++--+-+-+--+++
++-++++-++++-+-++-+---+-++-+++---+---+++++--+-+-+--++
+++-++++-++++---++-+---+-++++++---+---+++++--+-+-+--+
+-++-++++-+++++--++-+---+-++++++---+---+++++--+-+-+--
++-++-++++-+++++--++-+---+-++++++---+----++++--+-+-+-
+++-++-++++-++-++--++-+---+-++++++---+----++++--+-+-+
++++-++-++++-++-++--++-+-----++++++---+-+--++++--+-+-
+++++-++-++++--+-++--++-+-----++++++---+-+--++++--+-+
\ No newline at end of file
diff --git a/gptqmodel/exllamav3/util/hadamard_data/hadamard_92.txt b/gptqmodel/exllamav3/util/hadamard_data/hadamard_92.txt
new file mode 100644
index 000000000..34b9695c6
--- /dev/null
+++ b/gptqmodel/exllamav3/util/hadamard_data/hadamard_92.txt
@@ -0,0 +1,92 @@
++++-+++-+------+-+++-+++++---++-+-++-+-++---+++-++-++--++++++--++-++-++---+---+-++-+---+---+
+++++-+++-+------+-+++-+++++---++-+-++-+-++---+-+-++-++--++++++--++-+++++---+---+-++-+---+---
++++++-+++-+------+-+++-+++++---++-+-++-+-++---+-+-++-++--++++++--++-+-+++---+---+-++-+---+--
+-+++++-+++-+------+-+++-+++++---++-+-++-+-++--++-+-++-++--++++++--++---+++---+---+-++-+---+-
++-+++++-+++-+------+-++--+++++---++-+-++-+-++--++-+-++-++--++++++--++---+++---+---+-++-+---+
+++-+++++-+++-+------+-+---+++++---++-+-++-+-+++-++-+-++-++--++++++--++---+++---+---+-++-+---
++++-+++++-+++-+------+-+---+++++---++-+-++-+-+++-++-+-++-++--++++++---+---+++---+---+-++-+--
+-+++-+++++-+++-+------+++---+++++---++-+-++-+--++-++-+-++-++--++++++---+---+++---+---+-++-+-
++-+++-+++++-+++-+-------++---+++++---++-+-++-+--++-++-+-++-++--++++++---+---+++---+---+-++-+
+-+-+++-+++++-+++-+-----+-++---+++++---++-+-++-+--++-++-+-++-++--++++++---+---+++---+---+-++-
+--+-+++-+++++-+++-+-----+-++---+++++---++-+-++++--++-++-+-++-++--++++-+---+---+++---+---+-++
+---+-+++-+++++-+++-+---+-+-++---+++++---++-+-++++--++-++-+-++-++--++++-+---+---+++---+---+-+
+----+-+++-+++++-+++-+--++-+-++---+++++---++-+-++++--++-++-+-++-++--++++-+---+---+++---+---+-
+-----+-+++-+++++-+++-+--++-+-++---+++++---++-++++++--++-++-+-++-++--+-++-+---+---+++---+---+
+------+-+++-+++++-+++-++-++-+-++---+++++---++-++++++--++-++-+-++-++--+-++-+---+---+++---+---
++------+-+++-+++++-+++--+-++-+-++---+++++---++-++++++--++-++-+-++-++--+-++-+---+---+++---+--
+-+------+-+++-+++++-++++-+-++-+-++---+++++---+--++++++--++-++-+-++-++--+-++-+---+---+++---+-
++-+------+-+++-+++++-++++-+-++-+-++---+++++---+--++++++--++-++-+-++-+---+-++-+---+---+++---+
+++-+------+-+++-+++++-+-++-+-++-+-++---+++++--++--++++++--++-++-+-++-+---+-++-+---+---+++---
++++-+------+-+++-+++++---++-+-++-+-++---+++++--++--++++++--++-++-+-++-+---+-++-+---+---+++--
+-+++-+------+-+++-+++++---++-+-++-+-++---++++++-++--++++++--++-++-+-+--+---+-++-+---+---+++-
++-+++-+------+-+++-+++++---++-+-++-+-++---++++++-++--++++++--++-++-+----+---+-++-+---+---+++
+++-+++-+------+-+++-+++++---++-+-++-+-++---+++-++-++--++++++--++-++-++---+---+-++-+---+---++
+---+++--+-+--+-+--+++--+++-+++-+------+-+++-++--+++-+++-+--+-+++-+++-+-++-++--++++++--++-++-
+----+++--+-+--+-+--+++-++++-+++-+------+-+++-+---+++-+++-+--+-+++-+++-+-++-++--++++++--++-++
+-----+++--+-+--+-+--++++++++-+++-+------+-+++-+---+++-+++-+--+-+++-+++-+-++-++--++++++--++-+
++-----+++--+-+--+-+--++-+++++-+++-+------+-+++++---+++-+++-+--+-+++-+++-+-++-++--++++++--++-
+++-----+++--+-+--+-+--++-+++++-+++-+------+-+++++---+++-+++-+--+-+++--++-+-++-++--++++++--++
++++-----+++--+-+--+-+--++-+++++-+++-+------+-+-+++---+++-+++-+--+-++++-++-+-++-++--++++++--+
+-+++-----+++--+-+--+-+-+++-+++++-+++-+------+-+-+++---+++-+++-+--+-++++-++-+-++-++--++++++--
+--+++-----+++--+-+--+-+-+++-+++++-+++-+------+++-+++---+++-+++-+--+-+-++-++-+-++-++--++++++-
++--+++-----+++--+-+--+-+-+++-+++++-+++-+------+++-+++---+++-+++-+--+---++-++-+-++-++--++++++
+-+--+++-----+++--+-+--+-+-+++-+++++-+++-+------+++-+++---+++-+++-+--++--++-++-+-++-++--+++++
++-+--+++-----+++--+-+----+-+++-+++++-+++-+----+-+++-+++---+++-+++-+--++--++-++-+-++-++--++++
+-+-+--+++-----+++--+-+----+-+++-+++++-+++-+----+-+++-+++---+++-+++-+-+++--++-++-+-++-++--+++
+--+-+--+++-----+++--+-+----+-+++-+++++-+++-+----+-+++-+++---+++-+++-+++++--++-++-+-++-++--++
++--+-+--+++-----+++--+------+-+++-+++++-+++-+-+--+-+++-+++---+++-+++-+++++--++-++-+-++-++--+
+-+--+-+--+++-----+++--+------+-+++-+++++-+++-+-+--+-+++-+++---+++-+++++++++--++-++-+-++-++--
++-+--+-+--+++-----+++--+------+-+++-+++++-+++-+-+--+-+++-+++---+++-++-++++++--++-++-+-++-++-
+-+-+--+-+--+++-----+++--+------+-+++-+++++-+++++-+--+-+++-+++---+++-+--++++++--++-++-+-++-++
+--+-+--+-+--+++-----++++-+------+-+++-+++++-+++++-+--+-+++-+++---+++-+--++++++--++-++-+-++-+
++--+-+--+-+--+++-----++++-+------+-+++-+++++-+-+++-+--+-+++-+++---+++++--++++++--++-++-+-++-
+++--+-+--+-+--+++-----++++-+------+-+++-+++++-+-+++-+--+-+++-+++---++-++--++++++--++-++-+-++
++++--+-+--+-+--+++------+++-+------+-+++-+++++++-+++-+--+-+++-+++---++-++--++++++--++-++-+-+
+-+++--+-+--+-+--+++----+-+++-+------+-+++-+++++++-+++-+--+-+++-+++---++-++--++++++--++-++-+-
+--+++--+-+--+-+--+++---++-+++-+------+-+++-+++-+++-+++-+--+-+++-+++---++-++--++++++--++-++-+
+-+--+--++------++--+--+++---+---+-++-+---+---++++-+++-+------+-+++-++---+++--+-+--+-+--+++--
++-+--+--++------++--+--+++---+---+-++-+---+---++++-+++-+------+-+++-+----+++--+-+--+-+--+++-
+-+-+--+--++------++--+--+++---+---+-++-+---+--+++++-+++-+------+-+++------+++--+-+--+-+--+++
+--+-+--+--++------++--+--+++---+---+-++-+---+--+++++-+++-+------+-++++-----+++--+-+--+-+--++
++--+-+--+--++------++-----+++---+---+-++-+---++-+++++-+++-+------+-++++-----+++--+-+--+-+--+
+-+--+-+--+--++------++-+---+++---+---+-++-+---++-+++++-+++-+------+-++++-----+++--+-+--+-+--
+--+--+-+--+--++------++-+---+++---+---+-++-+--+++-+++++-+++-+------+--+++-----+++--+-+--+-+-
++--+--+-+--+--++------+--+---+++---+---+-++-+--+++-+++++-+++-+------+--+++-----+++--+-+--+-+
+++--+--+-+--+--++---------+---+++---+---+-++-++-+++-+++++-+++-+------+--+++-----+++--+-+--+-
+-++--+--+-+--+--++-----+---+---+++---+---+-++--+-+++-+++++-+++-+------+--+++-----+++--+-+--+
+--++--+--+-+--+--++-----+---+---+++---+---+-++--+-+++-+++++-+++-+----+-+--+++-----+++--+-+--
+---++--+--+-+--+--++---+-+---+---+++---+---+-+---+-+++-+++++-+++-+----+-+--+++-----+++--+-+-
+----++--+--+-+--+--++--++-+---+---+++---+---+-----+-+++-+++++-+++-+----+-+--+++-----+++--+-+
+-----++--+--+-+--+--++--++-+---+---+++---+---+-----+-+++-+++++-+++-+-+--+-+--+++-----+++--+-
+------++--+--+-+--+--+++-++-+---+---+++---+---------+-+++-+++++-+++-+-+--+-+--+++-----+++--+
++------++--+--+-+--+--+-+-++-+---+---+++---+--+------+-+++-+++++-+++-+-+--+-+--+++-----+++--
+++------++--+--+-+--+----+-++-+---+---+++---+--+------+-+++-+++++-+++-+-+--+-+--+++-----+++-
+-++------++--+--+-+--+----+-++-+---+---+++---++-+------+-+++-+++++-++--+-+--+-+--+++-----+++
+--++------++--+--+-+--++---+-++-+---+---+++---++-+------+-+++-+++++-++--+-+--+-+--+++-----++
++--++------++--+--+-+---+---+-++-+---+---+++--+++-+------+-+++-+++++-++--+-+--+-+--+++-----+
+-+--++------++--+--+-+---+---+-++-+---+---+++--+++-+------+-+++-++++++++--+-+--+-+--+++-----
+--+--++------++--+--+-+---+---+-++-+---+---++++-+++-+------+-+++-++++-+++--+-+--+-+--+++----
++--+--++------++--+--+-+---+---+-++-+---+---++++-+++-+------+-+++-+++--+++--+-+--+-+--+++---
+--+++-+++-+--+-+++-+++--+--+--++------++--+--++++---++-+-++-+-++---+++++-+++-+------+-+++-++
+---+++-+++-+--+-+++-++++-+--+--++------++--+--++++---++-+-++-+-++---+++++-+++-+------+-+++-+
++---+++-+++-+--+-+++-++-+-+--+--++------++--+-+++++---++-+-++-+-++---+++++-+++-+------+-+++-
+++---+++-+++-+--+-+++-+--+-+--+--++------++--+-+++++---++-+-++-+-++---+++++-+++-+------+-+++
++++---+++-+++-+--+-+++-+--+-+--+--++------++----+++++---++-+-++-+-++-+-+++++-+++-+------+-++
+-+++---+++-+++-+--+-+++-+--+-+--+--++------++----+++++---++-+-++-+-++++-+++++-+++-+------+-+
++-+++---+++-+++-+--+-++--+--+-+--+--++------+++---+++++---++-+-++-+-++++-+++++-+++-+------+-
+++-+++---+++-+++-+--+-++--+--+-+--+--++------+++---+++++---++-+-++-+--+++-+++++-+++-+------+
++++-+++---+++-+++-+--+-++--+--+-+--+--++-------++---+++++---++-+-++-++-+++-+++++-+++-+------
+-+++-+++---+++-+++-+--+-++--+--+-+--+--++-----+-++---+++++---++-+-++--+-+++-+++++-+++-+-----
++-+++-+++---+++-+++-+----++--+--+-+--+--++-----+-++---+++++---++-+-++--+-+++-+++++-+++-+----
+-+-+++-+++---+++-+++-+----++--+--+-+--+--++---+-+-++---+++++---++-+-+---+-+++-+++++-+++-+---
+--+-+++-+++---+++-+++-+----++--+--+-+--+--++--++-+-++---+++++---++-+-----+-+++-+++++-+++-+--
++--+-+++-+++---+++-+++------++--+--+-+--+--++--++-+-++---+++++---++-+-----+-+++-+++++-+++-+-
+-+--+-+++-+++---+++-+++------++--+--+-+--+--+++-++-+-++---+++++---++-------+-+++-+++++-+++-+
++-+--+-+++-+++---+++-+++------++--+--+-+--+--+-+-++-+-++---+++++---+++------+-+++-+++++-+++-
+++-+--+-+++-+++---+++-+++------++--+--+-+--+--+-+-++-+-++---+++++---+-+------+-+++-+++++-+++
++++-+--+-+++-+++---+++--++------++--+--+-+--+-++-+-++-+-++---+++++---+-+------+-+++-+++++-++
+-+++-+--+-+++-+++---+++--++------++--+--+-+--+-++-+-++-+-++---+++++--++-+------+-+++-+++++-+
++-+++-+--+-+++-+++---+++--++------++--+--+-+----++-+-++-+-++---+++++-+++-+------+-+++-+++++-
+++-+++-+--+-+++-+++---+-+--++------++--+--+-+----++-+-++-+-++---+++++-+++-+------+-+++-+++++
++++-+++-+--+-+++-+++-----+--++------++--+--+-++---++-+-++-+-++---+++++-+++-+------+-+++-++++
+-+++-+++-+--+-+++-+++--+--+--++------++--+--+-++---++-+-++-+-++---+++++-+++-+------+-+++-+++
\ No newline at end of file
diff --git a/gptqmodel/exllamav3/util/hadamard_data/primes.txt b/gptqmodel/exllamav3/util/hadamard_data/primes.txt
new file mode 100644
index 000000000..cf15629ed
--- /dev/null
+++ b/gptqmodel/exllamav3/util/hadamard_data/primes.txt
@@ -0,0 +1,10000 @@
+2
+3
+5
+7
+11
+13
+17
+19
+23
+29
+31
+37
+41
+43
+47
+53
+59
+61
+67
+71
+73
+79
+83
+89
+97
+101
+103
+107
+109
+113
+127
+131
+137
+139
+149
+151
+157
+163
+167
+173
+179
+181
+191
+193
+197
+199
+211
+223
+227
+229
+233
+239
+241
+251
+257
+263
+269
+271
+277
+281
+283
+293
+307
+311
+313
+317
+331
+337
+347
+349
+353
+359
+367
+373
+379
+383
+389
+397
+401
+409
+419
+421
+431
+433
+439
+443
+449
+457
+461
+463
+467
+479
+487
+491
+499
+503
+509
+521
+523
+541
+547
+557
+563
+569
+571
+577
+587
+593
+599
+601
+607
+613
+617
+619
+631
+641
+643
+647
+653
+659
+661
+673
+677
+683
+691
+701
+709
+719
+727
+733
+739
+743
+751
+757
+761
+769
+773
+787
+797
+809
+811
+821
+823
+827
+829
+839
+853
+857
+859
+863
+877
+881
+883
+887
+907
+911
+919
+929
+937
+941
+947
+953
+967
+971
+977
+983
+991
+997
+1009
+1013
+1019
+1021
+1031
+1033
+1039
+1049
+1051
+1061
+1063
+1069
+1087
+1091
+1093
+1097
+1103
+1109
+1117
+1123
+1129
+1151
+1153
+1163
+1171
+1181
+1187
+1193
+1201
+1213
+1217
+1223
+1229
+1231
+1237
+1249
+1259
+1277
+1279
+1283
+1289
+1291
+1297
+1301
+1303
+1307
+1319
+1321
+1327
+1361
+1367
+1373
+1381
+1399
+1409
+1423
+1427
+1429
+1433
+1439
+1447
+1451
+1453
+1459
+1471
+1481
+1483
+1487
+1489
+1493
+1499
+1511
+1523
+1531
+1543
+1549
+1553
+1559
+1567
+1571
+1579
+1583
+1597
+1601
+1607
+1609
+1613
+1619
+1621
+1627
+1637
+1657
+1663
+1667
+1669
+1693
+1697
+1699
+1709
+1721
+1723
+1733
+1741
+1747
+1753
+1759
+1777
+1783
+1787
+1789
+1801
+1811
+1823
+1831
+1847
+1861
+1867
+1871
+1873
+1877
+1879
+1889
+1901
+1907
+1913
+1931
+1933
+1949
+1951
+1973
+1979
+1987
+1993
+1997
+1999
+2003
+2011
+2017
+2027
+2029
+2039
+2053
+2063
+2069
+2081
+2083
+2087
+2089
+2099
+2111
+2113
+2129
+2131
+2137
+2141
+2143
+2153
+2161
+2179
+2203
+2207
+2213
+2221
+2237
+2239
+2243
+2251
+2267
+2269
+2273
+2281
+2287
+2293
+2297
+2309
+2311
+2333
+2339
+2341
+2347
+2351
+2357
+2371
+2377
+2381
+2383
+2389
+2393
+2399
+2411
+2417
+2423
+2437
+2441
+2447
+2459
+2467
+2473
+2477
+2503
+2521
+2531
+2539
+2543
+2549
+2551
+2557
+2579
+2591
+2593
+2609
+2617
+2621
+2633
+2647
+2657
+2659
+2663
+2671
+2677
+2683
+2687
+2689
+2693
+2699
+2707
+2711
+2713
+2719
+2729
+2731
+2741
+2749
+2753
+2767
+2777
+2789
+2791
+2797
+2801
+2803
+2819
+2833
+2837
+2843
+2851
+2857
+2861
+2879
+2887
+2897
+2903
+2909
+2917
+2927
+2939
+2953
+2957
+2963
+2969
+2971
+2999
+3001
+3011
+3019
+3023
+3037
+3041
+3049
+3061
+3067
+3079
+3083
+3089
+3109
+3119
+3121
+3137
+3163
+3167
+3169
+3181
+3187
+3191
+3203
+3209
+3217
+3221
+3229
+3251
+3253
+3257
+3259
+3271
+3299
+3301
+3307
+3313
+3319
+3323
+3329
+3331
+3343
+3347
+3359
+3361
+3371
+3373
+3389
+3391
+3407
+3413
+3433
+3449
+3457
+3461
+3463
+3467
+3469
+3491
+3499
+3511
+3517
+3527
+3529
+3533
+3539
+3541
+3547
+3557
+3559
+3571
+3581
+3583
+3593
+3607
+3613
+3617
+3623
+3631
+3637
+3643
+3659
+3671
+3673
+3677
+3691
+3697
+3701
+3709
+3719
+3727
+3733
+3739
+3761
+3767
+3769
+3779
+3793
+3797
+3803
+3821
+3823
+3833
+3847
+3851
+3853
+3863
+3877
+3881
+3889
+3907
+3911
+3917
+3919
+3923
+3929
+3931
+3943
+3947
+3967
+3989
+4001
+4003
+4007
+4013
+4019
+4021
+4027
+4049
+4051
+4057
+4073
+4079
+4091
+4093
+4099
+4111
+4127
+4129
+4133
+4139
+4153
+4157
+4159
+4177
+4201
+4211
+4217
+4219
+4229
+4231
+4241
+4243
+4253
+4259
+4261
+4271
+4273
+4283
+4289
+4297
+4327
+4337
+4339
+4349
+4357
+4363
+4373
+4391
+4397
+4409
+4421
+4423
+4441
+4447
+4451
+4457
+4463
+4481
+4483
+4493
+4507
+4513
+4517
+4519
+4523
+4547
+4549
+4561
+4567
+4583
+4591
+4597
+4603
+4621
+4637
+4639
+4643
+4649
+4651
+4657
+4663
+4673
+4679
+4691
+4703
+4721
+4723
+4729
+4733
+4751
+4759
+4783
+4787
+4789
+4793
+4799
+4801
+4813
+4817
+4831
+4861
+4871
+4877
+4889
+4903
+4909
+4919
+4931
+4933
+4937
+4943
+4951
+4957
+4967
+4969
+4973
+4987
+4993
+4999
+5003
+5009
+5011
+5021
+5023
+5039
+5051
+5059
+5077
+5081
+5087
+5099
+5101
+5107
+5113
+5119
+5147
+5153
+5167
+5171
+5179
+5189
+5197
+5209
+5227
+5231
+5233
+5237
+5261
+5273
+5279
+5281
+5297
+5303
+5309
+5323
+5333
+5347
+5351
+5381
+5387
+5393
+5399
+5407
+5413
+5417
+5419
+5431
+5437
+5441
+5443
+5449
+5471
+5477
+5479
+5483
+5501
+5503
+5507
+5519
+5521
+5527
+5531
+5557
+5563
+5569
+5573
+5581
+5591
+5623
+5639
+5641
+5647
+5651
+5653
+5657
+5659
+5669
+5683
+5689
+5693
+5701
+5711
+5717
+5737
+5741
+5743
+5749
+5779
+5783
+5791
+5801
+5807
+5813
+5821
+5827
+5839
+5843
+5849
+5851
+5857
+5861
+5867
+5869
+5879
+5881
+5897
+5903
+5923
+5927
+5939
+5953
+5981
+5987
+6007
+6011
+6029
+6037
+6043
+6047
+6053
+6067
+6073
+6079
+6089
+6091
+6101
+6113
+6121
+6131
+6133
+6143
+6151
+6163
+6173
+6197
+6199
+6203
+6211
+6217
+6221
+6229
+6247
+6257
+6263
+6269
+6271
+6277
+6287
+6299
+6301
+6311
+6317
+6323
+6329
+6337
+6343
+6353
+6359
+6361
+6367
+6373
+6379
+6389
+6397
+6421
+6427
+6449
+6451
+6469
+6473
+6481
+6491
+6521
+6529
+6547
+6551
+6553
+6563
+6569
+6571
+6577
+6581
+6599
+6607
+6619
+6637
+6653
+6659
+6661
+6673
+6679
+6689
+6691
+6701
+6703
+6709
+6719
+6733
+6737
+6761
+6763
+6779
+6781
+6791
+6793
+6803
+6823
+6827
+6829
+6833
+6841
+6857
+6863
+6869
+6871
+6883
+6899
+6907
+6911
+6917
+6947
+6949
+6959
+6961
+6967
+6971
+6977
+6983
+6991
+6997
+7001
+7013
+7019
+7027
+7039
+7043
+7057
+7069
+7079
+7103
+7109
+7121
+7127
+7129
+7151
+7159
+7177
+7187
+7193
+7207
+7211
+7213
+7219
+7229
+7237
+7243
+7247
+7253
+7283
+7297
+7307
+7309
+7321
+7331
+7333
+7349
+7351
+7369
+7393
+7411
+7417
+7433
+7451
+7457
+7459
+7477
+7481
+7487
+7489
+7499
+7507
+7517
+7523
+7529
+7537
+7541
+7547
+7549
+7559
+7561
+7573
+7577
+7583
+7589
+7591
+7603
+7607
+7621
+7639
+7643
+7649
+7669
+7673
+7681
+7687
+7691
+7699
+7703
+7717
+7723
+7727
+7741
+7753
+7757
+7759
+7789
+7793
+7817
+7823
+7829
+7841
+7853
+7867
+7873
+7877
+7879
+7883
+7901
+7907
+7919
+7927
+7933
+7937
+7949
+7951
+7963
+7993
+8009
+8011
+8017
+8039
+8053
+8059
+8069
+8081
+8087
+8089
+8093
+8101
+8111
+8117
+8123
+8147
+8161
+8167
+8171
+8179
+8191
+8209
+8219
+8221
+8231
+8233
+8237
+8243
+8263
+8269
+8273
+8287
+8291
+8293
+8297
+8311
+8317
+8329
+8353
+8363
+8369
+8377
+8387
+8389
+8419
+8423
+8429
+8431
+8443
+8447
+8461
+8467
+8501
+8513
+8521
+8527
+8537
+8539
+8543
+8563
+8573
+8581
+8597
+8599
+8609
+8623
+8627
+8629
+8641
+8647
+8663
+8669
+8677
+8681
+8689
+8693
+8699
+8707
+8713
+8719
+8731
+8737
+8741
+8747
+8753
+8761
+8779
+8783
+8803
+8807
+8819
+8821
+8831
+8837
+8839
+8849
+8861
+8863
+8867
+8887
+8893
+8923
+8929
+8933
+8941
+8951
+8963
+8969
+8971
+8999
+9001
+9007
+9011
+9013
+9029
+9041
+9043
+9049
+9059
+9067
+9091
+9103
+9109
+9127
+9133
+9137
+9151
+9157
+9161
+9173
+9181
+9187
+9199
+9203
+9209
+9221
+9227
+9239
+9241
+9257
+9277
+9281
+9283
+9293
+9311
+9319
+9323
+9337
+9341
+9343
+9349
+9371
+9377
+9391
+9397
+9403
+9413
+9419
+9421
+9431
+9433
+9437
+9439
+9461
+9463
+9467
+9473
+9479
+9491
+9497
+9511
+9521
+9533
+9539
+9547
+9551
+9587
+9601
+9613
+9619
+9623
+9629
+9631
+9643
+9649
+9661
+9677
+9679
+9689
+9697
+9719
+9721
+9733
+9739
+9743
+9749
+9767
+9769
+9781
+9787
+9791
+9803
+9811
+9817
+9829
+9833
+9839
+9851
+9857
+9859
+9871
+9883
+9887
+9901
+9907
+9923
+9929
+9931
+9941
+9949
+9967
+9973
+10007
+10009
+10037
+10039
+10061
+10067
+10069
+10079
+10091
+10093
+10099
+10103
+10111
+10133
+10139
+10141
+10151
+10159
+10163
+10169
+10177
+10181
+10193
+10211
+10223
+10243
+10247
+10253
+10259
+10267
+10271
+10273
+10289
+10301
+10303
+10313
+10321
+10331
+10333
+10337
+10343
+10357
+10369
+10391
+10399
+10427
+10429
+10433
+10453
+10457
+10459
+10463
+10477
+10487
+10499
+10501
+10513
+10529
+10531
+10559
+10567
+10589
+10597
+10601
+10607
+10613
+10627
+10631
+10639
+10651
+10657
+10663
+10667
+10687
+10691
+10709
+10711
+10723
+10729
+10733
+10739
+10753
+10771
+10781
+10789
+10799
+10831
+10837
+10847
+10853
+10859
+10861
+10867
+10883
+10889
+10891
+10903
+10909
+10937
+10939
+10949
+10957
+10973
+10979
+10987
+10993
+11003
+11027
+11047
+11057
+11059
+11069
+11071
+11083
+11087
+11093
+11113
+11117
+11119
+11131
+11149
+11159
+11161
+11171
+11173
+11177
+11197
+11213
+11239
+11243
+11251
+11257
+11261
+11273
+11279
+11287
+11299
+11311
+11317
+11321
+11329
+11351
+11353
+11369
+11383
+11393
+11399
+11411
+11423
+11437
+11443
+11447
+11467
+11471
+11483
+11489
+11491
+11497
+11503
+11519
+11527
+11549
+11551
+11579
+11587
+11593
+11597
+11617
+11621
+11633
+11657
+11677
+11681
+11689
+11699
+11701
+11717
+11719
+11731
+11743
+11777
+11779
+11783
+11789
+11801
+11807
+11813
+11821
+11827
+11831
+11833
+11839
+11863
+11867
+11887
+11897
+11903
+11909
+11923
+11927
+11933
+11939
+11941
+11953
+11959
+11969
+11971
+11981
+11987
+12007
+12011
+12037
+12041
+12043
+12049
+12071
+12073
+12097
+12101
+12107
+12109
+12113
+12119
+12143
+12149
+12157
+12161
+12163
+12197
+12203
+12211
+12227
+12239
+12241
+12251
+12253
+12263
+12269
+12277
+12281
+12289
+12301
+12323
+12329
+12343
+12347
+12373
+12377
+12379
+12391
+12401
+12409
+12413
+12421
+12433
+12437
+12451
+12457
+12473
+12479
+12487
+12491
+12497
+12503
+12511
+12517
+12527
+12539
+12541
+12547
+12553
+12569
+12577
+12583
+12589
+12601
+12611
+12613
+12619
+12637
+12641
+12647
+12653
+12659
+12671
+12689
+12697
+12703
+12713
+12721
+12739
+12743
+12757
+12763
+12781
+12791
+12799
+12809
+12821
+12823
+12829
+12841
+12853
+12889
+12893
+12899
+12907
+12911
+12917
+12919
+12923
+12941
+12953
+12959
+12967
+12973
+12979
+12983
+13001
+13003
+13007
+13009
+13033
+13037
+13043
+13049
+13063
+13093
+13099
+13103
+13109
+13121
+13127
+13147
+13151
+13159
+13163
+13171
+13177
+13183
+13187
+13217
+13219
+13229
+13241
+13249
+13259
+13267
+13291
+13297
+13309
+13313
+13327
+13331
+13337
+13339
+13367
+13381
+13397
+13399
+13411
+13417
+13421
+13441
+13451
+13457
+13463
+13469
+13477
+13487
+13499
+13513
+13523
+13537
+13553
+13567
+13577
+13591
+13597
+13613
+13619
+13627
+13633
+13649
+13669
+13679
+13681
+13687
+13691
+13693
+13697
+13709
+13711
+13721
+13723
+13729
+13751
+13757
+13759
+13763
+13781
+13789
+13799
+13807
+13829
+13831
+13841
+13859
+13873
+13877
+13879
+13883
+13901
+13903
+13907
+13913
+13921
+13931
+13933
+13963
+13967
+13997
+13999
+14009
+14011
+14029
+14033
+14051
+14057
+14071
+14081
+14083
+14087
+14107
+14143
+14149
+14153
+14159
+14173
+14177
+14197
+14207
+14221
+14243
+14249
+14251
+14281
+14293
+14303
+14321
+14323
+14327
+14341
+14347
+14369
+14387
+14389
+14401
+14407
+14411
+14419
+14423
+14431
+14437
+14447
+14449
+14461
+14479
+14489
+14503
+14519
+14533
+14537
+14543
+14549
+14551
+14557
+14561
+14563
+14591
+14593
+14621
+14627
+14629
+14633
+14639
+14653
+14657
+14669
+14683
+14699
+14713
+14717
+14723
+14731
+14737
+14741
+14747
+14753
+14759
+14767
+14771
+14779
+14783
+14797
+14813
+14821
+14827
+14831
+14843
+14851
+14867
+14869
+14879
+14887
+14891
+14897
+14923
+14929
+14939
+14947
+14951
+14957
+14969
+14983
+15013
+15017
+15031
+15053
+15061
+15073
+15077
+15083
+15091
+15101
+15107
+15121
+15131
+15137
+15139
+15149
+15161
+15173
+15187
+15193
+15199
+15217
+15227
+15233
+15241
+15259
+15263
+15269
+15271
+15277
+15287
+15289
+15299
+15307
+15313
+15319
+15329
+15331
+15349
+15359
+15361
+15373
+15377
+15383
+15391
+15401
+15413
+15427
+15439
+15443
+15451
+15461
+15467
+15473
+15493
+15497
+15511
+15527
+15541
+15551
+15559
+15569
+15581
+15583
+15601
+15607
+15619
+15629
+15641
+15643
+15647
+15649
+15661
+15667
+15671
+15679
+15683
+15727
+15731
+15733
+15737
+15739
+15749
+15761
+15767
+15773
+15787
+15791
+15797
+15803
+15809
+15817
+15823
+15859
+15877
+15881
+15887
+15889
+15901
+15907
+15913
+15919
+15923
+15937
+15959
+15971
+15973
+15991
+16001
+16007
+16033
+16057
+16061
+16063
+16067
+16069
+16073
+16087
+16091
+16097
+16103
+16111
+16127
+16139
+16141
+16183
+16187
+16189
+16193
+16217
+16223
+16229
+16231
+16249
+16253
+16267
+16273
+16301
+16319
+16333
+16339
+16349
+16361
+16363
+16369
+16381
+16411
+16417
+16421
+16427
+16433
+16447
+16451
+16453
+16477
+16481
+16487
+16493
+16519
+16529
+16547
+16553
+16561
+16567
+16573
+16603
+16607
+16619
+16631
+16633
+16649
+16651
+16657
+16661
+16673
+16691
+16693
+16699
+16703
+16729
+16741
+16747
+16759
+16763
+16787
+16811
+16823
+16829
+16831
+16843
+16871
+16879
+16883
+16889
+16901
+16903
+16921
+16927
+16931
+16937
+16943
+16963
+16979
+16981
+16987
+16993
+17011
+17021
+17027
+17029
+17033
+17041
+17047
+17053
+17077
+17093
+17099
+17107
+17117
+17123
+17137
+17159
+17167
+17183
+17189
+17191
+17203
+17207
+17209
+17231
+17239
+17257
+17291
+17293
+17299
+17317
+17321
+17327
+17333
+17341
+17351
+17359
+17377
+17383
+17387
+17389
+17393
+17401
+17417
+17419
+17431
+17443
+17449
+17467
+17471
+17477
+17483
+17489
+17491
+17497
+17509
+17519
+17539
+17551
+17569
+17573
+17579
+17581
+17597
+17599
+17609
+17623
+17627
+17657
+17659
+17669
+17681
+17683
+17707
+17713
+17729
+17737
+17747
+17749
+17761
+17783
+17789
+17791
+17807
+17827
+17837
+17839
+17851
+17863
+17881
+17891
+17903
+17909
+17911
+17921
+17923
+17929
+17939
+17957
+17959
+17971
+17977
+17981
+17987
+17989
+18013
+18041
+18043
+18047
+18049
+18059
+18061
+18077
+18089
+18097
+18119
+18121
+18127
+18131
+18133
+18143
+18149
+18169
+18181
+18191
+18199
+18211
+18217
+18223
+18229
+18233
+18251
+18253
+18257
+18269
+18287
+18289
+18301
+18307
+18311
+18313
+18329
+18341
+18353
+18367
+18371
+18379
+18397
+18401
+18413
+18427
+18433
+18439
+18443
+18451
+18457
+18461
+18481
+18493
+18503
+18517
+18521
+18523
+18539
+18541
+18553
+18583
+18587
+18593
+18617
+18637
+18661
+18671
+18679
+18691
+18701
+18713
+18719
+18731
+18743
+18749
+18757
+18773
+18787
+18793
+18797
+18803
+18839
+18859
+18869
+18899
+18911
+18913
+18917
+18919
+18947
+18959
+18973
+18979
+19001
+19009
+19013
+19031
+19037
+19051
+19069
+19073
+19079
+19081
+19087
+19121
+19139
+19141
+19157
+19163
+19181
+19183
+19207
+19211
+19213
+19219
+19231
+19237
+19249
+19259
+19267
+19273
+19289
+19301
+19309
+19319
+19333
+19373
+19379
+19381
+19387
+19391
+19403
+19417
+19421
+19423
+19427
+19429
+19433
+19441
+19447
+19457
+19463
+19469
+19471
+19477
+19483
+19489
+19501
+19507
+19531
+19541
+19543
+19553
+19559
+19571
+19577
+19583
+19597
+19603
+19609
+19661
+19681
+19687
+19697
+19699
+19709
+19717
+19727
+19739
+19751
+19753
+19759
+19763
+19777
+19793
+19801
+19813
+19819
+19841
+19843
+19853
+19861
+19867
+19889
+19891
+19913
+19919
+19927
+19937
+19949
+19961
+19963
+19973
+19979
+19991
+19993
+19997
+20011
+20021
+20023
+20029
+20047
+20051
+20063
+20071
+20089
+20101
+20107
+20113
+20117
+20123
+20129
+20143
+20147
+20149
+20161
+20173
+20177
+20183
+20201
+20219
+20231
+20233
+20249
+20261
+20269
+20287
+20297
+20323
+20327
+20333
+20341
+20347
+20353
+20357
+20359
+20369
+20389
+20393
+20399
+20407
+20411
+20431
+20441
+20443
+20477
+20479
+20483
+20507
+20509
+20521
+20533
+20543
+20549
+20551
+20563
+20593
+20599
+20611
+20627
+20639
+20641
+20663
+20681
+20693
+20707
+20717
+20719
+20731
+20743
+20747
+20749
+20753
+20759
+20771
+20773
+20789
+20807
+20809
+20849
+20857
+20873
+20879
+20887
+20897
+20899
+20903
+20921
+20929
+20939
+20947
+20959
+20963
+20981
+20983
+21001
+21011
+21013
+21017
+21019
+21023
+21031
+21059
+21061
+21067
+21089
+21101
+21107
+21121
+21139
+21143
+21149
+21157
+21163
+21169
+21179
+21187
+21191
+21193
+21211
+21221
+21227
+21247
+21269
+21277
+21283
+21313
+21317
+21319
+21323
+21341
+21347
+21377
+21379
+21383
+21391
+21397
+21401
+21407
+21419
+21433
+21467
+21481
+21487
+21491
+21493
+21499
+21503
+21517
+21521
+21523
+21529
+21557
+21559
+21563
+21569
+21577
+21587
+21589
+21599
+21601
+21611
+21613
+21617
+21647
+21649
+21661
+21673
+21683
+21701
+21713
+21727
+21737
+21739
+21751
+21757
+21767
+21773
+21787
+21799
+21803
+21817
+21821
+21839
+21841
+21851
+21859
+21863
+21871
+21881
+21893
+21911
+21929
+21937
+21943
+21961
+21977
+21991
+21997
+22003
+22013
+22027
+22031
+22037
+22039
+22051
+22063
+22067
+22073
+22079
+22091
+22093
+22109
+22111
+22123
+22129
+22133
+22147
+22153
+22157
+22159
+22171
+22189
+22193
+22229
+22247
+22259
+22271
+22273
+22277
+22279
+22283
+22291
+22303
+22307
+22343
+22349
+22367
+22369
+22381
+22391
+22397
+22409
+22433
+22441
+22447
+22453
+22469
+22481
+22483
+22501
+22511
+22531
+22541
+22543
+22549
+22567
+22571
+22573
+22613
+22619
+22621
+22637
+22639
+22643
+22651
+22669
+22679
+22691
+22697
+22699
+22709
+22717
+22721
+22727
+22739
+22741
+22751
+22769
+22777
+22783
+22787
+22807
+22811
+22817
+22853
+22859
+22861
+22871
+22877
+22901
+22907
+22921
+22937
+22943
+22961
+22963
+22973
+22993
+23003
+23011
+23017
+23021
+23027
+23029
+23039
+23041
+23053
+23057
+23059
+23063
+23071
+23081
+23087
+23099
+23117
+23131
+23143
+23159
+23167
+23173
+23189
+23197
+23201
+23203
+23209
+23227
+23251
+23269
+23279
+23291
+23293
+23297
+23311
+23321
+23327
+23333
+23339
+23357
+23369
+23371
+23399
+23417
+23431
+23447
+23459
+23473
+23497
+23509
+23531
+23537
+23539
+23549
+23557
+23561
+23563
+23567
+23581
+23593
+23599
+23603
+23609
+23623
+23627
+23629
+23633
+23663
+23669
+23671
+23677
+23687
+23689
+23719
+23741
+23743
+23747
+23753
+23761
+23767
+23773
+23789
+23801
+23813
+23819
+23827
+23831
+23833
+23857
+23869
+23873
+23879
+23887
+23893
+23899
+23909
+23911
+23917
+23929
+23957
+23971
+23977
+23981
+23993
+24001
+24007
+24019
+24023
+24029
+24043
+24049
+24061
+24071
+24077
+24083
+24091
+24097
+24103
+24107
+24109
+24113
+24121
+24133
+24137
+24151
+24169
+24179
+24181
+24197
+24203
+24223
+24229
+24239
+24247
+24251
+24281
+24317
+24329
+24337
+24359
+24371
+24373
+24379
+24391
+24407
+24413
+24419
+24421
+24439
+24443
+24469
+24473
+24481
+24499
+24509
+24517
+24527
+24533
+24547
+24551
+24571
+24593
+24611
+24623
+24631
+24659
+24671
+24677
+24683
+24691
+24697
+24709
+24733
+24749
+24763
+24767
+24781
+24793
+24799
+24809
+24821
+24841
+24847
+24851
+24859
+24877
+24889
+24907
+24917
+24919
+24923
+24943
+24953
+24967
+24971
+24977
+24979
+24989
+25013
+25031
+25033
+25037
+25057
+25073
+25087
+25097
+25111
+25117
+25121
+25127
+25147
+25153
+25163
+25169
+25171
+25183
+25189
+25219
+25229
+25237
+25243
+25247
+25253
+25261
+25301
+25303
+25307
+25309
+25321
+25339
+25343
+25349
+25357
+25367
+25373
+25391
+25409
+25411
+25423
+25439
+25447
+25453
+25457
+25463
+25469
+25471
+25523
+25537
+25541
+25561
+25577
+25579
+25583
+25589
+25601
+25603
+25609
+25621
+25633
+25639
+25643
+25657
+25667
+25673
+25679
+25693
+25703
+25717
+25733
+25741
+25747
+25759
+25763
+25771
+25793
+25799
+25801
+25819
+25841
+25847
+25849
+25867
+25873
+25889
+25903
+25913
+25919
+25931
+25933
+25939
+25943
+25951
+25969
+25981
+25997
+25999
+26003
+26017
+26021
+26029
+26041
+26053
+26083
+26099
+26107
+26111
+26113
+26119
+26141
+26153
+26161
+26171
+26177
+26183
+26189
+26203
+26209
+26227
+26237
+26249
+26251
+26261
+26263
+26267
+26293
+26297
+26309
+26317
+26321
+26339
+26347
+26357
+26371
+26387
+26393
+26399
+26407
+26417
+26423
+26431
+26437
+26449
+26459
+26479
+26489
+26497
+26501
+26513
+26539
+26557
+26561
+26573
+26591
+26597
+26627
+26633
+26641
+26647
+26669
+26681
+26683
+26687
+26693
+26699
+26701
+26711
+26713
+26717
+26723
+26729
+26731
+26737
+26759
+26777
+26783
+26801
+26813
+26821
+26833
+26839
+26849
+26861
+26863
+26879
+26881
+26891
+26893
+26903
+26921
+26927
+26947
+26951
+26953
+26959
+26981
+26987
+26993
+27011
+27017
+27031
+27043
+27059
+27061
+27067
+27073
+27077
+27091
+27103
+27107
+27109
+27127
+27143
+27179
+27191
+27197
+27211
+27239
+27241
+27253
+27259
+27271
+27277
+27281
+27283
+27299
+27329
+27337
+27361
+27367
+27397
+27407
+27409
+27427
+27431
+27437
+27449
+27457
+27479
+27481
+27487
+27509
+27527
+27529
+27539
+27541
+27551
+27581
+27583
+27611
+27617
+27631
+27647
+27653
+27673
+27689
+27691
+27697
+27701
+27733
+27737
+27739
+27743
+27749
+27751
+27763
+27767
+27773
+27779
+27791
+27793
+27799
+27803
+27809
+27817
+27823
+27827
+27847
+27851
+27883
+27893
+27901
+27917
+27919
+27941
+27943
+27947
+27953
+27961
+27967
+27983
+27997
+28001
+28019
+28027
+28031
+28051
+28057
+28069
+28081
+28087
+28097
+28099
+28109
+28111
+28123
+28151
+28163
+28181
+28183
+28201
+28211
+28219
+28229
+28277
+28279
+28283
+28289
+28297
+28307
+28309
+28319
+28349
+28351
+28387
+28393
+28403
+28409
+28411
+28429
+28433
+28439
+28447
+28463
+28477
+28493
+28499
+28513
+28517
+28537
+28541
+28547
+28549
+28559
+28571
+28573
+28579
+28591
+28597
+28603
+28607
+28619
+28621
+28627
+28631
+28643
+28649
+28657
+28661
+28663
+28669
+28687
+28697
+28703
+28711
+28723
+28729
+28751
+28753
+28759
+28771
+28789
+28793
+28807
+28813
+28817
+28837
+28843
+28859
+28867
+28871
+28879
+28901
+28909
+28921
+28927
+28933
+28949
+28961
+28979
+29009
+29017
+29021
+29023
+29027
+29033
+29059
+29063
+29077
+29101
+29123
+29129
+29131
+29137
+29147
+29153
+29167
+29173
+29179
+29191
+29201
+29207
+29209
+29221
+29231
+29243
+29251
+29269
+29287
+29297
+29303
+29311
+29327
+29333
+29339
+29347
+29363
+29383
+29387
+29389
+29399
+29401
+29411
+29423
+29429
+29437
+29443
+29453
+29473
+29483
+29501
+29527
+29531
+29537
+29567
+29569
+29573
+29581
+29587
+29599
+29611
+29629
+29633
+29641
+29663
+29669
+29671
+29683
+29717
+29723
+29741
+29753
+29759
+29761
+29789
+29803
+29819
+29833
+29837
+29851
+29863
+29867
+29873
+29879
+29881
+29917
+29921
+29927
+29947
+29959
+29983
+29989
+30011
+30013
+30029
+30047
+30059
+30071
+30089
+30091
+30097
+30103
+30109
+30113
+30119
+30133
+30137
+30139
+30161
+30169
+30181
+30187
+30197
+30203
+30211
+30223
+30241
+30253
+30259
+30269
+30271
+30293
+30307
+30313
+30319
+30323
+30341
+30347
+30367
+30389
+30391
+30403
+30427
+30431
+30449
+30467
+30469
+30491
+30493
+30497
+30509
+30517
+30529
+30539
+30553
+30557
+30559
+30577
+30593
+30631
+30637
+30643
+30649
+30661
+30671
+30677
+30689
+30697
+30703
+30707
+30713
+30727
+30757
+30763
+30773
+30781
+30803
+30809
+30817
+30829
+30839
+30841
+30851
+30853
+30859
+30869
+30871
+30881
+30893
+30911
+30931
+30937
+30941
+30949
+30971
+30977
+30983
+31013
+31019
+31033
+31039
+31051
+31063
+31069
+31079
+31081
+31091
+31121
+31123
+31139
+31147
+31151
+31153
+31159
+31177
+31181
+31183
+31189
+31193
+31219
+31223
+31231
+31237
+31247
+31249
+31253
+31259
+31267
+31271
+31277
+31307
+31319
+31321
+31327
+31333
+31337
+31357
+31379
+31387
+31391
+31393
+31397
+31469
+31477
+31481
+31489
+31511
+31513
+31517
+31531
+31541
+31543
+31547
+31567
+31573
+31583
+31601
+31607
+31627
+31643
+31649
+31657
+31663
+31667
+31687
+31699
+31721
+31723
+31727
+31729
+31741
+31751
+31769
+31771
+31793
+31799
+31817
+31847
+31849
+31859
+31873
+31883
+31891
+31907
+31957
+31963
+31973
+31981
+31991
+32003
+32009
+32027
+32029
+32051
+32057
+32059
+32063
+32069
+32077
+32083
+32089
+32099
+32117
+32119
+32141
+32143
+32159
+32173
+32183
+32189
+32191
+32203
+32213
+32233
+32237
+32251
+32257
+32261
+32297
+32299
+32303
+32309
+32321
+32323
+32327
+32341
+32353
+32359
+32363
+32369
+32371
+32377
+32381
+32401
+32411
+32413
+32423
+32429
+32441
+32443
+32467
+32479
+32491
+32497
+32503
+32507
+32531
+32533
+32537
+32561
+32563
+32569
+32573
+32579
+32587
+32603
+32609
+32611
+32621
+32633
+32647
+32653
+32687
+32693
+32707
+32713
+32717
+32719
+32749
+32771
+32779
+32783
+32789
+32797
+32801
+32803
+32831
+32833
+32839
+32843
+32869
+32887
+32909
+32911
+32917
+32933
+32939
+32941
+32957
+32969
+32971
+32983
+32987
+32993
+32999
+33013
+33023
+33029
+33037
+33049
+33053
+33071
+33073
+33083
+33091
+33107
+33113
+33119
+33149
+33151
+33161
+33179
+33181
+33191
+33199
+33203
+33211
+33223
+33247
+33287
+33289
+33301
+33311
+33317
+33329
+33331
+33343
+33347
+33349
+33353
+33359
+33377
+33391
+33403
+33409
+33413
+33427
+33457
+33461
+33469
+33479
+33487
+33493
+33503
+33521
+33529
+33533
+33547
+33563
+33569
+33577
+33581
+33587
+33589
+33599
+33601
+33613
+33617
+33619
+33623
+33629
+33637
+33641
+33647
+33679
+33703
+33713
+33721
+33739
+33749
+33751
+33757
+33767
+33769
+33773
+33791
+33797
+33809
+33811
+33827
+33829
+33851
+33857
+33863
+33871
+33889
+33893
+33911
+33923
+33931
+33937
+33941
+33961
+33967
+33997
+34019
+34031
+34033
+34039
+34057
+34061
+34123
+34127
+34129
+34141
+34147
+34157
+34159
+34171
+34183
+34211
+34213
+34217
+34231
+34253
+34259
+34261
+34267
+34273
+34283
+34297
+34301
+34303
+34313
+34319
+34327
+34337
+34351
+34361
+34367
+34369
+34381
+34403
+34421
+34429
+34439
+34457
+34469
+34471
+34483
+34487
+34499
+34501
+34511
+34513
+34519
+34537
+34543
+34549
+34583
+34589
+34591
+34603
+34607
+34613
+34631
+34649
+34651
+34667
+34673
+34679
+34687
+34693
+34703
+34721
+34729
+34739
+34747
+34757
+34759
+34763
+34781
+34807
+34819
+34841
+34843
+34847
+34849
+34871
+34877
+34883
+34897
+34913
+34919
+34939
+34949
+34961
+34963
+34981
+35023
+35027
+35051
+35053
+35059
+35069
+35081
+35083
+35089
+35099
+35107
+35111
+35117
+35129
+35141
+35149
+35153
+35159
+35171
+35201
+35221
+35227
+35251
+35257
+35267
+35279
+35281
+35291
+35311
+35317
+35323
+35327
+35339
+35353
+35363
+35381
+35393
+35401
+35407
+35419
+35423
+35437
+35447
+35449
+35461
+35491
+35507
+35509
+35521
+35527
+35531
+35533
+35537
+35543
+35569
+35573
+35591
+35593
+35597
+35603
+35617
+35671
+35677
+35729
+35731
+35747
+35753
+35759
+35771
+35797
+35801
+35803
+35809
+35831
+35837
+35839
+35851
+35863
+35869
+35879
+35897
+35899
+35911
+35923
+35933
+35951
+35963
+35969
+35977
+35983
+35993
+35999
+36007
+36011
+36013
+36017
+36037
+36061
+36067
+36073
+36083
+36097
+36107
+36109
+36131
+36137
+36151
+36161
+36187
+36191
+36209
+36217
+36229
+36241
+36251
+36263
+36269
+36277
+36293
+36299
+36307
+36313
+36319
+36341
+36343
+36353
+36373
+36383
+36389
+36433
+36451
+36457
+36467
+36469
+36473
+36479
+36493
+36497
+36523
+36527
+36529
+36541
+36551
+36559
+36563
+36571
+36583
+36587
+36599
+36607
+36629
+36637
+36643
+36653
+36671
+36677
+36683
+36691
+36697
+36709
+36713
+36721
+36739
+36749
+36761
+36767
+36779
+36781
+36787
+36791
+36793
+36809
+36821
+36833
+36847
+36857
+36871
+36877
+36887
+36899
+36901
+36913
+36919
+36923
+36929
+36931
+36943
+36947
+36973
+36979
+36997
+37003
+37013
+37019
+37021
+37039
+37049
+37057
+37061
+37087
+37097
+37117
+37123
+37139
+37159
+37171
+37181
+37189
+37199
+37201
+37217
+37223
+37243
+37253
+37273
+37277
+37307
+37309
+37313
+37321
+37337
+37339
+37357
+37361
+37363
+37369
+37379
+37397
+37409
+37423
+37441
+37447
+37463
+37483
+37489
+37493
+37501
+37507
+37511
+37517
+37529
+37537
+37547
+37549
+37561
+37567
+37571
+37573
+37579
+37589
+37591
+37607
+37619
+37633
+37643
+37649
+37657
+37663
+37691
+37693
+37699
+37717
+37747
+37781
+37783
+37799
+37811
+37813
+37831
+37847
+37853
+37861
+37871
+37879
+37889
+37897
+37907
+37951
+37957
+37963
+37967
+37987
+37991
+37993
+37997
+38011
+38039
+38047
+38053
+38069
+38083
+38113
+38119
+38149
+38153
+38167
+38177
+38183
+38189
+38197
+38201
+38219
+38231
+38237
+38239
+38261
+38273
+38281
+38287
+38299
+38303
+38317
+38321
+38327
+38329
+38333
+38351
+38371
+38377
+38393
+38431
+38447
+38449
+38453
+38459
+38461
+38501
+38543
+38557
+38561
+38567
+38569
+38593
+38603
+38609
+38611
+38629
+38639
+38651
+38653
+38669
+38671
+38677
+38693
+38699
+38707
+38711
+38713
+38723
+38729
+38737
+38747
+38749
+38767
+38783
+38791
+38803
+38821
+38833
+38839
+38851
+38861
+38867
+38873
+38891
+38903
+38917
+38921
+38923
+38933
+38953
+38959
+38971
+38977
+38993
+39019
+39023
+39041
+39043
+39047
+39079
+39089
+39097
+39103
+39107
+39113
+39119
+39133
+39139
+39157
+39161
+39163
+39181
+39191
+39199
+39209
+39217
+39227
+39229
+39233
+39239
+39241
+39251
+39293
+39301
+39313
+39317
+39323
+39341
+39343
+39359
+39367
+39371
+39373
+39383
+39397
+39409
+39419
+39439
+39443
+39451
+39461
+39499
+39503
+39509
+39511
+39521
+39541
+39551
+39563
+39569
+39581
+39607
+39619
+39623
+39631
+39659
+39667
+39671
+39679
+39703
+39709
+39719
+39727
+39733
+39749
+39761
+39769
+39779
+39791
+39799
+39821
+39827
+39829
+39839
+39841
+39847
+39857
+39863
+39869
+39877
+39883
+39887
+39901
+39929
+39937
+39953
+39971
+39979
+39983
+39989
+40009
+40013
+40031
+40037
+40039
+40063
+40087
+40093
+40099
+40111
+40123
+40127
+40129
+40151
+40153
+40163
+40169
+40177
+40189
+40193
+40213
+40231
+40237
+40241
+40253
+40277
+40283
+40289
+40343
+40351
+40357
+40361
+40387
+40423
+40427
+40429
+40433
+40459
+40471
+40483
+40487
+40493
+40499
+40507
+40519
+40529
+40531
+40543
+40559
+40577
+40583
+40591
+40597
+40609
+40627
+40637
+40639
+40693
+40697
+40699
+40709
+40739
+40751
+40759
+40763
+40771
+40787
+40801
+40813
+40819
+40823
+40829
+40841
+40847
+40849
+40853
+40867
+40879
+40883
+40897
+40903
+40927
+40933
+40939
+40949
+40961
+40973
+40993
+41011
+41017
+41023
+41039
+41047
+41051
+41057
+41077
+41081
+41113
+41117
+41131
+41141
+41143
+41149
+41161
+41177
+41179
+41183
+41189
+41201
+41203
+41213
+41221
+41227
+41231
+41233
+41243
+41257
+41263
+41269
+41281
+41299
+41333
+41341
+41351
+41357
+41381
+41387
+41389
+41399
+41411
+41413
+41443
+41453
+41467
+41479
+41491
+41507
+41513
+41519
+41521
+41539
+41543
+41549
+41579
+41593
+41597
+41603
+41609
+41611
+41617
+41621
+41627
+41641
+41647
+41651
+41659
+41669
+41681
+41687
+41719
+41729
+41737
+41759
+41761
+41771
+41777
+41801
+41809
+41813
+41843
+41849
+41851
+41863
+41879
+41887
+41893
+41897
+41903
+41911
+41927
+41941
+41947
+41953
+41957
+41959
+41969
+41981
+41983
+41999
+42013
+42017
+42019
+42023
+42043
+42061
+42071
+42073
+42083
+42089
+42101
+42131
+42139
+42157
+42169
+42179
+42181
+42187
+42193
+42197
+42209
+42221
+42223
+42227
+42239
+42257
+42281
+42283
+42293
+42299
+42307
+42323
+42331
+42337
+42349
+42359
+42373
+42379
+42391
+42397
+42403
+42407
+42409
+42433
+42437
+42443
+42451
+42457
+42461
+42463
+42467
+42473
+42487
+42491
+42499
+42509
+42533
+42557
+42569
+42571
+42577
+42589
+42611
+42641
+42643
+42649
+42667
+42677
+42683
+42689
+42697
+42701
+42703
+42709
+42719
+42727
+42737
+42743
+42751
+42767
+42773
+42787
+42793
+42797
+42821
+42829
+42839
+42841
+42853
+42859
+42863
+42899
+42901
+42923
+42929
+42937
+42943
+42953
+42961
+42967
+42979
+42989
+43003
+43013
+43019
+43037
+43049
+43051
+43063
+43067
+43093
+43103
+43117
+43133
+43151
+43159
+43177
+43189
+43201
+43207
+43223
+43237
+43261
+43271
+43283
+43291
+43313
+43319
+43321
+43331
+43391
+43397
+43399
+43403
+43411
+43427
+43441
+43451
+43457
+43481
+43487
+43499
+43517
+43541
+43543
+43573
+43577
+43579
+43591
+43597
+43607
+43609
+43613
+43627
+43633
+43649
+43651
+43661
+43669
+43691
+43711
+43717
+43721
+43753
+43759
+43777
+43781
+43783
+43787
+43789
+43793
+43801
+43853
+43867
+43889
+43891
+43913
+43933
+43943
+43951
+43961
+43963
+43969
+43973
+43987
+43991
+43997
+44017
+44021
+44027
+44029
+44041
+44053
+44059
+44071
+44087
+44089
+44101
+44111
+44119
+44123
+44129
+44131
+44159
+44171
+44179
+44189
+44201
+44203
+44207
+44221
+44249
+44257
+44263
+44267
+44269
+44273
+44279
+44281
+44293
+44351
+44357
+44371
+44381
+44383
+44389
+44417
+44449
+44453
+44483
+44491
+44497
+44501
+44507
+44519
+44531
+44533
+44537
+44543
+44549
+44563
+44579
+44587
+44617
+44621
+44623
+44633
+44641
+44647
+44651
+44657
+44683
+44687
+44699
+44701
+44711
+44729
+44741
+44753
+44771
+44773
+44777
+44789
+44797
+44809
+44819
+44839
+44843
+44851
+44867
+44879
+44887
+44893
+44909
+44917
+44927
+44939
+44953
+44959
+44963
+44971
+44983
+44987
+45007
+45013
+45053
+45061
+45077
+45083
+45119
+45121
+45127
+45131
+45137
+45139
+45161
+45179
+45181
+45191
+45197
+45233
+45247
+45259
+45263
+45281
+45289
+45293
+45307
+45317
+45319
+45329
+45337
+45341
+45343
+45361
+45377
+45389
+45403
+45413
+45427
+45433
+45439
+45481
+45491
+45497
+45503
+45523
+45533
+45541
+45553
+45557
+45569
+45587
+45589
+45599
+45613
+45631
+45641
+45659
+45667
+45673
+45677
+45691
+45697
+45707
+45737
+45751
+45757
+45763
+45767
+45779
+45817
+45821
+45823
+45827
+45833
+45841
+45853
+45863
+45869
+45887
+45893
+45943
+45949
+45953
+45959
+45971
+45979
+45989
+46021
+46027
+46049
+46051
+46061
+46073
+46091
+46093
+46099
+46103
+46133
+46141
+46147
+46153
+46171
+46181
+46183
+46187
+46199
+46219
+46229
+46237
+46261
+46271
+46273
+46279
+46301
+46307
+46309
+46327
+46337
+46349
+46351
+46381
+46399
+46411
+46439
+46441
+46447
+46451
+46457
+46471
+46477
+46489
+46499
+46507
+46511
+46523
+46549
+46559
+46567
+46573
+46589
+46591
+46601
+46619
+46633
+46639
+46643
+46649
+46663
+46679
+46681
+46687
+46691
+46703
+46723
+46727
+46747
+46751
+46757
+46769
+46771
+46807
+46811
+46817
+46819
+46829
+46831
+46853
+46861
+46867
+46877
+46889
+46901
+46919
+46933
+46957
+46993
+46997
+47017
+47041
+47051
+47057
+47059
+47087
+47093
+47111
+47119
+47123
+47129
+47137
+47143
+47147
+47149
+47161
+47189
+47207
+47221
+47237
+47251
+47269
+47279
+47287
+47293
+47297
+47303
+47309
+47317
+47339
+47351
+47353
+47363
+47381
+47387
+47389
+47407
+47417
+47419
+47431
+47441
+47459
+47491
+47497
+47501
+47507
+47513
+47521
+47527
+47533
+47543
+47563
+47569
+47581
+47591
+47599
+47609
+47623
+47629
+47639
+47653
+47657
+47659
+47681
+47699
+47701
+47711
+47713
+47717
+47737
+47741
+47743
+47777
+47779
+47791
+47797
+47807
+47809
+47819
+47837
+47843
+47857
+47869
+47881
+47903
+47911
+47917
+47933
+47939
+47947
+47951
+47963
+47969
+47977
+47981
+48017
+48023
+48029
+48049
+48073
+48079
+48091
+48109
+48119
+48121
+48131
+48157
+48163
+48179
+48187
+48193
+48197
+48221
+48239
+48247
+48259
+48271
+48281
+48299
+48311
+48313
+48337
+48341
+48353
+48371
+48383
+48397
+48407
+48409
+48413
+48437
+48449
+48463
+48473
+48479
+48481
+48487
+48491
+48497
+48523
+48527
+48533
+48539
+48541
+48563
+48571
+48589
+48593
+48611
+48619
+48623
+48647
+48649
+48661
+48673
+48677
+48679
+48731
+48733
+48751
+48757
+48761
+48767
+48779
+48781
+48787
+48799
+48809
+48817
+48821
+48823
+48847
+48857
+48859
+48869
+48871
+48883
+48889
+48907
+48947
+48953
+48973
+48989
+48991
+49003
+49009
+49019
+49031
+49033
+49037
+49043
+49057
+49069
+49081
+49103
+49109
+49117
+49121
+49123
+49139
+49157
+49169
+49171
+49177
+49193
+49199
+49201
+49207
+49211
+49223
+49253
+49261
+49277
+49279
+49297
+49307
+49331
+49333
+49339
+49363
+49367
+49369
+49391
+49393
+49409
+49411
+49417
+49429
+49433
+49451
+49459
+49463
+49477
+49481
+49499
+49523
+49529
+49531
+49537
+49547
+49549
+49559
+49597
+49603
+49613
+49627
+49633
+49639
+49663
+49667
+49669
+49681
+49697
+49711
+49727
+49739
+49741
+49747
+49757
+49783
+49787
+49789
+49801
+49807
+49811
+49823
+49831
+49843
+49853
+49871
+49877
+49891
+49919
+49921
+49927
+49937
+49939
+49943
+49957
+49991
+49993
+49999
+50021
+50023
+50033
+50047
+50051
+50053
+50069
+50077
+50087
+50093
+50101
+50111
+50119
+50123
+50129
+50131
+50147
+50153
+50159
+50177
+50207
+50221
+50227
+50231
+50261
+50263
+50273
+50287
+50291
+50311
+50321
+50329
+50333
+50341
+50359
+50363
+50377
+50383
+50387
+50411
+50417
+50423
+50441
+50459
+50461
+50497
+50503
+50513
+50527
+50539
+50543
+50549
+50551
+50581
+50587
+50591
+50593
+50599
+50627
+50647
+50651
+50671
+50683
+50707
+50723
+50741
+50753
+50767
+50773
+50777
+50789
+50821
+50833
+50839
+50849
+50857
+50867
+50873
+50891
+50893
+50909
+50923
+50929
+50951
+50957
+50969
+50971
+50989
+50993
+51001
+51031
+51043
+51047
+51059
+51061
+51071
+51109
+51131
+51133
+51137
+51151
+51157
+51169
+51193
+51197
+51199
+51203
+51217
+51229
+51239
+51241
+51257
+51263
+51283
+51287
+51307
+51329
+51341
+51343
+51347
+51349
+51361
+51383
+51407
+51413
+51419
+51421
+51427
+51431
+51437
+51439
+51449
+51461
+51473
+51479
+51481
+51487
+51503
+51511
+51517
+51521
+51539
+51551
+51563
+51577
+51581
+51593
+51599
+51607
+51613
+51631
+51637
+51647
+51659
+51673
+51679
+51683
+51691
+51713
+51719
+51721
+51749
+51767
+51769
+51787
+51797
+51803
+51817
+51827
+51829
+51839
+51853
+51859
+51869
+51871
+51893
+51899
+51907
+51913
+51929
+51941
+51949
+51971
+51973
+51977
+51991
+52009
+52021
+52027
+52051
+52057
+52067
+52069
+52081
+52103
+52121
+52127
+52147
+52153
+52163
+52177
+52181
+52183
+52189
+52201
+52223
+52237
+52249
+52253
+52259
+52267
+52289
+52291
+52301
+52313
+52321
+52361
+52363
+52369
+52379
+52387
+52391
+52433
+52453
+52457
+52489
+52501
+52511
+52517
+52529
+52541
+52543
+52553
+52561
+52567
+52571
+52579
+52583
+52609
+52627
+52631
+52639
+52667
+52673
+52691
+52697
+52709
+52711
+52721
+52727
+52733
+52747
+52757
+52769
+52783
+52807
+52813
+52817
+52837
+52859
+52861
+52879
+52883
+52889
+52901
+52903
+52919
+52937
+52951
+52957
+52963
+52967
+52973
+52981
+52999
+53003
+53017
+53047
+53051
+53069
+53077
+53087
+53089
+53093
+53101
+53113
+53117
+53129
+53147
+53149
+53161
+53171
+53173
+53189
+53197
+53201
+53231
+53233
+53239
+53267
+53269
+53279
+53281
+53299
+53309
+53323
+53327
+53353
+53359
+53377
+53381
+53401
+53407
+53411
+53419
+53437
+53441
+53453
+53479
+53503
+53507
+53527
+53549
+53551
+53569
+53591
+53593
+53597
+53609
+53611
+53617
+53623
+53629
+53633
+53639
+53653
+53657
+53681
+53693
+53699
+53717
+53719
+53731
+53759
+53773
+53777
+53783
+53791
+53813
+53819
+53831
+53849
+53857
+53861
+53881
+53887
+53891
+53897
+53899
+53917
+53923
+53927
+53939
+53951
+53959
+53987
+53993
+54001
+54011
+54013
+54037
+54049
+54059
+54083
+54091
+54101
+54121
+54133
+54139
+54151
+54163
+54167
+54181
+54193
+54217
+54251
+54269
+54277
+54287
+54293
+54311
+54319
+54323
+54331
+54347
+54361
+54367
+54371
+54377
+54401
+54403
+54409
+54413
+54419
+54421
+54437
+54443
+54449
+54469
+54493
+54497
+54499
+54503
+54517
+54521
+54539
+54541
+54547
+54559
+54563
+54577
+54581
+54583
+54601
+54617
+54623
+54629
+54631
+54647
+54667
+54673
+54679
+54709
+54713
+54721
+54727
+54751
+54767
+54773
+54779
+54787
+54799
+54829
+54833
+54851
+54869
+54877
+54881
+54907
+54917
+54919
+54941
+54949
+54959
+54973
+54979
+54983
+55001
+55009
+55021
+55049
+55051
+55057
+55061
+55073
+55079
+55103
+55109
+55117
+55127
+55147
+55163
+55171
+55201
+55207
+55213
+55217
+55219
+55229
+55243
+55249
+55259
+55291
+55313
+55331
+55333
+55337
+55339
+55343
+55351
+55373
+55381
+55399
+55411
+55439
+55441
+55457
+55469
+55487
+55501
+55511
+55529
+55541
+55547
+55579
+55589
+55603
+55609
+55619
+55621
+55631
+55633
+55639
+55661
+55663
+55667
+55673
+55681
+55691
+55697
+55711
+55717
+55721
+55733
+55763
+55787
+55793
+55799
+55807
+55813
+55817
+55819
+55823
+55829
+55837
+55843
+55849
+55871
+55889
+55897
+55901
+55903
+55921
+55927
+55931
+55933
+55949
+55967
+55987
+55997
+56003
+56009
+56039
+56041
+56053
+56081
+56087
+56093
+56099
+56101
+56113
+56123
+56131
+56149
+56167
+56171
+56179
+56197
+56207
+56209
+56237
+56239
+56249
+56263
+56267
+56269
+56299
+56311
+56333
+56359
+56369
+56377
+56383
+56393
+56401
+56417
+56431
+56437
+56443
+56453
+56467
+56473
+56477
+56479
+56489
+56501
+56503
+56509
+56519
+56527
+56531
+56533
+56543
+56569
+56591
+56597
+56599
+56611
+56629
+56633
+56659
+56663
+56671
+56681
+56687
+56701
+56711
+56713
+56731
+56737
+56747
+56767
+56773
+56779
+56783
+56807
+56809
+56813
+56821
+56827
+56843
+56857
+56873
+56891
+56893
+56897
+56909
+56911
+56921
+56923
+56929
+56941
+56951
+56957
+56963
+56983
+56989
+56993
+56999
+57037
+57041
+57047
+57059
+57073
+57077
+57089
+57097
+57107
+57119
+57131
+57139
+57143
+57149
+57163
+57173
+57179
+57191
+57193
+57203
+57221
+57223
+57241
+57251
+57259
+57269
+57271
+57283
+57287
+57301
+57329
+57331
+57347
+57349
+57367
+57373
+57383
+57389
+57397
+57413
+57427
+57457
+57467
+57487
+57493
+57503
+57527
+57529
+57557
+57559
+57571
+57587
+57593
+57601
+57637
+57641
+57649
+57653
+57667
+57679
+57689
+57697
+57709
+57713
+57719
+57727
+57731
+57737
+57751
+57773
+57781
+57787
+57791
+57793
+57803
+57809
+57829
+57839
+57847
+57853
+57859
+57881
+57899
+57901
+57917
+57923
+57943
+57947
+57973
+57977
+57991
+58013
+58027
+58031
+58043
+58049
+58057
+58061
+58067
+58073
+58099
+58109
+58111
+58129
+58147
+58151
+58153
+58169
+58171
+58189
+58193
+58199
+58207
+58211
+58217
+58229
+58231
+58237
+58243
+58271
+58309
+58313
+58321
+58337
+58363
+58367
+58369
+58379
+58391
+58393
+58403
+58411
+58417
+58427
+58439
+58441
+58451
+58453
+58477
+58481
+58511
+58537
+58543
+58549
+58567
+58573
+58579
+58601
+58603
+58613
+58631
+58657
+58661
+58679
+58687
+58693
+58699
+58711
+58727
+58733
+58741
+58757
+58763
+58771
+58787
+58789
+58831
+58889
+58897
+58901
+58907
+58909
+58913
+58921
+58937
+58943
+58963
+58967
+58979
+58991
+58997
+59009
+59011
+59021
+59023
+59029
+59051
+59053
+59063
+59069
+59077
+59083
+59093
+59107
+59113
+59119
+59123
+59141
+59149
+59159
+59167
+59183
+59197
+59207
+59209
+59219
+59221
+59233
+59239
+59243
+59263
+59273
+59281
+59333
+59341
+59351
+59357
+59359
+59369
+59377
+59387
+59393
+59399
+59407
+59417
+59419
+59441
+59443
+59447
+59453
+59467
+59471
+59473
+59497
+59509
+59513
+59539
+59557
+59561
+59567
+59581
+59611
+59617
+59621
+59627
+59629
+59651
+59659
+59663
+59669
+59671
+59693
+59699
+59707
+59723
+59729
+59743
+59747
+59753
+59771
+59779
+59791
+59797
+59809
+59833
+59863
+59879
+59887
+59921
+59929
+59951
+59957
+59971
+59981
+59999
+60013
+60017
+60029
+60037
+60041
+60077
+60083
+60089
+60091
+60101
+60103
+60107
+60127
+60133
+60139
+60149
+60161
+60167
+60169
+60209
+60217
+60223
+60251
+60257
+60259
+60271
+60289
+60293
+60317
+60331
+60337
+60343
+60353
+60373
+60383
+60397
+60413
+60427
+60443
+60449
+60457
+60493
+60497
+60509
+60521
+60527
+60539
+60589
+60601
+60607
+60611
+60617
+60623
+60631
+60637
+60647
+60649
+60659
+60661
+60679
+60689
+60703
+60719
+60727
+60733
+60737
+60757
+60761
+60763
+60773
+60779
+60793
+60811
+60821
+60859
+60869
+60887
+60889
+60899
+60901
+60913
+60917
+60919
+60923
+60937
+60943
+60953
+60961
+61001
+61007
+61027
+61031
+61043
+61051
+61057
+61091
+61099
+61121
+61129
+61141
+61151
+61153
+61169
+61211
+61223
+61231
+61253
+61261
+61283
+61291
+61297
+61331
+61333
+61339
+61343
+61357
+61363
+61379
+61381
+61403
+61409
+61417
+61441
+61463
+61469
+61471
+61483
+61487
+61493
+61507
+61511
+61519
+61543
+61547
+61553
+61559
+61561
+61583
+61603
+61609
+61613
+61627
+61631
+61637
+61643
+61651
+61657
+61667
+61673
+61681
+61687
+61703
+61717
+61723
+61729
+61751
+61757
+61781
+61813
+61819
+61837
+61843
+61861
+61871
+61879
+61909
+61927
+61933
+61949
+61961
+61967
+61979
+61981
+61987
+61991
+62003
+62011
+62017
+62039
+62047
+62053
+62057
+62071
+62081
+62099
+62119
+62129
+62131
+62137
+62141
+62143
+62171
+62189
+62191
+62201
+62207
+62213
+62219
+62233
+62273
+62297
+62299
+62303
+62311
+62323
+62327
+62347
+62351
+62383
+62401
+62417
+62423
+62459
+62467
+62473
+62477
+62483
+62497
+62501
+62507
+62533
+62539
+62549
+62563
+62581
+62591
+62597
+62603
+62617
+62627
+62633
+62639
+62653
+62659
+62683
+62687
+62701
+62723
+62731
+62743
+62753
+62761
+62773
+62791
+62801
+62819
+62827
+62851
+62861
+62869
+62873
+62897
+62903
+62921
+62927
+62929
+62939
+62969
+62971
+62981
+62983
+62987
+62989
+63029
+63031
+63059
+63067
+63073
+63079
+63097
+63103
+63113
+63127
+63131
+63149
+63179
+63197
+63199
+63211
+63241
+63247
+63277
+63281
+63299
+63311
+63313
+63317
+63331
+63337
+63347
+63353
+63361
+63367
+63377
+63389
+63391
+63397
+63409
+63419
+63421
+63439
+63443
+63463
+63467
+63473
+63487
+63493
+63499
+63521
+63527
+63533
+63541
+63559
+63577
+63587
+63589
+63599
+63601
+63607
+63611
+63617
+63629
+63647
+63649
+63659
+63667
+63671
+63689
+63691
+63697
+63703
+63709
+63719
+63727
+63737
+63743
+63761
+63773
+63781
+63793
+63799
+63803
+63809
+63823
+63839
+63841
+63853
+63857
+63863
+63901
+63907
+63913
+63929
+63949
+63977
+63997
+64007
+64013
+64019
+64033
+64037
+64063
+64067
+64081
+64091
+64109
+64123
+64151
+64153
+64157
+64171
+64187
+64189
+64217
+64223
+64231
+64237
+64271
+64279
+64283
+64301
+64303
+64319
+64327
+64333
+64373
+64381
+64399
+64403
+64433
+64439
+64451
+64453
+64483
+64489
+64499
+64513
+64553
+64567
+64577
+64579
+64591
+64601
+64609
+64613
+64621
+64627
+64633
+64661
+64663
+64667
+64679
+64693
+64709
+64717
+64747
+64763
+64781
+64783
+64793
+64811
+64817
+64849
+64853
+64871
+64877
+64879
+64891
+64901
+64919
+64921
+64927
+64937
+64951
+64969
+64997
+65003
+65011
+65027
+65029
+65033
+65053
+65063
+65071
+65089
+65099
+65101
+65111
+65119
+65123
+65129
+65141
+65147
+65167
+65171
+65173
+65179
+65183
+65203
+65213
+65239
+65257
+65267
+65269
+65287
+65293
+65309
+65323
+65327
+65353
+65357
+65371
+65381
+65393
+65407
+65413
+65419
+65423
+65437
+65447
+65449
+65479
+65497
+65519
+65521
+65537
+65539
+65543
+65551
+65557
+65563
+65579
+65581
+65587
+65599
+65609
+65617
+65629
+65633
+65647
+65651
+65657
+65677
+65687
+65699
+65701
+65707
+65713
+65717
+65719
+65729
+65731
+65761
+65777
+65789
+65809
+65827
+65831
+65837
+65839
+65843
+65851
+65867
+65881
+65899
+65921
+65927
+65929
+65951
+65957
+65963
+65981
+65983
+65993
+66029
+66037
+66041
+66047
+66067
+66071
+66083
+66089
+66103
+66107
+66109
+66137
+66161
+66169
+66173
+66179
+66191
+66221
+66239
+66271
+66293
+66301
+66337
+66343
+66347
+66359
+66361
+66373
+66377
+66383
+66403
+66413
+66431
+66449
+66457
+66463
+66467
+66491
+66499
+66509
+66523
+66529
+66533
+66541
+66553
+66569
+66571
+66587
+66593
+66601
+66617
+66629
+66643
+66653
+66683
+66697
+66701
+66713
+66721
+66733
+66739
+66749
+66751
+66763
+66791
+66797
+66809
+66821
+66841
+66851
+66853
+66863
+66877
+66883
+66889
+66919
+66923
+66931
+66943
+66947
+66949
+66959
+66973
+66977
+67003
+67021
+67033
+67043
+67049
+67057
+67061
+67073
+67079
+67103
+67121
+67129
+67139
+67141
+67153
+67157
+67169
+67181
+67187
+67189
+67211
+67213
+67217
+67219
+67231
+67247
+67261
+67271
+67273
+67289
+67307
+67339
+67343
+67349
+67369
+67391
+67399
+67409
+67411
+67421
+67427
+67429
+67433
+67447
+67453
+67477
+67481
+67489
+67493
+67499
+67511
+67523
+67531
+67537
+67547
+67559
+67567
+67577
+67579
+67589
+67601
+67607
+67619
+67631
+67651
+67679
+67699
+67709
+67723
+67733
+67741
+67751
+67757
+67759
+67763
+67777
+67783
+67789
+67801
+67807
+67819
+67829
+67843
+67853
+67867
+67883
+67891
+67901
+67927
+67931
+67933
+67939
+67943
+67957
+67961
+67967
+67979
+67987
+67993
+68023
+68041
+68053
+68059
+68071
+68087
+68099
+68111
+68113
+68141
+68147
+68161
+68171
+68207
+68209
+68213
+68219
+68227
+68239
+68261
+68279
+68281
+68311
+68329
+68351
+68371
+68389
+68399
+68437
+68443
+68447
+68449
+68473
+68477
+68483
+68489
+68491
+68501
+68507
+68521
+68531
+68539
+68543
+68567
+68581
+68597
+68611
+68633
+68639
+68659
+68669
+68683
+68687
+68699
+68711
+68713
+68729
+68737
+68743
+68749
+68767
+68771
+68777
+68791
+68813
+68819
+68821
+68863
+68879
+68881
+68891
+68897
+68899
+68903
+68909
+68917
+68927
+68947
+68963
+68993
+69001
+69011
+69019
+69029
+69031
+69061
+69067
+69073
+69109
+69119
+69127
+69143
+69149
+69151
+69163
+69191
+69193
+69197
+69203
+69221
+69233
+69239
+69247
+69257
+69259
+69263
+69313
+69317
+69337
+69341
+69371
+69379
+69383
+69389
+69401
+69403
+69427
+69431
+69439
+69457
+69463
+69467
+69473
+69481
+69491
+69493
+69497
+69499
+69539
+69557
+69593
+69623
+69653
+69661
+69677
+69691
+69697
+69709
+69737
+69739
+69761
+69763
+69767
+69779
+69809
+69821
+69827
+69829
+69833
+69847
+69857
+69859
+69877
+69899
+69911
+69929
+69931
+69941
+69959
+69991
+69997
+70001
+70003
+70009
+70019
+70039
+70051
+70061
+70067
+70079
+70099
+70111
+70117
+70121
+70123
+70139
+70141
+70157
+70163
+70177
+70181
+70183
+70199
+70201
+70207
+70223
+70229
+70237
+70241
+70249
+70271
+70289
+70297
+70309
+70313
+70321
+70327
+70351
+70373
+70379
+70381
+70393
+70423
+70429
+70439
+70451
+70457
+70459
+70481
+70487
+70489
+70501
+70507
+70529
+70537
+70549
+70571
+70573
+70583
+70589
+70607
+70619
+70621
+70627
+70639
+70657
+70663
+70667
+70687
+70709
+70717
+70729
+70753
+70769
+70783
+70793
+70823
+70841
+70843
+70849
+70853
+70867
+70877
+70879
+70891
+70901
+70913
+70919
+70921
+70937
+70949
+70951
+70957
+70969
+70979
+70981
+70991
+70997
+70999
+71011
+71023
+71039
+71059
+71069
+71081
+71089
+71119
+71129
+71143
+71147
+71153
+71161
+71167
+71171
+71191
+71209
+71233
+71237
+71249
+71257
+71261
+71263
+71287
+71293
+71317
+71327
+71329
+71333
+71339
+71341
+71347
+71353
+71359
+71363
+71387
+71389
+71399
+71411
+71413
+71419
+71429
+71437
+71443
+71453
+71471
+71473
+71479
+71483
+71503
+71527
+71537
+71549
+71551
+71563
+71569
+71593
+71597
+71633
+71647
+71663
+71671
+71693
+71699
+71707
+71711
+71713
+71719
+71741
+71761
+71777
+71789
+71807
+71809
+71821
+71837
+71843
+71849
+71861
+71867
+71879
+71881
+71887
+71899
+71909
+71917
+71933
+71941
+71947
+71963
+71971
+71983
+71987
+71993
+71999
+72019
+72031
+72043
+72047
+72053
+72073
+72077
+72089
+72091
+72101
+72103
+72109
+72139
+72161
+72167
+72169
+72173
+72211
+72221
+72223
+72227
+72229
+72251
+72253
+72269
+72271
+72277
+72287
+72307
+72313
+72337
+72341
+72353
+72367
+72379
+72383
+72421
+72431
+72461
+72467
+72469
+72481
+72493
+72497
+72503
+72533
+72547
+72551
+72559
+72577
+72613
+72617
+72623
+72643
+72647
+72649
+72661
+72671
+72673
+72679
+72689
+72701
+72707
+72719
+72727
+72733
+72739
+72763
+72767
+72797
+72817
+72823
+72859
+72869
+72871
+72883
+72889
+72893
+72901
+72907
+72911
+72923
+72931
+72937
+72949
+72953
+72959
+72973
+72977
+72997
+73009
+73013
+73019
+73037
+73039
+73043
+73061
+73063
+73079
+73091
+73121
+73127
+73133
+73141
+73181
+73189
+73237
+73243
+73259
+73277
+73291
+73303
+73309
+73327
+73331
+73351
+73361
+73363
+73369
+73379
+73387
+73417
+73421
+73433
+73453
+73459
+73471
+73477
+73483
+73517
+73523
+73529
+73547
+73553
+73561
+73571
+73583
+73589
+73597
+73607
+73609
+73613
+73637
+73643
+73651
+73673
+73679
+73681
+73693
+73699
+73709
+73721
+73727
+73751
+73757
+73771
+73783
+73819
+73823
+73847
+73849
+73859
+73867
+73877
+73883
+73897
+73907
+73939
+73943
+73951
+73961
+73973
+73999
+74017
+74021
+74027
+74047
+74051
+74071
+74077
+74093
+74099
+74101
+74131
+74143
+74149
+74159
+74161
+74167
+74177
+74189
+74197
+74201
+74203
+74209
+74219
+74231
+74257
+74279
+74287
+74293
+74297
+74311
+74317
+74323
+74353
+74357
+74363
+74377
+74381
+74383
+74411
+74413
+74419
+74441
+74449
+74453
+74471
+74489
+74507
+74509
+74521
+74527
+74531
+74551
+74561
+74567
+74573
+74587
+74597
+74609
+74611
+74623
+74653
+74687
+74699
+74707
+74713
+74717
+74719
+74729
+74731
+74747
+74759
+74761
+74771
+74779
+74797
+74821
+74827
+74831
+74843
+74857
+74861
+74869
+74873
+74887
+74891
+74897
+74903
+74923
+74929
+74933
+74941
+74959
+75011
+75013
+75017
+75029
+75037
+75041
+75079
+75083
+75109
+75133
+75149
+75161
+75167
+75169
+75181
+75193
+75209
+75211
+75217
+75223
+75227
+75239
+75253
+75269
+75277
+75289
+75307
+75323
+75329
+75337
+75347
+75353
+75367
+75377
+75389
+75391
+75401
+75403
+75407
+75431
+75437
+75479
+75503
+75511
+75521
+75527
+75533
+75539
+75541
+75553
+75557
+75571
+75577
+75583
+75611
+75617
+75619
+75629
+75641
+75653
+75659
+75679
+75683
+75689
+75703
+75707
+75709
+75721
+75731
+75743
+75767
+75773
+75781
+75787
+75793
+75797
+75821
+75833
+75853
+75869
+75883
+75913
+75931
+75937
+75941
+75967
+75979
+75983
+75989
+75991
+75997
+76001
+76003
+76031
+76039
+76079
+76081
+76091
+76099
+76103
+76123
+76129
+76147
+76157
+76159
+76163
+76207
+76213
+76231
+76243
+76249
+76253
+76259
+76261
+76283
+76289
+76303
+76333
+76343
+76367
+76369
+76379
+76387
+76403
+76421
+76423
+76441
+76463
+76471
+76481
+76487
+76493
+76507
+76511
+76519
+76537
+76541
+76543
+76561
+76579
+76597
+76603
+76607
+76631
+76649
+76651
+76667
+76673
+76679
+76697
+76717
+76733
+76753
+76757
+76771
+76777
+76781
+76801
+76819
+76829
+76831
+76837
+76847
+76871
+76873
+76883
+76907
+76913
+76919
+76943
+76949
+76961
+76963
+76991
+77003
+77017
+77023
+77029
+77041
+77047
+77069
+77081
+77093
+77101
+77137
+77141
+77153
+77167
+77171
+77191
+77201
+77213
+77237
+77239
+77243
+77249
+77261
+77263
+77267
+77269
+77279
+77291
+77317
+77323
+77339
+77347
+77351
+77359
+77369
+77377
+77383
+77417
+77419
+77431
+77447
+77471
+77477
+77479
+77489
+77491
+77509
+77513
+77521
+77527
+77543
+77549
+77551
+77557
+77563
+77569
+77573
+77587
+77591
+77611
+77617
+77621
+77641
+77647
+77659
+77681
+77687
+77689
+77699
+77711
+77713
+77719
+77723
+77731
+77743
+77747
+77761
+77773
+77783
+77797
+77801
+77813
+77839
+77849
+77863
+77867
+77893
+77899
+77929
+77933
+77951
+77969
+77977
+77983
+77999
+78007
+78017
+78031
+78041
+78049
+78059
+78079
+78101
+78121
+78137
+78139
+78157
+78163
+78167
+78173
+78179
+78191
+78193
+78203
+78229
+78233
+78241
+78259
+78277
+78283
+78301
+78307
+78311
+78317
+78341
+78347
+78367
+78401
+78427
+78437
+78439
+78467
+78479
+78487
+78497
+78509
+78511
+78517
+78539
+78541
+78553
+78569
+78571
+78577
+78583
+78593
+78607
+78623
+78643
+78649
+78653
+78691
+78697
+78707
+78713
+78721
+78737
+78779
+78781
+78787
+78791
+78797
+78803
+78809
+78823
+78839
+78853
+78857
+78877
+78887
+78889
+78893
+78901
+78919
+78929
+78941
+78977
+78979
+78989
+79031
+79039
+79043
+79063
+79087
+79103
+79111
+79133
+79139
+79147
+79151
+79153
+79159
+79181
+79187
+79193
+79201
+79229
+79231
+79241
+79259
+79273
+79279
+79283
+79301
+79309
+79319
+79333
+79337
+79349
+79357
+79367
+79379
+79393
+79397
+79399
+79411
+79423
+79427
+79433
+79451
+79481
+79493
+79531
+79537
+79549
+79559
+79561
+79579
+79589
+79601
+79609
+79613
+79621
+79627
+79631
+79633
+79657
+79669
+79687
+79691
+79693
+79697
+79699
+79757
+79769
+79777
+79801
+79811
+79813
+79817
+79823
+79829
+79841
+79843
+79847
+79861
+79867
+79873
+79889
+79901
+79903
+79907
+79939
+79943
+79967
+79973
+79979
+79987
+79997
+79999
+80021
+80039
+80051
+80071
+80077
+80107
+80111
+80141
+80147
+80149
+80153
+80167
+80173
+80177
+80191
+80207
+80209
+80221
+80231
+80233
+80239
+80251
+80263
+80273
+80279
+80287
+80309
+80317
+80329
+80341
+80347
+80363
+80369
+80387
+80407
+80429
+80447
+80449
+80471
+80473
+80489
+80491
+80513
+80527
+80537
+80557
+80567
+80599
+80603
+80611
+80621
+80627
+80629
+80651
+80657
+80669
+80671
+80677
+80681
+80683
+80687
+80701
+80713
+80737
+80747
+80749
+80761
+80777
+80779
+80783
+80789
+80803
+80809
+80819
+80831
+80833
+80849
+80863
+80897
+80909
+80911
+80917
+80923
+80929
+80933
+80953
+80963
+80989
+81001
+81013
+81017
+81019
+81023
+81031
+81041
+81043
+81047
+81049
+81071
+81077
+81083
+81097
+81101
+81119
+81131
+81157
+81163
+81173
+81181
+81197
+81199
+81203
+81223
+81233
+81239
+81281
+81283
+81293
+81299
+81307
+81331
+81343
+81349
+81353
+81359
+81371
+81373
+81401
+81409
+81421
+81439
+81457
+81463
+81509
+81517
+81527
+81533
+81547
+81551
+81553
+81559
+81563
+81569
+81611
+81619
+81629
+81637
+81647
+81649
+81667
+81671
+81677
+81689
+81701
+81703
+81707
+81727
+81737
+81749
+81761
+81769
+81773
+81799
+81817
+81839
+81847
+81853
+81869
+81883
+81899
+81901
+81919
+81929
+81931
+81937
+81943
+81953
+81967
+81971
+81973
+82003
+82007
+82009
+82013
+82021
+82031
+82037
+82039
+82051
+82067
+82073
+82129
+82139
+82141
+82153
+82163
+82171
+82183
+82189
+82193
+82207
+82217
+82219
+82223
+82231
+82237
+82241
+82261
+82267
+82279
+82301
+82307
+82339
+82349
+82351
+82361
+82373
+82387
+82393
+82421
+82457
+82463
+82469
+82471
+82483
+82487
+82493
+82499
+82507
+82529
+82531
+82549
+82559
+82561
+82567
+82571
+82591
+82601
+82609
+82613
+82619
+82633
+82651
+82657
+82699
+82721
+82723
+82727
+82729
+82757
+82759
+82763
+82781
+82787
+82793
+82799
+82811
+82813
+82837
+82847
+82883
+82889
+82891
+82903
+82913
+82939
+82963
+82981
+82997
+83003
+83009
+83023
+83047
+83059
+83063
+83071
+83077
+83089
+83093
+83101
+83117
+83137
+83177
+83203
+83207
+83219
+83221
+83227
+83231
+83233
+83243
+83257
+83267
+83269
+83273
+83299
+83311
+83339
+83341
+83357
+83383
+83389
+83399
+83401
+83407
+83417
+83423
+83431
+83437
+83443
+83449
+83459
+83471
+83477
+83497
+83537
+83557
+83561
+83563
+83579
+83591
+83597
+83609
+83617
+83621
+83639
+83641
+83653
+83663
+83689
+83701
+83717
+83719
+83737
+83761
+83773
+83777
+83791
+83813
+83833
+83843
+83857
+83869
+83873
+83891
+83903
+83911
+83921
+83933
+83939
+83969
+83983
+83987
+84011
+84017
+84047
+84053
+84059
+84061
+84067
+84089
+84121
+84127
+84131
+84137
+84143
+84163
+84179
+84181
+84191
+84199
+84211
+84221
+84223
+84229
+84239
+84247
+84263
+84299
+84307
+84313
+84317
+84319
+84347
+84349
+84377
+84389
+84391
+84401
+84407
+84421
+84431
+84437
+84443
+84449
+84457
+84463
+84467
+84481
+84499
+84503
+84509
+84521
+84523
+84533
+84551
+84559
+84589
+84629
+84631
+84649
+84653
+84659
+84673
+84691
+84697
+84701
+84713
+84719
+84731
+84737
+84751
+84761
+84787
+84793
+84809
+84811
+84827
+84857
+84859
+84869
+84871
+84913
+84919
+84947
+84961
+84967
+84977
+84979
+84991
+85009
+85021
+85027
+85037
+85049
+85061
+85081
+85087
+85091
+85093
+85103
+85109
+85121
+85133
+85147
+85159
+85193
+85199
+85201
+85213
+85223
+85229
+85237
+85243
+85247
+85259
+85297
+85303
+85313
+85331
+85333
+85361
+85363
+85369
+85381
+85411
+85427
+85429
+85439
+85447
+85451
+85453
+85469
+85487
+85513
+85517
+85523
+85531
+85549
+85571
+85577
+85597
+85601
+85607
+85619
+85621
+85627
+85639
+85643
+85661
+85667
+85669
+85691
+85703
+85711
+85717
+85733
+85751
+85781
+85793
+85817
+85819
+85829
+85831
+85837
+85843
+85847
+85853
+85889
+85903
+85909
+85931
+85933
+85991
+85999
+86011
+86017
+86027
+86029
+86069
+86077
+86083
+86111
+86113
+86117
+86131
+86137
+86143
+86161
+86171
+86179
+86183
+86197
+86201
+86209
+86239
+86243
+86249
+86257
+86263
+86269
+86287
+86291
+86293
+86297
+86311
+86323
+86341
+86351
+86353
+86357
+86369
+86371
+86381
+86389
+86399
+86413
+86423
+86441
+86453
+86461
+86467
+86477
+86491
+86501
+86509
+86531
+86533
+86539
+86561
+86573
+86579
+86587
+86599
+86627
+86629
+86677
+86689
+86693
+86711
+86719
+86729
+86743
+86753
+86767
+86771
+86783
+86813
+86837
+86843
+86851
+86857
+86861
+86869
+86923
+86927
+86929
+86939
+86951
+86959
+86969
+86981
+86993
+87011
+87013
+87037
+87041
+87049
+87071
+87083
+87103
+87107
+87119
+87121
+87133
+87149
+87151
+87179
+87181
+87187
+87211
+87221
+87223
+87251
+87253
+87257
+87277
+87281
+87293
+87299
+87313
+87317
+87323
+87337
+87359
+87383
+87403
+87407
+87421
+87427
+87433
+87443
+87473
+87481
+87491
+87509
+87511
+87517
+87523
+87539
+87541
+87547
+87553
+87557
+87559
+87583
+87587
+87589
+87613
+87623
+87629
+87631
+87641
+87643
+87649
+87671
+87679
+87683
+87691
+87697
+87701
+87719
+87721
+87739
+87743
+87751
+87767
+87793
+87797
+87803
+87811
+87833
+87853
+87869
+87877
+87881
+87887
+87911
+87917
+87931
+87943
+87959
+87961
+87973
+87977
+87991
+88001
+88003
+88007
+88019
+88037
+88069
+88079
+88093
+88117
+88129
+88169
+88177
+88211
+88223
+88237
+88241
+88259
+88261
+88289
+88301
+88321
+88327
+88337
+88339
+88379
+88397
+88411
+88423
+88427
+88463
+88469
+88471
+88493
+88499
+88513
+88523
+88547
+88589
+88591
+88607
+88609
+88643
+88651
+88657
+88661
+88663
+88667
+88681
+88721
+88729
+88741
+88747
+88771
+88789
+88793
+88799
+88801
+88807
+88811
+88813
+88817
+88819
+88843
+88853
+88861
+88867
+88873
+88883
+88897
+88903
+88919
+88937
+88951
+88969
+88993
+88997
+89003
+89009
+89017
+89021
+89041
+89051
+89057
+89069
+89071
+89083
+89087
+89101
+89107
+89113
+89119
+89123
+89137
+89153
+89189
+89203
+89209
+89213
+89227
+89231
+89237
+89261
+89269
+89273
+89293
+89303
+89317
+89329
+89363
+89371
+89381
+89387
+89393
+89399
+89413
+89417
+89431
+89443
+89449
+89459
+89477
+89491
+89501
+89513
+89519
+89521
+89527
+89533
+89561
+89563
+89567
+89591
+89597
+89599
+89603
+89611
+89627
+89633
+89653
+89657
+89659
+89669
+89671
+89681
+89689
+89753
+89759
+89767
+89779
+89783
+89797
+89809
+89819
+89821
+89833
+89839
+89849
+89867
+89891
+89897
+89899
+89909
+89917
+89923
+89939
+89959
+89963
+89977
+89983
+89989
+90001
+90007
+90011
+90017
+90019
+90023
+90031
+90053
+90059
+90067
+90071
+90073
+90089
+90107
+90121
+90127
+90149
+90163
+90173
+90187
+90191
+90197
+90199
+90203
+90217
+90227
+90239
+90247
+90263
+90271
+90281
+90289
+90313
+90353
+90359
+90371
+90373
+90379
+90397
+90401
+90403
+90407
+90437
+90439
+90469
+90473
+90481
+90499
+90511
+90523
+90527
+90529
+90533
+90547
+90583
+90599
+90617
+90619
+90631
+90641
+90647
+90659
+90677
+90679
+90697
+90703
+90709
+90731
+90749
+90787
+90793
+90803
+90821
+90823
+90833
+90841
+90847
+90863
+90887
+90901
+90907
+90911
+90917
+90931
+90947
+90971
+90977
+90989
+90997
+91009
+91019
+91033
+91079
+91081
+91097
+91099
+91121
+91127
+91129
+91139
+91141
+91151
+91153
+91159
+91163
+91183
+91193
+91199
+91229
+91237
+91243
+91249
+91253
+91283
+91291
+91297
+91303
+91309
+91331
+91367
+91369
+91373
+91381
+91387
+91393
+91397
+91411
+91423
+91433
+91453
+91457
+91459
+91463
+91493
+91499
+91513
+91529
+91541
+91571
+91573
+91577
+91583
+91591
+91621
+91631
+91639
+91673
+91691
+91703
+91711
+91733
+91753
+91757
+91771
+91781
+91801
+91807
+91811
+91813
+91823
+91837
+91841
+91867
+91873
+91909
+91921
+91939
+91943
+91951
+91957
+91961
+91967
+91969
+91997
+92003
+92009
+92033
+92041
+92051
+92077
+92083
+92107
+92111
+92119
+92143
+92153
+92173
+92177
+92179
+92189
+92203
+92219
+92221
+92227
+92233
+92237
+92243
+92251
+92269
+92297
+92311
+92317
+92333
+92347
+92353
+92357
+92363
+92369
+92377
+92381
+92383
+92387
+92399
+92401
+92413
+92419
+92431
+92459
+92461
+92467
+92479
+92489
+92503
+92507
+92551
+92557
+92567
+92569
+92581
+92593
+92623
+92627
+92639
+92641
+92647
+92657
+92669
+92671
+92681
+92683
+92693
+92699
+92707
+92717
+92723
+92737
+92753
+92761
+92767
+92779
+92789
+92791
+92801
+92809
+92821
+92831
+92849
+92857
+92861
+92863
+92867
+92893
+92899
+92921
+92927
+92941
+92951
+92957
+92959
+92987
+92993
+93001
+93047
+93053
+93059
+93077
+93083
+93089
+93097
+93103
+93113
+93131
+93133
+93139
+93151
+93169
+93179
+93187
+93199
+93229
+93239
+93241
+93251
+93253
+93257
+93263
+93281
+93283
+93287
+93307
+93319
+93323
+93329
+93337
+93371
+93377
+93383
+93407
+93419
+93427
+93463
+93479
+93481
+93487
+93491
+93493
+93497
+93503
+93523
+93529
+93553
+93557
+93559
+93563
+93581
+93601
+93607
+93629
+93637
+93683
+93701
+93703
+93719
+93739
+93761
+93763
+93787
+93809
+93811
+93827
+93851
+93871
+93887
+93889
+93893
+93901
+93911
+93913
+93923
+93937
+93941
+93949
+93967
+93971
+93979
+93983
+93997
+94007
+94009
+94033
+94049
+94057
+94063
+94079
+94099
+94109
+94111
+94117
+94121
+94151
+94153
+94169
+94201
+94207
+94219
+94229
+94253
+94261
+94273
+94291
+94307
+94309
+94321
+94327
+94331
+94343
+94349
+94351
+94379
+94397
+94399
+94421
+94427
+94433
+94439
+94441
+94447
+94463
+94477
+94483
+94513
+94529
+94531
+94541
+94543
+94547
+94559
+94561
+94573
+94583
+94597
+94603
+94613
+94621
+94649
+94651
+94687
+94693
+94709
+94723
+94727
+94747
+94771
+94777
+94781
+94789
+94793
+94811
+94819
+94823
+94837
+94841
+94847
+94849
+94873
+94889
+94903
+94907
+94933
+94949
+94951
+94961
+94993
+94999
+95003
+95009
+95021
+95027
+95063
+95071
+95083
+95087
+95089
+95093
+95101
+95107
+95111
+95131
+95143
+95153
+95177
+95189
+95191
+95203
+95213
+95219
+95231
+95233
+95239
+95257
+95261
+95267
+95273
+95279
+95287
+95311
+95317
+95327
+95339
+95369
+95383
+95393
+95401
+95413
+95419
+95429
+95441
+95443
+95461
+95467
+95471
+95479
+95483
+95507
+95527
+95531
+95539
+95549
+95561
+95569
+95581
+95597
+95603
+95617
+95621
+95629
+95633
+95651
+95701
+95707
+95713
+95717
+95723
+95731
+95737
+95747
+95773
+95783
+95789
+95791
+95801
+95803
+95813
+95819
+95857
+95869
+95873
+95881
+95891
+95911
+95917
+95923
+95929
+95947
+95957
+95959
+95971
+95987
+95989
+96001
+96013
+96017
+96043
+96053
+96059
+96079
+96097
+96137
+96149
+96157
+96167
+96179
+96181
+96199
+96211
+96221
+96223
+96233
+96259
+96263
+96269
+96281
+96289
+96293
+96323
+96329
+96331
+96337
+96353
+96377
+96401
+96419
+96431
+96443
+96451
+96457
+96461
+96469
+96479
+96487
+96493
+96497
+96517
+96527
+96553
+96557
+96581
+96587
+96589
+96601
+96643
+96661
+96667
+96671
+96697
+96703
+96731
+96737
+96739
+96749
+96757
+96763
+96769
+96779
+96787
+96797
+96799
+96821
+96823
+96827
+96847
+96851
+96857
+96893
+96907
+96911
+96931
+96953
+96959
+96973
+96979
+96989
+96997
+97001
+97003
+97007
+97021
+97039
+97073
+97081
+97103
+97117
+97127
+97151
+97157
+97159
+97169
+97171
+97177
+97187
+97213
+97231
+97241
+97259
+97283
+97301
+97303
+97327
+97367
+97369
+97373
+97379
+97381
+97387
+97397
+97423
+97429
+97441
+97453
+97459
+97463
+97499
+97501
+97511
+97523
+97547
+97549
+97553
+97561
+97571
+97577
+97579
+97583
+97607
+97609
+97613
+97649
+97651
+97673
+97687
+97711
+97729
+97771
+97777
+97787
+97789
+97813
+97829
+97841
+97843
+97847
+97849
+97859
+97861
+97871
+97879
+97883
+97919
+97927
+97931
+97943
+97961
+97967
+97973
+97987
+98009
+98011
+98017
+98041
+98047
+98057
+98081
+98101
+98123
+98129
+98143
+98179
+98207
+98213
+98221
+98227
+98251
+98257
+98269
+98297
+98299
+98317
+98321
+98323
+98327
+98347
+98369
+98377
+98387
+98389
+98407
+98411
+98419
+98429
+98443
+98453
+98459
+98467
+98473
+98479
+98491
+98507
+98519
+98533
+98543
+98561
+98563
+98573
+98597
+98621
+98627
+98639
+98641
+98663
+98669
+98689
+98711
+98713
+98717
+98729
+98731
+98737
+98773
+98779
+98801
+98807
+98809
+98837
+98849
+98867
+98869
+98873
+98887
+98893
+98897
+98899
+98909
+98911
+98927
+98929
+98939
+98947
+98953
+98963
+98981
+98993
+98999
+99013
+99017
+99023
+99041
+99053
+99079
+99083
+99089
+99103
+99109
+99119
+99131
+99133
+99137
+99139
+99149
+99173
+99181
+99191
+99223
+99233
+99241
+99251
+99257
+99259
+99277
+99289
+99317
+99347
+99349
+99367
+99371
+99377
+99391
+99397
+99401
+99409
+99431
+99439
+99469
+99487
+99497
+99523
+99527
+99529
+99551
+99559
+99563
+99571
+99577
+99581
+99607
+99611
+99623
+99643
+99661
+99667
+99679
+99689
+99707
+99709
+99713
+99719
+99721
+99733
+99761
+99767
+99787
+99793
+99809
+99817
+99823
+99829
+99833
+99839
+99859
+99871
+99877
+99881
+99901
+99907
+99923
+99929
+99961
+99971
+99989
+99991
+100003
+100019
+100043
+100049
+100057
+100069
+100103
+100109
+100129
+100151
+100153
+100169
+100183
+100189
+100193
+100207
+100213
+100237
+100267
+100271
+100279
+100291
+100297
+100313
+100333
+100343
+100357
+100361
+100363
+100379
+100391
+100393
+100403
+100411
+100417
+100447
+100459
+100469
+100483
+100493
+100501
+100511
+100517
+100519
+100523
+100537
+100547
+100549
+100559
+100591
+100609
+100613
+100621
+100649
+100669
+100673
+100693
+100699
+100703
+100733
+100741
+100747
+100769
+100787
+100799
+100801
+100811
+100823
+100829
+100847
+100853
+100907
+100913
+100927
+100931
+100937
+100943
+100957
+100981
+100987
+100999
+101009
+101021
+101027
+101051
+101063
+101081
+101089
+101107
+101111
+101113
+101117
+101119
+101141
+101149
+101159
+101161
+101173
+101183
+101197
+101203
+101207
+101209
+101221
+101267
+101273
+101279
+101281
+101287
+101293
+101323
+101333
+101341
+101347
+101359
+101363
+101377
+101383
+101399
+101411
+101419
+101429
+101449
+101467
+101477
+101483
+101489
+101501
+101503
+101513
+101527
+101531
+101533
+101537
+101561
+101573
+101581
+101599
+101603
+101611
+101627
+101641
+101653
+101663
+101681
+101693
+101701
+101719
+101723
+101737
+101741
+101747
+101749
+101771
+101789
+101797
+101807
+101833
+101837
+101839
+101863
+101869
+101873
+101879
+101891
+101917
+101921
+101929
+101939
+101957
+101963
+101977
+101987
+101999
+102001
+102013
+102019
+102023
+102031
+102043
+102059
+102061
+102071
+102077
+102079
+102101
+102103
+102107
+102121
+102139
+102149
+102161
+102181
+102191
+102197
+102199
+102203
+102217
+102229
+102233
+102241
+102251
+102253
+102259
+102293
+102299
+102301
+102317
+102329
+102337
+102359
+102367
+102397
+102407
+102409
+102433
+102437
+102451
+102461
+102481
+102497
+102499
+102503
+102523
+102533
+102539
+102547
+102551
+102559
+102563
+102587
+102593
+102607
+102611
+102643
+102647
+102653
+102667
+102673
+102677
+102679
+102701
+102761
+102763
+102769
+102793
+102797
+102811
+102829
+102841
+102859
+102871
+102877
+102881
+102911
+102913
+102929
+102931
+102953
+102967
+102983
+103001
+103007
+103043
+103049
+103067
+103069
+103079
+103087
+103091
+103093
+103099
+103123
+103141
+103171
+103177
+103183
+103217
+103231
+103237
+103289
+103291
+103307
+103319
+103333
+103349
+103357
+103387
+103391
+103393
+103399
+103409
+103421
+103423
+103451
+103457
+103471
+103483
+103511
+103529
+103549
+103553
+103561
+103567
+103573
+103577
+103583
+103591
+103613
+103619
+103643
+103651
+103657
+103669
+103681
+103687
+103699
+103703
+103723
+103769
+103787
+103801
+103811
+103813
+103837
+103841
+103843
+103867
+103889
+103903
+103913
+103919
+103951
+103963
+103967
+103969
+103979
+103981
+103991
+103993
+103997
+104003
+104009
+104021
+104033
+104047
+104053
+104059
+104087
+104089
+104107
+104113
+104119
+104123
+104147
+104149
+104161
+104173
+104179
+104183
+104207
+104231
+104233
+104239
+104243
+104281
+104287
+104297
+104309
+104311
+104323
+104327
+104347
+104369
+104381
+104383
+104393
+104399
+104417
+104459
+104471
+104473
+104479
+104491
+104513
+104527
+104537
+104543
+104549
+104551
+104561
+104579
+104593
+104597
+104623
+104639
+104651
+104659
+104677
+104681
+104683
+104693
+104701
+104707
+104711
+104717
+104723
+104729
diff --git a/gptqmodel/exllamav3/util/memory.py b/gptqmodel/exllamav3/util/memory.py
new file mode 100644
index 000000000..4ac6aa4ae
--- /dev/null
+++ b/gptqmodel/exllamav3/util/memory.py
@@ -0,0 +1,228 @@
+from dataclasses import dataclass
+from collections import deque
+import torch
+import gc
+import sys
+from pydantic import PydanticUserError
+
+# @lru_cache
+# def init_pynvml():
+# pynvml.nvmlInit()
+
+# Try to make sure device is live for correct measurement of free VRAM
+def touch_device(device: int):
+ d = torch.empty((32, 32), device = device, dtype = torch.float)
+ d = d @ d
+ d.add_(d)
+
+
+# Touch device and measure VRAM (child process)
+def touch_device_measure_vram(local_context: dict):
+ device = local_context["device"]
+ touch_device(device)
+ return torch.cuda.mem_get_info(device)
+
+
+# Reserve byte amount on device
+def set_memory_fraction_reserve(
+ reserve: int,
+ device: int
+):
+ touch_device(device)
+ free, total = torch.cuda.mem_get_info(device)
+ fraction = (free - reserve) / total
+ fraction = max(0.01, fraction)
+ torch.cuda.set_per_process_memory_fraction(fraction, device = device)
+
+
+# Reserve all but byte amount on device
+def set_memory_fraction_use(
+ use: int,
+ device: int
+):
+ touch_device(device)
+ free, total = torch.cuda.mem_get_info(device)
+ baseline = torch.cuda.memory_allocated(device)
+ fraction = min((baseline + use) / total, 1.0)
+ torch.cuda.set_per_process_memory_fraction(fraction, device = device)
+
+
+# Un-reserve VRAM
+def unset_memory_fraction(active_devices: list[int]):
+ for i in active_devices:
+ torch.cuda.set_per_process_memory_fraction(1.0, device = i)
+
+
+# Free unused VRAM
+def free_mem():
+ gc.collect()
+ torch.cuda.empty_cache()
+
+
+def list_gpu_tensors(min_size: int = 1, cuda_only: bool = True):
+ """
+ Search the current process for referenced CUDA tensors and list them.
+
+ :param min_size:
+ Ignore tensors smaller than this size, in megabytes
+
+ :param cuda_only:
+ Only list CUDA tensors
+ """
+
+ import threading
+ import warnings
+ from tabulate import tabulate
+
+ # Suppress FutureWarning from Torch every time we try to access certain objects
+ warnings.simplefilter(action = 'ignore', category = FutureWarning)
+
+ @dataclass
+ class Result:
+ paths: list[str]
+ shape: tuple
+ dtype: torch.dtype
+ device: str
+ size: int
+
+ results = {}
+ visited = set()
+
+ # Helper function to filter and collect items
+ def collect(path, item):
+ nonlocal results
+
+ # Only collect CUDA tensors
+ if not isinstance(item, torch.Tensor) or (cuda_only and not item.is_cuda):
+ return
+
+ # Tensor size in MB, filter anything smaller than the minimum size
+ size = item.nelement() * item.element_size() // (1024**2)
+ if size < min_size:
+ return
+
+ # Skip tensors in paths containing specific debug substrings
+ if any(x in path for x in [
+ ".stderr.dbg.",
+ "dbg.value_resolve_thread_list",
+ "global_vars[",
+ "local_vars[",
+ "updated_globals[",
+ ]):
+ return
+
+ # Adjust the path display for objects defined in __main__
+ if ".__main__." in path:
+ path = path[path.find(".__main__.") + 10:]
+
+ # If tensor is already recorded, just record the additional path
+ obj_id = id(item)
+ if obj_id in results and path not in results[obj_id].paths:
+ results[obj_id].paths.append(path)
+ else:
+ results[obj_id] = Result(
+ paths = [path],
+ shape = item.shape,
+ dtype = item.dtype,
+ device = str(item.device),
+ size = size
+ )
+
+ # Queue of items to scan recursively
+ queue = deque()
+
+ # Collect items that are global variables, and add to the queue
+ for name, obj in globals().items():
+ collect(name, obj)
+ queue.append((name, obj))
+
+ # Traverse each thread's frame stack, collecting items and queueing items
+ for thread_id, frame in sys._current_frames().items():
+ prefix = ""
+
+ # Skip the current frame for the current thread to avoid recursion issues
+ if thread_id == threading.get_ident():
+ frame = frame.f_back
+
+ # Collect/queue each local variable in the frame, extend the relative path prefix
+ # and walk the stack
+ while frame:
+ for name, obj in frame.f_locals.items():
+ # We actually start three levels deep but want variables in the "current" frame
+ # (i.e. the frame of the function calling list_gpu_tensors) to have a prefix of "."
+ new_path = f"{prefix[2:]}.{name}"
+ collect(new_path, obj)
+ queue.append((name, obj))
+ frame = frame.f_back
+ prefix += "."
+
+ # Process the queue by examining attributes, dictionary entries, and sequence items
+ while queue:
+ path, obj = queue.popleft()
+
+ # Iterate over entries in object with __dict__ attribute
+ try:
+ if hasattr(obj, '__dict__'):
+ for attr, value in obj.__dict__.items():
+ new_path = f"{path}.{attr}"
+ collect(new_path, value)
+ if id(value) not in visited:
+ visited.add(id(value))
+ queue.append((new_path, value))
+ except PydanticUserError:
+ pass
+
+ # If object is a dictionary, iterate through all its items
+ if isinstance(obj, dict):
+ try:
+ for key, value in obj.items():
+ new_path = f"{path}['{key}']"
+ collect(new_path, value)
+ if id(value) not in visited:
+ visited.add(id(value))
+ queue.append((new_path, value))
+ except Exception:
+ pass
+
+ # Same for list, tuple, set
+ if isinstance(obj, (list, tuple, set)):
+ for idx, item in enumerate(obj):
+ new_path = f"{path}[{idx}]"
+ collect(new_path, item)
+ if id(item) not in visited:
+ visited.add(id(item))
+ queue.append((new_path, item))
+
+ # Sort tensors by descending size
+ items = list(results.values())
+ items.sort(key = lambda x: -x.size)
+
+ # Build output table, grouped by device
+ devices: dict[str, list] = {}
+ for v in items:
+ if v.device not in devices:
+ devices[v.device] = []
+ dev = devices[v.device]
+ dev.append([
+ v.size,
+ v.paths[0],
+ tuple(v.shape),
+ str(v.dtype).replace("torch.", "")
+ ])
+ for p in v.paths[1:]:
+ dev.append([
+ None,
+ " + " + p,
+ None,
+ None
+ ])
+
+ # Print tables to console
+ for k in sorted(devices.keys()):
+ print()
+ print("--------------")
+ print(f"| {k:10} |")
+ print("--------------")
+ print()
+ headers = ["size // MB", "path", "shape", "dtype"]
+ print(tabulate(devices[k], headers = headers, tablefmt = "github", intfmt=","))
diff --git a/gptqmodel/exllamav3/util/misc.py b/gptqmodel/exllamav3/util/misc.py
new file mode 100644
index 000000000..91e51d6a2
--- /dev/null
+++ b/gptqmodel/exllamav3/util/misc.py
@@ -0,0 +1,137 @@
+import math
+import threading
+import time
+import torch
+import socket, contextlib
+import weakref
+
+lock = threading.RLock()
+
+def synchronized(func):
+ def wrapper(*args, **kwargs):
+ with lock:
+ return func(*args, **kwargs)
+ return wrapper
+
+def align_to(value, alignment):
+ return int(math.ceil(value / alignment) * alignment)
+
+
+class Timer:
+ """
+ Context manager to record duration
+ """
+
+ def __enter__(self):
+ self.start_time = time.time()
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self.end_time = time.time()
+ self.interval = self.end_time - self.start_time
+
+
+def cuda_sync_active():
+ """
+ Calling torch.cuda.synchronize() will create a CUDA context on CUDA:0 even if that device is not being used.
+ This function synchronizes only devices actively used by Torch in the current process.
+ """
+ for device_id in range(torch.cuda.device_count()):
+ device = torch.device(f'cuda:{device_id}')
+ if torch.cuda.memory_allocated(device) > 0:
+ torch.cuda.synchronize(device)
+
+
+def next_power_of_2(x):
+ return 1 if x == 0 else 2**(x - 1).bit_length()
+
+
+def human_time(seconds: float) -> str:
+ seconds = round(seconds)
+ minutes = seconds // 60
+ hours = minutes // 60
+ minutes -= hours * 60
+ if hours:
+ if minutes:
+ hs = "s" if hours > 1 else ""
+ ms = "s" if minutes > 1 else ""
+ return f"{hours} hour{hs}, {minutes} minute{ms}"
+ else:
+ hs = "s" if hours > 1 else ""
+ return f"{hours} hour{hs}"
+ elif minutes:
+ ms = "s" if minutes > 1 else ""
+ return f"{minutes} minute{ms}"
+ else:
+ return f"< 1 minute"
+
+
+def first_not_none(*values):
+ return next((v for v in values if v is not None), None)
+
+
+def ratio_split(d, weights, chunk_size = 128):
+ assert d % chunk_size == 0, "Total must be divisible by chunk size"
+ total_chunks = d // chunk_size
+ total_weight = sum(weights)
+ ideal_chunks = [total_chunks * w / total_weight for w in weights]
+ base_chunks = [int(c) for c in ideal_chunks]
+ remainder = total_chunks - sum(base_chunks)
+ residuals = [c - int(c) for c in ideal_chunks]
+ for i in sorted(range(len(residuals)), key = lambda i: -residuals[i])[:remainder]:
+ base_chunks[i] += 1
+ final_alloc = [c * chunk_size for c in base_chunks]
+ assert sum(final_alloc) == d
+ return final_alloc
+
+
+def find_free_port() -> int:
+ with contextlib.closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
+ s.bind(("", 0))
+ return s.getsockname()[1]
+
+
+class Cleanupper:
+ """
+ Utility class to call cleanup functions at the end of the __main__ scope. Similar functionality to
+ atexit but called before Python starts tearing down objects/threads.
+ """
+
+ def __init__(self):
+ self.atexit_fns = []
+ weakref.finalize(self, self._shutdown)
+
+ def register_atexit(self, fn):
+ self.atexit_fns.append(fn)
+
+ def unregister_atexit(self, fn):
+ if fn in self.atexit_fns:
+ self.atexit_fns.remove(fn)
+
+ def _shutdown(self):
+ for fn in self.atexit_fns:
+ fn()
+ self.atexit_fns = []
+
+
+def set_process_priority_and_affinity():
+ import psutil, os
+ import multiprocessing as mp
+
+ p = psutil.Process(os.getpid())
+ # Try to bump priority slightly. May need sudo (?)
+ try:
+ p.nice(psutil.ABOVE_NORMAL_PRIORITY_CLASS if os.name == "nt" else -5)
+ except PermissionError:
+ pass
+ except Exception as e:
+ pass
+
+ # Pin to a core
+ # TODO: Pick an idle core automatically?
+ try:
+ p.cpu_affinity([0]) # pick an isolated/quiet core if possible
+ except AttributeError:
+ pass
+ except Exception as e:
+ pass
diff --git a/gptqmodel/exllamav3/util/progress.py b/gptqmodel/exllamav3/util/progress.py
new file mode 100644
index 000000000..c4b855e5c
--- /dev/null
+++ b/gptqmodel/exllamav3/util/progress.py
@@ -0,0 +1,45 @@
+import sys
+from rich.progress import Progress, BarColumn, TextColumn, TimeElapsedColumn, TimeRemainingColumn
+
+class ProgressBar:
+
+ def __init__(self, text: str, count: int, transient: bool = True):
+ self.text = text
+ self.count = count
+ self.transient = transient
+ if self.text:
+ self.progress = Progress(
+ TextColumn("[progress.description]{task.description}"),
+ BarColumn(bar_width = None),
+ "[progress.percentage]{task.percentage:>3.0f}%",
+ TimeElapsedColumn(),
+ TimeRemainingColumn(),
+ transient = transient,
+ speed_estimate_period = 600.0,
+ )
+ self.task_id = self.progress.add_task(text, total = count)
+
+ def __enter__(self):
+ if self.text:
+ self.progress.start()
+ sys.stdout.flush()
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ if self.text:
+ if not self.transient:
+ self.progress.update(self.task_id, completed = self.count)
+ self.progress.stop()
+
+ def update(self, value: int):
+ if self.text:
+ self.progress.update(self.task_id, completed = value)
+ sys.stdout.flush()
+
+ def new_task(self, text: str, count: int):
+ self.text = text
+ self.count = count
+ if self.text:
+ self.progress.update(self.task_id, description = self.text, total = count, progress = 0)
+
+
diff --git a/gptqmodel/exllamav3/util/tensor.py b/gptqmodel/exllamav3/util/tensor.py
new file mode 100644
index 000000000..c5013e738
--- /dev/null
+++ b/gptqmodel/exllamav3/util/tensor.py
@@ -0,0 +1,210 @@
+from __future__ import annotations
+import torch
+
+class SeqTensor:
+
+ PAGE_SIZE = 256
+
+ tensor: torch.Tensor | None
+ seq_dim: int
+ seq_len: int
+ seq_cap: int
+
+ def __init__(
+ self,
+ shape: tuple,
+ dtype: torch.dtype,
+ seq_dim: int,
+ device: torch.device = "cpu",
+ init_cap: int = -1
+ ):
+ if seq_dim < 0: seq_dim = len(shape) + seq_dim
+ self.seq_dim = seq_dim
+ self.seq_len = 0
+ if init_cap == -1:
+ init_cap = self.PAGE_SIZE
+ else:
+ init_cap = (init_cap // self.PAGE_SIZE + 1) * self.PAGE_SIZE
+ shape = list(shape)
+ shape[seq_dim] = self.seq_cap = init_cap
+ shape = tuple(shape)
+ # Lazily allocate inner Tensor object to avoid committing too much virtual memory, which can crash the
+ # process on Windows
+ # self.tensor = torch.empty(shape, dtype = dtype, device = device)
+ self.init_shape = shape
+ self.dtype = dtype
+ self.device = device
+ self.tensor = None
+
+ def __len__(self):
+ return self.seq_len
+
+ def __bool__(self):
+ return self.seq_len > 0
+
+ def _ensure_init(self):
+ if self.tensor is None:
+ self.tensor = torch.empty(self.init_shape, dtype = self.dtype, device = self.device)
+
+ @staticmethod
+ def from_tensor(tensor: torch.Tensor, seq_dim: int):
+ s = SeqTensor(tensor.shape, tensor.dtype, seq_dim, tensor.device, init_cap = tensor.shape[seq_dim])
+ s.append(tensor)
+ return s
+
+ def clone(self, drop: int | None = None):
+ if drop and drop <= self.seq_len:
+ return SeqTensor.from_tensor(self.torch_slice(None, self.seq_len - drop), self.seq_dim)
+ else:
+ return SeqTensor.from_tensor(self.torch(), self.seq_dim)
+
+ def clear(self):
+ self.seq_len = 0
+
+ def set(self, new_data: SeqTensor | torch.tensor | None = None):
+ self.clear()
+ self.append(new_data)
+
+ def append(self, new_data: SeqTensor | torch.tensor | None):
+ self._ensure_init()
+ if new_data is None: return
+ if isinstance(new_data, SeqTensor):
+ new_data = new_data.torch()
+ new_len = new_data.shape[self.seq_dim]
+ end_pos = self.seq_len + new_len
+ if end_pos >= self.seq_cap:
+ new_cap = (end_pos // self.PAGE_SIZE + 1) * self.PAGE_SIZE
+ grow_shape = list(new_data.shape)
+ grow_shape[self.seq_dim] = new_cap - self.seq_cap
+ grow_shape = tuple(grow_shape)
+ grow_tensor = torch.empty(grow_shape, dtype = self.tensor.dtype, device = self.tensor.device)
+ self.tensor = torch.cat((self.tensor, grow_tensor), dim = self.seq_dim)
+ self.seq_cap = new_cap
+ s = self.tensor.narrow(self.seq_dim, self.seq_len, end_pos - self.seq_len)
+ s.copy_(new_data)
+ self.seq_len += new_len
+
+ def truncate(self, new_len: int):
+ assert new_len <= self.seq_len
+ self.seq_len = new_len
+
+ def torch(self):
+ self._ensure_init()
+ s = self.tensor.narrow(self.seq_dim, 0, self.seq_len)
+ return s
+
+ def slice(self, a: int | None, b: int | None):
+ return SeqTensor.from_tensor(self.torch_slice(a, b), self.seq_dim)
+
+ def torch_slice(self, a: int | None, b: int | None):
+ self._ensure_init()
+ if a is None and b is None:
+ return self.torch()
+ elif b is None:
+ s = self.tensor.narrow(self.seq_dim, a, self.seq_len - a)
+ elif a is None:
+ s = self.tensor.narrow(self.seq_dim, 0, b)
+ else:
+ s = self.tensor.narrow(self.seq_dim, a, b - a)
+ return s
+
+
+no_default = object()
+
+def get_for_device(
+ input_dict: dict,
+ key: str | int,
+ device: torch.device,
+ default = no_default,
+) -> torch.Tensor | None:
+ """
+ Read a tensor from a dict and ensure it is available on the specified device. Caches access per device and may
+ break if the tensor is updated after being accessed in this way. Intended for tensors that are read-only for the
+ lifetime of the dict, such as RoPE coefficients during a single forward pass.
+ """
+ if key not in input_dict and default is not no_default:
+ return default
+
+ if "dev_cache" not in input_dict:
+ cache = {}
+ input_dict["dev_cache"] = cache
+ else:
+ cache = input_dict["dev_cache"]
+
+ cache_key = f"{key}[{str(device)}]"
+ if cache_key in cache:
+ return cache[cache_key]
+
+ v = input_dict[key]
+ dv = None if v is None else input_dict[key].to(device)
+ cache[cache_key] = dv
+ return dv
+
+
+buffered_aranges = {}
+def buffered_arange(r: int, device: torch.device):
+ if r not in buffered_aranges:
+ buffered_aranges[r] = torch.arange(r)
+ return get_for_device(buffered_aranges, r, device)
+
+
+def to2(
+ x: torch.Tensor,
+ dtype1: torch.dtype | None,
+ dtype2: torch.dtype | None = None
+):
+ if dtype1 is not None:
+ x = x.to(dtype1)
+ elif dtype2 is not None:
+ x = x.to(dtype2)
+ return x
+
+
+def save_tensor_image(
+ t: torch.Tensor,
+ path: str,
+):
+ import matplotlib.cm as cm
+ from PIL import Image
+
+ t = t.detach().to("cpu", copy = True).float()
+
+ k = 3
+ _, sigma = t.mean(), t.std()
+ lo, hi = -k * sigma, k * sigma
+ t.clamp_(lo, hi)
+ t -= lo
+ t /= (hi - lo + 1e-8)
+
+ rgba = cm.get_cmap("berlin")(t.numpy())
+ rgb8 = (rgba[..., :3] * 255).astype("uint8")
+ im = Image.fromarray(rgb8)
+ im.save(path)
+
+
+class GTensorCache:
+ def __init__(self):
+ self.cache = {}
+
+ def make_key(self, device, shape, dtype, x):
+ device = torch.device(device)
+ return f"{device}/{str(shape)}/{str(dtype)}/{x}"
+
+ def get(self, device, shape, dtype, x = ""):
+ key = self.make_key(device, shape, dtype, x)
+ if key not in self.cache:
+ refc, v = (0, torch.empty(shape, dtype = dtype, device = device))
+ else:
+ refc, v = self.cache[key]
+ self.cache[key] = (refc + 1, v)
+ return v
+
+ def drop(self, device, shape, dtype, x = ""):
+ key = self.make_key(device, shape, dtype, x)
+ refc, v = self.cache[key]
+ if refc == 1:
+ del self.cache[key]
+ else:
+ self.cache[key] = (refc - 1, v)
+
+g_tensor_cache = GTensorCache()
diff --git a/gptqmodel/looper/awq_processor.py b/gptqmodel/looper/awq_processor.py
index 70bf1403a..cbf0b9cb6 100644
--- a/gptqmodel/looper/awq_processor.py
+++ b/gptqmodel/looper/awq_processor.py
@@ -27,10 +27,10 @@
from ..quantization.awq.quantize.scale import apply_clip, apply_scale
from ..quantization.awq.utils.module import append_str_prefix, get_op_name, get_op_by_name
from ..quantization.awq.utils.utils import get_best_device
-from ..quantization.config import FORMAT, METHOD, QuantizeConfig
+from ..quantization.config import FORMAT, METHOD, QuantizeConfig, resolve_quant_format
from ..utils.ctx import ctx
from ..utils.device import get_device
-from ..utils.failsafe import normalize_failsafe
+from ..utils.fallback import normalize_fallback
from ..utils.logger import setup_logger, log_time_block
from ..utils.model import find_modules, get_module_by_name_prefix, move_to, create_quant_module, pack_module
from ..utils.module_locks import parent_module_lock
@@ -94,7 +94,8 @@ def __init__(
self.gptq_model = gptq_model
model_kernel = getattr(self.gptq_model, "qlinear_kernel", None)
- self.qlinear_kernel = model_kernel or self._select_qlinear_kernel_for_format(qcfg.format)
+ self.format = resolve_quant_format(qcfg.format, qcfg.method)
+ self.qlinear_kernel = model_kernel or self._select_qlinear_kernel_for_format(self.format)
self.model = model
# Whether to apply clipping to the model during quantization. Some models may perform better with this set to False.
@@ -104,8 +105,6 @@ def __init__(
# " Default is 1GB (1024 * 1024 * 1024)."
self.max_chunk_memory = 1024 * 1024 * 1024
- self.format = qcfg.format
-
# Whether to scale using both w/x or just x.
self.duo_scaling = True
@@ -115,8 +114,8 @@ def __init__(
self._rotary_source_id: Optional[int] = None
self._initialize_sample_counts()
self._module_forward_kwargs.setdefault("attention_mask", None)
- # Preserve failsafe preference so AWQ can optionally fall back when no calibration data or activations are available.
- self.failsafe = qcfg.failsafe
+ # Preserve fallback preference so AWQ can optionally fall back when no calibration data or activations are available.
+ self.fallback = qcfg.fallback
def _get_root_rotary(self) -> Optional[nn.Module]:
if self.gptq_model.rotary_embedding:
@@ -194,8 +193,8 @@ def _select_qlinear_kernel_for_format(self, format_value: FORMAT):
def _resolve_qlinear_kernel(self, module_name: Optional[str] = None):
# Honor per-module dynamic format overrides when present.
format_override = self.qcfg.dynamic_get(module_name, "format", None) if module_name else None
- target_format = format_override or self.qcfg.format
- if target_format == self.qcfg.format:
+ target_format = resolve_quant_format(format_override or self.qcfg.format, self.qcfg.method)
+ if target_format == self.format:
model_kernel = getattr(self.gptq_model, "qlinear_kernel", None)
if model_kernel is not None:
return model_kernel
@@ -308,7 +307,7 @@ def _layer_input_features(self, state: _AWQLayerState) -> Dict[str, torch.Tensor
# features[root] = tensors[0]
return features
- def _quantize_layer_failsafe(
+ def _quantize_layer_fallback(
self,
layer_index: int,
state: _AWQLayerState,
@@ -416,12 +415,12 @@ def _refresh_forward_kwargs_from_cache(self) -> None:
self._module_forward_kwargs = refreshed
- def _should_failsafe_group(
+ def _should_fallback_group(
self,
layer_names: List[str],
input_feat: Dict[str, torch.Tensor],
) -> bool:
- from ..utils.failsafe import should_use_failsafe
+ from ..utils.fallback import should_use_fallback
captured_tokens = 0
for name in layer_names:
@@ -433,8 +432,8 @@ def _should_failsafe_group(
captured_tokens += feat.numel() // max(hidden, 1)
expected_tokens = getattr(self, "total_calibration_tokens", None) or self._nsamples_total
- return should_use_failsafe(
- self.failsafe,
+ return should_use_fallback(
+ self.fallback,
float(captured_tokens),
float(expected_tokens) if expected_tokens else None,
)
@@ -466,10 +465,10 @@ def _quantize_layer(self, layer_index: int, state: _AWQLayerState) -> None:
input_feat = self._layer_input_features(state)
missing = [name for name, tensor in input_feat.items() if tensor.numel() == 0]
- if missing and not self.failsafe:
+ if missing and not self.fallback:
raise RuntimeError(
f"AWQProcessor error: missing activation features for modules {missing} "
- f"with failsafe disabled."
+ f"with fallback disabled."
)
# Filtering MLP modules like Qwen3MoeSparseMoeBlock
@@ -521,7 +520,7 @@ def unwrap(m):
filtered_module_config: List[Dict] = []
skipped_groups: List[Tuple[List[str], List[str]]] = []
- failsafe_names = set()
+ fallback_names = set()
for cfg in sanitized_module_config:
layers_sample = cfg.get("layers") or []
prev_module = cfg.get("prev_op")
@@ -530,8 +529,8 @@ def unwrap(m):
get_op_name(layer_module_ref, layer) if isinstance(layer, torch.nn.Module) else str(layer)
for layer in layers_sample
]
- if self.failsafe and self._should_failsafe_group(layer_names, input_feat):
- failsafe_names.update(layer_names)
+ if self.fallback and self._should_fallback_group(layer_names, input_feat):
+ fallback_names.update(layer_names)
continue
first_layer_module = layers_sample[0] if layers_sample else None
@@ -576,7 +575,7 @@ def unwrap(m):
)
sanitized_module_config = filtered_module_config
- if not sanitized_module_config and not failsafe_names:
+ if not sanitized_module_config and not fallback_names:
log.warning(
"AWQProcessor: no valid scaling groups for layer %s after filtering; marking layer as quantized.",
layer_index,
@@ -662,7 +661,7 @@ def unwrap(m):
if self.apply_clip:
clip_list = self._search_best_clip(
layer_module_ref,
- {name: named.module for name, named in named_childs.items() if name not in failsafe_names},
+ {name: named.module for name, named in named_childs.items() if name not in fallback_names},
input_feat,
)
apply_clip(layer_module_ref, clip_list)
@@ -671,24 +670,24 @@ def unwrap(m):
get_op_name(self.model, layer_module_ref) + ".",
)
- failsafe_named_childs = {
+ fallback_named_childs = {
n: named_childs[n]
- for n in failsafe_names
+ for n in fallback_names
if n in named_childs
}
- named_childs = {name: named for name, named in named_childs.items() if name in input_feat and name not in failsafe_names}
+ named_childs = {name: named for name, named in named_childs.items() if name in input_feat and name not in fallback_names}
self.apply_quant(named_childs, scales_list)
- if failsafe_named_childs:
+ if fallback_named_childs:
log.warning(
"AWQProcessor: layer %s fallback quant %d modules: %s",
layer_index,
- len(failsafe_named_childs),
- list(failsafe_named_childs)[:6],
+ len(fallback_named_childs),
+ list(fallback_named_childs)[:6],
)
- self.apply_quant(failsafe_named_childs, scales_list=[])
+ self.apply_quant(fallback_named_childs, scales_list=[])
state.quantized = True
state.modules.clear()
@@ -1185,9 +1184,9 @@ def _module_forward(
effective_quant_batch_size = self._quant_batch_size if self._quant_batch_size and self._quant_batch_size > 0 else None
if (
- effective_quant_batch_size is None
- or x.dim() == 0
- or x.shape[0] <= effective_quant_batch_size
+ effective_quant_batch_size is None
+ or x.dim() == 0
+ or x.shape[0] <= effective_quant_batch_size
):
module_output = module(x, **module_kwargs)
if isinstance(module_output, tuple):
@@ -1353,10 +1352,10 @@ def _sanitize_kwargs(self, inputs_kwargs, module):
sanitized_kwargs[k] = v
return sanitized_kwargs
- def preprocess(self, module: NamedModule, failsafe=None, **kwargs):
+ def preprocess(self, module: NamedModule, fallback=None, **kwargs):
# Track the most recent preference so the processor can decide whether
# to fall back to simple quantization when activations are missing.
- self.failsafe = normalize_failsafe(failsafe, self.qcfg.failsafe)
+ self.fallback = normalize_fallback(fallback, self.qcfg.fallback)
layer_state = self._get_layer_state(module.layer_index)
with layer_state.lock:
layer_state.modules[module.name] = module
@@ -1483,7 +1482,7 @@ def pack_module(self, module):
create_quant_module(
name=module.full_name,
linear_cls=quant_linear_cls,
- bits=self.qcfg.bits,
+ bits=self.qcfg.runtime_bits,
desc_act=self.qcfg.desc_act,
dynamic=self.qcfg.dynamic,
group_size=self.qcfg.group_size,
@@ -1493,6 +1492,7 @@ def pack_module(self, module):
device=self.qcfg.device,
lm_head_name=self.gptq_model.lm_head,
pack_dtype=self.qcfg.pack_dtype,
+ format=self.format,
register_buffers=False,
)
if timer is not None and create_start is not None:
@@ -1536,7 +1536,7 @@ def finalize(self, model: BaseQModel, **kwargs):
# set quantized state
model.quantized = True
- model.quantize_config.quant_method = METHOD.AWQ
+ model.quantize_config.method = METHOD.AWQ
super().finalize(model=model, **kwargs)
diff --git a/gptqmodel/looper/exllamav3_processor.py b/gptqmodel/looper/exllamav3_processor.py
new file mode 100644
index 000000000..6556a325b
--- /dev/null
+++ b/gptqmodel/looper/exllamav3_processor.py
@@ -0,0 +1,355 @@
+# SPDX-FileCopyrightText: 2024-2025 ModelCloud.ai
+# SPDX-FileCopyrightText: 2024-2025 qubitium@modelcloud.ai
+# SPDX-License-Identifier: Apache-2.0
+# Contact: qubitium@modelcloud.ai, x.com/qubitium
+
+from __future__ import annotations
+
+import copy
+import threading
+import time
+from typing import Callable, Dict, Optional, Tuple
+
+import torch
+import transformers
+from torch.nn import Module
+from torch.nn.modules.conv import _ConvNd
+
+from ..exllamav3.modules.quant.exl3_lib.quantize import quantize_exl3
+from ..looper.loop_processor import DTYPE_SIZE_COLUMN, MODULE_FEATURE_COLUMN, LoopProcessor
+from ..looper.named_module import NamedModule
+from ..models import BaseQModel
+from ..models.writer import (
+ PROCESS_LOG_FWD_TIME,
+ PROCESS_LOG_LAYER,
+ PROCESS_LOG_MODULE,
+ PROCESS_LOG_NAME,
+ PROCESS_LOG_TIME,
+ PROCESS_USED_MEMORY,
+ QUANT_LOG_DAMP,
+ QUANT_LOG_LOSS,
+ QUANT_LOG_NSAMPLES,
+)
+from ..nn_modules.exllamav3 import ExllamaV3Linear
+from ..quantization import QuantizeConfig
+from ..quantization.config import EXL3QuantizeConfig, FORMAT, GPTQQuantizeConfig, METHOD
+from ..quantization.gptq import GPTQ
+from ..utils.device import get_device
+from ..utils.exllamav3 import create_exllamav3_module
+from ..utils.logger import setup_logger
+from ..utils.module_locks import parent_module_lock
+
+
+setup_logger()
+
+_EXL3_SIGMA_REG = 0.025
+_OUT_SCALES_TO_ARG = {
+ "always": True,
+ "never": False,
+ "auto": None,
+ None: None,
+}
+
+
+def clone_exllamav3_config_for_module(
+ qcfg: EXL3QuantizeConfig,
+ module_full_name: str,
+) -> Optional[EXL3QuantizeConfig]:
+ if qcfg.dynamic_get(layer_name=module_full_name) == False:
+ return None
+
+ qcfg_clone = copy.deepcopy(qcfg)
+
+ if qcfg.dynamic is not None:
+ qcfg_clone.bits = qcfg.dynamic_get(module_full_name, "bits", qcfg_clone.bits)
+ qcfg_clone.head_bits = qcfg.dynamic_get(module_full_name, "head_bits", qcfg_clone.head_bits)
+
+ out_scales_override = qcfg.dynamic_get(module_full_name, "out_scales", None)
+ if out_scales_override is not None:
+ qcfg_clone.out_scales = out_scales_override
+
+ codebook_override = qcfg.dynamic_get(module_full_name, "codebook", None)
+ if codebook_override is not None:
+ qcfg_clone.codebook = codebook_override
+
+ calibration_override = qcfg.dynamic_get(module_full_name, "calibration", None)
+ if calibration_override is not None:
+ qcfg_clone.calibration = calibration_override
+
+ qcfg_clone.__post_init__()
+ return qcfg_clone
+
+
+class EXL3Processor(LoopProcessor):
+ def __init__(
+ self,
+ tokenizer,
+ qcfg: QuantizeConfig,
+ calibration,
+ prepare_dataset_func,
+ calibration_concat_size: Optional[int],
+ calibration_sort: Optional[str],
+ batch_size: int,
+ require_fwd: bool = True,
+ calibration_concat_separator: Optional[str] = None,
+ lm_head_name: str = "lm_head",
+ ):
+ super().__init__(
+ tokenizer=tokenizer,
+ qcfg=qcfg,
+ calibration=calibration,
+ calibration_concat_size=calibration_concat_size,
+ calibration_sort=calibration_sort,
+ calibration_concat_separator=calibration_concat_separator,
+ prepare_dataset_func=prepare_dataset_func,
+ batch_size=batch_size,
+ require_fwd=require_fwd,
+ fwd_after_process=True,
+ subset_forward_early_stop=True,
+ )
+
+ self.avg_losses = []
+ self.lm_head_name = lm_head_name
+ self._stats_lock = threading.Lock()
+
+ def set_calibration_dataset(self, calibration_dataset):
+ raise NotImplementedError("EXL3Processor's calibration_dataset cannot be modified")
+
+ def preprocess(self, module: NamedModule, fallback=None, **kwargs):
+ del fallback, kwargs
+
+ module_qcfg = clone_exllamav3_config_for_module(self.qcfg, module.full_name)
+ if module_qcfg is None:
+ return
+
+ capture_qcfg = GPTQQuantizeConfig(
+ bits=max(1, module_qcfg.runtime_bits),
+ group_size=-1,
+ desc_act=False,
+ sym=True,
+ device=module_qcfg.device,
+ pack_dtype=module_qcfg.pack_dtype,
+ )
+
+ task = GPTQ(module=module, qcfg=capture_qcfg)
+ task.expected_nsamples = getattr(self, "total_calibration_tokens", None)
+ task.quantizer.configure(perchannel=True)
+
+ self.tasks[module.name] = {
+ "capture": task,
+ "qcfg": module_qcfg,
+ }
+
+ def is_skipped(self, module: NamedModule) -> bool:
+ return self.tasks.get(module.name, False) is False
+
+ def pre_process_fwd_hook(self, name: str) -> Callable[[Module, Tuple[torch.Tensor, ...], torch.Tensor], None]:
+ def tmp(module, inp: Tuple[torch.Tensor, ...], out: torch.Tensor):
+ capture = self.tasks[name]["capture"]
+ batch_idx = self.current_batch_index()
+ capture.add_batch(inp[0].data, out.data, batch_index=batch_idx)
+ del inp, out
+
+ return tmp
+
+ def _is_lm_head(self, module: NamedModule) -> bool:
+ if module.full_name == self.lm_head_name:
+ return True
+ return module.full_name.endswith(f".{self.lm_head_name}")
+
+ def _target_bits(self, module: NamedModule, module_qcfg: EXL3QuantizeConfig) -> int:
+ if self._is_lm_head(module) and module_qcfg.head_bits is not None:
+ return max(1, int(module_qcfg.head_bits))
+ return max(1, module_qcfg.runtime_bits)
+
+ def _build_quant_args(
+ self,
+ module: NamedModule,
+ module_qcfg: EXL3QuantizeConfig,
+ device: torch.device,
+ ) -> Dict[str, object]:
+ quant_args: Dict[str, object] = {
+ "K": self._target_bits(module, module_qcfg),
+ "devices": [device],
+ "apply_out_scales": _OUT_SCALES_TO_ARG.get(module_qcfg.out_scales, None),
+ "sigma_reg": _EXL3_SIGMA_REG,
+ "seed": 787,
+ }
+
+ if module_qcfg.codebook == "mcg":
+ quant_args["mcg"] = True
+ elif module_qcfg.codebook == "mul1":
+ quant_args["mul1"] = True
+
+ return quant_args
+
+ def _quant_input_weight(self, capture: GPTQ, device: torch.device) -> torch.Tensor:
+ normalized = capture.clone_module(copy=True, device=device)
+ return normalized.t().contiguous().to(torch.float32)
+
+ def _restore_module_weight(self, module: NamedModule, quantized_weight: torch.Tensor) -> torch.Tensor:
+ target = module.module if isinstance(module, NamedModule) else module
+
+ if isinstance(target, transformers.Conv1D):
+ return quantized_weight.contiguous().view_as(target.weight.data)
+
+ if isinstance(target, (torch.nn.Linear, _ConvNd)):
+ return quantized_weight.t().contiguous().view_as(target.weight.data)
+
+ raise NotImplementedError(f"Unsupported EXL3 module type: {target.__class__.__name__}")
+
+ def process(
+ self,
+ module: NamedModule,
+ device: torch.device = None,
+ subset: Optional[Dict[str, NamedModule]] = None,
+ previous_subset: Optional[Dict[str, NamedModule]] = None,
+ subset_index: Optional[int] = None,
+ subset_total: Optional[int] = None,
+ ):
+ del subset, previous_subset, subset_index, subset_total
+
+ base_title = f"Quantizing {module.name} in layer"
+ self._pause_controller.register_and_draw_progress_bar(self.pb, title=base_title, subtitle="")
+
+ task_entry = self.tasks[module.name]
+ capture: GPTQ = task_entry["capture"]
+ module_qcfg: EXL3QuantizeConfig = task_entry["qcfg"]
+
+ target_device = device or get_device(module.module)
+ target_device = torch.device(target_device)
+ if target_device.type != "cuda":
+ raise ValueError("EXL3 quantization requires CUDA/HIP execution.")
+
+ start_time = time.perf_counter()
+ capture.finalize_hessian(target_device=target_device)
+ hessian = capture.H
+ if hessian is None:
+ raise RuntimeError(f"EXL3 failed to capture Hessian for module `{module.full_name}`.")
+ if capture.nsamples <= 0:
+ raise RuntimeError(f"EXL3 captured no calibration activations for module `{module.full_name}`.")
+
+ h_data = {
+ "H": hessian,
+ "count": capture.nsamples,
+ "finalized": False,
+ }
+
+ quant_args = self._build_quant_args(module, module_qcfg, target_device)
+ input_weight = self._quant_input_weight(capture, target_device)
+ weight_q, proxy_err, out_tensors = quantize_exl3(
+ weight=input_weight,
+ H_data=h_data,
+ quant_args=quant_args,
+ return_weight_q=True,
+ )
+ duration = time.perf_counter() - start_time
+
+ stream_payload = dict(out_tensors)
+ if module.bias is not None:
+ stream_payload["bias"] = module.bias.detach()
+ module.stream_state_payload_to_cpu(stream_payload)
+
+ restored_weight = self._restore_module_weight(module, weight_q)
+ module.weight.data = restored_weight.to(dtype=module.weight.dtype)
+
+ workspace_summary = getattr(capture, "_borrow_workspace_last_summary", None)
+ workspace_totals = getattr(capture, "_borrow_workspace_totals", None)
+
+ if isinstance(proxy_err, str):
+ loss_display = proxy_err
+ else:
+ loss_display = f"{proxy_err:.10f}" if isinstance(proxy_err, (int, float)) else "unknown"
+
+ stat = {
+ PROCESS_LOG_NAME: self.name(),
+ PROCESS_LOG_LAYER: module.layer_index,
+ PROCESS_LOG_MODULE: module.name,
+ MODULE_FEATURE_COLUMN: self.module_feature_summary(module),
+ DTYPE_SIZE_COLUMN: self.module_dtype_size_summary(module),
+ QUANT_LOG_LOSS: loss_display,
+ QUANT_LOG_NSAMPLES: f"{capture.nsamples}",
+ QUANT_LOG_DAMP: f"{_EXL3_SIGMA_REG:.5f}",
+ PROCESS_LOG_TIME: f"{duration:.3f}",
+ PROCESS_LOG_FWD_TIME: self.formatted_fwd_time(),
+ PROCESS_USED_MEMORY: self.device_memory_report(),
+ }
+
+ if workspace_summary:
+ requests = int(workspace_summary.get("requests", 0) or 0)
+ if requests:
+ hit_rate = float(workspace_summary.get("hit_rate", 0.0) or 0.0)
+ chunk_rows = workspace_summary.get("chunk_rows")
+ stat["workspace_cache_requests"] = str(requests)
+ stat["workspace_cache_hit_rate"] = f"{hit_rate:.1%}"
+ stat["workspace_stage_dtype"] = workspace_summary.get("staging_dtype", "")
+ if chunk_rows is not None:
+ stat["workspace_chunk_rows"] = str(chunk_rows)
+ if workspace_totals:
+ total_requests = int(workspace_totals.get("requests", 0) or 0)
+ if total_requests:
+ cumulative_hit_rate = (
+ float(workspace_totals.get("materialized_hits", 0) or 0.0) / total_requests
+ )
+ stat["workspace_total_requests"] = str(total_requests)
+ stat["workspace_total_hit_rate"] = f"{cumulative_hit_rate:.1%}"
+
+ if self.qcfg.dynamic is not None:
+ stat["dynamic"] = self.qcfg.dynamic_get(layer_name=module.full_name)
+
+ with self._stats_lock:
+ self.durations.append(duration)
+ if isinstance(proxy_err, (int, float)):
+ self.avg_losses.append(proxy_err)
+ self.module_names.append(f"layer-{module.layer_index}-{module.name}")
+ self.log.append(stat)
+
+ self.log_new_row(stat)
+
+ capture.free()
+ del input_weight, restored_weight, weight_q, out_tensors, stream_payload
+
+ def submodule_finalize(self, module: NamedModule, model: BaseQModel, **kwargs):
+ del kwargs
+
+ module.stream_sync()
+
+ tensors: Dict[str, torch.Tensor] = {}
+ with self._stats_lock:
+ module.state.pop("w", None)
+ for tensor_name in ("trellis", "suh", "svh", "su", "sv", "bias", "mcg", "mul1"):
+ tensor = module.state.pop(tensor_name, None)
+ if tensor is not None:
+ tensors[tensor_name] = tensor.clone()
+
+ parent_key = getattr(module, "full_name", getattr(module, "name", None))
+ with parent_module_lock(parent_key):
+ create_exllamav3_module(
+ module_root=model.model,
+ name=module.full_name,
+ submodule=module,
+ tensors=tensors,
+ )
+
+ module.unregister_parameter("weight")
+ if getattr(module, "bias", None) is not None:
+ module.unregister_parameter("bias")
+
+ def finalize(self, model: BaseQModel, **kwargs):
+ model.quantized = True
+ model.quantize_config.method = METHOD.EXL3
+ model.quantize_config.format = FORMAT.EXL3
+ model.qlinear_kernel = ExllamaV3Linear
+ super().finalize(model=model, **kwargs)
+
+ def verify_calibration_dataset(self, processor_index: int) -> bool:
+ del processor_index
+ if self.calibration_dataset is None:
+ raise ValueError("EXL3Processor's calibration_dataset must be provided.")
+ return True
+
+ def name(self) -> str:
+ return "exl3"
+
+
+__all__ = ["EXL3Processor", "clone_exllamav3_config_for_module"]
diff --git a/gptqmodel/looper/gptq_processor.py b/gptqmodel/looper/gptq_processor.py
index e9b342a54..b650ea5a8 100644
--- a/gptqmodel/looper/gptq_processor.py
+++ b/gptqmodel/looper/gptq_processor.py
@@ -18,8 +18,8 @@
from ..models.writer import (PROCESS_LOG_FWD_TIME, PROCESS_LOG_LAYER, PROCESS_LOG_MODULE, PROCESS_LOG_NAME,
PROCESS_LOG_TIME, PROCESS_USED_MEMORY, QUANT_LOG_DAMP, QUANT_LOG_LOSS, QUANT_LOG_NSAMPLES)
from ..quantization import GPTAQ, GPTQ
-from ..quantization.config import GPTAQConfig, HessianConfig, METHOD, QuantizeConfig
-from ..utils.failsafe import normalize_failsafe
+from ..quantization.config import GPTAQConfig, HessianConfig, METHOD, QuantizeConfig, resolve_quant_format
+from ..utils.fallback import normalize_fallback
from ..utils.logger import setup_logger, log_time_block
from ..utils.device import get_device
from ..utils.model import create_quant_module, find_modules, pack_module
@@ -28,6 +28,59 @@
log = setup_logger()
lock = threading.Lock()
+
+def clone_gptq_config_for_module(
+ qcfg: QuantizeConfig,
+ module_full_name: str,
+ *,
+ fallback=None,
+) -> Optional[QuantizeConfig]:
+ # entire module is skipped
+ if qcfg.dynamic_get(layer_name=module_full_name) == False:
+ return None
+
+ qcfg_clone = copy.deepcopy(qcfg)
+
+ # dynamic overrides
+ if qcfg.dynamic is not None:
+ qcfg_clone.bits = qcfg.dynamic_get(module_full_name, "bits", qcfg_clone.bits)
+ qcfg_clone.sym = qcfg.dynamic_get(module_full_name, "sym", qcfg_clone.sym)
+ qcfg_clone.mse = qcfg.dynamic_get(module_full_name, "mse", qcfg_clone.mse)
+
+ qcfg_clone.group_size = qcfg.dynamic_get(module_full_name, "group_size", qcfg_clone.group_size)
+ desc_act_override = qcfg.dynamic_get(module_full_name, "desc_act", None)
+ if desc_act_override is not None:
+ qcfg_clone.desc_act = desc_act_override
+ act_group_aware_override = qcfg.dynamic_get(module_full_name, "act_group_aware", None)
+ if act_group_aware_override is not None:
+ qcfg_clone.act_group_aware = act_group_aware_override
+ qcfg_clone.damp_percent = qcfg.dynamic_get(module_full_name, "damp_percent", qcfg_clone.damp_percent)
+ qcfg_clone.static_groups = qcfg.dynamic_get(module_full_name, "static_groups", qcfg_clone.static_groups)
+ fallback_override = qcfg.dynamic_get(module_full_name, "fallback", None)
+ if fallback_override is not None:
+ qcfg_clone.fallback = normalize_fallback(fallback_override, qcfg_clone.fallback)
+ hessian_override = qcfg.dynamic_get(module_full_name, "hessian", None)
+ if hessian_override is not None:
+ if isinstance(hessian_override, dict):
+ qcfg_clone.hessian = HessianConfig(**hessian_override)
+ elif isinstance(hessian_override, HessianConfig):
+ qcfg_clone.hessian = hessian_override
+ else:
+ raise ValueError("QuantizeConfig: dynamic `hessian` must be a HessianConfig or dict.")
+ gptaq_override = qcfg.dynamic_get(module_full_name, "gptaq", None)
+ if gptaq_override is not None:
+ if isinstance(gptaq_override, dict):
+ qcfg_clone.gptaq = GPTAQConfig(**gptaq_override)
+ elif isinstance(gptaq_override, GPTAQConfig):
+ qcfg_clone.gptaq = gptaq_override
+ else:
+ raise ValueError("QuantizeConfig: dynamic `gptaq` must be a GPTAQConfig or dict.")
+
+ qcfg_clone._resolve_activation_ordering(desc_act_override, act_group_aware_override)
+
+ qcfg_clone.fallback = normalize_fallback(fallback, qcfg_clone.fallback)
+ return qcfg_clone
+
class GPTQProcessor(LoopProcessor):
def __init__(
self,
@@ -63,50 +116,15 @@ def __init__(
def set_calibration_dataset(self, calibration_dataset):
raise NotImplementedError("GPTQProcessor's calibration_dataset cannot be modified")
- def preprocess(self, module: NamedModule, failsafe=None, **kwargs):
- # entire module is skipped
- if self.qcfg.dynamic_get(layer_name=module.full_name) == False:
+ def preprocess(self, module: NamedModule, fallback=None, **kwargs):
+ qcfg_clone = clone_gptq_config_for_module(
+ self.qcfg,
+ module.full_name,
+ fallback=fallback,
+ )
+ if qcfg_clone is None:
return
- qcfg_clone = copy.deepcopy(self.qcfg)
-
- # dynamic overrides
- if self.qcfg.dynamic is not None:
- qcfg_clone.bits = self.qcfg.dynamic_get(module.full_name, "bits", qcfg_clone.bits)
- qcfg_clone.sym = self.qcfg.dynamic_get(module.full_name, "sym", qcfg_clone.sym)
- qcfg_clone.mse = self.qcfg.dynamic_get(module.full_name, "mse", qcfg_clone.mse)
-
- qcfg_clone.group_size = self.qcfg.dynamic_get(module.full_name, "group_size", qcfg_clone.group_size)
- desc_act_override = self.qcfg.dynamic_get(module.full_name, "desc_act", None)
- if desc_act_override is not None:
- qcfg_clone.desc_act = desc_act_override
- act_group_aware_override = self.qcfg.dynamic_get(module.full_name, "act_group_aware", None)
- if act_group_aware_override is not None:
- qcfg_clone.act_group_aware = act_group_aware_override
- qcfg_clone.damp_percent = self.qcfg.dynamic_get(module.full_name, "damp_percent", qcfg_clone.damp_percent)
- qcfg_clone.static_groups = self.qcfg.dynamic_get(module.full_name, "static_groups", qcfg_clone.static_groups)
- failsafe_override = self.qcfg.dynamic_get(module.full_name, "failsafe", None)
- if failsafe_override is not None:
- qcfg_clone.failsafe = normalize_failsafe(failsafe_override, qcfg_clone.failsafe)
- hessian_override = self.qcfg.dynamic_get(module.full_name, "hessian", None)
- if hessian_override is not None:
- if isinstance(hessian_override, dict):
- qcfg_clone.hessian = HessianConfig(**hessian_override)
- elif isinstance(hessian_override, HessianConfig):
- qcfg_clone.hessian = hessian_override
- else:
- raise ValueError("QuantizeConfig: dynamic `hessian` must be a HessianConfig or dict.")
- gptaq_override = self.qcfg.dynamic_get(module.full_name, "gptaq", None)
- if gptaq_override is not None:
- if isinstance(gptaq_override, dict):
- qcfg_clone.gptaq = GPTAQConfig(**gptaq_override)
- elif isinstance(gptaq_override, GPTAQConfig):
- qcfg_clone.gptaq = gptaq_override
- else:
- raise ValueError("QuantizeConfig: dynamic `gptaq` must be a GPTAQConfig or dict.")
-
- qcfg_clone._resolve_activation_ordering(desc_act_override, act_group_aware_override)
-
# store last used qcfg_dynamic
self.qcfg_dynamic = qcfg_clone
@@ -114,7 +132,7 @@ def preprocess(self, module: NamedModule, failsafe=None, **kwargs):
tmp = GPTAQ(module=module, qcfg=qcfg_clone)
else:
tmp = GPTQ(module=module, qcfg=qcfg_clone)
- tmp.failsafe = normalize_failsafe(failsafe, qcfg_clone.failsafe)
+ tmp.fallback = qcfg_clone.fallback
tmp.expected_nsamples = getattr(self, "total_calibration_tokens", None)
tmp.quantizer.configure(
@@ -356,7 +374,7 @@ def submodule_finalize(self, module: NamedModule, model: BaseQModel, **kwargs):
create_quant_module(
name=module.full_name,
linear_cls=model.qlinear_kernel,
- bits=self.qcfg.bits,
+ bits=self.qcfg.runtime_bits,
desc_act=self.qcfg.desc_act,
dynamic=self.qcfg.dynamic,
group_size=self.qcfg.group_size,
@@ -366,6 +384,7 @@ def submodule_finalize(self, module: NamedModule, model: BaseQModel, **kwargs):
device=self.qcfg.device,
lm_head_name=model.lm_head,
pack_dtype=self.qcfg.pack_dtype,
+ format=resolve_quant_format(self.qcfg.format, self.qcfg.method),
register_buffers=False,
)
if timer is not None and create_start is not None:
@@ -419,7 +438,7 @@ def finalize(self, model: BaseQModel, **kwargs):
# set quantized state
model.quantized = True
- model.quantize_config.quant_method = METHOD.GPTQ
+ model.quantize_config.method = METHOD.GPTQ
super().finalize(model=model, **kwargs)
diff --git a/gptqmodel/looper/loop_processor.py b/gptqmodel/looper/loop_processor.py
index f7698d3fc..34f2f7a06 100644
--- a/gptqmodel/looper/loop_processor.py
+++ b/gptqmodel/looper/loop_processor.py
@@ -288,7 +288,7 @@ def _format_log_value(self, key: str, value: Any, stat: Dict[str, Any]) -> str:
try:
color_code = self.loss_color(float(text))
except (TypeError, ValueError):
- if cleaned.endswith("failsafe") or cleaned.startswith("failsafe("):
+ if cleaned.endswith("fallback") or cleaned.startswith("fallback("):
return color_text(text, ANSIColor.ORANGE)
return text
return color_text(text, color_code)
diff --git a/gptqmodel/looper/module_looper.py b/gptqmodel/looper/module_looper.py
index 0a28737cc..90e4bd602 100644
--- a/gptqmodel/looper/module_looper.py
+++ b/gptqmodel/looper/module_looper.py
@@ -1389,15 +1389,15 @@ def cache_inputs(self, layers, calibration_data, use_cache):
use_cache=use_cache,
)
- def loop(self, failsafe=None, **kwargs):
+ def loop(self, fallback=None, **kwargs):
with tf32_high_precision_guard():
with self.pause_controller.lifecycle():
- return self._loop_impl(failsafe=failsafe, **kwargs)
+ return self._loop_impl(fallback=fallback, **kwargs)
@torch.inference_mode()
- def _loop_impl(self, failsafe=None, **kwargs):
- if failsafe is None:
- failsafe = getattr(self.gptq_model.quantize_config, "failsafe", None)
+ def _loop_impl(self, fallback=None, **kwargs):
+ if fallback is None:
+ fallback = getattr(self.gptq_model.quantize_config, "fallback", None)
if self.gptq_model.quantize_config.lm_head:
if self.gptq_model.model.config.tie_word_embeddings and hasattr(self.gptq_model.model.model, "_tied_weights_keys"):
@@ -1428,7 +1428,7 @@ def _loop_impl(self, failsafe=None, **kwargs):
for p_index, processor in enumerate(self.processors):
if not processor.verify_calibration_dataset(p_index):
if isinstance(processor, EoraProcessor) or\
- (isinstance(processor, GPTQProcessor) and self.gptq_model.quantize_config.gptaq is not None):
+ (isinstance(processor, GPTQProcessor) and getattr(self.gptq_model.quantize_config, "gptaq", None) is not None):
prev_processor = self.processors[p_index - 1]
processor.set_calibration_dataset(prev_processor.calibration_dataset)
# If calibration_dataset is None or Empty, the input_cache of the previous processor is used.
@@ -1502,7 +1502,7 @@ def _loop_impl(self, failsafe=None, **kwargs):
layers=layers,
layer_modules=layer_modules,
layers_prefix=layers_prefix,
- failsafe=failsafe,
+ fallback=fallback,
shared_kv_cache_dict=shared_kv_cache_dict,
pb=pb,
layer_count=layer_count,
@@ -1586,7 +1586,7 @@ def _loop_impl(self, failsafe=None, **kwargs):
return total_log
- def create_named_modules(self, module, full, is_lm_head_module, layer_index, layers_prefix, names, processor, failsafe, layer_module=None) -> Dict[str, NamedModule]:
+ def create_named_modules(self, module, full, is_lm_head_module, layer_index, layers_prefix, names, processor, fallback, layer_module=None) -> Dict[str, NamedModule]:
subset = {}
capture_only_flags: Dict[str, bool] = {}
for n in names:
@@ -1628,7 +1628,7 @@ def create_named_modules(self, module, full, is_lm_head_module, layer_index, lay
subset[name].state["capture_only"] = True
if isinstance(processor, GPTQProcessor):
- processor.preprocess(subset[name], failsafe=failsafe)
+ processor.preprocess(subset[name], fallback=fallback)
else:
processor.preprocess(subset[name])
# some modules are skipped
diff --git a/gptqmodel/looper/qqq_processor.py b/gptqmodel/looper/qqq_processor.py
index 1a9e78735..d5a7b5803 100644
--- a/gptqmodel/looper/qqq_processor.py
+++ b/gptqmodel/looper/qqq_processor.py
@@ -15,8 +15,8 @@
from ..models.writer import (PROCESS_LOG_FWD_TIME, PROCESS_LOG_LAYER, PROCESS_LOG_MODULE, PROCESS_LOG_NAME,
PROCESS_LOG_TIME, QUANT_LOG_DAMP, QUANT_LOG_LOSS, QUANT_LOG_NSAMPLES)
from ..nn_modules.qlinear.qqq import QQQQuantLinear
-from ..quantization.config import METHOD, QuantizeConfig
-from ..utils.failsafe import normalize_failsafe
+from ..quantization.config import METHOD, QuantizeConfig, resolve_quant_format
+from ..utils.fallback import normalize_fallback
from ..quantization.qqq import QQQ
from ..utils.logger import setup_logger, log_time_block
from ..utils.model import create_quant_module, find_modules, move_to, pack_module
@@ -57,7 +57,7 @@ def __init__(
def set_calibration_dataset(self, calibration_dataset):
raise NotImplementedError("QQQProcessor's calibration_dataset cannot be modified")
- def preprocess(self, module: NamedModule, failsafe=None, **kwargs):
+ def preprocess(self, module: NamedModule, fallback=None, **kwargs):
# entire module is skipped
if self.qcfg.dynamic_get(layer_name=module.full_name) == False:
return
@@ -84,7 +84,7 @@ def preprocess(self, module: NamedModule, failsafe=None, **kwargs):
tmp = QQQ(module=module, qcfg=qcfg_clone)
- tmp.failsafe = normalize_failsafe(failsafe, qcfg_clone.failsafe)
+ tmp.fallback = normalize_fallback(fallback, qcfg_clone.fallback)
tmp.expected_nsamples = getattr(self, "total_calibration_tokens", None)
if self.qcfg.mse > 0.0:
@@ -245,7 +245,7 @@ def submodule_finalize(self, module: NamedModule, model: BaseQModel, **kwargs):
create_quant_module(
name=module.full_name,
linear_cls=QQQQuantLinear,
- bits=self.qcfg.bits,
+ bits=self.qcfg.runtime_bits,
desc_act=self.qcfg.desc_act,
dynamic=self.qcfg.dynamic,
group_size=self.qcfg.group_size,
@@ -255,6 +255,7 @@ def submodule_finalize(self, module: NamedModule, model: BaseQModel, **kwargs):
device=self.qcfg.device,
lm_head_name=model.lm_head,
pack_dtype=self.qcfg.pack_dtype,
+ format=resolve_quant_format(self.qcfg.format, self.qcfg.method),
register_buffers=False,
)
@@ -292,7 +293,7 @@ def finalize(self, model: BaseQModel, **kwargs):
# set quantized state
model.quantized = True
- model.quantize_config.quant_method = METHOD.QQQ
+ model.quantize_config.method = METHOD.QQQ
super().finalize(model=model, **kwargs)
diff --git a/gptqmodel/looper/stage_layer.py b/gptqmodel/looper/stage_layer.py
index 9e316c019..420f4b2fb 100644
--- a/gptqmodel/looper/stage_layer.py
+++ b/gptqmodel/looper/stage_layer.py
@@ -41,7 +41,7 @@ def run_layer_stage(
layers: List[torch.nn.Module],
layer_modules: List[List[str]],
layers_prefix: Optional[str],
- failsafe,
+ fallback,
shared_kv_cache_dict: Dict[int, torch.Tensor],
pb,
layer_count: int,
@@ -129,7 +129,7 @@ def run_layer_stage(
layers_prefix=layers_prefix,
names=names,
processor=processor,
- failsafe=failsafe,
+ fallback=fallback,
layer_module=module,
)
# Skip empty subsets caused by per-layer structure differences or dynamic config exclusions;
@@ -179,7 +179,7 @@ def run_layer_stage(
subset_index=index,
subset_total=subset_total,
full=full,
- failsafe=failsafe,
+ fallback=fallback,
shared_kv_cache_dict=shared_kv_cache_dict,
pb=pb,
log=log,
diff --git a/gptqmodel/looper/stage_subset.py b/gptqmodel/looper/stage_subset.py
index a7b56f9ad..cc57fe580 100644
--- a/gptqmodel/looper/stage_subset.py
+++ b/gptqmodel/looper/stage_subset.py
@@ -64,7 +64,7 @@ def _run_single_subset_pass(
subset_index: int,
subset_total: int,
full,
- failsafe,
+ fallback,
shared_kv_cache_dict: Dict[int, torch.Tensor],
pb,
logger,
@@ -256,21 +256,21 @@ def _run_single_subset_pass(
torch_sync()
torch_empty_cache()
moe_skip_modules = []
- failsafe_enabled = failsafe is not None
+ fallback_enabled = fallback is not None
if isinstance(processor, GPTQProcessor) or isinstance(processor, QQQProcessor) or isinstance(processor, AWQProcessor):
for name in subset:
# Skip MoE experts that never fired; they likely lacked calibration
# traffic and would produce invalid statistics.
if not processor.has_captured_input_ids(name):
- # only log for moe if `failsafe` is not enabled
- if not failsafe_enabled:
+ # only log for moe if `fallback` is not enabled
+ if not fallback_enabled:
logger.error(
f"`{name}` was not invoked, if it is a MoE module, it may lack sufficient calibration data routed to it. "
- f"Please enable and use `failsafe` config option."
+ f"Please enable and use `fallback` config option."
)
moe_skip_modules.append(name)
- if not failsafe_enabled:
+ if not fallback_enabled:
for name in moe_skip_modules:
skipped_module = subset.pop(name)
task_map = getattr(processor, "tasks", None)
@@ -428,7 +428,7 @@ def run_subset_stage(
subset_index: int,
subset_total: int,
full,
- failsafe: bool,
+ fallback: bool,
shared_kv_cache_dict: Dict[int, torch.Tensor],
pb,
log=None,
@@ -632,7 +632,7 @@ def emit_subset_event(stage: str) -> None:
subset_index=subset_index,
subset_total=subset_total,
full=full,
- failsafe=failsafe,
+ fallback=fallback,
shared_kv_cache_dict=shared_kv_cache_dict,
pb=pb,
logger=logger,
@@ -678,7 +678,7 @@ def emit_subset_event(stage: str) -> None:
subset_index=subset_index,
subset_total=subset_total,
full=full,
- failsafe=failsafe,
+ fallback=fallback,
shared_kv_cache_dict=shared_kv_cache_dict,
pb=pb,
logger=logger,
@@ -716,7 +716,7 @@ def emit_subset_event(stage: str) -> None:
subset_index=subset_index,
subset_total=subset_total,
full=full,
- failsafe=failsafe,
+ fallback=fallback,
shared_kv_cache_dict=shared_kv_cache_dict,
pb=pb,
logger=logger,
diff --git a/gptqmodel/looper/weight_only_looper.py b/gptqmodel/looper/weight_only_looper.py
new file mode 100644
index 000000000..8d5991198
--- /dev/null
+++ b/gptqmodel/looper/weight_only_looper.py
@@ -0,0 +1,235 @@
+# SPDX-FileCopyrightText: 2024-2025 ModelCloud.ai
+# SPDX-FileCopyrightText: 2024-2025 qubitium@modelcloud.ai
+# SPDX-License-Identifier: Apache-2.0
+# Contact: qubitium@modelcloud.ai, x.com/qubitium
+
+"""Weight-only quantization loop for methods that do not capture activations.
+
+This looper intentionally does not share the activation-capture lifecycle used
+by GPTQ/AWQ calibration flows. Weight-only methods such as RTN, FP8, NVFP4, or
+GGUF can usually process each linear layer directly, so the control flow here
+stays narrow: iterate quantizable modules, quantize weights, finalize, and
+optionally offload.
+"""
+
+from __future__ import annotations
+
+from typing import Dict, Optional
+
+import torch
+from defuser.modeling.replace_modules import materialize_model
+
+from ..looper.weight_only_processor import WeightOnlyProcessor
+from ..looper.named_module import NamedModule
+from ..models import BaseQModel
+from ..models._const import CPU, SUPPORTS_MODULE_TYPES
+from ..nn_modules.converter import MODULE_CONVERTER_MAP
+from ..quantization.config import FP8Config, GGUFQuantizeConfig, RTNQuantizeConfig
+from ..utils.logger import setup_logger
+from ..utils.model import find_modules, get_module, get_module_by_name_prefix, move_to
+from ..utils.offload import offload_to_disk
+
+
+log = setup_logger()
+
+
+class WeightOnlyLooper:
+ """Run the simplified per-layer lifecycle for weight-only quantization."""
+
+ def __init__(self, model: BaseQModel, processor: WeightOnlyProcessor):
+ self.gptq_model = model
+ self.processor = processor
+
+ def _resolve_named_module(
+ self,
+ *,
+ layer_module: torch.nn.Module,
+ full: Dict[str, torch.nn.Module],
+ layer_index: int,
+ layers_prefix: Optional[str],
+ module_name: str,
+ is_lm_head_module: bool,
+ ) -> Optional[NamedModule]:
+ """Resolve a quantizable submodule and normalize it into a NamedModule."""
+ resolved = full.get(module_name)
+ if resolved is None:
+ resolved, _ = get_module_by_name_prefix(layer_module, module_name)
+ if resolved is None:
+ if self.gptq_model.layer_modules_strict:
+ raise ValueError(f"layer module item `{module_name}` not found in model, please check your model config.")
+ return None
+
+ if isinstance(resolved, NamedModule):
+ return resolved
+
+ layer_name = self.gptq_model.lm_head if is_lm_head_module else f"{layers_prefix}.{layer_index}.{module_name}"
+ named = NamedModule(
+ resolved,
+ name=module_name,
+ full_name=layer_name,
+ layer_index=layer_index,
+ )
+ full[module_name] = named
+ return named
+
+ def _offload_quantized_module(self, module: NamedModule) -> None:
+ """Persist an already-quantized module to disk when offload is enabled."""
+ quant_config = getattr(self.gptq_model, "quantize_config", None)
+ if not quant_config or not getattr(quant_config, "offload_to_disk", False):
+ return
+ offload_path = getattr(quant_config, "offload_to_disk_path", None)
+ if not offload_path:
+ return
+
+ module_full_name = getattr(module, "full_name", None)
+ target_module = (
+ self.gptq_model.model.get_submodule(module_full_name)
+ if module_full_name
+ else module
+ )
+ offload_to_disk(
+ model=self.gptq_model.model,
+ module=target_module,
+ disk_path=offload_path,
+ )
+
+ def loop(self, **kwargs):
+ """Quantize layers directly from weights without calibration forwards."""
+ quant_config = self.gptq_model.quantize_config
+ if not isinstance(quant_config, (RTNQuantizeConfig, GGUFQuantizeConfig, FP8Config)):
+ raise NotImplementedError(
+ "Weight-only looper only supports `RTNQuantizeConfig`, `GGUFQuantizeConfig`, and `FP8Config` today."
+ )
+
+ if quant_config.lm_head:
+ if self.gptq_model.model.config.tie_word_embeddings and hasattr(self.gptq_model.model.model, "_tied_weights_keys"):
+ tied_keys = self.gptq_model.model._tied_weights_keys
+ for item in tied_keys:
+ if self.gptq_model.lm_head in item:
+ raise NotImplementedError(
+ "quantization of `lm_head` layer with `tied_weights=True` model state is not supported. Please check model has `tied_weights=False`."
+ )
+
+ lm_head_module = get_module(self.gptq_model.model, key=self.gptq_model.lm_head)
+ if lm_head_module is None:
+ raise ValueError(f"could not find layer {self.gptq_model.lm_head} in the model, exit...")
+ if not isinstance(lm_head_module, tuple(SUPPORTS_MODULE_TYPES)):
+ raise NotImplementedError(
+ f"This type({type(lm_head_module)}) of lm_head quantization is currently not supported. SUPPORTS_MODULE_TYPES is {SUPPORTS_MODULE_TYPES}"
+ )
+
+ forward_pass_use_cache = (
+ self.gptq_model.model.config.use_cache
+ if hasattr(self.gptq_model.model.config, "use_cache")
+ else False
+ )
+ # No calibration forwards are executed here, but disabling cache keeps
+ # behavior aligned with the standard quantization path and avoids stale
+ # decoder-cache state while layers are being replaced.
+ self.gptq_model.model.config.use_cache = False
+
+ layers, layers_prefix = get_module_by_name_prefix(
+ self.gptq_model.model,
+ self.gptq_model.extract_layers_node(),
+ )
+
+ if quant_config.offload_to_disk:
+ log.info("Offloading base modules to disk...")
+ offload_to_disk(
+ model=self.gptq_model.model,
+ module=self.gptq_model.get_base_modules(model=self.gptq_model.model),
+ disk_path=quant_config.offload_to_disk_path,
+ )
+
+ layer_modules = self.gptq_model.simple_layer_modules(
+ model_config=self.gptq_model.model.config,
+ quantize_config=quant_config,
+ is_awq_quantize=False,
+ include_capture_only=False,
+ )
+ if not quant_config.true_sequential:
+ layer_modules = [sum(layer_modules, [])]
+
+ layer_count = len(layers)
+ total_layers = layer_count + (1 if quant_config.lm_head else 0)
+
+ try:
+ for layer_index in range(total_layers):
+ is_lm_head_module = layer_index >= layer_count
+
+ # Transformer blocks and lm_head follow the same weight-only
+ # lifecycle, but lm_head is resolved from the root model.
+ if is_lm_head_module:
+ module = get_module(self.gptq_model.model, key=self.gptq_model.lm_head)
+ subsets = [[self.gptq_model.lm_head]]
+ else:
+ module = layers[layer_index]
+ subsets = layer_modules
+
+ module = self.gptq_model.pre_quantize(module)
+ if not is_lm_head_module:
+ # Preserve existing module conversion behavior so the new
+ # lifecycle stays compatible with model-specific wrappers.
+ model_type = self.gptq_model.model.config.model_type
+ if model_type in MODULE_CONVERTER_MAP:
+ converter = MODULE_CONVERTER_MAP[model_type]
+ module = converter(module, self.gptq_model.model.config)
+ layers[layer_index] = module
+
+ # Resolve concrete submodules after any pre-quantization
+ # transforms so quantization targets the final layer layout.
+ materialize_model(module)
+ full = find_modules(module, name=self.gptq_model.lm_head if is_lm_head_module else "")
+
+ self.processor.collect_memory_info(layer_index)
+ for subset_names in subsets:
+ for module_name in subset_names:
+ named = self._resolve_named_module(
+ layer_module=module,
+ full=full,
+ layer_index=layer_index,
+ layers_prefix=layers_prefix,
+ module_name=module_name,
+ is_lm_head_module=is_lm_head_module,
+ )
+ if named is None:
+ continue
+
+ # Weight-only quantization happens entirely within the
+ # processor; no captured activations are needed.
+ active_qcfg = self.processor.quantize_module(named)
+ if active_qcfg is None:
+ continue
+
+ # Finalization and optional disk offload expect the
+ # packed module to be back on CPU memory.
+ move_to(named.module, device=CPU)
+ named.target_device = CPU
+ named.module.target_device = CPU
+
+ self.processor.submodule_finalize(
+ named,
+ self.gptq_model,
+ qcfg=active_qcfg,
+ )
+ self._offload_quantized_module(named)
+
+ # Submodule-level offload may swap packed tensors to meta/disk placeholders.
+ # Skip the layer-wide CPU move in that case to avoid `.to()` on meta buffers.
+ if getattr(self.gptq_model.quantize_config, "offload_to_disk", False):
+ if not is_lm_head_module:
+ layers[layer_index] = module
+ elif is_lm_head_module:
+ self.gptq_model.post_quantize(module)
+ else:
+ layers[layer_index] = self.gptq_model.post_quantize(module)
+ finally:
+ self.gptq_model.model.config.use_cache = forward_pass_use_cache
+
+ total_log = {self.processor.name(): self.processor.log}
+ self.gptq_model.quant_log = self.processor.log
+ self.processor.finalize(model=self.gptq_model)
+ return total_log
+
+
+__all__ = ["WeightOnlyLooper"]
diff --git a/gptqmodel/looper/weight_only_processor.py b/gptqmodel/looper/weight_only_processor.py
new file mode 100644
index 000000000..2190f723f
--- /dev/null
+++ b/gptqmodel/looper/weight_only_processor.py
@@ -0,0 +1,263 @@
+# SPDX-FileCopyrightText: 2026 ModelCloud.ai
+# SPDX-License-Identifier: Apache-2.0
+
+from __future__ import annotations
+
+import math
+import threading
+import time
+from typing import Optional
+
+import torch
+
+from ..looper.loop_processor import DTYPE_SIZE_COLUMN, MODULE_FEATURE_COLUMN, LoopProcessor
+from ..looper.named_module import NamedModule
+from ..models import BaseQModel
+from ..models._const import CPU
+from ..models.writer import (
+ PROCESS_LOG_FWD_TIME,
+ PROCESS_LOG_LAYER,
+ PROCESS_LOG_MODULE,
+ PROCESS_LOG_NAME,
+ PROCESS_LOG_TIME,
+ PROCESS_USED_MEMORY,
+ QUANT_LOG_DAMP,
+ QUANT_LOG_LOSS,
+ QUANT_LOG_NSAMPLES,
+)
+from ..quantization.config import (
+ BaseQuantizeConfig,
+ FP8Config,
+ GGUFQuantizeConfig,
+ METHOD,
+ RTNQuantizeConfig,
+ clone_weight_only_config_for_module,
+ resolve_quant_format,
+)
+from ..quantization.rtn import RTN, get_number_of_rows_and_cols
+from ..utils.logger import log_time_block, setup_logger
+from ..utils.model import create_quant_module, find_modules, pack_module
+from ..utils.module_locks import parent_module_lock
+
+
+log = setup_logger()
+
+
+class WeightOnlyProcessor(LoopProcessor):
+ """Process weight-only modules without entering activation-based quantization flows."""
+
+ _TP_TARGETS = (2, 4, 8)
+
+ def __init__(
+ self,
+ tokenizer,
+ qcfg: RTNQuantizeConfig | GGUFQuantizeConfig | FP8Config,
+ ):
+ super().__init__(
+ tokenizer=tokenizer,
+ qcfg=qcfg,
+ calibration=None,
+ prepare_dataset_func=None,
+ calibration_concat_size=None,
+ calibration_sort=None,
+ calibration_concat_separator=None,
+ batch_size=1,
+ require_fwd=False,
+ fwd_after_process=False,
+ )
+ self.lock = threading.Lock()
+
+ @staticmethod
+ def _uses_direct_pack(qcfg: RTNQuantizeConfig | GGUFQuantizeConfig | FP8Config) -> bool:
+ return qcfg.method in {METHOD.GGUF, METHOD.FP8}
+
+ def _update_logged_loss(self, module: NamedModule, avg_loss: str) -> None:
+ with self.lock:
+ for entry in reversed(self.log):
+ if entry.get(PROCESS_LOG_LAYER) == module.layer_index and entry.get(PROCESS_LOG_MODULE) == module.name:
+ entry[QUANT_LOG_LOSS] = avg_loss
+ return
+
+ def _annotate_tp_padding(self, module: NamedModule, qcfg: BaseQuantizeConfig) -> None:
+ target_multiple = math.lcm(*self._TP_TARGETS)
+ if qcfg.group_size > 0:
+ target_multiple = math.lcm(target_multiple, qcfg.group_size)
+
+ _, columns = get_number_of_rows_and_cols(module)
+ pad_cols = (target_multiple - (columns % target_multiple)) % target_multiple
+ if pad_cols == 0:
+ module.state.pop("tp_pad_info", None)
+ return
+
+ module.state["tp_pad_info"] = {
+ "pad_cols": pad_cols,
+ "target_multiple": target_multiple,
+ "original_columns": columns,
+ }
+
+ def quantize_module(self, module: NamedModule) -> Optional[RTNQuantizeConfig | GGUFQuantizeConfig | FP8Config]:
+ qcfg_clone = clone_weight_only_config_for_module(self.qcfg, module.full_name)
+ if qcfg_clone is None:
+ return None
+
+ if self._uses_direct_pack(qcfg_clone):
+ start_time = time.time()
+ duration = time.time() - start_time
+ avg_loss = f"{qcfg_clone.method.value}: pending"
+ damp_percent = 0.0
+ nsamples = 0
+ else:
+ self._annotate_tp_padding(module, qcfg_clone)
+
+ task = RTN(module=module, qcfg=qcfg_clone)
+ wq, q_scales, q_zeros, q_g_idx, duration, avg_loss, damp_percent, nsamples = task.quantize()
+
+ module.stream_state_payload_to_cpu(
+ {
+ "q_scales": q_scales,
+ "q_zeros": q_zeros,
+ "q_g_idx": q_g_idx,
+ },
+ )
+ del q_scales, q_zeros, q_g_idx
+
+ stat = {
+ PROCESS_LOG_NAME: self.name(),
+ PROCESS_LOG_LAYER: module.layer_index,
+ PROCESS_LOG_MODULE: module.name,
+ MODULE_FEATURE_COLUMN: self.module_feature_summary(module),
+ DTYPE_SIZE_COLUMN: self.module_dtype_size_summary(module),
+ QUANT_LOG_LOSS: avg_loss if isinstance(avg_loss, str) else f"{avg_loss:.10f}",
+ QUANT_LOG_NSAMPLES: f"{nsamples}",
+ QUANT_LOG_DAMP: f"{damp_percent:.5f}",
+ PROCESS_LOG_TIME: f"{duration:.3f}",
+ PROCESS_LOG_FWD_TIME: self.formatted_fwd_time(),
+ PROCESS_USED_MEMORY: self.device_memory_report(),
+ "lifecycle": "weight_only",
+ }
+
+ with self.lock:
+ self.log.append(stat)
+ self.log_new_row(stat)
+
+ if not self._uses_direct_pack(qcfg_clone):
+ module.weight.data = wq
+ return qcfg_clone
+
+ def submodule_finalize(
+ self,
+ module: NamedModule,
+ model: BaseQModel,
+ *,
+ qcfg: Optional[RTNQuantizeConfig | GGUFQuantizeConfig | FP8Config] = None,
+ **kwargs,
+ ):
+ active_qcfg = qcfg or self.qcfg
+ if not self._uses_direct_pack(active_qcfg):
+ module.stream_sync()
+ with self.lock:
+ q_zeros = module.state.pop("q_zeros").clone()
+ q_scales = module.state.pop("q_scales").clone()
+ q_g_idx = module.state.pop("q_g_idx").clone()
+
+ assert q_zeros.device == CPU
+ assert q_scales.device == CPU
+ assert q_g_idx.device == CPU
+
+ layers = find_modules(model.model)
+ module_label = getattr(module, "full_name", getattr(module, "name", ""))
+ parent_key = getattr(module, "full_name", getattr(module, "name", None))
+ original_layer = layers.get(module.full_name)
+ timer = getattr(model, "quant_region_timer", None)
+
+ create_start = time.perf_counter() if timer is not None else None
+ with log_time_block("create_quant_module", logger=log, module_name=module_label):
+ with parent_module_lock(parent_key):
+ create_quant_module(
+ name=module.full_name,
+ linear_cls=model.qlinear_kernel,
+ bits=active_qcfg.runtime_bits,
+ desc_act=active_qcfg.desc_act,
+ dynamic=active_qcfg.dynamic,
+ group_size=active_qcfg.group_size,
+ module=model.model,
+ submodule=module,
+ sym=active_qcfg.sym,
+ device=active_qcfg.device,
+ lm_head_name=model.lm_head,
+ pack_dtype=active_qcfg.pack_dtype,
+ format=resolve_quant_format(active_qcfg.format, active_qcfg.method),
+ register_buffers=False,
+ init_kwargs=active_qcfg.quant_linear_init_kwargs(),
+ )
+ if timer is not None and create_start is not None:
+ timer.record("submodule_finalize_create", time.perf_counter() - create_start, source=module_label)
+
+ qmodules = {
+ name: submodule
+ for name, submodule in find_modules(model.model, [model.qlinear_kernel]).items()
+ if name == module.full_name
+ }
+
+ if self._uses_direct_pack(active_qcfg):
+ pack_start = time.perf_counter() if timer is not None else None
+ with log_time_block("module.pack_original", logger=log, module_name=module_label):
+ with parent_module_lock(parent_key):
+ qmodule = qmodules[module.full_name]
+ qmodule.pack_original(
+ linear=original_layer,
+ scales=None,
+ zeros=None,
+ g_idx=None,
+ smooth=active_qcfg.smooth,
+ )
+ if timer is not None and pack_start is not None:
+ timer.record(
+ "submodule_finalize_pack",
+ time.perf_counter() - pack_start,
+ source=f"{module_label} [module.pack_original]",
+ )
+
+ reference_weight = qmodule._weight_to_matrix(original_layer).detach().cpu().to(torch.float32)
+ dequant_weight = qmodule.dequantize_weight().T.detach().cpu().to(torch.float32)
+ mean_abs_err = (dequant_weight - reference_weight).abs().mean().item()
+ self._update_logged_loss(module, f"{active_qcfg.method.value}: {mean_abs_err:.7f}")
+ module.unregister_parameter("weight")
+ return
+
+ pack_start = time.perf_counter() if timer is not None else None
+ with log_time_block("pack", logger=log, module_name=module_label):
+ with parent_module_lock(parent_key):
+ packer_label = pack_module(
+ name=module.full_name,
+ qModules=qmodules,
+ q_scales=q_scales,
+ q_zeros=q_zeros,
+ q_g_idx=q_g_idx,
+ layers=layers,
+ quant_linear_cls=model.qlinear_kernel,
+ lock=self.lock,
+ quantize_config=active_qcfg,
+ )
+ if timer is not None and pack_start is not None:
+ timer.record(
+ "submodule_finalize_pack",
+ time.perf_counter() - pack_start,
+ source=f"{module_label} [{packer_label or 'module.pack_original'}]",
+ )
+
+ del q_scales, q_zeros, q_g_idx
+ module.unregister_parameter("weight")
+
+ def finalize(self, model: BaseQModel, **kwargs):
+ model.quantized = True
+ super().finalize(model=model, **kwargs)
+
+ def name(self) -> str:
+ if self.qcfg.method == METHOD.GGUF:
+ return "weight_only_gguf"
+ if self.qcfg.method == METHOD.FP8:
+ return "weight_only_fp8"
+ return "weight_only_rtn"
+
+__all__ = ["WeightOnlyProcessor"]
diff --git a/gptqmodel/models/auto.py b/gptqmodel/models/auto.py
index b53183490..1687a4a58 100644
--- a/gptqmodel/models/auto.py
+++ b/gptqmodel/models/auto.py
@@ -6,6 +6,7 @@
from __future__ import annotations
import os
+from contextlib import contextmanager
from ..utils.logger import setup_logger
@@ -287,22 +288,57 @@ def _is_supported_quantization_config(config: AutoConfig) -> bool:
quant_format = quantization_config.get("quant_format")
if isinstance(quant_format, str) and quant_format.lower() in (
METHOD.GPTQ,
+ METHOD.GGUF,
+ METHOD.FP8,
METHOD.AWQ,
METHOD.QQQ,
+ METHOD.EXL3,
):
return True
- quant_method = quantization_config.get("quant_method")
- if isinstance(quant_method, str) and quant_method.lower() in (
+ method = quantization_config.get("method", quantization_config.get("quant_method"))
+ if isinstance(method, str) and method.lower() in (
METHOD.GPTQ,
+ METHOD.GGUF,
+ METHOD.FP8,
METHOD.AWQ,
METHOD.QQQ,
+ METHOD.EXL3,
):
return True
return False
+@contextmanager
+def _hide_unsupported_quantization_config_for_lm_eval(model):
+ config = getattr(model, "config", None)
+ if config is None:
+ yield
+ return
+
+ quantization_config = getattr(config, "quantization_config", None)
+ if not isinstance(quantization_config, dict):
+ yield
+ return
+
+ try:
+ from transformers.quantizers import AutoQuantizationConfig
+
+ AutoQuantizationConfig.from_dict(dict(quantization_config))
+ except Exception:
+ pass
+ else:
+ yield
+ return
+
+ setattr(config, "quantization_config", None)
+ try:
+ yield
+ finally:
+ setattr(config, "quantization_config", quantization_config)
+
+
def check_and_get_model_definition(model_dir, trust_remote_code=False):
config = AutoConfig.from_pretrained(model_dir, trust_remote_code=trust_remote_code)
model_type = config.model_type.lower()
@@ -499,11 +535,10 @@ def eval(
if isinstance(model_or_id_or_path, str):
load_backend = backend
- if llm_backend == "vllm":
- disallowed_keys = {"pretrained", "tokenizer", "gptqmodel", "trust_remote_code", "backend", "model_id_or_path"}
- load_kwargs = {k: v for k, v in model_args.items() if k not in disallowed_keys}
- else:
- load_kwargs = model_args
+ # These keys are consumed by eval wrappers and should never leak
+ # into GPTQModel.load when callers reuse a shared model_args dict.
+ disallowed_keys = {"pretrained", "tokenizer", "gptqmodel", "trust_remote_code", "backend", "model_id_or_path"}
+ load_kwargs = {k: v for k, v in model_args.items() if k not in disallowed_keys}
backend_name = load_backend.value if isinstance(load_backend, BACKEND) else str(load_backend)
log.info(f"Eval: loading using backend = `{backend_name}`")
@@ -567,11 +602,12 @@ def eval(
raise ValueError(f"lm_eval import failed: {e}. Please install via `pip install gptqmodel[eval]`.") from e
if llm_backend == "gptqmodel" and model is not None:
- model_name = HFLM(
- pretrained=model,
- batch_size=batch_size,
- trust_remote_code=trust_remote_code,
- )
+ with _hide_unsupported_quantization_config_for_lm_eval(model):
+ model_name = HFLM(
+ pretrained=model,
+ batch_size=batch_size,
+ trust_remote_code=trust_remote_code,
+ )
gen_kwargs = args.pop("gen_kwargs", None)
@@ -689,8 +725,11 @@ def export(model_id_or_path: str, target_path: str, format: str, trust_remote_co
gptq_config = config.quantization_config
+ method = gptq_config.get("method", gptq_config.get("quant_method", ""))
+ backend = BACKEND.GGUF_TORCH if str(method).lower() == METHOD.GGUF.value else BACKEND.TORCH
+
# load gptq model
- gptq_model = GPTQModel.load(model_id_or_path, backend=BACKEND.TORCH)
+ gptq_model = GPTQModel.load(model_id_or_path, backend=backend)
if format == "mlx":
try:
diff --git a/gptqmodel/models/base.py b/gptqmodel/models/base.py
index ebb8e28a6..78518c5ac 100644
--- a/gptqmodel/models/base.py
+++ b/gptqmodel/models/base.py
@@ -38,12 +38,23 @@
from .. import DEVICE_THREAD_POOL
from ..adapter.adapter import Adapter
+from ..nn_modules.exllamav3 import ExllamaV3Linear
from ..nn_modules.qlinear import BaseQuantLinear
from ..nn_modules.qlinear.lookahead import configure_default_lookahead
from ..nn_modules.qlinear.torch import TorchQuantLinear
from ..quantization import QuantizeConfig
-from ..quantization.config import FORMAT, METHOD, QUANTIZE_BLACK_LIST, GcMode, VramStrategy, dynamic_get
+from ..quantization.config import (
+ BaseQuantizeConfig,
+ FORMAT,
+ METHOD,
+ QUANTIZE_BLACK_LIST,
+ GcMode,
+ VramStrategy,
+ dynamic_get,
+ resolve_quant_format,
+)
from ..quantization.rotation.rotation import fuse_layer_norms, rotate_model
+from ..utils.attn_mask import normalize_seq_mask
from ..utils.backend import BACKEND
from ..utils.calibration import prepare_calibration_dataset
from ..utils.device import get_device
@@ -226,7 +237,7 @@ def __init__(
self,
model: PreTrainedModel,
quantized: bool,
- quantize_config: Optional[QuantizeConfig],
+ quantize_config: Optional[BaseQuantizeConfig],
tokenizer: Optional[PreTrainedTokenizerBase] = None,
qlinear_kernel: nn.Module = None,
load_quantized_model: bool = False,
@@ -239,7 +250,7 @@ def __init__(
super().__init__()
if quantize_config:
- quant_method = quantize_config.quant_method
+ quant_method = quantize_config.method
# override module_tree if need
if self.module_tree_overrides is not None and self.module_tree_overrides.get(quant_method) is not None:
log.info(f'Module Tree: overridden by METHOD.{quant_method.upper()}')
@@ -621,7 +632,7 @@ def prepare_dataset(
def quantize(
self,
- calibration: Union[List[Dict[str, Union[List[int], torch.LongTensor]]], List[str], List[int]],
+ calibration: Optional[Union[List[Dict[str, Union[List[int], torch.LongTensor]]], List[str], List[int]]] = None,
# Setting a fixed calibration_dataset_concat_size may improve the performance of the quantized model.
calibration_concat_size: Optional[int] = None,
calibration_sort: Optional[str] = "desc", # valid values are asc, desc, shuffle
@@ -635,7 +646,7 @@ def quantize(
calibration_data_min_length: int = 10,
calibration_concat_separator: Optional[str] = None,
) -> Dict[str, List[Dict[str, str]]]:
- if self.quantize_config is None or not isinstance(self.quantize_config, QuantizeConfig):
+ if self.quantize_config is None or not isinstance(self.quantize_config, BaseQuantizeConfig):
raise AttributeError("`quantize_config` must be not None")
if self.quantized:
@@ -648,22 +659,26 @@ def quantize(
self._turtle_reload_accum_bytes = 0
self._turtle_materialized_ids = set()
- if self.quantize_config.quant_method in QUANTIZE_BLACK_LIST:
+ if self.quantize_config.method in QUANTIZE_BLACK_LIST:
raise ValueError(
- f"Unsupported quantization operation for quant method: {self.quantize_config.quant_method}"
+ f"Unsupported quantization operation for quant method: {self.quantize_config.method}"
)
if not self.support_batch_quantize:
log.warn("Quantize: batch_size overridden by model class definition to `disabled`")
batch_size = 1 # but actually disabled
- if self.quantize_config.format == FORMAT.MARLIN:
+ format_code = resolve_quant_format(self.quantize_config.format, self.quantize_config.method)
+
+ if format_code == FORMAT.MARLIN:
raise ValueError(
"FORMAT.MARLIN is deprecated for quantization. Please switch to FORMAT.GPTQ. GPTQMOdel will auto-use Marlin kernel for accelerated inference for FORMAT.GPTQ."
)
- if self.quantize_config.quant_method == METHOD.AWQ:
- if self.quantize_config.format in [FORMAT.GEMV_FAST, FORMAT.LLM_AWQ]:
+ export_quant_method = self.quantize_config.export_quant_method()
+
+ if export_quant_method == METHOD.AWQ:
+ if format_code in [FORMAT.GEMV_FAST, FORMAT.LLM_AWQ]:
# AWQ GEMV_FAST / LLM_AWQ only supports pack_dtype is torch.int16
log.info("Quantize Model: Auto fix `pack_dtype` to `torch.int16`")
self.quantize_config.pack_dtype = torch.int16
@@ -678,35 +693,58 @@ def quantize(
preferred_backend = requested_backend
if preferred_backend in (None, BACKEND.AUTO):
- if self.quantize_config.quant_method == METHOD.AWQ:
- if self.quantize_config.format == FORMAT.GEMM:
+ if export_quant_method == METHOD.AWQ:
+ if format_code == FORMAT.GEMM:
preferred_backend = BACKEND.GEMM
- elif self.quantize_config.format == FORMAT.GEMV:
+ elif format_code == FORMAT.GEMV:
preferred_backend = BACKEND.GEMV
- elif self.quantize_config.format in [FORMAT.GEMV_FAST, FORMAT.LLM_AWQ]:
+ elif format_code in [FORMAT.GEMV_FAST, FORMAT.LLM_AWQ]:
preferred_backend = BACKEND.GEMV_FAST
else:
raise ValueError(f"Unsupported FORMAT: `{self.quantize_config.format}` with `METHOD.AWQ`")
- elif self.quantize_config.quant_method == METHOD.QQQ:
+ elif self.quantize_config.method == METHOD.QQQ:
preferred_backend = BACKEND.QQQ
+ elif self.quantize_config.method == METHOD.EXL3:
+ preferred_backend = BACKEND.EXLLAMA_V3
+ elif self.quantize_config.method == METHOD.GGUF:
+ preferred_backend = BACKEND.AUTO
+ elif self.quantize_config.method == METHOD.FP8:
+ preferred_backend = BACKEND.TORCH
else:
preferred_backend = BACKEND.TORCH
- # Validate quant linear before quantization starts
- _ = select_quant_linear(
- bits=self.quantize_config.bits,
- dynamic=self.quantize_config.dynamic,
- group_size=self.quantize_config.group_size,
- desc_act=self.quantize_config.desc_act,
- sym=self.quantize_config.sym,
- backend=preferred_backend,
- format=self.quantize_config.format,
- quant_method=self.quantize_config.quant_method,
- device=DEVICE(self.quantize_config.device),
- pack=True,
- pack_dtype=self.quantize_config.pack_dtype,
+ if self.quantize_config.method == METHOD.EXL3:
+ if preferred_backend not in (BACKEND.AUTO, BACKEND.EXLLAMA_V3):
+ raise ValueError("EXL3 quantization only supports BACKEND.AUTO or BACKEND.EXLLAMA_V3.")
- )
+ if not torch.cuda.is_available():
+ raise ValueError("EXL3 quantization requires CUDA/HIP.")
+
+ quant_device = self.quantize_config.device
+ if isinstance(quant_device, DEVICE):
+ quant_device_type = quant_device.type
+ elif isinstance(quant_device, torch.device):
+ quant_device_type = quant_device.type
+ else:
+ quant_device_type = str(quant_device).split(":")[0].lower()
+
+ if quant_device_type != "cuda":
+ raise ValueError("EXL3 quantization requires a CUDA/HIP quantization device.")
+ else:
+ # Validate quant linear before quantization starts
+ _ = select_quant_linear(
+ bits=self.quantize_config.runtime_bits,
+ dynamic=self.quantize_config.dynamic,
+ group_size=self.quantize_config.group_size,
+ desc_act=self.quantize_config.desc_act,
+ sym=self.quantize_config.sym,
+ backend=preferred_backend,
+ format=format_code,
+ quant_method=export_quant_method,
+ device=DEVICE(self.quantize_config.device),
+ pack=True,
+ pack_dtype=self.quantize_config.pack_dtype,
+ )
# Use the provided tokenizer if one is passed to quantize()
if tokenizer is not None:
@@ -717,7 +755,7 @@ def quantize(
raise ValueError(
f"Unsupported `tokenizer` type: Expected `PreTrainedTokenizerBase`, actual = `{type(tokenizer)}`.")
- if self.quantize_config.format == FORMAT.BITBLAS:
+ if format_code == FORMAT.BITBLAS:
from ..nn_modules.qlinear.bitblas import BITBLAS_AVAILABLE, BITBLAS_INSTALL_HINT
if BITBLAS_AVAILABLE is False:
raise ValueError(BITBLAS_INSTALL_HINT)
@@ -726,39 +764,23 @@ def quantize(
if adapter is not None:
self.quantize_config.adapter = adapter
- from ..adapter.adapter import Lora
- from ..looper.eora_processor import EoraProcessor
- from ..looper.module_looper import ModuleLooper
-
- # has lora process
- needs_lora = isinstance(self.quantize_config.adapter, Lora)
-
- args = {
- "tokenizer": self.tokenizer,
- "qcfg": self.quantize_config,
- "calibration": calibration,
- "prepare_dataset_func": self.prepare_dataset,
- "calibration_concat_size": calibration_concat_size,
- "calibration_sort": calibration_sort,
- "calibration_concat_separator": calibration_concat_separator,
- "batch_size": batch_size,
- "calculate_w_wq_diff": needs_lora, # lora needs original w - wq delta
- }
-
- self.qlinear_kernel = select_quant_linear(
- bits=self.quantize_config.bits,
- group_size=self.quantize_config.group_size,
- desc_act=self.quantize_config.desc_act,
- sym=self.quantize_config.sym,
- pack=True,
- dynamic=self.quantize_config.dynamic,
- device=self.quantize_config.device,
- pack_dtype=self.quantize_config.pack_dtype,
- multi_select=False,
- backend=preferred_backend,
- format=self.quantize_config.format,
- quant_method=self.quantize_config.quant_method,
- )
+ if self.quantize_config.method == METHOD.EXL3:
+ self.qlinear_kernel = ExllamaV3Linear
+ else:
+ self.qlinear_kernel = select_quant_linear(
+ bits=self.quantize_config.runtime_bits,
+ group_size=self.quantize_config.group_size,
+ desc_act=self.quantize_config.desc_act,
+ sym=self.quantize_config.sym,
+ pack=True,
+ dynamic=self.quantize_config.dynamic,
+ device=DEVICE(self.quantize_config.device),
+ pack_dtype=self.quantize_config.pack_dtype,
+ multi_select=False,
+ backend=preferred_backend,
+ format=format_code,
+ quant_method=export_quant_method,
+ )
# rotate model
if self.quantize_config.rotation:
@@ -787,24 +809,101 @@ def quantize(
self.model, _ = rotate_model(model=self.model, rotate_mode=self.quantize_config.rotation,
device=rotation_device, **module_name_args)
- # init processor with default GPTQ processor
+ if self.quantize_config.uses_weight_only_lifecycle():
+ result = self._quantize_weight_only(
+ calibration=calibration,
+ calibration_concat_size=calibration_concat_size,
+ calibration_sort=calibration_sort,
+ batch_size=batch_size,
+ backend=backend,
+ calibration_concat_separator=calibration_concat_separator,
+ )
+ else:
+ if calibration is None:
+ raise ValueError(
+ "Calibration dataset is required unless a weight-only quantize config is configured."
+ )
+ result = self._quantize_with_calibration(
+ calibration=calibration,
+ calibration_concat_size=calibration_concat_size,
+ calibration_sort=calibration_sort,
+ batch_size=batch_size,
+ backend=backend,
+ adapter_calibration_dataset=adapter_calibration_dataset,
+ calibration_concat_separator=calibration_concat_separator,
+ )
+
+ timer = getattr(self, "quant_region_timer", None)
+ if timer is not None:
+ timer.flush()
+
+ return result
+
+ def _quantize_with_calibration(
+ self,
+ *,
+ calibration,
+ calibration_concat_size: Optional[int],
+ calibration_sort: Optional[str],
+ batch_size: int,
+ backend: Optional[BACKEND],
+ adapter_calibration_dataset,
+ calibration_concat_separator: Optional[str],
+ ):
+ from ..adapter.adapter import Lora
+ from ..looper.eora_processor import EoraProcessor
+ from ..looper.module_looper import ModuleLooper
from ..looper.tensorparallel_weight_processor import TensorParallelWeightProcessor
- if self.quantize_config.quant_method == METHOD.QQQ:
+ needs_lora = isinstance(self.quantize_config.adapter, Lora)
+
+ args = {
+ "tokenizer": self.tokenizer,
+ "qcfg": self.quantize_config,
+ "calibration": calibration,
+ "prepare_dataset_func": self.prepare_dataset,
+ "calibration_concat_size": calibration_concat_size,
+ "calibration_sort": calibration_sort,
+ "calibration_concat_separator": calibration_concat_separator,
+ "batch_size": batch_size,
+ "calculate_w_wq_diff": needs_lora,
+ }
+
+ if self.quantize_config.method == METHOD.EXL3:
+ from ..looper.exllamav3_processor import EXL3Processor
+
+ if needs_lora:
+ raise NotImplementedError("EXL3 quantization does not support adapter/EoRA generation.")
+
+ if getattr(self.quantize_config, "gptaq", None) is not None:
+ raise NotImplementedError("EXL3 quantization does not support GPTAQ/native activation capture.")
+
+ exl3_args = {
+ "tokenizer": self.tokenizer,
+ "qcfg": self.quantize_config,
+ "calibration": calibration,
+ "prepare_dataset_func": self.prepare_dataset,
+ "calibration_concat_size": calibration_concat_size,
+ "calibration_sort": calibration_sort,
+ "calibration_concat_separator": calibration_concat_separator,
+ "batch_size": batch_size,
+ "lm_head_name": self.lm_head,
+ }
+ quantize_processor = [
+ EXL3Processor(**exl3_args),
+ ]
+ elif self.quantize_config.method == METHOD.QQQ:
from ..looper.qqq_processor import QQQProcessor
quantize_processor = [
TensorParallelWeightProcessor(**args),
QQQProcessor(**args),
]
- elif self.quantize_config.quant_method == METHOD.AWQ:
+ elif self.quantize_config.method == METHOD.AWQ:
from ..looper.awq_processor import AWQProcessor
os.environ["AWQ_BATCH_SIZE"] = str(batch_size)
- # if self.model.config.model_type not in AWQ_CAUSAL_LM_MODEL_MAP.keys():
- # raise TypeError(f"{self.model.config.model_type} isn't supported yet.")
-
awq_args = dict(args)
awq_args["gptq_model"] = self
awq_args["model"] = self.model
@@ -822,11 +921,9 @@ def quantize(
GPTQProcessor(**args),
]
- if self.quantize_config.gptaq is not None:
+ if getattr(self.quantize_config, "gptaq", None) is not None:
from ..looper.native_processor import NativeProcessor
- # During the deepcopy process, self.prepare_dataset will be deeply copied along with self. However,
- # self has a threading.RLock() , which is not serializable.
args_to_copy = {k: v for k, v in args.items() if k != "prepare_dataset_func"}
args_clone = copy.deepcopy(args_to_copy)
args_clone["prepare_dataset_func"] = args["prepare_dataset_func"]
@@ -835,7 +932,6 @@ def quantize(
quantize_processor.insert(0, NativeProcessor(**args_clone))
processors = quantize_processor
- # Append EoRA processor for lora adapter
if needs_lora:
processors.append(
EoraProcessor(
@@ -850,11 +946,8 @@ def quantize(
)
)
- # prepare processor worker (looper)
module_looper = ModuleLooper(self, processors=processors)
- # When gc_mode=ON_STAGE_END, disable auto-gc for the whole quantization process
- # to prevent interference with manual cleanups performed at stage ends
gc_context = (
DEVICE_THREAD_POOL.no_auto_gc()
if self.quantize_config.gc_mode == GcMode.ON_STAGE_END
@@ -862,16 +955,54 @@ def quantize(
)
with gc_context:
- result = module_looper.loop(
+ return module_looper.loop(
backend=backend,
- failsafe=self.quantize_config.failsafe,
+ fallback=self.quantize_config.fallback,
)
- timer = getattr(self, "quant_region_timer", None)
- if timer is not None:
- timer.flush()
+ def _quantize_weight_only(
+ self,
+ *,
+ calibration,
+ calibration_concat_size: Optional[int],
+ calibration_sort: Optional[str],
+ batch_size: int,
+ backend: Optional[BACKEND],
+ calibration_concat_separator: Optional[str],
+ ):
+ del calibration_concat_size, calibration_sort, batch_size, calibration_concat_separator
- return result
+ from ..adapter.adapter import Lora
+ from ..looper.weight_only_looper import WeightOnlyLooper
+ from ..looper.weight_only_processor import WeightOnlyProcessor
+
+ if calibration is not None:
+ log.info("Weight-only quantization selected; ignoring provided calibration dataset.")
+
+ if isinstance(self.quantize_config.adapter, Lora):
+ raise NotImplementedError(
+ "Weight-only quantization does not support adapter/EoRA generation."
+ )
+
+ if getattr(self.quantize_config, "gptaq", None) is not None:
+ raise NotImplementedError(
+ "Weight-only quantization does not support GPTAQ/native activation capture."
+ )
+
+ processor = WeightOnlyProcessor(
+ tokenizer=self.tokenizer,
+ qcfg=self.quantize_config,
+ )
+ module_looper = WeightOnlyLooper(model=self, processor=processor)
+
+ gc_context = (
+ DEVICE_THREAD_POOL.no_auto_gc()
+ if self.quantize_config.gc_mode == GcMode.ON_STAGE_END
+ else nullcontext()
+ )
+
+ with gc_context:
+ return module_looper.loop(backend=backend)
def _eora_generate(
self,
@@ -959,12 +1090,42 @@ def generate(self, inputs=None, **kwargs):
if pad_token_id is None and self.tokenizer:
kwargs["pad_token_id"] = self.tokenizer.pad_token_id
+ def _normalize_generate_attention_mask(input_ids, attention_mask):
+ if not torch.is_tensor(attention_mask) or attention_mask.ndim <= 2:
+ return attention_mask
+
+ seq_len = None
+ if torch.is_tensor(input_ids) and input_ids.ndim >= 2:
+ seq_len = input_ids.shape[-1]
+
+ return normalize_seq_mask(attention_mask, seq_len=seq_len)
+
if isinstance(inputs, str) or (isinstance(inputs, list) and all(isinstance(x, str) for x in inputs)):
if self.tokenizer is None:
raise ValueError("You passed in an `input` to `generate()` of type `str` but model is missing `model.tokenizer`. Please set `model.tokenizer = my_tokenizer`.")
- inputs = self.tokenizer(inputs, return_tensors="pt", padding=True, padding_side="left").to(self.model.device)
+ inputs = self.tokenizer(inputs, return_tensors="pt", padding=True, padding_side="left")
+ if "attention_mask" in inputs:
+ inputs["attention_mask"] = _normalize_generate_attention_mask(
+ inputs.get("input_ids"),
+ inputs["attention_mask"],
+ )
+ inputs = inputs.to(self.model.device)
return self.model.generate(**inputs, **kwargs)
+ if hasattr(inputs, "get") and not torch.is_tensor(inputs):
+ if "attention_mask" in inputs:
+ inputs["attention_mask"] = _normalize_generate_attention_mask(
+ inputs.get("input_ids"),
+ inputs["attention_mask"],
+ )
+ return self.model.generate(**inputs, **kwargs)
+
+ if "attention_mask" in kwargs:
+ kwargs["attention_mask"] = _normalize_generate_attention_mask(
+ kwargs.get("input_ids", inputs),
+ kwargs["attention_mask"],
+ )
+
return self.model.generate(inputs=inputs, **kwargs)
def prepare_inputs_for_generation(self, *args, **kwargs):
@@ -1856,8 +2017,8 @@ def __getattr__(self, item):
def _auto_detect_module_tree(self, model: PreTrainedModel, quant_method: METHOD):
log.warn("Model not yet support, attempting Module Tree AutoCompat...")
- if quant_method != METHOD.GPTQ:
- log.warn(f"Module Tree AutoCompat: Failed, quant_method={quant_method}, only support GPTQ")
+ if quant_method not in {METHOD.GPTQ, METHOD.GGUF, METHOD.FP8, METHOD.EXL3}:
+ log.warn(f"Module Tree AutoCompat: Failed, quant_method={quant_method}, only support GPTQ/GGUF/FP8/EXL3")
return None
def _get(path):
diff --git a/gptqmodel/models/definitions/ernie4_5.py b/gptqmodel/models/definitions/ernie4_5.py
index ed161d7e6..11a93ea0c 100644
--- a/gptqmodel/models/definitions/ernie4_5.py
+++ b/gptqmodel/models/definitions/ernie4_5.py
@@ -75,6 +75,34 @@ def ernie4_5_model_forward(
output_hidden_states=None,
return_dict=False,
):
+ def _coerce_legacy_past_key_values(cache_like):
+ if cache_like is None:
+ return tuple([None] * len(self.layers))
+
+ if isinstance(cache_like, tuple):
+ return cache_like
+
+ if hasattr(cache_like, "__iter__"):
+ legacy = []
+ for layer_cache in cache_like:
+ if layer_cache is None:
+ legacy.append(None)
+ continue
+ if isinstance(layer_cache, (tuple, list)) and len(layer_cache) >= 2:
+ if layer_cache[0] is None or layer_cache[1] is None:
+ legacy.append(None)
+ else:
+ legacy.append((layer_cache[0], layer_cache[1]))
+ continue
+ legacy.append(None)
+
+ if len(legacy) < len(self.layers):
+ legacy.extend([None] * (len(self.layers) - len(legacy)))
+
+ return tuple(legacy)
+
+ return cache_like
+
use_cache = use_cache if use_cache is not None else self.config.use_cache
# retrieve input_ids and inputs_embeds
@@ -91,8 +119,7 @@ def ernie4_5_model_forward(
"You have to specify either decoder_input_ids or decoder_inputs_embeds"
)
- if past_key_values is None:
- past_key_values = tuple([None] * len(self.layers))
+ past_key_values = _coerce_legacy_past_key_values(past_key_values)
if inputs_embeds is None:
inputs_embeds = self.embed_tokens(input_ids)
@@ -167,9 +194,8 @@ def ernie4_5_model_forward(
attentions=all_self_attns,
)
- if not self.load_quantized_model:
- ernie4_5_model = type(self.model.model)
- ernie4_5_model.forward = ernie4_5_model_forward
+ ernie4_5_model = type(self.model.model)
+ ernie4_5_model.forward = ernie4_5_model_forward
- ernie4_5_layer = type(self.model.model.layers[0])
- ernie4_5_layer.forward = ernie4_5_decode_layer_forward
+ ernie4_5_layer = type(self.model.model.layers[0])
+ ernie4_5_layer.forward = ernie4_5_decode_layer_forward
diff --git a/gptqmodel/models/definitions/granitemoehybrid.py b/gptqmodel/models/definitions/granitemoehybrid.py
index 94440c4b4..a5f080b8f 100644
--- a/gptqmodel/models/definitions/granitemoehybrid.py
+++ b/gptqmodel/models/definitions/granitemoehybrid.py
@@ -8,6 +8,7 @@
class GraniteMoeHybridQModel(BaseQModel):
pre_lm_head_norm_module = "model.norm"
+ require_monkeypatch = True
layer_modules_strict = False
@@ -23,3 +24,40 @@ class GraniteMoeHybridQModel(BaseQModel):
"post_attention_layernorm": ("post_attention_layernorm:!",),
}
]
+
+ def monkey_patch(self):
+ from gptqmodel.nn_modules.qlinear import BaseQuantLinear
+
+ mamba_layer_cls = type(self.model.model.layers[0].mamba)
+ original_forward = mamba_layer_cls.forward
+
+ def granitemoehybrid_mamba_forward(
+ layer_self,
+ hidden_states,
+ cache_params=None,
+ cache_position=None,
+ attention_mask=None,
+ seq_idx=None,
+ **kwargs,
+ ):
+ if isinstance(layer_self.in_proj, BaseQuantLinear) or isinstance(layer_self.out_proj, BaseQuantLinear):
+ if seq_idx is not None:
+ raise NotImplementedError(
+ "`seq_idx` support requires fast path support. Please install `mamba_ssm` and `causal_conv1d`"
+ )
+ dtype = hidden_states.dtype
+ if attention_mask is not None and attention_mask.shape[1] > 1 and attention_mask.shape[0] > 1:
+ hidden_states = (hidden_states * attention_mask[:, :, None]).to(dtype)
+ return layer_self.torch_forward(hidden_states, cache_params, cache_position, attention_mask)
+
+ return original_forward(
+ layer_self,
+ hidden_states,
+ cache_params=cache_params,
+ cache_position=cache_position,
+ attention_mask=attention_mask,
+ seq_idx=seq_idx,
+ **kwargs,
+ )
+
+ mamba_layer_cls.forward = granitemoehybrid_mamba_forward
diff --git a/gptqmodel/models/loader.py b/gptqmodel/models/loader.py
index 18e8c4c88..54b569eee 100644
--- a/gptqmodel/models/loader.py
+++ b/gptqmodel/models/loader.py
@@ -30,10 +30,13 @@
from transformers.utils.generic import ContextManagers
from ..adapter.adapter import Adapter
+from ..nn_modules.exllamav3 import ExllamaV3Linear
+from ..nn_modules.exllamav3_torch import ExllamaV3TorchLinear
from ..nn_modules.qlinear.exllamav2 import ExllamaV2QuantLinear
from ..quantization import QuantizeConfig
-from ..quantization.config import FORMAT, METHOD, MIN_VERSION_WITH_V2
+from ..quantization.config import BaseQuantizeConfig, FORMAT, METHOD, MIN_VERSION_WITH_V2, resolve_quant_format
from ..utils.backend import BACKEND
+from ..utils.exllamav3 import replace_exllamav3_placeholders
from ..utils.hf import no_init_weights
from ..utils.importer import auto_select_device, normalize_device_device_map, select_quant_linear
from ..utils.inspect import safe_kwargs_call
@@ -125,7 +128,7 @@ def ModelLoader(cls):
def from_pretrained(
cls,
pretrained_model_id_or_path: str,
- quantize_config: QuantizeConfig,
+ quantize_config: BaseQuantizeConfig,
trust_remote_code: bool = False,
dtype: [str | torch.dtype] = "auto",
device_map: Optional[Union[str, Dict[str, Union[int, str]]]] = None,
@@ -189,8 +192,8 @@ def from_pretrained(
# non-quantized models are always loaded into cpu
cpu_device_map = {"": "cpu"}
- if quantize_config is None or not isinstance(quantize_config, QuantizeConfig):
- raise AttributeError("`quantize_config` must be passed and be an instance of QuantizeConfig.")
+ if quantize_config is None or not isinstance(quantize_config, BaseQuantizeConfig):
+ raise AttributeError("`quantize_config` must be passed and be an instance of BaseQuantizeConfig.")
quantize_config.calculate_bits_per_weight()
@@ -396,8 +399,24 @@ def from_quantized(
config.torch_dtype = dtype
qcfg = QuantizeConfig.from_pretrained(model_local_path, **cached_file_kwargs, **kwargs)
-
- if qcfg.quant_method == METHOD.AWQ and qcfg.format in [FORMAT.GEMV_FAST, FORMAT.LLM_AWQ]:
+ export_quant_method = qcfg.export_quant_method()
+ format_code = resolve_quant_format(qcfg.format, qcfg.method)
+
+ if format_code == FORMAT.EXL3:
+ if backend not in (BACKEND.AUTO, BACKEND.EXLLAMA_V3, BACKEND.TORCH):
+ raise TypeError("FORMAT.EXL3 requires BACKEND.AUTO, BACKEND.EXLLAMA_V3, or BACKEND.TORCH.")
+ if backend == BACKEND.AUTO:
+ if torch.cuda.is_available() and device in (DEVICE.CUDA, DEVICE.ROCM):
+ backend = BACKEND.EXLLAMA_V3
+ else:
+ backend = BACKEND.TORCH
+ if backend == BACKEND.EXLLAMA_V3:
+ if not torch.cuda.is_available():
+ raise ValueError("EXL3 CUDA loading requires CUDA/HIP.")
+ if device not in (DEVICE.CUDA, DEVICE.ROCM):
+ raise ValueError("EXL3 CUDA loading requires a CUDA/HIP device.")
+
+ if export_quant_method == METHOD.AWQ and format_code in [FORMAT.GEMV_FAST, FORMAT.LLM_AWQ]:
# GEMV_FAST and LLM_AWQ only supports torch.float16
log.info("Loading Quantized Model: Auto fix `dtype` to `torch.float16`")
dtype = torch.float16
@@ -417,10 +436,10 @@ def from_quantized(
if backend == BACKEND.VLLM or backend == BACKEND.SGLANG:
if backend == BACKEND.VLLM:
- if qcfg.format != FORMAT.GPTQ and qcfg.format != FORMAT.GEMM:
+ if format_code not in [FORMAT.GPTQ, FORMAT.GEMM]:
raise ValueError(f"{backend} backend only supports FORMAT.GPTQ or FORMAT.GEMM: actual = {qcfg.format}")
elif backend == BACKEND.SGLANG:
- if qcfg.format != FORMAT.GPTQ:
+ if format_code != FORMAT.GPTQ:
raise ValueError(f"{backend} backend only supports FORMAT.GPTQ: actual = {qcfg.format}")
if backend == BACKEND.VLLM:
@@ -459,7 +478,7 @@ def from_quantized(
model_local_path=model_local_path,
)
- if qcfg.format == FORMAT.MARLIN:
+ if format_code == FORMAT.MARLIN:
# format marlin requires marlin kernel
if backend not in [BACKEND.MARLIN, BACKEND.MARLIN_FP16] and backend != BACKEND.AUTO:
raise TypeError(f"FORMAT.MARLIN requires BACKEND.AUTO or BACKEND.MARLIN: actual = `{backend}`.")
@@ -474,7 +493,7 @@ def from_quantized(
# "Hint: Model is compatible with the Marlin kernel. Marlin is optimized for batched inference on Nvidia GPU: `model = GPTQModel.load(..., backend=BACKEND.MARLIN)`."
# )
- if qcfg.format == FORMAT.BITBLAS:
+ if format_code == FORMAT.BITBLAS:
# format bitblas requires bitblas kernel
if backend != BACKEND.BITBLAS and backend != BACKEND.AUTO:
raise TypeError(f"FORMAT.BITBLAS requires BACKEND.AUTO or BACKEND.BITBLAS: actual = `{backend}`.")
@@ -485,10 +504,13 @@ def from_quantized(
if BITBLAS_AVAILABLE is False:
raise ValueError(BITBLAS_INSTALL_HINT)
- possible_model_basenames = [
- f"gptq_model-{qcfg.bits}bit-{qcfg.group_size}g",
- "model",
- ]
+ if format_code == FORMAT.EXL3:
+ possible_model_basenames = ["model"]
+ else:
+ possible_model_basenames = [
+ f"gptq_model-{qcfg.bits}bit-{qcfg.group_size}g",
+ "model",
+ ]
extensions = [".safetensors"]
@@ -508,7 +530,7 @@ def from_quantized(
"Loading of .bin files are not allowed due to safety. Please convert your model to safetensor or pytorch format."
)
- qcfg.runtime_format = qcfg.format
+ qcfg.runtime_format = format_code
model_save_name = resolved_archive_file # In case a model is sharded, this would be `model.safetensors.index.json` which may later break.
@@ -576,14 +598,27 @@ def skip(*args, **kwargs):
log.info(f"The layer {name} is not quantized.")
del modules[name]
- preload_qlinear_kernel = make_quant(
- model,
- qcfg=qcfg,
- quant_result=modules,
- backend=backend,
- lm_head_name=cls.lm_head,
- device=device,
- )
+ if format_code == FORMAT.EXL3:
+ if not isinstance(qcfg.tensor_storage, dict) or not qcfg.tensor_storage:
+ raise ValueError("EXL3 checkpoints require `quantization_config.tensor_storage` metadata.")
+
+ exl3_module_cls = ExllamaV3TorchLinear if backend == BACKEND.TORCH else ExllamaV3Linear
+ replace_exllamav3_placeholders(
+ model=model,
+ module_names=list(qcfg.tensor_storage.keys()),
+ tensor_storage=qcfg.tensor_storage,
+ module_cls=exl3_module_cls,
+ )
+ preload_qlinear_kernel = exl3_module_cls
+ else:
+ preload_qlinear_kernel = make_quant(
+ model,
+ qcfg=qcfg,
+ quant_result=modules,
+ backend=backend,
+ lm_head_name=cls.lm_head,
+ device=device,
+ )
if isinstance(device_map, str) and device_map not in [
"auto",
@@ -779,18 +814,23 @@ def assign(mod, device_id):
return device_map
log.info(f"Loader: device = {device}")
- layers, _ = get_module_by_name_prefix(model, extract_layers_node)
- num_gpus = 1
- if device is DEVICE.CUDA:
- num_gpus = torch.cuda.device_count()
- elif device is DEVICE.XPU:
- num_gpus = torch.xpu.device_count()
- device_map = build_layerwise_device_map(model, device, layers, ignore_modules, num_gpus)
- log.info(f"Loader: device_map = {device_map}")
+ explicit_device_map = device_map if isinstance(device_map, dict) else None
+ if explicit_device_map is not None:
+ device_map = {str(key): str(value) for key, value in explicit_device_map.items()}
+ log.info(f"Loader: honoring explicit device_map = {device_map}")
+ else:
+ layers, _ = get_module_by_name_prefix(model, extract_layers_node)
+ num_gpus = 1
+ if device is DEVICE.CUDA:
+ num_gpus = torch.cuda.device_count()
+ elif device is DEVICE.XPU:
+ num_gpus = torch.xpu.device_count()
+ device_map = build_layerwise_device_map(model, device, layers, ignore_modules, num_gpus)
+ log.info(f"Loader: device_map = {device_map}")
load_checkpoint_in_model = True
# compat: runtime convert checkpoint gptq(v1) to gptq_v2 format
- if qcfg.format in [FORMAT.GPTQ, FORMAT.GEMM]:
+ if format_code in [FORMAT.GPTQ, FORMAT.GEMM]:
load_checkpoint_in_model_then_tie_weights(
model,
dtype=dtype,
@@ -803,7 +843,7 @@ def assign(mod, device_id):
load_checkpoint_in_model = False
- if qcfg.format == FORMAT.GPTQ:
+ if format_code == FORMAT.GPTQ:
# validate sym=False v1 loading needs to be protected for models produced with new v2 format codebase
if not qcfg.sym and not qcfg.is_quantized_by_gptaq():
raise ValueError(
@@ -830,7 +870,7 @@ def assign(mod, device_id):
)
if backend in [BACKEND.MARLIN, BACKEND.MARLIN_FP16] and (
- preload_qlinear_kernel == ExllamaV2QuantLinear or qcfg.format == FORMAT.MARLIN):
+ preload_qlinear_kernel == ExllamaV2QuantLinear or format_code == FORMAT.MARLIN):
if is_sharded:
raise ValueError(
"Format: The loading of sharded checkpoints with Marlin is currently not supported."
@@ -878,18 +918,21 @@ def assign(mod, device_id):
# TODO: Why are we using this custom function and not dispatch_model?
model = simple_dispatch_model(model, device_map)
- qlinear_kernel = select_quant_linear(
- bits=qcfg.bits,
- dynamic=qcfg.dynamic,
- group_size=qcfg.group_size,
- desc_act=qcfg.desc_act,
- sym=qcfg.sym,
- backend=backend,
- format=qcfg.format,
- quant_method=qcfg.quant_method,
- device=device,
- pack_dtype=qcfg.pack_dtype,
- )
+ if format_code == FORMAT.EXL3:
+ qlinear_kernel = ExllamaV3TorchLinear if backend == BACKEND.TORCH else ExllamaV3Linear
+ else:
+ qlinear_kernel = select_quant_linear(
+ bits=qcfg.runtime_bits,
+ dynamic=qcfg.dynamic,
+ group_size=qcfg.group_size,
+ desc_act=qcfg.desc_act,
+ sym=qcfg.sym,
+ backend=backend,
+ format=format_code,
+ quant_method=export_quant_method,
+ device=device,
+ pack_dtype=qcfg.pack_dtype,
+ )
# == step4: set seqlen == #
model_config = model.config.to_dict()
@@ -901,8 +944,9 @@ def assign(mod, device_id):
log.warn("can't get model's sequence length from model config, will set to 4096.")
model.seqlen = 4096
- # Any post-initialization that require device information, for example buffers initialization on device.
- model = gptqmodel_post_init(model, use_act_order=qcfg.desc_act, quantize_config=qcfg)
+ if format_code != FORMAT.EXL3:
+ # Any post-initialization that require device information, for example buffers initialization on device.
+ model = gptqmodel_post_init(model, use_act_order=qcfg.desc_act, quantize_config=qcfg)
model.eval()
diff --git a/gptqmodel/models/writer.py b/gptqmodel/models/writer.py
index 0767fcc2e..679b7c73d 100644
--- a/gptqmodel/models/writer.py
+++ b/gptqmodel/models/writer.py
@@ -38,8 +38,10 @@
META_QUANTIZER_GPTQMODEL,
META_VALUE_URI,
MIN_VERSION_WITH_V2,
+ resolve_quant_format,
)
from ..utils.backend import BACKEND
+from ..utils.exllamav3 import build_exllamav3_tensor_storage
from ..utils.hf import no_init_weights, sanitize_generation_config_file
from ..utils.logger import setup_logger
from ..utils.model import (
@@ -171,19 +173,21 @@ def save_quantized(
)
# meta: write config fields to meta if they doe not participate in inference
+ gptaq_cfg = getattr(self.quantize_config, "gptaq", None)
+
self.quantize_config.meta_set(
key=META_FIELD_DAMP_PERCENT,
- value=self.quantize_config.damp_percent
+ value=getattr(self.quantize_config, "damp_percent", None)
)
self.quantize_config.meta_set(
key=META_FIELD_DAMP_AUTO_INCREMENT,
- value=self.quantize_config.damp_auto_increment
+ value=getattr(self.quantize_config, "damp_auto_increment", None)
)
self.quantize_config.meta_set(
key=META_FIELD_STATIC_GROUPS,
- value=self.quantize_config.static_groups
+ value=getattr(self.quantize_config, "static_groups", None)
)
self.quantize_config.meta_set(
@@ -193,24 +197,24 @@ def save_quantized(
self.quantize_config.meta_set(
key=META_FIELD_MSE,
- value=self.quantize_config.mse
+ value=getattr(self.quantize_config, "mse", None)
)
self.quantize_config.meta_set(
key=META_FIELD_GPTAQ_ENABLED,
- value=None if self.quantize_config.gptaq is None else {
- "alpha": self.quantize_config.gptaq.alpha,
+ value=None if gptaq_cfg is None else {
+ "alpha": gptaq_cfg.alpha,
"device": (
- self.quantize_config.gptaq.device
- if isinstance(self.quantize_config.gptaq.device, str)
- else str(self.quantize_config.gptaq.device)
+ gptaq_cfg.device
+ if isinstance(gptaq_cfg.device, str)
+ else str(gptaq_cfg.device)
),
}
)
self.quantize_config.meta_set(
key=META_FIELD_ACT_GROUP_AWARE,
- value=self.quantize_config.act_group_aware
+ value=getattr(self.quantize_config, "act_group_aware", None)
)
# The config, quantize_config and model may be edited in place in save_quantized.
@@ -220,12 +224,19 @@ def save_quantized(
if not self.quantized:
raise ValueError("Save aborted as model is not quantized. Please call `quantize()` first.")
- if quantize_config.format == FORMAT.GPTQ_V2:
+ runtime_format = resolve_quant_format(quantize_config.format, quantize_config.method)
+
+ if runtime_format == FORMAT.GPTQ_V2:
log.warn(
f"Using 'format = {FORMAT.GPTQ_V2}': the serialized model is only supported by GPTQModel version >= {MIN_VERSION_WITH_V2}."
)
- if self.load_quantized_model:
+ if runtime_format == FORMAT.EXL3:
+ tensor_storage = build_exllamav3_tensor_storage(self.model)
+ quantize_config.tensor_storage = tensor_storage
+ self.quantize_config.tensor_storage = copy.deepcopy(tensor_storage)
+
+ if self.load_quantized_model and runtime_format != FORMAT.EXL3:
self.model = self.get_model_with_quantize(
qcfg=quantize_config,
model_id_or_path=self.model_local_path,
diff --git a/gptqmodel/nn_modules/exllamav3.py b/gptqmodel/nn_modules/exllamav3.py
new file mode 100644
index 000000000..3d72b0982
--- /dev/null
+++ b/gptqmodel/nn_modules/exllamav3.py
@@ -0,0 +1,198 @@
+# SPDX-FileCopyrightText: 2024-2025 ModelCloud.ai
+# SPDX-FileCopyrightText: 2024-2025 qubitium@modelcloud.ai
+# SPDX-License-Identifier: Apache-2.0
+# Contact: qubitium@modelcloud.ai, x.com/qubitium
+#
+# Portions of this file are adapted from turboderp-org/exllamav3.
+# Credits: TurboDerp / ExLlamaV3 contributors.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Dict, Optional
+
+import torch
+import torch.nn as nn
+
+if TYPE_CHECKING:
+ from ..exllamav3.modules.quant.exl3 import LinearEXL3
+
+
+_EXL3_BUFFER_NAMES = ("trellis", "suh", "svh", "su", "sv", "bias", "mcg", "mul1")
+
+
+def _torch_dtype(value: Any) -> torch.dtype:
+ if isinstance(value, torch.dtype):
+ return value
+ if isinstance(value, str):
+ return getattr(torch, value)
+ raise TypeError(f"Unsupported torch dtype value: {value!r}")
+
+
+class ExllamaV3Linear(nn.Module):
+ QUANT_TYPE = "exl3"
+ SUPPORTS_SHARDS = True
+
+ def __init__(
+ self,
+ *,
+ in_features: int,
+ out_features: int,
+ name: str,
+ tensor_storage: Optional[Dict[str, Any]] = None,
+ tensors: Optional[Dict[str, torch.Tensor]] = None,
+ out_dtype: torch.dtype = torch.float16,
+ ):
+ super().__init__()
+ self.in_features = in_features
+ self.out_features = out_features
+ self.name = name
+ self.out_dtype = out_dtype
+ self.tensor_storage = tensor_storage or {}
+
+ self.weight = torch.zeros((1,), dtype=torch.float16, device="meta")
+ self._inner: Optional["LinearEXL3"] = None
+ self._inner_signature: Optional[tuple[Any, ...]] = None
+
+ if tensors is not None:
+ for buffer_name in _EXL3_BUFFER_NAMES:
+ tensor = tensors.get(buffer_name)
+ if tensor is None:
+ setattr(self, buffer_name, None)
+ else:
+ self.register_buffer(buffer_name, tensor)
+ return
+
+ stored_tensors = (self.tensor_storage or {}).get("stored_tensors", {})
+ for buffer_name in _EXL3_BUFFER_NAMES:
+ metadata = stored_tensors.get(f"{name}.{buffer_name}")
+ if metadata is None:
+ setattr(self, buffer_name, None)
+ continue
+
+ shape = tuple(metadata["shape"])
+ dtype = _torch_dtype(metadata["torch_dtype"])
+ self.register_buffer(buffer_name, torch.empty(shape, dtype=dtype, device="meta"))
+
+ @classmethod
+ def from_tensors(
+ cls,
+ *,
+ in_features: int,
+ out_features: int,
+ name: str,
+ tensors: Dict[str, torch.Tensor],
+ ) -> "ExllamaV3Linear":
+ return cls(
+ in_features=in_features,
+ out_features=out_features,
+ name=name,
+ tensors=tensors,
+ )
+
+ def _current_signature(self) -> tuple[Any, ...]:
+ trellis = getattr(self, "trellis", None)
+ if trellis is None or trellis.device.type == "meta":
+ return ("meta",)
+
+ signature: list[Any] = [str(trellis.device)]
+ for buffer_name in _EXL3_BUFFER_NAMES:
+ tensor = getattr(self, buffer_name, None)
+ if tensor is None:
+ signature.append(None)
+ continue
+ signature.append((tensor.data_ptr(), tuple(tensor.shape), str(tensor.dtype)))
+ return tuple(signature)
+
+ def _drop_inner(self) -> None:
+ if self._inner is not None:
+ try:
+ self._inner.unload()
+ except Exception:
+ pass
+ self._inner = None
+ self._inner_signature = None
+
+ def _ensure_inner(self) -> "LinearEXL3":
+ from ..exllamav3.modules.quant.exl3 import LinearEXL3
+
+ trellis = getattr(self, "trellis", None)
+ if trellis is None:
+ raise RuntimeError(f"EXL3 module `{self.name}` is missing `trellis`.")
+ if trellis.device.type == "meta":
+ raise RuntimeError(f"EXL3 module `{self.name}` has not been materialized from checkpoint tensors yet.")
+ if trellis.device.type != "cuda":
+ raise RuntimeError("EXL3 inference requires CUDA/HIP tensors.")
+
+ signature = self._current_signature()
+ if self._inner is not None and signature == self._inner_signature:
+ return self._inner
+
+ self._drop_inner()
+ self._inner = LinearEXL3(
+ config=None,
+ in_features=self.in_features,
+ out_features=self.out_features,
+ scale=None,
+ su=getattr(self, "su", None),
+ sv=getattr(self, "sv", None),
+ suh=getattr(self, "suh", None),
+ svh=getattr(self, "svh", None),
+ trellis=trellis,
+ mcg=getattr(self, "mcg", None),
+ mul1=getattr(self, "mul1", None),
+ bias=getattr(self, "bias", None),
+ out_dtype=self.out_dtype,
+ transformers_fix=True,
+ key=self.name,
+ )
+ self._inner_signature = signature
+ return self._inner
+
+ def post_init(self) -> None:
+ self._drop_inner()
+ if getattr(self, "trellis", None) is not None and self.trellis.device.type != "meta":
+ self._ensure_inner()
+
+ def _apply(self, fn):
+ self._drop_inner()
+ return super()._apply(fn)
+
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
+ inner = self._ensure_inner()
+ input_dtype = x.dtype
+ return inner.forward(x.half(), {}).to(input_dtype)
+
+ def _multiplier_value(self, name: str) -> Optional[int]:
+ tensor = getattr(self, name, None)
+ if tensor is None:
+ return None
+ return int(tensor.view(torch.uint32).item())
+
+ def tensor_storage_entry(self) -> Dict[str, Any]:
+ stored_tensors: Dict[str, Dict[str, Any]] = {}
+ for buffer_name in _EXL3_BUFFER_NAMES:
+ tensor = getattr(self, buffer_name, None)
+ if tensor is None:
+ continue
+ stored_tensors[f"{self.name}.{buffer_name}"] = {
+ "shape": list(tensor.shape),
+ "torch_dtype": str(tensor.dtype).split(".")[-1],
+ }
+
+ entry: Dict[str, Any] = {
+ "stored_tensors": stored_tensors,
+ "quant_format": "exl3",
+ }
+ trellis = getattr(self, "trellis", None)
+ if trellis is not None:
+ entry["bits_per_weight"] = int(trellis.shape[-1] // 16)
+
+ mcg_multiplier = self._multiplier_value("mcg")
+ if mcg_multiplier is not None:
+ entry["mcg_multiplier"] = mcg_multiplier
+
+ mul1_multiplier = self._multiplier_value("mul1")
+ if mul1_multiplier is not None:
+ entry["mul1_multiplier"] = mul1_multiplier
+
+ return entry
diff --git a/gptqmodel/nn_modules/exllamav3_torch.py b/gptqmodel/nn_modules/exllamav3_torch.py
new file mode 100644
index 000000000..f22b2dcfa
--- /dev/null
+++ b/gptqmodel/nn_modules/exllamav3_torch.py
@@ -0,0 +1,400 @@
+# SPDX-FileCopyrightText: 2026 ModelCloud.ai
+# SPDX-License-Identifier: Apache-2.0
+#
+# Clean-room EXL3 torch reference kernel derived from the public EXL3 tensor
+# format and documented runtime layout.
+
+from __future__ import annotations
+
+import math
+from functools import lru_cache
+from typing import Any, Dict, Optional
+
+import torch
+import torch.nn as nn
+
+from .exllamav3 import _EXL3_BUFFER_NAMES, _torch_dtype
+
+
+_EXL3_3INST_MULT = 89226354
+_EXL3_3INST_ADD = 64248484
+_EXL3_MCG_MULT = 0xCBAC1FED
+_EXL3_MUL1_MULT = 0x83DCD12D
+_EXL3_MUL1_ACC = 0x6400
+_EXL3_LOP3_MASK = 0x8FFF8FFF
+_EXL3_LOP3_BIAS = 0x3B603B60
+
+
+def _half_scalar_from_bits(bits: int) -> float:
+ return float(torch.tensor([bits], dtype=torch.uint16).view(torch.float16).item())
+
+
+_EXL3_MUL1_INV = _half_scalar_from_bits(0x1EEE)
+_EXL3_MUL1_BIAS = _half_scalar_from_bits(0xC931)
+
+
+@lru_cache(maxsize=None)
+def _tensor_core_perm(device_type: str, device_index: int | None) -> torch.Tensor:
+ device = torch.device(device_type, device_index)
+ perm = [0] * 256
+ for t in range(32):
+ r0 = (t % 4) * 2
+ r1 = r0 + 1
+ r2 = r0 + 8
+ r3 = r0 + 9
+ c0 = t // 4
+ c1 = c0 + 8
+ perm[t * 8 + 0] = r0 * 16 + c0
+ perm[t * 8 + 1] = r1 * 16 + c0
+ perm[t * 8 + 2] = r2 * 16 + c0
+ perm[t * 8 + 3] = r3 * 16 + c0
+ perm[t * 8 + 4] = r0 * 16 + c1
+ perm[t * 8 + 5] = r1 * 16 + c1
+ perm[t * 8 + 6] = r2 * 16 + c1
+ perm[t * 8 + 7] = r3 * 16 + c1
+ return torch.tensor(perm, dtype=torch.long, device=device)
+
+
+@lru_cache(maxsize=None)
+def _tensor_core_perm_i(device_type: str, device_index: int | None) -> torch.Tensor:
+ perm = _tensor_core_perm(device_type, device_index)
+ return torch.argsort(perm)
+
+
+@lru_cache(maxsize=None)
+def _hadamard_128(device_type: str, device_index: int | None) -> torch.Tensor:
+ device = torch.device(device_type, device_index)
+ had = torch.tensor([[1.0]], dtype=torch.float32, device=device)
+ while had.shape[0] < 128:
+ had = torch.cat(
+ (
+ torch.cat((had, had), dim=1),
+ torch.cat((had, -had), dim=1),
+ ),
+ dim=0,
+ )
+ had *= 1.0 / math.sqrt(128.0)
+ return had.contiguous()
+
+
+@lru_cache(maxsize=None)
+def _codebook_lut(
+ codebook: str,
+ device_type: str,
+ device_index: int | None,
+) -> torch.Tensor:
+ device = torch.device(device_type, device_index)
+ values = torch.arange(1 << 16, dtype=torch.int64, device=device)
+
+ if codebook == "3inst":
+ raw = (values * _EXL3_3INST_MULT + _EXL3_3INST_ADD) & 0xFFFFFFFF
+ raw = _EXL3_LOP3_BIAS ^ (raw & _EXL3_LOP3_MASK)
+ halves = torch.stack(
+ (
+ (raw & 0xFFFF).to(torch.uint16),
+ ((raw >> 16) & 0xFFFF).to(torch.uint16),
+ ),
+ dim=-1,
+ ).contiguous()
+ floats = halves.view(torch.float16).to(torch.float32)
+ return (floats[..., 0] + floats[..., 1]).contiguous()
+
+ if codebook == "mcg":
+ raw = (values * _EXL3_MCG_MULT) & 0xFFFFFFFF
+ raw = _EXL3_LOP3_BIAS ^ (raw & _EXL3_LOP3_MASK)
+ halves = torch.stack(
+ (
+ (raw & 0xFFFF).to(torch.uint16),
+ ((raw >> 16) & 0xFFFF).to(torch.uint16),
+ ),
+ dim=-1,
+ ).contiguous()
+ floats = halves.view(torch.float16).to(torch.float32)
+ return (floats[..., 0] + floats[..., 1]).contiguous()
+
+ if codebook == "mul1":
+ raw = (values * _EXL3_MUL1_MULT) & 0xFFFFFFFF
+ byte_sum = (
+ (raw & 0xFF)
+ + ((raw >> 8) & 0xFF)
+ + ((raw >> 16) & 0xFF)
+ + ((raw >> 24) & 0xFF)
+ )
+ accum = (byte_sum + _EXL3_MUL1_ACC).to(torch.uint16).contiguous()
+ floats = accum.view(torch.float16).to(torch.float32)
+ return (floats * _EXL3_MUL1_INV + _EXL3_MUL1_BIAS).contiguous()
+
+ raise ValueError(f"Unsupported EXL3 codebook: {codebook}")
+
+
+def _apply_hadamard_left(x: torch.Tensor) -> torch.Tensor:
+ if x.shape[0] % 128 != 0:
+ raise ValueError(f"EXL3 expects in_features to be divisible by 128, got {x.shape[0]}.")
+ had = _hadamard_128(x.device.type, x.device.index)
+ return (had @ x.view(-1, 128, x.shape[1])).view_as(x)
+
+
+def _apply_hadamard_right(x: torch.Tensor) -> torch.Tensor:
+ if x.shape[1] % 128 != 0:
+ raise ValueError(f"EXL3 expects out_features to be divisible by 128, got {x.shape[1]}.")
+ had = _hadamard_128(x.device.type, x.device.index)
+ return (x.view(x.shape[0], -1, 128) @ had).view_as(x)
+
+
+class ExllamaV3TorchLinear(nn.Module):
+ QUANT_TYPE = "exl3"
+ SUPPORTS_SHARDS = True
+
+ def __init__(
+ self,
+ *,
+ in_features: int,
+ out_features: int,
+ name: str,
+ tensor_storage: Optional[Dict[str, Any]] = None,
+ tensors: Optional[Dict[str, torch.Tensor]] = None,
+ out_dtype: torch.dtype = torch.float16,
+ ):
+ super().__init__()
+ self.in_features = in_features
+ self.out_features = out_features
+ self.name = name
+ self.out_dtype = out_dtype
+ self.tensor_storage = tensor_storage or {}
+
+ self.weight = torch.zeros((1,), dtype=torch.float16, device="meta")
+ self._cache_signature: Optional[tuple[Any, ...]] = None
+ self._inner_weight_fp32: Optional[torch.Tensor] = None
+ self._weight_fp32: Optional[torch.Tensor] = None
+
+ if tensors is not None:
+ for buffer_name in _EXL3_BUFFER_NAMES:
+ tensor = tensors.get(buffer_name)
+ if tensor is None:
+ setattr(self, buffer_name, None)
+ else:
+ self.register_buffer(buffer_name, tensor)
+ return
+
+ stored_tensors = (self.tensor_storage or {}).get("stored_tensors", {})
+ for buffer_name in _EXL3_BUFFER_NAMES:
+ metadata = stored_tensors.get(f"{name}.{buffer_name}")
+ if metadata is None:
+ setattr(self, buffer_name, None)
+ continue
+
+ shape = tuple(metadata["shape"])
+ dtype = _torch_dtype(metadata["torch_dtype"])
+ self.register_buffer(buffer_name, torch.empty(shape, dtype=dtype, device="meta"))
+
+ @classmethod
+ def from_tensors(
+ cls,
+ *,
+ in_features: int,
+ out_features: int,
+ name: str,
+ tensors: Dict[str, torch.Tensor],
+ ) -> "ExllamaV3TorchLinear":
+ return cls(
+ in_features=in_features,
+ out_features=out_features,
+ name=name,
+ tensors=tensors,
+ )
+
+ def _current_signature(self) -> tuple[Any, ...]:
+ trellis = getattr(self, "trellis", None)
+ if trellis is None or trellis.device.type == "meta":
+ return ("meta",)
+
+ signature: list[Any] = [str(trellis.device)]
+ for buffer_name in _EXL3_BUFFER_NAMES:
+ tensor = getattr(self, buffer_name, None)
+ if tensor is None:
+ signature.append(None)
+ continue
+ signature.append((tensor.data_ptr(), tuple(tensor.shape), str(tensor.dtype)))
+ return tuple(signature)
+
+ def _drop_cache(self) -> None:
+ self._cache_signature = None
+ self._inner_weight_fp32 = None
+ self._weight_fp32 = None
+
+ def _apply(self, fn):
+ self._drop_cache()
+ return super()._apply(fn)
+
+ def post_init(self) -> None:
+ self._drop_cache()
+
+ def _codebook_name(self) -> str:
+ if getattr(self, "mcg", None) is not None:
+ return "mcg"
+ if getattr(self, "mul1", None) is not None:
+ return "mul1"
+ return "3inst"
+
+ def _bits_per_weight(self) -> int:
+ trellis = getattr(self, "trellis", None)
+ if trellis is None:
+ raise RuntimeError(f"EXL3 module `{self.name}` is missing `trellis`.")
+ return int(trellis.shape[-1] // 16)
+
+ def _runtime_weight_dtype(self) -> torch.dtype:
+ trellis = getattr(self, "trellis", None)
+ if trellis is None or trellis.device.type == "cpu":
+ return torch.float32
+ return torch.float16
+
+ def _unpack_indices(self) -> torch.Tensor:
+ trellis = getattr(self, "trellis", None)
+ if trellis is None:
+ raise RuntimeError(f"EXL3 module `{self.name}` is missing `trellis`.")
+ if trellis.device.type == "meta":
+ raise RuntimeError(f"EXL3 module `{self.name}` has not been materialized from checkpoint tensors yet.")
+
+ bits = self._bits_per_weight()
+ mask = (1 << bits) - 1
+ words = (trellis.to(torch.int32) & 0xFFFF).contiguous()
+ words = words.view(*words.shape[:-1], -1, 2).flip(-1).reshape(*words.shape)
+ words = words.view(*words.shape[:-1], 16, bits)
+
+ symbols = torch.empty(
+ (*words.shape[:-2], 256),
+ dtype=torch.long,
+ device=words.device,
+ )
+ for pos in range(16):
+ bit_offset = pos * bits
+ word_idx = bit_offset // 16
+ bit_in_word = bit_offset % 16
+ if bit_in_word + bits <= 16:
+ shift = 16 - bit_in_word - bits
+ value = (words[..., word_idx] >> shift) & mask
+ else:
+ bits_first = 16 - bit_in_word
+ bits_second = bits - bits_first
+ high = (words[..., word_idx] & ((1 << bits_first) - 1)) << bits_second
+ low = words[..., word_idx + 1] >> (16 - bits_second)
+ value = (high | low) & mask
+ symbols[..., pos::16] = value.to(torch.long)
+
+ warmup = (16 + bits - 1) // bits - 1
+ state = torch.zeros_like(symbols[..., 0], dtype=torch.long)
+ for idx in range(256 - warmup, 256):
+ state = ((state << bits) | symbols[..., idx]) & 0xFFFF
+
+ encoded = torch.empty_like(symbols)
+ for idx in range(256):
+ state = ((state << bits) | symbols[..., idx]) & 0xFFFF
+ encoded[..., idx] = state
+
+ return encoded
+
+ def _ensure_inner_weight_fp32(self) -> torch.Tensor:
+ trellis = getattr(self, "trellis", None)
+ if trellis is None:
+ raise RuntimeError(f"EXL3 module `{self.name}` is missing `trellis`.")
+ if trellis.device.type == "meta":
+ raise RuntimeError(f"EXL3 module `{self.name}` has not been materialized from checkpoint tensors yet.")
+
+ signature = self._current_signature()
+ if self._inner_weight_fp32 is not None and self._cache_signature == signature:
+ return self._inner_weight_fp32
+
+ encoded = self._unpack_indices()
+ lut = _codebook_lut(self._codebook_name(), trellis.device.type, trellis.device.index)
+ decoded = lut[encoded]
+
+ perm_i = _tensor_core_perm_i(trellis.device.type, trellis.device.index)
+ decoded = decoded[..., perm_i]
+ tiles_k, tiles_n = decoded.shape[:2]
+ inner = decoded.view(tiles_k, tiles_n, 16, 16).permute(0, 2, 1, 3).reshape(
+ tiles_k * 16, tiles_n * 16
+ )
+
+ self._cache_signature = signature
+ self._inner_weight_fp32 = inner.contiguous().to(torch.float32)
+ self._weight_fp32 = None
+ return self._inner_weight_fp32
+
+ def get_inner_weight_tensor(self, dtype: Optional[torch.dtype] = None) -> torch.Tensor:
+ inner = self._ensure_inner_weight_fp32()
+ target_dtype = dtype or self._runtime_weight_dtype()
+ if inner.dtype == target_dtype:
+ return inner
+ return inner.to(dtype=target_dtype)
+
+ def _ensure_weight_fp32(self) -> torch.Tensor:
+ signature = self._current_signature()
+ if self._weight_fp32 is not None and self._cache_signature == signature:
+ return self._weight_fp32
+
+ inner = self._ensure_inner_weight_fp32().clone()
+ inner = _apply_hadamard_left(inner)
+ inner *= getattr(self, "suh").to(dtype=torch.float32).unsqueeze(1)
+ inner = _apply_hadamard_right(inner)
+ inner *= getattr(self, "svh").to(dtype=torch.float32).unsqueeze(0)
+
+ self._weight_fp32 = inner.contiguous()
+ return self._weight_fp32
+
+ def get_weight_tensor(self, dtype: Optional[torch.dtype] = None) -> torch.Tensor:
+ weight = self._ensure_weight_fp32()
+ target_dtype = dtype or self._runtime_weight_dtype()
+ if weight.dtype == target_dtype:
+ return weight
+ return weight.to(dtype=target_dtype)
+
+ def get_bias_tensor(self) -> torch.Tensor | None:
+ return getattr(self, "bias", None)
+
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
+ input_dtype = x.dtype
+ compute_dtype = torch.float32 if x.device.type == "cpu" else torch.float16
+ x_2d = x.view(-1, self.in_features).to(compute_dtype)
+ weight = self.get_weight_tensor(dtype=compute_dtype)
+ y = x_2d @ weight
+ bias = getattr(self, "bias", None)
+ if bias is not None:
+ y = y + bias.to(dtype=compute_dtype)
+ y = y.view(*x.shape[:-1], self.out_features)
+ return y.to(input_dtype)
+
+ def _multiplier_value(self, name: str) -> Optional[int]:
+ tensor = getattr(self, name, None)
+ if tensor is None:
+ return None
+ return int(tensor.view(torch.uint32).item())
+
+ def tensor_storage_entry(self) -> Dict[str, Any]:
+ stored_tensors: Dict[str, Dict[str, Any]] = {}
+ for buffer_name in _EXL3_BUFFER_NAMES:
+ tensor = getattr(self, buffer_name, None)
+ if tensor is None:
+ continue
+ stored_tensors[f"{self.name}.{buffer_name}"] = {
+ "shape": list(tensor.shape),
+ "torch_dtype": str(tensor.dtype).split(".")[-1],
+ }
+
+ entry: Dict[str, Any] = {
+ "stored_tensors": stored_tensors,
+ "quant_format": "exl3",
+ "bits_per_weight": self._bits_per_weight(),
+ }
+
+ mcg_multiplier = self._multiplier_value("mcg")
+ if mcg_multiplier is not None:
+ entry["mcg_multiplier"] = mcg_multiplier
+
+ mul1_multiplier = self._multiplier_value("mul1")
+ if mul1_multiplier is not None:
+ entry["mul1_multiplier"] = mul1_multiplier
+
+ return entry
+
+
+__all__ = ["ExllamaV3TorchLinear"]
diff --git a/gptqmodel/nn_modules/qlinear/__init__.py b/gptqmodel/nn_modules/qlinear/__init__.py
index 6ae833c65..6b9866a79 100644
--- a/gptqmodel/nn_modules/qlinear/__init__.py
+++ b/gptqmodel/nn_modules/qlinear/__init__.py
@@ -8,7 +8,7 @@
import sys
from concurrent.futures import ThreadPoolExecutor
from functools import lru_cache
-from typing import Dict, List, Optional, Tuple
+from typing import Any, Dict, List, Optional, Tuple
import numpy as np
import torch as t # conflict with torch.py
@@ -53,6 +53,7 @@ class BaseQuantLinear(nn.Module):
SUPPORTS_DTYPES: List[t.dtype] = None
REQUIRES_FORMAT_V2: bool = False
+ AUTOTUNE: bool = False
def __init__(self,
bits: int,
@@ -89,6 +90,9 @@ def __init__(self,
self.adapter = copy.deepcopy(adapter)
self.optimized = False
+ self.autotune_enabled = self.AUTOTUNE
+ self._autotune_complete = False
+ self._autotune_result: Any = None
if self.pack_dtype == t.int8:
self.pack_dtype_bits = 8
@@ -215,6 +219,7 @@ def qzero_format(self, format: int = None) -> int:
# override me, to perform post-weight load to device init
def post_init(self):
+ self.clear_autotune()
if self.adapter is not None:
self.adapter.post_init(
weight_key=self.name,
@@ -222,6 +227,27 @@ def post_init(self):
lora_A=getattr(self, "lora_A", None),
lora_B=getattr(self, "lora_B", None))
+ def clear_autotune(self):
+ self._autotune_complete = False
+ self._autotune_result = None
+
+ def get_autotune_result(self):
+ return self._autotune_result
+
+ def _autotune(self, *args, **kwargs):
+ raise NotImplementedError(f"{self.__class__.__name__} does not implement `_autotune()`.")
+
+ def maybe_autotune(self, *args, **kwargs):
+ if not self.autotune_enabled or self.training:
+ return self._autotune_result
+
+ if self._autotune_complete:
+ return self._autotune_result
+
+ self._autotune_result = self._autotune(*args, **kwargs)
+ self._autotune_complete = True
+ return self._autotune_result
+
@classmethod
@lru_cache(maxsize=1024)
def cached_validate_once(cls) -> Tuple[bool, Optional[Exception]]:
@@ -435,6 +461,7 @@ def train(self, mode=True):
pass
# log.info(f"{self.__class__.__name__}: `{self.name}` switching to eval mode.")
+ self.clear_autotune()
return super().train(mode)
class PackableQuantLinear(BaseQuantLinear):
diff --git a/gptqmodel/nn_modules/qlinear/fp8.py b/gptqmodel/nn_modules/qlinear/fp8.py
new file mode 100644
index 000000000..0add60d45
--- /dev/null
+++ b/gptqmodel/nn_modules/qlinear/fp8.py
@@ -0,0 +1,436 @@
+# SPDX-FileCopyrightText: 2026 ModelCloud.ai
+# SPDX-License-Identifier: Apache-2.0
+
+from __future__ import annotations
+
+from typing import Optional, Tuple
+
+import torch
+import torch.nn as nn
+import transformers
+from torch.nn.modules.conv import _ConvNd
+
+from ...adapter.adapter import Adapter, Lora
+from ...models._const import DEVICE, PLATFORM
+from ...quantization import FORMAT, METHOD
+from ...quantization.config import (
+ _normalize_fp8_fmt,
+ _normalize_fp8_scale_semantics,
+ _normalize_fp8_weight_block_size,
+ _normalize_fp8_weight_scale_method,
+)
+from ...utils.backend import BACKEND
+from . import BaseQuantLinear
+from .gguf import _apply_optional_smoother
+
+
+def _fp8_dtype_from_name(fmt: str) -> torch.dtype:
+ return getattr(torch, _normalize_fp8_fmt(fmt))
+
+
+def _weight_to_matrix(linear: nn.Module) -> torch.Tensor:
+ weight = linear.weight.detach()
+ if isinstance(linear, _ConvNd):
+ weight = weight.flatten(1)
+ if isinstance(linear, transformers.pytorch_utils.Conv1D):
+ weight = weight.T
+ return weight
+
+
+def _compute_scale_inv(abs_max: torch.Tensor, fp8_max: float) -> torch.Tensor:
+ abs_max = abs_max.to(torch.float32)
+ eps = torch.finfo(torch.float32).tiny
+ return torch.where(
+ abs_max > 0,
+ torch.full_like(abs_max, float(fp8_max)) / abs_max.clamp_min(eps),
+ torch.ones_like(abs_max),
+ )
+
+
+def quantize_fp8_weight(
+ weight: torch.Tensor,
+ *,
+ format: str = "float8_e4m3fn",
+ weight_scale_method: str = "row",
+ weight_block_size: Optional[Tuple[int, int]] = None,
+) -> tuple[torch.Tensor, torch.Tensor]:
+ if weight.ndim != 2:
+ raise ValueError(f"FP8 quantization expects a 2D weight matrix, got shape {tuple(weight.shape)}.")
+
+ format = _normalize_fp8_fmt(format)
+ block_size = _normalize_fp8_weight_block_size(weight_block_size)
+ weight_scale_method = _normalize_fp8_weight_scale_method(
+ weight_scale_method,
+ weight_block_size=block_size,
+ )
+ fp8_dtype = _fp8_dtype_from_name(format)
+ fp8_max = torch.finfo(fp8_dtype).max
+
+ weight = weight.to(device="cpu", dtype=torch.float32).contiguous()
+
+ if weight_scale_method == "tensor":
+ scale_inv = _compute_scale_inv(weight.abs().amax(), fp8_max)
+ quantized = torch.clamp(weight * scale_inv, min=-fp8_max, max=fp8_max).to(fp8_dtype)
+ return quantized.contiguous(), scale_inv.to(torch.float32)
+
+ if weight_scale_method == "row":
+ scale_inv = _compute_scale_inv(weight.abs().amax(dim=1), fp8_max)
+ quantized = torch.clamp(
+ weight * scale_inv.unsqueeze(1),
+ min=-fp8_max,
+ max=fp8_max,
+ ).to(fp8_dtype)
+ return quantized.contiguous(), scale_inv.to(torch.float32).contiguous()
+
+ if block_size is None:
+ raise ValueError("FP8 block quantization requires `weight_block_size`.")
+
+ block_rows, block_cols = block_size
+ rows, cols = weight.shape
+ if rows % block_rows != 0 or cols % block_cols != 0:
+ raise ValueError(
+ f"FP8 block quantization expects shape {tuple(weight.shape)} to be divisible by block size "
+ f"{block_size}."
+ )
+
+ row_blocks = rows // block_rows
+ col_blocks = cols // block_cols
+ blocks = weight.reshape(row_blocks, block_rows, col_blocks, block_cols)
+ scale_inv = _compute_scale_inv(blocks.abs().amax(dim=(1, 3)), fp8_max)
+ scaled = blocks * scale_inv.unsqueeze(1).unsqueeze(3)
+ quantized = torch.clamp(scaled, min=-fp8_max, max=fp8_max).to(fp8_dtype).reshape(rows, cols)
+ return quantized.contiguous(), scale_inv.to(torch.float32).contiguous()
+
+
+class TorchFP8QuantLinear(BaseQuantLinear):
+ SUPPORTS_BACKENDS = [BACKEND.TORCH]
+ SUPPORTS_METHODS = [METHOD.FP8]
+ SUPPORTS_FORMATS = {FORMAT.FP8: 15}
+ SUPPORTS_BITS = [8]
+ SUPPORTS_GROUP_SIZE = [-1]
+ SUPPORTS_DESC_ACT = [False]
+ SUPPORTS_SYM = [True]
+ SUPPORTS_SHARDS = True
+ SUPPORTS_TRAINING = True
+ SUPPORTS_AUTO_PADDING = False
+ SUPPORTS_IN_FEATURES_DIVISIBLE_BY = [1]
+ SUPPORTS_OUT_FEATURES_DIVISIBLE_BY = [1]
+ SUPPORTS_DEVICES = [DEVICE.ALL]
+ SUPPORTS_PLATFORM = [PLATFORM.ALL]
+ SUPPORTS_PACK_DTYPES = [torch.int8, torch.int16, torch.int32, torch.int64]
+ SUPPORTS_ADAPTERS = [Lora]
+ SUPPORTS_DTYPES = [torch.float16, torch.bfloat16, torch.float32]
+
+ QUANT_TYPE = "fp8"
+
+ def __init__(
+ self,
+ bits: int,
+ group_size: int,
+ sym: bool,
+ desc_act: bool,
+ in_features: int,
+ out_features: int,
+ bias: bool = False,
+ pack_dtype: torch.dtype = torch.int32,
+ adapter: Adapter = None,
+ register_buffers: bool = True,
+ format: str = "float8_e4m3fn",
+ weight_scale_method: str = "row",
+ weight_block_size: Optional[Tuple[int, int]] = None,
+ weight_scale_semantics: str = "inverse",
+ **kwargs,
+ ):
+ self.fp8_format = _normalize_fp8_fmt(format)
+ self.fp8_dtype = _fp8_dtype_from_name(self.fp8_format)
+ block_size = _normalize_fp8_weight_block_size(weight_block_size)
+ self.weight_scale_method = _normalize_fp8_weight_scale_method(
+ weight_scale_method,
+ weight_block_size=block_size,
+ )
+ self.weight_block_size = block_size
+ self.weight_scale_semantics = _normalize_fp8_scale_semantics(weight_scale_semantics)
+ self._scaled_mm_hard_disabled = False
+
+ if self.weight_scale_method == "block" and self.weight_block_size is not None:
+ block_rows, block_cols = self.weight_block_size
+ if out_features % block_rows != 0 or in_features % block_cols != 0:
+ raise ValueError(
+ f"TorchFP8QuantLinear block scaling requires out_features/in_features "
+ f"to be divisible by `weight_block_size={self.weight_block_size}`."
+ )
+
+ super().__init__(
+ bits=bits,
+ group_size=group_size,
+ desc_act=desc_act,
+ sym=sym,
+ in_features=in_features,
+ out_features=out_features,
+ bias=bias,
+ pack_dtype=pack_dtype,
+ backend=kwargs.pop("backend", BACKEND.TORCH),
+ adapter=adapter,
+ register_buffers=False,
+ **kwargs,
+ )
+
+ self.group_size = -1
+ self.desc_act = False
+ self.sym = True
+
+ if register_buffers:
+ self._allocate_buffers(bias=bias)
+
+ @classmethod
+ def validate_once(cls):
+ if not (hasattr(torch, "float8_e4m3fn") or hasattr(torch, "float8_e5m2")):
+ return False, RuntimeError("TorchFP8QuantLinear requires a PyTorch build with FP8 dtypes.")
+ return True, None
+
+ def _scale_shape(self) -> tuple[int, ...]:
+ if self.weight_scale_method == "tensor":
+ return ()
+ if self.weight_scale_method == "row":
+ return (self.out_features,)
+ if self.weight_block_size is None:
+ raise ValueError("TorchFP8QuantLinear block scaling requires `weight_block_size`.")
+ block_rows, block_cols = self.weight_block_size
+ return (
+ self.out_features // block_rows,
+ self.in_features // block_cols,
+ )
+
+ def _allocate_buffers(self, *, bias: bool) -> None:
+ weight = torch.zeros((self.out_features, self.in_features), dtype=self.fp8_dtype)
+ scale = torch.ones(self._scale_shape(), dtype=torch.float32)
+
+ if "weight" in self._buffers:
+ self.weight = weight
+ else:
+ self.register_buffer("weight", weight)
+
+ if "weight_scale_inv" in self._buffers:
+ self.weight_scale_inv = scale
+ else:
+ self.register_buffer("weight_scale_inv", scale)
+
+ if bias:
+ bias_tensor = torch.zeros(self.out_features, dtype=torch.float16)
+ if "bias" in self._buffers:
+ self.bias = bias_tensor
+ else:
+ self.register_buffer("bias", bias_tensor)
+ else:
+ self.bias = None
+
+ def list_buffers(self):
+ buffers = []
+ if hasattr(self, "weight") and self.weight is not None:
+ buffers.append(self.weight)
+ if hasattr(self, "weight_scale_inv") and self.weight_scale_inv is not None:
+ buffers.append(self.weight_scale_inv)
+ if hasattr(self, "bias") and self.bias is not None:
+ buffers.append(self.bias)
+ return buffers
+
+ def extra_repr(self) -> str:
+ return (
+ f"in_features={self.in_features}, out_features={self.out_features}, "
+ f"bias={self.bias is not None}, format={self.fp8_format}, "
+ f"weight_scale_method={self.weight_scale_method}"
+ )
+
+ def _weight_to_matrix(self, linear: nn.Module) -> torch.Tensor:
+ return _weight_to_matrix(linear)
+
+ def pack(self, linear: nn.Module, scales: torch.Tensor, zeros: torch.Tensor, g_idx: torch.Tensor = None):
+ self.pack_original(linear=linear, scales=scales, zeros=zeros, g_idx=g_idx)
+
+ def pack_block(
+ self,
+ linear: nn.Module,
+ scales: torch.Tensor,
+ zeros: torch.Tensor,
+ g_idx: torch.Tensor = None,
+ block_in: int = 8192,
+ workers: int = 1,
+ ):
+ del block_in, workers
+ self.pack_original(linear=linear, scales=scales, zeros=zeros, g_idx=g_idx)
+
+ def pack_gpu(
+ self,
+ linear: nn.Module,
+ scales: torch.Tensor,
+ zeros: torch.Tensor,
+ g_idx: torch.Tensor = None,
+ *,
+ block_in: int = 8192,
+ device: torch.device | None = None,
+ ):
+ del block_in, device
+ self.pack_original(linear=linear, scales=scales, zeros=zeros, g_idx=g_idx)
+
+ @torch.inference_mode()
+ def pack_original(
+ self,
+ linear: nn.Module,
+ scales: torch.Tensor,
+ zeros: torch.Tensor,
+ g_idx: torch.Tensor = None,
+ *,
+ smooth=None,
+ ):
+ del scales, zeros, g_idx
+
+ weight = self._weight_to_matrix(linear).to(device="cpu", dtype=torch.float32)
+ weight = _apply_optional_smoother(
+ weight,
+ smooth=smooth,
+ group_size=self.group_size,
+ )
+ qweight, weight_scale_inv = quantize_fp8_weight(
+ weight,
+ format=self.fp8_format,
+ weight_scale_method=self.weight_scale_method,
+ weight_block_size=self.weight_block_size,
+ )
+
+ if "weight" in self._buffers:
+ self.weight = qweight
+ else:
+ self.register_buffer("weight", qweight)
+
+ if "weight_scale_inv" in self._buffers:
+ self.weight_scale_inv = weight_scale_inv
+ else:
+ self.register_buffer("weight_scale_inv", weight_scale_inv)
+
+ if linear.bias is not None:
+ bias = linear.bias.detach().to(device="cpu", dtype=torch.float16)
+ if "bias" in self._buffers:
+ self.bias = bias
+ else:
+ self.register_buffer("bias", bias)
+ else:
+ self.bias = None
+
+ self._scaled_mm_hard_disabled = False
+
+ def _resolve_target(self, device=None, dtype=None) -> tuple[torch.device, torch.dtype]:
+ target_device = self.weight.device if device is None else torch.device(device)
+ target_dtype = torch.float32 if dtype is None else dtype
+ return target_device, target_dtype
+
+ def _expanded_scale_inv(self, *, target_device: torch.device, target_dtype: torch.dtype) -> torch.Tensor:
+ scale_inv = self.weight_scale_inv
+ if scale_inv.device != target_device or scale_inv.dtype != target_dtype:
+ scale_inv = scale_inv.to(device=target_device, dtype=target_dtype)
+
+ if self.weight_scale_method == "tensor":
+ return scale_inv
+ if self.weight_scale_method == "row":
+ return scale_inv.view(-1, 1)
+ if self.weight_block_size is None:
+ raise ValueError("TorchFP8QuantLinear block scaling requires `weight_block_size`.")
+
+ block_rows, block_cols = self.weight_block_size
+ expanded = scale_inv.repeat_interleave(block_rows, dim=0)
+ expanded = expanded.repeat_interleave(block_cols, dim=1)
+ return expanded
+
+ def dequantize_weight(
+ self,
+ *,
+ device: torch.device | str | None = None,
+ dtype: torch.dtype | None = None,
+ ) -> torch.Tensor:
+ target_device, target_dtype = self._resolve_target(device=device, dtype=dtype)
+ weight = self.weight if self.weight.device == target_device else self.weight.to(device=target_device)
+ weight = weight.to(target_dtype)
+ scale_inv = self._expanded_scale_inv(target_device=target_device, target_dtype=target_dtype)
+
+ if self.weight_scale_semantics != "inverse":
+ raise NotImplementedError(
+ f"Unsupported FP8 scale semantics `{self.weight_scale_semantics}`."
+ )
+
+ weight = weight / scale_inv
+ return weight.transpose(0, 1).contiguous()
+
+ def _scaled_mm_weight_scale(self, *, device: torch.device) -> torch.Tensor:
+ scale_inv = self.weight_scale_inv
+ if scale_inv.device != device:
+ scale_inv = scale_inv.to(device=device)
+ scale = torch.reciprocal(scale_inv.to(torch.float32))
+ if self.weight_scale_method == "tensor":
+ return scale
+ if self.weight_scale_method == "row":
+ return scale.view(1, -1)
+ raise NotImplementedError("scaled_mm is only used for tensorwise or rowwise FP8 scales.")
+
+ def _quantize_input_for_scaled_mm(self, x_flat: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
+ fp8_max = torch.finfo(self.fp8_dtype).max
+ x_work = x_flat.to(torch.float32)
+ if self.weight_scale_method == "tensor":
+ scale_inv = _compute_scale_inv(x_work.abs().amax(), fp8_max)
+ else:
+ scale_inv = _compute_scale_inv(x_work.abs().amax(dim=1, keepdim=True), fp8_max)
+ x_q = torch.clamp(x_work * scale_inv, min=-fp8_max, max=fp8_max).to(self.fp8_dtype)
+ return x_q, torch.reciprocal(scale_inv).to(torch.float32)
+
+ def _can_use_scaled_mm(self, x_flat: torch.Tensor) -> bool:
+ return (
+ not self._scaled_mm_hard_disabled
+ and hasattr(torch, "_scaled_mm")
+ and x_flat.device.type == "cuda"
+ and self.weight_scale_method == "tensor"
+ and x_flat.dtype in {torch.float16, torch.bfloat16}
+ and x_flat.shape[-1] == self.in_features
+ and self.in_features % 16 == 0
+ and self.out_features % 16 == 0
+ )
+
+ def _forward_dequant_matmul(self, x_flat: torch.Tensor) -> torch.Tensor:
+ weight = self.dequantize_weight(device=x_flat.device, dtype=x_flat.dtype)
+ return torch.matmul(x_flat, weight)
+
+ def _forward_scaled_mm(self, x_flat: torch.Tensor) -> torch.Tensor:
+ weight = self.weight if self.weight.device == x_flat.device else self.weight.to(device=x_flat.device)
+ x_q, scale_a = self._quantize_input_for_scaled_mm(x_flat)
+ scale_b = self._scaled_mm_weight_scale(device=x_flat.device)
+ return torch._scaled_mm(
+ x_q,
+ weight.t(),
+ scale_a=scale_a,
+ scale_b=scale_b,
+ out_dtype=x_flat.dtype,
+ )
+
+ def forward(self, x: torch.Tensor):
+ original_shape = x.shape[:-1] + (self.out_features,)
+ x_flat = x.reshape(-1, x.shape[-1])
+
+ if self._can_use_scaled_mm(x_flat):
+ try:
+ output = self._forward_scaled_mm(x_flat)
+ except Exception:
+ self._scaled_mm_hard_disabled = True
+ output = self._forward_dequant_matmul(x_flat)
+ else:
+ output = self._forward_dequant_matmul(x_flat)
+
+ if self.bias is not None:
+ bias = self.bias
+ if bias.device != output.device or bias.dtype != output.dtype:
+ bias = bias.to(device=output.device, dtype=output.dtype)
+ output = output + bias
+
+ if self.adapter:
+ output = self.adapter.apply(x=x_flat, out=output)
+
+ return output.reshape(original_shape)
+
+
+__all__ = ["TorchFP8QuantLinear", "quantize_fp8_weight"]
diff --git a/gptqmodel/nn_modules/qlinear/gguf.py b/gptqmodel/nn_modules/qlinear/gguf.py
new file mode 100644
index 000000000..4cac724e7
--- /dev/null
+++ b/gptqmodel/nn_modules/qlinear/gguf.py
@@ -0,0 +1,945 @@
+# SPDX-FileCopyrightText: 2026 ModelCloud.ai
+# SPDX-License-Identifier: Apache-2.0
+
+from __future__ import annotations
+
+import math
+import os
+import time
+
+import numpy as np
+import torch
+import torch.nn as nn
+import torch.nn.functional as F
+import transformers
+from torch.nn.modules.conv import _ConvNd
+
+from ...adapter.adapter import Adapter, Lora
+from ...models._const import DEVICE, PLATFORM
+from ...quantization.config import Fallback, FallbackStrategy, FORMAT, GGUFBits, METHOD, SmoothMethod, _normalize_quant_bits
+from ...quantization.fallback_smooth import smooth_block
+from ...utils.backend import BACKEND
+from ...utils.logger import setup_logger
+from . import BaseQuantLinear
+
+
+try:
+ import gguf as gguf_lib
+
+ _GGUF_AVAILABLE = True
+except Exception: # pragma: no cover - optional dependency
+ gguf_lib = None
+ _GGUF_AVAILABLE = False
+
+
+setup_logger()
+
+_GGUF_TYPE_INFO = {
+ "Q4_0": {"bits": 4, "block_size": 32, "type_size": 18},
+ "Q8_0": {"bits": 8, "block_size": 32, "type_size": 34},
+ "Q4_K": {"bits": 4, "block_size": 256, "type_size": 144},
+ "Q5_K": {"bits": 5, "block_size": 256, "type_size": 176},
+ "Q6_K": {"bits": 6, "block_size": 256, "type_size": 210},
+}
+_GGUF_BITS_ALIAS_TO_TENSOR_QTYPE = {
+ "q4_0": "Q4_0",
+ "q8_0": "Q8_0",
+ "q4_k": "Q4_K",
+ "q4_k_s": "Q4_K",
+ "q4_k_m": "Q4_K",
+ "q5_k": "Q5_K",
+ "q5_k_s": "Q5_K",
+ "q5_k_m": "Q5_K",
+ "q6_k": "Q6_K",
+}
+_GGUF_SCALE_QUANT_MAX = 63
+_GGUF_Q6_SCALE_QUANT_MAX = 127
+_GGUF_K_QTYPES = {"Q4_K", "Q5_K", "Q6_K"}
+
+
+def _normalize_gguf_bits(bits) -> tuple[GGUFBits, str]:
+ bits_spec = _normalize_quant_bits(bits, format_value=FORMAT.GGUF)
+ tensor_qtype = _GGUF_BITS_ALIAS_TO_TENSOR_QTYPE.get(bits_spec.name)
+ if tensor_qtype is None:
+ supported = ", ".join(sorted(_GGUF_BITS_ALIAS_TO_TENSOR_QTYPE))
+ raise ValueError(f"Unsupported GGUF bits `{bits}`. Supported values: {supported}.")
+
+ qtype_info = _GGUF_TYPE_INFO[tensor_qtype]
+ if qtype_info["bits"] != bits_spec.width:
+ raise ValueError(
+ f"GGUF bits `{bits_spec.name}` require {qtype_info['bits']}-bit GGUF packing, but got bits={bits_spec.width}."
+ )
+
+ return bits_spec, tensor_qtype
+
+
+def _apply_optional_smoother(
+ weight: torch.Tensor,
+ *,
+ smooth: SmoothMethod | None,
+ group_size: int,
+) -> torch.Tensor:
+ if smooth is None:
+ return weight
+
+ effective_group_size = weight.shape[1] if group_size == -1 else group_size
+ if effective_group_size <= 0:
+ effective_group_size = weight.shape[1]
+
+ fallback = Fallback(
+ strategy=FallbackStrategy.RTN,
+ threshold=True,
+ smooth=smooth,
+ )
+ smoothed = weight.clone()
+
+ for start in range(0, weight.shape[1], effective_group_size):
+ end = min(start + effective_group_size, weight.shape[1])
+ block, scale_factor = smooth_block(
+ smoothed[:, start:end],
+ fallback,
+ group_size=effective_group_size,
+ )
+ if scale_factor is not None:
+ raise ValueError(
+ "GGUF direct packing does not support smoothers that require post-quant rescaling."
+ )
+ smoothed[:, start:end] = block
+
+ return smoothed
+
+
+def _gguf_quantize_q4_0(blocks: np.ndarray) -> np.ndarray:
+ n_blocks = blocks.shape[0]
+ block_size = _GGUF_TYPE_INFO["Q4_0"]["block_size"]
+
+ imax = np.abs(blocks).argmax(axis=-1, keepdims=True)
+ max_vals = np.take_along_axis(blocks, imax, axis=-1)
+
+ d = max_vals / -8.0
+ with np.errstate(divide="ignore"):
+ inv_d = np.where(d == 0, 0, 1.0 / d)
+
+ # Match ggml's q4_0 reference path by truncating after the +8.5 offset.
+ qs = np.trunc((blocks.astype(np.float64) * inv_d.astype(np.float64)) + 8.5).astype(np.uint8)
+ qs = np.clip(qs, 0, 15)
+ qs = qs.reshape((n_blocks, 2, block_size // 2))
+ qs = qs[:, 0, :] | (qs[:, 1, :] << np.uint8(4))
+
+ d = d.astype(np.float16).view(np.uint8)
+ return np.concatenate([d, qs], axis=-1)
+
+
+def _gguf_quantize_q8_0(blocks: np.ndarray) -> np.ndarray:
+ d = np.abs(blocks).max(axis=-1, keepdims=True) / 127.0
+ with np.errstate(divide="ignore"):
+ inv_d = np.where(d == 0, 0, 1.0 / d)
+ qs = np.round(blocks * inv_d).astype(np.int8).view(np.uint8)
+ d = d.astype(np.float16).view(np.uint8)
+ return np.concatenate([d, qs], axis=-1)
+
+
+def _pack_q4_k_scale_min(scales: np.ndarray, mins: np.ndarray) -> np.ndarray:
+ scales = scales.astype(np.uint8, copy=False)
+ mins = mins.astype(np.uint8, copy=False)
+
+ d = (scales[:, :4] & np.uint8(0x3F)) | ((scales[:, 4:] & np.uint8(0x30)) << np.uint8(2))
+ m = (mins[:, :4] & np.uint8(0x3F)) | ((mins[:, 4:] & np.uint8(0x30)) << np.uint8(2))
+ md = (scales[:, 4:] & np.uint8(0x0F)) | ((mins[:, 4:] & np.uint8(0x0F)) << np.uint8(4))
+
+ return np.concatenate([d, m, md], axis=-1)
+
+
+def _unpack_q4_k_scale_min(scales: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
+ packed = scales.astype(np.uint8, copy=False).reshape((-1, 3, 4))
+ d, m, md = np.split(packed, 3, axis=-2)
+
+ sc = np.concatenate([d & 0x3F, (md & 0x0F) | ((d >> 2) & 0x30)], axis=-1)
+ mins = np.concatenate([m & 0x3F, (md >> 4) | ((m >> 2) & 0x30)], axis=-1)
+ return sc.reshape((-1, 8)), mins.reshape((-1, 8))
+
+
+def _unpack_q4_k_scale_min_torch(scales: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
+ packed = scales.reshape(*scales.shape[:-1], 3, 4)
+ d = packed[..., 0, :]
+ m = packed[..., 1, :]
+ md = packed[..., 2, :]
+
+ sc = torch.cat((d & 0x3F, (md & 0x0F) | ((d >> 2) & 0x30)), dim=-1)
+ mins = torch.cat((m & 0x3F, (md >> 4) | ((m >> 2) & 0x30)), dim=-1)
+ return sc, mins
+
+
+def _quantize_k_subblocks(
+ subblocks: np.ndarray,
+ *,
+ maxq: int,
+ scale_quant_max: int,
+ signed: bool,
+) -> tuple[np.ndarray, ...]:
+ if signed:
+ scale = np.abs(subblocks).max(axis=-1) / maxq
+ base = scale.max(axis=-1, keepdims=True) / scale_quant_max
+ with np.errstate(divide="ignore", invalid="ignore"):
+ quant_scales = np.where(base > 0, np.rint(scale / base), 0.0)
+ quant_scales = np.clip(quant_scales, 0, scale_quant_max).astype(np.int32)
+ eff_scale = base * quant_scales.astype(np.float32)
+ with np.errstate(divide="ignore", invalid="ignore"):
+ q = np.where(eff_scale[..., None] > 0, np.rint(subblocks / eff_scale[..., None]), 0.0)
+ q = np.clip(q, -32, 31).astype(np.int8)
+ return base.astype(np.float16), quant_scales.astype(np.int8), q
+
+ mins = np.maximum(-subblocks.min(axis=-1), 0.0)
+ scale = (subblocks.max(axis=-1) + mins) / maxq
+
+ base = scale.max(axis=-1, keepdims=True) / scale_quant_max
+ min_base = mins.max(axis=-1, keepdims=True) / scale_quant_max
+
+ with np.errstate(divide="ignore", invalid="ignore"):
+ quant_scales = np.where(base > 0, np.rint(scale / base), 0.0)
+ quant_mins = np.where(min_base > 0, np.rint(mins / min_base), 0.0)
+
+ quant_scales = np.clip(quant_scales, 0, scale_quant_max).astype(np.uint8)
+ quant_mins = np.clip(quant_mins, 0, scale_quant_max).astype(np.uint8)
+
+ eff_scale = base * quant_scales.astype(np.float32)
+ eff_min = min_base * quant_mins.astype(np.float32)
+ shifted = subblocks + eff_min[..., None]
+ with np.errstate(divide="ignore", invalid="ignore"):
+ q = np.where(eff_scale[..., None] > 0, np.rint(shifted / eff_scale[..., None]), 0.0)
+ q = np.clip(q, 0, maxq).astype(np.uint8)
+
+ return base.astype(np.float16), min_base.astype(np.float16), quant_scales, quant_mins, q
+
+
+def _gguf_quantize_q4_k(blocks: np.ndarray) -> np.ndarray:
+ n_blocks = blocks.shape[0]
+ subblocks = blocks.reshape(n_blocks, 8, 32)
+ d, dmin, sc, mins, q = _quantize_k_subblocks(
+ subblocks,
+ maxq=15,
+ scale_quant_max=_GGUF_SCALE_QUANT_MAX,
+ signed=False,
+ )
+ scales = _pack_q4_k_scale_min(sc, mins)
+ q_pairs = q.reshape(n_blocks, 4, 2, 32)
+ qs = q_pairs[:, :, 0, :] | (q_pairs[:, :, 1, :] << np.uint8(4))
+ return np.concatenate(
+ [
+ d.view(np.uint8),
+ dmin.view(np.uint8),
+ scales,
+ qs.reshape(n_blocks, 128),
+ ],
+ axis=-1,
+ )
+
+
+def _gguf_quantize_q5_k(blocks: np.ndarray) -> np.ndarray:
+ n_blocks = blocks.shape[0]
+ subblocks = blocks.reshape(n_blocks, 8, 32)
+ d, dmin, sc, mins, q = _quantize_k_subblocks(
+ subblocks,
+ maxq=31,
+ scale_quant_max=_GGUF_SCALE_QUANT_MAX,
+ signed=False,
+ )
+ scales = _pack_q4_k_scale_min(sc, mins)
+ q_pairs = q.reshape(n_blocks, 4, 2, 32)
+ qs = (q_pairs[:, :, 0, :] & np.uint8(0x0F)) | ((q_pairs[:, :, 1, :] & np.uint8(0x0F)) << np.uint8(4))
+ qh = np.sum(
+ (((q >> np.uint8(4)) & np.uint8(0x01)).astype(np.uint16) << np.arange(8, dtype=np.uint16).reshape(1, 8, 1)),
+ axis=1,
+ dtype=np.uint16,
+ ).astype(np.uint8)
+ return np.concatenate(
+ [
+ d.view(np.uint8),
+ dmin.view(np.uint8),
+ scales,
+ qh,
+ qs.reshape(n_blocks, 128),
+ ],
+ axis=-1,
+ )
+
+
+def _gguf_quantize_q6_k(blocks: np.ndarray) -> np.ndarray:
+ n_blocks = blocks.shape[0]
+ subblocks = blocks.reshape(n_blocks, 16, 16)
+ d, scales, q = _quantize_k_subblocks(
+ subblocks,
+ maxq=31,
+ scale_quant_max=_GGUF_Q6_SCALE_QUANT_MAX,
+ signed=True,
+ )
+ q_raw = (q.astype(np.int16) + 32).astype(np.uint8).reshape(n_blocks, 8, 32)
+
+ ql = np.empty((n_blocks, 128), dtype=np.uint8)
+ qh = np.empty((n_blocks, 64), dtype=np.uint8)
+
+ for group in range(2):
+ base_q = group * 4
+ base_ql = group * 64
+ base_qh = group * 32
+
+ ql[:, base_ql : base_ql + 32] = (
+ (q_raw[:, base_q + 0, :] & np.uint8(0x0F))
+ | ((q_raw[:, base_q + 2, :] & np.uint8(0x0F)) << np.uint8(4))
+ )
+ ql[:, base_ql + 32 : base_ql + 64] = (
+ (q_raw[:, base_q + 1, :] & np.uint8(0x0F))
+ | ((q_raw[:, base_q + 3, :] & np.uint8(0x0F)) << np.uint8(4))
+ )
+
+ qh[:, base_qh : base_qh + 32] = (
+ ((q_raw[:, base_q + 0, :] >> np.uint8(4)) & np.uint8(0x03))
+ | (((q_raw[:, base_q + 1, :] >> np.uint8(4)) & np.uint8(0x03)) << np.uint8(2))
+ | (((q_raw[:, base_q + 2, :] >> np.uint8(4)) & np.uint8(0x03)) << np.uint8(4))
+ | (((q_raw[:, base_q + 3, :] >> np.uint8(4)) & np.uint8(0x03)) << np.uint8(6))
+ )
+
+ return np.concatenate(
+ [
+ ql,
+ qh,
+ scales.view(np.uint8),
+ d.view(np.uint8),
+ ],
+ axis=-1,
+ )
+
+
+def _fallback_gguf_quantize(weight: np.ndarray, tensor_qtype: str) -> np.ndarray:
+ if weight.ndim != 2:
+ raise ValueError(f"GGUF quantization expects a 2D weight matrix, got shape {weight.shape}.")
+ qtype_info = _GGUF_TYPE_INFO[tensor_qtype]
+ block_size = qtype_info["block_size"]
+ if weight.shape[1] % block_size != 0:
+ raise ValueError(
+ f"GGUF quantization expects the input dimension to be divisible by {block_size}, got {weight.shape[1]}."
+ )
+
+ blocks = weight.reshape(-1, block_size)
+ if tensor_qtype == "Q4_0":
+ quantized_blocks = _gguf_quantize_q4_0(blocks)
+ elif tensor_qtype == "Q8_0":
+ quantized_blocks = _gguf_quantize_q8_0(blocks)
+ elif tensor_qtype == "Q4_K":
+ quantized_blocks = _gguf_quantize_q4_k(blocks)
+ elif tensor_qtype == "Q5_K":
+ quantized_blocks = _gguf_quantize_q5_k(blocks)
+ elif tensor_qtype == "Q6_K":
+ quantized_blocks = _gguf_quantize_q6_k(blocks)
+ else: # pragma: no cover - guarded by class SUPPORTS_BITS
+ raise NotImplementedError(f"Unsupported GGUF qtype: {tensor_qtype}")
+
+ bytes_per_block = qtype_info["type_size"]
+ rows = weight.shape[0]
+ return quantized_blocks.reshape(rows, (weight.shape[1] // block_size) * bytes_per_block)
+
+
+def _gguf_quantize(weight: np.ndarray, tensor_qtype: str) -> np.ndarray:
+ if _GGUF_AVAILABLE:
+ qtype = getattr(gguf_lib.GGMLQuantizationType, tensor_qtype)
+ try:
+ return gguf_lib.quantize(weight, qtype)
+ except NotImplementedError:
+ pass
+ return _fallback_gguf_quantize(weight, tensor_qtype)
+
+
+def _dequantize_q4_k_numpy(qweight: np.ndarray) -> np.ndarray:
+ rows = qweight.shape[0]
+ type_size = _GGUF_TYPE_INFO["Q4_K"]["type_size"]
+ blocks = qweight.reshape(-1, type_size)
+
+ d = blocks[:, :2].view(np.float16).astype(np.float32)
+ dmin = blocks[:, 2:4].view(np.float16).astype(np.float32)
+ scales = blocks[:, 4:16]
+ qs = blocks[:, 16:]
+
+ sc, mins = _unpack_q4_k_scale_min(scales)
+ d = (d * sc.astype(np.float32)).reshape((-1, 8, 1))
+ dm = (dmin * mins.astype(np.float32)).reshape((-1, 8, 1))
+
+ q = qs.reshape((-1, 4, 1, 32)) >> np.array([0, 4], dtype=np.uint8).reshape((1, 1, 2, 1))
+ q = (q & np.uint8(0x0F)).reshape((-1, 8, 32)).astype(np.float32)
+
+ return (d * q - dm).reshape(rows, -1)
+
+
+def _dequantize_q5_k_numpy(qweight: np.ndarray) -> np.ndarray:
+ rows = qweight.shape[0]
+ type_size = _GGUF_TYPE_INFO["Q5_K"]["type_size"]
+ blocks = qweight.reshape(-1, type_size)
+
+ d = blocks[:, :2].view(np.float16).astype(np.float32)
+ dmin = blocks[:, 2:4].view(np.float16).astype(np.float32)
+ scales = blocks[:, 4:16]
+ qh = blocks[:, 16:48]
+ qs = blocks[:, 48:]
+
+ sc, mins = _unpack_q4_k_scale_min(scales)
+ d = (d * sc.astype(np.float32)).reshape((-1, 8, 1))
+ dm = (dmin * mins.astype(np.float32)).reshape((-1, 8, 1))
+
+ ql = qs.reshape((-1, 4, 1, 32)) >> np.array([0, 4], dtype=np.uint8).reshape((1, 1, 2, 1))
+ qh = qh.reshape((-1, 1, 1, 32)) >> np.arange(8, dtype=np.uint8).reshape((1, 1, 8, 1))
+
+ ql = (ql & np.uint8(0x0F)).reshape((-1, 8, 32))
+ qh = (qh & np.uint8(0x01)).reshape((-1, 8, 32))
+ q = (ql | (qh << np.uint8(4))).astype(np.float32)
+
+ return (d * q - dm).reshape(rows, -1)
+
+
+def _dequantize_q6_k_numpy(qweight: np.ndarray) -> np.ndarray:
+ rows = qweight.shape[0]
+ type_size = _GGUF_TYPE_INFO["Q6_K"]["type_size"]
+ blocks = qweight.reshape(-1, type_size)
+
+ ql = blocks[:, :128]
+ qh = blocks[:, 128:192]
+ scales = blocks[:, 192:208].view(np.int8).astype(np.float32)
+ d = blocks[:, 208:210].view(np.float16).astype(np.float32)
+ d = (d * scales).reshape((-1, 16, 1))
+
+ ql = ql.reshape((-1, 2, 1, 64)) >> np.array([0, 4], dtype=np.uint8).reshape((1, 1, 2, 1))
+ ql = (ql & np.uint8(0x0F)).reshape((-1, 8, 32))
+ qh = qh.reshape((-1, 2, 1, 32)) >> np.array([0, 2, 4, 6], dtype=np.uint8).reshape((1, 1, 4, 1))
+ qh = (qh & np.uint8(0x03)).reshape((-1, 8, 32))
+
+ q = (ql | (qh << np.uint8(4))).astype(np.int16) - 32
+ q = q.reshape((-1, 16, 16)).astype(np.float32)
+
+ return (d * q).reshape(rows, -1)
+
+
+class GGUFTorchQuantLinear(BaseQuantLinear):
+ SUPPORTS_BACKENDS = [BACKEND.GGUF_TORCH]
+ SUPPORTS_METHODS = [METHOD.GGUF]
+ SUPPORTS_FORMATS = {FORMAT.GGUF: 15}
+ SUPPORTS_BITS = [4, 5, 6, 8]
+ SUPPORTS_GROUP_SIZE = [-1]
+ SUPPORTS_DESC_ACT = [False]
+ SUPPORTS_SYM = [True]
+ SUPPORTS_SHARDS = True
+ SUPPORTS_TRAINING = True
+ SUPPORTS_AUTO_PADDING = True
+ SUPPORTS_IN_FEATURES_DIVISIBLE_BY = [1]
+ SUPPORTS_OUT_FEATURES_DIVISIBLE_BY = [1]
+
+ SUPPORTS_DEVICES = [DEVICE.ALL]
+ SUPPORTS_PLATFORM = [PLATFORM.ALL]
+ SUPPORTS_PACK_DTYPES = [torch.int8, torch.int16, torch.int32]
+ SUPPORTS_ADAPTERS = [Lora]
+
+ SUPPORTS_DTYPES = [torch.float16, torch.bfloat16, torch.float32]
+
+ REQUIRES_FORMAT_V2 = False
+ AUTOTUNE = True
+
+ QUANT_TYPE = "gguf"
+ GGUF_FUSED_CUDA_MAX_ROWS = max(0, int(os.environ.get("GPTQMODEL_GGUF_FUSED_CUDA_MAX_ROWS", "32")))
+ GGUF_FUSED_CUDA_MIN_MATRIX_ELEMENTS = max(
+ 0,
+ int(os.environ.get("GPTQMODEL_GGUF_FUSED_CUDA_MIN_MATRIX_ELEMENTS", "8388608")),
+ )
+ GGUF_FUSED_CPU_MAX_ROWS = max(0, int(os.environ.get("GPTQMODEL_GGUF_FUSED_CPU_MAX_ROWS", "64")))
+ GGUF_FUSED_CPU_MIN_MATRIX_ELEMENTS = max(
+ 0,
+ int(os.environ.get("GPTQMODEL_GGUF_FUSED_CPU_MIN_MATRIX_ELEMENTS", "0")),
+ )
+ GGUF_FUSED_CHUNK_BLOCKS = max(1, int(os.environ.get("GPTQMODEL_GGUF_FUSED_CHUNK_BLOCKS", "8")))
+ GGUF_FUSED_AUTOTUNE_WARMUP = max(0, int(os.environ.get("GPTQMODEL_GGUF_FUSED_AUTOTUNE_WARMUP", "1")))
+ GGUF_FUSED_AUTOTUNE_ITERS = max(1, int(os.environ.get("GPTQMODEL_GGUF_FUSED_AUTOTUNE_ITERS", "2")))
+ GGUF_FUSED_AUTOTUNE_MARGIN = max(0.0, float(os.environ.get("GPTQMODEL_GGUF_FUSED_AUTOTUNE_MARGIN", "0.05")))
+
+ def __init__(
+ self,
+ bits,
+ group_size: int,
+ sym: bool,
+ desc_act: bool,
+ in_features: int,
+ out_features: int,
+ bias: bool = False,
+ pack_dtype: torch.dtype = torch.int32,
+ adapter: Adapter = None,
+ register_buffers: bool = True,
+ **kwargs,
+ ):
+ bits_spec, self.gguf_tensor_qtype = _normalize_gguf_bits(bits)
+ qtype_info = _GGUF_TYPE_INFO[self.gguf_tensor_qtype]
+ self.gguf_block_size = qtype_info["block_size"]
+ self.gguf_type_size = qtype_info["type_size"]
+ self.padded_in_features = math.ceil(in_features / self.gguf_block_size) * self.gguf_block_size
+ self.gguf_fused_cuda_max_rows = self.GGUF_FUSED_CUDA_MAX_ROWS
+ self.gguf_fused_cuda_min_matrix_elements = self.GGUF_FUSED_CUDA_MIN_MATRIX_ELEMENTS
+ self.gguf_fused_cpu_max_rows = self.GGUF_FUSED_CPU_MAX_ROWS
+ self.gguf_fused_cpu_min_matrix_elements = self.GGUF_FUSED_CPU_MIN_MATRIX_ELEMENTS
+ self.gguf_fused_chunk_blocks = self.GGUF_FUSED_CHUNK_BLOCKS
+ self.gguf_fused_autotune_warmup = self.GGUF_FUSED_AUTOTUNE_WARMUP
+ self.gguf_fused_autotune_iters = self.GGUF_FUSED_AUTOTUNE_ITERS
+ self.gguf_fused_autotune_margin = self.GGUF_FUSED_AUTOTUNE_MARGIN
+
+ super().__init__(
+ bits=int(bits_spec),
+ group_size=group_size,
+ sym=sym,
+ desc_act=desc_act,
+ in_features=in_features,
+ out_features=out_features,
+ bias=bias,
+ pack_dtype=pack_dtype,
+ backend=kwargs.pop("backend", BACKEND.GGUF_TORCH),
+ adapter=adapter,
+ register_buffers=False,
+ **kwargs,
+ )
+
+ self.group_size = -1
+ self.bits = bits_spec
+
+ if register_buffers:
+ self._allocate_buffers(bias=bias)
+
+ def _bytes_per_row(self) -> int:
+ return (self.padded_in_features // self.gguf_block_size) * self.gguf_type_size
+
+ def _allocate_buffers(self, *, bias: bool) -> None:
+ bytes_per_row = self._bytes_per_row()
+ qweight = torch.zeros((self.out_features, bytes_per_row), dtype=torch.uint8)
+ if "qweight" in self._buffers:
+ self.qweight = qweight
+ else:
+ self.register_buffer("qweight", qweight)
+
+ if bias:
+ bias_tensor = torch.zeros(self.out_features, dtype=torch.float16)
+ if "bias" in self._buffers:
+ self.bias = bias_tensor
+ else:
+ self.register_buffer("bias", bias_tensor)
+ else:
+ self.bias = None
+
+ def clear_weight_cache(self) -> None:
+ return None
+
+ def post_init(self):
+ self.clear_weight_cache()
+ super().post_init()
+
+ def train(self, mode: bool = True):
+ self.clear_weight_cache()
+ return super().train(mode=mode)
+
+ def extra_repr(self) -> str:
+ return (
+ f"in_features={self.in_features}, out_features={self.out_features}, "
+ f"bias={self.bias is not None}, bits={self.bits}"
+ )
+
+ def _weight_to_matrix(self, linear: nn.Module) -> torch.Tensor:
+ weight = linear.weight.detach()
+ if isinstance(linear, _ConvNd):
+ weight = weight.flatten(1)
+ if isinstance(linear, transformers.pytorch_utils.Conv1D):
+ weight = weight.T
+ return weight
+
+ def _pack_weight_tensor(
+ self,
+ linear: nn.Module,
+ *,
+ smooth: SmoothMethod | None = None,
+ ) -> torch.Tensor:
+ weight = self._weight_to_matrix(linear).to(device="cpu", dtype=torch.float32)
+ weight = _apply_optional_smoother(
+ weight,
+ smooth=smooth,
+ group_size=self.group_size,
+ )
+ if weight.shape[1] != self.padded_in_features:
+ weight = torch.nn.functional.pad(weight, (0, self.padded_in_features - weight.shape[1]))
+
+ quantized = _gguf_quantize(weight.contiguous().numpy(), self.gguf_tensor_qtype)
+ return torch.from_numpy(np.ascontiguousarray(quantized)).to(torch.uint8)
+
+ def pack(self, linear: nn.Module, scales: torch.Tensor, zeros: torch.Tensor, g_idx: torch.Tensor = None):
+ self.pack_original(linear=linear, scales=scales, zeros=zeros, g_idx=g_idx)
+
+ def pack_block(
+ self,
+ linear: nn.Module,
+ scales: torch.Tensor,
+ zeros: torch.Tensor,
+ g_idx: torch.Tensor = None,
+ block_in: int = 8192,
+ workers: int = 1,
+ ):
+ del block_in, workers
+ self.pack_original(linear=linear, scales=scales, zeros=zeros, g_idx=g_idx)
+
+ def pack_gpu(
+ self,
+ linear: nn.Module,
+ scales: torch.Tensor,
+ zeros: torch.Tensor,
+ g_idx: torch.Tensor = None,
+ *,
+ block_in: int = 8192,
+ device: torch.device | None = None,
+ ):
+ del block_in, device
+ self.pack_original(linear=linear, scales=scales, zeros=zeros, g_idx=g_idx)
+
+ @torch.inference_mode()
+ def pack_original(
+ self,
+ linear: nn.Module,
+ scales: torch.Tensor,
+ zeros: torch.Tensor,
+ g_idx: torch.Tensor = None,
+ *,
+ smooth: SmoothMethod | None = None,
+ ):
+ del scales, zeros, g_idx
+
+ qweight = self._pack_weight_tensor(linear, smooth=smooth)
+ expected_shape = (self.out_features, self._bytes_per_row())
+ if tuple(qweight.shape) != expected_shape:
+ raise RuntimeError(
+ f"{self.__class__.__name__} produced an invalid GGUF packed shape {tuple(qweight.shape)}; "
+ f"expected {expected_shape} for padded_in_features={self.padded_in_features}."
+ )
+ if "qweight" in self._buffers:
+ self.qweight = qweight
+ else:
+ self.register_buffer("qweight", qweight)
+
+ if linear.bias is not None:
+ bias = linear.bias.detach().to(device="cpu", dtype=torch.float16)
+ if "bias" in self._buffers:
+ self.bias = bias
+ else:
+ self.register_buffer("bias", bias)
+ else:
+ self.bias = None
+
+ self.clear_autotune()
+ self.clear_weight_cache()
+
+ def _resolve_dequant_target(
+ self,
+ *,
+ device: torch.device | str | None,
+ dtype: torch.dtype | None,
+ ) -> tuple[torch.device, torch.dtype]:
+ target_device = self.qweight.device if device is None else torch.device(device)
+ target_dtype = torch.float32 if dtype is None else dtype
+ if target_dtype not in self.SUPPORTS_DTYPES:
+ supported = ", ".join(str(dt).removeprefix("torch.") for dt in self.SUPPORTS_DTYPES)
+ raise ValueError(
+ f"{self.__class__.__name__} only supports GGUF dequantization dtypes {{{supported}}}, got `{target_dtype}`."
+ )
+ return target_device, target_dtype
+
+ def _reshape_blocks(
+ self,
+ *,
+ device: torch.device | str | None = None,
+ ) -> tuple[torch.Tensor, int, int]:
+ target_device = self.qweight.device if device is None else torch.device(device)
+ qweight = self.qweight if self.qweight.device == target_device else self.qweight.to(device=target_device)
+ rows = qweight.shape[0]
+ num_blocks = qweight.shape[1] // self.gguf_type_size
+ blocks = qweight.contiguous().view(rows, num_blocks, self.gguf_type_size)
+ return blocks, rows, num_blocks
+
+ @staticmethod
+ def _u8_shift(values: tuple[int, ...], device: torch.device) -> torch.Tensor:
+ return torch.tensor(values, dtype=torch.uint8, device=device)
+
+ def _dequantize_q4_0(
+ self,
+ *,
+ device: torch.device | str | None = None,
+ dtype: torch.dtype | None = None,
+ ) -> torch.Tensor:
+ target_device, target_dtype = self._resolve_dequant_target(device=device, dtype=dtype)
+ blocks, rows, _ = self._reshape_blocks(device=target_device)
+
+ d = blocks[..., :2].contiguous().view(torch.float16).squeeze(-1)
+ if d.dtype != target_dtype:
+ d = d.to(target_dtype)
+
+ qs = blocks[..., 2:]
+ low = torch.bitwise_and(qs, 0x0F)
+ high = torch.bitwise_right_shift(qs, 4)
+ values = torch.cat((low, high), dim=-1).to(torch.int16) - 8
+
+ weight = d.unsqueeze(-1) * values.to(target_dtype)
+ return weight.reshape(rows, self.padded_in_features)
+
+ def _dequantize_q8_0(
+ self,
+ *,
+ device: torch.device | str | None = None,
+ dtype: torch.dtype | None = None,
+ ) -> torch.Tensor:
+ target_device, target_dtype = self._resolve_dequant_target(device=device, dtype=dtype)
+ blocks, rows, _ = self._reshape_blocks(device=target_device)
+
+ d = blocks[..., :2].contiguous().view(torch.float16).squeeze(-1)
+ if d.dtype != target_dtype:
+ d = d.to(target_dtype)
+
+ x = blocks[..., 2:].contiguous().view(torch.int8).to(target_dtype)
+
+ weight = d.unsqueeze(-1) * x
+ return weight.reshape(rows, self.padded_in_features)
+
+ def _dequantize_numpy(
+ self,
+ fn,
+ *,
+ device: torch.device | str | None = None,
+ dtype: torch.dtype | None = None,
+ ) -> torch.Tensor:
+ target_device, target_dtype = self._resolve_dequant_target(device=device, dtype=dtype)
+ qweight = self.qweight.detach().cpu().numpy()
+ weight = fn(qweight)
+ tensor = torch.from_numpy(np.ascontiguousarray(weight))
+ if tensor.device != target_device or tensor.dtype != target_dtype:
+ tensor = tensor.to(device=target_device, dtype=target_dtype)
+ return tensor
+
+ def _dequantize_q4_k_blocks(self, blocks: torch.Tensor, target_dtype: torch.dtype) -> torch.Tensor:
+ rows, num_blocks = blocks.shape[0], blocks.shape[1]
+
+ d = blocks[..., :2].contiguous().view(torch.float16).squeeze(-1).to(target_dtype)
+ dmin = blocks[..., 2:4].contiguous().view(torch.float16).squeeze(-1).to(target_dtype)
+ scales = blocks[..., 4:16]
+ qs = blocks[..., 16:]
+
+ sc, mins = _unpack_q4_k_scale_min_torch(scales)
+ d = d.unsqueeze(-1) * sc.to(target_dtype)
+ dm = dmin.unsqueeze(-1) * mins.to(target_dtype)
+
+ q = qs.reshape(rows, num_blocks, 4, 1, 32)
+ q = torch.bitwise_right_shift(
+ q,
+ self._u8_shift((0, 4), device=blocks.device).view(1, 1, 1, 2, 1),
+ )
+ q = torch.bitwise_and(q, 0x0F).reshape(rows, num_blocks, 8, 32)
+
+ return (d.unsqueeze(-1) * q.to(target_dtype) - dm.unsqueeze(-1)).reshape(rows, num_blocks * 256)
+
+ def _dequantize_q5_k_blocks(self, blocks: torch.Tensor, target_dtype: torch.dtype) -> torch.Tensor:
+ rows, num_blocks = blocks.shape[0], blocks.shape[1]
+
+ d = blocks[..., :2].contiguous().view(torch.float16).squeeze(-1).to(target_dtype)
+ dmin = blocks[..., 2:4].contiguous().view(torch.float16).squeeze(-1).to(target_dtype)
+ scales = blocks[..., 4:16]
+ qh = blocks[..., 16:48]
+ qs = blocks[..., 48:]
+
+ sc, mins = _unpack_q4_k_scale_min_torch(scales)
+ d = d.unsqueeze(-1) * sc.to(target_dtype)
+ dm = dmin.unsqueeze(-1) * mins.to(target_dtype)
+
+ ql = qs.reshape(rows, num_blocks, 4, 1, 32)
+ ql = torch.bitwise_right_shift(
+ ql,
+ self._u8_shift((0, 4), device=blocks.device).view(1, 1, 1, 2, 1),
+ )
+ ql = torch.bitwise_and(ql, 0x0F).reshape(rows, num_blocks, 8, 32)
+
+ qh = torch.bitwise_right_shift(
+ qh.unsqueeze(-2),
+ self._u8_shift(tuple(range(8)), device=blocks.device).view(1, 1, 8, 1),
+ )
+ qh = torch.bitwise_and(qh, 0x01).reshape(rows, num_blocks, 8, 32)
+ q = torch.bitwise_or(ql, torch.bitwise_left_shift(qh, 4))
+
+ return (d.unsqueeze(-1) * q.to(target_dtype) - dm.unsqueeze(-1)).reshape(rows, num_blocks * 256)
+
+ def _dequantize_q6_k_blocks(self, blocks: torch.Tensor, target_dtype: torch.dtype) -> torch.Tensor:
+ rows, num_blocks = blocks.shape[0], blocks.shape[1]
+
+ ql = blocks[..., :128]
+ qh = blocks[..., 128:192]
+ scales = blocks[..., 192:208].contiguous().view(torch.int8).to(target_dtype)
+ d = blocks[..., 208:210].contiguous().view(torch.float16).squeeze(-1).to(target_dtype)
+ d = (d.unsqueeze(-1) * scales).reshape(rows, num_blocks, 16, 1)
+
+ ql = ql.reshape(rows, num_blocks, 2, 1, 64)
+ ql = torch.bitwise_right_shift(
+ ql,
+ self._u8_shift((0, 4), device=blocks.device).view(1, 1, 1, 2, 1),
+ )
+ ql = torch.bitwise_and(ql, 0x0F).reshape(rows, num_blocks, 8, 32)
+
+ qh = qh.reshape(rows, num_blocks, 2, 1, 32)
+ qh = torch.bitwise_right_shift(
+ qh,
+ self._u8_shift((0, 2, 4, 6), device=blocks.device).view(1, 1, 1, 4, 1),
+ )
+ qh = torch.bitwise_and(qh, 0x03).reshape(rows, num_blocks, 8, 32)
+
+ q = torch.bitwise_or(ql, torch.bitwise_left_shift(qh, 4)).to(torch.int16) - 32
+ q = q.reshape(rows, num_blocks, 16, 16).to(target_dtype)
+
+ return (d * q).reshape(rows, num_blocks * 256)
+
+ def dequantize_weight(
+ self,
+ *,
+ device: torch.device | str | None = None,
+ dtype: torch.dtype | None = None,
+ ) -> torch.Tensor:
+ if self.gguf_tensor_qtype == "Q4_0":
+ weight = self._dequantize_q4_0(device=device, dtype=dtype)
+ elif self.gguf_tensor_qtype == "Q8_0":
+ weight = self._dequantize_q8_0(device=device, dtype=dtype)
+ elif self.gguf_tensor_qtype == "Q4_K":
+ target_device, target_dtype = self._resolve_dequant_target(device=device, dtype=dtype)
+ blocks, _, _ = self._reshape_blocks(device=target_device)
+ weight = self._dequantize_q4_k_blocks(blocks, target_dtype)
+ elif self.gguf_tensor_qtype == "Q5_K":
+ target_device, target_dtype = self._resolve_dequant_target(device=device, dtype=dtype)
+ blocks, _, _ = self._reshape_blocks(device=target_device)
+ weight = self._dequantize_q5_k_blocks(blocks, target_dtype)
+ elif self.gguf_tensor_qtype == "Q6_K":
+ target_device, target_dtype = self._resolve_dequant_target(device=device, dtype=dtype)
+ blocks, _, _ = self._reshape_blocks(device=target_device)
+ weight = self._dequantize_q6_k_blocks(blocks, target_dtype)
+ else: # pragma: no cover - guarded by class SUPPORTS_BITS
+ raise NotImplementedError(f"Unsupported GGUF qtype: {self.gguf_tensor_qtype}")
+
+ return weight[:, : self.in_features].transpose(0, 1).contiguous()
+
+ def _is_fused_k_forward_candidate(self, x_flat: torch.Tensor) -> bool:
+ if x_flat.device.type == "cuda":
+ max_rows = self.gguf_fused_cuda_max_rows
+ min_matrix_elements = self.gguf_fused_cuda_min_matrix_elements
+ elif x_flat.device.type == "cpu":
+ max_rows = self.gguf_fused_cpu_max_rows
+ min_matrix_elements = self.gguf_fused_cpu_min_matrix_elements
+ else:
+ return False
+
+ return (
+ self.gguf_tensor_qtype in _GGUF_K_QTYPES
+ and self.adapter is None
+ and not self.training
+ and max_rows > 0
+ and (self.in_features * self.out_features) >= min_matrix_elements
+ and x_flat.shape[0] <= max_rows
+ )
+
+ @staticmethod
+ def _sync_benchmark_device(device: torch.device) -> None:
+ if device.type == "cuda":
+ torch.cuda.synchronize(device=device)
+
+ def _benchmark_forward_runner(self, fn, *, device: torch.device) -> float:
+ with torch.inference_mode():
+ for _ in range(self.gguf_fused_autotune_warmup):
+ fn()
+ self._sync_benchmark_device(device)
+
+ start = time.perf_counter()
+ for _ in range(self.gguf_fused_autotune_iters):
+ fn()
+ self._sync_benchmark_device(device)
+
+ return (time.perf_counter() - start) / self.gguf_fused_autotune_iters
+
+ def _benchmark_dense_forward(self, x_flat: torch.Tensor) -> float:
+ return self._benchmark_forward_runner(
+ lambda: self._forward_dequant_matmul(x_flat),
+ device=x_flat.device,
+ )
+
+ def _benchmark_fused_forward(self, x_flat: torch.Tensor) -> float:
+ return self._benchmark_forward_runner(
+ lambda: self._forward_fused_k(x_flat),
+ device=x_flat.device,
+ )
+
+ def _autotune(self, x_flat: torch.Tensor) -> bool:
+ try:
+ fused_time = self._benchmark_fused_forward(x_flat)
+ dense_time = self._benchmark_dense_forward(x_flat)
+ return fused_time <= dense_time * (1.0 - self.gguf_fused_autotune_margin)
+ except Exception:
+ return False
+
+ def _should_use_fused_k_forward(self, x_flat: torch.Tensor) -> bool:
+ if not self._is_fused_k_forward_candidate(x_flat):
+ return False
+
+ if not self.autotune_enabled:
+ return True
+
+ return bool(self.maybe_autotune(x_flat))
+
+ def _forward_dequant_matmul(self, x_flat: torch.Tensor) -> torch.Tensor:
+ weight = self.dequantize_weight(device=x_flat.device, dtype=x_flat.dtype)
+ return torch.matmul(x_flat, weight)
+
+ def _forward_fused_k(self, x_flat: torch.Tensor) -> torch.Tensor:
+ target_dtype = x_flat.dtype
+ blocks, _, num_blocks = self._reshape_blocks(device=x_flat.device)
+
+ if x_flat.shape[-1] != self.padded_in_features:
+ x_work = F.pad(x_flat, (0, self.padded_in_features - x_flat.shape[-1]))
+ else:
+ x_work = x_flat
+
+ output = torch.zeros((x_flat.shape[0], self.out_features), device=x_flat.device, dtype=target_dtype)
+
+ for start in range(0, num_blocks, self.gguf_fused_chunk_blocks):
+ end = min(start + self.gguf_fused_chunk_blocks, num_blocks)
+ block_chunk = blocks[:, start:end, :]
+
+ if self.gguf_tensor_qtype == "Q4_K":
+ weight_chunk = self._dequantize_q4_k_blocks(block_chunk, target_dtype)
+ elif self.gguf_tensor_qtype == "Q5_K":
+ weight_chunk = self._dequantize_q5_k_blocks(block_chunk, target_dtype)
+ elif self.gguf_tensor_qtype == "Q6_K":
+ weight_chunk = self._dequantize_q6_k_blocks(block_chunk, target_dtype)
+ else: # pragma: no cover - guarded by _should_use_fused_k_forward
+ raise NotImplementedError(f"Unsupported GGUF fused qtype: {self.gguf_tensor_qtype}")
+
+ x_chunk = x_work[:, start * self.gguf_block_size : end * self.gguf_block_size]
+ output = output + torch.matmul(x_chunk, weight_chunk.transpose(0, 1))
+
+ return output
+
+ def forward(self, x: torch.Tensor):
+ original_shape = x.shape[:-1] + (self.out_features,)
+ x_flat = x.reshape(-1, x.shape[-1])
+
+ if self._should_use_fused_k_forward(x_flat):
+ output = self._forward_fused_k(x_flat)
+ else:
+ output = self._forward_dequant_matmul(x_flat)
+
+ if self.bias is not None:
+ bias = self.bias
+ if bias.device != output.device or bias.dtype != output.dtype:
+ bias = bias.to(device=output.device, dtype=output.dtype)
+ output = output + bias
+
+ if self.adapter:
+ output = self.adapter.apply(x=x_flat, out=output)
+
+ return output.reshape(original_shape)
+
+
+__all__ = ["GGUFTorchQuantLinear"]
diff --git a/gptqmodel/nn_modules/qlinear/gguf_cpp.py b/gptqmodel/nn_modules/qlinear/gguf_cpp.py
new file mode 100644
index 000000000..03d92d261
--- /dev/null
+++ b/gptqmodel/nn_modules/qlinear/gguf_cpp.py
@@ -0,0 +1,773 @@
+# SPDX-FileCopyrightText: 2026 ModelCloud.ai
+# SPDX-License-Identifier: Apache-2.0
+
+from __future__ import annotations
+
+import ctypes
+import os
+from pathlib import Path
+from typing import Dict, Optional, Tuple
+
+import torch
+import torch.nn.functional as F
+
+from ...adapter.adapter import Adapter, Lora
+from ...models._const import DEVICE, PLATFORM
+from ...quantization.config import FORMAT, METHOD
+from ...utils.backend import BACKEND
+from .gguf import GGUFTorchQuantLinear
+
+
+try:
+ import llama_cpp as _llama_cpp_pkg
+ from llama_cpp import llama_cpp as _llama_cpp_lib
+ from llama_cpp._ctypes_extensions import load_shared_library as _llama_cpp_load_shared_library
+
+ _LLAMA_CPP_IMPORT_ERROR = None
+except Exception as exc: # pragma: no cover - optional dependency
+ _llama_cpp_pkg = None
+ _llama_cpp_lib = None
+ _llama_cpp_load_shared_library = None
+ _LLAMA_CPP_IMPORT_ERROR = exc
+
+
+class _GGMLInitParams(ctypes.Structure):
+ _fields_ = [
+ ("mem_size", ctypes.c_size_t),
+ ("mem_buffer", ctypes.c_void_p),
+ ("no_alloc", ctypes.c_bool),
+ ]
+
+
+class _GGMLMatmulPlan:
+ def __init__(
+ self,
+ *,
+ ctx,
+ buffer,
+ weight_tensor,
+ input_tensor,
+ output_tensor,
+ graph,
+ rows: int,
+ out_features: int,
+ output_nbytes: int,
+ output_dtype: torch.dtype,
+ backend_buffer_free,
+ ctx_free,
+ ) -> None:
+ self.ctx = ctx
+ self.buffer = buffer
+ self.weight_tensor = weight_tensor
+ self.input_tensor = input_tensor
+ self.output_tensor = output_tensor
+ self.graph = graph
+ self.rows = rows
+ self.out_features = out_features
+ self.output_nbytes = output_nbytes
+ self.output_dtype = output_dtype
+ self._backend_buffer_free = backend_buffer_free
+ self._ctx_free = ctx_free
+
+ def close(self) -> None:
+ if self.buffer:
+ self._backend_buffer_free(self.buffer)
+ self.buffer = None
+ if self.ctx:
+ self._ctx_free(self.ctx)
+ self.ctx = None
+
+ def __del__(self) -> None: # pragma: no cover - best effort cleanup
+ try:
+ self.close()
+ except Exception:
+ # Destructors must not raise during GC or interpreter shutdown.
+ pass
+
+
+class _GGMLBridge:
+ GGML_METADATA_BYTES = 1 << 20
+
+ def __init__(self) -> None:
+ if _LLAMA_CPP_IMPORT_ERROR is not None:
+ raise ModuleNotFoundError(
+ "GGUFCppKernel requires `llama-cpp-python` to be installed."
+ ) from _LLAMA_CPP_IMPORT_ERROR
+
+ lib_dir = Path(_llama_cpp_pkg.__file__).resolve().parent / "lib"
+ self._ggml_base = _llama_cpp_load_shared_library("ggml-base", lib_dir)
+ self._ggml_cpu = _llama_cpp_load_shared_library("ggml-cpu", lib_dir)
+ self._ggml_cuda = None
+ self._ggml_cuda_error: Optional[Exception] = None
+ try:
+ self._ggml_cuda = _llama_cpp_load_shared_library("ggml-cuda", lib_dir)
+ except Exception as exc: # pragma: no cover - optional shared library
+ self._ggml_cuda_error = exc
+ self._bind_functions()
+
+ self.ggml_type_f32 = int(_llama_cpp_lib.GGML_TYPE_F32)
+ self.ggml_type_f16 = int(_llama_cpp_lib.GGML_TYPE_F16)
+ self.ggml_qtypes = {
+ "Q4_0": int(_llama_cpp_lib.GGML_TYPE_Q4_0),
+ "Q8_0": int(_llama_cpp_lib.GGML_TYPE_Q8_0),
+ "Q4_K": int(_llama_cpp_lib.GGML_TYPE_Q4_K),
+ "Q5_K": int(_llama_cpp_lib.GGML_TYPE_Q5_K),
+ "Q6_K": int(_llama_cpp_lib.GGML_TYPE_Q6_K),
+ }
+ self._cpu_backend: Optional[int] = None
+ self._cuda_backends: Dict[int, int] = {}
+
+ def _bind(self, lib, name: str, argtypes, restype) -> None:
+ fn = getattr(lib, name)
+ fn.argtypes = argtypes
+ fn.restype = restype
+
+ def _bind_functions(self) -> None:
+ self._bind(self._ggml_base, "ggml_init", [_GGMLInitParams], ctypes.c_void_p)
+ self._bind(self._ggml_base, "ggml_free", [ctypes.c_void_p], None)
+ self._bind(
+ self._ggml_base,
+ "ggml_new_tensor_2d",
+ [ctypes.c_void_p, ctypes.c_int, ctypes.c_int64, ctypes.c_int64],
+ ctypes.c_void_p,
+ )
+ self._bind(self._ggml_base, "ggml_mul_mat", [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p], ctypes.c_void_p)
+ self._bind(self._ggml_base, "ggml_set_input", [ctypes.c_void_p], None)
+ self._bind(self._ggml_base, "ggml_new_graph", [ctypes.c_void_p], ctypes.c_void_p)
+ self._bind(self._ggml_base, "ggml_build_forward_expand", [ctypes.c_void_p, ctypes.c_void_p], None)
+ self._bind(self._ggml_base, "ggml_nbytes", [ctypes.c_void_p], ctypes.c_size_t)
+ self._bind(self._ggml_base, "ggml_element_size", [ctypes.c_void_p], ctypes.c_size_t)
+ self._bind(self._ggml_base, "ggml_backend_alloc_ctx_tensors", [ctypes.c_void_p, ctypes.c_void_p], ctypes.c_void_p)
+ self._bind(self._ggml_base, "ggml_backend_buffer_free", [ctypes.c_void_p], None)
+ self._bind(self._ggml_base, "ggml_backend_free", [ctypes.c_void_p], None)
+ self._bind(
+ self._ggml_base,
+ "ggml_backend_tensor_set",
+ [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_size_t],
+ None,
+ )
+ self._bind(
+ self._ggml_base,
+ "ggml_backend_tensor_get",
+ [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_size_t],
+ None,
+ )
+ self._bind(self._ggml_base, "ggml_backend_graph_compute", [ctypes.c_void_p, ctypes.c_void_p], ctypes.c_int)
+ self._bind(self._ggml_base, "ggml_backend_synchronize", [ctypes.c_void_p], None)
+ self._bind(self._ggml_cpu, "ggml_backend_cpu_init", [], ctypes.c_void_p)
+ self._bind(self._ggml_cpu, "ggml_backend_cpu_set_n_threads", [ctypes.c_void_p, ctypes.c_int], None)
+ if self._ggml_cuda is not None:
+ self._bind(self._ggml_cuda, "ggml_backend_cuda_init", [ctypes.c_int], ctypes.c_void_p)
+ self._bind(self._ggml_cuda, "ggml_backend_cuda_get_device_count", [], ctypes.c_int)
+
+ @staticmethod
+ def _cpu_threads() -> int:
+ override = os.environ.get("GPTQMODEL_GGUF_CPP_THREADS")
+ if override is not None:
+ try:
+ return max(1, int(override))
+ except ValueError:
+ pass
+ return max(1, torch.get_num_threads())
+
+ def cpu_available(self) -> Tuple[bool, Optional[Exception]]:
+ try:
+ self._get_cpu_backend()
+ except Exception as exc:
+ return False, exc
+ return True, None
+
+ def cuda_available(self) -> Tuple[bool, Optional[Exception]]:
+ try:
+ if self._ggml_cuda is None:
+ raise RuntimeError("llama-cpp-python was built without GGML CUDA support.")
+ if not torch.cuda.is_available():
+ raise RuntimeError("Torch CUDA is unavailable.")
+ device_count = int(self._ggml_cuda.ggml_backend_cuda_get_device_count())
+ if device_count <= 0:
+ raise RuntimeError("GGML CUDA backend found no CUDA devices.")
+ self._get_cuda_backend(0)
+ except Exception as exc:
+ return False, exc
+ return True, None
+
+ def _get_cpu_backend(self) -> int:
+ if self._cpu_backend is None:
+ backend = self._ggml_cpu.ggml_backend_cpu_init()
+ if not backend:
+ raise RuntimeError("GGUFCppKernel failed to initialize GGML CPU backend.")
+ self._ggml_cpu.ggml_backend_cpu_set_n_threads(backend, self._cpu_threads())
+ self._cpu_backend = backend
+ return self._cpu_backend
+
+ def _get_cuda_backend(self, device_index: int) -> int:
+ if self._ggml_cuda is None:
+ raise RuntimeError("llama-cpp-python was built without GGML CUDA support.") from self._ggml_cuda_error
+ if device_index < 0:
+ device_index = 0
+ device_count = int(self._ggml_cuda.ggml_backend_cuda_get_device_count())
+ if device_index >= device_count:
+ raise RuntimeError(
+ f"GGML CUDA backend device index `{device_index}` is out of range for `{device_count}` devices."
+ )
+ if device_index not in self._cuda_backends:
+ backend = self._ggml_cuda.ggml_backend_cuda_init(device_index)
+ if not backend:
+ raise RuntimeError(f"GGUFCudaKernel failed to initialize GGML CUDA backend for device `{device_index}`.")
+ self._cuda_backends[device_index] = backend
+ return self._cuda_backends[device_index]
+
+ @staticmethod
+ def _normalize_qweight_cpu(qweight: torch.Tensor) -> torch.Tensor:
+ qweight_cpu = qweight.detach()
+ if qweight_cpu.device.type != "cpu":
+ qweight_cpu = qweight_cpu.to(device="cpu")
+ if not qweight_cpu.is_contiguous():
+ qweight_cpu = qweight_cpu.contiguous()
+ return qweight_cpu
+
+ @staticmethod
+ def _normalize_input_cpu(x: torch.Tensor, padded_in_features: int) -> torch.Tensor:
+ x_cpu = x.detach().to(device="cpu", dtype=torch.float32)
+ if x_cpu.shape[-1] != padded_in_features:
+ x_cpu = F.pad(x_cpu, (0, padded_in_features - x_cpu.shape[-1]))
+ if not x_cpu.is_contiguous():
+ x_cpu = x_cpu.contiguous()
+ return x_cpu
+
+ def _normalize_input_cuda(
+ self,
+ x: torch.Tensor,
+ padded_in_features: int,
+ ) -> tuple[torch.Tensor, int]:
+ x_cuda = x.detach()
+ if x_cuda.dtype == torch.float16:
+ ggml_input_type = self.ggml_type_f16
+ elif x_cuda.dtype == torch.float32:
+ ggml_input_type = self.ggml_type_f32
+ elif x_cuda.dtype == torch.bfloat16:
+ # ggml in llama-cpp-python does not expose BF16 here, so use native CUDA fp16.
+ x_cuda = x_cuda.to(dtype=torch.float16)
+ ggml_input_type = self.ggml_type_f16
+ else:
+ raise RuntimeError(
+ "GGUFCudaKernel only supports float16, bfloat16, or float32 inputs."
+ )
+
+ if x_cuda.shape[-1] != padded_in_features:
+ x_cuda = F.pad(x_cuda, (0, padded_in_features - x_cuda.shape[-1]))
+ if not x_cuda.is_contiguous():
+ x_cuda = x_cuda.contiguous()
+ return x_cuda, ggml_input_type
+
+ @staticmethod
+ def _normalize_qweight_cuda(qweight: torch.Tensor, device: torch.device) -> torch.Tensor:
+ qweight_cuda = qweight.detach()
+ if qweight_cuda.device != device:
+ qweight_cuda = qweight_cuda.to(device=device)
+ if not qweight_cuda.is_contiguous():
+ qweight_cuda = qweight_cuda.contiguous()
+ return qweight_cuda
+
+ @staticmethod
+ def _torch_dtype_from_ggml_element_size(output_element_size: int, *, kernel_name: str) -> torch.dtype:
+ if output_element_size == 4:
+ return torch.float32
+ if output_element_size == 2:
+ return torch.float16
+ raise RuntimeError(
+ f"{kernel_name} received unsupported GGML output element size `{output_element_size}`."
+ )
+
+ def _run_quantized_matmul(
+ self,
+ *,
+ backend: int,
+ qweight_cpu: torch.Tensor,
+ x_cpu: torch.Tensor,
+ gguf_tensor_qtype: str,
+ padded_in_features: int,
+ out_features: int,
+ kernel_name: str,
+ ) -> torch.Tensor:
+
+ ctx = self._ggml_base.ggml_init(
+ _GGMLInitParams(
+ mem_size=self.GGML_METADATA_BYTES,
+ mem_buffer=None,
+ no_alloc=True,
+ )
+ )
+ if not ctx:
+ raise RuntimeError(f"{kernel_name} failed to initialize GGML metadata context.")
+
+ buffer = None
+ try:
+ weight_tensor = self._ggml_base.ggml_new_tensor_2d(
+ ctx,
+ self.ggml_qtypes[gguf_tensor_qtype],
+ padded_in_features,
+ out_features,
+ )
+ input_tensor = self._ggml_base.ggml_new_tensor_2d(
+ ctx,
+ self.ggml_type_f32,
+ padded_in_features,
+ x_cpu.shape[0],
+ )
+ if not weight_tensor or not input_tensor:
+ raise RuntimeError(f"{kernel_name} failed to create GGML tensors.")
+
+ self._ggml_base.ggml_set_input(input_tensor)
+ output_tensor = self._ggml_base.ggml_mul_mat(ctx, weight_tensor, input_tensor)
+ if not output_tensor:
+ raise RuntimeError(f"{kernel_name} failed to create GGML matmul node.")
+
+ graph = self._ggml_base.ggml_new_graph(ctx)
+ if not graph:
+ raise RuntimeError(f"{kernel_name} failed to allocate GGML graph.")
+ self._ggml_base.ggml_build_forward_expand(graph, output_tensor)
+
+ buffer = self._ggml_base.ggml_backend_alloc_ctx_tensors(ctx, backend)
+ if not buffer:
+ raise RuntimeError(f"{kernel_name} failed to allocate GGML backend tensors.")
+
+ self._ggml_base.ggml_backend_tensor_set(
+ weight_tensor,
+ ctypes.c_void_p(qweight_cpu.data_ptr()),
+ 0,
+ qweight_cpu.numel() * qweight_cpu.element_size(),
+ )
+ self._ggml_base.ggml_backend_tensor_set(
+ input_tensor,
+ ctypes.c_void_p(x_cpu.data_ptr()),
+ 0,
+ x_cpu.numel() * x_cpu.element_size(),
+ )
+
+ status = self._ggml_base.ggml_backend_graph_compute(backend, graph)
+ if status != 0:
+ raise RuntimeError(f"{kernel_name} GGML graph compute failed with status={status}.")
+ self._ggml_base.ggml_backend_synchronize(backend)
+
+ output_nbytes = self._ggml_base.ggml_nbytes(output_tensor)
+ output_element_size = self._ggml_base.ggml_element_size(output_tensor)
+ if output_element_size == 4:
+ output_dtype = torch.float32
+ elif output_element_size == 2:
+ output_dtype = torch.float16
+ else:
+ raise RuntimeError(
+ f"{kernel_name} received unsupported GGML output element size `{output_element_size}`."
+ )
+
+ output = torch.empty((x_cpu.shape[0], out_features), dtype=output_dtype, device="cpu")
+ expected_nbytes = output.numel() * output.element_size()
+ if expected_nbytes != output_nbytes:
+ raise RuntimeError(
+ f"{kernel_name} GGML output size mismatch: expected {expected_nbytes}, got {output_nbytes}."
+ )
+
+ self._ggml_base.ggml_backend_tensor_get(
+ output_tensor,
+ ctypes.c_void_p(output.data_ptr()),
+ 0,
+ output_nbytes,
+ )
+ return output
+ finally:
+ if buffer:
+ self._ggml_base.ggml_backend_buffer_free(buffer)
+ self._ggml_base.ggml_free(ctx)
+
+ def build_quantized_matmul_cuda_plan(
+ self,
+ *,
+ backend: int,
+ qweight: torch.Tensor,
+ gguf_tensor_qtype: str,
+ padded_in_features: int,
+ out_features: int,
+ rows: int,
+ input_ggml_type: int,
+ kernel_name: str,
+ ) -> _GGMLMatmulPlan:
+ ctx = self._ggml_base.ggml_init(
+ _GGMLInitParams(
+ mem_size=self.GGML_METADATA_BYTES,
+ mem_buffer=None,
+ no_alloc=True,
+ )
+ )
+ if not ctx:
+ raise RuntimeError(f"{kernel_name} failed to initialize GGML metadata context.")
+
+ buffer = None
+ try:
+ weight_tensor = self._ggml_base.ggml_new_tensor_2d(
+ ctx,
+ self.ggml_qtypes[gguf_tensor_qtype],
+ padded_in_features,
+ out_features,
+ )
+ input_tensor = self._ggml_base.ggml_new_tensor_2d(
+ ctx,
+ input_ggml_type,
+ padded_in_features,
+ rows,
+ )
+ if not weight_tensor or not input_tensor:
+ raise RuntimeError(f"{kernel_name} failed to create GGML tensors.")
+
+ self._ggml_base.ggml_set_input(input_tensor)
+ output_tensor = self._ggml_base.ggml_mul_mat(ctx, weight_tensor, input_tensor)
+ if not output_tensor:
+ raise RuntimeError(f"{kernel_name} failed to create GGML matmul node.")
+
+ graph = self._ggml_base.ggml_new_graph(ctx)
+ if not graph:
+ raise RuntimeError(f"{kernel_name} failed to allocate GGML graph.")
+ self._ggml_base.ggml_build_forward_expand(graph, output_tensor)
+
+ buffer = self._ggml_base.ggml_backend_alloc_ctx_tensors(ctx, backend)
+ if not buffer:
+ raise RuntimeError(f"{kernel_name} failed to allocate GGML backend tensors.")
+
+ self._ggml_base.ggml_backend_tensor_set(
+ weight_tensor,
+ ctypes.c_void_p(qweight.data_ptr()),
+ 0,
+ qweight.numel() * qweight.element_size(),
+ )
+ output_nbytes = self._ggml_base.ggml_nbytes(output_tensor)
+ output_dtype = self._torch_dtype_from_ggml_element_size(
+ int(self._ggml_base.ggml_element_size(output_tensor)),
+ kernel_name=kernel_name,
+ )
+ return _GGMLMatmulPlan(
+ ctx=ctx,
+ buffer=buffer,
+ weight_tensor=weight_tensor,
+ input_tensor=input_tensor,
+ output_tensor=output_tensor,
+ graph=graph,
+ rows=rows,
+ out_features=out_features,
+ output_nbytes=output_nbytes,
+ output_dtype=output_dtype,
+ backend_buffer_free=self._ggml_base.ggml_backend_buffer_free,
+ ctx_free=self._ggml_base.ggml_free,
+ )
+ except Exception:
+ if buffer:
+ self._ggml_base.ggml_backend_buffer_free(buffer)
+ self._ggml_base.ggml_free(ctx)
+ raise
+
+ def run_quantized_matmul_cuda_plan(
+ self,
+ *,
+ backend: int,
+ plan: _GGMLMatmulPlan,
+ x: torch.Tensor,
+ kernel_name: str,
+ ) -> torch.Tensor:
+ self._ggml_base.ggml_backend_tensor_set(
+ plan.input_tensor,
+ ctypes.c_void_p(x.data_ptr()),
+ 0,
+ x.numel() * x.element_size(),
+ )
+ status = self._ggml_base.ggml_backend_graph_compute(backend, plan.graph)
+ if status != 0:
+ raise RuntimeError(f"{kernel_name} GGML graph compute failed with status={status}.")
+ self._ggml_base.ggml_backend_synchronize(backend)
+
+ output = torch.empty((plan.rows, plan.out_features), dtype=plan.output_dtype, device=x.device)
+ expected_nbytes = output.numel() * output.element_size()
+ if expected_nbytes != plan.output_nbytes:
+ raise RuntimeError(
+ f"{kernel_name} GGML output size mismatch: expected {expected_nbytes}, got {plan.output_nbytes}."
+ )
+ self._ggml_base.ggml_backend_tensor_get(
+ plan.output_tensor,
+ ctypes.c_void_p(output.data_ptr()),
+ 0,
+ plan.output_nbytes,
+ )
+ return output
+
+ def quantized_matmul_cpu(
+ self,
+ *,
+ qweight: torch.Tensor,
+ x: torch.Tensor,
+ gguf_tensor_qtype: str,
+ padded_in_features: int,
+ out_features: int,
+ ) -> torch.Tensor:
+ if x.device.type != "cpu":
+ raise RuntimeError("GGUFCppKernel only supports CPU input tensors.")
+
+ return self._run_quantized_matmul(
+ backend=self._get_cpu_backend(),
+ qweight_cpu=self._normalize_qweight_cpu(qweight),
+ x_cpu=self._normalize_input_cpu(x, padded_in_features),
+ gguf_tensor_qtype=gguf_tensor_qtype,
+ padded_in_features=padded_in_features,
+ out_features=out_features,
+ kernel_name="GGUFCppKernel",
+ )
+
+ def quantized_matmul_cuda(
+ self,
+ *,
+ qweight: torch.Tensor,
+ x: torch.Tensor,
+ gguf_tensor_qtype: str,
+ padded_in_features: int,
+ out_features: int,
+ plan: _GGMLMatmulPlan | None = None,
+ ) -> tuple[torch.Tensor, _GGMLMatmulPlan]:
+ if x.device.type != "cuda":
+ raise RuntimeError("GGUFCudaKernel only supports CUDA input tensors.")
+
+ device = x.device
+ backend = self._get_cuda_backend(0 if device.index is None else device.index)
+ x_cuda, input_ggml_type = self._normalize_input_cuda(x, padded_in_features)
+ if plan is None:
+ qweight_cuda = self._normalize_qweight_cuda(qweight, device=device)
+ plan = self.build_quantized_matmul_cuda_plan(
+ backend=backend,
+ qweight=qweight_cuda,
+ gguf_tensor_qtype=gguf_tensor_qtype,
+ padded_in_features=padded_in_features,
+ out_features=out_features,
+ rows=x_cuda.shape[0],
+ input_ggml_type=input_ggml_type,
+ kernel_name="GGUFCudaKernel",
+ )
+ output = self.run_quantized_matmul_cuda_plan(
+ backend=backend,
+ plan=plan,
+ x=x_cuda,
+ kernel_name="GGUFCudaKernel",
+ )
+ return output, plan
+
+
+_GGML_BRIDGE: Optional[_GGMLBridge] = None
+
+
+def _get_ggml_bridge() -> _GGMLBridge:
+ global _GGML_BRIDGE
+ if _GGML_BRIDGE is None:
+ _GGML_BRIDGE = _GGMLBridge()
+ return _GGML_BRIDGE
+
+
+class GGUFCppKernel(GGUFTorchQuantLinear):
+ SUPPORTS_BACKENDS = [BACKEND.GGUF_CPP_CPU]
+ SUPPORTS_METHODS = [METHOD.GGUF]
+ SUPPORTS_FORMATS = {FORMAT.GGUF: 25}
+ SUPPORTS_BITS = [4, 5, 6, 8]
+ SUPPORTS_GROUP_SIZE = [-1]
+ SUPPORTS_DESC_ACT = [False]
+ SUPPORTS_SYM = [True]
+ SUPPORTS_SHARDS = True
+ SUPPORTS_TRAINING = False
+ SUPPORTS_AUTO_PADDING = True
+ SUPPORTS_IN_FEATURES_DIVISIBLE_BY = [1]
+ SUPPORTS_OUT_FEATURES_DIVISIBLE_BY = [1]
+ SUPPORTS_DEVICES = [DEVICE.CPU]
+ SUPPORTS_PLATFORM = [PLATFORM.ALL]
+ SUPPORTS_PACK_DTYPES = [torch.int8, torch.int16, torch.int32]
+ SUPPORTS_ADAPTERS = [Lora]
+ SUPPORTS_DTYPES = [torch.float16, torch.bfloat16, torch.float32]
+
+ REQUIRES_FORMAT_V2 = False
+ AUTOTUNE = False
+
+ QUANT_TYPE = "gguf"
+
+ pack = None
+ pack_block = None
+ pack_gpu = None
+ pack_original = None
+
+ def __init__(
+ self,
+ bits,
+ group_size: int,
+ sym: bool,
+ desc_act: bool,
+ in_features: int,
+ out_features: int,
+ bias: bool = False,
+ pack_dtype: torch.dtype = torch.int32,
+ adapter: Adapter = None,
+ register_buffers: bool = True,
+ **kwargs,
+ ):
+ kwargs.setdefault("backend", BACKEND.GGUF_CPP_CPU)
+ super().__init__(
+ bits=bits,
+ group_size=group_size,
+ sym=sym,
+ desc_act=desc_act,
+ in_features=in_features,
+ out_features=out_features,
+ bias=bias,
+ pack_dtype=pack_dtype,
+ adapter=adapter,
+ register_buffers=register_buffers,
+ **kwargs,
+ )
+
+ @classmethod
+ def validate_once(cls) -> Tuple[bool, Optional[Exception]]:
+ return _get_ggml_bridge().cpu_available()
+
+ def forward(self, x: torch.Tensor):
+ original_shape = x.shape[:-1] + (self.out_features,)
+ x_flat = x.reshape(-1, x.shape[-1])
+ if x_flat.device.type != "cpu":
+ raise RuntimeError(
+ f"{self.__class__.__name__} only supports CPU inference. "
+ "Load GGUF models on CPU or use BACKEND.GGUF_CPP_CUDA or BACKEND.GGUF_TORCH for CUDA inference."
+ )
+
+ output = _get_ggml_bridge().quantized_matmul_cpu(
+ qweight=self.qweight,
+ x=x_flat,
+ gguf_tensor_qtype=self.gguf_tensor_qtype,
+ padded_in_features=self.padded_in_features,
+ out_features=self.out_features,
+ )
+ if output.dtype != x_flat.dtype:
+ output = output.to(dtype=x_flat.dtype)
+
+ if self.bias is not None:
+ bias = self.bias
+ if bias.device != output.device or bias.dtype != output.dtype:
+ bias = bias.to(device=output.device, dtype=output.dtype)
+ output = output + bias
+
+ if self.adapter:
+ output = self.adapter.apply(x=x_flat, out=output)
+
+ return output.reshape(original_shape)
+
+class GGUFCudaKernel(GGUFTorchQuantLinear):
+ SUPPORTS_BACKENDS = [BACKEND.GGUF_CPP_CUDA]
+ SUPPORTS_METHODS = [METHOD.GGUF]
+ SUPPORTS_FORMATS = {FORMAT.GGUF: 35}
+ SUPPORTS_BITS = [4, 5, 6, 8]
+ SUPPORTS_GROUP_SIZE = [-1]
+ SUPPORTS_DESC_ACT = [False]
+ SUPPORTS_SYM = [True]
+ SUPPORTS_SHARDS = True
+ SUPPORTS_TRAINING = False
+ SUPPORTS_AUTO_PADDING = True
+ SUPPORTS_IN_FEATURES_DIVISIBLE_BY = [1]
+ SUPPORTS_OUT_FEATURES_DIVISIBLE_BY = [1]
+ SUPPORTS_DEVICES = [DEVICE.CUDA]
+ SUPPORTS_PLATFORM = [PLATFORM.ALL]
+ SUPPORTS_PACK_DTYPES = [torch.int8, torch.int16, torch.int32]
+ SUPPORTS_ADAPTERS = [Lora]
+ SUPPORTS_DTYPES = [torch.float16, torch.bfloat16, torch.float32]
+
+ REQUIRES_FORMAT_V2 = False
+ AUTOTUNE = False
+
+ QUANT_TYPE = "gguf"
+
+ pack = None
+ pack_block = None
+ pack_gpu = None
+ pack_original = None
+
+ def __init__(
+ self,
+ bits,
+ group_size: int,
+ sym: bool,
+ desc_act: bool,
+ in_features: int,
+ out_features: int,
+ bias: bool = False,
+ pack_dtype: torch.dtype = torch.int32,
+ adapter: Adapter = None,
+ register_buffers: bool = True,
+ **kwargs,
+ ):
+ kwargs.setdefault("backend", BACKEND.GGUF_CPP_CUDA)
+ super().__init__(
+ bits=bits,
+ group_size=group_size,
+ sym=sym,
+ desc_act=desc_act,
+ in_features=in_features,
+ out_features=out_features,
+ bias=bias,
+ pack_dtype=pack_dtype,
+ adapter=adapter,
+ register_buffers=register_buffers,
+ **kwargs,
+ )
+ self._ggml_cuda_plans: Dict[tuple[int, int, torch.dtype, int], _GGMLMatmulPlan] = {}
+
+ @classmethod
+ def validate_once(cls) -> Tuple[bool, Optional[Exception]]:
+ return _get_ggml_bridge().cuda_available()
+
+ def clear_weight_cache(self) -> None:
+ for plan in self._ggml_cuda_plans.values():
+ plan.close()
+ self._ggml_cuda_plans.clear()
+ return super().clear_weight_cache()
+
+ def _cuda_plan_key(self, x_flat: torch.Tensor) -> tuple[int, int, torch.dtype, int]:
+ device_index = 0 if x_flat.device.index is None else x_flat.device.index
+ return (
+ device_index,
+ x_flat.shape[0],
+ x_flat.dtype,
+ self.qweight.data_ptr(),
+ )
+
+ def forward(self, x: torch.Tensor):
+ original_shape = x.shape[:-1] + (self.out_features,)
+ x_flat = x.reshape(-1, x.shape[-1])
+ if x_flat.device.type != "cuda":
+ raise RuntimeError(
+ f"{self.__class__.__name__} only supports CUDA inference. "
+ "Load GGUF models on CUDA or use BACKEND.GGUF_CPP_CPU or BACKEND.GGUF_TORCH for CPU inference."
+ )
+
+ plan_key = self._cuda_plan_key(x_flat)
+ output, plan = _get_ggml_bridge().quantized_matmul_cuda(
+ qweight=self.qweight,
+ x=x_flat,
+ gguf_tensor_qtype=self.gguf_tensor_qtype,
+ padded_in_features=self.padded_in_features,
+ out_features=self.out_features,
+ plan=self._ggml_cuda_plans.get(plan_key),
+ )
+ self._ggml_cuda_plans.setdefault(plan_key, plan)
+ if output.device != x_flat.device or output.dtype != x_flat.dtype:
+ output = output.to(device=x_flat.device, dtype=x_flat.dtype)
+
+ if self.bias is not None:
+ bias = self.bias
+ if bias.device != output.device or bias.dtype != output.dtype:
+ bias = bias.to(device=output.device, dtype=output.dtype)
+ output = output + bias
+
+ if self.adapter:
+ output = self.adapter.apply(x=x_flat, out=output)
+
+ return output.reshape(original_shape)
+
+
+__all__ = ["GGUFCppKernel", "GGUFCudaKernel"]
diff --git a/gptqmodel/nn_modules/qlinear/gguf_triton.py b/gptqmodel/nn_modules/qlinear/gguf_triton.py
new file mode 100644
index 000000000..e48f8a785
--- /dev/null
+++ b/gptqmodel/nn_modules/qlinear/gguf_triton.py
@@ -0,0 +1,796 @@
+# SPDX-FileCopyrightText: 2026 ModelCloud.ai
+# SPDX-License-Identifier: Apache-2.0
+
+from __future__ import annotations
+
+from typing import Any, Callable, Optional, Tuple
+
+import torch
+
+from ...adapter.adapter import Adapter, Lora
+from ...models._const import DEVICE, PLATFORM
+from ...quantization import FORMAT, METHOD
+from ...utils.backend import BACKEND
+from ...utils.python import has_gil_disabled
+from .gguf import GGUFTorchQuantLinear, _unpack_q4_k_scale_min_torch
+
+
+try:
+ import triton
+ import triton.language as tl
+ from packaging import version
+
+ from ..triton_utils import custom_autotune
+
+ _TRITON_AVAILABLE = True
+except Exception: # pragma: no cover - optional dependency
+ triton = None
+ tl = None
+ custom_autotune = None
+ _TRITON_AVAILABLE = False
+
+
+def triton_available() -> bool:
+ return _TRITON_AVAILABLE
+
+
+if _TRITON_AVAILABLE:
+ _GGUF_TRITON_SMALL_CONFIGS = [
+ triton.Config(
+ {
+ "BLOCK_SIZE_M": 8,
+ "BLOCK_SIZE_N": 32,
+ },
+ num_stages=2,
+ num_warps=4,
+ ),
+ triton.Config(
+ {
+ "BLOCK_SIZE_M": 16,
+ "BLOCK_SIZE_N": 32,
+ },
+ num_stages=2,
+ num_warps=4,
+ ),
+ triton.Config(
+ {
+ "BLOCK_SIZE_M": 16,
+ "BLOCK_SIZE_N": 64,
+ },
+ num_stages=2,
+ num_warps=4,
+ ),
+ ]
+
+ _GGUF_TRITON_LARGE_CONFIGS = [
+ triton.Config(
+ {
+ "BLOCK_SIZE_M": 8,
+ "BLOCK_SIZE_N": 16,
+ },
+ num_stages=2,
+ num_warps=2,
+ ),
+ triton.Config(
+ {
+ "BLOCK_SIZE_M": 8,
+ "BLOCK_SIZE_N": 32,
+ },
+ num_stages=2,
+ num_warps=2,
+ ),
+ triton.Config(
+ {
+ "BLOCK_SIZE_M": 16,
+ "BLOCK_SIZE_N": 16,
+ },
+ num_stages=2,
+ num_warps=2,
+ ),
+ triton.Config(
+ {
+ "BLOCK_SIZE_M": 16,
+ "BLOCK_SIZE_N": 16,
+ },
+ num_stages=4,
+ num_warps=4,
+ ),
+ triton.Config(
+ {
+ "BLOCK_SIZE_M": 8,
+ "BLOCK_SIZE_N": 32,
+ },
+ num_stages=4,
+ num_warps=4,
+ ),
+ triton.Config(
+ {
+ "BLOCK_SIZE_M": 16,
+ "BLOCK_SIZE_N": 32,
+ },
+ num_stages=2,
+ num_warps=4,
+ ),
+ triton.Config(
+ {
+ "BLOCK_SIZE_M": 16,
+ "BLOCK_SIZE_N": 32,
+ },
+ num_stages=4,
+ num_warps=4,
+ ),
+ triton.Config(
+ {
+ "BLOCK_SIZE_M": 16,
+ "BLOCK_SIZE_N": 64,
+ },
+ num_stages=2,
+ num_warps=4,
+ ),
+ triton.Config(
+ {
+ "BLOCK_SIZE_M": 32,
+ "BLOCK_SIZE_N": 16,
+ },
+ num_stages=2,
+ num_warps=4,
+ ),
+ triton.Config(
+ {
+ "BLOCK_SIZE_M": 32,
+ "BLOCK_SIZE_N": 32,
+ },
+ num_stages=2,
+ num_warps=4,
+ ),
+ triton.Config(
+ {
+ "BLOCK_SIZE_M": 32,
+ "BLOCK_SIZE_N": 64,
+ },
+ num_stages=2,
+ num_warps=8,
+ ),
+ ]
+ _GGUF_TRITON_LARGE_NUM_BLOCKS = 16
+
+ @triton.jit
+ def _gguf_q4_k_fused_matmul_kernel_impl(
+ x_ptr,
+ qs_ptr,
+ scale_ptr,
+ min_ptr,
+ out_ptr,
+ M,
+ N,
+ NUM_BLOCKS,
+ stride_xm,
+ stride_xk,
+ stride_qb,
+ stride_qq,
+ stride_qn,
+ stride_sb,
+ stride_ss,
+ stride_sn,
+ stride_mb,
+ stride_ms,
+ stride_mn,
+ stride_om,
+ stride_on,
+ BLOCK_SIZE_M: tl.constexpr,
+ BLOCK_SIZE_N: tl.constexpr,
+ ):
+ pid_m = tl.program_id(0)
+ pid_n = tl.program_id(1)
+
+ offs_m = pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M)
+ offs_n = pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)
+ m_mask = offs_m < M
+ n_mask = offs_n < N
+
+ accumulator = tl.zeros((BLOCK_SIZE_M, BLOCK_SIZE_N), dtype=tl.float32)
+
+ for block_idx in range(0, NUM_BLOCKS):
+ for subblock in range(0, 8):
+ offs_k = block_idx * 256 + subblock * 32 + tl.arange(0, 32)
+ a = tl.load(
+ x_ptr + offs_m[:, None] * stride_xm + offs_k[None, :] * stride_xk,
+ mask=m_mask[:, None],
+ other=0.0,
+ )
+
+ byte_idx = (subblock // 2) * 32 + tl.arange(0, 32)
+ packed = tl.load(
+ qs_ptr + block_idx * stride_qb + byte_idx[:, None] * stride_qq + offs_n[None, :] * stride_qn,
+ mask=n_mask[None, :],
+ other=0,
+ )
+ if subblock % 2 == 0:
+ q = packed & 0x0F
+ else:
+ q = packed >> 4
+
+ scale = tl.load(
+ scale_ptr + block_idx * stride_sb + subblock * stride_ss + offs_n * stride_sn,
+ mask=n_mask,
+ other=0.0,
+ )
+ min_value = tl.load(
+ min_ptr + block_idx * stride_mb + subblock * stride_ms + offs_n * stride_mn,
+ mask=n_mask,
+ other=0.0,
+ )
+
+ weight = tl.cast(q, tl.float16) * scale[None, :] - min_value[None, :]
+ accumulator += tl.dot(a, weight)
+
+ tl.store(
+ out_ptr + offs_m[:, None] * stride_om + offs_n[None, :] * stride_on,
+ tl.cast(accumulator, tl.float16),
+ mask=m_mask[:, None] & n_mask[None, :],
+ )
+
+ _gguf_q4_k_fused_matmul_kernel_small = custom_autotune.autotune(
+ configs=_GGUF_TRITON_SMALL_CONFIGS,
+ key=["M", "N"],
+ nearest_power_of_two=True,
+ )(_gguf_q4_k_fused_matmul_kernel_impl)
+ _gguf_q4_k_fused_matmul_kernel_large = custom_autotune.autotune(
+ configs=_GGUF_TRITON_LARGE_CONFIGS,
+ key=["M", "N", "NUM_BLOCKS"],
+ nearest_power_of_two=True,
+ )(_gguf_q4_k_fused_matmul_kernel_impl)
+
+ @triton.jit
+ def _gguf_q5_k_fused_matmul_kernel_impl(
+ x_ptr,
+ qs_ptr,
+ qh_ptr,
+ scale_ptr,
+ min_ptr,
+ out_ptr,
+ M,
+ N,
+ NUM_BLOCKS,
+ stride_xm,
+ stride_xk,
+ stride_qsb,
+ stride_qsq,
+ stride_qsn,
+ stride_qhb,
+ stride_qhq,
+ stride_qhn,
+ stride_sb,
+ stride_ss,
+ stride_sn,
+ stride_mb,
+ stride_ms,
+ stride_mn,
+ stride_om,
+ stride_on,
+ BLOCK_SIZE_M: tl.constexpr,
+ BLOCK_SIZE_N: tl.constexpr,
+ ):
+ pid_m = tl.program_id(0)
+ pid_n = tl.program_id(1)
+
+ offs_m = pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M)
+ offs_n = pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)
+ m_mask = offs_m < M
+ n_mask = offs_n < N
+
+ accumulator = tl.zeros((BLOCK_SIZE_M, BLOCK_SIZE_N), dtype=tl.float32)
+
+ for block_idx in range(0, NUM_BLOCKS):
+ for subblock in range(0, 8):
+ offs_k = block_idx * 256 + subblock * 32 + tl.arange(0, 32)
+ a = tl.load(
+ x_ptr + offs_m[:, None] * stride_xm + offs_k[None, :] * stride_xk,
+ mask=m_mask[:, None],
+ other=0.0,
+ )
+
+ byte_idx = (subblock // 2) * 32 + tl.arange(0, 32)
+ packed = tl.load(
+ qs_ptr + block_idx * stride_qsb + byte_idx[:, None] * stride_qsq + offs_n[None, :] * stride_qsn,
+ mask=n_mask[None, :],
+ other=0,
+ )
+ if subblock % 2 == 0:
+ ql = packed & 0x0F
+ else:
+ ql = packed >> 4
+
+ qh = tl.load(
+ qh_ptr + block_idx * stride_qhb + tl.arange(0, 32)[:, None] * stride_qhq + offs_n[None, :] * stride_qhn,
+ mask=n_mask[None, :],
+ other=0,
+ )
+ qh = (qh >> subblock) & 0x01
+ q = ql | (qh << 4)
+
+ scale = tl.load(
+ scale_ptr + block_idx * stride_sb + subblock * stride_ss + offs_n * stride_sn,
+ mask=n_mask,
+ other=0.0,
+ )
+ min_value = tl.load(
+ min_ptr + block_idx * stride_mb + subblock * stride_ms + offs_n * stride_mn,
+ mask=n_mask,
+ other=0.0,
+ )
+
+ weight = tl.cast(q, tl.float16) * scale[None, :] - min_value[None, :]
+ accumulator += tl.dot(a, weight)
+
+ tl.store(
+ out_ptr + offs_m[:, None] * stride_om + offs_n[None, :] * stride_on,
+ tl.cast(accumulator, tl.float16),
+ mask=m_mask[:, None] & n_mask[None, :],
+ )
+
+ _gguf_q5_k_fused_matmul_kernel_small = custom_autotune.autotune(
+ configs=_GGUF_TRITON_SMALL_CONFIGS,
+ key=["M", "N"],
+ nearest_power_of_two=True,
+ )(_gguf_q5_k_fused_matmul_kernel_impl)
+ _gguf_q5_k_fused_matmul_kernel_large = custom_autotune.autotune(
+ configs=_GGUF_TRITON_LARGE_CONFIGS,
+ key=["M", "N", "NUM_BLOCKS"],
+ nearest_power_of_two=True,
+ )(_gguf_q5_k_fused_matmul_kernel_impl)
+
+ @triton.jit
+ def _gguf_q6_k_fused_matmul_kernel_impl(
+ x_ptr,
+ ql_ptr,
+ qh_ptr,
+ scale_ptr,
+ out_ptr,
+ M,
+ N,
+ NUM_BLOCKS,
+ stride_xm,
+ stride_xk,
+ stride_qlb,
+ stride_qlq,
+ stride_qln,
+ stride_qhb,
+ stride_qhq,
+ stride_qhn,
+ stride_sb,
+ stride_ss,
+ stride_sn,
+ stride_om,
+ stride_on,
+ BLOCK_SIZE_M: tl.constexpr,
+ BLOCK_SIZE_N: tl.constexpr,
+ ):
+ pid_m = tl.program_id(0)
+ pid_n = tl.program_id(1)
+
+ offs_m = pid_m * BLOCK_SIZE_M + tl.arange(0, BLOCK_SIZE_M)
+ offs_n = pid_n * BLOCK_SIZE_N + tl.arange(0, BLOCK_SIZE_N)
+ m_mask = offs_m < M
+ n_mask = offs_n < N
+
+ accumulator = tl.zeros((BLOCK_SIZE_M, BLOCK_SIZE_N), dtype=tl.float32)
+
+ for block_idx in range(0, NUM_BLOCKS):
+ for subblock in range(0, 16):
+ offs_k = block_idx * 256 + subblock * 16 + tl.arange(0, 16)
+ a = tl.load(
+ x_ptr + offs_m[:, None] * stride_xm + offs_k[None, :] * stride_xk,
+ mask=m_mask[:, None],
+ other=0.0,
+ )
+
+ pair_row = subblock // 2
+ half = subblock % 2
+ row_group = pair_row // 4
+ row_in_group = pair_row % 4
+ pos32 = half * 16 + tl.arange(0, 16)
+ ql_base = row_group * 64 + (32 if row_in_group == 1 or row_in_group == 3 else 0)
+
+ packed_ql = tl.load(
+ ql_ptr + block_idx * stride_qlb + (ql_base + pos32)[:, None] * stride_qlq + offs_n[None, :] * stride_qln,
+ mask=n_mask[None, :],
+ other=0,
+ )
+ if row_in_group == 0 or row_in_group == 1:
+ low = packed_ql & 0x0F
+ else:
+ low = packed_ql >> 4
+
+ packed_qh = tl.load(
+ qh_ptr + block_idx * stride_qhb + (row_group * 32 + pos32)[:, None] * stride_qhq + offs_n[None, :] * stride_qhn,
+ mask=n_mask[None, :],
+ other=0,
+ )
+ high = (packed_qh >> (row_in_group * 2)) & 0x03
+ q = tl.cast(low | (high << 4), tl.int16) - 32
+
+ scale = tl.load(
+ scale_ptr + block_idx * stride_sb + subblock * stride_ss + offs_n * stride_sn,
+ mask=n_mask,
+ other=0.0,
+ )
+
+ weight = tl.cast(q, tl.float16) * scale[None, :]
+ accumulator += tl.dot(a, weight)
+
+ tl.store(
+ out_ptr + offs_m[:, None] * stride_om + offs_n[None, :] * stride_on,
+ tl.cast(accumulator, tl.float16),
+ mask=m_mask[:, None] & n_mask[None, :],
+ )
+
+ _gguf_q6_k_fused_matmul_kernel_small = custom_autotune.autotune(
+ configs=_GGUF_TRITON_SMALL_CONFIGS,
+ key=["M", "N"],
+ nearest_power_of_two=True,
+ )(_gguf_q6_k_fused_matmul_kernel_impl)
+ _gguf_q6_k_fused_matmul_kernel_large = custom_autotune.autotune(
+ configs=_GGUF_TRITON_LARGE_CONFIGS,
+ key=["M", "N", "NUM_BLOCKS"],
+ nearest_power_of_two=True,
+ )(_gguf_q6_k_fused_matmul_kernel_impl)
+
+
+def _launch(
+ kernel: Callable,
+ x: torch.Tensor,
+ output: torch.Tensor,
+ *args,
+) -> torch.Tensor:
+ def grid(meta):
+ return (
+ triton.cdiv(x.shape[0], meta["BLOCK_SIZE_M"]),
+ triton.cdiv(output.shape[1], meta["BLOCK_SIZE_N"]),
+ )
+
+ kernel[grid](*args)
+ return output
+
+
+def _select_triton_kernel(
+ small_kernel: Callable,
+ large_kernel: Callable,
+ *,
+ num_blocks: int,
+) -> Callable:
+ if num_blocks >= _GGUF_TRITON_LARGE_NUM_BLOCKS:
+ return large_kernel
+ return small_kernel
+
+
+def fused_q4_k_matmul(
+ x: torch.Tensor,
+ qs: torch.Tensor,
+ scale: torch.Tensor,
+ min_value: torch.Tensor,
+) -> torch.Tensor:
+ if not _TRITON_AVAILABLE:
+ raise RuntimeError("Triton is not available for GGUF Q4_K fused matmul.")
+
+ output = torch.empty((x.shape[0], scale.shape[2]), device=x.device, dtype=x.dtype)
+ kernel = _select_triton_kernel(
+ _gguf_q4_k_fused_matmul_kernel_small,
+ _gguf_q4_k_fused_matmul_kernel_large,
+ num_blocks=qs.shape[0],
+ )
+ return _launch(
+ kernel,
+ x,
+ output,
+ x,
+ qs,
+ scale,
+ min_value,
+ output,
+ x.shape[0],
+ output.shape[1],
+ qs.shape[0],
+ x.stride(0),
+ x.stride(1),
+ qs.stride(0),
+ qs.stride(1),
+ qs.stride(2),
+ scale.stride(0),
+ scale.stride(1),
+ scale.stride(2),
+ min_value.stride(0),
+ min_value.stride(1),
+ min_value.stride(2),
+ output.stride(0),
+ output.stride(1),
+ )
+
+
+def fused_q5_k_matmul(
+ x: torch.Tensor,
+ qs: torch.Tensor,
+ qh: torch.Tensor,
+ scale: torch.Tensor,
+ min_value: torch.Tensor,
+) -> torch.Tensor:
+ if not _TRITON_AVAILABLE:
+ raise RuntimeError("Triton is not available for GGUF Q5_K fused matmul.")
+
+ output = torch.empty((x.shape[0], scale.shape[2]), device=x.device, dtype=x.dtype)
+ kernel = _select_triton_kernel(
+ _gguf_q5_k_fused_matmul_kernel_small,
+ _gguf_q5_k_fused_matmul_kernel_large,
+ num_blocks=qs.shape[0],
+ )
+ return _launch(
+ kernel,
+ x,
+ output,
+ x,
+ qs,
+ qh,
+ scale,
+ min_value,
+ output,
+ x.shape[0],
+ output.shape[1],
+ qs.shape[0],
+ x.stride(0),
+ x.stride(1),
+ qs.stride(0),
+ qs.stride(1),
+ qs.stride(2),
+ qh.stride(0),
+ qh.stride(1),
+ qh.stride(2),
+ scale.stride(0),
+ scale.stride(1),
+ scale.stride(2),
+ min_value.stride(0),
+ min_value.stride(1),
+ min_value.stride(2),
+ output.stride(0),
+ output.stride(1),
+ )
+
+
+def fused_q6_k_matmul(
+ x: torch.Tensor,
+ ql: torch.Tensor,
+ qh: torch.Tensor,
+ scale: torch.Tensor,
+) -> torch.Tensor:
+ if not _TRITON_AVAILABLE:
+ raise RuntimeError("Triton is not available for GGUF Q6_K fused matmul.")
+
+ output = torch.empty((x.shape[0], scale.shape[2]), device=x.device, dtype=x.dtype)
+ kernel = _select_triton_kernel(
+ _gguf_q6_k_fused_matmul_kernel_small,
+ _gguf_q6_k_fused_matmul_kernel_large,
+ num_blocks=ql.shape[0],
+ )
+ return _launch(
+ kernel,
+ x,
+ output,
+ x,
+ ql,
+ qh,
+ scale,
+ output,
+ x.shape[0],
+ output.shape[1],
+ ql.shape[0],
+ x.stride(0),
+ x.stride(1),
+ ql.stride(0),
+ ql.stride(1),
+ ql.stride(2),
+ qh.stride(0),
+ qh.stride(1),
+ qh.stride(2),
+ scale.stride(0),
+ scale.stride(1),
+ scale.stride(2),
+ output.stride(0),
+ output.stride(1),
+ )
+
+
+class GGUFTritonKernel(GGUFTorchQuantLinear):
+ SUPPORTS_BACKENDS = [BACKEND.GGUF_TRITON]
+ SUPPORTS_METHODS = [METHOD.GGUF]
+ SUPPORTS_FORMATS = {FORMAT.GGUF: 45}
+ SUPPORTS_BITS = [4, 5, 6]
+ SUPPORTS_GROUP_SIZE = [-1]
+ SUPPORTS_DESC_ACT = [False]
+ SUPPORTS_SYM = [True]
+ SUPPORTS_SHARDS = True
+ SUPPORTS_TRAINING = False
+ SUPPORTS_AUTO_PADDING = True
+ SUPPORTS_IN_FEATURES_DIVISIBLE_BY = [1]
+ SUPPORTS_OUT_FEATURES_DIVISIBLE_BY = [1]
+ SUPPORTS_DEVICES = [DEVICE.CUDA]
+ SUPPORTS_PLATFORM = [PLATFORM.LINUX, PLATFORM.WIN32]
+ SUPPORTS_PACK_DTYPES = [torch.int8, torch.int16, torch.int32]
+ SUPPORTS_ADAPTERS = [Lora]
+ SUPPORTS_DTYPES = [torch.float16, torch.bfloat16, torch.float32]
+
+ REQUIRES_FORMAT_V2 = False
+ AUTOTUNE = False
+
+ QUANT_TYPE = "gguf"
+
+ def __init__(
+ self,
+ bits,
+ group_size: int,
+ sym: bool,
+ desc_act: bool,
+ in_features: int,
+ out_features: int,
+ bias: bool = False,
+ pack_dtype: torch.dtype = torch.int32,
+ adapter: Adapter = None,
+ register_buffers: bool = True,
+ **kwargs,
+ ):
+ kwargs.setdefault("backend", BACKEND.GGUF_TRITON)
+ super().__init__(
+ bits=bits,
+ group_size=group_size,
+ sym=sym,
+ desc_act=desc_act,
+ in_features=in_features,
+ out_features=out_features,
+ bias=bias,
+ pack_dtype=pack_dtype,
+ adapter=adapter,
+ register_buffers=register_buffers,
+ **kwargs,
+ )
+ if self.gguf_tensor_qtype not in {"Q4_K", "Q5_K", "Q6_K"}:
+ raise ValueError(
+ f"{self.__class__.__name__} only supports GGUF K-block formats "
+ f"(Q4_K, Q5_K, Q6_K). Actual GGUF qtype: {self.gguf_tensor_qtype}. "
+ "Use BACKEND.GGUF_TORCH or BACKEND.GGUF_CPP_* for non-K GGUF formats."
+ )
+ self._gguf_triton_cache: dict[tuple[int, str], dict[str, Any]] = {}
+
+ @classmethod
+ def validate_once(cls) -> Tuple[bool, Optional[Exception]]:
+ if not _TRITON_AVAILABLE:
+ raise ModuleNotFoundError("GGUFTritonKernel requires `triton` to be installed.")
+
+ triton_v = version.parse(triton.__version__)
+ if triton_v < version.parse("2.0.0"):
+ raise ImportError(f"triton version must be >= 2.0.0: actual = {triton.__version__}")
+
+ if has_gil_disabled() and triton_v < version.parse("3.4.0"):
+ raise Exception("GIL is disabled and not compatible with current Triton. Please upgrade to Triton >= 3.4.0")
+
+ return True, None
+
+ def clear_weight_cache(self) -> None:
+ self._gguf_triton_cache.clear()
+ return super().clear_weight_cache()
+
+ def _triton_cache_key(self, device: torch.device) -> tuple[int, str]:
+ return (device.index if device.index is not None else -1, self.gguf_tensor_qtype)
+
+ def _build_triton_cache(self, device: torch.device) -> dict[str, Any]:
+ blocks, _, _ = self._reshape_blocks(device=device)
+
+ if self.gguf_tensor_qtype == "Q4_K":
+ d = blocks[..., :2].contiguous().view(torch.float16).squeeze(-1)
+ dmin = blocks[..., 2:4].contiguous().view(torch.float16).squeeze(-1)
+ sc, mins = _unpack_q4_k_scale_min_torch(blocks[..., 4:16])
+ scale = (d.unsqueeze(-1) * sc.to(torch.float16)).permute(1, 2, 0).contiguous()
+ min_value = (dmin.unsqueeze(-1) * mins.to(torch.float16)).permute(1, 2, 0).contiguous()
+ qs = blocks[..., 16:].permute(1, 2, 0).contiguous()
+ return {
+ "qweight_ptr": self.qweight.data_ptr(),
+ "qs": qs,
+ "scale": scale,
+ "min": min_value,
+ }
+
+ if self.gguf_tensor_qtype == "Q5_K":
+ d = blocks[..., :2].contiguous().view(torch.float16).squeeze(-1)
+ dmin = blocks[..., 2:4].contiguous().view(torch.float16).squeeze(-1)
+ sc, mins = _unpack_q4_k_scale_min_torch(blocks[..., 4:16])
+ scale = (d.unsqueeze(-1) * sc.to(torch.float16)).permute(1, 2, 0).contiguous()
+ min_value = (dmin.unsqueeze(-1) * mins.to(torch.float16)).permute(1, 2, 0).contiguous()
+ qh = blocks[..., 16:48].permute(1, 2, 0).contiguous()
+ qs = blocks[..., 48:].permute(1, 2, 0).contiguous()
+ return {
+ "qweight_ptr": self.qweight.data_ptr(),
+ "qs": qs,
+ "qh": qh,
+ "scale": scale,
+ "min": min_value,
+ }
+
+ if self.gguf_tensor_qtype == "Q6_K":
+ ql = blocks[..., :128].permute(1, 2, 0).contiguous()
+ qh = blocks[..., 128:192].permute(1, 2, 0).contiguous()
+ scale = blocks[..., 192:208].contiguous().view(torch.int8).to(torch.float16)
+ d = blocks[..., 208:210].contiguous().view(torch.float16).squeeze(-1)
+ scale = (d.unsqueeze(-1) * scale).permute(1, 2, 0).contiguous()
+ return {
+ "qweight_ptr": self.qweight.data_ptr(),
+ "ql": ql,
+ "qh": qh,
+ "scale": scale,
+ }
+
+ raise NotImplementedError(f"Unsupported GGUF Triton qtype: {self.gguf_tensor_qtype}")
+
+ def _get_triton_cache(self, device: torch.device) -> dict[str, Any]:
+ key = self._triton_cache_key(device)
+ cached = self._gguf_triton_cache.get(key)
+ if cached is not None and cached.get("qweight_ptr") == self.qweight.data_ptr():
+ return cached
+
+ cached = self._build_triton_cache(device)
+ self._gguf_triton_cache[key] = cached
+ return cached
+
+ def _forward_triton(self, x_flat: torch.Tensor) -> torch.Tensor:
+ if x_flat.device.type != "cuda":
+ raise RuntimeError(
+ f"{self.__class__.__name__} only supports CUDA inference. "
+ "Load GGUF models on CUDA or use BACKEND.GGUF_TORCH for the torch fallback."
+ )
+
+ if x_flat.shape[-1] != self.padded_in_features:
+ x_work = torch.nn.functional.pad(x_flat, (0, self.padded_in_features - x_flat.shape[-1])).contiguous()
+ else:
+ x_work = x_flat.contiguous()
+
+ cache = self._get_triton_cache(x_work.device)
+
+ if self.gguf_tensor_qtype == "Q4_K":
+ return fused_q4_k_matmul(x_work, cache["qs"], cache["scale"], cache["min"])
+ if self.gguf_tensor_qtype == "Q5_K":
+ return fused_q5_k_matmul(x_work, cache["qs"], cache["qh"], cache["scale"], cache["min"])
+ if self.gguf_tensor_qtype == "Q6_K":
+ return fused_q6_k_matmul(x_work, cache["ql"], cache["qh"], cache["scale"])
+
+ raise NotImplementedError(f"Unsupported GGUF Triton qtype: {self.gguf_tensor_qtype}")
+
+ def forward(self, x: torch.Tensor):
+ original_shape = x.shape[:-1] + (self.out_features,)
+ x_flat = x.reshape(-1, x.shape[-1])
+
+ input_dtype = x_flat.dtype
+ if input_dtype != torch.float16:
+ x_work = x_flat.to(dtype=torch.float16)
+ else:
+ x_work = x_flat
+
+ output = self._forward_triton(x_work)
+
+ if self.bias is not None:
+ bias = self.bias
+ if bias.device != output.device or bias.dtype != output.dtype:
+ bias = bias.to(device=output.device, dtype=output.dtype)
+ output = output + bias
+
+ if input_dtype != output.dtype:
+ output = output.to(dtype=input_dtype)
+
+ if self.adapter:
+ output = self.adapter.apply(x=x_flat, out=output)
+
+ return output.reshape(original_shape)
+
+
+__all__ = [
+ "GGUFTritonKernel",
+ "fused_q4_k_matmul",
+ "fused_q5_k_matmul",
+ "fused_q6_k_matmul",
+ "triton_available",
+]
diff --git a/gptqmodel/nn_modules/qlinear/torch.py b/gptqmodel/nn_modules/qlinear/torch.py
index 74cb532b9..9cddca239 100644
--- a/gptqmodel/nn_modules/qlinear/torch.py
+++ b/gptqmodel/nn_modules/qlinear/torch.py
@@ -136,6 +136,13 @@ def post_init(self):
self.clear_weight_cache()
self._reset_prefetch_state()
+ @property
+ def weight(self):
+ # Some upstream model implementations only probe `weight.device` to
+ # select a fast path. Expose the packed buffer for that compatibility
+ # check without materializing a dense dequantized tensor.
+ return self.qweight
+
def dequantize_weight(self, num_itr: int = 1):
# Triton dequant currently handles the common single-iteration layout.
# Multi-iteration requests (num_itr > 1) are routed to the torch path below.
@@ -230,13 +237,21 @@ def _forward(self, x, out_shape):
def _forward_eager(self, x: torch.Tensor, out_shape):
num_itr = self.g_idx.shape[0] // x.shape[-1]
- weights = self._consume_prefetched_weights(x.dtype)
+ weights = self._consume_prefetched_weights(x.dtype, device=x.device)
if weights is None:
- weights = self.dequantize_weight(num_itr=num_itr).to(x.dtype)
+ weights = self.dequantize_weight(num_itr=num_itr)
+ if weights.device != x.device or weights.dtype != x.dtype:
+ # Quantized modules can be staged on a different accelerator than the
+ # caller tensor during multi-device kernel validation; matmul still
+ # needs both operands on the same device and dtype.
+ weights = weights.to(device=x.device, dtype=x.dtype)
self._update_cached_weights(weights)
out = torch.matmul(x, weights).reshape(out_shape)
if self.bias is not None:
- out.add_(self.bias)
+ bias = self.bias
+ if bias.device != out.device or bias.dtype != out.dtype:
+ bias = bias.to(device=out.device, dtype=out.dtype)
+ out.add_(bias)
if self.adapter:
out = self.adapter.apply(x=x, out=out)
@@ -320,13 +335,15 @@ def _update_cached_weights(self, weights: torch.Tensor):
return
self._cached_weights[weights.dtype] = weights.detach()
- def _consume_prefetched_weights(self, dtype: torch.dtype):
+ def _consume_prefetched_weights(self, dtype: torch.dtype, device: torch.device = None):
if not self._lookahead_enabled or self.training:
return None
tensor = self._prefetched_weights.pop(dtype, None)
if tensor is None:
return None
event = self._prefetch_events.pop(dtype, None)
+ if device is not None and tensor.device != device:
+ return None
if event is not None and HAS_CUDA and tensor.device.type == "cuda":
torch.cuda.current_stream(device=tensor.device).wait_event(event)
return tensor
diff --git a/gptqmodel/nn_modules/qlinear/torch_awq.py b/gptqmodel/nn_modules/qlinear/torch_awq.py
index 15748c777..813f26eab 100644
--- a/gptqmodel/nn_modules/qlinear/torch_awq.py
+++ b/gptqmodel/nn_modules/qlinear/torch_awq.py
@@ -80,6 +80,60 @@ def extra_repr(self) -> str:
f"bias={self.bias is not None}, bits={self.bits}, group_size={self.group_size}"
)
+ def pack(self, linear: torch.nn.Module, scales: torch.Tensor, zeros: torch.Tensor, g_idx: torch.Tensor = None):
+ del g_idx
+ assert scales is not None and zeros is not None
+
+ scales = scales.t().contiguous()
+ zeros = zeros.t().contiguous()
+ scale_zeros = zeros * scales
+
+ self.register_buffer("scales", scales.clone().half())
+ if linear.bias is not None:
+ self.register_buffer("bias", linear.bias.clone().half())
+ else:
+ self.bias = None
+
+ pack_num = 32 // self.bits
+
+ intweight = []
+ for idx in range(self.in_features):
+ intweight.append(
+ torch.round(
+ (linear.weight.data[:, idx] + scale_zeros[idx // self.group_size])
+ / self.scales[idx // self.group_size]
+ ).to(torch.int32)[:, None]
+ )
+ intweight = torch.cat(intweight, dim=1).t().contiguous()
+
+ qweight = torch.zeros(
+ (intweight.shape[0], intweight.shape[1] // 32 * self.bits),
+ dtype=torch.int32,
+ device=intweight.device,
+ )
+ qzeros = torch.zeros(
+ (zeros.shape[0], zeros.shape[1] // 32 * self.bits),
+ dtype=torch.int32,
+ device=zeros.device,
+ )
+
+ if self.bits != 4:
+ raise NotImplementedError("Only 4-bit are supported for now.")
+ order_map = [0, 2, 4, 6, 1, 3, 5, 7]
+
+ for col in range(intweight.shape[1] // pack_num):
+ for i in range(pack_num):
+ qweight_col = intweight[:, col * pack_num + order_map[i]]
+ qweight[:, col] |= qweight_col << (i * self.bits)
+
+ for col in range(zeros.shape[1] // pack_num):
+ for i in range(pack_num):
+ qzero_col = zeros[:, col * pack_num + order_map[i]].to(torch.int32)
+ qzeros[:, col] |= qzero_col << (i * self.bits)
+
+ self.register_buffer("qweight", qweight)
+ self.register_buffer("qzeros", qzeros)
+
def forward(self, x: torch.Tensor):
original_shape = x.shape[:-1] + (self.out_features,)
device = x.device
diff --git a/gptqmodel/quantization/__init__.py b/gptqmodel/quantization/__init__.py
index 6cb608ca3..860310d24 100644
--- a/gptqmodel/quantization/__init__.py
+++ b/gptqmodel/quantization/__init__.py
@@ -4,18 +4,33 @@
# Contact: qubitium@modelcloud.ai, x.com/qubitium
from .config import (
+ AWQQuantizeConfig,
+ BaseComplexBits,
+ BasePreFilterConfig,
FORMAT,
FORMAT_FIELD_CHECKPOINT,
FORMAT_FIELD_CODE,
+ EXL3QuantizeConfig,
+ FP8Config,
+ FP8QuantizeConfig,
+ GGUFConfig,
+ GGUFQuantizeConfig,
+ GGUFBits,
+ GPTQQuantizeConfig,
METHOD,
+ METHOD_FIELD_CODE,
+ PreFilterCode,
+ PreFilterQuantizeConfig,
QUANT_CONFIG_FILENAME,
QUANT_METHOD_FIELD,
BaseQuantizeConfig,
- FailSafe,
- FailSafeStrategy,
+ Fallback,
+ FallbackStrategy,
GPTAQConfig,
HessianConfig,
+ QuantBits,
QuantizeConfig,
+ RTNQuantizeConfig,
SmoothLog,
SmoothMAD,
SmoothMethod,
@@ -25,7 +40,28 @@
SmoothPercentileAsymmetric,
SmoothRowCol,
SmoothSoftNorm,
+ SmootherConfig,
+ WeightOnlyConfig,
+ WeightOnlyMethod,
)
from .gptaq import GPTAQ
from .gptq import GPTQ
+from .protocol import (
+ ExecutionPlan,
+ ExportSpec,
+ MatchSpec,
+ OperationSpec,
+ QuantizeSpec,
+ Rule,
+ Stage,
+ TargetSpec,
+ compile_plan_to_quantize_config,
+ compile_protocol,
+ compile_protocol_to_quantize_config,
+ compile_protocol_yaml_file,
+ compile_protocol_yaml_text,
+ compile_protocol_yaml_to_quantize_config,
+ skip,
+)
from .quantizer import Quantizer, quantize
+from .rtn import RTN
diff --git a/gptqmodel/quantization/config.py b/gptqmodel/quantization/config.py
index 6b8167673..1d14f9b61 100644
--- a/gptqmodel/quantization/config.py
+++ b/gptqmodel/quantization/config.py
@@ -3,10 +3,14 @@
# SPDX-License-Identifier: Apache-2.0
# Contact: qubitium@modelcloud.ai, x.com/qubitium
+import copy
import json
+import math
import os.path
+from abc import ABC, abstractmethod
from dataclasses import asdict, dataclass, field, fields
from enum import Enum
+from functools import total_ordering
from os.path import join
from typing import Any, Dict, List, Optional, Tuple, Union
@@ -25,8 +29,12 @@
GROUP_SIZE_FIELD_CODE = "group_size"
FORMAT_FIELD_CODE = "format"
SYMMETRIC_FIELD_CODE = "sym"
+# Deprecated JSON alias retained for backward compatibility.
FORMAT_FIELD_CHECKPOINT = "checkpoint_format"
+# Hard-deprecated legacy alias. Presence should fail fast.
FORMAT_FIELD_COMPAT_MARLIN = "is_marlin_format"
+# Canonical method field; `quant_method` is a deprecated JSON alias.
+METHOD_FIELD_CODE = "method"
QUANT_METHOD_FIELD = "quant_method"
PACK_DTYPE_FIELD = "pack_dtype"
QUANT_CONFIG_FILENAME = "quantize_config.json"
@@ -64,9 +72,12 @@ class FORMAT(str, Enum):
GPTQ = "gptq"
# v2 format fixed sym = False quantization
GPTQ_V2 = "gptq_v2"
+ GGUF = "gguf"
+ FP8 = "fp8"
MARLIN = "marlin"
BITBLAS = "bitblas"
QQQ = "qqq"
+ EXL3 = "exl3"
GEMM = "gemm"
GEMV = "gemv"
@@ -77,8 +88,11 @@ class FORMAT(str, Enum):
# quant methods
class METHOD(str, Enum):
GPTQ = "gptq"
+ GGUF = "gguf"
+ FP8 = "fp8"
QQQ = "qqq"
AWQ = "awq"
+ EXL3 = "exl3"
class VramStrategy(str, Enum):
@@ -86,7 +100,7 @@ class VramStrategy(str, Enum):
BALANCED = "balanced"
-class FailSafeStrategy(str, Enum):
+class FallbackStrategy(str, Enum):
"""
+-----------+----------------------+---------------------------+------------------------------+
| strategy | center | scale | strengths / weaknesses |
@@ -104,6 +118,533 @@ class FailSafeStrategy(str, Enum):
MEDIAN = "median"
STDCLIP = "stdclip"
+
+class WeightOnlyMethod(str, Enum):
+ RTN = "rtn"
+ GGUF = "gguf"
+ FP8 = "fp8"
+ NVFP4 = "nvfp4"
+
+
+class PreFilterCode(str, Enum):
+ SMOOTHER = "smoother"
+
+
+_GGUF_BITS_ALIAS_INFO = {
+ "q4_0": {"bits": 4, "version": "q", "variant": "0", "quality": None},
+ "q8_0": {"bits": 8, "version": "q", "variant": "0", "quality": None},
+ "q4_k": {"bits": 4, "version": "q", "variant": "k", "quality": None},
+ "q4_k_s": {"bits": 4, "version": "q", "variant": "k", "quality": "s"},
+ "q4_k_m": {"bits": 4, "version": "q", "variant": "k", "quality": "m"},
+ "q5_k": {"bits": 5, "version": "q", "variant": "k", "quality": None},
+ "q5_k_s": {"bits": 5, "version": "q", "variant": "k", "quality": "s"},
+ "q5_k_m": {"bits": 5, "version": "q", "variant": "k", "quality": "m"},
+ "q6_k": {"bits": 6, "version": "q", "variant": "k", "quality": None},
+}
+_GGUF_DEFAULT_BITS_ALIAS_BY_WIDTH = {
+ 4: "q4_0",
+ 5: "q5_k_m",
+ 6: "q6_k",
+ 8: "q8_0",
+}
+_GGUF_APPROX_BITS_PER_WEIGHT_BY_ALIAS = {
+ "q4_0": 4.5,
+ "q8_0": 8.5,
+ "q4_k": 4.5,
+ "q4_k_s": 4.5,
+ "q4_k_m": 4.5,
+ "q5_k": 5.5,
+ "q5_k_s": 5.0,
+ "q5_k_m": 5.5,
+ "q6_k": 6.0,
+}
+
+
+@total_ordering
+class BaseComplexBits(ABC):
+ @classmethod
+ @abstractmethod
+ def from_string(cls, value: str) -> "BaseComplexBits":
+ raise NotImplementedError
+
+ @abstractmethod
+ def to_string(self) -> str:
+ raise NotImplementedError
+
+ @property
+ def width(self) -> int:
+ return self.bits
+
+ @property
+ def name(self) -> str:
+ return self.to_string()
+
+ def _coerce_bits(self, other: Any) -> Any:
+ if isinstance(other, BaseComplexBits):
+ return other.bits
+ if isinstance(other, int):
+ return other
+ if isinstance(other, str) and other.strip().isdigit():
+ return int(other.strip())
+ return NotImplemented
+
+ def __str__(self) -> str:
+ return self.to_string()
+
+ def __hash__(self) -> int:
+ return hash(self.bits)
+
+ def __int__(self) -> int:
+ return self.bits
+
+ def __index__(self) -> int:
+ return self.bits
+
+ def __float__(self) -> float:
+ return float(self.bits)
+
+ def __eq__(self, other: Any) -> bool:
+ if isinstance(other, BaseComplexBits):
+ return self.to_string() == other.to_string()
+ if isinstance(other, int):
+ return self.bits == other
+ if isinstance(other, str):
+ normalized = other.strip().lower().replace("-", "_")
+ if normalized.isdigit():
+ return self.bits == int(normalized)
+ return self.to_string() == normalized
+ return False
+
+ def __lt__(self, other: Any) -> bool:
+ coerced = self._coerce_bits(other)
+ if coerced is NotImplemented:
+ return NotImplemented
+ return self.bits < coerced
+
+ def __add__(self, other: Any) -> int:
+ coerced = self._coerce_bits(other)
+ if coerced is NotImplemented:
+ return NotImplemented
+ return self.bits + coerced
+
+ def __radd__(self, other: Any) -> int:
+ return self.__add__(other)
+
+ def __sub__(self, other: Any) -> int:
+ coerced = self._coerce_bits(other)
+ if coerced is NotImplemented:
+ return NotImplemented
+ return self.bits - coerced
+
+ def __rsub__(self, other: Any) -> int:
+ coerced = self._coerce_bits(other)
+ if coerced is NotImplemented:
+ return NotImplemented
+ return coerced - self.bits
+
+ def __mul__(self, other: Any) -> int:
+ coerced = self._coerce_bits(other)
+ if coerced is NotImplemented:
+ return NotImplemented
+ return self.bits * coerced
+
+ def __rmul__(self, other: Any) -> int:
+ return self.__mul__(other)
+
+ def __floordiv__(self, other: Any) -> int:
+ coerced = self._coerce_bits(other)
+ if coerced is NotImplemented:
+ return NotImplemented
+ return self.bits // coerced
+
+ def __rfloordiv__(self, other: Any) -> int:
+ coerced = self._coerce_bits(other)
+ if coerced is NotImplemented:
+ return NotImplemented
+ return coerced // self.bits
+
+ def __truediv__(self, other: Any) -> float:
+ coerced = self._coerce_bits(other)
+ if coerced is NotImplemented:
+ return NotImplemented
+ return self.bits / coerced
+
+ def __rtruediv__(self, other: Any) -> float:
+ coerced = self._coerce_bits(other)
+ if coerced is NotImplemented:
+ return NotImplemented
+ return coerced / self.bits
+
+ def __mod__(self, other: Any) -> int:
+ coerced = self._coerce_bits(other)
+ if coerced is NotImplemented:
+ return NotImplemented
+ return self.bits % coerced
+
+ def __rmod__(self, other: Any) -> int:
+ coerced = self._coerce_bits(other)
+ if coerced is NotImplemented:
+ return NotImplemented
+ return coerced % self.bits
+
+ def __pow__(self, other: Any) -> int:
+ coerced = self._coerce_bits(other)
+ if coerced is NotImplemented:
+ return NotImplemented
+ return self.bits ** coerced
+
+ def __rpow__(self, other: Any) -> int:
+ coerced = self._coerce_bits(other)
+ if coerced is NotImplemented:
+ return NotImplemented
+ return coerced ** self.bits
+
+
+@dataclass(frozen=True, eq=False)
+class GGUFBits(BaseComplexBits):
+ bits: int
+ version: str
+ variant: str
+ quality: Optional[str] = None
+
+ def __post_init__(self):
+ if self.bits <= 0:
+ raise ValueError("GGUFBits: `bits` must be a positive integer.")
+ if self.version not in {"q", "iq"}:
+ raise ValueError("GGUFBits: `version` must be `q` or `iq`.")
+ if self.variant not in {"0", "k"}:
+ raise ValueError("GGUFBits: `variant` must be `0` or `k`.")
+ if self.quality not in {None, "xs", "s", "m", "l"}:
+ raise ValueError("GGUFBits: `quality` must be one of `[None, xs, s, m, l]`.")
+
+ @classmethod
+ def from_string(cls, value: str) -> "GGUFBits":
+ normalized = str(value).strip().lower().replace("-", "_")
+ info = _GGUF_BITS_ALIAS_INFO.get(normalized)
+ if info is None:
+ supported = ", ".join(sorted(_GGUF_BITS_ALIAS_INFO))
+ raise ValueError(f"Unsupported GGUF bits `{value}`. Supported values: {supported}.")
+ return cls(
+ bits=info["bits"],
+ version=info["version"],
+ variant=info["variant"],
+ quality=info["quality"],
+ )
+
+ def to_string(self) -> str:
+ alias = f"{self.version}{self.bits}_{self.variant}"
+ if self.quality is not None:
+ alias = f"{alias}_{self.quality}"
+ return alias
+
+ @classmethod
+ def from_alias(cls, value: str) -> "GGUFBits":
+ return cls.from_string(value)
+
+ def serialize(self) -> str:
+ return self.to_string()
+
+ def __repr__(self) -> str:
+ return f"GGUFBits({self.to_string()!r})"
+
+ def to_public_format(self) -> str:
+ public_format = f"{self.version}_{self.variant}"
+ if self.quality is not None:
+ public_format = f"{public_format}_{self.quality}"
+ return public_format
+
+
+# Backward-compatible alias for the earlier wrapper-based refactor.
+QuantBits = GGUFBits
+
+
+_GGUF_PUBLIC_FORMAT_RE = re.compile(r"^(q|iq)_(0|k)(?:_(xs|s|m|l))?$")
+
+
+def _gguf_public_format_from_bits(bits: GGUFBits) -> str:
+ return bits.to_public_format()
+
+
+def _normalize_gguf_public_format(value: Any) -> Optional[str]:
+ if value is None:
+ return None
+
+ if isinstance(value, GGUFBits):
+ return _gguf_public_format_from_bits(value)
+
+ if isinstance(value, FORMAT):
+ value = value.value
+
+ normalized = str(value).strip().lower().replace("-", "_")
+ if normalized in {"", FORMAT.GGUF.value}:
+ return None
+ if normalized in _GGUF_BITS_ALIAS_INFO:
+ return _gguf_public_format_from_bits(GGUFBits.from_alias(normalized))
+ if _GGUF_PUBLIC_FORMAT_RE.fullmatch(normalized):
+ return normalized
+
+ raise ValueError(
+ "GGUFConfig: `format` must be a GGUF subtype like `q_0`, `q_k`, `q_k_s`, or `q_k_m`."
+ )
+
+
+def _default_gguf_public_format(bits: int) -> str:
+ alias = _GGUF_DEFAULT_BITS_ALIAS_BY_WIDTH.get(bits)
+ if alias is None:
+ raise ValueError(f"GGUFConfig: no default GGUF format exists for `{bits}`-bit quantization.")
+ return _gguf_public_format_from_bits(GGUFBits.from_alias(alias))
+
+
+def _gguf_bits_from_components(bits: int, public_format: str) -> GGUFBits:
+ match = _GGUF_PUBLIC_FORMAT_RE.fullmatch(public_format)
+ if match is None:
+ raise ValueError(
+ "GGUFConfig: `format` must be a GGUF subtype like `q_0`, `q_k`, `q_k_s`, or `q_k_m`."
+ )
+
+ version_name, variant, quality = match.groups()
+ bits_spec = GGUFBits(bits=bits, version=version_name, variant=variant, quality=quality)
+ if bits_spec.to_string() not in _GGUF_BITS_ALIAS_INFO:
+ raise ValueError(
+ f"Unsupported GGUF combination: bits={bits}, format={public_format}."
+ )
+ return bits_spec
+
+
+def _normalize_gguf_config_spec(
+ bits: Union[int, str, GGUFBits],
+ format_value: Optional[Union[str, FORMAT, GGUFBits]],
+) -> Tuple[int, str, GGUFBits]:
+ bits_spec_from_bits: Optional[GGUFBits] = None
+ normalized_bits = bits
+
+ if isinstance(bits, GGUFBits):
+ bits_spec_from_bits = bits
+ normalized_bits = bits.bits
+ elif isinstance(bits, str):
+ raw_bits = bits.strip().lower().replace("-", "_")
+ if raw_bits.isdigit():
+ normalized_bits = int(raw_bits)
+ else:
+ bits_spec_from_bits = GGUFBits.from_alias(raw_bits)
+ normalized_bits = bits_spec_from_bits.bits
+ elif not isinstance(bits, int):
+ raise ValueError(f"GGUFConfig: unsupported bits specification `{bits}`.")
+
+ normalized_bits = int(normalized_bits)
+ if normalized_bits not in [2, 3, 4, 5, 6, 8]:
+ raise ValueError("GGUFConfig: `bits` must resolve to one of `[2, 3, 4, 5, 6, 8]`.")
+
+ normalized_format = _normalize_gguf_public_format(format_value)
+ if normalized_format is None:
+ if bits_spec_from_bits is not None:
+ bits_spec = bits_spec_from_bits
+ normalized_format = _gguf_public_format_from_bits(bits_spec)
+ else:
+ normalized_format = _default_gguf_public_format(normalized_bits)
+ bits_spec = _gguf_bits_from_components(normalized_bits, normalized_format)
+ else:
+ bits_spec = _gguf_bits_from_components(normalized_bits, normalized_format)
+ if bits_spec_from_bits is not None and bits_spec_from_bits != bits_spec:
+ raise ValueError(
+ f"GGUFConfig: incompatible GGUF bits/format combination: bits={bits}, format={format_value}."
+ )
+
+ return normalized_bits, normalized_format, bits_spec
+
+
+def _normalize_quant_bits(bits: Union[int, float, str, GGUFBits], format_value: Optional[Union[str, FORMAT]] = None) -> Union[int, GGUFBits]:
+ if isinstance(format_value, str):
+ format_value = _normalize_format(format_value)
+
+ if isinstance(bits, GGUFBits):
+ normalized = bits
+ elif isinstance(bits, float):
+ if format_value == FORMAT.EXL3:
+ normalized = bits
+ elif bits.is_integer():
+ normalized = int(bits)
+ else:
+ raise ValueError(f"QuantizeConfig: unsupported bits specification `{bits}`.")
+ elif isinstance(bits, int):
+ normalized = bits
+ elif isinstance(bits, str):
+ raw = bits.strip().lower().replace("-", "_")
+ normalized = int(raw) if raw.isdigit() else GGUFBits.from_alias(raw)
+ else:
+ raise ValueError(f"QuantizeConfig: unsupported bits specification `{bits}`.")
+
+ normalized_width = normalized.bits if isinstance(normalized, GGUFBits) else normalized
+ if normalized_width not in [2, 3, 4, 5, 6, 8]:
+ raise ValueError("QuantizeConfig: `bits` must resolve to one of `[2, 3, 4, 5, 6, 8]`.")
+
+ if format_value == FORMAT.GGUF and not isinstance(normalized, GGUFBits):
+ default_alias = _GGUF_DEFAULT_BITS_ALIAS_BY_WIDTH.get(normalized_width)
+ if default_alias is None:
+ raise ValueError(
+ f"QuantizeConfig: no default GGUF bits alias exists for `{normalized_width}`-bit quantization."
+ )
+ normalized = GGUFBits.from_alias(default_alias)
+
+ if isinstance(normalized, GGUFBits) and format_value is not None and format_value != FORMAT.GGUF:
+ raise ValueError("QuantizeConfig: GGUF bit encodings require `format=gguf`.")
+
+ return normalized
+
+
+def resolve_quant_format(
+ format_value: Optional[Union[str, FORMAT]],
+ method: Optional[Union[str, METHOD]] = None,
+ quant_method: Optional[Union[str, METHOD]] = None,
+) -> FORMAT:
+ if method is None:
+ method = quant_method
+
+ if isinstance(method, str):
+ method = _normalize_quant_method(method)
+
+ if method == METHOD.GGUF:
+ return FORMAT.GGUF
+ if method == METHOD.FP8:
+ return FORMAT.FP8
+ if method == METHOD.EXL3:
+ return FORMAT.EXL3
+
+ if isinstance(format_value, FORMAT):
+ return format_value
+
+ try:
+ if _normalize_gguf_public_format(format_value) is not None:
+ return FORMAT.GGUF
+ except ValueError:
+ pass
+
+ if _looks_like_fp8_fmt(format_value):
+ return FORMAT.FP8
+
+ if format_value is None:
+ return FORMAT.GPTQ
+
+ return _normalize_format(format_value)
+
+
+def _looks_like_gguf_bits(bits: Any) -> bool:
+ if isinstance(bits, GGUFBits):
+ return True
+ if not isinstance(bits, str):
+ return False
+ normalized = bits.strip().lower().replace("-", "_")
+ return normalized in _GGUF_BITS_ALIAS_INFO
+
+
+def quant_bits_width(bits: Union[int, str, GGUFBits]) -> int:
+ if isinstance(bits, float):
+ if bits <= 0:
+ raise ValueError("QuantizeConfig: EXL3 bits per weight must be greater than 0.")
+ return max(1, int(math.floor(bits)))
+ normalized = _normalize_quant_bits(bits)
+ return normalized.bits if isinstance(normalized, GGUFBits) else normalized
+
+
+def serialize_quant_bits(bits: Union[int, str, GGUFBits]) -> Union[int, str]:
+ if isinstance(bits, float):
+ return float(bits)
+ normalized = _normalize_quant_bits(bits)
+ return normalized.serialize() if isinstance(normalized, GGUFBits) else normalized
+
+
+def _normalize_exl3_bits(bits: Union[int, float, str]) -> float:
+ if isinstance(bits, str):
+ bits = float(bits.strip())
+ elif isinstance(bits, int):
+ bits = float(bits)
+ elif not isinstance(bits, float):
+ raise ValueError(f"EXL3QuantizeConfig: unsupported bits specification `{bits}`.")
+
+ if not math.isfinite(bits):
+ raise ValueError("EXL3QuantizeConfig: `bits` must be finite.")
+ if bits < 1.0 or bits > 8.0:
+ raise ValueError("EXL3QuantizeConfig: `bits` must be between 1.0 and 8.0.")
+ return float(bits)
+
+
+_FP8_FMT_ALIASES = {
+ "e4m3": "float8_e4m3fn",
+ "float8_e4m3": "float8_e4m3fn",
+ "float8_e4m3fn": "float8_e4m3fn",
+ "e5m2": "float8_e5m2",
+ "float8_e5m2": "float8_e5m2",
+}
+_FP8_WEIGHT_SCALE_METHODS = {"tensor", "row", "block"}
+_FP8_SCALE_SEMANTICS = {"inverse"}
+
+
+def _looks_like_fp8_fmt(value: Any) -> bool:
+ if value is None:
+ return False
+ normalized = str(value).strip().lower()
+ return normalized in _FP8_FMT_ALIASES
+
+
+def _normalize_fp8_fmt(value: Optional[str]) -> str:
+ if isinstance(value, FORMAT):
+ if value != FORMAT.FP8:
+ raise ValueError(f"FP8Config: unsupported `format` `{value}`.")
+ value = None
+
+ normalized = "float8_e4m3fn" if value is None else str(value).strip().lower()
+ if normalized in {"", FORMAT.FP8.value}:
+ normalized = "float8_e4m3fn"
+ resolved = _FP8_FMT_ALIASES.get(normalized)
+ if resolved is None:
+ supported = ", ".join(sorted(_FP8_FMT_ALIASES))
+ raise ValueError(f"FP8Config: unsupported `format` `{value}`. Supported values: {supported}.")
+ if not hasattr(torch, resolved):
+ raise ValueError(f"FP8Config: current PyTorch build does not provide `{resolved}`.")
+ return resolved
+
+
+def _normalize_fp8_weight_block_size(value: Optional[Union[List[int], Tuple[int, int]]]) -> Optional[Tuple[int, int]]:
+ if value is None:
+ return None
+ if not isinstance(value, (list, tuple)) or len(value) != 2:
+ raise ValueError("FP8Config: `weight_block_size` must be a 2-item list/tuple or None.")
+ rows, cols = int(value[0]), int(value[1])
+ if rows <= 0 or cols <= 0:
+ raise ValueError("FP8Config: `weight_block_size` entries must be positive integers.")
+ return rows, cols
+
+
+def _normalize_fp8_weight_scale_method(
+ value: Optional[str],
+ *,
+ weight_block_size: Optional[Tuple[int, int]],
+) -> str:
+ normalized = "block" if weight_block_size is not None and value is None else (value or "row")
+ normalized = str(normalized).strip().lower()
+ if normalized not in _FP8_WEIGHT_SCALE_METHODS:
+ supported = ", ".join(sorted(_FP8_WEIGHT_SCALE_METHODS))
+ raise ValueError(
+ f"FP8Config: `weight_scale_method` must be one of {{{supported}}}, got `{value}`."
+ )
+ if normalized == "block" and weight_block_size is None:
+ raise ValueError("FP8Config: `weight_scale_method='block'` requires `weight_block_size`.")
+ if normalized != "block" and weight_block_size is not None:
+ raise ValueError(
+ "FP8Config: `weight_block_size` is only valid when `weight_scale_method='block'`."
+ )
+ return normalized
+
+
+def _normalize_fp8_scale_semantics(value: Optional[str]) -> str:
+ normalized = "inverse" if value is None else str(value).strip().lower()
+ if normalized not in _FP8_SCALE_SEMANTICS:
+ supported = ", ".join(sorted(_FP8_SCALE_SEMANTICS))
+ raise ValueError(
+ f"FP8Config: `weight_scale_semantics` must be one of {{{supported}}}, got `{value}`."
+ )
+ return normalized
+
@dataclass
class SmoothMethod:
name: str
@@ -277,18 +818,62 @@ class GcMode(str, Enum):
@dataclass
-class FailSafe:
- strategy: FailSafeStrategy = FailSafeStrategy.RTN # enable failsafe by default due to moe routing behavior breaking calibration based quantization
+class Fallback:
+ strategy: FallbackStrategy = FallbackStrategy.RTN # enable fallback by default due to moe routing behavior breaking calibration based quantization
# int/float = if captured module fwd tokens is less than value, trigger strategy
# string = if string is int/float followed by %, then if captured module fwd tokens is less than value in percentage relative to calibration, trigger strategy
- threshold: int | float | str = "0.5%" # if less than 0.5% of calibration reaches module (think moe) then we trigger per-module failsafe quantization
+ threshold: int | float | str = "0.5%" # if less than 0.5% of calibration reaches module (think moe) then we trigger per-module fallback quantization
# Smoothers can help some low-sample fallback cases, but a static default can
# hurt whole-model RTN quality. Leave smoothing opt-in.
smooth: Optional[SmoothMethod] = None
+@dataclass
+class WeightOnlyConfig:
+ method: WeightOnlyMethod = WeightOnlyMethod.RTN
+ # Whole-model RTN is noticeably more stable without a smoother by default.
+ smooth: Optional[SmoothMethod] = None
+
+ def __post_init__(self):
+ if isinstance(self.method, str):
+ try:
+ self.method = WeightOnlyMethod(self.method.lower())
+ except ValueError as exc:
+ raise ValueError(
+ f"WeightOnlyConfig: `method` must be one of {[v.value for v in WeightOnlyMethod]}."
+ ) from exc
+ elif not isinstance(self.method, WeightOnlyMethod):
+ raise ValueError(
+ f"WeightOnlyConfig: `method` must be one of {[v.value for v in WeightOnlyMethod]}."
+ )
+
+ self.smooth = _parse_smooth_method(self.smooth)
+
+
+@dataclass
+class BasePreFilterConfig:
+ code: str
+
+ def to_dict(self) -> Dict[str, Any]:
+ return {"code": self.code}
+
+
+@dataclass
+class SmootherConfig(BasePreFilterConfig):
+ smooth: Optional[SmoothMethod] = None
+ code: str = field(default=PreFilterCode.SMOOTHER.value, init=False)
+
+ def __post_init__(self):
+ self.smooth = _parse_smooth_method(self.smooth)
+
+ def to_dict(self) -> Dict[str, Any]:
+ payload = super().to_dict()
+ payload["smooth"] = _serialize_smooth_method(self.smooth)
+ return payload
+
+
@dataclass
class HessianConfig:
# Hessian accumulation controls (GPTQ only)
@@ -458,6 +1043,15 @@ def to_dict(self) -> Dict[str, Any]:
FORMAT.MARLIN,
FORMAT.BITBLAS,
},
+ METHOD.FP8: {
+ FORMAT.FP8,
+ },
+ METHOD.EXL3: {
+ FORMAT.EXL3,
+ },
+ METHOD.GGUF: {
+ FORMAT.GGUF,
+ },
METHOD.QQQ: {
FORMAT.QQQ,
},
@@ -470,6 +1064,54 @@ def to_dict(self) -> Dict[str, Any]:
},
}
+GPTQ_EXPORT_FORMATS: Tuple[FORMAT, ...] = (
+ FORMAT.GPTQ,
+ FORMAT.GPTQ_V2,
+ FORMAT.MARLIN,
+ FORMAT.BITBLAS,
+)
+AWQ_EXPORT_FORMATS: Tuple[FORMAT, ...] = (
+ FORMAT.GEMM,
+ FORMAT.GEMV,
+ FORMAT.GEMV_FAST,
+ FORMAT.MARLIN,
+ FORMAT.LLM_AWQ,
+)
+QQQ_EXPORT_FORMATS: Tuple[FORMAT, ...] = (
+ FORMAT.QQQ,
+)
+FP8_EXPORT_FORMATS: Tuple[FORMAT, ...] = (
+ FORMAT.FP8,
+)
+EXL3_EXPORT_FORMATS: Tuple[FORMAT, ...] = (
+ FORMAT.EXL3,
+)
+RTN_EXPORT_FORMATS: Tuple[FORMAT, ...] = (
+ FORMAT.GPTQ,
+ FORMAT.GPTQ_V2,
+ FORMAT.GEMM,
+ FORMAT.GEMV,
+ FORMAT.GEMV_FAST,
+ FORMAT.LLM_AWQ,
+)
+GGUF_EXPORT_FORMATS: Tuple[FORMAT, ...] = (
+ FORMAT.GGUF,
+)
+
+_UNAMBIGUOUS_EXPORT_METHOD_BY_FORMAT = {
+ FORMAT.GPTQ: METHOD.GPTQ,
+ FORMAT.GPTQ_V2: METHOD.GPTQ,
+ FORMAT.FP8: METHOD.FP8,
+ FORMAT.EXL3: METHOD.EXL3,
+ FORMAT.GGUF: METHOD.GGUF,
+ FORMAT.BITBLAS: METHOD.GPTQ,
+ FORMAT.GEMM: METHOD.AWQ,
+ FORMAT.GEMV: METHOD.AWQ,
+ FORMAT.GEMV_FAST: METHOD.AWQ,
+ FORMAT.LLM_AWQ: METHOD.AWQ,
+ FORMAT.QQQ: METHOD.QQQ,
+}
+
# inference only methods should go here
QUANTIZE_BLACK_LIST = {}
@@ -484,8 +1126,9 @@ def to_dict(self) -> Dict[str, Any]:
# AWQ compat
"version" : FORMAT_FIELD_CODE,
- # map format field (checkpoint_format) to class/code (format)
+ # map deprecated aliases to canonical fields
FORMAT_FIELD_CHECKPOINT: FORMAT_FIELD_CODE,
+ QUANT_METHOD_FIELD: METHOD_FIELD_CODE,
}
# compat (values are negated)
@@ -572,7 +1215,76 @@ def _parse_smooth_method(setting: Any) -> Optional[SmoothMethod]:
return _build_smooth_method_from_dict({"type": setting})
if isinstance(setting, dict):
return _build_smooth_method_from_dict(setting)
- raise ValueError("QuantizeConfig: `failsafe.smooth` must be a SmoothMethod, string, or dict.")
+ raise ValueError("QuantizeConfig: `fallback.smooth` must be a SmoothMethod, string, or dict.")
+
+
+def _serialize_smooth_method(method: Optional[SmoothMethod]) -> Optional[Dict[str, Any]]:
+ if method is None:
+ return None
+
+ payload = {"type": method.name, "group_size_threshold": method.group_size_threshold}
+ if isinstance(method, SmoothPercentile):
+ payload["percentile"] = method.percentile
+ elif isinstance(method, SmoothPercentileAsymmetric):
+ payload["low"] = method.low
+ payload["high"] = method.high
+ elif isinstance(method, SmoothMAD):
+ payload["k"] = method.k
+ elif isinstance(method, SmoothMSE):
+ payload["steps"] = method.steps
+ payload["maxshrink"] = method.maxshrink
+ elif isinstance(method, SmoothOutlier):
+ payload["pct"] = method.pct
+ elif isinstance(method, SmoothSoftNorm):
+ payload["k"] = method.k
+ elif isinstance(method, SmoothLog):
+ payload["percentile"] = method.percentile
+ payload["mu"] = method.mu
+ elif isinstance(method, SmoothRowCol):
+ payload["axis"] = method.axis
+ return payload
+
+
+def _normalize_smoother_config(
+ payload: Optional[Union[SmootherConfig, SmoothMethod, Dict[str, Any], str]]
+) -> Optional[SmootherConfig]:
+ if payload is None:
+ return None
+ if isinstance(payload, SmootherConfig):
+ return payload
+ if isinstance(payload, dict) and "smooth" in payload and "type" not in payload:
+ return SmootherConfig(smooth=payload.get("smooth"))
+ return SmootherConfig(smooth=payload)
+
+
+def _normalize_pre_filter_config(payload: Any) -> BasePreFilterConfig:
+ if isinstance(payload, BasePreFilterConfig):
+ return payload
+ if isinstance(payload, SmoothMethod):
+ return SmootherConfig(smooth=payload)
+ if isinstance(payload, str):
+ normalized = payload.strip().lower()
+ if normalized == PreFilterCode.SMOOTHER.value:
+ return SmootherConfig(smooth=None)
+ return SmootherConfig(smooth=payload)
+ if isinstance(payload, dict):
+ code = str(payload.get("code", "")).strip().lower()
+ if code and code != PreFilterCode.SMOOTHER.value:
+ raise ValueError(f"QuantizeConfig: unsupported pre-filter code `{code}`.")
+ if "smooth" in payload:
+ return SmootherConfig(smooth=payload.get("smooth"))
+ if "type" in payload:
+ return SmootherConfig(smooth=payload)
+ return SmootherConfig(smooth=None)
+ raise ValueError("QuantizeConfig: `pre_filters` entries must be pre-filter configs, smooth configs, dicts, or strings.")
+
+
+def _normalize_pre_filters(payload: Optional[List[Any]]) -> List[BasePreFilterConfig]:
+ if payload is None:
+ return []
+ if not isinstance(payload, list):
+ raise ValueError("QuantizeConfig: `pre_filters` must be a list or None.")
+ return [_normalize_pre_filter_config(item) for item in payload]
def dynamic_get(dynamic: Dict[str, Dict[str, Union[int, bool]]], module_name: str, key: str = None,
@@ -611,28 +1323,453 @@ def dynamic_get(dynamic: Dict[str, Dict[str, Union[int, bool]]], module_name: st
return default
return default
+def _normalize_quant_method(value: Union[str, METHOD]) -> METHOD:
+ if isinstance(value, str):
+ value = value.lower()
+ if value == FORMAT.MARLIN:
+ return METHOD.GPTQ
+ if value == FORMAT.BITBLAS:
+ return METHOD.GPTQ
+ if value == FORMAT.FP8:
+ return METHOD.FP8
+ if value == FORMAT.EXL3:
+ return METHOD.EXL3
+ try:
+ return METHOD(value)
+ except ValueError as exc:
+ raise ValueError(f"QuantizeConfig: Unknown quantization method: `{value}`.") from exc
+ if not isinstance(value, METHOD):
+ raise ValueError(f"QuantizeConfig: Unsupported `method`: {value}")
+ return value
+
+
+def _normalize_format(value: Union[str, FORMAT]) -> FORMAT:
+ if isinstance(value, str):
+ try:
+ return FORMAT(value.lower())
+ except ValueError as exc:
+ raise ValueError(f"QuantizeConfig: Unknown quantization format: `{value}`.") from exc
+ if not isinstance(value, FORMAT):
+ raise ValueError(f"QuantizeConfig: Unknown quantization format: `{value}`.")
+ return value
+
+
+def _normalize_pack_dtype(pack_dtype: Optional[Union[str, torch.dtype]]) -> torch.dtype:
+ if pack_dtype is None:
+ return torch.int32
+ if isinstance(pack_dtype, str):
+ pack_dtype = pack_dtype.lower()
+ if pack_dtype not in ["int64", "int32", "int16", "int8"]:
+ raise ValueError(f"QuantizeConfig: Unsupported `pack_dtype`: {pack_dtype}")
+ return getattr(torch, pack_dtype)
+ if isinstance(pack_dtype, torch.dtype):
+ if pack_dtype not in [torch.int64, torch.int32, torch.int16, torch.int8]:
+ raise ValueError(f"QuantizeConfig: Unsupported `pack_dtype`: {pack_dtype}")
+ return pack_dtype
+ raise ValueError(f"QuantizeConfig: Unsupported `pack_dtype`: {pack_dtype}")
+
+
+def _normalize_fallback(fallback: Optional[Union[Fallback, Dict[str, Any], str, int, float]]) -> Optional[Fallback]:
+ if fallback is None:
+ return None
+ if isinstance(fallback, dict):
+ strategy = fallback.get("strategy", FallbackStrategy.RTN)
+ threshold = fallback.get("threshold", "1.0%")
+ smooth = fallback.get("smooth")
+ if smooth is None:
+ smooth = fallback.get("smooth_method")
+ if smooth is None and "clip_method" in fallback:
+ smooth = fallback.get("clip_method")
+ smooth = _parse_smooth_method(smooth)
+ if smooth is None:
+ if "smooth_percentile" in fallback:
+ smooth = SmoothPercentile(percentile=float(fallback.get("smooth_percentile", 99.0)))
+ elif "smooth_mad_k" in fallback:
+ smooth = SmoothMAD(k=float(fallback.get("smooth_mad_k", 3.0)))
+ elif "smooth_mse_steps" in fallback or "smooth_mse_maxshrink" in fallback:
+ smooth = SmoothMSE(
+ steps=int(fallback.get("smooth_mse_steps", 32)),
+ maxshrink=float(fallback.get("smooth_mse_maxshrink", 0.8)),
+ )
+ elif "smooth_outlier_pct" in fallback:
+ smooth = SmoothOutlier(pct=float(fallback.get("smooth_outlier_pct", 1.0)))
+ elif "smooth_rms_k" in fallback:
+ smooth = SmoothSoftNorm(k=float(fallback.get("smooth_rms_k", 3.0)))
+ elif "smooth_log_mu" in fallback:
+ smooth = SmoothLog(
+ percentile=float(fallback.get("smooth_percentile", 99.0)),
+ mu=float(fallback.get("smooth_log_mu", 8.0)),
+ )
+ elif "smooth_axis" in fallback:
+ smooth = SmoothRowCol(axis=str(fallback.get("smooth_axis", "row")))
+ fallback = Fallback(strategy=strategy, threshold=threshold, smooth=smooth)
+ elif isinstance(fallback, (str, int, float)):
+ fallback = Fallback(strategy=FallbackStrategy.RTN, threshold=fallback)
+ elif not isinstance(fallback, Fallback):
+ raise ValueError("QuantizeConfig: `fallback` must be a Fallback config, dict, string, int, float, or None.")
+
+ if isinstance(fallback.strategy, str):
+ try:
+ fallback.strategy = FallbackStrategy(fallback.strategy.lower())
+ except ValueError as exc:
+ raise ValueError(
+ f"QuantizeConfig: `fallback.strategy` must be one of {[v.value for v in FallbackStrategy]}."
+ ) from exc
+ elif not isinstance(fallback.strategy, FallbackStrategy):
+ raise ValueError(
+ f"QuantizeConfig: `fallback.strategy` must be one of {[v.value for v in FallbackStrategy]}."
+ )
+
+ fallback.smooth = _parse_smooth_method(fallback.smooth)
+ return fallback
+
+
+def _normalize_weight_only(
+ weight_only: Optional[Union[WeightOnlyConfig, Dict[str, Any], str]]
+) -> Optional[WeightOnlyConfig]:
+ if weight_only is None:
+ return None
+ if isinstance(weight_only, dict):
+ method = weight_only.get("method", WeightOnlyMethod.RTN)
+ smooth = weight_only.get("smooth")
+ if smooth is None:
+ smooth = weight_only.get("smooth_method")
+ return WeightOnlyConfig(method=method, smooth=smooth)
+ if isinstance(weight_only, str):
+ return WeightOnlyConfig(method=weight_only)
+ if not isinstance(weight_only, WeightOnlyConfig):
+ raise ValueError(
+ "QuantizeConfig: `weight_only` must be a WeightOnlyConfig, dict, string, or None."
+ )
+ return weight_only
+
+
+def _normalize_hessian(hessian: Optional[Union[HessianConfig, Dict[str, Any]]]) -> HessianConfig:
+ if hessian is None:
+ return HessianConfig()
+ if isinstance(hessian, dict):
+ return HessianConfig(**hessian)
+ if not isinstance(hessian, HessianConfig):
+ raise ValueError("QuantizeConfig: `hessian` must be a HessianConfig, dict, or None.")
+ return hessian
+
+
+def _normalize_gptaq(gptaq: Optional[Union[GPTAQConfig, Dict[str, Any]]]) -> Optional[GPTAQConfig]:
+ if gptaq is None:
+ return None
+ if isinstance(gptaq, dict):
+ return GPTAQConfig(**gptaq)
+ if not isinstance(gptaq, GPTAQConfig):
+ raise ValueError("QuantizeConfig: `gptaq` must be a GPTAQConfig, dict, or None.")
+ return gptaq
+
+
+def _normalize_vram_strategy(value: Union[str, VramStrategy]) -> VramStrategy:
+ if isinstance(value, str):
+ try:
+ return VramStrategy(value.lower())
+ except ValueError as exc:
+ raise ValueError(
+ f"QuantizeConfig: `vram_strategy` must be one of {[v.value for v in VramStrategy]}."
+ ) from exc
+ if not isinstance(value, VramStrategy):
+ raise ValueError(
+ f"QuantizeConfig: `vram_strategy` must be one of {[v.value for v in VramStrategy]}."
+ )
+ return value
+
+
+def _normalize_gc_mode(value: Union[str, GcMode]) -> GcMode:
+ if isinstance(value, str):
+ try:
+ return GcMode(value.lower())
+ except ValueError as exc:
+ raise ValueError(
+ f"QuantizeConfig: `gc_mode` must be one of {[v.value for v in GcMode]}."
+ ) from exc
+ if not isinstance(value, GcMode):
+ raise ValueError(
+ f"QuantizeConfig: `gc_mode` must be one of {[v.value for v in GcMode]}."
+ )
+ return value
+
+
+def _normalize_moe_config(value: Optional[Union[MoEConfig, Dict[str, Any]]]) -> Optional[MoEConfig]:
+ if value is None:
+ return None
+ if isinstance(value, MoEConfig):
+ return value
+ if not isinstance(value, dict):
+ raise ValueError("QuantizeConfig: `moe` must be a MoEConfig, dict, or None.")
+
+ routing = value.get("routing")
+ if isinstance(routing, BaseMoERouting):
+ return MoEConfig(routing=routing)
+ if not isinstance(routing, dict):
+ raise ValueError("QuantizeConfig: `moe.routing` must be a BaseMoERouting, dict, or None.")
+
+ routing_class = routing.get("class")
+ if routing_class == ExpertsRoutingOverride.__name__:
+ routing_obj = ExpertsRoutingOverride(
+ num_experts_per_tok=routing.get("num_experts_per_tok", MOE_ALL_EXPERTS)
+ )
+ elif routing_class == ExpertsRoutingBypass.__name__:
+ routing_obj = ExpertsRoutingBypass(batch_size=routing.get("batch_size"))
+ else:
+ raise ValueError(f"QuantizeConfig: Unknown `moe.routing.class`: `{routing_class}`.")
+
+ return MoEConfig(routing=routing_obj)
+
+
+def _resolve_dynamic_group_size_error() -> str:
+ return "QuantizeConfig: `group_size` must be one of `[-1, 16, 32, 64, 128, 256, 512, 1024]`."
+
+
+def _default_damp_percent(method: METHOD) -> float:
+ return 0.005 if method == METHOD.QQQ else 0.05
+
+
+def _default_damp_auto_increment(method: METHOD) -> float:
+ return 0.001 if method == METHOD.QQQ else 0.01
+
+
+def _peek_weight_only_method(payload: Any) -> Optional[WeightOnlyMethod]:
+ if payload is None:
+ return None
+ if isinstance(payload, WeightOnlyConfig):
+ return payload.method
+ if isinstance(payload, str):
+ try:
+ return WeightOnlyMethod(payload.lower())
+ except ValueError:
+ return None
+ if isinstance(payload, dict):
+ method = payload.get("method", WeightOnlyMethod.RTN)
+ try:
+ return WeightOnlyMethod(str(method).lower())
+ except ValueError:
+ return None
+ return None
+
+
+def _extract_weight_only_smooth(payload: Any) -> Any:
+ if payload is None:
+ return None
+ if isinstance(payload, WeightOnlyConfig):
+ return payload.smooth
+ if isinstance(payload, dict):
+ smooth = payload.get("smooth")
+ if smooth is None:
+ smooth = payload.get("smooth_method")
+ return smooth
+ if isinstance(payload, str):
+ return None
+ raise ValueError("QuantizeConfig: `weight_only` must be a WeightOnlyConfig, dict, string, or None.")
+
+
+def _extract_weight_only_legacy_gguf_bits(payload: Any) -> Any:
+ if payload is None:
+ return None
+ if isinstance(payload, WeightOnlyConfig):
+ return getattr(payload, "gguf_qtype", None)
+ if isinstance(payload, dict):
+ return payload.get("gguf_qtype")
+ if isinstance(payload, str):
+ return None
+ raise ValueError("QuantizeConfig: `weight_only` must be a WeightOnlyConfig, dict, string, or None.")
+
+
+def _normalize_rtn_kwargs(payload: Dict[str, Any]) -> Dict[str, Any]:
+ normalized = dict(payload)
+ legacy_gguf_bits = normalized.pop("gguf_qtype", None)
+ weight_only = normalized.pop("weight_only", None)
+ weight_only_method = _peek_weight_only_method(weight_only)
+
+ # `weight_only.method="gguf"` is a backward-compatible shorthand for the direct GGUF weight-only lifecycle.
+ if weight_only_method == WeightOnlyMethod.GGUF and FORMAT_FIELD_CODE not in normalized:
+ normalized[FORMAT_FIELD_CODE] = FORMAT.GGUF
+
+ if "smooth" not in normalized:
+ normalized["smooth"] = _extract_weight_only_smooth(weight_only)
+ if legacy_gguf_bits is None:
+ legacy_gguf_bits = _extract_weight_only_legacy_gguf_bits(weight_only)
+ if legacy_gguf_bits is not None and BITS_FIELD_CODE not in normalized:
+ normalized[BITS_FIELD_CODE] = legacy_gguf_bits
+ return normalized
+
+
+def _normalize_gguf_kwargs(payload: Dict[str, Any]) -> Dict[str, Any]:
+ normalized = dict(payload)
+ legacy_gguf_bits = normalized.pop("gguf_qtype", None)
+ weight_only = normalized.pop("weight_only", None)
+
+ if "smoother" not in normalized and "smooth" not in normalized:
+ normalized["smoother"] = _extract_weight_only_smooth(weight_only)
+ if legacy_gguf_bits is None:
+ legacy_gguf_bits = _extract_weight_only_legacy_gguf_bits(weight_only)
+ if legacy_gguf_bits is not None and BITS_FIELD_CODE not in normalized:
+ normalized[BITS_FIELD_CODE] = legacy_gguf_bits
+ normalized[BITS_FIELD_CODE], normalized[FORMAT_FIELD_CODE], _ = _normalize_gguf_config_spec(
+ normalized.get(BITS_FIELD_CODE, 4),
+ normalized.get(FORMAT_FIELD_CODE),
+ )
+ return normalized
+
+
+def _normalize_fp8_kwargs(payload: Dict[str, Any]) -> Dict[str, Any]:
+ normalized = dict(payload)
+ weight_only = normalized.pop("weight_only", None)
+ legacy_fmt = normalized.pop("fmt", None)
+
+ if "smoother" not in normalized and "smooth" not in normalized:
+ normalized["smoother"] = _extract_weight_only_smooth(weight_only)
+
+ normalized[FORMAT_FIELD_CODE] = _normalize_fp8_fmt(
+ normalized.get(FORMAT_FIELD_CODE, legacy_fmt)
+ )
+
+ weight_block_size = _normalize_fp8_weight_block_size(normalized.get("weight_block_size"))
+ normalized["weight_block_size"] = list(weight_block_size) if weight_block_size is not None else None
+
+ normalized["weight_scale_method"] = _normalize_fp8_weight_scale_method(
+ normalized.get("weight_scale_method"),
+ weight_block_size=weight_block_size,
+ )
+ return normalized
+
+
+def _resolve_export_quant_method(format_value: FORMAT, fallback_method: Optional[METHOD] = None) -> METHOD:
+ if format_value == FORMAT.MARLIN:
+ if fallback_method is None:
+ raise ValueError("QuantizeConfig: FORMAT.MARLIN requires an explicit quantization method family.")
+ return fallback_method
+
+ method = _UNAMBIGUOUS_EXPORT_METHOD_BY_FORMAT.get(format_value)
+ if method is None:
+ if fallback_method is not None:
+ return fallback_method
+ raise ValueError(f"QuantizeConfig: Unable to resolve export method for format `{format_value}`.")
+ return method
+
+
+def _normalize_quantize_config_payload_for_target_cls(target_cls, payload: Dict[str, Any]) -> Dict[str, Any]:
+ normalized = dict(payload)
+
+ if target_cls is AWQQuantizeConfig:
+ expected_method = METHOD.AWQ
+ elif target_cls is FP8Config:
+ expected_method = METHOD.FP8
+ elif target_cls is EXL3QuantizeConfig:
+ expected_method = METHOD.EXL3
+ format_value = normalized.get(FORMAT_FIELD_CODE)
+ normalized_format = None
+ if format_value is not None:
+ try:
+ normalized_format = _normalize_format(format_value)
+ normalized[FORMAT_FIELD_CODE] = normalized_format
+ except ValueError:
+ normalized_format = None
+ if normalized_format is not None and normalized_format != FORMAT.EXL3:
+ log.info(f"QuantizeConfig: Auto fix `format` to `{FORMAT.EXL3}`")
+ normalized[FORMAT_FIELD_CODE] = FORMAT.EXL3
+ elif target_cls is GGUFConfig:
+ expected_method = METHOD.GGUF
+ elif target_cls is QQQQuantizeConfig:
+ expected_method = METHOD.QQQ
+ format_value = normalized.get(FORMAT_FIELD_CODE)
+ normalized_format = None
+ if format_value is not None:
+ try:
+ normalized_format = _normalize_format(format_value)
+ normalized[FORMAT_FIELD_CODE] = normalized_format
+ except ValueError:
+ normalized_format = None
+ if normalized_format is not None and normalized_format != FORMAT.QQQ:
+ log.info(f"QuantizeConfig: Auto fix `format` to `{FORMAT.QQQ}`")
+ normalized[FORMAT_FIELD_CODE] = FORMAT.QQQ
+ else:
+ expected_method = METHOD.GPTQ
+
+ method = normalized.get(METHOD_FIELD_CODE)
+ normalized_method = None
+ if method is not None:
+ try:
+ normalized_method = _normalize_quant_method(method)
+ normalized[METHOD_FIELD_CODE] = normalized_method
+ except ValueError:
+ normalized_method = None
+
+ if normalized_method is not None and normalized_method != expected_method:
+ if target_cls is GGUFConfig and normalized_method == METHOD.GPTQ:
+ pass
+ else:
+ log.warn(
+ f"QuantizeConfig: `{METHOD_FIELD_CODE}`=`{normalized_method}` is incompatible with `{target_cls.__name__}`. "
+ f"Auto-fix method to `{expected_method}`."
+ )
+ normalized[METHOD_FIELD_CODE] = expected_method
+
+ return normalized
+
+
+def _filter_quantize_config_payload_for_target_cls(target_cls, payload: Dict[str, Any]) -> Dict[str, Any]:
+ target_field_names = {field.name for field in fields(target_cls) if field.init}
+ return {key: value for key, value in payload.items() if key in target_field_names}
+
+
+def _prepare_target_quantize_config_kwargs(target_cls, payload: Dict[str, Any]) -> Dict[str, Any]:
+ normalized = _normalize_quantize_config_payload_for_target_cls(target_cls, payload)
+ if target_cls is RTNQuantizeConfig:
+ normalized = _normalize_rtn_kwargs(normalized)
+ elif target_cls is GGUFConfig:
+ normalized = _normalize_gguf_kwargs(normalized)
+ elif target_cls is FP8Config:
+ normalized = _normalize_fp8_kwargs(normalized)
+ return _filter_quantize_config_payload_for_target_cls(target_cls, normalized)
+
+
+class QuantizeConfigMeta(type):
+ def __call__(cls, *args, **kwargs):
+ kwargs = _normalize_quantize_config_constructor_kwargs(kwargs)
+ if cls is QuantizeConfig:
+ target_cls = _resolve_quantize_config_class(kwargs)
+ target_kwargs = _prepare_target_quantize_config_kwargs(target_cls, kwargs)
+ return type.__call__(target_cls, *args, **target_kwargs)
+ return super().__call__(*args, **kwargs)
+
+
+def _normalize_quantize_config_constructor_kwargs(kwargs: Dict[str, Any]) -> Dict[str, Any]:
+ if not kwargs:
+ return kwargs
+
+ normalized = dict(kwargs)
+ if FORMAT_FIELD_COMPAT_MARLIN in normalized:
+ raise ValueError(
+ "QuantizeConfig: `is_marlin_format` has been removed. Use `format=\"marlin\"` only for legacy checkpoint inspection, "
+ "or `format=\"gptq\"` for new GPTQ quantization."
+ )
+ if METHOD_FIELD_CODE not in normalized and QUANT_METHOD_FIELD in normalized:
+ normalized[METHOD_FIELD_CODE] = normalized[QUANT_METHOD_FIELD]
+ normalized.pop(QUANT_METHOD_FIELD, None)
+
+ if FORMAT_FIELD_CODE not in normalized and FORMAT_FIELD_CHECKPOINT in normalized:
+ normalized[FORMAT_FIELD_CODE] = normalized[FORMAT_FIELD_CHECKPOINT]
+ normalized.pop(FORMAT_FIELD_CHECKPOINT, None)
+ return normalized
+
+
@dataclass
-class QuantizeConfig():
- bits: int = field(default=4, metadata={"choices": [2, 3, 4, 8]})
+class BaseQuantizeConfig(metaclass=QuantizeConfigMeta):
+ bits: Union[int, str, GGUFBits] = field(default=4, metadata={"choices": [2, 3, 4, 5, 6, 8]})
# allow dynamic bitsize per layer, if None or some layer not set, use bits
- dynamic: Optional[Dict[str, Dict[str, Union[int, bool]]]] = field(default=None)
+ dynamic: Optional[Dict[str, Dict[str, Union[int, str, bool, GGUFBits]]]] = field(default=None)
- # GPTQ only
- # 128 offer good balance between inference speed, vram usage (bpw), and quality
- # use 32 for highest quality with slower inference and higher vram usage
+ # 128 offers a good balance between inference speed, VRAM usage, and quality.
group_size: int = field(default=128)
- # increase damp if NaN is encountered during `.quantize()` and/or increase calib dataset size
- damp_percent: float = field(default=None)
- damp_auto_increment: float = field(default=None)
-
desc_act: Optional[bool] = field(default=None)
- # GPTQ only
- act_group_aware: Optional[bool] = field(default=None)
- static_groups: bool = field(default=False)
-
# symmetric quantization toggle (True=symmetric, False=asymmetric).
sym: bool = field(default=True)
@@ -640,23 +1777,12 @@ class QuantizeConfig():
lm_head: bool = field(default=False)
- quant_method: METHOD = field(default=METHOD.GPTQ)
+ method: METHOD = field(default=METHOD.GPTQ)
- # default to gptq v1 format for maximum compat with 3rd party inference libs with minimal loss vs v2
- # if you inference with gptqmodel, save to gptq_v2 format for best result
+ # Serialized/exported checkpoint layout. This is the authoritative post-quantization format.
format: FORMAT = field(default=FORMAT.GPTQ)
- # quantization_order: str = "activate",
- # quantization_scale: str = "mse", # or absmax
- # is_distributed: bool = False,
- # tied_gptq_handle: Optional["GPTQ"] = None
-
- # GPTQ only
- # mean square error calculation: may reduce error loss for some models
- mse: float = field(default=0.0)
-
- # properties that do not directly contributes to quantization or quant inference should be placed in meta
- # i.e. quantizer tool (producer) + version, timestamp, entity who made the quant, etc
+ # properties that do not directly contribute to quantization or inference should be placed in meta
meta: Optional[Dict] = field(default=None)
# normalized to DEVICE after passing to load()
@@ -667,49 +1793,33 @@ class QuantizeConfig():
# affects [`qweights`, `qzeros`]
pack_dtype: Optional[Union[str, torch.dtype]] = field(default=torch.int32)
- # packing implementation hinpt (`original` = legacy CPU pack, `gpu` enables CUDA pack, `cpu` forces block CPU pack).
+ # packing implementation hint (`original` = legacy CPU pack, `gpu` enables CUDA pack, `cpu` forces block CPU pack).
pack_impl: str = field(default="cpu")
- # pending used field
adapter: Optional[Union[Dict[str, Any], Lora]] = field(default=None)
- # quantization only:
# controls cpu memory saving by offloading layers/modules to disk in the slow quantization process
- # default to true as the benefit of ~73.5% cpu memory saving is tremendous
- offload_to_disk: bool = field(default=True, metadata={"help": "Offload completed module memory to disk during quantization loop"})
- offload_to_disk_path: str = field(default=None, metadata={"help": "Offload disk path. Only applicable if Offload to disk is enabled"})
+ offload_to_disk: bool = field(
+ default=True,
+ metadata={"help": "Offload completed module memory to disk during quantization loop"},
+ )
+ offload_to_disk_path: str = field(
+ default=None,
+ metadata={"help": "Offload disk path. Only applicable if Offload to disk is enabled"},
+ )
rotation: Optional[str] = field(default=None, metadata={"choices": ["hadamard", "random"]})
- # GPTQ only
- # deprecated: only used for compat
- is_marlin_format: bool = False
-
- # gptq only:
- # if calibration is insufficient, fallback to a simple quantization strategy; encapsulated in FailSafe config
- failsafe: Optional[FailSafe] = field(default_factory=FailSafe)
-
- # GPTQ only
- # gptaq only:
- gptaq: Optional[GPTAQConfig] = field(default=None)
-
- # gptq only:
- # skip all heavy computations for testing model loading
- mock_quantization: bool = field(default=False, metadata={"help": "Skip heavy computations for fast model loading validation"})
-
- # GPTQ only
- # Hessian accumulation controls (GPTQ only)
- hessian: Optional[HessianConfig] = field(default_factory=HessianConfig)
+ # if calibration is insufficient, fallback to a simple quantization strategy
+ fallback: Optional[Fallback] = field(default_factory=Fallback)
# Callback function to filter devices for compute-intensive stages (quantization and forwarding)
- # Takes a list of devices and returns either the original list or a filtered subset
compute_device_filter: Optional[callable] = field(
default=None,
metadata={"help": "Callback function to filter devices for compute-intensive stages. Function signature: fn(devices: List) -> List. "
"Example to exclude device 0: compute_device_filter=lambda devices: [d for d in devices if d.index != 0]"}
)
- # Works faster than data parallel with some configurations
auto_forward_data_parallel: bool = field(
default=True,
metadata={"help": "When multi-gpu is detected, we may data clone modules to each gpu for data parallelism "
@@ -717,7 +1827,6 @@ class QuantizeConfig():
"leading in some cases to slower forwarding or vram OOM"}
)
- # VRAM allocation strategy for MoE-heavy subsets
vram_strategy: VramStrategy = field(default=VramStrategy.EXCLUSIVE)
gc_mode: GcMode = field(
@@ -725,14 +1834,12 @@ class QuantizeConfig():
metadata={"help": "Garbage collection mode: 'interval' for regular GC or 'on_stage_end' for GC after stage end (after forward pass, quantize, layer finilization)."}
)
- # Control whether to wait for layer finalization (packing, writing) before proceeding to next layer
- # Default False preserves current behavior (async finalization in background while next layer starts)
wait_for_submodule_finalizers: bool = field(
default=False,
metadata={"help": "Wait for all layer finalization tasks (packing, offloading to disk, etc) to complete before proceeding to next layer. May reduce vram pressure for some env."}
)
- moe: MoEConfig = field(
+ moe: Optional[MoEConfig] = field(
default=None,
metadata={"help": "Mixture-of-Experts (MoE) configuration for routing strategy and expert batching. "
"Example with bypass routing (forward all data to each expert): "
@@ -744,252 +1851,142 @@ class QuantizeConfig():
"moe={'routing': {'class': 'ExpertsRoutingOverride', 'num_experts_per_tok': 'all'}}"}
)
- def __post_init__(self):
- fields_info = fields(self)
+ @property
+ def quant_method(self) -> METHOD:
+ return self.method
- # validate/normalizes pack_dtype from string and dtype to valid dtype
- if self.pack_dtype is None:
- self.pack_dtype = torch.int32
- else:
- if isinstance(self.pack_dtype, str):
- self.pack_dtype = self.pack_dtype.lower()
- if self.pack_dtype not in ["int64", "int32", "int16", "int8"]:
- raise ValueError(f"QuantizeConfig: Unsupported `pack_dtype`: {self.pack_dtype}")
- self.pack_dtype = getattr(torch, self.pack_dtype)
- elif isinstance(self.pack_dtype, torch.dtype):
- if self.pack_dtype not in [torch.int64, torch.int32, torch.int16, torch.int8]:
- raise ValueError(f"QuantizeConfig: Unsupported `pack_dtype`: {self.pack_dtype}")
- else:
- raise ValueError(f"QuantizeConfig: Unsupported `pack_dtype`: {self.pack_dtype}")
+ @quant_method.setter
+ def quant_method(self, value: Union[str, METHOD]) -> None:
+ self.method = value
+
+ @property
+ def checkpoint_format(self):
+ return self.format
+
+ @checkpoint_format.setter
+ def checkpoint_format(self, value) -> None:
+ self.format = value
+
+ @property
+ def runtime_bits(self):
+ return self.bits
+
+ def _resolve_checkpoint_format(self) -> FORMAT:
+ self.format = _normalize_format(self.format)
+ return self.format
+
+ def _normalize_bits_field(self, bits_value, checkpoint_format: FORMAT):
+ return _normalize_quant_bits(bits_value, format_value=checkpoint_format)
+
+ def _normalize_dynamic_layer_config(
+ self,
+ layer_name: str,
+ layer_dict: Dict[str, Any],
+ *,
+ valid_bit_widths: List[int],
+ checkpoint_format: FORMAT,
+ ) -> None:
+ for key, value in layer_dict.items():
+ if key == "bits":
+ normalized_bits = self._normalize_bits_field(value, checkpoint_format=checkpoint_format)
+ layer_dict[key] = normalized_bits
+ if quant_bits_width(normalized_bits) not in valid_bit_widths:
+ raise ValueError(
+ f"QuantizeConfig: Layer `{layer_name}` only support quantization of `{valid_bit_widths}` bits."
+ )
+ if key == "group_size" and value != -1 and value <= 0:
+ raise ValueError(_resolve_dynamic_group_size_error())
- # validate quant method and format is matched
- valid_formats = QUANT_METHOD_FORMAT_MAPPING.get(self.quant_method, None)
+ def allowed_quant_methods(self) -> Tuple[METHOD, ...]:
+ return tuple(METHOD)
+
+ def supported_export_formats(self) -> Tuple[FORMAT, ...]:
+ valid_formats = QUANT_METHOD_FORMAT_MAPPING.get(self.method, None)
if valid_formats is None:
- raise ValueError(f"QuantizeConfig: Unsupported `quant_method`: {self.quant_method}")
+ raise ValueError(f"QuantizeConfig: Unsupported `method`: {self.method}")
+ return tuple(valid_formats)
- # If the user does not pass it, the default value will be set according to quant_method
- if self.damp_percent is None:
- if self.quant_method == METHOD.QQQ:
- self.damp_percent = 0.005
- else:
- self.damp_percent = 0.05
- if self.damp_auto_increment is None:
- if self.quant_method == METHOD.QQQ:
- self.damp_auto_increment = 0.001
- else:
- self.damp_auto_increment = 0.01
+ def export_quant_method(self) -> METHOD:
+ return _resolve_export_quant_method(resolve_quant_format(self.format, self.method), fallback_method=self.method)
- # TODO FIXME awq compat which didn't have checkpoint_format before merging to gptqmodel
- if self.quant_method == METHOD.AWQ and self.format not in [FORMAT.MARLIN, FORMAT.GEMV, FORMAT.GEMV_FAST, FORMAT.GEMM, FORMAT.LLM_AWQ]:
- log.info(f"QuantizeConfig: Auto fix `format` to `{FORMAT.GEMM}`")
- self.format = FORMAT.GEMM
+ def default_desc_act(self) -> bool:
+ return True
+
+ def __post_init__(self):
+ fields_info = fields(self)
- if self.format not in valid_formats:
+ self.method = _normalize_quant_method(self.method)
+ format_family = self._resolve_checkpoint_format()
+ self.pack_dtype = _normalize_pack_dtype(self.pack_dtype)
+ self.bits = self._normalize_bits_field(self.bits, checkpoint_format=format_family)
+
+ allowed_methods = self.allowed_quant_methods()
+ if allowed_methods and self.method not in allowed_methods:
raise ValueError(
- f"QuantizeConfig: checkpoint `format` used is {self.format}, and the quantization method is {self.quant_method}. "
+ f"{self.__class__.__name__}: `method` must be one of {[v.value for v in allowed_methods]}."
)
- # normalize failsafe config
- if self.failsafe is None:
- pass
- elif isinstance(self.failsafe, dict):
- strategy = self.failsafe.get("strategy", FailSafeStrategy.RTN)
- threshold = self.failsafe.get("threshold", "1.0%")
- smooth = self.failsafe.get("smooth")
- if smooth is None:
- smooth = self.failsafe.get("smooth_method")
- if smooth is None and "clip_method" in self.failsafe:
- smooth = self.failsafe.get("clip_method")
- smooth = _parse_smooth_method(smooth)
- if smooth is None:
- if "smooth_percentile" in self.failsafe:
- smooth = SmoothPercentile(
- percentile=float(self.failsafe.get("smooth_percentile", 99.0))
- )
- elif "smooth_mad_k" in self.failsafe:
- smooth = SmoothMAD(k=float(self.failsafe.get("smooth_mad_k", 3.0)))
- elif "smooth_mse_steps" in self.failsafe or "smooth_mse_maxshrink" in self.failsafe:
- smooth = SmoothMSE(
- steps=int(self.failsafe.get("smooth_mse_steps", 32)),
- maxshrink=float(self.failsafe.get("smooth_mse_maxshrink", 0.8)),
- )
- elif "smooth_outlier_pct" in self.failsafe:
- smooth = SmoothOutlier(pct=float(self.failsafe.get("smooth_outlier_pct", 1.0)))
- elif "smooth_rms_k" in self.failsafe:
- smooth = SmoothSoftNorm(k=float(self.failsafe.get("smooth_rms_k", 3.0)))
- elif "smooth_log_mu" in self.failsafe:
- smooth = SmoothLog(
- percentile=float(self.failsafe.get("smooth_percentile", 99.0)),
- mu=float(self.failsafe.get("smooth_log_mu", 8.0)),
- )
- elif "smooth_axis" in self.failsafe:
- smooth = SmoothRowCol(axis=str(self.failsafe.get("smooth_axis", "row")))
- self.failsafe = FailSafe(
- strategy=strategy,
- threshold=threshold,
- smooth=smooth,
+ valid_formats = self.supported_export_formats()
+ if format_family not in valid_formats:
+ raise ValueError(
+ f"{self.__class__.__name__}: unsupported export `format` `{format_family}`."
)
- elif isinstance(self.failsafe, (str, int, float)):
- self.failsafe = FailSafe(strategy=FailSafeStrategy.RTN, threshold=self.failsafe)
- elif not isinstance(self.failsafe, FailSafe):
- raise ValueError("QuantizeConfig: `failsafe` must be a FailSafe config, dict, string, int, float, or None.")
-
- if self.failsafe is not None:
- if isinstance(self.failsafe.strategy, str):
- try:
- self.failsafe.strategy = FailSafeStrategy(self.failsafe.strategy.lower())
- except ValueError as exc:
- raise ValueError(
- f"QuantizeConfig: `failsafe.strategy` must be one of {[v.value for v in FailSafeStrategy]}."
- ) from exc
- elif not isinstance(self.failsafe.strategy, FailSafeStrategy):
- raise ValueError(
- f"QuantizeConfig: `failsafe.strategy` must be one of {[v.value for v in FailSafeStrategy]}."
- )
- self.failsafe.smooth = _parse_smooth_method(self.failsafe.smooth)
+ self.fallback = _normalize_fallback(self.fallback)
- if self.bits not in fields_info[0].metadata["choices"]:
+ valid_bit_widths = fields_info[0].metadata["choices"]
+ if quant_bits_width(self.bits) not in valid_bit_widths:
raise ValueError(f"QuantizeConfig: `bits` must be in the set of `{fields_info[0].metadata['choices']}`.")
if self.dynamic is not None:
self.dynamic = {
- **{k: v for k, v in self.dynamic.items() if k.startswith('-')}, # 先添加以 "-" 开头的键
- **{k: v for k, v in self.dynamic.items() if not k.startswith('-')} # 然后添加其他键
+ **{k: v for k, v in self.dynamic.items() if k.startswith('-')},
+ **{k: v for k, v in self.dynamic.items() if not k.startswith('-')},
}
for layer, layer_dict in self.dynamic.items():
- for key, value in layer_dict.items():
- if key == "bits" and value not in fields_info[0].metadata["choices"]:
- raise ValueError(f"QuantizeConfig: Layer `{layer}` only support quantization of `{fields_info[0].metadata['choices']}` bits.")
- elif key == "group_size" and value != -1 and value <= 0:
- raise ValueError("QuantizeConfig: `group_size` must be one of `[-1, 16, 32, 64, 128, 256, 512, 1024]`.")
+ self._normalize_dynamic_layer_config(
+ layer,
+ layer_dict,
+ valid_bit_widths=valid_bit_widths,
+ checkpoint_format=format_family,
+ )
if self.group_size != -1 and self.group_size <= 0:
- raise ValueError("QuantizeConfig: `group_size` must be one of `[-1, 16, 32, 64, 128, 256, 512, 1024]`.")
-
- if not (0 < self.damp_percent < 1):
- raise ValueError("QuantizeConfig: `damp_percent` must between 0 and 1.")
-
- if self.damp_auto_increment < 0:
- raise ValueError("QuantizeConfig:: `damp_auto_increment` must greater than 0.")
+ raise ValueError(_resolve_dynamic_group_size_error())
- if self.hessian is None:
- self.hessian = HessianConfig()
- elif isinstance(self.hessian, dict):
- self.hessian = HessianConfig(**self.hessian)
- elif not isinstance(self.hessian, HessianConfig):
- raise ValueError("QuantizeConfig: `hessian` must be a HessianConfig, dict, or None.")
+ if self.desc_act is None:
+ self.desc_act = self.default_desc_act()
+ elif not isinstance(self.desc_act, bool):
+ self.desc_act = bool(self.desc_act)
- if self.gptaq is None:
- pass
- elif isinstance(self.gptaq, dict):
- self.gptaq = GPTAQConfig(**self.gptaq)
- elif not isinstance(self.gptaq, GPTAQConfig):
- raise ValueError("QuantizeConfig: `gptaq` must be a GPTAQConfig, dict, or None.")
-
- # resolve activation ordering compatibility and defaults
- desc_act_user_value = self.desc_act
- act_group_aware_user_value = self.act_group_aware
-
- if desc_act_user_value is None:
- # GPTQ defaults to higher quality ordering disabled, others retain legacy default
- self.desc_act = False if self.quant_method == METHOD.GPTQ else True
- elif isinstance(desc_act_user_value, bool):
- self.desc_act = desc_act_user_value
- else:
- self.desc_act = bool(desc_act_user_value)
-
- if act_group_aware_user_value is None:
- # auto-enable for GPTQ unless user explicitly disables it
- self.act_group_aware = self.quant_method == METHOD.GPTQ
- elif isinstance(act_group_aware_user_value, bool):
- self.act_group_aware = act_group_aware_user_value
- else:
- self.act_group_aware = bool(act_group_aware_user_value)
-
- self._resolve_activation_ordering(desc_act_user_value, act_group_aware_user_value)
-
- # validate hybrid act order
- if self.act_group_aware and self.desc_act:
- raise ValueError("QuantizeConfig:: `act_group_aware` == `True` requires `desc_act` == `False`.")
-
- # validate meta
if self.meta is not None:
if not isinstance(self.meta, dict):
raise ValueError("QuantizeConfig: `meta` must be a dictionary")
- for key, value in self.meta.items():
+ for key in self.meta:
if not isinstance(key, str):
raise ValueError("QuantizeConfig: `meta` keys must be strings")
else:
self.meta = {}
- # adapter normalize
self.adapter = normalize_adapter(self.adapter)
- #print(f"adapter: {self.adapter}")
-
if self.offload_to_disk and not self.offload_to_disk_path:
path_key = f"{get_random_string()}-{get_random_string()}"
self.offload_to_disk_path = f"./gptqmodel_offload/{path_key}/"
log.info(f"QuantizeConfig: offload_to_disk_path auto set to `{self.offload_to_disk_path}`")
- if isinstance(self.vram_strategy, str):
- try:
- self.vram_strategy = VramStrategy(self.vram_strategy.lower())
- except ValueError as exc:
- raise ValueError(
- f"QuantizeConfig: `vram_strategy` must be one of {[v.value for v in VramStrategy]}."
- ) from exc
- elif not isinstance(self.vram_strategy, VramStrategy):
- raise ValueError(
- f"QuantizeConfig: `vram_strategy` must be one of {[v.value for v in VramStrategy]}."
- )
-
- if isinstance(self.gc_mode, str):
- try:
- self.gc_mode = GcMode(self.gc_mode.lower())
- except ValueError as exc:
- raise ValueError(
- f"QuantizeConfig: `gc_mode` must be one of {[v.value for v in GcMode]}."
- ) from exc
- elif not isinstance(self.gc_mode, GcMode):
- raise ValueError(
- f"QuantizeConfig: `gc_mode` must be one of {[v.value for v in GcMode]}."
- )
+ self.vram_strategy = _normalize_vram_strategy(self.vram_strategy)
+ self.gc_mode = _normalize_gc_mode(self.gc_mode)
+ self.moe = _normalize_moe_config(self.moe)
def extension_set(self, key: str, value: Any):
if self.adapter is None:
self.adapter = {}
-
self.adapter[key.lower()] = value
- def _resolve_activation_ordering(
- self,
- desc_act_user_value: Optional[bool],
- act_group_aware_user_value: Optional[bool],
- ) -> None:
- """Normalize defaults and enforce compatibility between desc_act and act_group_aware."""
-
- desc_act_enabled_by_user = bool(desc_act_user_value) if desc_act_user_value is not None else False
- act_group_aware_enabled_by_user = (
- bool(act_group_aware_user_value) if act_group_aware_user_value is not None else False
- )
-
- if desc_act_enabled_by_user and act_group_aware_user_value is not None and act_group_aware_enabled_by_user:
- raise ValueError(
- "QuantizeConfig:: `act_group_aware` == `True` requires `desc_act` == `False` when both are explicitly set."
- )
-
- if desc_act_enabled_by_user and act_group_aware_user_value is None and self.act_group_aware:
- log.warn(
- "QuantizeConfig: `desc_act=True` automatically disables `act_group_aware`. "
- "Set `act_group_aware=False` explicitly to silence this warning."
- )
- self.act_group_aware = False
-
def extension_get(self, key: str) -> Any:
- return self.adapter.get(key.lower()) if self.adapter else None
+ return self.adapter.get(key.lower()) if self.adapter else None
def meta_set(self, key: str, value: Any):
self.meta[key] = value
@@ -997,15 +1994,18 @@ def meta_set(self, key: str, value: Any):
def meta_get(self, key: str) -> Any:
return self.meta.get(key)
- def dynamic_get(self, layer_name: str, key: str = None, default: Union[int, bool, float] = None, sub_key: str = None
- ) -> Union[Dict, int, bool, float]:
+ def dynamic_get(
+ self,
+ layer_name: str,
+ key: str = None,
+ default: Union[int, bool, float] = None,
+ sub_key: str = None,
+ ) -> Union[Dict, int, bool, float]:
return dynamic_get(self.dynamic, layer_name, key, default, sub_key)
- # versionable is a meta.property that pairs value with version i.e "value:1.0.0"
def meta_set_versionable(self, key: str, value: List[str]):
self.meta_set(key, value)
- # versionable is a meta.property that pairs value with version i.e "value:1.0.0"
def meta_get_versionable(self, key: str) -> List[Tuple[str, str]]:
values = self.meta_get(key)
if values is None:
@@ -1019,176 +2019,184 @@ def meta_get_versionable(self, key: str) -> List[Tuple[str, str]]:
result.append((parts[0].lower(), parts[1].lower()))
return result
- # is quantized model quantized or packed by gptqmodel version with gptaq format code
def is_quantized_by_gptaq(self) -> bool:
- # check meta.quantizer
result = self.meta_get_versionable(META_FIELD_QUANTIZER)
if len(result) > 0:
for producer, _version in result:
if producer == META_QUANTIZER_GPTQMODEL:
return version.parse(_version) >= version.parse(MIN_VERSION_WITH_V2)
-
return False
def extract_adapter_rank_patterns(self) -> Optional[Dict[str, int]]:
adapter_rank_patterns = {}
-
- # no rank can be had if there is no dynamic or adapter
if not self.dynamic or not self.adapter:
return adapter_rank_patterns
- # override format: `{ "adapter": { "rank": 512 } }`
for k, v in self.dynamic.items():
- adapter_override = v.get("adapter", None) # TODO use const, not str
+ adapter_override = v.get("adapter", None)
if adapter_override and isinstance(adapter_override, Dict):
rank = adapter_override.get("rank", None)
if rank and isinstance(rank, int):
- # need to strip `+:` positive prefix
- adapter_rank_patterns[k.lstrip("+:")] = rank # TODO use const, not str
+ adapter_rank_patterns[k.lstrip("+:")] = rank
return adapter_rank_patterns
def save_pretrained(self, save_dir: str, **kwargs):
with open(join(save_dir, QUANT_CONFIG_FILENAME), "w", encoding="utf-8") as f:
- d = self.to_dict()
- json_str = json.dumps(d, indent=2)
+ payload = self.to_dict()
+ json_str = json.dumps(payload, indent=2)
log.info(f"Saved Quantize Config: \n{json_str}")
f.write(json_str)
@classmethod
- # normalize quant config for compat and also performs validation
def from_quant_config(cls, quantize_cfg, format: str = None):
- valid_formats = {FORMAT.GPTQ, FORMAT.GPTQ_V2, FORMAT.MARLIN, FORMAT.BITBLAS}
+ valid_formats = set(FORMAT)
format_auto_inferred = False
- # compat: format can be passed in via from_quantized() if field missing from json
+ checkpoint_format_hint = quantize_cfg.get(FORMAT_FIELD_CHECKPOINT) if isinstance(quantize_cfg, dict) else None
+ serialized_format = quantize_cfg.get(FORMAT_FIELD_CODE) if isinstance(quantize_cfg, dict) else None
if format:
- if format not in valid_formats:
- raise ValueError(f"QuantizeConfig: Unknown quantization checkpoint format: {format}.")
- if quantize_cfg.get(FORMAT_FIELD_CHECKPOINT):
+ if _looks_like_fp8_fmt(format):
+ format = _normalize_fp8_fmt(format)
+ else:
+ format = _normalize_format(format)
+ if format not in valid_formats:
+ raise ValueError(f"QuantizeConfig: Unknown quantization checkpoint format: {format}.")
+ if checkpoint_format_hint is not None or serialized_format is not None:
raise ValueError("QuantizeConfig: Conflicting quantization format passed in manually and also exists in model config.")
- # compat: warn if checkpoint_format is missing
- elif quantize_cfg.get(FORMAT_FIELD_CHECKPOINT) is None:
+ elif checkpoint_format_hint is None and serialized_format is None:
format_auto_inferred = True
- field_names = [field.name for field in fields(cls)]
+ field_names = _known_quantize_config_field_names()
- # FIXME convert awg quantize_config to gptq quantize_config
normalized = {
- QUANT_METHOD_FIELD: METHOD.GPTQ,
- # compat: default to gptq(v1) when loading models
+ METHOD_FIELD_CODE: METHOD.GPTQ,
FORMAT_FIELD_CODE: format if format else FORMAT.GPTQ,
}
+ format_field_present = format is not None
+ legacy_checkpoint_format = None
for key, val in quantize_cfg.items():
key = key.lower()
- # remap keys according to compat map
+ if key == FORMAT_FIELD_COMPAT_MARLIN:
+ raise ValueError(
+ "QuantizeConfig: `is_marlin_format` is no longer supported. Replace it with an explicit `format` field."
+ )
+
+ if key == FORMAT_FIELD_CHECKPOINT:
+ if _looks_like_fp8_fmt(val):
+ legacy_checkpoint_format = _normalize_fp8_fmt(val)
+ else:
+ try:
+ legacy_checkpoint_format = _normalize_gguf_public_format(val)
+ except ValueError:
+ legacy_checkpoint_format = None
+ if legacy_checkpoint_format is None:
+ legacy_checkpoint_format = _normalize_format(val)
+ if legacy_checkpoint_format is not None:
+ checkpoint_format_hint = legacy_checkpoint_format
+ continue
+
if key in QUANT_CONFIG_ARG_SYNONYMS and QUANT_CONFIG_ARG_SYNONYMS[key] in field_names:
key = QUANT_CONFIG_ARG_SYNONYMS[key]
elif key in QUANT_CONFIG_ARG_SYNONYMS_NEGATED and QUANT_CONFIG_ARG_SYNONYMS_NEGATED[key] in field_names:
key = QUANT_CONFIG_ARG_SYNONYMS_NEGATED[key]
val = not bool(val)
- if key == FORMAT_FIELD_CHECKPOINT:
- val = val.lower()
-
- if val in {FORMAT.GPTQ, FORMAT.GPTQ_V2, FORMAT.MARLIN, FORMAT.BITBLAS}:
- normalized[key] = val
- else:
- raise ValueError(f"QuantizeConfig: Unknown quantization format: `{val}`.")
- elif key == QUANT_METHOD_FIELD:
- val = val.lower()
- # compat: some hf models use quant_method=marlin or bitblas
- if val == FORMAT.MARLIN:
+ if key == METHOD_FIELD_CODE:
+ if isinstance(val, str) and val.lower() == FORMAT.MARLIN:
normalized[FORMAT_FIELD_CODE] = FORMAT.MARLIN
- elif val == FORMAT.BITBLAS:
+ elif isinstance(val, str) and val.lower() == FORMAT.BITBLAS:
normalized[FORMAT_FIELD_CODE] = FORMAT.BITBLAS
- elif val not in {METHOD.GPTQ, METHOD.QQQ, METHOD.AWQ}:
- raise ValueError(f"QuantizeConfig: Unknown quantization method: `{val}`.")
else:
- normalized[QUANT_METHOD_FIELD] = val
+ normalized[METHOD_FIELD_CODE] = _normalize_quant_method(val)
elif key == FORMAT_FIELD_CODE:
- normalized[key] = val.lower() if isinstance(val, str) else val
- elif key == "failsafe":
- normalized[key] = val
+ format_field_present = True
+ serialized_format_hint = None
+ try:
+ serialized_format_hint = resolve_quant_format(
+ val,
+ normalized.get(METHOD_FIELD_CODE),
+ )
+ except ValueError:
+ serialized_format_hint = None
+
+ format_hint = format or legacy_checkpoint_format or checkpoint_format_hint
+ if format_hint is not None:
+ try:
+ format_hint = resolve_quant_format(
+ format_hint,
+ normalized.get(METHOD_FIELD_CODE),
+ )
+ except ValueError:
+ format_hint = None
+ if serialized_format_hint in {FORMAT.GGUF, FORMAT.FP8} or format_hint in {FORMAT.GGUF, FORMAT.FP8}:
+ normalized[key] = val
+ else:
+ normalized[key] = _normalize_format(val)
elif key in field_names:
normalized[key] = val
else:
log.info(f"QuantizeConfig: Ignoring unknown parameter in the quantization configuration: {key}.")
- # fix method if format is not allowed for the method
- fmt = normalized.get(FORMAT_FIELD_CODE)
- method = normalized.get(QUANT_METHOD_FIELD)
+ if not format_field_present and legacy_checkpoint_format is not None:
+ normalized[FORMAT_FIELD_CODE] = legacy_checkpoint_format
- # TODO FIXME qqq compat which didn't have checkpoint_format before merging to gptqmodel
- if method == METHOD.QQQ and fmt != FORMAT.QQQ:
- log.info(f"QuantizeConfig: Auto fix `format` to `{FORMAT.QQQ}`")
- normalized[FORMAT_FIELD_CODE] = FORMAT.QQQ
- fmt = FORMAT.QQQ
-
- if fmt is not None:
- allowed_methods = [m for m, fmts in QUANT_METHOD_FORMAT_MAPPING.items() if fmt in fmts]
- if method not in allowed_methods:
- if fmt in {FORMAT.GEMM, FORMAT.GEMV, FORMAT.GEMV_FAST}:
- new_method = METHOD.AWQ
- elif fmt in {FORMAT.GPTQ, FORMAT.GPTQ_V2, FORMAT.BITBLAS}:
- new_method = METHOD.GPTQ
- elif fmt == FORMAT.QQQ:
- new_method = METHOD.QQQ
- elif fmt == FORMAT.MARLIN:
- new_method = method if method in {METHOD.GPTQ, METHOD.AWQ} else METHOD.GPTQ
- else:
- new_method = allowed_methods[0] if allowed_methods else METHOD.GPTQ
- if new_method != method:
- log.warn(
- f"QuantizeConfig: `{FORMAT_FIELD_CODE}`=`{fmt}` is incompatible with `{QUANT_METHOD_FIELD}`=`{method}`. Auto-fix method to `{new_method}`.")
- normalized[QUANT_METHOD_FIELD] = new_method
+ if quantize_cfg.get(AWQ_PACKING_BACKEND_FIELD) == "llm-awq":
+ normalized[METHOD_FIELD_CODE] = METHOD.AWQ
+ normalized[FORMAT_FIELD_CODE] = FORMAT.LLM_AWQ
+ normalized[PACK_DTYPE_FIELD] = torch.int16
+ log.info("Detected llm-awq quantization format; FORMAT automatically set to FORMAT.LLM_AWQ.")
+
+ meta_payload = normalized.get(META_FIELD)
+ meta_field_map = {
+ "fallback": "fallback",
+ "hessian": "hessian",
+ "gptaq": "gptaq",
+ "weight_only": "weight_only",
+ "pre_filters": "pre_filters",
+ "gc_mode": "gc_mode",
+ "wait_for_submodule_finalizers": "wait_for_submodule_finalizers",
+ "auto_forward_data_parallel": "auto_forward_data_parallel",
+ "vram_strategy": "vram_strategy",
+ "moe": "moe",
+ "offload_to_disk": "offload_to_disk",
+ "offload_to_disk_path": "offload_to_disk_path",
+ "pack_impl": "pack_impl",
+ "mse": "mse",
+ "mock_quantization": "mock_quantization",
+ "act_group_aware": "act_group_aware",
+ "true_sequential": "true_sequential",
+ "damp_percent": "damp_percent",
+ "damp_auto_increment": "damp_auto_increment",
+ }
+ if isinstance(meta_payload, dict):
+ for normalized_key, meta_key in meta_field_map.items():
+ if normalized_key not in normalized and meta_key in meta_payload:
+ normalized[normalized_key] = meta_payload.get(meta_key)
+
+ target_cls = cls if cls not in {BaseQuantizeConfig, QuantizeConfig} else _resolve_quantize_config_class(normalized)
+ normalized = _normalize_quantize_config_payload_for_target_cls(target_cls, normalized)
+ if target_cls is RTNQuantizeConfig:
+ normalized = _normalize_rtn_kwargs(normalized)
+ elif target_cls is GGUFConfig:
+ normalized = _normalize_gguf_kwargs(normalized)
+ elif target_cls is FP8Config:
+ normalized = _normalize_fp8_kwargs(normalized)
if format_auto_inferred:
- log.info(f"QuantizeConfig: `{FORMAT_FIELD_CHECKPOINT}` is missing from the quantization configuration and is automatically inferred to {normalized[FORMAT_FIELD_CODE]}")
+ log.info(
+ f"QuantizeConfig: `{FORMAT_FIELD_CODE}` is missing from the quantization configuration and is automatically inferred to {normalized[FORMAT_FIELD_CODE]}"
+ )
if normalized[FORMAT_FIELD_CODE] in {FORMAT.BITBLAS}:
- # AWQ and Marlin do not reorder the rows.
normalized["desc_act"] = False
- if "sym" not in normalized:
+ if "sym" not in normalized and target_cls not in {GGUFConfig, FP8Config, EXL3QuantizeConfig}:
log.warn(
"QuantizeConfig: config does not contain `sym` (symmetric quantization). This may result in silent errors. Defaulting to `sym=True`."
)
-
- meta_payload = normalized.get(META_FIELD)
- if "failsafe" not in normalized and isinstance(meta_payload, dict) and "failsafe" in meta_payload:
- normalized["failsafe"] = meta_payload.get("failsafe")
- if "hessian" not in normalized and isinstance(meta_payload, dict) and "hessian" in meta_payload:
- normalized["hessian"] = meta_payload.get("hessian")
- if "gptaq" not in normalized and isinstance(meta_payload, dict) and "gptaq" in meta_payload:
- normalized["gptaq"] = meta_payload.get("gptaq")
- if "gc_mode" not in normalized and isinstance(meta_payload, dict) and "gc_mode" in meta_payload:
- normalized["gc_mode"] = meta_payload.get("gc_mode")
- if (
- "wait_for_submodule_finalizers" not in normalized
- and isinstance(meta_payload, dict)
- and "wait_for_submodule_finalizers" in meta_payload
- ):
- normalized["wait_for_submodule_finalizers"] = meta_payload.get("wait_for_submodule_finalizers")
- if (
- "auto_forward_data_parallel" not in normalized
- and isinstance(meta_payload, dict)
- and "auto_forward_data_parallel" in meta_payload
- ):
- normalized["auto_forward_data_parallel"] = meta_payload.get("auto_forward_data_parallel")
-
- cfg = cls(**normalized)
-
- if quantize_cfg.get(AWQ_PACKING_BACKEND_FIELD) and quantize_cfg[AWQ_PACKING_BACKEND_FIELD] == "llm-awq":
- cfg.quant_method = METHOD.AWQ
- cfg.format = FORMAT.LLM_AWQ
- cfg.pack_dtype = torch.int16
- log.info(
- "Detected llm-awq quantization format; FORMAT automatically set to FORMAT.LLM_AWQ."
- )
-
- return cfg
+ return target_cls(**_filter_quantize_config_payload_for_target_cls(target_cls, normalized))
@classmethod
def from_pretrained(cls, save_dir: str, **kwargs):
@@ -1210,137 +2218,84 @@ def from_pretrained(cls, save_dir: str, **kwargs):
with open(resolved_config_file, "r", encoding="utf-8") as f:
args_from_json = json.load(f)
-
if transformers_config:
args_from_json = args_from_json["quantization_config"]
-
return cls.from_quant_config(args_from_json, format)
+ def _update_meta_payload(self, meta_payload: Dict[str, Any]) -> None:
+ return None
+
+ def _update_output_payload(self, out: Dict[str, Any]) -> None:
+ return None
+
def to_dict(self):
- smooth = None
- if self.failsafe is not None and self.failsafe.smooth is not None:
- payload = {"type": self.failsafe.smooth.name}
- payload["group_size_threshold"] = self.failsafe.smooth.group_size_threshold
- if isinstance(self.failsafe.smooth, SmoothPercentile):
- payload["percentile"] = self.failsafe.smooth.percentile
- elif isinstance(self.failsafe.smooth, SmoothPercentileAsymmetric):
- payload["low"] = self.failsafe.smooth.low
- payload["high"] = self.failsafe.smooth.high
- elif isinstance(self.failsafe.smooth, SmoothMAD):
- payload["k"] = self.failsafe.smooth.k
- elif isinstance(self.failsafe.smooth, SmoothMSE):
- payload["steps"] = self.failsafe.smooth.steps
- payload["maxshrink"] = self.failsafe.smooth.maxshrink
- elif isinstance(self.failsafe.smooth, SmoothOutlier):
- payload["pct"] = self.failsafe.smooth.pct
- elif isinstance(self.failsafe.smooth, SmoothSoftNorm):
- payload["k"] = self.failsafe.smooth.k
- elif isinstance(self.failsafe.smooth, SmoothLog):
- payload["percentile"] = self.failsafe.smooth.percentile
- payload["mu"] = self.failsafe.smooth.mu
- elif isinstance(self.failsafe.smooth, SmoothRowCol):
- payload["axis"] = self.failsafe.smooth.axis
- smooth = payload
+ smooth = _serialize_smooth_method(self.fallback.smooth if self.fallback is not None else None)
meta_payload = dict(self.meta) if self.meta else {}
if self.moe:
meta_payload["moe"] = self.moe.to_dict()
- if self.failsafe is None:
- meta_payload["failsafe"] = None
+ if self.fallback is None:
+ meta_payload["fallback"] = None
else:
- meta_payload["failsafe"] = {
- "strategy": self.failsafe.strategy.value if isinstance(self.failsafe.strategy, FailSafeStrategy) else self.failsafe.strategy,
- "threshold": self.failsafe.threshold,
+ meta_payload["fallback"] = {
+ "strategy": (
+ self.fallback.strategy.value
+ if isinstance(self.fallback.strategy, FallbackStrategy)
+ else self.fallback.strategy
+ ),
+ "threshold": self.fallback.threshold,
"smooth": smooth,
}
- if self.gptaq is None:
- meta_payload["gptaq"] = None
- else:
- device = self.gptaq.device
- device_value = device if isinstance(device, str) else str(device)
- meta_payload["gptaq"] = {
- "alpha": self.gptaq.alpha,
- "device": device_value,
- }
meta_payload["offload_to_disk"] = self.offload_to_disk
meta_payload["offload_to_disk_path"] = self.offload_to_disk_path
meta_payload["pack_impl"] = self.pack_impl
- meta_payload["mse"] = self.mse
- meta_payload["mock_quantization"] = self.mock_quantization
- meta_payload["act_group_aware"] = self.act_group_aware
- meta_payload["gc_mode"] = self.gc_mode
+ meta_payload["gc_mode"] = self.gc_mode.value if isinstance(self.gc_mode, GcMode) else self.gc_mode
meta_payload["wait_for_submodule_finalizers"] = self.wait_for_submodule_finalizers
meta_payload["auto_forward_data_parallel"] = self.auto_forward_data_parallel
- meta_payload["hessian"] = {
- "chunk_size": self.hessian.chunk_size,
- "chunk_bytes": self.hessian.chunk_bytes,
- "staging_dtype": str(self.hessian.staging_dtype).split(".")[-1],
- }
meta_payload["vram_strategy"] = (
self.vram_strategy.value if isinstance(self.vram_strategy, VramStrategy) else self.vram_strategy
)
+ self._update_meta_payload(meta_payload)
out = {
- "bits": self.bits,
+ "bits": serialize_quant_bits(self.bits),
"dynamic": self.dynamic,
"group_size": self.group_size,
"desc_act": self.desc_act,
"lm_head": self.lm_head,
- QUANT_METHOD_FIELD:self.quant_method,
+ METHOD_FIELD_CODE: self.method,
+ QUANT_METHOD_FIELD: self.method,
+ FORMAT_FIELD_CODE: self.format,
FORMAT_FIELD_CHECKPOINT: self.format,
- # torch.dtype convert to string
PACK_DTYPE_FIELD: str(self.pack_dtype).split(".")[-1],
META_FIELD: meta_payload,
- # DO NOT EXPORT Adapter to config/json since adapter can be swapped out/in
- # ADAPTER_FIELD: self.adapter.to_dict() if self.adapter else None,
- # DO NOT EXPORT compute_device_filter since functions are not serializable
}
-
- if self.quant_method == METHOD.AWQ:
- out["zero_point"] = not self.sym
- # awq compat with vllm/sglang/transformers loaders
- out["version"] = self.format
- out[FORMAT_FIELD_CODE] = self.format
- else:
- out["sym"] = self.sym
- if self.quant_method == METHOD.GPTQ:
- out[FORMAT_FIELD_CODE] = self.format
+ self._update_output_payload(out)
dynamic = out["dynamic"]
if dynamic:
- # dynamic adapter config is only used in the quantize phase and is deleted when saving.
for _, v in dynamic.items():
v.pop("adapter", None)
+ if "bits" in v:
+ v["bits"] = serialize_quant_bits(v["bits"])
- # simplify: clean keys where the value is None or empty [list, dict]
out = {k: v for k, v in out.items() if v is not None and (v not in [None, {}])}
-
dict_scale_dtype_to_str(out)
return out
- # TODO FIX ME, g_idx int32 per infeature but infeature count is per module
def calculate_bits_per_weight(self):
+ bit_width = quant_bits_width(self.bits)
if self.group_size != -1:
- # naive bits is
- #mlp.down_proj.g_idx: I32
- #mlp.down_proj.qweight: I32
- #mlp.down_proj.qzeros: I32
- #mlp.down_proj.scales: F16
- per_group_bits = self.group_size * self.bits # qweight: packed by group_size
- per_group_bits += 16 # scales fp16: one per group
- per_group_bits += self.bits # qzeros: one per group
- # FIX ME: g_idx is I32, one per infeature
- per_group_bits += 4 # ESTIMATE for g_idx int32: one per features/group_size item
+ per_group_bits = self.group_size * bit_width
+ per_group_bits += 16
+ per_group_bits += bit_width
+ per_group_bits += 4
bpw = per_group_bits / self.group_size
-
- # normally g_idx (int32 allocated one per in_feature) is allocated in device memory
- # but each module may have different infeatures we don't have enouch ctx here, use estimated `0.1` for now
bpw += 0.1
else:
- # there is only one scale int32 + one qzero int32 per entire module so overall it contributes to close to 0 bpw
- bpw = self.bits
+ bpw = bit_width
log.info(f"Estimated Quantization BPW (bits per weight): {bpw} bpw, based on [bits: {self.bits}, group_size: {self.group_size}]")
def moe_routing_override(self, num_experts: int) -> Union[int, None]:
@@ -1353,9 +2308,749 @@ def moe_routing_bypass(self) -> bool:
return False
return self.moe.routing_bypass()
-# deprecated: will be removed in future update
+ def uses_weight_only_lifecycle(self) -> bool:
+ return False
+
+ def requires_calibration_dataset(self) -> bool:
+ return not self.uses_weight_only_lifecycle()
+
+ def quant_linear_init_kwargs(self) -> Dict[str, Any]:
+ return {}
+
+
+@dataclass
+class PreFilterQuantizeConfig(BaseQuantizeConfig):
+ pre_filters: Optional[List[Union[BasePreFilterConfig, Dict[str, Any], str]]] = field(default=None)
+ smoother: Optional[Union[SmootherConfig, SmoothMethod, Dict[str, Any], str]] = field(default=None)
+ # Backward-compatible alias. New code should use `smoother`.
+ smooth: Optional[Union[SmoothMethod, Dict[str, Any], str]] = field(default=None, repr=False)
+
+ def _normalize_prefilter_state(self) -> None:
+ self.pre_filters = _normalize_pre_filters(self.pre_filters)
+
+ smoother_payload = self.smoother if self.smoother is not None else self.smooth
+ self.smoother = _normalize_smoother_config(smoother_payload)
+
+ if self.smoother is None:
+ for pre_filter in self.pre_filters:
+ if isinstance(pre_filter, SmootherConfig):
+ self.smoother = pre_filter
+ break
+
+ non_smoother_filters = [pre_filter for pre_filter in self.pre_filters if not isinstance(pre_filter, SmootherConfig)]
+ if self.smoother is not None:
+ non_smoother_filters.append(self.smoother)
+ self.pre_filters = non_smoother_filters
+ self.smooth = self.resolve_smooth_method()
+
+ def __post_init__(self):
+ self._normalize_prefilter_state()
+ super().__post_init__()
+
+ def resolve_smooth_method(self) -> Optional[SmoothMethod]:
+ if self.smoother is None:
+ return None
+ return self.smoother.smooth
+
+ def _update_meta_payload(self, meta_payload: Dict[str, Any]) -> None:
+ if self.pre_filters:
+ meta_payload["pre_filters"] = [pre_filter.to_dict() for pre_filter in self.pre_filters]
+
+
@dataclass
-class BaseQuantizeConfig(QuantizeConfig):
- def __init__(self, **kwargs):
- super().__init__(**kwargs)
- log.warn("QuantizeConfig: BaseQuantizeConfig is re-named and pending deprecation. Please use `QuantizeConfig` instead.")
+class QuantizeConfig(BaseQuantizeConfig, metaclass=QuantizeConfigMeta):
+ """Backward-compatible quantization config factory.
+
+ Direct construction dispatches to a concrete method-specific config class.
+ """
+
+
+@dataclass
+class GPTQQuantizeConfig(QuantizeConfig):
+ damp_percent: Optional[float] = field(default=None)
+ damp_auto_increment: Optional[float] = field(default=None)
+ act_group_aware: Optional[bool] = field(default=None)
+ static_groups: bool = field(default=False)
+ mse: float = field(default=0.0)
+ gptaq: Optional[GPTAQConfig] = field(default=None)
+ mock_quantization: bool = field(
+ default=False,
+ metadata={"help": "Skip heavy computations for fast model loading validation"},
+ )
+ hessian: Optional[HessianConfig] = field(default_factory=HessianConfig)
+
+ def allowed_quant_methods(self) -> Tuple[METHOD, ...]:
+ return (METHOD.GPTQ,)
+
+ def supported_export_formats(self) -> Tuple[FORMAT, ...]:
+ return GPTQ_EXPORT_FORMATS
+
+ def default_desc_act(self) -> bool:
+ return False
+
+ def __post_init__(self):
+ desc_act_user_value = self.desc_act
+ act_group_aware_user_value = self.act_group_aware
+ super().__post_init__()
+
+ if self.damp_percent is None:
+ self.damp_percent = _default_damp_percent(self.method)
+ if self.damp_auto_increment is None:
+ self.damp_auto_increment = _default_damp_auto_increment(self.method)
+ if not (0 < self.damp_percent < 1):
+ raise ValueError("QuantizeConfig: `damp_percent` must between 0 and 1.")
+ if self.damp_auto_increment < 0:
+ raise ValueError("QuantizeConfig:: `damp_auto_increment` must greater than 0.")
+
+ self.hessian = _normalize_hessian(self.hessian)
+ self.gptaq = _normalize_gptaq(self.gptaq)
+
+ if act_group_aware_user_value is None:
+ self.act_group_aware = self.method == METHOD.GPTQ
+ elif not isinstance(act_group_aware_user_value, bool):
+ self.act_group_aware = bool(act_group_aware_user_value)
+
+ self._resolve_activation_ordering(desc_act_user_value, act_group_aware_user_value)
+ if self.act_group_aware and self.desc_act:
+ raise ValueError("QuantizeConfig:: `act_group_aware` == `True` requires `desc_act` == `False`.")
+
+ def _resolve_activation_ordering(
+ self,
+ desc_act_user_value: Optional[bool],
+ act_group_aware_user_value: Optional[bool],
+ ) -> None:
+ desc_act_enabled_by_user = bool(desc_act_user_value) if desc_act_user_value is not None else False
+ act_group_aware_enabled_by_user = (
+ bool(act_group_aware_user_value) if act_group_aware_user_value is not None else False
+ )
+
+ if desc_act_enabled_by_user and act_group_aware_user_value is not None and act_group_aware_enabled_by_user:
+ raise ValueError(
+ "QuantizeConfig:: `act_group_aware` == `True` requires `desc_act` == `False` when both are explicitly set."
+ )
+
+ if desc_act_enabled_by_user and act_group_aware_user_value is None and self.act_group_aware:
+ log.warn(
+ "QuantizeConfig: `desc_act=True` automatically disables `act_group_aware`. "
+ "Set `act_group_aware=False` explicitly to silence this warning."
+ )
+ self.act_group_aware = False
+
+ def _update_meta_payload(self, meta_payload: Dict[str, Any]) -> None:
+ if self.gptaq is None:
+ meta_payload["gptaq"] = None
+ else:
+ device = self.gptaq.device
+ meta_payload["gptaq"] = {
+ "alpha": self.gptaq.alpha,
+ "device": device if isinstance(device, str) else str(device),
+ }
+
+ meta_payload["mse"] = self.mse
+ meta_payload["mock_quantization"] = self.mock_quantization
+ meta_payload["act_group_aware"] = self.act_group_aware
+ meta_payload["hessian"] = {
+ "chunk_size": self.hessian.chunk_size,
+ "chunk_bytes": self.hessian.chunk_bytes,
+ "staging_dtype": str(self.hessian.staging_dtype).split(".")[-1],
+ }
+
+ def _update_output_payload(self, out: Dict[str, Any]) -> None:
+ out["sym"] = self.sym
+ out[FORMAT_FIELD_CODE] = self.format
+
+
+@dataclass
+class AWQQuantizeConfig(QuantizeConfig):
+ method: METHOD = field(default=METHOD.AWQ)
+ format: FORMAT = field(default=FORMAT.GEMM)
+
+ def allowed_quant_methods(self) -> Tuple[METHOD, ...]:
+ return (METHOD.AWQ,)
+
+ def supported_export_formats(self) -> Tuple[FORMAT, ...]:
+ return AWQ_EXPORT_FORMATS
+
+ def __post_init__(self):
+ self.method = _normalize_quant_method(self.method)
+ self.format = _normalize_format(self.format)
+ if self.format not in self.supported_export_formats():
+ log.info(f"QuantizeConfig: Auto fix `format` to `{FORMAT.GEMM}`")
+ self.format = FORMAT.GEMM
+ super().__post_init__()
+
+ def _update_output_payload(self, out: Dict[str, Any]) -> None:
+ out["zero_point"] = not self.sym
+ out["version"] = self.format
+ out[FORMAT_FIELD_CODE] = self.format
+
+
+@dataclass
+class QQQQuantizeConfig(GPTQQuantizeConfig):
+ method: METHOD = field(default=METHOD.QQQ)
+ format: FORMAT = field(default=FORMAT.QQQ)
+
+ def allowed_quant_methods(self) -> Tuple[METHOD, ...]:
+ return (METHOD.QQQ,)
+
+ def supported_export_formats(self) -> Tuple[FORMAT, ...]:
+ return QQQ_EXPORT_FORMATS
+
+ def default_desc_act(self) -> bool:
+ return True
+
+
+@dataclass
+class FP8Config(PreFilterQuantizeConfig):
+ bits: int = field(default=8, metadata={"choices": [8]})
+ method: METHOD = field(default=METHOD.FP8)
+ format: Optional[str] = field(default="float8_e4m3fn")
+ group_size: int = field(default=-1)
+ desc_act: Optional[bool] = field(default=False)
+ sym: bool = field(default=True)
+ weight_scale_method: str = field(default="row")
+ weight_block_size: Optional[Union[List[int], Tuple[int, int]]] = field(default=None)
+ weight_scale_semantics: str = field(default="inverse")
+
+ def _resolve_checkpoint_format(self) -> FORMAT:
+ self.format = _normalize_fp8_fmt(self.format)
+ return FORMAT.FP8
+
+ def allowed_quant_methods(self) -> Tuple[METHOD, ...]:
+ return (METHOD.FP8,)
+
+ def supported_export_formats(self) -> Tuple[FORMAT, ...]:
+ return FP8_EXPORT_FORMATS
+
+ def default_desc_act(self) -> bool:
+ return False
+
+ def __post_init__(self):
+ self._normalize_prefilter_state()
+ super(PreFilterQuantizeConfig, self).__post_init__()
+
+ if self.bits != 8:
+ raise ValueError("FP8Config: `bits` must be `8`.")
+
+ if self.method != METHOD.FP8:
+ raise ValueError("FP8Config: `method` must be `fp8`.")
+
+ self.group_size = -1
+ self.desc_act = False
+ self.sym = True
+
+ self.format = _normalize_fp8_fmt(self.format)
+ block_size = _normalize_fp8_weight_block_size(self.weight_block_size)
+ self.weight_scale_method = _normalize_fp8_weight_scale_method(
+ self.weight_scale_method,
+ weight_block_size=block_size,
+ )
+ self.weight_block_size = list(block_size) if block_size is not None else None
+ self.weight_scale_semantics = _normalize_fp8_scale_semantics(self.weight_scale_semantics)
+
+ if self.dynamic is not None:
+ self.dynamic = {
+ **{k: v for k, v in self.dynamic.items() if k.startswith('-')},
+ **{k: v for k, v in self.dynamic.items() if not k.startswith('-')},
+ }
+ for layer, layer_dict in self.dynamic.items():
+ self._normalize_dynamic_layer_config(
+ layer,
+ layer_dict,
+ valid_bit_widths=[8],
+ checkpoint_format=FORMAT.FP8,
+ )
+
+ def _normalize_dynamic_layer_config(
+ self,
+ layer_name: str,
+ layer_dict: Dict[str, Any],
+ *,
+ valid_bit_widths: List[int],
+ checkpoint_format: FORMAT,
+ ) -> None:
+ del valid_bit_widths, checkpoint_format
+ if "bits" in layer_dict and int(layer_dict["bits"]) != 8:
+ raise ValueError(f"FP8Config: layer `{layer_name}` only supports 8-bit FP8 weights.")
+ if "group_size" in layer_dict and layer_dict["group_size"] not in (-1, None):
+ raise ValueError("FP8Config: `group_size` is not used; keep it at `-1`.")
+
+ block_size = _normalize_fp8_weight_block_size(layer_dict.get("weight_block_size"))
+ raw_format = layer_dict.get(FORMAT_FIELD_CODE, layer_dict.get("fmt"))
+ if raw_format is not None:
+ layer_dict[FORMAT_FIELD_CODE] = _normalize_fp8_fmt(raw_format)
+ layer_dict.pop("fmt", None)
+ if "weight_scale_method" in layer_dict or block_size is not None:
+ layer_dict["weight_scale_method"] = _normalize_fp8_weight_scale_method(
+ layer_dict.get("weight_scale_method"),
+ weight_block_size=block_size,
+ )
+ if "weight_scale_semantics" in layer_dict:
+ layer_dict["weight_scale_semantics"] = _normalize_fp8_scale_semantics(
+ layer_dict["weight_scale_semantics"]
+ )
+ if "weight_block_size" in layer_dict:
+ layer_dict["weight_block_size"] = list(block_size) if block_size is not None else None
+
+ def quant_linear_init_kwargs(self) -> Dict[str, Any]:
+ return {
+ "format": self.format,
+ "weight_scale_method": self.weight_scale_method,
+ "weight_block_size": self.weight_block_size,
+ "weight_scale_semantics": self.weight_scale_semantics,
+ }
+
+ def _update_output_payload(self, out: Dict[str, Any]) -> None:
+ out[FORMAT_FIELD_CODE] = self.format
+ out["weight_scale_method"] = self.weight_scale_method
+ out["weight_block_size"] = self.weight_block_size
+ out["weight_scale_semantics"] = self.weight_scale_semantics
+
+ def uses_weight_only_lifecycle(self) -> bool:
+ return True
+
+
+FP8QuantizeConfig = FP8Config
+
+
+@dataclass
+class EXL3QuantizeConfig(BaseQuantizeConfig):
+ bits: float = field(default=3.0)
+ method: METHOD = field(default=METHOD.EXL3)
+ format: FORMAT = field(default=FORMAT.EXL3)
+ group_size: int = field(default=-1)
+ desc_act: Optional[bool] = field(default=False)
+ sym: bool = field(default=True)
+ head_bits: Optional[float] = field(default=None)
+ out_scales: Optional[str] = field(default="auto")
+ codebook: str = field(default="mcg")
+ tensor_storage: Optional[Dict[str, Any]] = field(default=None)
+ calibration: Optional[Dict[str, int]] = field(default=None)
+
+ @property
+ def runtime_bits(self) -> int:
+ return quant_bits_width(self.bits)
+
+ def allowed_quant_methods(self) -> Tuple[METHOD, ...]:
+ return (METHOD.EXL3,)
+
+ def supported_export_formats(self) -> Tuple[FORMAT, ...]:
+ return EXL3_EXPORT_FORMATS
+
+ def default_desc_act(self) -> bool:
+ return False
+
+ def _normalize_bits_field(self, bits_value, checkpoint_format: FORMAT):
+ return _normalize_exl3_bits(bits_value)
+
+ def _normalize_dynamic_layer_config(
+ self,
+ layer_name: str,
+ layer_dict: Dict[str, Any],
+ *,
+ valid_bit_widths: List[int],
+ checkpoint_format: FORMAT,
+ ) -> None:
+ del valid_bit_widths, checkpoint_format
+ for key, value in layer_dict.items():
+ if key == "bits":
+ layer_dict[key] = _normalize_exl3_bits(value)
+ elif key == "head_bits":
+ layer_dict[key] = None if value is None else _normalize_exl3_bits(value)
+ elif key == "group_size" and value not in (-1, None):
+ raise ValueError("EXL3QuantizeConfig: `group_size` is not used; keep it at `-1`.")
+
+ def __post_init__(self):
+ self.method = _normalize_quant_method(self.method)
+ self.format = _normalize_format(self.format)
+ self.pack_dtype = _normalize_pack_dtype(self.pack_dtype)
+ self.bits = _normalize_exl3_bits(self.bits)
+ self.head_bits = None if self.head_bits is None else _normalize_exl3_bits(self.head_bits)
+
+ if self.method != METHOD.EXL3:
+ raise ValueError("EXL3QuantizeConfig: `method` must be `exl3`.")
+ if self.format != FORMAT.EXL3:
+ raise ValueError("EXL3QuantizeConfig: `format` must be `exl3`.")
+
+ self.group_size = -1
+ self.desc_act = False
+ self.sym = True
+
+ self.fallback = _normalize_fallback(self.fallback)
+
+ if self.dynamic is not None:
+ self.dynamic = {
+ **{k: v for k, v in self.dynamic.items() if k.startswith('-')},
+ **{k: v for k, v in self.dynamic.items() if not k.startswith('-')},
+ }
+ for layer, layer_dict in self.dynamic.items():
+ self._normalize_dynamic_layer_config(
+ layer,
+ layer_dict,
+ valid_bit_widths=[],
+ checkpoint_format=FORMAT.EXL3,
+ )
+
+ if self.out_scales is not None:
+ normalized_out_scales = str(self.out_scales).strip().lower()
+ out_scale_aliases = {
+ "always": "always",
+ "true": "always",
+ "never": "never",
+ "false": "never",
+ "auto": "auto",
+ "none": "auto",
+ }
+ if normalized_out_scales not in out_scale_aliases:
+ raise ValueError("EXL3QuantizeConfig: `out_scales` must be one of `always`, `never`, or `auto`.")
+ self.out_scales = out_scale_aliases[normalized_out_scales]
+
+ self.codebook = str(self.codebook).strip().lower()
+ if self.codebook not in {"mcg", "mul1", "3inst"}:
+ raise ValueError("EXL3QuantizeConfig: `codebook` must be one of `mcg`, `mul1`, or `3inst`.")
+
+ if self.tensor_storage is not None and not isinstance(self.tensor_storage, dict):
+ raise ValueError("EXL3QuantizeConfig: `tensor_storage` must be a dictionary when provided.")
+ if self.calibration is not None:
+ if not isinstance(self.calibration, dict):
+ raise ValueError("EXL3QuantizeConfig: `calibration` must be a dictionary when provided.")
+ self.calibration = {
+ str(key): int(value)
+ for key, value in self.calibration.items()
+ }
+
+ if self.meta is not None:
+ if not isinstance(self.meta, dict):
+ raise ValueError("QuantizeConfig: `meta` must be a dictionary")
+ for key in self.meta:
+ if not isinstance(key, str):
+ raise ValueError("QuantizeConfig: `meta` keys must be strings")
+ else:
+ self.meta = {}
+
+ self.adapter = normalize_adapter(self.adapter)
+
+ if self.offload_to_disk and not self.offload_to_disk_path:
+ path_key = f"{get_random_string()}-{get_random_string()}"
+ self.offload_to_disk_path = f"./gptqmodel_offload/{path_key}/"
+ log.info(f"QuantizeConfig: offload_to_disk_path auto set to `{self.offload_to_disk_path}`")
+
+ self.vram_strategy = _normalize_vram_strategy(self.vram_strategy)
+ self.gc_mode = _normalize_gc_mode(self.gc_mode)
+ self.moe = _normalize_moe_config(self.moe)
+
+ def _update_output_payload(self, out: Dict[str, Any]) -> None:
+ out["bits"] = float(self.bits)
+ out["head_bits"] = None if self.head_bits is None else float(self.head_bits)
+ out["out_scales"] = self.out_scales
+ out["codebook"] = self.codebook
+ out["tensor_storage"] = self.tensor_storage
+ out["calibration"] = self.calibration
+
+ def calculate_bits_per_weight(self):
+ head_bits = self.head_bits if self.head_bits is not None else self.bits
+ log.info(
+ "Estimated Quantization BPW (bits per weight): %s bpw, based on [bits: %s, head_bits: %s]",
+ self.bits,
+ self.bits,
+ head_bits,
+ )
+
+@dataclass
+class RTNQuantizeConfig(PreFilterQuantizeConfig):
+ method: METHOD = field(default=METHOD.GPTQ)
+ format: FORMAT = field(default=FORMAT.GPTQ)
+
+ def allowed_quant_methods(self) -> Tuple[METHOD, ...]:
+ return (METHOD.GPTQ,)
+
+ def supported_export_formats(self) -> Tuple[FORMAT, ...]:
+ return RTN_EXPORT_FORMATS
+
+ def default_desc_act(self) -> bool:
+ return False
+
+ def __post_init__(self):
+ super().__post_init__()
+
+ def _update_output_payload(self, out: Dict[str, Any]) -> None:
+ out["sym"] = self.sym
+ out[FORMAT_FIELD_CODE] = self.format
+
+ def _update_meta_payload(self, meta_payload: Dict[str, Any]) -> None:
+ super()._update_meta_payload(meta_payload)
+ meta_payload["weight_only"] = {
+ "smooth": _serialize_smooth_method(self.smooth),
+ }
+
+ def uses_weight_only_lifecycle(self) -> bool:
+ return True
+
+
+@dataclass
+class GGUFConfig(PreFilterQuantizeConfig):
+ format: Optional[str] = field(default=None)
+ method: METHOD = field(default=METHOD.GGUF, init=False)
+ group_size: int = field(default=-1, init=False, repr=False)
+ desc_act: Optional[bool] = field(default=False, init=False, repr=False)
+ sym: bool = field(default=True, init=False, repr=False)
+ _gguf_bits: GGUFBits = field(init=False, repr=False, compare=False)
+
+ @property
+ def runtime_bits(self) -> GGUFBits:
+ return self._gguf_bits
+
+ def allowed_quant_methods(self) -> Tuple[METHOD, ...]:
+ return (METHOD.GGUF,)
+
+ def supported_export_formats(self) -> Tuple[FORMAT, ...]:
+ return (FORMAT.GGUF,)
+
+ def default_desc_act(self) -> bool:
+ return False
+
+ def _resolve_checkpoint_format(self) -> FORMAT:
+ self.bits, self.format, self._gguf_bits = _normalize_gguf_config_spec(self.bits, self.format)
+ return FORMAT.GGUF
+
+ def _normalize_bits_field(self, bits_value, checkpoint_format: FORMAT):
+ normalized = _normalize_quant_bits(bits_value, format_value=None)
+ return normalized.bits if isinstance(normalized, GGUFBits) else normalized
+
+ def _normalize_dynamic_layer_config(
+ self,
+ layer_name: str,
+ layer_dict: Dict[str, Any],
+ *,
+ valid_bit_widths: List[int],
+ checkpoint_format: FORMAT,
+ ) -> None:
+ bits_override_present = "bits" in layer_dict
+ format_override_present = FORMAT_FIELD_CODE in layer_dict
+
+ if bits_override_present or format_override_present:
+ raw_bits = layer_dict.get("bits", self.bits)
+ raw_format = layer_dict.get(FORMAT_FIELD_CODE, self.format)
+ normalized_bits, normalized_format, normalized_runtime_bits = _normalize_gguf_config_spec(raw_bits, raw_format)
+
+ layer_dict["bits"] = normalized_bits
+
+ bits_implied_format = (
+ isinstance(raw_bits, GGUFBits)
+ or (isinstance(raw_bits, str) and not raw_bits.strip().isdigit())
+ )
+ if format_override_present or bits_implied_format:
+ layer_dict[FORMAT_FIELD_CODE] = normalized_format
+
+ if quant_bits_width(normalized_runtime_bits) not in valid_bit_widths:
+ raise ValueError(
+ f"QuantizeConfig: Layer `{layer_name}` only support quantization of `{valid_bit_widths}` bits."
+ )
+
+ if "group_size" in layer_dict and layer_dict["group_size"] != -1 and layer_dict["group_size"] <= 0:
+ raise ValueError(_resolve_dynamic_group_size_error())
+
+ def __post_init__(self):
+ self._normalize_prefilter_state()
+ # GGUFConfig already normalized pre-filters above; skip the parent hook to
+ # avoid running that normalization twice.
+ BaseQuantizeConfig.__post_init__(self)
+ self._gguf_bits = _gguf_bits_from_components(self.bits, self.format)
+
+ def _update_meta_payload(self, meta_payload: Dict[str, Any]) -> None:
+ super()._update_meta_payload(meta_payload)
+
+ def _update_output_payload(self, out: Dict[str, Any]) -> None:
+ out[FORMAT_FIELD_CODE] = self.format
+
+ def to_dict(self):
+ out = super().to_dict()
+ out.pop(GROUP_SIZE_FIELD_CODE, None)
+ out.pop("desc_act", None)
+ out.pop(PACK_DTYPE_FIELD, None)
+
+ meta_payload = out.get(META_FIELD)
+ if isinstance(meta_payload, dict):
+ for key in (
+ "fallback",
+ "offload_to_disk",
+ "offload_to_disk_path",
+ "pack_impl",
+ "gc_mode",
+ "wait_for_submodule_finalizers",
+ "auto_forward_data_parallel",
+ "vram_strategy",
+ "weight_only",
+ ):
+ meta_payload.pop(key, None)
+ if not meta_payload:
+ out.pop(META_FIELD, None)
+
+ return out
+
+ def calculate_bits_per_weight(self):
+ bits_name = self.runtime_bits.to_string()
+ bpw = _GGUF_APPROX_BITS_PER_WEIGHT_BY_ALIAS.get(bits_name, float(quant_bits_width(self.runtime_bits)))
+ log.info(
+ f"Estimated Quantization BPW (bits per weight): {bpw} bpw, based on [bits: {self.bits}, format: {self.format}]"
+ )
+
+ def uses_weight_only_lifecycle(self) -> bool:
+ return True
+
+
+GGUFQuantizeConfig = GGUFConfig
+
+
+def clone_weight_only_config_for_module(
+ qcfg: Union[RTNQuantizeConfig, GGUFConfig, FP8Config],
+ module_full_name: str,
+) -> Optional[Union[RTNQuantizeConfig, GGUFConfig, FP8Config]]:
+ if qcfg.dynamic_get(layer_name=module_full_name) is False:
+ return None
+
+ qcfg_clone = copy.deepcopy(qcfg)
+
+ if qcfg.dynamic is not None:
+ smooth_override = qcfg.dynamic_get(module_full_name, "smoother", None)
+ if smooth_override is None:
+ smooth_override = qcfg.dynamic_get(module_full_name, "smooth", None)
+ if smooth_override is not None:
+ qcfg_clone.smoother = _normalize_smoother_config(smooth_override)
+ qcfg_clone.smooth = qcfg_clone.resolve_smooth_method()
+
+ if isinstance(qcfg_clone, GGUFConfig):
+ dynamic_bits = qcfg.dynamic_get(module_full_name, "bits", qcfg_clone.bits)
+ dynamic_format = qcfg.dynamic_get(module_full_name, FORMAT_FIELD_CODE, qcfg_clone.format)
+ qcfg_clone.bits, qcfg_clone.format, qcfg_clone._gguf_bits = _normalize_gguf_config_spec(
+ dynamic_bits,
+ dynamic_format,
+ )
+ elif isinstance(qcfg_clone, FP8Config):
+ dynamic_format = qcfg.dynamic_get(module_full_name, FORMAT_FIELD_CODE, None)
+ if dynamic_format is None:
+ dynamic_format = qcfg.dynamic_get(module_full_name, "fmt", qcfg_clone.format)
+ dynamic_block_size = qcfg.dynamic_get(
+ module_full_name,
+ "weight_block_size",
+ qcfg_clone.weight_block_size,
+ )
+ block_size = _normalize_fp8_weight_block_size(dynamic_block_size)
+ qcfg_clone.format = _normalize_fp8_fmt(dynamic_format)
+ qcfg_clone.weight_scale_method = _normalize_fp8_weight_scale_method(
+ qcfg.dynamic_get(
+ module_full_name,
+ "weight_scale_method",
+ qcfg_clone.weight_scale_method,
+ ),
+ weight_block_size=block_size,
+ )
+ qcfg_clone.weight_block_size = list(block_size) if block_size is not None else None
+ qcfg_clone.weight_scale_semantics = _normalize_fp8_scale_semantics(
+ qcfg.dynamic_get(
+ module_full_name,
+ "weight_scale_semantics",
+ qcfg_clone.weight_scale_semantics,
+ )
+ )
+ else:
+ qcfg_clone.bits = _normalize_quant_bits(
+ qcfg.dynamic_get(module_full_name, "bits", qcfg_clone.bits),
+ format_value=resolve_quant_format(qcfg_clone.format, qcfg_clone.method),
+ )
+
+ if isinstance(qcfg_clone, RTNQuantizeConfig):
+ qcfg_clone.sym = qcfg.dynamic_get(module_full_name, "sym", qcfg_clone.sym)
+ qcfg_clone.group_size = qcfg.dynamic_get(module_full_name, "group_size", qcfg_clone.group_size)
+
+ desc_act_override = qcfg.dynamic_get(module_full_name, "desc_act", None)
+ if desc_act_override is not None:
+ qcfg_clone.desc_act = desc_act_override
+
+ return qcfg_clone
+
+
+clone_rtn_config_for_module = clone_weight_only_config_for_module
+
+
+def _resolve_quantize_config_class(payload: Dict[str, Any]) -> type[BaseQuantizeConfig]:
+ method = payload.get(METHOD_FIELD_CODE, payload.get(QUANT_METHOD_FIELD, METHOD.GPTQ))
+ raw_format_value = payload.get(FORMAT_FIELD_CODE, payload.get(FORMAT_FIELD_CHECKPOINT, FORMAT.GPTQ))
+ weight_only = payload.get("weight_only")
+ bits = payload.get(BITS_FIELD_CODE)
+ gguf_public_format = payload.get(FORMAT_FIELD_CODE)
+
+ try:
+ method = _normalize_quant_method(method)
+ except Exception:
+ method = METHOD.GPTQ
+
+ if _looks_like_fp8_fmt(raw_format_value):
+ format_value = FORMAT.FP8
+ else:
+ try:
+ format_value = _normalize_format(raw_format_value)
+ except Exception:
+ try:
+ gguf_public_format = _normalize_gguf_public_format(raw_format_value)
+ except ValueError:
+ gguf_public_format = payload.get(FORMAT_FIELD_CODE)
+ format_value = FORMAT.GPTQ
+
+ gguf_format_detected = False
+ if gguf_public_format is not None:
+ try:
+ gguf_format_detected = _normalize_gguf_public_format(gguf_public_format) is not None
+ except ValueError:
+ gguf_format_detected = False
+
+ weight_only_method = _peek_weight_only_method(weight_only)
+ fp8_storage_fmt = payload.get(FORMAT_FIELD_CODE, payload.get("fmt"))
+ if weight_only is not None and weight_only_method not in {None, WeightOnlyMethod.RTN, WeightOnlyMethod.GGUF, WeightOnlyMethod.FP8}:
+ raise ValueError(
+ "QuantizeConfig: unsupported weight-only config. Weight-only export currently supports `rtn`, `gguf`, and `fp8`."
+ )
+ if (
+ format_value == FORMAT.GGUF
+ or weight_only_method == WeightOnlyMethod.GGUF
+ or _looks_like_gguf_bits(bits)
+ or gguf_format_detected
+ ):
+ return GGUFConfig
+ if weight_only_method == WeightOnlyMethod.FP8:
+ return FP8Config
+ if weight_only_method == WeightOnlyMethod.RTN:
+ return RTNQuantizeConfig
+ if weight_only is not None:
+ return RTNQuantizeConfig
+ if method == METHOD.FP8 or format_value == FORMAT.FP8 or _looks_like_fp8_fmt(fp8_storage_fmt):
+ return FP8Config
+ if method == METHOD.EXL3 or format_value == FORMAT.EXL3:
+ return EXL3QuantizeConfig
+ if method == METHOD.QQQ or format_value == FORMAT.QQQ:
+ return QQQQuantizeConfig
+ if method == METHOD.AWQ:
+ return AWQQuantizeConfig
+ if format_value in {FORMAT.GEMM, FORMAT.GEMV, FORMAT.GEMV_FAST, FORMAT.LLM_AWQ}:
+ return AWQQuantizeConfig
+ if format_value == FORMAT.MARLIN:
+ return AWQQuantizeConfig if method == METHOD.AWQ else GPTQQuantizeConfig
+ return GPTQQuantizeConfig
+
+
+def _known_quantize_config_field_names() -> set[str]:
+ field_names: set[str] = set()
+ for cls in (
+ BaseQuantizeConfig,
+ PreFilterQuantizeConfig,
+ QuantizeConfig,
+ GPTQQuantizeConfig,
+ AWQQuantizeConfig,
+ QQQQuantizeConfig,
+ FP8Config,
+ EXL3QuantizeConfig,
+ RTNQuantizeConfig,
+ GGUFConfig,
+ ):
+ field_names.update(field.name for field in fields(cls))
+ return field_names
diff --git a/gptqmodel/quantization/dtype.py b/gptqmodel/quantization/dtype.py
index f24fe5818..34a10095b 100644
--- a/gptqmodel/quantization/dtype.py
+++ b/gptqmodel/quantization/dtype.py
@@ -20,11 +20,19 @@
__all__ = [
"device_supports_native_fp8",
+ "dequantize_fp8",
"dequantize_f8_e4m3",
"dequantize_f4_e2m1",
]
+_FLOAT8_DTYPES = tuple(
+ getattr(torch, name)
+ for name in ("float8_e4m3fn", "float8_e5m2")
+ if hasattr(torch, name)
+)
+
+
def device_supports_native_fp8(device: Optional[torch.device] = None) -> bool:
"""Return ``True`` when the target CUDA device supports native FP8 (E4M3).
@@ -92,13 +100,13 @@ def dequantize_f8_e4m3(
omitted the helper falls back to a plain dtype conversion.
"""
- if not hasattr(torch, "float8_e4m3fn"):
- raise RuntimeError("Current PyTorch build does not provide float8_e4m3fn tensors")
+ if not _FLOAT8_DTYPES:
+ raise RuntimeError("Current PyTorch build does not provide FP8 tensors")
if scale is not None and scale_inv is not None:
raise ValueError("Provide either scale or scale_inv, not both")
- if tensor.dtype is not torch.float8_e4m3fn:
+ if tensor.dtype not in _FLOAT8_DTYPES:
result = tensor.to(target_dtype)
else:
result = tensor.to(target_dtype)
@@ -172,6 +180,23 @@ def _expand_scale(scale_tensor: torch.Tensor, *, axis_hint: Optional[int]) -> to
return result
+def dequantize_fp8(
+ tensor: torch.Tensor,
+ *,
+ scale: Optional[torch.Tensor] = None,
+ scale_inv: Optional[torch.Tensor] = None,
+ axis: Optional[int] = 0,
+ target_dtype: torch.dtype = torch.bfloat16,
+) -> torch.Tensor:
+ return dequantize_f8_e4m3(
+ tensor,
+ scale=scale,
+ scale_inv=scale_inv,
+ axis=axis,
+ target_dtype=target_dtype,
+ )
+
+
def dequantize_f4_e2m1(
tensor: torch.Tensor,
*,
diff --git a/gptqmodel/quantization/failsafe_smooth.py b/gptqmodel/quantization/fallback_smooth.py
similarity index 98%
rename from gptqmodel/quantization/failsafe_smooth.py
rename to gptqmodel/quantization/fallback_smooth.py
index 1bb3734f3..12e6ec330 100644
--- a/gptqmodel/quantization/failsafe_smooth.py
+++ b/gptqmodel/quantization/fallback_smooth.py
@@ -10,7 +10,7 @@
import torch
from .config import (
- FailSafe,
+ Fallback,
QuantizeConfig,
SmoothLog,
SmoothMAD,
@@ -23,7 +23,7 @@
# For Gaussian-like rows, raw MAD ~= 0.67449 * sigma. Normalize MAD so the
-# configured `k` behaves like a sigma-width window instead of clipping more
+# configured `k` behaves like a sigma-width window instead of clipping far more
# aggressively than intended.
MAD_TO_STD_SCALE = 1.4826
@@ -53,12 +53,12 @@ def _clamp_block(block: torch.Tensor, lo: torch.Tensor, hi: torch.Tensor) -> tor
def smooth_block(
block: torch.Tensor,
- failsafe: FailSafe,
+ fallback: Fallback,
*,
eps: float = 1e-8,
group_size: Optional[int] = None,
) -> Tuple[torch.Tensor, Optional[torch.Tensor]]:
- method = getattr(failsafe, "smooth", None)
+ method = getattr(fallback, "smooth", None)
if method is None:
return block, None
if group_size is not None and group_size < 0:
diff --git a/gptqmodel/quantization/gptq.py b/gptqmodel/quantization/gptq.py
index 08072de93..fc3347c35 100644
--- a/gptqmodel/quantization/gptq.py
+++ b/gptqmodel/quantization/gptq.py
@@ -21,11 +21,11 @@
from ..looper.named_module import NamedModule
from ..quantization import QuantizeConfig
-from ..quantization.config import FailSafeStrategy, SmoothMSE
+from ..quantization.config import FallbackStrategy, SmoothMSE
from ..utils.device import get_device
from ..utils.logger import setup_logger
from ..utils.torch import torch_sync
-from .failsafe_smooth import mse_optimal_quant, smooth_block
+from .fallback_smooth import mse_optimal_quant, smooth_block
from .gar import compose_final_perm, compute_global_perm, compute_local_perms, invert_perm
from .quantizer import HF_OPTIMUM, Quantizer
@@ -199,7 +199,7 @@ def __init__(self, module: nn.Module, qcfg: Optional[QuantizeConfig] = None):
# fwd counter
self.fwd_counter = 0
- self.failsafe = self.qcfg.failsafe
+ self.fallback = self.qcfg.fallback
self.expected_nsamples: Optional[float] = None
self.H: Optional[torch.Tensor] = None
@@ -618,13 +618,13 @@ def create_H(self, target_device):
return torch.zeros((self.columns, self.columns), dtype=torch.float32,
device=self._select_hessian_target_device(target_device))
- def _failsafe_quantize(self, strategy: FailSafeStrategy, blocksize: int):
+ def _fallback_quantize(self, strategy: FallbackStrategy, blocksize: int):
"""Apply a lightweight quantization fallback using the requested strategy."""
maxq = 2 ** self.qcfg.bits - 1
sigma = 3.0
effective_group_size = self.qcfg.group_size if self.qcfg.group_size != -1 else self.columns
start_time = time.time()
- smooth_method = getattr(self.failsafe, "smooth", None)
+ smooth_method = getattr(self.fallback, "smooth", None)
mse_steps = 32
mse_maxshrink = 0.8
if isinstance(smooth_method, SmoothMSE):
@@ -652,10 +652,10 @@ def _failsafe_quantize(self, strategy: FailSafeStrategy, blocksize: int):
else:
block_mod, scale_factor = smooth_block(
block,
- self.failsafe,
+ self.fallback,
group_size=effective_group_size,
)
- if strategy == FailSafeStrategy.MIDPOINT:
+ if strategy == FallbackStrategy.MIDPOINT:
w_min = block_mod.min(dim=1, keepdim=True).values
w_max = block_mod.max(dim=1, keepdim=True).values
mid = (w_max + w_min) / 2.0
@@ -666,7 +666,7 @@ def _failsafe_quantize(self, strategy: FailSafeStrategy, blocksize: int):
zero = torch.round(zero_mid - (mid / scale))
zero = torch.clamp(zero, 0, maxq)
dequant = (q - zero) * scale
- elif strategy == FailSafeStrategy.MEAN:
+ elif strategy == FallbackStrategy.MEAN:
mean = block_mod.mean(dim=1, keepdim=True)
max_dev = torch.max((block_mod - mean).abs(), dim=1, keepdim=True).values
max_dev = torch.clamp(max_dev, min=1e-8)
@@ -677,7 +677,7 @@ def _failsafe_quantize(self, strategy: FailSafeStrategy, blocksize: int):
zero = torch.round(zero_mid - (mean / scale))
zero = torch.clamp(zero, 0, maxq)
dequant = (q - zero) * scale
- elif strategy == FailSafeStrategy.MEDIAN:
+ elif strategy == FallbackStrategy.MEDIAN:
median = block_mod.median(dim=1, keepdim=True).values
max_dev = torch.max((block_mod - median).abs(), dim=1, keepdim=True).values
max_dev = torch.clamp(max_dev, min=1e-8)
@@ -688,7 +688,7 @@ def _failsafe_quantize(self, strategy: FailSafeStrategy, blocksize: int):
zero = torch.round(zero_mid - (median / scale))
zero = torch.clamp(zero, 0, maxq)
dequant = (q - zero) * scale
- elif strategy == FailSafeStrategy.STDCLIP:
+ elif strategy == FallbackStrategy.STDCLIP:
mean = block_mod.mean(dim=1, keepdim=True)
std = block_mod.std(dim=1, keepdim=True, unbiased=False)
std = torch.clamp(std, min=1e-8)
@@ -700,13 +700,13 @@ def _failsafe_quantize(self, strategy: FailSafeStrategy, blocksize: int):
q = torch.round(block_mod / scale + zero)
q = torch.clamp(q, 0, maxq)
dequant = (q - zero) * scale
- elif strategy == FailSafeStrategy.RTN:
+ elif strategy == FallbackStrategy.RTN:
self.quantizer.find_params(block_mod, weight=True)
dequant = self.quantizer.quantize(block_mod)
scale = self.quantizer.scale
zero = self.quantizer.zero
else:
- raise ValueError(f"Unsupported failsafe strategy: {strategy}")
+ raise ValueError(f"Unsupported fallback strategy: {strategy}")
if scale_factor is not None:
scale = scale * scale_factor
@@ -748,7 +748,7 @@ def _failsafe_quantize(self, strategy: FailSafeStrategy, blocksize: int):
Q = Q.to(device=self.module.weight.data.device, non_blocking=False)
mean_abs_err = (Q - self.module.weight.data).abs().mean().item()
duration = time.time() - start_time
- avg_loss = f"failsafe({strategy.value}): {mean_abs_err:.7f}"
+ avg_loss = f"fallback({strategy.value}): {mean_abs_err:.7f}"
damp = 0.0
self.H = None
@@ -886,28 +886,28 @@ def quantize(
start = time.time()
target_device = getattr(self.module, "target_device", None)
- from ..utils.failsafe import resolve_failsafe_strategy, resolve_threshold, should_use_failsafe
+ from ..utils.fallback import resolve_fallback_strategy, resolve_threshold, should_use_fallback
- resolved_strategy = resolve_failsafe_strategy(self.failsafe)
- fallback_requested = should_use_failsafe(
- self.failsafe,
+ resolved_strategy = resolve_fallback_strategy(self.fallback)
+ fallback_requested = should_use_fallback(
+ self.fallback,
float(self.nsamples),
self.expected_nsamples,
)
- threshold_raw, is_percent = resolve_threshold(self.failsafe, self.expected_nsamples)
- failsafe_configured = threshold_raw is not None
+ threshold_raw, is_percent = resolve_threshold(self.fallback, self.expected_nsamples)
+ fallback_configured = threshold_raw is not None
if fallback_requested:
use_hessian = False
- threshold_text = str(getattr(self.failsafe, "threshold", None))
+ threshold_text = str(getattr(self.fallback, "threshold", None))
threshold_info = f", threshold_raw={threshold_raw}" if threshold_raw is not None and is_percent else ""
log.warn(
f"Quantization: Module `{self.name}` -> "
- f"Using `{resolved_strategy.value}` failsafe quantization (observed {self.nsamples} samples, threshold={threshold_text}{threshold_info}, max_total={self.expected_nsamples})."
+ f"Using `{resolved_strategy.value}` fallback quantization (observed {self.nsamples} samples, threshold={threshold_text}{threshold_info}, max_total={self.expected_nsamples})."
)
self.H = self.create_H(target_device=target_device)
- return self._failsafe_quantize(resolved_strategy, blocksize)
+ return self._fallback_quantize(resolved_strategy, blocksize)
else:
use_hessian = True
self.finalize_hessian(target_device=target_device)
@@ -1157,20 +1157,20 @@ def quantize(
if math.isnan(avg_loss):
print("Losses sum item:", torch.sum(Losses).item())
- if failsafe_configured:
+ if fallback_configured:
log.info(f"Quantization: Failed due to `NaN` loss for `{self.name}`, use mock quantization retry for `{self.name}`")
self.qcfg.mock_quantization = True
return self.quantize(blocksize=blocksize)
else:
- raise ValueError(f"Quantization: Failed due to `NaN` loss for `{self.name}`, please try increasing calibration data samples or enable failsafe=True")
+ raise ValueError(f"Quantization: Failed due to `NaN` loss for `{self.name}`, please try increasing calibration data samples or enable fallback=True")
else:
- if failsafe_configured:
+ if fallback_configured:
log.warn(f"Quantization: Module `{self.name}` -> using fail safe mode. Please check if calibration data is sufficient.")
else:
log.warn(f"Quantization: `{self.name}` is not activated due to model inference logic (MoE)")
- avg_loss = f"{resolved_strategy.value} failsafe" if failsafe_configured else 999999999
+ avg_loss = f"{resolved_strategy.value} fallback" if fallback_configured else 999999999
else:
- avg_loss = f"{resolved_strategy.value} failsafe" if failsafe_configured else 999999999
+ avg_loss = f"{resolved_strategy.value} fallback" if fallback_configured else 999999999
del Losses
del self.H
diff --git a/gptqmodel/quantization/protocol.py b/gptqmodel/quantization/protocol.py
new file mode 100644
index 000000000..61044ce65
--- /dev/null
+++ b/gptqmodel/quantization/protocol.py
@@ -0,0 +1,526 @@
+# SPDX-FileCopyrightText: 2026 ModelCloud.ai
+# SPDX-License-Identifier: Apache-2.0
+
+from __future__ import annotations
+
+import re
+from dataclasses import dataclass, field, is_dataclass
+from pathlib import Path
+from typing import Any, Mapping, Optional
+
+from .config import FORMAT, METHOD, GGUFConfig, GGUFBits, QuantizeConfig, SmoothMAD
+
+
+@dataclass(frozen=True)
+class OperationSpec:
+ method: str
+ args: dict[str, Any] = field(default_factory=dict)
+
+
+@dataclass(frozen=True)
+class QuantizeSpec:
+ method: Optional[str] = None
+ args: dict[str, Any] = field(default_factory=dict)
+
+
+@dataclass(frozen=True)
+class ExportSpec:
+ format: Optional[str] = None
+ variant: Optional[str] = None
+ impl: Optional[str] = None
+ version: Optional[int | str] = None
+ options: dict[str, Any] = field(default_factory=dict)
+
+
+@dataclass(frozen=True)
+class TargetSpec:
+ mode: Optional[str] = None
+ prepare: tuple[OperationSpec, ...] = ()
+ quantize: Optional[QuantizeSpec] = None
+ export: Optional[ExportSpec] = None
+
+
+@dataclass(frozen=True)
+class MatchSpec:
+ pattern: str
+ include: bool = True
+
+ @property
+ def modifier(self) -> str:
+ return "+" if self.include else "-"
+
+ def matches(self, module_name: str) -> bool:
+ return _pattern_matches(self.pattern, module_name)
+
+
+@dataclass(frozen=True)
+class Rule:
+ match: tuple[MatchSpec, ...]
+ aliases: dict[str, Any] | None = None
+ actions: tuple[OperationSpec, ...] = ()
+ stop: bool = False
+ weight: Optional[TargetSpec] = None
+ input: Optional[TargetSpec] = None
+ output: Optional[TargetSpec] = None
+ kv_cache: Optional[TargetSpec] = None
+
+ def matches(self, module_name: str) -> bool:
+ includes = tuple(selector for selector in self.match if selector.include)
+ excludes = tuple(selector for selector in self.match if not selector.include)
+ if not includes:
+ return False
+ if not any(selector.matches(module_name) for selector in includes):
+ return False
+ return not any(selector.matches(module_name) for selector in excludes)
+
+
+@dataclass(frozen=True)
+class Stage:
+ name: str
+ rules: tuple[Rule, ...] = ()
+
+
+@dataclass(frozen=True)
+class ExecutionPlan:
+ version: int
+ stages: tuple[Stage, ...]
+
+
+def skip() -> dict[str, str]:
+ return {"method": "skip"}
+
+
+def compile_protocol(source: Any) -> ExecutionPlan:
+ payload = _normalize_root(source)
+ version = int(payload.get("version", 2))
+ if version != 2:
+ raise ValueError(f"Unsupported quantization protocol version: {version}.")
+
+ stages = tuple(_normalize_stage(stage) for stage in payload.get("stages", ()))
+ if not stages:
+ raise ValueError("Quantization protocol must define at least one stage.")
+
+ return ExecutionPlan(version=version, stages=stages)
+
+
+def compile_protocol_yaml_text(text: str) -> ExecutionPlan:
+ try:
+ import yaml
+ except Exception as exc: # pragma: no cover - dependency/runtime guard
+ raise ModuleNotFoundError("PyYAML is required to parse protocol YAML.") from exc
+
+ payload = yaml.safe_load(text)
+ return compile_protocol(payload)
+
+
+def compile_protocol_yaml_file(path: str | Path) -> ExecutionPlan:
+ protocol_path = Path(path)
+ return compile_protocol_yaml_text(protocol_path.read_text())
+
+
+def compile_plan_to_quantize_config(plan: ExecutionPlan):
+ if len(plan.stages) != 1:
+ raise NotImplementedError("Initial protocol implementation supports exactly one stage for config compilation.")
+
+ stage = plan.stages[0]
+ if len(stage.rules) != 1:
+ raise NotImplementedError("Initial protocol implementation supports exactly one rule for config compilation.")
+
+ rule = stage.rules[0]
+ if rule.aliases:
+ raise NotImplementedError("Initial protocol implementation does not support aliases during config compilation.")
+ if rule.actions:
+ raise NotImplementedError("Initial protocol implementation does not support actions during config compilation.")
+ if rule.stop:
+ raise NotImplementedError("Initial protocol implementation does not support stop during config compilation.")
+ if rule.input is not None or rule.output is not None or rule.kv_cache is not None:
+ raise NotImplementedError("Initial protocol implementation only supports weight-target compilation.")
+ if rule.weight is None:
+ raise ValueError("Initial protocol implementation requires a `weight` target.")
+
+ return _compile_weight_target(rule.weight, matchers=rule.match)
+
+
+def compile_protocol_to_quantize_config(source: Any):
+ return compile_plan_to_quantize_config(compile_protocol(source))
+
+
+def compile_protocol_yaml_to_quantize_config(text: str):
+ return compile_plan_to_quantize_config(compile_protocol_yaml_text(text))
+
+
+def _normalize_root(source: Any) -> dict[str, Any]:
+ if isinstance(source, ExecutionPlan):
+ return {
+ "version": source.version,
+ "stages": list(source.stages),
+ }
+ if isinstance(source, Mapping):
+ return dict(source)
+ if is_dataclass(source):
+ return {
+ "version": getattr(source, "version"),
+ "stages": getattr(source, "stages"),
+ }
+ raise TypeError(
+ "Quantization protocol root must be a mapping or dataclass-like object with `version` and `stages`."
+ )
+
+
+def _normalize_stage(source: Any) -> Stage:
+ if isinstance(source, Stage):
+ return Stage(
+ name=source.name,
+ rules=tuple(_normalize_rule(rule) for rule in source.rules),
+ )
+ data = _coerce_mapping(source, context="stage")
+ name = data.get("name")
+ if not name:
+ raise ValueError("Stage requires a non-empty `name`.")
+ rules = tuple(_normalize_rule(rule) for rule in data.get("rules", ()))
+ if not rules:
+ raise ValueError(f"Stage `{name}` must define at least one rule.")
+ return Stage(name=str(name), rules=rules)
+
+
+def _normalize_rule(source: Any) -> Rule:
+ if isinstance(source, Rule):
+ return Rule(
+ match=_normalize_match(source.match),
+ aliases=_copy_optional_mapping(source.aliases),
+ actions=tuple(_normalize_operation(action) for action in source.actions),
+ stop=bool(source.stop),
+ weight=_normalize_target(source.weight),
+ input=_normalize_target(source.input),
+ output=_normalize_target(source.output),
+ kv_cache=_normalize_target(source.kv_cache),
+ )
+
+ data = _coerce_mapping(source, context="rule")
+ match = data.get("match")
+ if not match:
+ raise ValueError("Rule requires a non-empty `match`.")
+
+ return Rule(
+ match=_normalize_match(match),
+ aliases=_copy_optional_mapping(data.get("aliases")),
+ actions=tuple(_normalize_operation(action) for action in data.get("actions", ())),
+ stop=bool(data.get("stop", False)),
+ weight=_normalize_target(data.get("weight")),
+ input=_normalize_target(data.get("input")),
+ output=_normalize_target(data.get("output")),
+ kv_cache=_normalize_target(data.get("kv_cache")),
+ )
+
+
+def _normalize_target(source: Any) -> Optional[TargetSpec]:
+ if source is None:
+ return None
+ if isinstance(source, TargetSpec):
+ return TargetSpec(
+ mode=source.mode,
+ prepare=tuple(_normalize_operation(op) for op in source.prepare),
+ quantize=_normalize_quantize(source.quantize),
+ export=_normalize_export(source.export),
+ )
+
+ data = _coerce_mapping(source, context="target")
+ return TargetSpec(
+ mode=data.get("mode"),
+ prepare=tuple(_normalize_operation(op) for op in data.get("prepare", ()) or ()),
+ quantize=_normalize_quantize(data.get("quantize")),
+ export=_normalize_export(data.get("export")),
+ )
+
+
+def _normalize_match(source: Any) -> tuple[MatchSpec, ...]:
+ if isinstance(source, MatchSpec):
+ return (MatchSpec(pattern=source.pattern, include=bool(source.include)),)
+ if isinstance(source, str):
+ return (_normalize_match_selector(source),)
+ if isinstance(source, (tuple, list)):
+ selectors = tuple(_normalize_match_selector(item) for item in source)
+ if not selectors:
+ raise ValueError("Rule `match` list must not be empty.")
+ return selectors
+ raise TypeError("Rule `match` must be a string, MatchSpec, or a list/tuple of selector strings.")
+
+
+def _normalize_match_selector(source: Any) -> MatchSpec:
+ if isinstance(source, MatchSpec):
+ return MatchSpec(pattern=source.pattern, include=bool(source.include))
+ if not isinstance(source, str):
+ raise TypeError("Match selector must be a string or MatchSpec.")
+
+ selector = source.strip()
+ if not selector:
+ raise ValueError("Match selector must not be empty.")
+
+ include = True
+ if selector.startswith("+:"):
+ selector = selector[2:].strip()
+ elif selector.startswith("-:"):
+ include = False
+ selector = selector[2:].strip()
+
+ if not selector:
+ raise ValueError("Match selector pattern must not be empty.")
+
+ return MatchSpec(pattern=selector, include=include)
+
+
+def _normalize_operation(source: Any) -> OperationSpec:
+ if isinstance(source, OperationSpec):
+ return OperationSpec(method=source.method, args=dict(source.args))
+ if isinstance(source, str):
+ return OperationSpec(method=source)
+ data = _coerce_mapping(source, context="operation")
+ method = data.get("method")
+ if not method:
+ raise ValueError("Operation requires a non-empty `method`.")
+ args = {key: value for key, value in data.items() if key != "method"}
+ return OperationSpec(method=str(method), args=args)
+
+
+def _normalize_quantize(source: Any) -> Optional[QuantizeSpec]:
+ if source is None:
+ return None
+ if isinstance(source, QuantizeSpec):
+ return QuantizeSpec(method=source.method, args=dict(source.args))
+ if isinstance(source, str):
+ return QuantizeSpec(method=source)
+ data = _coerce_mapping(source, context="quantize")
+ method = data.get("method")
+ args = {key: value for key, value in data.items() if key != "method"}
+ return QuantizeSpec(method=str(method) if method is not None else None, args=args)
+
+
+def _normalize_export(source: Any) -> Optional[ExportSpec]:
+ if source is None:
+ return None
+ if isinstance(source, ExportSpec):
+ return ExportSpec(
+ format=source.format,
+ variant=source.variant,
+ impl=source.impl,
+ version=source.version,
+ options=dict(source.options),
+ )
+ if isinstance(source, str):
+ return ExportSpec(format=source)
+ data = _coerce_mapping(source, context="export")
+ options = dict(data.get("options", {}) or {})
+ return ExportSpec(
+ format=data.get("format"),
+ variant=data.get("variant"),
+ impl=data.get("impl"),
+ version=data.get("version"),
+ options=options,
+ )
+
+
+def _coerce_mapping(source: Any, *, context: str) -> dict[str, Any]:
+ if isinstance(source, Mapping):
+ return dict(source)
+ if is_dataclass(source):
+ return {field_name: getattr(source, field_name) for field_name in source.__dataclass_fields__}
+ raise TypeError(f"Quantization protocol {context} must be provided as a mapping or dataclass.")
+
+
+def _copy_optional_mapping(source: Any) -> dict[str, Any] | None:
+ if source is None:
+ return None
+ if not isinstance(source, Mapping):
+ raise TypeError("Rule `aliases` must be a mapping when provided.")
+ return dict(source)
+
+
+def _compile_weight_target(weight: TargetSpec, *, matchers: tuple[MatchSpec, ...]):
+ if weight.mode not in {None, "merge"}:
+ raise NotImplementedError("Initial protocol compiler supports only the default target merge mode.")
+
+ quantize = weight.quantize
+ if quantize is None or not quantize.method:
+ raise ValueError("Weight target requires `weight.quantize.method`.")
+
+ method = str(quantize.method).strip().lower()
+ if method == METHOD.GGUF.value:
+ return _compile_gguf_weight_target(weight, matchers=matchers)
+ if method in {METHOD.GPTQ.value, METHOD.AWQ.value}:
+ return _compile_quantize_config_weight_target(weight, matchers=matchers, method=METHOD(method))
+ raise NotImplementedError(
+ "Initial protocol compiler supports only `weight.quantize.method` in {\"gguf\", \"gptq\", \"awq\"}."
+ )
+
+
+def _compile_gguf_weight_target(weight: TargetSpec, *, matchers: tuple[MatchSpec, ...]) -> GGUFConfig:
+ if not _supports_initial_weight_match_compilation(matchers):
+ raise NotImplementedError(
+ "Initial GGUF protocol compiler supports only `match=\"*\"` or `match=[\"*\", \"-:...\"]`."
+ )
+
+ quantize = weight.quantize
+ if quantize is None:
+ raise ValueError("GGUF weight target requires `weight.quantize`.")
+ if quantize.method != "gguf":
+ raise NotImplementedError(
+ "Initial GGUF compiler supports only `weight.quantize.method = \"gguf\"`."
+ )
+
+ bits = quantize.args.get("bits")
+ if bits is None:
+ raise ValueError("GGUF weight target requires `weight.quantize.bits`.")
+
+ export = weight.export
+ if export is not None and export.format not in {None, "gguf"}:
+ raise NotImplementedError("Initial GGUF compiler supports only `weight.export.format = \"gguf\"`.")
+
+ smoother = _compile_supported_smoother(weight.prepare)
+ gguf_format = _resolve_gguf_public_format(bits=bits, export=export)
+ dynamic = _compile_negative_match_dynamic(matchers)
+ return GGUFConfig(bits=bits, format=gguf_format, smoother=smoother, dynamic=dynamic)
+
+
+def _compile_quantize_config_weight_target(weight: TargetSpec, *, matchers: tuple[MatchSpec, ...], method: METHOD):
+ if not _supports_initial_weight_match_compilation(matchers):
+ raise NotImplementedError(
+ f"Initial {method.value.upper()} protocol compiler supports only `match=\"*\"` or `match=[\"*\", \"-:...\"]`."
+ )
+ if weight.prepare:
+ raise NotImplementedError(
+ f"Initial {method.value.upper()} protocol compiler does not yet support `weight.prepare`."
+ )
+
+ quantize = weight.quantize
+ if quantize is None:
+ raise ValueError(f"{method.value.upper()} weight target requires `weight.quantize`.")
+ if quantize.method != method.value:
+ raise NotImplementedError(
+ f"Initial {method.value.upper()} compiler supports only `weight.quantize.method = \"{method.value}\"`."
+ )
+
+ bits = quantize.args.get("bits")
+ if bits is None:
+ raise ValueError(f"{method.value.upper()} weight target requires `weight.quantize.bits`.")
+
+ export_format = _resolve_export_format(method=method, export=weight.export)
+ dynamic = _compile_negative_match_dynamic(matchers)
+ group_size = quantize.args.get("group_size", 128)
+ sym = bool(quantize.args.get("sym", True))
+
+ kwargs = {
+ "method": method,
+ "format": export_format,
+ "bits": bits,
+ "group_size": group_size,
+ "sym": sym,
+ "dynamic": dynamic,
+ }
+
+ if "desc_act" in quantize.args or method == METHOD.GPTQ:
+ kwargs["desc_act"] = bool(quantize.args.get("desc_act", False))
+
+ if method == METHOD.GPTQ:
+ if "act_group_aware" in quantize.args:
+ kwargs["act_group_aware"] = bool(quantize.args["act_group_aware"])
+
+ return QuantizeConfig(**kwargs)
+
+
+def _compile_supported_smoother(prepare: tuple[OperationSpec, ...]) -> Optional[SmoothMAD]:
+ if not prepare:
+ return None
+ if len(prepare) != 1:
+ raise NotImplementedError("Initial GGUF compiler supports at most one weight.prepare operation.")
+
+ op = prepare[0]
+ if op.method not in {"smooth.mad", "smoother"}:
+ raise NotImplementedError(
+ "Initial GGUF compiler supports only `smooth.mad` in `weight.prepare`."
+ )
+ k = op.args.get("k")
+ if k is None:
+ smooth_payload = op.args.get("smooth")
+ if isinstance(smooth_payload, Mapping):
+ if smooth_payload.get("type") not in {None, "mad"}:
+ raise NotImplementedError("Initial GGUF compiler supports only MAD smoothers.")
+ k = smooth_payload.get("k")
+ return SmoothMAD(k=2.75 if k is None else float(k))
+
+
+def _resolve_gguf_public_format(bits: Any, export: Optional[ExportSpec]) -> Optional[str]:
+ variant = export.variant if export is not None else None
+
+ if isinstance(bits, str):
+ normalized = bits.strip().lower().replace("-", "_")
+ if normalized and not normalized.isdigit():
+ bits_spec = GGUFBits.from_string(normalized)
+ public_format = bits_spec.to_public_format()
+ if variant is not None and variant != public_format:
+ raise ValueError(
+ f"GGUF protocol uses incompatible bits/export variant combination: bits={bits}, export.variant={variant}."
+ )
+ return variant or public_format
+
+ return variant
+
+
+def _is_global_match(matchers: tuple[MatchSpec, ...]) -> bool:
+ return len(matchers) == 1 and matchers[0].include and matchers[0].pattern == "*"
+
+
+def _supports_initial_weight_match_compilation(matchers: tuple[MatchSpec, ...]) -> bool:
+ includes = tuple(selector for selector in matchers if selector.include)
+ return bool(includes) and all(selector.pattern == "*" for selector in includes)
+
+
+def _compile_negative_match_dynamic(matchers: tuple[MatchSpec, ...]) -> Optional[dict[str, dict[str, Any]]]:
+ excludes = tuple(selector for selector in matchers if not selector.include)
+ if not excludes:
+ return None
+ return {f"-:{selector.pattern}": {} for selector in excludes}
+
+
+def _resolve_export_format(method: METHOD, export: Optional[ExportSpec]) -> FORMAT:
+ if method == METHOD.GPTQ:
+ if export is None:
+ return FORMAT.GPTQ
+ if export.format not in {None, METHOD.GPTQ.value}:
+ raise NotImplementedError("Initial GPTQ compiler supports only `weight.export.format = \"gptq\"`.")
+ variant = str(export.variant or FORMAT.GPTQ.value).strip().lower().replace("-", "_")
+ mapping = {
+ FORMAT.GPTQ.value: FORMAT.GPTQ,
+ FORMAT.GPTQ_V2.value: FORMAT.GPTQ_V2,
+ FORMAT.MARLIN.value: FORMAT.MARLIN,
+ FORMAT.BITBLAS.value: FORMAT.BITBLAS,
+ }
+ if variant not in mapping:
+ raise NotImplementedError(f"Unsupported GPTQ export variant: `{variant}`.")
+ return mapping[variant]
+
+ if method == METHOD.AWQ:
+ if export is None:
+ return FORMAT.GEMM
+ if export.format not in {None, METHOD.AWQ.value}:
+ raise NotImplementedError("Initial AWQ compiler supports only `weight.export.format = \"awq\"`.")
+ variant = str(export.variant or FORMAT.GEMM.value).strip().lower().replace("-", "_")
+ mapping = {
+ FORMAT.GEMM.value: FORMAT.GEMM,
+ FORMAT.GEMV.value: FORMAT.GEMV,
+ FORMAT.GEMV_FAST.value: FORMAT.GEMV_FAST,
+ "gemvfast": FORMAT.GEMV_FAST,
+ FORMAT.LLM_AWQ.value.replace("-", "_"): FORMAT.LLM_AWQ,
+ FORMAT.LLM_AWQ.value: FORMAT.LLM_AWQ,
+ FORMAT.MARLIN.value: FORMAT.MARLIN,
+ }
+ if variant not in mapping:
+ raise NotImplementedError(f"Unsupported AWQ export variant: `{variant}`.")
+ return mapping[variant]
+
+ raise NotImplementedError(f"Unsupported export method resolution for `{method}`.")
+
+
+def _pattern_matches(pattern: str, module_name: str) -> bool:
+ if pattern == "*":
+ return True
+ return re.search(pattern, module_name) is not None
diff --git a/gptqmodel/quantization/qqq.py b/gptqmodel/quantization/qqq.py
index c7bb32d5b..7952c27eb 100644
--- a/gptqmodel/quantization/qqq.py
+++ b/gptqmodel/quantization/qqq.py
@@ -13,10 +13,10 @@
from .. import QuantizeConfig
from ..looper.named_module import NamedModule
-from ..quantization.config import FailSafeStrategy, SmoothMSE
+from ..quantization.config import FallbackStrategy, SmoothMSE
from ..quantization.quantizer import HF_OPTIMUM
from ..utils import setup_logger
-from .failsafe_smooth import mse_optimal_quant, smooth_block
+from .fallback_smooth import mse_optimal_quant, smooth_block
from .gptq import get_number_of_rows_and_cols
@@ -238,7 +238,7 @@ def __init__(self, module: nn.Module, qcfg: Optional[QuantizeConfig] = None):
# fwd counter
self.fwd_counter = 0
- self.failsafe = self.qcfg.failsafe
+ self.fallback = self.qcfg.fallback
self.expected_nsamples: Optional[float] = None
self.H = torch.zeros((self.columns, self.columns),
@@ -261,12 +261,12 @@ def _truncate_last_dim(tensor: torch.Tensor, length: int) -> torch.Tensor:
return tensor.narrow(tensor.dim() - 1, 0, trim).contiguous()
- def _failsafe_quantize(self, strategy: FailSafeStrategy):
+ def _fallback_quantize(self, strategy: FallbackStrategy):
maxq = 2 ** self.qcfg.bits - 1
sigma = 3.0
group_size = self.qcfg.group_size if self.qcfg.group_size != -1 else self.columns
start_time = time.time()
- smooth_method = getattr(self.failsafe, "smooth", None)
+ smooth_method = getattr(self.fallback, "smooth", None)
mse_steps = 32
mse_maxshrink = 0.8
if isinstance(smooth_method, SmoothMSE):
@@ -306,10 +306,10 @@ def _failsafe_quantize(self, strategy: FailSafeStrategy):
else:
block_mod, scale_factor = smooth_block(
block,
- self.failsafe,
+ self.fallback,
group_size=self.qcfg.group_size if self.qcfg.group_size != -1 else self.columns,
)
- if strategy == FailSafeStrategy.MIDPOINT:
+ if strategy == FallbackStrategy.MIDPOINT:
w_min = block_mod.min(dim=1, keepdim=True).values
w_max = block_mod.max(dim=1, keepdim=True).values
mid = (w_max + w_min) / 2.0
@@ -320,7 +320,7 @@ def _failsafe_quantize(self, strategy: FailSafeStrategy):
zero = torch.round(zero_mid - (mid / scale))
zero = torch.clamp(zero, 0, maxq)
dequant = (q - zero) * scale
- elif strategy == FailSafeStrategy.MEAN:
+ elif strategy == FallbackStrategy.MEAN:
mean = block_mod.mean(dim=1, keepdim=True)
max_dev = torch.max((block_mod - mean).abs(), dim=1, keepdim=True).values
max_dev = torch.clamp(max_dev, min=1e-8)
@@ -331,7 +331,7 @@ def _failsafe_quantize(self, strategy: FailSafeStrategy):
zero = torch.round(zero_mid - (mean / scale))
zero = torch.clamp(zero, 0, maxq)
dequant = (q - zero) * scale
- elif strategy == FailSafeStrategy.MEDIAN:
+ elif strategy == FallbackStrategy.MEDIAN:
median = block_mod.median(dim=1, keepdim=True).values
max_dev = torch.max((block_mod - median).abs(), dim=1, keepdim=True).values
max_dev = torch.clamp(max_dev, min=1e-8)
@@ -342,7 +342,7 @@ def _failsafe_quantize(self, strategy: FailSafeStrategy):
zero = torch.round(zero_mid - (median / scale))
zero = torch.clamp(zero, 0, maxq)
dequant = (q - zero) * scale
- elif strategy == FailSafeStrategy.STDCLIP:
+ elif strategy == FallbackStrategy.STDCLIP:
mean = block_mod.mean(dim=1, keepdim=True)
std = block_mod.std(dim=1, keepdim=True, unbiased=False)
std = torch.clamp(std, min=1e-8)
@@ -354,13 +354,13 @@ def _failsafe_quantize(self, strategy: FailSafeStrategy):
q = torch.round(block_mod / scale + zero)
q = torch.clamp(q, 0, maxq)
dequant = (q - zero) * scale
- elif strategy == FailSafeStrategy.RTN:
+ elif strategy == FallbackStrategy.RTN:
self.quantizer.find_params(block_mod, weight=True)
dequant = self.quantizer.quantize(block_mod)
scale = self.quantizer.scale
zero = self.quantizer.zero
else:
- raise ValueError(f"Unsupported failsafe strategy: {strategy}")
+ raise ValueError(f"Unsupported fallback strategy: {strategy}")
if scale_factor is not None:
scale = scale * scale_factor
@@ -412,7 +412,7 @@ def _failsafe_quantize(self, strategy: FailSafeStrategy):
duration = time.time() - start_time
mean_abs_err = (Q - self.layer.weight.data).abs().mean().item()
- avg_loss = f"failsafe({strategy.value}): {mean_abs_err:.7f}"
+ avg_loss = f"fallback({strategy.value}): {mean_abs_err:.7f}"
damp_percent = 0.0
self.H = None
return Q, scale, zero, g_idx, duration, avg_loss, damp_percent, scale_extra, self.nsamples
@@ -454,9 +454,9 @@ def quantize(
blocksize=128,
):
start = time.time()
- from ..utils.failsafe import resolve_failsafe_strategy, resolve_threshold, should_use_failsafe
+ from ..utils.fallback import resolve_fallback_strategy, resolve_threshold, should_use_fallback
- resolved_strategy = resolve_failsafe_strategy(self.failsafe)
+ resolved_strategy = resolve_fallback_strategy(self.fallback)
percdamp = self.qcfg.damp_percent
groupsize = self.qcfg.group_size
@@ -516,8 +516,8 @@ def quantize(
damp = percdamp * torch.mean(torch.diag(H))
diag = torch.arange(self.columns, device=self.dev)
H[diag, diag] += damp
- threshold_raw, is_percent = resolve_threshold(self.failsafe, self.expected_nsamples)
- failsafe_configured = threshold_raw is not None
+ threshold_raw, is_percent = resolve_threshold(self.fallback, self.expected_nsamples)
+ fallback_configured = threshold_raw is not None
try:
H = torch.linalg.cholesky(H)
@@ -525,24 +525,24 @@ def quantize(
H = torch.linalg.cholesky(H, upper=True)
Hinv = H
except Exception:
- fallback_requested = should_use_failsafe(
- self.failsafe,
+ fallback_requested = should_use_fallback(
+ self.fallback,
float(self.nsamples),
self.expected_nsamples,
)
if fallback_requested:
extra = f", threshold_raw={threshold_raw}" if threshold_raw is not None and is_percent else ""
log.warn(
- "Quantization: Module `%s` -> Using `%s` failsafe quantization (observed %s samples, threshold=%s%s, max_total=%s).",
+ "Quantization: Module `%s` -> Using `%s` fallback quantization (observed %s samples, threshold=%s%s, max_total=%s).",
self.name,
resolved_strategy.value,
self.nsamples,
- self.failsafe,
+ self.fallback,
extra,
self.expected_nsamples,
)
- if resolved_strategy != FailSafeStrategy.RTN:
- return self._failsafe_quantize(resolved_strategy)
+ if resolved_strategy != FallbackStrategy.RTN:
+ return self._fallback_quantize(resolved_strategy)
Hinv = None
else:
raise
@@ -615,20 +615,20 @@ def quantize(
if math.isnan(avg_loss):
print("Losses sum item:", torch.sum(Losses).item())
- if failsafe_configured:
+ if fallback_configured:
log.info(f"Quantization: Failed due to `NaN` loss for `{self.name}`, use mock quantization retry for `{self.name}`")
self.qcfg.mock_quantization = True
return self.quantize(blocksize=blocksize)
else:
- raise ValueError(f"Quantization: Failed due to `NaN` loss for `{self.name}`, please try increasing calibration data samples or enable failsafe=True")
+ raise ValueError(f"Quantization: Failed due to `NaN` loss for `{self.name}`, please try increasing calibration data samples or enable fallback=True")
else:
- if failsafe_configured:
+ if fallback_configured:
log.warn(f"Quantization: Module `{self.name}` -> using fail safe mode. Please check if calibration data is sufficient.")
else:
log.warn(f"Quantization: `{self.name}` is not activated due to model inference logic (MoE)")
- avg_loss = f"{resolved_strategy.value} failsafe" if failsafe_configured else 999999999
+ avg_loss = f"{resolved_strategy.value} fallback" if fallback_configured else 999999999
else:
- avg_loss = f"{resolved_strategy.value} failsafe" if failsafe_configured else 999999999
+ avg_loss = f"{resolved_strategy.value} fallback" if fallback_configured else 999999999
del Losses
diff --git a/gptqmodel/quantization/quantizer.py b/gptqmodel/quantization/quantizer.py
index 7614fcc6d..2e42f4882 100644
--- a/gptqmodel/quantization/quantizer.py
+++ b/gptqmodel/quantization/quantizer.py
@@ -8,7 +8,7 @@
import torch
import torch.nn as nn
-from ..quantization import QuantizeConfig
+from .config import BaseQuantizeConfig, _normalize_quant_bits, resolve_quant_format
from ..utils.logger import setup_logger
@@ -28,7 +28,7 @@ def quantize(x, scale, zero, maxq, requires_groupwise_processing: bool):
class Quantizer(nn.Module):
- def __init__(self, qcfg: QuantizeConfig, shape=1, name: str=None):
+ def __init__(self, qcfg: BaseQuantizeConfig, shape=1, name: str=None):
super(Quantizer, self).__init__()
self.qcfg = qcfg
@@ -48,12 +48,14 @@ def configure(
grid=100,
maxshrink=0.8,
trits=False,
- bits:int=4, # for hf compat
- sym:bool=False, # for hf compat
+ bits: int | str | None = None, # for hf compat
+ sym: bool | None = None, # for hf compat
):
if self.name == HF_OPTIMUM:
- self.qcfg.bits = bits
- self.qcfg.sym = sym
+ if bits is not None:
+ self.qcfg.bits = _normalize_quant_bits(bits, format_value=resolve_quant_format(self.qcfg.format, self.qcfg.method))
+ if sym is not None:
+ self.qcfg.sym = sym
if self.requires_groupwise_processing():
self.maxq = torch.tensor(2 ** (self.qcfg.bits - 1) - 1)
@@ -112,7 +114,8 @@ def find_params(self, x, weight=False):
else:
self.zero = torch.round(-xmin / self.scale)
- if self.qcfg.mse > 0.0:
+ mse = float(getattr(self.qcfg, "mse", 0.0) or 0.0)
+ if mse > 0.0:
best = torch.full([x.shape[0]], float("inf"), device=dev)
for i in range(int(self.maxshrink * self.grid)):
p = 1 - i / self.grid
@@ -127,7 +130,7 @@ def find_params(self, x, weight=False):
q = quantize(x, scale1.unsqueeze(1), zero1.unsqueeze(1), self.maxq, self.requires_groupwise_processing())
q -= x
q.abs_()
- q.pow_(self.qcfg.mse)
+ q.pow_(mse)
err = torch.sum(q, 1)
tmp = err < best
if torch.any(tmp):
diff --git a/gptqmodel/quantization/rtn.py b/gptqmodel/quantization/rtn.py
new file mode 100644
index 000000000..26bfc47d1
--- /dev/null
+++ b/gptqmodel/quantization/rtn.py
@@ -0,0 +1,199 @@
+# SPDX-FileCopyrightText: 2026 ModelCloud.ai
+# SPDX-License-Identifier: Apache-2.0
+
+from __future__ import annotations
+
+import math
+import time
+from typing import Optional, Tuple
+
+import torch
+import torch.nn as nn
+import transformers
+from torch.nn.modules.conv import _ConvNd
+
+from ..looper.named_module import NamedModule
+from .config import Fallback, FallbackStrategy, RTNQuantizeConfig, SmoothMSE
+from .fallback_smooth import mse_optimal_quant, smooth_block
+from .quantizer import HF_OPTIMUM, Quantizer
+
+
+def get_number_of_rows_and_cols(layer: nn.Module) -> Tuple[int, int]:
+ if isinstance(layer, NamedModule):
+ layer = layer.module
+
+ if isinstance(layer, transformers.Conv1D):
+ return layer.weight.shape[1], layer.weight.shape[0]
+
+ return layer.weight.shape[0], math.prod(layer.weight.shape[1:])
+
+
+class RTN:
+ """Native weight-only RTN quantizer with optional smoothing.
+
+ This path never enters GPTQ's activation/Hessian lifecycle. It quantizes the
+ module weights directly, then returns tensors that can be packed into GPTQ,
+ AWQ, or future export layouts by the existing packing stage.
+ """
+
+ def __init__(self, module: nn.Module, qcfg: RTNQuantizeConfig):
+ self.rows, self.columns = get_number_of_rows_and_cols(module)
+ if isinstance(module, NamedModule):
+ self.module = module.module
+ self.name = module.name
+ self._named_module = module
+ else:
+ self.module = module
+ self.name = HF_OPTIMUM
+ self._named_module = None
+
+ self.validate_module(self.module)
+ self.qcfg = qcfg
+ self.quantizer = Quantizer(qcfg=qcfg, name=self.name)
+ self.quantizer.configure(perchannel=True)
+ self.nsamples = 0
+ self._primary = Fallback(
+ strategy=FallbackStrategy.RTN,
+ threshold=True,
+ smooth=qcfg.smooth,
+ )
+
+ self._original_columns = self.columns
+ if self._named_module is not None:
+ pad_info = self._named_module.state.get("tp_pad_info")
+ else:
+ pad_info = getattr(self.module, "_tp_pad_info", None)
+
+ if isinstance(pad_info, dict):
+ pad_cols = int(pad_info.get("pad_cols", 0) or 0)
+ pad_cols = max(pad_cols, 0)
+ else:
+ pad_cols = 0
+
+ self._tp_pad_cols = pad_cols
+ if self._tp_pad_cols:
+ self.columns += self._tp_pad_cols
+
+ @staticmethod
+ def validate_module(module: nn.Module) -> None:
+ assert isinstance(
+ module,
+ (nn.Linear, nn.Conv1d, nn.Conv2d, transformers.Conv1D),
+ ), f"We supports only linear and convolutional layers. actual = `{module}`"
+
+ def clone_module(self, device: Optional[torch.device] = None) -> torch.Tensor:
+ if device is None:
+ device = self.module.weight.data.device
+
+ clone = self.module.weight.data.to(copy=True, device=device)
+ if isinstance(self.module, _ConvNd):
+ clone = clone.flatten(1)
+ if isinstance(self.module, transformers.pytorch_utils.Conv1D):
+ clone = clone.t()
+ if self._tp_pad_cols:
+ pad = torch.zeros(
+ (clone.shape[0], self._tp_pad_cols),
+ dtype=clone.dtype,
+ device=clone.device,
+ )
+ clone = torch.cat((clone, pad), dim=1)
+ return clone.float()
+
+ @staticmethod
+ def truncate_last_dim(tensor: torch.Tensor, length: int) -> torch.Tensor:
+ if tensor.dim() == 0:
+ return tensor
+
+ trim = min(length, tensor.shape[-1])
+ if trim == tensor.shape[-1]:
+ return tensor
+
+ return tensor.narrow(tensor.dim() - 1, 0, trim).contiguous()
+
+ @staticmethod
+ def _collapse_group_param(param: torch.Tensor) -> torch.Tensor:
+ collapsed = param if param.dim() > 1 else param.unsqueeze(1)
+ if collapsed.shape[1] > 1:
+ collapsed = collapsed.mean(dim=1, keepdim=True)
+ return collapsed
+
+ @torch.inference_mode()
+ def quantize(self):
+ maxq = 2 ** self.qcfg.bits - 1
+ effective_group_size = self.qcfg.group_size if self.qcfg.group_size != -1 else self.columns
+ smooth_method = self.qcfg.smooth
+ mse_steps = 32
+ mse_maxshrink = 0.8
+ if isinstance(smooth_method, SmoothMSE):
+ mse_steps = smooth_method.steps
+ mse_maxshrink = smooth_method.maxshrink
+
+ start_time = time.time()
+ target_device = self.module.weight.device
+ weights = self.clone_module(device=target_device)
+ quantized = torch.empty_like(weights)
+ scale_chunks = []
+ zero_chunks = []
+
+ for start in range(0, self.columns, effective_group_size):
+ end = min(start + effective_group_size, self.columns)
+ block = weights[:, start:end]
+
+ if isinstance(smooth_method, SmoothMSE):
+ dequant, scale, zero = mse_optimal_quant(
+ block,
+ self.qcfg,
+ maxq,
+ steps=mse_steps,
+ maxshrink=mse_maxshrink,
+ )
+ else:
+ block_mod, scale_factor = smooth_block(
+ block,
+ self._primary,
+ group_size=effective_group_size,
+ )
+ self.quantizer.find_params(block_mod, weight=True)
+ dequant = self.quantizer.quantize(block_mod)
+ scale = self.quantizer.scale
+ zero = self.quantizer.zero
+
+ if scale_factor is not None:
+ scale = scale * scale_factor
+ dequant = dequant * scale_factor
+
+ quantized[:, start:end] = dequant
+ scale_chunks.append(self._collapse_group_param(scale))
+ zero_chunks.append(self._collapse_group_param(zero))
+
+ scale = torch.cat(scale_chunks, dim=1)
+ zero = torch.cat(zero_chunks, dim=1)
+
+ if self._tp_pad_cols:
+ valid_cols = self._original_columns
+ quantized = quantized[:, :valid_cols]
+ scale = self.truncate_last_dim(scale, valid_cols)
+ zero = self.truncate_last_dim(zero, valid_cols)
+ else:
+ valid_cols = self.columns
+
+ g_idx = torch.arange(valid_cols, device=quantized.device, dtype=torch.int32) // effective_group_size
+
+ if isinstance(self.module, transformers.Conv1D):
+ quantized = quantized.t()
+
+ if quantized.shape != self.module.weight.shape:
+ quantized = quantized.reshape(self.module.weight.shape).to(self.module.weight.dtype)
+ else:
+ quantized = quantized.to(self.module.weight.dtype)
+
+ quantized = quantized.to(device=self.module.weight.data.device, non_blocking=False)
+ mean_abs_err = (quantized - self.module.weight.data).abs().mean().item()
+ duration = time.time() - start_time
+ avg_loss = f"rtn: {mean_abs_err:.7f}"
+ damp = 0.0
+
+ return quantized, scale, zero, g_idx, duration, avg_loss, damp, self.nsamples
+
+
+__all__ = ["RTN", "get_number_of_rows_and_cols"]
diff --git a/gptqmodel/utils/backend.py b/gptqmodel/utils/backend.py
index b715e035e..95ef5eb40 100644
--- a/gptqmodel/utils/backend.py
+++ b/gptqmodel/utils/backend.py
@@ -17,6 +17,7 @@ class BACKEND(str, Enum):
TRITON = "triton" # VERY GOOD: all-around kernel
EXLLAMA_V1 = "exllama_v1" # FAST: optimized for batching == 1
EXLLAMA_V2 = "exllama_v2" # FASTER: optimized for batching > 1
+ EXLLAMA_V3 = "exllama_v3"
EXLLAMA_EORA = "exllama_eora"
MACHETE = "machete" # CUTLASS-based kernel optimized for Hopper (SM90+)
MARLIN = "marlin" # FASTEST: marlin reduce ops in fp32 (higher precision -> more accurate, slightly slower)
@@ -38,6 +39,10 @@ class BACKEND(str, Enum):
TORCH_AWQ = "torch_awq"
# external
+ GGUF_TORCH = "gguf_torch" # GGUF module-level inference via native torch kernel
+ GGUF_TRITON = "gguf_triton" # GGUF module-level inference via Triton fused CUDA kernel
+ GGUF_CPP_CPU = "gguf_cpp_cpu" # GGUF module-level inference via llama.cpp / ggml CPU backend
+ GGUF_CPP_CUDA = "gguf_cpp_cuda" # GGUF module-level inference via llama.cpp / ggml CUDA backend
VLLM = "vllm" # External inference engine: CUDA + ROCm + IPEX
SGLANG = "sglang" # External inference engine: CUDA + ROCm
MLX = "mlx" # External inference engine: Apple MLX on M1+ (Apple Silicon)
diff --git a/gptqmodel/utils/bitblas.py b/gptqmodel/utils/bitblas.py
index 71523dd1c..d361292f2 100644
--- a/gptqmodel/utils/bitblas.py
+++ b/gptqmodel/utils/bitblas.py
@@ -10,6 +10,7 @@
from ..nn_modules.qlinear.bitblas import BitBLASQuantLinear
from ..quantization import FORMAT, QuantizeConfig
+from ..quantization.config import resolve_quant_format
from ..utils.logger import setup_logger
from .model import load_checkpoint_in_model_then_tie_weights
from .safe import THREADPOOLCTL
@@ -30,7 +31,7 @@ def prepare_model_for_bitblas_load(
load_checkpoint_in_model: bool,
):
# The model (e.g. model.safetensors) is already serialized in the BitBLAS format, load it directly.
- if qcfg.format == FORMAT.BITBLAS:
+ if resolve_quant_format(qcfg.format, qcfg.method) == FORMAT.BITBLAS:
# if the checkpoint is already in bitblas format, we can load it directly.
log.info(f"Loading a GPTQ model, detected BitBLAS serialized format at {model_save_name}.")
model = convert_to_bitblas(model, quant_linear_class, qcfg, sym, desc_act, repack=False)
@@ -99,7 +100,7 @@ def convert_to_bitblas(model, model_quantlinear, qcfg: QuantizeConfig, sym: bool
# from checkpoints holding zero bias.
with torch.device("meta"):
bitblas_module = BitBLASQuantLinear(
- bits=qcfg.bits,
+ bits=qcfg.runtime_bits,
group_size=qcfg.group_size,
sym=sym,
desc_act=desc_act,
diff --git a/gptqmodel/utils/calibration.py b/gptqmodel/utils/calibration.py
index de7fcb4c7..e00b92beb 100644
--- a/gptqmodel/utils/calibration.py
+++ b/gptqmodel/utils/calibration.py
@@ -106,6 +106,13 @@ def _to_2d_long_tensor(value: Any, name: str, idx: int) -> torch.Tensor:
raise ValueError(f"Quantize: `{name}` for calibration item {idx} must be 1D or 2D, got scalar.")
if tensor.ndim == 1:
tensor = tensor.unsqueeze(0)
+ elif tensor.ndim > 2 and name == "attention_mask":
+ # Some tokenizers emit causal masks shaped like [B, 1, T, T] or [B, T, T].
+ # Collapse those higher-rank masks back to the token presence mask expected here.
+ tensor = tensor.ne(0)
+ for dim in range(tensor.ndim - 2, 0, -1):
+ tensor = tensor.any(dim=dim)
+ tensor = tensor.to(torch.long)
elif tensor.ndim != 2:
raise ValueError(
f"Quantize: `{name}` for calibration item {idx} must be rank 1 or 2, got rank {tensor.ndim}."
diff --git a/gptqmodel/utils/exllamav3.py b/gptqmodel/utils/exllamav3.py
new file mode 100644
index 000000000..775e1e376
--- /dev/null
+++ b/gptqmodel/utils/exllamav3.py
@@ -0,0 +1,100 @@
+# SPDX-FileCopyrightText: 2024-2025 ModelCloud.ai
+# SPDX-FileCopyrightText: 2024-2025 qubitium@modelcloud.ai
+# SPDX-License-Identifier: Apache-2.0
+# Contact: qubitium@modelcloud.ai, x.com/qubitium
+#
+# Portions of this file are adapted from turboderp-org/exllamav3.
+# Credits: TurboDerp / ExLlamaV3 contributors.
+
+from __future__ import annotations
+
+from typing import Any, Dict, Iterable, Optional, Type
+
+import torch
+import torch.nn as nn
+import transformers
+from torch.nn.modules.conv import _ConvNd
+
+from ..looper.named_module import NamedModule
+from ..nn_modules.exllamav3 import ExllamaV3Linear
+from .model import recurse_setattr
+
+
+def _resolve_linear_shape(submodule: nn.Module) -> tuple[int, int]:
+ named = submodule if isinstance(submodule, NamedModule) else None
+ target = named.module if named is not None else submodule
+
+ if named is not None:
+ in_features = named.state.get("in_features")
+ out_features = named.state.get("out_features")
+ if in_features is not None and out_features is not None:
+ return int(in_features), int(out_features)
+
+ if isinstance(target, nn.Linear):
+ return target.in_features, target.out_features
+ if isinstance(target, _ConvNd):
+ return target.in_channels, target.out_channels
+ if isinstance(target, transformers.Conv1D):
+ return target.weight.shape[0], target.weight.shape[1]
+
+ in_features = getattr(target, "in_features", None)
+ out_features = getattr(target, "out_features", None)
+ if in_features is not None and out_features is not None:
+ return int(in_features), int(out_features)
+
+ raise NotImplementedError(f"Unsupported EXL3 module type: {target.__class__.__name__}")
+
+
+def create_exllamav3_module(
+ *,
+ module_root: nn.Module,
+ name: str,
+ submodule: nn.Module,
+ tensors: Dict[str, torch.Tensor],
+ module_cls: Type[nn.Module] = ExllamaV3Linear,
+) -> nn.Module:
+ in_features, out_features = _resolve_linear_shape(submodule)
+ new_module = module_cls.from_tensors(
+ in_features=in_features,
+ out_features=out_features,
+ name=name,
+ tensors=tensors,
+ )
+ recurse_setattr(module_root, name, new_module)
+ return new_module
+
+
+def build_exllamav3_tensor_storage(model: nn.Module) -> Dict[str, Dict[str, Any]]:
+ storage: Dict[str, Dict[str, Any]] = {}
+ for name, module in model.named_modules():
+ if getattr(module, "QUANT_TYPE", None) == "exl3" and hasattr(module, "tensor_storage_entry"):
+ storage[name] = module.tensor_storage_entry()
+ return storage
+
+
+def replace_exllamav3_placeholders(
+ *,
+ model: nn.Module,
+ module_names: Iterable[str],
+ tensor_storage: Optional[Dict[str, Dict[str, Any]]] = None,
+ module_cls: Type[nn.Module] = ExllamaV3Linear,
+) -> None:
+ module_lookup = dict(model.named_modules())
+ storage_map = tensor_storage or {}
+
+ for module_name in module_names:
+ submodule = module_lookup.get(module_name)
+ if submodule is None:
+ continue
+
+ if not isinstance(submodule, (nn.Linear, transformers.Conv1D, _ConvNd)):
+ continue
+
+ in_features, out_features = _resolve_linear_shape(submodule)
+ new_module = module_cls(
+ in_features=in_features,
+ out_features=out_features,
+ name=module_name,
+ tensor_storage=storage_map.get(module_name),
+ )
+ recurse_setattr(model, module_name, new_module)
diff --git a/gptqmodel/utils/failsafe.py b/gptqmodel/utils/fallback.py
similarity index 75%
rename from gptqmodel/utils/failsafe.py
rename to gptqmodel/utils/fallback.py
index 83b9b708f..2f2ac1037 100644
--- a/gptqmodel/utils/failsafe.py
+++ b/gptqmodel/utils/fallback.py
@@ -4,25 +4,25 @@
from typing import Any, Optional, Tuple
-from gptqmodel.quantization.config import FailSafe, FailSafeStrategy
+from gptqmodel.quantization.config import Fallback, FallbackStrategy
-def normalize_failsafe(
+def normalize_fallback(
value: Any,
- default: Optional[FailSafe] = None,
-) -> Optional[FailSafe]:
+ default: Optional[Fallback] = None,
+) -> Optional[Fallback]:
if value is None:
return default
- if isinstance(value, FailSafe):
+ if isinstance(value, Fallback):
return value
if isinstance(value, dict):
- fallback = default if isinstance(default, FailSafe) else FailSafe()
- return FailSafe(
+ fallback = default if isinstance(default, Fallback) else Fallback()
+ return Fallback(
strategy=value.get("strategy", fallback.strategy),
threshold=value.get("threshold", fallback.threshold),
)
raise ValueError(
- "normalize_failsafe: expected FailSafe, dict, or None. "
+ "normalize_fallback: expected Fallback, dict, or None. "
)
@@ -50,36 +50,36 @@ def _parse_threshold(setting: Any) -> Tuple[Optional[float], bool]:
return None, False
-def resolve_failsafe_strategy(strategy: Any) -> FailSafeStrategy:
+def resolve_fallback_strategy(strategy: Any) -> FallbackStrategy:
"""
- Normalize a failsafe strategy.
+ Normalize a fallback strategy.
"""
- if isinstance(strategy, FailSafe):
+ if isinstance(strategy, Fallback):
strategy = strategy.strategy
if isinstance(strategy, dict):
- strategy = strategy.get("strategy", FailSafeStrategy.RTN)
+ strategy = strategy.get("strategy", FallbackStrategy.RTN)
if strategy is None:
- resolved = FailSafeStrategy.RTN
- elif isinstance(strategy, FailSafeStrategy):
+ resolved = FallbackStrategy.RTN
+ elif isinstance(strategy, FallbackStrategy):
resolved = strategy
elif isinstance(strategy, str):
normalized = strategy.strip().lower()
try:
- resolved = FailSafeStrategy(normalized)
+ resolved = FallbackStrategy(normalized)
except ValueError:
- resolved = FailSafeStrategy.RTN
+ resolved = FallbackStrategy.RTN
else:
- resolved = FailSafeStrategy.RTN
+ resolved = FallbackStrategy.RTN
return resolved
-def should_use_failsafe(
+def should_use_fallback(
setting: Any,
observed_samples: float,
expected_total_samples: Optional[float] = None,
) -> bool:
- if isinstance(setting, FailSafe):
+ if isinstance(setting, Fallback):
setting = setting.threshold
if isinstance(setting, dict):
setting = setting.get("threshold", None)
@@ -96,7 +96,7 @@ def resolve_threshold(
"""
Resolve a threshold into a raw numeric value and whether it was percent-based.
"""
- if isinstance(setting, FailSafe):
+ if isinstance(setting, Fallback):
setting = setting.threshold
if isinstance(setting, dict):
setting = setting.get("threshold", None)
diff --git a/gptqmodel/utils/hf.py b/gptqmodel/utils/hf.py
index a695bbc60..8c49a191c 100644
--- a/gptqmodel/utils/hf.py
+++ b/gptqmodel/utils/hf.py
@@ -26,6 +26,66 @@
log = setup_logger()
+_ORIGINAL_GET_EXPANDED_TIED_WEIGHTS_KEYS = PreTrainedModel.get_expanded_tied_weights_keys
+
+
+def _resolve_input_embedding_weight_name(model: PreTrainedModel) -> Optional[str]:
+ get_input_embeddings = getattr(model, "get_input_embeddings", None)
+ if not callable(get_input_embeddings):
+ return None
+
+ try:
+ input_embeddings = get_input_embeddings()
+ except Exception:
+ return None
+
+ if input_embeddings is None:
+ return None
+
+ weight = getattr(input_embeddings, "weight", None)
+ if weight is None:
+ return None
+
+ for name, param in model.named_parameters():
+ if param is weight:
+ return name
+
+ for name, module in model.named_modules():
+ if module is input_embeddings:
+ return f"{name}.weight" if name else "weight"
+
+ return None
+
+
+def _legacy_tied_weights_mapping(model: PreTrainedModel) -> Optional[dict[str, str]]:
+ tied_keys = getattr(model, "_tied_weights_keys", None)
+ if not isinstance(tied_keys, (list, tuple, set)):
+ return None
+
+ if not getattr(getattr(model, "config", None), "tie_word_embeddings", False):
+ return {}
+
+ input_weight_name = _resolve_input_embedding_weight_name(model)
+ if input_weight_name is None:
+ return {}
+
+ return {str(name): input_weight_name for name in tied_keys}
+
+
+def _compat_get_expanded_tied_weights_keys(self, all_submodels: bool = False) -> dict:
+ if not all_submodels:
+ legacy_mapping = _legacy_tied_weights_mapping(self)
+ if legacy_mapping is not None:
+ # Newer transformers expects a mapping in multiple code paths, including save_pretrained().
+ self._tied_weights_keys = legacy_mapping
+ return legacy_mapping
+
+ return _ORIGINAL_GET_EXPANDED_TIED_WEIGHTS_KEYS(self, all_submodels=all_submodels)
+
+
+if PreTrainedModel.get_expanded_tied_weights_keys is not _compat_get_expanded_tied_weights_keys:
+ PreTrainedModel.get_expanded_tied_weights_keys = _compat_get_expanded_tied_weights_keys
+
def _sanitize_generation_config(cfg: GenerationConfig, *, drop_sampling_fields: bool = False) -> bool:
changed = False
if cfg is None:
diff --git a/gptqmodel/utils/importer.py b/gptqmodel/utils/importer.py
index a35f25a09..6e18d2e63 100644
--- a/gptqmodel/utils/importer.py
+++ b/gptqmodel/utils/importer.py
@@ -16,6 +16,7 @@
from ..models._const import DEVICE, normalize_device
from ..nn_modules.qlinear import BaseQuantLinear, PackableQuantLinear
from ..quantization import FORMAT, METHOD
+from ..quantization.config import _normalize_quant_bits, quant_bits_width
from ..utils.env import env_flag
from ..utils.logger import setup_logger
from . import BACKEND
@@ -31,6 +32,15 @@
message_logged = False
log = setup_logger()
+
+def _supports_pack_api(cls: Type[BaseQuantLinear]) -> bool:
+ return (
+ issubclass(cls, PackableQuantLinear)
+ or (hasattr(cls, "pack") and callable(getattr(cls, "pack")))
+ or (hasattr(cls, "pack_block") and callable(getattr(cls, "pack_block")))
+ )
+
+
def iter_quant_linear_kernels() -> List[Type[BaseQuantLinear]]:
kernels = []
seen = set()
@@ -409,7 +419,7 @@ def _normalize_dtype(value: Optional[Union[str, torch.dtype]], field: str) -> Op
# auto select the correct/optimal QuantLinear class
def select_quant_linear(
- bits: int,
+ bits,
group_size: int,
desc_act: bool,
sym: bool,
@@ -429,6 +439,10 @@ def select_quant_linear(
format = FORMAT(format.lower())
if isinstance(quant_method, str):
quant_method = METHOD(quant_method.lower())
+ if isinstance(backend, str):
+ backend = BACKEND(backend.lower())
+
+ bits = quant_bits_width(_normalize_quant_bits(bits, format_value=format))
supported_formats = BACKEND_TO_METHOD_FORMAT_MAPPING.get(quant_method)
if supported_formats is None:
@@ -467,10 +481,7 @@ def select_quant_linear(
log.info(f"skip {k} for {str(err)}")
if validate:
if pack:
- check_pack_func = issubclass(cls, PackableQuantLinear) or (
- hasattr(cls, "pack_block") and callable(getattr(cls, "pack_block"))
- )
- if check_pack_func:
+ if _supports_pack_api(cls):
#if not message_logged:
# logger.info(f"Auto pick kernel based on compatibility: {cls}")
# message_logged = True
@@ -517,8 +528,13 @@ def select_quant_linear(
log.info(f"{'Packing ' if pack else ''}Kernel: selected: `{qlinear.__name__}`")
if not validate:
raise ValueError(err)
- else:
- if multi_select:
- return [qlinear]
- else:
- return qlinear
+
+ if pack:
+ if not _supports_pack_api(qlinear):
+ raise ValueError(
+ f"Selected backend `{backend}` with kernel `{qlinear.__name__}` cannot pack quantized weights for format `{format}`."
+ )
+
+ if multi_select:
+ return [qlinear]
+ return qlinear
diff --git a/gptqmodel/utils/marlin.py b/gptqmodel/utils/marlin.py
index f5f829f98..d6b09c195 100644
--- a/gptqmodel/utils/marlin.py
+++ b/gptqmodel/utils/marlin.py
@@ -51,8 +51,11 @@ def marlin_make_workspace_new(device: torch.device,
max_blocks_per_sm: int = 1) -> torch.Tensor:
# In the new marlin kernel, we use the num of threadblocks as workspace
# size. The num of threadblocks is sms_count * max_blocks_per_sm.
+ # Some kernels require a larger fixed minimum than the SM count on
+ # lower-SM but still-supported GPUs, so clamp to that floor.
sms = torch.cuda.get_device_properties(device).multi_processor_count
- return torch.zeros(sms * max_blocks_per_sm,
+ workspace_blocks = max(sms * max_blocks_per_sm, 128)
+ return torch.zeros(workspace_blocks,
dtype=torch.int,
device=device,
requires_grad=False)
diff --git a/gptqmodel/utils/mlx.py b/gptqmodel/utils/mlx.py
index 9586ea527..ef7f5f41c 100644
--- a/gptqmodel/utils/mlx.py
+++ b/gptqmodel/utils/mlx.py
@@ -11,6 +11,7 @@
from ..models import BaseQModel
from ..nn_modules.qlinear.torch import TorchQuantLinear
from ..quantization import FORMAT
+from ..quantization.config import resolve_quant_format
from .logger import setup_logger
from .torch import torch_empty_cache
@@ -35,8 +36,8 @@ def convert_gptq_to_mlx_weights(model_id_or_path: str, model: Union[PreTrainedMo
if gptq_config["bits"] not in [2, 3, 4, 8]:
raise ValueError("Model bits is not in [2,3,4,8]")
- if gptq_config["checkpoint_format"] not in [FORMAT.GPTQ, FORMAT.GPTQ_V2]:
- raise ValueError("Model checkpoint format is not gptq or gptq_v2")
+ if resolve_quant_format(gptq_config.get("format"), gptq_config.get("method", gptq_config.get("quant_method"))) not in [FORMAT.GPTQ, FORMAT.GPTQ_V2]:
+ raise ValueError("Model format is not gptq or gptq_v2")
if gptq_config.get("dynamic") is not None:
print(gptq_config["dynamic"])
diff --git a/gptqmodel/utils/model.py b/gptqmodel/utils/model.py
index 3ba42dde7..07443543e 100644
--- a/gptqmodel/utils/model.py
+++ b/gptqmodel/utils/model.py
@@ -50,7 +50,18 @@
from ..nn_modules.qlinear.exllamav2 import ExllamaV2QuantLinear
from ..nn_modules.qlinear.exllamav2_awq import AwqExllamaV2QuantLinear
from ..quantization import FORMAT, QuantizeConfig
-from ..quantization.config import FORMAT_FIELD_CHECKPOINT, METHOD, dynamic_get
+from ..quantization.config import (
+ FORMAT_FIELD_CODE,
+ METHOD,
+ _normalize_fp8_fmt,
+ _normalize_fp8_scale_semantics,
+ _normalize_fp8_weight_block_size,
+ _normalize_fp8_weight_scale_method,
+ _normalize_quant_bits,
+ dynamic_get,
+ quant_bits_width,
+ resolve_quant_format,
+)
from . import has_gil_disabled
from .backend import BACKEND
from .ctx import ctx
@@ -76,6 +87,11 @@
torch.bool: ("BOOL", 1),
}
+if hasattr(torch, "float8_e4m3fn"):
+ _DTYPE_SAFE_MAP[torch.float8_e4m3fn] = ("F8_E4M3", 1)
+if hasattr(torch, "float8_e5m2"):
+ _DTYPE_SAFE_MAP[torch.float8_e5m2] = ("F8_E5M2", 1)
+
_DTYPE_STR_MAP = {
"float32": torch.float32,
@@ -96,6 +112,13 @@
"bool": torch.bool,
}
+if hasattr(torch, "float8_e4m3fn"):
+ _DTYPE_STR_MAP["float8_e4m3fn"] = torch.float8_e4m3fn
+ _DTYPE_STR_MAP["f8_e4m3"] = torch.float8_e4m3fn
+if hasattr(torch, "float8_e5m2"):
+ _DTYPE_STR_MAP["float8_e5m2"] = torch.float8_e5m2
+ _DTYPE_STR_MAP["f8_e5m2"] = torch.float8_e5m2
+
MoETopKState = List[Tuple[nn.Module, str, int]]
MOE_TOPK_FIELD_NAMES = [
@@ -254,19 +277,22 @@ def make_quant(
from_quantized: bool = False,
) -> Type[BaseQuantLinear]:
- bits = qcfg.bits
+ bits = qcfg.runtime_bits
group_size =qcfg.group_size
extension = qcfg.adapter
- format = qcfg.format
+ format = resolve_quant_format(qcfg.format, qcfg.method)
desc_act = qcfg.desc_act
sym = qcfg.sym
dynamic = qcfg.dynamic
pack_dtype = qcfg.pack_dtype
+ init_kwargs = qcfg.quant_linear_init_kwargs()
# Bitblas needs to be loaded as gptq's quant linear first, and then converted to bitblas format.
if not pack and format in (FORMAT.GPTQ, FORMAT.GPTQ_V2) and backend == BACKEND.BITBLAS:
backend = BACKEND.TORCH
+ export_quant_method = qcfg.export_quant_method()
+
# returns multiple validated kernels
quant_linear_candidates = select_quant_linear(
bits=bits,
@@ -275,7 +301,7 @@ def make_quant(
sym=sym,
backend=backend,
format=format,
- quant_method=qcfg.quant_method,
+ quant_method=export_quant_method,
pack=pack,
dynamic=dynamic,
device=device,
@@ -308,6 +334,8 @@ def make_quant(
pack_dtype=pack_dtype,
backend=backend,
adapter=qcfg.adapter,
+ format=format,
+ init_kwargs=init_kwargs,
)
log.info(f"Kernel: selected -> `{linear_cls.__name__}`.")
return linear_cls
@@ -323,7 +351,7 @@ def make_quant(
def create_quant_module(
name: str,
linear_cls: Type[BaseQuantLinear],
- bits: int,
+ bits,
desc_act: bool,
dynamic,
group_size: int,
@@ -333,9 +361,11 @@ def create_quant_module(
device: DEVICE,
lm_head_name: str,
pack_dtype: torch.dtype,
+ format: FORMAT,
backend: BACKEND = BACKEND.AUTO,
register_buffers: bool = True,
adapter: Optional[Adapter] = None,
+ init_kwargs: Optional[Dict[str, Any]] = None,
):
# unwrap named module
@@ -379,11 +409,12 @@ def create_quant_module(
bias = submodule.bias is not None
# need copies as dynamic config may override these in for loop
- tmp_bits = bits
+ tmp_bits = _normalize_quant_bits(bits, format_value=format)
tmp_group_size = group_size
tmp_desc_act = desc_act
tmp_sym = sym
tmp_pack_dtype = pack_dtype
+ tmp_init_kwargs = dict(init_kwargs or {})
# dynamic bits, group_size, sym, pack_dtype for each layer/module
if dynamic is not None:
@@ -395,30 +426,57 @@ def create_quant_module(
# positive module match
if overrides:
# override base QuantizeConfig for every quant config key/value
- tmp_bits = overrides.get("bits", bits)
+ tmp_bits = _normalize_quant_bits(overrides.get("bits", bits), format_value=format)
tmp_group_size = overrides.get("group_size", group_size)
tmp_desc_act = overrides.get("desc_act", desc_act)
tmp_sym = overrides.get("sym", sym)
tmp_pack_dtype = overrides.get("pack_dtype", pack_dtype)
+ if format == FORMAT.FP8:
+ fp8_format_override = overrides.get(FORMAT_FIELD_CODE, overrides.get("fmt"))
+ if fp8_format_override is not None:
+ tmp_init_kwargs["format"] = _normalize_fp8_fmt(fp8_format_override)
+ block_size_override = overrides.get(
+ "weight_block_size",
+ tmp_init_kwargs.get("weight_block_size"),
+ )
+ normalized_block_size = _normalize_fp8_weight_block_size(block_size_override)
+ if "weight_scale_method" in overrides or block_size_override is not None:
+ tmp_init_kwargs["weight_scale_method"] = _normalize_fp8_weight_scale_method(
+ overrides.get(
+ "weight_scale_method",
+ tmp_init_kwargs.get("weight_scale_method"),
+ ),
+ weight_block_size=normalized_block_size,
+ )
+ if "weight_scale_semantics" in overrides:
+ tmp_init_kwargs["weight_scale_semantics"] = _normalize_fp8_scale_semantics(
+ overrides["weight_scale_semantics"]
+ )
+ if "weight_block_size" in overrides:
+ tmp_init_kwargs["weight_block_size"] = normalized_block_size
+
+ validate_bits = quant_bits_width(tmp_bits)
+ constructor_bits = tmp_bits if getattr(linear_cls, "QUANT_TYPE", None) == "gguf" else validate_bits
+
# when loading a quantized model, device is target device passed in GPTQModel.load()
# check in_features and out_features validate
_, err = linear_cls.validate(
- bits=tmp_bits,
+ bits=validate_bits,
group_size=tmp_group_size,
desc_act=tmp_desc_act,
sym=tmp_sym,
pack_dtype=tmp_pack_dtype,
in_features=in_features,
out_features=out_features,
- device=device,
+ device=DEVICE(device) if isinstance(device, str) else device,
adapter=adapter, # TODO FIX ME..need to pass Eora if loaded
)
if err is not None:
raise err
new_layer = linear_cls(
- bits=tmp_bits,
+ bits=constructor_bits,
group_size=tmp_group_size,
desc_act=tmp_desc_act,
sym=tmp_sym,
@@ -432,13 +490,14 @@ def create_quant_module(
backend=backend,
register_buffers=register_buffers,
adapter=adapter,
+ **tmp_init_kwargs,
)
new_layer.device = ori_layer_device
recurse_setattr(module, name, new_layer.to(ori_layer_device))
def create_quant_layer(
linear_cls: Type[BaseQuantLinear],
- bits: int,
+ bits,
desc_act: bool,
dynamic,
group_size: int,
@@ -450,6 +509,8 @@ def create_quant_layer(
pack_dtype: torch.dtype,
backend: BACKEND,
adapter: Optional[Adapter] = None,
+ format: FORMAT = FORMAT.GPTQ,
+ init_kwargs: Optional[Dict[str, Any]] = None,
) -> Type[BaseQuantLinear]:
if isinstance(module, linear_cls):
@@ -472,8 +533,10 @@ def create_quant_layer(
device=device,
lm_head_name=lm_head_name,
pack_dtype=pack_dtype,
+ format=format,
backend=backend,
adapter=adapter,
+ init_kwargs=init_kwargs,
)
return linear_cls
@@ -588,7 +651,7 @@ def convert_gptq_v1_to_v2_format(
qlinear_kernel: Type[BaseQuantLinear],
):
# skip v2 to v1 conversion for gptq_v1 kernels
- if cfg.quant_method in [METHOD.GPTQ] and not qlinear_kernel.REQUIRES_FORMAT_V2:
+ if cfg.export_quant_method() == METHOD.GPTQ and not qlinear_kernel.REQUIRES_FORMAT_V2:
log.info(
f"Format: Skipped v1 to v2 conversion due to Kernel `{qlinear_kernel}`.")
return model
@@ -597,7 +660,7 @@ def convert_gptq_v1_to_v2_format(
# with tctl.threadpool_limits(limits=1):
time.time()
log.info(
- f"Format: Converting `{FORMAT_FIELD_CHECKPOINT}` from `{FORMAT.GPTQ}` to internal `{FORMAT.GPTQ_V2}`.")
+ f"Format: Converting `{FORMAT_FIELD_CODE}` from `{FORMAT.GPTQ}` to internal `{FORMAT.GPTQ_V2}`.")
for _, submodule in model.named_modules():
# v1 checkpoint format used to do `qzeros = qzeros -= 1` before serialization, thus the
@@ -665,7 +728,7 @@ def convert_gptq_v2_to_v1_format(
):
# skip v2 to v1 conversion for gptq_v1 kernels
- if quantize_config.quant_method in [METHOD.GPTQ] and not qlinear_kernel.REQUIRES_FORMAT_V2:
+ if quantize_config.export_quant_method() == METHOD.GPTQ and not qlinear_kernel.REQUIRES_FORMAT_V2:
return model
# Limit thread usage to avoid auto-parallizataion regression
@@ -819,8 +882,8 @@ def pack_module(
if (
quantize_config is not None
- and quantize_config.quant_method == METHOD.GPTQ
- and quantize_config.format == FORMAT.GPTQ
+ and quantize_config.export_quant_method() == METHOD.GPTQ
+ and resolve_quant_format(quantize_config.format, quantize_config.method) == FORMAT.GPTQ
and getattr(quant_linear_cls, "REQUIRES_FORMAT_V2", False)
):
with log_time_block(
diff --git a/gptqmodel/utils/model_dequant.py b/gptqmodel/utils/model_dequant.py
index ed37909d5..83ff7ed6a 100644
--- a/gptqmodel/utils/model_dequant.py
+++ b/gptqmodel/utils/model_dequant.py
@@ -18,12 +18,18 @@
from safetensors import safe_open
from safetensors.torch import save_file
-from ..quantization.dtype import dequantize_f4_e2m1, dequantize_f8_e4m3
+from ..quantization.dtype import dequantize_f4_e2m1, dequantize_fp8
from ..utils.logger import setup_logger
LOG = logging.getLogger(__name__)
+_FLOAT8_DTYPES = tuple(
+ getattr(torch, name)
+ for name in ("float8_e4m3fn", "float8_e5m2")
+ if hasattr(torch, name)
+)
+
if TYPE_CHECKING:
from compressed_tensors.compressors.base import BaseCompressor
from compressed_tensors.quantization.quant_scheme import QuantizationScheme
@@ -315,8 +321,8 @@ def infer_block_shape(weight_shape: Tuple[int, int], scale_tensor: torch.Tensor)
def detect_format(model_path: Path, config: dict) -> str:
quant_cfg = config.get("quantization_config", {}) or {}
- method = (quant_cfg.get("quant_method") or "").lower()
- fmt = (quant_cfg.get("fmt") or "").lower()
+ method = (quant_cfg.get("method") or quant_cfg.get("quant_method") or "").lower()
+ format_name = (quant_cfg.get("format") or "").lower()
files, _ = list_safetensor_files(model_path)
if not files:
@@ -328,7 +334,7 @@ def detect_format(model_path: Path, config: dict) -> str:
for key in keys:
if key.endswith(".weight"):
tensor = reader.get_tensor(key)
- if tensor.dtype == torch.float8_e4m3fn:
+ if tensor.dtype in _FLOAT8_DTYPES:
LOG.debug("Detected FP8 weights via dtype on tensor '%s'", key)
return "fp8"
if tensor.dtype == torch.uint8 and (key + "_scale") in keys:
@@ -347,6 +353,9 @@ def detect_format(model_path: Path, config: dict) -> str:
if any(k.endswith(".weight_scale_inv") for k in keys):
LOG.debug("Detected FP8 format via '.weight_scale_inv' metadata in shard '%s'", files[0])
return "fp8"
+ if any(k.endswith(".trellis") for k in keys):
+ LOG.debug("Detected EXL3 format via '.trellis' metadata in shard '%s'", files[0])
+ return "exl3"
if any(k.endswith(".qweight") for k in keys):
has_g = any(k.endswith(".g_idx") for k in keys)
LOG.debug(
@@ -356,20 +365,26 @@ def detect_format(model_path: Path, config: dict) -> str:
)
return "gptq" if has_g else "awq"
- if fmt == "float8_e4m3fn":
- LOG.debug("Detected FP8 format via config fmt=%s", fmt)
+ if format_name in {"float8_e4m3fn", "float8_e5m2"}:
+ LOG.debug("Detected FP8 format via config format=%s", format_name)
+ return "fp8"
+ if method == "fp8":
+ LOG.debug("Detected FP8 format via method=%s", method)
return "fp8"
if method in ("gptq", "gptqmodel"):
- LOG.debug("Detected GPTQ format via quant_method=%s", method)
+ LOG.debug("Detected GPTQ format via method=%s", method)
return "gptq"
if method == "awq":
- LOG.debug("Detected AWQ format via quant_method=%s", method)
+ LOG.debug("Detected AWQ format via method=%s", method)
return "awq"
+ if method == "exl3":
+ LOG.debug("Detected EXL3 format via method=%s", method)
+ return "exl3"
if method == "compressed-tensors":
fmt_name = (quant_cfg.get("format") or "").lower()
if fmt_name == "pack-quantized":
LOG.debug(
- "Detected compressed-tensors format via quant_method=%s and format=%s",
+ "Detected compressed-tensors format via method=%s and format=%s",
method,
fmt_name,
)
@@ -407,11 +422,12 @@ def convert_fp8_shard(
target_dtype: torch.dtype,
*,
block_shape: Optional[Tuple[int, int]],
+ scale_semantics: str = "heuristic",
) -> Dict[str, torch.Tensor]:
tensors: Dict[str, torch.Tensor] = {}
for key in reader.keys():
tensor = reader.get_tensor(key)
- if key.endswith(".weight") and tensor.dtype == torch.float8_e4m3fn:
+ if key.endswith(".weight") and tensor.dtype in _FLOAT8_DTYPES:
scale_key = key + "_scale_inv"
if scale_key not in reader.keys():
raise KeyError(f"Missing scale inverse tensor for {key}")
@@ -442,9 +458,15 @@ def convert_fp8_shard(
f"Tensor {key} shape {tensor.shape} incompatible with block size {effective_block}"
)
- deq = dequantize_f8_e4m3(
+ scale_arg = None
+ scale_inv_arg = scale_inv
+ if scale_semantics == "inverse":
+ scale_arg = torch.reciprocal(scale_inv.to(torch.float32))
+ scale_inv_arg = None
+ deq = dequantize_fp8(
tensor,
- scale_inv=scale_inv,
+ scale=scale_arg,
+ scale_inv=scale_inv_arg,
axis=None,
target_dtype=target_dtype,
)
@@ -699,6 +721,7 @@ def dequantize_model(
open_device = device_str or "cpu"
block_shape = resolve_block_size(config) if fmt == "fp8" else None
+ fp8_scale_semantics = str(quant_cfg.get("weight_scale_semantics") or "heuristic").strip().lower()
if block_shape is not None:
LOG.debug("Configured FP8 block size %s found in quantization_config", block_shape)
@@ -740,7 +763,12 @@ def dequantize_model(
LOG.debug("Processing shard '%s' for format %s on device %s", filename, fmt, open_device)
if fmt == "fp8":
with safe_open(path, framework="pt", device=open_device) as reader:
- tensors = convert_fp8_shard(reader, target_dtype, block_shape=block_shape)
+ tensors = convert_fp8_shard(
+ reader,
+ target_dtype,
+ block_shape=block_shape,
+ scale_semantics=fp8_scale_semantics,
+ )
elif fmt == "nvfp4":
with safe_open(path, framework="pt", device=open_device) as reader:
tensors = convert_nvfp4_shard(reader, target_dtype)
diff --git a/gptqmodel/utils/stream.py b/gptqmodel/utils/stream.py
index 38a48bc58..04f8322f6 100644
--- a/gptqmodel/utils/stream.py
+++ b/gptqmodel/utils/stream.py
@@ -195,9 +195,9 @@ def stream_tensor_dict_to_cpu(
# store_callback(host_map)
# return host_map
- first = next(iter(filtered.values()))
+ first_cuda = next((tensor for tensor in filtered.values() if tensor.device.type == "cuda"), None)
- if first.device.type != "cuda" or not torch.cuda.is_available():
+ if first_cuda is None or not torch.cuda.is_available():
host_map = {name: tensor.detach().to("cpu") for name, tensor in filtered.items()}
with state_lock:
store_callback(host_map)
@@ -205,18 +205,25 @@ def stream_tensor_dict_to_cpu(
host_map: Dict[str, torch.Tensor] = {}
- copy_device = first.device
+ copy_device = first_cuda.device
compute_stream = torch.cuda.current_stream(device=copy_device)
copy_stream = _get_cached_copy_stream(copy_device)
- done_event = torch.cuda.Event(enable_timing=False, blocking=False)
pending_sources: List[torch.Tensor] = []
+ pending_keys: List[str] = []
with torch.cuda.stream(copy_stream):
copy_stream.wait_stream(compute_stream)
for name, tensor in filtered.items():
src = tensor.detach()
+ if src.device.type != "cuda":
+ host_map[name] = src.to("cpu")
+ continue
+ if src.device != copy_device:
+ host_map[name] = src.to("cpu")
+ continue
src.record_stream(copy_stream)
pending_sources.append(src)
+ pending_keys.append(name)
host = torch.empty(
src.shape,
dtype=src.dtype,
@@ -226,12 +233,19 @@ def stream_tensor_dict_to_cpu(
)
host.copy_(src, non_blocking=True)
host_map[name] = host
+
+ if not pending_sources:
+ with state_lock:
+ store_callback(host_map)
+ return host_map
+
+ done_event = torch.cuda.Event(enable_timing=False, blocking=False)
done_event.record(copy_stream)
ticket = StreamCopyTicket(
event=done_event,
device=copy_device,
- keys=tuple(host_map.keys()),
+ keys=tuple(pending_keys),
sources=pending_sources,
stream=copy_stream,
)
diff --git a/gptqmodel/version.py b/gptqmodel/version.py
index d7457a2f2..b6d1a8a49 100644
--- a/gptqmodel/version.py
+++ b/gptqmodel/version.py
@@ -7,4 +7,4 @@
# even minor versions are release
# 5.2.0 => release, 5.1.0 => devel
# micro version (5.2.x) denotes patch fix, i.e. 5.2.1 is a patch fix release
-__version__ = "5.8.0"
+__version__ = "6.0.0"
diff --git a/gptqmodel_ext/exllamav3/bindings.cpp b/gptqmodel_ext/exllamav3/bindings.cpp
new file mode 100644
index 000000000..a73353c5f
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/bindings.cpp
@@ -0,0 +1,31 @@
+#include
+
+#include
+#include
+#include
+
+#include "hadamard.h"
+#include "hgemm.cuh"
+
+#include "quant/quantize.cuh"
+#include "quant/pack.cuh"
+#include "quant/reconstruct.cuh"
+#include "quant/hadamard.cuh"
+
+#include "libtorch/linear.h"
+
+PYBIND11_MODULE(TORCH_EXTENSION_NAME, m)
+{
+ m.def("had_paley", &had_paley, "had_paley");
+ m.def("had_paley2", &had_paley2, "had_paley2");
+
+ m.def("quantize_tiles", &quantize_tiles, "quantize_tiles");
+ m.def("pack_trellis", &pack_trellis, "pack_trellis");
+ m.def("unpack_trellis", &unpack_trellis, "unpack_trellis");
+ m.def("pack_signs", &pack_signs, "pack_signs");
+ m.def("reconstruct", &reconstruct, "reconstruct");
+ m.def("had_r_128", &had_r_128, "had_r_128");
+ m.def("hgemm", &hgemm, "hgemm");
+
+ #include "libtorch/linear_bc.h"
+}
diff --git a/gptqmodel_ext/exllamav3/hadamard.cpp b/gptqmodel_ext/exllamav3/hadamard.cpp
new file mode 100644
index 000000000..0a9cc2ec3
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/hadamard.cpp
@@ -0,0 +1,112 @@
+#include "hadamard.h"
+#include "util.h"
+
+#define HALF_P 0x3C00
+#define HALF_N 0xBC00
+#define HALF_PP 0x3C003C00
+#define HALF_PN 0xBC003C00
+#define HALF_NP 0x3C00BC00
+#define HALF_NN 0xBC00BC00
+
+inline int pmod(int a, int b)
+{
+ int ret = a % b;
+ if (ret < 0 && b > 0) ret += b;
+ return ret;
+}
+
+inline int modular_pow(int base, int exp, int mod)
+{
+ int result = 1;
+ base = pmod(base, mod);
+ while (exp > 0)
+ {
+ if (exp % 2 == 1) result = pmod((result * base), mod);
+ exp = exp >> 1;
+ base = pmod((base * base), mod);
+ }
+ return result;
+}
+
+inline bool is_quadratic_residue(int a, int p)
+{
+ return modular_pow(a, (p - 1) / 2, p) == 1;
+}
+
+// Paley construction
+
+void had_paley
+(
+ at::Tensor h
+)
+{
+ TORCH_CHECK_DTYPE(h, kHalf);
+ TORCH_CHECK_SHAPES(h, 0, h, 1, 1);
+ TORCH_CHECK(h.is_contiguous());
+ int n = h.size(0);
+ int p = n - 1;
+ uint16_t* ptr = (uint16_t*) h.data_ptr();
+
+ for (int j = 0; j < n; ++j)
+ *ptr++ = HALF_P;
+
+ for (int i = 0; i < p; ++i)
+ {
+ *ptr++ = HALF_N;
+ for (int j = 0; j < p; ++j)
+ {
+ if (i == j) *ptr++ = HALF_P;
+ else
+ {
+ int residue = pmod(i - j, p);
+ if (is_quadratic_residue(residue, p))
+ *ptr++ = HALF_P;
+ else
+ *ptr++ = HALF_N;
+ }
+ }
+ }
+}
+
+// Paley construction, type 2
+
+void had_paley2
+(
+ at::Tensor h
+)
+{
+ TORCH_CHECK_DTYPE(h, kHalf);
+ TORCH_CHECK_SHAPES(h, 0, h, 1, 1);
+ int n = h.size(0);
+ int p = n / 2 - 1;
+ uint32_t* ptr0 = (uint32_t*) h.data_ptr();
+ uint32_t* ptr1 = ptr0 + n / 2;
+
+ for (int i = 0; i < n / 2; ++i)
+ {
+ for (int j = 0; j < n / 2; ++j)
+ {
+ if (i == j)
+ {
+ *ptr0++ = HALF_PN;
+ *ptr1++ = HALF_NN;
+ }
+ else
+ {
+ int residue = pmod(i - j, p);
+ if (i == 0 || j == 0 || is_quadratic_residue(residue, p))
+ {
+ *ptr0++ = HALF_PP;
+ *ptr1++ = HALF_PN;
+ }
+ else
+ {
+ *ptr0++ = HALF_NN;
+ *ptr1++ = HALF_NP;
+ }
+ }
+ }
+ ptr0 += n / 2;
+ ptr1 += n / 2;
+ }
+}
diff --git a/gptqmodel_ext/exllamav3/hadamard.h b/gptqmodel_ext/exllamav3/hadamard.h
new file mode 100644
index 000000000..fe4f81f09
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/hadamard.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include
+
+void had_paley
+(
+ at::Tensor h
+);
+
+void had_paley2
+(
+ at::Tensor h
+);
\ No newline at end of file
diff --git a/gptqmodel_ext/exllamav3/hgemm.cu b/gptqmodel_ext/exllamav3/hgemm.cu
new file mode 100644
index 000000000..bd1b807de
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/hgemm.cu
@@ -0,0 +1,93 @@
+#include
+#include "hgemm.cuh"
+#include
+#include
+#include "util.h"
+#include "util.cuh"
+#include "quant/exl3_devctx.cuh"
+
+/*
+
+Row-major matmul using cuBLAS, a @ b -> c
+- if c is float16, operation is float16 @ float16 -> float16 (float16 accumulate)
+- if c is float32, operation is float16 @ float16 -> float32 (float32 accumulate)
+*/
+
+void hgemm
+(
+ at::Tensor a,
+ at::Tensor b,
+ at::Tensor c
+)
+{
+ const at::cuda::OptionalCUDAGuard device_guard(a.device());
+ cudaStream_t stream = at::cuda::getCurrentCUDAStream().stream();
+
+ bool output_fp32 = c.dtype() == at::kFloat;
+ bool output_fp16 = c.dtype() == at::kHalf;
+
+ TORCH_CHECK(output_fp32 || output_fp16, "c must be float32 or float16");
+
+ // Check shapes of a,b,c are compatible
+ TORCH_CHECK_DTYPE(a, kHalf);
+ TORCH_CHECK_DTYPE(b, kHalf);
+ TORCH_CHECK_DIM(b, 2);
+ TORCH_CHECK_SHAPES(a, -1, b, 0, 1);
+ TORCH_CHECK_SHAPES(b, 1, c, -1, 1);
+
+ const half* a_ptr = (const half*) a.data_ptr();
+ const half* b_ptr = (const half*) b.data_ptr();
+
+ int size_k = a.size(-1);
+ int size_m = a.numel() / size_k;
+ int size_n = b.size(-1);
+
+ // Set cuBLAS modes and workspace
+ cublasHandle_t cublas_handle = at::cuda::getCurrentCUDABlasHandle();
+ cublasSetStream(cublas_handle, stream);
+ cublasSetPointerMode(cublas_handle, CUBLAS_POINTER_MODE_HOST);
+ int device;
+ cudaGetDevice(&device);
+ void* ws = DevCtx::instance().get_ws(device);
+ cublasSetWorkspace(cublas_handle, ws, WORKSPACE_SIZE);
+
+ if (output_fp16)
+ {
+ half alpha_ = __float2half(1.0f);
+ half beta_ = __float2half(0.0f);
+
+ half* c_ptr = (half*) c.data_ptr();
+ auto r = cublasHgemm
+ (
+ cublas_handle,
+ CUBLAS_OP_N,
+ CUBLAS_OP_N,
+ size_n, size_m, size_k,
+ &alpha_, b_ptr, size_n,
+ a_ptr, size_k,
+ &beta_, c_ptr, size_n
+ );
+ cublas_check(r);
+ cuda_check(cudaPeekAtLastError());
+ }
+ if (output_fp32)
+ {
+ float alpha_ = 1.0f;
+ float beta_ = 0.0f;
+
+ float* c_ptr = (float*) c.data_ptr();
+ auto r = cublasGemmEx
+ (
+ cublas_handle,
+ CUBLAS_OP_N, CUBLAS_OP_N,
+ size_n, size_m, size_k,
+ &alpha_, b_ptr, CUDA_R_16F, size_n,
+ a_ptr, CUDA_R_16F, size_k,
+ &beta_, c_ptr, CUDA_R_32F, size_n,
+ CUBLAS_COMPUTE_32F,
+ CUBLAS_GEMM_DEFAULT_TENSOR_OP
+ );
+ cublas_check(r);
+ cuda_check(cudaPeekAtLastError());
+ }
+}
diff --git a/gptqmodel_ext/exllamav3/hgemm.cuh b/gptqmodel_ext/exllamav3/hgemm.cuh
new file mode 100644
index 000000000..9e3ee3578
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/hgemm.cuh
@@ -0,0 +1,10 @@
+#pragma once
+
+#include
+
+void hgemm
+(
+ at::Tensor a,
+ at::Tensor b,
+ at::Tensor c
+);
diff --git a/gptqmodel_ext/exllamav3/libtorch/linear.cpp b/gptqmodel_ext/exllamav3/libtorch/linear.cpp
new file mode 100644
index 000000000..7ed5d756e
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/libtorch/linear.cpp
@@ -0,0 +1,23 @@
+#include
+#include "linear.h"
+#include
+#include
+#include
+#include "../util.h"
+#include "../quant/exl3_gemm.cuh"
+
+void BC_LinearEXL3::run(const at::Tensor& x, at::Tensor& y)
+{
+ if (x.numel() == x.size(-1))
+ {
+ exl3_gemm(x, trellis, y, suh, xh, svh, -1, mcg, mul1, 0);
+ }
+ else
+ {
+ at::Tensor xh_ = at::empty_like(x);
+ exl3_gemm(x, trellis, y, suh, xh_, svh, -1, mcg, mul1, 0);
+ }
+
+ if (bias)
+ y.add_(bias.value());
+}
diff --git a/gptqmodel_ext/exllamav3/libtorch/linear.h b/gptqmodel_ext/exllamav3/libtorch/linear.h
new file mode 100644
index 000000000..fc29c467e
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/libtorch/linear.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include
+struct BC_LinearEXL3
+{
+ at::Tensor trellis;
+ at::Tensor suh;
+ at::Tensor svh;
+ int K;
+ c10::optional bias;
+ bool mcg;
+ bool mul1;
+ at::Tensor xh;
+
+ BC_LinearEXL3
+ (
+ at::Tensor _trellis,
+ at::Tensor _suh,
+ at::Tensor _svh,
+ int _K,
+ c10::optional _bias,
+ bool _mcg,
+ bool _mul1,
+ at::Tensor _xh
+ ) :
+ trellis(std::move(_trellis)),
+ suh(std::move(_suh)),
+ svh(std::move(_svh)),
+ K(_K),
+ bias(std::move(_bias)),
+ mcg(_mcg),
+ mul1(_mul1),
+ xh(std::move(_xh))
+ {}
+
+ void run(const at::Tensor& x, at::Tensor& y);
+};
diff --git a/gptqmodel_ext/exllamav3/libtorch/linear_bc.h b/gptqmodel_ext/exllamav3/libtorch/linear_bc.h
new file mode 100644
index 000000000..702a5e326
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/libtorch/linear_bc.h
@@ -0,0 +1,22 @@
+py::class_>(m, "BC_LinearEXL3").def
+(
+ py::init<
+ at::Tensor,
+ at::Tensor,
+ at::Tensor,
+ int,
+ c10::optional,
+ bool,
+ bool,
+ at::Tensor
+ >(),
+ py::arg("trellis"),
+ py::arg("suh"),
+ py::arg("svh"),
+ py::arg("K"),
+ py::arg("bias"),
+ py::arg("mcg"),
+ py::arg("mul1"),
+ py::arg("xh")
+)
+.def("run", &BC_LinearEXL3::run);
diff --git a/gptqmodel_ext/exllamav3/ptx.cuh b/gptqmodel_ext/exllamav3/ptx.cuh
new file mode 100644
index 000000000..8de8e65c1
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/ptx.cuh
@@ -0,0 +1,314 @@
+#pragma once
+
+// Tensor core fragments
+
+template
+struct Vec
+{
+ T elems[n];
+ __device__ T& operator[](int i) { return elems[i]; }
+};
+
+using FragA = Vec;
+using FragB = Vec;
+using FragC = Vec;
+using FragC_h = Vec;
+
+// m8n8k4 tensor core matmul (emulated on Ampere and later), don't use
+//
+// https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#matrix-fragments-for-mma-m8n8k4-with-f16-floating-point-type
+
+__device__ inline void ptx_mma_m8n8k4
+(
+ const Vec& frag_a,
+ const Vec& frag_b,
+ Vec& frag_c
+)
+{
+ const uint32_t* a = reinterpret_cast(&frag_a);
+ const uint32_t* b = reinterpret_cast(&frag_b);
+ float* c = reinterpret_cast(&frag_c);
+ const float* d = reinterpret_cast(&frag_c);
+
+ asm
+ (
+ "mma.sync.aligned.m8n8k4.row.col.f32.f16.f16.f32 "
+ "{%0,%1,%2,%3,%4,%5,%6,%7}, {%8,%9}, {%10,%11}, {%12,%13,%14,%15,%16,%17,%18,%19};\n"
+
+ : "=f"(c[0]), "=f"(c[1]), "=f"(c[2]), "=f"(c[3]),"=f"(c[4]), "=f"(c[5]), "=f"(c[6]), "=f"(c[7])
+
+ : "r"(a[0]), "r"(a[1]),
+ "r"(b[0]), "r"(b[1]),
+ "f"(d[0]), "f"(d[1]), "f"(d[2]), "f"(d[3]), "f"(d[4]), "f"(d[5]), "f"(d[6]), "f"(d[7])
+ );
+}
+
+// m16n8k16 tensor core matmul
+//
+// https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#matrix-fragments-for-mma-m16n8k16-with-floating-point-type
+
+// FP16 @ FP16 + FP32 -> FP32
+__device__ inline void ptx_mma_m16n8k16
+(
+ const FragA& frag_a,
+ const FragB& frag_b,
+ FragC& frag_c
+)
+{
+ const uint32_t* a = reinterpret_cast(&frag_a);
+ const uint32_t* b = reinterpret_cast(&frag_b);
+ float* c = reinterpret_cast(&frag_c);
+ const float* d = reinterpret_cast(&frag_c);
+
+ asm
+ (
+ "mma.sync.aligned.m16n8k16.row.col.f32.f16.f16.f32 "
+ "{%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, {%10,%11,%12,%13};\n"
+
+ : "=f"(c[0]), "=f"(c[1]), "=f"(c[2]), "=f"(c[3])
+ : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]),
+ "r"(b[0]), "r"(b[1]),
+ "f"(d[0]), "f"(d[1]), "f"(d[2]), "f"(d[3])
+ );
+}
+
+// FP16 @ FP16 + FP16 -> FP16
+__device__ inline void ptx_mma_m16n8k16
+(
+ const FragA& frag_a,
+ const FragB& frag_b,
+ FragC_h& frag_c
+)
+{
+ const uint32_t* a = reinterpret_cast(&frag_a);
+ const uint32_t* b = reinterpret_cast(&frag_b);
+ uint32_t* c = reinterpret_cast(&frag_c);
+ const uint32_t* d = reinterpret_cast(&frag_c);
+
+ asm
+ (
+ "mma.sync.aligned.m16n8k16.row.col.f16.f16.f16.f16 "
+ "{%0,%1}, {%2,%3,%4,%5}, {%6,%7}, {%8,%9};\n"
+
+ : "=r"(c[0]), "=r"(c[1])
+ : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]),
+ "r"(b[0]), "r"(b[1]),
+ "r"(d[0]), "r"(d[1])
+ );
+}
+
+// Global barrier
+
+__device__ inline void barrier_acquire
+(
+ int* lock,
+ int stage
+)
+{
+ if (threadIdx.x == 0)
+ {
+ volatile int state = -1;
+ do
+ {
+ asm volatile ("ld.global.acquire.gpu.b32 %0, [%1];\n" : "=r"(state) : "l"(lock));
+ }
+ while (state != stage);
+ }
+ __syncthreads();
+}
+
+__device__ inline void barrier_release
+(
+ int* lock,
+ int val,
+ bool reset
+)
+{
+ __syncthreads();
+ if (threadIdx.x == 0)
+ {
+ if (reset)
+ {
+ *lock = 0;
+ return;
+ }
+ asm volatile ("fence.acq_rel.gpu;\n");
+ asm volatile ("red.relaxed.gpu.global.add.s32 [%0], %1;\n" : : "l"(lock), "r"(val));
+ }
+}
+
+// Load global to shared memory, predicated. Seems to produce incorrect code when compiling for Blackwell, but
+// `if (...) cp_async(...)` compiles to a predicated instruction anyway
+
+__device__ inline void cp_async_pred(void* smem_ptr, const void* glob_ptr, bool pred = true)
+{
+ const int bytes = 16;
+ uint32_t smem = static_cast(__cvta_generic_to_shared(smem_ptr));
+ asm volatile(
+ "{\n"
+ " .reg .pred p;\n"
+ " setp.ne.b32 p, %0, 0;\n"
+ " @p cp.async.cg.shared.global [%1], [%2], %3;\n"
+ "}\n" :: "r"((int) pred), "r"(smem), "l"(glob_ptr), "n"(bytes)
+ );
+}
+
+// Load global to shared memory
+
+__device__ inline void cp_async(void* smem_ptr, const void* glob_ptr)
+{
+ const int bytes = 16;
+ uint32_t smem = static_cast(__cvta_generic_to_shared(smem_ptr));
+ asm volatile(
+ "{\n"
+ " cp.async.cg.shared.global [%0], [%1], %2;\n"
+ "}\n" :: "r"(smem), "l"(glob_ptr), "n"(bytes)
+ );
+}
+
+// Load global to shared memory with cache hint to evict data from L2 ASAP
+
+__device__ inline void cp_async_stream(void* smem_ptr, const void* glob_ptr)
+{
+ uint32_t smem = static_cast(__cvta_generic_to_shared(smem_ptr));
+ const int bytes = 16;
+ asm volatile
+ (
+ "{\n"
+ " .reg .b64 p;\n"
+ " createpolicy.fractional.L2::evict_first.b64 p, 1.0;\n"
+ " cp.async.cg.shared.global.L2::cache_hint [%0], [%1], %2, p;\n"
+ "}\n" :: "r"(smem), "l"(glob_ptr), "n"(bytes)
+ );
+}
+
+// Async copy fence, commit all pending async copies
+
+__device__ inline void cp_async_fence()
+{
+ asm volatile("cp.async.commit_group;\n" ::);
+}
+
+// Wait until at most n async groups are still pending.
+
+template
+__device__ inline void cp_async_wait()
+{
+ asm volatile("cp.async.wait_group %0;\n" :: "n"(n));
+}
+
+// Load 16x16 matrix fragment from shared memory, directly in tensor core layout
+
+__device__ inline void ldsm4(FragA& frag_a, const void* smem_ptr)
+{
+ uint32_t* a = reinterpret_cast(&frag_a);
+ uint32_t smem = static_cast(__cvta_generic_to_shared(smem_ptr));
+ asm volatile
+ (
+ "ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0,%1,%2,%3}, [%4];\n"
+ : "=r"(a[0]), "=r"(a[1]), "=r"(a[2]), "=r"(a[3]) : "r"(smem)
+ );
+}
+
+__device__ inline uint32_t mul_lo_u32(uint32_t x, uint32_t y)
+{
+ uint32_t w;
+ asm volatile
+ (
+ "mul.lo.u32 %0, %1, %2;"
+ : "=r"(w)
+ : "r"(x), "r"(y)
+ );
+ return w;
+}
+
+__device__ inline uint32_t mul_hi_u32(uint32_t x, uint32_t y)
+{
+ uint32_t w;
+ asm volatile
+ (
+ "mul.hi.u32 %0, %1, %2;"
+ : "=r"(w)
+ : "r"(x), "r"(y)
+ );
+ return w;
+}
+
+// Memory ops
+
+__device__ __forceinline__ void stg_wt_u32(uint32_t* p, uint32_t v)
+{
+ asm volatile("st.global.wt.u32 [%0], %1;" :: "l"(p), "r"(v));
+}
+
+__device__ __forceinline__ void stg_wt_u128(uint4* p, const uint4 v)
+{
+ asm volatile ("st.global.wt.v4.u32 [%0], {%1,%2,%3,%4};"
+ :: "l"(p),
+ "r"(v.x), "r"(v.y), "r"(v.z), "r"(v.w));
+}
+
+__device__ __forceinline__ uint32_t ldg_cv_u32(const uint32_t* p)
+{
+ uint32_t v;
+ asm volatile("ld.global.cv.u32 %0, [%1];" : "=r"(v) : "l"(p));
+ return v;
+}
+
+__device__ __forceinline__ uint4 ldg_cv_u128(const uint4* p)
+{
+ uint4 v;
+ asm volatile ("ld.global.cv.v4.u32 {%0,%1,%2,%3}, [%4];"
+ : "=r"(v.x), "=r"(v.y), "=r"(v.z), "=r"(v.w)
+ : "l"(p));
+ return v;
+}
+
+__device__ __forceinline__ uint32_t ldg_acquire_sys_u32(const uint32_t* p)
+{
+ uint32_t v;
+ asm volatile("ld.global.acquire.sys.u32 %0, [%1];"
+ : "=r"(v) : "l"(p));
+ return v;
+}
+
+__device__ __forceinline__ uint64_t ldg_acquire_sys_u64(const uint64_t* p)
+{
+ uint64_t v;
+ asm volatile("ld.global.acquire.sys.u64 %0, [%1];" : "=l"(v) : "l"(p) : "memory");
+ return v;
+}
+
+__device__ __forceinline__ void stg_release_sys_u32(uint32_t* p, uint32_t v)
+{
+ asm volatile("st.global.release.sys.u32 [%0], %1;" :: "l"(p), "r"(v) : "memory");
+}
+
+__device__ __forceinline__ void stg_release_sys_u64(uint64_t* p, uint64_t v)
+{
+ asm volatile("st.global.release.sys.u64 [%0], %1;" :: "l"(p), "l"(v) : "memory");
+}
+
+// Global time in nanoseconds
+
+__device__ __forceinline__ uint64_t globaltimer_ns()
+{
+ uint64_t t;
+ asm volatile("mov.u64 %0, %%globaltimer;" : "=l"(t));
+ return t;
+}
+
+// Bitfield stuff
+
+static __forceinline__ __device__ uint32_t bfe64(uint32_t lo, uint32_t hi, int offset, int length)
+{
+ uint64_t value = (static_cast(hi) << 32) | static_cast(lo);
+ uint64_t result64;
+ asm ("bfe.u64 %0, %1, %2, %3;"
+ : "=l"(result64)
+ : "l"(value), "r"(offset), "r"(length));
+ return static_cast(result64);
+}
+
+#define FSHF_IMM(dst, lo, hi, imm) asm("shf.r.wrap.b32 %0, %1, %2, " #imm ";" : "=r"(dst) : "r"(lo), "r"(hi))
+#define BFE16_IMM(dst, src, imm) asm("bfe.u32 %0, %1, " #imm ", 16;" : "=r"(dst) : "r"(src))
\ No newline at end of file
diff --git a/gptqmodel_ext/exllamav3/quant/codebook.cuh b/gptqmodel_ext/exllamav3/quant/codebook.cuh
new file mode 100644
index 000000000..e8201937a
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/codebook.cuh
@@ -0,0 +1,146 @@
+#pragma once
+
+// Force integer MAD on sm<=86. For some reason this performs better than letting the compiler emit IMUL
+// TODO: Keep an eye on new behavior in future versions of nvcc. While this is faster on RTX 3090, it really shouldn't be.
+template
+__device__ __forceinline__
+uint32_t mul_const_u32(uint32_t x)
+{
+ #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ == 860)
+ uint32_t r;
+ asm volatile (
+ "{ .reg .u32 z,t;"
+ " mov.u32 t, %laneid;" // runtime SR
+ " sub.u32 z, t, t;" // z = 0 but data-dependent
+ " mad.lo.u32 %0, %1, %2, z;"
+ "}"
+ : "=r"(r)
+ : "r"(x), "n"(w));
+ return r;
+ #else
+ return x * w;
+ #endif
+}
+
+template
+__device__ inline half decode_3inst(uint32_t x)
+{
+ if constexpr (cb == 0)
+ {
+ x *= 89226354u;
+ x += 64248484u;
+ asm ("lop3.b32 %0, %0, 0x8fff8fff, 0x3b603b60, 0x6a;" : "+r"(x));
+ half2_uint32 xu(x);
+ return __hadd(__low2half(xu.as_half2), __high2half(xu.as_half2));
+ }
+ if constexpr (cb == 1)
+ {
+// x *= 0xCBAC1FEDu;
+ x = mul_const_u32<0xCBAC1FEDu>(x);
+
+ asm ("lop3.b32 %0, %0, 0x8fff8fff, 0x3b603b60, 0x6a;" : "+r"(x));
+ half2_uint32 xu(x);
+ return __hadd(__low2half(xu.as_half2), __high2half(xu.as_half2));
+ }
+ if constexpr (cb == 2)
+ {
+ x *= 0x83DCD12Du;
+ uint32_t sum;
+ const uint32_t acc = 0x6400u; // 0x6400 -> 1024.0 .. 0x67FF -> 2047.0
+ asm ("vabsdiff4.u32.u32.u32.add %0, %1, %2, %3;" : "=r"(sum) : "r"(x), "r"(0), "r"(acc) : );
+ const __half k_inv_h = __ushort_as_half(0x1eee); // 0.00677 = 1/147.7
+ const __half k_bias_h = __ushort_as_half(0xc931); // -10.39 = (-1024.0 - 510.0) * k_inv_h
+ half_uint16 h((uint16_t) sum);
+ return __hfma(h.as_half, k_inv_h, k_bias_h);
+ }
+}
+
+template
+__device__ inline half2 decode_3inst_2(uint32_t x0, uint32_t x1)
+{
+ if constexpr (cb == 0)
+ {
+ x0 *= 89226354u;
+ x1 *= 89226354u;
+ x0 += 64248484u;
+ x1 += 64248484u;
+ asm ("lop3.b32 %0, %0, 0x8fff8fff, 0x3b603b60, 0x6a;" : "+r"(x0));
+ asm ("lop3.b32 %0, %0, 0x8fff8fff, 0x3b603b60, 0x6a;" : "+r"(x1));
+ half2_uint32 xu0(x0);
+ half2_uint32 xu1(x1);
+ half2 d0 = __lows2half2(xu0.as_half2, xu1.as_half2);
+ half2 d1 = __highs2half2(xu0.as_half2, xu1.as_half2);
+ return __hadd2(d0, d1);
+ }
+ if constexpr (cb == 1)
+ {
+// x0 *= 0xCBAC1FEDu;
+// x1 *= 0xCBAC1FEDu;
+ x0 = mul_const_u32<0xCBAC1FEDu>(x0);
+ x1 = mul_const_u32<0xCBAC1FEDu>(x1);
+ asm ("lop3.b32 %0, %0, 0x8fff8fff, 0x3b603b60, 0x6a;" : "+r"(x0));
+ asm ("lop3.b32 %0, %0, 0x8fff8fff, 0x3b603b60, 0x6a;" : "+r"(x1));
+ half2_uint32 xu0(x0);
+ half2_uint32 xu1(x1);
+ half2 d0 = __lows2half2(xu0.as_half2, xu1.as_half2);
+ half2 d1 = __highs2half2(xu0.as_half2, xu1.as_half2);
+ return __hadd2(d0, d1);
+ }
+ if constexpr (cb == 2)
+ {
+ x0 *= 0x83DCD12Du;
+ x1 *= 0x83DCD12Du;
+ uint32_t sum0;
+ uint32_t sum1;
+ const uint32_t acc = 0x6400u; // 0x6400 -> 1024.0 .. 0x67FF -> 2047.0
+ asm ("vabsdiff4.u32.u32.u32.add %0, %1, %2, %3;" : "=r"(sum0) : "r"(x0), "r"(0), "r"(acc) : );
+ asm ("vabsdiff4.u32.u32.u32.add %0, %1, %2, %3;" : "=r"(sum1) : "r"(x1), "r"(0), "r"(acc) : );
+ half2 k_inv_h2 = __half2half2(__ushort_as_half(0x1eee)); // 0.00677 = 1/147.7
+ half2 k_bias_h2 = __half2half2(__ushort_as_half(0xc931)); // -10.39 = (-1024.0 - 510.0) * k_inv_h
+ half_uint16 h0((uint16_t) sum0);
+ half_uint16 h1((uint16_t) sum1);
+ return __hfma2(__halves2half2(h0.as_half, h1.as_half), k_inv_h2, k_bias_h2);
+ }
+}
+
+template
+__device__ inline float decode_3inst_f(uint64_t x)
+{
+ return __half2float(decode_3inst(x));
+}
+
+template
+__device__ inline float decode_3inst_f_diff(uint64_t x, float d)
+{
+ return __half2float(decode_3inst(x)) - d;
+}
+
+// "2MAD" procedural codebook, much more overhead than 3INST, slightly better distribution at 2bpw
+// Not used currently
+
+//__device__ inline half decode_2mad(uint64_t x)
+//{
+// x = x * 264435761u + 1013904223u;
+// x = ((x * 1664525u) >> 32) + x;
+// int32_t c = (int32_t) __dp4a((uint32_t) x, 0x01010101u, 0xFFFFFE02u);
+// half y = __hmul(__int2half_rn(c), __float2half_rn(0.008415));
+// return y;
+//}
+//
+//__device__ inline float decode_2mad_f(uint64_t x)
+//{
+// x = x * 264435761u + 1013904223u;
+// x = ((x * 1664525u) >> 32) + x;
+// int32_t c = (int32_t) __dp4a((uint32_t) x, 0x01010101u, 0xFFFFFE02u);
+// float y = __int2float_rn(c) * 0.008415f;
+// return y;
+//}
+//
+//__device__ inline float decode_2mad_f_diff(uint64_t x, float d)
+//{
+// x = x * 264435761u + 1013904223u;
+// x = ((x * 1664525u) >> 32) + x;
+// int32_t c = (int32_t) __dp4a((uint32_t) x, 0x01010101u, 0xFFFFFE02u);
+// float y = fma(__int2float_rn(c), 0.008415f, -d);
+// return y;
+//}
diff --git a/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_1.cu b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_1.cu
new file mode 100644
index 000000000..ca23e3db6
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_1.cu
@@ -0,0 +1,12 @@
+#include
+#include
+#include
+#include
+namespace cg = cooperative_groups;
+#include "../../util.h"
+#include "../../util.cuh"
+#include "../../ptx.cuh"
+#include "../exl3_gemm_kernel.cuh"
+#include "exl3_comp_unit_1.cuh"
+
+ALL_EXL3_KERNEL_INSTANCES(1)
\ No newline at end of file
diff --git a/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_1.cuh b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_1.cuh
new file mode 100644
index 000000000..876a1dc82
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_1.cuh
@@ -0,0 +1,3 @@
+#pragma once
+
+ALL_EXL3_KERNEL_EXTERNS(1)
\ No newline at end of file
diff --git a/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_2.cu b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_2.cu
new file mode 100644
index 000000000..5ebb29de2
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_2.cu
@@ -0,0 +1,12 @@
+#include
+#include
+#include
+#include
+namespace cg = cooperative_groups;
+#include "../../util.h"
+#include "../../util.cuh"
+#include "../../ptx.cuh"
+#include "../exl3_gemm_kernel.cuh"
+#include "exl3_comp_unit_2.cuh"
+
+ALL_EXL3_KERNEL_INSTANCES(2)
diff --git a/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_2.cuh b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_2.cuh
new file mode 100644
index 000000000..e6d356463
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_2.cuh
@@ -0,0 +1,3 @@
+#pragma once
+
+ALL_EXL3_KERNEL_EXTERNS(2)
\ No newline at end of file
diff --git a/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_3.cu b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_3.cu
new file mode 100644
index 000000000..31efeed5e
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_3.cu
@@ -0,0 +1,12 @@
+#include
+#include
+#include
+#include
+namespace cg = cooperative_groups;
+#include "../../util.h"
+#include "../../util.cuh"
+#include "../../ptx.cuh"
+#include "../exl3_gemm_kernel.cuh"
+#include "exl3_comp_unit_3.cuh"
+
+ALL_EXL3_KERNEL_INSTANCES(3)
diff --git a/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_3.cuh b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_3.cuh
new file mode 100644
index 000000000..c1236e6bb
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_3.cuh
@@ -0,0 +1,3 @@
+#pragma once
+
+ALL_EXL3_KERNEL_EXTERNS(3)
\ No newline at end of file
diff --git a/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_4.cu b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_4.cu
new file mode 100644
index 000000000..a26b866ce
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_4.cu
@@ -0,0 +1,12 @@
+#include
+#include
+#include
+#include
+namespace cg = cooperative_groups;
+#include "../../util.h"
+#include "../../util.cuh"
+#include "../../ptx.cuh"
+#include "../exl3_gemm_kernel.cuh"
+#include "exl3_comp_unit_4.cuh"
+
+ALL_EXL3_KERNEL_INSTANCES(4)
diff --git a/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_4.cuh b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_4.cuh
new file mode 100644
index 000000000..3eb77cda0
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_4.cuh
@@ -0,0 +1,3 @@
+#pragma once
+
+ALL_EXL3_KERNEL_EXTERNS(4)
\ No newline at end of file
diff --git a/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_5.cu b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_5.cu
new file mode 100644
index 000000000..42d9e76b7
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_5.cu
@@ -0,0 +1,12 @@
+#include
+#include
+#include
+#include
+namespace cg = cooperative_groups;
+#include "../../util.h"
+#include "../../util.cuh"
+#include "../../ptx.cuh"
+#include "../exl3_gemm_kernel.cuh"
+#include "exl3_comp_unit_5.cuh"
+
+ALL_EXL3_KERNEL_INSTANCES(5)
diff --git a/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_5.cuh b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_5.cuh
new file mode 100644
index 000000000..52814b523
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_5.cuh
@@ -0,0 +1,3 @@
+#pragma once
+
+ALL_EXL3_KERNEL_EXTERNS(5)
\ No newline at end of file
diff --git a/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_6.cu b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_6.cu
new file mode 100644
index 000000000..57936aec0
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_6.cu
@@ -0,0 +1,12 @@
+#include
+#include
+#include
+#include
+namespace cg = cooperative_groups;
+#include "../../util.h"
+#include "../../util.cuh"
+#include "../../ptx.cuh"
+#include "../exl3_gemm_kernel.cuh"
+#include "exl3_comp_unit_6.cuh"
+
+ALL_EXL3_KERNEL_INSTANCES(6)
diff --git a/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_6.cuh b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_6.cuh
new file mode 100644
index 000000000..98bfb931a
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_6.cuh
@@ -0,0 +1,3 @@
+#pragma once
+
+ALL_EXL3_KERNEL_EXTERNS(6)
\ No newline at end of file
diff --git a/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_7.cu b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_7.cu
new file mode 100644
index 000000000..beb2b7f26
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_7.cu
@@ -0,0 +1,12 @@
+#include
+#include
+#include
+#include
+namespace cg = cooperative_groups;
+#include "../../util.h"
+#include "../../util.cuh"
+#include "../../ptx.cuh"
+#include "../exl3_gemm_kernel.cuh"
+#include "exl3_comp_unit_7.cuh"
+
+ALL_EXL3_KERNEL_INSTANCES(7)
diff --git a/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_7.cuh b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_7.cuh
new file mode 100644
index 000000000..49d5ba8f1
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_7.cuh
@@ -0,0 +1,3 @@
+#pragma once
+
+ALL_EXL3_KERNEL_EXTERNS(7)
\ No newline at end of file
diff --git a/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_8.cu b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_8.cu
new file mode 100644
index 000000000..890d1b6a7
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_8.cu
@@ -0,0 +1,12 @@
+#include
+#include
+#include
+#include
+namespace cg = cooperative_groups;
+#include "../../util.h"
+#include "../../util.cuh"
+#include "../../ptx.cuh"
+#include "../exl3_gemm_kernel.cuh"
+#include "exl3_comp_unit_8.cuh"
+
+ALL_EXL3_KERNEL_INSTANCES(8)
diff --git a/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_8.cuh b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_8.cuh
new file mode 100644
index 000000000..951bc2ef2
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/comp_units/exl3_comp_unit_8.cuh
@@ -0,0 +1,3 @@
+#pragma once
+
+ALL_EXL3_KERNEL_EXTERNS(8)
\ No newline at end of file
diff --git a/gptqmodel_ext/exllamav3/quant/exl3_devctx.cu b/gptqmodel_ext/exllamav3/quant/exl3_devctx.cu
new file mode 100644
index 000000000..620b23c6d
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/exl3_devctx.cu
@@ -0,0 +1,86 @@
+#include
+#include
+#include
+#include
+namespace cg = cooperative_groups;
+#include "exl3_devctx.cuh"
+#include "../util.h"
+#include "../util.cuh"
+
+//DevCtx::DevCtc()
+//{
+// int num_sms[MAX_DEVICES] = {};
+// int cc[MAX_DEVICES] = {};
+// void* locks[MAX_DEVICES] = {};
+// std::mutex mtx;
+//}
+
+DevCtx& DevCtx::instance()
+{
+ static DevCtx ctx;
+ return ctx;
+}
+
+int DevCtx::get_num_sms(int device)
+{
+ std::lock_guard lock(mtx);
+ if (!num_sms[device])
+ cuda_check(cudaDeviceGetAttribute(&num_sms[device], cudaDevAttrMultiProcessorCount, device));
+ return num_sms[device];
+}
+
+int DevCtx::get_cc(int device)
+{
+ std::lock_guard lock(mtx);
+ if (!cc[device])
+ {
+ cudaDeviceProp prop;
+ cuda_check(cudaGetDeviceProperties(&prop, device));
+ if (prop.major >= 10) cc[device] = CC_BLACKWELL;
+ else if (prop.major >= 9) cc[device] = CC_HOPPER;
+ else if (prop.major >= 8 && prop.minor >= 9) cc[device] = CC_ADA;
+ else if (prop.major >= 8) cc[device] = CC_AMPERE;
+ else cc[device] = CC_OLD;
+ }
+ return cc[device];
+}
+
+void* DevCtx::get_ws(int device)
+{
+ std::lock_guard lock(mtx);
+ if (!ws[device])
+ {
+ cudaSetDevice(device);
+ cudaMalloc(&ws[device], WORKSPACE_SIZE);
+ }
+ return ws[device];
+}
+
+int* DevCtx::get_locks(int device)
+{
+ std::lock_guard lock(mtx);
+ if (!locks[device])
+ {
+ cudaSetDevice(device);
+ cudaMalloc(&locks[device], MAX_TILES_C * sizeof(int));
+ cudaMemset(locks[device], 0, MAX_TILES_C * sizeof(int));
+ }
+ return (int*) locks[device];
+}
+
+int g_get_cc(int device)
+{
+ return DevCtx::instance().get_cc(device);
+}
+
+int g_get_num_sms(int device)
+{
+ return DevCtx::instance().get_num_sms(device);
+}
+
+void prepare_ctx(int device)
+{
+ DevCtx::instance().get_num_sms(device);
+ DevCtx::instance().get_cc(device);
+ DevCtx::instance().get_locks(device);
+}
diff --git a/gptqmodel_ext/exllamav3/quant/exl3_devctx.cuh b/gptqmodel_ext/exllamav3/quant/exl3_devctx.cuh
new file mode 100644
index 000000000..ac8efaea0
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/exl3_devctx.cuh
@@ -0,0 +1,46 @@
+#pragma once
+
+#include
+#include
+
+// Max allowable output size, in tiles. Used to allocate global lock buffer per device for sync across threadblocks
+#define MAX_TILES_C (1024 * 1024)
+
+// Workspace size
+#define WORKSPACE_SIZE (4*1024*1024)
+
+// Treat hopper and blackwell as same arch for now
+#define MAX_DEVICES 16
+#define CC_OLD 1
+#define CC_AMPERE 2
+#define CC_ADA 3
+#define CC_HOPPER 4
+#define CC_BLACKWELL 4
+
+// Singleton to manage context for each device. Stores device attributes and a large-enough lock buffer per device
+class DevCtx
+{
+private:
+ int num_sms[MAX_DEVICES] = {};
+ int cc[MAX_DEVICES] = {};
+ void* locks[MAX_DEVICES] = {};
+ void* ws[MAX_DEVICES] = {};
+ std::mutex mtx;
+
+public:
+ static DevCtx& instance();
+ int get_num_sms(int device);
+ int get_cc(int device);
+ void* get_ws(int device);
+ int* get_locks(int device);
+
+private:
+ DevCtx() = default;
+ DevCtx(const DevCtx&) = delete;
+ DevCtx& operator=(const DevCtx&) = delete;
+};
+
+int g_get_cc(int device);
+int g_get_num_sms(int device);
+
+void prepare_ctx(int device);
\ No newline at end of file
diff --git a/gptqmodel_ext/exllamav3/quant/exl3_dq.cuh b/gptqmodel_ext/exllamav3/quant/exl3_dq.cuh
new file mode 100644
index 000000000..2cf103654
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/exl3_dq.cuh
@@ -0,0 +1,293 @@
+#pragma once
+
+#include "codebook.cuh"
+
+__device__ __forceinline__ uint32_t fshift(const uint32_t b, const uint32_t a, int shift)
+{
+ uint64_t merged = ((uint64_t)a << 32) | (uint64_t) b;
+ return (uint32_t)(merged >> shift);
+
+ // Conditional funnel shift is somehow no longer faster
+ // if (shift < 32) return __funnelshift_r(b, a, shift);
+ // return a >> (shift - 32);
+}
+
+template
+__device__ __forceinline__ half dq(const uint32_t* ptr, int t_offset)
+{
+ int b0 = t_offset * bits + bits - 16 + 256 * bits; // bit index, start of word0
+ int b1 = b0 + 16; // bit index, end of word0
+ int i0 = b0 / 32; // uint32 containing first bit of word0
+ int i1 = (b1 - 1) / 32; // uint32 containing last bit of word0, may be == i0
+ int s0 = (i1 + 1) * 32 - b1; // shift value to align word1 to 32-bit boundary
+
+ // Load 32 or 64 bits containing word0
+ uint32_t a = ptr[i0 % (bits * 256 / 32)];
+ uint32_t b = ptr[i1 % (bits * 256 / 32)];
+
+ // Shift into place
+ uint32_t w0 = __funnelshift_r(b, a, s0) & 0xffff;
+ return decode_3inst(w0);
+}
+
+template
+__device__ __forceinline__ half2 dq2(const uint32_t* ptr, int t_offset)
+{
+ int b0 = t_offset * bits + bits - 16 + 256 * bits; // bit index, start of word0
+ int b1 = b0 + 16; // bit index, end of word0
+ int i0 = b0 / 32; // uint32 containing first bit of word0
+ int i1 = (b1 - 1) / 32; // uint32 containing last bit of word0, may be == i0
+ int s0 = (i1 + 1) * 32 - b1; // shift value to align word1 to 32-bit boundary
+
+ // Load 32 or 64 bits containing word0
+ uint32_t a = ptr[i0 % (bits * 256 / 32)];
+ uint32_t b = ptr[i1 % (bits * 256 / 32)];
+
+ // Shift into place
+ uint32_t w1 = __funnelshift_r(b, a, s0) & 0xffff;
+ uint32_t w0 = __funnelshift_r(b, a, s0 + bits) & 0xffff;
+ return decode_3inst_2(w0, w1);
+}
+
+template
+__device__ __forceinline__ void dq4(const uint32_t* ptr, int t_offset, FragB& frag)
+{
+ int b0 = (t_offset + 257) * bits - 16; // start of first word
+ int b1 = b0 + 3 * bits; // start of last word
+ int b2 = b1 + 16; // end of last word
+ int i0 = b0 / 32; // uint32 containing first bit of first word
+ int i2 = (b2 - 1) / 32; // uint32 containing last bit of last word, may be == i0
+ int s2 = (i2 + 1) * 32 - b2; // shift value to align last word to 32-bit boundary
+
+ uint32_t a = ptr[i0 % (bits * 256 / 32)];
+ uint32_t b = ptr[i2 % (bits * 256 / 32)];
+ uint32_t w3 = fshift(b, a, s2) & 0xffff;
+ uint32_t w2 = fshift(b, a, s2 + bits) & 0xffff;
+ uint32_t w1 = fshift(b, a, s2 + bits * 2) & 0xffff;
+ uint32_t w0 = fshift(b, a, s2 + bits * 3) & 0xffff;
+ half2 d0d1 = decode_3inst_2(w0, w1);
+ half2 d2d3 = decode_3inst_2(w2, w3);
+ frag[0] = d0d1;
+ frag[1] = d2d3;
+}
+
+template
+__device__ __forceinline__ void dq2x2(const uint32_t* ptr, int t_offset, FragB& frag)
+{
+ #pragma unroll
+ for (int i = 0; i < 2; ++i)
+ {
+ int b0 = (t_offset + 2 * i + 257) * bits - 16; // start of first word
+ int b1 = b0 + 1 * bits; // start of last word
+ int b2 = b1 + 16; // end of last word
+ int i0 = b0 / 32; // uint32 containing first bit of first word
+ int i2 = (b2 - 1) / 32; // uint32 containing last bit of last word, may be == i0
+ int s2 = (i2 + 1) * 32 - b2; // shift value to align last word to 32-bit boundary
+
+ uint32_t a = ptr[i0 % (bits * 256 / 32)];
+ uint32_t b = ptr[i2 % (bits * 256 / 32)];
+ uint32_t w1 = fshift(b, a, s2) & 0xffff;
+ uint32_t w0 = fshift(b, a, s2 + bits) & 0xffff;
+ half2 d0d1 = decode_3inst_2(w0, w1);
+ frag[i] = d0d1;
+ }
+}
+
+template
+__device__ __forceinline__ void dq8(const uint32_t* ptr, int t_offset, FragB& frag0, FragB& frag1)
+{
+ int b1 = (t_offset + 257) * bits; // end of first word
+ int b0 = b1 - 16; // start of first word
+ int b2 = b1 + bits * 7;
+ int i0 = b0 / 32; // uint32 containing first bit of word0
+ int i2 = (b2 - 1) / 32; // uint32 containing last bit of word0, may be == i0
+ int s2 = (i2 + 1) * 32 - b2; // shift value to align last word to 32-bit boundary
+
+ uint32_t a = ptr[i0 % (bits * 256 / 32)];
+ uint32_t b = ptr[i2 % (bits * 256 / 32)];
+ uint32_t w0, w1, w2, w3, w4, w5, w6, w7;
+ if constexpr (align == 1)
+ {
+ w7 = fshift(b, a, s2);
+ w6 = fshift(b, a, s2 + bits);
+ w5 = fshift(b, a, s2 + bits * 2);
+ w4 = fshift(b, a, s2 + bits * 3);
+ w3 = fshift(b, a, s2 + bits * 4);
+ w2 = fshift(b, a, s2 + bits * 5);
+ w1 = fshift(b, a, s2 + bits * 6);
+ w0 = fshift(b, a, s2 + bits * 7);
+ }
+ if constexpr (align == 2)
+ {
+ w7 = fshift(b, a, s2);
+ w6 = w7 >> bits;
+ w5 = fshift(b, a, s2 + bits * 2);
+ w4 = w5 >> bits;
+ w3 = fshift(b, a, s2 + bits * 4);
+ w2 = w3 >> bits;
+ w1 = fshift(b, a, s2 + bits * 6);
+ w0 = w1 >> bits;
+ }
+ if constexpr (align == 4)
+ {
+ w7 = fshift(b, a, s2);
+ w6 = w7 >> bits;
+ w5 = w6 >> bits;
+ w4 = w5 >> bits;
+ w3 = fshift(b, a, s2 + bits * 4);
+ w2 = w3 >> bits;
+ w1 = w2 >> bits;
+ w0 = w1 >> bits;
+ }
+ if constexpr (align == 8)
+ {
+ w7 = fshift(b, a, s2);
+ w6 = w7 >> bits;
+ w5 = w6 >> bits;
+ w4 = w5 >> bits;
+ w3 = w4 >> bits;
+ w2 = w3 >> bits;
+ w1 = w2 >> bits;
+ w0 = w1 >> bits;
+ }
+ half2 d0d1 = decode_3inst_2(w0 & 0xffff, w1 & 0xffff);
+ half2 d2d3 = decode_3inst_2(w2 & 0xffff, w3 & 0xffff);
+ half2 d4d5 = decode_3inst_2(w4 & 0xffff, w5 & 0xffff);
+ half2 d6d7 = decode_3inst_2(w6 & 0xffff, w7 & 0xffff);
+ frag0[0] = d0d1;
+ frag0[1] = d2d3;
+ frag1[0] = d4d5;
+ frag1[1] = d6d7;
+}
+
+template
+__device__ __forceinline__ void dq8_aligned_4bits(const uint32_t* ptr, int t_offset, FragB& frag0, FragB& frag1)
+{
+ uint32_t i0, i1, a, b, s, w0, w1, w2, w3, w4, w5, w6, w7;
+ i1 = t_offset >> 3;
+ i0 = (i1 + 31) & 31;
+ a = ptr[i0];
+ b = ptr[i1];
+ FSHF_IMM(s, b, a, 20);
+ w7 = b & 0xffff;
+ BFE16_IMM(w6, b, 4);
+ BFE16_IMM(w5, b, 8);
+ BFE16_IMM(w4, b, 12);
+ BFE16_IMM(w3, b, 16);
+ w2 = s & 0xffff;
+ BFE16_IMM(w1, s, 4);
+ BFE16_IMM(w0, s, 8);
+ frag0[0] = decode_3inst_2(w0, w1);
+ frag0[1] = decode_3inst_2(w2, w3);
+ frag1[0] = decode_3inst_2(w4, w5);
+ frag1[1] = decode_3inst_2(w6, w7);
+}
+
+template
+__device__ __forceinline__ void dq8_aligned_2bits(const uint32_t* ptr, int t_offset, FragB& frag0, FragB& frag1)
+{
+ uint32_t i0, i1, a, b, w0, w1, w2, w3, w4, w5, w6, w7;
+ i1 = t_offset >> 4;
+ i0 = (i1 + 15) & 15;
+ a = ptr[i0];
+ b = ptr[i1];
+ b = fshift(b, a, ((~t_offset) & 8) << 1);
+ w7 = b & 0xffff;
+ BFE16_IMM(w6, b, 2);
+ BFE16_IMM(w5, b, 4);
+ BFE16_IMM(w4, b, 6);
+ BFE16_IMM(w3, b, 8);
+ BFE16_IMM(w2, b, 10);
+ BFE16_IMM(w1, b, 12);
+ BFE16_IMM(w0, b, 14);
+ frag0[0] = decode_3inst_2(w0, w1);
+ frag0[1] = decode_3inst_2(w2, w3);
+ frag1[0] = decode_3inst_2(w4, w5);
+ frag1[1] = decode_3inst_2(w6, w7);
+}
+
+template
+__device__ __forceinline__ void dq8_aligned_1bit(const uint32_t* ptr, int t_offset, FragB& frag0, FragB& frag1)
+{
+ uint32_t i0, i1, a, b, w0, w1, w2, w3, w4, w5, w6, w7;
+ i1 = t_offset >> 5;
+ i0 = (i1 + 7) & 7;
+ a = ptr[i0];
+ b = ptr[i1];
+ b = fshift(b, a, ((~t_offset) & 24));
+ w7 = b & 0xffff;
+ BFE16_IMM(w6, b, 1);
+ BFE16_IMM(w5, b, 2);
+ BFE16_IMM(w4, b, 3);
+ BFE16_IMM(w3, b, 4);
+ BFE16_IMM(w2, b, 5);
+ BFE16_IMM(w1, b, 6);
+ BFE16_IMM(w0, b, 7);
+ frag0[0] = decode_3inst_2(w0, w1);
+ frag0[1] = decode_3inst_2(w2, w3);
+ frag1[0] = decode_3inst_2(w4, w5);
+ frag1[1] = decode_3inst_2(w6, w7);
+}
+
+
+template
+__device__ __forceinline__ void dq8_aligned_4bits_bfe64(const uint32_t* ptr, int t_offset, FragB& frag0, FragB& frag1)
+{
+ int i1 = t_offset / 8;
+ int i0 = (i1 + 31) % 32;
+ uint32_t a = ptr[i0];
+ uint32_t b = ptr[i1];
+ uint32_t w7 = bfe64(b, a, 0, 16);
+ uint32_t w6 = bfe64(b, a, 4, 16);
+ uint32_t w5 = bfe64(b, a, 8, 16);
+ uint32_t w4 = bfe64(b, a, 12, 16);
+ uint32_t w3 = bfe64(b, a, 16, 16);
+ uint32_t w2 = bfe64(b, a, 20, 16);
+ uint32_t w1 = bfe64(b, a, 24, 16);
+ uint32_t w0 = bfe64(b, a, 28, 16);
+ frag0[0] = decode_3inst_2(w0, w1);
+ frag0[1] = decode_3inst_2(w2, w3);
+ frag1[0] = decode_3inst_2(w4, w5);
+ frag1[1] = decode_3inst_2(w6, w7);
+}
+
+template
+__device__ __forceinline__ void dq_dispatch(const uint32_t* ptr, int idx, FragB& frag0, FragB& frag1)
+{
+ if constexpr (bits == 1)
+ {
+ dq8_aligned_1bit(ptr, idx, frag0, frag1);
+ }
+ else if constexpr (bits == 2)
+ {
+ dq8_aligned_2bits(ptr, idx, frag0, frag1);
+ }
+ else if constexpr (bits == 3)
+ {
+ dq8(ptr, idx, frag0, frag1);
+ }
+ else if constexpr (bits == 4)
+ {
+ dq8_aligned_4bits(ptr, idx, frag0, frag1);
+ }
+ else if constexpr (bits == 5)
+ {
+ dq4(ptr, idx, frag0);
+ dq4(ptr, idx + 4, frag1);
+ }
+ else if constexpr (bits == 6)
+ {
+ dq4(ptr, idx, frag0);
+ dq4(ptr, idx + 4, frag1);
+ }
+ else if constexpr (bits == 7)
+ {
+ dq2x2(ptr, idx, frag0);
+ dq2x2(ptr, idx + 4, frag1);
+ }
+ else if constexpr (bits == 8)
+ {
+ dq4(ptr, idx, frag0);
+ dq4(ptr, idx + 4, frag1);
+ }
+}
\ No newline at end of file
diff --git a/gptqmodel_ext/exllamav3/quant/exl3_gemm.cu b/gptqmodel_ext/exllamav3/quant/exl3_gemm.cu
new file mode 100644
index 000000000..67b22b0c5
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/exl3_gemm.cu
@@ -0,0 +1,141 @@
+#include
+#include "exl3_gemm.cuh"
+
+#include
+#include
+#include "../util.h"
+#include "../util.cuh"
+#include "exl3_kernel_map.cuh"
+#include "exl3_devctx.cuh"
+#include
+
+constexpr int EXL3_GEMM_SMEM_MAX = 90 * 1024;
+
+/*
+EXL3 matmul, A @ B -> C
+
+- A: row-major A tensor, shape (m, k), dtype float16, contiguous
+- B: EXL3-quantized B tensor, shape (k//16, n//16, 16*K), dtype uint16
+- C: empty row-major C tensor, shape (m, n), dtype float16 or float32, contiguous. Does not need to be zero-initialized
+- suh: optional, packed input scales/flips, shape (k//16), dtype float16
+- A_had: required if suh given, may be reference to A, temporary storage for input transform, size and dtype as A
+- svh: optional, packed output scales/flips, shape (n//16), dtype float16
+
+limitations:
+- k % 16 == 0
+- n % 128 == 0
+*/
+
+std::set kernel_attr_set[MAX_DEVICES] = {};
+
+int exl3_gemm
+(
+ const at::Tensor& A,
+ const at::Tensor& B,
+ at::Tensor& C,
+ const c10::optional& suh,
+ const c10::optional& A_had,
+ const c10::optional& svh,
+ int force_shape_idx,
+ bool mcg,
+ bool mul1,
+ int force_num_sms
+)
+{
+ const at::cuda::OptionalCUDAGuard device_guard(A.device());
+ cudaStream_t stream = at::cuda::getCurrentCUDAStream().stream();
+
+ TORCH_CHECK_DIM(B, 3);
+ TORCH_CHECK_SHAPES(A, -1, B, 0, 16);
+ TORCH_CHECK_SHAPES(C, -1, B, 1, 16);
+ // TORCH_CHECK_SHAPES(A, 0, C, 0, 1);
+ TORCH_CHECK_DTYPE(A, kHalf);
+ TORCH_CHECK_DTYPE(B, kShort);
+ bool c_fp32 = C.dtype() == at::kFloat;
+ if (!c_fp32) TORCH_CHECK_DTYPE(C, kHalf);
+
+ // Get SU, optionally
+ const half* suh_ptr = (const half*) OPTPTR(suh);
+ half* A_had_ptr = nullptr;
+ if (suh_ptr)
+ {
+ // TORCH_CHECK_SHAPES(suh.value(), 0, A, 1, 1);
+ A_had_ptr = (half*) OPTPTR(A_had);
+ // TORCH_CHECK(A_had_ptr, "Must supply A_had with suh");
+ // TORCH_CHECK_SHAPES_FULL(A_had.value(), A);
+ }
+
+ // Get SV, optionally
+ const half* svh_ptr = (const half*) OPTPTR(svh);
+ // if (svh_ptr)
+ // TORCH_CHECK_SHAPES(svh.value(), 0, B, 1, 16);
+
+ // Device properties
+ int device;
+ cudaGetDevice(&device);
+ int num_sms = force_num_sms ? force_num_sms : DevCtx::instance().get_num_sms(device);
+ int cc = DevCtx::instance().get_cc(device);
+ int* locks = DevCtx::instance().get_locks(device);
+
+ // Dispatch
+ int K = B.size(2) / 16;
+ const half* A_ptr = (const half*) A.data_ptr();
+ const uint16_t* B_ptr = (const uint16_t*) B.data_ptr();
+ void* C_ptr = (void*) C.data_ptr();
+
+ int size_m = 1;
+ int dim = A.dim();
+ for (int d = 0; d < dim - 1; ++d) size_m *= A.size(d);
+ int size_k = A.size(-1);
+ int size_n = B.size(1) * 16;
+
+ // Select kernel
+ TORCH_CHECK(!(mcg && mul1), "Specified both mcg and mul1")
+ int cb = 0;
+ if (mcg) cb = 1;
+ if (mul1) cb = 2;
+
+ int block_dim;
+ int shape_idx;
+ fp_exl3_gemm_kernel kernel;
+
+ TResult* tr = select_exl3_gemm_kernel_tuned(cc, size_k, size_n, K, c_fp32, force_shape_idx, force_num_sms, cb);
+ if (!tr) return 0;
+ num_sms = MIN(num_sms, tr->num_sms);
+ kernel = tr->kernel;
+ block_dim = tr->block_dim;
+ shape_idx = tr->shape_idx;
+
+ // Launch
+ if (kernel_attr_set[device].find((void*) kernel) == kernel_attr_set[device].end())
+ {
+ cudaFuncSetAttribute(kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, EXL3_GEMM_SMEM_MAX);
+ kernel_attr_set[device].insert((void*) kernel);
+ cuda_check(cudaPeekAtLastError());
+ }
+ void* kernelArgs[] =
+ {
+ (void*)& A_ptr,
+ (void*)& B_ptr,
+ (void*)& C_ptr,
+ (void*)& size_m,
+ (void*)& size_k,
+ (void*)& size_n,
+ (void*)& locks,
+ (void*)& suh_ptr,
+ (void*)& A_had_ptr,
+ (void*)& svh_ptr
+ };
+ cudaLaunchCooperativeKernel
+ (
+ (void*) kernel,
+ num_sms,
+ block_dim,
+ kernelArgs,
+ EXL3_GEMM_SMEM_MAX,
+ stream
+ );
+
+ cuda_check(cudaPeekAtLastError());
+ return shape_idx;
+}
diff --git a/gptqmodel_ext/exllamav3/quant/exl3_gemm.cuh b/gptqmodel_ext/exllamav3/quant/exl3_gemm.cuh
new file mode 100644
index 000000000..3cde308eb
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/exl3_gemm.cuh
@@ -0,0 +1,17 @@
+#pragma once
+
+#include
+
+int exl3_gemm
+(
+ const at::Tensor& A,
+ const at::Tensor& B,
+ at::Tensor& C,
+ const c10::optional& suh,
+ const c10::optional& A_had,
+ const c10::optional& svh,
+ int force_shape_idx,
+ bool mcg,
+ bool mul1,
+ int force_num_sms
+);
diff --git a/gptqmodel_ext/exllamav3/quant/exl3_gemm_inner.cuh b/gptqmodel_ext/exllamav3/quant/exl3_gemm_inner.cuh
new file mode 100644
index 000000000..74cf66461
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/exl3_gemm_inner.cuh
@@ -0,0 +1,610 @@
+#pragma once
+
+#include "../ptx.cuh"
+
+// Constants
+#define EXL3_GEMM_BASE_THREADS 256
+#define SMEM_MAX (90 * 1024) // max shared memory on compute capability 8.6
+
+#include "exl3_dq.cuh"
+
+template
+inline __device__
+void exl3_gemm_kernel_inner
+(
+ const half* __restrict__ A,
+ const uint16_t* __restrict__ B,
+ void* __restrict__ C,
+ const int size_m,
+ const int size_k,
+ const int size_n,
+ int* __restrict__ locks
+)
+{
+ const int TILEBLOCKS_M = TILESIZE_M / 16;
+ const int TILEBLOCKS_K = TILESIZE_K / 16;
+ const int TILEBLOCKS_N = TILESIZE_N / 16;
+ const int FRAGS_M = TILEBLOCKS_M;
+ const int FRAGS_N_PER_WARP = 2 * TILEBLOCKS_N / (EXL3_GEMM_BASE_THREADS / 32);
+
+ const int sh_a_stage_size = TILESIZE_M * TILESIZE_K; // in halfs
+ const int sh_b_stage_size = TILEBLOCKS_K * TILEBLOCKS_N * 256 / 16 * bits; // in uint16s
+ const int sh_c_size = 4 * EXL3_GEMM_BASE_THREADS; // in floats
+
+ // Sanity checks
+ static_assert(EXL3_GEMM_BASE_THREADS == 256);
+ static_assert(TILESIZE_M % 16 == 0, "Invalid kernel params");
+ static_assert(TILESIZE_K % 16 == 0, "Invalid kernel params");
+ static_assert(TILESIZE_N % 128 == 0, "Invalid kernel params");
+ static_assert
+ (
+ SMEM_MAX >= SH_STAGES * (2 * sh_a_stage_size + 2 * sh_b_stage_size) + 4 * sh_c_size,
+ "Invalid kernel params (insufficient shared memory for shape)"
+ );
+
+ // Shared memory
+ extern __shared__ half shared[];
+ half* sh_a = shared;
+ uint16_t* sh_b = (uint16_t*) (sh_a + SH_STAGES * sh_a_stage_size);
+ float* sh_c = (float*) (sh_b + sh_b_stage_size * SH_STAGES);
+
+ // Thread index
+ int t = threadIdx.x % EXL3_GEMM_BASE_THREADS;
+ int sub_k = threadIdx.x / EXL3_GEMM_BASE_THREADS;
+ int warp_id = t / 32;
+ int lane_id = t % 32;
+
+ // Dimensions
+ //int tiles_m = CEIL_DIVIDE(size_m, TILESIZE_M);
+ int tiles_k = size_k / TILESIZE_K;
+ int tiles_n = size_n / TILESIZE_N;
+ //int blocks_m = 1;
+ //int blocks_k = tiles_k * TILEBLOCKS_K;
+ int blocks_n = tiles_n * TILEBLOCKS_N;
+
+ // Start and end index of current slice, must span at least one tile
+ int num_slices = gridDim.x;
+ int slice_beg = tiles_k * tiles_n * blockIdx.x / num_slices;
+ int slice_end = tiles_k * tiles_n * (blockIdx.x + 1) / num_slices;
+ int slice_len = slice_end - slice_beg;
+ if (slice_len < 1) return;
+
+ auto index_m = [&] (int slice_i) { return 0; }; //blockIdx.y; };
+ auto index_k = [&] (int slice_i) { return (slice_i % tiles_k); };
+ auto index_n = [&] (int slice_i) { return (slice_i / tiles_k); };
+
+ // Batch dimension
+ int slice_m = index_m(slice_beg);
+ int max_m = MIN(size_m - slice_m * TILESIZE_M, TILESIZE_M);
+
+ // Pipe 0, global A, B tile and shared A, B tile
+ int slice0_k = index_k(slice_beg);
+ int slice0_n = index_n(slice_beg);
+ int slice0_iters = slice_len;
+
+ int gl_a_stride_m = TILESIZE_M * size_k;
+ const int gl_a_stride_k = TILESIZE_K;
+ const int sh0_a_stride_m = TILESIZE_M * TILESIZE_K;
+ const half* gl_a_ptr = A + slice_m * gl_a_stride_m + slice0_k * gl_a_stride_k;
+ half* sh0_a_ptr = sh_a + (slice0_iters % SH_STAGES) * sh_a_stage_size;
+
+ const int load_a_iters = CEIL_DIVIDE(sh0_a_stride_m / 8, EXL3_GEMM_BASE_THREADS);
+ bool pred_a_gl[load_a_iters];
+ int load_a_gl[load_a_iters];
+ for (int i = 0; i < load_a_iters; ++i)
+ {
+ int k = (i * EXL3_GEMM_BASE_THREADS + t) % (gl_a_stride_k / 8);
+ int m = (i * EXL3_GEMM_BASE_THREADS + t) / (gl_a_stride_k / 8);
+ load_a_gl[i] = m * size_k / 8 + k;
+ pred_a_gl[i] = m < max_m;
+ }
+
+ int gl_b_stride_k = blocks_n * TILEBLOCKS_K * 256 / 16 * bits;
+ const int gl_b_stride_n = TILEBLOCKS_N * 256 / 16 * bits;
+ const int sh0_b_stride_k = TILEBLOCKS_K * TILEBLOCKS_N * 256 / 16 * bits;
+ const uint16_t* gl_b_ptr = B + slice0_k * gl_b_stride_k + slice0_n * gl_b_stride_n;
+ uint16_t* sh0_b_ptr = sh_b + (slice0_iters % SH_STAGES) * sh_b_stage_size;
+
+ const int load_b_iters = CEIL_DIVIDE(sh0_b_stride_k / 8, EXL3_GEMM_BASE_THREADS);
+ bool pred_b_gl[load_b_iters];
+ int load_b_gl[load_b_iters];
+ for (int i = 0; i < load_b_iters; ++i)
+ {
+ int n = (i * EXL3_GEMM_BASE_THREADS + t) % (gl_b_stride_n / 8);
+ int k = (i * EXL3_GEMM_BASE_THREADS + t) / (gl_b_stride_n / 8);
+ load_b_gl[i] = k * blocks_n * 256 / 16 * bits / 8 * k + n;
+ pred_b_gl[i] = i * EXL3_GEMM_BASE_THREADS + t < sh0_b_stride_k / 8;
+ }
+
+ auto advance0 = [&] ()
+ {
+ slice0_k++;
+ slice0_iters--;
+
+ int stage = slice0_iters % SH_STAGES;
+ sh0_a_ptr = sh_a + stage * sh_a_stage_size;
+ sh0_b_ptr = sh_b + stage * sh_b_stage_size;
+
+ if (slice0_k >= tiles_k)
+ {
+ slice0_k = 0;
+ slice0_n++;
+ gl_a_ptr = A + slice_m * gl_a_stride_m + slice0_k * gl_a_stride_k;
+ gl_b_ptr = B + slice0_k * gl_b_stride_k + slice0_n * gl_b_stride_n;
+ }
+ else
+ {
+ gl_a_ptr += gl_a_stride_k;
+ gl_b_ptr += gl_b_stride_k;
+ }
+ };
+
+ // Pipe 1, shared A, B tile and registers
+ int slice1_k = slice0_k;
+ int slice1_n = slice0_n;
+ int slice1_iters = slice0_iters;
+
+ half* sh1_a_ptr = sh_a + (slice1_iters % SH_STAGES) * sh_a_stage_size;
+ uint16_t* sh1_b_ptr = sh_b + (slice1_iters % SH_STAGES) * sh_b_stage_size;
+
+ auto advance1 = [&] ()
+ {
+ slice1_k++;
+ slice1_iters--;
+
+ int stage = slice1_iters % SH_STAGES;
+ sh1_a_ptr = sh_a + stage * sh_a_stage_size;
+ sh1_b_ptr = sh_b + stage * sh_b_stage_size;
+
+ if (slice1_k >= tiles_k)
+ {
+ slice1_k = 0;
+ slice1_n++;
+ }
+ };
+
+ // Pipe 2
+ int slice2_k = slice0_k;
+ int slice2_k0 = slice0_k;
+ int slice2_n = slice0_n;
+ int slice2_iters = slice0_iters;
+
+ int gl_c_stride_n = TILESIZE_N;
+ int gl_c_stride_m = TILESIZE_M * size_n;
+
+ half* gl_c_ptr_16 = ((half*) C) + slice_m * gl_c_stride_m + slice2_n * gl_c_stride_n;
+ float* gl_c_ptr_32 = ((float*) C) + slice_m * gl_c_stride_m + slice2_n * gl_c_stride_n;
+
+ register FragA frag_a[FRAG_STAGES][FRAGS_M];
+ register FragB frag_b[FRAG_STAGES][FRAGS_N_PER_WARP];
+ register FragC frag_c[FRAGS_M][FRAGS_N_PER_WARP];
+
+ auto advance2 = [&] ()
+ {
+ slice2_k++;
+ slice2_iters--;
+
+ if (slice2_k >= tiles_k)
+ {
+ slice2_k = 0;
+ slice2_k0 = 0;
+ slice2_n++;
+ if constexpr (c_fp32)
+ gl_c_ptr_32 += gl_c_stride_n;
+ else
+ gl_c_ptr_16 += gl_c_stride_n;
+ }
+ };
+
+ // Schedule load of the next A, B tiles to shared memory and advance the pipeline
+ auto async_load_gl = [&] ()
+ {
+ if (sub_k)
+ {
+ cp_async_fence();
+ return;
+ }
+
+ if (slice0_iters)
+ {
+ // Copy tile from row-major A matrix
+ {
+ const int4* gl = (const int4*) gl_a_ptr;
+ int4* sh = (int4*) sh0_a_ptr;
+ #pragma unroll
+ for (int i = 0; i < load_a_iters; ++i)
+ {
+ // TODO: Rearrange into ldmatrix friendly layout while loading?
+ // cp_async_pred(sh + EXL3_GEMM_BASE_THREADS * i + t, gl + load_a_gl[i], pred_a_gl[i]);
+ if (pred_a_gl[i]) cp_async(sh + EXL3_GEMM_BASE_THREADS * i + t, gl + load_a_gl[i]);
+ }
+ }
+
+ // Copy tile of 256-element blocks from quantized B matrix
+ {
+ const int4* gl = (const int4*) gl_b_ptr;
+ int4* sh = (int4*) sh0_b_ptr;
+ #pragma unroll
+ for (int i = 0; i < load_b_iters; ++i)
+ {
+ // cp_async_pred(sh + EXL3_GEMM_BASE_THREADS * i + t, gl + load_b_gl[i], pred_b_gl[i]);
+ if (pred_b_gl[i]) cp_async(sh + EXL3_GEMM_BASE_THREADS * i + t, gl + load_b_gl[i]);
+ }
+ }
+ advance0();
+ }
+
+ // Sync and advance
+ cp_async_fence();
+ };
+
+ // Load fragments
+ // Ref. for fragment layout:
+ // https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#matrix-fragments-for-mma-m16n8k16-with-floating-point-type
+ auto load_frags = [&] (int buf)
+ {
+ if (!slice1_iters) return;
+
+ // A fragments
+ {
+ // TODO: Resolve bank conflicts
+ int r = (lane_id % 8) + 8 * ((lane_id / 8) % 2);
+ int c = lane_id / 16;
+ int4* sha = (int4*) sh1_a_ptr + r * TILESIZE_K / 8 + c;
+ #pragma unroll
+ for (int m = 0; m < TILEBLOCKS_M; ++m)
+ ldsm4(frag_a[buf][m], sha + (m * 16) * TILESIZE_K / 8 + sub_k * 16 / 8);
+ }
+
+ // B fragments
+ #pragma unroll
+ for (int n2 = 0; n2 < FRAGS_N_PER_WARP; n2 += 2)
+ {
+ int sub_n2 = warp_id * FRAGS_N_PER_WARP / 2 + n2 / 2;
+ const uint32_t* shb = (const uint32_t*) (sh1_b_ptr + (sub_k * TILEBLOCKS_N + sub_n2) * 256 / 16 * bits);
+
+ dq_dispatch(shb, lane_id << 3, frag_b[buf][n2], frag_b[buf][n2 + 1]);
+ }
+
+ __syncthreads();
+ advance1();
+ };
+
+ // Clear C fragments
+ auto clear_frag_c = [&] ()
+ {
+ #pragma unroll
+ for (int m = 0; m < FRAGS_M; ++m)
+ #pragma unroll
+ for (int n = 0; n < FRAGS_N_PER_WARP; ++n)
+ frag_c[m][n] = {};
+ };
+
+ // Threadblock reduction
+ auto threadblock_reduce = [&] ()
+ {
+ auto store = [&] (int i, int m, int n)
+ {
+ // TODO: Shuffle to avoid bank conflicts here? Doesn't seem to be a bottleneck
+ if (sub_k == i)
+ {
+ float* sh_red = sh_c + (FRAGS_N_PER_WARP * 4) * t;
+ if (size_m <= 8)
+ {
+ #pragma unroll
+ for (int i = 0; i < 2; ++i) *sh_red++ = frag_c[m][n][i];
+ }
+ else
+ {
+ #pragma unroll
+ for (int i = 0; i < 4; ++i) *sh_red++ = frag_c[m][n][i];
+ }
+ }
+ __syncthreads();
+ };
+
+ auto add = [&] (int i, int m, int n)
+ {
+ if (sub_k == i)
+ {
+ float* sh_red = sh_c + (FRAGS_N_PER_WARP * 4) * t;
+ if (size_m <= 8)
+ {
+ #pragma unroll
+ for (int i = 0; i < 2; ++i) frag_c[m][n][i] += *sh_red++;
+ }
+ else
+ {
+ #pragma unroll
+ for (int i = 0; i < 4; ++i) frag_c[m][n][i] += *sh_red++;
+ }
+ }
+ __syncthreads();
+ };
+
+ #pragma unroll
+ for (int m = 0; m < FRAGS_M; ++m)
+ {
+ #pragma unroll
+ for (int n = 0; n < FRAGS_N_PER_WARP; ++n)
+ {
+ if constexpr (TILEBLOCKS_K == 2)
+ {
+ store(1, m, n);
+ add(0, m, n);
+ }
+ if constexpr (TILEBLOCKS_K == 3)
+ {
+ store(1, m, n);
+ add(0, m, n);
+ store(2, m, n);
+ add(0, m, n);
+ }
+ if constexpr (TILEBLOCKS_K == 4)
+ {
+ store(3, m, n);
+ add(2, m, n);
+ store(1, m, n);
+ add(0, m, n);
+ store(2, m, n);
+ add(0, m, n);
+ }
+ }
+ }
+ };
+
+ // Output reduction
+ auto reduce = [&] ()
+ {
+ // First reduce all partial sums along k for the current slice
+ threadblock_reduce();
+
+ // Process (partial) slices within column in reverse order so the threadblock doing the bottom slice is
+ // free to proceed to the next column right away
+ int lock_i = tiles_k - slice2_k - 1;
+ int lock_d = slice2_k - slice2_k0 + 1;
+ int* lock = &locks[slice_m * blocks_n + slice2_n];
+
+ barrier_acquire(lock, lock_i);
+
+ bool first = lock_i == 0;
+ bool last = lock_i + lock_d == tiles_k;
+
+ int n0 = warp_id * FRAGS_N_PER_WARP;
+
+ // Second and subsequent threadblocks in column read back the intermediate sum from global memory
+ if (!sub_k && !first)
+ {
+ #pragma unroll
+ for (int n = 0; n < FRAGS_N_PER_WARP; ++n)
+ {
+ #pragma unroll
+ for (int m = 0; m < FRAGS_M; ++m)
+ {
+ int r0 = lane_id / 4 + 16 * m;
+ int r1 = r0 + 8;
+ int c = (lane_id % 4) * 2;
+ if (r0 < max_m)
+ {
+ if constexpr (c_fp32)
+ {
+ float* c_ptr = gl_c_ptr_32 + r0 * size_n + (n0 + n) * 8 + c;
+ frag_c[m][n][0] += *c_ptr++;
+ frag_c[m][n][1] += *c_ptr++;
+ }
+ else
+ {
+ half2* c_ptr = (half2*) (gl_c_ptr_16 + r0 * size_n + (n0 + n) * 8 + c);
+ float2 interm = __half22float2(*c_ptr);
+ frag_c[m][n][0] += interm.x;
+ frag_c[m][n][1] += interm.y;
+ }
+ }
+ if (r1 < max_m)
+ {
+ if constexpr (c_fp32)
+ {
+ float* c_ptr = gl_c_ptr_32 + r1 * size_n + (n0 + n) * 8 + c;
+ frag_c[m][n][2] += *c_ptr++;
+ frag_c[m][n][3] += *c_ptr++;
+ }
+ else
+ {
+ half2* c_ptr = (half2*) (gl_c_ptr_16 + r1 * size_n + (n0 + n) * 8 + c);
+ float2 interm = __half22float2(*c_ptr);
+ frag_c[m][n][2] += interm.x;
+ frag_c[m][n][3] += interm.y;
+ }
+ }
+ }
+ }
+ }
+
+ // All but last threadblock in column write the intermediate result to global memory
+ if (!sub_k && !last)
+ {
+ #pragma unroll
+ for (int n = 0; n < FRAGS_N_PER_WARP; ++n)
+ {
+ #pragma unroll
+ for (int m = 0; m < FRAGS_M; ++m)
+ {
+ int r0 = lane_id / 4 + 16 * m;
+ int r1 = r0 + 8;
+ int c = (lane_id % 4) * 2;
+ if (r0 < max_m)
+ {
+ if constexpr (c_fp32)
+ {
+ float* c_ptr = gl_c_ptr_32 + r0 * size_n + (n0 + n) * 8 + c;
+ *c_ptr++ = frag_c[m][n][0];
+ *c_ptr++ = frag_c[m][n][1];
+ }
+ else
+ {
+ half2* c_ptr = (half2*) (gl_c_ptr_16 + r0 * size_n + (n0 + n) * 8 + c);
+ half2 sum = __floats2half2_rn(frag_c[m][n][0], frag_c[m][n][1]);
+ *c_ptr = sum;
+ }
+ }
+ if (r1 < max_m)
+ {
+ if constexpr (c_fp32)
+ {
+ float* c_ptr = gl_c_ptr_32 + r1 * size_n + (n0 + n) * 8 + c;
+ *c_ptr++ = frag_c[m][n][2];
+ *c_ptr++ = frag_c[m][n][3];
+ }
+ else
+ {
+ half2* c_ptr = (half2*) (gl_c_ptr_16 + r1 * size_n + (n0 + n) * 8 + c);
+ half2 sum = __floats2half2_rn(frag_c[m][n][2], frag_c[m][n][3]);
+ *c_ptr = sum;
+ }
+ }
+ }
+ }
+ }
+
+ // Last block writes in row-major format
+ if (!sub_k && last)
+ {
+ #pragma unroll
+ for (int n = 0; n < FRAGS_N_PER_WARP; ++n)
+ {
+ #pragma unroll
+ for (int m = 0; m < FRAGS_M; ++m)
+ {
+ int r0 = lane_id / 4 + 16 * m;
+ int r1 = r0 + 8;
+ int c = (lane_id % 4) * 2;
+ if (r0 < max_m)
+ {
+ if constexpr (c_fp32)
+ {
+ float* c_ptr = gl_c_ptr_32 + r0 * size_n + (n0 + n) * 8 + c;
+ *c_ptr++ = frag_c[m][n][0];
+ *c_ptr++ = frag_c[m][n][1];
+ }
+ else
+ {
+ half2* c_ptr = (half2*) (gl_c_ptr_16 + r0 * size_n + (n0 + n) * 8 + c);
+ half2 sum = __floats2half2_rn(frag_c[m][n][0], frag_c[m][n][1]);
+ *c_ptr = sum;
+ }
+ }
+ if (r1 < max_m)
+ {
+ if constexpr (c_fp32)
+ {
+ float* c_ptr = gl_c_ptr_32 + r1 * size_n + (n0 + n) * 8 + c;
+ *c_ptr++ = frag_c[m][n][2];
+ *c_ptr++ = frag_c[m][n][3];
+ }
+ else
+ {
+ half2* c_ptr = (half2*) (gl_c_ptr_16 + r1 * size_n + (n0 + n) * 8 + c);
+ half2 sum = __floats2half2_rn(frag_c[m][n][2], frag_c[m][n][3]);
+ *c_ptr = sum;
+ }
+ }
+ }
+ }
+ }
+
+ barrier_release(lock, lock_d, last);
+
+ clear_frag_c();
+ };
+
+ // Wait until there are at most SH_STAGES - 2 async copies pending, i.e. at least one stage has finished loading
+ auto wait_stage = [&] ()
+ {
+ cp_async_wait();
+ __syncthreads();
+ };
+
+ // Perform tensor core matmul on current tile
+ auto matmul = [&] (int buf)
+ {
+ #pragma unroll
+ for (int m = 0; m < FRAGS_M; ++m)
+ #pragma unroll
+ for (int n = 0; n < FRAGS_N_PER_WARP; ++n)
+ ptx_mma_m16n8k16(frag_a[buf][m], frag_b[buf][n], frag_c[m][n]);
+ };
+
+ // Start global to shared pipeline
+ #pragma unroll
+ for (int i = 0; i < SH_STAGES - 1; ++i)
+ async_load_gl();
+ wait_stage();
+
+ // Start shared to register pipeline.
+ clear_frag_c();
+ if constexpr (FRAG_STAGES > 1)
+ load_frags(0);
+
+ // Main loop. Fragments are double buffered to allow more interleaving. This is especially important to hide the
+ // dequantization overhead, but we need two different iterations of the main loop to avoid confusing the compiler
+ // and making it (sometimes) place the fragment arrays in local memory
+
+ #define FSTAGE(_load, _mul) \
+ async_load_gl(); \
+ wait_stage(); \
+ load_frags(_load); \
+ matmul(_mul); \
+ if (slice2_k == tiles_k - 1 || slice2_iters == 1) { reduce(); slice2_k0 = slice2_k + 1; } \
+ advance2(); \
+ if (!slice2_iters) break; \
+
+ if constexpr (FRAG_STAGES == 1)
+ {
+ while (true)
+ {
+ FSTAGE(0, 0);
+ }
+ }
+
+ if constexpr (FRAG_STAGES == 2)
+ {
+ while (true)
+ {
+ FSTAGE(1, 0);
+ FSTAGE(0, 1);
+ }
+ }
+
+ if constexpr (FRAG_STAGES == 3)
+ {
+ while (true)
+ {
+ FSTAGE(1, 0);
+ FSTAGE(2, 1);
+ FSTAGE(0, 2);
+ }
+ }
+
+ if constexpr (FRAG_STAGES == 4)
+ {
+ while (true)
+ {
+ FSTAGE(1, 0);
+ FSTAGE(2, 1);
+ FSTAGE(3, 2);
+ FSTAGE(0, 3);
+ }
+ }
+
+ if constexpr (FRAG_STAGES == 5)
+ {
+ while (true)
+ {
+ FSTAGE(1, 0);
+ FSTAGE(2, 1);
+ FSTAGE(3, 2);
+ FSTAGE(4, 3);
+ FSTAGE(0, 4);
+ }
+ }
+}
diff --git a/gptqmodel_ext/exllamav3/quant/exl3_gemm_kernel.cuh b/gptqmodel_ext/exllamav3/quant/exl3_gemm_kernel.cuh
new file mode 100644
index 000000000..510d94519
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/exl3_gemm_kernel.cuh
@@ -0,0 +1,80 @@
+#pragma once
+
+#include "exl3_kernel_map.cuh"
+#include "hadamard_inner.cuh"
+#include "exl3_gemm_inner.cuh"
+
+template
+__global__ __launch_bounds__(EXL3_GEMM_BASE_THREADS * TILESIZE_K / 16)
+void exl3_gemm_kernel(EXL3_GEMM_ARGS)
+{
+ auto grid = cg::this_grid();
+
+ if (suh)
+ {
+ int total_warps = size_m * size_k / 128;
+ int warps_grid = gridDim.x * blockDim.x / 32;
+ int this_warp = threadIdx.x / 32 + blockDim.x / 32 * blockIdx.x;
+
+ for(; this_warp < total_warps; this_warp += warps_grid)
+ had_hf_r_128_inner
+ (
+ A + this_warp * 128,
+ A_had + this_warp * 128,
+ suh + (this_warp * 128) % size_k,
+ nullptr,
+ 0.088388347648f // 1/sqrt(128)
+ );
+
+ grid.sync();
+ A = A_had;
+ }
+
+ int size_m_ = size_m;
+ const half* A_ = A;
+ void* C_ = C;
+
+ while (size_m_ > 0)
+ {
+ exl3_gemm_kernel_inner
+
+ (A_, B, C_, size_m_, size_k, size_n, locks);
+
+ A_ += 16 * size_k;
+ if constexpr (c_fp32) C_ = (void*) (((float*) C_) + 16 * size_n);
+ else C_ = (void*) (((half*) C_) + 16 * size_n);
+ size_m_ -= 16;
+
+ if (size_m_ > 0 || svh)
+ grid.sync();
+ }
+
+ if (svh)
+ {
+ int total_warps = size_m * size_n / 128;
+ int warps_grid = gridDim.x * blockDim.x / 32;
+ int this_warp = threadIdx.x / 32 + blockDim.x / 32 * blockIdx.x;
+
+ for(; this_warp < total_warps; this_warp += warps_grid)
+ {
+ if constexpr (c_fp32)
+ had_ff_r_128_inner
+ (
+ ((const float*) C) + this_warp * 128,
+ ((float*) C) + this_warp * 128,
+ nullptr,
+ svh + (this_warp * 128) % size_n,
+ 0.088388347648f // 1/sqrt(128)
+ );
+ else
+ had_hf_r_128_inner
+ (
+ ((const half*) C) + this_warp * 128,
+ ((half*) C) + this_warp * 128,
+ nullptr,
+ svh + (this_warp * 128) % size_n,
+ 0.088388347648f // 1/sqrt(128)
+ );
+ }
+ }
+}
diff --git a/gptqmodel_ext/exllamav3/quant/exl3_kernel_map.cu b/gptqmodel_ext/exllamav3/quant/exl3_kernel_map.cu
new file mode 100644
index 000000000..b107ff1d9
--- /dev/null
+++ b/gptqmodel_ext/exllamav3/quant/exl3_kernel_map.cu
@@ -0,0 +1,143 @@
+#include
+
+#include
+#include
+#include