From 9a3c927d43d2afb031b9141e6701108128d568fa Mon Sep 17 00:00:00 2001 From: Jaemin Choi Date: Wed, 22 Apr 2026 11:02:23 -0700 Subject: [PATCH 001/105] Fix nvtx_decorator to check _nvtx_enabled at call time (#4184) Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: Xin Yao --- megatron/core/utils.py | 35 +++++++++++++++++----------------- pyproject.toml | 2 -- tests/unit_tests/test_utils.py | 21 +++++++++++++++----- uv.lock | 27 -------------------------- 4 files changed, 34 insertions(+), 51 deletions(-) diff --git a/megatron/core/utils.py b/megatron/core/utils.py index 3fac8fdafff..39ebf9a5044 100644 --- a/megatron/core/utils.py +++ b/megatron/core/utils.py @@ -52,13 +52,6 @@ except ImportError: HAVE_PACKAGING = False -try: - import nvtx - - HAVE_NVTX = True -except ImportError: - HAVE_NVTX = False - logger = logging.getLogger(__name__) try: @@ -2219,14 +2212,16 @@ def _nvtx_decorator_get_func_path(func): return f"{module.__name__}.{caller_func}" -def nvtx_decorator( - message: Optional[str] = None, color: Optional[str] = None -) -> Callable[[_Wrapped], _Wrapped]: +def nvtx_decorator(message: Optional[str] = None) -> Callable[[_Wrapped], _Wrapped]: """Decorator to add NVTX range to a function. + The ``_nvtx_enabled`` flag is checked at **call time** inside + ``nvtx_range_push`` / ``nvtx_range_pop``, so the decorator works + correctly even when applied before ``configure_nvtx_profiling()`` + is called (e.g. at module-import time). + Args: message (str, optional): Custom message for the NVTX range. If None, uses function path - color (str, optional): Color for the NVTX range. Defaults to None Returns: Callable: Decorated function with NVTX profiling if enabled @@ -2236,17 +2231,23 @@ def nvtx_decorator( def my_function(): pass - @nvtx_decorator(message="Custom Range", color="blue") + @nvtx_decorator(message="Custom Range") def another_function(): pass """ def decorator(func: _Wrapped) -> _Wrapped: - if _nvtx_enabled and HAVE_NVTX: - return nvtx.annotate( - message=message or _nvtx_decorator_get_func_path(func), color=color - )(func) - return func + msg = message or _nvtx_decorator_get_func_path(func) + + @functools.wraps(func) + def wrapper(*args, **kwargs): + nvtx_range_push(msg) + try: + return func(*args, **kwargs) + finally: + nvtx_range_pop(msg) + + return wrapper # type: ignore[return-value] return decorator diff --git a/pyproject.toml b/pyproject.toml index 5b09cbd2a66..f7611078b9e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,7 +94,6 @@ dev = [ "tqdm", "einops~=0.8", "tensorstore~=0.1,!=0.1.46,!=0.1.72", - "nvtx~=0.2", "multi-storage-client~=0.27", "opentelemetry-api~=1.33.1", "mamba-ssm~=2.2", @@ -118,7 +117,6 @@ lts = [ "tqdm", "einops~=0.8", "tensorstore~=0.1,!=0.1.46,!=0.1.72", - "nvtx~=0.2", "multi-storage-client~=0.27", "opentelemetry-api~=1.33.1", "mamba-ssm~=2.2", diff --git a/tests/unit_tests/test_utils.py b/tests/unit_tests/test_utils.py index dc554612811..02f178387f3 100644 --- a/tests/unit_tests/test_utils.py +++ b/tests/unit_tests/test_utils.py @@ -163,17 +163,19 @@ def test_nvtx_decorator(): # Track function execution execution_tracker = {'decorated': False, 'decorated_with_message': False} - # Create decorated functions + # Decorate while NVTX is disabled (the common import-time scenario). + # The _nvtx_enabled flag must be checked at call time, not decoration time. + util.configure_nvtx_profiling(False) + @util.nvtx_decorator() def nvtx_decorated_function(): execution_tracker['decorated'] = True - @util.nvtx_decorator(message="test_nvtx_decorator", color="red") + @util.nvtx_decorator(message="test_nvtx_decorator") def nvtx_decorated_function_with_message(): execution_tracker['decorated_with_message'] = True - # Test with NVTX disabled - util.configure_nvtx_profiling(False) + # Call with NVTX disabled — should still execute the wrapped function nvtx_decorated_function() nvtx_decorated_function_with_message() assert all(execution_tracker.values()) @@ -181,12 +183,21 @@ def nvtx_decorated_function_with_message(): # Reset tracker execution_tracker = {'decorated': False, 'decorated_with_message': False} - # Test with NVTX enabled + # Enable NVTX *after* decoration — should pick up the new flag value util.configure_nvtx_profiling(True) nvtx_decorated_function() nvtx_decorated_function_with_message() assert all(execution_tracker.values()) + # Reset tracker + execution_tracker = {'decorated': False, 'decorated_with_message': False} + + # Disable NVTX again — should respect the toggled flag + util.configure_nvtx_profiling(False) + nvtx_decorated_function() + nvtx_decorated_function_with_message() + assert all(execution_tracker.values()) + @pytest.mark.flaky @pytest.mark.flaky_in_dev diff --git a/uv.lock b/uv.lock index cad7702bc60..f69541db539 100644 --- a/uv.lock +++ b/uv.lock @@ -2655,7 +2655,6 @@ dev = [ { name = "multi-storage-client" }, { name = "nvidia-modelopt", marker = "(sys_platform != 'darwin' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "nvidia-resiliency-ext" }, - { name = "nvtx" }, { name = "onnxscript", version = "0.5.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "onnxscript", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "openai", extra = ["aiohttp"], marker = "extra == 'extra-13-megatron-core-dev'" }, @@ -2680,7 +2679,6 @@ lts = [ { name = "mamba-ssm" }, { name = "megatron-energon", extra = ["av-decode"], marker = "extra == 'extra-13-megatron-core-lts'" }, { name = "multi-storage-client" }, - { name = "nvtx" }, { name = "onnxscript", version = "0.6.2", source = { registry = "https://pypi.org/simple" } }, { name = "opentelemetry-api", version = "1.33.1", source = { registry = "https://pypi.org/simple" } }, { name = "tensorstore", version = "0.1.82", source = { registry = "https://pypi.org/simple" } }, @@ -2788,8 +2786,6 @@ requires-dist = [ { name = "numpy" }, { name = "nvidia-modelopt", extras = ["torch"], marker = "sys_platform != 'darwin' and extra == 'dev'" }, { name = "nvidia-resiliency-ext", marker = "extra == 'dev'", git = "https://github.com/NVIDIA/nvidia-resiliency-ext.git?rev=b2bb3d728a18795807d9f76c535e005a609a1b01" }, - { name = "nvtx", marker = "extra == 'dev'", specifier = "~=0.2" }, - { name = "nvtx", marker = "extra == 'lts'", specifier = "~=0.2" }, { name = "onnxscript", marker = "extra == 'dev'" }, { name = "onnxscript", marker = "extra == 'lts'" }, { name = "openai", extras = ["aiohttp"], marker = "extra == 'dev'" }, @@ -3742,29 +3738,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8c/79/017fab2f7167a9a9795665f894d04f77aafceca80821b51589bb4b23ff5c/nvidia_sphinx_theme-0.0.9.post1-py3-none-any.whl", hash = "sha256:21ca60206dff2f380d7783d64bbaf71a5b9cacae53c7d0686f089c16b5a3d45a", size = 143816, upload-time = "2025-11-09T23:16:55.719Z" }, ] -[[package]] -name = "nvtx" -version = "0.2.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/92/dd/692765e87de30bae1522cdffaa0f2b52949658a92a0fa6d96b1a01eae9d2/nvtx-0.2.15.tar.gz", hash = "sha256:2287d3be05b85661deb386f878d1f536c2e532774aa9ec7a50c434942ed81ae5", size = 121230, upload-time = "2026-03-18T10:01:25.547Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/07/698355285a03a366ef63ea9762fc1feef3f9f25483e1655408f72d827090/nvtx-0.2.15-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2cc530cd0f1a2c14a3a7e683833db509888ac5ed4ead94e5c9e2c7317c6937a7", size = 807159, upload-time = "2026-03-18T10:09:49.232Z" }, - { url = "https://files.pythonhosted.org/packages/c0/d1/08f22448d83481408d663065764ba583df091a7de629ed38fc97e522f1af/nvtx-0.2.15-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ca8030a6d197952318013dd1c12c22da1d4b9feb76ba72e0fcd449961183c2c", size = 806187, upload-time = "2026-03-18T10:13:32.972Z" }, - { url = "https://files.pythonhosted.org/packages/54/23/c97c39e3b7ba256aa343cb828ca0d1c8421f705ca84795658ecd14ca95ed/nvtx-0.2.15-cp312-cp312-win_amd64.whl", hash = "sha256:70a1e768964e0520b68ccabc4df391cc227537c45936a7eba6507bc65e617e00", size = 129178, upload-time = "2026-03-18T10:02:55.299Z" }, - { url = "https://files.pythonhosted.org/packages/05/c9/8341224b8284f7deb6a634119939de5885adc421e64b6743693b30da2186/nvtx-0.2.15-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d28660d9c46f8ba750d781572b6aa5a1e6221abba224ab32d7fb32c2d0fd67df", size = 780787, upload-time = "2026-03-18T10:10:40.634Z" }, - { url = "https://files.pythonhosted.org/packages/b1/c0/4a5bb7897918de7c7e0191d9342df8ae4cb797ff07276e0f20d13e497ce7/nvtx-0.2.15-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10749686633f880ad53dcdbb2179fad41b45dcf5b7631d4a1070a577577bd386", size = 782575, upload-time = "2026-03-18T10:13:57.3Z" }, - { url = "https://files.pythonhosted.org/packages/38/b9/6b381ac7c5a3ded331aebbf25f8959d19b51d320fb2514c76c6b6edddaaa/nvtx-0.2.15-cp313-cp313-win_amd64.whl", hash = "sha256:a6650b029263d12f8427a4dee8bd59cb9c91bccb60543bfcb20bc2b00fdcd672", size = 128764, upload-time = "2026-03-18T10:02:33.343Z" }, - { url = "https://files.pythonhosted.org/packages/75/69/a9acb6d95d2e0e381b2956544768528dd8d7a9e827af8c2014169d838284/nvtx-0.2.15-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25813ead4fff4d3a6e04f69a72507b096a6bdbecefa369f1100b0e584767bca8", size = 833375, upload-time = "2026-03-18T10:06:31.955Z" }, - { url = "https://files.pythonhosted.org/packages/38/56/c7e8645061cc2fc23f3a54f33e1e340df59216f07dcfb97d46b8ae7dd26c/nvtx-0.2.15-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3741edac4678b92f03d22a3f0a2dfd469f422f85e63db71b038e02525b2404ad", size = 788639, upload-time = "2026-03-18T10:12:01.69Z" }, - { url = "https://files.pythonhosted.org/packages/96/03/fadd82acdbca6d1c49ac517081a0c3714346f52f4c7e1d4449d77605b4aa/nvtx-0.2.15-cp313-cp313t-win_amd64.whl", hash = "sha256:8be06c3c8c267eba56a0396366b9593092e0b75ea8d3702b303d48c0a1662f0e", size = 142609, upload-time = "2026-03-18T10:01:48.832Z" }, - { url = "https://files.pythonhosted.org/packages/e0/5b/ca0ba6fa769d08174b7a5b4775c279e2e26611cdd5e7833aa699187871c7/nvtx-0.2.15-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5171b8283dd3ea9ae688a86d16901b4c2c142c4eb0a4bdbf6c222f5f67f9524", size = 781769, upload-time = "2026-03-18T10:08:59.357Z" }, - { url = "https://files.pythonhosted.org/packages/f7/e1/e02fafc01c18f1868a2d2c030953f49e38d65f2d95884789a6c46ff308f1/nvtx-0.2.15-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c6d0f27d4f8a2f479eb64a6b842c13aee32120348a1715d995b9bb9f75b35cf", size = 774614, upload-time = "2026-03-18T10:12:46.979Z" }, - { url = "https://files.pythonhosted.org/packages/20/77/a2b64335bab7c75fe1c054cc4ebe2d3b3234cbdb04d2e1d6ca73551c54f5/nvtx-0.2.15-cp314-cp314-win_amd64.whl", hash = "sha256:9934fad0b441cfa6e896a848b092498ba23e2ff205c2b9a7b60520ff8367ffef", size = 130932, upload-time = "2026-03-18T10:03:43.507Z" }, - { url = "https://files.pythonhosted.org/packages/db/24/528619230976c18364eda2340906ea67b3bf7588b7ce59e054723614abae/nvtx-0.2.15-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aca61135c76b8107ae3c994325613afa661e1336a991c59cc9c6176829b3b32c", size = 834439, upload-time = "2026-03-18T10:05:01.181Z" }, - { url = "https://files.pythonhosted.org/packages/ef/7b/c1b96f13ef89bdf2a8c2f326a97bed89699271990d7c8624fda3fedc6e61/nvtx-0.2.15-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:58653bf6fd8453947b9e5153da2ad7aeb0ceafa030de7f133efb3eada5da7ca7", size = 790247, upload-time = "2026-03-18T10:11:39.124Z" }, - { url = "https://files.pythonhosted.org/packages/14/5d/e000de781d92b732d52c572517db0e9e3a0085795f8bdc18201713c52d1f/nvtx-0.2.15-cp314-cp314t-win_amd64.whl", hash = "sha256:9d1d10db4fb4a3b0ffd6ed37bf25f0a966a3b4d34b3c9abb1f6572732959a6e5", size = 149109, upload-time = "2026-03-18T10:03:21.615Z" }, -] - [[package]] name = "omegaconf" version = "2.3.0" From 60f71e132ef30d249a6e01c26d698b307daf40b1 Mon Sep 17 00:00:00 2001 From: Chelsea John <36626249+chelseajohn@users.noreply.github.com> Date: Wed, 22 Apr 2026 20:16:56 +0200 Subject: [PATCH 002/105] fix merges_file typo in megatron_hf_tokenizer (#4392) Co-authored-by: john2 --- .../core/tokenizers/text/libraries/megatron_hf_tokenizer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/megatron/core/tokenizers/text/libraries/megatron_hf_tokenizer.py b/megatron/core/tokenizers/text/libraries/megatron_hf_tokenizer.py index 849d3f0de0e..63110444aa5 100644 --- a/megatron/core/tokenizers/text/libraries/megatron_hf_tokenizer.py +++ b/megatron/core/tokenizers/text/libraries/megatron_hf_tokenizer.py @@ -95,7 +95,7 @@ def __init__( ) vocab_file = self._get_vocab_file(tokenizer_name, vocab_file) - merges_file = self._get_merges_file(tokenizer_name, vocab_file) + merges_file = self._get_merges_file(tokenizer_name, merges_file) tokenizer_path = MEGATRON_CONFIG_MAP[tokenizer_name]["tokenizer_name"] super().__init__(tokenizer_path, vocab_file, merges_file, **kwargs) From c9dfe347985a3f6a2cfcdbf98a36cb2ada169057 Mon Sep 17 00:00:00 2001 From: Antoni-Joan Solergibert Date: Wed, 22 Apr 2026 21:00:43 +0200 Subject: [PATCH 003/105] Enable NullTokenizer for pretraining to reduce I/O access (#4057) --- docs/user-guide/features/tokenizers.md | 33 +++++++++++++++++-- .../text/libraries/null_tokenizer.py | 25 +++++++++++--- .../core/tokenizers/utils/build_tokenizer.py | 7 ++++ megatron/training/arguments.py | 5 +++ .../common/ckpt_converter/__main__.py | 2 +- .../model_config.yaml | 2 +- .../model_config.yaml | 2 +- .../model_config.yaml | 4 +-- .../model_config.yaml | 2 +- .../model_config.yaml | 2 +- tests/unit_tests/tokenizers/test_tokenizer.py | 4 ++- 11 files changed, 72 insertions(+), 16 deletions(-) diff --git a/docs/user-guide/features/tokenizers.md b/docs/user-guide/features/tokenizers.md index 672f0f0cd98..1455d6e617e 100644 --- a/docs/user-guide/features/tokenizers.md +++ b/docs/user-guide/features/tokenizers.md @@ -149,7 +149,24 @@ tokenizer = MegatronTokenizer.from_pretrained( ### Null Tokenizer -Use a null tokenizer for testing or non-text models: +The Null tokenizer is a lightweight, zero-I/O tokenizer that requires no model files. +It is useful in three scenarios: + +1. **Performance benchmarking** with `--mock-data` where real tokenization is unnecessary. +2. **Testing** in functional tests and CI pipelines where tokenizer model files may not + be available. The Null tokenizer removes the dependency on external files, making + tests self-contained and portable. +3. **Pretraining with pretokenized data** where all data is already tokenized into + `.bin`/`.idx` files. In this case the tokenizer is only needed for metadata + (`vocab_size`, `eod`, `pad`) — not for actual tokenization. Using the Null tokenizer + avoids redundant filesystem access at scale, which is particularly beneficial on + shared filesystems like Lustre where thousands of ranks would otherwise all load the + same tokenizer files. + +Properties derived from `--vocab-size N`: +- `vocab_size` = `N` (the exact value passed) +- `eod` = `N - 1` (last token in the vocabulary) +- `pad` = `0` ```python tokenizer = MegatronTokenizer.from_pretrained( @@ -165,10 +182,20 @@ tokenizer = MegatronTokenizer.from_pretrained( The tokenizer system works with Megatron-LM training scripts: ```bash -# Null tokenizer for testing +# Null tokenizer for benchmarking with mock data torchrun --nproc_per_node=8 pretrain_gpt.py \ --tokenizer-type NullTokenizer \ --vocab-size 131072 \ + --mock-data \ + ... +``` + +```bash +# Null tokenizer for pretraining with pretokenized data (no tokenizer files needed) +torchrun --nproc_per_node=8 pretrain_gpt.py \ + --tokenizer-type NullTokenizer \ + --vocab-size 128256 \ + --data-path /path/to/pretokenized_data \ ... ``` @@ -195,7 +222,7 @@ The following table lists supported tokenizer backends: | **SentencePiece** | Google's tokenizer | GPT-style models, custom vocabularies | | **TikToken** | OpenAI's tokenizer | GPT-3.5/GPT-4 style tokenization | | **Megatron** | Built-in tokenizers | Legacy GPT-2 BPE | -| **Null** | No-op tokenizer | Testing, non-text modalities | +| **Null** | Zero-I/O tokenizer | Benchmarking, pretokenized data | ## Common Tokenizer Types diff --git a/megatron/core/tokenizers/text/libraries/null_tokenizer.py b/megatron/core/tokenizers/text/libraries/null_tokenizer.py index 4ddf77fc774..96a0d3afd57 100644 --- a/megatron/core/tokenizers/text/libraries/null_tokenizer.py +++ b/megatron/core/tokenizers/text/libraries/null_tokenizer.py @@ -9,12 +9,15 @@ class NullTokenizer: Args: vocab_size: vocabulary size for embedding + eod_id: id of the end-of-document token. Defaults to ``vocab_size - 1``. + pad_id: id of the padding token. Defaults to ``-1`` (no pad token). """ - def __init__(self, vocab_size): + def __init__(self, vocab_size, eod_id=None, pad_id=-1, **kwargs): """ """ - self._vocab_size_without_eod = int(vocab_size) - self._eod_id = self._vocab_size_without_eod + self._vocab_size = int(vocab_size) + self._eod_id = int(eod_id) if eod_id is not None else self._vocab_size - 1 + self._pad_id = int(pad_id) def text_to_ids(self, text): """Converts text to ids.""" @@ -44,12 +47,19 @@ def offsets(self, ids: list[int], text: str) -> list[int]: @property def unique_identifiers(self) -> OrderedDict: """Property required for use with megatron-core datasets.""" - return OrderedDict({"class": f"{type(self).__module__}.{type(self).__qualname__}"}) + return OrderedDict( + { + "class": f"{type(self).__module__}.{type(self).__qualname__}", + "vocab_size": self._vocab_size, + "eod_id": self._eod_id, + "pad_id": self._pad_id, + } + ) @property def vocab_size(self): """Returns vocab size.""" - return self._vocab_size_without_eod + 1 + return self._vocab_size @property def vocab(self): @@ -81,6 +91,11 @@ def eod(self): """Returns eod token.""" return self._eod_id + @property + def pad_id(self): + """Returns pad token.""" + return self._pad_id + @property def additional_special_tokens_ids(self): """ """ diff --git a/megatron/core/tokenizers/utils/build_tokenizer.py b/megatron/core/tokenizers/utils/build_tokenizer.py index bf02451ae6c..9e92d12fa58 100644 --- a/megatron/core/tokenizers/utils/build_tokenizer.py +++ b/megatron/core/tokenizers/utils/build_tokenizer.py @@ -74,6 +74,13 @@ def build_tokenizer(args, **kwargs): metadata = {'library': tokenizer_library} if args.vocab_size: kwargs['vocab_size'] = args.vocab_size + if args.tokenizer_type == 'NullTokenizer': + null_eod_id = getattr(args, 'null_tokenizer_eod_id', None) + if null_eod_id is not None: + kwargs['eod_id'] = null_eod_id + null_pad_id = getattr(args, 'null_tokenizer_pad_id', None) + if null_pad_id is not None: + kwargs['pad_id'] = null_pad_id tokenizer = MegatronTokenizer.from_pretrained(metadata_path=metadata, **kwargs) # Add vocab size (if not already set from a checkpoint). diff --git a/megatron/training/arguments.py b/megatron/training/arguments.py index ba4099c3b71..061ff618e17 100644 --- a/megatron/training/arguments.py +++ b/megatron/training/arguments.py @@ -2904,6 +2904,11 @@ def _add_tokenizer_args(parser): help='Converting text to ids will not include special for HuggingFace tokenizer.') group.add_argument("--trust-remote-code", action="store_true", default=False, help='Whether or not to allow PreTrainedTokenizer to execute remote code') + group.add_argument('--null-tokenizer-eod-id', type=int, default=None, + help='EOD token id for NullTokenizer. Defaults to `vocab_size - 1`.') + group.add_argument('--null-tokenizer-pad-id', type=int, default=-1, + help='Pad token id for NullTokenizer. Defaults to -1 (no pad token). ' + 'Set to a value outside the dataset to avoid masking real tokens.') return parser diff --git a/tests/functional_tests/test_cases/common/ckpt_converter/__main__.py b/tests/functional_tests/test_cases/common/ckpt_converter/__main__.py index 543ddd36a6d..5e876dcf23d 100644 --- a/tests/functional_tests/test_cases/common/ckpt_converter/__main__.py +++ b/tests/functional_tests/test_cases/common/ckpt_converter/__main__.py @@ -579,7 +579,7 @@ def get_model_argv(self): "--tokenizer-type", "NullTokenizer", "--vocab-size", - "127", # ... NullTokenizer adds +1 EOD token. + "128", "--make-vocab-size-divisible-by", "1", ] diff --git a/tests/functional_tests/test_cases/gpt/gpt3_7b_tp1_pp4_memory_speed/model_config.yaml b/tests/functional_tests/test_cases/gpt/gpt3_7b_tp1_pp4_memory_speed/model_config.yaml index e91d1105e83..eb253b243f1 100644 --- a/tests/functional_tests/test_cases/gpt/gpt3_7b_tp1_pp4_memory_speed/model_config.yaml +++ b/tests/functional_tests/test_cases/gpt/gpt3_7b_tp1_pp4_memory_speed/model_config.yaml @@ -28,7 +28,7 @@ MODEL_ARGS: --save: ${CHECKPOINT_SAVE_PATH} --load: ${CHECKPOINT_LOAD_PATH} --tokenizer-type: NullTokenizer - --vocab-size: 131072 + --vocab-size: 131073 --mock-data: true --split: 949,50,1 --distributed-backend: nccl diff --git a/tests/functional_tests/test_cases/gpt/gpt3_7b_tp4_pp1_memory_speed/model_config.yaml b/tests/functional_tests/test_cases/gpt/gpt3_7b_tp4_pp1_memory_speed/model_config.yaml index 96bc016ca97..ae067d246c9 100644 --- a/tests/functional_tests/test_cases/gpt/gpt3_7b_tp4_pp1_memory_speed/model_config.yaml +++ b/tests/functional_tests/test_cases/gpt/gpt3_7b_tp4_pp1_memory_speed/model_config.yaml @@ -28,7 +28,7 @@ MODEL_ARGS: --save: ${CHECKPOINT_SAVE_PATH} --load: ${CHECKPOINT_LOAD_PATH} --tokenizer-type: NullTokenizer - --vocab-size: 131072 + --vocab-size: 131073 --mock-data: true --split: 949,50,1 --distributed-backend: nccl diff --git a/tests/functional_tests/test_cases/gpt/gpt3_mcore_te_tp2_pp2_cp2/model_config.yaml b/tests/functional_tests/test_cases/gpt/gpt3_mcore_te_tp2_pp2_cp2/model_config.yaml index 56df7d07a0d..fc6d56fab55 100644 --- a/tests/functional_tests/test_cases/gpt/gpt3_mcore_te_tp2_pp2_cp2/model_config.yaml +++ b/tests/functional_tests/test_cases/gpt/gpt3_mcore_te_tp2_pp2_cp2/model_config.yaml @@ -20,8 +20,8 @@ MODEL_ARGS: --save: ${CHECKPOINT_SAVE_PATH} --load: ${CHECKPOINT_LOAD_PATH} --data-path: ${DATA_PATH}/text/common_pile/v01_filtered_data/my-gpt3_00_text_document - --vocab-file: ${DATA_PATH}/text/common_pile/v01_filtered_data/bpe/vocab.json - --merge-file: ${DATA_PATH}/text/common_pile/v01_filtered_data/bpe/merges.txt + --tokenizer-type: NullTokenizer + --vocab-size: 50257 --split: 949,50,1 --distributed-backend: nccl --lr: 0.00015 diff --git a/tests/functional_tests/test_cases/multimodal-llava/multimodal_llava_mcore_te_tp1_pp1/model_config.yaml b/tests/functional_tests/test_cases/multimodal-llava/multimodal_llava_mcore_te_tp1_pp1/model_config.yaml index 2898070f957..377371b2370 100644 --- a/tests/functional_tests/test_cases/multimodal-llava/multimodal_llava_mcore_te_tp1_pp1/model_config.yaml +++ b/tests/functional_tests/test_cases/multimodal-llava/multimodal_llava_mcore_te_tp1_pp1/model_config.yaml @@ -25,7 +25,7 @@ MODEL_ARGS: --load: ${CHECKPOINT_LOAD_PATH} --split: 949,50,1 --tokenizer-type: NullTokenizer - --vocab-size: 8192 + --vocab-size: 8193 --distributed-backend: nccl --lr: 0.00015 --lr-decay-style: cosine diff --git a/tests/functional_tests/test_cases/multimodal-llava/multimodal_llava_mcore_te_tp4_sp_cp2/model_config.yaml b/tests/functional_tests/test_cases/multimodal-llava/multimodal_llava_mcore_te_tp4_sp_cp2/model_config.yaml index 23bdaac5010..9332c934613 100644 --- a/tests/functional_tests/test_cases/multimodal-llava/multimodal_llava_mcore_te_tp4_sp_cp2/model_config.yaml +++ b/tests/functional_tests/test_cases/multimodal-llava/multimodal_llava_mcore_te_tp4_sp_cp2/model_config.yaml @@ -27,7 +27,7 @@ MODEL_ARGS: --load: ${CHECKPOINT_LOAD_PATH} --split: 949,50,1 --tokenizer-type: NullTokenizer - --vocab-size: 8192 + --vocab-size: 8193 --distributed-backend: nccl --lr: 0.00015 --lr-decay-style: cosine diff --git a/tests/unit_tests/tokenizers/test_tokenizer.py b/tests/unit_tests/tokenizers/test_tokenizer.py index a376b1336fc..4fbc9cebfc5 100755 --- a/tests/unit_tests/tokenizers/test_tokenizer.py +++ b/tests/unit_tests/tokenizers/test_tokenizer.py @@ -275,7 +275,9 @@ def test_null_tokenizer(): ids = tokenizer.tokenize("11 325 97") assert ids == [11, 325, 97] - assert tokenizer.vocab_size == 131073 + assert tokenizer.vocab_size == 131072 + assert tokenizer.eod == 131071 + assert tokenizer.pad == -1 @pytest.mark.parametrize("skip_special_tokens", [True, False]) From 707349287d6dc014e48245fdc61d17d3d7b3cabf Mon Sep 17 00:00:00 2001 From: Charlie Truong Date: Wed, 22 Apr 2026 14:14:31 -0500 Subject: [PATCH 004/105] docs: Add SECURITY.md (#4431) Signed-off-by: Charlie Truong --- SECURITY.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..728cdb4a1d2 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,25 @@ +## Security + +NVIDIA is dedicated to the security and trust of our software products and services, including all source code repositories managed through our organization. + +If you need to report a security issue, please use the appropriate contact points outlined below. **Please do not report security vulnerabilities through GitHub.** If a potential security issue is inadvertently reported via a public issue or pull request, NVIDIA maintainers may limit public discussion and redirect the reporter to the appropriate private disclosure channels. + +## Reporting Potential Security Vulnerability in an NVIDIA Product + +To report a potential security vulnerability in any NVIDIA product: + +- Web: [Security Vulnerability Submission Form](https://www.nvidia.com/object/submit-security-vulnerability.html) +- E-Mail: psirt@nvidia.com + - We encourage you to use the following PGP key for secure email communication: [NVIDIA public PGP Key for communication](https://www.nvidia.com/en-us/security/pgp-key) + - Please include the following information: + - Product/Driver name and version/branch that contains the vulnerability + - Type of vulnerability (code execution, denial of service, buffer overflow, etc.) + - Instructions to reproduce the vulnerability + - Proof-of-concept or exploit code + - Potential impact of the vulnerability, including how an attacker could exploit the vulnerability + +While NVIDIA currently does not have a bug bounty program, we do offer acknowledgement when an externally reported security issue is addressed under our coordinated vulnerability disclosure policy. Please visit our [Product Security Incident Response Team (PSIRT)](https://www.nvidia.com/en-us/security/psirt-policies/) policies page for more information. + +## NVIDIA Product Security + +For all security-related concerns, please visit NVIDIA's Product Security portal at https://www.nvidia.com/en-us/security From 40627d00d96cef74aceab35e0e3c170e900f3aff Mon Sep 17 00:00:00 2001 From: wdykas <73254672+wdykas@users.noreply.github.com> Date: Wed, 22 Apr 2026 15:35:20 -0400 Subject: [PATCH 005/105] Mamba inference opt (#4414) Co-authored-by: root Co-authored-by: William Dykas Co-authored-by: root --- .../attention_context/mamba_metadata.py | 5 ++- megatron/core/ssm/mamba_mixer.py | 42 +++++++++++++++---- megatron/core/transformer/moe/moe_utils.py | 3 +- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/megatron/core/inference/contexts/attention_context/mamba_metadata.py b/megatron/core/inference/contexts/attention_context/mamba_metadata.py index 19091d35bfb..b07802a8f8c 100644 --- a/megatron/core/inference/contexts/attention_context/mamba_metadata.py +++ b/megatron/core/inference/contexts/attention_context/mamba_metadata.py @@ -40,9 +40,10 @@ def __init__( (self.max_requests,), -1, dtype=torch.int32, device=torch.cuda.current_device() ) - # Map from requests to slots in the static Mamba state buffer for active decode requests + # Map from requests to slots in the static Mamba state buffer for active decode requests. + # int64 so selective_state_update can index directly without a per-layer upcast kernel; self._batch_indices_decode_buffer = torch.full( - (self.max_requests,), -1, dtype=torch.int32, device=self.device + (self.max_requests,), -1, dtype=torch.int64, device=self.device ) # Map from requests to slots in the static Mamba state buffer for active prefill requests diff --git a/megatron/core/ssm/mamba_mixer.py b/megatron/core/ssm/mamba_mixer.py index 4368681b585..69c6d89c286 100644 --- a/megatron/core/ssm/mamba_mixer.py +++ b/megatron/core/ssm/mamba_mixer.py @@ -347,6 +347,14 @@ def __init__( self.A_log = nn.Parameter(A_log) setattr(self.A_log, "tensor_model_parallel", True) setattr(self.A_log, "partition_dim", 0) + # Persistent inference cache for -exp(A_log.float()). Allocated + # here (outside any later CUDA-graph capture) so its address + # lives in the default memory pool and stays valid across every + # graph capture and replay, including across RL train/eval + # cycles. Never freed -- the memory cost is ``nheads * 4B`` per + # layer (a few KB across a full model). + self._A_neg_exp_cache = torch.empty_like(A_log, dtype=torch.float32) + self._A_neg_exp_cache_stale = True # D "skip" parameter self.D = nn.Parameter( torch.ones( @@ -1015,6 +1023,29 @@ def _ssm_prefill( return y + def _get_decode_A_neg_exp(self) -> torch.Tensor: + """Cached ``-exp(A_log.float())`` pre-expanded to ``(nheads, headdim, dstate)``. + + A_log is frozen during inference; recomputing it per token otherwise + launches three small elementwise kernels (float cast, exp, neg) that + rival ``selective_state_update`` itself in the decode profile. The + stride-0 expand view also triggers the kernel's TIE_HDIM fast path. + """ + if self.training or torch.is_grad_enabled(): + base = -torch.exp(self.A_log.float()) + return base.view(-1, 1, 1).expand(-1, self.headdim, self.d_state) + # Inference path. Refill when stale + if torch.cuda.is_current_stream_capturing() or self._A_neg_exp_cache_stale: + with torch.no_grad(): + self._A_neg_exp_cache.copy_(-torch.exp(self.A_log.float())) + self._A_neg_exp_cache_stale = False + return self._A_neg_exp_cache.view(-1, 1, 1).expand(-1, self.headdim, self.d_state) + + def train(self, mode: bool = True): + """Mark the decode cache stale; weights may have updated.""" + self._A_neg_exp_cache_stale = True + return super().train(mode) + def _ssm_decode( self, zxBCdt: torch.Tensor, @@ -1093,10 +1124,10 @@ def _ssm_decode( ], dim=-1, ) - A = -torch.exp(self.A_log.float()) - # SSM step if selective_state_update is None: + # Fallback uses 1D A; the decode cache is pre-expanded for Triton. + A = -torch.exp(self.A_log.float()) # TODO(ksanthanam): Consider deprecating this path assert seq_len == 1, "Native PyTorch fallback only supports 1 token at a time" @@ -1154,7 +1185,7 @@ def _ssm_decode( y = y.unsqueeze(1) # Restore seq dimension else: - A = repeat(A, "h -> h p n", p=self.headdim, n=self.d_state).to(dtype=torch.float32) + A = self._get_decode_A_neg_exp() # Incorporate sequence dimension in einops rearrengements dt = repeat(dt, "b s h -> b s h p", p=self.headdim) @@ -1166,11 +1197,6 @@ def _ssm_decode( if not self.rmsnorm: z = rearrange(z, "b s (h p) -> b s h p", p=self.headdim) - # Upcast the batch_indices to prevent integer overflow errors in the case of - # large max request counts. - if batch_indices is not None: - batch_indices = batch_indices.to(torch.int64) - y = selective_state_update( ssm_state, x_reshaped, diff --git a/megatron/core/transformer/moe/moe_utils.py b/megatron/core/transformer/moe/moe_utils.py index 896495d0710..f258f3474ae 100644 --- a/megatron/core/transformer/moe/moe_utils.py +++ b/megatron/core/transformer/moe/moe_utils.py @@ -776,7 +776,8 @@ def _compute_topk( group_topk=group_topk, ) else: - return torch.topk(scores, k=topk, dim=1) + # Sorting top-k turned off during inference + return torch.topk(scores, k=topk, dim=1, sorted=torch.is_grad_enabled()) def compute_topk(scores, topk, num_groups=None, group_topk=None): # Default behavior if no replay is active From 55b8111ad84051cd0e2106ad1f5ab35fa2ab98f1 Mon Sep 17 00:00:00 2001 From: Deepak Narayanan Date: Wed, 22 Apr 2026 13:45:49 -0700 Subject: [PATCH 006/105] DDP refactoring: Extract parameter layout computation into optimizer classmethod (#3812) Co-authored-by: Claude Opus 4.6 --- .../distributed/distributed_data_parallel.py | 294 ++++++----- .../core/distributed/param_and_grad_buffer.py | 416 +++++++-------- megatron/core/optimizer/distrib_optimizer.py | 134 ++++- megatron/core/optimizer/param_layout.py | 92 ++++ megatron/training/training.py | 53 +- .../dist_checkpointing/test_optimizer.py | 4 +- .../distributed/test_param_and_grad_buffer.py | 148 +++++- .../distributed/test_param_layout.py | 478 ++++++++++++++++++ 8 files changed, 1254 insertions(+), 365 deletions(-) create mode 100644 megatron/core/optimizer/param_layout.py create mode 100644 tests/unit_tests/distributed/test_param_layout.py diff --git a/megatron/core/distributed/distributed_data_parallel.py b/megatron/core/distributed/distributed_data_parallel.py index 35325d70ce9..06f3fb32238 100644 --- a/megatron/core/distributed/distributed_data_parallel.py +++ b/megatron/core/distributed/distributed_data_parallel.py @@ -7,15 +7,15 @@ import torch from ..config_logger import has_config_logger_enabled, log_config_to_disk -from ..fp4_utils import is_nvfp4tensor from ..fp8_utils import is_float8tensor, post_all_gather_processing +from ..optimizer.param_layout import FullParamLayout from ..process_groups_config import ProcessGroupCollection from ..transformer.cuda_graphs import is_graph_capturing from ..transformer.transformer_config import TransformerConfig from ..utils import log_single_rank from .data_parallel_base import _BaseDataParallel from .distributed_data_parallel_config import DistributedDataParallelConfig -from .param_and_grad_buffer import _ParamAndGradBuffer, partition_buckets +from .param_and_grad_buffer import _ParamAndGradBuffer, group_params_for_buffers, partition_buckets logger = logging.getLogger(__name__) @@ -36,6 +36,9 @@ class DistributedDataParallel(_BaseDataParallel): use standard bucketing policy: assign parameters to smaller buckets and all-reduce per bucket _if_ overlap_grad_reduce is True and pp_rank is 0. pg_collection: Optional unified process group for distributed training. + full_param_layout: Optional FullParamLayout providing pre-computed layouts for all + dtype groups. When provided, each buffer uses the corresponding PerBufferParamLayout + instead of computing a default one. """ @@ -46,6 +49,7 @@ def __init__( module: torch.nn.Module, disable_bucketing: bool = False, pg_collection: Optional[ProcessGroupCollection] = None, + full_param_layout: Optional[FullParamLayout] = None, ): super().__init__(config=config, module=module) if has_config_logger_enabled(config): @@ -104,11 +108,10 @@ def __init__( self.param_to_bucket_group = {} - # Group parameters by their gradient type. + # Collect all trainable parameters. param_to_name = {} - dense_params = [] - expert_parallel_params = [] self.params_with_grad = [] + all_params = [] for name, param in self.module.named_parameters(): if not param.requires_grad: continue @@ -119,142 +122,50 @@ def __init__( param.grad_added_to_main_grad = False param_to_name[param] = name + all_params.append(param) + + # Group parameters by (param_dtype, grad_dtype, is_expert_parallel). + buffer_groups = group_params_for_buffers(all_params, self.ddp_config.grad_reduce_in_fp32) + + # Auto-compute layouts when using distributed optimizer but no layout was provided. + # This maintains backward compatibility for callers that create DDP directly + # without pre-computing layouts (e.g., tests, external code). + if full_param_layout is None and self.ddp_config.use_distributed_optimizer: + log_single_rank( + logger, + logging.WARNING, + "DistributedDataParallel: full_param_layout not provided with " + "use_distributed_optimizer=True. Auto-computing layout inside DDP. " + "Callers should pre-compute layouts via " + "DistributedOptimizer.compute_full_param_layout() and pass them in.", + ) + from ..optimizer.distrib_optimizer import DistributedOptimizer + + full_param_layout = DistributedOptimizer.compute_full_param_layout( + all_params, + self.bucket_size, + self.intra_dp_cp_group.size(), + self.ddp_config, + expert_data_parallel_world_size=self.intra_expt_dp_group.size(), + ) - if getattr(param, 'allreduce', True): - dense_params.append((param, name)) - else: - expert_parallel_params.append((param, name)) - - def _allocate_buffers_for_parameters( - input_params, data_parallel_group, gradient_scaling_factor - ): - param_and_grad_dtype_to_params = {} - param_and_grad_dtype_to_offsets = {} - param_and_grad_dtype_to_indices = {} - - # Group parameters by their gradient type. - for param, param_name in input_params: - assert param.requires_grad - - param_dtype = param.dtype - if is_float8tensor(param) or is_nvfp4tensor(param): - # Currently TE's Float8Tensor is a wrapper of torch.Tensor. It has a "fake" - # dtype (usually a higher precision dtype such as bfloat16), but its actual - # data is stored in the form of a torch uint8 tensor within the Float8Tensor's - # ".data" attribute. Therefore, when creating the param buffer for fp8/fp4 - # params,it is necessary to use torch.uint8, not the "fake" dtype got from - # "param.dtype". - param_dtype = torch.uint8 - grad_dtype = torch.float if self.ddp_config.grad_reduce_in_fp32 else param.dtype - - params = param_and_grad_dtype_to_params.get((param_dtype, grad_dtype), []) - params.append((param, param_name)) - param_and_grad_dtype_to_params[(param_dtype, grad_dtype)] = params - - # Get the index of each param among the params with same dtype, if a param is fp8, - # use its "fake" high precision dtype to find which params have same dtype with it. - # For example: - # Case 1: - # params = [p1(bf16), p2(bf16), p3(bf16), p4(bf16)] - # param_and_grad_dtype_to_indices = { - # (torch.bfloat16, torch.float32): [0, 1, 2, 3], - # } - # Case 2: - # params = [p1(bf16), p2(fp8), p3(fp8), p4(bf16)] - # param_and_grad_dtype_to_indices = { - # (torch.bfloat16, torch.float32): [0, 3], - # (torch.uint8, torch.float32): [1, 2], - # } - # We need these indices to load a non-native-fp8 checkpoint in native-fp8 mode. - offset = param_and_grad_dtype_to_offsets.get((param.dtype, grad_dtype), 0) - param_and_grad_dtype_to_offsets[(param.dtype, grad_dtype)] = offset + 1 - indices = param_and_grad_dtype_to_indices.get((param_dtype, grad_dtype), []) - indices.append(offset) - param_and_grad_dtype_to_indices[(param_dtype, grad_dtype)] = indices - - if not config.calculate_per_token_loss: - target_gradient_scaling_factor = 1.0 / self.dp_cp_group.size() - if self.ddp_config.average_in_collective: - if self.ddp_config.num_distributed_optimizer_instances == 1: - # Collective is averaging gradients in collective with data_parallel_group. - assert ( - gradient_scaling_factor / data_parallel_group.size() - == target_gradient_scaling_factor - ) - else: - # For non-expert parameters, gradient_scaling_factor is 1. - # For expert parameters, gradient_scaling_factor is edp_size/dp_size. - assert (gradient_scaling_factor == 1) or ( - gradient_scaling_factor - == (self.expt_dp_group.size() / self.dp_cp_group.size()) - ) - else: - assert gradient_scaling_factor == target_gradient_scaling_factor - - # Allocate the grad buffers and map the grads. - buffers = [] - pg_collection = ProcessGroupCollection() - pg_collection.tp = self.tp_group - pg_collection.dp_cp = self.dp_cp_group - for (param_dtype, grad_dtype), params in param_and_grad_dtype_to_params.items(): - buffers.append( - _ParamAndGradBuffer( - self.ddp_config, - param_dtype, - grad_dtype, - params, - data_parallel_group, - self.bucket_size, - param_to_name, - gradient_scaling_factor, - param_and_grad_dtype_to_indices[(param_dtype, grad_dtype)], - self.ddp_config.nccl_ub, - pg_collection, - ) - ) - - # In some scenarios, we want to put buckets from different buffers into a group so that - # their communication can be aggregated. For example, when there are both fp8 buffers - # and bf16 buffers in the model and vpp is enabled, each model chunk will have an fp8 - # bucket and a bf16 bucket, which doubles the number of communication kernels, and - # because of the use of CUDA_DEVICE_MAX_CONNECTIONS=1, having multiple back-to-back - # communications will prevent the overlap of the communication kernels with computation - # kernels. - # If bucketing is explicitly disabled, then put all buckets in a buffer into a single - # bucket group. - bucket_groups = partition_buckets(buffers, force_single_bucket_group=disable_bucketing) - - if self.ddp_config.num_distributed_optimizer_instances > 1: + # When a full_param_layout is provided, verify that the grouping is consistent + # with the layout (same buffer keys, same params per key, same param_indices). + if full_param_layout is not None: + assert set(buffer_groups.keys()) == set(full_param_layout.layouts.keys()), ( + f"Buffer keys from param grouping {set(buffer_groups.keys())} do not match " + f"full_param_layout keys {set(full_param_layout.layouts.keys())}" + ) + for buffer_key, (params, param_indices) in buffer_groups.items(): + layout = full_param_layout.layouts[buffer_key] + assert set(params) == set( + layout.param_index_map.keys() + ), f"Params for {buffer_key} do not match between grouping and layout" assert ( - self.ddp_config.use_distributed_optimizer - ), 'Partial DistOpt cannot be used without DistOpt' - communication_stream = torch.cuda.Stream(device=torch.cuda.current_device()) - for bucket_group in bucket_groups: - bucket_group.inter_distributed_optimizer_instance_group = ( - self.inter_dist_opt_group - ) - bucket_group.communication_stream = communication_stream - - # Set `next_param_gather_bucket_group` for different bucket groups by iterating through - # buckets in reverse order (since all-gathers happen in reverse order of buckets). - # Note: overlap_param_gather covers both the distributed optimizer and the - # layer-wise optimizer cases; the latter sets overlap_param_gather=True - # without use_distributed_optimizer. - if self.ddp_config.overlap_param_gather: - num_bucket_groups = len(bucket_groups) - for i in range(1, num_bucket_groups): - bucket_groups[num_bucket_groups - i].next_param_gather_bucket_group = ( - bucket_groups[num_bucket_groups - i - 1] - ) - - # Create map from param to bucket group, used in pre_hook. - for bucket_group in bucket_groups: - for bucket in bucket_group.buckets: - for param in bucket.params_list: - self.param_to_bucket_group[param] = bucket_group - - return buffers, bucket_groups + param_indices == layout.param_indices + ), f"param_indices for {buffer_key} do not match between grouping and layout" + # Compute gradient scaling factors. if config.calculate_per_token_loss: assert ( not self.ddp_config.average_in_collective @@ -291,20 +202,107 @@ def _allocate_buffers_for_parameters( gradient_scaling_factor = 1.0 / data_parallel_world_size expert_gradient_scaling_factor = 1.0 / data_parallel_world_size - # Allocate the param+grad buffers for dense params' grads. - self.buffers, self.bucket_groups = _allocate_buffers_for_parameters( - dense_params, self.intra_dp_cp_group, gradient_scaling_factor=gradient_scaling_factor - ) + # Allocate buffers for each group. + self.buffers = [] + self.expert_parallel_buffers = [] + pg_collection = ProcessGroupCollection(tp=self.tp_group, dp_cp=self.dp_cp_group) + for buffer_key, (params, param_indices) in buffer_groups.items(): + if buffer_key.is_expert_parallel: + data_parallel_group = self.intra_expt_dp_group + scaling_factor = expert_gradient_scaling_factor + else: + data_parallel_group = self.intra_dp_cp_group + scaling_factor = gradient_scaling_factor - # Allocate separate param+grad buffers for expert parallel params' grads. - self.expert_parallel_buffers, self.expert_parallel_bucket_groups = ( - _allocate_buffers_for_parameters( - expert_parallel_params, - self.intra_expt_dp_group, - gradient_scaling_factor=expert_gradient_scaling_factor, + if not config.calculate_per_token_loss: + target_gradient_scaling_factor = 1.0 / self.dp_cp_group.size() + if self.ddp_config.average_in_collective: + if self.ddp_config.num_distributed_optimizer_instances == 1: + # Collective is averaging gradients in collective with data_parallel_group. + assert ( + scaling_factor / data_parallel_group.size() + == target_gradient_scaling_factor + ) + else: + # For non-expert parameters, gradient_scaling_factor is 1. + # For expert parameters, gradient_scaling_factor is edp_size/dp_size. + assert (scaling_factor == 1) or ( + scaling_factor == (self.expt_dp_group.size() / self.dp_cp_group.size()) + ) + else: + assert scaling_factor == target_gradient_scaling_factor + + param_layout = ( + full_param_layout.layouts.get(buffer_key) if full_param_layout is not None else None ) + params_with_names = [(p, param_to_name[p]) for p in params] + buffer = _ParamAndGradBuffer( + self.ddp_config, + buffer_key.param_dtype, + buffer_key.grad_dtype, + params_with_names, + data_parallel_group, + self.bucket_size, + param_to_name, + scaling_factor, + param_indices, + self.ddp_config.nccl_ub, + pg_collection, + param_layout=param_layout, + ) + if buffer_key.is_expert_parallel: + self.expert_parallel_buffers.append(buffer) + else: + self.buffers.append(buffer) + + # In some scenarios, we want to put buckets from different buffers into a group so that + # their communication can be aggregated. For example, when there are both fp8 buffers + # and bf16 buffers in the model and vpp is enabled, each model chunk will have an fp8 + # bucket and a bf16 bucket, which doubles the number of communication kernels, and + # because of the use of CUDA_DEVICE_MAX_CONNECTIONS=1, having multiple back-to-back + # communications will prevent the overlap of the communication kernels with computation + # kernels. + # If bucketing is explicitly disabled, then put all buckets in a buffer into a single + # bucket group. + self.bucket_groups = partition_buckets( + self.buffers, force_single_bucket_group=disable_bucketing + ) + self.expert_parallel_bucket_groups = partition_buckets( + self.expert_parallel_buffers, force_single_bucket_group=disable_bucketing ) + if self.ddp_config.num_distributed_optimizer_instances > 1: + assert ( + self.ddp_config.use_distributed_optimizer + ), 'Partial DistOpt cannot be used without DistOpt' + for bucket_groups in [self.bucket_groups, self.expert_parallel_bucket_groups]: + communication_stream = torch.cuda.Stream(device=torch.cuda.current_device()) + for bucket_group in bucket_groups: + bucket_group.inter_distributed_optimizer_instance_group = ( + self.inter_dist_opt_group + ) + bucket_group.communication_stream = communication_stream + + # Set `next_param_gather_bucket_group` for different bucket groups by iterating through + # buckets in reverse order (since all-gathers happen in reverse order of buckets). + # Note: overlap_param_gather covers both the distributed optimizer and the + # layer-wise optimizer cases; the latter sets overlap_param_gather=True + # without use_distributed_optimizer. + if self.ddp_config.overlap_param_gather: + for bucket_groups in [self.bucket_groups, self.expert_parallel_bucket_groups]: + num_bucket_groups = len(bucket_groups) + for i in range(1, num_bucket_groups): + bucket_groups[num_bucket_groups - i].next_param_gather_bucket_group = ( + bucket_groups[num_bucket_groups - i - 1] + ) + + # Create map from param to bucket group, used in pre_hook. + for bucket_groups in [self.bucket_groups, self.expert_parallel_bucket_groups]: + for bucket_group in bucket_groups: + for bucket in bucket_group.buckets: + for param in bucket.params_list: + self.param_to_bucket_group[param] = bucket_group + # Delete references to weight_tensor if they exist since we don't want two parameter copies # if we re-mapped parameters (which happens when we use the distributed optimizer). # This is a temporary workaround around a TE bug that is fixed with diff --git a/megatron/core/distributed/param_and_grad_buffer.py b/megatron/core/distributed/param_and_grad_buffer.py index fe0a51b86b7..dc3014d72d2 100644 --- a/megatron/core/distributed/param_and_grad_buffer.py +++ b/megatron/core/distributed/param_and_grad_buffer.py @@ -27,6 +27,7 @@ modify_underlying_storage, post_all_gather_processing, ) +from ..optimizer.param_layout import pad_bucket_end, pad_param_start from ..utils import is_torch_min_version, log_on_each_pipeline_stage from .distributed_data_parallel_config import DistributedDataParallelConfig from .reduce_scatter_with_fp32_accumulation import reduce_scatter_with_fp32_accumulation @@ -753,6 +754,114 @@ def register_grad_ready( self.start_grad_sync(force_all_reduce=force_all_reduce) +def group_params_for_buffers( + params: List[torch.nn.Parameter], grad_reduce_in_fp32: bool +) -> Dict['BufferKey', Tuple[List[torch.nn.Parameter], List[int]]]: + """Group parameters by buffer identity for buffer allocation. + + Each distinct buffer is identified by a BufferKey with three dimensions: + - param_dtype: storage dtype (torch.uint8 for FP8/NVFP4 parameters, else param.dtype). + - grad_dtype: gradient reduction dtype (torch.float if grad_reduce_in_fp32, else param.dtype). + - is_expert_parallel: whether the parameter is expert-parallel (param.allreduce == False), + which requires a separate buffer with a different data-parallel group. + + The param_indices track each parameter's position among same-dtype params (using + the "fake" high-precision dtype for FP8/NVFP4 params), needed for loading non-native-fp8 + checkpoints in native-fp8 mode. + + Args: + params: List of parameters to group. + grad_reduce_in_fp32: Whether gradients are reduced in FP32. + + Returns: + Dict mapping BufferKey to (params_list, param_indices). + """ + from ..optimizer.param_layout import BufferKey + + key_to_params = {} + dtype_to_offsets = {} + key_to_indices = {} + + for param in params: + assert param.requires_grad + + param_dtype = param.dtype + if is_float8tensor(param) or is_nvfp4tensor(param): + param_dtype = torch.uint8 + grad_dtype = torch.float if grad_reduce_in_fp32 else param.dtype + is_expert_parallel = not getattr(param, 'allreduce', True) + + key = BufferKey(param_dtype, grad_dtype, is_expert_parallel) + param_list = key_to_params.get(key, []) + param_list.append(param) + key_to_params[key] = param_list + + # Use param.dtype (not param_dtype) so FP8/NVFP4 params share offsets with their + # logical high-precision dtype, needed for checkpoint compatibility. + offset_key = BufferKey(param.dtype, grad_dtype, is_expert_parallel) + offset = dtype_to_offsets.get(offset_key, 0) + dtype_to_offsets[offset_key] = offset + 1 + indices = key_to_indices.get(key, []) + indices.append(offset) + key_to_indices[key] = indices + + result = {} + for key, param_list in key_to_params.items(): + result[key] = (param_list, key_to_indices[key]) + return result + + +def _compute_default_per_buffer_param_layout( + params: List[torch.nn.Parameter], bucket_size: Optional[int] +) -> 'PerBufferParamLayout': + """Compute parameter layout for the non-distributed-optimizer case. + + No padding is applied. Parameters are iterated in reverse order (backprop order) + and grouped into buckets of approximately `bucket_size` elements. + + Args: + params: List of parameters to lay out. + bucket_size: Approximate number of elements per bucket, or None for a single bucket. + + Returns: + PerBufferParamLayout with the computed mapping. + """ + from ..optimizer.param_layout import PerBufferParamLayout + + param_index_map = {} + bucket_indices = [] + per_bucket_numel_unpadded = [] + + param_start_index = 0 + bucket_start_index = 0 + bucket_params = set() + bucket_id = 0 + + for param in params[::-1]: + this_numel = param.data.nelement() + param_end_index = param_start_index + this_numel + param_index_map[param] = (param_start_index, param_end_index, bucket_id) + bucket_params.add(param) + + if bucket_size is not None and (param_end_index - bucket_start_index) >= bucket_size: + per_bucket_numel_unpadded.append(param_end_index - bucket_start_index) + bucket_indices.append((bucket_start_index, param_end_index)) + bucket_start_index = param_end_index + bucket_params = set() + bucket_id += 1 + param_start_index = param_end_index + + if len(bucket_params) > 0: + per_bucket_numel_unpadded.append(param_end_index - bucket_start_index) + bucket_indices.append((bucket_start_index, param_end_index)) + + return PerBufferParamLayout( + param_index_map=param_index_map, + bucket_indices=bucket_indices, + per_bucket_numel_unpadded=per_bucket_numel_unpadded, + ) + + class _ParamAndGradBuffer: """ Groups parameters and gradients into a contiguous buffer, and then breaks the buffer into @@ -788,6 +897,7 @@ def __init__( param_indices: List[int], nccl_ub: bool, pg_collection: Optional[ProcessGroupCollection] = None, + param_layout: Optional['PerBufferParamLayout'] = None, ): if pg_collection is None: @@ -822,121 +932,13 @@ def __init__( # Data structures to store underlying buckets and relevant indexing data. self.buckets = [] self.param_to_bucket = {} # Param -> bucket mapping. - self.param_index_map = {} # Param -> location in buffer mapping (used in dist. optimizer). - - def _pad(number_to_be_padded: int, divisor: int) -> int: - return int(math.ceil(number_to_be_padded / divisor) * divisor) - - def _pad_end_of_bucket_if_needed(bucket_end_index: int) -> int: - """ - Pads end index of bucket if using distributed optimizer (to ensure uniform sharding). - """ - if self.ddp_config.use_distributed_optimizer: - # Workaround for TE bug causing cuBLAS to pick an incompatible algorithm. - # This also helps cuBLAS pick more efficient algorithms for GEMMs. - # We now ensure that all buckets start at a memory address that is 256-byte - # aligned (128 values since params and grads use >= 16-bit precision). - if self.ddp_config.pad_buckets_for_high_nccl_busbw: - # Make sure the bucket size is divisible by a large power of 2 (2^16) to - # ensure NCCL collectives have high bus bandwidth at large DP counts, - # since NCCL message size (which for ring algorithms is bucket_size / - # dp_size) apparently needs to be divisible by a power of 2 for high busbw. - bucket_size_divisor = math.lcm(self.data_parallel_world_size, 128, 2**16) - else: - bucket_size_divisor = math.lcm(self.data_parallel_world_size, 128) - return _pad(bucket_end_index, bucket_size_divisor) - return bucket_end_index - - def _pad_start_of_param_if_needed(param_start_index: int) -> int: - """ - Pads start index of param if using distributed optimizer (to ensure "good" alignment). - """ - if self.ddp_config.use_distributed_optimizer: - # Ensure that params start at 128-byte aligned addresses (64 values - # since params are >= 16-bit precision). - return _pad(param_start_index, 64) - return param_start_index - # First, figure out how many elements should be in the underlying buffer storage. - # Note that if we need to split the buffer into smaller buckets, each of these - # might need to be padded as well (if using the distributed optimizer). - param_start_index = 0 - bucket_start_index = param_start_index - bucket_params = set() - self.bucket_indices = [] - per_bucket_numel_unpadded = [] - bucket_id = 0 - - def _update_bucket_metadata( - param_end_index: int, - bucket_start_index: int, - bucket_indices: list, - numel_unpadded_list: list, - ) -> int: - """ - Record metadata for a bucket. Returns the bucket's (padded) end_index. - - Args: - param_end_index: End index of the last param in this bucket (unpadded). - bucket_start_index: Start index of this bucket. - bucket_indices: List to append (start, end) bucket boundaries to. - numel_unpadded_list: List to append unpadded bucket numel to. - - Returns: - The bucket's end index, padded if using distributed optimizer. - """ - numel_unpadded_list.append(param_end_index - bucket_start_index) - bucket_end_index = _pad_end_of_bucket_if_needed(param_end_index) - bucket_indices.append((bucket_start_index, bucket_end_index)) - return bucket_end_index - - def _finalize_bucket_all_index_spaces( - param_end_index: int, - bucket_start_index: int, - nvfp4_packed_param_end_index: int = None, - nvfp4_packed_bucket_start_index: int = None, - ) -> tuple: - """ - Record metadata for the current bucket across both main and (if applicable) - NVFP4 packed index spaces. Also resets bucket_params and increments bucket_id. - - Args: - param_end_index: End index of the last param in the bucket (full numel). - bucket_start_index: Start index of the bucket (full numel). - nvfp4_packed_param_end_index: End index in packed space (NVFP4 only). - nvfp4_packed_bucket_start_index: Bucket start in packed space (NVFP4 only). - - Returns: - Tuple of (bucket_end_index, nvfp4_packed_bucket_end_index). - """ - nonlocal bucket_params, bucket_id - bucket_end_index = _update_bucket_metadata( - param_end_index, bucket_start_index, self.bucket_indices, per_bucket_numel_unpadded - ) - nvfp4_packed_bucket_end_index = None - if self.has_nvfp4_params: - nvfp4_packed_bucket_end_index = _update_bucket_metadata( - nvfp4_packed_param_end_index, - nvfp4_packed_bucket_start_index, - self.nvfp4_packed_bucket_indices, - nvfp4_packed_per_bucket_numel_unpadded, - ) - bucket_params = set() - bucket_id += 1 - return bucket_end_index, nvfp4_packed_bucket_end_index - - def _does_param_require_new_bucket(param): - """ - Split shared embedding parameters into separate bucket if using distributed - optimizer that makes use of reduce-scatters instead of all-reduces. - This ensures that the first and last pipeline stage partition optimizer state - for the shared embedding parameters the same way across DP replicas, allowing - the DP reduce-scatter to be before the embedding all-reduce. - """ - return ( - getattr(param, "shared_embedding", False) - and self.ddp_config.use_distributed_optimizer - ) + # Use the provided layout if given, otherwise compute the default (no-padding) layout. + if param_layout is None: + param_layout = _compute_default_per_buffer_param_layout(self.params, bucket_size) + self.param_index_map = param_layout.param_index_map + self.bucket_indices = param_layout.bucket_indices + per_bucket_numel_unpadded = param_layout.per_bucket_numel_unpadded # Check if this buffer contains NVFP4 params. # @@ -953,95 +955,17 @@ def _does_param_require_new_bucket(param): # Grad buffer: [g0, g1, g2, g3, ...] numel = N # # We therefore maintain two index maps: - # - param_index_map: offsets using full numel. + # - param_index_map: offsets using full numel (from pre-computed layout). # - nvfp4_packed_param_index_map: offsets into the packed param buffer (numel // 2). # + # The packed index map is derived from param_index_map by iterating through + # the already-computed layout and halving numel for NVFP4 tensors. + # self.has_nvfp4_params = any(is_nvfp4tensor(p) for p in self.params) - # Secondary (packed) index map, counters, and bucket tracking for NVFP4. - self.nvfp4_packed_param_index_map = {} if self.has_nvfp4_params else None - nvfp4_packed_param_start_index = 0 if self.has_nvfp4_params else None - nvfp4_packed_param_end_index = None - nvfp4_packed_bucket_start_index = 0 if self.has_nvfp4_params else None - self.nvfp4_packed_bucket_indices = [] if self.has_nvfp4_params else None - nvfp4_packed_per_bucket_numel_unpadded = [] if self.has_nvfp4_params else None - - for param, _ in params_with_names[::-1]: - # Iterate through parameters in reverse order to roughly follow backprop order. - - param_start_index = _pad_start_of_param_if_needed(param_start_index) - if self.has_nvfp4_params: - nvfp4_packed_param_start_index = _pad_start_of_param_if_needed( - nvfp4_packed_param_start_index - ) - - # Create bucket with collected parameters if current param needs its own bucket. - if _does_param_require_new_bucket(param) and len(bucket_params) > 0: - # Finalize the current bucket and update start indices for the next bucket. - bucket_start_index, nvfp4_packed_bucket_start_index = ( - _finalize_bucket_all_index_spaces( - param_start_index, - bucket_start_index, - nvfp4_packed_param_start_index, - nvfp4_packed_bucket_start_index, - ) - ) - param_start_index = bucket_start_index - if self.has_nvfp4_params: - nvfp4_packed_param_start_index = nvfp4_packed_bucket_start_index - - # Primary index computation: always uses full param numel. - param_numel = param.data.nelement() - param_end_index = param_start_index + param_numel - self.param_index_map[param] = (param_start_index, param_end_index, bucket_id) - - # Secondary (packed) index computation for NVFP4. - if self.has_nvfp4_params: - if is_nvfp4tensor(param): - assert ( - param_numel % 2 == 0 - ), f"NVFP4 requires even numel for packing, got {param_numel}" - # NVFP4 packs two FP4 values into one byte, so packed numel is half. - nvfp4_packed_param_end_index = nvfp4_packed_param_start_index + param_numel // 2 - else: - nvfp4_packed_param_end_index = nvfp4_packed_param_start_index + param_numel - self.nvfp4_packed_param_index_map[param] = ( - nvfp4_packed_param_start_index, - nvfp4_packed_param_end_index, - bucket_id, - ) - - bucket_params.add(param) - - # If we have enough elements already or the current param is part of the shared - # embedding layer and needs a separate bucket, form a new bucket. - if ( - bucket_size is not None and (param_end_index - bucket_start_index) >= bucket_size - ) or _does_param_require_new_bucket(param): - # Finalize the current bucket and update start indices for the next bucket. - bucket_start_index, nvfp4_packed_bucket_start_index = ( - _finalize_bucket_all_index_spaces( - param_end_index, - bucket_start_index, - nvfp4_packed_param_end_index, - nvfp4_packed_bucket_start_index, - ) - ) - param_start_index = bucket_start_index - if self.has_nvfp4_params: - nvfp4_packed_param_start_index = nvfp4_packed_bucket_start_index - else: - param_start_index = param_end_index - if self.has_nvfp4_params: - nvfp4_packed_param_start_index = nvfp4_packed_param_end_index - - # Add remaining params to a new bucket. - if len(bucket_params) > 0: - _finalize_bucket_all_index_spaces( - param_end_index, - bucket_start_index, - nvfp4_packed_param_end_index, - nvfp4_packed_bucket_start_index, - ) + self.nvfp4_packed_param_index_map = None + self.nvfp4_packed_bucket_indices = None + if self.has_nvfp4_params: + self._compute_nvfp4_packed_layout(params_with_names) # Next, create underlying storage for buffer (with numel elements that includes # padding as necessary). @@ -1049,13 +973,14 @@ def _does_param_require_new_bucket(param): self.numel_unpadded = sum(per_bucket_numel_unpadded) if self.has_nvfp4_params: self.nvfp4_packed_numel = self.nvfp4_packed_bucket_indices[-1][1] - self.nvfp4_packed_numel_unpadded = sum(nvfp4_packed_per_bucket_numel_unpadded) + # nvfp4_packed_numel_unpadded is already set by _compute_nvfp4_packed_layout. assert self.numel_unpadded <= self.numel + if self.has_nvfp4_params: + assert self.nvfp4_packed_numel_unpadded <= self.nvfp4_packed_numel if self.ddp_config.use_distributed_optimizer: assert self.numel % self.data_parallel_world_size == 0 if self.has_nvfp4_params: - assert self.nvfp4_packed_numel_unpadded <= self.nvfp4_packed_numel assert self.nvfp4_packed_numel % self.data_parallel_world_size == 0 else: assert self.numel == self.numel_unpadded @@ -1286,6 +1211,93 @@ def _create_bucket(bucket_id, bucket_params, bucket_params_with_extra_main_grads dp_cp_group=self.dp_cp_group, ) + def _compute_nvfp4_packed_layout(self, params_with_names): + """Derive packed NVFP4 index map and bucket indices from the primary layout. + + The primary layout (self.param_index_map, self.bucket_indices) uses full numel + for all params. NVFP4 tensors pack two FP4 values into one byte, so the param + buffer needs a separate "packed" index map where NVFP4 params occupy half the + space. Non-NVFP4 params keep their full numel in the packed space. + + The same padding rules used by the primary layout are applied here: + - 64-element alignment at the start of each param. + - Bucket-end padding for DP-divisibility (when using distributed optimizer). + + Sets: + self.nvfp4_packed_param_index_map: param -> (start, end, bucket_id) + self.nvfp4_packed_bucket_indices: list of (start, end) per bucket + self.nvfp4_packed_numel_unpadded: total unpadded elements across all buckets + """ + + def _pad_start_of_param(param_start_index: int) -> int: + if self.ddp_config.use_distributed_optimizer: + return pad_param_start(param_start_index) + return param_start_index + + def _pad_end_of_bucket(bucket_end_index: int) -> int: + if self.ddp_config.use_distributed_optimizer: + return pad_bucket_end( + bucket_end_index, + self.data_parallel_world_size, + self.ddp_config.pad_buckets_for_high_nccl_busbw, + ) + return bucket_end_index + + self.nvfp4_packed_param_index_map = {} + self.nvfp4_packed_bucket_indices = [] + nvfp4_packed_per_bucket_numel_unpadded = [] + + packed_param_start = 0 + packed_bucket_start = 0 + cur_bucket_id = 0 + + for param, _ in params_with_names[::-1]: + _, _, bucket_id = self.param_index_map[param] + param_numel = param.data.nelement() + + packed_param_start = _pad_start_of_param(packed_param_start) + + # Finalize previous bucket if we've moved to a new one. + if bucket_id != cur_bucket_id: + # Record unpadded numel, then pad the bucket end. + nvfp4_packed_per_bucket_numel_unpadded.append( + packed_param_start - packed_bucket_start + ) + packed_bucket_end = _pad_end_of_bucket(packed_param_start) + self.nvfp4_packed_bucket_indices.append((packed_bucket_start, packed_bucket_end)) + packed_bucket_start = packed_bucket_end + packed_param_start = packed_bucket_start + cur_bucket_id = bucket_id + + # NVFP4 tensors use half the numel in the packed param buffer. + if is_nvfp4tensor(param): + assert ( + param_numel % 2 == 0 + ), f"NVFP4 requires even numel for packing, got {param_numel}" + packed_numel = param_numel // 2 + else: + packed_numel = param_numel + + packed_param_end = packed_param_start + packed_numel + self.nvfp4_packed_param_index_map[param] = ( + packed_param_start, + packed_param_end, + bucket_id, + ) + packed_param_start = packed_param_end + + # Finalize last bucket. + if packed_param_start > packed_bucket_start: + nvfp4_packed_per_bucket_numel_unpadded.append(packed_param_start - packed_bucket_start) + packed_bucket_end = _pad_end_of_bucket(packed_param_start) + self.nvfp4_packed_bucket_indices.append((packed_bucket_start, packed_bucket_end)) + + assert len(self.nvfp4_packed_bucket_indices) == len(self.bucket_indices), ( + f"Packed bucket count ({len(self.nvfp4_packed_bucket_indices)}) != " + f"primary bucket count ({len(self.bucket_indices)})" + ) + self.nvfp4_packed_numel_unpadded = sum(nvfp4_packed_per_bucket_numel_unpadded) + def scale_gradients(self, scaling_factor: float) -> None: """Scale the gradient data by `scaling_factor`.""" self.grad_data *= scaling_factor diff --git a/megatron/core/optimizer/distrib_optimizer.py b/megatron/core/optimizer/distrib_optimizer.py index b4d52e6b56b..95e8a0c407b 100644 --- a/megatron/core/optimizer/distrib_optimizer.py +++ b/megatron/core/optimizer/distrib_optimizer.py @@ -47,7 +47,11 @@ ShardedTensorFactory, ) from ..dist_checkpointing.utils import extract_sharded_tensors_and_factories -from ..distributed.param_and_grad_buffer import _ParamAndGradBuffer, partition_buckets +from ..distributed.param_and_grad_buffer import ( + _ParamAndGradBuffer, + group_params_for_buffers, + partition_buckets, +) from ..fp4_utils import is_nvfp4tensor, quantize_nvfp4_param_shard from ..fp8_utils import dequantize_fp8_tensor, is_float8tensor, quantize_param_shard from ..transformer.fsdp_dtensor_checkpoint import handle_experts_in_state_dict @@ -55,6 +59,7 @@ from .grad_scaler import MegatronGradScaler from .optimizer import MixedPrecisionOptimizer, _zero_grad_group_helper, param_group_identifier_keys from .optimizer_config import OptimizerConfig +from .param_layout import FullParamLayout, PerBufferParamLayout, pad_bucket_end, pad_param_start logger = getLogger(__name__) @@ -470,6 +475,133 @@ def _build_model_and_main_param_groups( shard_fp32_from_float16_groups, ) + @staticmethod + def _compute_per_buffer_param_layout( + params: List[torch.nn.Parameter], + bucket_size: Optional[int], + data_parallel_world_size: int, + ddp_config, + param_indices: Optional[List[int]] = None, + ) -> 'PerBufferParamLayout': + """Compute how parameters should be laid out in the contiguous buffer. + + Iterates params in reverse order (backprop order), applies 64-byte param + alignment, bucket-end padding for DP divisibility, and shared-embedding + bucket splitting. + + Args: + params: List of parameters to lay out. + bucket_size: Approximate number of elements per bucket, or None for single bucket. + data_parallel_world_size: Size of the data-parallel group. + ddp_config: DistributedDataParallel config object. + param_indices: Optional indices for each param among same-dtype params. + + Returns: + PerBufferParamLayout with the computed mapping. + """ + + def _does_param_require_new_bucket(param): + return getattr(param, "shared_embedding", False) + + param_index_map = {} + bucket_indices = [] + per_bucket_numel_unpadded = [] + + param_start_index = 0 + bucket_start_index = 0 + bucket_params = set() + bucket_id = 0 + + def _finalize_bucket(param_end_index, bucket_start_index, bucket_id): + per_bucket_numel_unpadded.append(param_end_index - bucket_start_index) + bucket_end_index = pad_bucket_end( + param_end_index, + data_parallel_world_size, + ddp_config.pad_buckets_for_high_nccl_busbw, + ) + bucket_indices.append((bucket_start_index, bucket_end_index)) + return bucket_end_index, bucket_id + 1 + + for param in params[::-1]: + param_start_index = pad_param_start(param_start_index) + + # Split shared embedding params into separate bucket. + if _does_param_require_new_bucket(param) and len(bucket_params) > 0: + bucket_start_index, bucket_id = _finalize_bucket( + param_start_index, bucket_start_index, bucket_id + ) + bucket_params = set() + param_start_index = bucket_start_index + + param_numel = param.data.nelement() + param_end_index = param_start_index + param_numel + param_index_map[param] = (param_start_index, param_end_index, bucket_id) + bucket_params.add(param) + + if ( + bucket_size is not None and (param_end_index - bucket_start_index) >= bucket_size + ) or _does_param_require_new_bucket(param): + bucket_start_index, bucket_id = _finalize_bucket( + param_end_index, bucket_start_index, bucket_id + ) + bucket_params = set() + param_start_index = bucket_start_index + else: + param_start_index = param_end_index + + if len(bucket_params) > 0: + _finalize_bucket(param_end_index, bucket_start_index, bucket_id) + + return PerBufferParamLayout( + param_index_map=param_index_map, + bucket_indices=bucket_indices, + per_bucket_numel_unpadded=per_bucket_numel_unpadded, + param_indices=param_indices if param_indices is not None else [], + ) + + @staticmethod + def compute_full_param_layout( + params: List[torch.nn.Parameter], + bucket_size: Optional[int], + data_parallel_world_size: int, + ddp_config, + expert_data_parallel_world_size: Optional[int] = None, + ) -> 'FullParamLayout': + """Compute parameter layouts for all buffer groups. + + Groups parameters by (param_dtype, grad_dtype, is_expert_parallel), then + computes a padded PerBufferParamLayout for each group. Expert-parallel groups use + expert_data_parallel_world_size for padding alignment. + + Args: + params: List of all parameters to lay out. + bucket_size: Approximate number of elements per bucket, or None for single bucket. + data_parallel_world_size: Size of the data-parallel group for dense params. + ddp_config: DistributedDataParallel config object. + expert_data_parallel_world_size: Size of the expert data-parallel group. + Required if any expert-parallel params are present. Defaults to + data_parallel_world_size if not provided. + + Returns: + FullParamLayout with a PerBufferParamLayout per buffer group. + """ + buffer_groups = group_params_for_buffers(params, ddp_config.grad_reduce_in_fp32) + layouts = {} + for buffer_key, (group_params, param_indices) in buffer_groups.items(): + if buffer_key.is_expert_parallel: + dp_world_size = ( + expert_data_parallel_world_size + if expert_data_parallel_world_size is not None + else data_parallel_world_size + ) + else: + dp_world_size = data_parallel_world_size + layout = DistributedOptimizer._compute_per_buffer_param_layout( + group_params, bucket_size, dp_world_size, ddp_config, param_indices + ) + layouts[buffer_key] = layout + return FullParamLayout(layouts=layouts) + def __init__( self, optimizer: torch.optim.Optimizer, diff --git a/megatron/core/optimizer/param_layout.py b/megatron/core/optimizer/param_layout.py new file mode 100644 index 00000000000..6ebcc348f84 --- /dev/null +++ b/megatron/core/optimizer/param_layout.py @@ -0,0 +1,92 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. + +"""Parameter layout dataclasses for optimizer-driven buffer layout. + +These dataclasses describe how parameters are laid out in contiguous buffers. +Each distributed optimizer implementation (e.g., DistributedOptimizer) is +responsible for computing these layouts via a _compute_per_buffer_param_layout method, +applying its own padding, alignment, and bucket splitting rules. DDP and +buffers consume the resulting layouts without any optimizer-specific knowledge. +""" + +import math +from dataclasses import dataclass, field +from typing import Dict, List, Tuple + +import torch + + +def pad_to_divisor(value: int, divisor: int) -> int: + """Round up ``value`` to the nearest multiple of ``divisor``.""" + return int(math.ceil(value / divisor) * divisor) + + +def pad_param_start(param_start_index: int) -> int: + """Align parameter start index to a 64-element boundary.""" + return pad_to_divisor(param_start_index, 64) + + +def pad_bucket_end( + bucket_end_index: int, data_parallel_world_size: int, pad_for_high_nccl_busbw: bool +) -> int: + """Pad bucket end for DP-divisibility (and optionally high NCCL bus bandwidth).""" + if pad_for_high_nccl_busbw: + divisor = math.lcm(data_parallel_world_size, 128, 2**16) + else: + divisor = math.lcm(data_parallel_world_size, 128) + return pad_to_divisor(bucket_end_index, divisor) + + +@dataclass(frozen=True) +class BufferKey: + """Identifies a distinct parameter buffer. + + Each unique combination of these fields corresponds to a separate contiguous + buffer in DDP. Parameters are grouped into buffers by these dimensions. + + Attributes: + param_dtype: Storage dtype (torch.uint8 for FP8/NVFP4 parameters, else param.dtype). + grad_dtype: Gradient reduction dtype. + is_expert_parallel: Whether the buffer holds expert-parallel parameters, + which use a separate data-parallel group. + """ + + param_dtype: torch.dtype + grad_dtype: torch.dtype + is_expert_parallel: bool + + +@dataclass +class PerBufferParamLayout: + """Layout for parameters within a single contiguous buffer. + + Describes how parameters should be laid out in the contiguous buffer. + + Attributes: + param_index_map: Mapping from parameter to (start_index, end_index, bucket_id) in buffer. + bucket_indices: List of (start_index, end_index) for each bucket. + per_bucket_numel_unpadded: Number of unpadded elements per bucket. + param_indices: The index of each param among same-dtype params (using the "fake" + high-precision dtype for FP8/NVFP4 params). Needed for loading non-native-fp8 + checkpoints in native-fp8 mode. Order matches param_index_map iteration order. + """ + + param_index_map: Dict[torch.nn.Parameter, Tuple[int, int, int]] = field(default_factory=dict) + bucket_indices: List[Tuple[int, int]] = field(default_factory=list) + per_bucket_numel_unpadded: List[int] = field(default_factory=list) + param_indices: List[int] = field(default_factory=list) + + +@dataclass +class FullParamLayout: + """Layout for all parameters across all buffer groups in a model chunk. + + Maps BufferKey to per-buffer PerBufferParamLayout objects. Each PerBufferParamLayout has its + own independent index space since different buffer groups are physically + separate buffers. + + Attributes: + layouts: Mapping from BufferKey to PerBufferParamLayout. + """ + + layouts: Dict[BufferKey, PerBufferParamLayout] = field(default_factory=dict) diff --git a/megatron/training/training.py b/megatron/training/training.py index d8825a7b52f..5d97412d8f1 100644 --- a/megatron/training/training.py +++ b/megatron/training/training.py @@ -1506,18 +1506,49 @@ def build_model(): dp_init_kwargs = {} if args.use_megatron_fsdp: dp_init_kwargs["pg_collection"] = pg_collection - model = [ - DP( - config=config, - ddp_config=ddp_config, - module=model_chunk, - # Turn off bucketing for model_chunk 2 onwards, since communication - # for these model chunks is overlapped with compute anyway. - disable_bucketing=(model_chunk_idx > 0) or args.overlap_param_gather_with_optimizer_step, - **dp_init_kwargs, + + wrapped_model = [] + for model_chunk_idx, model_chunk in enumerate(model): + chunk_kwargs = dict(dp_init_kwargs) + disable_bucketing = ( + (model_chunk_idx > 0) + or args.overlap_param_gather_with_optimizer_step + ) + + # Pre-compute parameter layouts for the distributed optimizer. + # Only pass to DDP; FSDP variants don't accept full_param_layout. + if args.use_distributed_optimizer and DP is DDP: + all_params = [ + p for p in model_chunk.parameters() if p.requires_grad + ] + pp_rank = mpu.get_pipeline_model_parallel_rank() + effective_bucket_size = ( + None + if disable_bucketing or pp_rank > 0 + else ddp_config.bucket_size + ) + chunk_kwargs["full_param_layout"] = ( + DistributedOptimizer.compute_full_param_layout( + all_params, + effective_bucket_size, + mpu.get_data_parallel_world_size(with_context_parallel=True), + ddp_config, + expert_data_parallel_world_size=( + mpu.get_expert_data_parallel_world_size() + ), + ) + ) + + wrapped_model.append( + DP( + config=config, + ddp_config=ddp_config, + module=model_chunk, + disable_bucketing=disable_bucketing, + **chunk_kwargs, + ) ) - for (model_chunk_idx, model_chunk) in enumerate(model) - ] + model = wrapped_model # End of setup_stream # Critical: ensure side-stream work completes before touching params on default stream torch.cuda.current_stream().wait_stream(ddp_stream) diff --git a/tests/unit_tests/dist_checkpointing/test_optimizer.py b/tests/unit_tests/dist_checkpointing/test_optimizer.py index 1db844dd9a8..149323707de 100644 --- a/tests/unit_tests/dist_checkpointing/test_optimizer.py +++ b/tests/unit_tests/dist_checkpointing/test_optimizer.py @@ -589,7 +589,7 @@ def test_bucket_space_optimizer_save_load( ) as ckpt_dir_B, ): # Init model and optimizer with "src" bucket padding - with patch('megatron.core.distributed.param_and_grad_buffer.math.lcm') as lcm_mock: + with patch('megatron.core.optimizer.param_layout.math.lcm') as lcm_mock: lcm_mock.return_value = src_bucket_pad_divisor model_A, optimizer_A = setup_model_and_optimizer( @@ -615,7 +615,7 @@ def test_bucket_space_optimizer_save_load( parallel_state.get_model_parallel_group() ) # Init model and optimizer with "dest" bucket padding - with patch('megatron.core.distributed.param_and_grad_buffer.math.lcm') as lcm_mock: + with patch('megatron.core.optimizer.param_layout.math.lcm') as lcm_mock: lcm_mock.return_value = dest_bucket_pad_divisor model_B, optimizer_B = setup_model_and_optimizer( diff --git a/tests/unit_tests/distributed/test_param_and_grad_buffer.py b/tests/unit_tests/distributed/test_param_and_grad_buffer.py index 223383bba64..ac1fdfe2ed6 100644 --- a/tests/unit_tests/distributed/test_param_and_grad_buffer.py +++ b/tests/unit_tests/distributed/test_param_and_grad_buffer.py @@ -11,10 +11,39 @@ from megatron.core import parallel_state from megatron.core.distributed import DistributedDataParallel, DistributedDataParallelConfig from megatron.core.distributed.param_and_grad_buffer import _ParamAndGradBuffer, partition_buckets +from megatron.core.optimizer.distrib_optimizer import DistributedOptimizer from megatron.core.transformer import TransformerConfig from tests.unit_tests.test_utilities import TestModel, Utils +class TestModelWithExperts(torch.nn.Module): + """Model with both dense and expert-parallel parameters. + + Dense layers have the default allreduce=True. Expert layers have + allreduce=False on their parameters, which routes them to a separate + buffer with a different data-parallel group. + """ + + def __init__( + self, + input_dim: int, + output_dim: int, + num_dense_layers: int, + num_expert_layers: int, + bias: bool, + ): + super().__init__() + self.dense_layers = torch.nn.ModuleList( + [torch.nn.Linear(input_dim, output_dim, bias) for _ in range(num_dense_layers)] + ) + self.expert_layers = torch.nn.ModuleList( + [torch.nn.Linear(input_dim, output_dim, bias) for _ in range(num_expert_layers)] + ) + for layer in self.expert_layers: + for param in layer.parameters(): + param.allreduce = False + + def get_model_and_buffers( input_dim: int, output_dim: int, @@ -49,8 +78,18 @@ def get_model_and_buffers( # Wrap with DistributedDataParallel, and get underlying buffer. # Use dummy TransformerConfig with mostly default values. Avoid divide-by-zero # errors for num_attention_heads and num_layers. + # Pre-compute parameter layouts for the distributed optimizer. + full_param_layout = None + if use_distributed_optimizer: + all_params = [p for p in model.parameters() if p.requires_grad] + full_param_layout = DistributedOptimizer.compute_full_param_layout( + all_params, bucket_size, parallel_state.get_data_parallel_world_size(), ddp_config + ) model = DistributedDataParallel( - TransformerConfig(num_attention_heads=1, num_layers=1), ddp_config=ddp_config, module=model + TransformerConfig(num_attention_heads=1, num_layers=1), + ddp_config=ddp_config, + module=model, + full_param_layout=full_param_layout, ) assert len(model.buffers) == 1 param_and_grad_buffer = model.buffers[0] @@ -733,6 +772,14 @@ def mock_packed_shape(shape): average_in_collective=False, ) + # Pre-compute layout for distributed optimizer (with padding); + # otherwise use default (no padding). + param_layout = None + if use_distributed_optimizer: + param_layout = DistributedOptimizer._compute_per_buffer_param_layout( + params, bucket_size, dp_world_size, ddp_config, list(range(len(params))) + ) + with ( mock.patch( 'megatron.core.distributed.param_and_grad_buffer.is_nvfp4tensor', @@ -760,6 +807,7 @@ def mock_packed_shape(shape): param_indices=list(range(len(params))), nccl_ub=False, pg_collection=mock_pg, + param_layout=param_layout, ) return buffer, params @@ -884,3 +932,101 @@ def test_nvfp4_varied_param_sizes(self): assert buffer.param_index_map[params[1]] == (large_unpacked_start, large_unpacked_end, 0) assert buffer.param_index_map[params[0]] == (small_unpacked_start, small_unpacked_end, 0) + + +@pytest.mark.parametrize("use_distributed_optimizer", [False, True]) +def test_expert_parallel_params_get_separate_buffers(use_distributed_optimizer: bool): + """Verify that expert-parallel params (allreduce=False) land in separate buffers + with correctly scoped layouts and independent param_index_maps.""" + Utils.initialize_model_parallel() + + input_dim = 95 + output_dim = 95 + num_dense_layers = 3 + num_expert_layers = 2 + bucket_size = None # Single bucket per buffer. + + ddp_config = DistributedDataParallelConfig( + grad_reduce_in_fp32=True, + use_distributed_optimizer=use_distributed_optimizer, + overlap_grad_reduce=True, + bucket_size=bucket_size, + average_in_collective=False, + ) + model = TestModelWithExperts( + input_dim=input_dim, + output_dim=output_dim, + num_dense_layers=num_dense_layers, + num_expert_layers=num_expert_layers, + bias=True, + ).bfloat16() + + full_param_layout = None + if use_distributed_optimizer: + all_params = [p for p in model.parameters() if p.requires_grad] + full_param_layout = DistributedOptimizer.compute_full_param_layout( + all_params, bucket_size, parallel_state.get_data_parallel_world_size(), ddp_config + ) + + ddp_model = DistributedDataParallel( + TransformerConfig(num_attention_heads=1, num_layers=1), + ddp_config=ddp_config, + module=model, + full_param_layout=full_param_layout, + ) + + # Should have exactly one dense buffer and one expert buffer. + assert len(ddp_model.buffers) == 1, f"Expected 1 dense buffer, got {len(ddp_model.buffers)}" + assert ( + len(ddp_model.expert_parallel_buffers) == 1 + ), f"Expected 1 expert buffer, got {len(ddp_model.expert_parallel_buffers)}" + + dense_buffer = ddp_model.buffers[0] + expert_buffer = ddp_model.expert_parallel_buffers[0] + + # Collect expected params for each buffer. + expected_dense_params = set() + expected_expert_params = set() + for param in model.parameters(): + if not param.requires_grad: + continue + if getattr(param, 'allreduce', True): + expected_dense_params.add(param) + else: + expected_expert_params.add(param) + + # Verify each buffer contains exactly the right params. + dense_buffer_params = set() + for bucket in dense_buffer.buckets: + dense_buffer_params.update(bucket.params) + assert ( + dense_buffer_params == expected_dense_params + ), "Dense buffer should contain exactly the dense params" + + expert_buffer_params = set() + for bucket in expert_buffer.buckets: + expert_buffer_params.update(bucket.params) + assert ( + expert_buffer_params == expected_expert_params + ), "Expert buffer should contain exactly the expert-parallel params" + + # Verify param_index_maps are scoped to their own buffer (no cross-contamination). + assert set(dense_buffer.param_index_map.keys()) == expected_dense_params + assert set(expert_buffer.param_index_map.keys()) == expected_expert_params + + # Verify both buffers have indices starting from 0 (independent index spaces). + dense_starts = [s for s, _, _ in dense_buffer.param_index_map.values()] + expert_starts = [s for s, _, _ in expert_buffer.param_index_map.values()] + assert min(dense_starts) == 0, "Dense buffer indices should start at 0" + assert min(expert_starts) == 0, "Expert buffer indices should start at 0" + + # Verify DP divisibility for distributed optimizer. + if use_distributed_optimizer: + dp_world_size = parallel_state.get_data_parallel_world_size() + for buffer_name, buffer in [("dense", dense_buffer), ("expert", expert_buffer)]: + assert buffer.numel % dp_world_size == 0, ( + f"{buffer_name} buffer numel ({buffer.numel}) should be " + f"divisible by dp_world_size ({dp_world_size})" + ) + + Utils.destroy_model_parallel() diff --git a/tests/unit_tests/distributed/test_param_layout.py b/tests/unit_tests/distributed/test_param_layout.py new file mode 100644 index 00000000000..99922e37970 --- /dev/null +++ b/tests/unit_tests/distributed/test_param_layout.py @@ -0,0 +1,478 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. + +"""Tests for parameter layout computation functions. + +These tests verify the pure-computation layout functions without requiring +GPU or distributed setup: +- pad_to_divisor, pad_param_start, pad_bucket_end (shared padding utilities) +- group_params_for_buffers (parameter grouping by dtype/expert) +- _compute_default_per_buffer_param_layout (no-padding layout) +- DistributedOptimizer._compute_per_buffer_param_layout (padded layout) +- DistributedOptimizer.compute_full_param_layout (end-to-end layout) +""" + +import math +from unittest import mock + +import pytest +import torch + +from megatron.core.distributed import DistributedDataParallelConfig +from megatron.core.distributed.param_and_grad_buffer import ( + _compute_default_per_buffer_param_layout, + group_params_for_buffers, +) +from megatron.core.optimizer.distrib_optimizer import DistributedOptimizer +from megatron.core.optimizer.param_layout import ( + BufferKey, + pad_bucket_end, + pad_param_start, + pad_to_divisor, +) + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _make_params(*shapes, dtype=torch.bfloat16): + """Create a list of nn.Parameters with the given shapes.""" + return [torch.nn.Parameter(torch.randn(s, dtype=dtype)) for s in shapes] + + +def _make_param_with_attrs(shape, dtype=torch.bfloat16, **attrs): + """Create an nn.Parameter with extra attributes (e.g. allreduce, shared_embedding).""" + param = torch.nn.Parameter(torch.randn(shape, dtype=dtype)) + for k, v in attrs.items(): + setattr(param, k, v) + return param + + +# --------------------------------------------------------------------------- +# Tests for shared padding utilities +# --------------------------------------------------------------------------- + + +class TestPaddingUtilities: + + def test_pad_to_divisor_exact_multiple(self): + assert pad_to_divisor(128, 64) == 128 + + def test_pad_to_divisor_rounds_up(self): + assert pad_to_divisor(65, 64) == 128 + + def test_pad_to_divisor_zero(self): + assert pad_to_divisor(0, 64) == 0 + + def test_pad_param_start(self): + assert pad_param_start(0) == 0 + assert pad_param_start(1) == 64 + assert pad_param_start(63) == 64 + assert pad_param_start(64) == 64 + assert pad_param_start(65) == 128 + + def test_pad_bucket_end_basic(self): + dp_size = 4 + divisor = math.lcm(dp_size, 128) + result = pad_bucket_end(1, dp_size, pad_for_high_nccl_busbw=False) + assert result == divisor + assert result % dp_size == 0 + assert result % 128 == 0 + + def test_pad_bucket_end_high_busbw(self): + dp_size = 4 + divisor = math.lcm(dp_size, 128, 2**16) + result = pad_bucket_end(1, dp_size, pad_for_high_nccl_busbw=True) + assert result == divisor + assert result % (2**16) == 0 + + def test_pad_bucket_end_already_aligned(self): + dp_size = 2 + divisor = math.lcm(dp_size, 128) + result = pad_bucket_end(divisor, dp_size, pad_for_high_nccl_busbw=False) + assert result == divisor + + +# --------------------------------------------------------------------------- +# Tests for group_params_for_buffers +# --------------------------------------------------------------------------- + + +class TestGroupParamsForBuffers: + + def test_single_dtype_no_experts(self): + """All bf16 params with no expert-parallel should go in one group.""" + params = _make_params((100, 100), (50, 50)) + result = group_params_for_buffers(params, grad_reduce_in_fp32=True) + + assert len(result) == 1 + key = list(result.keys())[0] + assert key == BufferKey(torch.bfloat16, torch.float, False) + group_params, indices = result[key] + assert group_params == params + assert indices == [0, 1] + + def test_grad_reduce_not_fp32(self): + """When grad_reduce_in_fp32=False, grad_dtype matches param dtype.""" + params = _make_params((100,)) + result = group_params_for_buffers(params, grad_reduce_in_fp32=False) + + key = list(result.keys())[0] + assert key.grad_dtype == torch.bfloat16 + + def test_expert_parallel_separation(self): + """Params with allreduce=False should be in a separate group.""" + dense = _make_param_with_attrs((100,)) + expert = _make_param_with_attrs((100,), allreduce=False) + result = group_params_for_buffers([dense, expert], grad_reduce_in_fp32=True) + + assert len(result) == 2 + dense_key = BufferKey(torch.bfloat16, torch.float, False) + expert_key = BufferKey(torch.bfloat16, torch.float, True) + assert dense_key in result + assert expert_key in result + assert result[dense_key][0] == [dense] + assert result[expert_key][0] == [expert] + + def test_param_indices_independent_per_group(self): + """Expert and dense groups should have independent param_indices starting at 0.""" + dense_params = _make_params((100,), (200,)) + expert = _make_param_with_attrs((100,), allreduce=False) + result = group_params_for_buffers( + [dense_params[0], expert, dense_params[1]], grad_reduce_in_fp32=True + ) + + dense_key = BufferKey(torch.bfloat16, torch.float, False) + expert_key = BufferKey(torch.bfloat16, torch.float, True) + _, dense_indices = result[dense_key] + _, expert_indices = result[expert_key] + assert dense_indices == [0, 1] + assert expert_indices == [0] + + def test_mixed_dtypes(self): + """Params with different dtypes go in separate groups.""" + bf16_param = _make_params((100,), dtype=torch.bfloat16)[0] + fp32_param = _make_params((100,), dtype=torch.float32)[0] + result = group_params_for_buffers([bf16_param, fp32_param], grad_reduce_in_fp32=True) + + assert len(result) == 2 + bf16_key = BufferKey(torch.bfloat16, torch.float, False) + fp32_key = BufferKey(torch.float32, torch.float, False) + assert bf16_key in result + assert fp32_key in result + + +# --------------------------------------------------------------------------- +# Tests for _compute_default_per_buffer_param_layout +# --------------------------------------------------------------------------- + + +class TestDefaultParamLayout: + + def test_single_bucket_no_padding(self): + """With bucket_size=None, all params go in one bucket with no padding.""" + params = _make_params((100, 100), (50, 50)) + layout = _compute_default_per_buffer_param_layout(params, bucket_size=None) + + # Params iterated in reverse: params[1] first (2500 elems), params[0] second (10000). + assert layout.param_index_map[params[1]] == (0, 2500, 0) + assert layout.param_index_map[params[0]] == (2500, 12500, 0) + assert layout.bucket_indices == [(0, 12500)] + assert layout.per_bucket_numel_unpadded == [12500] + + def test_multiple_buckets(self): + """Params should split into buckets when exceeding bucket_size.""" + params = _make_params((100, 100), (100, 100), (100, 100)) + layout = _compute_default_per_buffer_param_layout(params, bucket_size=15000) + + # Reverse order: params[2], params[1], params[0]. Each is 10000 elems. + # After params[2]: 10000 < 15000, continue. + # After params[1]: 20000 >= 15000, finalize bucket. + # After params[0]: 10000 < 15000, finalize at end. + assert len(layout.bucket_indices) == 2 + assert layout.per_bucket_numel_unpadded[0] == 20000 + assert layout.per_bucket_numel_unpadded[1] == 10000 + + def test_no_padding_applied(self): + """Default layout should never add padding.""" + params = _make_params((97,), (103,)) + layout = _compute_default_per_buffer_param_layout(params, bucket_size=None) + + total_numel = 97 + 103 + assert layout.bucket_indices == [(0, total_numel)] + assert layout.per_bucket_numel_unpadded == [total_numel] + + def test_single_param(self): + params = _make_params((256,)) + layout = _compute_default_per_buffer_param_layout(params, bucket_size=None) + + assert layout.param_index_map[params[0]] == (0, 256, 0) + assert layout.bucket_indices == [(0, 256)] + + +# --------------------------------------------------------------------------- +# Tests for DistributedOptimizer._compute_per_buffer_param_layout +# --------------------------------------------------------------------------- + + +class TestDistOptParamLayout: + + @staticmethod + def _make_ddp_config(**overrides): + defaults = dict( + grad_reduce_in_fp32=False, + use_distributed_optimizer=True, + overlap_grad_reduce=True, + bucket_size=None, + average_in_collective=False, + ) + defaults.update(overrides) + return DistributedDataParallelConfig(**defaults) + + def test_param_start_64_alignment(self): + """Each param's start index should be 64-aligned.""" + # 97 is not a multiple of 64, so second param must be padded. + params = _make_params((97,), (103,)) + ddp_config = self._make_ddp_config() + layout = DistributedOptimizer._compute_per_buffer_param_layout( + params, bucket_size=None, data_parallel_world_size=2, ddp_config=ddp_config + ) + + for param in params: + start, end, _ = layout.param_index_map[param] + assert start % 64 == 0, f"Start {start} should be 64-aligned" + assert end - start == param.numel() + + def test_bucket_end_dp_divisible(self): + """Each bucket end should be divisible by lcm(dp_size, 128).""" + params = _make_params((1000,), (1000,)) + dp_size = 4 + ddp_config = self._make_ddp_config() + layout = DistributedOptimizer._compute_per_buffer_param_layout( + params, bucket_size=None, data_parallel_world_size=dp_size, ddp_config=ddp_config + ) + + divisor = math.lcm(dp_size, 128) + for start, end in layout.bucket_indices: + assert end % divisor == 0, f"Bucket end {end} should be divisible by {divisor}" + + def test_bucket_end_high_busbw_padding(self): + """With pad_buckets_for_high_nccl_busbw, bucket end should be divisible by 2^16.""" + params = _make_params((1000,)) + dp_size = 2 + ddp_config = self._make_ddp_config(pad_buckets_for_high_nccl_busbw=True) + layout = DistributedOptimizer._compute_per_buffer_param_layout( + params, bucket_size=None, data_parallel_world_size=dp_size, ddp_config=ddp_config + ) + + divisor = math.lcm(dp_size, 128, 2**16) + for _, end in layout.bucket_indices: + assert end % divisor == 0 + + def test_shared_embedding_gets_separate_bucket(self): + """Params with shared_embedding=True should be placed in their own bucket.""" + regular = _make_param_with_attrs((1000,)) + shared = _make_param_with_attrs((1000,), shared_embedding=True) + # Reverse order: shared first (since it's last in list), then regular. + params = [regular, shared] + ddp_config = self._make_ddp_config() + layout = DistributedOptimizer._compute_per_buffer_param_layout( + params, bucket_size=None, data_parallel_world_size=2, ddp_config=ddp_config + ) + + # shared_embedding param should be in its own bucket. + _, _, shared_bucket = layout.param_index_map[shared] + _, _, regular_bucket = layout.param_index_map[regular] + assert shared_bucket != regular_bucket + + def test_shared_embedding_as_first_reversed_param_no_extra_bucket(self): + """If shared_embedding param is the first in reversed order (last in list), + it should not create an empty extra bucket before it.""" + shared = _make_param_with_attrs((1000,), shared_embedding=True) + regular = _make_param_with_attrs((1000,)) + # Reverse order: regular first, then shared. + params = [shared, regular] + ddp_config = self._make_ddp_config() + layout = DistributedOptimizer._compute_per_buffer_param_layout( + params, bucket_size=None, data_parallel_world_size=2, ddp_config=ddp_config + ) + + # Both params should be in their own buckets (shared splits after regular). + assert len(layout.bucket_indices) == 2 + + def test_multiple_buckets_with_bucket_size(self): + """Verify bucket splitting with an explicit bucket_size.""" + params = _make_params((5000,), (5000,), (5000,)) + dp_size = 2 + ddp_config = self._make_ddp_config() + layout = DistributedOptimizer._compute_per_buffer_param_layout( + params, bucket_size=8000, data_parallel_world_size=dp_size, ddp_config=ddp_config + ) + + # Each param is 5000 elems. With 64-alignment: + # Bucket 0: params[2] starts at 0, ends at 5000 (< 8000); params[1] starts at 5000 + # (already 64-aligned since 5000 rounds to 5056), ends at 10056 >= 8000 → finalize. + # Bucket 1: params[0]. + assert len(layout.bucket_indices) == 2 + assert len(layout.per_bucket_numel_unpadded) == 2 + + def test_numel_unpadded_vs_padded(self): + """per_bucket_numel_unpadded should be <= padded bucket size.""" + params = _make_params((1000,)) + dp_size = 8 + ddp_config = self._make_ddp_config() + layout = DistributedOptimizer._compute_per_buffer_param_layout( + params, bucket_size=None, data_parallel_world_size=dp_size, ddp_config=ddp_config + ) + + for i, (start, end) in enumerate(layout.bucket_indices): + padded_numel = end - start + assert layout.per_bucket_numel_unpadded[i] <= padded_numel + + def test_shared_embedding_with_bucket_size(self): + """Shared embedding that hits bucket_size threshold should still get its own bucket.""" + # 3 regular params + 1 shared embedding, each 5000 elements. + # With bucket_size=8000, regular params would form 2-param buckets, + # but shared embedding must always be isolated. + regulars = [_make_param_with_attrs((5000,)) for _ in range(3)] + shared = _make_param_with_attrs((5000,), shared_embedding=True) + # Order: regulars first, shared last → reversed: shared first. + params = regulars + [shared] + dp_size = 2 + ddp_config = self._make_ddp_config() + layout = DistributedOptimizer._compute_per_buffer_param_layout( + params, bucket_size=8000, data_parallel_world_size=dp_size, ddp_config=ddp_config + ) + + # Shared embedding must be alone in its bucket. + _, _, shared_bucket = layout.param_index_map[shared] + shared_bucket_params = [ + p for p, (_, _, bid) in layout.param_index_map.items() if bid == shared_bucket + ] + assert len(shared_bucket_params) == 1 + assert shared_bucket_params[0] is shared + + def test_layout_matches_default_when_no_padding_needed(self): + """When params are 64-aligned and bucket_size=None, distributed optimizer layout + should produce the same param_index_map ordering as the default layout + (only bucket end padding differs).""" + # Use param sizes that are multiples of 64 to avoid start-of-param padding. + params = _make_params((64 * 10,), (64 * 20,), (64 * 15,)) + ddp_config = self._make_ddp_config() + dist_layout = DistributedOptimizer._compute_per_buffer_param_layout( + params, bucket_size=None, data_parallel_world_size=1, ddp_config=ddp_config + ) + default_layout = _compute_default_per_buffer_param_layout(params, bucket_size=None) + + # With dp_size=1, lcm(1, 128) = 128, so bucket end padding may differ. + # But param ordering and unpadded numel should match. + for param in params: + dist_start, dist_end, dist_bid = dist_layout.param_index_map[param] + def_start, def_end, def_bid = default_layout.param_index_map[param] + assert dist_start == def_start, f"Start mismatch for param: {dist_start} vs {def_start}" + assert dist_end == def_end, f"End mismatch for param: {dist_end} vs {def_end}" + assert dist_bid == def_bid, f"Bucket ID mismatch for param: {dist_bid} vs {def_bid}" + assert dist_layout.per_bucket_numel_unpadded == default_layout.per_bucket_numel_unpadded + + +# --------------------------------------------------------------------------- +# Tests for DistributedOptimizer.compute_full_param_layout +# --------------------------------------------------------------------------- + + +class TestComputeFullParamLayout: + + @staticmethod + def _make_ddp_config(**overrides): + defaults = dict( + grad_reduce_in_fp32=False, + use_distributed_optimizer=True, + overlap_grad_reduce=True, + bucket_size=None, + average_in_collective=False, + ) + defaults.update(overrides) + return DistributedDataParallelConfig(**defaults) + + def test_dense_only(self): + """With only dense params, should produce a single layout.""" + params = _make_params((100, 100), (50, 50)) + ddp_config = self._make_ddp_config() + full_layout = DistributedOptimizer.compute_full_param_layout( + params, bucket_size=None, data_parallel_world_size=2, ddp_config=ddp_config + ) + + assert len(full_layout.layouts) == 1 + key = list(full_layout.layouts.keys())[0] + assert key.is_expert_parallel is False + layout = full_layout.layouts[key] + assert set(layout.param_index_map.keys()) == set(params) + + def test_dense_and_expert_separate_layouts(self): + """Dense and expert-parallel params should get independent layouts.""" + dense = _make_param_with_attrs((100, 100)) + expert = _make_param_with_attrs((100, 100), allreduce=False) + ddp_config = self._make_ddp_config() + full_layout = DistributedOptimizer.compute_full_param_layout( + [dense, expert], bucket_size=None, data_parallel_world_size=2, ddp_config=ddp_config + ) + + assert len(full_layout.layouts) == 2 + dense_key = BufferKey(torch.bfloat16, torch.bfloat16, False) + expert_key = BufferKey(torch.bfloat16, torch.bfloat16, True) + assert dense_key in full_layout.layouts + assert expert_key in full_layout.layouts + + # Each layout should only contain its own params. + assert set(full_layout.layouts[dense_key].param_index_map.keys()) == {dense} + assert set(full_layout.layouts[expert_key].param_index_map.keys()) == {expert} + + # Both should start at index 0 (independent index spaces). + dense_starts = [s for s, _, _ in full_layout.layouts[dense_key].param_index_map.values()] + expert_starts = [s for s, _, _ in full_layout.layouts[expert_key].param_index_map.values()] + assert min(dense_starts) == 0 + assert min(expert_starts) == 0 + + def test_expert_uses_expert_dp_world_size(self): + """Expert-parallel layout should use expert_data_parallel_world_size for padding.""" + dense = _make_param_with_attrs((1000,)) + expert = _make_param_with_attrs((1000,), allreduce=False) + ddp_config = self._make_ddp_config() + + # Dense dp_size=3, expert dp_size=256. + # lcm(3, 128) = 384, lcm(256, 128) = 256 — different divisors. + full_layout = DistributedOptimizer.compute_full_param_layout( + [dense, expert], + bucket_size=None, + data_parallel_world_size=3, + ddp_config=ddp_config, + expert_data_parallel_world_size=256, + ) + + dense_key = BufferKey(torch.bfloat16, torch.bfloat16, False) + expert_key = BufferKey(torch.bfloat16, torch.bfloat16, True) + + # Expert bucket end should be divisible by lcm(256, 128) = 256. + expert_divisor = math.lcm(256, 128) + assert expert_divisor == 256 + for _, end in full_layout.layouts[expert_key].bucket_indices: + assert end % expert_divisor == 0 + + # Dense bucket end should be divisible by lcm(3, 128) = 384. + dense_divisor = math.lcm(3, 128) + assert dense_divisor == 384 + for _, end in full_layout.layouts[dense_key].bucket_indices: + assert end % dense_divisor == 0 + + def test_param_indices_populated(self): + """compute_full_param_layout should populate param_indices on each layout.""" + params = _make_params((100,), (200,), (300,)) + ddp_config = self._make_ddp_config() + full_layout = DistributedOptimizer.compute_full_param_layout( + params, bucket_size=None, data_parallel_world_size=2, ddp_config=ddp_config + ) + + layout = list(full_layout.layouts.values())[0] + assert len(layout.param_indices) == 3 + assert min(layout.param_indices) == 0 + assert max(layout.param_indices) == 2 From 90e09b68c7ddbf36f168b2bdc123ed0f77fc39cd Mon Sep 17 00:00:00 2001 From: Philip Petrakian Date: Wed, 22 Apr 2026 13:57:52 -0700 Subject: [PATCH 007/105] Update PR template with explicit request for issue (#4409) --- .github/pull_request_template.md | 9 +++++++++ docs/developer/contribute.md | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d2825f9c34b..8f319e66f87 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -3,6 +3,15 @@ :warning: For major changes (either in lines of code or in its impact), please make sure to first share a design doc with the team. If you're unsure what's the best way to do so, contact the @mcore-oncall. +## Issue tracking + +For PRs from open-source community contributors: + +- **New features**: a linked issue is **required**. Please open a [feature request](https://github.com/NVIDIA/Megatron-LM/issues/new?template=feature_request.md) and reference it here before submitting the PR. +- **Small updates (bug fixes, minor improvements)**: a linked issue is **recommended** and will accelerate the PR review process. + +Linked issue: + ## Contribution process ### Pre-checks diff --git a/docs/developer/contribute.md b/docs/developer/contribute.md index aeb785f915d..30a39e1cbc0 100644 --- a/docs/developer/contribute.md +++ b/docs/developer/contribute.md @@ -13,7 +13,7 @@ This document outlines the processes and policies for issues and pull requests b Everyone is welcome to contribute to the project! We recently migrated from using an internal repo to doing all development directly from the GitHub repository. -When contributing it is important to ensure that changes are in line with the project direction. Small changes to fix bugs are welcomed and appreciated. If proposing large architectural changes or changes for stylistic reasons open an issue first so we can discuss it. +When contributing it is important to ensure that changes are in line with the project direction. Small changes to fix bugs are welcomed and appreciated. **If proposing large architectural changes or changes for stylistic reasons open an issue first so we can discuss it.** ## Issue policy From ab2b33d549247e44e269af1365f8a5589a09acb2 Mon Sep 17 00:00:00 2001 From: Siddharth Singh Date: Wed, 22 Apr 2026 21:19:40 -0700 Subject: [PATCH 008/105] Misc inference fixes (#4397) --- megatron/core/inference/moe/activations.py | 2 +- .../endpoints/completions.py | 55 +++++++++++++++++-- megatron/core/ssm/ops/mamba_ssm.py | 43 +++++++++++---- 3 files changed, 83 insertions(+), 17 deletions(-) diff --git a/megatron/core/inference/moe/activations.py b/megatron/core/inference/moe/activations.py index 169d8499116..b69d092cc1b 100644 --- a/megatron/core/inference/moe/activations.py +++ b/megatron/core/inference/moe/activations.py @@ -46,7 +46,7 @@ def _squared_relu_kernel(input_ptr, output_ptr, src_idx_ptr, M, N, BLOCK_N: tl.c def padded_squared_relu(x: torch.Tensor, permutation_map: torch.Tensor) -> torch.Tensor: """Squared ReLU activation that skips padding rows.""" M, N = x.shape - out = torch.zeros(M, N, dtype=x.dtype, device=x.device) + out = torch.empty(M, N, dtype=x.dtype, device=x.device) BLOCK_N = min(triton.next_power_of_2(N), 1024) _squared_relu_kernel[(M,)](x, out, permutation_map, M, N, BLOCK_N=BLOCK_N) return out diff --git a/megatron/core/inference/text_generation_server/dynamic_text_gen_server/endpoints/completions.py b/megatron/core/inference/text_generation_server/dynamic_text_gen_server/endpoints/completions.py index d2279b0d07d..6f57a863c1c 100644 --- a/megatron/core/inference/text_generation_server/dynamic_text_gen_server/endpoints/completions.py +++ b/megatron/core/inference/text_generation_server/dynamic_text_gen_server/endpoints/completions.py @@ -3,6 +3,7 @@ import asyncio import logging import time +import uuid from megatron.core.inference.inference_request import unwrap_serialized_tensors from megatron.core.inference.sampling_params import SamplingParams @@ -92,6 +93,8 @@ async def completions(): if isinstance(stop, str): stop = [stop] + ignore_eos = bool(req.get("ignore_eos", False)) + sampling_params = SamplingParams( temperature=temperature, top_k=top_k, @@ -101,6 +104,7 @@ async def completions(): skip_prompt_log_probs=skip_prompt_log_probs, num_tokens_to_generate=int(req.get("max_tokens", 16)), stop_words=stop, + termination_id=-1 if ignore_eos else None, ) except ValueError as e: return f"Invalid sampling parameter: {e}", 400 @@ -117,6 +121,7 @@ async def completions(): skip_prompt_log_probs=sampling_params.skip_prompt_log_probs, num_tokens_to_generate=sampling_params.num_tokens_to_generate, stop_words=sampling_params.stop_words, + termination_id=sampling_params.termination_id, ) tasks.append(client.add_request(prompt_tokens, per_req_params)) @@ -160,6 +165,8 @@ async def completions(): # --- 5. Format Response (matching old_completions.py) --- choices = [] + total_completion_tokens = 0 + prompt_tokens_counts = [] request_idx = 0 for completed_request in batch_results: @@ -167,6 +174,17 @@ async def completions(): full_text = result["generated_text"] or "" text_output = (prompts_as_strings[request_idx] + full_text) if echo else full_text + generated_tokens = result.get("generated_tokens") or [] + prompt_tokens_list = result.get("prompt_tokens") or [] + total_completion_tokens += len(generated_tokens) + prompt_tokens_counts.append(len(prompt_tokens_list)) + + finish_reason = "length" + sampling_params_result = result.get("sampling_params") or {} + num_tokens_requested = sampling_params_result.get("num_tokens_to_generate") + if num_tokens_requested is None or len(generated_tokens) < num_tokens_requested: + finish_reason = "stop" + logprobs_data = None if sampling_params.return_log_probs: # Get prompt tokens and logprobs @@ -230,20 +248,49 @@ async def completions(): "top_logprobs": top_logprobs, } - choices.append({"index": request_idx, "text": text_output, "logprobs": logprobs_data}) + choice_data = { + "index": request_idx, + "text": text_output, + "logprobs": logprobs_data, + "finish_reason": finish_reason, + "prompt_token_ids": result["prompt_tokens"], + "generation_token_ids": result["generated_tokens"], + "generation_log_probs": result.get("generated_log_probs", []), + } + choice_data["policy_epoch"] = result["policy_epoch"] + choice_data["kv_cache_epoch"] = result["kv_cache_epoch"] + choice_data["num_evictions"] = sum( + 1 for e in result["events"] if e.get("type") == "EVICT" + ) + if result["routing_indices"] is not None: - choices[-1]["moe_topk_indices"] = result["routing_indices"] + choice_data["moe_topk_indices"] = result["routing_indices"] prompt_length = ( len(result["prompt_tokens"]) if result["prompt_tokens"] is not None else 0 ) if prompt_length: - choices[-1]["prompt_moe_topk_indices"] = result["routing_indices"][ + choice_data["prompt_moe_topk_indices"] = result["routing_indices"][ :prompt_length ] + choices.append(choice_data) request_idx += 1 - return jsonify({"choices": choices}) + prompt_token_count = max(prompt_tokens_counts) if prompt_tokens_counts else 0 + return jsonify( + { + "id": str(uuid.uuid4()), + "object": "text_completion", # as per the openAI spec + "created": int(time.time()), + "model": "EMPTY", + "choices": choices, + "usage": { + "prompt_tokens": prompt_token_count, + "completion_tokens": total_completion_tokens, + "total_tokens": prompt_token_count + total_completion_tokens, + }, + } + ) except ImportError as e: logger.warning(f"Could not import quart: {e}") diff --git a/megatron/core/ssm/ops/mamba_ssm.py b/megatron/core/ssm/ops/mamba_ssm.py index 4e079da8a31..672b00cf7dc 100644 --- a/megatron/core/ssm/ops/mamba_ssm.py +++ b/megatron/core/ssm/ops/mamba_ssm.py @@ -2,9 +2,11 @@ # Copyright (c) 2024, Tri Dao, Albert Gu. # Some of this code was adopted from https://github.com/state-spaces/mamba/ +# and https://github.com/vllm-project/vllm. # This source code is licensed under the Apache license found in the # LICENSE file in the root directory of this source tree. + import torch from packaging import version @@ -41,6 +43,15 @@ def softplus(dt): return tl.math.log1p(tl.exp(dt)) +@triton.jit +def fast_exp(x): + """ + Fast calculation of exponent via exponent of 2. + """ + LOG2E = tl.constexpr(1.4426950408889634) + return tl.math.exp2(LOG2E * x) + + @triton.heuristics({"HAS_DT_BIAS": lambda args: args["dt_bias_ptr"] is not None}) @triton.heuristics({"HAS_D": lambda args: args["D_ptr"] is not None}) @triton.heuristics({"HAS_Z": lambda args: args["z_ptr"] is not None}) @@ -49,7 +60,7 @@ def softplus(dt): ) @triton.heuristics({"HAS_INT_STATE": lambda args: args["int_state_ptr"] is not None}) @triton.heuristics({"BLOCK_SIZE_DSTATE": lambda args: triton.next_power_of_2(args["dstate"])}) -@triton.jit +@triton.jit(do_not_specialize=["batch"]) def _selective_scan_update_kernel( # Pointers to matrices state_ptr, @@ -223,14 +234,14 @@ def _selective_scan_update_kernel( dt += tl.load(dt_bias_ptrs, mask=offs_m < dim, other=0.0).to(tl.float32) if DT_SOFTPLUS: dt = tl.where(dt <= 20.0, softplus(dt), dt) - dA = tl.exp(A * dt[:, None]) + dA = fast_exp(A * dt[:, None]) else: dt = tl.load(dt_ptr + s * stride_dt_seq).to(tl.float32) if HAS_DT_BIAS: dt += tl.load(dt_bias_ptr).to(tl.float32) if DT_SOFTPLUS: dt = tl.where(dt <= 20.0, softplus(dt), dt) - dA = tl.exp(A * dt) + dA = fast_exp(A * dt) # Load B and C B = tl.load(B_s_ptrs, mask=offs_n < dstate, other=0.0).to(tl.float32) @@ -368,15 +379,23 @@ def selective_state_update( (z.stride(0), z.stride(1), z.stride(2), z.stride(3)) if z is not None else (0, 0, 0, 0) ) - BLOCK_SIZE_M, num_warps = ( - (32, 4) - if dstate <= 16 - else ( - (16, 4) - if dstate <= 32 - else ((8, 4) if dstate <= 64 else ((4, 4) if dstate <= 128 else ((4, 8)))) - ) - ) + is_blackwell = torch.cuda.get_device_capability(x.device)[0] >= 10 + + # Default + BLOCK_SIZE_M, num_warps = 4, 8 + if dstate <= 16: + BLOCK_SIZE_M, num_warps = 32, 4 + elif dstate <= 32: + BLOCK_SIZE_M, num_warps = 16, 4 + elif dstate <= 64: + BLOCK_SIZE_M, num_warps = 8, 4 + else: + # dstate > 64 + if is_blackwell: + # Optimized for B200 with dstate>64 + BLOCK_SIZE_M, num_warps = 32, 8 + elif dstate <= 128: + BLOCK_SIZE_M, num_warps = 4, 4 tie_hdim = ( A.stride(-1) == 0 From 60408d5f12fb2757df6ba015db5787c3ed648652 Mon Sep 17 00:00:00 2001 From: Philip Petrakian Date: Wed, 22 Apr 2026 21:27:46 -0700 Subject: [PATCH 009/105] Rename Mamba to Hybrid outside megatron/core (#4159) Co-authored-by: Claude Opus 4.6 (1M context) --- examples/mamba/run_text_gen_server_8b.sh | 4 +- examples/mamba/train.sh | 4 +- examples/multimodal/layer_specs.py | 10 +- examples/multimodal/model.py | 6 +- .../NVIDIA-Nemotron-3-Nano-30B-A3B-BF16.sh | 2 +- .../NVIDIA-Nemotron-3-Super-120B-A12B-BF16.sh | 2 +- .../conf/nvidia/NVIDIA-Nemotron-Nano-9B-v2.sh | 2 +- .../nvidia/Nemotron-H-47B-Reasoning-128K.sh | 2 +- .../conf/nvidia/Nemotron-H-4B-Instruct.sh | 2 +- .../conf/nvidia/Nemotron-H-56B-Base-8K.sh | 2 +- .../conf/nvidia/Nemotron-H-8B-Base-8K.sh | 2 +- .../post_training/modelopt/convert_model.py | 4 +- .../post_training/modelopt/distillation.md | 2 +- examples/post_training/modelopt/export.py | 4 +- examples/post_training/modelopt/finetune.py | 4 +- examples/post_training/modelopt/generate.py | 4 +- examples/post_training/modelopt/mmlu.py | 4 +- .../modelopt/offline_feature_extract.py | 4 +- examples/post_training/modelopt/prune.py | 4 +- examples/post_training/modelopt/quantize.py | 4 +- examples/post_training/modelopt/train.sh | 4 +- examples/post_training/modelopt/validate.py | 4 +- examples/rl/model_configs/nemotron5_56b.sh | 2 +- examples/rl/model_configs/nemotron5_8b.sh | 2 +- .../rl/model_configs/nemotron5p5_12b_H.sh | 2 +- hybrid_builders.py | 54 +++ mamba_builders.py | 59 +-- megatron/inference/utils.py | 20 +- megatron/post_training/arguments.py | 7 +- megatron/post_training/model_builder.py | 35 +- megatron/training/arguments.py | 4 +- megatron/training/training.py | 4 +- model_provider.py | 14 +- pretrain_gpt.py | 2 +- pretrain_hybrid.py | 369 +++++++++++++++++ pretrain_mamba.py | 374 +----------------- .../model_config.yaml | 4 +- .../model_config.yaml | 4 +- .../model_config.yaml | 2 +- .../model_config.yaml | 2 +- .../model_config.yaml | 2 +- .../model_config.yaml | 2 +- .../model_config.yaml | 2 +- .../model_config.yaml | 4 +- .../model_config.yaml | 4 +- .../model_config.yaml | 4 +- tests/test_utils/recipes/h100/mamba.yaml | 2 +- .../contexts/test_dynamic_context.py | 2 +- .../inference/engines/test_dynamic_engine.py | 50 +-- ...e.py => test_hybrid_prefix_caching_e2e.py} | 8 +- .../test_prefix_caching_cuda_graphs.py | 12 +- .../models/test_dsa_gpt_mamba_equivalence.py | 50 +-- .../test_modelopt_model_builder.py | 2 +- .../unit_tests/resharding/test_model_swap.py | 10 +- tools/checkpoint/remap_gpt_dsa_to_mamba.py | 22 +- ...y => run_hybrid_text_generation_server.py} | 2 +- ...rid_text_generation_server_completions.py} | 2 +- tools/run_inference_performance_test.py | 2 +- tools/run_text_generation_server.py | 14 +- train_rl.py | 4 +- 60 files changed, 656 insertions(+), 584 deletions(-) create mode 100644 hybrid_builders.py create mode 100644 pretrain_hybrid.py rename tests/unit_tests/inference/engines/{test_mamba_prefix_caching_e2e.py => test_hybrid_prefix_caching_e2e.py} (99%) rename tools/{run_mamba_text_generation_server.py => run_hybrid_text_generation_server.py} (89%) rename tools/{run_mamba_text_generation_server_completions.py => run_hybrid_text_generation_server_completions.py} (89%) diff --git a/examples/mamba/run_text_gen_server_8b.sh b/examples/mamba/run_text_gen_server_8b.sh index d228e0c0edb..f183dea4ad1 100755 --- a/examples/mamba/run_text_gen_server_8b.sh +++ b/examples/mamba/run_text_gen_server_8b.sh @@ -22,7 +22,7 @@ export NCCL_IB_QPS_PER_CONNECTION=4 export TRITON_CACHE_DIR="./triton-cache/" export TRITON_CACHE_MANAGER="megatron.core.ssm.triton_cache_manager:ParallelFileCacheManager" -torchrun $DISTRIBUTED_ARGS ../../tools/run_mamba_text_generation_server.py \ +torchrun $DISTRIBUTED_ARGS ../../tools/run_hybrid_text_generation_server.py \ --tensor-model-parallel-size 1 \ --pipeline-model-parallel-size 1 \ --untie-embeddings-and-output-weights \ @@ -46,5 +46,5 @@ torchrun $DISTRIBUTED_ARGS ../../tools/run_mamba_text_generation_server.py \ --bf16 \ --micro-batch-size 1 \ --use-mcore-models \ - --spec megatron.core.models.mamba.mamba_layer_specs mamba_stack_spec \ + --spec megatron.core.models.hybrid.hybrid_layer_specs hybrid_stack_spec \ --seed 42 diff --git a/examples/mamba/train.sh b/examples/mamba/train.sh index ba83f0d4e33..f971242ff0b 100755 --- a/examples/mamba/train.sh +++ b/examples/mamba/train.sh @@ -96,8 +96,8 @@ options=" \ --eval-iters 32 \ --bf16 \ --use-mcore-models \ - --spec megatron.core.models.mamba.mamba_layer_specs mamba_stack_spec \ + --spec megatron.core.models.hybrid.hybrid_layer_specs hybrid_stack_spec \ --no-create-attention-mask-in-dataloader \ --tensorboard-dir ${TENSORBOARD_DIR}" -torchrun --nproc_per_node 8 ../../pretrain_mamba.py ${options} +torchrun --nproc_per_node 8 ../../pretrain_hybrid.py ${options} diff --git a/examples/multimodal/layer_specs.py b/examples/multimodal/layer_specs.py index ad24850b631..acced15eeb6 100644 --- a/examples/multimodal/layer_specs.py +++ b/examples/multimodal/layer_specs.py @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2024-2026, NVIDIA CORPORATION. All rights reserved. import torch from megatron.core.fusions.fused_bias_dropout import get_bias_dropout_add -from megatron.core.ssm.mamba_block import MambaStack, MambaStackSubmodules +from megatron.core.models.hybrid.hybrid_block import HybridStack, HybridStackSubmodules from megatron.core.ssm.mamba_layer import MambaLayer, MambaLayerSubmodules from megatron.core.ssm.mamba_mixer import MambaMixer, MambaMixerSubmodules from megatron.core.ssm.mlp_layer import MLPLayer @@ -125,15 +125,15 @@ def get_layer_spec_te(is_vit=False, padding=False) -> ModuleSpec: ) -def get_mamba_layer_spec_te(padding=False) -> ModuleSpec: +def get_hybrid_layer_spec_te(padding=False) -> ModuleSpec: attn_mask_type = AttnMaskType.causal # Padding mask is needed for e.g. Context Parallel. if padding: attn_mask_type = AttnMaskType.padding_causal return ModuleSpec( - module=MambaStack, - submodules=MambaStackSubmodules( + module=HybridStack, + submodules=HybridStackSubmodules( mamba_layer=ModuleSpec( module=MambaLayer, submodules=MambaLayerSubmodules( diff --git a/examples/multimodal/model.py b/examples/multimodal/model.py index 494a854099e..a2d83428338 100644 --- a/examples/multimodal/model.py +++ b/examples/multimodal/model.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2024-2026, NVIDIA CORPORATION. All rights reserved. import warnings import logging from copy import deepcopy @@ -6,7 +6,7 @@ import torch from config import get_language_model_config, get_vision_model_config, get_vision_projection_config from layer_specs import (get_layer_spec, get_layer_spec_te, get_mlp_module_spec, get_norm_mlp_module_spec_te, - get_mamba_layer_spec_te) + get_hybrid_layer_spec_te) from megatron.core.models.multimodal.llava_model import IMAGE_TOKEN, LLaVAModel from megatron.core.models.vision.clip_vit_model import get_num_image_embeddings @@ -99,7 +99,7 @@ def model_provider( # Padding mask needed for SP/CP. padding = args.context_parallel_size > 1 and args.sequence_parallel if args.language_model_type.startswith('nemotron5-hybrid'): - language_transformer_layer_spec = get_mamba_layer_spec_te(padding=padding) + language_transformer_layer_spec = get_hybrid_layer_spec_te(padding=padding) else: language_transformer_layer_spec = get_layer_spec_te( is_vit=False, padding=padding diff --git a/examples/post_training/modelopt/conf/nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16.sh b/examples/post_training/modelopt/conf/nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16.sh index 1fa00889e99..805302498fc 100644 --- a/examples/post_training/modelopt/conf/nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16.sh +++ b/examples/post_training/modelopt/conf/nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16.sh @@ -51,5 +51,5 @@ MODEL_ARGS=" \ --bf16 \ --seq-length 8192 \ --max-position-embeddings 8192 \ - --export-model-type MambaModel \ + --export-model-type HybridModel \ " diff --git a/examples/post_training/modelopt/conf/nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-BF16.sh b/examples/post_training/modelopt/conf/nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-BF16.sh index 977be033df0..b9da9429eb5 100644 --- a/examples/post_training/modelopt/conf/nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-BF16.sh +++ b/examples/post_training/modelopt/conf/nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-BF16.sh @@ -58,5 +58,5 @@ MODEL_ARGS=" \ --bf16 \ --seq-length 8192 \ --max-position-embeddings 8192 \ - --export-model-type MambaModel \ + --export-model-type HybridModel \ " diff --git a/examples/post_training/modelopt/conf/nvidia/NVIDIA-Nemotron-Nano-9B-v2.sh b/examples/post_training/modelopt/conf/nvidia/NVIDIA-Nemotron-Nano-9B-v2.sh index 83867430a97..51aff10a22a 100644 --- a/examples/post_training/modelopt/conf/nvidia/NVIDIA-Nemotron-Nano-9B-v2.sh +++ b/examples/post_training/modelopt/conf/nvidia/NVIDIA-Nemotron-Nano-9B-v2.sh @@ -35,6 +35,6 @@ MODEL_ARGS=" \ --tokenizer-type HuggingFaceTokenizer \ --make-vocab-size-divisible-by 1 \ --use-mcore-models \ - --export-model-type MambaModel \ + --export-model-type HybridModel \ --padded-vocab-size 131072 \ " diff --git a/examples/post_training/modelopt/conf/nvidia/Nemotron-H-47B-Reasoning-128K.sh b/examples/post_training/modelopt/conf/nvidia/Nemotron-H-47B-Reasoning-128K.sh index 901e607f298..e2da6a3c33d 100644 --- a/examples/post_training/modelopt/conf/nvidia/Nemotron-H-47B-Reasoning-128K.sh +++ b/examples/post_training/modelopt/conf/nvidia/Nemotron-H-47B-Reasoning-128K.sh @@ -33,5 +33,5 @@ MODEL_ARGS=" \ --max-position-embeddings 8192 \ --tokenizer-type HuggingFaceTokenizer \ --use-mcore-models \ - --export-model-type MambaModel \ + --export-model-type HybridModel \ " diff --git a/examples/post_training/modelopt/conf/nvidia/Nemotron-H-4B-Instruct.sh b/examples/post_training/modelopt/conf/nvidia/Nemotron-H-4B-Instruct.sh index 084db49e0eb..523f7d521b0 100644 --- a/examples/post_training/modelopt/conf/nvidia/Nemotron-H-4B-Instruct.sh +++ b/examples/post_training/modelopt/conf/nvidia/Nemotron-H-4B-Instruct.sh @@ -38,5 +38,5 @@ MODEL_ARGS=" \ --make-vocab-size-divisible-by 1 \ --use-mcore-models \ --rotary-base 10000 \ - --export-model-type MambaModel \ + --export-model-type HybridModel \ " diff --git a/examples/post_training/modelopt/conf/nvidia/Nemotron-H-56B-Base-8K.sh b/examples/post_training/modelopt/conf/nvidia/Nemotron-H-56B-Base-8K.sh index 645a159d075..be80d8a9a19 100644 --- a/examples/post_training/modelopt/conf/nvidia/Nemotron-H-56B-Base-8K.sh +++ b/examples/post_training/modelopt/conf/nvidia/Nemotron-H-56B-Base-8K.sh @@ -35,5 +35,5 @@ MODEL_ARGS=" \ --max-position-embeddings 8192 \ --tokenizer-type HuggingFaceTokenizer \ --bf16 \ - --export-model-type MambaModel \ + --export-model-type HybridModel \ " diff --git a/examples/post_training/modelopt/conf/nvidia/Nemotron-H-8B-Base-8K.sh b/examples/post_training/modelopt/conf/nvidia/Nemotron-H-8B-Base-8K.sh index 66f3ad368b4..36b242e36dd 100644 --- a/examples/post_training/modelopt/conf/nvidia/Nemotron-H-8B-Base-8K.sh +++ b/examples/post_training/modelopt/conf/nvidia/Nemotron-H-8B-Base-8K.sh @@ -37,6 +37,6 @@ MODEL_ARGS=" \ --use-mcore-models \ --rotary-percent 0.5 \ --rotary-base 500000 \ - --export-model-type MambaModel \ + --export-model-type HybridModel \ " # --rotary-base 10000 \ diff --git a/examples/post_training/modelopt/convert_model.py b/examples/post_training/modelopt/convert_model.py index eaec9789e1e..136b0273724 100644 --- a/examples/post_training/modelopt/convert_model.py +++ b/examples/post_training/modelopt/convert_model.py @@ -19,7 +19,7 @@ from megatron.core.parallel_state import destroy_model_parallel from megatron.post_training.arguments import add_modelopt_args from megatron.post_training.checkpointing import load_modelopt_checkpoint -from megatron.post_training.model_builder import modelopt_gpt_mamba_builder +from megatron.post_training.model_builder import modelopt_gpt_hybrid_builder from megatron.post_training.utils import ( report_current_memory_info, to_empty_if_meta, @@ -129,7 +129,7 @@ def check_arguments(): ) model = get_model( - functools.partial(model_provider, modelopt_gpt_mamba_builder), wrap_with_ddp=False + functools.partial(model_provider, modelopt_gpt_hybrid_builder), wrap_with_ddp=False ) report_current_memory_info() diff --git a/examples/post_training/modelopt/distillation.md b/examples/post_training/modelopt/distillation.md index 49f73c4edde..9946723364e 100644 --- a/examples/post_training/modelopt/distillation.md +++ b/examples/post_training/modelopt/distillation.md @@ -53,7 +53,7 @@ Without this configuration file, the default logits-only distillation with scale ### Training -Distillation is triggered by calling `pretrain_gpt.py` or `pretrain_mamba.py` with the following arguments: +Distillation is triggered by calling `pretrain_gpt.py` or `pretrain_hybrid.py` with the following arguments: ```bash --export-kd-teacher-load diff --git a/examples/post_training/modelopt/export.py b/examples/post_training/modelopt/export.py index 5e3b2a1716e..31c72d95eab 100755 --- a/examples/post_training/modelopt/export.py +++ b/examples/post_training/modelopt/export.py @@ -15,7 +15,7 @@ from megatron.post_training.arguments import add_modelopt_args from megatron.post_training.checkpointing import load_modelopt_checkpoint -from megatron.post_training.model_builder import modelopt_gpt_mamba_builder +from megatron.post_training.model_builder import modelopt_gpt_hybrid_builder from megatron.training import get_args, get_model from megatron.training.initialize import initialize_megatron from megatron.training.utils import unwrap_model @@ -74,7 +74,7 @@ def add_modelopt_export_args(parser): ) model = get_model( - functools.partial(model_provider, modelopt_gpt_mamba_builder), wrap_with_ddp=False + functools.partial(model_provider, modelopt_gpt_hybrid_builder), wrap_with_ddp=False ) # Materialize the model from meta device to cpu before loading the checkpoint. diff --git a/examples/post_training/modelopt/finetune.py b/examples/post_training/modelopt/finetune.py index f7f7c24f970..2efd3cde6a4 100755 --- a/examples/post_training/modelopt/finetune.py +++ b/examples/post_training/modelopt/finetune.py @@ -19,7 +19,7 @@ from megatron.core.models.gpt import GPTModel from megatron.post_training.arguments import add_modelopt_args from megatron.post_training.loss_func import loss_func -from megatron.post_training.model_builder import modelopt_gpt_mamba_builder +from megatron.post_training.model_builder import modelopt_gpt_hybrid_builder from megatron.post_training.non_loss_data_func import report_draft_acceptance_length from megatron.training import get_args, get_timers, pretrain from megatron.training.utils import ( @@ -486,7 +486,7 @@ def forward_step(data_iterator, model: GPTModel): if __name__ == "__main__": pretrain( train_valid_test_sft_datasets_provider, - partial(model_provider, modelopt_gpt_mamba_builder), + partial(model_provider, modelopt_gpt_hybrid_builder), ModelType.encoder_or_decoder, forward_step, extra_args_provider=add_finetune_args, diff --git a/examples/post_training/modelopt/generate.py b/examples/post_training/modelopt/generate.py index 3d3f6571b34..cc4c4e37a80 100644 --- a/examples/post_training/modelopt/generate.py +++ b/examples/post_training/modelopt/generate.py @@ -14,7 +14,7 @@ from megatron.post_training.arguments import add_modelopt_args from megatron.post_training.checkpointing import load_modelopt_checkpoint from megatron.post_training.generate import simple_generate -from megatron.post_training.model_builder import modelopt_gpt_mamba_builder +from megatron.post_training.model_builder import modelopt_gpt_hybrid_builder from megatron.post_training.utils import report_current_memory_info, to_empty_if_meta from megatron.training import get_args, get_model, initialize_megatron from utils import get_hf_tokenizer @@ -100,7 +100,7 @@ def get_conversations(example): UserWarning, ) - model = get_model(functools.partial(model_provider, modelopt_gpt_mamba_builder), wrap_with_ddp=False) + model = get_model(functools.partial(model_provider, modelopt_gpt_hybrid_builder), wrap_with_ddp=False) report_current_memory_info() unwrapped_model = unwrap_model(model)[0] diff --git a/examples/post_training/modelopt/mmlu.py b/examples/post_training/modelopt/mmlu.py index 5aa5d1c24c7..466d5052b50 100644 --- a/examples/post_training/modelopt/mmlu.py +++ b/examples/post_training/modelopt/mmlu.py @@ -17,7 +17,7 @@ from megatron.post_training.arguments import add_modelopt_args from megatron.post_training.checkpointing import load_modelopt_checkpoint from megatron.post_training.generate import simple_generate -from megatron.post_training.model_builder import modelopt_gpt_mamba_builder +from megatron.post_training.model_builder import modelopt_gpt_hybrid_builder from megatron.post_training.utils import report_current_memory_info from megatron.training import get_args, get_model, initialize_megatron from utils import get_hf_tokenizer @@ -158,7 +158,7 @@ def generate_prompt(test_example, dev_examples, few_shots=0, no_subject_prompt=F UserWarning, ) - model = get_model(functools.partial(model_provider, modelopt_gpt_mamba_builder), wrap_with_ddp=False) + model = get_model(functools.partial(model_provider, modelopt_gpt_hybrid_builder), wrap_with_ddp=False) report_current_memory_info() # Materialize the model from meta device to gpu before loading the checkpoint. diff --git a/examples/post_training/modelopt/offline_feature_extract.py b/examples/post_training/modelopt/offline_feature_extract.py index 80207faf2b2..92500b2950e 100644 --- a/examples/post_training/modelopt/offline_feature_extract.py +++ b/examples/post_training/modelopt/offline_feature_extract.py @@ -14,7 +14,7 @@ from megatron.core import mpu from megatron.post_training.arguments import add_modelopt_args from megatron.post_training.checkpointing import load_modelopt_checkpoint -from megatron.post_training.model_builder import modelopt_gpt_mamba_builder +from megatron.post_training.model_builder import modelopt_gpt_hybrid_builder from megatron.training import get_args, get_model, get_tokenizer, initialize_megatron from megatron.training.utils import print_rank_0, unwrap_model from model_provider import model_provider @@ -53,7 +53,7 @@ def extract_feature(dataset, model, output_dir, idx_start, idx_end): args = get_args() tokenizer = get_tokenizer() - model = get_model(functools.partial(model_provider, modelopt_gpt_mamba_builder), wrap_with_ddp=False) + model = get_model(functools.partial(model_provider, modelopt_gpt_hybrid_builder), wrap_with_ddp=False) load_modelopt_checkpoint(model, strict=not args.untie_embeddings_and_output_weights) print_rank_0("Done loading checkpoint") diff --git a/examples/post_training/modelopt/prune.py b/examples/post_training/modelopt/prune.py index 56bbffa0cd0..99e351a6198 100644 --- a/examples/post_training/modelopt/prune.py +++ b/examples/post_training/modelopt/prune.py @@ -28,7 +28,7 @@ from megatron.post_training.arguments import add_modelopt_args from megatron.post_training.checkpointing import load_modelopt_checkpoint from megatron.post_training.generate import simple_generate -from megatron.post_training.model_builder import modelopt_gpt_mamba_builder +from megatron.post_training.model_builder import modelopt_gpt_hybrid_builder from megatron.post_training.utils import ( report_current_memory_info, ) @@ -163,7 +163,7 @@ def get_params(model): tokenizer = get_hf_tokenizer() model = get_model( - functools.partial(model_provider, modelopt_gpt_mamba_builder), wrap_with_ddp=False + functools.partial(model_provider, modelopt_gpt_hybrid_builder), wrap_with_ddp=False ) unwrapped_model = unwrap_model(model)[0] diff --git a/examples/post_training/modelopt/quantize.py b/examples/post_training/modelopt/quantize.py index dc4947038e5..0c10696df84 100644 --- a/examples/post_training/modelopt/quantize.py +++ b/examples/post_training/modelopt/quantize.py @@ -39,7 +39,7 @@ from megatron.post_training.arguments import add_modelopt_args from megatron.post_training.checkpointing import load_modelopt_checkpoint from megatron.post_training.generate import simple_generate -from megatron.post_training.model_builder import modelopt_gpt_mamba_builder +from megatron.post_training.model_builder import modelopt_gpt_hybrid_builder from megatron.post_training.utils import ( print_distributed_quant_summary, report_current_memory_info, @@ -362,7 +362,7 @@ def get_calib_dataloader( tokenizer = get_hf_tokenizer() model = get_model( - functools.partial(model_provider, modelopt_gpt_mamba_builder), wrap_with_ddp=False + functools.partial(model_provider, modelopt_gpt_hybrid_builder), wrap_with_ddp=False ) report_current_memory_info() diff --git a/examples/post_training/modelopt/train.sh b/examples/post_training/modelopt/train.sh index 1ebb8bf3d76..3afcd4f5be7 100755 --- a/examples/post_training/modelopt/train.sh +++ b/examples/post_training/modelopt/train.sh @@ -69,8 +69,8 @@ fi export HF_TOKEN=${HF_TOKEN} -if [[ ${MODEL_ARGS} == *"MambaModel"* ]]; then - PRETRAIN_EXE=${SCRIPT_DIR}/../../../pretrain_mamba.py +if [[ ${MODEL_ARGS} == *"HybridModel"* ]] || [[ ${MODEL_ARGS} == *"MambaModel"* ]]; then + PRETRAIN_EXE=${SCRIPT_DIR}/../../../pretrain_hybrid.py else PRETRAIN_EXE=${SCRIPT_DIR}/../../../pretrain_gpt.py fi diff --git a/examples/post_training/modelopt/validate.py b/examples/post_training/modelopt/validate.py index 8b8f1ffc9dd..4d9757da00c 100644 --- a/examples/post_training/modelopt/validate.py +++ b/examples/post_training/modelopt/validate.py @@ -14,7 +14,7 @@ from megatron.post_training.arguments import add_modelopt_args from megatron.post_training.checkpointing import load_modelopt_checkpoint -from megatron.post_training.model_builder import modelopt_gpt_mamba_builder +from megatron.post_training.model_builder import modelopt_gpt_hybrid_builder from megatron.post_training.utils import get_mtbench_chat_data from megatron.training import get_args, get_model, initialize_megatron from utils import get_hf_tokenizer @@ -116,7 +116,7 @@ def report_current_memory_info(): ground_truth = [None for _ in range(len(prompts))] tokenizer = get_hf_tokenizer() - model = get_model(functools.partial(model_provider, modelopt_gpt_mamba_builder), wrap_with_ddp=False) + model = get_model(functools.partial(model_provider, modelopt_gpt_hybrid_builder), wrap_with_ddp=False) report_current_memory_info() diff --git a/examples/rl/model_configs/nemotron5_56b.sh b/examples/rl/model_configs/nemotron5_56b.sh index 23b9f99a72a..b4fcee17a8e 100644 --- a/examples/rl/model_configs/nemotron5_56b.sh +++ b/examples/rl/model_configs/nemotron5_56b.sh @@ -69,7 +69,7 @@ MODEL_OPTIONS="\ \ --fp8-recipe tensorwise \ --hybrid-layer-pattern M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M- \ - --spec megatron.core.models.mamba.mamba_layer_specs mamba_stack_spec \ + --spec megatron.core.models.hybrid.hybrid_layer_specs hybrid_stack_spec \ --mamba-state-dim 256 \ --per-split-data-args-path ${BLEND_PATH} \ --tiktoken-pattern v2 \ diff --git a/examples/rl/model_configs/nemotron5_8b.sh b/examples/rl/model_configs/nemotron5_8b.sh index c18149f03d6..198efd2a163 100644 --- a/examples/rl/model_configs/nemotron5_8b.sh +++ b/examples/rl/model_configs/nemotron5_8b.sh @@ -61,7 +61,7 @@ MODEL_OPTIONS="\ --inference-max-requests $MAX_INFERENCE_BS \ --pretrained-checkpoint $CHECKPOINT \ --hybrid-layer-pattern M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M- \ - --spec megatron.core.models.mamba.mamba_layer_specs mamba_stack_spec \ + --spec megatron.core.models.hybrid.hybrid_layer_specs hybrid_stack_spec \ --tiktoken-pattern v2 \ --distributed-timeout-minutes 60 \ --use-mcore-models \ diff --git a/examples/rl/model_configs/nemotron5p5_12b_H.sh b/examples/rl/model_configs/nemotron5p5_12b_H.sh index 1826d57e913..bfb4c7e4727 100644 --- a/examples/rl/model_configs/nemotron5p5_12b_H.sh +++ b/examples/rl/model_configs/nemotron5p5_12b_H.sh @@ -76,7 +76,7 @@ MODEL_OPTIONS="\ --disable-gloo-process-groups \ --mamba-head-dim 80 \ --hybrid-layer-pattern M-M-M-M*-M-M-M-M*-M-M-M-M*-M-M-M-M*-M-M-M-M*-M-M-M-M*-M-M-M-M- \ - --spec megatron.core.models.mamba.mamba_layer_specs mamba_stack_spec \ + --spec megatron.core.models.hybrid.hybrid_layer_specs hybrid_stack_spec \ --tiktoken-pattern v2 \ --distributed-timeout-minutes 10 \ --use-mcore-models \ diff --git a/hybrid_builders.py b/hybrid_builders.py new file mode 100644 index 00000000000..36a87a3940b --- /dev/null +++ b/hybrid_builders.py @@ -0,0 +1,54 @@ +# Copyright (c) 2025-2026, NVIDIA CORPORATION. All rights reserved. + +from model_provider import count_parameters_in_layer +from megatron.core.models.hybrid.hybrid_model import HybridModel +from megatron.core.transformer import TransformerConfig +from megatron.core.transformer.spec_utils import import_module +from megatron.training import print_rank_0 +from megatron.training.arguments import core_transformer_config_from_args +from megatron.core.models.hybrid.hybrid_layer_specs import hybrid_inference_stack_spec + + +def hybrid_builder(args, pre_process, post_process, vp_stage=None, config=None, pg_collection=None): + print_rank_0('building Hybrid model ...') + if config is None: + config = core_transformer_config_from_args(args, TransformerConfig) + assert args.use_legacy_models is False, "Hybrid model only supported in Mcore!" + + if config.transformer_impl == "inference_optimized": + hybrid_stack_spec = hybrid_inference_stack_spec + assert ( + not config.inference_fuse_tp_communication + ), "inference_fuse_tp_communication is not supported for HybridModel" + elif args.spec is not None: + hybrid_stack_spec = import_module(args.spec) + else: + raise ValueError("You must provide a valid hybrid layer spec via --spec") + + model = HybridModel( + config=config, + hybrid_stack_spec=hybrid_stack_spec, + vocab_size=args.padded_vocab_size, + max_sequence_length=args.max_position_embeddings, + hybrid_layer_pattern=args.hybrid_layer_pattern, + pre_process=pre_process, + post_process=post_process, + fp16_lm_cross_entropy=args.fp16_lm_cross_entropy, + parallel_output=True, + share_embeddings_and_output_weights=not args.untie_embeddings_and_output_weights, + position_embedding_type=args.position_embedding_type, + rotary_percent=args.rotary_percent, + rotary_base=args.rotary_base, + pg_collection=pg_collection, + vp_stage=vp_stage, + ) + + for l in range(model.decoder.num_layers_per_pipeline_rank): + layer_params = count_parameters_in_layer(model, f'decoder.layers.{l}.') + print_rank_0(f" == params layer {l}: {layer_params}") + + return model + + +# Backward-compatible alias +mamba_builder = hybrid_builder diff --git a/mamba_builders.py b/mamba_builders.py index 650ea4a719f..f824fce9be3 100644 --- a/mamba_builders.py +++ b/mamba_builders.py @@ -1,50 +1,15 @@ -# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2025-2026, NVIDIA CORPORATION. All rights reserved. +"""Backward-compatible re-export of hybrid_builders. -from model_provider import count_parameters_in_layer -from megatron.core.models.mamba import MambaModel -from megatron.core.transformer import TransformerConfig -from megatron.core.transformer.spec_utils import import_module -from megatron.training import print_rank_0 -from megatron.training.arguments import core_transformer_config_from_args -from megatron.core.models.mamba.mamba_layer_specs import mamba_inference_stack_spec +Deprecated. Use hybrid_builders instead. +""" +import warnings +warnings.warn( + "mamba_builders has been deprecated. Use hybrid_builders instead.", + DeprecationWarning, + stacklevel=2, +) -def mamba_builder(args, pre_process, post_process, vp_stage=None, config=None, pg_collection=None): - print_rank_0('building MAMBA model ...') - if config is None: - config = core_transformer_config_from_args(args, TransformerConfig) - assert args.use_legacy_models is False, "Mamba only supported in Mcore!" - - if config.transformer_impl == "inference_optimized": - mamba_stack_spec = mamba_inference_stack_spec - assert ( - not config.inference_fuse_tp_communication - ), "inference_fuse_tp_communication is not supported for Mamba" - elif args.spec is not None: - mamba_stack_spec = import_module(args.spec) - else: - raise ValueError("You must provide a valid Mamba layer spec via --spec") - - model = MambaModel( - config=config, - mamba_stack_spec=mamba_stack_spec, - vocab_size=args.padded_vocab_size, - max_sequence_length=args.max_position_embeddings, - hybrid_layer_pattern=args.hybrid_layer_pattern, - pre_process=pre_process, - post_process=post_process, - fp16_lm_cross_entropy=args.fp16_lm_cross_entropy, - parallel_output=True, - share_embeddings_and_output_weights=not args.untie_embeddings_and_output_weights, - position_embedding_type=args.position_embedding_type, - rotary_percent=args.rotary_percent, - rotary_base=args.rotary_base, - pg_collection=pg_collection, - vp_stage=vp_stage, - ) - - for l in range(model.decoder.num_layers_per_pipeline_rank): - layer_params = count_parameters_in_layer(model, f'decoder.layers.{l}.') - print_rank_0(f" == params layer {l}: {layer_params}") - - return model +from hybrid_builders import * # noqa: F401,F403 +from hybrid_builders import hybrid_builder as mamba_builder # noqa: F401 diff --git a/megatron/inference/utils.py b/megatron/inference/utils.py index a1204db487a..bb65c754ab1 100644 --- a/megatron/inference/utils.py +++ b/megatron/inference/utils.py @@ -7,7 +7,7 @@ import torch from gpt_builders import gpt_builder -from mamba_builders import mamba_builder +from hybrid_builders import hybrid_builder from megatron.core.inference.config import ( InferenceConfig, KVCacheManagementMode, @@ -43,8 +43,16 @@ def get_model_for_inference() -> MegatronModule: if args.model_provider == "gpt": model_builder = gpt_builder - elif args.model_provider == "mamba": - model_builder = mamba_builder + elif args.model_provider in ("hybrid", "mamba"): + if args.model_provider == "mamba": + import warnings + + warnings.warn( + '--model-provider "mamba" is deprecated. Use --model-provider "hybrid" instead.', + DeprecationWarning, + stacklevel=2, + ) + model_builder = hybrid_builder else: raise ValueError(f"Invalid model provider {args.model_provider}") @@ -158,7 +166,11 @@ def add_inference_args(parser: ArgumentParser) -> ArgumentParser: "total number of requests. Set to -1 to add all requests together.", ) group.add_argument( - "--model-provider", choices=["mamba", "gpt"], default="gpt", help="Model provider" + "--model-provider", + choices=["hybrid", "mamba", "gpt"], + default="gpt", + help='Model provider. Use "hybrid" for HybridModel (formerly MambaModel). ' + '"mamba" is accepted for backward compatibility but deprecated.', ) group.add_argument( "--skip-prompt-log-probs", action='store_true', default=False, help='Skip prompt log probs.' diff --git a/megatron/post_training/arguments.py b/megatron/post_training/arguments.py index dc98c6d28e4..47c667b4d0a 100644 --- a/megatron/post_training/arguments.py +++ b/megatron/post_training/arguments.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2024-2026, NVIDIA CORPORATION. All rights reserved. def add_modelopt_args(parser): @@ -10,8 +10,9 @@ def add_modelopt_args(parser): "--export-model-type", type=str, default="GPTModel", - choices=["GPTModel", "MambaModel"], - help="Model type to use in model_provider.", + choices=["GPTModel", "HybridModel", "MambaModel"], + help='Model type to use in model_provider. Use "HybridModel" for hybrid models ' + '(formerly MambaModel). "MambaModel" is accepted for backward compatibility but deprecated.', ) group.add_argument( "--export-legacy-megatron", diff --git a/megatron/post_training/model_builder.py b/megatron/post_training/model_builder.py index 085d188e811..383ae6ec8aa 100644 --- a/megatron/post_training/model_builder.py +++ b/megatron/post_training/model_builder.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2024-2026, NVIDIA CORPORATION. All rights reserved. """ModelOpt GPT model provider.""" @@ -16,7 +16,7 @@ from megatron.core.models.gpt.heterogeneous.heterogeneous_layer_specs import ( get_gpt_heterogeneous_layer_spec, ) -from megatron.core.models.mamba import MambaModel as MCoreMambaModel +from megatron.core.models.hybrid.hybrid_model import HybridModel as MCoreHybridModel from megatron.core.post_training.modelopt.gpt.model_specs import get_gpt_modelopt_spec from megatron.core.post_training.modelopt.gpt.state_dict_hooks import ( mcore_gpt_load_te_state_dict_pre_hook, @@ -124,7 +124,7 @@ def _load_teacher_model(config, config_raw: Namespace, model_kwargs: Dict[str, A # _load_teacher_model_config, so config_raw.hybrid_layer_pattern is always set here. model_kwargs["hybrid_layer_pattern"] = config_raw.hybrid_layer_pattern - teacher = MCoreMambaModel(config=config, **model_kwargs) + teacher = MCoreHybridModel(config=config, **model_kwargs) else: # GPT layer spec needs re-creation since it depends on number of model layers. if config.heterogeneous_block_specs: @@ -158,14 +158,14 @@ def _load_teacher_model(config, config_raw: Namespace, model_kwargs: Dict[str, A return teacher -def modelopt_gpt_mamba_builder( +def modelopt_gpt_hybrid_builder( args, pre_process, post_process, vp_stage=None, config=None, pg_collection=None, -) -> MCoreGPTModel | MCoreMambaModel: +) -> MCoreGPTModel | MCoreHybridModel: """Builds the model. Args: @@ -179,7 +179,7 @@ def modelopt_gpt_mamba_builder( attached to the returned model for downstream routing/resharding utilities. Returns: - MCoreGPTModel | MCoreMambaModel: The returned model + MCoreGPTModel | MCoreHybridModel: The returned model """ print_rank_0("building GPT model ...") @@ -259,8 +259,17 @@ def modelopt_gpt_mamba_builder( "pg_collection": pg_collection, } model = MCoreGPTModel(config=config, **model_kwargs) - elif args.export_model_type == "MambaModel" or getattr(args, 'hybrid_layer_pattern', None) is not None: - from megatron.core.post_training.modelopt.mamba.model_specs import get_mamba_stack_modelopt_spec + elif args.export_model_type in ("HybridModel", "MambaModel") or getattr(args, 'hybrid_layer_pattern', None) is not None: + if args.export_model_type == "MambaModel": + import warnings + + warnings.warn( + '--export-model-type "MambaModel" is deprecated. ' + 'Use --export-model-type "HybridModel" instead.', + DeprecationWarning, + stacklevel=2, + ) + from megatron.core.post_training.modelopt.hybrid.model_specs import get_hybrid_stack_modelopt_spec if args.export_default_te_spec and args.export_te_mcore_model: logging.getLogger(__name__).warning( @@ -269,12 +278,12 @@ def modelopt_gpt_mamba_builder( ) args.export_te_mcore_model = False - mamba_stack_spec = get_mamba_stack_modelopt_spec( + hybrid_stack_spec = get_hybrid_stack_modelopt_spec( remap_te_layernorm=args.export_te_mcore_model, use_default_te_spec=args.export_default_te_spec, ) model_kwargs = { - "mamba_stack_spec": mamba_stack_spec, + "hybrid_stack_spec": hybrid_stack_spec, "vocab_size": args.padded_vocab_size, "max_sequence_length": args.max_position_embeddings, "hybrid_layer_pattern": args.hybrid_layer_pattern, @@ -289,7 +298,7 @@ def modelopt_gpt_mamba_builder( "pg_collection": pg_collection, } - model = MCoreMambaModel(config=config, **model_kwargs) + model = MCoreHybridModel(config=config, **model_kwargs) for l in range(model.decoder.num_layers_per_pipeline_rank): layer_params = count_parameters_in_layer(model, f'decoder.layers.{l}.') @@ -352,3 +361,7 @@ def modelopt_gpt_mamba_builder( print_distributed_quant_summary(model) return model + + +# Backward-compatible alias +modelopt_gpt_mamba_builder = modelopt_gpt_hybrid_builder diff --git a/megatron/training/arguments.py b/megatron/training/arguments.py index 061ff618e17..a1ae68d4a38 100644 --- a/megatron/training/arguments.py +++ b/megatron/training/arguments.py @@ -663,7 +663,7 @@ def validate_args(args, defaults={}): args.rank, ) - from megatron.core.ssm.mamba_hybrid_layer_allocation import ( + from megatron.core.models.hybrid.hybrid_layer_allocation import ( Symbols, parse_hybrid_pattern, get_hybrid_total_layer_count, get_hybrid_total_pipeline_segment_count, ) @@ -1780,7 +1780,7 @@ def core_transformer_config_from_args(args, config_class=None): kw_args['cp_comm_type'] = args.cp_comm_type[0] if args.hybrid_layer_pattern is not None: kw_args['is_hybrid_model'] = True - from megatron.core.ssm.mamba_hybrid_layer_allocation import Symbols + from megatron.core.models.hybrid.hybrid_layer_allocation import Symbols if Symbols.DS_ATTENTION in args.hybrid_layer_pattern: kw_args['experimental_attention_variant'] = 'dsa' diff --git a/megatron/training/training.py b/megatron/training/training.py index 5d97412d8f1..cdb60a0722e 100644 --- a/megatron/training/training.py +++ b/megatron/training/training.py @@ -677,7 +677,7 @@ def transformer_flops(): # Calculate the number of each type of layer. from operator import itemgetter - from megatron.core.ssm.mamba_hybrid_layer_allocation import Symbols, get_hybrid_layer_counts + from megatron.core.models.hybrid.hybrid_layer_allocation import Symbols, get_hybrid_layer_counts num_mamba_layers, num_gdn_layers, num_attn_layers, num_mlp_layers, num_moe_layers = ( itemgetter(Symbols.MAMBA, Symbols.GDN, Symbols.ATTENTION, Symbols.MLP, Symbols.MOE)( get_hybrid_layer_counts(args.hybrid_layer_pattern) @@ -2206,7 +2206,7 @@ def training_log( if is_hybrid_model(args): from operator import itemgetter - from megatron.core.ssm.mamba_hybrid_layer_allocation import ( + from megatron.core.models.hybrid.hybrid_layer_allocation import ( Symbols, get_hybrid_layer_counts, ) layers = itemgetter(Symbols.MOE)(get_hybrid_layer_counts(args.hybrid_layer_pattern)) diff --git a/model_provider.py b/model_provider.py index 0c80c54dfdb..3e61343bb0d 100644 --- a/model_provider.py +++ b/model_provider.py @@ -1,4 +1,4 @@ -# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2025-2026, NVIDIA CORPORATION. All rights reserved. """Common functions used in train_*.py and pretrain_*.py scripts.""" @@ -7,11 +7,11 @@ import torch from megatron.core.models.gpt import GPTModel -from megatron.core.models.mamba import MambaModel +from megatron.core.models.hybrid.hybrid_model import HybridModel from megatron.training import get_args, print_rank_0 try: - from megatron.post_training.model_builder import modelopt_gpt_mamba_builder + from megatron.post_training.model_builder import modelopt_gpt_hybrid_builder has_nvidia_modelopt = True except ImportError: has_nvidia_modelopt = False @@ -23,18 +23,18 @@ def model_provider( model_builder: Callable, pre_process=True, post_process=True, vp_stage: Optional[int] = None, config=None, pg_collection=None, -) -> Union[GPTModel, megatron.legacy.model.GPTModel, MambaModel]: +) -> Union[GPTModel, megatron.legacy.model.GPTModel, HybridModel]: """Builds the model. If you set the use_legacy_models to True, it will return the legacy GPT model and if not the mcore GPT model. Args: - model_builder: A callable that builds the actual model, its signature is the same as model_provider's with an exception of the first argument which is a builder itself. In addition might take a config passed from outside to skip its own config loading. See gpt_builder or mamba_builder for an example, see _gpt_model_builder in train_rl.py to see how to augment a default gpt builder and pass the config from outside + model_builder: A callable that builds the actual model, its signature is the same as model_provider's with an exception of the first argument which is a builder itself. In addition might take a config passed from outside to skip its own config loading. See gpt_builder or hybrid_builder for an example, see _gpt_model_builder in train_rl.py to see how to augment a default gpt builder and pass the config from outside pre_process (bool, optional): Set to true if you need to compute embedings. Defaults to True. post_process (bool, optional): Set to true if you need to compute output logits/loss. Defaults to True. Returns: - Union[GPTModel, megatron.legacy.model.GPTModel, MambaModel]: The returned model + Union[GPTModel, megatron.legacy.model.GPTModel, HybridModel]: The returned model """ args = get_args() @@ -58,7 +58,7 @@ def oom_observer(device, alloc, device_alloc, device_free): if has_nvidia_modelopt and getattr(args, 'modelopt_enabled', False): # [ModelOpt]: Use custom builder + spec when modelopt is enabled - model_builder = modelopt_gpt_mamba_builder + model_builder = modelopt_gpt_hybrid_builder return model_builder(args, pre_process, post_process, vp_stage, config=config, pg_collection=pg_collection) diff --git a/pretrain_gpt.py b/pretrain_gpt.py index 929a9d0f866..02ca5dd72c0 100644 --- a/pretrain_gpt.py +++ b/pretrain_gpt.py @@ -90,7 +90,7 @@ def get_batch(data_iterator, vp_stage: Optional[int] = None): - MTP ranks (``mtp_on_this_rank``) also receive the full batch, regardless of pipeline stage. - Difference from ``pretrain_mamba.py``: + Difference from ``pretrain_hybrid.py``: - Return format: GPT returns a 6-tuple ``(tokens, labels, loss_mask, attention_mask, position_ids, packed_seq_params)`` where ``packed_seq_params`` is a diff --git a/pretrain_hybrid.py b/pretrain_hybrid.py new file mode 100644 index 00000000000..f073e8e9ab3 --- /dev/null +++ b/pretrain_hybrid.py @@ -0,0 +1,369 @@ +# Copyright (c) 2025-2026, NVIDIA CORPORATION. All rights reserved. +"""Pretrain and SFT Hybrid.""" + +# Capture the true program start time BEFORE any heavy imports. +import time +_PROGRAM_START_TIME = time.time() + +import json + +# Suppress warnings on all ranks but rank 0. +import os +import warnings +rank = int(os.environ.get('RANK', 0)) +if rank != 0: + warnings.filterwarnings("ignore", category=UserWarning) + warnings.filterwarnings("ignore", category=FutureWarning) + +from functools import partial +from typing import List, Optional, Tuple + +import torch + +from hybrid_builders import hybrid_builder +from megatron.core import mpu +from megatron.core.datasets.blended_megatron_dataset_builder import BlendedMegatronDatasetBuilder +from megatron.core.datasets.gpt_dataset import GPTDataset, GPTDatasetConfig, MockGPTDataset +from megatron.core.enums import ModelType +from megatron.core.packed_seq_params import PackedSeqParams +from megatron.core.parallel_state import ( + get_context_parallel_rank, + get_context_parallel_world_size, +) +from megatron.core.models.hybrid.hybrid_model import HybridModel +from megatron.core.rerun_state_machine import get_rerun_state_machine +from megatron.core.tokenizers.utils.build_tokenizer import build_tokenizer +from megatron.core.utils import get_attr_wrapped_model, is_te_min_version, StragglerDetector +from megatron.training import ( + get_args, + get_timers, + inprocess_restart, + pretrain, + print_rank_0, + set_startup_timestamps, +) +from megatron.training.arguments import parse_and_validate_args +from megatron.training.datasets.sft_dataset import SFTDataset +from megatron.training.utils import ( + get_batch_on_this_cp_rank, + get_batch_on_this_tp_rank, + get_blend_and_blend_per_split, + is_first_or_last_pipeline_stage, +) +from model_provider import model_provider + +try: + from megatron.post_training.arguments import add_modelopt_args + from megatron.post_training.loss_func import loss_func as loss_func_modelopt + has_nvidia_modelopt = True +except ImportError: + has_nvidia_modelopt = False + +try: + # Register the TE CUDA kernels + import transformer_engine # pylint: disable=unused-import + + # Alias the PyTorch wrapper so we can call tex.* APIs + import transformer_engine_torch as tex +except ImportError: + # TE isn’t installed or the torch wrapper is missing + tex = None + +stimer = StragglerDetector() + + +def get_batch(data_iterator, vp_stage=None): + """Generate a batch.""" + + empty_batch = { + 'tokens': None, + 'labels': None, + 'loss_mask': None, + 'attention_mask': None, + 'position_ids': None, + 'cu_seqlens': None, + 'max_seqlen': None, + } + + # TODO(duncan): Is there a more efficient way to access is_packed_sequence here? + is_packed_sequence = get_args().sft # SFT always uses packed sequence + if not is_first_or_last_pipeline_stage(vp_stage) and not is_packed_sequence: + return empty_batch.values() + + batch = get_batch_on_this_tp_rank(data_iterator) + + cu_seqlens = batch['cu_seqlens'] + # Unused at the moment + cu_seqlens_padded = batch.pop('cu_seqlens_padded', None) + # Support for Hybrid Context Parallel (Unused in this script) + local_cp_size = batch.pop('local_cp_size', None) + + if cu_seqlens is not None: + assert ( + cu_seqlens.dim() == 2 and cu_seqlens.shape[0] == 1 + ), "micro-batch-size must be 1 for packing" + cu_seqlens = cu_seqlens[0] + batch['cu_seqlens'] = cu_seqlens + + max_seqlen = batch['max_seqlen'] + assert max_seqlen.dim() == 1 + # TODO(duncan): can this be kept as a 0-D tensor? + batch['max_seqlen'] = int(max_seqlen[0].item()) + + if mpu.is_pipeline_first_stage(ignore_virtual=(vp_stage is None), vp_stage=vp_stage): + total_tokens = batch['tokens'].size(1) + elif mpu.is_pipeline_last_stage(ignore_virtual=(vp_stage is None), vp_stage=vp_stage): + total_tokens = batch['labels'].size(1) + else: # packed sequence + empty_batch['cu_seqlens'] = cu_seqlens + empty_batch['max_seqlen'] = max_seqlen + return empty_batch.values() + + if cu_seqlens is None: + # slice batch along sequence dimension for context parallelism + batch = get_batch_on_this_cp_rank(batch) # The implementation of this function is in MCore + else: # Packed THD format + cp_size = get_context_parallel_world_size() + if cp_size > 1: # slice batch along sequence dimension for context parallelism + assert tex is not None and is_te_min_version("1.10.0"), ( + "Please update Transformer Engine to >= 1.10 to use " + "Context Parallel with THD format data" + ) + cp_rank = get_context_parallel_rank() + index = tex.thd_get_partitioned_indices( + cu_seqlens, + total_tokens, + cp_size, + cp_rank, + ) + for key, data in batch.items(): + if key in {'attention_mask', 'cu_seqlens', 'max_seqlen'}: + continue + if data is not None: + # On first PP rank, labels and loss_mask can be None. + # On last PP rank, tokens and position_ids can be None. + batch[key] = data.index_select(1, index) + + return batch.values() + + +# define spiky loss as a loss that's 10x the max loss observed +SPIKY_LOSS_FACTOR = 10 + +def loss_func(loss_mask: torch.Tensor, output_tensor: torch.Tensor, model: Optional[HybridModel] = None): + """Loss function. + + Args: + loss_mask (torch.Tensor): Used to mask out some portions of the loss + output_tensor (torch.Tensor): The tensor with the losses + + Returns: + the loss scalar for this micro-batch + the number of non-padded tokens in this microbatch + a dict containing reporting metrics on the loss and number of tokens across + the data parallel ranks + """ + args = get_args() + if has_nvidia_modelopt and getattr(args, 'modelopt_enabled', False): # [ModelOpt] + loss, num_tokens, report = loss_func_modelopt(loss_mask, output_tensor, model=model) + else: + losses = output_tensor.view(-1).float() + loss_mask = loss_mask.view(-1).float() + loss = torch.sum(losses * loss_mask) + + num_tokens = loss_mask.sum().clone().detach().to(torch.int) + report = {'lm loss': torch.cat([loss.clone().detach().view(1), num_tokens.view(1)])} + + # Check individual rank losses are not NaN prior to DP all-reduce. + rerun_state_machine = get_rerun_state_machine() + if args.check_for_nan_in_loss_and_grad: + rerun_state_machine.validate_result( + result=loss, + rejection_func=torch.isnan, + message="found NaN in local forward loss calculation", + tolerance=0.0, # forward pass calculations are deterministic + fatal=True, + ) + rerun_state_machine.validate_result( + result=loss, + rejection_func=torch.isinf, + message="found Inf in local forward loss calculation", + tolerance=0.0, # forward pass calculations are deterministic + fatal=True, + ) + # Check for spiky loss + if args.check_for_spiky_loss: + rerun_state_machine.validate_result( + result=loss, + rejection_func=partial( + rerun_state_machine.is_unexpectedly_large, + threshold=SPIKY_LOSS_FACTOR, + context="loss", + ), + message="Spiky loss", + tolerance=0.0, # forward pass calculations are deterministic + fatal=False, + ) + + return loss, num_tokens, report + + +def forward_step(data_iterator, model: HybridModel): + """Forward training step. + + Args: + data_iterator : Input data iterator + model (HybridModel): The Model + """ + timers = get_timers() + + # Get the batch. + timers('batch-generator', log_level=2).start() + + global stimer + + with stimer(bdata=True): + vp_stage = get_attr_wrapped_model(model, "vp_stage") + ( + tokens, + labels, + loss_mask, + attention_mask, + position_ids, + cu_seqlens, + max_seqlen, + ) = get_batch(data_iterator, vp_stage) + + if cu_seqlens is None: + packed_seq_params = None + else: + total_tokens = tokens.size(1) if tokens is not None else labels.size(1) + packed_seq_params = PackedSeqParams( + qkv_format="thd", + cu_seqlens_q=cu_seqlens, + cu_seqlens_kv=cu_seqlens, + cu_seqlens_q_padded=None, + cu_seqlens_kv_padded=None, + max_seqlen_q=max_seqlen, + max_seqlen_kv=max_seqlen, + total_tokens=total_tokens, + ) + + timers('batch-generator').stop() + + with stimer: + output_tensor = model( + tokens, + position_ids, + attention_mask, + labels=labels, + packed_seq_params=packed_seq_params, + loss_mask=loss_mask + ) + + # [ModelOpt]: model is needed to access ModelOpt distillation losses + return output_tensor, partial(loss_func, loss_mask, model=model) + + +def is_dataset_built_on_rank(vp_stage=None, is_packed_sequence=False): + if mpu.get_tensor_model_parallel_rank() != 0: + return False + elif is_packed_sequence: + return True + else: + return is_first_or_last_pipeline_stage(vp_stage) + + +def core_gpt_dataset_config_from_args(args): + tokenizer = build_tokenizer(args) + + # Sometimes --data-path is too long, instead we parse it from a file. + blend: Optional[Tuple[List[str], Optional[List[float]]]] + blend_per_split: Optional[List[Optional[Tuple[List[str], Optional[List[float]]]]]] + blend, blend_per_split = get_blend_and_blend_per_split(args) + + sequences_per_dataset = None + if args.per_dataset_sequences_path is not None: + with open(args.per_dataset_sequences_path, "r") as f: + sequences_per_dataset = json.load(f) + + return GPTDatasetConfig( + random_seed=args.seed, + sequence_length=args.seq_length, + blend=blend, + blend_per_split=blend_per_split, + split=args.split, + num_dataset_builder_threads=args.num_dataset_builder_threads, + path_to_cache=args.data_cache_path, + mmap_bin_files=args.mmap_bin_files, + tokenizer=tokenizer, + reset_position_ids=args.reset_position_ids, + reset_attention_mask=args.reset_attention_mask, + eod_mask_loss=args.eod_mask_loss, + create_attention_mask=args.create_attention_mask_in_dataloader, + object_storage_cache_path=args.object_storage_cache_path, + mid_level_dataset_surplus=args.mid_level_dataset_surplus, + allow_ambiguous_pad_tokens=args.allow_ambiguous_pad_tokens, + fast_cache_load=args.dataloader_fast_cache_load, + sequences_per_dataset=sequences_per_dataset, + defer_npy_index_mmap=args.dataloader_defer_npy_index_mmap, + context_parallel_size=args.context_parallel_size, + ) + + +def train_valid_test_datasets_provider(train_val_test_num_samples, vp_stage=None): + """Build the train test and validation datasets. + + Args: + train_val_test_num_samples : A list containing the number of samples in train test and validation. + """ + args = get_args() + config = core_gpt_dataset_config_from_args(args) + + is_packed_sequence = False + if args.sft: + dataset_type = SFTDataset + is_packed_sequence = True # SFT always uses packed sequence + else: + if args.mock_data: + dataset_type = MockGPTDataset + else: + dataset_type = GPTDataset + + print_rank_0("> building train, validation, and test datasets for GPT ...") + + train_ds, valid_ds, test_ds = BlendedMegatronDatasetBuilder( + dataset_type, + train_val_test_num_samples, + partial(is_dataset_built_on_rank, vp_stage=vp_stage, is_packed_sequence=is_packed_sequence), + config + ).build() + + print_rank_0("> finished creating GPT datasets ...") + + return train_ds, valid_ds, test_ds + + +if __name__ == "__main__": + # Timestamp right after entering __main__ block (after all imports/library setup) + _MAIN_ENTRY_TIME = time.time() + + # Register startup timestamps for timing report in pretrain() + set_startup_timestamps(program_start=_PROGRAM_START_TIME, main_entry=_MAIN_ENTRY_TIME) + + # Temporary for transition to core datasets + train_valid_test_datasets_provider.is_distributed = True + + # Optionally enable inprocess restart on pretrain + pretrain, store = inprocess_restart.maybe_wrap_for_inprocess_restart(pretrain) + + args = parse_and_validate_args( + extra_args_provider=add_modelopt_args if has_nvidia_modelopt else None, + args_defaults={'tokenizer_type': 'GPT2BPETokenizer'}, + ) + pretrain(train_valid_test_datasets_provider, + partial(model_provider, hybrid_builder), + ModelType.encoder_or_decoder, + forward_step, + store=store, + ) diff --git a/pretrain_mamba.py b/pretrain_mamba.py index 590eb92ab28..7eb7f461cab 100644 --- a/pretrain_mamba.py +++ b/pretrain_mamba.py @@ -1,369 +1,19 @@ -# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. -"""Pretrain and SFT Mamba.""" +# Copyright (c) 2025-2026, NVIDIA CORPORATION. All rights reserved. +"""Backward-compatible wrapper for pretrain_hybrid.py. -# Capture the true program start time BEFORE any heavy imports. -import time -_PROGRAM_START_TIME = time.time() - -import json - -# Suppress warnings on all ranks but rank 0. +Deprecated. Use pretrain_hybrid.py instead. +""" import os +import runpy import warnings -rank = int(os.environ.get('RANK', 0)) -if rank != 0: - warnings.filterwarnings("ignore", category=UserWarning) - warnings.filterwarnings("ignore", category=FutureWarning) - -from functools import partial -from typing import List, Optional, Tuple - -import torch -from mamba_builders import mamba_builder -from megatron.core import mpu -from megatron.core.datasets.blended_megatron_dataset_builder import BlendedMegatronDatasetBuilder -from megatron.core.datasets.gpt_dataset import GPTDataset, GPTDatasetConfig, MockGPTDataset -from megatron.core.enums import ModelType -from megatron.core.packed_seq_params import PackedSeqParams -from megatron.core.parallel_state import ( - get_context_parallel_rank, - get_context_parallel_world_size, +warnings.warn( + "pretrain_mamba.py has been deprecated. Use pretrain_hybrid.py instead.", + DeprecationWarning, + stacklevel=2, ) -from megatron.core.models.mamba import MambaModel -from megatron.core.rerun_state_machine import get_rerun_state_machine -from megatron.core.tokenizers.utils.build_tokenizer import build_tokenizer -from megatron.core.utils import get_attr_wrapped_model, is_te_min_version, StragglerDetector -from megatron.training import ( - get_args, - get_timers, - inprocess_restart, - pretrain, - print_rank_0, - set_startup_timestamps, -) -from megatron.training.arguments import parse_and_validate_args -from megatron.training.datasets.sft_dataset import SFTDataset -from megatron.training.utils import ( - get_batch_on_this_cp_rank, - get_batch_on_this_tp_rank, - get_blend_and_blend_per_split, - is_first_or_last_pipeline_stage, -) -from model_provider import model_provider - -try: - from megatron.post_training.arguments import add_modelopt_args - from megatron.post_training.loss_func import loss_func as loss_func_modelopt - has_nvidia_modelopt = True -except ImportError: - has_nvidia_modelopt = False - -try: - # Register the TE CUDA kernels - import transformer_engine # pylint: disable=unused-import - - # Alias the PyTorch wrapper so we can call tex.* APIs - import transformer_engine_torch as tex -except ImportError: - # TE isn’t installed or the torch wrapper is missing - tex = None - -stimer = StragglerDetector() - - -def get_batch(data_iterator, vp_stage=None): - """Generate a batch.""" - - empty_batch = { - 'tokens': None, - 'labels': None, - 'loss_mask': None, - 'attention_mask': None, - 'position_ids': None, - 'cu_seqlens': None, - 'max_seqlen': None, - } - - # TODO(duncan): Is there a more efficient way to access is_packed_sequence here? - is_packed_sequence = get_args().sft # SFT always uses packed sequence - if not is_first_or_last_pipeline_stage(vp_stage) and not is_packed_sequence: - return empty_batch.values() - - batch = get_batch_on_this_tp_rank(data_iterator) - - cu_seqlens = batch['cu_seqlens'] - # Unused at the moment - cu_seqlens_padded = batch.pop('cu_seqlens_padded', None) - # Support for Hybrid Context Parallel (Unused in this script) - local_cp_size = batch.pop('local_cp_size', None) - - if cu_seqlens is not None: - assert ( - cu_seqlens.dim() == 2 and cu_seqlens.shape[0] == 1 - ), "micro-batch-size must be 1 for packing" - cu_seqlens = cu_seqlens[0] - batch['cu_seqlens'] = cu_seqlens - - max_seqlen = batch['max_seqlen'] - assert max_seqlen.dim() == 1 - # TODO(duncan): can this be kept as a 0-D tensor? - batch['max_seqlen'] = int(max_seqlen[0].item()) - - if mpu.is_pipeline_first_stage(ignore_virtual=(vp_stage is None), vp_stage=vp_stage): - total_tokens = batch['tokens'].size(1) - elif mpu.is_pipeline_last_stage(ignore_virtual=(vp_stage is None), vp_stage=vp_stage): - total_tokens = batch['labels'].size(1) - else: # packed sequence - empty_batch['cu_seqlens'] = cu_seqlens - empty_batch['max_seqlen'] = max_seqlen - return empty_batch.values() - - if cu_seqlens is None: - # slice batch along sequence dimension for context parallelism - batch = get_batch_on_this_cp_rank(batch) # The implementation of this function is in MCore - else: # Packed THD format - cp_size = get_context_parallel_world_size() - if cp_size > 1: # slice batch along sequence dimension for context parallelism - assert tex is not None and is_te_min_version("1.10.0"), ( - "Please update Transformer Engine to >= 1.10 to use " - "Context Parallel with THD format data" - ) - cp_rank = get_context_parallel_rank() - index = tex.thd_get_partitioned_indices( - cu_seqlens, - total_tokens, - cp_size, - cp_rank, - ) - for key, data in batch.items(): - if key in {'attention_mask', 'cu_seqlens', 'max_seqlen'}: - continue - if data is not None: - # On first PP rank, labels and loss_mask can be None. - # On last PP rank, tokens and position_ids can be None. - batch[key] = data.index_select(1, index) - - return batch.values() - - -# define spiky loss as a loss that's 10x the max loss observed -SPIKY_LOSS_FACTOR = 10 - -def loss_func(loss_mask: torch.Tensor, output_tensor: torch.Tensor, model: Optional[MambaModel] = None): - """Loss function. - - Args: - loss_mask (torch.Tensor): Used to mask out some portions of the loss - output_tensor (torch.Tensor): The tensor with the losses - - Returns: - the loss scalar for this micro-batch - the number of non-padded tokens in this microbatch - a dict containing reporting metrics on the loss and number of tokens across - the data parallel ranks - """ - args = get_args() - if has_nvidia_modelopt and getattr(args, 'modelopt_enabled', False): # [ModelOpt] - loss, num_tokens, report = loss_func_modelopt(loss_mask, output_tensor, model=model) - else: - losses = output_tensor.view(-1).float() - loss_mask = loss_mask.view(-1).float() - loss = torch.sum(losses * loss_mask) - - num_tokens = loss_mask.sum().clone().detach().to(torch.int) - report = {'lm loss': torch.cat([loss.clone().detach().view(1), num_tokens.view(1)])} - - # Check individual rank losses are not NaN prior to DP all-reduce. - rerun_state_machine = get_rerun_state_machine() - if args.check_for_nan_in_loss_and_grad: - rerun_state_machine.validate_result( - result=loss, - rejection_func=torch.isnan, - message="found NaN in local forward loss calculation", - tolerance=0.0, # forward pass calculations are deterministic - fatal=True, - ) - rerun_state_machine.validate_result( - result=loss, - rejection_func=torch.isinf, - message="found Inf in local forward loss calculation", - tolerance=0.0, # forward pass calculations are deterministic - fatal=True, - ) - # Check for spiky loss - if args.check_for_spiky_loss: - rerun_state_machine.validate_result( - result=loss, - rejection_func=partial( - rerun_state_machine.is_unexpectedly_large, - threshold=SPIKY_LOSS_FACTOR, - context="loss", - ), - message="Spiky loss", - tolerance=0.0, # forward pass calculations are deterministic - fatal=False, - ) - - return loss, num_tokens, report - - -def forward_step(data_iterator, model: MambaModel): - """Forward training step. - - Args: - data_iterator : Input data iterator - model (MambaModel): The GPT Model - """ - timers = get_timers() - - # Get the batch. - timers('batch-generator', log_level=2).start() - - global stimer - - with stimer(bdata=True): - vp_stage = get_attr_wrapped_model(model, "vp_stage") - ( - tokens, - labels, - loss_mask, - attention_mask, - position_ids, - cu_seqlens, - max_seqlen, - ) = get_batch(data_iterator, vp_stage) - - if cu_seqlens is None: - packed_seq_params = None - else: - total_tokens = tokens.size(1) if tokens is not None else labels.size(1) - packed_seq_params = PackedSeqParams( - qkv_format="thd", - cu_seqlens_q=cu_seqlens, - cu_seqlens_kv=cu_seqlens, - cu_seqlens_q_padded=None, - cu_seqlens_kv_padded=None, - max_seqlen_q=max_seqlen, - max_seqlen_kv=max_seqlen, - total_tokens=total_tokens, - ) - - timers('batch-generator').stop() - - with stimer: - output_tensor = model( - tokens, - position_ids, - attention_mask, - labels=labels, - packed_seq_params=packed_seq_params, - loss_mask=loss_mask - ) - - # [ModelOpt]: model is needed to access ModelOpt distillation losses - return output_tensor, partial(loss_func, loss_mask, model=model) - - -def is_dataset_built_on_rank(vp_stage=None, is_packed_sequence=False): - if mpu.get_tensor_model_parallel_rank() != 0: - return False - elif is_packed_sequence: - return True - else: - return is_first_or_last_pipeline_stage(vp_stage) - - -def core_gpt_dataset_config_from_args(args): - tokenizer = build_tokenizer(args) - - # Sometimes --data-path is too long, instead we parse it from a file. - blend: Optional[Tuple[List[str], Optional[List[float]]]] - blend_per_split: Optional[List[Optional[Tuple[List[str], Optional[List[float]]]]]] - blend, blend_per_split = get_blend_and_blend_per_split(args) - - sequences_per_dataset = None - if args.per_dataset_sequences_path is not None: - with open(args.per_dataset_sequences_path, "r") as f: - sequences_per_dataset = json.load(f) - - return GPTDatasetConfig( - random_seed=args.seed, - sequence_length=args.seq_length, - blend=blend, - blend_per_split=blend_per_split, - split=args.split, - num_dataset_builder_threads=args.num_dataset_builder_threads, - path_to_cache=args.data_cache_path, - mmap_bin_files=args.mmap_bin_files, - tokenizer=tokenizer, - reset_position_ids=args.reset_position_ids, - reset_attention_mask=args.reset_attention_mask, - eod_mask_loss=args.eod_mask_loss, - create_attention_mask=args.create_attention_mask_in_dataloader, - object_storage_cache_path=args.object_storage_cache_path, - mid_level_dataset_surplus=args.mid_level_dataset_surplus, - allow_ambiguous_pad_tokens=args.allow_ambiguous_pad_tokens, - fast_cache_load=args.dataloader_fast_cache_load, - sequences_per_dataset=sequences_per_dataset, - defer_npy_index_mmap=args.dataloader_defer_npy_index_mmap, - context_parallel_size=args.context_parallel_size, - ) - - -def train_valid_test_datasets_provider(train_val_test_num_samples, vp_stage=None): - """Build the train test and validation datasets. - - Args: - train_val_test_num_samples : A list containing the number of samples in train test and validation. - """ - args = get_args() - config = core_gpt_dataset_config_from_args(args) - - is_packed_sequence = False - if args.sft: - dataset_type = SFTDataset - is_packed_sequence = True # SFT always uses packed sequence - else: - if args.mock_data: - dataset_type = MockGPTDataset - else: - dataset_type = GPTDataset - - print_rank_0("> building train, validation, and test datasets for GPT ...") - - train_ds, valid_ds, test_ds = BlendedMegatronDatasetBuilder( - dataset_type, - train_val_test_num_samples, - partial(is_dataset_built_on_rank, vp_stage=vp_stage, is_packed_sequence=is_packed_sequence), - config - ).build() - - print_rank_0("> finished creating GPT datasets ...") - - return train_ds, valid_ds, test_ds - if __name__ == "__main__": - # Timestamp right after entering __main__ block (after all imports/library setup) - _MAIN_ENTRY_TIME = time.time() - - # Register startup timestamps for timing report in pretrain() - set_startup_timestamps(program_start=_PROGRAM_START_TIME, main_entry=_MAIN_ENTRY_TIME) - - # Temporary for transition to core datasets - train_valid_test_datasets_provider.is_distributed = True - - # Optionally enable inprocess restart on pretrain - pretrain, store = inprocess_restart.maybe_wrap_for_inprocess_restart(pretrain) - - args = parse_and_validate_args( - extra_args_provider=add_modelopt_args if has_nvidia_modelopt else None, - args_defaults={'tokenizer_type': 'GPT2BPETokenizer'}, - ) - pretrain(train_valid_test_datasets_provider, - partial(model_provider, mamba_builder), - ModelType.encoder_or_decoder, - forward_step, - store=store, - ) + # Execute pretrain_hybrid.py as if it were invoked directly. + _this_dir = os.path.dirname(os.path.abspath(__file__)) + runpy.run_path(os.path.join(_this_dir, "pretrain_hybrid.py"), run_name="__main__") diff --git a/tests/functional_tests/test_cases/hybrid/hybrid_dynamic_inference_tp1_pp1_dp8_583m/model_config.yaml b/tests/functional_tests/test_cases/hybrid/hybrid_dynamic_inference_tp1_pp1_dp8_583m/model_config.yaml index f5de6eaac72..4b258afe0d6 100644 --- a/tests/functional_tests/test_cases/hybrid/hybrid_dynamic_inference_tp1_pp1_dp8_583m/model_config.yaml +++ b/tests/functional_tests/test_cases/hybrid/hybrid_dynamic_inference_tp1_pp1_dp8_583m/model_config.yaml @@ -24,7 +24,7 @@ MODEL_ARGS: --pipeline-model-parallel-size: 1 --expert-model-parallel-size: 1 --use-mcore-models: true - --model-provider: mamba + --model-provider: hybrid --init-method-std: 0.0198 --untie-embeddings-and-output-weights: true --disable-bias-linear: true @@ -35,7 +35,7 @@ MODEL_ARGS: --num-attention-heads: 16 --kv-channels: 128 --hybrid-layer-pattern: M-M-M-M*-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M- - --spec: megatron.core.models.mamba.mamba_layer_specs mamba_stack_spec + --spec: megatron.core.models.hybrid.hybrid_layer_specs hybrid_stack_spec --normalization: RMSNorm --swiglu: true --attention-dropout: 0.0 diff --git a/tests/functional_tests/test_cases/hybrid/hybrid_dynamic_inference_tp1_pp1_dp8_583m_chunked_prefill/model_config.yaml b/tests/functional_tests/test_cases/hybrid/hybrid_dynamic_inference_tp1_pp1_dp8_583m_chunked_prefill/model_config.yaml index b10698d521f..bd86d2faa44 100644 --- a/tests/functional_tests/test_cases/hybrid/hybrid_dynamic_inference_tp1_pp1_dp8_583m_chunked_prefill/model_config.yaml +++ b/tests/functional_tests/test_cases/hybrid/hybrid_dynamic_inference_tp1_pp1_dp8_583m_chunked_prefill/model_config.yaml @@ -24,7 +24,7 @@ MODEL_ARGS: --pipeline-model-parallel-size: 1 --expert-model-parallel-size: 1 --use-mcore-models: true - --model-provider: mamba + --model-provider: hybrid --init-method-std: 0.0198 --untie-embeddings-and-output-weights: true --disable-bias-linear: true @@ -35,7 +35,7 @@ MODEL_ARGS: --num-attention-heads: 16 --kv-channels: 128 --hybrid-layer-pattern: M-M-M-M*-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M- - --spec: megatron.core.models.mamba.mamba_layer_specs mamba_stack_spec + --spec: megatron.core.models.hybrid.hybrid_layer_specs hybrid_stack_spec --normalization: RMSNorm --swiglu: true --attention-dropout: 0.0 diff --git a/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp1_pp1_cp1_dgx_a100_1N8G/model_config.yaml b/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp1_pp1_cp1_dgx_a100_1N8G/model_config.yaml index 6d40098499d..9add53f8a49 100644 --- a/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp1_pp1_cp1_dgx_a100_1N8G/model_config.yaml +++ b/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp1_pp1_cp1_dgx_a100_1N8G/model_config.yaml @@ -9,7 +9,7 @@ MODEL_ARGS: --group-query-attention: true --num-query-groups: 8 --hybrid-layer-pattern: M-M-M-M*-M-M-M-M*-M-M-M-M*-M-M-M-M*-M-M-M-M- - --spec: "[megatron.core.models.mamba.mamba_layer_specs mamba_stack_spec]" + --spec: "[megatron.core.models.hybrid.hybrid_layer_specs hybrid_stack_spec]" --log-params-norm: true --log-num-zeros-in-grad: true --log-validation-ppl-to-tensorboard: true diff --git a/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp1_pp2_vpp2_cp1_dgx_a100_1N8G/model_config.yaml b/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp1_pp2_vpp2_cp1_dgx_a100_1N8G/model_config.yaml index 51492f98c6e..25df6aa0359 100644 --- a/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp1_pp2_vpp2_cp1_dgx_a100_1N8G/model_config.yaml +++ b/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp1_pp2_vpp2_cp1_dgx_a100_1N8G/model_config.yaml @@ -9,7 +9,7 @@ MODEL_ARGS: --group-query-attention: true --num-query-groups: 8 --hybrid-layer-pattern: M-M-M-M*-M-|M-M-M*-M-M-|M-M*-M-M-M-|M*-M-M-M-M- - --spec: "[megatron.core.models.mamba.mamba_layer_specs mamba_stack_spec]" + --spec: "[megatron.core.models.hybrid.hybrid_layer_specs hybrid_stack_spec]" --log-params-norm: true --log-num-zeros-in-grad: true --log-validation-ppl-to-tensorboard: true diff --git a/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp1_pp4_cp1_dgx_a100_1N8G/model_config.yaml b/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp1_pp4_cp1_dgx_a100_1N8G/model_config.yaml index 6eff846884a..fe4f9e63714 100644 --- a/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp1_pp4_cp1_dgx_a100_1N8G/model_config.yaml +++ b/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp1_pp4_cp1_dgx_a100_1N8G/model_config.yaml @@ -9,7 +9,7 @@ MODEL_ARGS: --group-query-attention: true --num-query-groups: 8 --hybrid-layer-pattern: M-M-M-M*-M-|M-M-M*-M-M-|M-M*-M-M-M-|M*-M-M-M-M- - --spec: "[megatron.core.models.mamba.mamba_layer_specs mamba_stack_spec]" + --spec: "[megatron.core.models.hybrid.hybrid_layer_specs hybrid_stack_spec]" --log-params-norm: true --log-num-zeros-in-grad: true --log-validation-ppl-to-tensorboard: true diff --git a/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp2_pp1_cp1_dgx_a100_1N8G/model_config.yaml b/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp2_pp1_cp1_dgx_a100_1N8G/model_config.yaml index 8c655bc135c..2339f7a7ce9 100644 --- a/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp2_pp1_cp1_dgx_a100_1N8G/model_config.yaml +++ b/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp2_pp1_cp1_dgx_a100_1N8G/model_config.yaml @@ -9,7 +9,7 @@ MODEL_ARGS: --group-query-attention: true --num-query-groups: 8 --hybrid-layer-pattern: M-M-M-M*-M-M-M-M*-M-M-M-M*-M-M-M-M*-M-M-M-M- - --spec: "[megatron.core.models.mamba.mamba_layer_specs mamba_stack_spec]" + --spec: "[megatron.core.models.hybrid.hybrid_layer_specs hybrid_stack_spec]" --log-params-norm: true --log-num-zeros-in-grad: true --log-validation-ppl-to-tensorboard: true diff --git a/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp2_pp1_cp4_dgx_a100_1N8G/model_config.yaml b/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp2_pp1_cp4_dgx_a100_1N8G/model_config.yaml index 44b588ee140..3efc155949f 100644 --- a/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp2_pp1_cp4_dgx_a100_1N8G/model_config.yaml +++ b/tests/functional_tests/test_cases/hybrid/hybrid_mr_mcore_te_tp2_pp1_cp4_dgx_a100_1N8G/model_config.yaml @@ -9,7 +9,7 @@ MODEL_ARGS: --group-query-attention: true --num-query-groups: 8 --hybrid-layer-pattern: M-M-M-M*-M-M-M-M*-M-M-M-M*-M-M-M-M*-M-M-M-M- - --spec: "[megatron.core.models.mamba.mamba_layer_specs mamba_stack_spec]" + --spec: "[megatron.core.models.hybrid.hybrid_layer_specs hybrid_stack_spec]" --log-params-norm: true --log-num-zeros-in-grad: true --log-validation-ppl-to-tensorboard: true diff --git a/tests/functional_tests/test_cases/hybrid/hybrid_static_inference_tp1_pp1_2B_cudagraphs/model_config.yaml b/tests/functional_tests/test_cases/hybrid/hybrid_static_inference_tp1_pp1_2B_cudagraphs/model_config.yaml index 26708b32a60..02c5cc3055c 100644 --- a/tests/functional_tests/test_cases/hybrid/hybrid_static_inference_tp1_pp1_2B_cudagraphs/model_config.yaml +++ b/tests/functional_tests/test_cases/hybrid/hybrid_static_inference_tp1_pp1_2B_cudagraphs/model_config.yaml @@ -22,7 +22,7 @@ MODEL_ARGS: --pipeline-model-parallel-size: 1 --expert-model-parallel-size: 1 --use-mcore-models: true - --model-provider: mamba + --model-provider: hybrid --init-method-std: 0.0198 --untie-embeddings-and-output-weights: true --disable-bias-linear: true @@ -33,7 +33,7 @@ MODEL_ARGS: --num-attention-heads: 16 --kv-channels: 128 --hybrid-layer-pattern: M-M-M-M*-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M- - --spec: megatron.core.models.mamba.mamba_layer_specs mamba_stack_spec + --spec: megatron.core.models.hybrid.hybrid_layer_specs hybrid_stack_spec --normalization: RMSNorm --swiglu: true --attention-dropout: 0.0 diff --git a/tests/functional_tests/test_cases/hybrid/hybrid_static_inference_tp1_pp1_2B_logitsmatch/model_config.yaml b/tests/functional_tests/test_cases/hybrid/hybrid_static_inference_tp1_pp1_2B_logitsmatch/model_config.yaml index 3964bcb8ecb..2543f59e668 100644 --- a/tests/functional_tests/test_cases/hybrid/hybrid_static_inference_tp1_pp1_2B_logitsmatch/model_config.yaml +++ b/tests/functional_tests/test_cases/hybrid/hybrid_static_inference_tp1_pp1_2B_logitsmatch/model_config.yaml @@ -22,7 +22,7 @@ MODEL_ARGS: --pipeline-model-parallel-size: 1 --expert-model-parallel-size: 1 --use-mcore-models: true - --model-provider: mamba + --model-provider: hybrid --init-method-std: 0.0198 --untie-embeddings-and-output-weights: true --disable-bias-linear: true @@ -33,7 +33,7 @@ MODEL_ARGS: --num-attention-heads: 16 --kv-channels: 128 --hybrid-layer-pattern: M-M-M-M*-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M- - --spec: megatron.core.models.mamba.mamba_layer_specs mamba_stack_spec + --spec: megatron.core.models.hybrid.hybrid_layer_specs hybrid_stack_spec --normalization: RMSNorm --swiglu: true --attention-dropout: 0.0 diff --git a/tests/functional_tests/test_cases/nemotron/nemotron3_super_release_g200/model_config.yaml b/tests/functional_tests/test_cases/nemotron/nemotron3_super_release_g200/model_config.yaml index 1147dda6118..9c5f1807c2d 100644 --- a/tests/functional_tests/test_cases/nemotron/nemotron3_super_release_g200/model_config.yaml +++ b/tests/functional_tests/test_cases/nemotron/nemotron3_super_release_g200/model_config.yaml @@ -42,7 +42,7 @@ MODEL_ARGS: # Network size args --use-mcore-models: true - --spec: megatron.core.models.mamba.mamba_layer_specs mamba_stack_spec + --spec: megatron.core.models.hybrid.hybrid_layer_specs hybrid_stack_spec --is-hybrid-model: true --mamba-num-heads: 128 --num-layers: 88 @@ -90,7 +90,7 @@ MODEL_ARGS: --moe-shared-expert-compute-before-router: true # MTP args - --mtp-spec: megatron.core.models.mamba.mamba_layer_specs mamba_stack_spec + --mtp-spec: megatron.core.models.hybrid.hybrid_layer_specs hybrid_stack_spec --mtp-num-layers: 2 --mtp-hybrid-override-pattern: \"*E\" --calculate-per-token-loss: true diff --git a/tests/test_utils/recipes/h100/mamba.yaml b/tests/test_utils/recipes/h100/mamba.yaml index 703fb53160f..72b44495617 100644 --- a/tests/test_utils/recipes/h100/mamba.yaml +++ b/tests/test_utils/recipes/h100/mamba.yaml @@ -44,7 +44,7 @@ spec: "TENSORBOARD_PATH={assets_dir}/tensorboard" "CHECKPOINT_SAVE_PATH={artifacts_dir}/checkpoints" "CHECKPOINT_LOAD_PATH=/mnt/artifacts/model/{name}" - "TRAINING_SCRIPT_PATH=pretrain_mamba.py" + "TRAINING_SCRIPT_PATH=pretrain_hybrid.py" "TRAINING_PARAMS_PATH=./tests/functional_tests/test_cases/{model}/{test_case}/model_config.yaml" "GOLDEN_VALUES_PATH=./tests/functional_tests/test_cases/{model}/{test_case}/golden_values_{environment}_{platforms}.json" "N_REPEAT={n_repeat}" diff --git a/tests/unit_tests/inference/contexts/test_dynamic_context.py b/tests/unit_tests/inference/contexts/test_dynamic_context.py index 06acdcfec9f..721e69212e3 100644 --- a/tests/unit_tests/inference/contexts/test_dynamic_context.py +++ b/tests/unit_tests/inference/contexts/test_dynamic_context.py @@ -16,7 +16,7 @@ ) from megatron.core.inference.inference_request import DynamicInferenceRequest from megatron.core.inference.sampling_params import SamplingParams -from megatron.core.ssm.mamba_hybrid_layer_allocation import Symbols +from megatron.core.models.hybrid.hybrid_layer_allocation import Symbols from megatron.core.tensor_parallel.random import model_parallel_cuda_manual_seed from megatron.core.transformer.transformer_config import TransformerConfig from tests.unit_tests.test_utilities import Utils diff --git a/tests/unit_tests/inference/engines/test_dynamic_engine.py b/tests/unit_tests/inference/engines/test_dynamic_engine.py index fe2b8fc5802..b23e9562242 100644 --- a/tests/unit_tests/inference/engines/test_dynamic_engine.py +++ b/tests/unit_tests/inference/engines/test_dynamic_engine.py @@ -1,4 +1,4 @@ -# Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. import asyncio import gc @@ -46,8 +46,8 @@ get_gpt_mtp_block_spec, ) from megatron.core.models.gpt.gpt_model import GPTModel -from megatron.core.models.mamba.mamba_layer_specs import mamba_stack_spec -from megatron.core.models.mamba.mamba_model import MambaModel +from megatron.core.models.hybrid.hybrid_layer_specs import hybrid_stack_spec +from megatron.core.models.hybrid.hybrid_model import HybridModel from megatron.core.ssm.mamba_mixer import _check_mamba_sequence_packing_support from megatron.core.tensor_parallel.random import model_parallel_cuda_manual_seed from megatron.core.transformer.cuda_graphs import CudaGraphManager, _CudagraphGlobalRecord @@ -65,7 +65,7 @@ def skip_if_mamba_sequence_packing_not_available(model_provider: str): - if model_provider == "mamba": + if model_provider == "hybrid": sequence_packing_available, reason_for_no_sequence_packing = ( _check_mamba_sequence_packing_support() ) @@ -368,7 +368,7 @@ def _build_test_env(cls, test_config): mtp_block_spec=mtp_block_spec, position_embedding_type=test_config.position_embedding_type, ).cuda() - elif test_config.model_provider == "mamba": + elif test_config.model_provider == "hybrid": pp_size = test_config.pipeline_model_parallel_size # Transformer config. transformer_config = TransformerConfig( @@ -407,7 +407,7 @@ def _build_test_env(cls, test_config): is_hybrid_model=True, # Needs to be set for correct out_proj init ) - # Mamba model. + # Hybrid model. # When speculative tokens are configured, append MTP depth sections # to the hybrid layer pattern so the model creates MTP blocks. mtp_suffix = "/M" * test_config.num_speculative_tokens @@ -415,9 +415,9 @@ def _build_test_env(cls, test_config): mamba_pattern = "M*-" + mtp_suffix else: mamba_pattern = "M*-|M*-" + mtp_suffix - model = MambaModel( + model = HybridModel( config=transformer_config, - mamba_stack_spec=mamba_stack_spec, + hybrid_stack_spec=hybrid_stack_spec, vocab_size=test_config.vocab_size, max_sequence_length=test_config.max_sequence_length, parallel_output=True, @@ -574,7 +574,7 @@ def teardown_class(cls): @pytest.mark.skipif( not is_fa_min_version("2.7.3"), reason="need latest flash attn for dynamic batching" ) - @pytest.mark.parametrize("model_provider", ["gpt", "mamba"]) + @pytest.mark.parametrize("model_provider", ["gpt", "hybrid"]) @pytest.mark.parametrize("num_cuda_graphs", [None, 1, 4, -1]) @pytest.mark.parametrize("cuda_graph_scope", [[], [CudaGraphScope.full_iteration_inference]]) def test_simple(self, model_provider, num_cuda_graphs, cuda_graph_scope) -> None: @@ -632,7 +632,7 @@ def test_simple(self, model_provider, num_cuda_graphs, cuda_graph_scope) -> None if model_provider == "gpt": expected_generated_tokens_list = gpt_expected_generated_tokens - elif model_provider == "mamba": + elif model_provider == "hybrid": expected_generated_tokens_list = mamba_expected_generated_tokens else: raise ValueError(f"Invalid model_provider {model_provider}") @@ -693,7 +693,7 @@ def test_token_overflow_nontransient(self) -> None: @pytest.mark.skipif( not is_fa_min_version("2.7.3"), reason="need latest flash attn for dynamic batching" ) - @pytest.mark.parametrize("model_provider", ["gpt", "mamba"]) + @pytest.mark.parametrize("model_provider", ["gpt", "hybrid"]) def test_block_overflow(self, model_provider: str) -> None: """Test block overflow.""" skip_if_mamba_sequence_packing_not_available(model_provider) @@ -739,7 +739,7 @@ def test_block_overflow_insufficient_kv_cache(self) -> None: @pytest.mark.skipif( not is_fa_min_version("2.7.3"), reason="need latest flash attn for dynamic batching" ) - @pytest.mark.parametrize("model_provider", ["gpt", "mamba"]) + @pytest.mark.parametrize("model_provider", ["gpt", "hybrid"]) def test_multi_add(self, model_provider: str) -> None: """Test adding multiple requests simultaneously.""" skip_if_mamba_sequence_packing_not_available(model_provider) @@ -749,7 +749,7 @@ def test_multi_add(self, model_provider: str) -> None: @pytest.mark.skipif( not is_fa_min_version("2.7.3"), reason="need latest flash attn for dynamic batching" ) - @pytest.mark.parametrize("model_provider", ["gpt", "mamba"]) + @pytest.mark.parametrize("model_provider", ["gpt", "hybrid"]) def test_fixed_output_lengths(self, model_provider: str) -> None: """Test generating a fixed number of output tokens.""" skip_if_mamba_sequence_packing_not_available(model_provider) @@ -792,7 +792,7 @@ def test_cuda_graph_token_counts(self) -> None: @pytest.mark.skipif( not is_fa_min_version("2.7.3"), reason="need latest flash attn for dynamic batching" ) - @pytest.mark.parametrize("model_provider", ["gpt", "mamba"]) + @pytest.mark.parametrize("model_provider", ["gpt", "hybrid"]) @torch.inference_mode() def test_generate_function(self, model_provider: str) -> None: """Test the generate function that processes multiple prompts at once.""" @@ -886,7 +886,7 @@ async def test_run_engine(self): not is_fa_min_version("2.7.3"), reason="need latest flash attn for dynamic batching" ) @pytest.mark.skipif(not is_te_min_version("2.2.0"), reason="TE 2.2.0 is required") - @pytest.mark.parametrize("model_provider", ["gpt", "mamba"]) + @pytest.mark.parametrize("model_provider", ["gpt", "hybrid"]) def test_fp8_inference(self, model_provider: str): skip_if_mamba_sequence_packing_not_available(model_provider) @@ -1092,7 +1092,7 @@ def test_log_probs_token_correspondence(self): @pytest.mark.parametrize("ep_size", [1, 2]) @pytest.mark.parametrize("pp_size", [1, 2]) @pytest.mark.parametrize("tp_size", [1, 2]) - @pytest.mark.parametrize("model_provider", ["gpt", "mamba"]) + @pytest.mark.parametrize("model_provider", ["gpt", "hybrid"]) @pytest.mark.parametrize("transformer_impl", ["local", "inference_optimized"]) @torch.inference_mode() def test_parallel_inference( @@ -1131,7 +1131,7 @@ def test_parallel_inference( "when tp_size > 1." ) ) - if model_provider == "mamba": + if model_provider == "hybrid": pytest.skip( reason="Mamba model is not supported with the inference optimized transformer." ) @@ -1299,11 +1299,11 @@ def test_mamba_chunked_prefill(self): """ Test chunked prefill with a Mamba model. """ - skip_if_mamba_sequence_packing_not_available("mamba") + skip_if_mamba_sequence_packing_not_available("hybrid") # Context max tokens = 50. test_config = DynamicEngineTestConfig( - model_provider="mamba", + model_provider="hybrid", num_requests=0, num_tokens_to_generate=None, num_tokens_total=200, @@ -4319,7 +4319,7 @@ def test_speculative_decoding_mamba_hybrid(self, rejection_mode): Two requests run simultaneously to exercise batched rewind indexing where mamba_metadata.request_to_mamba_state_idx differs per request. """ - skip_if_mamba_sequence_packing_not_available("mamba") + skip_if_mamba_sequence_packing_not_available("hybrid") num_tokens_to_generate = 8 test_config = DynamicEngineTestConfig( @@ -4329,7 +4329,7 @@ def test_speculative_decoding_mamba_hybrid(self, rejection_mode): num_tokens_to_generate=num_tokens_to_generate, num_speculative_tokens=2, materialize_only_last_token_logits=False, - model_provider="mamba", + model_provider="hybrid", ) env = self._build_test_env(test_config) @@ -4460,7 +4460,7 @@ def _create_model(self, model_provider, num_cuda_graphs): pre_process=parallel_state.is_pipeline_first_stage(), post_process=parallel_state.is_pipeline_last_stage(), ).cuda() - elif model_provider == "mamba": + elif model_provider == "hybrid": config = TransformerConfig( params_dtype=torch.bfloat16, num_layers=3, @@ -4476,9 +4476,9 @@ def _create_model(self, model_provider, num_cuda_graphs): add_bias_linear=True, is_hybrid_model=True, ) - model = MambaModel( + model = HybridModel( config=config, - mamba_stack_spec=mamba_stack_spec, + hybrid_stack_spec=hybrid_stack_spec, vocab_size=CHUNKED_CG_VOCAB_SIZE, max_sequence_length=CHUNKED_CG_MAX_SEQ_LEN, parallel_output=True, @@ -4564,7 +4564,7 @@ def _run_to_completion(self, engine, prompts, num_tokens_to_generate): return finished, step_count - @pytest.mark.parametrize("model_provider", ["gpt", "mamba"]) + @pytest.mark.parametrize("model_provider", ["gpt", "hybrid"]) @pytest.mark.parametrize("chunked_prefill", [False, True]) @pytest.mark.parametrize("num_cuda_graphs", [None, 2]) @torch.inference_mode() diff --git a/tests/unit_tests/inference/engines/test_mamba_prefix_caching_e2e.py b/tests/unit_tests/inference/engines/test_hybrid_prefix_caching_e2e.py similarity index 99% rename from tests/unit_tests/inference/engines/test_mamba_prefix_caching_e2e.py rename to tests/unit_tests/inference/engines/test_hybrid_prefix_caching_e2e.py index ce21c775b73..303cf76d122 100644 --- a/tests/unit_tests/inference/engines/test_mamba_prefix_caching_e2e.py +++ b/tests/unit_tests/inference/engines/test_hybrid_prefix_caching_e2e.py @@ -54,8 +54,8 @@ from megatron.core.inference.text_generation_controllers.text_generation_controller import ( TextGenerationController, ) -from megatron.core.models.mamba.mamba_layer_specs import mamba_stack_spec -from megatron.core.models.mamba.mamba_model import MambaModel +from megatron.core.models.hybrid.hybrid_layer_specs import hybrid_stack_spec +from megatron.core.models.hybrid.hybrid_model import HybridModel from megatron.core.ssm.mamba_mixer import _check_mamba_sequence_packing_support from megatron.core.tensor_parallel.random import model_parallel_cuda_manual_seed from megatron.core.transformer.cuda_graphs import CudaGraphManager, _CudagraphGlobalRecord @@ -131,9 +131,9 @@ def _create_model(self, num_cuda_graphs=None): add_bias_linear=True, is_hybrid_model=True, ) - model = MambaModel( + model = HybridModel( config=transformer_config, - mamba_stack_spec=mamba_stack_spec, + hybrid_stack_spec=hybrid_stack_spec, vocab_size=VOCAB_SIZE, max_sequence_length=MAX_SEQ_LEN, parallel_output=True, diff --git a/tests/unit_tests/inference/engines/test_prefix_caching_cuda_graphs.py b/tests/unit_tests/inference/engines/test_prefix_caching_cuda_graphs.py index 52a05f7f80f..26a81c5baef 100644 --- a/tests/unit_tests/inference/engines/test_prefix_caching_cuda_graphs.py +++ b/tests/unit_tests/inference/engines/test_prefix_caching_cuda_graphs.py @@ -37,8 +37,8 @@ ) from megatron.core.models.gpt.gpt_layer_specs import get_gpt_layer_local_spec from megatron.core.models.gpt.gpt_model import GPTModel -from megatron.core.models.mamba.mamba_layer_specs import mamba_stack_spec -from megatron.core.models.mamba.mamba_model import MambaModel +from megatron.core.models.hybrid.hybrid_layer_specs import hybrid_stack_spec +from megatron.core.models.hybrid.hybrid_model import HybridModel from megatron.core.ssm.mamba_mixer import _check_mamba_sequence_packing_support from megatron.core.tensor_parallel.random import model_parallel_cuda_manual_seed from megatron.core.transformer.cuda_graphs import CudaGraphManager, _CudagraphGlobalRecord @@ -121,9 +121,9 @@ def _create_model(self, model_type, num_cuda_graphs=None): add_bias_linear=True, is_hybrid_model=True, ) - model = MambaModel( + model = HybridModel( config=config, - mamba_stack_spec=mamba_stack_spec, + hybrid_stack_spec=hybrid_stack_spec, vocab_size=VOCAB_SIZE, max_sequence_length=MAX_SEQ_LEN, parallel_output=True, @@ -343,9 +343,9 @@ def _create_hybrid_model(self, num_cuda_graphs=None): add_bias_linear=True, is_hybrid_model=True, ) - model = MambaModel( + model = HybridModel( config=config, - mamba_stack_spec=mamba_stack_spec, + hybrid_stack_spec=hybrid_stack_spec, vocab_size=VOCAB_SIZE, max_sequence_length=MAX_SEQ_LEN, parallel_output=True, diff --git a/tests/unit_tests/models/test_dsa_gpt_mamba_equivalence.py b/tests/unit_tests/models/test_dsa_gpt_mamba_equivalence.py index 96b782fad85..229af268a79 100644 --- a/tests/unit_tests/models/test_dsa_gpt_mamba_equivalence.py +++ b/tests/unit_tests/models/test_dsa_gpt_mamba_equivalence.py @@ -1,6 +1,6 @@ # Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. """ -Equivalence tests: GPTModel with DSA vs MambaModel with DSA pattern. +Equivalence tests: GPTModel with DSA vs HybridModel with DSA pattern. A small DeepSeek-V3.2 proxy model (4 GPT layers / 8 Mamba layers) is built, weights are remapped GPT→Mamba, and logprobs are compared to verify they are @@ -9,8 +9,8 @@ Architecture equivalence ------------------------ GPTModel layer N (combined attention + MLP in one TransformerLayer) - ≡ MambaModel layer 2N (D, DSA TransformerLayer: input_layernorm + MLASelfAttention) - + MambaModel layer 2N+1 (-, MLPLayer: fused-norm MLP) + ≡ HybridModel layer 2N (D, DSA TransformerLayer: input_layernorm + MLASelfAttention) + + HybridModel layer 2N+1 (-, MLPLayer: fused-norm MLP) Run with:: @@ -35,10 +35,10 @@ get_transformer_block_with_experimental_attention_variant_spec, ) from megatron.core.models.gpt.gpt_model import GPTModel -from megatron.core.models.mamba.mamba_layer_specs import mamba_stack_spec -from megatron.core.models.mamba.mamba_model import MambaModel +from megatron.core.models.hybrid.hybrid_layer_allocation import validate_segment_layers +from megatron.core.models.hybrid.hybrid_layer_specs import hybrid_stack_spec +from megatron.core.models.hybrid.hybrid_model import HybridModel from megatron.core.process_groups_config import ProcessGroupCollection -from megatron.core.ssm.mamba_hybrid_layer_allocation import validate_segment_layers from megatron.core.tensor_parallel.random import model_parallel_cuda_manual_seed from megatron.core.transformer.transformer_config import MLATransformerConfig from megatron.rl.rl_utils import selective_log_softmax @@ -193,15 +193,15 @@ def _build_mamba_model( layer_pattern: str, pre_process: bool = True, post_process: bool = True, -) -> MambaModel: - """Build a MambaModel with the given hybrid layer pattern.""" +) -> HybridModel: + """Build a HybridModel with the given hybrid layer pattern.""" layer_type_list = validate_segment_layers(layer_pattern) mamba_config = copy.deepcopy(config) mamba_config.num_layers = len(layer_type_list) assert mamba_config.num_layers == _NUM_GPT_LAYERS * 2 - model = MambaModel( + model = HybridModel( config=mamba_config, - mamba_stack_spec=mamba_stack_spec, + hybrid_stack_spec=hybrid_stack_spec, vocab_size=_VOCAB_SIZE, max_sequence_length=_MAX_SEQ_LEN, pre_process=pre_process, @@ -221,14 +221,14 @@ def _build_mamba_model( def _remap_gpt_to_mamba_state_dict( gpt_sd: Dict[str, torch.Tensor], num_local_gpt_layers: int ) -> Dict[str, torch.Tensor]: - """Remap a GPTModel state_dict to a MambaModel state_dict. + """Remap a GPTModel state_dict to a HybridModel state_dict. GPTModel layer N (combined attention + MLP) maps to: - * MambaModel layer 2N – DSA attention (input_layernorm + self_attention) - * MambaModel layer 2N+1 – MLP (mlp.*) + * HybridModel layer 2N – DSA attention (input_layernorm + self_attention) + * HybridModel layer 2N+1 – MLP (mlp.*) Additionally, ``decoder.final_layernorm.*`` (TransformerBlock naming) is - remapped to ``decoder.final_norm.*`` (MambaStack naming). + remapped to ``decoder.final_norm.*`` (HybridStack naming). All other keys (embedding, output_layer, rotary_pos_emb, …) are unchanged. @@ -238,7 +238,7 @@ def _remap_gpt_to_mamba_state_dict( pipeline stage (i.e. ``len(gpt_model.decoder.layers)``). Returns: - Remapped state dict ready for MambaModel.load_state_dict(strict=True). + Remapped state dict ready for HybridModel.load_state_dict(strict=True). """ mamba_sd: Dict[str, torch.Tensor] = {} layer_prefix = "decoder.layers." @@ -380,12 +380,12 @@ def _compare_against_golden_values( @pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA required") @pytest.mark.parametrize("tp,pp", [(1, 1), (2, 1), (1, 2)]) class TestDSAGPTMambaEquivalence: - """Verify logprob equivalence between GPTModel+DSA and MambaModel+DSA. + """Verify logprob equivalence between GPTModel+DSA and HybridModel+DSA. For each distributed configuration (TP, PP), the test: 1. Builds a GPTModel with 4 DSA layers. - 2. Builds a MambaModel with pattern "D-D-D-D-" (8 layers). - 3. Remaps and loads GPT weights into MambaModel (strict=True). + 2. Builds a HybridModel with pattern "D-D-D-D-" (8 layers). + 3. Remaps and loads GPT weights into HybridModel (strict=True). 4. Runs the same random tokens through both models. 5. Asserts logprob tensors are numerically close. """ @@ -416,7 +416,7 @@ def test_dsa_logprobs_match(self, tp: int, pp: int) -> None: num_local_gpt_layers = len(gpt_model.decoder.layers) gpt_sd = gpt_model.state_dict() - # ---- Build MambaModel ---- + # ---- Build HybridModel ---- mamba_model = _build_mamba_model( gpt_config, _MAMBA_PATTERN, pre_process=pre_process, post_process=post_process ) @@ -481,7 +481,7 @@ def test_weight_loading_strict(self, tp: int, pp: int) -> None: assert not unexpected, f"Unexpected keys: {unexpected}" def test_record_and_compare_golden_values(self, tp: int, pp: int) -> None: - """Record GPTModel logprobs as golden values, then compare MambaModel against them. + """Record GPTModel logprobs as golden values, then compare HybridModel against them. Golden values are written to the functional test directory so they can be committed and used by the CI inference golden-value tests. @@ -508,7 +508,7 @@ def test_record_and_compare_golden_values(self, tp: int, pp: int) -> None: gpt_logprobs = _forward_logprobs_pp1(gpt_model, tokens) mamba_logprobs = _forward_logprobs_pp1(mamba_model, tokens) - # Verify MambaModel matches golden values + # Verify HybridModel matches golden values _compare_against_golden_values(mamba_logprobs, gpt_logprobs, abs_tol=1e-3) @@ -520,7 +520,7 @@ def test_record_and_compare_golden_values(self, tp: int, pp: int) -> None: @pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA required") @pytest.mark.parametrize("tp,pp", [(1, 1), (2, 1), (1, 2)]) class TestDSAMoEGPTMambaEquivalence: - """Verify logprob equivalence between GPTModel+DSA+MoE and MambaModel+DSA+MoE. + """Verify logprob equivalence between GPTModel+DSA+MoE and HybridModel+DSA+MoE. Architecture: 4 GPT layers with moe_layer_freq=[0,0,1,1] (first 2 dense, last 2 MoE) maps to 8 Mamba layers with pattern "D-D-DEDE": @@ -556,7 +556,7 @@ def test_dsa_moe_logprobs_match(self, tp: int, pp: int) -> None: num_local_gpt_layers = len(gpt_model.decoder.layers) gpt_sd = gpt_model.state_dict() - # ---- Build MambaModel with MoE pattern ---- + # ---- Build HybridModel with MoE pattern ---- mamba_model = _build_mamba_model( gpt_config, _MOE_MAMBA_PATTERN, pre_process=pre_process, post_process=post_process ) @@ -618,7 +618,7 @@ def test_moe_weight_loading_strict(self, tp: int, pp: int) -> None: assert not unexpected, f"Unexpected keys: {unexpected}" def test_moe_record_and_compare_golden_values(self, tp: int, pp: int) -> None: - """Record GPTModel+MoE logprobs as golden values, then compare MambaModel+MoE.""" + """Record GPTModel+MoE logprobs as golden values, then compare HybridModel+MoE.""" self._skip_if_insufficient_gpus(tp, pp) if tp != 1 or pp != 1: pytest.skip("Golden-value recording only runs for tp=1, pp=1") @@ -640,5 +640,5 @@ def test_moe_record_and_compare_golden_values(self, tp: int, pp: int) -> None: gpt_logprobs = _forward_logprobs_pp1(gpt_model, tokens) mamba_logprobs = _forward_logprobs_pp1(mamba_model, tokens) - # Verify MambaModel matches golden values + # Verify HybridModel matches golden values _compare_against_golden_values(mamba_logprobs, gpt_logprobs, abs_tol=1e-3) diff --git a/tests/unit_tests/post_training/test_modelopt_model_builder.py b/tests/unit_tests/post_training/test_modelopt_model_builder.py index b489d659ec4..2ab8ebfe947 100644 --- a/tests/unit_tests/post_training/test_modelopt_model_builder.py +++ b/tests/unit_tests/post_training/test_modelopt_model_builder.py @@ -39,7 +39,7 @@ def test_model_provider_switches_to_modelopt_builder(monkeypatch): monkeypatch.setattr(mp, "has_nvidia_modelopt", True) monkeypatch.setattr(mp, "get_args", lambda: args) monkeypatch.setattr( - mp, "modelopt_gpt_mamba_builder", _sentinel_builder(modelopt_result, modelopt_calls) + mp, "modelopt_gpt_hybrid_builder", _sentinel_builder(modelopt_result, modelopt_calls) ) # original_builder should be ignored when ModelOpt is enabled. diff --git a/tests/unit_tests/resharding/test_model_swap.py b/tests/unit_tests/resharding/test_model_swap.py index 70d81d97829..e2d6a2bd096 100644 --- a/tests/unit_tests/resharding/test_model_swap.py +++ b/tests/unit_tests/resharding/test_model_swap.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2024-2026, NVIDIA CORPORATION. All rights reserved. import copy import gc import os @@ -37,8 +37,8 @@ try: import mamba_ssm # noqa: F401 - from megatron.core.models.mamba.mamba_layer_specs import mamba_stack_spec - from megatron.core.models.mamba.mamba_model import MambaModel + from megatron.core.models.hybrid.hybrid_layer_specs import hybrid_stack_spec + from megatron.core.models.hybrid.hybrid_model import HybridModel has_mamba_deps = True except Exception: @@ -203,9 +203,9 @@ def _build_mamba( parallel_output: bool = True, ): pre_process, post_process = _pp_flags(pg_collection) - model = MambaModel( + model = HybridModel( config=config, - mamba_stack_spec=mamba_stack_spec, + hybrid_stack_spec=hybrid_stack_spec, vocab_size=vocab_size, max_sequence_length=seq_len, hybrid_layer_pattern=hybrid_layer_pattern, diff --git a/tools/checkpoint/remap_gpt_dsa_to_mamba.py b/tools/checkpoint/remap_gpt_dsa_to_mamba.py index 8a6888d1dc7..3d11c981c25 100644 --- a/tools/checkpoint/remap_gpt_dsa_to_mamba.py +++ b/tools/checkpoint/remap_gpt_dsa_to_mamba.py @@ -1,16 +1,16 @@ #!/usr/bin/env python3 # Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. -"""Convert a GPTModel DSA checkpoint to a MambaModel-compatible checkpoint. +"""Convert a GPTModel DSA checkpoint to a HybridModel-compatible checkpoint. A GPTModel with ``--experimental-attention-variant dsa`` uses one combined -TransformerLayer per model layer (attention + MLP). The equivalent MambaModel +TransformerLayer per model layer (attention + MLP). The equivalent HybridModel with pattern ``D-D-...`` stores them as two separate layers: * Layer 2N – DSA attention (TransformerLayer: input_layernorm + MLASelfAttention) * Layer 2N+1 – MLP (MLPLayer: fused-norm MLP) This script loads a GPTModel Distributed Checkpoint (DCP), remaps the state-dict -keys, and saves a new DCP that can be loaded by MambaModel. +keys, and saves a new DCP that can be loaded by HybridModel. Usage ----- @@ -43,14 +43,14 @@ def _remap_key(key: str, num_gpt_layers: int) -> str: - """Return the MambaModel state-dict key corresponding to *key* from GPTModel. + """Return the HybridModel state-dict key corresponding to *key* from GPTModel. Args: key: A key from the GPTModel state dict. num_gpt_layers: Total number of GPT decoder layers (across all PP stages). Returns: - The remapped key for MambaModel. + The remapped key for HybridModel. Raises: ValueError: If an unexpected sub-key is encountered in a decoder layer. @@ -58,7 +58,7 @@ def _remap_key(key: str, num_gpt_layers: int) -> str: layer_prefix = "decoder.layers." final_ln_prefix = "decoder.final_layernorm." - # Final layernorm name differs between TransformerBlock and MambaStack + # Final layernorm name differs between TransformerBlock and HybridStack if key.startswith(final_ln_prefix): return "decoder.final_norm." + key[len(final_ln_prefix):] @@ -96,11 +96,11 @@ def _remap_state_dict( def convert(input_path: Path, output_path: Path, num_gpt_layers: int) -> None: - """Load a GPTModel DCP checkpoint, remap keys, and save as MambaModel DCP. + """Load a GPTModel DCP checkpoint, remap keys, and save as HybridModel DCP. Args: input_path: Path to the GPTModel DCP checkpoint directory. - output_path: Destination directory for the MambaModel DCP checkpoint. + output_path: Destination directory for the HybridModel DCP checkpoint. num_gpt_layers: Number of GPT decoder layers in the original model. """ try: @@ -139,7 +139,7 @@ def convert(input_path: Path, output_path: Path, num_gpt_layers: int) -> None: output_path.mkdir(parents=True, exist_ok=True) torch_save_to_dcp(str(tmp_mamba), str(output_path)) - print(f"MambaModel DCP checkpoint saved to: {output_path}") + print(f"HybridModel DCP checkpoint saved to: {output_path}") finally: for tmp in (tmp_flat, output_path.parent / "_tmp_mamba_flat.pt"): @@ -149,7 +149,7 @@ def convert(input_path: Path, output_path: Path, num_gpt_layers: int) -> None: def main() -> None: parser = argparse.ArgumentParser( - description="Convert GPTModel DSA checkpoint to MambaModel-compatible format." + description="Convert GPTModel DSA checkpoint to HybridModel-compatible format." ) parser.add_argument( "--input", required=True, type=Path, @@ -157,7 +157,7 @@ def main() -> None: ) parser.add_argument( "--output", required=True, type=Path, - help="Destination path for the MambaModel DCP checkpoint.", + help="Destination path for the HybridModel DCP checkpoint.", ) parser.add_argument( "--num-gpt-layers", required=True, type=int, diff --git a/tools/run_mamba_text_generation_server.py b/tools/run_hybrid_text_generation_server.py similarity index 89% rename from tools/run_mamba_text_generation_server.py rename to tools/run_hybrid_text_generation_server.py index 33465f1bb4a..e70e5389e88 100644 --- a/tools/run_mamba_text_generation_server.py +++ b/tools/run_hybrid_text_generation_server.py @@ -8,4 +8,4 @@ from run_text_generation_server import main if __name__ == "__main__": - main(model_type="mamba") + main(model_type="hybrid") diff --git a/tools/run_mamba_text_generation_server_completions.py b/tools/run_hybrid_text_generation_server_completions.py similarity index 89% rename from tools/run_mamba_text_generation_server_completions.py rename to tools/run_hybrid_text_generation_server_completions.py index 33465f1bb4a..e70e5389e88 100644 --- a/tools/run_mamba_text_generation_server_completions.py +++ b/tools/run_hybrid_text_generation_server_completions.py @@ -8,4 +8,4 @@ from run_text_generation_server import main if __name__ == "__main__": - main(model_type="mamba") + main(model_type="hybrid") diff --git a/tools/run_inference_performance_test.py b/tools/run_inference_performance_test.py index ac9e92d3639..d42453c62ed 100644 --- a/tools/run_inference_performance_test.py +++ b/tools/run_inference_performance_test.py @@ -9,7 +9,7 @@ import torch from gpt_builders import gpt_builder -from mamba_builders import mamba_builder +from hybrid_builders import hybrid_builder from megatron.core.inference.contexts import StaticInferenceContext from megatron.core.inference.engines import DynamicInferenceEngine, StaticInferenceEngine from megatron.core.inference.engines.abstract_engine import AbstractEngine diff --git a/tools/run_text_generation_server.py b/tools/run_text_generation_server.py index 5a2940f1a4c..e871214e739 100644 --- a/tools/run_text_generation_server.py +++ b/tools/run_text_generation_server.py @@ -15,7 +15,7 @@ import torch from gpt_builders import gpt_builder -from mamba_builders import mamba_builder +from hybrid_builders import hybrid_builder from megatron.core.inference.contexts import StaticInferenceContext from megatron.core.inference.engines import AbstractEngine, StaticInferenceEngine from megatron.core.inference.engines.abstract_engine import AbstractEngine @@ -140,8 +140,16 @@ def main(model_type: str = "gpt"): # Set up model and load checkpoint if model_type == "gpt": model_builder = gpt_builder - elif model_type == "mamba": - model_builder = mamba_builder + elif model_type in ("hybrid", "mamba"): + if model_type == "mamba": + import warnings + + warnings.warn( + 'model_type="mamba" is deprecated. Use model_type="hybrid" instead.', + DeprecationWarning, + stacklevel=2, + ) + model_builder = hybrid_builder else: raise ValueError(f"Invalid model provider {model_type}") model = get_model(partial(model_provider, model_builder), wrap_with_ddp=False) diff --git a/train_rl.py b/train_rl.py index 8bcee5f096d..3e4ccdf4f39 100644 --- a/train_rl.py +++ b/train_rl.py @@ -8,7 +8,7 @@ import torch from gpt_builders import gpt_builder -from mamba_builders import mamba_builder +from hybrid_builders import hybrid_builder from megatron.core import mpu from megatron.core.enums import ModelType from megatron.core.models.gpt import GPTModel @@ -392,7 +392,7 @@ def _model_builder( args, pre_process, post_process, vp_stage=None, config=None, pg_collection=None ): if is_hybrid_model(args): - return mamba_builder( + return hybrid_builder( args, pre_process, post_process, From a52014ca000b52ea5772033e9639650896003415 Mon Sep 17 00:00:00 2001 From: Haoran Zhang Date: Thu, 23 Apr 2026 06:16:42 -0700 Subject: [PATCH 010/105] Include mtp layers in token per expert logging (#4412) --- megatron/training/activation_logging.py | 76 +++++++++++++++------ tests/unit_tests/test_activation_logging.py | 29 +++++--- 2 files changed, 77 insertions(+), 28 deletions(-) diff --git a/megatron/training/activation_logging.py b/megatron/training/activation_logging.py index 8f790a116c9..0600cda419b 100644 --- a/megatron/training/activation_logging.py +++ b/megatron/training/activation_logging.py @@ -65,6 +65,25 @@ def _discover_te_types(): LINEAR_TYPES = (nn.Linear, nn.Embedding, ColumnParallelLinear, RowParallelLinear, Router, *_TE_TYPES) +def _parse_tpe_module_name(module_name: str) -> Tuple[str, int | None, int] | None: + """Parse a TPE-eligible module name into ``(block, mtp_idx, layer)``. + + Returns ``None`` if *module_name* matches neither the decoder nor the MTP pattern. + + Examples:: + + decoder.layers.3.mlp.experts.linear_fc1 -> ("decoder", None, 3) + mtp.layers.0.mtp_model_layer.layers.1.mlp.experts.linear_fc1 -> ("mtp", 0, 1) + """ + if m := re.fullmatch(r'decoder\.layers\.(\d+)\.mlp\.experts\.linear_fc1', module_name): + return "decoder", None, int(m.group(1)) + if m := re.fullmatch( + r'mtp\.layers\.(\d+)\.mtp_model_layer\.layers\.(\d+)\.mlp\.experts\.linear_fc1', + module_name, + ): + return "mtp", int(m.group(1)), int(m.group(2)) + return None + def _register_hooks(model, module_types, hook_factory, *, name_filter=None): """Walk *model* and register a forward hook on every module matching *module_types*. @@ -109,8 +128,10 @@ def __init__(self, save_dir: str): self._activations_state_dict: defaultdict = defaultdict(dict) self._activation_hooks: List[torch.utils.hooks.RemovableHook] = [] - # Tokens-per-expert state: layer -> list of per-microbatch token counts. - self._tpe_records: dict[str, list] = defaultdict(list) + # Tokens-per-expert state: per-microbatch token counts. Decoder entries + # are keyed by ``layer``; MTP entries by ``(mtp_idx, inner_layer)``. + self._decoder_tpe_records: dict[int, list[list[int]]] = defaultdict(list) + self._mtp_tpe_records: dict[Tuple[int, int], list[list[int]]] = defaultdict(list) self._tpe_hooks: List[torch.utils.hooks.RemovableHook] = [] # ------------------------------------------------------------------ @@ -158,27 +179,33 @@ def save_activations(self, iteration: int): # Tokens-per-expert hooks # ------------------------------------------------------------------ - def _make_tpe_hook(self, _model_chunk_name: str, module_name: str) -> Callable: + def _make_tpe_hook(self, _model_chunk_name: str, module_name: str) -> Callable | None: """Forward hook that captures only the non-Tensor ``input1`` (tokens_per_expert). - The layer number is extracted from *module_name* - (e.g. ``decoder.layers.3.mlp.experts.linear_fc1`` → ``3``). + Attaches to main decoder MoE layers + (``decoder.layers..mlp.experts.linear_fc1``) and MTP MoE layers + (``mtp.layers..mtp_model_layer.layers..mlp.experts.linear_fc1``). + Returns ``None`` (and logs a warning) for any other module name. """ - m = re.search(r'\.layers\.(\d+)\.', module_name) - if not m: + parsed = _parse_tpe_module_name(module_name) + if parsed is None: logger.warning( "Cannot extract layer number from module name: %r — " "skipping tokens-per-expert hook for this module", module_name ) return None - layer = m.group(1) + block, mtp_idx, layer = parsed + if block == "decoder": + records, key = self._decoder_tpe_records, layer + else: + records, key = self._mtp_tpe_records, (mtp_idx, layer) def hook(_, args, kwargs, output): input_tuple = args if isinstance(args, tuple) else (args,) if len(input_tuple) > 1 and input_tuple[1] is not None: inp = input_tuple[1] if not isinstance(inp, torch.Tensor): - self._tpe_records[layer].append(list(inp)) + records[key].append(list(inp)) return hook @@ -198,24 +225,35 @@ def save_tpe(self, iteration: int): """Append captured tokens-per-expert records as JSON Lines. Each rank writes to its own file under ``{save_dir}/tokens_per_expert/``, - e.g. ``rank0.jsonl``, ``rank1.jsonl``. Each line is a JSON object:: + e.g. ``rank0.jsonl``, ``rank1.jsonl``. Each line is a JSON object; the + ``mtp_idx`` field is present only for MTP entries:: - {"iter": 100, "layer": 3, "tpe": [[128, 64], [96, 80]]} + {"iter": 100, "block": "decoder", "layer": 3, "tpe": [[128, 64], [96, 80]]} + {"iter": 100, "block": "mtp", "mtp_idx": 0, "layer": 1, "tpe": [[50, 50]]} """ - if not self._tpe_records: + if not self._decoder_tpe_records and not self._mtp_tpe_records: return rank = torch.distributed.get_rank() if torch.distributed.is_initialized() else 0 tpe_dir = os.path.join(self._save_dir, "tokens_per_expert") os.makedirs(tpe_dir, exist_ok=True) filepath = os.path.join(tpe_dir, f"rank{rank}.jsonl") - lines = "".join( - json.dumps({"iter": iteration, "layer": int(layer), - "tpe": microbatches}) + "\n" - for layer, microbatches in sorted(self._tpe_records.items()) - ) + + lines = [] + for layer, microbatches in sorted(self._decoder_tpe_records.items()): + lines.append(json.dumps({ + "iter": iteration, "block": "decoder", + "layer": layer, "tpe": microbatches, + }) + "\n") + for (mtp_idx, layer), microbatches in sorted(self._mtp_tpe_records.items()): + lines.append(json.dumps({ + "iter": iteration, "block": "mtp", + "mtp_idx": mtp_idx, "layer": layer, "tpe": microbatches, + }) + "\n") + with open(filepath, "a") as f: - f.write(lines) - self._tpe_records.clear() + f.writelines(lines) + self._decoder_tpe_records.clear() + self._mtp_tpe_records.clear() _LOGGER: ActivationLogger | None = None diff --git a/tests/unit_tests/test_activation_logging.py b/tests/unit_tests/test_activation_logging.py index cc7634d6558..afd6bc2f795 100644 --- a/tests/unit_tests/test_activation_logging.py +++ b/tests/unit_tests/test_activation_logging.py @@ -25,12 +25,21 @@ def simple_model(): class TestMakeTpeHook: """Tests for _make_tpe_hook regex layer extraction.""" - def test_extracts_layer_number(self, logger): + def test_extracts_decoder_layer_number(self, logger): hook = logger._make_tpe_hook("chunk0", "decoder.layers.3.mlp.experts.linear_fc1") assert hook is not None fake_tpe = [128, 64, 96, 80] hook(None, (torch.zeros(1), fake_tpe), {}, torch.zeros(1)) - assert logger._tpe_records["3"] == [fake_tpe] + assert logger._decoder_tpe_records[3] == [fake_tpe] + + def test_extracts_mtp_layer_number(self, logger): + hook = logger._make_tpe_hook( + "chunk0", "mtp.layers.0.mtp_model_layer.layers.1.mlp.experts.linear_fc1" + ) + assert hook is not None + fake_tpe = [50, 50] + hook(None, (torch.zeros(1), fake_tpe), {}, torch.zeros(1)) + assert logger._mtp_tpe_records[(0, 1)] == [fake_tpe] def test_returns_none_for_non_matching_name(self, logger, caplog): with caplog.at_level(logging.WARNING): @@ -43,9 +52,10 @@ class TestSaveTpe: """Tests for save_tpe JSONL output.""" def test_creates_jsonl(self, tmp_path, logger): - logger._tpe_records["3"].append([128, 64, 96, 80]) - logger._tpe_records["3"].append([100, 90, 110, 70]) - logger._tpe_records["7"].append([200, 200]) + logger._decoder_tpe_records[3].append([10, 20]) + logger._decoder_tpe_records[3].append([30, 40]) + logger._decoder_tpe_records[7].append([50, 60]) + logger._mtp_tpe_records[(0, 1)].append([70, 80]) logger.save_tpe(iteration=100) @@ -55,15 +65,16 @@ def test_creates_jsonl(self, tmp_path, logger): records = [json.loads(line) for line in filepath.read_text().strip().split("\n")] assert records == [ - {"iter": 100, "layer": 3, "tpe": [[128, 64, 96, 80], [100, 90, 110, 70]]}, - {"iter": 100, "layer": 7, "tpe": [[200, 200]]}, + {"iter": 100, "block": "decoder", "layer": 3, "tpe": [[10, 20], [30, 40]]}, + {"iter": 100, "block": "decoder", "layer": 7, "tpe": [[50, 60]]}, + {"iter": 100, "block": "mtp", "mtp_idx": 0, "layer": 1, "tpe": [[70, 80]]}, ] def test_appends_across_calls(self, tmp_path, logger): - logger._tpe_records["0"].append([10, 20]) + logger._decoder_tpe_records[0].append([10, 20]) logger.save_tpe(iteration=100) - logger._tpe_records["0"].append([30, 40]) + logger._decoder_tpe_records[0].append([30, 40]) logger.save_tpe(iteration=200) rank = torch.distributed.get_rank() if torch.distributed.is_initialized() else 0 From 32275b20f1926813713969ab1d9f20046b89f9e4 Mon Sep 17 00:00:00 2001 From: Seonmyeong Bak Date: Thu, 23 Apr 2026 09:56:13 -0700 Subject: [PATCH 011/105] fix: NVRx async compatibility and defer resiliency import (#4420) Co-authored-by: Dmytro Pykhtar <37850217+dimapihtar@users.noreply.github.com> --- .../core/dist_checkpointing/serialization.py | 7 ++- .../strategies/fully_parallel.py | 2 +- .../dist_checkpointing/strategies/nvrx.py | 55 +++++++++++++++++++ .../dist_checkpointing/strategies/torch.py | 50 +++++++++-------- megatron/training/arguments.py | 3 + megatron/training/async_utils.py | 44 +++++++++++---- megatron/training/checkpointing.py | 21 +++---- megatron/training/config/training_config.py | 17 ++++-- megatron/training/inprocess_restart.py | 17 +++--- megatron/training/training.py | 8 +-- megatron/training/utils.py | 6 ++ .../dist_checkpointing/test_async_save.py | 10 ++++ 12 files changed, 171 insertions(+), 69 deletions(-) create mode 100644 megatron/core/dist_checkpointing/strategies/nvrx.py diff --git a/megatron/core/dist_checkpointing/serialization.py b/megatron/core/dist_checkpointing/serialization.py index 2ee7970f143..93d04e54c06 100644 --- a/megatron/core/dist_checkpointing/serialization.py +++ b/megatron/core/dist_checkpointing/serialization.py @@ -139,7 +139,12 @@ def load( ckpt_sharded_metadata, ) - async_strategy = getattr(common_state_dict.get("args"), "async_strategy", "nvrx") + ckpt_args = common_state_dict.get("args") + async_strategy = ( + getattr(ckpt_args, "async_strategy", "mcore") + if getattr(ckpt_args, "async_save", False) + else "mcore" + ) loaded_state_dict = sharded_strategy.load(sharded_state_dict, checkpoint_dir, async_strategy) merge(common_state_dict, loaded_state_dict) diff --git a/megatron/core/dist_checkpointing/strategies/fully_parallel.py b/megatron/core/dist_checkpointing/strategies/fully_parallel.py index 6638f215cd4..db3c8ee6cae 100644 --- a/megatron/core/dist_checkpointing/strategies/fully_parallel.py +++ b/megatron/core/dist_checkpointing/strategies/fully_parallel.py @@ -189,7 +189,7 @@ def load( self, sharded_state_dict: ShardedStateDict, checkpoint_dir: Path, - async_strategy: str = "nvrx", + async_strategy: str = "mcore", ) -> StateDict: """Distributes the load and calls underlying strategy only for parts of the state dict. diff --git a/megatron/core/dist_checkpointing/strategies/nvrx.py b/megatron/core/dist_checkpointing/strategies/nvrx.py new file mode 100644 index 00000000000..1df26f00ff4 --- /dev/null +++ b/megatron/core/dist_checkpointing/strategies/nvrx.py @@ -0,0 +1,55 @@ +# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +"""Helpers for interacting with the experimental nvidia-resiliency-ext API.""" + +from importlib import import_module +from typing import Any, Callable, Dict + + +def has_nvrx_async_support() -> bool: + """Checks whether the NVRx async checkpointing symbols Megatron uses are importable.""" + try: + core = import_module("nvidia_resiliency_ext.checkpointing.async_ckpt.core") + cached_metadata_reader = import_module( + "nvidia_resiliency_ext.checkpointing.async_ckpt.cached_metadata_filesystem_reader" + ) + filesystem_async = import_module( + "nvidia_resiliency_ext.checkpointing.async_ckpt.filesystem_async" + ) + state_dict_saver = import_module( + "nvidia_resiliency_ext.checkpointing.async_ckpt.state_dict_saver" + ) + except (ImportError, ModuleNotFoundError): + return False + + required_symbols = ( + getattr(core, "AsyncCallsQueue", None), + getattr(core, "AsyncRequest", None), + getattr(cached_metadata_reader, "CachedMetadataFileSystemReader", None), + getattr(filesystem_async, "FileSystemWriterAsync", None), + getattr(filesystem_async, "get_write_results_queue", None), + getattr(state_dict_saver, "CheckpointMetadataCache", None), + getattr(state_dict_saver, "save_state_dict_async_finalize", None), + getattr(state_dict_saver, "save_state_dict_async_plan", None), + ) + return all(symbol is not None for symbol in required_symbols) and hasattr( + filesystem_async, "_results_queue" + ) + + +def make_nvrx_async_request( + async_request_cls: type, + async_fn: Callable[..., Any], + async_fn_args: Any, + finalize_fns: list[Callable[..., Any]], + async_fn_kwargs: Dict[str, Any] | None = None, + preload_fn: Callable[..., Any] | None = None, +): + """Builds an AsyncRequest using the expected NVRx API.""" + return async_request_cls( + async_fn, + async_fn_args, + finalize_fns, + async_fn_kwargs=async_fn_kwargs or {}, + preload_fn=preload_fn, + ) diff --git a/megatron/core/dist_checkpointing/strategies/torch.py b/megatron/core/dist_checkpointing/strategies/torch.py index 3e467c1f9dd..7943561700f 100644 --- a/megatron/core/dist_checkpointing/strategies/torch.py +++ b/megatron/core/dist_checkpointing/strategies/torch.py @@ -6,13 +6,12 @@ import os import pickle import warnings -from abc import ABC from collections import defaultdict from contextlib import contextmanager from itertools import product from logging import getLogger from pathlib import Path -from typing import Any, Dict, Iterable, List, Optional, Tuple, Union, cast +from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Tuple, Union, cast import torch from packaging.version import Version as PkgVersion @@ -51,19 +50,18 @@ ) from .async_utils import AsyncRequest from .checkpointable import CheckpointableShardedTensor, LocalShardsContainer +from .nvrx import has_nvrx_async_support, make_nvrx_async_request -try: +if TYPE_CHECKING: from nvidia_resiliency_ext.checkpointing.async_ckpt.core import AsyncRequest as NVRxAsyncRequest from nvidia_resiliency_ext.checkpointing.async_ckpt.state_dict_saver import ( CheckpointMetadataCache, ) +else: + CheckpointMetadataCache = Any + NVRxAsyncRequest = Any - HAVE_NVRX = True -except (ImportError, ModuleNotFoundError): - CheckpointMetadataCache = ABC - NVRxAsyncRequest = ABC - - HAVE_NVRX = False +HAVE_NVRX = has_nvrx_async_support() try: if not torch.cuda.is_available(): @@ -103,6 +101,7 @@ class MCoreSavePlan: logger = getLogger(__name__) +_logged_mcore_async_deprecation = False def flatten_state_dict( @@ -651,9 +650,8 @@ def __init__( self.validated_loaded_metadata_reuse = False def save(self, sharded_state_dict: ShardedStateDict, checkpoint_dir: Path): - """Each async strategy can be trivially used as a sync strategy.""" - strategy = "nvrx" if HAVE_NVRX else "mcore" - async_request = self.async_save(sharded_state_dict, checkpoint_dir, async_strategy=strategy) + """Sync save always uses the built-in implementation.""" + async_request = self.async_save(sharded_state_dict, checkpoint_dir, async_strategy="mcore") async_request.execute_sync() del async_request @@ -671,11 +669,14 @@ def async_save( Returns: None """ + global _logged_mcore_async_deprecation if async_strategy == "mcore": - logger.warning( - "MCore's async save is deprecated and will be removed in the future releases. " - "Please, use NVRx async solution by setting `async_strategy` to `nvrx`." - ) + if not _logged_mcore_async_deprecation: + logger.warning( + "MCore's async save is deprecated and will be removed in the future releases. " + "Please, use NVRx async solution by setting `async_strategy` to `nvrx`." + ) + _logged_mcore_async_deprecation = True # Translate the state dict (sharded_state_dict, flat_mapping, rename_mapping) = ( @@ -701,7 +702,9 @@ def async_save( if async_strategy == "nvrx": if self._metadata_cache is None: self._metadata_cache = checkpointable_metadata_cache() - if self.cached_global_metadata is not None: + if self.cached_global_metadata is not None and hasattr( + self._metadata_cache, "set_cached_global_metadata" + ): self._metadata_cache.set_cached_global_metadata(self.cached_global_metadata) # Define additional arguments async_writer_kwargs["use_cached_data_structure"] = self.use_cached_ckpt_structure @@ -818,11 +821,13 @@ def _get_save_and_finalize_callbacks( def finalize_fn(): save_state_dict_async_finalize(*save_state_dict_ret) - return async_request(save_fn, save_args, [finalize_fn], preload_fn=preload_fn) + return make_nvrx_async_request( + async_request, save_fn, save_args, [finalize_fn], preload_fn=preload_fn + ) def _get_filesystem_reader( - checkpoint_dir: Union[str, Path], cache_metadata: bool = False, async_strategy: str = "nvrx" + checkpoint_dir: Union[str, Path], cache_metadata: bool = False, async_strategy: str = "mcore" ) -> FileSystemReader: if MultiStorageClientFeature.is_enabled(): msc = MultiStorageClientFeature.import_package() @@ -846,7 +851,7 @@ def load( self, sharded_state_dict: ShardedStateDict, checkpoint_dir: Path, - async_strategy: str = "nvrx", + async_strategy: str = "mcore", ) -> StateDict: """Translates MCore ShardedTensors to PyT ShardedTensors & loads from PyT Distributed fmt. @@ -1062,9 +1067,8 @@ def get_async_strategy(async_strategy: str = "nvrx", module: str = None) -> tupl async_strategy = "nvrx" except (ImportError, ModuleNotFoundError): raise ModuleNotFoundError( - "nvidia-resiliency-ext package is not installed. " - "Please, install nvidia-resiliency-ext package or set `async_strategy` to `mcore` " - "to enable async save strategy." + "A compatible `nvidia-resiliency-ext` installation is required for " + '`async_strategy="nvrx"`. Please install it or set `async_strategy` to `mcore`.' ) elif async_strategy == "mcore": # do mcore async imports diff --git a/megatron/training/arguments.py b/megatron/training/arguments.py index a1ae68d4a38..d6a886c6397 100644 --- a/megatron/training/arguments.py +++ b/megatron/training/arguments.py @@ -1535,6 +1535,9 @@ def validate_args(args, defaults={}): ) args.async_save = False + if not args.async_save: + args.async_strategy = "mcore" + # Inference args if args.inference_batch_times_seqlen_threshold > -1: assert args.pipeline_model_parallel_size > 1, \ diff --git a/megatron/training/async_utils.py b/megatron/training/async_utils.py index 3d283c056fa..4dae2170039 100644 --- a/megatron/training/async_utils.py +++ b/megatron/training/async_utils.py @@ -8,14 +8,22 @@ import logging import time from abc import ABC +from typing import TYPE_CHECKING, Any from megatron.core.dist_checkpointing.strategies.async_utils import AsyncRequest +from megatron.core.dist_checkpointing.strategies.nvrx import ( + make_nvrx_async_request, +) from megatron.core.dist_checkpointing.strategies.torch import get_async_strategy from megatron.training import get_args from megatron.training.utils import print_rank_0 -try: +if TYPE_CHECKING: from nvidia_resiliency_ext.checkpointing.async_ckpt.core import AsyncRequest as NVRxAsyncRequest +else: + NVRxAsyncRequest = Any + +try: from nvidia_resiliency_ext.checkpointing.async_ckpt.filesystem_async import _results_queue from nvidia_resiliency_ext.checkpointing.async_ckpt.state_dict_saver import ( save_state_dict_async_finalize, @@ -26,8 +34,6 @@ save_state_dict_async_finalize, ) - NVRxAsyncRequest = ABC - logger = logging.getLogger(__name__) # Singleton manager of async calls @@ -70,13 +76,13 @@ def init_persistent_async_worker(rank: int, mp_mode: str = 'spawn'): ), ) # initialize the persistent caller with QoS priorities from args - kwargs = {} + warmup_kwargs = {} if async_strategy == "mcore": # Note: nvidia-resiliency-ext uses is_daemon instead of mp_mode (always spawns) - kwargs["mp_mode"] = mp_mode + warmup_kwargs["mp_mode"] = mp_mode elif async_strategy == "nvrx": if "cpu_shm_mode" in inspect.signature(AsyncCallsQueue.warmup_persistent_caller).parameters: - kwargs["cpu_shm_mode"] = args.async_ckpt_use_cpu_shm + warmup_kwargs["cpu_shm_mode"] = args.async_ckpt_use_cpu_shm elif args.async_ckpt_use_cpu_shm: raise AssertionError( "Installed nvidia-resiliency-ext does not support cpu_shm_mode. " @@ -86,10 +92,16 @@ def init_persistent_async_worker(rank: int, mp_mode: str = 'spawn'): rank, cpu_priority=args.async_ckpt_cpu_priority, io_priority=args.async_ckpt_io_priority, - **kwargs, + **warmup_kwargs, ) # initialize ckpt write results queue - get_write_results_queue('fork') + if async_strategy == "nvrx": + if "mp_mode" not in inspect.signature(get_write_results_queue).parameters: + raise AssertionError( + "Installed nvidia-resiliency-ext does not support " + "get_write_results_queue(mp_mode=...). Update nvidia-resiliency-ext." + ) + get_write_results_queue(mp_mode="fork") if rank == 0: print(f"init_persistent_async_worker: rank {rank}, Async Caller Started in {time.time() - time_start} seconds", flush=True) @@ -157,14 +169,24 @@ def reset_persistent_async_worker(async_strategy): module.clear_metadata_cache() -def get_save_and_finalize_callbacks(writer, save_state_dict_ret) -> NVRxAsyncRequest: +def get_save_and_finalize_callbacks( + writer, save_state_dict_ret, async_strategy: str = "nvrx" +) -> AsyncRequest | NVRxAsyncRequest: """Creates an async save request for fsdp_dtensor & torch_dcp with a finalize function.""" save_fn, preload_fn, save_args = writer.get_save_function_and_args() + _, async_modules = get_async_strategy(async_strategy) + async_request_cls = async_modules["AsyncRequest"] + save_state_dict_async_finalize = async_modules["save_state_dict_async_finalize"] def finalize_fn(): """Finalizes async checkpointing and synchronizes processes.""" save_state_dict_async_finalize(*save_state_dict_ret) - return NVRxAsyncRequest( - save_fn, save_args, [finalize_fn], async_fn_kwargs={}, preload_fn=preload_fn + return make_nvrx_async_request( + async_request_cls, + save_fn, + save_args, + [finalize_fn], + async_fn_kwargs={}, + preload_fn=preload_fn, ) diff --git a/megatron/training/checkpointing.py b/megatron/training/checkpointing.py index 1441a71518d..c7b382a1f7a 100644 --- a/megatron/training/checkpointing.py +++ b/megatron/training/checkpointing.py @@ -33,6 +33,7 @@ from megatron.core.dist_checkpointing.strategies.torch import ( TorchDistLoadShardedStrategy, TorchDistSaveShardedStrategy, + get_async_strategy, ) from megatron.core.msc_utils import MultiStorageClientFeature, open_file from megatron.core.num_microbatches_calculator import update_num_microbatches @@ -72,19 +73,6 @@ has_nvidia_modelopt = False -try: - from nvidia_resiliency_ext.checkpointing.async_ckpt.filesystem_async import ( - FileSystemWriterAsync, - ) - from nvidia_resiliency_ext.checkpointing.async_ckpt.state_dict_saver import ( - save_state_dict_async_plan, - ) - - HAVE_NVRX = True -except (ImportError, ModuleNotFoundError): - - HAVE_NVRX = False - _CHECKPOINT_VERSION = None _LOADED_ITERATION = None @@ -693,6 +681,9 @@ def save_checkpoint(iteration, model, optimizer, opt_param_scheduler, num_floati if args.async_save: planner = torch.distributed.checkpoint.DefaultSavePlanner() coordinator_rank = 0 + _, async_modules = get_async_strategy(args.async_strategy) + FileSystemWriterAsync = async_modules["FileSystemWriterAsync"] + save_state_dict_async_plan = async_modules["save_state_dict_async_plan"] _cpu_shm = getattr(args, 'async_ckpt_use_cpu_shm', False) _writer_kwargs = {} if _cpu_shm: @@ -717,7 +708,9 @@ def save_checkpoint(iteration, model, optimizer, opt_param_scheduler, num_floati save_state_dict_ret = save_state_dict_async_plan( state_dict, fs_storage_writer, None, coordinator_rank, planner=planner, enable_cache=args.ckpt_assume_constant_structure ) - async_save_request = get_save_and_finalize_callbacks(fs_storage_writer, save_state_dict_ret) + async_save_request = get_save_and_finalize_callbacks( + fs_storage_writer, save_state_dict_ret, args.async_strategy + ) else: fs_storage_writer = torch.distributed.checkpoint.FileSystemWriter(checkpoint_name) torch.distributed.checkpoint.save( diff --git a/megatron/training/config/training_config.py b/megatron/training/config/training_config.py index 27cffb837f4..de507297817 100644 --- a/megatron/training/config/training_config.py +++ b/megatron/training/config/training_config.py @@ -575,13 +575,20 @@ class CheckpointConfig: """Number of machines storing the replica of a given rank's data.""" def __post_init__(self): - from megatron.training.utils import has_nvrx_installed + from megatron.training.utils import has_nvrx_checkpointing_async_support assert self.async_strategy in ["nvrx", "mcore"], \ f"async_strategy {self.async_strategy} is not supported. Available strategies: nvrx, mcore." - if self.async_save and self.ckpt_format in ["torch_dcp", "fsdp_dtensor"]: - assert has_nvrx_installed(), ( - "nvidia-resiliency-ext is not installed. " - "Please, install nvidia-resiliency-ext to enable async save." + if not self.async_save: + self.async_strategy = "mcore" + + if ( + self.async_save + and self.async_strategy == "nvrx" + and self.ckpt_format in ["torch_dcp", "fsdp_dtensor"] + ): + assert has_nvrx_checkpointing_async_support(), ( + "A compatible nvidia-resiliency-ext installation is required to enable " + "async save with async_strategy='nvrx'." ) diff --git a/megatron/training/inprocess_restart.py b/megatron/training/inprocess_restart.py index ac377de4ac0..5e00b55b2ec 100644 --- a/megatron/training/inprocess_restart.py +++ b/megatron/training/inprocess_restart.py @@ -1,15 +1,10 @@ # Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. +import importlib import os import socket -from datetime import timedelta - -try: - import nvidia_resiliency_ext.inprocess as inprocess -except ImportError: - inprocess = None - import warnings +from datetime import timedelta import torch @@ -22,12 +17,20 @@ from . import arguments +def _get_inprocess_module(): + try: + return importlib.import_module("nvidia_resiliency_ext.inprocess") + except ImportError: + return None + + def destroy_state(): from . import training training.destroy_global_state() rerun_state_machine.destroy_rerun_state_machine() def inprocess_restart(train, args): + inprocess = _get_inprocess_module() if inprocess is None: warnings.warn('In-process restart is not available') return train diff --git a/megatron/training/training.py b/megatron/training/training.py index cdb60a0722e..b74213ff828 100644 --- a/megatron/training/training.py +++ b/megatron/training/training.py @@ -124,12 +124,6 @@ def set_startup_timestamps(program_start=None, main_entry=None): except ImportError: has_nvidia_modelopt = False -try: - from nvidia_resiliency_ext.inprocess import CallWrapper -except ImportError: - CallWrapper = type(None) - - from megatron.core import mpu, tensor_parallel from megatron.core.models.gpt.experimental_attention_variant_module_specs import ( is_linear_attention_variant, @@ -839,7 +833,7 @@ def pretrain( get_position_embedding_ranks=None, non_loss_data_func=None, store=None, - inprocess_call_wrapper: Optional[CallWrapper] = None, + inprocess_call_wrapper: Optional[Any] = None, ): """Main training program. diff --git a/megatron/training/utils.py b/megatron/training/utils.py index aaa10c7edcd..7abb80de14f 100644 --- a/megatron/training/utils.py +++ b/megatron/training/utils.py @@ -13,6 +13,7 @@ from megatron.core.msc_utils import MultiStorageClientFeature, open_file from megatron.core._rank_utils import safe_get_rank as _safe_get_rank +from megatron.core.dist_checkpointing.strategies.nvrx import has_nvrx_async_support try: from transformer_engine.pytorch.optimizers import multi_tensor_applier, multi_tensor_l2norm @@ -791,3 +792,8 @@ def has_nvrx_installed(): return True except (ImportError, ModuleNotFoundError): return False + + +def has_nvrx_checkpointing_async_support(): + """Checks whether the installed NVRx package exposes the async checkpointing API Megatron uses.""" + return has_nvrx_async_support() diff --git a/tests/unit_tests/dist_checkpointing/test_async_save.py b/tests/unit_tests/dist_checkpointing/test_async_save.py index cbb0b3f79b7..1575e01e0d0 100644 --- a/tests/unit_tests/dist_checkpointing/test_async_save.py +++ b/tests/unit_tests/dist_checkpointing/test_async_save.py @@ -107,3 +107,13 @@ def test_get_async_strategy_no_nvrx_installed(self, async_strategy): assert strategy == "mcore" assert module == MCoreAsyncRequest + + def test_get_async_strategy_missing_nvrx_cached_metadata_reader(self): + with mock.patch.dict( + 'sys.modules', + { + 'nvidia_resiliency_ext.checkpointing.async_ckpt.cached_metadata_filesystem_reader': None + }, + ): + with pytest.raises(ModuleNotFoundError): + get_async_strategy("nvrx", module="CachedMetadataFileSystemReader") From 9bb35a8f8d25c0e84aea617c4edcf96c1536b620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?oliver=20k=C3=B6nig?= Date: Thu, 23 Apr 2026 21:13:34 +0200 Subject: [PATCH 012/105] ci: add base_sha to codecov/codecov-action upload step (#4445) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: oliver könig --- .github/workflows/cicd-main.yml | 6 ++ .../core/dist_checkpointing/serialization.py | 7 +-- .../strategies/fully_parallel.py | 2 +- .../dist_checkpointing/strategies/nvrx.py | 55 ------------------- .../dist_checkpointing/strategies/torch.py | 50 ++++++++--------- megatron/training/arguments.py | 3 - megatron/training/async_utils.py | 44 ++++----------- megatron/training/checkpointing.py | 21 ++++--- megatron/training/config/training_config.py | 17 ++---- megatron/training/inprocess_restart.py | 17 +++--- megatron/training/training.py | 8 ++- megatron/training/utils.py | 6 -- .../dist_checkpointing/test_async_save.py | 10 ---- 13 files changed, 75 insertions(+), 171 deletions(-) delete mode 100644 megatron/core/dist_checkpointing/strategies/nvrx.py diff --git a/.github/workflows/cicd-main.yml b/.github/workflows/cicd-main.yml index 1e62f6b3016..e5bf1ac116c 100644 --- a/.github/workflows/cicd-main.yml +++ b/.github/workflows/cicd-main.yml @@ -1006,6 +1006,11 @@ jobs: matrix: flag: [unit-test] steps: + - name: Get PR info + id: get-pr-info + if: startsWith(github.ref, 'refs/heads/pull-request/') && github.event_name == 'push' + uses: nv-gha-runners/get-pr-info@main + - name: Checkout uses: actions/checkout@v6 @@ -1036,6 +1041,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} verbose: true flags: ${{ matrix.flag }} + base_sha: ${{ fromJSON(steps.get-pr-info.outputs.pr-info || '{}').base.sha }} - name: Upload artifacts uses: actions/upload-artifact@v6 diff --git a/megatron/core/dist_checkpointing/serialization.py b/megatron/core/dist_checkpointing/serialization.py index 93d04e54c06..2ee7970f143 100644 --- a/megatron/core/dist_checkpointing/serialization.py +++ b/megatron/core/dist_checkpointing/serialization.py @@ -139,12 +139,7 @@ def load( ckpt_sharded_metadata, ) - ckpt_args = common_state_dict.get("args") - async_strategy = ( - getattr(ckpt_args, "async_strategy", "mcore") - if getattr(ckpt_args, "async_save", False) - else "mcore" - ) + async_strategy = getattr(common_state_dict.get("args"), "async_strategy", "nvrx") loaded_state_dict = sharded_strategy.load(sharded_state_dict, checkpoint_dir, async_strategy) merge(common_state_dict, loaded_state_dict) diff --git a/megatron/core/dist_checkpointing/strategies/fully_parallel.py b/megatron/core/dist_checkpointing/strategies/fully_parallel.py index db3c8ee6cae..6638f215cd4 100644 --- a/megatron/core/dist_checkpointing/strategies/fully_parallel.py +++ b/megatron/core/dist_checkpointing/strategies/fully_parallel.py @@ -189,7 +189,7 @@ def load( self, sharded_state_dict: ShardedStateDict, checkpoint_dir: Path, - async_strategy: str = "mcore", + async_strategy: str = "nvrx", ) -> StateDict: """Distributes the load and calls underlying strategy only for parts of the state dict. diff --git a/megatron/core/dist_checkpointing/strategies/nvrx.py b/megatron/core/dist_checkpointing/strategies/nvrx.py deleted file mode 100644 index 1df26f00ff4..00000000000 --- a/megatron/core/dist_checkpointing/strategies/nvrx.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. - -"""Helpers for interacting with the experimental nvidia-resiliency-ext API.""" - -from importlib import import_module -from typing import Any, Callable, Dict - - -def has_nvrx_async_support() -> bool: - """Checks whether the NVRx async checkpointing symbols Megatron uses are importable.""" - try: - core = import_module("nvidia_resiliency_ext.checkpointing.async_ckpt.core") - cached_metadata_reader = import_module( - "nvidia_resiliency_ext.checkpointing.async_ckpt.cached_metadata_filesystem_reader" - ) - filesystem_async = import_module( - "nvidia_resiliency_ext.checkpointing.async_ckpt.filesystem_async" - ) - state_dict_saver = import_module( - "nvidia_resiliency_ext.checkpointing.async_ckpt.state_dict_saver" - ) - except (ImportError, ModuleNotFoundError): - return False - - required_symbols = ( - getattr(core, "AsyncCallsQueue", None), - getattr(core, "AsyncRequest", None), - getattr(cached_metadata_reader, "CachedMetadataFileSystemReader", None), - getattr(filesystem_async, "FileSystemWriterAsync", None), - getattr(filesystem_async, "get_write_results_queue", None), - getattr(state_dict_saver, "CheckpointMetadataCache", None), - getattr(state_dict_saver, "save_state_dict_async_finalize", None), - getattr(state_dict_saver, "save_state_dict_async_plan", None), - ) - return all(symbol is not None for symbol in required_symbols) and hasattr( - filesystem_async, "_results_queue" - ) - - -def make_nvrx_async_request( - async_request_cls: type, - async_fn: Callable[..., Any], - async_fn_args: Any, - finalize_fns: list[Callable[..., Any]], - async_fn_kwargs: Dict[str, Any] | None = None, - preload_fn: Callable[..., Any] | None = None, -): - """Builds an AsyncRequest using the expected NVRx API.""" - return async_request_cls( - async_fn, - async_fn_args, - finalize_fns, - async_fn_kwargs=async_fn_kwargs or {}, - preload_fn=preload_fn, - ) diff --git a/megatron/core/dist_checkpointing/strategies/torch.py b/megatron/core/dist_checkpointing/strategies/torch.py index 7943561700f..3e467c1f9dd 100644 --- a/megatron/core/dist_checkpointing/strategies/torch.py +++ b/megatron/core/dist_checkpointing/strategies/torch.py @@ -6,12 +6,13 @@ import os import pickle import warnings +from abc import ABC from collections import defaultdict from contextlib import contextmanager from itertools import product from logging import getLogger from pathlib import Path -from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Tuple, Union, cast +from typing import Any, Dict, Iterable, List, Optional, Tuple, Union, cast import torch from packaging.version import Version as PkgVersion @@ -50,18 +51,19 @@ ) from .async_utils import AsyncRequest from .checkpointable import CheckpointableShardedTensor, LocalShardsContainer -from .nvrx import has_nvrx_async_support, make_nvrx_async_request -if TYPE_CHECKING: +try: from nvidia_resiliency_ext.checkpointing.async_ckpt.core import AsyncRequest as NVRxAsyncRequest from nvidia_resiliency_ext.checkpointing.async_ckpt.state_dict_saver import ( CheckpointMetadataCache, ) -else: - CheckpointMetadataCache = Any - NVRxAsyncRequest = Any -HAVE_NVRX = has_nvrx_async_support() + HAVE_NVRX = True +except (ImportError, ModuleNotFoundError): + CheckpointMetadataCache = ABC + NVRxAsyncRequest = ABC + + HAVE_NVRX = False try: if not torch.cuda.is_available(): @@ -101,7 +103,6 @@ class MCoreSavePlan: logger = getLogger(__name__) -_logged_mcore_async_deprecation = False def flatten_state_dict( @@ -650,8 +651,9 @@ def __init__( self.validated_loaded_metadata_reuse = False def save(self, sharded_state_dict: ShardedStateDict, checkpoint_dir: Path): - """Sync save always uses the built-in implementation.""" - async_request = self.async_save(sharded_state_dict, checkpoint_dir, async_strategy="mcore") + """Each async strategy can be trivially used as a sync strategy.""" + strategy = "nvrx" if HAVE_NVRX else "mcore" + async_request = self.async_save(sharded_state_dict, checkpoint_dir, async_strategy=strategy) async_request.execute_sync() del async_request @@ -669,14 +671,11 @@ def async_save( Returns: None """ - global _logged_mcore_async_deprecation if async_strategy == "mcore": - if not _logged_mcore_async_deprecation: - logger.warning( - "MCore's async save is deprecated and will be removed in the future releases. " - "Please, use NVRx async solution by setting `async_strategy` to `nvrx`." - ) - _logged_mcore_async_deprecation = True + logger.warning( + "MCore's async save is deprecated and will be removed in the future releases. " + "Please, use NVRx async solution by setting `async_strategy` to `nvrx`." + ) # Translate the state dict (sharded_state_dict, flat_mapping, rename_mapping) = ( @@ -702,9 +701,7 @@ def async_save( if async_strategy == "nvrx": if self._metadata_cache is None: self._metadata_cache = checkpointable_metadata_cache() - if self.cached_global_metadata is not None and hasattr( - self._metadata_cache, "set_cached_global_metadata" - ): + if self.cached_global_metadata is not None: self._metadata_cache.set_cached_global_metadata(self.cached_global_metadata) # Define additional arguments async_writer_kwargs["use_cached_data_structure"] = self.use_cached_ckpt_structure @@ -821,13 +818,11 @@ def _get_save_and_finalize_callbacks( def finalize_fn(): save_state_dict_async_finalize(*save_state_dict_ret) - return make_nvrx_async_request( - async_request, save_fn, save_args, [finalize_fn], preload_fn=preload_fn - ) + return async_request(save_fn, save_args, [finalize_fn], preload_fn=preload_fn) def _get_filesystem_reader( - checkpoint_dir: Union[str, Path], cache_metadata: bool = False, async_strategy: str = "mcore" + checkpoint_dir: Union[str, Path], cache_metadata: bool = False, async_strategy: str = "nvrx" ) -> FileSystemReader: if MultiStorageClientFeature.is_enabled(): msc = MultiStorageClientFeature.import_package() @@ -851,7 +846,7 @@ def load( self, sharded_state_dict: ShardedStateDict, checkpoint_dir: Path, - async_strategy: str = "mcore", + async_strategy: str = "nvrx", ) -> StateDict: """Translates MCore ShardedTensors to PyT ShardedTensors & loads from PyT Distributed fmt. @@ -1067,8 +1062,9 @@ def get_async_strategy(async_strategy: str = "nvrx", module: str = None) -> tupl async_strategy = "nvrx" except (ImportError, ModuleNotFoundError): raise ModuleNotFoundError( - "A compatible `nvidia-resiliency-ext` installation is required for " - '`async_strategy="nvrx"`. Please install it or set `async_strategy` to `mcore`.' + "nvidia-resiliency-ext package is not installed. " + "Please, install nvidia-resiliency-ext package or set `async_strategy` to `mcore` " + "to enable async save strategy." ) elif async_strategy == "mcore": # do mcore async imports diff --git a/megatron/training/arguments.py b/megatron/training/arguments.py index d6a886c6397..a1ae68d4a38 100644 --- a/megatron/training/arguments.py +++ b/megatron/training/arguments.py @@ -1535,9 +1535,6 @@ def validate_args(args, defaults={}): ) args.async_save = False - if not args.async_save: - args.async_strategy = "mcore" - # Inference args if args.inference_batch_times_seqlen_threshold > -1: assert args.pipeline_model_parallel_size > 1, \ diff --git a/megatron/training/async_utils.py b/megatron/training/async_utils.py index 4dae2170039..3d283c056fa 100644 --- a/megatron/training/async_utils.py +++ b/megatron/training/async_utils.py @@ -8,22 +8,14 @@ import logging import time from abc import ABC -from typing import TYPE_CHECKING, Any from megatron.core.dist_checkpointing.strategies.async_utils import AsyncRequest -from megatron.core.dist_checkpointing.strategies.nvrx import ( - make_nvrx_async_request, -) from megatron.core.dist_checkpointing.strategies.torch import get_async_strategy from megatron.training import get_args from megatron.training.utils import print_rank_0 -if TYPE_CHECKING: - from nvidia_resiliency_ext.checkpointing.async_ckpt.core import AsyncRequest as NVRxAsyncRequest -else: - NVRxAsyncRequest = Any - try: + from nvidia_resiliency_ext.checkpointing.async_ckpt.core import AsyncRequest as NVRxAsyncRequest from nvidia_resiliency_ext.checkpointing.async_ckpt.filesystem_async import _results_queue from nvidia_resiliency_ext.checkpointing.async_ckpt.state_dict_saver import ( save_state_dict_async_finalize, @@ -34,6 +26,8 @@ save_state_dict_async_finalize, ) + NVRxAsyncRequest = ABC + logger = logging.getLogger(__name__) # Singleton manager of async calls @@ -76,13 +70,13 @@ def init_persistent_async_worker(rank: int, mp_mode: str = 'spawn'): ), ) # initialize the persistent caller with QoS priorities from args - warmup_kwargs = {} + kwargs = {} if async_strategy == "mcore": # Note: nvidia-resiliency-ext uses is_daemon instead of mp_mode (always spawns) - warmup_kwargs["mp_mode"] = mp_mode + kwargs["mp_mode"] = mp_mode elif async_strategy == "nvrx": if "cpu_shm_mode" in inspect.signature(AsyncCallsQueue.warmup_persistent_caller).parameters: - warmup_kwargs["cpu_shm_mode"] = args.async_ckpt_use_cpu_shm + kwargs["cpu_shm_mode"] = args.async_ckpt_use_cpu_shm elif args.async_ckpt_use_cpu_shm: raise AssertionError( "Installed nvidia-resiliency-ext does not support cpu_shm_mode. " @@ -92,16 +86,10 @@ def init_persistent_async_worker(rank: int, mp_mode: str = 'spawn'): rank, cpu_priority=args.async_ckpt_cpu_priority, io_priority=args.async_ckpt_io_priority, - **warmup_kwargs, + **kwargs, ) # initialize ckpt write results queue - if async_strategy == "nvrx": - if "mp_mode" not in inspect.signature(get_write_results_queue).parameters: - raise AssertionError( - "Installed nvidia-resiliency-ext does not support " - "get_write_results_queue(mp_mode=...). Update nvidia-resiliency-ext." - ) - get_write_results_queue(mp_mode="fork") + get_write_results_queue('fork') if rank == 0: print(f"init_persistent_async_worker: rank {rank}, Async Caller Started in {time.time() - time_start} seconds", flush=True) @@ -169,24 +157,14 @@ def reset_persistent_async_worker(async_strategy): module.clear_metadata_cache() -def get_save_and_finalize_callbacks( - writer, save_state_dict_ret, async_strategy: str = "nvrx" -) -> AsyncRequest | NVRxAsyncRequest: +def get_save_and_finalize_callbacks(writer, save_state_dict_ret) -> NVRxAsyncRequest: """Creates an async save request for fsdp_dtensor & torch_dcp with a finalize function.""" save_fn, preload_fn, save_args = writer.get_save_function_and_args() - _, async_modules = get_async_strategy(async_strategy) - async_request_cls = async_modules["AsyncRequest"] - save_state_dict_async_finalize = async_modules["save_state_dict_async_finalize"] def finalize_fn(): """Finalizes async checkpointing and synchronizes processes.""" save_state_dict_async_finalize(*save_state_dict_ret) - return make_nvrx_async_request( - async_request_cls, - save_fn, - save_args, - [finalize_fn], - async_fn_kwargs={}, - preload_fn=preload_fn, + return NVRxAsyncRequest( + save_fn, save_args, [finalize_fn], async_fn_kwargs={}, preload_fn=preload_fn ) diff --git a/megatron/training/checkpointing.py b/megatron/training/checkpointing.py index c7b382a1f7a..1441a71518d 100644 --- a/megatron/training/checkpointing.py +++ b/megatron/training/checkpointing.py @@ -33,7 +33,6 @@ from megatron.core.dist_checkpointing.strategies.torch import ( TorchDistLoadShardedStrategy, TorchDistSaveShardedStrategy, - get_async_strategy, ) from megatron.core.msc_utils import MultiStorageClientFeature, open_file from megatron.core.num_microbatches_calculator import update_num_microbatches @@ -73,6 +72,19 @@ has_nvidia_modelopt = False +try: + from nvidia_resiliency_ext.checkpointing.async_ckpt.filesystem_async import ( + FileSystemWriterAsync, + ) + from nvidia_resiliency_ext.checkpointing.async_ckpt.state_dict_saver import ( + save_state_dict_async_plan, + ) + + HAVE_NVRX = True +except (ImportError, ModuleNotFoundError): + + HAVE_NVRX = False + _CHECKPOINT_VERSION = None _LOADED_ITERATION = None @@ -681,9 +693,6 @@ def save_checkpoint(iteration, model, optimizer, opt_param_scheduler, num_floati if args.async_save: planner = torch.distributed.checkpoint.DefaultSavePlanner() coordinator_rank = 0 - _, async_modules = get_async_strategy(args.async_strategy) - FileSystemWriterAsync = async_modules["FileSystemWriterAsync"] - save_state_dict_async_plan = async_modules["save_state_dict_async_plan"] _cpu_shm = getattr(args, 'async_ckpt_use_cpu_shm', False) _writer_kwargs = {} if _cpu_shm: @@ -708,9 +717,7 @@ def save_checkpoint(iteration, model, optimizer, opt_param_scheduler, num_floati save_state_dict_ret = save_state_dict_async_plan( state_dict, fs_storage_writer, None, coordinator_rank, planner=planner, enable_cache=args.ckpt_assume_constant_structure ) - async_save_request = get_save_and_finalize_callbacks( - fs_storage_writer, save_state_dict_ret, args.async_strategy - ) + async_save_request = get_save_and_finalize_callbacks(fs_storage_writer, save_state_dict_ret) else: fs_storage_writer = torch.distributed.checkpoint.FileSystemWriter(checkpoint_name) torch.distributed.checkpoint.save( diff --git a/megatron/training/config/training_config.py b/megatron/training/config/training_config.py index de507297817..27cffb837f4 100644 --- a/megatron/training/config/training_config.py +++ b/megatron/training/config/training_config.py @@ -575,20 +575,13 @@ class CheckpointConfig: """Number of machines storing the replica of a given rank's data.""" def __post_init__(self): - from megatron.training.utils import has_nvrx_checkpointing_async_support + from megatron.training.utils import has_nvrx_installed assert self.async_strategy in ["nvrx", "mcore"], \ f"async_strategy {self.async_strategy} is not supported. Available strategies: nvrx, mcore." - if not self.async_save: - self.async_strategy = "mcore" - - if ( - self.async_save - and self.async_strategy == "nvrx" - and self.ckpt_format in ["torch_dcp", "fsdp_dtensor"] - ): - assert has_nvrx_checkpointing_async_support(), ( - "A compatible nvidia-resiliency-ext installation is required to enable " - "async save with async_strategy='nvrx'." + if self.async_save and self.ckpt_format in ["torch_dcp", "fsdp_dtensor"]: + assert has_nvrx_installed(), ( + "nvidia-resiliency-ext is not installed. " + "Please, install nvidia-resiliency-ext to enable async save." ) diff --git a/megatron/training/inprocess_restart.py b/megatron/training/inprocess_restart.py index 5e00b55b2ec..ac377de4ac0 100644 --- a/megatron/training/inprocess_restart.py +++ b/megatron/training/inprocess_restart.py @@ -1,11 +1,16 @@ # Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. -import importlib import os import socket -import warnings from datetime import timedelta +try: + import nvidia_resiliency_ext.inprocess as inprocess +except ImportError: + inprocess = None + +import warnings + import torch from megatron.core import rerun_state_machine @@ -17,20 +22,12 @@ from . import arguments -def _get_inprocess_module(): - try: - return importlib.import_module("nvidia_resiliency_ext.inprocess") - except ImportError: - return None - - def destroy_state(): from . import training training.destroy_global_state() rerun_state_machine.destroy_rerun_state_machine() def inprocess_restart(train, args): - inprocess = _get_inprocess_module() if inprocess is None: warnings.warn('In-process restart is not available') return train diff --git a/megatron/training/training.py b/megatron/training/training.py index b74213ff828..cdb60a0722e 100644 --- a/megatron/training/training.py +++ b/megatron/training/training.py @@ -124,6 +124,12 @@ def set_startup_timestamps(program_start=None, main_entry=None): except ImportError: has_nvidia_modelopt = False +try: + from nvidia_resiliency_ext.inprocess import CallWrapper +except ImportError: + CallWrapper = type(None) + + from megatron.core import mpu, tensor_parallel from megatron.core.models.gpt.experimental_attention_variant_module_specs import ( is_linear_attention_variant, @@ -833,7 +839,7 @@ def pretrain( get_position_embedding_ranks=None, non_loss_data_func=None, store=None, - inprocess_call_wrapper: Optional[Any] = None, + inprocess_call_wrapper: Optional[CallWrapper] = None, ): """Main training program. diff --git a/megatron/training/utils.py b/megatron/training/utils.py index 7abb80de14f..aaa10c7edcd 100644 --- a/megatron/training/utils.py +++ b/megatron/training/utils.py @@ -13,7 +13,6 @@ from megatron.core.msc_utils import MultiStorageClientFeature, open_file from megatron.core._rank_utils import safe_get_rank as _safe_get_rank -from megatron.core.dist_checkpointing.strategies.nvrx import has_nvrx_async_support try: from transformer_engine.pytorch.optimizers import multi_tensor_applier, multi_tensor_l2norm @@ -792,8 +791,3 @@ def has_nvrx_installed(): return True except (ImportError, ModuleNotFoundError): return False - - -def has_nvrx_checkpointing_async_support(): - """Checks whether the installed NVRx package exposes the async checkpointing API Megatron uses.""" - return has_nvrx_async_support() diff --git a/tests/unit_tests/dist_checkpointing/test_async_save.py b/tests/unit_tests/dist_checkpointing/test_async_save.py index 1575e01e0d0..cbb0b3f79b7 100644 --- a/tests/unit_tests/dist_checkpointing/test_async_save.py +++ b/tests/unit_tests/dist_checkpointing/test_async_save.py @@ -107,13 +107,3 @@ def test_get_async_strategy_no_nvrx_installed(self, async_strategy): assert strategy == "mcore" assert module == MCoreAsyncRequest - - def test_get_async_strategy_missing_nvrx_cached_metadata_reader(self): - with mock.patch.dict( - 'sys.modules', - { - 'nvidia_resiliency_ext.checkpointing.async_ckpt.cached_metadata_filesystem_reader': None - }, - ): - with pytest.raises(ModuleNotFoundError): - get_async_strategy("nvrx", module="CachedMetadataFileSystemReader") From 3034d868ddfd43790b7832b479ba6bcf7042efe2 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 24 Apr 2026 00:24:58 +0000 Subject: [PATCH 013/105] Update copy-pr-bot.yaml [skip ci] --- .github/copy-pr-bot.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/copy-pr-bot.yaml b/.github/copy-pr-bot.yaml index 618c7c4c9c9..f72c2fe51e5 100644 --- a/.github/copy-pr-bot.yaml +++ b/.github/copy-pr-bot.yaml @@ -1,4 +1,4 @@ enabled: true auto_sync_draft: false auto_sync_ready: true -trustees_override: ["AAnoosheh", "ArEsKay3", "Autumn1998", "BestJuly", "BoxiangW", "CarlosGomes98", "ChenhanYu", "Connor-XY", "FDecaYed", "HaochenYuan", "ISEEKYAN", "JRD971000", "Mellonta", "Phlip79", "QiZhangNV", "RPrenger", "ShriyaRishab", "Victarry", "WanZzzzzz", "Wohox", "YangFei1990", "ZhiyuLi-Nvidia", "ahmadki", "aklife97", "ananthsub", "aroshanghias-nvd", "asolergi-nv", "buptzyb", "chtruong814", "cjld", "cspades", "cuichenx", "deepakn94", "dimapihtar", "dingqingy-nv", "duncanriach", "erhoo82", "ericharper", "fanshiqing", "faradawn", "fitsumreda", "frsun-nvda", "gautham-kollu", "gdengk", "guihong-nv", "guyueh1", "hexinw-nvidia", "huvunvidia", "hxbai", "ilml", "jalbericiola", "janEbert", "jaredcasper", "jenchen13", "jiemingz", "jingqiny-99", "jkamalu", "jon-barker", "jstjohn", "kajalj22", "kanz-nv", "keshavb96", "kevalmorabia97", "ko3n1g", "ksivaman", "kunlunl", "kvareddy", "kwyss-nvidia", "layalir", "lhb8125", "lmcafee-nvidia", "maanug-nv", "mathemakitten", "matthieule", "mchrzanowski", "mehraakash", "minitu", "mkhona-nvidia", "nanz-nv", "parthmannan", "prajwal1210", "pthombre", "rhewett-nv", "rogerwaleffe", "sajadn", "sanandaraj5597", "sancha", "santhnm2", "sbak5", "shanmugamr1992", "sharathts", "sheliang-nv", "shengf-nv", "shifangx", "shjwudp", "sidsingh-nvidia", "skyw", "sraman-rgb", "sudhakarsingh27", "tdene", "theothermike", "thomasdhc", "tomlifu", "trintamaki", "tylerpoon", "wdykas", "wplf", "wujingyue", "xiaoyao0115", "xuwchen", "yanring", "yaox12", "yaoyu-33", "yashaswikarnati", "yeyu-nvidia", "yobibyte", "youngeunkwon0405", "yueshen2016", "yuzhongw-nvidia", "zhongbozhu"] +trustees_override: ["AAnoosheh", "ArEsKay3", "Autumn1998", "BestJuly", "BoxiangW", "CarlosGomes98", "ChenhanYu", "Connor-XY", "FDecaYed", "HaochenYuan", "ISEEKYAN", "JRD971000", "Mellonta", "Phlip79", "QiZhangNV", "RPrenger", "ShriyaRishab", "Victarry", "WanZzzzzz", "Wohox", "YangFei1990", "ZhiyuLi-Nvidia", "ahmadki", "aklife97", "ananthsub", "aroshanghias-nvd", "asolergi-nv", "buptzyb", "chtruong814", "cjld", "cspades", "cuichenx", "deepakn94", "dimapihtar", "dingqingy-nv", "duncanriach", "erhoo82", "ericharper", "fanshiqing", "faradawn", "fitsumreda", "frsun-nvda", "gautham-kollu", "gdengk", "guihong-nv", "guyueh1", "hexinw-nvidia", "huvunvidia", "hxbai", "ilml", "jalbericiola", "janEbert", "jaredcasper", "jenchen13", "jiemingz", "jingqiny-99", "jkamalu", "jon-barker", "jstjohn", "kajalj22", "kanz-nv", "kevalmorabia97", "ko3n1g", "ksivaman", "kunlunl", "kvareddy", "kwyss-nvidia", "layalir", "lhb8125", "lmcafee-nvidia", "maanug-nv", "mathemakitten", "matthieule", "mchrzanowski", "mehraakash", "minitu", "mkhona-nvidia", "nanz-nv", "parthmannan", "prajwal1210", "pthombre", "rhewett-nv", "rogerwaleffe", "sajadn", "sanandaraj5597", "sancha", "santhnm2", "sbak5", "shanmugamr1992", "sharathts", "sheliang-nv", "shengf-nv", "shifangx", "shjwudp", "sidsingh-nvidia", "skyw", "sraman-rgb", "sudhakarsingh27", "tdene", "theothermike", "thomasdhc", "tomlifu", "trintamaki", "tylerpoon", "wdykas", "wplf", "wujingyue", "xiaoyao0115", "xuwchen", "yanring", "yaox12", "yaoyu-33", "yashaswikarnati", "yeyu-nvidia", "yobibyte", "youngeunkwon0405", "yueshen2016", "yuzhongw-nvidia", "zhongbozhu"] From f78ed05eb4fe5716292a3ac4baadb45af64d9278 Mon Sep 17 00:00:00 2001 From: DAISY-gh <125536928+DAISY-gh@users.noreply.github.com> Date: Fri, 24 Apr 2026 11:14:36 +0800 Subject: [PATCH 014/105] fix(checkpoint_inspector): allow empty --param-to-param-group-map-json (#4403) Co-authored-by: Antoni-Joan Solergibert --- tools/checkpoint/checkpoint_inspector.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tools/checkpoint/checkpoint_inspector.py b/tools/checkpoint/checkpoint_inspector.py index 74da955912f..ac3d9aab7fa 100644 --- a/tools/checkpoint/checkpoint_inspector.py +++ b/tools/checkpoint/checkpoint_inspector.py @@ -815,8 +815,10 @@ def has_layer_index(key: str) -> bool: @click.option( "--param-to-param-group-map-json", type=str, - default="{}", - help="JSON string representing the param to parameter group map." + default=None, + help="Path to a JSON file mapping parameter names to optimizer param group ids. " + "Required only if the source checkpoint has multiple optimizer param groups " + "(e.g. different LR/weight-decay per group). Leave unset for single-group checkpoints." ) @click.option( "--rename-mtp-keys", @@ -921,8 +923,11 @@ def oom_observer(device, alloc, device_alloc, device_free): ckpt_path = Path(input_dir) output_dir = Path(output_dir) - with open(param_to_param_group_map_json, "r") as f: - param_to_param_group_map = json.load(f) + if param_to_param_group_map_json: + with open(param_to_param_group_map_json, "r") as f: + param_to_param_group_map = json.load(f) + else: + param_to_param_group_map = {} _swiglu_modules = ( [m.strip() for m in swiglu_modules.split(",") if m.strip()] if swiglu_modules is not None else None From 4d6cdd52f2b8ec29ad6d761cceb60f1196583f2e Mon Sep 17 00:00:00 2001 From: Guihong Li Date: Thu, 23 Apr 2026 21:51:01 -0700 Subject: [PATCH 015/105] Add the YARN support for hybrid_model (#4244) Co-authored-by: Philip Petrakian --- megatron/core/models/hybrid/hybrid_model.py | 35 ++++++- megatron/training/arguments.py | 2 +- tests/unit_tests/models/test_hybrid_model.py | 104 +++++++++++++++++++ 3 files changed, 137 insertions(+), 4 deletions(-) diff --git a/megatron/core/models/hybrid/hybrid_model.py b/megatron/core/models/hybrid/hybrid_model.py index f1b3c102634..88a97ec777f 100644 --- a/megatron/core/models/hybrid/hybrid_model.py +++ b/megatron/core/models/hybrid/hybrid_model.py @@ -10,6 +10,7 @@ from megatron.core.inference.contexts import BaseInferenceContext from megatron.core.models.common.embeddings.language_model_embedding import LanguageModelEmbedding from megatron.core.models.common.embeddings.rotary_pos_embedding import RotaryEmbedding +from megatron.core.models.common.embeddings.yarn_rotary_pos_embedding import YarnRotaryEmbedding from megatron.core.models.common.language_module.language_module import LanguageModule from megatron.core.packed_seq_params import PackedSeqParams from megatron.core.pipeline_parallel.fine_grained_activation_offload import ( @@ -71,7 +72,7 @@ class HybridModel(LanguageModule): parallel ranks. Defaults to True. share_embeddings_and_output_weights (bool, optional): When True, input embeddings and output logit weights are shared. Defaults to False. - position_embedding_type (Literal[learned_absolute,rope,none], optional): Position + position_embedding_type (Literal[learned_absolute,rope,yarn,none], optional): Position embedding type. Defaults to 'none'. rotary_percent (float, optional): Percent of rotary dimension to use for rotary position embeddings. Ignored unless position_embedding_type is 'rope'. Defaults to 1.0. @@ -100,7 +101,7 @@ def __init__( parallel_output: bool = True, share_embeddings_and_output_weights: bool = False, # Mamba with no attention has no need for position embeddings, so none is default - position_embedding_type: Literal['learned_absolute', 'rope', 'none'] = 'none', + position_embedding_type: Literal['learned_absolute', 'rope', 'yarn', 'none'] = 'none', rotary_percent: float = 1.0, rotary_base: int = 10000, scatter_embedding_sequence_parallel: bool = True, @@ -228,7 +229,26 @@ def __init__( use_cpu_initialization=self.config.use_cpu_initialization, cp_group=self.pg_collection.cp, ) - + elif self.position_embedding_type == 'yarn': + self.rotary_pos_emb = YarnRotaryEmbedding( + kv_channels=self.config.kv_channels, + rotary_percent=rotary_percent, + seq_len_interpolation_factor=seq_len_interpolation_factor, + rotary_base=rotary_base, + scaling_factor=getattr(self.config, "yarn_rotary_scaling_factor"), + original_max_position_embeddings=getattr( + self.config, "yarn_original_max_position_embeddings" + ), + beta_fast=getattr(self.config, "yarn_beta_fast"), + beta_slow=getattr(self.config, "yarn_beta_slow"), + mscale=getattr(self.config, "yarn_mscale"), + mscale_all_dim=getattr(self.config, "yarn_mscale_all_dim"), + correction_range_round_to_int=getattr( + self.config, "yarn_correction_range_round_to_int" + ), + use_cpu_initialization=self.config.use_cpu_initialization, + cp_group=self.pg_collection.cp, + ) self.decoder = build_module( hybrid_stack_spec, self.config, @@ -383,6 +403,15 @@ def forward( rotary_seq_len, packed_seq=packed_seq_params is not None and packed_seq_params.qkv_format == 'thd', ) + elif self.position_embedding_type == 'yarn': + rotary_seq_len = self.rotary_pos_emb.get_rotary_seq_len( + inference_context, self.decoder, decoder_input, self.config, packed_seq_params + ) + # YarnRotaryEmbedding.forward returns (emb, mscale); discard mscale here + rotary_pos_emb, _ = self.rotary_pos_emb( + rotary_seq_len, + packed_seq=packed_seq_params is not None and packed_seq_params.qkv_format == 'thd', + ) # Wrap decoder_input to allow the decoder (HybridStack) to delete the # reference held by this caller function, enabling early garbage collection diff --git a/megatron/training/arguments.py b/megatron/training/arguments.py index a1ae68d4a38..5f1968dcc27 100644 --- a/megatron/training/arguments.py +++ b/megatron/training/arguments.py @@ -2121,7 +2121,7 @@ def _add_network_size_args(parser): help='Maximum number of position embeddings to use. ' 'This is the size of position embedding.') group.add_argument('--position-embedding-type', type=str, default='learned_absolute', - choices=['learned_absolute', 'rope', 'mrope', 'relative', 'none'], + choices=['learned_absolute', 'rope', 'yarn', 'mrope', 'relative', 'none'], help='Position embedding type.') group.add_argument('--relative-attention-num-buckets', type=int, default=32, help='Number of buckets for relative position embeddings.') diff --git a/tests/unit_tests/models/test_hybrid_model.py b/tests/unit_tests/models/test_hybrid_model.py index c4bdc147621..98a53da0314 100644 --- a/tests/unit_tests/models/test_hybrid_model.py +++ b/tests/unit_tests/models/test_hybrid_model.py @@ -15,6 +15,7 @@ from megatron.core.inference.contexts.dynamic_context import DynamicInferenceContext from megatron.core.inference.inference_request import DynamicInferenceRequest from megatron.core.inference.sampling_params import SamplingParams +from megatron.core.models.common.embeddings.yarn_rotary_pos_embedding import YarnRotaryEmbedding from megatron.core.models.hybrid.hybrid_layer_specs import hybrid_stack_spec from megatron.core.models.hybrid.hybrid_model import HybridModel from megatron.core.packed_seq_params import PackedSeqParams @@ -523,3 +524,106 @@ def test_dynamic_inference_padding_with_fp8(self): # Assert that all padding logits are zero. assert torch.all(padding_logits == 0.0), "Logits for padding tokens are not all zero." + + +def _make_yarn_config(**kwargs): + """Build a TransformerConfig with yarn positional embedding attributes.""" + cfg = TransformerConfig( + num_layers=3, # 1 Mamba layer, 1 attention layer, 1 MLP layer + hidden_size=256, + num_attention_heads=4, + use_cpu_initialization=True, + **kwargs, + ) + # Yarn-specific attributes are set dynamically on the config (not TransformerConfig fields). + cfg.yarn_rotary_scaling_factor = 2.0 + cfg.yarn_original_max_position_embeddings = 4 + cfg.yarn_beta_fast = 32.0 + cfg.yarn_beta_slow = 1.0 + cfg.yarn_mscale = 1.0 + cfg.yarn_mscale_all_dim = 0.0 + cfg.yarn_correction_range_round_to_int = True + return cfg + + +class TestHybridModelWithYarn: + """Tests for HybridModel with YaRN positional embeddings.""" + + def setup_method(self, method): + Utils.initialize_model_parallel(1, 1) + model_parallel_cuda_manual_seed(123) + model_config = _make_yarn_config() + self.model = HybridModel( + config=model_config, + hybrid_stack_spec=hybrid_stack_spec, + vocab_size=100, + max_sequence_length=4, + hybrid_layer_pattern="M*-", # 1 Mamba, 1 attention, 1 MLP + position_embedding_type='yarn', + rotary_base=10000, + ) + + def teardown_method(self, method): + Utils.destroy_model_parallel() + + def test_constructor(self): + assert isinstance(self.model, HybridModel) + assert self.model.max_sequence_length == 4 + assert self.model.position_embedding_type == 'yarn' + # YaRN creates a YarnRotaryEmbedding rather than a plain RotaryEmbedding. + assert isinstance(self.model.rotary_pos_emb, YarnRotaryEmbedding) + + def test_forward(self): + sequence_length = self.model.max_sequence_length + micro_batch_size = 2 + + self.model.cuda() + + data = list(range(sequence_length)) + input_ids = torch.tensor(data, dtype=torch.int64).repeat((micro_batch_size, 1)).cuda() + position_ids = torch.tensor(data, dtype=torch.int64).repeat((micro_batch_size, 1)).cuda() + attention_mask = torch.ones( + (micro_batch_size, 1, sequence_length, sequence_length), dtype=bool + ).cuda() + + logits = self.model.forward( + input_ids=input_ids, position_ids=position_ids, attention_mask=attention_mask + ) + + assert logits.shape[0] == micro_batch_size + assert logits.shape[1] == sequence_length + assert logits.shape[2] == self.model.vocab_size + + def test_inference(self): + micro_batch_size = 2 + inference_context: BaseInferenceContext = StaticInferenceContext( + max_batch_size=micro_batch_size, max_sequence_length=self.model.max_sequence_length + ) + prompt_length = self.model.max_sequence_length - 1 + + self.model.cuda() + + # load-context/first-output-token, step/generate + for offset in (0, prompt_length): + sequence_length = prompt_length if offset == 0 else 1 + inference_context.sequence_len_offset = offset + + data = list(range(sequence_length)) + input_ids = torch.tensor(data, dtype=torch.int64).repeat((micro_batch_size, 1)).cuda() + position_ids = ( + torch.tensor(data, dtype=torch.int64).repeat((micro_batch_size, 1)).cuda() + ) + attention_mask = torch.ones( + (micro_batch_size, 1, sequence_length, sequence_length), dtype=bool + ).cuda() + + logits = self.model.forward( + input_ids=input_ids, + position_ids=position_ids, + attention_mask=attention_mask, + inference_context=inference_context, + ) + + assert logits.shape[0] == micro_batch_size + assert logits.shape[1] == sequence_length + assert logits.shape[2] == self.model.vocab_size From 41ffa83de777d079d5f77191b05563160e3e809e Mon Sep 17 00:00:00 2001 From: Maanu Grover Date: Fri, 24 Apr 2026 00:40:57 -0700 Subject: [PATCH 016/105] [training migration] Add container class for config dataclasses (#4227) Signed-off-by: Maanu Grover Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> --- examples/mimo/train.py | 5 +- megatron/training/argument_utils.py | 71 +++++++- megatron/training/config/__init__.py | 2 + megatron/training/config/container.py | 28 +++ megatron/training/training.py | 85 +++++---- pretrain_bert.py | 6 +- pretrain_gpt.py | 4 +- pretrain_hybrid.py | 5 +- pretrain_t5.py | 5 +- pretrain_vlm.py | 5 +- tests/unit_tests/test_argument_utils.py | 232 +++++++++++++++++++++++- train_rl.py | 5 +- 12 files changed, 404 insertions(+), 49 deletions(-) create mode 100644 megatron/training/config/container.py diff --git a/examples/mimo/train.py b/examples/mimo/train.py index a89c83728e0..594170faa7e 100644 --- a/examples/mimo/train.py +++ b/examples/mimo/train.py @@ -9,6 +9,7 @@ from functools import partial from typing import Any, Dict, Iterator +from megatron.training.argument_utils import pretrain_cfg_container_from_args from megatron.training.arguments import parse_and_validate_args import torch from megatron.training import get_args, pretrain, print_rank_0 @@ -276,8 +277,10 @@ def model_provider( if __name__ == "__main__": train_valid_test_datasets_provider.is_distributed = True - parse_and_validate_args(args_defaults={}, extra_args_provider=add_mimo_args) + args = parse_and_validate_args(args_defaults={}, extra_args_provider=add_mimo_args) + full_config = pretrain_cfg_container_from_args(args) pretrain( + full_config, train_valid_test_datasets_provider, model_provider, ModelType.encoder_or_decoder, diff --git a/megatron/training/argument_utils.py b/megatron/training/argument_utils.py index b9f7c7b22d1..64e6940089b 100644 --- a/megatron/training/argument_utils.py +++ b/megatron/training/argument_utils.py @@ -3,8 +3,8 @@ import dataclasses import typing import types -from typing import Any, Optional -from argparse import ArgumentParser, _ArgumentGroup +from typing import Any, Callable, Optional +from argparse import ArgumentParser, _ArgumentGroup, Namespace import inspect import itertools import builtins @@ -12,6 +12,17 @@ import enum from dataclasses import Field, fields +from megatron.training.config import ( + DistributedInitConfig, + PretrainConfigContainer, + SchedulerConfig, + TrainingConfig, + ValidationConfig, + RNGConfig, + LoggerConfig, + StragglerDetectionConfig, + RerunStateMachineConfig, CheckpointConfig, ProfilingConfig +) # TODO: support arg renames class TypeInferenceError(Exception): @@ -248,3 +259,59 @@ def _get_field_docstrings(self, src_cfg_class: type) -> dict[str, str]: field_docstrings.update(self._get_field_docstrings(base_classes[0])) return field_docstrings + + +def _default_config_from_args(cls: type, args: Namespace, return_instance: bool = True) -> Any: + """Create a config dataclass from the appropriate values in the `args` Namespace. + + This is generic, i.e. it will work if dataclass attribute names map 1-to-1 with + names in `args`. Some classes might require additional logic. + """ + kwargs = {} + for f in fields(cls): + if hasattr(args, f.name): + kwargs[f.name] = getattr(args, f.name) + + if return_instance: + return cls(**kwargs) + else: + return kwargs + +def pretrain_cfg_container_from_args(args: Namespace) -> PretrainConfigContainer: + """Build a PretrainConfigContainer from the argparse arguments.""" + from megatron.training.training import get_megatron_ddp_config, get_megatron_optimizer_config + + ckpt_kwargs = _default_config_from_args(CheckpointConfig, args, return_instance=False) + ckpt_kwargs["save_optim"] = not args.no_save_optim + ckpt_kwargs["save_rng"] = not args.no_save_rng + ckpt_kwargs["load_optim"] = not args.no_load_optim + ckpt_kwargs["load_rng"] = not args.no_load_rng + ckpt_kwargs["fully_parallel_save"] = args.ckpt_fully_parallel_save + ckpt_kwargs["fully_parallel_load"] = args.ckpt_fully_parallel_load + + prof_kwargs = _default_config_from_args(ProfilingConfig, args, return_instance=False) + prof_kwargs["use_nsys_profiler"] = args.profile + + rerunsm_kwargs = _default_config_from_args(RerunStateMachineConfig, args, return_instance=False) + rerunsm_kwargs["check_for_nan_in_loss"] = args.check_for_nan_in_loss_and_grad + + optim_cfg, _ = get_megatron_optimizer_config(args) + ddp_config = get_megatron_ddp_config(args) + + cfg = PretrainConfigContainer( + train=_default_config_from_args(TrainingConfig, args), + validation=_default_config_from_args(ValidationConfig, args), + optimizer=optim_cfg, + scheduler=_default_config_from_args(SchedulerConfig, args), + ddp=ddp_config, + dist=_default_config_from_args(DistributedInitConfig, args), + rng=_default_config_from_args(RNGConfig, args), + logger=_default_config_from_args(LoggerConfig, args), + checkpoint=CheckpointConfig(**ckpt_kwargs), + profiling=ProfilingConfig(**prof_kwargs), + + rerun_state_machine=RerunStateMachineConfig(**rerunsm_kwargs), + straggler=_default_config_from_args(StragglerDetectionConfig, args), + ) + + return cfg diff --git a/megatron/training/config/__init__.py b/megatron/training/config/__init__.py index 3d346ddd8fe..2d25388afc9 100644 --- a/megatron/training/config/__init__.py +++ b/megatron/training/config/__init__.py @@ -16,3 +16,5 @@ RerunStateMachineConfig, StragglerDetectionConfig, ) + +from megatron.training.config.container import PretrainConfigContainer diff --git a/megatron/training/config/container.py b/megatron/training/config/container.py new file mode 100644 index 00000000000..1ac8c4f2531 --- /dev/null +++ b/megatron/training/config/container.py @@ -0,0 +1,28 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +from dataclasses import dataclass, field +from megatron.training.config.common_config import RNGConfig, DistributedInitConfig, ProfilingConfig +from megatron.training.config.training_config import TrainingConfig, ValidationConfig, SchedulerConfig, LoggerConfig, CheckpointConfig +from megatron.core.optimizer import OptimizerConfig +from megatron.core.distributed.distributed_data_parallel_config import DistributedDataParallelConfig +from megatron.training.config.resilience_config import RerunStateMachineConfig, StragglerDetectionConfig + +@dataclass(kw_only=True) +class PretrainConfigContainer: + """Top-level container holding all configuration objects.""" + + train: TrainingConfig + validation: ValidationConfig = field(default_factory=ValidationConfig) + # model: GPTModelConfig | MambaModelConfig # TODO (@maanug): add support + optimizer: OptimizerConfig + scheduler: SchedulerConfig + # dataset: GPTDatasetConfig # TODO (@maanug): add support + # tokenizer: TokenizerConfig # TODO (@maanug): add support + ddp: DistributedDataParallelConfig = field(default_factory=DistributedDataParallelConfig) + dist: DistributedInitConfig = field(default_factory=DistributedInitConfig) + rng: RNGConfig = field(default_factory=RNGConfig) + logger: LoggerConfig + checkpoint: CheckpointConfig + profiling: ProfilingConfig = field(default_factory=ProfilingConfig) + + rerun_state_machine: RerunStateMachineConfig = field(default_factory=RerunStateMachineConfig) + straggler: StragglerDetectionConfig | None = None diff --git a/megatron/training/training.py b/megatron/training/training.py index cdb60a0722e..a16b7d9fb18 100644 --- a/megatron/training/training.py +++ b/megatron/training/training.py @@ -1,7 +1,10 @@ # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. """Pretrain utilities.""" +import argparse import time + +from megatron.training.config.container import PretrainConfigContainer # The earliest we can measure the start time. _TRAIN_START_TIME = time.time() @@ -830,6 +833,7 @@ def reorder_inner_param_groups(optimizer_state_dict): def pretrain( + cfg_container: PretrainConfigContainer, train_valid_test_dataset_provider, model_provider, model_type, @@ -917,7 +921,7 @@ def pretrain( set_ideal_affinity_for_current_gpu() - if args.log_progress: + if cfg_container.logger.log_progress: append_to_progress_log("Starting job") # Set pytorch JIT layer fusion options and warmup JIT functions. @@ -997,7 +1001,7 @@ def pretrain( one_logger_utils.on_pretrain_start() # Context used for persisting some state between checkpoint saves. - if args.non_persistent_ckpt_type == 'local': + if cfg_container.checkpoint.non_persistent_ckpt_type == 'local': try: from nvidia_resiliency_ext.checkpointing.local.ckpt_managers.local_manager import ( LocalCheckpointManager, @@ -1015,16 +1019,16 @@ def pretrain( "checkpointing but was not found. Please ensure it is installed." ) - if args.replication: + if cfg_container.checkpoint.replication: repl_strategy = CliqueReplicationStrategy.from_replication_params( - args.replication_jump, args.replication_factor + cfg_container.checkpoint.replication_jump, cfg_container.checkpoint.replication_factor ) else: repl_strategy = None checkpointing_context = { 'local_checkpoint_manager': LocalCheckpointManager( - args.non_persistent_local_ckpt_dir, repl_strategy=repl_strategy + cfg_container.checkpoint.non_persistent_local_ckpt_dir, repl_strategy=repl_strategy ) } else: @@ -1038,7 +1042,7 @@ def pretrain( timers('model-and-optimizer-setup').stop() print_datetime('after model, optimizer, and learning rate ' 'scheduler are built') - config = get_model_config(model[0]) + model_cfg = get_model_config(model[0]) # Build a separate inference model for RL if requested. inference_model = None @@ -1068,7 +1072,7 @@ def pretrain( ) # Build an isolated inference config so training config remains unchanged - inference_config = copy.deepcopy(config) + inference_config = copy.deepcopy(model_cfg) if args.rl_inference_tensor_model_parallel_size is not None: inference_config.tensor_model_parallel_size = args.rl_inference_tensor_model_parallel_size if args.rl_inference_pipeline_model_parallel_size is not None: @@ -1158,7 +1162,7 @@ def pretrain( # Track if training is enabled. Can only be done once args.do_train is assigned after dataloader is built. one_logger_utils.track_config_flags( args.train_iters, - args.skip_train, + cfg_container.validation.skip_train, args.do_train, args.do_valid, args.do_test, @@ -1177,8 +1181,8 @@ def pretrain( # Add job name to the wandb config to make it easier to run more singleton dependency jobs. wandb_writer.config.update({'slurm_job_name': os.getenv("SLURM_JOB_NAME", "N/A")}) - if not args.skip_train or args.perform_rl_step: - if args.skip_train: + if not cfg_container.validation.skip_train or args.perform_rl_step: + if cfg_container.validation.skip_train: print_rank_0('RL inference-only mode (--skip-train --perform-rl-step) ...') else: print_rank_0('training ...') @@ -1194,7 +1198,7 @@ def pretrain( train_data_iterator, valid_data_iterator, process_non_loss_data_func, - config, + model_cfg, checkpointing_context, non_loss_data_func, inference_model, @@ -1202,7 +1206,7 @@ def pretrain( print_datetime('after training is done') - if not args.skip_train and args.save and iteration != 0 and iteration % args.save_interval != 0: + if not cfg_container.validation.skip_train and cfg_container.checkpoint.save and iteration != 0 and iteration % cfg_container.checkpoint.save_interval != 0: save_checkpoint_and_time( iteration, model, @@ -1240,15 +1244,15 @@ def pretrain( rl_eval_model, optimizer, iteration, - write_to_tensorboard=not args.skip_train, + write_to_tensorboard=not cfg_container.validation.skip_train, training_model=rl_training_model, ) else: evaluate_and_print_results( prefix, forward_step_func, valid_data_iterator, model, - iteration, process_non_loss_data_func, config, - verbose=True, write_to_tensorboard=not args.skip_train, + iteration, process_non_loss_data_func, model_cfg, + verbose=True, write_to_tensorboard=not cfg_container.validation.skip_train, non_loss_data_func=non_loss_data_func ) @@ -1261,9 +1265,9 @@ def pretrain( model, iteration, process_non_loss_data_func, - config, + model_cfg, verbose=True, - write_to_tensorboard=not args.skip_train, + write_to_tensorboard=not cfg_container.validation.skip_train, non_loss_data_func=non_loss_data_func, ) @@ -1453,34 +1457,18 @@ def build_model(): reshard_after_forward = getattr(args, "torch_fsdp2_reshard_after_forward", True) ddp_config = TorchFullyShardedDataParallelConfig(reshard_after_forward=reshard_after_forward) else: - kwargs = {} - for f in dataclasses.fields(DistributedDataParallelConfig): - if hasattr(args, f.name): - kwargs[f.name] = getattr(args, f.name) - kwargs['grad_reduce_in_fp32'] = args.accumulate_allreduce_grads_in_fp32 - kwargs['check_for_nan_in_grad'] = args.check_for_nan_in_loss_and_grad - kwargs['check_for_large_grads'] = args.check_for_large_grads if args.ddp_num_buckets is not None: assert args.ddp_bucket_size is None, \ "Cannot specify both --ddp-num-buckets and --ddp-bucket-size" assert args.ddp_num_buckets > 0, \ "--ddp-num-buckets must be greater than 0" - kwargs['bucket_size'] = num_parameters // args.ddp_num_buckets + bucket_size = num_parameters // args.ddp_num_buckets else: - kwargs['bucket_size'] = args.ddp_bucket_size - kwargs['pad_buckets_for_high_nccl_busbw'] = args.ddp_pad_buckets_for_high_nccl_busbw - kwargs['reduce_scatter_with_fp32_accumulation'] = args.ddp_reduce_scatter_with_fp32_accumulation - kwargs['param_name_patterns_for_fp32_local_accumulation'] = \ - tuple(args.ddp_param_name_patterns_for_fp32_local_accumulation) - kwargs['average_in_collective'] = args.ddp_average_in_collective - # Megatron-FSDP arguments. - kwargs['megatron_fsdp_main_params_dtype'] = args.megatron_fsdp_main_params_dtype - kwargs['megatron_fsdp_main_grads_dtype'] = args.megatron_fsdp_main_grads_dtype - kwargs['megatron_fsdp_grad_comm_dtype'] = args.megatron_fsdp_grad_comm_dtype - kwargs['megatron_fsdp_use_decoupled_grad'] = args.use_precision_aware_optimizer + bucket_size = args.ddp_bucket_size # Initialize DDPConfig. - ddp_config = DistributedDataParallelConfig(**kwargs) + ddp_config = get_megatron_ddp_config(args) + ddp_config.bucket_size = bucket_size # In the Megatron FSDP and DDP use path, we need to initialize the bucket size. # If bucket_size is not provided as an input, use sane default. @@ -1632,6 +1620,29 @@ def get_megatron_optimizer_config(args: Any) -> OptimizerConfig: return config, config_overrides +def get_megatron_ddp_config(args: argparse.Namespace) -> DistributedDataParallelConfig: + """Return an MCore DDPConfig from the argparse arguments.""" + + kwargs = {} + for f in dataclasses.fields(DistributedDataParallelConfig): + if hasattr(args, f.name): + kwargs[f.name] = getattr(args, f.name) + kwargs["grad_reduce_in_fp32"] = args.accumulate_allreduce_grads_in_fp32 + kwargs["check_for_nan_in_grad"] = args.check_for_nan_in_loss_and_grad + kwargs["check_for_large_grads"] = args.check_for_large_grads + kwargs["pad_buckets_for_high_nccl_busbw"] = args.ddp_pad_buckets_for_high_nccl_busbw + kwargs["reduce_scatter_with_fp32_accumulation"] = args.ddp_reduce_scatter_with_fp32_accumulation + kwargs["param_name_patterns_for_fp32_local_accumulation"] = \ + tuple(args.ddp_param_name_patterns_for_fp32_local_accumulation) + kwargs["average_in_collective"] = args.ddp_average_in_collective + # Megatron-FSDP arguments. + kwargs["megatron_fsdp_main_params_dtype"] = args.megatron_fsdp_main_params_dtype + kwargs["megatron_fsdp_main_grads_dtype"] = args.megatron_fsdp_main_grads_dtype + kwargs["megatron_fsdp_grad_comm_dtype"] = args.megatron_fsdp_grad_comm_dtype + kwargs["megatron_fsdp_use_decoupled_grad"] = args.use_precision_aware_optimizer + + return DistributedDataParallelConfig(**kwargs) + def setup_model_and_optimizer( model_provider_func, diff --git a/pretrain_bert.py b/pretrain_bert.py index a5a75d87879..3eb95ecf396 100644 --- a/pretrain_bert.py +++ b/pretrain_bert.py @@ -16,6 +16,7 @@ from megatron.training import pretrain from megatron.training.utils import average_losses_across_data_parallel_group from megatron.training.arguments import core_transformer_config_from_args, parse_and_validate_args +from megatron.training.argument_utils import pretrain_cfg_container_from_args from megatron.core.transformer.spec_utils import import_module from megatron.core.models.bert.bert_layer_specs import bert_layer_with_transformer_engine_spec, bert_layer_local_spec from megatron.core.tokenizers.utils.build_tokenizer import build_tokenizer @@ -181,7 +182,8 @@ def train_valid_test_datasets_provider(train_val_test_num_samples, vp_stage=None # Temporary for transition to core datasets train_valid_test_datasets_provider.is_distributed = True - parse_and_validate_args(args_defaults={'tokenizer_type': 'BertWordPieceLowerCase'}) - pretrain(train_valid_test_datasets_provider, model_provider, + args = parse_and_validate_args(args_defaults={'tokenizer_type': 'BertWordPieceLowerCase'}) + full_config = pretrain_cfg_container_from_args(args) + pretrain(full_config, train_valid_test_datasets_provider, model_provider, ModelType.encoder_or_decoder, forward_step) diff --git a/pretrain_gpt.py b/pretrain_gpt.py index 02ca5dd72c0..e9f6987f3f0 100644 --- a/pretrain_gpt.py +++ b/pretrain_gpt.py @@ -42,6 +42,7 @@ from megatron.training.datasets.sft_dataset import SFTDataset from megatron.core.transformer.multi_token_prediction import mtp_on_this_rank, get_mtp_ranks from megatron.training.arguments import core_transformer_config_from_args, parse_and_validate_args +from megatron.training.argument_utils import pretrain_cfg_container_from_args from megatron.training.datasets.fim_dataset import GPTFIMDataset, GPTFIMDatasetConfig from megatron.training.utils import ( get_batch_on_this_cp_rank, @@ -413,7 +414,8 @@ def get_embedding_ranks(pp_ranks: List[int]): extra_args_provider=add_modelopt_args if has_nvidia_modelopt else None, args_defaults={'tokenizer_type': 'GPT2BPETokenizer'}, ) - pretrain( + full_config = pretrain_cfg_container_from_args(args) + pretrain(full_config, train_valid_test_datasets_provider, partial(model_provider, gpt_builder), ModelType.encoder_or_decoder, diff --git a/pretrain_hybrid.py b/pretrain_hybrid.py index f073e8e9ab3..293b8c76935 100644 --- a/pretrain_hybrid.py +++ b/pretrain_hybrid.py @@ -43,6 +43,7 @@ set_startup_timestamps, ) from megatron.training.arguments import parse_and_validate_args +from megatron.training.argument_utils import pretrain_cfg_container_from_args from megatron.training.datasets.sft_dataset import SFTDataset from megatron.training.utils import ( get_batch_on_this_cp_rank, @@ -361,7 +362,9 @@ def train_valid_test_datasets_provider(train_val_test_num_samples, vp_stage=None extra_args_provider=add_modelopt_args if has_nvidia_modelopt else None, args_defaults={'tokenizer_type': 'GPT2BPETokenizer'}, ) - pretrain(train_valid_test_datasets_provider, + full_config = pretrain_cfg_container_from_args(args) + pretrain(full_config, + train_valid_test_datasets_provider, partial(model_provider, hybrid_builder), ModelType.encoder_or_decoder, forward_step, diff --git a/pretrain_t5.py b/pretrain_t5.py index 2b10fa3ffe3..171166d08b2 100644 --- a/pretrain_t5.py +++ b/pretrain_t5.py @@ -27,6 +27,7 @@ ) from megatron.training import get_args, get_timers, pretrain, print_rank_0 from megatron.training.arguments import core_transformer_config_from_args, parse_and_validate_args +from megatron.training.argument_utils import pretrain_cfg_container_from_args from pretrain_gpt import loss_func """ @@ -269,8 +270,10 @@ def t5_position_embedding_ranks(pp_ranks): # Temporary for transition to core datasets train_valid_test_datasets_provider.is_distributed = True - parse_and_validate_args(args_defaults={'tokenizer_type': 'BertWordPieceLowerCase'}) + args = parse_and_validate_args(args_defaults={'tokenizer_type': 'BertWordPieceLowerCase'}) + full_config = pretrain_cfg_container_from_args(args) pretrain( + full_config, train_valid_test_datasets_provider, model_provider, ModelType.encoder_or_decoder, diff --git a/pretrain_vlm.py b/pretrain_vlm.py index a6aef770002..988172be4ff 100644 --- a/pretrain_vlm.py +++ b/pretrain_vlm.py @@ -39,6 +39,7 @@ print_rank_0, ) from megatron.training.arguments import core_transformer_config_from_args, parse_and_validate_args +from megatron.training.argument_utils import pretrain_cfg_container_from_args from pretrain_gpt import loss_func @@ -439,11 +440,13 @@ def llava_position_embedding_ranks(pp_ranks): if __name__ == "__main__": train_valid_test_datasets_provider.is_distributed = True - parse_and_validate_args( + args = parse_and_validate_args( extra_args_provider=add_vlm_extra_args, args_defaults={'tokenizer_type': 'GPT2BPETokenizer'}, ) + full_config = pretrain_cfg_container_from_args(args) pretrain( + full_config, train_valid_test_datasets_provider, model_provider, ModelType.encoder_or_decoder, diff --git a/tests/unit_tests/test_argument_utils.py b/tests/unit_tests/test_argument_utils.py index e5744c3b074..f698e879951 100644 --- a/tests/unit_tests/test_argument_utils.py +++ b/tests/unit_tests/test_argument_utils.py @@ -1,13 +1,21 @@ # Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. import signal -from argparse import ArgumentError, ArgumentParser +from argparse import ArgumentError, ArgumentParser, Namespace from dataclasses import dataclass, field from typing import Callable, Literal, Optional, Union +from unittest.mock import MagicMock, patch import pytest -from megatron.training.argument_utils import ArgumentGroupFactory, TypeInferenceError +from megatron.core.distributed.distributed_data_parallel_config import DistributedDataParallelConfig +from megatron.core.optimizer import OptimizerConfig +from megatron.training.argument_utils import ( + ArgumentGroupFactory, + TypeInferenceError, + pretrain_cfg_container_from_args, +) +from megatron.training.config import PretrainConfigContainer @dataclass @@ -641,3 +649,223 @@ def test_handled_unsupported_unions(self): with pytest.raises(ArgumentError, match="invalid choice"): args = parser.parse_args(['--unsupported-with-metadata', 'baz']) + + +# --------------------------------------------------------------------------- +# Tests for pretrain_cfg_container_from_args +# --------------------------------------------------------------------------- + + +def _make_args(**overrides): + """Build the minimum Namespace required by pretrain_cfg_container_from_args. + + These fields have non-trivial CLI→config name mappings or boolean inversions + that pretrain_cfg_container_from_args handles explicitly. + """ + defaults = { + # CheckpointConfig boolean inversions (legacy-style --no-* flags) + "no_save_optim": False, + "no_save_rng": False, + "no_load_optim": False, + "no_load_rng": False, + # CheckpointConfig custom argparse dest names + "ckpt_fully_parallel_save": True, + "ckpt_fully_parallel_load": False, + # ProfilingConfig: use_nsys_profiler is exposed as --profile on the CLI + "profile": False, + # RerunStateMachineConfig: field is check_for_nan_in_loss, CLI flag is check_for_nan_in_loss_and_grad + "check_for_nan_in_loss_and_grad": True, + } + defaults.update(overrides) + return Namespace(**defaults) + + +@pytest.fixture +def mock_optimizer_config(): + return MagicMock(spec=OptimizerConfig) + + +@pytest.fixture +def mock_ddp_config(): + return MagicMock(spec=DistributedDataParallelConfig) + + +@pytest.fixture +def patch_training_helpers(mock_optimizer_config, mock_ddp_config): + """Patch the two helper functions called by pretrain_cfg_container_from_args.""" + with ( + patch( + "megatron.training.training.get_megatron_optimizer_config", + return_value=(mock_optimizer_config, {}), + ), + patch("megatron.training.training.get_megatron_ddp_config", return_value=mock_ddp_config), + ): + yield + + +class TestPretrainContainerFromArgsStructure: + """Test the top-level structure of the object returned by pretrain_cfg_container_from_args.""" + + def test_returns_pretrain_config_container(self, patch_training_helpers): + result = pretrain_cfg_container_from_args(_make_args()) + assert isinstance(result, PretrainConfigContainer) + + @patch("megatron.training.training.get_megatron_ddp_config") + @patch("megatron.training.training.get_megatron_optimizer_config") + def test_optimizer_config_comes_from_helper(self, mock_opt, mock_ddp): + """Test that optimizer config comes from get_megatron_optimizer_config.""" + mock_optimizer = MagicMock(spec=OptimizerConfig) + mock_opt.return_value = (mock_optimizer, {}) + mock_ddp.return_value = MagicMock(spec=DistributedDataParallelConfig) + args = _make_args() + result = pretrain_cfg_container_from_args(args) + mock_opt.assert_called_once_with(args) + assert result.optimizer is mock_optimizer + + @patch("megatron.training.training.get_megatron_ddp_config") + @patch("megatron.training.training.get_megatron_optimizer_config") + def test_ddp_config_comes_from_helper(self, mock_opt, mock_ddp): + """Test that ddp config comes from get_megatron_ddp_config.""" + mock_ddp_instance = MagicMock(spec=DistributedDataParallelConfig) + mock_opt.return_value = (MagicMock(spec=OptimizerConfig), {}) + mock_ddp.return_value = mock_ddp_instance + args = _make_args() + result = pretrain_cfg_container_from_args(args) + mock_ddp.assert_called_once_with(args) + assert result.ddp is mock_ddp_instance + + +class TestCheckpointConfigMapping: + """Test the boolean inversions and custom dest mappings for CheckpointConfig.""" + + def test_no_save_optim_false_means_save_optim_true(self, patch_training_helpers): + result = pretrain_cfg_container_from_args(_make_args(no_save_optim=False)) + assert result.checkpoint.save_optim is True + + def test_no_save_optim_true_means_save_optim_false(self, patch_training_helpers): + result = pretrain_cfg_container_from_args(_make_args(no_save_optim=True)) + assert result.checkpoint.save_optim is False + + def test_no_save_rng_false_means_save_rng_true(self, patch_training_helpers): + result = pretrain_cfg_container_from_args(_make_args(no_save_rng=False)) + assert result.checkpoint.save_rng is True + + def test_no_save_rng_true_means_save_rng_false(self, patch_training_helpers): + result = pretrain_cfg_container_from_args(_make_args(no_save_rng=True)) + assert result.checkpoint.save_rng is False + + def test_no_load_optim_false_means_load_optim_true(self, patch_training_helpers): + result = pretrain_cfg_container_from_args(_make_args(no_load_optim=False)) + assert result.checkpoint.load_optim is True + + def test_no_load_optim_true_means_load_optim_false(self, patch_training_helpers): + result = pretrain_cfg_container_from_args(_make_args(no_load_optim=True)) + assert result.checkpoint.load_optim is False + + def test_no_load_rng_false_means_load_rng_true(self, patch_training_helpers): + result = pretrain_cfg_container_from_args(_make_args(no_load_rng=False)) + assert result.checkpoint.load_rng is True + + def test_no_load_rng_true_means_load_rng_false(self, patch_training_helpers): + result = pretrain_cfg_container_from_args(_make_args(no_load_rng=True)) + assert result.checkpoint.load_rng is False + + def test_ckpt_fully_parallel_save_mapping(self, patch_training_helpers): + """ckpt_fully_parallel_save in args maps to fully_parallel_save in CheckpointConfig.""" + assert ( + pretrain_cfg_container_from_args( + _make_args(ckpt_fully_parallel_save=True) + ).checkpoint.fully_parallel_save + is True + ) + assert ( + pretrain_cfg_container_from_args( + _make_args(ckpt_fully_parallel_save=False) + ).checkpoint.fully_parallel_save + is False + ) + + def test_ckpt_fully_parallel_load_mapping(self, patch_training_helpers): + """ckpt_fully_parallel_load in args maps to fully_parallel_load in CheckpointConfig.""" + assert ( + pretrain_cfg_container_from_args( + _make_args(ckpt_fully_parallel_load=True) + ).checkpoint.fully_parallel_load + is True + ) + assert ( + pretrain_cfg_container_from_args( + _make_args(ckpt_fully_parallel_load=False) + ).checkpoint.fully_parallel_load + is False + ) + + def test_direct_checkpoint_fields_from_args(self, patch_training_helpers): + """Checkpoint fields with 1-to-1 name mapping are pulled directly from args.""" + args = _make_args(save="/path/to/save", load="/path/to/load", save_interval=500) + result = pretrain_cfg_container_from_args(args) + assert result.checkpoint.save == "/path/to/save" + assert result.checkpoint.load == "/path/to/load" + assert result.checkpoint.save_interval == 500 + + +class TestProfilingConfigMapping: + """Test the --profile → use_nsys_profiler mapping for ProfilingConfig.""" + + def test_profile_false_disables_nsys_profiler(self, patch_training_helpers): + result = pretrain_cfg_container_from_args(_make_args(profile=False)) + assert result.profiling.use_nsys_profiler is False + + def test_profile_true_enables_nsys_profiler(self, patch_training_helpers): + result = pretrain_cfg_container_from_args(_make_args(profile=True)) + assert result.profiling.use_nsys_profiler is True + + def test_direct_profiling_fields_from_args(self, patch_training_helpers): + """Profiling fields with 1-to-1 name mapping are pulled directly from args.""" + args = _make_args(profile_step_start=5, profile_step_end=15) + result = pretrain_cfg_container_from_args(args) + assert result.profiling.profile_step_start == 5 + assert result.profiling.profile_step_end == 15 + + +class TestRerunStateMachineConfigMapping: + """Test the check_for_nan_in_loss_and_grad → check_for_nan_in_loss mapping.""" + + def test_check_for_nan_true(self, patch_training_helpers): + result = pretrain_cfg_container_from_args(_make_args(check_for_nan_in_loss_and_grad=True)) + assert result.rerun_state_machine.check_for_nan_in_loss is True + + def test_check_for_nan_false(self, patch_training_helpers): + result = pretrain_cfg_container_from_args(_make_args(check_for_nan_in_loss_and_grad=False)) + assert result.rerun_state_machine.check_for_nan_in_loss is False + + def test_direct_rerun_state_machine_fields_from_args(self, patch_training_helpers): + """RerunStateMachineConfig fields with 1-to-1 name mapping are pulled directly from args.""" + args = _make_args(error_injection_rate=500, rerun_mode="report_stats") + result = pretrain_cfg_container_from_args(args) + assert result.rerun_state_machine.error_injection_rate == 500 + assert result.rerun_state_machine.rerun_mode == "report_stats" + + +class TestTrainingConfigMapping: + """Test that fields are pulled from args for configs that use _default_config_from_args() directly. + + TrainingConfig is used as the representative case. The same pass-through logic applies to + ValidationConfig, SchedulerConfig, RNGConfig, DistributedInitConfig, LoggerConfig, and + StragglerDetectionConfig — dedicated test classes for those are only warranted if + pretrain_cfg_container_from_args() adds special handling for them. + """ + + def test_training_fields_from_args(self, patch_training_helpers): + args = _make_args(train_iters=1000, micro_batch_size=4, global_batch_size=64) + result = pretrain_cfg_container_from_args(args) + assert result.train.train_iters == 1000 + assert result.train.micro_batch_size == 4 + assert result.train.global_batch_size == 64 + + def test_training_uses_defaults_when_fields_absent(self, patch_training_helpers): + """When training fields are absent from args, TrainingConfig uses its defaults.""" + result = pretrain_cfg_container_from_args(_make_args()) + assert result.train.train_iters is None + assert result.train.micro_batch_size is None + assert result.train.global_batch_size is None diff --git a/train_rl.py b/train_rl.py index 3e4ccdf4f39..7d742772e91 100644 --- a/train_rl.py +++ b/train_rl.py @@ -24,6 +24,7 @@ from megatron.training import get_args, get_timers, pretrain, print_rank_0 from megatron.training.utils import is_hybrid_model from megatron.training.arguments import core_transformer_config_from_args, parse_and_validate_args +from megatron.training.argument_utils import pretrain_cfg_container_from_args from model_provider import model_provider from megatron.core.packed_seq_params import PackedSeqParams @@ -410,11 +411,13 @@ def _model_builder( pg_collection=pg_collection, ) - parse_and_validate_args( + args = parse_and_validate_args( extra_args_provider=add_inference_args, args_defaults={}, ) + full_config = pretrain_cfg_container_from_args(args) pretrain( + full_config, None, # we don't need to build any datasets for RL training partial(model_provider, _model_builder), ModelType.encoder_or_decoder, From a1165fabcad97eae3778f386839c233dfabf3f8b Mon Sep 17 00:00:00 2001 From: Siddharth Singh Date: Fri, 24 Apr 2026 01:12:44 -0700 Subject: [PATCH 017/105] Inference: Fix broken functional tests on gitlab (#4454) --- .../test_inference_regular_pipeline.py | 15 +- .../golden_values_dev_dgx_h100.json | 26220 +--------------- .../golden_values_dev_dgx_h100.json | 26220 +--------------- 3 files changed, 2917 insertions(+), 49538 deletions(-) diff --git a/tests/functional_tests/python_test_utils/test_inference_regular_pipeline.py b/tests/functional_tests/python_test_utils/test_inference_regular_pipeline.py index 165e17b102b..a212ed417d6 100644 --- a/tests/functional_tests/python_test_utils/test_inference_regular_pipeline.py +++ b/tests/functional_tests/python_test_utils/test_inference_regular_pipeline.py @@ -6,6 +6,7 @@ import os from statistics import median +import numpy as np import pytest import yaml @@ -189,10 +190,16 @@ def test_inference_pipeline( if "routing_indices" in groundtruth_results and "routing_indices" in metrics: at_least_one_test_loop = True - routing_indices_groundtruth = groundtruth_results["routing_indices"] - routing_indices_current = current_results["routing_indices"] - assert ( - routing_indices_groundtruth == routing_indices_current + token_indices = groundtruth_results.get("routing_indices_token_indices") + current_routing = np.array(current_results["routing_indices"]) + assert token_indices is not None + current_routing = current_routing[token_indices] + routing_indices_groundtruth = np.sort( + np.array(groundtruth_results["routing_indices"]), axis=-1 + ) + routing_indices_current = np.sort(current_routing, axis=-1) + assert np.array_equal( + routing_indices_groundtruth, routing_indices_current ), f"Routing indices mismatch:\nGround truth: {routing_indices_groundtruth}\nCurrent: {routing_indices_current}" if not at_least_one_test_loop: diff --git a/tests/functional_tests/test_cases/moe/gpt_dynamic_inference_tp4_etp1_pp1_ep8_16B_logitsmatch_cudagraph_zmq/golden_values_dev_dgx_h100.json b/tests/functional_tests/test_cases/moe/gpt_dynamic_inference_tp4_etp1_pp1_ep8_16B_logitsmatch_cudagraph_zmq/golden_values_dev_dgx_h100.json index f0bbe4685d3..b553507edfb 100644 --- a/tests/functional_tests/test_cases/moe/gpt_dynamic_inference_tp4_etp1_pp1_ep8_16B_logitsmatch_cudagraph_zmq/golden_values_dev_dgx_h100.json +++ b/tests/functional_tests/test_cases/moe/gpt_dynamic_inference_tp4_etp1_pp1_ep8_16B_logitsmatch_cudagraph_zmq/golden_values_dev_dgx_h100.json @@ -157,25510 +157,2196 @@ "routing_indices": [ [ [ + 48, 33, - 50, - 36, - 4, - 25, - 63 + 24, + 52, + 38, + 61 ], [ - 0, - 16, 3, - 26, - 9, - 54 + 40, + 45, + 19, + 58, + 35 ], [ - 62, + 46, + 39, + 15, 60, - 8, - 16, - 58, - 52 + 31, + 25 ], [ - 58, - 10, - 6, + 49, + 62, + 61, + 48, + 55, + 46 + ], + [ + 41, + 35, + 53, + 52, + 13, + 10 + ], + [ + 52, 45, - 16, - 32 + 15, + 63, + 46, + 10 ], [ - 43, + 14, + 15, + 35, + 60, 49, - 18, - 54, - 55, - 13 + 31 ], [ - 27, - 19, - 26, 44, - 12, - 28 + 3, + 6, + 8, + 23, + 48 ], [ - 53, - 42, - 3, + 32, + 15, + 44, 27, - 26, - 19 + 4, + 41 ], [ - 6, - 47, - 1, - 19, - 8, - 22 + 51, + 25, + 62, + 53, + 48, + 10 ], [ - 51, - 27, - 1, - 38, + 8, + 50, + 5, + 19, 16, - 62 + 22 ], [ - 41, + 36, 49, - 21, - 57, - 16, - 24 + 60, + 44, + 15, + 33 ], [ 13, - 28, - 38, + 27, + 53, + 30, + 56, + 43 + ], + [ + 26, 22, - 49, - 48 + 14, + 7, + 32, + 17 ], [ - 52, + 26, + 60, + 2, 58, - 6, - 25, - 29, - 17 + 54, + 10 ], [ - 49, 50, - 25, - 41, - 54, - 58 + 55, + 17, + 51, + 47, + 14 + ], + [ + 0, + 12, + 16, + 4, + 23, + 9 ], [ + 10, + 23, 27, - 41, - 3, - 26, - 1, - 29 + 46, + 56, + 55 ], [ - 41, - 18, - 34, - 45, - 1, - 52 + 3, + 37, + 4, + 60, + 16, + 59 ], [ - 17, - 26, - 59, - 22, - 19, - 4 + 38, + 3, + 29, + 40, + 25, + 50 ], [ - 1, - 43, + 31, + 16, 62, - 57, - 61, - 29 + 54, + 42, + 63 ], [ + 47, + 35, 11, + 37, 46, - 15, - 28, - 61, - 50 - ], - [ - 14, - 30, - 58, - 26, - 38, - 53 + 32 ], [ - 59, + 51, + 27, 20, - 63, - 54, - 47, - 61 + 50, + 16, + 55 ], [ - 9, - 5, - 43, - 33, - 15, - 46 + 63, + 55, + 46, + 27, + 48, + 12 ], [ - 16, - 52, - 33, - 61, - 49, - 11 - ], - [ - 52, - 35, - 40, - 43, - 29, - 36 - ], - [ - 57, - 34, - 38, - 44, - 20, - 18 + 53, + 50, + 30, + 2, + 39, + 20 ], [ + 53, + 24, 44, + 8, 51, - 2, - 63, - 7, - 17 - ], - [ - 32, - 47, - 58, - 9, - 54, - 5 + 14 ], [ - 12, - 59, - 54, - 33, - 50, - 6 + 19, + 49, + 37, + 14, + 44, + 0 ] ], [ [ - 49, - 43, - 18, - 28, - 23, - 25 - ], - [ + 21, + 50, + 29, + 41, 34, - 24, - 23, - 60, - 2, - 18 + 60 ], [ + 28, + 51, + 60, 33, - 40, - 30, - 3, - 59, - 48 - ], - [ - 16, - 58, - 17, - 48, - 6, + 14, 45 ], [ - 23, - 58, + 31, + 2, 46, - 37, - 34, - 48 + 33, + 24, + 49 ], [ - 28, - 26, - 35, - 33, - 43, - 22 + 42, + 3, + 18, + 62, + 39, + 49 ], [ - 6, - 31, - 13, - 46, - 41, - 37 + 25, + 15, + 62, + 27, + 12, + 11 ], [ - 60, - 29, 44, + 50, 36, - 39, - 15 + 57, + 55, + 41 ], [ - 51, - 18, - 62, - 5, - 27, - 35 + 41, + 37, + 22, + 15, + 2, + 40 ], [ + 36, 62, - 45, - 32, - 56, - 25, - 3 + 53, + 30, + 14, + 57 ], [ - 6, - 20, - 3, 16, - 49, - 41 + 58, + 2, + 29, + 4, + 3 ], [ - 36, 41, - 50, + 38, + 26, + 16, 45, - 35, - 48 + 46 ], [ - 39, + 45, 46, - 48, - 25, + 32, + 41, + 56, + 26 + ], + [ + 17, + 53, 21, - 33 + 11, + 36, + 35 ], [ - 30, - 38, 11, + 16, + 28, + 14, + 51, + 61 + ], + [ + 9, + 35, + 33, 22, - 15, - 9 + 52, + 62 ], [ - 24, - 2, - 32, - 56, + 58, + 50, 63, - 3 + 30, + 7, + 27 ], [ - 36, 55, - 35, - 32, - 17, - 44 + 3, + 8, + 41, + 63, + 37 ], [ - 59, + 3, + 51, 46, 32, - 44, - 24, - 14 - ], - [ - 51, 15, - 61, - 43, - 30, - 22 + 6 ], [ 32, - 2, + 12, + 10, + 25, 5, + 49 + ], + [ + 34, + 2, + 37, + 61, 39, - 11, - 50 + 63 ], [ - 28, 42, - 6, - 30, - 57, - 37 + 22, + 27, + 53, + 11, + 56 ], [ - 28, - 55, - 45, + 53, + 12, 0, - 7, - 5 + 47, + 61, + 1 ], [ + 39, + 45, + 53, + 17, 48, - 40, - 34, - 3, - 22, - 49 + 14 ], [ - 25, 6, - 62, - 18, - 50, - 53 + 0, + 4, + 53, + 25, + 11 ], [ - 13, - 22, - 20, - 28, - 25, - 59 + 51, + 11, + 1, + 63, + 54, + 45 ], [ - 58, - 39, - 36, - 47, - 29, - 37 + 40, + 56, + 37, + 53, + 5, + 35 ], [ - 4, - 33, + 59, + 28, 41, - 12, - 3, - 17 + 10, + 1, + 45 ], [ - 22, - 13, - 20, - 52, + 27, + 30, + 28, 24, - 62 + 32, + 57 ] ], [ [ - 17, - 10, - 57, - 54, + 24, + 56, 6, - 15 + 0, + 19, + 45 ], [ - 33, - 43, - 13, - 1, - 16, - 62 + 11, + 57, + 59, + 25, + 46, + 30 ], [ - 63, - 1, - 35, - 43, - 27, - 10 + 11, + 26, + 37, + 29, + 14, + 52 ], [ - 47, - 4, + 3, + 32, + 7, 38, - 50, - 51, - 0 + 36, + 24 ], [ - 11, - 51, - 57, - 23, + 61, + 2, + 24, 14, - 34 + 51, + 44 ], [ - 10, - 43, - 35, - 33, 20, - 22 + 47, + 0, + 63, + 30, + 58 ], [ + 4, 36, - 48, - 35, - 19, - 21, - 28 + 29, + 58, + 16, + 3 ], [ + 20, + 0, + 45, 14, - 8, - 7, - 46, - 35, - 13 + 28, + 44 ], [ - 18, - 44, - 63, - 6, - 4, - 37 + 29, + 56, + 47, + 35, + 16, + 4 ], [ - 62, - 29, - 15, - 38, - 39, - 34 + 33, + 61, + 55, + 41, + 51, + 38 ], [ + 58, 1, - 6, - 16, - 46, - 22, - 13 + 38, + 14, + 4, + 19 ], [ + 0, 36, - 8, - 16, - 37, - 10, - 14 + 14, + 18, + 52, + 42 ], [ + 29, + 36, + 45, + 25, 8, - 0, - 32, - 3, - 43, - 10 + 6 ], [ - 25, - 0, - 22, - 30, - 60, - 57 + 6, + 57, + 50, + 40, + 58, + 61 ], [ - 60, + 44, 58, - 55, - 32, - 2, - 7 + 29, + 19, + 61, + 56 ], [ + 23, + 18, + 28, 55, - 11, - 19, 5, - 24, - 43 + 37 ], [ + 13, 9, - 47, - 36, - 39, - 5, - 42 + 19, + 43, + 37, + 3 ], [ - 31, - 20, - 9, - 43, - 18, + 32, + 22, + 63, + 14, + 57, 41 ], [ - 12, - 5, - 32, - 50, + 10, + 61, 3, - 31 + 1, + 19, + 32 ], [ - 40, - 18, - 30, - 63, - 7, - 25 + 16, + 55, + 10, + 41, + 59, + 22 ], [ - 14, + 53, 7, - 3, + 29, 38, - 59, - 54 + 27, + 46 ], [ - 31, - 20, - 1, - 22, + 24, 47, - 6 + 18, + 53, + 39, + 30 ], [ + 0, + 33, + 19, + 5, 51, - 15, - 18, - 53, - 40, - 52 + 17 ], [ - 27, - 54, + 51, 8, - 38, - 3, - 59 + 11, + 45, + 44, + 41 ], [ + 40, + 4, + 23, 11, - 16, - 39, - 59, - 9, - 23 + 27, + 19 ], [ - 7, - 37, - 51, - 30, + 16, 18, - 48 + 3, + 48, + 51, + 21 ], [ - 0, - 36, - 33, - 40, + 43, 46, - 48 + 60, + 19, + 53, + 12 ] ], [ [ - 48, - 33, 24, - 52, - 38, - 61 - ], - [ - 3, - 40, - 45, + 56, + 6, + 0, 19, - 58, - 35 - ], - [ - 46, - 39, - 15, - 60, - 31, - 25 - ], - [ - 49, - 62, - 61, - 48, - 55, - 46 - ], - [ - 41, - 35, - 53, - 52, - 13, - 10 + 45 ], [ - 52, - 45, - 15, - 63, + 11, + 57, + 59, 46, - 10 + 25, + 30 ], [ + 26, + 11, + 37, 14, - 15, - 35, - 60, - 49, - 31 + 29, + 49 ], [ - 44, + 38, + 36, 3, - 6, - 8, - 23, - 48 - ], - [ - 32, - 15, - 44, - 27, - 4, - 41 + 24, + 18, + 20 ], [ + 61, 51, - 25, - 62, - 53, - 48, - 10 + 14, + 2, + 24, + 1 ], [ + 20, + 0, + 47, + 30, 8, - 50, - 5, - 19, - 16, - 22 + 35 ], [ + 4, + 58, 36, - 49, - 60, + 54, + 29, + 12 + ], + [ + 20, + 58, 44, - 15, - 33 + 28, + 45, + 9 ], [ - 13, - 27, - 53, - 30, 56, - 43 + 47, + 10, + 29, + 35, + 27 ], [ - 26, - 22, - 14, - 7, - 32, - 17 + 61, + 33, + 55, + 54, + 4, + 36 ], [ - 26, - 60, - 2, 58, - 54, - 10 + 1, + 14, + 4, + 38, + 52 ], [ - 50, - 55, - 17, - 51, - 47, - 14 + 14, + 0, + 36, + 63, + 15, + 52 ], [ - 0, - 12, + 29, + 36, + 44, + 8, 16, - 4, - 23, - 9 + 2 ], [ - 10, - 23, + 6, + 40, 27, - 46, - 56, - 55 + 57, + 50, + 42 ], [ - 3, + 19, + 44, + 58, + 61, 37, - 4, - 60, - 16, - 59 + 38 ], [ - 38, - 3, + 23, + 18, + 17, + 57, + 13, + 40 + ], + [ + 13, + 9, + 19, + 37, + 50, + 15 + ], + [ + 32, + 14, + 57, + 58, 29, - 40, - 25, - 50 + 22 ], [ - 31, + 61, + 10, + 1, + 3, + 14, + 59 + ], + [ + 55, 16, - 62, - 54, - 42, - 63 + 34, + 18, + 22, + 49 + ], + [ + 53, + 27, + 38, + 28, + 23, + 44 ], [ + 24, 47, - 35, - 11, - 37, - 46, - 32 + 18, + 62, + 41, + 30 ], [ + 33, 51, - 27, - 20, - 50, - 16, - 55 + 19, + 5, + 0, + 31 ], [ - 63, - 55, - 46, + 51, + 8, + 25, + 53, 27, - 48, - 12 + 16 ], [ - 53, - 50, - 30, - 2, - 39, - 20 + 40, + 11, + 27, + 4, + 23, + 19 ], [ - 53, - 24, - 44, - 8, + 16, + 18, 51, - 14 + 48, + 3, + 47 ], [ - 19, - 49, - 37, - 14, - 44, - 0 + 46, + 43, + 36, + 9, + 5, + 12 ] ], [ [ 0, - 52, - 16, - 12, - 54, - 7 + 10, + 49, + 23, + 62, + 44 ], [ - 42, - 25, - 51, - 61, - 35, - 58 + 28, + 0, + 36, + 26, + 47, + 52 ], [ - 51, - 42, - 19, - 57, - 28, - 8 + 30, + 4, + 16, + 48, + 40, + 10 ], [ - 49, - 62, - 5, - 2, - 46, - 21 + 61, + 32, + 26, + 16, + 33, + 62 ], [ - 3, - 41, - 53, - 25, + 30, 39, - 37 + 53, + 5, + 57, + 20 ], [ - 45, - 15, + 5, 37, - 48, - 19, - 60 + 61, + 15, + 25, + 6 ], [ - 14, 15, - 47, 17, 24, - 35 + 60, + 49, + 62 + ], + [ + 34, + 39, + 61, + 0, + 58, + 40 ], [ - 3, - 52, - 63, 16, - 28, - 47 + 39, + 36, + 51, + 2, + 29 ], [ - 32, - 27, - 15, + 9, + 11, + 41, + 31, + 56, + 52 + ], + [ + 10, + 48, 24, + 45, 62, - 23 + 51 ], [ - 62, - 25, - 51, - 53, - 0, - 20 + 11, + 38, + 36, + 37, + 6, + 42 ], [ - 8, - 19, + 51, 50, - 16, - 32, - 22 - ], - [ - 13, - 36, - 42, - 49, - 60, - 44 - ], - [ - 30, - 27, - 53, - 56, - 0, - 10 - ], - [ - 22, - 7, - 14, - 26, - 32, - 17 - ], - [ - 60, - 7, - 26, - 2, - 58, - 13 - ], - [ - 55, - 51, - 17, - 60, - 62, - 47 - ], - [ - 53, - 16, - 18, - 4, - 50, - 5 - ], - [ - 28, - 39, - 41, - 1, - 55, - 18 - ], - [ - 59, - 49, - 16, - 23, - 42, - 11 - ], - [ - 17, - 30, - 46, - 55, - 25, - 0 - ], - [ - 54, - 9, - 45, - 0, - 6, - 19 - ], - [ - 51, - 9, - 22, - 23, - 16, - 25 - ], - [ - 62, - 50, - 43, - 51, - 55, - 27 - ], - [ - 13, - 43, - 27, - 1, - 14, - 52 - ], - [ - 61, - 26, - 1, - 17, - 32, - 63 - ], - [ - 37, - 46, - 63, - 20, - 24, - 4 - ], - [ - 63, - 11, - 12, - 61, - 31, - 22 - ] - ], - [ - [ - 49, - 54, - 56, - 11, - 38, - 40 - ], - [ - 53, - 46, - 49, - 38, - 57, - 17 - ], - [ - 48, - 9, - 31, - 12, - 6, - 56 - ], - [ - 49, - 62, - 23, - 5, - 12, - 63 - ], - [ - 36, - 26, - 38, - 7, - 20, - 23 - ], - [ - 37, - 33, - 41, - 58, - 57, - 32 - ], - [ - 10, - 14, - 15, - 31, - 8, - 43 - ], - [ - 16, - 52, - 3, - 2, - 34, - 14 - ], - [ - 15, - 32, - 35, - 27, - 62, - 54 - ], - [ - 51, - 62, - 25, - 53, - 10, - 20 - ], - [ - 19, - 8, - 22, - 50, - 1, - 5 - ], - [ - 13, - 49, - 36, - 60, - 42, - 20 - ], - [ - 27, - 30, - 53, - 54, - 26, - 43 - ], - [ - 14, - 32, - 26, - 22, - 7, - 17 - ], - [ - 26, - 60, - 7, - 2, - 52, - 54 - ], - [ - 55, - 17, - 51, - 47, - 62, - 26 - ], - [ - 16, - 18, - 44, - 4, - 53, - 50 - ], - [ - 1, - 47, - 39, - 45, - 28, - 56 - ], - [ - 23, - 16, - 55, - 49, - 8, - 32 - ], - [ - 17, - 55, - 30, - 62, - 31, - 23 - ], - [ - 9, - 54, - 0, - 44, - 14, - 56 - ], - [ - 23, - 9, - 51, - 22, - 31, - 50 - ], - [ - 50, - 27, - 14, - 51, - 18, - 8 - ], - [ - 12, - 13, - 33, - 1, - 5, - 43 - ], - [ - 26, - 32, - 1, - 50, - 37, - 57 - ], - [ - 37, - 47, - 63, - 46, - 5, - 4 - ], - [ - 63, - 11, - 12, - 19, - 33, - 61 - ] - ], - [ - [ - 49, - 54, - 40, - 56, - 11, - 3 - ], - [ - 37, - 15, - 12, - 33, - 59, - 17 - ], - [ - 38, - 49, - 14, - 46, - 35, - 59 - ], - [ - 25, - 20, - 39, - 62, - 49, - 32 - ], - [ - 26, - 51, - 16, - 36, - 8, - 7 - ], - [ - 37, - 41, - 51, - 33, - 32, - 60 - ], - [ - 10, - 14, - 59, - 8, - 15, - 40 - ], - [ - 16, - 52, - 19, - 61, - 32, - 3 - ], - [ - 32, - 15, - 27, - 24, - 62, - 35 - ], - [ - 51, - 25, - 62, - 0, - 49, - 54 - ], - [ - 8, - 50, - 19, - 48, - 16, - 54 - ], - [ - 13, - 49, - 36, - 42, - 11, - 60 - ], - [ - 53, - 27, - 7, - 26, - 30, - 21 - ], - [ - 14, - 22, - 26, - 37, - 32, - 7 - ], - [ - 26, - 40, - 60, - 2, - 52, - 7 - ], - [ - 55, - 51, - 17, - 46, - 13, - 62 - ], - [ - 38, - 16, - 53, - 44, - 4, - 18 - ], - [ - 39, - 1, - 4, - 14, - 56, - 57 - ], - [ - 55, - 23, - 32, - 14, - 13, - 16 - ], - [ - 55, - 17, - 2, - 30, - 62, - 12 - ], - [ - 13, - 54, - 0, - 62, - 61, - 25 - ], - [ - 9, - 51, - 5, - 22, - 19, - 23 - ], - [ - 50, - 51, - 7, - 19, - 48, - 53 - ], - [ - 12, - 1, - 5, - 43, - 61, - 13 - ], - [ - 26, - 32, - 30, - 37, - 34, - 57 - ], - [ - 37, - 63, - 46, - 4, - 47, - 8 - ], - [ - 63, - 12, - 11, - 31, - 33, - 61 - ] - ], - [ - [ - 47, - 34, - 30, - 25, - 31, - 3 - ], - [ - 15, - 24, - 46, - 21, - 8, - 6 - ], - [ - 34, - 21, - 18, - 62, - 28, - 55 - ], - [ - 35, - 32, - 20, - 39, - 59, - 54 - ], - [ - 26, - 27, - 15, - 48, - 60, - 47 - ], - [ - 37, - 8, - 50, - 18, - 54, - 61 - ], - [ - 35, - 31, - 8, - 24, - 14, - 15 - ], - [ - 16, - 52, - 34, - 29, - 48, - 36 - ], - [ - 11, - 32, - 62, - 27, - 46, - 26 - ], - [ - 61, - 62, - 25, - 56, - 46, - 53 - ], - [ - 56, - 50, - 63, - 3, - 45, - 28 - ], - [ - 11, - 36, - 5, - 60, - 35, - 50 - ], - [ - 21, - 26, - 41, - 51, - 46, - 53 - ], - [ - 14, - 22, - 33, - 19, - 41, - 16 - ], - [ - 2, - 52, - 34, - 60, - 21, - 49 - ], - [ - 59, - 55, - 29, - 8, - 61, - 22 - ], - [ - 51, - 44, - 2, - 59, - 47, - 53 - ], - [ - 39, - 25, - 18, - 12, - 51, - 56 - ], - [ - 34, - 53, - 32, - 12, - 9, - 38 - ], - [ - 30, - 53, - 56, - 7, - 40, - 62 - ], - [ - 40, - 49, - 28, - 14, - 23, - 55 - ], - [ - 15, - 48, - 40, - 47, - 9, - 1 - ], - [ - 50, - 41, - 25, - 53, - 18, - 0 - ], - [ - 22, - 1, - 59, - 3, - 55, - 8 - ], - [ - 1, - 53, - 32, - 26, - 47, - 3 - ], - [ - 4, - 33, - 28, - 37, - 55, - 54 - ], - [ - 30, - 22, - 57, - 12, - 33, - 63 - ] - ], - [ - [ - 16, - 11, - 0, - 31, - 46, - 22 - ], - [ - 49, - 13, - 5, - 11, - 31, - 14 - ], - [ - 36, - 13, - 56, - 27, - 46, - 3 - ], - [ - 24, - 44, - 62, - 29, - 15, - 13 - ], - [ - 17, - 2, - 50, - 8, - 45, - 1 - ], - [ - 8, - 7, - 49, - 0, - 62, - 13 - ], - [ - 35, - 61, - 58, - 23, - 36, - 0 - ], - [ - 16, - 48, - 42, - 4, - 32, - 29 - ], - [ - 63, - 18, - 32, - 45, - 4, - 34 - ], - [ - 57, - 62, - 54, - 27, - 25, - 53 - ], - [ - 1, - 59, - 60, - 29, - 22, - 14 - ], - [ - 31, - 36, - 11, - 14, - 20, - 10 - ], - [ - 34, - 2, - 19, - 14, - 8, - 37 - ], - [ - 57, - 22, - 40, - 14, - 62, - 48 - ], - [ - 44, - 60, - 7, - 14, - 45, - 2 - ], - [ - 8, - 55, - 6, - 25, - 50, - 59 - ], - [ - 13, - 47, - 42, - 23, - 61, - 39 - ], - [ - 23, - 25, - 4, - 14, - 46, - 60 - ], - [ - 8, - 45, - 32, - 53, - 10, - 54 - ], - [ - 15, - 38, - 53, - 55, - 30, - 7 - ], - [ - 41, - 14, - 28, - 5, - 58, - 27 - ], - [ - 11, - 41, - 57, - 1, - 10, - 47 - ], - [ - 50, - 0, - 51, - 53, - 34, - 45 - ], - [ - 1, - 14, - 55, - 8, - 25, - 3 - ], - [ - 11, - 49, - 1, - 9, - 0, - 3 - ], - [ - 50, - 51, - 6, - 42, - 4, - 54 - ], - [ - 17, - 37, - 31, - 5, - 40, - 36 - ] - ], - [ - [ - 22, - 53, - 47, - 6, - 57, - 21 - ], - [ - 27, - 11, - 14, - 6, - 57, - 16 - ], - [ - 1, - 11, - 29, - 26, - 41, - 17 - ], - [ - 14, - 22, - 38, - 31, - 29, - 36 - ], - [ - 14, - 59, - 29, - 61, - 45, - 52 - ], - [ - 30, - 8, - 0, - 21, - 47, - 58 - ], - [ - 58, - 35, - 4, - 61, - 23, - 36 - ], - [ - 42, - 20, - 48, - 16, - 9, - 4 - ], - [ - 47, - 29, - 4, - 18, - 63, - 32 - ], - [ - 19, - 54, - 62, - 53, - 57, - 29 - ], - [ - 1, - 60, - 14, - 59, - 4, - 29 - ], - [ - 36, - 0, - 47, - 3, - 31, - 8 - ], - [ - 2, - 19, - 36, - 8, - 20, - 37 - ], - [ - 57, - 22, - 40, - 31, - 49, - 14 - ], - [ - 44, - 37, - 2, - 5, - 60, - 21 - ], - [ - 6, - 43, - 24, - 5, - 2, - 59 - ], - [ - 13, - 19, - 61, - 47, - 50, - 39 - ], - [ - 58, - 14, - 28, - 4, - 11, - 22 - ], - [ - 35, - 32, - 46, - 10, - 31, - 45 - ], - [ - 15, - 13, - 55, - 45, - 18, - 63 - ], - [ - 15, - 27, - 28, - 14, - 5, - 60 - ], - [ - 57, - 41, - 47, - 19, - 36, - 10 - ], - [ - 34, - 10, - 53, - 55, - 22, - 19 - ], - [ - 38, - 55, - 39, - 27, - 3, - 25 - ], - [ - 11, - 39, - 0, - 9, - 3, - 49 - ], - [ - 51, - 6, - 43, - 18, - 50, - 53 - ], - [ - 55, - 43, - 9, - 36, - 40, - 5 - ] - ], - [ - [ - 18, - 9, - 1, - 36, - 61, - 44 - ], - [ - 56, - 34, - 19, - 42, - 3, - 5 - ], - [ - 39, - 20, - 15, - 60, - 46, - 32 - ], - [ - 60, - 22, - 31, - 27, - 14, - 19 - ], - [ - 59, - 58, - 10, - 7, - 46, - 18 - ], - [ - 43, - 2, - 57, - 62, - 11, - 30 - ], - [ - 54, - 19, - 9, - 21, - 48, - 56 - ], - [ - 46, - 24, - 7, - 14, - 3, - 8 - ], - [ - 47, - 0, - 4, - 18, - 31, - 29 - ], - [ - 54, - 62, - 47, - 38, - 4, - 32 - ], - [ - 1, - 14, - 15, - 22, - 59, - 38 - ], - [ - 16, - 36, - 42, - 55, - 15, - 18 - ], - [ - 49, - 8, - 20, - 14, - 0, - 33 - ], - [ - 18, - 39, - 25, - 2, - 62, - 22 - ], - [ - 62, - 5, - 58, - 37, - 7, - 32 - ], - [ - 43, - 5, - 42, - 63, - 55, - 37 - ], - [ - 47, - 33, - 15, - 63, - 50, - 12 - ], - [ - 60, - 0, - 7, - 16, - 32, - 13 - ], - [ - 12, - 39, - 32, - 61, - 16, - 45 - ], - [ - 52, - 34, - 15, - 62, - 18, - 30 - ], - [ - 28, - 26, - 46, - 40, - 6, - 14 - ], - [ - 1, - 19, - 17, - 20, - 4, - 21 - ], - [ - 41, - 40, - 4, - 53, - 55, - 19 - ], - [ - 25, - 38, - 27, - 34, - 52, - 46 - ], - [ - 11, - 29, - 52, - 44, - 53, - 13 - ], - [ - 50, - 51, - 41, - 16, - 4, - 15 - ], - [ - 19, - 6, - 23, - 36, - 60, - 0 - ] - ], - [ - [ - 17, - 10, - 57, - 27, - 5, - 54 - ], - [ - 33, - 9, - 43, - 40, - 56, - 11 - ], - [ - 63, - 1, - 35, - 43, - 10, - 27 - ], - [ - 51, - 47, - 20, - 21, - 28, - 61 - ], - [ - 25, - 11, - 58, - 23, - 55, - 46 - ], - [ - 43, - 10, - 12, - 2, - 62, - 30 - ], - [ - 48, - 19, - 21, - 8, - 7, - 54 - ], - [ - 14, - 7, - 24, - 8, - 46, - 2 - ], - [ - 4, - 47, - 37, - 0, - 44, - 27 - ], - [ - 54, - 38, - 62, - 47, - 15, - 14 - ], - [ - 1, - 46, - 15, - 22, - 51, - 38 - ], - [ - 36, - 16, - 42, - 55, - 24, - 37 - ], - [ - 49, - 10, - 0, - 3, - 43, - 8 - ], - [ - 39, - 58, - 0, - 62, - 22, - 25 - ], - [ - 58, - 38, - 7, - 55, - 62, - 56 - ], - [ - 19, - 42, - 55, - 43, - 11, - 37 - ], - [ - 9, - 47, - 43, - 52, - 18, - 50 - ], - [ - 31, - 41, - 32, - 25, - 20, - 13 - ], - [ - 12, - 32, - 61, - 3, - 21, - 43 - ], - [ - 36, - 13, - 40, - 7, - 62, - 16 - ], - [ - 14, - 53, - 50, - 47, - 51, - 1 - ], - [ - 1, - 38, - 19, - 18, - 30, - 16 - ], - [ - 0, - 19, - 51, - 18, - 52, - 15 - ], - [ - 8, - 52, - 27, - 34, - 38, - 3 - ], - [ - 27, - 53, - 59, - 9, - 40, - 4 - ], - [ - 37, - 3, - 26, - 48, - 8, - 16 - ], - [ - 46, - 18, - 11, - 40, - 33, - 44 - ] - ], - [ - [ - 48, - 62, - 61, - 50, - 26, - 59 - ], - [ - 3, - 45, - 40, - 35, - 29, - 54 - ], - [ - 56, - 31, - 23, - 28, - 2, - 53 - ], - [ - 62, - 49, - 20, - 61, - 6, - 41 - ], - [ - 18, - 25, - 50, - 0, - 14, - 57 - ], - [ - 58, - 4, - 10, - 43, - 56, - 20 - ], - [ - 35, - 15, - 25, - 24, - 3, - 7 - ], - [ - 14, - 23, - 8, - 12, - 57, - 24 - ], - [ - 29, - 17, - 35, - 44, - 24, - 27 - ], - [ - 62, - 15, - 38, - 20, - 58, - 21 - ], - [ - 19, - 46, - 1, - 26, - 63, - 22 - ], - [ - 36, - 60, - 16, - 42, - 55, - 11 - ], - [ - 17, - 7, - 14, - 26, - 16, - 49 - ], - [ - 45, - 47, - 22, - 0, - 62, - 58 - ], - [ - 58, - 38, - 48, - 49, - 63, - 2 - ], - [ - 55, - 0, - 1, - 37, - 30, - 10 - ], - [ - 12, - 43, - 21, - 9, - 47, - 23 - ], - [ - 32, - 57, - 42, - 25, - 43, - 63 - ], - [ - 3, - 32, - 49, - 61, - 21, - 12 - ], - [ - 5, - 36, - 22, - 16, - 62, - 42 - ], - [ - 53, - 7, - 46, - 61, - 14, - 52 - ], - [ - 55, - 30, - 3, - 5, - 53, - 31 - ], - [ - 0, - 44, - 15, - 18, - 19, - 28 - ], - [ - 8, - 52, - 51, - 11, - 4, - 29 - ], - [ - 27, - 40, - 4, - 9, - 35, - 39 - ], - [ - 14, - 26, - 3, - 48, - 16, - 21 - ], - [ - 60, - 54, - 35, - 20, - 53, - 12 - ] - ], - [ - [ - 21, - 50, - 29, - 41, - 34, - 60 - ], - [ - 28, - 51, - 60, - 33, - 14, - 45 - ], - [ - 31, - 2, - 46, - 33, - 24, - 49 - ], - [ - 42, - 3, - 18, - 62, - 39, - 49 - ], - [ - 25, - 15, - 62, - 27, - 12, - 11 - ], - [ - 44, - 50, - 36, - 57, - 55, - 41 - ], - [ - 41, - 37, - 22, - 15, - 2, - 40 - ], - [ - 36, - 62, - 53, - 30, - 14, - 57 - ], - [ - 16, - 58, - 2, - 29, - 4, - 3 - ], - [ - 41, - 38, - 26, - 16, - 45, - 46 - ], - [ - 45, - 46, - 32, - 41, - 56, - 26 - ], - [ - 17, - 53, - 21, - 11, - 36, - 35 - ], - [ - 11, - 16, - 28, - 14, - 51, - 61 - ], - [ - 9, - 35, - 33, - 22, - 52, - 62 - ], - [ - 58, - 50, - 63, - 30, - 7, - 27 - ], - [ - 55, - 3, - 8, - 41, - 63, - 37 - ], - [ - 3, - 51, - 46, - 32, - 15, - 6 - ], - [ - 32, - 12, - 10, - 25, - 5, - 49 - ], - [ - 34, - 2, - 37, - 61, - 39, - 63 - ], - [ - 42, - 22, - 27, - 53, - 11, - 56 - ], - [ - 53, - 12, - 0, - 47, - 61, - 1 - ], - [ - 39, - 45, - 53, - 17, - 48, - 14 - ], - [ - 6, - 0, - 4, - 53, - 25, - 11 - ], - [ - 51, - 11, - 1, - 63, - 54, - 45 - ], - [ - 40, - 56, - 37, - 53, - 5, - 35 - ], - [ - 59, - 28, - 41, - 10, - 1, - 45 - ], - [ - 27, - 30, - 28, - 24, - 32, - 57 - ] - ], - [ - [ - 24, - 56, - 6, - 0, - 19, - 45 - ], - [ - 11, - 57, - 59, - 25, - 46, - 30 - ], - [ - 11, - 26, - 37, - 29, - 14, - 52 - ], - [ - 3, - 32, - 7, - 38, - 36, - 24 - ], - [ - 61, - 2, - 24, - 14, - 51, - 44 - ], - [ - 20, - 47, - 0, - 63, - 30, - 58 - ], - [ - 4, - 36, - 29, - 58, - 16, - 3 - ], - [ - 20, - 0, - 45, - 14, - 28, - 44 - ], - [ - 29, - 56, - 47, - 35, - 16, - 4 - ], - [ - 33, - 61, - 55, - 41, - 51, - 38 - ], - [ - 58, - 1, - 38, - 14, - 4, - 19 - ], - [ - 0, - 36, - 14, - 18, - 52, - 42 - ], - [ - 29, - 36, - 45, - 25, - 8, - 6 - ], - [ - 6, - 57, - 50, - 40, - 58, - 61 - ], - [ - 44, - 58, - 29, - 19, - 61, - 56 - ], - [ - 23, - 18, - 28, - 55, - 5, - 37 - ], - [ - 13, - 9, - 19, - 43, - 37, - 3 - ], - [ - 32, - 22, - 63, - 14, - 57, - 41 - ], - [ - 10, - 61, - 3, - 1, - 19, - 32 - ], - [ - 16, - 55, - 10, - 41, - 59, - 22 - ], - [ - 53, - 7, - 29, - 38, - 27, - 46 - ], - [ - 24, - 47, - 18, - 53, - 39, - 30 - ], - [ - 0, - 33, - 19, - 5, - 51, - 17 - ], - [ - 51, - 8, - 11, - 45, - 44, - 41 - ], - [ - 40, - 4, - 23, - 11, - 27, - 19 - ], - [ - 16, - 18, - 3, - 48, - 51, - 21 - ], - [ - 43, - 46, - 60, - 19, - 53, - 12 - ] - ], - [ - [ - 48, - 62, - 61, - 30, - 50, - 52 - ], - [ - 45, - 3, - 35, - 29, - 54, - 2 - ], - [ - 56, - 31, - 53, - 23, - 49, - 28 - ], - [ - 60, - 57, - 14, - 46, - 41, - 48 - ], - [ - 18, - 61, - 59, - 14, - 44, - 32 - ], - [ - 45, - 58, - 47, - 20, - 4, - 30 - ], - [ - 54, - 13, - 25, - 36, - 26, - 47 - ], - [ - 20, - 12, - 0, - 47, - 30, - 45 - ], - [ - 56, - 29, - 47, - 17, - 35, - 16 - ], - [ - 33, - 61, - 55, - 11, - 38, - 48 - ], - [ - 58, - 19, - 14, - 1, - 38, - 36 - ], - [ - 14, - 36, - 0, - 60, - 11, - 52 - ], - [ - 29, - 44, - 7, - 36, - 16, - 45 - ], - [ - 6, - 47, - 50, - 33, - 42, - 62 - ], - [ - 44, - 58, - 61, - 38, - 29, - 56 - ], - [ - 23, - 55, - 18, - 0, - 57, - 37 - ], - [ - 9, - 12, - 43, - 19, - 13, - 6 - ], - [ - 32, - 22, - 63, - 57, - 42, - 29 - ], - [ - 3, - 61, - 1, - 10, - 49, - 32 - ], - [ - 5, - 16, - 55, - 36, - 22, - 59 - ], - [ - 53, - 7, - 46, - 29, - 9, - 14 - ], - [ - 24, - 30, - 18, - 39, - 55, - 53 - ], - [ - 33, - 0, - 19, - 44, - 51, - 5 - ], - [ - 51, - 8, - 53, - 41, - 4, - 11 - ], - [ - 40, - 4, - 27, - 19, - 23, - 16 - ], - [ - 16, - 14, - 48, - 3, - 21, - 26 - ], - [ - 60, - 54, - 35, - 53, - 12, - 43 - ] - ], - [ - [ - 19, - 41, - 8, - 7, - 13, - 2 - ], - [ - 48, - 46, - 62, - 29, - 5, - 41 - ], - [ - 12, - 5, - 59, - 3, - 58, - 49 - ], - [ - 60, - 3, - 42, - 39, - 14, - 18 - ], - [ - 42, - 12, - 27, - 11, - 25, - 19 - ], - [ - 50, - 36, - 44, - 26, - 33, - 37 - ], - [ - 41, - 54, - 22, - 52, - 37, - 35 - ], - [ - 62, - 30, - 36, - 53, - 10, - 14 - ], - [ - 2, - 16, - 58, - 29, - 7, - 41 - ], - [ - 18, - 32, - 45, - 16, - 22, - 38 - ], - [ - 45, - 56, - 41, - 10, - 3, - 46 - ], - [ - 50, - 21, - 36, - 35, - 53, - 12 - ], - [ - 11, - 28, - 16, - 41, - 39, - 46 - ], - [ - 16, - 9, - 33, - 38, - 28, - 19 - ], - [ - 58, - 50, - 63, - 62, - 27, - 52 - ], - [ - 55, - 59, - 13, - 8, - 43, - 3 - ], - [ - 3, - 51, - 15, - 46, - 47, - 57 - ], - [ - 32, - 30, - 12, - 10, - 25, - 18 - ], - [ - 34, - 2, - 61, - 27, - 53, - 59 - ], - [ - 42, - 22, - 56, - 53, - 44, - 34 - ], - [ - 53, - 12, - 49, - 41, - 44, - 8 - ], - [ - 45, - 1, - 48, - 47, - 16, - 17 - ], - [ - 4, - 0, - 53, - 25, - 24, - 11 - ], - [ - 1, - 11, - 44, - 45, - 34, - 51 - ], - [ - 40, - 5, - 53, - 6, - 22, - 18 - ], - [ - 28, - 10, - 1, - 3, - 15, - 41 - ], - [ - 30, - 27, - 24, - 57, - 32, - 16 - ] - ], - [ - [ - 24, - 56, - 6, - 0, - 19, - 45 - ], - [ - 11, - 57, - 59, - 46, - 25, - 30 - ], - [ - 26, - 11, - 37, - 14, - 29, - 49 - ], - [ - 38, - 36, - 3, - 24, - 18, - 20 - ], - [ - 61, - 51, - 14, - 2, - 24, - 1 - ], - [ - 20, - 0, - 47, - 30, - 8, - 35 - ], - [ - 4, - 58, - 36, - 54, - 29, - 12 - ], - [ - 20, - 58, - 44, - 28, - 45, - 9 - ], - [ - 56, - 47, - 10, - 29, - 35, - 27 - ], - [ - 61, - 33, - 55, - 54, - 4, - 36 - ], - [ - 58, - 1, - 14, - 4, - 38, - 52 - ], - [ - 14, - 0, - 36, - 63, - 15, - 52 - ], - [ - 29, - 36, - 44, - 8, - 16, - 2 - ], - [ - 6, - 40, - 27, - 57, - 50, - 42 - ], - [ - 19, - 44, - 58, - 61, - 37, - 38 - ], - [ - 23, - 18, - 17, - 57, - 13, - 40 - ], - [ - 13, - 9, - 19, - 37, - 50, - 15 - ], - [ - 32, - 14, - 57, - 58, - 29, - 22 - ], - [ - 61, - 10, - 1, - 3, - 14, - 59 - ], - [ - 55, - 16, - 34, - 18, - 22, - 49 - ], - [ - 53, - 27, - 38, - 28, - 23, - 44 - ], - [ - 24, - 47, - 18, - 62, - 41, - 30 - ], - [ - 33, - 51, - 19, - 5, - 0, - 31 - ], - [ - 51, - 8, - 25, - 53, - 27, - 16 - ], - [ - 40, - 11, - 27, - 4, - 23, - 19 - ], - [ - 16, - 18, - 51, - 48, - 3, - 47 - ], - [ - 46, - 43, - 36, - 9, - 5, - 12 - ] - ], - [ - [ - 37, - 10, - 46, - 60, - 61, - 59 - ], - [ - 35, - 53, - 34, - 43, - 19, - 57 - ], - [ - 49, - 56, - 45, - 30, - 6, - 12 - ], - [ - 60, - 27, - 14, - 48, - 46, - 57 - ], - [ - 61, - 59, - 14, - 41, - 16, - 1 - ], - [ - 45, - 4, - 3, - 58, - 24, - 47 - ], - [ - 54, - 13, - 9, - 43, - 16, - 26 - ], - [ - 47, - 23, - 12, - 20, - 63, - 30 - ], - [ - 23, - 44, - 56, - 29, - 47, - 17 - ], - [ - 33, - 60, - 61, - 48, - 41, - 14 - ], - [ - 58, - 63, - 19, - 11, - 9, - 38 - ], - [ - 60, - 63, - 0, - 36, - 15, - 9 - ], - [ - 29, - 36, - 30, - 59, - 11, - 27 - ], - [ - 6, - 7, - 47, - 62, - 50, - 57 - ], - [ - 27, - 58, - 19, - 46, - 29, - 56 - ], - [ - 29, - 60, - 56, - 55, - 23, - 26 - ], - [ - 53, - 59, - 6, - 9, - 16, - 43 - ], - [ - 41, - 32, - 57, - 63, - 18, - 37 - ], - [ - 42, - 61, - 3, - 10, - 34, - 59 - ], - [ - 4, - 43, - 17, - 16, - 52, - 60 - ], - [ - 45, - 53, - 61, - 56, - 16, - 7 - ], - [ - 55, - 9, - 18, - 61, - 45, - 3 - ], - [ - 60, - 47, - 53, - 33, - 12, - 27 - ], - [ - 43, - 51, - 11, - 8, - 45, - 63 - ], - [ - 6, - 40, - 15, - 27, - 26, - 23 - ], - [ - 49, - 14, - 9, - 21, - 58, - 12 - ], - [ - 63, - 24, - 60, - 31, - 12, - 34 - ] - ], - [ - [ - 16, - 13, - 4, - 44, - 23, - 46 - ], - [ - 16, - 50, - 9, - 13, - 23, - 36 - ], - [ - 11, - 35, - 21, - 7, - 59, - 8 - ], - [ - 1, - 3, - 25, - 15, - 60, - 39 - ], - [ - 54, - 61, - 31, - 35, - 55, - 1 - ], - [ - 51, - 52, - 46, - 15, - 4, - 45 - ], - [ - 60, - 54, - 59, - 44, - 10, - 7 - ], - [ - 12, - 22, - 14, - 47, - 0, - 30 - ], - [ - 42, - 29, - 23, - 56, - 47, - 33 - ], - [ - 33, - 61, - 20, - 60, - 0, - 49 - ], - [ - 35, - 58, - 63, - 14, - 51, - 24 - ], - [ - 29, - 33, - 36, - 60, - 0, - 49 - ], - [ - 29, - 17, - 30, - 12, - 31, - 36 - ], - [ - 6, - 0, - 61, - 50, - 48, - 3 - ], - [ - 8, - 6, - 58, - 37, - 29, - 19 - ], - [ - 60, - 39, - 27, - 19, - 1, - 57 - ], - [ - 56, - 9, - 30, - 6, - 10, - 43 - ], - [ - 32, - 20, - 13, - 57, - 63, - 49 - ], - [ - 8, - 42, - 21, - 61, - 37, - 4 - ], - [ - 45, - 49, - 16, - 13, - 2, - 58 - ], - [ - 53, - 29, - 14, - 50, - 61, - 3 - ], - [ - 37, - 57, - 27, - 54, - 46, - 9 - ], - [ - 52, - 19, - 22, - 0, - 18, - 5 - ], - [ - 14, - 49, - 30, - 33, - 53, - 34 - ], - [ - 13, - 9, - 4, - 40, - 23, - 39 - ], - [ - 27, - 43, - 47, - 36, - 49, - 3 - ], - [ - 59, - 43, - 40, - 28, - 0, - 33 - ] - ], - [ - [ - 48, - 42, - 63, - 50, - 34, - 38 - ], - [ - 3, - 40, - 61, - 62, - 6, - 2 - ], - [ - 39, - 7, - 36, - 6, - 45, - 40 - ], - [ - 41, - 35, - 46, - 13, - 63, - 56 - ], - [ - 6, - 1, - 54, - 37, - 38, - 34 - ], - [ - 59, - 46, - 51, - 31, - 4, - 52 - ], - [ - 60, - 44, - 11, - 54, - 4, - 24 - ], - [ - 12, - 0, - 2, - 63, - 50, - 47 - ], - [ - 33, - 42, - 29, - 23, - 16, - 56 - ], - [ - 20, - 61, - 33, - 60, - 53, - 0 - ], - [ - 35, - 58, - 63, - 9, - 8, - 19 - ], - [ - 59, - 48, - 36, - 60, - 10, - 14 - ], - [ - 29, - 44, - 7, - 17, - 36, - 12 - ], - [ - 47, - 27, - 6, - 62, - 42, - 48 - ], - [ - 18, - 58, - 49, - 46, - 42, - 44 - ], - [ - 60, - 34, - 27, - 18, - 23, - 55 - ], - [ - 43, - 40, - 9, - 50, - 18, - 45 - ], - [ - 32, - 57, - 48, - 42, - 29, - 39 - ], - [ - 42, - 61, - 49, - 3, - 32, - 1 - ], - [ - 23, - 37, - 1, - 16, - 36, - 39 - ], - [ - 53, - 21, - 7, - 61, - 50, - 31 - ], - [ - 8, - 60, - 18, - 24, - 9, - 30 - ], - [ - 51, - 33, - 28, - 5, - 44, - 8 - ], - [ - 51, - 52, - 8, - 4, - 41, - 45 - ], - [ - 40, - 4, - 27, - 9, - 60, - 19 - ], - [ - 3, - 61, - 16, - 26, - 48, - 12 - ], - [ - 54, - 61, - 35, - 1, - 53, - 43 - ] - ], - [ - [ - 62, - 28, - 1, - 42, - 8, - 55 - ], - [ - 18, - 12, - 8, - 41, - 40, - 31 - ], - [ - 12, - 6, - 50, - 4, - 23, - 45 - ], - [ - 43, - 35, - 8, - 20, - 42, - 46 - ], - [ - 39, - 41, - 29, - 22, - 3, - 56 - ], - [ - 61, - 45, - 46, - 48, - 28, - 51 - ], - [ - 44, - 4, - 11, - 25, - 54, - 59 - ], - [ - 12, - 33, - 56, - 52, - 30, - 17 - ], - [ - 55, - 29, - 17, - 42, - 23, - 14 - ], - [ - 60, - 12, - 18, - 61, - 33, - 28 - ], - [ - 35, - 58, - 37, - 63, - 6, - 27 - ], - [ - 48, - 59, - 10, - 36, - 58, - 60 - ], - [ - 17, - 7, - 28, - 31, - 29, - 27 - ], - [ - 47, - 42, - 50, - 6, - 8, - 14 - ], - [ - 39, - 58, - 56, - 37, - 18, - 59 - ], - [ - 60, - 18, - 57, - 9, - 55, - 23 - ], - [ - 43, - 63, - 18, - 60, - 19, - 22 - ], - [ - 1, - 32, - 42, - 57, - 35, - 63 - ], - [ - 42, - 61, - 3, - 32, - 1, - 50 - ], - [ - 37, - 36, - 10, - 23, - 16, - 57 - ], - [ - 53, - 61, - 7, - 57, - 21, - 23 - ], - [ - 9, - 39, - 30, - 18, - 14, - 17 - ], - [ - 33, - 44, - 8, - 5, - 0, - 19 - ], - [ - 51, - 53, - 49, - 4, - 52, - 41 - ], - [ - 40, - 4, - 27, - 6, - 9, - 16 - ], - [ - 3, - 16, - 48, - 26, - 12, - 4 - ], - [ - 61, - 14, - 12, - 54, - 35, - 53 - ] - ], - [ - [ - 47, - 23, - 63, - 11, - 61, - 55 - ], - [ - 17, - 44, - 28, - 39, - 47, - 27 - ], - [ - 34, - 53, - 50, - 38, - 29, - 5 - ], - [ - 11, - 10, - 17, - 52, - 47, - 42 - ], - [ - 15, - 41, - 27, - 20, - 12, - 6 - ], - [ - 34, - 44, - 50, - 39, - 36, - 61 - ], - [ - 37, - 41, - 52, - 29, - 46, - 47 - ], - [ - 62, - 36, - 34, - 30, - 39, - 22 - ], - [ - 62, - 16, - 58, - 5, - 2, - 8 - ], - [ - 32, - 41, - 56, - 12, - 46, - 8 - ], - [ - 10, - 35, - 45, - 41, - 3, - 56 - ], - [ - 50, - 48, - 35, - 53, - 36, - 12 - ], - [ - 39, - 11, - 46, - 7, - 23, - 51 - ], - [ - 9, - 47, - 19, - 22, - 52, - 34 - ], - [ - 35, - 18, - 56, - 50, - 3, - 23 - ], - [ - 3, - 60, - 38, - 36, - 9, - 35 - ], - [ - 46, - 28, - 32, - 5, - 43, - 56 - ], - [ - 30, - 32, - 57, - 42, - 52, - 19 - ], - [ - 2, - 32, - 34, - 61, - 14, - 42 - ], - [ - 42, - 37, - 20, - 50, - 9, - 48 - ], - [ - 53, - 7, - 56, - 25, - 60, - 13 - ], - [ - 17, - 39, - 14, - 53, - 30, - 25 - ], - [ - 5, - 40, - 6, - 33, - 29, - 25 - ], - [ - 51, - 4, - 11, - 58, - 57, - 28 - ], - [ - 40, - 37, - 4, - 44, - 8, - 48 - ], - [ - 3, - 48, - 26, - 9, - 12, - 41 - ], - [ - 47, - 61, - 26, - 24, - 20, - 53 - ] - ], - [ - [ - 31, - 43, - 41, - 47, - 11, - 25 - ], - [ - 50, - 25, - 31, - 40, - 24, - 46 - ], - [ - 23, - 9, - 62, - 15, - 20, - 53 - ], - [ - 4, - 47, - 44, - 58, - 48, - 25 - ], - [ - 2, - 19, - 12, - 52, - 0, - 40 - ], - [ - 49, - 15, - 24, - 34, - 60, - 42 - ], - [ - 12, - 46, - 17, - 29, - 41, - 3 - ], - [ - 39, - 60, - 44, - 41, - 33, - 36 - ], - [ - 21, - 60, - 16, - 44, - 51, - 57 - ], - [ - 24, - 41, - 12, - 33, - 13, - 21 - ], - [ - 43, - 62, - 3, - 12, - 28, - 45 - ], - [ - 58, - 19, - 39, - 17, - 49, - 42 - ], - [ - 25, - 54, - 4, - 7, - 11, - 39 - ], - [ - 34, - 35, - 4, - 42, - 62, - 19 - ], - [ - 43, - 41, - 42, - 35, - 40, - 32 - ], - [ - 21, - 63, - 3, - 17, - 20, - 50 - ], - [ - 58, - 46, - 44, - 1, - 25, - 20 - ], - [ - 26, - 32, - 16, - 25, - 46, - 41 - ], - [ - 37, - 63, - 61, - 28, - 24, - 56 - ], - [ - 33, - 42, - 40, - 37, - 48, - 50 - ], - [ - 11, - 53, - 25, - 39, - 4, - 61 - ], - [ - 12, - 54, - 4, - 27, - 50, - 14 - ], - [ - 47, - 19, - 42, - 17, - 35, - 40 - ], - [ - 54, - 40, - 60, - 63, - 45, - 57 - ], - [ - 44, - 56, - 40, - 62, - 37, - 3 - ], - [ - 59, - 41, - 57, - 34, - 48, - 22 - ], - [ - 56, - 13, - 59, - 51, - 26, - 58 - ] - ], - [ - [ - 35, - 32, - 8, - 40, - 51, - 52 - ], - [ - 52, - 5, - 22, - 21, - 6, - 33 - ], - [ - 22, - 58, - 11, - 25, - 3, - 51 - ], - [ - 63, - 2, - 56, - 4, - 23, - 54 - ], - [ - 39, - 12, - 23, - 32, - 30, - 46 - ], - [ - 50, - 34, - 36, - 58, - 26, - 28 - ], - [ - 46, - 41, - 3, - 2, - 22, - 16 - ], - [ - 60, - 36, - 53, - 30, - 54, - 39 - ], - [ - 16, - 51, - 3, - 39, - 2, - 26 - ], - [ - 45, - 26, - 18, - 41, - 32, - 46 - ], - [ - 45, - 3, - 10, - 56, - 36, - 35 - ], - [ - 21, - 36, - 35, - 50, - 11, - 19 - ], - [ - 28, - 11, - 46, - 59, - 41, - 15 - ], - [ - 23, - 16, - 38, - 19, - 15, - 22 - ], - [ - 27, - 7, - 34, - 58, - 3, - 42 - ], - [ - 9, - 22, - 36, - 46, - 26, - 41 - ], - [ - 3, - 51, - 40, - 56, - 46, - 8 - ], - [ - 12, - 25, - 21, - 50, - 17, - 62 - ], - [ - 27, - 34, - 61, - 13, - 60, - 11 - ], - [ - 53, - 12, - 56, - 0, - 42, - 33 - ], - [ - 53, - 37, - 12, - 24, - 25, - 63 - ], - [ - 45, - 55, - 18, - 26, - 17, - 43 - ], - [ - 4, - 25, - 32, - 1, - 48, - 53 - ], - [ - 17, - 27, - 63, - 4, - 62, - 31 - ], - [ - 6, - 52, - 62, - 40, - 46, - 23 - ], - [ - 10, - 42, - 28, - 49, - 3, - 53 - ], - [ - 45, - 27, - 41, - 21, - 16, - 47 - ] - ], - [ - [ - 44, - 24, - 33, - 56, - 15, - 41 - ], - [ - 38, - 26, - 24, - 29, - 19, - 53 - ], - [ - 12, - 15, - 29, - 9, - 1, - 63 - ], - [ - 58, - 38, - 50, - 0, - 43, - 61 - ], - [ - 24, - 51, - 31, - 34, - 60, - 7 - ], - [ - 0, - 7, - 22, - 43, - 35, - 1 - ], - [ - 63, - 36, - 11, - 1, - 16, - 4 - ], - [ - 8, - 50, - 56, - 4, - 30, - 55 - ], - [ - 43, - 16, - 42, - 29, - 60, - 35 - ], - [ - 34, - 0, - 9, - 22, - 18, - 26 - ], - [ - 54, - 51, - 45, - 35, - 2, - 36 - ], - [ - 37, - 36, - 43, - 60, - 11, - 59 - ], - [ - 56, - 38, - 10, - 28, - 14, - 43 - ], - [ - 30, - 0, - 58, - 62, - 22, - 19 - ], - [ - 7, - 55, - 42, - 58, - 30, - 38 - ], - [ - 11, - 33, - 1, - 39, - 19, - 16 - ], - [ - 55, - 20, - 40, - 9, - 18, - 30 - ], - [ - 18, - 20, - 57, - 32, - 45, - 1 - ], - [ - 43, - 61, - 12, - 32, - 31, - 30 - ], - [ - 23, - 25, - 7, - 28, - 40, - 19 - ], - [ - 14, - 51, - 48, - 58, - 53, - 25 - ], - [ - 18, - 30, - 1, - 49, - 41, - 9 - ], - [ - 2, - 51, - 22, - 0, - 52, - 5 - ], - [ - 53, - 4, - 47, - 52, - 51, - 40 - ], - [ - 40, - 16, - 9, - 47, - 23, - 11 - ], - [ - 47, - 3, - 43, - 46, - 26, - 53 - ], - [ - 8, - 40, - 18, - 46, - 33, - 63 - ] - ], - [ - [ - 48, - 38, - 50, - 42, - 63, - 36 - ], - [ - 3, - 10, - 26, - 2, - 6, - 61 - ], - [ - 39, - 44, - 45, - 40, - 6, - 7 - ], - [ - 41, - 5, - 20, - 49, - 56, - 13 - ], - [ - 6, - 1, - 30, - 37, - 28, - 38 - ], - [ - 59, - 46, - 22, - 35, - 61, - 0 - ], - [ - 1, - 63, - 35, - 3, - 60, - 49 - ], - [ - 8, - 12, - 2, - 50, - 5, - 55 - ], - [ - 42, - 33, - 43, - 16, - 32, - 29 - ], - [ - 9, - 34, - 0, - 20, - 41, - 31 - ], - [ - 51, - 54, - 8, - 19, - 63, - 9 - ], - [ - 37, - 56, - 36, - 11, - 59, - 43 - ], - [ - 38, - 10, - 28, - 17, - 56, - 63 - ], - [ - 27, - 30, - 42, - 19, - 0, - 22 - ], - [ - 7, - 55, - 49, - 42, - 58, - 38 - ], - [ - 29, - 34, - 39, - 33, - 47, - 11 - ], - [ - 55, - 40, - 20, - 18, - 7, - 5 - ], - [ - 43, - 57, - 39, - 54, - 48, - 28 - ], - [ - 12, - 43, - 61, - 42, - 32, - 49 - ], - [ - 23, - 36, - 1, - 7, - 59, - 28 - ], - [ - 14, - 53, - 21, - 7, - 57, - 37 - ], - [ - 18, - 1, - 24, - 60, - 30, - 9 - ], - [ - 51, - 0, - 33, - 2, - 44, - 5 - ], - [ - 52, - 29, - 4, - 41, - 54, - 58 - ], - [ - 40, - 19, - 16, - 9, - 46, - 47 - ], - [ - 61, - 3, - 47, - 22, - 21, - 53 - ], - [ - 35, - 60, - 54, - 1, - 5, - 40 - ] - ], - [ - [ - 17, - 18, - 8, - 53, - 25, - 43 - ], - [ - 9, - 38, - 24, - 47, - 25, - 63 - ], - [ - 20, - 24, - 5, - 12, - 54, - 28 - ], - [ - 43, - 10, - 20, - 42, - 11, - 8 - ], - [ - 53, - 61, - 30, - 39, - 29, - 18 - ], - [ - 61, - 56, - 25, - 40, - 5, - 22 - ], - [ - 62, - 17, - 24, - 1, - 47, - 33 - ], - [ - 41, - 16, - 34, - 39, - 29, - 8 - ], - [ - 39, - 16, - 36, - 42, - 29, - 23 - ], - [ - 9, - 11, - 41, - 63, - 56, - 31 - ], - [ - 48, - 51, - 10, - 62, - 63, - 45 - ], - [ - 36, - 11, - 37, - 42, - 58, - 46 - ], - [ - 51, - 38, - 25, - 63, - 29, - 44 - ], - [ - 4, - 56, - 44, - 62, - 58, - 30 - ], - [ - 3, - 7, - 46, - 42, - 33, - 35 - ], - [ - 39, - 9, - 33, - 58, - 60, - 29 - ], - [ - 40, - 37, - 20, - 16, - 55, - 25 - ], - [ - 54, - 19, - 11, - 57, - 0, - 39 - ], - [ - 12, - 43, - 61, - 25, - 49, - 32 - ], - [ - 4, - 23, - 54, - 36, - 7, - 28 - ], - [ - 40, - 25, - 26, - 14, - 2, - 58 - ], - [ - 18, - 58, - 24, - 1, - 22, - 46 - ], - [ - 2, - 63, - 22, - 6, - 44, - 56 - ], - [ - 52, - 29, - 51, - 4, - 40, - 32 - ], - [ - 40, - 17, - 15, - 16, - 46, - 57 - ], - [ - 9, - 61, - 3, - 47, - 24, - 11 - ], - [ - 2, - 39, - 24, - 42, - 0, - 44 - ] - ], - [ - [ - 0, - 10, - 49, - 23, - 62, - 44 - ], - [ - 28, - 0, - 36, - 26, - 47, - 52 - ], - [ - 30, - 4, - 16, - 48, - 40, - 10 - ], - [ - 61, - 32, - 26, - 16, - 33, - 62 - ], - [ - 30, - 39, - 53, - 5, - 57, - 20 - ], - [ - 5, - 37, - 61, - 15, - 25, - 6 - ], - [ - 15, - 17, - 24, - 60, - 49, - 62 - ], - [ - 34, - 39, - 61, - 0, - 58, - 40 - ], - [ - 16, - 39, - 36, - 51, - 2, - 29 - ], - [ - 9, - 11, - 41, - 31, - 56, - 52 - ], - [ - 10, - 48, - 24, - 45, - 62, - 51 - ], - [ - 11, - 38, - 36, - 37, - 6, - 42 - ], - [ - 51, - 50, - 15, - 30, - 25, - 38 - ], - [ - 4, - 19, - 24, - 35, - 31, - 48 - ], - [ - 7, - 46, - 3, - 58, - 30, - 41 - ], - [ - 58, - 9, - 39, - 32, - 29, - 40 - ], - [ - 40, - 37, - 20, - 8, - 25, - 55 - ], - [ - 19, - 0, - 54, - 52, - 17, - 39 - ], - [ - 25, - 43, - 12, - 61, - 14, - 11 - ], - [ - 23, - 4, - 54, - 36, - 28, - 33 - ], - [ - 40, - 2, - 25, - 58, - 36, - 53 - ], - [ - 18, - 46, - 35, - 22, - 53, - 16 - ], - [ - 2, - 6, - 63, - 14, - 42, - 11 - ], - [ - 35, - 7, - 52, - 40, - 29, - 57 - ], - [ - 40, - 15, - 19, - 57, - 17, - 23 - ], - [ - 9, - 11, - 47, - 22, - 49, - 1 - ], - [ - 24, - 39, - 42, - 2, - 16, - 0 - ] - ], - [ - [ - 55, - 39, - 9, - 43, - 21, - 46 - ], - [ - 56, - 0, - 63, - 39, - 30, - 41 - ], - [ - 20, - 1, - 26, - 58, - 34, - 19 - ], - [ - 54, - 24, - 32, - 51, - 26, - 44 - ], - [ - 30, - 53, - 56, - 39, - 34, - 40 - ], - [ - 5, - 37, - 25, - 50, - 6, - 61 - ], - [ - 24, - 49, - 37, - 15, - 6, - 29 - ], - [ - 34, - 16, - 30, - 61, - 10, - 36 - ], - [ - 16, - 29, - 2, - 5, - 51, - 26 - ], - [ - 9, - 56, - 11, - 31, - 46, - 45 - ], - [ - 10, - 45, - 56, - 62, - 25, - 36 - ], - [ - 11, - 6, - 35, - 36, - 1, - 52 - ], - [ - 51, - 50, - 41, - 46, - 38, - 4 - ], - [ - 19, - 33, - 41, - 16, - 31, - 52 - ], - [ - 34, - 7, - 17, - 47, - 63, - 3 - ], - [ - 58, - 9, - 22, - 61, - 59, - 8 - ], - [ - 40, - 37, - 3, - 51, - 22, - 57 - ], - [ - 12, - 52, - 21, - 54, - 25, - 19 - ], - [ - 34, - 53, - 27, - 43, - 14, - 13 - ], - [ - 56, - 44, - 53, - 24, - 60, - 43 - ], - [ - 12, - 53, - 40, - 49, - 2, - 62 - ], - [ - 18, - 39, - 44, - 61, - 26, - 23 - ], - [ - 0, - 4, - 53, - 25, - 41, - 21 - ], - [ - 1, - 7, - 25, - 10, - 40, - 56 - ], - [ - 40, - 22, - 6, - 29, - 19, - 48 - ], - [ - 28, - 10, - 47, - 55, - 42, - 44 - ], - [ - 30, - 27, - 57, - 16, - 50, - 59 - ] - ], - [ - [ - 45, - 37, - 48, - 29, - 30, - 3 - ], - [ - 8, - 60, - 10, - 59, - 43, - 6 - ], - [ - 51, - 45, - 28, - 59, - 63, - 34 - ], - [ - 4, - 16, - 20, - 58, - 44, - 28 - ], - [ - 50, - 31, - 57, - 24, - 51, - 53 - ], - [ - 58, - 9, - 0, - 61, - 35, - 41 - ], - [ - 16, - 63, - 11, - 61, - 23, - 36 - ], - [ - 4, - 47, - 42, - 53, - 8, - 30 - ], - [ - 44, - 14, - 16, - 33, - 3, - 20 - ], - [ - 34, - 28, - 26, - 57, - 22, - 18 - ], - [ - 20, - 35, - 19, - 59, - 2, - 38 - ], - [ - 12, - 60, - 43, - 63, - 32, - 62 - ], - [ - 28, - 12, - 29, - 11, - 14, - 50 - ], - [ - 23, - 29, - 33, - 22, - 11, - 19 - ], - [ - 23, - 60, - 51, - 50, - 7, - 22 - ], - [ - 44, - 46, - 49, - 7, - 1, - 12 - ], - [ - 2, - 54, - 27, - 61, - 18, - 5 - ], - [ - 17, - 50, - 51, - 32, - 33, - 34 - ], - [ - 5, - 19, - 61, - 27, - 32, - 11 - ], - [ - 6, - 0, - 5, - 13, - 41, - 57 - ], - [ - 27, - 33, - 53, - 45, - 38, - 32 - ], - [ - 26, - 36, - 55, - 59, - 61, - 18 - ], - [ - 47, - 46, - 3, - 37, - 57, - 49 - ], - [ - 20, - 22, - 4, - 16, - 51, - 11 - ], - [ - 62, - 11, - 21, - 34, - 4, - 1 - ], - [ - 34, - 18, - 7, - 60, - 33, - 32 - ], - [ - 45, - 52, - 4, - 36, - 21, - 9 - ] - ], - [ - [ - 18, - 8, - 20, - 49, - 30, - 23 - ], - [ - 1, - 27, - 26, - 22, - 59, - 36 - ], - [ - 43, - 26, - 15, - 58, - 0, - 46 - ], - [ - 55, - 1, - 35, - 28, - 16, - 32 - ], - [ - 59, - 9, - 10, - 53, - 12, - 58 - ], - [ - 9, - 2, - 27, - 11, - 61, - 43 - ], - [ - 16, - 57, - 63, - 23, - 19, - 12 - ], - [ - 46, - 45, - 26, - 4, - 30, - 37 - ], - [ - 43, - 44, - 20, - 16, - 14, - 9 - ], - [ - 34, - 47, - 42, - 43, - 26, - 51 - ], - [ - 42, - 2, - 38, - 45, - 20, - 36 - ], - [ - 18, - 7, - 12, - 2, - 43, - 60 - ], - [ - 1, - 28, - 12, - 3, - 29, - 33 - ], - [ - 25, - 13, - 0, - 63, - 2, - 62 - ], - [ - 18, - 36, - 6, - 29, - 19, - 15 - ], - [ - 1, - 42, - 63, - 41, - 57, - 19 - ], - [ - 57, - 54, - 5, - 27, - 18, - 31 - ], - [ - 50, - 6, - 13, - 32, - 17, - 20 - ], - [ - 17, - 5, - 27, - 32, - 1, - 55 - ], - [ - 49, - 0, - 61, - 5, - 10, - 30 - ], - [ - 29, - 53, - 51, - 13, - 33, - 46 - ], - [ - 29, - 17, - 21, - 30, - 14, - 40 - ], - [ - 5, - 17, - 33, - 32, - 18, - 28 - ], - [ - 51, - 4, - 20, - 54, - 58, - 41 - ], - [ - 47, - 4, - 27, - 48, - 37, - 60 - ], - [ - 3, - 26, - 12, - 59, - 2, - 48 - ], - [ - 46, - 43, - 18, - 20, - 9, - 53 - ] - ], - [ - [ - 45, - 6, - 57, - 43, - 40, - 58 - ], - [ - 38, - 63, - 36, - 27, - 54, - 33 - ], - [ - 37, - 14, - 19, - 41, - 58, - 63 - ], - [ - 9, - 12, - 2, - 55, - 28, - 23 - ], - [ - 39, - 59, - 7, - 13, - 33, - 43 - ], - [ - 45, - 9, - 63, - 27, - 32, - 58 - ], - [ - 16, - 57, - 10, - 63, - 11, - 23 - ], - [ - 51, - 45, - 25, - 4, - 21, - 30 - ], - [ - 21, - 44, - 14, - 16, - 33, - 39 - ], - [ - 42, - 44, - 43, - 5, - 37, - 34 - ], - [ - 42, - 19, - 20, - 2, - 38, - 61 - ], - [ - 4, - 12, - 2, - 62, - 63, - 36 - ], - [ - 32, - 55, - 0, - 11, - 47, - 28 - ], - [ - 43, - 13, - 2, - 44, - 26, - 50 - ], - [ - 49, - 33, - 15, - 28, - 29, - 35 - ], - [ - 44, - 41, - 7, - 2, - 22, - 63 - ], - [ - 48, - 6, - 54, - 20, - 2, - 27 - ], - [ - 50, - 51, - 32, - 3, - 17, - 36 - ], - [ - 5, - 61, - 57, - 48, - 19, - 32 - ], - [ - 21, - 0, - 6, - 31, - 29, - 47 - ], - [ - 33, - 9, - 53, - 27, - 17, - 36 - ], - [ - 29, - 26, - 55, - 19, - 17, - 62 - ], - [ - 12, - 46, - 5, - 37, - 57, - 3 - ], - [ - 20, - 51, - 4, - 22, - 16, - 41 - ], - [ - 21, - 11, - 62, - 46, - 23, - 48 - ], - [ - 32, - 60, - 37, - 18, - 3, - 7 - ], - [ - 9, - 11, - 36, - 48, - 0, - 45 - ] - ], - [ - [ - 49, - 42, - 28, - 23, - 33, - 61 - ], - [ - 4, - 2, - 12, - 6, - 8, - 55 - ], - [ - 12, - 0, - 26, - 41, - 6, - 27 - ], - [ - 9, - 57, - 6, - 23, - 51, - 28 - ], - [ - 40, - 7, - 20, - 16, - 15, - 33 - ], - [ - 45, - 59, - 63, - 62, - 32, - 3 - ], - [ - 10, - 39, - 57, - 13, - 16, - 19 - ], - [ - 45, - 23, - 51, - 33, - 25, - 46 - ], - [ - 28, - 21, - 44, - 11, - 16, - 59 - ], - [ - 5, - 42, - 44, - 24, - 43, - 47 - ], - [ - 42, - 53, - 30, - 18, - 2, - 27 - ], - [ - 2, - 62, - 4, - 43, - 10, - 36 - ], - [ - 0, - 56, - 55, - 47, - 32, - 49 - ], - [ - 43, - 25, - 2, - 5, - 3, - 49 - ], - [ - 4, - 28, - 15, - 8, - 49, - 58 - ], - [ - 2, - 42, - 44, - 41, - 7, - 63 - ], - [ - 48, - 27, - 54, - 20, - 2, - 18 - ], - [ - 50, - 51, - 0, - 36, - 3, - 32 - ], - [ - 57, - 5, - 61, - 19, - 32, - 38 - ], - [ - 21, - 0, - 6, - 63, - 23, - 51 - ], - [ - 33, - 53, - 27, - 36, - 9, - 38 - ], - [ - 29, - 26, - 55, - 62, - 18, - 31 - ], - [ - 46, - 56, - 12, - 53, - 29, - 0 - ], - [ - 20, - 16, - 22, - 4, - 51, - 17 - ], - [ - 21, - 62, - 11, - 31, - 46, - 33 - ], - [ - 37, - 60, - 18, - 7, - 32, - 44 - ], - [ - 11, - 9, - 36, - 0, - 48, - 63 - ] - ], - [ - [ - 41, - 32, - 49, - 39, - 61, - 44 - ], - [ - 47, - 26, - 16, - 21, - 36, - 22 - ], - [ - 4, - 30, - 37, - 42, - 60, - 54 - ], - [ - 9, - 57, - 26, - 32, - 50, - 20 - ], - [ - 56, - 3, - 40, - 33, - 36, - 54 - ], - [ - 11, - 38, - 2, - 32, - 61, - 30 - ], - [ - 39, - 57, - 19, - 10, - 16, - 42 - ], - [ - 46, - 21, - 35, - 39, - 45, - 25 - ], - [ - 21, - 37, - 12, - 20, - 11, - 28 - ], - [ - 5, - 47, - 44, - 10, - 42, - 23 - ], - [ - 18, - 42, - 61, - 2, - 38, - 31 - ], - [ - 54, - 4, - 2, - 7, - 22, - 16 - ], - [ - 5, - 3, - 17, - 56, - 32, - 55 - ], - [ - 55, - 0, - 2, - 25, - 43, - 5 - ], - [ - 22, - 28, - 15, - 6, - 5, - 49 - ], - [ - 2, - 57, - 19, - 54, - 41, - 30 - ], - [ - 7, - 48, - 20, - 54, - 27, - 0 - ], - [ - 3, - 56, - 13, - 37, - 43, - 59 - ], - [ - 45, - 55, - 57, - 61, - 48, - 52 - ], - [ - 21, - 5, - 0, - 16, - 27, - 23 - ], - [ - 25, - 42, - 17, - 54, - 23, - 14 - ], - [ - 21, - 44, - 15, - 20, - 42, - 18 - ], - [ - 35, - 12, - 25, - 53, - 61, - 2 - ], - [ - 38, - 54, - 48, - 53, - 21, - 36 - ], - [ - 13, - 31, - 48, - 33, - 18, - 55 - ], - [ - 38, - 27, - 19, - 6, - 44, - 3 - ], - [ - 29, - 62, - 43, - 59, - 46, - 5 - ] - ], - [ - [ - 57, - 9, - 19, - 51, - 18, - 41 - ], - [ - 28, - 57, - 36, - 8, - 48, - 60 - ], - [ - 2, - 51, - 59, - 5, - 34, - 9 - ], - [ - 9, - 55, - 59, - 26, - 4, - 2 - ], - [ - 49, - 56, - 35, - 42, - 30, - 23 - ], - [ - 18, - 30, - 22, - 29, - 19, - 52 - ], - [ - 39, - 34, - 33, - 51, - 56, - 3 - ], - [ - 32, - 21, - 1, - 7, - 46, - 49 - ], - [ - 33, - 54, - 23, - 21, - 12, - 11 - ], - [ - 5, - 30, - 60, - 47, - 15, - 18 - ], - [ - 4, - 18, - 46, - 27, - 20, - 22 - ], - [ - 22, - 59, - 54, - 48, - 19, - 4 - ], - [ - 17, - 5, - 56, - 31, - 49, - 4 - ], - [ - 29, - 47, - 55, - 2, - 53, - 60 - ], - [ - 8, - 22, - 11, - 44, - 36, - 15 - ], - [ - 60, - 44, - 30, - 57, - 54, - 39 - ], - [ - 7, - 44, - 27, - 20, - 2, - 61 - ], - [ - 48, - 17, - 21, - 37, - 32, - 57 - ], - [ - 48, - 32, - 46, - 6, - 61, - 42 - ], - [ - 4, - 57, - 1, - 36, - 0, - 30 - ], - [ - 7, - 17, - 61, - 53, - 21, - 63 - ], - [ - 60, - 14, - 53, - 35, - 18, - 55 - ], - [ - 10, - 15, - 33, - 51, - 36, - 5 - ], - [ - 11, - 4, - 19, - 51, - 21, - 52 - ], - [ - 47, - 19, - 43, - 48, - 58, - 4 - ], - [ - 3, - 33, - 26, - 21, - 52, - 19 - ], - [ - 24, - 45, - 60, - 35, - 49, - 1 - ] - ], - [ - [ - 16, - 4, - 44, - 23, - 22, - 43 - ], - [ - 16, - 23, - 50, - 9, - 32, - 13 - ], - [ - 11, - 35, - 21, - 7, - 48, - 59 - ], - [ - 55, - 15, - 1, - 11, - 8, - 40 - ], - [ - 35, - 61, - 30, - 59, - 31, - 62 - ], - [ - 51, - 29, - 15, - 52, - 38, - 61 - ], - [ - 60, - 0, - 55, - 34, - 59, - 33 - ], - [ - 12, - 22, - 56, - 63, - 54, - 55 - ], - [ - 42, - 54, - 23, - 33, - 27, - 47 - ], - [ - 30, - 60, - 20, - 5, - 4, - 22 - ], - [ - 4, - 35, - 22, - 46, - 23, - 19 - ], - [ - 29, - 22, - 59, - 49, - 24, - 28 - ], - [ - 17, - 31, - 5, - 56, - 4, - 9 - ], - [ - 61, - 29, - 0, - 48, - 59, - 50 - ], - [ - 8, - 6, - 22, - 60, - 55, - 31 - ], - [ - 60, - 39, - 19, - 57, - 53, - 1 - ], - [ - 56, - 30, - 22, - 10, - 5, - 18 - ], - [ - 20, - 31, - 1, - 26, - 61, - 37 - ], - [ - 8, - 23, - 7, - 46, - 48, - 4 - ], - [ - 45, - 23, - 51, - 13, - 17, - 4 - ], - [ - 17, - 13, - 61, - 29, - 14, - 55 - ], - [ - 14, - 27, - 21, - 43, - 57, - 56 - ], - [ - 52, - 51, - 15, - 58, - 8, - 5 - ], - [ - 4, - 51, - 49, - 14, - 21, - 34 - ], - [ - 4, - 9, - 47, - 13, - 8, - 61 - ], - [ - 27, - 3, - 16, - 43, - 31, - 47 - ], - [ - 59, - 43, - 29, - 61, - 0, - 40 - ] - ], - [ - [ - 48, - 21, - 18, - 49, - 41, - 23 - ], - [ - 36, - 4, - 60, - 8, - 49, - 44 - ], - [ - 20, - 39, - 30, - 59, - 45, - 55 - ], - [ - 35, - 46, - 15, - 48, - 33, - 2 - ], - [ - 61, - 37, - 8, - 15, - 54, - 10 - ], - [ - 46, - 6, - 51, - 29, - 58, - 4 - ], - [ - 28, - 11, - 44, - 60, - 0, - 1 - ], - [ - 63, - 12, - 13, - 27, - 10, - 0 - ], - [ - 33, - 42, - 54, - 44, - 23, - 14 - ], - [ - 18, - 60, - 30, - 22, - 40, - 14 - ], - [ - 35, - 4, - 61, - 9, - 18, - 33 - ], - [ - 59, - 45, - 48, - 28, - 62, - 22 - ], - [ - 17, - 56, - 7, - 5, - 53, - 36 - ], - [ - 8, - 47, - 29, - 59, - 1, - 6 - ], - [ - 9, - 8, - 18, - 22, - 60, - 15 - ], - [ - 46, - 60, - 22, - 44, - 30, - 57 - ], - [ - 61, - 2, - 27, - 34, - 7, - 60 - ], - [ - 48, - 21, - 37, - 17, - 50, - 57 - ], - [ - 44, - 42, - 5, - 2, - 48, - 61 - ], - [ - 37, - 57, - 4, - 36, - 17, - 59 - ], - [ - 7, - 17, - 44, - 61, - 53, - 33 - ], - [ - 60, - 45, - 14, - 42, - 18, - 9 - ], - [ - 10, - 8, - 36, - 33, - 15, - 58 - ], - [ - 19, - 20, - 11, - 4, - 49, - 51 - ], - [ - 61, - 37, - 47, - 23, - 12, - 3 - ], - [ - 17, - 33, - 3, - 40, - 19, - 26 - ], - [ - 61, - 45, - 49, - 1, - 14, - 63 - ] - ], - [ - [ - 13, - 40, - 55, - 63, - 26, - 41 - ], - [ - 5, - 35, - 49, - 40, - 17, - 46 - ], - [ - 38, - 17, - 59, - 49, - 2, - 58 - ], - [ - 40, - 8, - 1, - 16, - 0, - 11 - ], - [ - 37, - 62, - 51, - 10, - 8, - 38 - ], - [ - 9, - 42, - 61, - 29, - 35, - 33 - ], - [ - 63, - 53, - 11, - 16, - 33, - 60 - ], - [ - 63, - 37, - 5, - 13, - 17, - 39 - ], - [ - 44, - 20, - 31, - 54, - 38, - 21 - ], - [ - 43, - 21, - 30, - 34, - 18, - 49 - ], - [ - 2, - 11, - 19, - 35, - 4, - 9 - ], - [ - 22, - 60, - 43, - 2, - 4, - 49 - ], - [ - 29, - 5, - 17, - 22, - 24, - 55 - ], - [ - 2, - 59, - 29, - 5, - 55, - 6 - ], - [ - 23, - 8, - 36, - 22, - 15, - 30 - ], - [ - 12, - 44, - 41, - 5, - 45, - 22 - ], - [ - 54, - 7, - 41, - 11, - 1, - 53 - ], - [ - 6, - 50, - 2, - 9, - 21, - 37 - ], - [ - 13, - 19, - 5, - 10, - 48, - 8 - ], - [ - 41, - 6, - 32, - 21, - 0, - 47 - ], - [ - 38, - 33, - 36, - 53, - 31, - 61 - ], - [ - 3, - 26, - 7, - 62, - 18, - 59 - ], - [ - 56, - 57, - 46, - 12, - 35, - 3 - ], - [ - 20, - 16, - 22, - 24, - 27, - 42 - ], - [ - 36, - 21, - 46, - 34, - 3, - 11 - ], - [ - 33, - 34, - 60, - 45, - 7, - 32 - ], - [ - 56, - 34, - 52, - 58, - 26, - 48 - ] - ], - [ - [ - 54, - 23, - 53, - 11, - 58, - 3 - ], - [ - 11, - 30, - 59, - 58, - 63, - 4 - ], - [ - 20, - 29, - 58, - 17, - 42, - 4 - ], - [ - 1, - 35, - 40, - 45, - 53, - 21 - ], - [ - 40, - 55, - 33, - 21, - 38, - 49 - ], - [ - 45, - 29, - 61, - 27, - 63, - 62 - ], - [ - 33, - 57, - 11, - 28, - 53, - 34 - ], - [ - 11, - 63, - 39, - 10, - 45, - 14 - ], - [ - 30, - 54, - 57, - 59, - 33, - 26 - ], - [ - 43, - 23, - 5, - 18, - 21, - 42 - ], - [ - 11, - 18, - 2, - 9, - 34, - 6 - ], - [ - 22, - 60, - 28, - 2, - 63, - 17 - ], - [ - 5, - 41, - 6, - 17, - 56, - 29 - ], - [ - 55, - 6, - 5, - 2, - 48, - 59 - ], - [ - 23, - 19, - 22, - 62, - 11, - 9 - ], - [ - 12, - 45, - 41, - 8, - 27, - 42 - ], - [ - 11, - 53, - 41, - 44, - 51, - 7 - ], - [ - 21, - 2, - 6, - 36, - 50, - 56 - ], - [ - 13, - 10, - 48, - 53, - 61, - 19 - ], - [ - 47, - 21, - 56, - 44, - 6, - 31 - ], - [ - 44, - 12, - 3, - 55, - 41, - 53 - ], - [ - 44, - 47, - 28, - 43, - 45, - 63 - ], - [ - 1, - 25, - 53, - 11, - 39, - 19 - ], - [ - 1, - 59, - 38, - 3, - 37, - 42 - ], - [ - 45, - 3, - 0, - 21, - 22, - 33 - ], - [ - 10, - 28, - 42, - 49, - 11, - 3 - ], - [ - 30, - 57, - 15, - 16, - 56, - 41 - ] - ], - [ - [ - 53, - 15, - 34, - 0, - 46, - 33 - ], - [ - 8, - 12, - 41, - 19, - 39, - 32 - ], - [ - 56, - 31, - 36, - 13, - 23, - 9 - ], - [ - 36, - 51, - 30, - 21, - 1, - 11 - ], - [ - 13, - 58, - 50, - 2, - 53, - 54 - ], - [ - 49, - 52, - 32, - 7, - 23, - 47 - ], - [ - 61, - 38, - 23, - 39, - 0, - 35 - ], - [ - 42, - 27, - 9, - 20, - 17, - 57 - ], - [ - 34, - 1, - 29, - 4, - 35, - 45 - ], - [ - 54, - 57, - 27, - 19, - 38, - 62 - ], - [ - 59, - 1, - 60, - 26, - 38, - 22 - ], - [ - 25, - 31, - 51, - 36, - 32, - 8 - ], - [ - 14, - 62, - 2, - 19, - 37, - 11 - ], - [ - 57, - 40, - 13, - 22, - 37, - 46 - ], - [ - 45, - 34, - 58, - 44, - 42, - 16 - ], - [ - 50, - 16, - 6, - 5, - 33, - 43 - ], - [ - 42, - 39, - 61, - 13, - 5, - 15 - ], - [ - 46, - 23, - 27, - 4, - 28, - 63 - ], - [ - 62, - 31, - 10, - 45, - 35, - 56 - ], - [ - 15, - 13, - 38, - 63, - 4, - 31 - ], - [ - 34, - 15, - 38, - 57, - 27, - 19 - ], - [ - 41, - 62, - 36, - 57, - 19, - 47 - ], - [ - 34, - 22, - 53, - 10, - 46, - 33 - ], - [ - 24, - 51, - 4, - 47, - 39, - 10 - ], - [ - 11, - 57, - 51, - 50, - 54, - 48 - ], - [ - 51, - 7, - 11, - 43, - 50, - 18 - ], - [ - 39, - 37, - 9, - 42, - 40, - 44 - ] - ], - [ - [ - 57, - 17, - 62, - 42, - 23, - 60 - ], - [ - 18, - 7, - 53, - 43, - 26, - 60 - ], - [ - 60, - 5, - 3, - 53, - 23, - 57 - ], - [ - 17, - 10, - 22, - 19, - 11, - 31 - ], - [ - 10, - 15, - 12, - 27, - 17, - 4 - ], - [ - 2, - 44, - 39, - 36, - 25, - 54 - ], - [ - 52, - 62, - 37, - 21, - 41, - 42 - ], - [ - 62, - 7, - 46, - 30, - 36, - 14 - ], - [ - 50, - 4, - 10, - 58, - 0, - 16 - ], - [ - 32, - 47, - 38, - 54, - 8, - 41 - ], - [ - 41, - 32, - 3, - 18, - 1, - 22 - ], - [ - 16, - 50, - 53, - 7, - 44, - 21 - ], - [ - 11, - 39, - 3, - 35, - 25, - 46 - ], - [ - 18, - 9, - 15, - 13, - 63, - 28 - ], - [ - 62, - 58, - 13, - 5, - 17, - 3 - ], - [ - 3, - 31, - 43, - 53, - 35, - 57 - ], - [ - 24, - 51, - 15, - 46, - 32, - 5 - ], - [ - 13, - 30, - 0, - 32, - 5, - 59 - ], - [ - 2, - 39, - 32, - 38, - 34, - 22 - ], - [ - 42, - 26, - 34, - 28, - 37, - 54 - ], - [ - 28, - 43, - 53, - 41, - 13, - 23 - ], - [ - 14, - 15, - 34, - 1, - 48, - 40 - ], - [ - 5, - 25, - 4, - 33, - 39, - 6 - ], - [ - 58, - 4, - 57, - 17, - 51, - 11 - ], - [ - 37, - 47, - 35, - 31, - 63, - 29 - ], - [ - 15, - 3, - 28, - 33, - 23, - 9 - ], - [ - 23, - 6, - 58, - 47, - 56, - 30 - ] - ], - [ - [ - 47, - 29, - 14, - 6, - 51, - 43 - ], - [ - 30, - 29, - 39, - 7, - 52, - 12 - ], - [ - 63, - 34, - 41, - 2, - 47, - 7 - ], - [ - 4, - 28, - 54, - 45, - 52, - 58 - ], - [ - 29, - 7, - 12, - 15, - 41, - 6 - ], - [ - 34, - 29, - 48, - 3, - 43, - 40 - ], - [ - 30, - 29, - 16, - 47, - 42, - 45 - ], - [ - 33, - 39, - 25, - 60, - 41, - 3 - ], - [ - 50, - 26, - 4, - 25, - 17, - 13 - ], - [ - 5, - 54, - 43, - 16, - 12, - 53 - ], - [ - 18, - 6, - 35, - 3, - 21, - 1 - ], - [ - 56, - 46, - 48, - 10, - 16, - 44 - ], - [ - 9, - 35, - 7, - 24, - 47, - 57 - ], - [ - 53, - 42, - 15, - 56, - 59, - 47 - ], - [ - 39, - 11, - 36, - 32, - 35, - 18 - ], - [ - 46, - 20, - 53, - 38, - 56, - 26 - ], - [ - 58, - 29, - 14, - 26, - 17, - 49 - ], - [ - 24, - 25, - 39, - 16, - 1, - 57 - ], - [ - 24, - 41, - 4, - 1, - 63, - 28 - ], - [ - 42, - 37, - 48, - 34, - 26, - 41 - ], - [ - 11, - 28, - 16, - 32, - 7, - 56 - ], - [ - 14, - 42, - 6, - 16, - 22, - 15 - ], - [ - 33, - 56, - 42, - 8, - 25, - 38 - ], - [ - 4, - 58, - 48, - 33, - 11, - 28 - ], - [ - 37, - 47, - 29, - 48, - 30, - 53 - ], - [ - 12, - 41, - 3, - 4, - 48, - 46 - ], - [ - 14, - 13, - 61, - 6, - 62, - 1 - ] - ], - [ - [ - 45, - 10, - 44, - 43, - 53, - 33 - ], - [ - 32, - 63, - 22, - 27, - 30, - 29 - ], - [ - 54, - 35, - 37, - 32, - 26, - 30 - ], - [ - 24, - 63, - 0, - 17, - 25, - 45 - ], - [ - 40, - 7, - 0, - 57, - 29, - 22 - ], - [ - 10, - 34, - 20, - 22, - 43, - 33 - ], - [ - 42, - 30, - 5, - 25, - 19, - 34 - ], - [ - 33, - 18, - 35, - 51, - 7, - 57 - ], - [ - 50, - 28, - 25, - 4, - 10, - 9 - ], - [ - 5, - 38, - 16, - 43, - 54, - 21 - ], - [ - 18, - 21, - 3, - 6, - 39, - 53 - ], - [ - 56, - 16, - 53, - 39, - 46, - 42 - ], - [ - 9, - 35, - 57, - 11, - 47, - 13 - ], - [ - 56, - 15, - 3, - 59, - 9, - 28 - ], - [ - 4, - 62, - 39, - 58, - 63, - 36 - ], - [ - 20, - 53, - 57, - 8, - 51, - 35 - ], - [ - 51, - 49, - 11, - 26, - 15, - 14 - ], - [ - 0, - 25, - 59, - 62, - 21, - 13 - ], - [ - 39, - 34, - 48, - 53, - 61, - 33 - ], - [ - 47, - 26, - 28, - 34, - 21, - 39 - ], - [ - 28, - 43, - 12, - 53, - 41, - 32 - ], - [ - 14, - 52, - 17, - 1, - 15, - 38 - ], - [ - 25, - 4, - 5, - 11, - 58, - 50 - ], - [ - 58, - 4, - 17, - 10, - 25, - 57 - ], - [ - 29, - 47, - 35, - 31, - 52, - 48 - ], - [ - 55, - 28, - 23, - 15, - 3, - 24 - ], - [ - 6, - 30, - 57, - 32, - 34, - 62 - ] - ], - [ - [ - 39, - 5, - 30, - 17, - 61, - 15 - ], - [ - 11, - 63, - 0, - 23, - 61, - 46 - ], - [ - 61, - 15, - 53, - 22, - 7, - 57 - ], - [ - 50, - 57, - 58, - 63, - 45, - 47 - ], - [ - 55, - 31, - 57, - 24, - 60, - 5 - ], - [ - 22, - 7, - 43, - 1, - 10, - 0 - ], - [ - 14, - 58, - 1, - 34, - 19, - 45 - ], - [ - 50, - 8, - 14, - 7, - 57, - 9 - ], - [ - 43, - 0, - 4, - 10, - 45, - 46 - ], - [ - 38, - 0, - 4, - 55, - 54, - 10 - ], - [ - 51, - 54, - 46, - 39, - 1, - 38 - ], - [ - 16, - 37, - 33, - 36, - 21, - 63 - ], - [ - 10, - 38, - 57, - 58, - 3, - 63 - ], - [ - 30, - 0, - 63, - 13, - 22, - 18 - ], - [ - 55, - 58, - 62, - 38, - 6, - 36 - ], - [ - 19, - 53, - 11, - 1, - 57, - 25 - ], - [ - 11, - 6, - 51, - 16, - 30, - 18 - ], - [ - 20, - 57, - 32, - 36, - 13, - 56 - ], - [ - 3, - 12, - 61, - 26, - 32, - 1 - ], - [ - 25, - 36, - 34, - 51, - 59, - 37 - ], - [ - 13, - 53, - 28, - 16, - 14, - 9 - ], - [ - 14, - 42, - 15, - 55, - 22, - 38 - ], - [ - 62, - 58, - 29, - 33, - 5, - 34 - ], - [ - 21, - 4, - 51, - 49, - 12, - 58 - ], - [ - 61, - 60, - 40, - 35, - 59, - 47 - ], - [ - 37, - 46, - 3, - 48, - 12, - 53 - ], - [ - 63, - 8, - 46, - 33, - 1, - 53 - ] - ], - [ - [ - 62, - 0, - 9, - 61, - 26, - 41 - ], - [ - 45, - 3, - 29, - 35, - 2, - 54 - ], - [ - 56, - 31, - 53, - 28, - 23, - 2 - ], - [ - 13, - 41, - 46, - 49, - 5, - 45 - ], - [ - 18, - 25, - 57, - 55, - 50, - 15 - ], - [ - 58, - 22, - 4, - 46, - 19, - 12 - ], - [ - 1, - 25, - 58, - 22, - 43, - 35 - ], - [ - 50, - 23, - 12, - 8, - 9, - 16 - ], - [ - 29, - 17, - 16, - 43, - 10, - 4 - ], - [ - 38, - 55, - 0, - 40, - 20, - 10 - ], - [ - 51, - 19, - 54, - 39, - 46, - 1 - ], - [ - 60, - 16, - 37, - 11, - 56, - 36 - ], - [ - 10, - 7, - 38, - 57, - 54, - 44 - ], - [ - 45, - 13, - 0, - 30, - 47, - 63 - ], - [ - 58, - 48, - 55, - 38, - 36, - 29 - ], - [ - 29, - 53, - 0, - 1, - 55, - 57 - ], - [ - 11, - 12, - 6, - 29, - 5, - 40 - ], - [ - 57, - 32, - 42, - 8, - 20, - 36 - ], - [ - 3, - 61, - 49, - 32, - 26, - 1 - ], - [ - 42, - 5, - 36, - 16, - 39, - 51 - ], - [ - 9, - 13, - 7, - 53, - 46, - 28 - ], - [ - 14, - 55, - 42, - 24, - 33, - 47 - ], - [ - 33, - 58, - 62, - 15, - 36, - 5 - ], - [ - 51, - 4, - 49, - 58, - 21, - 41 - ], - [ - 40, - 60, - 19, - 35, - 61, - 15 - ], - [ - 14, - 21, - 3, - 12, - 5, - 37 - ], - [ - 35, - 60, - 1, - 10, - 53, - 54 - ] - ], - [ - [ - 40, - 28, - 60, - 31, - 59, - 23 - ], - [ - 42, - 12, - 26, - 34, - 0, - 61 - ], - [ - 16, - 5, - 62, - 3, - 32, - 0 - ], - [ - 13, - 3, - 18, - 39, - 42, - 52 - ], - [ - 25, - 12, - 39, - 0, - 57, - 15 - ], - [ - 33, - 50, - 58, - 36, - 6, - 26 - ], - [ - 41, - 43, - 1, - 15, - 2, - 25 - ], - [ - 36, - 16, - 53, - 14, - 58, - 30 - ], - [ - 16, - 29, - 3, - 2, - 10, - 5 - ], - [ - 38, - 16, - 32, - 26, - 45, - 11 - ], - [ - 45, - 19, - 3, - 46, - 56, - 53 - ], - [ - 21, - 36, - 11, - 9, - 16, - 41 - ], - [ - 11, - 16, - 10, - 50, - 8, - 63 - ], - [ - 35, - 38, - 33, - 51, - 25, - 22 - ], - [ - 58, - 48, - 27, - 50, - 29, - 26 - ], - [ - 29, - 57, - 53, - 3, - 54, - 4 - ], - [ - 3, - 23, - 6, - 46, - 51, - 11 - ], - [ - 32, - 5, - 57, - 54, - 52, - 30 - ], - [ - 34, - 61, - 40, - 0, - 3, - 48 - ], - [ - 22, - 53, - 12, - 33, - 39, - 60 - ], - [ - 20, - 53, - 47, - 40, - 12, - 8 - ], - [ - 55, - 17, - 52, - 14, - 45, - 56 - ], - [ - 1, - 4, - 13, - 11, - 39, - 33 - ], - [ - 17, - 10, - 51, - 44, - 4, - 55 - ], - [ - 35, - 6, - 45, - 21, - 52, - 37 - ], - [ - 10, - 42, - 9, - 1, - 53, - 24 - ], - [ - 27, - 41, - 32, - 45, - 10, - 47 - ] - ], - [ - [ - 11, - 34, - 44, - 51, - 41, - 12 - ], - [ - 20, - 34, - 3, - 25, - 63, - 16 - ], - [ - 40, - 26, - 37, - 22, - 15, - 54 - ], - [ - 4, - 16, - 25, - 28, - 45, - 58 - ], - [ - 33, - 10, - 32, - 24, - 3, - 4 - ], - [ - 0, - 9, - 58, - 3, - 34, - 15 - ], - [ - 16, - 63, - 43, - 59, - 25, - 42 - ], - [ - 16, - 48, - 36, - 33, - 25, - 58 - ], - [ - 20, - 50, - 16, - 23, - 42, - 29 - ], - [ - 60, - 51, - 16, - 22, - 38, - 48 - ], - [ - 31, - 19, - 38, - 3, - 2, - 43 - ], - [ - 56, - 9, - 21, - 40, - 18, - 44 - ], - [ - 11, - 13, - 10, - 45, - 27, - 57 - ], - [ - 56, - 14, - 35, - 44, - 45, - 13 - ], - [ - 58, - 43, - 26, - 33, - 31, - 50 - ], - [ - 13, - 38, - 46, - 51, - 28, - 37 - ], - [ - 58, - 63, - 42, - 10, - 0, - 16 - ], - [ - 0, - 1, - 53, - 52, - 24, - 59 - ], - [ - 17, - 29, - 0, - 40, - 60, - 24 - ], - [ - 17, - 24, - 2, - 26, - 35, - 23 - ], - [ - 44, - 20, - 22, - 4, - 50, - 40 - ], - [ - 9, - 61, - 17, - 16, - 27, - 37 - ], - [ - 63, - 13, - 16, - 11, - 2, - 24 - ], - [ - 44, - 48, - 23, - 49, - 56, - 45 - ], - [ - 6, - 5, - 49, - 33, - 31, - 63 - ], - [ - 49, - 47, - 1, - 57, - 4, - 53 - ], - [ - 2, - 56, - 19, - 63, - 39, - 50 - ] - ], - [ - [ - 16, - 22, - 44, - 23, - 3, - 13 - ], - [ - 16, - 23, - 50, - 9, - 13, - 59 - ], - [ - 11, - 35, - 21, - 7, - 59, - 24 - ], - [ - 53, - 32, - 7, - 60, - 1, - 13 - ], - [ - 24, - 33, - 57, - 35, - 0, - 39 - ], - [ - 15, - 38, - 0, - 45, - 32, - 58 - ], - [ - 59, - 16, - 25, - 49, - 35, - 5 - ], - [ - 45, - 40, - 16, - 14, - 12, - 30 - ], - [ - 20, - 16, - 42, - 6, - 17, - 23 - ], - [ - 60, - 10, - 22, - 48, - 50, - 38 - ], - [ - 31, - 19, - 35, - 27, - 38, - 53 - ], - [ - 33, - 59, - 48, - 16, - 60, - 39 - ], - [ - 9, - 57, - 10, - 11, - 7, - 44 - ], - [ - 13, - 0, - 59, - 61, - 19, - 33 - ], - [ - 18, - 39, - 58, - 21, - 50, - 36 - ], - [ - 57, - 60, - 49, - 19, - 53, - 54 - ], - [ - 6, - 36, - 17, - 41, - 38, - 10 - ], - [ - 63, - 1, - 57, - 20, - 36, - 31 - ], - [ - 40, - 41, - 7, - 0, - 61, - 50 - ], - [ - 39, - 16, - 23, - 14, - 17, - 59 - ], - [ - 51, - 30, - 50, - 7, - 3, - 61 - ], - [ - 9, - 14, - 42, - 15, - 1, - 55 - ], - [ - 33, - 19, - 58, - 56, - 11, - 15 - ], - [ - 9, - 26, - 4, - 41, - 51, - 10 - ], - [ - 4, - 60, - 35, - 16, - 47, - 48 - ], - [ - 3, - 52, - 31, - 16, - 26, - 39 - ], - [ - 4, - 1, - 3, - 61, - 0, - 54 - ] - ], - [ - [ - 38, - 45, - 52, - 43, - 32, - 39 - ], - [ - 29, - 48, - 22, - 60, - 55, - 57 - ], - [ - 5, - 49, - 8, - 20, - 14, - 55 - ], - [ - 35, - 46, - 49, - 32, - 7, - 48 - ], - [ - 24, - 29, - 49, - 16, - 41, - 0 - ], - [ - 56, - 3, - 35, - 15, - 4, - 40 - ], - [ - 49, - 59, - 4, - 16, - 33, - 11 - ], - [ - 12, - 51, - 30, - 56, - 40, - 0 - ], - [ - 23, - 46, - 14, - 55, - 42, - 17 - ], - [ - 60, - 22, - 38, - 20, - 21, - 6 - ], - [ - 11, - 19, - 31, - 35, - 38, - 22 - ], - [ - 59, - 48, - 19, - 33, - 16, - 9 - ], - [ - 9, - 28, - 45, - 57, - 55, - 53 - ], - [ - 59, - 1, - 25, - 6, - 47, - 3 - ], - [ - 9, - 39, - 18, - 50, - 58, - 21 - ], - [ - 60, - 57, - 38, - 53, - 50, - 54 - ], - [ - 41, - 53, - 6, - 38, - 10, - 25 - ], - [ - 1, - 63, - 15, - 24, - 36, - 29 - ], - [ - 41, - 40, - 7, - 42, - 62, - 32 - ], - [ - 4, - 10, - 14, - 37, - 39, - 17 - ], - [ - 61, - 30, - 7, - 50, - 3, - 57 - ], - [ - 9, - 58, - 14, - 30, - 42, - 8 - ], - [ - 33, - 56, - 19, - 60, - 24, - 59 - ], - [ - 9, - 26, - 4, - 51, - 25, - 41 - ], - [ - 4, - 35, - 47, - 41, - 48, - 60 - ], - [ - 39, - 52, - 3, - 26, - 8, - 30 - ], - [ - 4, - 1, - 61, - 10, - 0, - 12 - ] - ], - [ - [ - 51, - 53, - 33, - 13, - 28, - 48 - ], - [ - 63, - 31, - 41, - 39, - 40, - 49 - ], - [ - 42, - 14, - 3, - 24, - 50, - 44 - ], - [ - 11, - 39, - 52, - 10, - 17, - 42 - ], - [ - 7, - 60, - 58, - 15, - 12, - 27 - ], - [ - 34, - 36, - 44, - 28, - 29, - 40 - ], - [ - 52, - 17, - 47, - 42, - 37, - 41 - ], - [ - 62, - 33, - 38, - 39, - 41, - 36 - ], - [ - 50, - 58, - 37, - 10, - 16, - 62 - ], - [ - 41, - 59, - 12, - 62, - 49, - 32 - ], - [ - 3, - 35, - 10, - 41, - 6, - 25 - ], - [ - 39, - 58, - 53, - 21, - 19, - 54 - ], - [ - 42, - 35, - 7, - 39, - 63, - 21 - ], - [ - 15, - 9, - 3, - 54, - 51, - 32 - ], - [ - 3, - 35, - 32, - 36, - 18, - 13 - ], - [ - 20, - 3, - 35, - 45, - 32, - 4 - ], - [ - 46, - 24, - 32, - 33, - 14, - 44 - ], - [ - 16, - 57, - 30, - 19, - 61, - 63 - ], - [ - 41, - 25, - 2, - 24, - 26, - 4 - ], - [ - 42, - 37, - 47, - 48, - 33, - 50 - ], - [ - 54, - 13, - 53, - 56, - 63, - 0 - ], - [ - 34, - 14, - 7, - 51, - 42, - 6 - ], - [ - 6, - 43, - 25, - 58, - 11, - 39 - ], - [ - 0, - 26, - 44, - 4, - 51, - 9 - ], - [ - 37, - 63, - 4, - 15, - 13, - 61 - ], - [ - 9, - 59, - 41, - 1, - 52, - 25 - ], - [ - 56, - 58, - 47, - 1, - 62, - 51 - ] - ], - [ - [ - 52, - 47, - 27, - 36, - 38, - 33 - ], - [ - 43, - 56, - 4, - 25, - 52, - 21 - ], - [ - 25, - 54, - 35, - 18, - 11, - 63 - ], - [ - 17, - 4, - 1, - 18, - 50, - 39 - ], - [ - 36, - 7, - 32, - 4, - 30, - 60 - ], - [ - 34, - 3, - 61, - 48, - 24, - 40 - ], - [ - 17, - 42, - 3, - 12, - 29, - 25 - ], - [ - 33, - 38, - 39, - 55, - 17, - 19 - ], - [ - 17, - 50, - 41, - 16, - 13, - 51 - ], - [ - 41, - 12, - 60, - 16, - 62, - 58 - ], - [ - 3, - 36, - 28, - 39, - 35, - 2 - ], - [ - 48, - 27, - 53, - 54, - 19, - 43 - ], - [ - 42, - 63, - 7, - 35, - 62, - 3 - ], - [ - 56, - 15, - 9, - 51, - 42, - 47 - ], - [ - 35, - 39, - 18, - 36, - 43, - 38 - ], - [ - 60, - 20, - 0, - 3, - 35, - 31 - ], - [ - 24, - 14, - 42, - 17, - 32, - 62 - ], - [ - 24, - 57, - 63, - 0, - 42, - 1 - ], - [ - 41, - 50, - 28, - 26, - 32, - 40 - ], - [ - 2, - 37, - 46, - 17, - 42, - 57 - ], - [ - 49, - 7, - 11, - 54, - 13, - 53 - ], - [ - 14, - 9, - 42, - 6, - 19, - 58 - ], - [ - 63, - 33, - 43, - 19, - 58, - 25 - ], - [ - 9, - 26, - 48, - 4, - 41, - 44 - ], - [ - 4, - 41, - 5, - 15, - 9, - 53 - ], - [ - 14, - 38, - 9, - 3, - 52, - 8 - ], - [ - 23, - 1, - 61, - 4, - 47, - 14 - ] - ], - [ - [ - 37, - 14, - 3, - 5, - 33, - 53 - ], - [ - 34, - 0, - 56, - 58, - 37, - 13 - ], - [ - 61, - 14, - 22, - 29, - 15, - 46 - ], - [ - 50, - 58, - 18, - 44, - 47, - 17 - ], - [ - 4, - 36, - 53, - 7, - 32, - 2 - ], - [ - 22, - 1, - 34, - 61, - 10, - 33 - ], - [ - 28, - 25, - 42, - 29, - 30, - 3 - ], - [ - 33, - 51, - 38, - 39, - 62, - 60 - ], - [ - 17, - 51, - 26, - 16, - 46, - 50 - ], - [ - 12, - 16, - 59, - 6, - 38, - 3 - ], - [ - 39, - 31, - 35, - 3, - 36, - 0 - ], - [ - 48, - 19, - 53, - 56, - 39, - 27 - ], - [ - 7, - 62, - 9, - 63, - 15, - 42 - ], - [ - 56, - 19, - 9, - 47, - 15, - 59 - ], - [ - 39, - 36, - 18, - 35, - 42, - 38 - ], - [ - 60, - 20, - 0, - 57, - 47, - 53 - ], - [ - 6, - 32, - 29, - 62, - 43, - 5 - ], - [ - 24, - 63, - 57, - 1, - 42, - 2 - ], - [ - 41, - 40, - 7, - 62, - 32, - 50 - ], - [ - 37, - 14, - 10, - 2, - 57, - 17 - ], - [ - 7, - 54, - 30, - 53, - 50, - 49 - ], - [ - 9, - 14, - 8, - 6, - 42, - 58 - ], - [ - 33, - 60, - 56, - 19, - 18, - 15 - ], - [ - 9, - 26, - 4, - 41, - 59, - 23 - ], - [ - 4, - 41, - 47, - 9, - 2, - 16 - ], - [ - 3, - 14, - 16, - 30, - 8, - 52 - ], - [ - 1, - 4, - 14, - 61, - 0, - 12 - ] - ], - [ - [ - 27, - 21, - 61, - 30, - 22, - 63 - ], - [ - 33, - 12, - 0, - 44, - 47, - 27 - ], - [ - 14, - 39, - 58, - 37, - 16, - 63 - ], - [ - 11, - 47, - 4, - 18, - 32, - 35 - ], - [ - 2, - 19, - 20, - 39, - 61, - 10 - ], - [ - 39, - 61, - 49, - 15, - 30, - 47 - ], - [ - 12, - 47, - 17, - 49, - 29, - 46 - ], - [ - 39, - 41, - 62, - 30, - 52, - 37 - ], - [ - 21, - 15, - 28, - 48, - 26, - 27 - ], - [ - 12, - 32, - 16, - 24, - 55, - 41 - ], - [ - 43, - 35, - 28, - 31, - 60, - 47 - ], - [ - 3, - 17, - 19, - 49, - 10, - 42 - ], - [ - 7, - 54, - 27, - 59, - 4, - 23 - ], - [ - 35, - 33, - 7, - 6, - 14, - 51 - ], - [ - 51, - 28, - 36, - 42, - 41, - 32 - ], - [ - 45, - 52, - 60, - 21, - 53, - 12 - ], - [ - 53, - 1, - 23, - 5, - 6, - 46 - ], - [ - 35, - 9, - 63, - 16, - 26, - 22 - ], - [ - 41, - 60, - 27, - 40, - 42, - 15 - ], - [ - 42, - 48, - 46, - 9, - 17, - 37 - ], - [ - 31, - 11, - 23, - 58, - 39, - 44 - ], - [ - 34, - 2, - 4, - 14, - 13, - 9 - ], - [ - 60, - 59, - 47, - 40, - 17, - 27 - ], - [ - 60, - 63, - 7, - 42, - 40, - 49 - ], - [ - 59, - 15, - 38, - 62, - 44, - 25 - ], - [ - 23, - 57, - 60, - 31, - 41, - 3 - ], - [ - 38, - 59, - 31, - 51, - 36, - 7 - ] - ], - [ - [ - 12, - 10, - 50, - 23, - 53, - 33 - ], - [ - 48, - 56, - 44, - 11, - 31, - 17 - ], - [ - 33, - 11, - 17, - 54, - 15, - 62 - ], - [ - 31, - 13, - 17, - 40, - 8, - 3 - ], - [ - 42, - 4, - 27, - 15, - 12, - 5 - ], - [ - 34, - 50, - 33, - 29, - 55, - 6 - ], - [ - 16, - 49, - 52, - 29, - 41, - 30 - ], - [ - 0, - 30, - 62, - 57, - 26, - 22 - ], - [ - 26, - 62, - 5, - 58, - 51, - 49 - ], - [ - 5, - 62, - 16, - 46, - 39, - 37 - ], - [ - 31, - 10, - 24, - 45, - 18, - 35 - ], - [ - 50, - 19, - 48, - 54, - 16, - 35 - ], - [ - 59, - 39, - 62, - 7, - 35, - 28 - ], - [ - 20, - 9, - 19, - 6, - 22, - 15 - ], - [ - 35, - 18, - 50, - 32, - 36, - 39 - ], - [ - 3, - 4, - 13, - 38, - 60, - 26 - ], - [ - 46, - 32, - 28, - 3, - 37, - 33 - ], - [ - 30, - 63, - 16, - 19, - 24, - 42 - ], - [ - 41, - 17, - 4, - 2, - 32, - 34 - ], - [ - 42, - 37, - 48, - 33, - 3, - 31 - ], - [ - 56, - 7, - 25, - 11, - 39, - 44 - ], - [ - 42, - 14, - 26, - 13, - 12, - 22 - ], - [ - 25, - 11, - 6, - 42, - 13, - 38 - ], - [ - 0, - 9, - 26, - 41, - 4, - 57 - ], - [ - 37, - 4, - 63, - 41, - 2, - 44 - ], - [ - 9, - 52, - 41, - 3, - 16, - 59 - ], - [ - 58, - 47, - 56, - 1, - 26, - 62 - ] - ], - [ - [ - 54, - 30, - 22, - 26, - 3, - 55 - ], - [ - 44, - 7, - 49, - 50, - 25, - 5 - ], - [ - 54, - 4, - 48, - 58, - 26, - 32 - ], - [ - 20, - 25, - 3, - 9, - 55, - 28 - ], - [ - 7, - 27, - 42, - 12, - 58, - 32 - ], - [ - 50, - 34, - 6, - 42, - 29, - 55 - ], - [ - 16, - 49, - 40, - 3, - 27, - 11 - ], - [ - 51, - 30, - 26, - 62, - 53, - 57 - ], - [ - 3, - 5, - 49, - 28, - 26, - 50 - ], - [ - 16, - 22, - 46, - 6, - 49, - 45 - ], - [ - 31, - 45, - 11, - 10, - 56, - 3 - ], - [ - 21, - 51, - 50, - 19, - 9, - 61 - ], - [ - 28, - 41, - 59, - 13, - 34, - 53 - ], - [ - 23, - 20, - 16, - 9, - 38, - 19 - ], - [ - 50, - 34, - 58, - 20, - 27, - 35 - ], - [ - 8, - 59, - 29, - 61, - 35, - 53 - ], - [ - 3, - 46, - 51, - 10, - 25, - 18 - ], - [ - 62, - 21, - 25, - 9, - 18, - 56 - ], - [ - 34, - 27, - 10, - 29, - 53, - 59 - ], - [ - 56, - 33, - 44, - 24, - 9, - 18 - ], - [ - 44, - 12, - 19, - 30, - 8, - 49 - ], - [ - 47, - 62, - 51, - 12, - 16, - 17 - ], - [ - 11, - 37, - 25, - 58, - 35, - 45 - ], - [ - 4, - 9, - 10, - 39, - 37, - 16 - ], - [ - 22, - 11, - 21, - 48, - 45, - 47 - ], - [ - 10, - 28, - 55, - 12, - 24, - 23 - ], - [ - 30, - 16, - 27, - 32, - 57, - 15 - ] - ], - [ - [ - 16, - 11, - 31, - 46, - 35, - 49 - ], - [ - 13, - 49, - 54, - 5, - 6, - 14 - ], - [ - 36, - 13, - 27, - 46, - 3, - 18 - ], - [ - 24, - 61, - 15, - 0, - 63, - 13 - ], - [ - 17, - 2, - 58, - 50, - 35, - 19 - ], - [ - 8, - 7, - 49, - 52, - 47, - 23 - ], - [ - 61, - 58, - 16, - 20, - 38, - 23 - ], - [ - 20, - 42, - 9, - 51, - 35, - 16 - ], - [ - 25, - 47, - 4, - 1, - 50, - 63 - ], - [ - 54, - 38, - 57, - 27, - 33, - 28 - ], - [ - 1, - 59, - 30, - 60, - 14, - 31 - ], - [ - 31, - 51, - 25, - 14, - 41, - 55 - ], - [ - 34, - 2, - 14, - 11, - 19, - 28 - ], - [ - 40, - 57, - 13, - 61, - 59, - 18 - ], - [ - 44, - 58, - 50, - 45, - 37, - 26 - ], - [ - 5, - 8, - 62, - 24, - 38, - 61 - ], - [ - 13, - 47, - 15, - 3, - 5, - 39 - ], - [ - 25, - 23, - 9, - 18, - 63, - 33 - ], - [ - 1, - 10, - 20, - 8, - 53, - 4 - ], - [ - 38, - 15, - 37, - 0, - 34, - 60 - ], - [ - 41, - 27, - 30, - 57, - 19, - 40 - ], - [ - 11, - 62, - 41, - 14, - 46, - 44 - ], - [ - 11, - 33, - 46, - 31, - 45, - 0 - ], - [ - 4, - 51, - 47, - 16, - 9, - 12 - ], - [ - 11, - 21, - 45, - 47, - 61, - 18 - ], - [ - 10, - 50, - 51, - 12, - 18, - 3 - ], - [ - 17, - 31, - 36, - 5, - 19, - 1 - ] - ], - [ - [ - 22, - 6, - 39, - 57, - 29, - 47 - ], - [ - 27, - 6, - 14, - 17, - 51, - 32 - ], - [ - 1, - 29, - 11, - 26, - 47, - 51 - ], - [ - 14, - 38, - 22, - 31, - 29, - 53 - ], - [ - 14, - 61, - 59, - 1, - 29, - 49 - ], - [ - 30, - 8, - 21, - 47, - 52, - 0 - ], - [ - 4, - 58, - 61, - 23, - 20, - 29 - ], - [ - 20, - 9, - 42, - 35, - 7, - 24 - ], - [ - 47, - 25, - 4, - 1, - 29, - 0 - ], - [ - 54, - 19, - 38, - 29, - 33, - 44 - ], - [ - 14, - 1, - 59, - 40, - 60, - 20 - ], - [ - 51, - 0, - 14, - 62, - 16, - 52 - ], - [ - 2, - 36, - 20, - 29, - 19, - 52 - ], - [ - 57, - 13, - 40, - 22, - 60, - 6 - ], - [ - 37, - 44, - 58, - 8, - 5, - 50 - ], - [ - 24, - 5, - 43, - 62, - 23, - 59 - ], - [ - 13, - 19, - 47, - 39, - 61, - 15 - ], - [ - 58, - 33, - 9, - 7, - 4, - 28 - ], - [ - 1, - 35, - 10, - 19, - 31, - 20 - ], - [ - 15, - 55, - 63, - 18, - 34, - 38 - ], - [ - 27, - 15, - 38, - 30, - 57, - 42 - ], - [ - 62, - 41, - 16, - 29, - 6, - 46 - ], - [ - 46, - 33, - 45, - 10, - 34, - 23 - ], - [ - 4, - 47, - 51, - 1, - 16, - 41 - ], - [ - 11, - 21, - 18, - 7, - 48, - 28 - ], - [ - 18, - 51, - 7, - 50, - 6, - 32 - ], - [ - 9, - 11, - 36, - 55, - 43, - 48 - ] - ], - [ - [ - 47, - 8, - 36, - 61, - 21, - 45 - ], - [ - 46, - 2, - 15, - 32, - 0, - 51 - ], - [ - 24, - 15, - 33, - 61, - 2, - 43 - ], - [ - 60, - 22, - 31, - 27, - 14, - 11 - ], - [ - 59, - 58, - 39, - 57, - 46, - 3 - ], - [ - 57, - 43, - 2, - 31, - 7, - 62 - ], - [ - 9, - 42, - 54, - 19, - 4, - 55 - ], - [ - 46, - 14, - 7, - 24, - 43, - 35 - ], - [ - 47, - 4, - 0, - 37, - 12, - 13 - ], - [ - 54, - 38, - 4, - 47, - 25, - 6 - ], - [ - 47, - 14, - 15, - 24, - 1, - 61 - ], - [ - 16, - 54, - 5, - 0, - 7, - 63 - ], - [ - 49, - 3, - 33, - 11, - 13, - 10 - ], - [ - 18, - 13, - 10, - 39, - 58, - 63 - ], - [ - 58, - 62, - 10, - 33, - 5, - 26 - ], - [ - 48, - 5, - 63, - 53, - 43, - 2 - ], - [ - 24, - 47, - 51, - 15, - 53, - 0 - ], - [ - 44, - 0, - 6, - 3, - 34, - 7 - ], - [ - 58, - 38, - 53, - 61, - 0, - 54 - ], - [ - 55, - 9, - 15, - 27, - 42, - 34 - ], - [ - 43, - 1, - 51, - 41, - 4, - 5 - ], - [ - 49, - 20, - 15, - 6, - 37, - 46 - ], - [ - 11, - 25, - 52, - 5, - 4, - 39 - ], - [ - 38, - 40, - 44, - 51, - 10, - 14 - ], - [ - 13, - 8, - 52, - 63, - 2, - 23 - ], - [ - 23, - 38, - 59, - 57, - 55, - 41 - ], - [ - 23, - 6, - 62, - 0, - 7, - 28 - ] - ], - [ - [ - 41, - 2, - 42, - 16, - 50, - 61 - ], - [ - 51, - 41, - 5, - 15, - 61, - 63 - ], - [ - 43, - 1, - 29, - 21, - 55, - 60 - ], - [ - 24, - 53, - 25, - 13, - 51, - 32 - ], - [ - 31, - 41, - 57, - 49, - 34, - 11 - ], - [ - 17, - 4, - 35, - 30, - 10, - 38 - ], - [ - 34, - 7, - 56, - 42, - 19, - 21 - ], - [ - 14, - 46, - 7, - 27, - 25, - 52 - ], - [ - 0, - 4, - 6, - 12, - 60, - 47 - ], - [ - 54, - 25, - 4, - 38, - 47, - 6 - ], - [ - 24, - 61, - 15, - 46, - 7, - 22 - ], - [ - 5, - 16, - 57, - 0, - 22, - 55 - ], - [ - 49, - 3, - 26, - 17, - 57, - 52 - ], - [ - 13, - 10, - 61, - 60, - 0, - 58 - ], - [ - 58, - 6, - 49, - 10, - 5, - 2 - ], - [ - 25, - 19, - 2, - 11, - 54, - 53 - ], - [ - 47, - 30, - 27, - 18, - 5, - 10 - ], - [ - 44, - 55, - 63, - 13, - 22, - 31 - ], - [ - 58, - 7, - 38, - 35, - 32, - 40 - ], - [ - 13, - 34, - 16, - 49, - 45, - 55 - ], - [ - 51, - 35, - 30, - 58, - 55, - 4 - ], - [ - 46, - 57, - 15, - 36, - 30, - 27 - ], - [ - 52, - 33, - 23, - 51, - 10, - 15 - ], - [ - 40, - 41, - 4, - 51, - 10, - 31 - ], - [ - 8, - 4, - 59, - 48, - 34, - 9 - ], - [ - 43, - 3, - 27, - 26, - 19, - 31 - ], - [ - 46, - 18, - 8, - 4, - 50, - 40 - ] - ], - [ - [ - 22, - 36, - 35, - 63, - 43, - 23 - ], - [ - 54, - 30, - 4, - 36, - 35, - 55 - ], - [ - 28, - 19, - 23, - 49, - 50, - 59 - ], - [ - 62, - 5, - 50, - 53, - 42, - 48 - ], - [ - 0, - 3, - 61, - 57, - 41, - 49 - ], - [ - 60, - 4, - 29, - 16, - 53, - 30 - ], - [ - 34, - 32, - 33, - 9, - 56, - 35 - ], - [ - 12, - 53, - 14, - 36, - 25, - 61 - ], - [ - 23, - 48, - 35, - 29, - 4, - 16 - ], - [ - 22, - 25, - 4, - 54, - 62, - 5 - ], - [ - 24, - 19, - 27, - 55, - 47, - 25 - ], - [ - 18, - 5, - 22, - 34, - 63, - 30 - ], - [ - 17, - 49, - 30, - 28, - 11, - 42 - ], - [ - 29, - 13, - 54, - 25, - 45, - 47 - ], - [ - 27, - 2, - 10, - 5, - 54, - 33 - ], - [ - 55, - 2, - 57, - 54, - 56, - 22 - ], - [ - 53, - 3, - 60, - 27, - 5, - 50 - ], - [ - 17, - 8, - 47, - 50, - 61, - 44 - ], - [ - 27, - 38, - 32, - 14, - 61, - 39 - ], - [ - 22, - 43, - 32, - 57, - 39, - 34 - ], - [ - 16, - 44, - 37, - 23, - 61, - 27 - ], - [ - 45, - 40, - 55, - 32, - 31, - 3 - ], - [ - 32, - 28, - 41, - 15, - 1, - 52 - ], - [ - 21, - 22, - 31, - 10, - 4, - 40 - ], - [ - 29, - 35, - 62, - 60, - 41, - 1 - ], - [ - 39, - 58, - 1, - 63, - 3, - 35 - ], - [ - 10, - 62, - 31, - 45, - 27, - 7 - ] - ], - [ - [ - 17, - 5, - 10, - 57, - 14, - 27 - ], - [ - 43, - 9, - 33, - 56, - 1, - 20 - ], - [ - 63, - 1, - 35, - 43, - 27, - 10 - ], - [ - 47, - 6, - 18, - 3, - 38, - 15 - ], - [ - 11, - 51, - 61, - 34, - 44, - 55 - ], - [ - 10, - 63, - 53, - 60, - 37, - 58 - ], - [ - 51, - 45, - 63, - 34, - 18, - 60 - ], - [ - 35, - 51, - 52, - 53, - 38, - 45 - ], - [ - 44, - 0, - 35, - 55, - 38, - 9 - ], - [ - 42, - 43, - 5, - 25, - 21, - 6 - ], - [ - 42, - 13, - 25, - 27, - 38, - 32 - ], - [ - 43, - 5, - 1, - 38, - 22, - 2 - ], - [ - 17, - 49, - 0, - 37, - 28, - 30 - ], - [ - 2, - 29, - 54, - 5, - 13, - 60 - ], - [ - 2, - 27, - 10, - 58, - 40, - 28 - ], - [ - 2, - 49, - 54, - 62, - 53, - 57 - ], - [ - 60, - 53, - 12, - 27, - 28, - 0 - ], - [ - 17, - 50, - 51, - 33, - 3, - 11 - ], - [ - 38, - 5, - 19, - 13, - 27, - 32 - ], - [ - 43, - 22, - 0, - 57, - 40, - 63 - ], - [ - 27, - 16, - 35, - 52, - 38, - 39 - ], - [ - 29, - 31, - 55, - 40, - 62, - 27 - ], - [ - 41, - 48, - 32, - 46, - 40, - 57 - ], - [ - 20, - 54, - 4, - 22, - 0, - 55 - ], - [ - 62, - 23, - 25, - 33, - 28, - 20 - ], - [ - 7, - 18, - 60, - 22, - 58, - 3 - ], - [ - 36, - 9, - 11, - 0, - 48, - 31 - ] - ], - [ - [ - 11, - 59, - 52, - 28, - 6, - 45 - ], - [ - 7, - 23, - 15, - 17, - 55, - 61 - ], - [ - 28, - 0, - 29, - 46, - 58, - 14 - ], - [ - 62, - 49, - 39, - 18, - 6, - 13 - ], - [ - 36, - 61, - 7, - 40, - 35, - 33 - ], - [ - 8, - 16, - 29, - 43, - 57, - 37 - ], - [ - 30, - 19, - 21, - 57, - 42, - 14 - ], - [ - 35, - 11, - 51, - 53, - 36, - 33 - ], - [ - 48, - 35, - 4, - 50, - 11, - 40 - ], - [ - 5, - 3, - 21, - 47, - 43, - 38 - ], - [ - 21, - 18, - 2, - 6, - 3, - 10 - ], - [ - 18, - 43, - 5, - 45, - 22, - 4 - ], - [ - 33, - 28, - 37, - 41, - 49, - 10 - ], - [ - 56, - 55, - 22, - 54, - 62, - 15 - ], - [ - 2, - 27, - 57, - 5, - 63, - 19 - ], - [ - 55, - 8, - 54, - 2, - 59, - 48 - ], - [ - 0, - 53, - 51, - 3, - 50, - 52 - ], - [ - 21, - 12, - 62, - 60, - 18, - 43 - ], - [ - 38, - 13, - 53, - 27, - 14, - 28 - ], - [ - 22, - 56, - 44, - 43, - 51, - 53 - ], - [ - 29, - 43, - 12, - 16, - 41, - 52 - ], - [ - 51, - 1, - 35, - 44, - 48, - 34 - ], - [ - 32, - 25, - 4, - 41, - 53, - 54 - ], - [ - 48, - 17, - 25, - 60, - 1, - 62 - ], - [ - 22, - 29, - 5, - 18, - 53, - 20 - ], - [ - 28, - 55, - 15, - 1, - 49, - 8 - ], - [ - 30, - 57, - 6, - 7, - 31, - 50 - ] - ], - [ - [ - 11, - 16, - 31, - 46, - 35, - 49 - ], - [ - 13, - 49, - 31, - 16, - 34, - 19 - ], - [ - 36, - 13, - 27, - 34, - 42, - 18 - ], - [ - 24, - 7, - 61, - 63, - 34, - 15 - ], - [ - 17, - 35, - 2, - 48, - 44, - 62 - ], - [ - 8, - 7, - 23, - 47, - 51, - 26 - ], - [ - 61, - 58, - 29, - 38, - 33, - 45 - ], - [ - 20, - 9, - 42, - 27, - 54, - 29 - ], - [ - 25, - 47, - 34, - 1, - 4, - 38 - ], - [ - 54, - 27, - 57, - 4, - 28, - 25 - ], - [ - 1, - 30, - 59, - 60, - 17, - 22 - ], - [ - 31, - 25, - 51, - 5, - 58, - 49 - ], - [ - 34, - 2, - 19, - 14, - 29, - 36 - ], - [ - 57, - 40, - 6, - 9, - 32, - 14 - ], - [ - 8, - 44, - 34, - 16, - 45, - 47 - ], - [ - 8, - 25, - 40, - 37, - 14, - 59 - ], - [ - 13, - 15, - 19, - 51, - 25, - 37 - ], - [ - 23, - 4, - 44, - 52, - 19, - 15 - ], - [ - 8, - 10, - 45, - 1, - 31, - 53 - ], - [ - 38, - 59, - 15, - 2, - 34, - 0 - ], - [ - 41, - 25, - 57, - 55, - 27, - 24 - ], - [ - 11, - 44, - 41, - 39, - 62, - 53 - ], - [ - 31, - 30, - 42, - 37, - 34, - 59 - ], - [ - 33, - 14, - 12, - 10, - 54, - 51 - ], - [ - 11, - 18, - 45, - 20, - 33, - 48 - ], - [ - 50, - 51, - 54, - 23, - 10, - 2 - ], - [ - 17, - 31, - 37, - 5, - 19, - 36 - ] - ], - [ - [ - 31, - 35, - 51, - 18, - 53, - 61 - ], - [ - 7, - 40, - 39, - 41, - 31, - 37 - ], - [ - 16, - 29, - 26, - 50, - 33, - 10 - ], - [ - 14, - 22, - 37, - 17, - 6, - 25 - ], - [ - 60, - 20, - 46, - 4, - 3, - 57 - ], - [ - 11, - 2, - 49, - 21, - 27, - 9 - ], - [ - 42, - 30, - 54, - 58, - 19, - 56 - ], - [ - 46, - 33, - 38, - 41, - 35, - 49 - ], - [ - 50, - 48, - 57, - 37, - 38, - 10 - ], - [ - 59, - 63, - 47, - 28, - 10, - 35 - ], - [ - 28, - 3, - 2, - 52, - 33, - 43 - ], - [ - 58, - 19, - 17, - 16, - 57, - 63 - ], - [ - 42, - 4, - 3, - 23, - 45, - 57 - ], - [ - 18, - 42, - 51, - 3, - 20, - 15 - ], - [ - 32, - 43, - 12, - 3, - 0, - 35 - ], - [ - 15, - 45, - 4, - 7, - 53, - 21 - ], - [ - 28, - 24, - 14, - 60, - 15, - 31 - ], - [ - 26, - 30, - 0, - 54, - 5, - 36 - ], - [ - 63, - 37, - 11, - 41, - 51, - 4 - ], - [ - 48, - 9, - 33, - 0, - 54, - 42 - ], - [ - 22, - 60, - 11, - 39, - 1, - 49 - ], - [ - 17, - 4, - 34, - 2, - 27, - 53 - ], - [ - 17, - 6, - 40, - 58, - 42, - 39 - ], - [ - 60, - 54, - 35, - 39, - 0, - 32 - ], - [ - 44, - 15, - 37, - 13, - 8, - 25 - ], - [ - 41, - 59, - 25, - 45, - 13, - 6 - ], - [ - 23, - 58, - 13, - 19, - 62, - 29 - ] - ], - [ - [ - 52, - 47, - 27, - 36, - 38, - 33 - ], - [ - 43, - 56, - 4, - 21, - 25, - 6 - ], - [ - 25, - 54, - 35, - 18, - 11, - 57 - ], - [ - 17, - 16, - 1, - 6, - 33, - 45 - ], - [ - 32, - 36, - 0, - 60, - 46, - 57 - ], - [ - 11, - 1, - 3, - 21, - 2, - 34 - ], - [ - 30, - 42, - 58, - 54, - 19, - 13 - ], - [ - 33, - 35, - 56, - 42, - 38, - 54 - ], - [ - 50, - 37, - 36, - 14, - 11, - 20 - ], - [ - 10, - 28, - 32, - 4, - 37, - 6 - ], - [ - 3, - 18, - 28, - 61, - 44, - 2 - ], - [ - 16, - 19, - 56, - 27, - 46, - 43 - ], - [ - 42, - 33, - 57, - 3, - 58, - 26 - ], - [ - 56, - 15, - 51, - 55, - 50, - 13 - ], - [ - 12, - 43, - 40, - 13, - 16, - 29 - ], - [ - 15, - 7, - 28, - 53, - 5, - 20 - ], - [ - 14, - 58, - 24, - 60, - 31, - 51 - ], - [ - 0, - 60, - 3, - 44, - 24, - 19 - ], - [ - 36, - 28, - 11, - 63, - 53, - 15 - ], - [ - 2, - 26, - 9, - 34, - 0, - 3 - ], - [ - 49, - 28, - 43, - 41, - 30, - 11 - ], - [ - 25, - 51, - 12, - 6, - 61, - 16 - ], - [ - 39, - 17, - 41, - 50, - 40, - 21 - ], - [ - 25, - 58, - 48, - 12, - 60, - 33 - ], - [ - 31, - 49, - 5, - 52, - 63, - 3 - ], - [ - 15, - 55, - 38, - 47, - 1, - 49 - ], - [ - 23, - 6, - 32, - 19, - 62, - 7 - ] - ], - [ - [ - 44, - 24, - 56, - 33, - 15, - 7 - ], - [ - 38, - 26, - 24, - 29, - 53, - 19 - ], - [ - 12, - 15, - 29, - 9, - 1, - 63 - ], - [ - 38, - 61, - 58, - 50, - 45, - 6 - ], - [ - 24, - 34, - 4, - 36, - 57, - 31 - ], - [ - 1, - 22, - 43, - 21, - 10, - 7 - ], - [ - 20, - 19, - 54, - 58, - 18, - 42 - ], - [ - 56, - 33, - 14, - 21, - 51, - 18 - ], - [ - 60, - 50, - 14, - 36, - 4, - 43 - ], - [ - 6, - 10, - 9, - 63, - 4, - 38 - ], - [ - 54, - 39, - 46, - 18, - 3, - 2 - ], - [ - 30, - 16, - 37, - 53, - 56, - 43 - ], - [ - 56, - 10, - 42, - 58, - 57, - 23 - ], - [ - 30, - 56, - 13, - 51, - 50, - 0 - ], - [ - 55, - 40, - 12, - 62, - 13, - 30 - ], - [ - 53, - 28, - 11, - 61, - 7, - 19 - ], - [ - 55, - 14, - 17, - 47, - 30, - 5 - ], - [ - 18, - 31, - 20, - 60, - 57, - 32 - ], - [ - 43, - 12, - 53, - 26, - 32, - 61 - ], - [ - 25, - 16, - 26, - 61, - 3, - 53 - ], - [ - 49, - 28, - 51, - 59, - 55, - 11 - ], - [ - 56, - 6, - 32, - 14, - 10, - 21 - ], - [ - 2, - 15, - 58, - 17, - 13, - 62 - ], - [ - 53, - 51, - 4, - 29, - 50, - 25 - ], - [ - 61, - 31, - 15, - 19, - 60, - 49 - ], - [ - 46, - 44, - 19, - 36, - 8, - 40 - ], - [ - 8, - 29, - 46, - 7, - 53, - 20 - ] - ], - [ - [ - 48, - 42, - 38, - 63, - 50, - 62 - ], - [ - 3, - 2, - 40, - 33, - 14, - 61 - ], - [ - 39, - 7, - 45, - 40, - 6, - 44 - ], - [ - 41, - 5, - 20, - 56, - 13, - 0 - ], - [ - 6, - 37, - 30, - 1, - 38, - 52 - ], - [ - 59, - 46, - 4, - 22, - 5, - 6 - ], - [ - 20, - 1, - 44, - 35, - 13, - 3 - ], - [ - 12, - 56, - 8, - 50, - 31, - 2 - ], - [ - 33, - 60, - 41, - 43, - 37, - 52 - ], - [ - 9, - 10, - 0, - 55, - 40, - 37 - ], - [ - 54, - 39, - 9, - 8, - 61, - 46 - ], - [ - 30, - 56, - 53, - 24, - 16, - 59 - ], - [ - 7, - 58, - 57, - 42, - 52, - 10 - ], - [ - 27, - 30, - 42, - 25, - 59, - 13 - ], - [ - 9, - 11, - 49, - 55, - 61, - 0 - ], - [ - 34, - 53, - 54, - 57, - 29, - 37 - ], - [ - 55, - 17, - 5, - 60, - 31, - 16 - ], - [ - 57, - 48, - 43, - 39, - 32, - 36 - ], - [ - 12, - 43, - 42, - 49, - 7, - 61 - ], - [ - 23, - 36, - 1, - 0, - 16, - 61 - ], - [ - 21, - 35, - 11, - 31, - 55, - 7 - ], - [ - 60, - 8, - 24, - 42, - 6, - 14 - ], - [ - 15, - 51, - 58, - 2, - 33, - 30 - ], - [ - 52, - 51, - 4, - 28, - 21, - 19 - ], - [ - 19, - 60, - 15, - 34, - 54, - 61 - ], - [ - 19, - 61, - 58, - 40, - 12, - 3 - ], - [ - 35, - 49, - 54, - 53, - 1, - 25 - ] - ], - [ - [ - 47, - 37, - 59, - 38, - 33, - 10 - ], - [ - 36, - 7, - 21, - 51, - 8, - 47 - ], - [ - 20, - 32, - 44, - 47, - 4, - 54 - ], - [ - 43, - 20, - 42, - 52, - 8, - 19 - ], - [ - 27, - 9, - 39, - 57, - 12, - 54 - ], - [ - 28, - 16, - 29, - 11, - 61, - 58 - ], - [ - 42, - 2, - 30, - 43, - 28, - 25 - ], - [ - 36, - 18, - 32, - 21, - 53, - 15 - ], - [ - 50, - 12, - 37, - 48, - 14, - 52 - ], - [ - 9, - 10, - 13, - 3, - 58, - 26 - ], - [ - 18, - 3, - 9, - 55, - 6, - 61 - ], - [ - 30, - 18, - 45, - 60, - 16, - 24 - ], - [ - 35, - 5, - 42, - 26, - 37, - 58 - ], - [ - 55, - 38, - 15, - 13, - 14, - 54 - ], - [ - 9, - 62, - 2, - 17, - 13, - 26 - ], - [ - 9, - 40, - 37, - 54, - 17, - 47 - ], - [ - 22, - 60, - 51, - 14, - 40, - 39 - ], - [ - 17, - 3, - 21, - 30, - 36, - 25 - ], - [ - 56, - 41, - 24, - 9, - 43, - 14 - ], - [ - 53, - 34, - 26, - 22, - 12, - 35 - ], - [ - 36, - 42, - 24, - 37, - 8, - 4 - ], - [ - 34, - 16, - 8, - 46, - 56, - 6 - ], - [ - 31, - 10, - 16, - 41, - 56, - 32 - ], - [ - 48, - 0, - 42, - 56, - 31, - 30 - ], - [ - 6, - 54, - 26, - 19, - 8, - 7 - ], - [ - 49, - 22, - 6, - 13, - 24, - 59 - ], - [ - 45, - 62, - 27, - 47, - 50, - 7 - ] - ], - [ - [ - 45, - 37, - 48, - 29, - 30, - 3 - ], - [ - 8, - 60, - 59, - 43, - 10, - 48 - ], - [ - 51, - 45, - 28, - 34, - 59, - 63 - ], - [ - 43, - 2, - 38, - 12, - 20, - 4 - ], - [ - 50, - 57, - 39, - 31, - 0, - 63 - ], - [ - 58, - 53, - 18, - 9, - 30, - 21 - ], - [ - 63, - 51, - 34, - 7, - 20, - 27 - ], - [ - 32, - 21, - 46, - 47, - 25, - 18 - ], - [ - 6, - 12, - 50, - 14, - 33, - 37 - ], - [ - 30, - 10, - 9, - 6, - 13, - 29 - ], - [ - 61, - 18, - 23, - 20, - 44, - 6 - ], - [ - 16, - 12, - 30, - 60, - 0, - 62 - ], - [ - 5, - 26, - 57, - 35, - 37, - 61 - ], - [ - 46, - 55, - 13, - 1, - 17, - 31 - ], - [ - 6, - 10, - 2, - 22, - 16, - 15 - ], - [ - 6, - 54, - 19, - 57, - 25, - 46 - ], - [ - 2, - 30, - 60, - 61, - 18, - 49 - ], - [ - 17, - 4, - 2, - 27, - 3, - 44 - ], - [ - 56, - 46, - 8, - 32, - 6, - 14 - ], - [ - 5, - 13, - 0, - 34, - 14, - 30 - ], - [ - 15, - 23, - 59, - 57, - 27, - 53 - ], - [ - 36, - 61, - 8, - 43, - 57, - 37 - ], - [ - 10, - 15, - 52, - 31, - 29, - 23 - ], - [ - 10, - 38, - 1, - 4, - 57, - 31 - ], - [ - 8, - 32, - 54, - 33, - 3, - 50 - ], - [ - 6, - 33, - 19, - 50, - 2, - 3 - ], - [ - 55, - 43, - 4, - 5, - 25, - 8 - ] - ], - [ - [ - 25, - 14, - 18, - 49, - 51, - 63 - ], - [ - 42, - 21, - 30, - 24, - 43, - 7 - ], - [ - 54, - 39, - 9, - 59, - 28, - 49 - ], - [ - 23, - 1, - 55, - 45, - 43, - 40 - ], - [ - 37, - 30, - 41, - 59, - 21, - 44 - ], - [ - 4, - 41, - 31, - 35, - 19, - 14 - ], - [ - 28, - 55, - 44, - 63, - 9, - 51 - ], - [ - 63, - 12, - 32, - 13, - 47, - 28 - ], - [ - 33, - 12, - 59, - 35, - 6, - 39 - ], - [ - 30, - 40, - 10, - 29, - 52, - 13 - ], - [ - 63, - 19, - 23, - 61, - 8, - 55 - ], - [ - 45, - 62, - 22, - 12, - 38, - 42 - ], - [ - 58, - 26, - 57, - 20, - 45, - 30 - ], - [ - 8, - 59, - 47, - 17, - 25, - 22 - ], - [ - 24, - 54, - 51, - 55, - 10, - 15 - ], - [ - 46, - 22, - 54, - 44, - 57, - 40 - ], - [ - 34, - 60, - 2, - 17, - 27, - 18 - ], - [ - 51, - 6, - 61, - 2, - 39, - 32 - ], - [ - 5, - 49, - 38, - 8, - 32, - 46 - ], - [ - 57, - 4, - 23, - 14, - 59, - 5 - ], - [ - 7, - 36, - 45, - 21, - 53, - 4 - ], - [ - 60, - 3, - 39, - 8, - 14, - 35 - ], - [ - 33, - 57, - 60, - 47, - 15, - 53 - ], - [ - 20, - 11, - 22, - 19, - 58, - 4 - ], - [ - 36, - 34, - 47, - 41, - 60, - 1 - ], - [ - 33, - 3, - 49, - 29, - 59, - 14 - ], - [ - 52, - 60, - 24, - 25, - 35, - 34 - ] - ], - [ - [ - 23, - 54, - 53, - 11, - 58, - 8 - ], - [ - 11, - 30, - 15, - 63, - 59, - 49 - ], - [ - 20, - 58, - 29, - 17, - 52, - 30 - ], - [ - 18, - 1, - 43, - 15, - 3, - 8 - ], - [ - 59, - 55, - 13, - 20, - 44, - 30 - ], - [ - 33, - 45, - 27, - 53, - 63, - 52 - ], - [ - 28, - 57, - 51, - 34, - 53, - 31 - ], - [ - 13, - 63, - 3, - 32, - 44, - 45 - ], - [ - 59, - 33, - 12, - 57, - 6, - 35 - ], - [ - 23, - 30, - 42, - 10, - 29, - 13 - ], - [ - 63, - 23, - 18, - 42, - 38, - 19 - ], - [ - 62, - 2, - 22, - 19, - 45, - 23 - ], - [ - 6, - 20, - 26, - 30, - 5, - 25 - ], - [ - 3, - 6, - 8, - 31, - 17, - 37 - ], - [ - 59, - 2, - 10, - 54, - 55, - 19 - ], - [ - 45, - 7, - 57, - 42, - 54, - 46 - ], - [ - 60, - 55, - 53, - 34, - 41, - 18 - ], - [ - 36, - 2, - 8, - 31, - 32, - 46 - ], - [ - 36, - 45, - 38, - 32, - 61, - 25 - ], - [ - 57, - 39, - 6, - 18, - 19, - 30 - ], - [ - 37, - 39, - 59, - 48, - 53, - 14 - ], - [ - 52, - 28, - 46, - 55, - 47, - 61 - ], - [ - 1, - 28, - 52, - 15, - 31, - 18 - ], - [ - 22, - 21, - 8, - 10, - 46, - 39 - ], - [ - 45, - 41, - 35, - 9, - 54, - 33 - ], - [ - 42, - 39, - 58, - 61, - 24, - 3 - ], - [ - 41, - 46, - 10, - 3, - 15, - 33 - ] - ], - [ - [ - 48, - 38, - 63, - 42, - 47, - 7 - ], - [ - 3, - 10, - 26, - 2, - 6, - 62 - ], - [ - 39, - 7, - 44, - 6, - 45, - 40 - ], - [ - 21, - 39, - 1, - 18, - 15, - 57 - ], - [ - 6, - 33, - 9, - 13, - 3, - 27 - ], - [ - 59, - 33, - 53, - 62, - 21, - 45 - ], - [ - 28, - 10, - 33, - 7, - 57, - 50 - ], - [ - 50, - 13, - 12, - 49, - 3, - 55 - ], - [ - 33, - 59, - 26, - 35, - 48, - 38 - ], - [ - 23, - 30, - 20, - 43, - 10, - 58 - ], - [ - 63, - 23, - 18, - 17, - 38, - 9 - ], - [ - 22, - 62, - 36, - 33, - 6, - 2 - ], - [ - 12, - 17, - 59, - 7, - 26, - 49 - ], - [ - 47, - 3, - 27, - 6, - 24, - 22 - ], - [ - 59, - 57, - 46, - 54, - 2, - 10 - ], - [ - 27, - 45, - 57, - 54, - 34, - 61 - ], - [ - 34, - 60, - 55, - 31, - 58, - 43 - ], - [ - 48, - 42, - 32, - 39, - 2, - 38 - ], - [ - 42, - 49, - 32, - 44, - 12, - 61 - ], - [ - 57, - 36, - 39, - 1, - 30, - 59 - ], - [ - 48, - 7, - 21, - 53, - 17, - 29 - ], - [ - 8, - 60, - 58, - 35, - 46, - 14 - ], - [ - 51, - 15, - 28, - 47, - 33, - 30 - ], - [ - 11, - 19, - 4, - 8, - 58, - 52 - ], - [ - 24, - 5, - 41, - 60, - 40, - 54 - ], - [ - 61, - 3, - 21, - 58, - 19, - 8 - ], - [ - 60, - 35, - 54, - 49, - 1, - 0 - ] - ], - [ - [ - 6, - 24, - 63, - 25, - 26, - 45 - ], - [ - 47, - 13, - 49, - 44, - 20, - 19 - ], - [ - 23, - 32, - 49, - 20, - 24, - 2 - ], - [ - 43, - 21, - 8, - 40, - 39, - 45 - ], - [ - 39, - 29, - 3, - 5, - 41, - 10 - ], - [ - 61, - 33, - 48, - 40, - 29, - 62 - ], - [ - 29, - 28, - 25, - 33, - 44, - 31 - ], - [ - 50, - 33, - 13, - 11, - 30, - 54 - ], - [ - 48, - 26, - 35, - 17, - 55, - 6 - ], - [ - 43, - 23, - 12, - 9, - 25, - 62 - ], - [ - 63, - 35, - 18, - 53, - 38, - 9 - ], - [ - 19, - 48, - 5, - 36, - 59, - 39 - ], - [ - 7, - 26, - 59, - 17, - 12, - 30 - ], - [ - 47, - 24, - 22, - 62, - 42, - 14 - ], - [ - 59, - 46, - 39, - 35, - 57, - 2 - ], - [ - 27, - 9, - 20, - 0, - 57, - 52 - ], - [ - 38, - 58, - 60, - 34, - 43, - 29 - ], - [ - 42, - 32, - 39, - 54, - 38, - 57 - ], - [ - 59, - 32, - 42, - 6, - 21, - 18 - ], - [ - 24, - 36, - 57, - 4, - 30, - 60 - ], - [ - 9, - 48, - 7, - 53, - 21, - 13 - ], - [ - 49, - 31, - 14, - 8, - 19, - 52 - ], - [ - 15, - 33, - 44, - 8, - 3, - 14 - ], - [ - 11, - 4, - 21, - 28, - 41, - 23 - ], - [ - 37, - 27, - 24, - 12, - 9, - 42 - ], - [ - 58, - 9, - 19, - 3, - 12, - 48 - ], - [ - 24, - 14, - 60, - 47, - 25, - 35 - ] - ], - [ - [ - 26, - 62, - 58, - 18, - 38, - 5 - ], - [ - 19, - 12, - 40, - 39, - 31, - 57 - ], - [ - 27, - 38, - 9, - 22, - 23, - 61 - ], - [ - 42, - 20, - 63, - 39, - 45, - 43 - ], - [ - 7, - 36, - 60, - 29, - 57, - 24 - ], - [ - 61, - 34, - 3, - 1, - 44, - 51 - ], - [ - 29, - 42, - 25, - 47, - 30, - 17 - ], - [ - 33, - 38, - 39, - 50, - 62, - 0 - ], - [ - 17, - 26, - 13, - 50, - 41, - 11 - ], - [ - 12, - 43, - 35, - 16, - 55, - 60 - ], - [ - 3, - 18, - 39, - 63, - 35, - 62 - ], - [ - 19, - 48, - 39, - 60, - 54, - 2 - ], - [ - 7, - 42, - 56, - 59, - 37, - 24 - ], - [ - 56, - 47, - 54, - 59, - 3, - 6 - ], - [ - 35, - 39, - 43, - 59, - 2, - 13 - ], - [ - 20, - 27, - 9, - 56, - 0, - 54 - ], - [ - 17, - 60, - 29, - 18, - 58, - 5 - ], - [ - 57, - 42, - 45, - 0, - 24, - 29 - ], - [ - 49, - 28, - 52, - 32, - 50, - 42 - ], - [ - 24, - 2, - 36, - 30, - 46, - 59 - ], - [ - 11, - 7, - 48, - 54, - 53, - 21 - ], - [ - 33, - 14, - 42, - 6, - 8, - 5 - ], - [ - 15, - 36, - 33, - 17, - 51, - 13 - ], - [ - 41, - 2, - 4, - 11, - 8, - 29 - ], - [ - 30, - 15, - 60, - 5, - 46, - 9 - ], - [ - 21, - 3, - 14, - 19, - 61, - 58 - ], - [ - 24, - 60, - 1, - 14, - 35, - 53 - ] - ], - [ - [ - 8, - 56, - 54, - 4, - 37, - 38 - ], - [ - 36, - 9, - 24, - 8, - 1, - 2 - ], - [ - 16, - 57, - 29, - 32, - 58, - 30 - ], - [ - 32, - 63, - 42, - 52, - 4, - 20 - ], - [ - 27, - 24, - 44, - 39, - 63, - 20 - ], - [ - 24, - 11, - 28, - 6, - 15, - 31 - ], - [ - 30, - 41, - 42, - 25, - 52, - 2 - ], - [ - 26, - 36, - 62, - 18, - 50, - 21 - ], - [ - 48, - 50, - 32, - 51, - 27, - 26 - ], - [ - 13, - 43, - 3, - 26, - 12, - 41 - ], - [ - 3, - 32, - 18, - 53, - 39, - 17 - ], - [ - 21, - 19, - 9, - 36, - 48, - 1 - ], - [ - 35, - 59, - 42, - 54, - 63, - 17 - ], - [ - 15, - 12, - 38, - 9, - 51, - 54 - ], - [ - 3, - 2, - 48, - 59, - 57, - 13 - ], - [ - 27, - 9, - 4, - 20, - 22, - 3 - ], - [ - 60, - 40, - 22, - 27, - 46, - 32 - ], - [ - 30, - 25, - 21, - 17, - 0, - 42 - ], - [ - 33, - 27, - 34, - 28, - 38, - 44 - ], - [ - 53, - 32, - 33, - 35, - 31, - 56 - ], - [ - 40, - 11, - 20, - 47, - 48, - 24 - ], - [ - 45, - 62, - 33, - 53, - 17, - 34 - ], - [ - 41, - 9, - 39, - 25, - 17, - 32 - ], - [ - 44, - 30, - 2, - 31, - 0, - 47 - ], - [ - 6, - 62, - 37, - 52, - 55, - 33 - ], - [ - 10, - 24, - 57, - 9, - 49, - 13 - ], - [ - 45, - 24, - 27, - 47, - 19, - 26 - ] - ], - [ - [ - 4, - 16, - 59, - 44, - 13, - 56 - ], - [ - 16, - 23, - 9, - 59, - 13, - 50 - ], - [ - 11, - 35, - 21, - 7, - 9, - 59 - ], - [ - 44, - 1, - 25, - 26, - 15, - 20 - ], - [ - 35, - 57, - 52, - 31, - 24, - 5 - ], - [ - 35, - 15, - 38, - 53, - 12, - 0 - ], - [ - 36, - 20, - 63, - 28, - 60, - 33 - ], - [ - 49, - 50, - 12, - 13, - 8, - 16 - ], - [ - 42, - 48, - 6, - 16, - 35, - 41 - ], - [ - 23, - 9, - 54, - 34, - 30, - 13 - ], - [ - 23, - 63, - 51, - 53, - 55, - 3 - ], - [ - 33, - 36, - 62, - 19, - 59, - 57 - ], - [ - 59, - 38, - 12, - 32, - 17, - 53 - ], - [ - 48, - 0, - 19, - 24, - 61, - 22 - ], - [ - 55, - 6, - 39, - 7, - 60, - 18 - ], - [ - 27, - 57, - 19, - 1, - 60, - 47 - ], - [ - 30, - 40, - 10, - 17, - 36, - 60 - ], - [ - 20, - 13, - 1, - 31, - 17, - 43 - ], - [ - 50, - 32, - 23, - 7, - 33, - 30 - ], - [ - 14, - 19, - 23, - 13, - 10, - 7 - ], - [ - 53, - 23, - 19, - 13, - 7, - 50 - ], - [ - 30, - 18, - 46, - 14, - 1, - 15 - ], - [ - 15, - 18, - 52, - 0, - 51, - 27 - ], - [ - 4, - 21, - 41, - 53, - 10, - 14 - ], - [ - 4, - 9, - 39, - 8, - 16, - 54 - ], - [ - 3, - 19, - 16, - 47, - 30, - 27 - ], - [ - 33, - 8, - 46, - 40, - 29, - 14 - ] - ], - [ - [ - 6, - 26, - 3, - 24, - 11, - 38 - ], - [ - 49, - 16, - 19, - 57, - 0, - 18 - ], - [ - 16, - 60, - 57, - 0, - 22, - 30 - ], - [ - 35, - 46, - 49, - 44, - 26, - 17 - ], - [ - 25, - 54, - 9, - 43, - 45, - 27 - ], - [ - 55, - 33, - 30, - 6, - 28, - 57 - ], - [ - 55, - 31, - 49, - 52, - 15, - 25 - ], - [ - 11, - 36, - 15, - 29, - 30, - 62 - ], - [ - 55, - 42, - 48, - 54, - 46, - 6 - ], - [ - 10, - 3, - 43, - 21, - 62, - 54 - ], - [ - 32, - 56, - 6, - 3, - 18, - 55 - ], - [ - 50, - 21, - 36, - 19, - 4, - 42 - ], - [ - 38, - 35, - 53, - 39, - 41, - 32 - ], - [ - 28, - 24, - 38, - 41, - 15, - 12 - ], - [ - 2, - 40, - 18, - 60, - 55, - 13 - ], - [ - 27, - 22, - 57, - 8, - 54, - 37 - ], - [ - 40, - 27, - 60, - 46, - 44, - 50 - ], - [ - 17, - 21, - 30, - 12, - 29, - 26 - ], - [ - 33, - 38, - 23, - 2, - 13, - 27 - ], - [ - 32, - 56, - 9, - 44, - 31, - 60 - ], - [ - 53, - 12, - 22, - 40, - 41, - 5 - ], - [ - 44, - 45, - 49, - 17, - 14, - 56 - ], - [ - 25, - 39, - 41, - 4, - 9, - 53 - ], - [ - 44, - 1, - 17, - 0, - 13, - 58 - ], - [ - 62, - 52, - 37, - 54, - 42, - 36 - ], - [ - 10, - 9, - 28, - 55, - 2, - 24 - ], - [ - 57, - 30, - 27, - 45, - 47, - 16 - ] - ], - [ - [ - 16, - 11, - 31, - 46, - 0, - 35 - ], - [ - 13, - 49, - 34, - 31, - 16, - 8 - ], - [ - 36, - 13, - 27, - 58, - 18, - 34 - ], - [ - 24, - 61, - 15, - 46, - 63, - 25 - ], - [ - 17, - 2, - 50, - 35, - 58, - 44 - ], - [ - 8, - 23, - 7, - 49, - 26, - 47 - ], - [ - 61, - 38, - 29, - 58, - 0, - 36 - ], - [ - 42, - 20, - 9, - 4, - 11, - 27 - ], - [ - 1, - 34, - 30, - 48, - 26, - 7 - ], - [ - 27, - 10, - 28, - 62, - 13, - 54 - ], - [ - 60, - 1, - 30, - 59, - 6, - 52 - ], - [ - 31, - 62, - 25, - 42, - 32, - 21 - ], - [ - 34, - 32, - 19, - 2, - 62, - 14 - ], - [ - 6, - 40, - 24, - 31, - 13, - 57 - ], - [ - 44, - 2, - 40, - 8, - 34, - 45 - ], - [ - 8, - 27, - 7, - 57, - 50, - 47 - ], - [ - 13, - 60, - 45, - 22, - 52, - 25 - ], - [ - 4, - 23, - 33, - 46, - 58, - 34 - ], - [ - 8, - 33, - 56, - 45, - 51, - 59 - ], - [ - 38, - 53, - 39, - 26, - 35, - 31 - ], - [ - 41, - 46, - 53, - 11, - 59, - 8 - ], - [ - 11, - 44, - 33, - 55, - 52, - 53 - ], - [ - 31, - 1, - 16, - 9, - 15, - 41 - ], - [ - 2, - 31, - 14, - 10, - 44, - 7 - ], - [ - 45, - 6, - 62, - 12, - 24, - 18 - ], - [ - 10, - 50, - 42, - 54, - 24, - 3 - ], - [ - 17, - 31, - 37, - 5, - 10, - 41 - ] - ], - [ - [ - 45, - 37, - 48, - 29, - 30, - 59 - ], - [ - 8, - 59, - 10, - 60, - 43, - 55 - ], - [ - 45, - 51, - 28, - 59, - 34, - 31 - ], - [ - 14, - 31, - 22, - 2, - 19, - 44 - ], - [ - 59, - 50, - 4, - 31, - 9, - 44 - ], - [ - 58, - 23, - 47, - 9, - 53, - 30 - ], - [ - 54, - 13, - 51, - 22, - 29, - 61 - ], - [ - 47, - 42, - 32, - 20, - 24, - 37 - ], - [ - 33, - 12, - 47, - 10, - 30, - 44 - ], - [ - 30, - 10, - 27, - 28, - 33, - 13 - ], - [ - 61, - 23, - 14, - 20, - 1, - 29 - ], - [ - 12, - 14, - 32, - 62, - 31, - 52 - ], - [ - 5, - 36, - 19, - 20, - 2, - 32 - ], - [ - 46, - 24, - 1, - 40, - 17, - 33 - ], - [ - 44, - 8, - 16, - 30, - 2, - 6 - ], - [ - 6, - 46, - 15, - 54, - 21, - 33 - ], - [ - 2, - 30, - 60, - 61, - 18, - 10 - ], - [ - 6, - 17, - 33, - 27, - 58, - 4 - ], - [ - 56, - 46, - 8, - 60, - 22, - 16 - ], - [ - 5, - 13, - 14, - 35, - 36, - 52 - ], - [ - 15, - 53, - 59, - 23, - 6, - 46 - ], - [ - 36, - 30, - 60, - 33, - 53, - 38 - ], - [ - 10, - 15, - 16, - 52, - 4, - 38 - ], - [ - 31, - 1, - 10, - 4, - 41, - 59 - ], - [ - 32, - 9, - 8, - 59, - 33, - 18 - ], - [ - 33, - 19, - 6, - 3, - 45, - 50 - ], - [ - 55, - 4, - 43, - 5, - 25, - 34 - ] - ], - [ - [ - 25, - 14, - 18, - 49, - 51, - 63 - ], - [ - 42, - 21, - 30, - 43, - 24, - 7 - ], - [ - 54, - 39, - 9, - 59, - 28, - 45 - ], - [ - 1, - 23, - 55, - 8, - 16, - 7 - ], - [ - 59, - 37, - 30, - 41, - 16, - 1 - ], - [ - 4, - 31, - 41, - 45, - 56, - 18 - ], - [ - 28, - 55, - 44, - 54, - 9, - 51 - ], - [ - 24, - 47, - 12, - 63, - 32, - 61 - ], - [ - 33, - 47, - 39, - 44, - 38, - 56 - ], - [ - 40, - 30, - 59, - 33, - 58, - 10 - ], - [ - 19, - 8, - 23, - 4, - 25, - 34 - ], - [ - 45, - 14, - 12, - 42, - 62, - 52 - ], - [ - 58, - 45, - 59, - 29, - 25, - 36 - ], - [ - 8, - 59, - 1, - 47, - 33, - 24 - ], - [ - 24, - 30, - 38, - 56, - 4, - 7 - ], - [ - 46, - 22, - 44, - 57, - 12, - 40 - ], - [ - 34, - 60, - 2, - 17, - 18, - 29 - ], - [ - 6, - 61, - 51, - 39, - 53, - 32 - ], - [ - 49, - 5, - 47, - 60, - 21, - 59 - ], - [ - 57, - 4, - 23, - 39, - 45, - 60 - ], - [ - 7, - 36, - 45, - 53, - 21, - 56 - ], - [ - 60, - 3, - 39, - 18, - 8, - 53 - ], - [ - 57, - 33, - 15, - 47, - 38, - 16 - ], - [ - 20, - 11, - 19, - 4, - 2, - 8 - ], - [ - 36, - 34, - 60, - 47, - 1, - 40 - ], - [ - 33, - 14, - 3, - 21, - 19, - 35 - ], - [ - 60, - 24, - 22, - 52, - 35, - 53 - ] - ], - [ - [ - 23, - 54, - 53, - 58, - 11, - 8 - ], - [ - 11, - 30, - 15, - 59, - 63, - 55 - ], - [ - 20, - 58, - 29, - 17, - 42, - 30 - ], - [ - 18, - 1, - 43, - 15, - 8, - 3 - ], - [ - 59, - 55, - 13, - 28, - 26, - 63 - ], - [ - 33, - 45, - 27, - 53, - 63, - 19 - ], - [ - 28, - 57, - 51, - 54, - 34, - 53 - ], - [ - 24, - 13, - 3, - 47, - 45, - 50 - ], - [ - 47, - 59, - 33, - 57, - 37, - 35 - ], - [ - 23, - 42, - 33, - 41, - 48, - 30 - ], - [ - 42, - 63, - 23, - 25, - 17, - 34 - ], - [ - 14, - 62, - 2, - 19, - 45, - 43 - ], - [ - 12, - 6, - 20, - 30, - 29, - 17 - ], - [ - 6, - 3, - 17, - 8, - 27, - 31 - ], - [ - 19, - 59, - 2, - 10, - 54, - 55 - ], - [ - 45, - 61, - 19, - 57, - 42, - 17 - ], - [ - 55, - 60, - 41, - 34, - 35, - 53 - ], - [ - 36, - 2, - 8, - 31, - 14, - 5 - ], - [ - 36, - 45, - 16, - 38, - 51, - 21 - ], - [ - 57, - 39, - 48, - 6, - 19, - 30 - ], - [ - 39, - 37, - 59, - 48, - 42, - 40 - ], - [ - 52, - 28, - 33, - 46, - 18, - 55 - ], - [ - 1, - 15, - 28, - 13, - 52, - 18 - ], - [ - 10, - 21, - 4, - 2, - 23, - 31 - ], - [ - 45, - 9, - 41, - 18, - 54, - 3 - ], - [ - 42, - 61, - 36, - 3, - 19, - 27 - ], - [ - 41, - 46, - 33, - 3, - 10, - 15 - ] - ], - [ - [ - 49, - 52, - 60, - 63, - 21, - 0 - ], - [ - 14, - 7, - 25, - 52, - 58, - 36 - ], - [ - 46, - 57, - 28, - 24, - 49, - 12 - ], - [ - 10, - 21, - 39, - 1, - 11, - 17 - ], - [ - 53, - 3, - 2, - 16, - 46, - 44 - ], - [ - 25, - 40, - 39, - 15, - 34, - 30 - ], - [ - 62, - 24, - 47, - 52, - 17, - 49 - ], - [ - 34, - 41, - 39, - 11, - 33, - 15 - ], - [ - 36, - 26, - 48, - 39, - 15, - 56 - ], - [ - 23, - 36, - 43, - 20, - 10, - 49 - ], - [ - 63, - 48, - 51, - 10, - 62, - 18 - ], - [ - 10, - 42, - 36, - 39, - 49, - 19 - ], - [ - 22, - 38, - 51, - 56, - 50, - 7 - ], - [ - 4, - 31, - 19, - 22, - 17, - 7 - ], - [ - 41, - 7, - 39, - 2, - 26, - 57 - ], - [ - 33, - 27, - 58, - 28, - 20, - 45 - ], - [ - 58, - 8, - 37, - 18, - 34, - 62 - ], - [ - 24, - 34, - 43, - 39, - 17, - 19 - ], - [ - 23, - 50, - 12, - 32, - 43, - 31 - ], - [ - 9, - 7, - 51, - 16, - 30, - 60 - ], - [ - 25, - 62, - 14, - 53, - 58, - 0 - ], - [ - 53, - 4, - 18, - 22, - 14, - 29 - ], - [ - 6, - 14, - 15, - 48, - 58, - 0 - ], - [ - 35, - 4, - 45, - 3, - 59, - 39 - ], - [ - 17, - 57, - 23, - 16, - 40, - 37 - ], - [ - 9, - 19, - 3, - 36, - 11, - 47 - ], - [ - 39, - 14, - 22, - 33, - 62, - 42 - ] - ], - [ - [ - 14, - 17, - 2, - 39, - 47, - 63 - ], - [ - 23, - 58, - 2, - 25, - 5, - 18 - ], - [ - 27, - 33, - 0, - 56, - 6, - 7 - ], - [ - 23, - 5, - 30, - 7, - 21, - 39 - ], - [ - 39, - 53, - 27, - 38, - 54, - 41 - ], - [ - 37, - 25, - 50, - 40, - 33, - 36 - ], - [ - 49, - 37, - 24, - 55, - 29, - 28 - ], - [ - 34, - 15, - 36, - 16, - 30, - 11 - ], - [ - 5, - 36, - 2, - 26, - 16, - 48 - ], - [ - 56, - 46, - 10, - 23, - 39, - 45 - ], - [ - 10, - 56, - 62, - 45, - 49, - 3 - ], - [ - 35, - 6, - 36, - 50, - 11, - 1 - ], - [ - 50, - 46, - 39, - 51, - 38, - 41 - ], - [ - 41, - 19, - 31, - 48, - 6, - 24 - ], - [ - 2, - 7, - 57, - 18, - 47, - 63 - ], - [ - 27, - 58, - 22, - 8, - 57, - 45 - ], - [ - 37, - 59, - 44, - 51, - 55, - 18 - ], - [ - 52, - 21, - 25, - 12, - 34, - 39 - ], - [ - 23, - 34, - 60, - 13, - 27, - 32 - ], - [ - 32, - 56, - 26, - 24, - 44, - 30 - ], - [ - 41, - 12, - 53, - 2, - 5, - 56 - ], - [ - 44, - 18, - 39, - 33, - 38, - 46 - ], - [ - 25, - 0, - 59, - 18, - 11, - 53 - ], - [ - 1, - 7, - 17, - 30, - 4, - 37 - ], - [ - 62, - 48, - 19, - 54, - 37, - 22 - ], - [ - 10, - 28, - 3, - 16, - 11, - 42 - ], - [ - 26, - 30, - 57, - 42, - 41, - 33 - ] - ], - [ - [ - 36, - 31, - 37, - 16, - 43, - 63 - ], - [ - 26, - 51, - 0, - 48, - 42, - 21 - ], - [ - 18, - 41, - 37, - 34, - 24, - 30 - ], - [ - 58, - 4, - 22, - 44, - 18, - 51 - ], - [ - 18, - 26, - 4, - 61, - 14, - 31 - ], - [ - 9, - 23, - 34, - 29, - 3, - 31 - ], - [ - 12, - 58, - 36, - 46, - 16, - 25 - ], - [ - 4, - 56, - 9, - 33, - 60, - 32 - ], - [ - 14, - 26, - 43, - 30, - 46, - 35 - ], - [ - 2, - 0, - 10, - 43, - 28, - 27 - ], - [ - 61, - 18, - 19, - 17, - 2, - 33 - ], - [ - 60, - 19, - 23, - 62, - 37, - 43 - ], - [ - 56, - 32, - 8, - 61, - 58, - 38 - ], - [ - 25, - 28, - 22, - 0, - 26, - 29 - ], - [ - 22, - 49, - 7, - 23, - 60, - 55 - ], - [ - 46, - 57, - 19, - 37, - 12, - 27 - ], - [ - 22, - 60, - 11, - 30, - 19, - 20 - ], - [ - 36, - 20, - 17, - 2, - 37, - 32 - ], - [ - 13, - 16, - 32, - 58, - 41, - 10 - ], - [ - 50, - 55, - 13, - 25, - 14, - 41 - ], - [ - 14, - 3, - 44, - 48, - 60, - 6 - ], - [ - 36, - 18, - 57, - 22, - 1, - 43 - ], - [ - 10, - 22, - 15, - 52, - 13, - 18 - ], - [ - 4, - 56, - 10, - 31, - 14, - 52 - ], - [ - 9, - 4, - 16, - 54, - 8, - 59 - ], - [ - 3, - 43, - 27, - 45, - 6, - 30 - ], - [ - 33, - 4, - 43, - 40, - 46, - 18 - ] - ], - [ - [ - 48, - 38, - 42, - 63, - 47, - 7 - ], - [ - 3, - 10, - 62, - 6, - 26, - 2 - ], - [ - 39, - 44, - 6, - 7, - 45, - 40 - ], - [ - 56, - 33, - 2, - 41, - 62, - 48 - ], - [ - 6, - 18, - 37, - 41, - 27, - 30 - ], - [ - 59, - 45, - 23, - 31, - 53, - 9 - ], - [ - 0, - 13, - 12, - 44, - 33, - 16 - ], - [ - 12, - 56, - 50, - 60, - 28, - 63 - ], - [ - 14, - 33, - 26, - 24, - 32, - 16 - ], - [ - 43, - 2, - 0, - 50, - 28, - 22 - ], - [ - 61, - 9, - 63, - 8, - 17, - 39 - ], - [ - 60, - 19, - 59, - 23, - 24, - 30 - ], - [ - 56, - 59, - 53, - 32, - 7, - 38 - ], - [ - 28, - 25, - 22, - 24, - 59, - 42 - ], - [ - 22, - 49, - 46, - 23, - 60, - 7 - ], - [ - 46, - 34, - 27, - 29, - 0, - 12 - ], - [ - 17, - 22, - 5, - 31, - 20, - 27 - ], - [ - 39, - 48, - 57, - 37, - 17, - 36 - ], - [ - 42, - 49, - 32, - 7, - 16, - 61 - ], - [ - 1, - 7, - 23, - 14, - 36, - 39 - ], - [ - 21, - 7, - 48, - 14, - 60, - 35 - ], - [ - 8, - 18, - 24, - 60, - 1, - 0 - ], - [ - 51, - 15, - 33, - 36, - 5, - 30 - ], - [ - 52, - 4, - 8, - 29, - 9, - 26 - ], - [ - 4, - 9, - 16, - 40, - 58, - 60 - ], - [ - 3, - 17, - 21, - 61, - 19, - 8 - ], - [ - 35, - 54, - 1, - 53, - 60, - 0 - ] - ], - [ - [ - 19, - 1, - 31, - 52, - 49, - 63 - ], - [ - 7, - 47, - 5, - 60, - 22, - 46 - ], - [ - 59, - 30, - 3, - 11, - 0, - 19 - ], - [ - 43, - 42, - 19, - 62, - 8, - 56 - ], - [ - 61, - 15, - 25, - 18, - 39, - 27 - ], - [ - 61, - 50, - 36, - 45, - 33, - 44 - ], - [ - 8, - 37, - 52, - 1, - 2, - 41 - ], - [ - 36, - 30, - 53, - 11, - 16, - 29 - ], - [ - 14, - 58, - 46, - 49, - 3, - 26 - ], - [ - 62, - 43, - 0, - 45, - 22, - 46 - ], - [ - 6, - 56, - 45, - 18, - 10, - 41 - ], - [ - 60, - 21, - 50, - 47, - 30, - 35 - ], - [ - 11, - 53, - 28, - 56, - 41, - 39 - ], - [ - 23, - 9, - 33, - 28, - 22, - 26 - ], - [ - 23, - 56, - 34, - 27, - 2, - 63 - ], - [ - 22, - 9, - 44, - 41, - 37, - 47 - ], - [ - 1, - 11, - 46, - 27, - 3, - 52 - ], - [ - 51, - 37, - 17, - 21, - 61, - 30 - ], - [ - 13, - 19, - 32, - 5, - 2, - 9 - ], - [ - 41, - 32, - 6, - 47, - 29, - 56 - ], - [ - 53, - 33, - 9, - 35, - 38, - 12 - ], - [ - 40, - 19, - 51, - 7, - 26, - 22 - ], - [ - 37, - 5, - 25, - 46, - 34, - 53 - ], - [ - 16, - 55, - 20, - 24, - 44, - 53 - ], - [ - 21, - 46, - 11, - 0, - 36, - 7 - ], - [ - 18, - 60, - 32, - 3, - 34, - 28 - ], - [ - 52, - 9, - 36, - 48, - 11, - 41 - ] - ], - [ - [ - 53, - 15, - 34, - 0, - 36, - 63 - ], - [ - 8, - 12, - 41, - 11, - 19, - 55 - ], - [ - 56, - 13, - 31, - 36, - 23, - 47 - ], - [ - 36, - 51, - 30, - 7, - 26, - 54 - ], - [ - 58, - 13, - 50, - 2, - 53, - 34 - ], - [ - 49, - 52, - 23, - 32, - 7, - 26 - ], - [ - 61, - 38, - 23, - 0, - 4, - 28 - ], - [ - 42, - 27, - 17, - 9, - 18, - 20 - ], - [ - 1, - 34, - 45, - 4, - 12, - 7 - ], - [ - 54, - 27, - 57, - 38, - 44, - 19 - ], - [ - 59, - 40, - 1, - 48, - 30, - 60 - ], - [ - 25, - 31, - 32, - 51, - 62, - 8 - ], - [ - 62, - 19, - 14, - 2, - 37, - 26 - ], - [ - 40, - 57, - 37, - 35, - 22, - 61 - ], - [ - 45, - 16, - 34, - 42, - 37, - 48 - ], - [ - 50, - 16, - 62, - 33, - 25, - 37 - ], - [ - 42, - 13, - 39, - 47, - 3, - 63 - ], - [ - 46, - 23, - 28, - 27, - 4, - 15 - ], - [ - 56, - 62, - 31, - 35, - 59, - 45 - ], - [ - 15, - 38, - 13, - 63, - 4, - 48 - ], - [ - 34, - 15, - 57, - 38, - 13, - 24 - ], - [ - 62, - 36, - 41, - 54, - 46, - 29 - ], - [ - 22, - 53, - 46, - 34, - 30, - 23 - ], - [ - 24, - 10, - 4, - 47, - 18, - 36 - ], - [ - 50, - 57, - 51, - 11, - 49, - 3 - ], - [ - 51, - 7, - 18, - 0, - 11, - 44 - ], - [ - 39, - 37, - 9, - 42, - 40, - 44 - ] - ], - [ - [ - 36, - 25, - 57, - 55, - 47, - 63 - ], - [ - 0, - 2, - 46, - 3, - 51, - 34 - ], - [ - 24, - 2, - 46, - 15, - 33, - 43 - ], - [ - 22, - 31, - 17, - 19, - 10, - 55 - ], - [ - 58, - 59, - 3, - 9, - 40, - 57 - ], - [ - 23, - 31, - 43, - 2, - 57, - 38 - ], - [ - 9, - 62, - 13, - 42, - 52, - 47 - ], - [ - 24, - 7, - 14, - 10, - 46, - 59 - ], - [ - 47, - 18, - 4, - 37, - 0, - 13 - ], - [ - 54, - 4, - 25, - 47, - 36, - 38 - ], - [ - 15, - 47, - 1, - 24, - 58, - 14 - ], - [ - 54, - 5, - 16, - 63, - 14, - 7 - ], - [ - 49, - 3, - 33, - 13, - 46, - 10 - ], - [ - 18, - 10, - 11, - 13, - 63, - 39 - ], - [ - 19, - 62, - 32, - 58, - 10, - 43 - ], - [ - 43, - 48, - 63, - 5, - 55, - 53 - ], - [ - 24, - 51, - 47, - 15, - 59, - 32 - ], - [ - 44, - 0, - 34, - 43, - 3, - 6 - ], - [ - 58, - 38, - 54, - 47, - 11, - 4 - ], - [ - 55, - 27, - 15, - 9, - 42, - 31 - ], - [ - 43, - 41, - 1, - 51, - 5, - 29 - ], - [ - 49, - 27, - 20, - 6, - 4, - 13 - ], - [ - 11, - 25, - 2, - 54, - 27, - 50 - ], - [ - 38, - 44, - 40, - 54, - 33, - 14 - ], - [ - 13, - 63, - 52, - 2, - 29, - 8 - ], - [ - 23, - 41, - 59, - 57, - 38, - 15 - ], - [ - 23, - 6, - 62, - 50, - 51, - 34 - ] - ], - [ - [ - 41, - 2, - 42, - 16, - 50, - 23 - ], - [ - 51, - 41, - 5, - 15, - 40, - 21 - ], - [ - 43, - 1, - 29, - 55, - 21, - 35 - ], - [ - 24, - 53, - 25, - 51, - 32, - 29 - ], - [ - 41, - 31, - 49, - 57, - 60, - 34 - ], - [ - 17, - 4, - 35, - 30, - 10, - 38 - ], - [ - 34, - 7, - 21, - 9, - 48, - 31 - ], - [ - 14, - 24, - 7, - 46, - 25, - 27 - ], - [ - 47, - 0, - 12, - 6, - 37, - 60 - ], - [ - 30, - 4, - 25, - 47, - 36, - 54 - ], - [ - 24, - 61, - 15, - 47, - 46, - 1 - ], - [ - 5, - 14, - 24, - 16, - 57, - 63 - ], - [ - 49, - 3, - 17, - 26, - 36, - 44 - ], - [ - 13, - 10, - 61, - 0, - 11, - 22 - ], - [ - 10, - 6, - 2, - 49, - 58, - 46 - ], - [ - 19, - 11, - 2, - 25, - 54, - 18 - ], - [ - 10, - 5, - 52, - 24, - 18, - 17 - ], - [ - 44, - 13, - 55, - 31, - 63, - 38 - ], - [ - 58, - 7, - 25, - 32, - 38, - 14 - ], - [ - 49, - 55, - 34, - 13, - 16, - 40 - ], - [ - 51, - 23, - 59, - 35, - 5, - 4 - ], - [ - 57, - 15, - 46, - 27, - 42, - 32 - ], - [ - 33, - 15, - 23, - 52, - 24, - 27 - ], - [ - 4, - 41, - 33, - 10, - 26, - 40 - ], - [ - 8, - 13, - 59, - 4, - 9, - 39 - ], - [ - 19, - 3, - 27, - 31, - 43, - 2 - ], - [ - 46, - 61, - 25, - 8, - 29, - 50 - ] - ], - [ - [ - 48, - 38, - 42, - 50, - 62, - 63 - ], - [ - 3, - 2, - 10, - 26, - 17, - 6 - ], - [ - 39, - 44, - 6, - 45, - 7, - 40 - ], - [ - 5, - 62, - 53, - 50, - 41, - 3 - ], - [ - 6, - 41, - 49, - 37, - 30, - 23 - ], - [ - 59, - 60, - 4, - 46, - 53, - 29 - ], - [ - 44, - 34, - 7, - 15, - 13, - 43 - ], - [ - 12, - 24, - 14, - 25, - 58, - 7 - ], - [ - 33, - 35, - 4, - 37, - 8, - 36 - ], - [ - 40, - 30, - 36, - 25, - 20, - 54 - ], - [ - 9, - 8, - 24, - 5, - 25, - 63 - ], - [ - 59, - 34, - 5, - 24, - 57, - 6 - ], - [ - 17, - 49, - 44, - 26, - 55, - 7 - ], - [ - 47, - 13, - 59, - 27, - 22, - 26 - ], - [ - 49, - 2, - 10, - 44, - 46, - 54 - ], - [ - 34, - 2, - 54, - 55, - 57, - 53 - ], - [ - 5, - 17, - 29, - 31, - 43, - 27 - ], - [ - 48, - 57, - 38, - 39, - 63, - 43 - ], - [ - 42, - 38, - 49, - 32, - 7, - 40 - ], - [ - 59, - 1, - 16, - 23, - 60, - 10 - ], - [ - 21, - 7, - 35, - 53, - 48, - 31 - ], - [ - 8, - 60, - 24, - 42, - 14, - 35 - ], - [ - 33, - 51, - 15, - 28, - 23, - 5 - ], - [ - 4, - 41, - 9, - 8, - 11, - 51 - ], - [ - 60, - 24, - 19, - 48, - 9, - 4 - ], - [ - 19, - 3, - 26, - 58, - 12, - 21 - ], - [ - 54, - 35, - 1, - 60, - 53, - 49 - ] - ], - [ - [ - 21, - 7, - 53, - 56, - 63, - 33 - ], - [ - 3, - 34, - 57, - 16, - 20, - 51 - ], - [ - 55, - 11, - 16, - 60, - 0, - 13 - ], - [ - 62, - 43, - 5, - 50, - 8, - 42 - ], - [ - 35, - 52, - 9, - 43, - 0, - 27 - ], - [ - 16, - 60, - 29, - 61, - 28, - 58 - ], - [ - 31, - 34, - 43, - 30, - 2, - 18 - ], - [ - 36, - 25, - 24, - 53, - 48, - 35 - ], - [ - 48, - 35, - 4, - 32, - 8, - 60 - ], - [ - 25, - 54, - 30, - 10, - 4, - 41 - ], - [ - 9, - 3, - 19, - 20, - 61, - 24 - ], - [ - 30, - 5, - 17, - 45, - 18, - 49 - ], - [ - 49, - 21, - 17, - 26, - 42, - 37 - ], - [ - 38, - 25, - 10, - 51, - 54, - 13 - ], - [ - 2, - 27, - 26, - 10, - 58, - 30 - ], - [ - 9, - 2, - 54, - 21, - 25, - 13 - ], - [ - 22, - 23, - 33, - 27, - 51, - 52 - ], - [ - 30, - 21, - 35, - 55, - 5, - 17 - ], - [ - 38, - 27, - 24, - 56, - 21, - 35 - ], - [ - 53, - 45, - 22, - 28, - 0, - 32 - ], - [ - 40, - 42, - 48, - 37, - 52, - 8 - ], - [ - 46, - 45, - 57, - 36, - 51, - 32 - ], - [ - 62, - 10, - 38, - 42, - 41, - 54 - ], - [ - 60, - 1, - 10, - 31, - 44, - 36 - ], - [ - 29, - 6, - 9, - 8, - 56, - 7 - ], - [ - 9, - 59, - 22, - 0, - 35, - 57 - ], - [ - 45, - 27, - 62, - 47, - 3, - 28 - ] - ], - [ - [ - 27, - 13, - 18, - 8, - 63, - 55 - ], - [ - 36, - 21, - 57, - 8, - 46, - 55 - ], - [ - 43, - 61, - 10, - 13, - 41, - 37 - ], - [ - 43, - 16, - 24, - 6, - 26, - 61 - ], - [ - 60, - 29, - 35, - 31, - 16, - 23 - ], - [ - 9, - 58, - 60, - 0, - 17, - 38 - ], - [ - 63, - 16, - 7, - 13, - 31, - 18 - ], - [ - 16, - 25, - 24, - 2, - 47, - 48 - ], - [ - 8, - 35, - 6, - 14, - 48, - 4 - ], - [ - 30, - 25, - 54, - 34, - 4, - 10 - ], - [ - 20, - 23, - 19, - 7, - 34, - 38 - ], - [ - 5, - 3, - 12, - 32, - 24, - 42 - ], - [ - 49, - 17, - 10, - 26, - 32, - 60 - ], - [ - 13, - 10, - 29, - 22, - 58, - 54 - ], - [ - 44, - 8, - 2, - 58, - 6, - 5 - ], - [ - 25, - 6, - 2, - 54, - 19, - 53 - ], - [ - 5, - 61, - 30, - 17, - 27, - 1 - ], - [ - 22, - 55, - 63, - 57, - 19, - 33 - ], - [ - 35, - 46, - 6, - 32, - 14, - 38 - ], - [ - 45, - 13, - 51, - 14, - 5, - 7 - ], - [ - 15, - 23, - 50, - 51, - 13, - 59 - ], - [ - 57, - 36, - 6, - 53, - 14, - 61 - ], - [ - 10, - 23, - 15, - 33, - 27, - 30 - ], - [ - 4, - 10, - 33, - 41, - 26, - 36 - ], - [ - 8, - 4, - 33, - 9, - 47, - 20 - ], - [ - 3, - 43, - 6, - 19, - 30, - 2 - ], - [ - 55, - 4, - 46, - 40, - 18, - 20 - ] - ], - [ - [ - 48, - 38, - 63, - 42, - 47, - 50 - ], - [ - 3, - 10, - 26, - 6, - 35, - 2 - ], - [ - 39, - 44, - 7, - 45, - 6, - 42 - ], - [ - 56, - 23, - 61, - 16, - 33, - 41 - ], - [ - 6, - 37, - 44, - 16, - 30, - 19 - ], - [ - 59, - 17, - 60, - 19, - 5, - 52 - ], - [ - 44, - 55, - 33, - 63, - 13, - 31 - ], - [ - 12, - 25, - 63, - 2, - 24, - 54 - ], - [ - 33, - 8, - 42, - 4, - 35, - 41 - ], - [ - 30, - 40, - 25, - 21, - 48, - 51 - ], - [ - 8, - 23, - 34, - 5, - 19, - 22 - ], - [ - 34, - 59, - 42, - 11, - 5, - 3 - ], - [ - 10, - 26, - 50, - 55, - 53, - 59 - ], - [ - 59, - 54, - 13, - 47, - 10, - 25 - ], - [ - 24, - 2, - 49, - 46, - 38, - 8 - ], - [ - 34, - 2, - 53, - 54, - 40, - 57 - ], - [ - 17, - 29, - 5, - 27, - 18, - 43 - ], - [ - 48, - 57, - 63, - 39, - 38, - 32 - ], - [ - 42, - 49, - 7, - 32, - 38, - 61 - ], - [ - 1, - 59, - 23, - 14, - 16, - 10 - ], - [ - 21, - 7, - 35, - 53, - 13, - 11 - ], - [ - 8, - 60, - 24, - 25, - 55, - 42 - ], - [ - 33, - 5, - 15, - 51, - 28, - 44 - ], - [ - 4, - 9, - 51, - 8, - 41, - 39 - ], - [ - 60, - 4, - 19, - 48, - 9, - 24 - ], - [ - 19, - 3, - 26, - 12, - 21, - 53 - ], - [ - 53, - 1, - 35, - 54, - 60, - 20 - ] - ], - [ - [ - 17, - 37, - 31, - 32, - 63, - 50 - ], - [ - 12, - 2, - 9, - 32, - 47, - 17 - ], - [ - 3, - 57, - 56, - 50, - 33, - 38 - ], - [ - 43, - 42, - 19, - 52, - 8, - 17 - ], - [ - 61, - 39, - 27, - 12, - 15, - 57 - ], - [ - 6, - 33, - 36, - 44, - 29, - 61 - ], - [ - 2, - 41, - 42, - 15, - 52, - 5 - ], - [ - 36, - 37, - 15, - 53, - 18, - 62 - ], - [ - 50, - 58, - 3, - 5, - 16, - 4 - ], - [ - 16, - 22, - 55, - 32, - 41, - 26 - ], - [ - 3, - 41, - 56, - 45, - 34, - 10 - ], - [ - 21, - 53, - 50, - 34, - 38, - 35 - ], - [ - 35, - 11, - 37, - 16, - 53, - 42 - ], - [ - 9, - 15, - 54, - 38, - 12, - 52 - ], - [ - 56, - 2, - 23, - 24, - 9, - 5 - ], - [ - 2, - 44, - 9, - 53, - 35, - 54 - ], - [ - 27, - 46, - 1, - 5, - 60, - 50 - ], - [ - 51, - 61, - 50, - 54, - 33, - 44 - ], - [ - 2, - 38, - 19, - 9, - 5, - 32 - ], - [ - 32, - 28, - 6, - 15, - 0, - 33 - ], - [ - 26, - 53, - 27, - 13, - 5, - 4 - ], - [ - 34, - 40, - 45, - 55, - 62, - 53 - ], - [ - 41, - 5, - 46, - 4, - 45, - 3 - ], - [ - 20, - 4, - 22, - 47, - 59, - 58 - ], - [ - 62, - 36, - 25, - 28, - 53, - 33 - ], - [ - 22, - 33, - 7, - 3, - 54, - 0 - ], - [ - 58, - 27, - 52, - 48, - 45, - 17 - ] - ], - [ - [ - 3, - 43, - 17, - 42, - 35, - 55 - ], - [ - 0, - 42, - 36, - 34, - 32, - 40 - ], - [ - 43, - 12, - 19, - 20, - 2, - 4 - ], - [ - 4, - 28, - 54, - 22, - 58, - 23 - ], - [ - 12, - 60, - 52, - 57, - 46, - 7 - ], - [ - 43, - 2, - 1, - 63, - 20, - 11 - ], - [ - 42, - 19, - 57, - 31, - 15, - 21 - ], - [ - 35, - 6, - 46, - 25, - 36, - 33 - ], - [ - 11, - 4, - 35, - 50, - 32, - 52 - ], - [ - 21, - 25, - 38, - 47, - 31, - 58 - ], - [ - 3, - 2, - 22, - 45, - 42, - 15 - ], - [ - 2, - 21, - 5, - 7, - 43, - 38 - ], - [ - 33, - 10, - 41, - 42, - 60, - 16 - ], - [ - 44, - 56, - 30, - 54, - 9, - 43 - ], - [ - 9, - 5, - 33, - 28, - 58, - 49 - ], - [ - 8, - 53, - 59, - 54, - 31, - 2 - ], - [ - 47, - 0, - 48, - 27, - 24, - 51 - ], - [ - 0, - 60, - 21, - 12, - 56, - 28 - ], - [ - 38, - 13, - 53, - 51, - 9, - 7 - ], - [ - 19, - 28, - 56, - 17, - 21, - 26 - ], - [ - 12, - 43, - 26, - 4, - 0, - 5 - ], - [ - 43, - 1, - 12, - 55, - 4, - 46 - ], - [ - 41, - 5, - 21, - 45, - 25, - 12 - ], - [ - 4, - 20, - 1, - 22, - 30, - 42 - ], - [ - 5, - 62, - 25, - 54, - 48, - 47 - ], - [ - 10, - 28, - 55, - 23, - 0, - 53 - ], - [ - 30, - 57, - 8, - 29, - 17, - 5 - ] - ], - [ - [ - 11, - 16, - 31, - 0, - 46, - 35 - ], - [ - 13, - 49, - 31, - 50, - 16, - 34 - ], - [ - 36, - 13, - 53, - 27, - 4, - 18 - ], - [ - 24, - 7, - 29, - 26, - 12, - 32 - ], - [ - 17, - 35, - 2, - 44, - 10, - 48 - ], - [ - 8, - 7, - 23, - 5, - 51, - 26 - ], - [ - 58, - 15, - 61, - 29, - 38, - 62 - ], - [ - 20, - 9, - 42, - 35, - 3, - 6 - ], - [ - 4, - 47, - 25, - 11, - 1, - 52 - ], - [ - 54, - 25, - 55, - 38, - 21, - 27 - ], - [ - 1, - 60, - 14, - 59, - 22, - 30 - ], - [ - 51, - 31, - 5, - 25, - 14, - 52 - ], - [ - 34, - 2, - 10, - 26, - 52, - 47 - ], - [ - 40, - 57, - 13, - 54, - 9, - 6 - ], - [ - 8, - 44, - 58, - 5, - 16, - 1 - ], - [ - 8, - 53, - 59, - 25, - 52, - 24 - ], - [ - 13, - 47, - 45, - 0, - 42, - 8 - ], - [ - 23, - 44, - 55, - 33, - 38, - 7 - ], - [ - 53, - 11, - 38, - 1, - 8, - 24 - ], - [ - 15, - 38, - 14, - 28, - 0, - 19 - ], - [ - 41, - 27, - 8, - 42, - 40, - 57 - ], - [ - 11, - 62, - 55, - 16, - 10, - 41 - ], - [ - 31, - 20, - 46, - 37, - 34, - 41 - ], - [ - 47, - 4, - 55, - 33, - 49, - 22 - ], - [ - 11, - 45, - 47, - 54, - 48, - 36 - ], - [ - 50, - 10, - 23, - 51, - 18, - 3 - ], - [ - 17, - 31, - 5, - 36, - 4, - 20 - ] - ], - [ - [ - 22, - 6, - 39, - 57, - 29, - 47 - ], - [ - 27, - 6, - 14, - 17, - 51, - 55 - ], - [ - 1, - 11, - 29, - 26, - 47, - 4 - ], - [ - 14, - 38, - 31, - 22, - 29, - 6 - ], - [ - 14, - 59, - 61, - 16, - 1, - 19 - ], - [ - 30, - 8, - 23, - 21, - 47, - 1 - ], - [ - 58, - 4, - 15, - 61, - 27, - 31 - ], - [ - 20, - 42, - 3, - 9, - 35, - 6 - ], - [ - 47, - 4, - 25, - 8, - 36, - 0 - ], - [ - 54, - 55, - 21, - 19, - 33, - 25 - ], - [ - 14, - 4, - 60, - 20, - 24, - 40 - ], - [ - 51, - 0, - 5, - 32, - 52, - 3 - ], - [ - 2, - 36, - 10, - 52, - 26, - 32 - ], - [ - 40, - 13, - 54, - 36, - 57, - 46 - ], - [ - 44, - 8, - 5, - 37, - 58, - 2 - ], - [ - 6, - 53, - 24, - 2, - 54, - 37 - ], - [ - 13, - 47, - 61, - 5, - 19, - 17 - ], - [ - 58, - 55, - 44, - 38, - 63, - 6 - ], - [ - 35, - 46, - 31, - 1, - 32, - 19 - ], - [ - 15, - 13, - 63, - 45, - 9, - 55 - ], - [ - 27, - 15, - 23, - 6, - 35, - 63 - ], - [ - 36, - 62, - 57, - 10, - 41, - 16 - ], - [ - 10, - 33, - 41, - 20, - 5, - 46 - ], - [ - 47, - 4, - 26, - 55, - 10, - 49 - ], - [ - 11, - 4, - 8, - 36, - 48, - 33 - ], - [ - 18, - 51, - 43, - 33, - 50, - 3 - ], - [ - 4, - 55, - 9, - 36, - 43, - 5 - ] - ], - [ - [ - 48, - 38, - 63, - 42, - 47, - 7 - ], - [ - 3, - 26, - 10, - 6, - 42, - 2 - ], - [ - 39, - 44, - 6, - 7, - 45, - 8 - ], - [ - 60, - 31, - 22, - 0, - 54, - 45 - ], - [ - 6, - 59, - 14, - 16, - 37, - 44 - ], - [ - 59, - 30, - 8, - 47, - 60, - 17 - ], - [ - 44, - 56, - 4, - 13, - 15, - 9 - ], - [ - 12, - 24, - 20, - 58, - 61, - 28 - ], - [ - 47, - 33, - 8, - 4, - 36, - 42 - ], - [ - 40, - 54, - 55, - 33, - 21, - 51 - ], - [ - 8, - 14, - 22, - 39, - 31, - 7 - ], - [ - 34, - 59, - 14, - 5, - 52, - 0 - ], - [ - 36, - 10, - 52, - 26, - 44, - 59 - ], - [ - 59, - 44, - 13, - 1, - 36, - 22 - ], - [ - 24, - 5, - 46, - 2, - 44, - 38 - ], - [ - 34, - 53, - 6, - 40, - 30, - 2 - ], - [ - 17, - 29, - 50, - 5, - 47, - 27 - ], - [ - 48, - 38, - 57, - 63, - 32, - 60 - ], - [ - 42, - 7, - 49, - 46, - 32, - 1 - ], - [ - 59, - 1, - 23, - 14, - 10, - 16 - ], - [ - 21, - 35, - 7, - 53, - 6, - 17 - ], - [ - 8, - 24, - 60, - 13, - 55, - 14 - ], - [ - 33, - 5, - 51, - 15, - 3, - 23 - ], - [ - 4, - 9, - 51, - 26, - 41, - 28 - ], - [ - 60, - 4, - 19, - 47, - 40, - 48 - ], - [ - 19, - 3, - 26, - 21, - 53, - 8 - ], - [ - 53, - 1, - 35, - 54, - 60, - 20 - ] - ], - [ - [ - 37, - 46, - 39, - 54, - 27, - 55 - ], - [ - 34, - 5, - 16, - 47, - 6, - 42 - ], - [ - 32, - 38, - 16, - 42, - 3, - 20 - ], - [ - 43, - 19, - 60, - 42, - 52, - 11 - ], - [ - 7, - 61, - 39, - 57, - 12, - 46 - ], - [ - 50, - 36, - 44, - 24, - 28, - 31 - ], - [ - 15, - 42, - 41, - 2, - 40, - 32 - ], - [ - 36, - 35, - 38, - 53, - 58, - 51 - ], - [ - 50, - 4, - 16, - 3, - 36, - 58 - ], - [ - 59, - 16, - 32, - 38, - 26, - 56 - ], - [ - 3, - 45, - 37, - 41, - 21, - 33 - ], - [ - 21, - 53, - 34, - 35, - 5, - 39 - ], - [ - 35, - 42, - 63, - 16, - 11, - 46 - ], - [ - 23, - 9, - 51, - 54, - 15, - 38 - ], - [ - 2, - 5, - 20, - 43, - 24, - 29 - ], - [ - 53, - 44, - 9, - 2, - 54, - 20 - ], - [ - 27, - 46, - 1, - 47, - 50, - 5 - ], - [ - 61, - 54, - 57, - 44, - 51, - 43 - ], - [ - 19, - 9, - 33, - 38, - 61, - 37 - ], - [ - 0, - 33, - 15, - 32, - 6, - 9 - ], - [ - 26, - 27, - 53, - 5, - 47, - 54 - ], - [ - 55, - 34, - 12, - 62, - 3, - 4 - ], - [ - 41, - 5, - 46, - 40, - 4, - 32 - ], - [ - 55, - 20, - 44, - 26, - 4, - 40 - ], - [ - 62, - 12, - 28, - 34, - 23, - 33 - ], - [ - 18, - 7, - 22, - 3, - 54, - 14 - ], - [ - 36, - 9, - 27, - 52, - 48, - 11 - ] - ], - [ - [ - 46, - 37, - 61, - 18, - 36, - 63 - ], - [ - 22, - 34, - 28, - 59, - 24, - 56 - ], - [ - 32, - 15, - 17, - 60, - 38, - 20 - ], - [ - 28, - 4, - 58, - 16, - 30, - 35 - ], - [ - 7, - 36, - 9, - 57, - 33, - 23 - ], - [ - 43, - 63, - 2, - 30, - 11, - 19 - ], - [ - 19, - 42, - 57, - 15, - 3, - 22 - ], - [ - 35, - 46, - 47, - 6, - 58, - 8 - ], - [ - 32, - 4, - 37, - 36, - 35, - 57 - ], - [ - 21, - 47, - 55, - 58, - 38, - 54 - ], - [ - 3, - 42, - 2, - 38, - 5, - 17 - ], - [ - 43, - 18, - 2, - 21, - 5, - 10 - ], - [ - 10, - 49, - 24, - 32, - 25, - 4 - ], - [ - 30, - 21, - 63, - 49, - 16, - 51 - ], - [ - 62, - 2, - 13, - 5, - 29, - 40 - ], - [ - 53, - 2, - 9, - 63, - 46, - 54 - ], - [ - 27, - 0, - 63, - 47, - 5, - 14 - ], - [ - 60, - 57, - 43, - 44, - 0, - 50 - ], - [ - 38, - 9, - 61, - 33, - 60, - 13 - ], - [ - 25, - 0, - 15, - 34, - 35, - 11 - ], - [ - 26, - 54, - 28, - 47, - 53, - 37 - ], - [ - 55, - 1, - 34, - 4, - 21, - 22 - ], - [ - 41, - 5, - 40, - 21, - 17, - 23 - ], - [ - 25, - 43, - 52, - 26, - 4, - 59 - ], - [ - 52, - 29, - 35, - 17, - 45, - 60 - ], - [ - 38, - 10, - 15, - 7, - 50, - 3 - ], - [ - 23, - 6, - 19, - 56, - 41, - 15 - ] - ], - [ - [ - 44, - 14, - 20, - 47, - 19, - 56 - ], - [ - 28, - 34, - 2, - 56, - 0, - 11 - ], - [ - 46, - 15, - 61, - 14, - 22, - 60 - ], - [ - 15, - 50, - 36, - 47, - 25, - 21 - ], - [ - 25, - 12, - 13, - 36, - 23, - 57 - ], - [ - 22, - 43, - 1, - 37, - 36, - 30 - ], - [ - 28, - 42, - 19, - 31, - 14, - 21 - ], - [ - 35, - 51, - 47, - 33, - 7, - 46 - ], - [ - 32, - 4, - 16, - 11, - 35, - 19 - ], - [ - 21, - 38, - 16, - 47, - 62, - 15 - ], - [ - 3, - 45, - 39, - 50, - 21, - 2 - ], - [ - 21, - 5, - 56, - 43, - 53, - 52 - ], - [ - 10, - 41, - 33, - 11, - 63, - 37 - ], - [ - 56, - 16, - 9, - 63, - 33, - 45 - ], - [ - 58, - 52, - 2, - 5, - 30, - 56 - ], - [ - 53, - 8, - 59, - 55, - 2, - 15 - ], - [ - 0, - 47, - 51, - 45, - 14, - 37 - ], - [ - 0, - 43, - 21, - 12, - 60, - 62 - ], - [ - 38, - 53, - 60, - 34, - 36, - 9 - ], - [ - 27, - 9, - 56, - 0, - 53, - 11 - ], - [ - 28, - 12, - 43, - 54, - 5, - 62 - ], - [ - 50, - 55, - 34, - 16, - 4, - 21 - ], - [ - 4, - 21, - 5, - 40, - 32, - 54 - ], - [ - 25, - 57, - 49, - 1, - 44, - 43 - ], - [ - 5, - 35, - 42, - 25, - 22, - 2 - ], - [ - 15, - 28, - 38, - 55, - 35, - 37 - ], - [ - 19, - 30, - 6, - 57, - 39, - 51 - ] - ], - [ - [ - 11, - 31, - 46, - 49, - 0, - 16 - ], - [ - 13, - 49, - 50, - 16, - 31, - 19 - ], - [ - 36, - 13, - 27, - 34, - 4, - 53 - ], - [ - 24, - 32, - 7, - 59, - 13, - 15 - ], - [ - 17, - 35, - 2, - 44, - 10, - 63 - ], - [ - 8, - 7, - 23, - 26, - 56, - 42 - ], - [ - 58, - 61, - 29, - 38, - 62, - 50 - ], - [ - 20, - 42, - 3, - 35, - 61, - 47 - ], - [ - 4, - 10, - 7, - 47, - 25, - 34 - ], - [ - 54, - 55, - 18, - 38, - 27, - 28 - ], - [ - 60, - 1, - 14, - 59, - 3, - 30 - ], - [ - 51, - 31, - 5, - 21, - 25, - 52 - ], - [ - 34, - 10, - 2, - 26, - 11, - 47 - ], - [ - 16, - 40, - 34, - 23, - 9, - 33 - ], - [ - 34, - 8, - 1, - 31, - 5, - 32 - ], - [ - 25, - 59, - 8, - 52, - 53, - 27 - ], - [ - 13, - 47, - 45, - 48, - 42, - 0 - ], - [ - 4, - 23, - 53, - 10, - 3, - 25 - ], - [ - 53, - 8, - 38, - 11, - 24, - 63 - ], - [ - 38, - 15, - 0, - 11, - 21, - 8 - ], - [ - 27, - 23, - 62, - 41, - 8, - 42 - ], - [ - 55, - 11, - 10, - 41, - 16, - 34 - ], - [ - 31, - 21, - 41, - 54, - 34, - 9 - ], - [ - 55, - 25, - 22, - 33, - 44, - 47 - ], - [ - 47, - 45, - 20, - 35, - 12, - 42 - ], - [ - 50, - 10, - 47, - 35, - 53, - 57 - ], - [ - 17, - 31, - 5, - 37, - 36, - 20 - ] - ], - [ - [ - 22, - 6, - 29, - 39, - 57, - 44 - ], - [ - 27, - 6, - 14, - 17, - 55, - 57 - ], - [ - 1, - 11, - 29, - 26, - 47, - 4 - ], - [ - 14, - 38, - 31, - 22, - 36, - 29 - ], - [ - 14, - 61, - 59, - 16, - 44, - 63 - ], - [ - 30, - 8, - 47, - 23, - 21, - 0 - ], - [ - 58, - 4, - 54, - 61, - 15, - 62 - ], - [ - 20, - 42, - 58, - 3, - 35, - 0 - ], - [ - 10, - 47, - 4, - 8, - 41, - 19 - ], - [ - 54, - 55, - 33, - 11, - 38, - 21 - ], - [ - 14, - 1, - 4, - 60, - 20, - 40 - ], - [ - 51, - 5, - 14, - 0, - 32, - 62 - ], - [ - 2, - 10, - 36, - 49, - 32, - 52 - ], - [ - 13, - 40, - 54, - 36, - 22, - 11 - ], - [ - 2, - 44, - 37, - 58, - 5, - 8 - ], - [ - 6, - 24, - 2, - 53, - 19, - 52 - ], - [ - 13, - 47, - 61, - 5, - 50, - 30 - ], - [ - 55, - 58, - 4, - 63, - 22, - 38 - ], - [ - 35, - 32, - 46, - 14, - 7, - 58 - ], - [ - 15, - 13, - 45, - 0, - 51, - 60 - ], - [ - 15, - 23, - 27, - 50, - 6, - 17 - ], - [ - 57, - 36, - 10, - 55, - 14, - 16 - ], - [ - 10, - 23, - 41, - 33, - 30, - 54 - ], - [ - 33, - 4, - 26, - 10, - 49, - 55 - ], - [ - 4, - 8, - 33, - 9, - 20, - 12 - ], - [ - 43, - 6, - 3, - 50, - 18, - 38 - ], - [ - 4, - 55, - 43, - 36, - 46, - 5 - ] - ], - [ - [ - 48, - 38, - 63, - 47, - 7, - 42 - ], - [ - 3, - 10, - 26, - 6, - 2, - 38 - ], - [ - 39, - 44, - 6, - 7, - 45, - 8 - ], - [ - 60, - 31, - 22, - 0, - 54, - 27 - ], - [ - 6, - 59, - 16, - 14, - 37, - 44 - ], - [ - 59, - 8, - 30, - 47, - 48, - 3 - ], - [ - 44, - 54, - 4, - 56, - 15, - 13 - ], - [ - 12, - 24, - 20, - 58, - 31, - 61 - ], - [ - 47, - 33, - 10, - 8, - 4, - 36 - ], - [ - 54, - 40, - 55, - 33, - 11, - 51 - ], - [ - 8, - 14, - 23, - 29, - 22, - 31 - ], - [ - 34, - 59, - 14, - 5, - 57, - 50 - ], - [ - 44, - 36, - 10, - 59, - 52, - 9 - ], - [ - 59, - 44, - 13, - 9, - 54, - 1 - ], - [ - 24, - 2, - 46, - 38, - 44, - 47 - ], - [ - 34, - 53, - 6, - 2, - 57, - 40 - ], - [ - 17, - 29, - 47, - 5, - 50, - 34 - ], - [ - 48, - 63, - 57, - 38, - 60, - 32 - ], - [ - 42, - 7, - 49, - 32, - 46, - 1 - ], - [ - 1, - 59, - 23, - 14, - 16, - 10 - ], - [ - 21, - 35, - 7, - 53, - 6, - 17 - ], - [ - 8, - 24, - 60, - 14, - 13, - 55 - ], - [ - 33, - 5, - 51, - 15, - 23, - 3 - ], - [ - 9, - 4, - 51, - 26, - 41, - 8 - ], - [ - 4, - 60, - 19, - 47, - 40, - 9 - ], - [ - 19, - 3, - 26, - 21, - 17, - 8 - ], - [ - 1, - 35, - 53, - 54, - 60, - 20 - ] - ], - [ - [ - 12, - 41, - 14, - 62, - 24, - 10 - ], - [ - 10, - 53, - 39, - 35, - 41, - 58 - ], - [ - 33, - 32, - 50, - 31, - 3, - 34 - ], - [ - 43, - 10, - 42, - 11, - 17, - 47 - ], - [ - 42, - 12, - 11, - 19, - 58, - 54 - ], - [ - 36, - 50, - 55, - 61, - 25, - 56 - ], - [ - 41, - 22, - 16, - 52, - 2, - 15 - ], - [ - 26, - 36, - 62, - 53, - 15, - 51 - ], - [ - 58, - 16, - 5, - 53, - 3, - 49 - ], - [ - 32, - 46, - 26, - 45, - 16, - 62 - ], - [ - 41, - 45, - 56, - 49, - 11, - 3 - ], - [ - 52, - 34, - 35, - 50, - 21, - 53 - ], - [ - 59, - 53, - 46, - 30, - 39, - 37 - ], - [ - 20, - 9, - 52, - 2, - 7, - 33 - ], - [ - 20, - 50, - 24, - 29, - 23, - 2 - ], - [ - 53, - 2, - 44, - 41, - 9, - 13 - ], - [ - 47, - 27, - 1, - 5, - 45, - 46 - ], - [ - 61, - 7, - 51, - 30, - 35, - 9 - ], - [ - 19, - 34, - 32, - 17, - 2, - 14 - ], - [ - 15, - 32, - 6, - 45, - 9, - 11 - ], - [ - 35, - 27, - 53, - 6, - 13, - 60 - ], - [ - 26, - 62, - 16, - 28, - 41, - 3 - ], - [ - 5, - 20, - 46, - 37, - 11, - 55 - ], - [ - 55, - 47, - 4, - 16, - 14, - 27 - ], - [ - 36, - 11, - 27, - 62, - 33, - 7 - ], - [ - 33, - 7, - 54, - 3, - 32, - 12 - ], - [ - 58, - 52, - 27, - 26, - 48, - 38 - ] - ], - [ - [ - 6, - 52, - 19, - 63, - 46, - 38 - ], - [ - 8, - 42, - 4, - 47, - 57, - 56 - ], - [ - 31, - 46, - 32, - 4, - 14, - 10 - ], - [ - 28, - 27, - 4, - 37, - 58, - 20 - ], - [ - 57, - 59, - 60, - 22, - 62, - 14 - ], - [ - 30, - 2, - 9, - 57, - 11, - 13 - ], - [ - 20, - 19, - 57, - 42, - 51, - 27 - ], - [ - 46, - 35, - 47, - 32, - 7, - 0 - ], - [ - 12, - 4, - 35, - 10, - 50, - 47 - ], - [ - 15, - 47, - 54, - 25, - 38, - 51 - ], - [ - 18, - 42, - 5, - 15, - 38, - 61 - ], - [ - 18, - 22, - 8, - 16, - 5, - 7 - ], - [ - 5, - 3, - 24, - 35, - 4, - 30 - ], - [ - 17, - 13, - 20, - 55, - 36, - 22 - ], - [ - 22, - 10, - 21, - 54, - 47, - 6 - ], - [ - 30, - 53, - 6, - 19, - 2, - 54 - ], - [ - 22, - 26, - 5, - 7, - 47, - 21 - ], - [ - 4, - 41, - 13, - 46, - 55, - 43 - ], - [ - 17, - 56, - 32, - 45, - 14, - 6 - ], - [ - 5, - 49, - 53, - 28, - 34, - 60 - ], - [ - 42, - 55, - 57, - 17, - 28, - 22 - ], - [ - 40, - 23, - 28, - 57, - 16, - 21 - ], - [ - 51, - 21, - 35, - 24, - 44, - 10 - ], - [ - 38, - 25, - 4, - 14, - 62, - 31 - ], - [ - 31, - 50, - 13, - 56, - 39, - 53 - ], - [ - 6, - 51, - 55, - 8, - 0, - 21 - ], - [ - 29, - 46, - 18, - 55, - 37, - 50 - ] - ], - [ - [ - 31, - 46, - 49, - 59, - 35, - 14 - ], - [ - 13, - 16, - 31, - 50, - 33, - 19 - ], - [ - 13, - 36, - 27, - 52, - 3, - 19 - ], - [ - 9, - 24, - 29, - 12, - 55, - 61 - ], - [ - 35, - 17, - 2, - 10, - 41, - 48 - ], - [ - 51, - 23, - 30, - 57, - 52, - 17 - ], - [ - 34, - 48, - 0, - 38, - 27, - 51 - ], - [ - 47, - 32, - 2, - 35, - 58, - 55 - ], - [ - 15, - 12, - 35, - 45, - 52, - 4 - ], - [ - 36, - 15, - 4, - 59, - 11, - 54 - ], - [ - 60, - 5, - 59, - 50, - 29, - 18 - ], - [ - 18, - 8, - 31, - 23, - 24, - 3 - ], - [ - 34, - 4, - 5, - 58, - 14, - 30 - ], - [ - 17, - 57, - 36, - 55, - 51, - 6 - ], - [ - 10, - 45, - 8, - 54, - 22, - 47 - ], - [ - 30, - 50, - 14, - 6, - 37, - 16 - ], - [ - 26, - 0, - 22, - 5, - 13, - 4 - ], - [ - 4, - 46, - 23, - 43, - 60, - 34 - ], - [ - 45, - 8, - 56, - 62, - 17, - 51 - ], - [ - 4, - 38, - 53, - 15, - 58, - 5 - ], - [ - 57, - 24, - 34, - 15, - 55, - 42 - ], - [ - 23, - 37, - 40, - 41, - 54, - 11 - ], - [ - 48, - 29, - 51, - 31, - 34, - 9 - ], - [ - 62, - 14, - 18, - 4, - 31, - 59 - ], - [ - 50, - 31, - 49, - 45, - 56, - 57 - ], - [ - 51, - 50, - 56, - 8, - 52, - 12 - ], - [ - 17, - 37, - 5, - 39, - 44, - 20 - ] - ], - [ - [ - 45, - 13, - 63, - 37, - 38, - 56 - ], - [ - 63, - 6, - 12, - 18, - 27, - 51 - ], - [ - 3, - 21, - 4, - 48, - 17, - 27 - ], - [ - 14, - 55, - 9, - 37, - 29, - 26 - ], - [ - 35, - 57, - 2, - 13, - 41, - 10 - ], - [ - 39, - 30, - 57, - 13, - 53, - 23 - ], - [ - 34, - 48, - 38, - 27, - 56, - 51 - ], - [ - 47, - 35, - 46, - 2, - 32, - 7 - ], - [ - 12, - 56, - 4, - 35, - 52, - 50 - ], - [ - 15, - 36, - 14, - 30, - 47, - 48 - ], - [ - 5, - 60, - 59, - 22, - 15, - 46 - ], - [ - 8, - 22, - 23, - 47, - 5, - 26 - ], - [ - 4, - 5, - 30, - 58, - 26, - 8 - ], - [ - 17, - 29, - 13, - 50, - 14, - 34 - ], - [ - 54, - 10, - 41, - 14, - 21, - 5 - ], - [ - 50, - 6, - 54, - 30, - 56, - 16 - ], - [ - 0, - 47, - 7, - 4, - 5, - 34 - ], - [ - 4, - 46, - 23, - 41, - 59, - 55 - ], - [ - 62, - 1, - 45, - 47, - 57, - 32 - ], - [ - 4, - 53, - 15, - 50, - 60, - 1 - ], - [ - 34, - 57, - 24, - 31, - 15, - 53 - ], - [ - 23, - 41, - 45, - 12, - 57, - 14 - ], - [ - 51, - 29, - 49, - 48, - 18, - 33 - ], - [ - 14, - 4, - 62, - 26, - 18, - 28 - ], - [ - 50, - 49, - 7, - 24, - 9, - 48 - ], - [ - 56, - 20, - 51, - 3, - 14, - 26 - ], - [ - 55, - 37, - 50, - 14, - 42, - 20 - ] - ], - [ - [ - 51, - 43, - 27, - 30, - 5, - 55 - ], - [ - 24, - 16, - 48, - 15, - 7, - 30 - ], - [ - 26, - 21, - 50, - 52, - 56, - 4 - ], - [ - 19, - 17, - 2, - 14, - 57, - 22 - ], - [ - 3, - 35, - 37, - 45, - 1, - 6 - ], - [ - 13, - 30, - 23, - 39, - 57, - 60 - ], - [ - 9, - 34, - 48, - 17, - 27, - 26 - ], - [ - 47, - 32, - 2, - 59, - 28, - 57 - ], - [ - 18, - 12, - 31, - 20, - 52, - 4 - ], - [ - 14, - 15, - 36, - 30, - 58, - 31 - ], - [ - 5, - 4, - 26, - 19, - 22, - 59 - ], - [ - 22, - 8, - 34, - 26, - 42, - 52 - ], - [ - 20, - 5, - 52, - 4, - 30, - 33 - ], - [ - 34, - 17, - 2, - 5, - 39, - 20 - ], - [ - 54, - 61, - 30, - 14, - 20, - 25 - ], - [ - 56, - 41, - 2, - 5, - 54, - 37 - ], - [ - 12, - 34, - 47, - 7, - 54, - 59 - ], - [ - 9, - 11, - 2, - 43, - 33, - 50 - ], - [ - 8, - 10, - 19, - 20, - 32, - 18 - ], - [ - 17, - 38, - 6, - 29, - 49, - 41 - ], - [ - 38, - 52, - 45, - 57, - 63, - 27 - ], - [ - 26, - 28, - 3, - 40, - 5, - 47 - ], - [ - 60, - 49, - 35, - 34, - 38, - 3 - ], - [ - 61, - 24, - 4, - 55, - 45, - 16 - ], - [ - 46, - 11, - 27, - 0, - 56, - 48 - ], - [ - 60, - 45, - 44, - 25, - 32, - 3 - ], - [ - 11, - 38, - 52, - 48, - 9, - 21 - ] - ], - [ - [ - 22, - 19, - 46, - 31, - 3, - 23 - ], - [ - 32, - 62, - 15, - 54, - 10, - 55 - ], - [ - 47, - 30, - 38, - 5, - 7, - 60 - ], - [ - 15, - 13, - 1, - 8, - 2, - 25 - ], - [ - 13, - 59, - 5, - 6, - 62, - 52 - ], - [ - 27, - 63, - 62, - 45, - 12, - 56 - ], - [ - 50, - 9, - 8, - 51, - 48, - 18 - ], - [ - 59, - 57, - 28, - 2, - 61, - 6 - ], - [ - 18, - 59, - 6, - 52, - 39, - 57 - ], - [ - 14, - 23, - 11, - 36, - 15, - 32 - ], - [ - 26, - 5, - 42, - 25, - 22, - 23 - ], - [ - 33, - 22, - 55, - 28, - 24, - 16 - ], - [ - 6, - 20, - 33, - 14, - 52, - 15 - ], - [ - 17, - 6, - 5, - 39, - 2, - 34 - ], - [ - 54, - 62, + 15, + 30, 25, - 61, - 21, - 14 - ], - [ - 41, - 45, - 14, - 5, - 2, - 54 - ], - [ - 47, - 34, - 10, - 31, - 5, - 41 - ], - [ - 9, - 2, - 36, - 6, - 43, 38 ], [ - 57, - 20, - 41, - 10, - 32, - 18 - ], - [ - 47, - 6, - 49, - 15, - 7, - 34 - ], - [ - 3, - 57, - 44, - 38, - 50, - 53 - ], - [ - 47, - 28, - 57, - 26, - 19, - 22 - ], - [ - 11, - 1, - 18, - 5, - 46, - 22 - ], - [ - 9, - 55, 4, - 3, - 25, - 10 - ], - [ - 45, - 11, - 0, - 63, - 48, - 57 - ], - [ - 42, - 10, - 3, - 43, - 49, - 0 - ], - [ - 38, - 15, - 17, - 41, - 10, - 3 - ] - ], - [ - [ 19, - 18, - 51, - 25, - 60, - 55 - ], - [ - 56, - 27, - 61, - 42, - 55, - 23 + 24, + 35, + 31, + 48 ], [ - 32, - 39, - 37, + 7, 46, - 20, - 52 - ], - [ - 41, - 21, - 37, - 13, - 57, - 2 - ], - [ - 10, - 9, 3, - 46, 58, - 32 - ], - [ - 62, - 57, - 27, - 43, - 2, - 53 - ], - [ - 50, - 21, - 19, - 48, - 15, - 6 - ], - [ - 14, - 21, - 54, - 8, - 57, - 28 + 30, + 41 ], [ - 6, - 18, - 4, - 52, + 58, + 9, 39, - 24 - ], - [ 32, - 38, - 23, - 53, - 25, - 17 - ], - [ - 26, - 22, - 25, - 5, - 42, - 33 - ], - [ - 5, - 22, - 16, - 9, - 61, - 55 + 29, + 40 ], [ - 49, - 33, - 30, - 25, + 40, + 37, 20, - 22 - ], - [ - 18, - 17, + 8, 25, - 63, - 39, - 11 - ], - [ - 62, - 54, - 10, - 5, - 58, - 37 - ], - [ - 42, - 41, - 5, - 24, - 37, - 54 - ], - [ - 47, - 34, - 27, - 10, - 22, 55 ], [ - 60, - 43, - 44, - 7, - 52, - 37 - ], - [ - 12, - 32, - 39, - 38, - 1, - 20 + 19, + 0, + 54, + 52, + 17, + 39 ], [ - 34, - 52, - 49, - 15, - 28, - 40 + 25, + 43, + 12, + 61, + 14, + 11 ], [ + 23, + 4, + 54, + 36, 28, - 39, - 3, - 26, - 30, - 22 + 33 ], [ - 29, - 1, 40, - 22, - 19, - 63 + 2, + 25, + 58, + 36, + 53 ], [ - 41, - 5, + 18, + 46, + 35, + 22, 53, - 33, - 26, - 39 + 16 ], [ - 25, - 38, - 4, - 34, - 49, - 51 + 2, + 6, + 63, + 14, + 42, + 11 ], [ - 29, + 35, + 7, 52, - 48, - 47, - 20, - 33 + 40, + 29, + 57 ], [ - 50, - 3, - 16, - 38, - 53, - 15 + 40, + 15, + 19, + 57, + 17, + 23 ], [ - 19, - 62, - 6, - 23, - 10, - 36 + 9, + 11, + 47, + 22, + 49, + 1 + ], + [ + 24, + 39, + 42, + 2, + 16, + 0 ] ], [ [ - 5, - 14, - 17, - 57, - 10, - 27 + 18, + 8, + 20, + 49, + 30, + 23 ], [ - 43, - 9, - 56, 1, - 14, - 33 + 27, + 26, + 22, + 59, + 36 ], [ - 63, - 35, 43, - 1, - 10, - 27 - ], - [ - 51, - 50, - 57, - 41, - 20, - 54 + 26, + 15, + 58, + 0, + 46 ], [ - 11, - 43, - 40, - 54, - 30, - 33 + 55, + 1, + 35, + 28, + 16, + 32 ], [ - 27, + 59, + 9, + 10, 53, - 63, - 30, - 15, + 12, 58 ], [ - 51, - 48, - 21, - 57, 9, - 50 + 2, + 27, + 11, + 61, + 43 ], [ - 21, - 14, - 59, - 52, - 28, - 7 + 16, + 57, + 63, + 23, + 19, + 12 ], [ - 6, - 18, + 46, + 45, + 26, 4, - 0, - 12, - 59 + 30, + 37 ], [ + 43, + 44, + 20, + 16, 14, - 53, - 30, - 42, - 4, - 36 + 9 ], [ - 26, + 34, + 47, 42, - 5, - 22, - 58, - 19 + 43, + 26, + 51 ], [ - 22, - 5, - 34, + 42, + 2, 38, - 52, - 29 - ], - [ - 49, - 33, - 0, + 45, 20, - 26, - 8 + 36 ], [ - 17, + 18, + 7, + 12, 2, - 39, - 13, - 44, - 63 + 43, + 60 ], [ - 10, - 54, - 20, - 37, - 5, - 59 + 1, + 28, + 12, + 3, + 29, + 33 + ], + [ + 25, + 13, + 0, + 63, + 2, + 62 + ], + [ + 18, + 36, + 6, + 29, + 19, + 15 ], [ + 1, + 42, + 63, 41, - 43, - 24, - 5, - 53, - 14 + 57, + 19 ], [ - 12, - 47, - 34, 57, + 54, + 5, 27, - 5 + 18, + 31 ], [ - 9, - 33, - 7, - 38, - 43, - 31 + 50, + 6, + 13, + 32, + 17, + 20 ], [ - 19, - 10, - 20, + 17, + 5, + 27, 32, - 18, - 1 + 1, + 55 ], [ - 36, - 63, - 15, 49, - 50, - 40 + 0, + 61, + 5, + 10, + 30 ], [ - 39, - 3, - 38, - 27, - 36, - 35 + 29, + 53, + 51, + 13, + 33, + 46 ], [ 29, - 19, - 40, - 47, - 62, - 14 + 17, + 21, + 30, + 14, + 40 ], [ 5, - 46, + 17, 33, - 53, - 49, - 21 + 32, + 18, + 28 ], [ - 55, - 4, - 27, - 61, 51, - 34 + 4, + 20, + 54, + 58, + 41 ], [ - 11, - 0, - 59, + 47, + 4, + 27, 48, - 14, - 27 + 37, + 60 ], [ - 7, - 32, 3, - 51, - 18, - 14 + 26, + 12, + 59, + 2, + 48 ], [ - 11, + 46, + 43, + 18, + 20, 9, - 36, - 48, - 0, - 46 + 53 ] ], [ [ - 63, - 62, - 60, + 57, + 9, 19, - 23, - 56 + 51, + 18, + 41 ], [ + 28, + 57, + 36, + 8, 48, - 32, - 1, - 35, - 5, - 21 + 60 ], [ - 22, - 24, - 46, - 58, + 2, + 51, 59, - 60 + 5, + 34, + 9 ], [ - 27, - 37, - 50, - 28, - 61, - 6 + 9, + 55, + 59, + 26, + 4, + 2 ], [ - 10, - 12, - 15, - 58, + 49, + 56, 35, + 42, + 30, 23 ], [ - 2, - 43, - 57, - 36, + 18, 30, - 20 - ], - [ - 21, - 14, + 22, + 29, 19, - 63, - 41, - 42 - ], - [ - 46, - 7, - 35, - 43, - 21, - 36 + 52 ], [ - 6, - 18, - 0, - 35, - 40, - 4 + 39, + 34, + 33, + 51, + 56, + 3 ], [ - 47, - 53, 32, - 38, + 21, 1, - 58 - ], - [ - 26, - 18, - 22, - 15, - 5, - 46 + 7, + 46, + 49 ], [ - 7, - 22, - 24, - 63, - 53, - 5 + 33, + 54, + 23, + 21, + 12, + 11 ], [ - 11, - 3, - 10, - 37, - 18, - 24 + 5, + 30, + 60, + 47, + 15, + 18 ], [ - 63, + 4, 18, - 13, - 39, - 0, - 17 + 46, + 27, + 20, + 22 ], [ - 62, + 22, + 59, 54, - 5, - 18, - 6, - 14 + 48, + 19, + 4 ], [ - 19, - 43, + 17, + 5, + 56, 31, - 30, - 57, - 42 + 49, + 4 ], [ - 24, - 5, - 51, - 34, + 29, 47, - 21 + 55, + 2, + 53, + 60 ], [ - 59, - 13, - 33, - 44, + 8, + 22, 11, - 41 + 44, + 36, + 15 ], [ - 58, - 32, - 1, - 7, - 39, - 38 + 60, + 44, + 30, + 57, + 54, + 39 ], [ - 49, - 13, - 28, - 9, - 34, - 7 + 7, + 44, + 27, + 20, + 2, + 61 ], [ + 48, + 17, + 21, + 37, 32, - 51, - 3, - 53, + 57 + ], + [ 48, - 13 + 32, + 46, + 6, + 61, + 42 ], [ - 40, - 15, - 37, + 4, 57, 1, - 23 + 36, + 0, + 30 ], [ - 5, + 7, + 17, + 61, 53, - 51, - 35, - 22, - 38 + 21, + 63 ], [ - 4, + 60, 14, - 44, - 41, + 53, + 35, + 18, + 55 + ], + [ + 10, + 15, + 33, + 51, + 36, + 5 + ], + [ 11, - 51 + 4, + 19, + 51, + 21, + 52 ], [ 47, + 19, + 43, 48, - 8, - 61, - 13, + 58, 4 ], [ - 23, 3, - 27, - 43, - 0, - 1 + 33, + 26, + 21, + 52, + 19 ], [ - 46, - 23, - 59, - 62, - 18, - 50 + 24, + 45, + 60, + 35, + 49, + 1 ] ], [ [ - 62, - 0, - 9, - 61, + 23, 54, - 32 + 53, + 58, + 11, + 8 ], [ - 45, + 11, + 30, + 15, + 59, + 63, + 55 + ], + [ + 20, + 58, 29, - 7, - 35, - 22, - 62 + 17, + 42, + 30 ], [ - 56, - 31, - 23, - 53, - 28, - 2 + 18, + 1, + 43, + 15, + 8, + 3 ], [ - 36, - 2, - 5, - 4, - 48, - 41 + 59, + 55, + 13, + 28, + 26, + 63 ], [ - 18, - 0, - 15, - 23, - 16, - 11 + 33, + 45, + 27, + 53, + 63, + 19 ], [ - 4, - 36, + 28, 57, - 56, - 14, - 5 + 51, + 54, + 34, + 53 ], [ - 59, - 56, - 21, - 8, - 7, - 33 + 24, + 13, + 3, + 47, + 45, + 50 ], [ - 12, - 23, - 7, - 2, - 28, - 34 + 47, + 59, + 33, + 57, + 37, + 35 ], [ - 17, - 6, 23, - 19, - 62, - 27 - ], - [ - 53, - 32, - 51, - 38, + 42, + 33, 41, - 58 + 48, + 30 ], [ - 18, + 42, 63, - 19, - 22, - 26, - 5 + 23, + 25, + 17, + 34 ], [ - 22, - 7, - 60, - 11, - 12, - 19 + 14, + 62, + 2, + 19, + 45, + 43 ], [ - 24, - 7, - 11, - 17, + 12, + 6, + 20, 30, - 37 + 29, + 17 ], [ - 47, - 13, - 63, - 50, - 39, - 45 + 6, + 3, + 17, + 8, + 27, + 31 ], [ - 62, + 19, + 59, + 2, + 10, 54, - 18, - 14, - 29, - 56 + 55 ], [ - 56, - 30, - 0, - 55, - 51, - 10 + 45, + 61, + 19, + 57, + 42, + 17 ], [ - 12, - 5, - 21, + 55, + 60, + 41, 34, - 29, - 63 + 35, + 53 ], [ - 42, + 36, + 2, 8, + 31, + 14, + 5 + ], + [ + 36, + 45, + 16, 38, + 51, + 21 + ], + [ 57, - 41, - 44 + 39, + 48, + 6, + 19, + 30 ], [ - 32, - 54, - 1, + 39, 37, - 49, + 59, + 48, + 42, 40 ], [ - 5, - 57, - 49, + 52, 28, - 34, - 10 - ], - [ + 33, 46, - 32, - 13, - 7, - 61, - 48 + 18, + 55 ], [ - 40, - 3, - 30, - 60, - 39, - 14 + 1, + 15, + 28, + 13, + 52, + 18 ], [ - 5, - 44, - 33, - 36, - 28, + 10, + 21, + 4, + 2, + 23, 31 ], [ - 4, - 11, - 28, + 45, + 9, 41, - 51, - 5 + 18, + 54, + 3 ], [ - 48, - 47, + 42, 61, - 28, - 60, + 36, + 3, + 19, 27 ], [ - 14, + 41, + 46, + 33, 3, - 26, - 12, - 53, - 61 - ], - [ - 54, - 60, - 49, - 35, 10, - 62 + 15 ] ], [ [ - 27, - 62, - 63, - 23, - 47, - 56 + 19, + 1, + 31, + 52, + 49, + 63 ], [ 7, - 4, - 2, - 10, - 35, - 36 + 47, + 5, + 60, + 22, + 46 ], [ + 59, + 30, 3, + 11, 0, - 27, - 62, - 50, - 60 + 19 ], [ - 36, - 3, + 43, 42, - 18, - 2, - 48 + 19, + 62, + 8, + 56 ], [ 61, - 12, - 27, - 10, 15, - 14 + 25, + 18, + 39, + 27 ], [ + 61, + 50, 36, + 45, 33, - 50, - 6, - 29, - 16 + 44 ], [ - 2, - 41, 8, - 43, - 40, - 59 + 37, + 52, + 1, + 2, + 41 ], [ 36, + 30, 53, - 15, - 7, - 37, - 2 + 11, + 16, + 29 ], [ - 3, + 14, 58, - 4, - 19, - 5, - 27 + 46, + 49, + 3, + 26 ], [ - 22, - 26, - 53, + 62, + 43, + 0, 45, - 25, - 38 + 22, + 46 ], [ + 6, + 56, + 45, 18, - 41, - 22, - 3, - 34, - 45 + 10, + 41 ], [ + 60, 21, - 53, - 5, - 22, - 28, - 9 + 50, + 47, + 30, + 35 ], [ 11, - 24, + 53, 28, - 16, - 8, - 35 + 56, + 41, + 39 ], [ - 38, + 23, 9, - 21, - 52, - 41, - 39 + 33, + 28, + 22, + 26 ], [ + 23, + 56, + 34, 27, - 54, - 14, - 5, - 8, + 2, 63 ], [ - 56, - 55, - 43, - 30, - 35, - 51 + 22, + 9, + 44, + 41, + 37, + 47 ], [ - 51, + 1, + 11, + 46, + 27, 3, - 14, - 60, - 5, - 26 + 52 ], [ - 62, - 30, - 8, + 51, + 37, + 17, 21, - 44, - 35 + 61, + 30 ], [ - 38, - 34, - 27, + 13, + 19, 32, - 22, - 8 + 5, + 2, + 9 ], [ - 22, - 53, - 28, - 60, - 49, - 12 + 41, + 32, + 6, + 47, + 29, + 56 ], [ - 49, - 32, - 41, 53, - 12, - 33 + 33, + 9, + 35, + 38, + 12 ], [ 40, - 45, - 22, - 44, - 1, - 34 + 19, + 51, + 7, + 26, + 22 ], [ + 37, 5, - 4, - 53, - 25, - 63, - 41 - ], - [ - 4, - 11, 25, - 43, - 31, - 14 - ], - [ - 31, - 52, - 5, - 58, - 43, - 48 - ], - [ - 23, - 1, - 28, - 3, - 0, - 55 + 46, + 34, + 53 ], [ - 27, - 45, - 32, - 4, - 30, - 6 - ] - ], - [ - [ - 41, - 2, - 42, 16, - 50, - 32 - ], - [ - 51, - 5, - 41, - 40, + 55, + 20, + 24, 44, - 21 + 53 ], [ - 43, - 1, - 29, - 55, 21, - 35 - ], - [ + 46, + 11, + 0, 36, - 58, - 25, - 3, - 18, - 54 + 7 ], [ - 31, + 18, 60, - 24, - 12, - 61, - 41 + 32, + 3, + 34, + 28 ], [ - 4, - 10, - 35, + 52, + 9, 36, - 0, - 43 + 48, + 11, + 41 + ] + ], + [ + [ + 17, + 37, + 31, + 32, + 63, + 50 ], [ - 45, - 43, - 63, - 35, - 36, - 48 + 12, + 2, + 9, + 32, + 47, + 17 ], [ 3, - 9, - 8, - 7, - 43, - 27 + 57, + 56, + 50, + 33, + 38 ], [ - 0, 43, - 4, - 40, - 18, - 44 + 42, + 19, + 52, + 8, + 17 ], [ - 34, + 61, + 39, + 27, + 12, 15, - 50, - 38, - 53, - 25 + 57 + ], + [ + 6, + 33, + 36, + 44, + 29, + 61 ], [ - 24, - 46, - 26, - 13, 2, - 18 + 41, + 42, + 15, + 52, + 5 ], [ - 57, + 36, 37, + 15, + 53, + 18, + 62 + ], + [ + 50, + 58, + 3, 5, + 16, + 4 + ], + [ + 16, 22, - 7, - 53 + 55, + 32, + 41, + 26 ], [ 3, - 9, + 41, + 56, + 45, + 34, + 10 + ], + [ + 21, + 53, + 50, + 34, 38, - 26, - 37, - 23 + 35 ], [ - 60, - 26, - 0, - 39, - 13, - 55 + 35, + 11, + 37, + 16, + 53, + 42 ], [ + 9, + 15, 54, - 21, - 58, - 6, - 55, - 14 + 38, + 12, + 52 ], [ - 11, - 19, - 43, - 57, 56, - 37 - ], - [ - 10, + 2, + 23, 24, - 60, - 21, - 5, - 17 + 9, + 5 ], [ - 31, - 20, - 63, + 2, 44, - 55, - 33 + 9, + 53, + 35, + 54 ], [ - 7, - 25, - 40, - 32, - 54, - 58 + 27, + 46, + 1, + 5, + 60, + 50 ], [ - 49, - 30, - 13, + 51, + 61, 50, - 55, - 28 + 54, + 33, + 44 ], [ - 30, - 3, - 51, - 61, - 0, - 16 + 2, + 38, + 19, + 9, + 5, + 32 ], [ - 57, - 46, - 43, - 40, - 30, - 22 + 32, + 28, + 6, + 15, + 0, + 33 ], [ - 52, - 33, - 51, - 19, + 26, + 53, 27, - 29 + 13, + 5, + 4 ], [ - 14, - 4, + 34, 40, + 45, + 55, + 62, + 53 + ], + [ 41, - 0, - 34 + 5, + 46, + 4, + 45, + 3 ], [ + 20, 4, - 13, - 8, - 61, + 22, 47, - 9 + 59, + 58 ], [ - 31, - 27, - 43, - 1, + 62, + 36, + 25, + 28, + 53, + 33 + ], + [ + 22, + 33, + 7, 3, + 54, 0 ], [ - 8, - 4, - 46, - 9, - 3, - 50 + 58, + 27, + 52, + 48, + 45, + 17 ] ] + ], + "routing_indices_token_indices": [ + 3, + 13, + 14, + 17, + 28, + 31, + 35, + 81, + 86, + 94 ] } } \ No newline at end of file diff --git a/tests/functional_tests/test_cases/moe/gpt_dynamic_inference_tp4_etp1_pp1_ep8_16B_logitsmatch_zmq/golden_values_dev_dgx_h100.json b/tests/functional_tests/test_cases/moe/gpt_dynamic_inference_tp4_etp1_pp1_ep8_16B_logitsmatch_zmq/golden_values_dev_dgx_h100.json index 032eaf98f95..e277ccea027 100644 --- a/tests/functional_tests/test_cases/moe/gpt_dynamic_inference_tp4_etp1_pp1_ep8_16B_logitsmatch_zmq/golden_values_dev_dgx_h100.json +++ b/tests/functional_tests/test_cases/moe/gpt_dynamic_inference_tp4_etp1_pp1_ep8_16B_logitsmatch_zmq/golden_values_dev_dgx_h100.json @@ -157,25510 +157,2196 @@ "routing_indices": [ [ [ + 48, 33, - 50, - 36, - 4, - 25, - 63 + 24, + 52, + 38, + 61 ], [ - 0, - 16, 3, - 26, - 9, - 54 + 40, + 45, + 19, + 58, + 35 ], [ - 62, + 46, + 39, + 15, 60, - 8, - 16, - 58, - 52 + 31, + 25 ], [ - 58, - 10, - 6, + 49, + 62, + 61, + 48, + 55, + 46 + ], + [ + 41, + 35, + 53, + 52, + 13, + 10 + ], + [ + 52, 45, - 16, - 32 + 15, + 63, + 46, + 10 ], [ - 43, + 14, + 15, + 35, + 60, 49, - 18, - 54, - 55, - 13 + 31 ], [ - 27, - 19, - 26, 44, - 12, - 28 + 3, + 6, + 8, + 23, + 48 ], [ - 53, - 42, - 3, + 32, + 15, + 44, 27, - 26, - 19 + 4, + 41 ], [ - 6, - 47, - 1, - 19, - 8, - 22 + 51, + 25, + 62, + 53, + 48, + 10 ], [ - 51, - 27, - 1, - 38, + 8, + 50, + 5, + 19, 16, - 62 + 22 ], [ - 41, + 36, 49, - 21, - 57, - 16, - 24 + 60, + 44, + 15, + 33 ], [ 13, - 28, - 38, + 27, + 53, + 30, + 56, + 43 + ], + [ + 26, 22, - 49, - 48 + 14, + 7, + 32, + 17 ], [ - 52, + 26, + 60, + 2, 58, - 6, - 25, - 29, - 17 + 54, + 10 ], [ - 49, 50, - 25, - 41, - 54, - 58 + 55, + 17, + 51, + 47, + 14 + ], + [ + 0, + 12, + 16, + 4, + 23, + 9 ], [ + 10, + 23, 27, - 41, - 3, - 26, - 1, - 29 + 46, + 56, + 55 ], [ - 41, - 18, - 34, - 45, - 1, - 52 + 3, + 37, + 4, + 60, + 16, + 59 ], [ - 17, - 26, - 59, - 22, - 19, - 4 + 38, + 3, + 29, + 40, + 25, + 50 ], [ - 1, - 43, + 31, + 16, 62, - 57, - 61, - 29 + 54, + 42, + 63 ], [ + 47, + 35, 11, + 37, 46, - 15, - 28, - 61, - 50 - ], - [ - 14, - 30, - 58, - 26, - 38, - 53 + 32 ], [ - 59, + 51, + 27, 20, - 63, - 54, - 47, - 61 + 50, + 16, + 55 ], [ - 9, - 5, - 43, - 33, - 15, - 46 + 63, + 55, + 46, + 27, + 48, + 12 ], [ - 16, - 52, - 33, - 61, - 49, - 11 - ], - [ - 52, - 35, - 40, - 43, - 29, - 36 - ], - [ - 57, - 34, - 38, - 44, - 20, - 18 + 53, + 50, + 30, + 2, + 39, + 20 ], [ + 53, + 24, 44, + 8, 51, - 2, - 63, - 7, - 17 - ], - [ - 32, - 47, - 58, - 9, - 54, - 5 + 14 ], [ - 12, - 59, - 54, - 33, - 50, - 6 + 19, + 49, + 37, + 14, + 44, + 0 ] ], [ [ - 49, - 43, - 18, - 28, - 23, - 25 - ], - [ + 21, + 50, + 29, + 41, 34, - 24, - 23, - 60, - 2, - 18 + 60 ], [ + 28, + 51, + 60, 33, - 40, - 30, - 3, - 59, - 48 - ], - [ - 16, - 58, - 17, - 48, - 6, + 14, 45 ], [ - 23, - 58, + 31, + 2, 46, - 37, - 34, - 48 + 33, + 24, + 49 ], [ - 28, - 26, - 35, - 33, - 43, - 22 + 42, + 3, + 18, + 62, + 39, + 49 ], [ - 6, - 31, - 13, - 46, - 41, - 37 + 25, + 15, + 62, + 27, + 12, + 11 ], [ - 60, - 29, 44, + 50, 36, - 39, - 15 + 57, + 55, + 41 ], [ - 51, - 18, - 62, - 5, - 27, - 35 + 41, + 37, + 22, + 15, + 2, + 40 ], [ + 36, 62, - 45, - 32, - 56, - 25, - 3 + 53, + 30, + 14, + 57 ], [ - 6, - 20, - 3, 16, - 49, - 41 + 58, + 2, + 29, + 4, + 3 ], [ - 36, 41, - 50, + 38, + 26, + 16, 45, - 35, - 48 + 46 ], [ - 39, + 45, 46, - 48, - 25, + 32, + 41, + 56, + 26 + ], + [ + 17, + 53, 21, - 33 + 11, + 36, + 35 ], [ - 30, - 38, 11, + 16, + 28, + 14, + 51, + 61 + ], + [ + 9, + 35, + 33, 22, - 15, - 9 + 52, + 62 ], [ - 24, - 2, - 32, - 56, + 58, + 50, 63, - 3 + 30, + 7, + 27 ], [ - 36, 55, - 35, - 32, - 17, - 44 + 3, + 8, + 41, + 63, + 37 ], [ - 59, + 3, + 51, 46, 32, - 44, - 24, - 14 - ], - [ - 51, 15, - 61, - 43, - 30, - 22 + 6 ], [ 32, - 2, + 12, + 10, + 25, 5, + 49 + ], + [ + 34, + 2, + 37, + 61, 39, - 11, - 50 + 63 ], [ - 28, 42, - 6, - 30, - 57, - 37 + 22, + 27, + 53, + 11, + 56 ], [ - 28, - 55, - 45, + 53, + 12, 0, - 7, - 5 + 47, + 61, + 1 ], [ + 39, + 45, + 53, + 17, 48, - 40, - 34, - 3, - 22, - 49 + 14 ], [ - 25, 6, - 62, - 18, - 50, - 53 + 0, + 4, + 53, + 25, + 11 ], [ - 13, - 22, - 20, - 28, - 25, - 59 + 51, + 11, + 1, + 63, + 54, + 45 ], [ - 58, - 39, - 36, - 47, - 29, - 37 + 40, + 56, + 37, + 53, + 5, + 35 ], [ - 4, - 33, + 59, + 28, 41, - 12, - 3, - 17 + 10, + 1, + 45 ], [ - 22, - 13, - 20, - 52, + 27, + 30, + 28, 24, - 62 + 32, + 57 ] ], [ [ - 17, - 10, - 57, - 54, + 24, + 56, 6, - 15 + 0, + 19, + 45 ], [ - 33, - 43, - 13, - 1, - 16, - 62 + 11, + 57, + 59, + 25, + 46, + 30 ], [ - 63, - 1, - 35, - 43, - 27, - 10 + 11, + 26, + 37, + 29, + 14, + 52 ], [ - 47, - 4, + 3, + 32, + 7, 38, - 50, - 51, - 0 + 36, + 24 ], [ - 11, - 51, - 57, - 23, + 61, + 2, + 24, 14, - 34 + 51, + 44 ], [ - 10, - 43, - 35, - 33, 20, - 22 + 47, + 0, + 63, + 30, + 58 ], [ + 4, 36, - 48, - 35, - 19, - 21, - 28 + 29, + 58, + 16, + 3 ], [ + 20, + 0, + 45, 14, - 8, - 7, - 46, - 35, - 13 + 28, + 44 ], [ - 18, - 44, - 63, - 6, - 4, - 37 + 29, + 56, + 47, + 35, + 16, + 4 ], [ - 62, - 29, - 15, - 38, - 39, - 34 + 33, + 61, + 55, + 41, + 51, + 38 ], [ + 58, 1, - 6, - 16, - 46, - 22, - 13 + 38, + 14, + 4, + 19 ], [ + 0, 36, - 8, - 16, - 37, - 10, - 14 + 14, + 18, + 52, + 42 ], [ + 29, + 36, + 45, + 25, 8, - 0, - 32, - 3, - 43, - 10 + 6 ], [ - 25, - 0, - 22, - 30, - 60, - 57 + 6, + 57, + 50, + 40, + 58, + 61 ], [ - 60, + 44, 58, - 55, - 32, - 2, - 7 + 29, + 19, + 61, + 56 ], [ + 23, + 18, + 28, 55, - 11, - 19, 5, - 24, - 43 + 37 ], [ + 13, 9, - 47, - 36, - 39, - 5, - 42 + 19, + 43, + 37, + 3 ], [ - 31, - 20, - 9, - 43, - 18, + 32, + 22, + 63, + 14, + 57, 41 ], [ - 12, - 5, - 32, - 50, + 10, + 61, 3, - 31 + 1, + 19, + 32 ], [ - 40, - 18, - 30, - 63, - 7, - 25 + 16, + 55, + 10, + 41, + 59, + 22 ], [ - 14, + 53, 7, - 3, + 29, 38, - 59, - 54 + 27, + 46 ], [ - 31, - 20, - 1, - 22, + 24, 47, - 6 + 18, + 53, + 39, + 30 ], [ + 0, + 33, + 19, + 5, 51, - 15, - 18, - 53, - 40, - 52 + 17 ], [ - 27, - 54, + 51, 8, - 38, - 3, - 59 + 11, + 45, + 44, + 41 ], [ + 40, + 4, + 23, 11, - 16, - 39, - 59, - 9, - 23 + 27, + 19 ], [ - 7, - 37, - 51, - 30, + 16, 18, - 48 + 3, + 48, + 51, + 21 ], [ - 0, - 36, - 33, - 40, + 43, 46, - 48 + 60, + 19, + 53, + 12 ] ], [ [ - 48, - 33, 24, - 52, - 38, - 61 - ], - [ - 3, - 40, - 45, + 56, + 6, + 0, 19, - 58, - 35 - ], - [ - 46, - 39, - 15, - 60, - 31, - 25 - ], - [ - 49, - 62, - 61, - 48, - 55, - 46 - ], - [ - 41, - 35, - 53, - 52, - 13, - 10 + 45 ], [ - 52, - 45, - 15, - 63, + 11, + 57, + 59, 46, - 10 + 25, + 30 ], [ + 26, + 11, + 37, 14, - 15, - 35, - 60, - 49, - 31 + 29, + 49 ], [ - 44, + 38, + 36, 3, - 6, - 8, - 23, - 48 - ], - [ - 32, - 15, - 44, - 27, - 4, - 41 + 24, + 18, + 20 ], [ + 61, 51, - 25, - 62, - 53, - 48, - 10 + 14, + 2, + 24, + 1 ], [ + 20, + 0, + 47, + 30, 8, - 50, - 5, - 19, - 16, - 22 + 35 ], [ + 4, + 58, 36, - 49, - 60, + 54, + 29, + 12 + ], + [ + 20, + 58, 44, - 15, - 33 + 28, + 45, + 9 ], [ - 13, - 27, - 53, - 30, 56, - 43 + 47, + 10, + 29, + 35, + 27 ], [ - 26, - 22, - 14, - 7, - 32, - 17 + 61, + 33, + 55, + 54, + 4, + 36 ], [ - 26, - 60, - 2, 58, - 54, - 10 + 1, + 14, + 4, + 38, + 52 ], [ - 50, - 55, - 17, - 51, - 47, - 14 + 14, + 0, + 36, + 63, + 15, + 52 ], [ - 0, - 12, + 29, + 36, + 44, + 8, 16, - 4, - 23, - 9 + 2 ], [ - 10, - 23, + 6, + 40, 27, - 46, - 56, - 55 + 57, + 50, + 42 ], [ - 3, + 19, + 44, + 58, + 61, 37, - 4, - 60, - 16, - 59 + 38 ], [ - 38, - 3, + 23, + 18, + 17, + 57, + 13, + 40 + ], + [ + 13, + 9, + 19, + 37, + 50, + 15 + ], + [ + 32, + 14, + 57, + 58, 29, - 40, - 25, - 50 + 22 ], [ - 31, + 61, + 10, + 1, + 3, + 14, + 59 + ], + [ + 55, 16, - 62, - 54, - 42, - 63 + 34, + 18, + 22, + 49 + ], + [ + 53, + 27, + 38, + 28, + 23, + 44 ], [ + 24, 47, - 35, - 11, - 37, - 46, - 32 + 18, + 62, + 41, + 30 ], [ + 33, 51, - 27, - 20, - 50, - 16, - 55 + 19, + 5, + 0, + 31 ], [ - 63, - 55, - 46, + 51, + 8, + 25, + 53, 27, - 48, - 12 + 16 ], [ - 53, - 50, - 30, - 2, - 39, - 20 + 40, + 11, + 27, + 4, + 23, + 19 ], [ - 53, - 24, - 44, - 8, + 16, + 18, 51, - 14 + 48, + 3, + 47 ], [ - 19, - 49, - 37, - 14, - 44, - 0 + 46, + 43, + 36, + 9, + 5, + 12 ] ], [ [ 0, - 52, - 16, - 12, - 54, - 7 + 10, + 49, + 23, + 62, + 44 ], [ - 42, - 25, - 51, - 61, - 35, - 58 + 28, + 0, + 36, + 26, + 47, + 52 ], [ - 51, - 42, - 19, - 57, - 28, - 8 + 30, + 4, + 16, + 48, + 40, + 10 ], [ - 49, - 62, - 5, - 2, - 46, - 21 + 61, + 32, + 26, + 16, + 33, + 62 ], [ - 3, - 41, - 53, - 25, + 30, 39, - 37 + 53, + 5, + 57, + 20 ], [ - 45, - 15, + 5, 37, - 48, - 19, - 60 + 61, + 15, + 25, + 6 ], [ - 14, 15, - 47, 17, 24, - 35 + 60, + 49, + 62 + ], + [ + 34, + 39, + 61, + 0, + 58, + 40 ], [ - 3, - 52, - 63, 16, - 28, - 47 + 39, + 36, + 51, + 2, + 29 ], [ - 32, - 27, - 15, + 9, + 11, + 41, + 31, + 56, + 52 + ], + [ + 10, + 48, 24, + 45, 62, - 23 + 51 ], [ - 62, - 25, - 51, - 53, - 0, - 20 + 11, + 38, + 36, + 37, + 6, + 42 ], [ - 8, - 19, + 51, 50, - 16, - 32, - 22 - ], - [ - 13, - 36, - 42, - 49, - 60, - 44 - ], - [ - 30, - 27, - 53, - 56, - 0, - 10 - ], - [ - 22, - 7, - 14, - 26, - 32, - 17 - ], - [ - 60, - 7, - 26, - 2, - 58, - 13 - ], - [ - 55, - 51, - 17, - 60, - 62, - 47 - ], - [ - 53, - 16, - 18, - 4, - 50, - 5 - ], - [ - 28, - 39, - 41, - 1, - 55, - 18 - ], - [ - 59, - 49, - 16, - 23, - 42, - 11 - ], - [ - 17, - 30, - 46, - 55, - 25, - 0 - ], - [ - 54, - 9, - 45, - 0, - 6, - 19 - ], - [ - 51, - 9, - 22, - 23, - 16, - 25 - ], - [ - 62, - 50, - 43, - 51, - 55, - 27 - ], - [ - 13, - 43, - 27, - 1, - 14, - 52 - ], - [ - 61, - 26, - 1, - 17, - 32, - 63 - ], - [ - 37, - 46, - 63, - 20, - 24, - 4 - ], - [ - 63, - 11, - 12, - 61, - 31, - 22 - ] - ], - [ - [ - 49, - 54, - 56, - 11, - 38, - 40 - ], - [ - 53, - 46, - 49, - 38, - 57, - 17 - ], - [ - 48, - 9, - 31, - 12, - 6, - 56 - ], - [ - 49, - 62, - 23, - 5, - 12, - 63 - ], - [ - 36, - 26, - 38, - 7, - 20, - 23 - ], - [ - 37, - 33, - 41, - 58, - 57, - 32 - ], - [ - 10, - 14, - 15, - 31, - 8, - 43 - ], - [ - 16, - 52, - 3, - 2, - 34, - 14 - ], - [ - 15, - 32, - 35, - 27, - 62, - 54 - ], - [ - 51, - 62, - 25, - 53, - 10, - 20 - ], - [ - 19, - 8, - 22, - 50, - 1, - 5 - ], - [ - 13, - 49, - 36, - 60, - 42, - 20 - ], - [ - 27, - 30, - 53, - 54, - 26, - 43 - ], - [ - 14, - 32, - 26, - 22, - 7, - 17 - ], - [ - 26, - 60, - 7, - 2, - 52, - 54 - ], - [ - 55, - 17, - 51, - 47, - 62, - 26 - ], - [ - 16, - 18, - 44, - 4, - 53, - 50 - ], - [ - 1, - 47, - 39, - 45, - 28, - 56 - ], - [ - 23, - 16, - 55, - 49, - 8, - 32 - ], - [ - 17, - 55, - 30, - 62, - 31, - 23 - ], - [ - 9, - 54, - 0, - 44, - 14, - 56 - ], - [ - 23, - 9, - 51, - 22, - 31, - 50 - ], - [ - 50, - 27, - 14, - 51, - 18, - 8 - ], - [ - 12, - 13, - 33, - 1, - 5, - 43 - ], - [ - 26, - 32, - 1, - 50, - 37, - 57 - ], - [ - 37, - 47, - 63, - 46, - 5, - 4 - ], - [ - 63, - 11, - 12, - 19, - 33, - 61 - ] - ], - [ - [ - 49, - 54, - 40, - 56, - 11, - 3 - ], - [ - 37, - 15, - 12, - 33, - 59, - 17 - ], - [ - 38, - 49, - 14, - 46, - 35, - 59 - ], - [ - 25, - 20, - 39, - 62, - 49, - 32 - ], - [ - 26, - 51, - 16, - 36, - 8, - 7 - ], - [ - 37, - 41, - 51, - 33, - 32, - 60 - ], - [ - 10, - 14, - 59, - 8, - 15, - 40 - ], - [ - 16, - 52, - 19, - 61, - 32, - 3 - ], - [ - 32, - 15, - 27, - 24, - 62, - 35 - ], - [ - 51, - 25, - 62, - 0, - 49, - 54 - ], - [ - 8, - 50, - 19, - 48, - 16, - 54 - ], - [ - 13, - 49, - 36, - 42, - 11, - 60 - ], - [ - 53, - 27, - 7, - 26, - 30, - 21 - ], - [ - 14, - 22, - 26, - 37, - 32, - 7 - ], - [ - 26, - 40, - 60, - 2, - 52, - 7 - ], - [ - 55, - 51, - 17, - 46, - 13, - 62 - ], - [ - 38, - 16, - 53, - 44, - 4, - 18 - ], - [ - 39, - 1, - 4, - 14, - 56, - 57 - ], - [ - 55, - 23, - 32, - 14, - 13, - 16 - ], - [ - 55, - 17, - 2, - 30, - 62, - 12 - ], - [ - 13, - 54, - 0, - 62, - 61, - 25 - ], - [ - 9, - 51, - 5, - 22, - 19, - 23 - ], - [ - 50, - 51, - 7, - 19, - 48, - 53 - ], - [ - 12, - 1, - 5, - 43, - 61, - 13 - ], - [ - 26, - 32, - 30, - 37, - 34, - 57 - ], - [ - 37, - 63, - 46, - 4, - 47, - 8 - ], - [ - 63, - 12, - 11, - 31, - 33, - 61 - ] - ], - [ - [ - 47, - 34, - 30, - 25, - 31, - 3 - ], - [ - 15, - 24, - 46, - 21, - 8, - 6 - ], - [ - 34, - 21, - 18, - 62, - 28, - 55 - ], - [ - 35, - 32, - 20, - 39, - 59, - 54 - ], - [ - 26, - 27, - 15, - 48, - 60, - 47 - ], - [ - 37, - 8, - 50, - 18, - 54, - 61 - ], - [ - 35, - 31, - 8, - 24, - 14, - 15 - ], - [ - 16, - 52, - 34, - 29, - 48, - 36 - ], - [ - 11, - 32, - 62, - 27, - 46, - 26 - ], - [ - 61, - 62, - 25, - 56, - 46, - 53 - ], - [ - 56, - 50, - 63, - 3, - 45, - 28 - ], - [ - 11, - 36, - 5, - 60, - 35, - 50 - ], - [ - 21, - 26, - 41, - 51, - 46, - 53 - ], - [ - 14, - 22, - 33, - 19, - 41, - 16 - ], - [ - 2, - 52, - 34, - 60, - 21, - 49 - ], - [ - 59, - 55, - 29, - 8, - 61, - 22 - ], - [ - 51, - 44, - 2, - 59, - 47, - 53 - ], - [ - 39, - 25, - 18, - 12, - 51, - 56 - ], - [ - 34, - 53, - 32, - 12, - 9, - 38 - ], - [ - 30, - 53, - 56, - 7, - 40, - 62 - ], - [ - 40, - 49, - 28, - 14, - 23, - 55 - ], - [ - 15, - 48, - 40, - 47, - 9, - 1 - ], - [ - 50, - 41, - 25, - 53, - 18, - 0 - ], - [ - 22, - 1, - 59, - 3, - 55, - 8 - ], - [ - 1, - 53, - 32, - 26, - 47, - 3 - ], - [ - 4, - 33, - 28, - 37, - 55, - 54 - ], - [ - 30, - 22, - 57, - 12, - 33, - 63 - ] - ], - [ - [ - 16, - 11, - 0, - 31, - 46, - 22 - ], - [ - 49, - 13, - 5, - 11, - 31, - 14 - ], - [ - 36, - 13, - 56, - 27, - 46, - 3 - ], - [ - 24, - 44, - 62, - 29, - 15, - 13 - ], - [ - 17, - 2, - 50, - 8, - 45, - 1 - ], - [ - 8, - 7, - 49, - 0, - 62, - 13 - ], - [ - 35, - 61, - 58, - 23, - 36, - 0 - ], - [ - 16, - 48, - 42, - 4, - 32, - 29 - ], - [ - 63, - 18, - 32, - 45, - 4, - 34 - ], - [ - 57, - 62, - 54, - 27, - 25, - 53 - ], - [ - 1, - 59, - 60, - 29, - 22, - 14 - ], - [ - 31, - 36, - 11, - 14, - 20, - 10 - ], - [ - 34, - 2, - 19, - 14, - 8, - 37 - ], - [ - 57, - 22, - 40, - 14, - 62, - 48 - ], - [ - 44, - 60, - 7, - 14, - 45, - 2 - ], - [ - 8, - 55, - 6, - 25, - 50, - 59 - ], - [ - 13, - 47, - 42, - 23, - 61, - 39 - ], - [ - 23, - 25, - 4, - 14, - 46, - 60 - ], - [ - 8, - 45, - 32, - 53, - 10, - 54 - ], - [ - 15, - 38, - 53, - 55, - 30, - 7 - ], - [ - 41, - 14, - 28, - 5, - 58, - 27 - ], - [ - 11, - 41, - 57, - 1, - 10, - 47 - ], - [ - 50, - 0, - 51, - 53, - 34, - 45 - ], - [ - 1, - 14, - 55, - 8, - 25, - 3 - ], - [ - 11, - 49, - 1, - 9, - 0, - 3 - ], - [ - 50, - 51, - 6, - 42, - 4, - 54 - ], - [ - 17, - 37, - 31, - 5, - 40, - 36 - ] - ], - [ - [ - 22, - 53, - 47, - 6, - 57, - 21 - ], - [ - 27, - 11, - 14, - 6, - 57, - 16 - ], - [ - 1, - 11, - 29, - 26, - 41, - 17 - ], - [ - 14, - 22, - 38, - 31, - 29, - 36 - ], - [ - 14, - 59, - 29, - 61, - 45, - 52 - ], - [ - 30, - 8, - 0, - 21, - 47, - 58 - ], - [ - 58, - 35, - 4, - 61, - 23, - 36 - ], - [ - 42, - 20, - 48, - 16, - 9, - 4 - ], - [ - 47, - 29, - 4, - 18, - 63, - 32 - ], - [ - 19, - 54, - 62, - 53, - 57, - 29 - ], - [ - 1, - 60, - 14, - 59, - 4, - 29 - ], - [ - 36, - 0, - 47, - 3, - 31, - 8 - ], - [ - 2, - 19, - 36, - 8, - 20, - 37 - ], - [ - 57, - 22, - 40, - 31, - 49, - 14 - ], - [ - 44, - 37, - 2, - 5, - 60, - 21 - ], - [ - 6, - 43, - 24, - 5, - 2, - 59 - ], - [ - 13, - 19, - 61, - 47, - 50, - 39 - ], - [ - 58, - 14, - 28, - 4, - 11, - 22 - ], - [ - 35, - 32, - 46, - 10, - 31, - 45 - ], - [ - 15, - 13, - 55, - 45, - 18, - 63 - ], - [ - 15, - 27, - 28, - 14, - 5, - 60 - ], - [ - 57, - 41, - 47, - 19, - 36, - 10 - ], - [ - 34, - 10, - 53, - 55, - 22, - 19 - ], - [ - 38, - 55, - 39, - 27, - 3, - 25 - ], - [ - 11, - 39, - 0, - 9, - 3, - 49 - ], - [ - 51, - 6, - 43, - 18, - 50, - 53 - ], - [ - 55, - 43, - 9, - 36, - 40, - 5 - ] - ], - [ - [ - 18, - 9, - 1, - 36, - 61, - 44 - ], - [ - 56, - 34, - 19, - 42, - 3, - 5 - ], - [ - 39, - 20, - 15, - 60, - 46, - 32 - ], - [ - 60, - 22, - 31, - 27, - 14, - 19 - ], - [ - 59, - 58, - 10, - 7, - 46, - 18 - ], - [ - 43, - 2, - 57, - 62, - 11, - 30 - ], - [ - 54, - 19, - 9, - 21, - 48, - 56 - ], - [ - 46, - 24, - 7, - 14, - 3, - 8 - ], - [ - 47, - 0, - 4, - 18, - 31, - 29 - ], - [ - 54, - 62, - 47, - 38, - 4, - 32 - ], - [ - 1, - 14, - 15, - 22, - 59, - 38 - ], - [ - 16, - 36, - 42, - 55, - 15, - 18 - ], - [ - 49, - 8, - 20, - 14, - 0, - 33 - ], - [ - 18, - 39, - 25, - 2, - 62, - 22 - ], - [ - 62, - 5, - 58, - 37, - 7, - 32 - ], - [ - 43, - 5, - 42, - 63, - 55, - 37 - ], - [ - 47, - 33, - 15, - 63, - 50, - 12 - ], - [ - 60, - 0, - 7, - 16, - 32, - 13 - ], - [ - 12, - 39, - 32, - 61, - 16, - 45 - ], - [ - 52, - 34, - 15, - 62, - 18, - 30 - ], - [ - 28, - 26, - 46, - 40, - 6, - 14 - ], - [ - 1, - 19, - 17, - 20, - 4, - 21 - ], - [ - 41, - 40, - 4, - 53, - 55, - 19 - ], - [ - 25, - 38, - 27, - 34, - 52, - 46 - ], - [ - 11, - 29, - 52, - 44, - 53, - 13 - ], - [ - 50, - 51, - 41, - 16, - 4, - 15 - ], - [ - 19, - 6, - 23, - 36, - 60, - 0 - ] - ], - [ - [ - 17, - 10, - 57, - 27, - 5, - 54 - ], - [ - 33, - 9, - 43, - 40, - 56, - 11 - ], - [ - 63, - 1, - 35, - 43, - 10, - 27 - ], - [ - 51, - 47, - 20, - 21, - 28, - 61 - ], - [ - 25, - 11, - 58, - 23, - 55, - 46 - ], - [ - 43, - 10, - 12, - 2, - 62, - 30 - ], - [ - 48, - 19, - 21, - 8, - 7, - 54 - ], - [ - 14, - 7, - 24, - 8, - 46, - 2 - ], - [ - 4, - 47, - 37, - 0, - 44, - 27 - ], - [ - 54, - 38, - 62, - 47, - 15, - 14 - ], - [ - 1, - 46, - 15, - 22, - 51, - 38 - ], - [ - 36, - 16, - 42, - 55, - 24, - 37 - ], - [ - 49, - 10, - 0, - 3, - 43, - 8 - ], - [ - 39, - 58, - 0, - 62, - 22, - 25 - ], - [ - 58, - 38, - 7, - 55, - 62, - 56 - ], - [ - 19, - 42, - 55, - 43, - 11, - 37 - ], - [ - 9, - 47, - 43, - 52, - 18, - 50 - ], - [ - 31, - 41, - 32, - 25, - 20, - 13 - ], - [ - 12, - 32, - 61, - 3, - 21, - 43 - ], - [ - 36, - 13, - 40, - 7, - 62, - 16 - ], - [ - 14, - 53, - 50, - 47, - 51, - 1 - ], - [ - 1, - 38, - 19, - 18, - 30, - 16 - ], - [ - 0, - 19, - 51, - 18, - 52, - 15 - ], - [ - 8, - 52, - 27, - 34, - 38, - 3 - ], - [ - 27, - 53, - 59, - 9, - 40, - 4 - ], - [ - 37, - 3, - 26, - 48, - 8, - 16 - ], - [ - 46, - 18, - 11, - 40, - 33, - 44 - ] - ], - [ - [ - 48, - 62, - 61, - 50, - 26, - 59 - ], - [ - 3, - 45, - 40, - 35, - 29, - 54 - ], - [ - 56, - 31, - 23, - 28, - 2, - 53 - ], - [ - 62, - 49, - 20, - 61, - 6, - 41 - ], - [ - 18, - 25, - 50, - 0, - 14, - 57 - ], - [ - 58, - 4, - 10, - 43, - 56, - 20 - ], - [ - 35, - 15, - 25, - 24, - 3, - 7 - ], - [ - 14, - 23, - 8, - 12, - 57, - 24 - ], - [ - 29, - 17, - 35, - 44, - 24, - 27 - ], - [ - 62, - 15, - 38, - 20, - 58, - 21 - ], - [ - 19, - 46, - 1, - 26, - 63, - 22 - ], - [ - 36, - 60, - 16, - 42, - 55, - 11 - ], - [ - 17, - 7, - 14, - 26, - 16, - 49 - ], - [ - 45, - 47, - 22, - 0, - 62, - 58 - ], - [ - 58, - 38, - 48, - 49, - 63, - 2 - ], - [ - 55, - 0, - 1, - 37, - 30, - 10 - ], - [ - 12, - 43, - 21, - 9, - 47, - 23 - ], - [ - 32, - 57, - 42, - 25, - 43, - 63 - ], - [ - 3, - 32, - 49, - 61, - 21, - 12 - ], - [ - 5, - 36, - 22, - 16, - 62, - 42 - ], - [ - 53, - 7, - 46, - 61, - 14, - 52 - ], - [ - 55, - 30, - 3, - 5, - 53, - 31 - ], - [ - 0, - 44, - 15, - 18, - 19, - 28 - ], - [ - 8, - 52, - 51, - 11, - 4, - 29 - ], - [ - 27, - 40, - 4, - 9, - 35, - 39 - ], - [ - 14, - 26, - 3, - 48, - 16, - 21 - ], - [ - 60, - 54, - 35, - 20, - 53, - 12 - ] - ], - [ - [ - 21, - 50, - 29, - 41, - 34, - 60 - ], - [ - 28, - 51, - 60, - 33, - 14, - 45 - ], - [ - 31, - 2, - 46, - 33, - 24, - 49 - ], - [ - 42, - 3, - 18, - 62, - 39, - 49 - ], - [ - 25, - 15, - 62, - 27, - 12, - 11 - ], - [ - 44, - 50, - 36, - 57, - 55, - 41 - ], - [ - 41, - 37, - 22, - 15, - 2, - 40 - ], - [ - 36, - 62, - 53, - 30, - 14, - 57 - ], - [ - 16, - 58, - 2, - 29, - 4, - 3 - ], - [ - 41, - 38, - 26, - 16, - 45, - 46 - ], - [ - 45, - 46, - 32, - 41, - 56, - 26 - ], - [ - 17, - 53, - 21, - 11, - 36, - 35 - ], - [ - 11, - 16, - 28, - 14, - 51, - 61 - ], - [ - 9, - 35, - 33, - 22, - 52, - 62 - ], - [ - 58, - 50, - 63, - 30, - 7, - 27 - ], - [ - 55, - 3, - 8, - 41, - 63, - 37 - ], - [ - 3, - 51, - 46, - 32, - 15, - 6 - ], - [ - 32, - 12, - 10, - 25, - 5, - 49 - ], - [ - 34, - 2, - 37, - 61, - 39, - 63 - ], - [ - 42, - 22, - 27, - 53, - 11, - 56 - ], - [ - 53, - 12, - 0, - 47, - 61, - 1 - ], - [ - 39, - 45, - 53, - 17, - 48, - 14 - ], - [ - 6, - 0, - 4, - 53, - 25, - 11 - ], - [ - 51, - 11, - 1, - 63, - 54, - 45 - ], - [ - 40, - 56, - 37, - 53, - 5, - 35 - ], - [ - 59, - 28, - 41, - 10, - 1, - 45 - ], - [ - 27, - 30, - 28, - 24, - 32, - 57 - ] - ], - [ - [ - 24, - 56, - 6, - 0, - 19, - 45 - ], - [ - 11, - 57, - 59, - 25, - 46, - 30 - ], - [ - 11, - 26, - 37, - 29, - 14, - 52 - ], - [ - 3, - 32, - 7, - 38, - 36, - 24 - ], - [ - 61, - 2, - 24, - 14, - 51, - 44 - ], - [ - 20, - 47, - 0, - 63, - 30, - 58 - ], - [ - 4, - 36, - 29, - 58, - 16, - 3 - ], - [ - 20, - 0, - 45, - 14, - 28, - 44 - ], - [ - 29, - 56, - 47, - 35, - 16, - 4 - ], - [ - 33, - 61, - 55, - 41, - 51, - 38 - ], - [ - 58, - 1, - 38, - 14, - 4, - 19 - ], - [ - 0, - 36, - 14, - 18, - 52, - 42 - ], - [ - 29, - 36, - 45, - 25, - 8, - 6 - ], - [ - 6, - 57, - 50, - 40, - 58, - 61 - ], - [ - 44, - 58, - 29, - 19, - 61, - 56 - ], - [ - 23, - 18, - 28, - 55, - 5, - 37 - ], - [ - 13, - 9, - 19, - 43, - 37, - 3 - ], - [ - 32, - 22, - 63, - 14, - 57, - 41 - ], - [ - 10, - 61, - 3, - 1, - 19, - 32 - ], - [ - 16, - 55, - 10, - 41, - 59, - 22 - ], - [ - 53, - 7, - 29, - 38, - 27, - 46 - ], - [ - 24, - 47, - 18, - 53, - 39, - 30 - ], - [ - 0, - 33, - 19, - 5, - 51, - 17 - ], - [ - 51, - 8, - 11, - 45, - 44, - 41 - ], - [ - 40, - 4, - 23, - 11, - 27, - 19 - ], - [ - 16, - 18, - 3, - 48, - 51, - 21 - ], - [ - 43, - 46, - 60, - 19, - 53, - 12 - ] - ], - [ - [ - 48, - 62, - 61, - 30, - 50, - 52 - ], - [ - 45, - 3, - 35, - 29, - 54, - 2 - ], - [ - 56, - 31, - 53, - 23, - 49, - 28 - ], - [ - 60, - 57, - 14, - 46, - 41, - 48 - ], - [ - 18, - 61, - 59, - 14, - 44, - 32 - ], - [ - 45, - 58, - 47, - 20, - 4, - 30 - ], - [ - 54, - 13, - 25, - 36, - 26, - 47 - ], - [ - 20, - 12, - 0, - 47, - 30, - 45 - ], - [ - 56, - 29, - 47, - 17, - 35, - 16 - ], - [ - 33, - 61, - 55, - 11, - 38, - 48 - ], - [ - 58, - 19, - 14, - 1, - 38, - 36 - ], - [ - 14, - 36, - 0, - 60, - 11, - 52 - ], - [ - 29, - 44, - 7, - 36, - 16, - 45 - ], - [ - 6, - 47, - 50, - 33, - 42, - 62 - ], - [ - 44, - 58, - 61, - 38, - 29, - 56 - ], - [ - 23, - 55, - 18, - 0, - 57, - 37 - ], - [ - 9, - 12, - 43, - 19, - 13, - 6 - ], - [ - 32, - 22, - 63, - 57, - 42, - 29 - ], - [ - 3, - 61, - 1, - 10, - 49, - 32 - ], - [ - 5, - 16, - 55, - 36, - 22, - 59 - ], - [ - 53, - 7, - 46, - 29, - 9, - 14 - ], - [ - 24, - 30, - 18, - 39, - 55, - 53 - ], - [ - 33, - 0, - 19, - 44, - 51, - 5 - ], - [ - 51, - 8, - 53, - 41, - 4, - 11 - ], - [ - 40, - 4, - 27, - 19, - 23, - 16 - ], - [ - 16, - 14, - 48, - 3, - 21, - 26 - ], - [ - 60, - 54, - 35, - 53, - 12, - 43 - ] - ], - [ - [ - 19, - 41, - 8, - 7, - 13, - 2 - ], - [ - 48, - 46, - 62, - 29, - 5, - 41 - ], - [ - 12, - 5, - 59, - 3, - 58, - 49 - ], - [ - 60, - 3, - 42, - 39, - 14, - 18 - ], - [ - 42, - 12, - 27, - 11, - 25, - 19 - ], - [ - 50, - 36, - 44, - 26, - 33, - 37 - ], - [ - 41, - 54, - 22, - 52, - 37, - 35 - ], - [ - 62, - 30, - 36, - 53, - 10, - 14 - ], - [ - 2, - 16, - 58, - 29, - 7, - 41 - ], - [ - 18, - 32, - 45, - 16, - 22, - 38 - ], - [ - 45, - 56, - 41, - 10, - 3, - 46 - ], - [ - 50, - 21, - 36, - 35, - 53, - 12 - ], - [ - 11, - 28, - 16, - 41, - 39, - 46 - ], - [ - 16, - 9, - 33, - 38, - 28, - 19 - ], - [ - 58, - 50, - 63, - 62, - 27, - 52 - ], - [ - 55, - 59, - 13, - 8, - 43, - 3 - ], - [ - 3, - 51, - 15, - 46, - 47, - 57 - ], - [ - 32, - 30, - 12, - 10, - 25, - 18 - ], - [ - 34, - 2, - 61, - 27, - 53, - 59 - ], - [ - 42, - 22, - 56, - 53, - 44, - 34 - ], - [ - 53, - 12, - 49, - 41, - 44, - 8 - ], - [ - 45, - 1, - 48, - 47, - 16, - 17 - ], - [ - 4, - 0, - 53, - 25, - 24, - 11 - ], - [ - 1, - 11, - 44, - 45, - 34, - 51 - ], - [ - 40, - 5, - 53, - 6, - 22, - 18 - ], - [ - 28, - 10, - 1, - 3, - 15, - 41 - ], - [ - 30, - 27, - 24, - 57, - 32, - 16 - ] - ], - [ - [ - 24, - 56, - 6, - 0, - 19, - 45 - ], - [ - 11, - 57, - 59, - 46, - 25, - 30 - ], - [ - 26, - 11, - 37, - 14, - 29, - 49 - ], - [ - 38, - 36, - 3, - 24, - 18, - 20 - ], - [ - 61, - 51, - 14, - 2, - 24, - 1 - ], - [ - 20, - 0, - 47, - 30, - 8, - 35 - ], - [ - 4, - 58, - 36, - 54, - 29, - 12 - ], - [ - 20, - 58, - 44, - 28, - 45, - 9 - ], - [ - 56, - 47, - 10, - 29, - 35, - 27 - ], - [ - 61, - 33, - 55, - 54, - 4, - 36 - ], - [ - 58, - 1, - 14, - 4, - 38, - 52 - ], - [ - 14, - 0, - 36, - 63, - 15, - 52 - ], - [ - 29, - 36, - 44, - 8, - 16, - 2 - ], - [ - 6, - 40, - 27, - 57, - 50, - 42 - ], - [ - 19, - 44, - 58, - 61, - 37, - 38 - ], - [ - 23, - 18, - 17, - 57, - 13, - 40 - ], - [ - 13, - 9, - 19, - 37, - 50, - 15 - ], - [ - 32, - 14, - 57, - 58, - 29, - 22 - ], - [ - 61, - 10, - 1, - 3, - 14, - 59 - ], - [ - 55, - 16, - 34, - 18, - 22, - 49 - ], - [ - 53, - 27, - 38, - 28, - 23, - 44 - ], - [ - 24, - 47, - 18, - 62, - 41, - 30 - ], - [ - 33, - 51, - 19, - 5, - 0, - 31 - ], - [ - 51, - 8, - 25, - 53, - 27, - 16 - ], - [ - 40, - 11, - 27, - 4, - 23, - 19 - ], - [ - 16, - 18, - 51, - 48, - 3, - 47 - ], - [ - 46, - 43, - 36, - 9, - 5, - 12 - ] - ], - [ - [ - 37, - 10, - 46, - 60, - 61, - 59 - ], - [ - 35, - 53, - 34, - 43, - 19, - 57 - ], - [ - 49, - 56, - 45, - 30, - 6, - 12 - ], - [ - 60, - 27, - 14, - 48, - 46, - 57 - ], - [ - 61, - 59, - 14, - 41, - 16, - 1 - ], - [ - 45, - 4, - 3, - 58, - 24, - 47 - ], - [ - 54, - 13, - 9, - 43, - 16, - 26 - ], - [ - 47, - 23, - 12, - 20, - 63, - 30 - ], - [ - 23, - 44, - 56, - 29, - 47, - 17 - ], - [ - 33, - 60, - 61, - 48, - 41, - 14 - ], - [ - 58, - 63, - 19, - 11, - 9, - 38 - ], - [ - 60, - 63, - 0, - 36, - 15, - 9 - ], - [ - 29, - 36, - 30, - 59, - 11, - 27 - ], - [ - 6, - 7, - 47, - 62, - 50, - 57 - ], - [ - 27, - 58, - 19, - 46, - 29, - 56 - ], - [ - 29, - 60, - 56, - 55, - 23, - 26 - ], - [ - 53, - 59, - 6, - 9, - 16, - 43 - ], - [ - 41, - 32, - 57, - 63, - 18, - 37 - ], - [ - 42, - 61, - 3, - 10, - 34, - 59 - ], - [ - 4, - 43, - 17, - 16, - 52, - 60 - ], - [ - 45, - 53, - 61, - 56, - 16, - 7 - ], - [ - 55, - 9, - 18, - 61, - 45, - 3 - ], - [ - 60, - 47, - 53, - 33, - 12, - 27 - ], - [ - 43, - 51, - 11, - 8, - 45, - 63 - ], - [ - 6, - 40, - 15, - 27, - 26, - 23 - ], - [ - 49, - 14, - 9, - 21, - 58, - 12 - ], - [ - 63, - 24, - 60, - 31, - 12, - 34 - ] - ], - [ - [ - 16, - 13, - 4, - 44, - 23, - 46 - ], - [ - 16, - 50, - 9, - 13, - 23, - 36 - ], - [ - 11, - 35, - 21, - 7, - 59, - 8 - ], - [ - 1, - 3, - 25, - 15, - 60, - 39 - ], - [ - 54, - 61, - 31, - 35, - 55, - 1 - ], - [ - 51, - 52, - 46, - 15, - 4, - 45 - ], - [ - 60, - 54, - 59, - 44, - 10, - 7 - ], - [ - 12, - 22, - 14, - 47, - 0, - 30 - ], - [ - 42, - 29, - 23, - 56, - 47, - 33 - ], - [ - 33, - 61, - 20, - 60, - 0, - 49 - ], - [ - 35, - 58, - 63, - 14, - 51, - 24 - ], - [ - 29, - 33, - 36, - 60, - 0, - 49 - ], - [ - 29, - 17, - 30, - 12, - 31, - 36 - ], - [ - 6, - 0, - 61, - 50, - 48, - 3 - ], - [ - 8, - 6, - 58, - 37, - 29, - 19 - ], - [ - 60, - 39, - 27, - 19, - 1, - 57 - ], - [ - 56, - 9, - 30, - 6, - 10, - 43 - ], - [ - 32, - 20, - 13, - 57, - 63, - 49 - ], - [ - 8, - 42, - 21, - 61, - 37, - 4 - ], - [ - 45, - 49, - 16, - 13, - 2, - 58 - ], - [ - 53, - 29, - 14, - 50, - 61, - 3 - ], - [ - 37, - 57, - 27, - 54, - 46, - 9 - ], - [ - 52, - 19, - 22, - 0, - 18, - 5 - ], - [ - 14, - 49, - 30, - 33, - 53, - 34 - ], - [ - 13, - 9, - 4, - 40, - 23, - 39 - ], - [ - 27, - 43, - 47, - 36, - 49, - 3 - ], - [ - 59, - 43, - 40, - 28, - 0, - 33 - ] - ], - [ - [ - 48, - 42, - 63, - 50, - 34, - 38 - ], - [ - 3, - 40, - 61, - 62, - 6, - 2 - ], - [ - 39, - 7, - 36, - 6, - 45, - 40 - ], - [ - 41, - 35, - 46, - 13, - 63, - 56 - ], - [ - 6, - 1, - 54, - 37, - 38, - 34 - ], - [ - 59, - 46, - 51, - 31, - 4, - 52 - ], - [ - 60, - 44, - 11, - 54, - 4, - 24 - ], - [ - 12, - 0, - 2, - 63, - 50, - 47 - ], - [ - 33, - 42, - 29, - 23, - 16, - 56 - ], - [ - 20, - 61, - 33, - 60, - 53, - 0 - ], - [ - 35, - 58, - 63, - 9, - 8, - 19 - ], - [ - 59, - 48, - 36, - 60, - 10, - 14 - ], - [ - 29, - 44, - 7, - 17, - 36, - 12 - ], - [ - 47, - 27, - 6, - 62, - 42, - 48 - ], - [ - 18, - 58, - 49, - 46, - 42, - 44 - ], - [ - 60, - 34, - 27, - 18, - 23, - 55 - ], - [ - 43, - 40, - 9, - 50, - 18, - 45 - ], - [ - 32, - 57, - 48, - 42, - 29, - 39 - ], - [ - 42, - 61, - 49, - 3, - 32, - 1 - ], - [ - 23, - 37, - 1, - 16, - 36, - 39 - ], - [ - 53, - 21, - 7, - 61, - 50, - 31 - ], - [ - 8, - 60, - 18, - 24, - 9, - 30 - ], - [ - 51, - 33, - 28, - 5, - 44, - 8 - ], - [ - 51, - 52, - 8, - 4, - 41, - 45 - ], - [ - 40, - 4, - 27, - 9, - 60, - 19 - ], - [ - 3, - 61, - 16, - 26, - 48, - 12 - ], - [ - 54, - 61, - 35, - 1, - 53, - 43 - ] - ], - [ - [ - 62, - 28, - 1, - 42, - 8, - 55 - ], - [ - 18, - 12, - 8, - 41, - 40, - 31 - ], - [ - 12, - 6, - 50, - 4, - 23, - 45 - ], - [ - 43, - 35, - 8, - 20, - 42, - 46 - ], - [ - 39, - 41, - 29, - 22, - 3, - 56 - ], - [ - 61, - 45, - 46, - 48, - 28, - 51 - ], - [ - 44, - 4, - 11, - 25, - 54, - 59 - ], - [ - 12, - 33, - 56, - 52, - 30, - 17 - ], - [ - 55, - 29, - 17, - 42, - 23, - 14 - ], - [ - 60, - 12, - 18, - 61, - 33, - 28 - ], - [ - 35, - 58, - 37, - 63, - 6, - 27 - ], - [ - 48, - 59, - 10, - 36, - 58, - 60 - ], - [ - 17, - 7, - 28, - 31, - 29, - 27 - ], - [ - 47, - 42, - 50, - 6, - 8, - 14 - ], - [ - 39, - 58, - 56, - 37, - 18, - 59 - ], - [ - 60, - 18, - 57, - 9, - 55, - 23 - ], - [ - 43, - 63, - 18, - 60, - 19, - 22 - ], - [ - 1, - 32, - 42, - 57, - 35, - 63 - ], - [ - 42, - 61, - 3, - 32, - 1, - 50 - ], - [ - 37, - 36, - 10, - 23, - 16, - 57 - ], - [ - 53, - 61, - 7, - 57, - 21, - 23 - ], - [ - 9, - 39, - 30, - 18, - 14, - 17 - ], - [ - 33, - 44, - 8, - 5, - 0, - 19 - ], - [ - 51, - 53, - 49, - 4, - 52, - 41 - ], - [ - 40, - 4, - 27, - 6, - 9, - 16 - ], - [ - 3, - 16, - 48, - 26, - 12, - 4 - ], - [ - 61, - 14, - 12, - 54, - 35, - 53 - ] - ], - [ - [ - 47, - 23, - 63, - 11, - 61, - 55 - ], - [ - 17, - 44, - 28, - 39, - 47, - 27 - ], - [ - 34, - 53, - 50, - 38, - 29, - 5 - ], - [ - 11, - 10, - 17, - 52, - 47, - 42 - ], - [ - 15, - 41, - 27, - 20, - 12, - 6 - ], - [ - 34, - 44, - 50, - 39, - 36, - 61 - ], - [ - 37, - 41, - 52, - 29, - 46, - 47 - ], - [ - 62, - 36, - 34, - 30, - 39, - 22 - ], - [ - 62, - 16, - 58, - 5, - 2, - 8 - ], - [ - 32, - 41, - 56, - 12, - 46, - 8 - ], - [ - 10, - 35, - 45, - 41, - 3, - 56 - ], - [ - 50, - 48, - 35, - 53, - 36, - 12 - ], - [ - 39, - 11, - 46, - 7, - 23, - 51 - ], - [ - 9, - 47, - 19, - 22, - 52, - 34 - ], - [ - 35, - 18, - 56, - 50, - 3, - 23 - ], - [ - 3, - 60, - 38, - 36, - 9, - 35 - ], - [ - 46, - 28, - 32, - 5, - 43, - 56 - ], - [ - 30, - 32, - 57, - 42, - 52, - 19 - ], - [ - 2, - 32, - 34, - 61, - 14, - 42 - ], - [ - 42, - 37, - 20, - 50, - 9, - 48 - ], - [ - 53, - 7, - 56, - 25, - 60, - 13 - ], - [ - 17, - 39, - 14, - 53, - 30, - 25 - ], - [ - 5, - 40, - 6, - 33, - 29, - 25 - ], - [ - 51, - 4, - 11, - 58, - 57, - 28 - ], - [ - 40, - 37, - 4, - 44, - 8, - 48 - ], - [ - 3, - 48, - 26, - 9, - 12, - 41 - ], - [ - 47, - 61, - 26, - 24, - 20, - 53 - ] - ], - [ - [ - 31, - 43, - 41, - 47, - 11, - 25 - ], - [ - 50, - 25, - 31, - 40, - 24, - 46 - ], - [ - 23, - 9, - 62, - 15, - 20, - 53 - ], - [ - 4, - 47, - 44, - 58, - 48, - 25 - ], - [ - 2, - 19, - 12, - 52, - 0, - 40 - ], - [ - 49, - 15, - 24, - 34, - 60, - 42 - ], - [ - 12, - 46, - 17, - 29, - 41, - 3 - ], - [ - 39, - 60, - 44, - 41, - 33, - 36 - ], - [ - 21, - 60, - 16, - 44, - 51, - 57 - ], - [ - 24, - 41, - 12, - 33, - 13, - 21 - ], - [ - 43, - 62, - 3, - 12, - 28, - 45 - ], - [ - 58, - 19, - 39, - 17, - 49, - 42 - ], - [ - 25, - 54, - 4, - 7, - 11, - 39 - ], - [ - 34, - 35, - 4, - 42, - 62, - 19 - ], - [ - 43, - 41, - 42, - 35, - 40, - 32 - ], - [ - 21, - 63, - 3, - 17, - 20, - 50 - ], - [ - 58, - 46, - 44, - 1, - 25, - 20 - ], - [ - 26, - 32, - 16, - 25, - 46, - 41 - ], - [ - 37, - 63, - 61, - 28, - 24, - 56 - ], - [ - 33, - 42, - 40, - 37, - 48, - 50 - ], - [ - 11, - 53, - 25, - 39, - 4, - 61 - ], - [ - 12, - 54, - 4, - 27, - 50, - 14 - ], - [ - 47, - 19, - 42, - 17, - 35, - 40 - ], - [ - 54, - 40, - 60, - 63, - 45, - 57 - ], - [ - 44, - 56, - 40, - 62, - 37, - 3 - ], - [ - 59, - 41, - 57, - 34, - 48, - 22 - ], - [ - 56, - 13, - 59, - 51, - 26, - 58 - ] - ], - [ - [ - 35, - 32, - 8, - 40, - 51, - 52 - ], - [ - 52, - 5, - 22, - 21, - 6, - 33 - ], - [ - 22, - 58, - 11, - 25, - 3, - 51 - ], - [ - 63, - 2, - 56, - 4, - 23, - 54 - ], - [ - 39, - 12, - 23, - 32, - 30, - 46 - ], - [ - 50, - 34, - 36, - 58, - 26, - 28 - ], - [ - 46, - 41, - 3, - 2, - 22, - 16 - ], - [ - 60, - 36, - 53, - 30, - 54, - 39 - ], - [ - 16, - 51, - 3, - 39, - 2, - 26 - ], - [ - 45, - 26, - 18, - 41, - 32, - 46 - ], - [ - 45, - 3, - 10, - 56, - 36, - 35 - ], - [ - 21, - 36, - 35, - 50, - 11, - 19 - ], - [ - 28, - 11, - 46, - 59, - 41, - 15 - ], - [ - 23, - 16, - 38, - 19, - 15, - 22 - ], - [ - 27, - 7, - 34, - 58, - 3, - 42 - ], - [ - 9, - 22, - 36, - 46, - 26, - 41 - ], - [ - 3, - 51, - 40, - 56, - 46, - 8 - ], - [ - 12, - 25, - 21, - 50, - 17, - 62 - ], - [ - 27, - 34, - 61, - 13, - 60, - 11 - ], - [ - 53, - 12, - 56, - 0, - 42, - 33 - ], - [ - 53, - 37, - 12, - 24, - 25, - 63 - ], - [ - 45, - 55, - 18, - 26, - 17, - 43 - ], - [ - 4, - 25, - 32, - 1, - 48, - 53 - ], - [ - 17, - 27, - 63, - 4, - 62, - 31 - ], - [ - 6, - 52, - 62, - 40, - 46, - 23 - ], - [ - 10, - 42, - 28, - 49, - 3, - 53 - ], - [ - 45, - 27, - 41, - 21, - 16, - 47 - ] - ], - [ - [ - 44, - 24, - 33, - 56, - 15, - 41 - ], - [ - 38, - 26, - 24, - 29, - 19, - 53 - ], - [ - 12, - 15, - 29, - 9, - 1, - 63 - ], - [ - 58, - 38, - 50, - 0, - 43, - 61 - ], - [ - 24, - 51, - 31, - 34, - 60, - 7 - ], - [ - 0, - 7, - 22, - 43, - 35, - 1 - ], - [ - 63, - 36, - 11, - 1, - 16, - 4 - ], - [ - 8, - 50, - 56, - 4, - 30, - 55 - ], - [ - 43, - 16, - 42, - 29, - 60, - 35 - ], - [ - 34, - 0, - 9, - 22, - 18, - 26 - ], - [ - 54, - 51, - 45, - 35, - 2, - 36 - ], - [ - 37, - 36, - 43, - 60, - 11, - 59 - ], - [ - 56, - 38, - 10, - 28, - 14, - 43 - ], - [ - 30, - 0, - 58, - 62, - 22, - 19 - ], - [ - 7, - 55, - 42, - 58, - 30, - 38 - ], - [ - 11, - 33, - 1, - 39, - 19, - 16 - ], - [ - 55, - 20, - 40, - 9, - 18, - 30 - ], - [ - 18, - 20, - 57, - 32, - 45, - 1 - ], - [ - 43, - 61, - 12, - 32, - 31, - 30 - ], - [ - 23, - 25, - 7, - 28, - 40, - 19 - ], - [ - 14, - 51, - 48, - 58, - 53, - 25 - ], - [ - 18, - 30, - 1, - 49, - 41, - 9 - ], - [ - 2, - 51, - 22, - 0, - 52, - 5 - ], - [ - 53, - 4, - 47, - 52, - 51, - 40 - ], - [ - 40, - 16, - 9, - 47, - 23, - 11 - ], - [ - 47, - 3, - 43, - 46, - 26, - 53 - ], - [ - 8, - 40, - 18, - 46, - 33, - 63 - ] - ], - [ - [ - 48, - 38, - 50, - 42, - 63, - 36 - ], - [ - 3, - 10, - 26, - 2, - 6, - 61 - ], - [ - 39, - 44, - 45, - 40, - 6, - 7 - ], - [ - 41, - 5, - 20, - 49, - 56, - 13 - ], - [ - 6, - 1, - 30, - 37, - 28, - 38 - ], - [ - 59, - 46, - 22, - 35, - 61, - 0 - ], - [ - 1, - 63, - 35, - 3, - 60, - 49 - ], - [ - 8, - 12, - 2, - 50, - 5, - 55 - ], - [ - 42, - 33, - 43, - 16, - 32, - 29 - ], - [ - 9, - 34, - 0, - 20, - 41, - 31 - ], - [ - 51, - 54, - 8, - 19, - 63, - 9 - ], - [ - 37, - 56, - 36, - 11, - 59, - 43 - ], - [ - 38, - 10, - 28, - 17, - 56, - 63 - ], - [ - 27, - 30, - 42, - 19, - 0, - 22 - ], - [ - 7, - 55, - 49, - 42, - 58, - 38 - ], - [ - 29, - 34, - 39, - 33, - 47, - 11 - ], - [ - 55, - 40, - 20, - 18, - 7, - 5 - ], - [ - 43, - 57, - 39, - 54, - 48, - 28 - ], - [ - 12, - 43, - 61, - 42, - 32, - 49 - ], - [ - 23, - 36, - 1, - 7, - 59, - 28 - ], - [ - 14, - 53, - 21, - 7, - 57, - 37 - ], - [ - 18, - 1, - 24, - 60, - 30, - 9 - ], - [ - 51, - 0, - 33, - 2, - 44, - 5 - ], - [ - 52, - 29, - 4, - 41, - 54, - 58 - ], - [ - 40, - 19, - 16, - 9, - 46, - 47 - ], - [ - 61, - 3, - 47, - 22, - 21, - 53 - ], - [ - 35, - 60, - 54, - 1, - 5, - 40 - ] - ], - [ - [ - 17, - 18, - 8, - 53, - 25, - 43 - ], - [ - 9, - 38, - 24, - 47, - 25, - 63 - ], - [ - 20, - 24, - 5, - 12, - 54, - 28 - ], - [ - 43, - 10, - 20, - 42, - 11, - 8 - ], - [ - 53, - 61, - 30, - 39, - 29, - 18 - ], - [ - 61, - 56, - 25, - 40, - 5, - 22 - ], - [ - 62, - 17, - 24, - 1, - 47, - 33 - ], - [ - 41, - 16, - 34, - 39, - 29, - 8 - ], - [ - 39, - 16, - 36, - 42, - 29, - 23 - ], - [ - 9, - 11, - 41, - 63, - 56, - 31 - ], - [ - 48, - 51, - 10, - 62, - 63, - 45 - ], - [ - 36, - 11, - 37, - 42, - 58, - 46 - ], - [ - 51, - 38, - 25, - 63, - 29, - 44 - ], - [ - 4, - 56, - 44, - 62, - 58, - 30 - ], - [ - 3, - 7, - 46, - 42, - 33, - 35 - ], - [ - 39, - 9, - 33, - 58, - 60, - 29 - ], - [ - 40, - 37, - 20, - 16, - 55, - 25 - ], - [ - 54, - 19, - 11, - 57, - 0, - 39 - ], - [ - 12, - 43, - 61, - 25, - 49, - 32 - ], - [ - 4, - 23, - 54, - 36, - 7, - 28 - ], - [ - 40, - 25, - 26, - 14, - 2, - 58 - ], - [ - 18, - 58, - 24, - 1, - 22, - 46 - ], - [ - 2, - 63, - 22, - 6, - 44, - 56 - ], - [ - 52, - 29, - 51, - 4, - 40, - 32 - ], - [ - 40, - 17, - 15, - 16, - 46, - 57 - ], - [ - 9, - 61, - 3, - 47, - 24, - 11 - ], - [ - 2, - 39, - 24, - 42, - 0, - 44 - ] - ], - [ - [ - 0, - 10, - 49, - 23, - 62, - 44 - ], - [ - 28, - 0, - 36, - 26, - 47, - 52 - ], - [ - 30, - 4, - 16, - 48, - 40, - 10 - ], - [ - 61, - 32, - 26, - 16, - 33, - 62 - ], - [ - 30, - 39, - 53, - 5, - 57, - 20 - ], - [ - 5, - 37, - 61, - 15, - 25, - 6 - ], - [ - 15, - 17, - 24, - 60, - 49, - 62 - ], - [ - 34, - 39, - 61, - 0, - 58, - 40 - ], - [ - 16, - 39, - 36, - 51, - 2, - 29 - ], - [ - 9, - 11, - 41, - 31, - 56, - 52 - ], - [ - 10, - 48, - 24, - 45, - 62, - 51 - ], - [ - 11, - 38, - 36, - 37, - 6, - 42 - ], - [ - 51, - 50, - 15, - 30, - 25, - 38 - ], - [ - 4, - 19, - 24, - 35, - 31, - 48 - ], - [ - 7, - 46, - 3, - 58, - 30, - 41 - ], - [ - 58, - 9, - 39, - 32, - 29, - 40 - ], - [ - 40, - 37, - 20, - 8, - 25, - 55 - ], - [ - 19, - 0, - 54, - 52, - 17, - 39 - ], - [ - 25, - 43, - 12, - 61, - 14, - 11 - ], - [ - 23, - 4, - 54, - 36, - 28, - 33 - ], - [ - 40, - 2, - 25, - 58, - 36, - 53 - ], - [ - 18, - 46, - 35, - 22, - 53, - 16 - ], - [ - 2, - 6, - 63, - 14, - 42, - 11 - ], - [ - 35, - 7, - 52, - 40, - 29, - 57 - ], - [ - 40, - 15, - 19, - 57, - 17, - 23 - ], - [ - 9, - 11, - 47, - 22, - 49, - 1 - ], - [ - 24, - 39, - 42, - 2, - 16, - 0 - ] - ], - [ - [ - 55, - 39, - 9, - 43, - 21, - 46 - ], - [ - 56, - 0, - 63, - 39, - 30, - 41 - ], - [ - 20, - 1, - 26, - 58, - 34, - 19 - ], - [ - 54, - 24, - 32, - 51, - 26, - 44 - ], - [ - 30, - 53, - 56, - 39, - 34, - 40 - ], - [ - 5, - 37, - 25, - 50, - 6, - 61 - ], - [ - 24, - 49, - 37, - 15, - 6, - 29 - ], - [ - 34, - 16, - 30, - 61, - 10, - 36 - ], - [ - 16, - 29, - 2, - 5, - 51, - 26 - ], - [ - 9, - 56, - 11, - 31, - 46, - 45 - ], - [ - 10, - 45, - 56, - 62, - 25, - 36 - ], - [ - 11, - 6, - 35, - 36, - 1, - 52 - ], - [ - 51, - 50, - 41, - 46, - 38, - 4 - ], - [ - 19, - 33, - 41, - 16, - 31, - 52 - ], - [ - 34, - 7, - 17, - 47, - 63, - 3 - ], - [ - 58, - 9, - 22, - 61, - 59, - 8 - ], - [ - 40, - 37, - 3, - 51, - 22, - 57 - ], - [ - 12, - 52, - 21, - 54, - 25, - 19 - ], - [ - 34, - 53, - 27, - 43, - 14, - 13 - ], - [ - 56, - 44, - 53, - 24, - 60, - 43 - ], - [ - 12, - 53, - 40, - 49, - 2, - 62 - ], - [ - 18, - 39, - 44, - 61, - 26, - 23 - ], - [ - 0, - 4, - 53, - 25, - 41, - 21 - ], - [ - 1, - 7, - 25, - 10, - 40, - 56 - ], - [ - 40, - 22, - 6, - 29, - 19, - 48 - ], - [ - 28, - 10, - 47, - 55, - 42, - 44 - ], - [ - 30, - 27, - 57, - 16, - 50, - 59 - ] - ], - [ - [ - 45, - 37, - 48, - 29, - 30, - 3 - ], - [ - 8, - 60, - 10, - 59, - 43, - 6 - ], - [ - 51, - 45, - 28, - 59, - 63, - 34 - ], - [ - 4, - 16, - 20, - 58, - 44, - 28 - ], - [ - 50, - 31, - 57, - 24, - 51, - 53 - ], - [ - 58, - 9, - 0, - 61, - 35, - 41 - ], - [ - 16, - 63, - 11, - 61, - 23, - 36 - ], - [ - 4, - 47, - 42, - 53, - 8, - 30 - ], - [ - 44, - 14, - 16, - 33, - 3, - 20 - ], - [ - 34, - 28, - 26, - 57, - 22, - 18 - ], - [ - 20, - 35, - 19, - 59, - 2, - 38 - ], - [ - 12, - 60, - 43, - 63, - 32, - 62 - ], - [ - 28, - 12, - 29, - 11, - 14, - 50 - ], - [ - 23, - 29, - 33, - 22, - 11, - 19 - ], - [ - 23, - 60, - 51, - 50, - 7, - 22 - ], - [ - 44, - 46, - 49, - 7, - 1, - 12 - ], - [ - 2, - 54, - 27, - 61, - 18, - 5 - ], - [ - 17, - 50, - 51, - 32, - 33, - 34 - ], - [ - 5, - 19, - 61, - 27, - 32, - 11 - ], - [ - 6, - 0, - 5, - 13, - 41, - 57 - ], - [ - 27, - 33, - 53, - 45, - 38, - 32 - ], - [ - 26, - 36, - 55, - 59, - 61, - 18 - ], - [ - 47, - 46, - 3, - 37, - 57, - 49 - ], - [ - 20, - 22, - 4, - 16, - 51, - 11 - ], - [ - 62, - 11, - 21, - 34, - 4, - 1 - ], - [ - 34, - 18, - 7, - 60, - 33, - 32 - ], - [ - 45, - 52, - 4, - 36, - 21, - 9 - ] - ], - [ - [ - 18, - 8, - 20, - 49, - 30, - 23 - ], - [ - 1, - 27, - 26, - 22, - 59, - 36 - ], - [ - 43, - 26, - 15, - 58, - 0, - 46 - ], - [ - 55, - 1, - 35, - 28, - 16, - 32 - ], - [ - 59, - 9, - 10, - 53, - 12, - 58 - ], - [ - 9, - 2, - 27, - 11, - 61, - 43 - ], - [ - 16, - 57, - 63, - 23, - 19, - 12 - ], - [ - 46, - 45, - 26, - 4, - 30, - 37 - ], - [ - 43, - 44, - 20, - 16, - 14, - 9 - ], - [ - 34, - 47, - 42, - 43, - 26, - 51 - ], - [ - 42, - 2, - 38, - 45, - 20, - 36 - ], - [ - 18, - 7, - 12, - 2, - 43, - 60 - ], - [ - 1, - 28, - 12, - 3, - 29, - 33 - ], - [ - 25, - 13, - 0, - 63, - 2, - 62 - ], - [ - 18, - 36, - 6, - 29, - 19, - 15 - ], - [ - 1, - 42, - 63, - 41, - 57, - 19 - ], - [ - 57, - 54, - 5, - 27, - 18, - 31 - ], - [ - 50, - 6, - 13, - 32, - 17, - 20 - ], - [ - 17, - 5, - 27, - 32, - 1, - 55 - ], - [ - 49, - 0, - 61, - 5, - 10, - 30 - ], - [ - 29, - 53, - 51, - 13, - 33, - 46 - ], - [ - 29, - 17, - 21, - 30, - 14, - 40 - ], - [ - 5, - 17, - 33, - 32, - 18, - 28 - ], - [ - 51, - 4, - 20, - 54, - 58, - 41 - ], - [ - 47, - 4, - 27, - 48, - 37, - 60 - ], - [ - 3, - 26, - 12, - 59, - 2, - 48 - ], - [ - 46, - 43, - 18, - 20, - 9, - 53 - ] - ], - [ - [ - 45, - 6, - 57, - 43, - 40, - 58 - ], - [ - 38, - 63, - 36, - 27, - 54, - 33 - ], - [ - 37, - 14, - 19, - 41, - 58, - 63 - ], - [ - 9, - 12, - 2, - 55, - 28, - 23 - ], - [ - 39, - 59, - 7, - 13, - 33, - 43 - ], - [ - 45, - 9, - 63, - 27, - 32, - 58 - ], - [ - 16, - 57, - 10, - 63, - 11, - 23 - ], - [ - 51, - 45, - 25, - 4, - 21, - 30 - ], - [ - 21, - 44, - 14, - 16, - 33, - 39 - ], - [ - 42, - 44, - 43, - 5, - 37, - 34 - ], - [ - 42, - 19, - 20, - 2, - 38, - 61 - ], - [ - 4, - 12, - 2, - 62, - 63, - 36 - ], - [ - 32, - 55, - 0, - 11, - 47, - 28 - ], - [ - 43, - 13, - 2, - 44, - 26, - 50 - ], - [ - 49, - 33, - 15, - 28, - 29, - 35 - ], - [ - 44, - 41, - 7, - 2, - 22, - 63 - ], - [ - 48, - 6, - 54, - 20, - 2, - 27 - ], - [ - 50, - 51, - 32, - 3, - 17, - 36 - ], - [ - 5, - 61, - 57, - 48, - 19, - 32 - ], - [ - 21, - 0, - 6, - 31, - 29, - 47 - ], - [ - 33, - 9, - 53, - 27, - 17, - 36 - ], - [ - 29, - 26, - 55, - 19, - 17, - 62 - ], - [ - 12, - 46, - 5, - 37, - 57, - 3 - ], - [ - 20, - 51, - 4, - 22, - 16, - 41 - ], - [ - 21, - 11, - 62, - 46, - 23, - 48 - ], - [ - 32, - 60, - 37, - 18, - 3, - 7 - ], - [ - 9, - 11, - 36, - 48, - 0, - 45 - ] - ], - [ - [ - 49, - 42, - 28, - 23, - 33, - 61 - ], - [ - 4, - 2, - 12, - 6, - 8, - 55 - ], - [ - 12, - 0, - 26, - 41, - 6, - 27 - ], - [ - 9, - 57, - 6, - 23, - 51, - 28 - ], - [ - 40, - 7, - 20, - 16, - 15, - 33 - ], - [ - 45, - 59, - 63, - 62, - 32, - 3 - ], - [ - 10, - 39, - 57, - 13, - 16, - 19 - ], - [ - 45, - 23, - 51, - 33, - 25, - 46 - ], - [ - 28, - 21, - 44, - 11, - 16, - 59 - ], - [ - 5, - 42, - 44, - 24, - 43, - 47 - ], - [ - 42, - 53, - 30, - 18, - 2, - 27 - ], - [ - 2, - 62, - 4, - 43, - 10, - 36 - ], - [ - 0, - 56, - 55, - 47, - 32, - 49 - ], - [ - 43, - 25, - 2, - 5, - 3, - 49 - ], - [ - 4, - 28, - 15, - 8, - 49, - 58 - ], - [ - 2, - 42, - 44, - 41, - 7, - 63 - ], - [ - 48, - 27, - 54, - 20, - 2, - 18 - ], - [ - 50, - 51, - 0, - 36, - 3, - 32 - ], - [ - 57, - 5, - 61, - 19, - 32, - 38 - ], - [ - 21, - 0, - 6, - 63, - 23, - 51 - ], - [ - 33, - 53, - 27, - 36, - 9, - 38 - ], - [ - 29, - 26, - 55, - 62, - 18, - 31 - ], - [ - 46, - 56, - 12, - 53, - 29, - 0 - ], - [ - 20, - 16, - 22, - 4, - 51, - 17 - ], - [ - 21, - 62, - 11, - 31, - 46, - 33 - ], - [ - 37, - 60, - 18, - 7, - 32, - 44 - ], - [ - 11, - 9, - 36, - 0, - 48, - 63 - ] - ], - [ - [ - 41, - 32, - 49, - 39, - 61, - 44 - ], - [ - 47, - 26, - 16, - 21, - 36, - 22 - ], - [ - 4, - 30, - 37, - 42, - 60, - 54 - ], - [ - 9, - 57, - 26, - 32, - 50, - 20 - ], - [ - 56, - 3, - 40, - 33, - 36, - 54 - ], - [ - 11, - 38, - 2, - 32, - 61, - 30 - ], - [ - 39, - 57, - 19, - 10, - 16, - 42 - ], - [ - 46, - 21, - 35, - 39, - 45, - 25 - ], - [ - 21, - 37, - 12, - 20, - 11, - 28 - ], - [ - 5, - 47, - 44, - 10, - 42, - 23 - ], - [ - 18, - 42, - 61, - 2, - 38, - 31 - ], - [ - 54, - 4, - 2, - 7, - 22, - 16 - ], - [ - 5, - 3, - 17, - 56, - 32, - 55 - ], - [ - 55, - 0, - 2, - 25, - 43, - 5 - ], - [ - 22, - 28, - 15, - 6, - 5, - 49 - ], - [ - 2, - 57, - 19, - 54, - 41, - 30 - ], - [ - 7, - 48, - 20, - 54, - 27, - 0 - ], - [ - 3, - 56, - 13, - 37, - 43, - 59 - ], - [ - 45, - 55, - 57, - 61, - 48, - 52 - ], - [ - 21, - 5, - 0, - 16, - 27, - 23 - ], - [ - 25, - 42, - 17, - 54, - 23, - 14 - ], - [ - 21, - 44, - 15, - 20, - 42, - 18 - ], - [ - 35, - 12, - 25, - 53, - 61, - 2 - ], - [ - 38, - 54, - 48, - 53, - 21, - 36 - ], - [ - 13, - 31, - 48, - 33, - 18, - 55 - ], - [ - 38, - 27, - 19, - 6, - 44, - 3 - ], - [ - 29, - 62, - 43, - 59, - 46, - 5 - ] - ], - [ - [ - 57, - 9, - 19, - 51, - 18, - 41 - ], - [ - 28, - 57, - 36, - 8, - 48, - 60 - ], - [ - 2, - 51, - 59, - 5, - 34, - 9 - ], - [ - 9, - 55, - 59, - 26, - 4, - 2 - ], - [ - 49, - 56, - 35, - 42, - 30, - 23 - ], - [ - 18, - 30, - 22, - 29, - 19, - 52 - ], - [ - 39, - 34, - 33, - 51, - 56, - 3 - ], - [ - 32, - 21, - 1, - 7, - 46, - 49 - ], - [ - 33, - 54, - 23, - 21, - 12, - 11 - ], - [ - 5, - 30, - 60, - 47, - 15, - 18 - ], - [ - 4, - 18, - 46, - 27, - 20, - 22 - ], - [ - 22, - 59, - 54, - 48, - 19, - 4 - ], - [ - 17, - 5, - 56, - 31, - 49, - 4 - ], - [ - 29, - 47, - 55, - 2, - 53, - 60 - ], - [ - 8, - 22, - 11, - 44, - 36, - 15 - ], - [ - 60, - 44, - 30, - 57, - 54, - 39 - ], - [ - 7, - 44, - 27, - 20, - 2, - 61 - ], - [ - 48, - 17, - 21, - 37, - 32, - 57 - ], - [ - 48, - 32, - 46, - 6, - 61, - 42 - ], - [ - 4, - 57, - 1, - 36, - 0, - 30 - ], - [ - 7, - 17, - 61, - 53, - 21, - 63 - ], - [ - 60, - 14, - 53, - 35, - 18, - 55 - ], - [ - 10, - 15, - 33, - 51, - 36, - 5 - ], - [ - 11, - 4, - 19, - 51, - 21, - 52 - ], - [ - 47, - 19, - 43, - 48, - 58, - 4 - ], - [ - 3, - 33, - 26, - 21, - 52, - 19 - ], - [ - 24, - 45, - 60, - 35, - 49, - 1 - ] - ], - [ - [ - 16, - 4, - 44, - 23, - 22, - 43 - ], - [ - 16, - 23, - 50, - 9, - 32, - 13 - ], - [ - 11, - 35, - 21, - 7, - 48, - 59 - ], - [ - 55, - 15, - 1, - 11, - 8, - 40 - ], - [ - 35, - 61, - 30, - 59, - 31, - 62 - ], - [ - 51, - 29, - 15, - 52, - 38, - 61 - ], - [ - 60, - 0, - 55, - 34, - 59, - 33 - ], - [ - 12, - 22, - 56, - 63, - 54, - 55 - ], - [ - 42, - 54, - 23, - 33, - 27, - 47 - ], - [ - 30, - 60, - 20, - 5, - 4, - 22 - ], - [ - 4, - 35, - 22, - 46, - 23, - 19 - ], - [ - 29, - 22, - 59, - 49, - 24, - 28 - ], - [ - 17, - 31, - 5, - 56, - 4, - 9 - ], - [ - 61, - 29, - 0, - 48, - 59, - 50 - ], - [ - 8, - 6, - 22, - 60, - 55, - 31 - ], - [ - 60, - 39, - 19, - 57, - 53, - 1 - ], - [ - 56, - 30, - 22, - 10, - 5, - 18 - ], - [ - 20, - 31, - 1, - 26, - 61, - 37 - ], - [ - 8, - 23, - 7, - 46, - 48, - 4 - ], - [ - 45, - 23, - 51, - 13, - 17, - 4 - ], - [ - 17, - 13, - 61, - 29, - 14, - 55 - ], - [ - 14, - 27, - 21, - 43, - 57, - 56 - ], - [ - 52, - 51, - 15, - 58, - 8, - 5 - ], - [ - 4, - 51, - 49, - 14, - 21, - 34 - ], - [ - 4, - 9, - 47, - 13, - 8, - 61 - ], - [ - 27, - 3, - 16, - 43, - 31, - 47 - ], - [ - 59, - 43, - 29, - 61, - 0, - 40 - ] - ], - [ - [ - 48, - 21, - 18, - 49, - 41, - 23 - ], - [ - 36, - 4, - 60, - 8, - 49, - 44 - ], - [ - 20, - 39, - 30, - 59, - 45, - 55 - ], - [ - 35, - 46, - 15, - 48, - 33, - 2 - ], - [ - 61, - 37, - 8, - 15, - 54, - 10 - ], - [ - 46, - 6, - 51, - 29, - 58, - 4 - ], - [ - 28, - 11, - 44, - 60, - 0, - 1 - ], - [ - 63, - 12, - 13, - 27, - 10, - 0 - ], - [ - 33, - 42, - 54, - 44, - 23, - 14 - ], - [ - 18, - 60, - 30, - 22, - 40, - 14 - ], - [ - 35, - 4, - 61, - 9, - 18, - 33 - ], - [ - 59, - 45, - 48, - 28, - 62, - 22 - ], - [ - 17, - 56, - 7, - 5, - 53, - 36 - ], - [ - 8, - 47, - 29, - 59, - 1, - 6 - ], - [ - 9, - 8, - 18, - 22, - 60, - 15 - ], - [ - 46, - 60, - 22, - 44, - 30, - 57 - ], - [ - 61, - 2, - 27, - 34, - 7, - 60 - ], - [ - 48, - 21, - 37, - 17, - 50, - 57 - ], - [ - 44, - 42, - 5, - 2, - 48, - 61 - ], - [ - 37, - 57, - 4, - 36, - 17, - 59 - ], - [ - 7, - 17, - 44, - 61, - 53, - 33 - ], - [ - 60, - 45, - 14, - 42, - 18, - 9 - ], - [ - 10, - 8, - 36, - 33, - 15, - 58 - ], - [ - 19, - 20, - 11, - 4, - 49, - 51 - ], - [ - 61, - 37, - 47, - 23, - 12, - 3 - ], - [ - 17, - 33, - 3, - 40, - 19, - 26 - ], - [ - 61, - 45, - 49, - 1, - 14, - 63 - ] - ], - [ - [ - 13, - 40, - 55, - 63, - 26, - 41 - ], - [ - 5, - 35, - 49, - 40, - 17, - 46 - ], - [ - 38, - 17, - 59, - 49, - 2, - 58 - ], - [ - 40, - 8, - 1, - 16, - 0, - 11 - ], - [ - 37, - 62, - 51, - 10, - 8, - 38 - ], - [ - 9, - 42, - 61, - 29, - 35, - 33 - ], - [ - 63, - 53, - 11, - 16, - 33, - 60 - ], - [ - 63, - 37, - 5, - 13, - 17, - 39 - ], - [ - 44, - 20, - 31, - 54, - 38, - 21 - ], - [ - 43, - 21, - 30, - 34, - 18, - 49 - ], - [ - 2, - 11, - 19, - 35, - 4, - 9 - ], - [ - 22, - 60, - 43, - 2, - 4, - 49 - ], - [ - 29, - 5, - 17, - 22, - 24, - 55 - ], - [ - 2, - 59, - 29, - 5, - 55, - 6 - ], - [ - 23, - 8, - 36, - 22, - 15, - 30 - ], - [ - 12, - 44, - 41, - 5, - 45, - 22 - ], - [ - 54, - 7, - 41, - 11, - 1, - 53 - ], - [ - 6, - 50, - 2, - 9, - 21, - 37 - ], - [ - 13, - 19, - 5, - 10, - 48, - 8 - ], - [ - 41, - 6, - 32, - 21, - 0, - 47 - ], - [ - 38, - 33, - 36, - 53, - 31, - 61 - ], - [ - 3, - 26, - 7, - 62, - 18, - 59 - ], - [ - 56, - 57, - 46, - 12, - 35, - 3 - ], - [ - 20, - 16, - 22, - 24, - 27, - 42 - ], - [ - 36, - 21, - 46, - 34, - 3, - 11 - ], - [ - 33, - 34, - 60, - 45, - 7, - 32 - ], - [ - 56, - 34, - 52, - 58, - 26, - 48 - ] - ], - [ - [ - 54, - 23, - 53, - 11, - 58, - 3 - ], - [ - 11, - 30, - 59, - 58, - 63, - 4 - ], - [ - 20, - 29, - 58, - 17, - 42, - 4 - ], - [ - 1, - 35, - 40, - 45, - 53, - 21 - ], - [ - 40, - 55, - 33, - 21, - 38, - 49 - ], - [ - 45, - 29, - 61, - 27, - 63, - 62 - ], - [ - 33, - 57, - 11, - 28, - 53, - 34 - ], - [ - 11, - 63, - 39, - 10, - 45, - 14 - ], - [ - 30, - 54, - 57, - 59, - 33, - 26 - ], - [ - 43, - 23, - 5, - 18, - 21, - 42 - ], - [ - 11, - 18, - 2, - 9, - 34, - 6 - ], - [ - 22, - 60, - 28, - 2, - 63, - 17 - ], - [ - 5, - 41, - 6, - 17, - 56, - 29 - ], - [ - 55, - 6, - 5, - 2, - 48, - 59 - ], - [ - 23, - 19, - 22, - 62, - 11, - 9 - ], - [ - 12, - 45, - 41, - 8, - 27, - 42 - ], - [ - 11, - 53, - 41, - 44, - 51, - 7 - ], - [ - 21, - 2, - 6, - 36, - 50, - 56 - ], - [ - 13, - 10, - 48, - 53, - 61, - 19 - ], - [ - 47, - 21, - 56, - 44, - 6, - 31 - ], - [ - 44, - 12, - 3, - 55, - 41, - 53 - ], - [ - 44, - 47, - 28, - 43, - 45, - 63 - ], - [ - 1, - 25, - 53, - 11, - 39, - 19 - ], - [ - 1, - 59, - 38, - 3, - 37, - 42 - ], - [ - 45, - 3, - 0, - 21, - 22, - 33 - ], - [ - 10, - 28, - 42, - 49, - 11, - 3 - ], - [ - 30, - 57, - 15, - 16, - 56, - 41 - ] - ], - [ - [ - 53, - 15, - 34, - 0, - 46, - 33 - ], - [ - 8, - 12, - 41, - 19, - 39, - 32 - ], - [ - 56, - 31, - 36, - 13, - 23, - 9 - ], - [ - 36, - 51, - 30, - 21, - 1, - 11 - ], - [ - 13, - 58, - 50, - 2, - 53, - 54 - ], - [ - 49, - 52, - 32, - 7, - 23, - 47 - ], - [ - 61, - 38, - 23, - 39, - 0, - 35 - ], - [ - 42, - 27, - 9, - 20, - 17, - 57 - ], - [ - 34, - 1, - 29, - 4, - 35, - 45 - ], - [ - 54, - 57, - 27, - 19, - 38, - 62 - ], - [ - 59, - 1, - 60, - 26, - 38, - 22 - ], - [ - 25, - 31, - 51, - 36, - 32, - 8 - ], - [ - 14, - 62, - 2, - 19, - 37, - 11 - ], - [ - 57, - 40, - 13, - 22, - 37, - 46 - ], - [ - 45, - 34, - 58, - 44, - 42, - 16 - ], - [ - 50, - 16, - 6, - 5, - 33, - 43 - ], - [ - 42, - 39, - 61, - 13, - 5, - 15 - ], - [ - 46, - 23, - 27, - 4, - 28, - 63 - ], - [ - 62, - 31, - 10, - 45, - 35, - 56 - ], - [ - 15, - 13, - 38, - 63, - 4, - 31 - ], - [ - 34, - 15, - 38, - 57, - 27, - 19 - ], - [ - 41, - 62, - 36, - 57, - 19, - 47 - ], - [ - 34, - 22, - 53, - 10, - 46, - 33 - ], - [ - 24, - 51, - 4, - 47, - 39, - 10 - ], - [ - 11, - 57, - 51, - 50, - 54, - 48 - ], - [ - 51, - 7, - 11, - 43, - 50, - 18 - ], - [ - 39, - 37, - 9, - 42, - 40, - 44 - ] - ], - [ - [ - 57, - 17, - 62, - 42, - 23, - 60 - ], - [ - 18, - 7, - 53, - 43, - 26, - 60 - ], - [ - 60, - 5, - 3, - 53, - 23, - 57 - ], - [ - 17, - 10, - 22, - 19, - 11, - 31 - ], - [ - 10, - 15, - 12, - 27, - 17, - 4 - ], - [ - 2, - 44, - 39, - 36, - 25, - 54 - ], - [ - 52, - 62, - 37, - 21, - 41, - 42 - ], - [ - 62, - 7, - 46, - 30, - 36, - 14 - ], - [ - 50, - 4, - 10, - 58, - 0, - 16 - ], - [ - 32, - 47, - 38, - 54, - 8, - 41 - ], - [ - 41, - 32, - 3, - 18, - 1, - 22 - ], - [ - 16, - 50, - 53, - 7, - 44, - 21 - ], - [ - 11, - 39, - 3, - 35, - 25, - 46 - ], - [ - 18, - 9, - 15, - 13, - 63, - 28 - ], - [ - 62, - 58, - 13, - 5, - 17, - 3 - ], - [ - 3, - 31, - 43, - 53, - 35, - 57 - ], - [ - 24, - 51, - 15, - 46, - 32, - 5 - ], - [ - 13, - 30, - 0, - 32, - 5, - 59 - ], - [ - 2, - 39, - 32, - 38, - 34, - 22 - ], - [ - 42, - 26, - 34, - 28, - 37, - 54 - ], - [ - 28, - 43, - 53, - 41, - 13, - 23 - ], - [ - 14, - 15, - 34, - 1, - 48, - 40 - ], - [ - 5, - 25, - 4, - 33, - 39, - 6 - ], - [ - 58, - 4, - 57, - 17, - 51, - 11 - ], - [ - 37, - 47, - 35, - 31, - 63, - 29 - ], - [ - 15, - 3, - 28, - 33, - 23, - 9 - ], - [ - 23, - 6, - 58, - 47, - 56, - 30 - ] - ], - [ - [ - 47, - 29, - 14, - 6, - 51, - 43 - ], - [ - 30, - 29, - 39, - 7, - 52, - 12 - ], - [ - 63, - 34, - 41, - 2, - 47, - 7 - ], - [ - 4, - 28, - 54, - 45, - 52, - 58 - ], - [ - 29, - 7, - 12, - 15, - 41, - 6 - ], - [ - 34, - 29, - 48, - 3, - 43, - 40 - ], - [ - 30, - 29, - 16, - 47, - 42, - 45 - ], - [ - 33, - 39, - 25, - 60, - 41, - 3 - ], - [ - 50, - 26, - 4, - 25, - 17, - 13 - ], - [ - 5, - 54, - 43, - 16, - 12, - 53 - ], - [ - 18, - 6, - 35, - 3, - 21, - 1 - ], - [ - 56, - 46, - 48, - 10, - 16, - 44 - ], - [ - 9, - 35, - 7, - 24, - 47, - 57 - ], - [ - 53, - 42, - 15, - 56, - 59, - 47 - ], - [ - 39, - 11, - 36, - 32, - 35, - 18 - ], - [ - 46, - 20, - 53, - 38, - 56, - 26 - ], - [ - 58, - 29, - 14, - 26, - 17, - 49 - ], - [ - 24, - 25, - 39, - 16, - 1, - 57 - ], - [ - 24, - 41, - 4, - 1, - 63, - 28 - ], - [ - 42, - 37, - 48, - 34, - 26, - 41 - ], - [ - 11, - 28, - 16, - 32, - 7, - 56 - ], - [ - 14, - 42, - 6, - 16, - 22, - 15 - ], - [ - 33, - 56, - 42, - 8, - 25, - 38 - ], - [ - 4, - 58, - 48, - 33, - 11, - 28 - ], - [ - 37, - 47, - 29, - 48, - 30, - 53 - ], - [ - 12, - 41, - 3, - 4, - 48, - 46 - ], - [ - 14, - 13, - 61, - 6, - 62, - 1 - ] - ], - [ - [ - 45, - 10, - 44, - 43, - 53, - 33 - ], - [ - 32, - 63, - 22, - 27, - 30, - 29 - ], - [ - 54, - 35, - 37, - 32, - 26, - 30 - ], - [ - 24, - 63, - 0, - 17, - 25, - 45 - ], - [ - 40, - 7, - 0, - 57, - 29, - 22 - ], - [ - 10, - 34, - 20, - 22, - 43, - 33 - ], - [ - 42, - 30, - 5, - 25, - 19, - 34 - ], - [ - 33, - 18, - 35, - 51, - 7, - 57 - ], - [ - 50, - 28, - 25, - 4, - 10, - 9 - ], - [ - 5, - 38, - 16, - 43, - 54, - 21 - ], - [ - 18, - 21, - 3, - 6, - 39, - 53 - ], - [ - 56, - 16, - 53, - 39, - 46, - 42 - ], - [ - 9, - 35, - 57, - 11, - 47, - 13 - ], - [ - 56, - 15, - 3, - 59, - 9, - 28 - ], - [ - 4, - 62, - 39, - 58, - 63, - 36 - ], - [ - 20, - 53, - 57, - 8, - 51, - 35 - ], - [ - 51, - 49, - 11, - 26, - 15, - 14 - ], - [ - 0, - 25, - 59, - 62, - 21, - 13 - ], - [ - 39, - 34, - 48, - 53, - 61, - 33 - ], - [ - 47, - 26, - 28, - 34, - 21, - 39 - ], - [ - 28, - 43, - 12, - 53, - 41, - 32 - ], - [ - 14, - 52, - 17, - 1, - 15, - 38 - ], - [ - 25, - 4, - 5, - 11, - 58, - 50 - ], - [ - 58, - 4, - 17, - 10, - 25, - 57 - ], - [ - 29, - 47, - 35, - 31, - 52, - 48 - ], - [ - 55, - 28, - 23, - 15, - 3, - 24 - ], - [ - 6, - 30, - 57, - 32, - 34, - 62 - ] - ], - [ - [ - 39, - 5, - 30, - 17, - 61, - 15 - ], - [ - 11, - 63, - 0, - 23, - 61, - 46 - ], - [ - 61, - 15, - 53, - 22, - 7, - 57 - ], - [ - 50, - 57, - 58, - 63, - 45, - 47 - ], - [ - 55, - 31, - 57, - 24, - 60, - 5 - ], - [ - 22, - 7, - 43, - 1, - 10, - 0 - ], - [ - 14, - 58, - 1, - 34, - 19, - 45 - ], - [ - 50, - 8, - 14, - 7, - 57, - 9 - ], - [ - 43, - 0, - 4, - 10, - 45, - 46 - ], - [ - 38, - 0, - 4, - 55, - 54, - 10 - ], - [ - 51, - 54, - 46, - 39, - 1, - 38 - ], - [ - 16, - 37, - 33, - 36, - 21, - 63 - ], - [ - 10, - 38, - 57, - 58, - 3, - 63 - ], - [ - 30, - 0, - 63, - 13, - 22, - 18 - ], - [ - 55, - 58, - 62, - 38, - 6, - 36 - ], - [ - 19, - 53, - 11, - 1, - 57, - 25 - ], - [ - 11, - 6, - 51, - 16, - 30, - 18 - ], - [ - 20, - 57, - 32, - 36, - 13, - 56 - ], - [ - 3, - 12, - 61, - 26, - 32, - 1 - ], - [ - 25, - 36, - 34, - 51, - 59, - 37 - ], - [ - 13, - 53, - 28, - 16, - 14, - 9 - ], - [ - 14, - 42, - 15, - 55, - 22, - 38 - ], - [ - 62, - 58, - 29, - 33, - 5, - 34 - ], - [ - 21, - 4, - 51, - 49, - 12, - 58 - ], - [ - 61, - 60, - 40, - 35, - 59, - 47 - ], - [ - 37, - 46, - 3, - 48, - 12, - 53 - ], - [ - 63, - 8, - 46, - 33, - 1, - 53 - ] - ], - [ - [ - 62, - 0, - 9, - 61, - 26, - 41 - ], - [ - 45, - 3, - 29, - 35, - 2, - 54 - ], - [ - 56, - 31, - 53, - 28, - 23, - 2 - ], - [ - 13, - 41, - 46, - 49, - 5, - 45 - ], - [ - 18, - 25, - 57, - 55, - 50, - 15 - ], - [ - 58, - 22, - 4, - 46, - 19, - 12 - ], - [ - 1, - 25, - 58, - 22, - 43, - 35 - ], - [ - 50, - 23, - 12, - 8, - 9, - 16 - ], - [ - 29, - 17, - 16, - 43, - 10, - 4 - ], - [ - 38, - 55, - 0, - 40, - 20, - 10 - ], - [ - 51, - 19, - 54, - 39, - 46, - 1 - ], - [ - 60, - 16, - 37, - 11, - 56, - 36 - ], - [ - 10, - 7, - 38, - 57, - 54, - 44 - ], - [ - 45, - 13, - 0, - 30, - 47, - 63 - ], - [ - 58, - 48, - 55, - 38, - 36, - 29 - ], - [ - 29, - 53, - 0, - 1, - 55, - 57 - ], - [ - 11, - 12, - 6, - 29, - 5, - 40 - ], - [ - 57, - 32, - 42, - 8, - 20, - 36 - ], - [ - 3, - 61, - 49, - 32, - 26, - 1 - ], - [ - 42, - 5, - 36, - 16, - 39, - 51 - ], - [ - 9, - 13, - 7, - 53, - 46, - 28 - ], - [ - 14, - 55, - 42, - 24, - 33, - 47 - ], - [ - 33, - 58, - 62, - 15, - 36, - 5 - ], - [ - 51, - 4, - 49, - 58, - 21, - 41 - ], - [ - 40, - 60, - 19, - 35, - 61, - 15 - ], - [ - 14, - 21, - 3, - 12, - 5, - 37 - ], - [ - 35, - 60, - 1, - 10, - 53, - 54 - ] - ], - [ - [ - 40, - 28, - 60, - 31, - 59, - 23 - ], - [ - 42, - 12, - 26, - 34, - 0, - 61 - ], - [ - 16, - 5, - 62, - 3, - 32, - 0 - ], - [ - 13, - 3, - 18, - 39, - 42, - 52 - ], - [ - 25, - 12, - 39, - 0, - 57, - 15 - ], - [ - 33, - 50, - 58, - 36, - 6, - 26 - ], - [ - 41, - 43, - 1, - 15, - 2, - 25 - ], - [ - 36, - 16, - 53, - 14, - 58, - 30 - ], - [ - 16, - 29, - 3, - 2, - 10, - 5 - ], - [ - 38, - 16, - 32, - 26, - 45, - 11 - ], - [ - 45, - 19, - 3, - 46, - 56, - 53 - ], - [ - 21, - 36, - 11, - 9, - 16, - 41 - ], - [ - 11, - 16, - 10, - 50, - 8, - 63 - ], - [ - 35, - 38, - 33, - 51, - 25, - 22 - ], - [ - 58, - 48, - 27, - 50, - 29, - 26 - ], - [ - 29, - 57, - 53, - 3, - 54, - 4 - ], - [ - 3, - 23, - 6, - 46, - 51, - 11 - ], - [ - 32, - 5, - 57, - 54, - 52, - 30 - ], - [ - 34, - 61, - 40, - 0, - 3, - 48 - ], - [ - 22, - 53, - 12, - 33, - 39, - 60 - ], - [ - 20, - 53, - 47, - 40, - 12, - 8 - ], - [ - 55, - 17, - 52, - 14, - 45, - 56 - ], - [ - 1, - 4, - 13, - 11, - 39, - 33 - ], - [ - 17, - 10, - 51, - 44, - 4, - 55 - ], - [ - 35, - 6, - 45, - 21, - 52, - 37 - ], - [ - 10, - 42, - 9, - 1, - 53, - 24 - ], - [ - 27, - 41, - 32, - 45, - 10, - 47 - ] - ], - [ - [ - 11, - 34, - 44, - 51, - 41, - 12 - ], - [ - 20, - 34, - 3, - 25, - 63, - 16 - ], - [ - 40, - 26, - 37, - 22, - 15, - 54 - ], - [ - 4, - 16, - 25, - 28, - 45, - 58 - ], - [ - 33, - 10, - 32, - 24, - 3, - 4 - ], - [ - 0, - 9, - 58, - 3, - 34, - 15 - ], - [ - 16, - 63, - 43, - 59, - 25, - 42 - ], - [ - 16, - 48, - 36, - 33, - 25, - 58 - ], - [ - 20, - 50, - 16, - 23, - 42, - 29 - ], - [ - 60, - 51, - 16, - 22, - 38, - 48 - ], - [ - 31, - 19, - 38, - 3, - 2, - 43 - ], - [ - 56, - 9, - 21, - 40, - 18, - 44 - ], - [ - 11, - 13, - 10, - 45, - 27, - 57 - ], - [ - 56, - 14, - 35, - 44, - 45, - 13 - ], - [ - 58, - 43, - 26, - 33, - 31, - 50 - ], - [ - 13, - 38, - 46, - 51, - 28, - 37 - ], - [ - 58, - 63, - 42, - 10, - 0, - 16 - ], - [ - 0, - 1, - 53, - 52, - 24, - 59 - ], - [ - 17, - 29, - 0, - 40, - 60, - 24 - ], - [ - 17, - 24, - 2, - 26, - 35, - 23 - ], - [ - 44, - 20, - 22, - 4, - 50, - 40 - ], - [ - 9, - 61, - 17, - 16, - 27, - 37 - ], - [ - 63, - 13, - 16, - 11, - 2, - 24 - ], - [ - 44, - 48, - 23, - 49, - 56, - 45 - ], - [ - 6, - 5, - 49, - 33, - 31, - 63 - ], - [ - 49, - 47, - 1, - 57, - 4, - 53 - ], - [ - 2, - 56, - 19, - 63, - 39, - 50 - ] - ], - [ - [ - 16, - 22, - 44, - 23, - 3, - 13 - ], - [ - 16, - 23, - 50, - 9, - 13, - 59 - ], - [ - 11, - 35, - 21, - 7, - 59, - 24 - ], - [ - 53, - 32, - 7, - 60, - 1, - 13 - ], - [ - 24, - 33, - 57, - 35, - 0, - 39 - ], - [ - 15, - 38, - 0, - 45, - 32, - 58 - ], - [ - 59, - 16, - 25, - 49, - 35, - 5 - ], - [ - 45, - 40, - 16, - 14, - 12, - 30 - ], - [ - 20, - 16, - 42, - 6, - 17, - 23 - ], - [ - 60, - 10, - 22, - 48, - 50, - 38 - ], - [ - 31, - 19, - 35, - 27, - 38, - 53 - ], - [ - 33, - 59, - 48, - 16, - 60, - 39 - ], - [ - 9, - 57, - 10, - 11, - 7, - 44 - ], - [ - 13, - 0, - 59, - 61, - 19, - 33 - ], - [ - 18, - 39, - 58, - 21, - 50, - 36 - ], - [ - 57, - 60, - 49, - 19, - 53, - 54 - ], - [ - 6, - 36, - 17, - 41, - 38, - 10 - ], - [ - 63, - 1, - 57, - 20, - 36, - 31 - ], - [ - 40, - 41, - 7, - 0, - 61, - 50 - ], - [ - 39, - 16, - 23, - 14, - 17, - 59 - ], - [ - 51, - 30, - 50, - 7, - 3, - 61 - ], - [ - 9, - 14, - 42, - 15, - 1, - 55 - ], - [ - 33, - 19, - 58, - 56, - 11, - 15 - ], - [ - 9, - 26, - 4, - 41, - 51, - 10 - ], - [ - 4, - 60, - 35, - 16, - 47, - 48 - ], - [ - 3, - 52, - 31, - 16, - 26, - 39 - ], - [ - 4, - 1, - 3, - 61, - 0, - 54 - ] - ], - [ - [ - 38, - 45, - 52, - 43, - 32, - 39 - ], - [ - 29, - 48, - 22, - 60, - 55, - 57 - ], - [ - 5, - 49, - 8, - 20, - 14, - 55 - ], - [ - 35, - 46, - 49, - 32, - 7, - 48 - ], - [ - 24, - 29, - 49, - 16, - 41, - 0 - ], - [ - 56, - 3, - 35, - 15, - 4, - 40 - ], - [ - 49, - 59, - 4, - 16, - 33, - 11 - ], - [ - 12, - 51, - 30, - 56, - 40, - 0 - ], - [ - 23, - 46, - 14, - 55, - 42, - 17 - ], - [ - 60, - 22, - 38, - 20, - 21, - 6 - ], - [ - 11, - 19, - 31, - 35, - 38, - 22 - ], - [ - 59, - 48, - 19, - 33, - 16, - 9 - ], - [ - 9, - 28, - 45, - 57, - 55, - 53 - ], - [ - 59, - 1, - 25, - 6, - 47, - 3 - ], - [ - 9, - 39, - 18, - 50, - 58, - 21 - ], - [ - 60, - 57, - 38, - 53, - 50, - 54 - ], - [ - 41, - 53, - 6, - 38, - 10, - 25 - ], - [ - 1, - 63, - 15, - 24, - 36, - 29 - ], - [ - 41, - 40, - 7, - 42, - 62, - 32 - ], - [ - 4, - 10, - 14, - 37, - 39, - 17 - ], - [ - 61, - 30, - 7, - 50, - 3, - 57 - ], - [ - 9, - 58, - 14, - 30, - 42, - 8 - ], - [ - 33, - 56, - 19, - 60, - 24, - 59 - ], - [ - 9, - 26, - 4, - 51, - 25, - 41 - ], - [ - 4, - 35, - 47, - 41, - 48, - 60 - ], - [ - 39, - 52, - 3, - 26, - 8, - 30 - ], - [ - 4, - 1, - 61, - 10, - 0, - 12 - ] - ], - [ - [ - 51, - 53, - 33, - 13, - 28, - 48 - ], - [ - 63, - 31, - 41, - 39, - 40, - 49 - ], - [ - 42, - 14, - 3, - 24, - 50, - 44 - ], - [ - 11, - 39, - 52, - 10, - 17, - 42 - ], - [ - 7, - 60, - 58, - 15, - 12, - 27 - ], - [ - 34, - 36, - 44, - 28, - 29, - 40 - ], - [ - 52, - 17, - 47, - 42, - 37, - 41 - ], - [ - 62, - 33, - 38, - 39, - 41, - 36 - ], - [ - 50, - 58, - 37, - 10, - 16, - 62 - ], - [ - 41, - 59, - 12, - 62, - 49, - 32 - ], - [ - 3, - 35, - 10, - 41, - 6, - 25 - ], - [ - 39, - 58, - 53, - 21, - 19, - 54 - ], - [ - 42, - 35, - 7, - 39, - 63, - 21 - ], - [ - 15, - 9, - 3, - 54, - 51, - 32 - ], - [ - 3, - 35, - 32, - 36, - 18, - 13 - ], - [ - 20, - 3, - 35, - 45, - 32, - 4 - ], - [ - 46, - 24, - 32, - 33, - 14, - 44 - ], - [ - 16, - 57, - 30, - 19, - 61, - 63 - ], - [ - 41, - 25, - 2, - 24, - 26, - 4 - ], - [ - 42, - 37, - 47, - 48, - 33, - 50 - ], - [ - 54, - 13, - 53, - 56, - 63, - 0 - ], - [ - 34, - 14, - 7, - 51, - 42, - 6 - ], - [ - 6, - 43, - 25, - 58, - 11, - 39 - ], - [ - 0, - 26, - 44, - 4, - 51, - 9 - ], - [ - 37, - 63, - 4, - 15, - 13, - 61 - ], - [ - 9, - 59, - 41, - 1, - 52, - 25 - ], - [ - 56, - 58, - 47, - 1, - 62, - 51 - ] - ], - [ - [ - 52, - 47, - 27, - 36, - 38, - 33 - ], - [ - 43, - 56, - 4, - 25, - 52, - 21 - ], - [ - 25, - 54, - 35, - 18, - 11, - 63 - ], - [ - 17, - 4, - 1, - 18, - 50, - 39 - ], - [ - 36, - 7, - 32, - 4, - 30, - 60 - ], - [ - 34, - 3, - 61, - 48, - 24, - 40 - ], - [ - 17, - 42, - 3, - 12, - 29, - 25 - ], - [ - 33, - 38, - 39, - 55, - 17, - 19 - ], - [ - 17, - 50, - 41, - 16, - 13, - 51 - ], - [ - 41, - 12, - 60, - 16, - 62, - 58 - ], - [ - 3, - 36, - 28, - 39, - 35, - 2 - ], - [ - 48, - 27, - 53, - 54, - 19, - 43 - ], - [ - 42, - 63, - 7, - 35, - 62, - 3 - ], - [ - 56, - 15, - 9, - 51, - 42, - 47 - ], - [ - 35, - 39, - 18, - 36, - 43, - 38 - ], - [ - 60, - 20, - 0, - 3, - 35, - 31 - ], - [ - 24, - 14, - 42, - 17, - 32, - 62 - ], - [ - 24, - 57, - 63, - 0, - 42, - 1 - ], - [ - 41, - 50, - 28, - 26, - 32, - 40 - ], - [ - 2, - 37, - 46, - 17, - 42, - 57 - ], - [ - 49, - 7, - 11, - 54, - 13, - 53 - ], - [ - 14, - 9, - 42, - 6, - 19, - 58 - ], - [ - 63, - 33, - 43, - 19, - 58, - 25 - ], - [ - 9, - 26, - 48, - 4, - 41, - 44 - ], - [ - 4, - 41, - 5, - 15, - 9, - 53 - ], - [ - 14, - 38, - 9, - 3, - 52, - 8 - ], - [ - 23, - 1, - 61, - 4, - 47, - 14 - ] - ], - [ - [ - 37, - 14, - 3, - 5, - 33, - 53 - ], - [ - 34, - 0, - 56, - 58, - 37, - 13 - ], - [ - 61, - 14, - 22, - 29, - 15, - 46 - ], - [ - 50, - 58, - 18, - 44, - 47, - 17 - ], - [ - 4, - 36, - 53, - 7, - 32, - 2 - ], - [ - 22, - 1, - 34, - 61, - 10, - 33 - ], - [ - 28, - 25, - 42, - 29, - 30, - 3 - ], - [ - 33, - 51, - 38, - 39, - 62, - 60 - ], - [ - 17, - 51, - 26, - 16, - 46, - 50 - ], - [ - 12, - 16, - 59, - 6, - 38, - 3 - ], - [ - 39, - 31, - 35, - 3, - 36, - 0 - ], - [ - 48, - 19, - 53, - 56, - 39, - 27 - ], - [ - 7, - 62, - 9, - 63, - 15, - 42 - ], - [ - 56, - 19, - 9, - 47, - 15, - 59 - ], - [ - 39, - 36, - 18, - 35, - 42, - 38 - ], - [ - 60, - 20, - 0, - 57, - 47, - 53 - ], - [ - 6, - 32, - 29, - 62, - 43, - 5 - ], - [ - 24, - 63, - 57, - 1, - 42, - 2 - ], - [ - 41, - 40, - 7, - 62, - 32, - 50 - ], - [ - 37, - 14, - 10, - 2, - 57, - 17 - ], - [ - 7, - 54, - 30, - 53, - 50, - 49 - ], - [ - 9, - 14, - 8, - 6, - 42, - 58 - ], - [ - 33, - 60, - 56, - 19, - 18, - 15 - ], - [ - 9, - 26, - 4, - 41, - 59, - 23 - ], - [ - 4, - 41, - 47, - 9, - 2, - 16 - ], - [ - 3, - 14, - 16, - 30, - 8, - 52 - ], - [ - 1, - 4, - 14, - 61, - 0, - 12 - ] - ], - [ - [ - 27, - 21, - 61, - 30, - 22, - 63 - ], - [ - 33, - 12, - 0, - 44, - 47, - 27 - ], - [ - 14, - 39, - 58, - 37, - 16, - 63 - ], - [ - 11, - 47, - 4, - 18, - 32, - 35 - ], - [ - 2, - 19, - 20, - 39, - 61, - 10 - ], - [ - 39, - 61, - 49, - 15, - 30, - 47 - ], - [ - 12, - 47, - 17, - 49, - 29, - 46 - ], - [ - 39, - 41, - 62, - 30, - 52, - 37 - ], - [ - 21, - 15, - 28, - 48, - 26, - 27 - ], - [ - 12, - 32, - 16, - 24, - 55, - 41 - ], - [ - 43, - 35, - 28, - 31, - 60, - 47 - ], - [ - 3, - 17, - 19, - 49, - 10, - 42 - ], - [ - 7, - 54, - 27, - 59, - 4, - 23 - ], - [ - 35, - 33, - 7, - 6, - 14, - 51 - ], - [ - 51, - 28, - 36, - 42, - 41, - 32 - ], - [ - 45, - 52, - 60, - 21, - 53, - 12 - ], - [ - 53, - 1, - 23, - 5, - 6, - 46 - ], - [ - 35, - 9, - 63, - 16, - 26, - 22 - ], - [ - 41, - 60, - 27, - 40, - 42, - 15 - ], - [ - 42, - 48, - 46, - 9, - 17, - 37 - ], - [ - 31, - 11, - 23, - 58, - 39, - 44 - ], - [ - 34, - 2, - 4, - 14, - 13, - 9 - ], - [ - 60, - 59, - 47, - 40, - 17, - 27 - ], - [ - 60, - 63, - 7, - 42, - 40, - 49 - ], - [ - 59, - 15, - 38, - 62, - 44, - 25 - ], - [ - 23, - 57, - 60, - 31, - 41, - 3 - ], - [ - 38, - 59, - 31, - 51, - 36, - 7 - ] - ], - [ - [ - 12, - 10, - 50, - 23, - 53, - 33 - ], - [ - 48, - 56, - 44, - 11, - 31, - 17 - ], - [ - 33, - 11, - 17, - 54, - 15, - 62 - ], - [ - 31, - 13, - 17, - 40, - 8, - 3 - ], - [ - 42, - 4, - 27, - 15, - 12, - 5 - ], - [ - 34, - 50, - 33, - 29, - 55, - 6 - ], - [ - 16, - 49, - 52, - 29, - 41, - 30 - ], - [ - 0, - 30, - 62, - 57, - 26, - 22 - ], - [ - 26, - 62, - 5, - 58, - 51, - 49 - ], - [ - 5, - 62, - 16, - 46, - 39, - 37 - ], - [ - 31, - 10, - 24, - 45, - 18, - 35 - ], - [ - 50, - 19, - 48, - 54, - 16, - 35 - ], - [ - 59, - 39, - 62, - 7, - 35, - 28 - ], - [ - 20, - 9, - 19, - 6, - 22, - 15 - ], - [ - 35, - 18, - 50, - 32, - 36, - 39 - ], - [ - 3, - 4, - 13, - 38, - 60, - 26 - ], - [ - 46, - 32, - 28, - 3, - 37, - 33 - ], - [ - 30, - 63, - 16, - 19, - 24, - 42 - ], - [ - 41, - 17, - 4, - 2, - 32, - 34 - ], - [ - 42, - 37, - 48, - 33, - 3, - 31 - ], - [ - 56, - 7, - 25, - 11, - 39, - 44 - ], - [ - 42, - 14, - 26, - 13, - 12, - 22 - ], - [ - 25, - 11, - 6, - 42, - 13, - 38 - ], - [ - 0, - 9, - 26, - 41, - 4, - 57 - ], - [ - 37, - 4, - 63, - 41, - 2, - 44 - ], - [ - 9, - 52, - 41, - 3, - 16, - 59 - ], - [ - 58, - 47, - 56, - 1, - 26, - 62 - ] - ], - [ - [ - 54, - 30, - 22, - 26, - 3, - 55 - ], - [ - 44, - 7, - 49, - 50, - 25, - 5 - ], - [ - 54, - 4, - 48, - 58, - 26, - 32 - ], - [ - 20, - 25, - 3, - 9, - 55, - 28 - ], - [ - 7, - 27, - 42, - 12, - 58, - 32 - ], - [ - 50, - 34, - 6, - 42, - 29, - 55 - ], - [ - 16, - 49, - 40, - 3, - 27, - 11 - ], - [ - 51, - 30, - 26, - 62, - 53, - 57 - ], - [ - 3, - 5, - 49, - 28, - 26, - 50 - ], - [ - 16, - 22, - 46, - 6, - 49, - 45 - ], - [ - 31, - 45, - 11, - 10, - 56, - 3 - ], - [ - 21, - 51, - 50, - 19, - 9, - 61 - ], - [ - 28, - 41, - 59, - 13, - 34, - 53 - ], - [ - 23, - 20, - 16, - 9, - 38, - 19 - ], - [ - 50, - 34, - 58, - 20, - 27, - 35 - ], - [ - 8, - 59, - 29, - 61, - 35, - 53 - ], - [ - 3, - 46, - 51, - 10, - 25, - 18 - ], - [ - 62, - 21, - 25, - 9, - 18, - 56 - ], - [ - 34, - 27, - 10, - 29, - 53, - 59 - ], - [ - 56, - 33, - 44, - 24, - 9, - 18 - ], - [ - 44, - 12, - 19, - 30, - 8, - 49 - ], - [ - 47, - 62, - 51, - 12, - 16, - 17 - ], - [ - 11, - 37, - 25, - 58, - 35, - 45 - ], - [ - 4, - 9, - 10, - 39, - 37, - 16 - ], - [ - 22, - 11, - 21, - 48, - 45, - 47 - ], - [ - 10, - 28, - 55, - 12, - 24, - 23 - ], - [ - 30, - 16, - 27, - 32, - 57, - 15 - ] - ], - [ - [ - 16, - 11, - 31, - 46, - 35, - 49 - ], - [ - 13, - 49, - 54, - 5, - 6, - 14 - ], - [ - 36, - 13, - 27, - 46, - 3, - 18 - ], - [ - 24, - 61, - 15, - 0, - 63, - 13 - ], - [ - 17, - 2, - 58, - 50, - 35, - 19 - ], - [ - 8, - 7, - 49, - 52, - 47, - 23 - ], - [ - 61, - 58, - 16, - 20, - 38, - 23 - ], - [ - 20, - 42, - 9, - 51, - 35, - 16 - ], - [ - 25, - 47, - 4, - 1, - 50, - 63 - ], - [ - 54, - 38, - 57, - 27, - 33, - 28 - ], - [ - 1, - 59, - 30, - 60, - 14, - 31 - ], - [ - 31, - 51, - 25, - 14, - 41, - 55 - ], - [ - 34, - 2, - 14, - 11, - 19, - 28 - ], - [ - 40, - 57, - 13, - 61, - 59, - 18 - ], - [ - 44, - 58, - 50, - 45, - 37, - 26 - ], - [ - 5, - 8, - 62, - 24, - 38, - 61 - ], - [ - 13, - 47, - 15, - 3, - 5, - 39 - ], - [ - 25, - 23, - 9, - 18, - 63, - 33 - ], - [ - 1, - 10, - 20, - 8, - 53, - 4 - ], - [ - 38, - 15, - 37, - 0, - 34, - 60 - ], - [ - 41, - 27, - 30, - 57, - 19, - 40 - ], - [ - 11, - 62, - 41, - 14, - 46, - 44 - ], - [ - 11, - 33, - 46, - 31, - 45, - 0 - ], - [ - 4, - 51, - 47, - 16, - 9, - 12 - ], - [ - 11, - 21, - 45, - 47, - 61, - 18 - ], - [ - 10, - 50, - 51, - 12, - 18, - 3 - ], - [ - 17, - 31, - 36, - 5, - 19, - 1 - ] - ], - [ - [ - 22, - 6, - 39, - 57, - 29, - 47 - ], - [ - 27, - 6, - 14, - 17, - 51, - 32 - ], - [ - 1, - 29, - 11, - 26, - 47, - 51 - ], - [ - 14, - 38, - 22, - 31, - 29, - 53 - ], - [ - 14, - 61, - 59, - 1, - 29, - 49 - ], - [ - 30, - 8, - 21, - 47, - 52, - 0 - ], - [ - 4, - 58, - 61, - 23, - 20, - 29 - ], - [ - 20, - 9, - 42, - 35, - 7, - 24 - ], - [ - 47, - 25, - 4, - 1, - 29, - 0 - ], - [ - 54, - 19, - 38, - 29, - 33, - 44 - ], - [ - 14, - 1, - 59, - 40, - 60, - 20 - ], - [ - 51, - 0, - 14, - 62, - 16, - 52 - ], - [ - 2, - 36, - 20, - 29, - 19, - 52 - ], - [ - 57, - 13, - 40, - 22, - 60, - 6 - ], - [ - 37, - 44, - 58, - 8, - 5, - 50 - ], - [ - 24, - 5, - 43, - 62, - 23, - 59 - ], - [ - 13, - 19, - 47, - 39, - 61, - 15 - ], - [ - 58, - 33, - 9, - 7, - 4, - 28 - ], - [ - 1, - 35, - 10, - 19, - 31, - 20 - ], - [ - 15, - 55, - 63, - 18, - 34, - 38 - ], - [ - 27, - 15, - 38, - 30, - 57, - 42 - ], - [ - 62, - 41, - 16, - 29, - 6, - 46 - ], - [ - 46, - 33, - 45, - 10, - 34, - 23 - ], - [ - 4, - 47, - 51, - 1, - 16, - 41 - ], - [ - 11, - 21, - 18, - 7, - 48, - 28 - ], - [ - 18, - 51, - 7, - 50, - 6, - 32 - ], - [ - 9, - 11, - 36, - 55, - 43, - 48 - ] - ], - [ - [ - 47, - 8, - 36, - 61, - 21, - 45 - ], - [ - 46, - 2, - 15, - 32, - 0, - 51 - ], - [ - 24, - 15, - 33, - 61, - 2, - 43 - ], - [ - 60, - 22, - 31, - 27, - 14, - 11 - ], - [ - 59, - 58, - 39, - 57, - 46, - 3 - ], - [ - 57, - 43, - 2, - 31, - 7, - 62 - ], - [ - 9, - 42, - 54, - 19, - 4, - 55 - ], - [ - 46, - 14, - 7, - 24, - 43, - 35 - ], - [ - 47, - 4, - 0, - 37, - 12, - 13 - ], - [ - 54, - 38, - 4, - 47, - 25, - 6 - ], - [ - 47, - 14, - 15, - 24, - 1, - 61 - ], - [ - 16, - 54, - 5, - 0, - 7, - 63 - ], - [ - 49, - 3, - 33, - 11, - 13, - 10 - ], - [ - 18, - 13, - 10, - 39, - 58, - 63 - ], - [ - 58, - 62, - 10, - 33, - 5, - 26 - ], - [ - 48, - 5, - 63, - 53, - 43, - 2 - ], - [ - 24, - 47, - 51, - 15, - 53, - 0 - ], - [ - 44, - 0, - 6, - 3, - 34, - 7 - ], - [ - 58, - 38, - 53, - 61, - 0, - 54 - ], - [ - 55, - 9, - 15, - 27, - 42, - 34 - ], - [ - 43, - 1, - 51, - 41, - 4, - 5 - ], - [ - 49, - 20, - 15, - 6, - 37, - 46 - ], - [ - 11, - 25, - 52, - 5, - 4, - 39 - ], - [ - 38, - 40, - 44, - 51, - 10, - 14 - ], - [ - 13, - 8, - 52, - 63, - 2, - 23 - ], - [ - 23, - 38, - 59, - 57, - 55, - 41 - ], - [ - 23, - 6, - 62, - 0, - 7, - 28 - ] - ], - [ - [ - 41, - 2, - 42, - 16, - 50, - 61 - ], - [ - 51, - 41, - 5, - 15, - 61, - 63 - ], - [ - 43, - 1, - 29, - 21, - 55, - 60 - ], - [ - 24, - 53, - 25, - 13, - 51, - 32 - ], - [ - 31, - 41, - 57, - 49, - 34, - 11 - ], - [ - 17, - 4, - 35, - 30, - 10, - 38 - ], - [ - 34, - 7, - 56, - 42, - 19, - 21 - ], - [ - 14, - 46, - 7, - 27, - 25, - 52 - ], - [ - 0, - 4, - 6, - 12, - 60, - 47 - ], - [ - 54, - 25, - 4, - 38, - 47, - 6 - ], - [ - 24, - 61, - 15, - 46, - 7, - 22 - ], - [ - 5, - 16, - 57, - 0, - 22, - 55 - ], - [ - 49, - 3, - 26, - 17, - 57, - 52 - ], - [ - 13, - 10, - 61, - 60, - 0, - 58 - ], - [ - 58, - 6, - 49, - 10, - 5, - 2 - ], - [ - 25, - 19, - 2, - 11, - 54, - 53 - ], - [ - 47, - 30, - 27, - 18, - 5, - 10 - ], - [ - 44, - 55, - 63, - 13, - 22, - 31 - ], - [ - 58, - 7, - 38, - 35, - 32, - 40 - ], - [ - 13, - 34, - 16, - 49, - 45, - 55 - ], - [ - 51, - 35, - 30, - 58, - 55, - 4 - ], - [ - 46, - 57, - 15, - 36, - 30, - 27 - ], - [ - 52, - 33, - 23, - 51, - 10, - 15 - ], - [ - 40, - 41, - 4, - 51, - 10, - 31 - ], - [ - 8, - 4, - 59, - 48, - 34, - 9 - ], - [ - 43, - 3, - 27, - 26, - 19, - 31 - ], - [ - 46, - 18, - 8, - 4, - 50, - 40 - ] - ], - [ - [ - 22, - 36, - 35, - 63, - 43, - 23 - ], - [ - 54, - 30, - 4, - 36, - 35, - 55 - ], - [ - 28, - 19, - 23, - 49, - 50, - 59 - ], - [ - 62, - 5, - 50, - 53, - 42, - 48 - ], - [ - 0, - 3, - 61, - 57, - 41, - 49 - ], - [ - 60, - 4, - 29, - 16, - 53, - 30 - ], - [ - 34, - 32, - 33, - 9, - 56, - 35 - ], - [ - 12, - 53, - 14, - 36, - 25, - 61 - ], - [ - 23, - 48, - 35, - 29, - 4, - 16 - ], - [ - 22, - 25, - 4, - 54, - 62, - 5 - ], - [ - 24, - 19, - 27, - 55, - 47, - 25 - ], - [ - 18, - 5, - 22, - 34, - 63, - 30 - ], - [ - 17, - 49, - 30, - 28, - 11, - 42 - ], - [ - 29, - 13, - 54, - 25, - 45, - 47 - ], - [ - 27, - 2, - 10, - 5, - 54, - 33 - ], - [ - 55, - 2, - 57, - 54, - 56, - 22 - ], - [ - 53, - 3, - 60, - 27, - 5, - 50 - ], - [ - 17, - 8, - 47, - 50, - 61, - 44 - ], - [ - 27, - 38, - 32, - 14, - 61, - 39 - ], - [ - 22, - 43, - 32, - 57, - 39, - 34 - ], - [ - 16, - 44, - 37, - 23, - 61, - 27 - ], - [ - 45, - 40, - 55, - 32, - 31, - 3 - ], - [ - 32, - 28, - 41, - 15, - 1, - 52 - ], - [ - 21, - 22, - 31, - 10, - 4, - 40 - ], - [ - 29, - 35, - 62, - 60, - 41, - 1 - ], - [ - 39, - 58, - 1, - 63, - 3, - 35 - ], - [ - 10, - 62, - 31, - 45, - 27, - 7 - ] - ], - [ - [ - 17, - 5, - 10, - 57, - 14, - 27 - ], - [ - 43, - 9, - 33, - 56, - 1, - 20 - ], - [ - 63, - 1, - 35, - 43, - 27, - 10 - ], - [ - 47, - 6, - 18, - 3, - 38, - 15 - ], - [ - 11, - 51, - 61, - 34, - 44, - 55 - ], - [ - 10, - 63, - 53, - 60, - 37, - 58 - ], - [ - 51, - 45, - 63, - 34, - 18, - 60 - ], - [ - 35, - 51, - 52, - 53, - 38, - 45 - ], - [ - 44, - 0, - 35, - 55, - 38, - 9 - ], - [ - 42, - 43, - 5, - 25, - 21, - 6 - ], - [ - 42, - 13, - 25, - 27, - 38, - 32 - ], - [ - 43, - 5, - 1, - 38, - 22, - 2 - ], - [ - 17, - 49, - 0, - 37, - 28, - 30 - ], - [ - 2, - 29, - 54, - 5, - 13, - 60 - ], - [ - 2, - 27, - 10, - 58, - 40, - 28 - ], - [ - 2, - 49, - 54, - 62, - 53, - 57 - ], - [ - 60, - 53, - 12, - 27, - 28, - 0 - ], - [ - 17, - 50, - 51, - 33, - 3, - 11 - ], - [ - 38, - 5, - 19, - 13, - 27, - 32 - ], - [ - 43, - 22, - 0, - 57, - 40, - 63 - ], - [ - 27, - 16, - 35, - 52, - 38, - 39 - ], - [ - 29, - 31, - 55, - 40, - 62, - 27 - ], - [ - 41, - 48, - 32, - 46, - 40, - 57 - ], - [ - 20, - 54, - 4, - 22, - 0, - 55 - ], - [ - 62, - 23, - 25, - 33, - 28, - 20 - ], - [ - 7, - 18, - 60, - 22, - 58, - 3 - ], - [ - 36, - 9, - 11, - 0, - 48, - 31 - ] - ], - [ - [ - 11, - 59, - 52, - 28, - 6, - 45 - ], - [ - 7, - 23, - 15, - 17, - 55, - 61 - ], - [ - 28, - 0, - 29, - 46, - 58, - 14 - ], - [ - 62, - 49, - 39, - 18, - 6, - 13 - ], - [ - 36, - 61, - 7, - 40, - 35, - 33 - ], - [ - 8, - 16, - 29, - 43, - 57, - 37 - ], - [ - 30, - 19, - 21, - 57, - 42, - 14 - ], - [ - 35, - 11, - 51, - 53, - 36, - 33 - ], - [ - 48, - 35, - 4, - 50, - 11, - 40 - ], - [ - 5, - 3, - 21, - 47, - 43, - 38 - ], - [ - 21, - 18, - 2, - 6, - 3, - 10 - ], - [ - 18, - 43, - 5, - 45, - 22, - 4 - ], - [ - 33, - 28, - 37, - 41, - 49, - 10 - ], - [ - 56, - 55, - 22, - 54, - 62, - 15 - ], - [ - 2, - 27, - 57, - 5, - 63, - 19 - ], - [ - 55, - 8, - 54, - 2, - 59, - 48 - ], - [ - 0, - 53, - 51, - 3, - 50, - 52 - ], - [ - 21, - 12, - 62, - 60, - 18, - 43 - ], - [ - 38, - 13, - 53, - 27, - 14, - 28 - ], - [ - 22, - 56, - 44, - 43, - 51, - 53 - ], - [ - 29, - 43, - 12, - 16, - 41, - 52 - ], - [ - 51, - 1, - 35, - 44, - 48, - 34 - ], - [ - 32, - 25, - 4, - 41, - 53, - 54 - ], - [ - 48, - 17, - 25, - 60, - 1, - 62 - ], - [ - 22, - 29, - 5, - 18, - 53, - 20 - ], - [ - 28, - 55, - 15, - 1, - 49, - 8 - ], - [ - 30, - 57, - 6, - 7, - 31, - 50 - ] - ], - [ - [ - 11, - 16, - 31, - 46, - 35, - 49 - ], - [ - 13, - 49, - 31, - 16, - 34, - 19 - ], - [ - 36, - 13, - 27, - 34, - 42, - 18 - ], - [ - 24, - 7, - 61, - 63, - 34, - 15 - ], - [ - 17, - 35, - 2, - 48, - 44, - 62 - ], - [ - 8, - 7, - 23, - 47, - 51, - 26 - ], - [ - 61, - 58, - 29, - 38, - 33, - 45 - ], - [ - 20, - 9, - 42, - 27, - 54, - 29 - ], - [ - 25, - 47, - 34, - 1, - 4, - 38 - ], - [ - 54, - 27, - 57, - 4, - 28, - 25 - ], - [ - 1, - 30, - 59, - 60, - 17, - 22 - ], - [ - 31, - 25, - 51, - 5, - 58, - 49 - ], - [ - 34, - 2, - 19, - 14, - 29, - 36 - ], - [ - 57, - 40, - 6, - 9, - 32, - 14 - ], - [ - 8, - 44, - 34, - 16, - 45, - 47 - ], - [ - 8, - 25, - 40, - 37, - 14, - 59 - ], - [ - 13, - 15, - 19, - 51, - 25, - 37 - ], - [ - 23, - 4, - 44, - 52, - 19, - 15 - ], - [ - 8, - 10, - 45, - 1, - 31, - 53 - ], - [ - 38, - 59, - 15, - 2, - 34, - 0 - ], - [ - 41, - 25, - 57, - 55, - 27, - 24 - ], - [ - 11, - 44, - 41, - 39, - 62, - 53 - ], - [ - 31, - 30, - 42, - 37, - 34, - 59 - ], - [ - 33, - 14, - 12, - 10, - 54, - 51 - ], - [ - 11, - 18, - 45, - 20, - 33, - 48 - ], - [ - 50, - 51, - 54, - 23, - 10, - 2 - ], - [ - 17, - 31, - 37, - 5, - 19, - 36 - ] - ], - [ - [ - 31, - 35, - 51, - 18, - 53, - 61 - ], - [ - 7, - 40, - 39, - 41, - 31, - 37 - ], - [ - 16, - 29, - 26, - 50, - 33, - 10 - ], - [ - 14, - 22, - 37, - 17, - 6, - 25 - ], - [ - 60, - 20, - 46, - 4, - 3, - 57 - ], - [ - 11, - 2, - 49, - 21, - 27, - 9 - ], - [ - 42, - 30, - 54, - 58, - 19, - 56 - ], - [ - 46, - 33, - 38, - 41, - 35, - 49 - ], - [ - 50, - 48, - 57, - 37, - 38, - 10 - ], - [ - 59, - 63, - 47, - 28, - 10, - 35 - ], - [ - 28, - 3, - 2, - 52, - 33, - 43 - ], - [ - 58, - 19, - 17, - 16, - 57, - 63 - ], - [ - 42, - 4, - 3, - 23, - 45, - 57 - ], - [ - 18, - 42, - 51, - 3, - 20, - 15 - ], - [ - 32, - 43, - 12, - 3, - 0, - 35 - ], - [ - 15, - 45, - 4, - 7, - 53, - 21 - ], - [ - 28, - 24, - 14, - 60, - 15, - 31 - ], - [ - 26, - 30, - 0, - 54, - 5, - 36 - ], - [ - 63, - 37, - 11, - 41, - 51, - 4 - ], - [ - 48, - 9, - 33, - 0, - 54, - 42 - ], - [ - 22, - 60, - 11, - 39, - 1, - 49 - ], - [ - 17, - 4, - 34, - 2, - 27, - 53 - ], - [ - 17, - 6, - 40, - 58, - 42, - 39 - ], - [ - 60, - 54, - 35, - 39, - 0, - 32 - ], - [ - 44, - 15, - 37, - 13, - 8, - 25 - ], - [ - 41, - 59, - 25, - 45, - 13, - 6 - ], - [ - 23, - 58, - 13, - 19, - 62, - 29 - ] - ], - [ - [ - 52, - 47, - 27, - 36, - 38, - 33 - ], - [ - 43, - 56, - 4, - 21, - 25, - 6 - ], - [ - 25, - 54, - 35, - 18, - 11, - 57 - ], - [ - 17, - 16, - 1, - 6, - 33, - 45 - ], - [ - 32, - 36, - 0, - 60, - 46, - 57 - ], - [ - 11, - 1, - 3, - 21, - 2, - 34 - ], - [ - 30, - 42, - 58, - 54, - 19, - 13 - ], - [ - 33, - 35, - 56, - 42, - 38, - 54 - ], - [ - 50, - 37, - 36, - 14, - 11, - 20 - ], - [ - 10, - 28, - 32, - 4, - 37, - 6 - ], - [ - 3, - 18, - 28, - 61, - 44, - 2 - ], - [ - 16, - 19, - 56, - 27, - 46, - 43 - ], - [ - 42, - 33, - 57, - 3, - 58, - 26 - ], - [ - 56, - 15, - 51, - 55, - 50, - 13 - ], - [ - 12, - 43, - 40, - 13, - 16, - 29 - ], - [ - 15, - 7, - 28, - 53, - 5, - 20 - ], - [ - 14, - 58, - 24, - 60, - 31, - 51 - ], - [ - 0, - 60, - 3, - 44, - 24, - 19 - ], - [ - 36, - 28, - 11, - 63, - 53, - 15 - ], - [ - 2, - 26, - 9, - 34, - 0, - 3 - ], - [ - 49, - 28, - 43, - 41, - 30, - 11 - ], - [ - 25, - 51, - 12, - 6, - 61, - 16 - ], - [ - 39, - 17, - 41, - 50, - 40, - 21 - ], - [ - 25, - 58, - 48, - 12, - 60, - 33 - ], - [ - 31, - 49, - 5, - 52, - 63, - 3 - ], - [ - 15, - 55, - 38, - 47, - 1, - 49 - ], - [ - 23, - 6, - 32, - 19, - 62, - 7 - ] - ], - [ - [ - 44, - 24, - 56, - 33, - 15, - 7 - ], - [ - 38, - 26, - 24, - 29, - 53, - 19 - ], - [ - 12, - 15, - 29, - 9, - 1, - 63 - ], - [ - 38, - 61, - 58, - 50, - 45, - 6 - ], - [ - 24, - 34, - 4, - 36, - 57, - 31 - ], - [ - 1, - 22, - 43, - 21, - 10, - 7 - ], - [ - 20, - 19, - 54, - 58, - 18, - 42 - ], - [ - 56, - 33, - 14, - 21, - 51, - 18 - ], - [ - 60, - 50, - 14, - 36, - 4, - 43 - ], - [ - 6, - 10, - 9, - 63, - 4, - 38 - ], - [ - 54, - 39, - 46, - 18, - 3, - 2 - ], - [ - 30, - 16, - 37, - 53, - 56, - 43 - ], - [ - 56, - 10, - 42, - 58, - 57, - 23 - ], - [ - 30, - 56, - 13, - 51, - 50, - 0 - ], - [ - 55, - 40, - 12, - 62, - 13, - 30 - ], - [ - 53, - 28, - 11, - 61, - 7, - 19 - ], - [ - 55, - 14, - 17, - 47, - 30, - 5 - ], - [ - 18, - 31, - 20, - 60, - 57, - 32 - ], - [ - 43, - 12, - 53, - 26, - 32, - 61 - ], - [ - 25, - 16, - 26, - 61, - 3, - 53 - ], - [ - 49, - 28, - 51, - 59, - 55, - 11 - ], - [ - 56, - 6, - 32, - 14, - 10, - 21 - ], - [ - 2, - 15, - 58, - 17, - 13, - 62 - ], - [ - 53, - 51, - 4, - 29, - 50, - 25 - ], - [ - 61, - 31, - 15, - 19, - 60, - 49 - ], - [ - 46, - 44, - 19, - 36, - 8, - 40 - ], - [ - 8, - 29, - 46, - 7, - 53, - 20 - ] - ], - [ - [ - 48, - 42, - 38, - 63, - 50, - 62 - ], - [ - 3, - 2, - 40, - 33, - 14, - 61 - ], - [ - 39, - 7, - 45, - 40, - 6, - 44 - ], - [ - 41, - 5, - 20, - 56, - 13, - 0 - ], - [ - 6, - 37, - 30, - 1, - 38, - 52 - ], - [ - 59, - 46, - 4, - 22, - 5, - 6 - ], - [ - 20, - 1, - 44, - 35, - 13, - 3 - ], - [ - 12, - 56, - 8, - 50, - 31, - 2 - ], - [ - 33, - 60, - 41, - 43, - 37, - 52 - ], - [ - 9, - 10, - 0, - 55, - 40, - 37 - ], - [ - 54, - 39, - 9, - 8, - 61, - 46 - ], - [ - 30, - 56, - 53, - 24, - 16, - 59 - ], - [ - 7, - 58, - 57, - 42, - 52, - 10 - ], - [ - 27, - 30, - 42, - 25, - 59, - 13 - ], - [ - 9, - 11, - 49, - 55, - 61, - 0 - ], - [ - 34, - 53, - 54, - 57, - 29, - 37 - ], - [ - 55, - 17, - 5, - 60, - 31, - 16 - ], - [ - 57, - 48, - 43, - 39, - 32, - 36 - ], - [ - 12, - 43, - 42, - 49, - 7, - 61 - ], - [ - 23, - 36, - 1, - 0, - 16, - 61 - ], - [ - 21, - 35, - 11, - 31, - 55, - 7 - ], - [ - 60, - 8, - 24, - 42, - 6, - 14 - ], - [ - 15, - 51, - 58, - 2, - 33, - 30 - ], - [ - 52, - 51, - 4, - 28, - 21, - 19 - ], - [ - 19, - 60, - 15, - 34, - 54, - 61 - ], - [ - 19, - 61, - 58, - 40, - 12, - 3 - ], - [ - 35, - 49, - 54, - 53, - 1, - 25 - ] - ], - [ - [ - 47, - 37, - 59, - 38, - 33, - 10 - ], - [ - 36, - 7, - 21, - 51, - 8, - 47 - ], - [ - 20, - 32, - 44, - 47, - 4, - 54 - ], - [ - 43, - 20, - 42, - 52, - 8, - 19 - ], - [ - 27, - 9, - 39, - 57, - 12, - 54 - ], - [ - 28, - 16, - 29, - 11, - 61, - 58 - ], - [ - 42, - 2, - 30, - 43, - 28, - 25 - ], - [ - 36, - 18, - 32, - 21, - 53, - 15 - ], - [ - 50, - 12, - 37, - 48, - 14, - 52 - ], - [ - 9, - 10, - 13, - 3, - 58, - 26 - ], - [ - 18, - 3, - 9, - 55, - 6, - 61 - ], - [ - 30, - 18, - 45, - 60, - 16, - 24 - ], - [ - 35, - 5, - 42, - 26, - 37, - 58 - ], - [ - 55, - 38, - 15, - 13, - 14, - 54 - ], - [ - 9, - 62, - 2, - 17, - 13, - 26 - ], - [ - 9, - 40, - 37, - 54, - 17, - 47 - ], - [ - 22, - 60, - 51, - 14, - 40, - 39 - ], - [ - 17, - 3, - 21, - 30, - 36, - 25 - ], - [ - 56, - 41, - 24, - 9, - 43, - 14 - ], - [ - 53, - 34, - 26, - 22, - 12, - 35 - ], - [ - 36, - 42, - 24, - 37, - 8, - 4 - ], - [ - 34, - 16, - 8, - 46, - 56, - 6 - ], - [ - 31, - 10, - 16, - 41, - 56, - 32 - ], - [ - 48, - 0, - 42, - 56, - 31, - 30 - ], - [ - 6, - 54, - 26, - 19, - 8, - 7 - ], - [ - 49, - 22, - 6, - 13, - 24, - 59 - ], - [ - 45, - 62, - 27, - 47, - 50, - 7 - ] - ], - [ - [ - 45, - 37, - 48, - 29, - 30, - 3 - ], - [ - 8, - 60, - 59, - 43, - 10, - 48 - ], - [ - 51, - 45, - 28, - 34, - 59, - 63 - ], - [ - 43, - 2, - 38, - 12, - 20, - 4 - ], - [ - 50, - 57, - 39, - 31, - 0, - 63 - ], - [ - 58, - 53, - 18, - 9, - 30, - 21 - ], - [ - 63, - 51, - 34, - 7, - 20, - 27 - ], - [ - 32, - 21, - 46, - 47, - 25, - 18 - ], - [ - 6, - 12, - 50, - 14, - 33, - 37 - ], - [ - 30, - 10, - 9, - 6, - 13, - 29 - ], - [ - 61, - 18, - 23, - 20, - 44, - 6 - ], - [ - 16, - 12, - 30, - 60, - 0, - 62 - ], - [ - 5, - 26, - 57, - 35, - 37, - 61 - ], - [ - 46, - 55, - 13, - 1, - 17, - 31 - ], - [ - 6, - 10, - 2, - 22, - 16, - 15 - ], - [ - 6, - 54, - 19, - 57, - 25, - 46 - ], - [ - 2, - 30, - 60, - 61, - 18, - 49 - ], - [ - 17, - 4, - 2, - 27, - 3, - 44 - ], - [ - 56, - 46, - 8, - 32, - 6, - 14 - ], - [ - 5, - 13, - 0, - 34, - 14, - 30 - ], - [ - 15, - 23, - 59, - 57, - 27, - 53 - ], - [ - 36, - 61, - 8, - 43, - 57, - 37 - ], - [ - 10, - 15, - 52, - 31, - 29, - 23 - ], - [ - 10, - 38, - 1, - 4, - 57, - 31 - ], - [ - 8, - 32, - 54, - 33, - 3, - 50 - ], - [ - 6, - 33, - 19, - 50, - 2, - 3 - ], - [ - 55, - 43, - 4, - 5, - 25, - 8 - ] - ], - [ - [ - 25, - 14, - 18, - 49, - 51, - 63 - ], - [ - 42, - 21, - 30, - 24, - 43, - 7 - ], - [ - 54, - 39, - 9, - 59, - 28, - 49 - ], - [ - 23, - 1, - 55, - 45, - 43, - 40 - ], - [ - 37, - 30, - 41, - 59, - 21, - 44 - ], - [ - 4, - 41, - 31, - 35, - 19, - 14 - ], - [ - 28, - 55, - 44, - 63, - 9, - 51 - ], - [ - 63, - 12, - 32, - 13, - 47, - 28 - ], - [ - 33, - 12, - 59, - 35, - 6, - 39 - ], - [ - 30, - 40, - 10, - 29, - 52, - 13 - ], - [ - 63, - 19, - 23, - 61, - 8, - 55 - ], - [ - 45, - 62, - 22, - 12, - 38, - 42 - ], - [ - 58, - 26, - 57, - 20, - 45, - 30 - ], - [ - 8, - 59, - 47, - 17, - 25, - 22 - ], - [ - 24, - 54, - 51, - 55, - 10, - 15 - ], - [ - 46, - 22, - 54, - 44, - 57, - 40 - ], - [ - 34, - 60, - 2, - 17, - 27, - 18 - ], - [ - 51, - 6, - 61, - 2, - 39, - 32 - ], - [ - 5, - 49, - 38, - 8, - 32, - 46 - ], - [ - 57, - 4, - 23, - 14, - 59, - 5 - ], - [ - 7, - 36, - 45, - 21, - 53, - 4 - ], - [ - 60, - 3, - 39, - 8, - 14, - 35 - ], - [ - 33, - 57, - 60, - 47, - 15, - 53 - ], - [ - 20, - 11, - 22, - 19, - 58, - 4 - ], - [ - 36, - 34, - 47, - 41, - 60, - 1 - ], - [ - 33, - 3, - 49, - 29, - 59, - 14 - ], - [ - 52, - 60, - 24, - 25, - 35, - 34 - ] - ], - [ - [ - 23, - 54, - 53, - 11, - 58, - 8 - ], - [ - 11, - 30, - 15, - 63, - 59, - 49 - ], - [ - 20, - 58, - 29, - 17, - 52, - 30 - ], - [ - 18, - 1, - 43, - 15, - 3, - 8 - ], - [ - 59, - 55, - 13, - 20, - 44, - 30 - ], - [ - 33, - 45, - 27, - 53, - 63, - 52 - ], - [ - 28, - 57, - 51, - 34, - 53, - 31 - ], - [ - 13, - 63, - 3, - 32, - 44, - 45 - ], - [ - 59, - 33, - 12, - 57, - 6, - 35 - ], - [ - 23, - 30, - 42, - 10, - 29, - 13 - ], - [ - 63, - 23, - 18, - 42, - 38, - 19 - ], - [ - 62, - 2, - 22, - 19, - 45, - 23 - ], - [ - 6, - 20, - 26, - 30, - 5, - 25 - ], - [ - 3, - 6, - 8, - 31, - 17, - 37 - ], - [ - 59, - 2, - 10, - 54, - 55, - 19 - ], - [ - 45, - 7, - 57, - 42, - 54, - 46 - ], - [ - 60, - 55, - 53, - 34, - 41, - 18 - ], - [ - 36, - 2, - 8, - 31, - 32, - 46 - ], - [ - 36, - 45, - 38, - 32, - 61, - 25 - ], - [ - 57, - 39, - 6, - 18, - 19, - 30 - ], - [ - 37, - 39, - 59, - 48, - 53, - 14 - ], - [ - 52, - 28, - 46, - 55, - 47, - 61 - ], - [ - 1, - 28, - 52, - 15, - 31, - 18 - ], - [ - 22, - 21, - 8, - 10, - 46, - 39 - ], - [ - 45, - 41, - 35, - 9, - 54, - 33 - ], - [ - 42, - 39, - 58, - 61, - 24, - 3 - ], - [ - 41, - 46, - 10, - 3, - 15, - 33 - ] - ], - [ - [ - 48, - 38, - 63, - 42, - 47, - 7 - ], - [ - 3, - 10, - 26, - 2, - 6, - 62 - ], - [ - 39, - 7, - 44, - 6, - 45, - 40 - ], - [ - 21, - 39, - 1, - 18, - 15, - 57 - ], - [ - 6, - 33, - 9, - 13, - 3, - 27 - ], - [ - 59, - 33, - 53, - 62, - 21, - 45 - ], - [ - 28, - 10, - 33, - 7, - 57, - 50 - ], - [ - 50, - 13, - 12, - 49, - 3, - 55 - ], - [ - 33, - 59, - 26, - 35, - 48, - 38 - ], - [ - 23, - 30, - 20, - 43, - 10, - 58 - ], - [ - 63, - 23, - 18, - 17, - 38, - 9 - ], - [ - 22, - 62, - 36, - 33, - 6, - 2 - ], - [ - 12, - 17, - 59, - 7, - 26, - 49 - ], - [ - 47, - 3, - 27, - 6, - 24, - 22 - ], - [ - 59, - 57, - 46, - 54, - 2, - 10 - ], - [ - 27, - 45, - 57, - 54, - 34, - 61 - ], - [ - 34, - 60, - 55, - 31, - 58, - 43 - ], - [ - 48, - 42, - 32, - 39, - 2, - 38 - ], - [ - 42, - 49, - 32, - 44, - 12, - 61 - ], - [ - 57, - 36, - 39, - 1, - 30, - 59 - ], - [ - 48, - 7, - 21, - 53, - 17, - 29 - ], - [ - 8, - 60, - 58, - 35, - 46, - 14 - ], - [ - 51, - 15, - 28, - 47, - 33, - 30 - ], - [ - 11, - 19, - 4, - 8, - 58, - 52 - ], - [ - 24, - 5, - 41, - 60, - 40, - 54 - ], - [ - 61, - 3, - 21, - 58, - 19, - 8 - ], - [ - 60, - 35, - 54, - 49, - 1, - 0 - ] - ], - [ - [ - 6, - 24, - 63, - 25, - 26, - 45 - ], - [ - 47, - 13, - 49, - 44, - 20, - 19 - ], - [ - 23, - 32, - 49, - 20, - 24, - 2 - ], - [ - 43, - 21, - 8, - 40, - 39, - 45 - ], - [ - 39, - 29, - 3, - 5, - 41, - 10 - ], - [ - 61, - 33, - 48, - 40, - 29, - 62 - ], - [ - 29, - 28, - 25, - 33, - 44, - 31 - ], - [ - 50, - 33, - 13, - 11, - 30, - 54 - ], - [ - 48, - 26, - 35, - 17, - 55, - 6 - ], - [ - 43, - 23, - 12, - 9, - 25, - 62 - ], - [ - 63, - 35, - 18, - 53, - 38, - 9 - ], - [ - 19, - 48, - 5, - 36, - 59, - 39 - ], - [ - 7, - 26, - 59, - 17, - 12, - 30 - ], - [ - 47, - 24, - 22, - 62, - 42, - 14 - ], - [ - 59, - 46, - 39, - 35, - 57, - 2 - ], - [ - 27, - 9, - 20, - 0, - 57, - 52 - ], - [ - 38, - 58, - 60, - 34, - 43, - 29 - ], - [ - 42, - 32, - 39, - 54, - 38, - 57 - ], - [ - 59, - 32, - 42, - 6, - 21, - 18 - ], - [ - 24, - 36, - 57, - 4, - 30, - 60 - ], - [ - 9, - 48, - 7, - 53, - 21, - 13 - ], - [ - 49, - 31, - 14, - 8, - 19, - 52 - ], - [ - 15, - 33, - 44, - 8, - 3, - 14 - ], - [ - 11, - 4, - 21, - 28, - 41, - 23 - ], - [ - 37, - 27, - 24, - 12, - 9, - 42 - ], - [ - 58, - 9, - 19, - 3, - 12, - 48 - ], - [ - 24, - 14, - 60, - 47, - 25, - 35 - ] - ], - [ - [ - 26, - 62, - 58, - 18, - 38, - 5 - ], - [ - 19, - 12, - 40, - 39, - 31, - 57 - ], - [ - 27, - 38, - 9, - 22, - 23, - 61 - ], - [ - 42, - 20, - 63, - 39, - 45, - 43 - ], - [ - 7, - 36, - 60, - 29, - 57, - 24 - ], - [ - 61, - 34, - 3, - 1, - 44, - 51 - ], - [ - 29, - 42, - 25, - 47, - 30, - 17 - ], - [ - 33, - 38, - 39, - 50, - 62, - 0 - ], - [ - 17, - 26, - 13, - 50, - 41, - 11 - ], - [ - 12, - 43, - 35, - 16, - 55, - 60 - ], - [ - 3, - 18, - 39, - 63, - 35, - 62 - ], - [ - 19, - 48, - 39, - 60, - 54, - 2 - ], - [ - 7, - 42, - 56, - 59, - 37, - 24 - ], - [ - 56, - 47, - 54, - 59, - 3, - 6 - ], - [ - 35, - 39, - 43, - 59, - 2, - 13 - ], - [ - 20, - 27, - 9, - 56, - 0, - 54 - ], - [ - 17, - 60, - 29, - 18, - 58, - 5 - ], - [ - 57, - 42, - 45, - 0, - 24, - 29 - ], - [ - 49, - 28, - 52, - 32, - 50, - 42 - ], - [ - 24, - 2, - 36, - 30, - 46, - 59 - ], - [ - 11, - 7, - 48, - 54, - 53, - 21 - ], - [ - 33, - 14, - 42, - 6, - 8, - 5 - ], - [ - 15, - 36, - 33, - 17, - 51, - 13 - ], - [ - 41, - 2, - 4, - 11, - 8, - 29 - ], - [ - 30, - 15, - 60, - 5, - 46, - 9 - ], - [ - 21, - 3, - 14, - 19, - 61, - 58 - ], - [ - 24, - 60, - 1, - 14, - 35, - 53 - ] - ], - [ - [ - 8, - 56, - 54, - 4, - 37, - 38 - ], - [ - 36, - 9, - 24, - 8, - 1, - 2 - ], - [ - 16, - 57, - 29, - 32, - 58, - 30 - ], - [ - 32, - 63, - 42, - 52, - 4, - 20 - ], - [ - 27, - 24, - 44, - 39, - 63, - 20 - ], - [ - 24, - 11, - 28, - 6, - 15, - 31 - ], - [ - 30, - 41, - 42, - 25, - 52, - 2 - ], - [ - 26, - 36, - 62, - 18, - 50, - 21 - ], - [ - 48, - 50, - 32, - 51, - 27, - 26 - ], - [ - 13, - 43, - 3, - 26, - 12, - 41 - ], - [ - 3, - 32, - 18, - 53, - 39, - 17 - ], - [ - 21, - 19, - 9, - 36, - 48, - 1 - ], - [ - 35, - 59, - 42, - 54, - 63, - 17 - ], - [ - 15, - 12, - 38, - 9, - 51, - 54 - ], - [ - 3, - 2, - 48, - 59, - 57, - 13 - ], - [ - 27, - 9, - 4, - 20, - 22, - 3 - ], - [ - 60, - 40, - 22, - 27, - 46, - 32 - ], - [ - 30, - 25, - 21, - 17, - 0, - 42 - ], - [ - 33, - 27, - 34, - 28, - 38, - 44 - ], - [ - 53, - 32, - 33, - 35, - 31, - 56 - ], - [ - 40, - 11, - 20, - 47, - 48, - 24 - ], - [ - 45, - 62, - 33, - 53, - 17, - 34 - ], - [ - 41, - 9, - 39, - 25, - 17, - 32 - ], - [ - 44, - 30, - 2, - 31, - 0, - 47 - ], - [ - 6, - 62, - 37, - 52, - 55, - 33 - ], - [ - 10, - 24, - 57, - 9, - 49, - 13 - ], - [ - 45, - 24, - 27, - 47, - 19, - 26 - ] - ], - [ - [ - 4, - 16, - 59, - 44, - 13, - 56 - ], - [ - 16, - 23, - 9, - 59, - 13, - 50 - ], - [ - 11, - 35, - 21, - 7, - 9, - 59 - ], - [ - 44, - 1, - 25, - 26, - 15, - 20 - ], - [ - 35, - 57, - 52, - 31, - 24, - 5 - ], - [ - 35, - 15, - 38, - 53, - 12, - 0 - ], - [ - 36, - 20, - 63, - 28, - 60, - 33 - ], - [ - 49, - 50, - 12, - 13, - 8, - 16 - ], - [ - 42, - 48, - 6, - 16, - 35, - 41 - ], - [ - 23, - 9, - 54, - 34, - 30, - 13 - ], - [ - 23, - 63, - 51, - 53, - 55, - 3 - ], - [ - 33, - 36, - 62, - 19, - 59, - 57 - ], - [ - 59, - 38, - 12, - 32, - 17, - 53 - ], - [ - 48, - 0, - 19, - 24, - 61, - 22 - ], - [ - 55, - 6, - 39, - 7, - 60, - 18 - ], - [ - 27, - 57, - 19, - 1, - 60, - 47 - ], - [ - 30, - 40, - 10, - 17, - 36, - 60 - ], - [ - 20, - 13, - 1, - 31, - 17, - 43 - ], - [ - 50, - 32, - 23, - 7, - 33, - 30 - ], - [ - 14, - 19, - 23, - 13, - 10, - 7 - ], - [ - 53, - 23, - 19, - 13, - 7, - 50 - ], - [ - 30, - 18, - 46, - 14, - 1, - 15 - ], - [ - 15, - 18, - 52, - 0, - 51, - 27 - ], - [ - 4, - 21, - 41, - 53, - 10, - 14 - ], - [ - 4, - 9, - 39, - 8, - 16, - 54 - ], - [ - 3, - 19, - 16, - 47, - 30, - 27 - ], - [ - 33, - 8, - 46, - 40, - 29, - 14 - ] - ], - [ - [ - 6, - 26, - 3, - 24, - 11, - 38 - ], - [ - 49, - 16, - 19, - 57, - 0, - 18 - ], - [ - 16, - 60, - 57, - 0, - 22, - 30 - ], - [ - 35, - 46, - 49, - 44, - 26, - 17 - ], - [ - 25, - 54, - 9, - 43, - 45, - 27 - ], - [ - 55, - 33, - 30, - 6, - 28, - 57 - ], - [ - 55, - 31, - 49, - 52, - 15, - 25 - ], - [ - 11, - 36, - 15, - 29, - 30, - 62 - ], - [ - 55, - 42, - 48, - 54, - 46, - 6 - ], - [ - 10, - 3, - 43, - 21, - 62, - 54 - ], - [ - 32, - 56, - 6, - 3, - 18, - 55 - ], - [ - 50, - 21, - 36, - 19, - 4, - 42 - ], - [ - 38, - 35, - 53, - 39, - 41, - 32 - ], - [ - 28, - 24, - 38, - 41, - 15, - 12 - ], - [ - 2, - 40, - 18, - 60, - 55, - 13 - ], - [ - 27, - 22, - 57, - 8, - 54, - 37 - ], - [ - 40, - 27, - 60, - 46, - 44, - 50 - ], - [ - 17, - 21, - 30, - 12, - 29, - 26 - ], - [ - 33, - 38, - 23, - 2, - 13, - 27 - ], - [ - 32, - 56, - 9, - 44, - 31, - 60 - ], - [ - 53, - 12, - 22, - 40, - 41, - 5 - ], - [ - 44, - 45, - 49, - 17, - 14, - 56 - ], - [ - 25, - 39, - 41, - 4, - 9, - 53 - ], - [ - 44, - 1, - 17, - 0, - 13, - 58 - ], - [ - 62, - 52, - 37, - 54, - 42, - 36 - ], - [ - 10, - 9, - 28, - 55, - 2, - 24 - ], - [ - 57, - 30, - 27, - 45, - 47, - 16 - ] - ], - [ - [ - 16, - 11, - 31, - 46, - 0, - 35 - ], - [ - 13, - 49, - 34, - 31, - 16, - 8 - ], - [ - 36, - 13, - 27, - 58, - 18, - 34 - ], - [ - 24, - 61, - 15, - 46, - 63, - 25 - ], - [ - 17, - 2, - 50, - 35, - 58, - 44 - ], - [ - 8, - 23, - 7, - 49, - 26, - 47 - ], - [ - 61, - 38, - 29, - 58, - 0, - 36 - ], - [ - 42, - 20, - 9, - 4, - 11, - 27 - ], - [ - 1, - 34, - 30, - 48, - 26, - 7 - ], - [ - 27, - 10, - 28, - 62, - 13, - 54 - ], - [ - 60, - 1, - 30, - 59, - 6, - 52 - ], - [ - 31, - 62, - 25, - 42, - 32, - 21 - ], - [ - 34, - 32, - 19, - 2, - 62, - 14 - ], - [ - 6, - 40, - 24, - 31, - 13, - 57 - ], - [ - 44, - 2, - 40, - 8, - 34, - 45 - ], - [ - 8, - 27, - 7, - 57, - 50, - 47 - ], - [ - 13, - 60, - 45, - 22, - 52, - 25 - ], - [ - 4, - 23, - 33, - 46, - 58, - 34 - ], - [ - 8, - 33, - 56, - 45, - 51, - 59 - ], - [ - 38, - 53, - 39, - 26, - 35, - 31 - ], - [ - 41, - 46, - 53, - 11, - 59, - 8 - ], - [ - 11, - 44, - 33, - 55, - 52, - 53 - ], - [ - 31, - 1, - 16, - 9, - 15, - 41 - ], - [ - 2, - 31, - 14, - 10, - 44, - 7 - ], - [ - 45, - 6, - 62, - 12, - 24, - 18 - ], - [ - 10, - 50, - 42, - 54, - 24, - 3 - ], - [ - 17, - 31, - 37, - 5, - 10, - 41 - ] - ], - [ - [ - 45, - 37, - 48, - 29, - 30, - 59 - ], - [ - 8, - 59, - 10, - 60, - 43, - 55 - ], - [ - 45, - 51, - 28, - 59, - 34, - 31 - ], - [ - 14, - 31, - 22, - 2, - 19, - 44 - ], - [ - 59, - 50, - 4, - 31, - 9, - 44 - ], - [ - 58, - 23, - 47, - 9, - 53, - 30 - ], - [ - 54, - 13, - 51, - 22, - 29, - 61 - ], - [ - 47, - 42, - 32, - 20, - 24, - 37 - ], - [ - 33, - 12, - 47, - 10, - 30, - 44 - ], - [ - 30, - 10, - 27, - 28, - 33, - 13 - ], - [ - 61, - 23, - 14, - 20, - 1, - 29 - ], - [ - 12, - 14, - 32, - 62, - 31, - 52 - ], - [ - 5, - 36, - 19, - 20, - 2, - 32 - ], - [ - 46, - 24, - 1, - 40, - 17, - 33 - ], - [ - 44, - 8, - 16, - 30, - 2, - 6 - ], - [ - 6, - 46, - 15, - 54, - 21, - 33 - ], - [ - 2, - 30, - 60, - 61, - 18, - 10 - ], - [ - 6, - 17, - 33, - 27, - 58, - 4 - ], - [ - 56, - 46, - 8, - 60, - 22, - 16 - ], - [ - 5, - 13, - 14, - 35, - 36, - 52 - ], - [ - 15, - 53, - 59, - 23, - 6, - 46 - ], - [ - 36, - 30, - 60, - 33, - 53, - 38 - ], - [ - 10, - 15, - 16, - 52, - 4, - 38 - ], - [ - 31, - 1, - 10, - 4, - 41, - 59 - ], - [ - 32, - 9, - 8, - 59, - 33, - 18 - ], - [ - 33, - 19, - 6, - 3, - 45, - 50 - ], - [ - 55, - 4, - 43, - 5, - 25, - 34 - ] - ], - [ - [ - 25, - 14, - 18, - 49, - 51, - 63 - ], - [ - 42, - 21, - 30, - 43, - 24, - 7 - ], - [ - 54, - 39, - 9, - 59, - 28, - 45 - ], - [ - 1, - 23, - 55, - 8, - 16, - 7 - ], - [ - 59, - 37, - 30, - 41, - 16, - 1 - ], - [ - 4, - 31, - 41, - 45, - 56, - 18 - ], - [ - 28, - 55, - 44, - 54, - 9, - 51 - ], - [ - 24, - 47, - 12, - 63, - 32, - 61 - ], - [ - 33, - 47, - 39, - 44, - 38, - 56 - ], - [ - 40, - 30, - 59, - 33, - 58, - 10 - ], - [ - 19, - 8, - 23, - 4, - 25, - 34 - ], - [ - 45, - 14, - 12, - 42, - 62, - 52 - ], - [ - 58, - 45, - 59, - 29, - 25, - 36 - ], - [ - 8, - 59, - 1, - 47, - 33, - 24 - ], - [ - 24, - 30, - 38, - 56, - 4, - 7 - ], - [ - 46, - 22, - 44, - 57, - 12, - 40 - ], - [ - 34, - 60, - 2, - 17, - 18, - 29 - ], - [ - 6, - 61, - 51, - 39, - 53, - 32 - ], - [ - 49, - 5, - 47, - 60, - 21, - 59 - ], - [ - 57, - 4, - 23, - 39, - 45, - 60 - ], - [ - 7, - 36, - 45, - 53, - 21, - 56 - ], - [ - 60, - 3, - 39, - 18, - 8, - 53 - ], - [ - 57, - 33, - 15, - 47, - 38, - 16 - ], - [ - 20, - 11, - 19, - 4, - 2, - 8 - ], - [ - 36, - 34, - 60, - 47, - 1, - 40 - ], - [ - 33, - 14, - 3, - 21, - 19, - 35 - ], - [ - 60, - 24, - 22, - 52, - 35, - 53 - ] - ], - [ - [ - 23, - 54, - 53, - 58, - 11, - 8 - ], - [ - 11, - 30, - 15, - 59, - 63, - 55 - ], - [ - 20, - 58, - 29, - 17, - 42, - 30 - ], - [ - 18, - 1, - 43, - 15, - 8, - 3 - ], - [ - 59, - 55, - 13, - 28, - 26, - 63 - ], - [ - 33, - 45, - 27, - 53, - 63, - 19 - ], - [ - 28, - 57, - 51, - 54, - 34, - 53 - ], - [ - 24, - 13, - 3, - 47, - 45, - 50 - ], - [ - 47, - 59, - 33, - 57, - 37, - 35 - ], - [ - 23, - 42, - 33, - 41, - 48, - 30 - ], - [ - 42, - 63, - 23, - 25, - 17, - 34 - ], - [ - 14, - 62, - 2, - 19, - 45, - 43 - ], - [ - 12, - 6, - 20, - 30, - 29, - 17 - ], - [ - 6, - 3, - 17, - 8, - 27, - 31 - ], - [ - 19, - 59, - 2, - 10, - 54, - 55 - ], - [ - 45, - 61, - 19, - 57, - 42, - 17 - ], - [ - 55, - 60, - 41, - 34, - 35, - 53 - ], - [ - 36, - 2, - 8, - 31, - 14, - 5 - ], - [ - 36, - 45, - 16, - 38, - 51, - 21 - ], - [ - 57, - 39, - 48, - 6, - 19, - 30 - ], - [ - 39, - 37, - 59, - 48, - 42, - 40 - ], - [ - 52, - 28, - 33, - 46, - 18, - 55 - ], - [ - 1, - 15, - 28, - 13, - 52, - 18 - ], - [ - 10, - 21, - 4, - 2, - 23, - 31 - ], - [ - 45, - 9, - 41, - 18, - 54, - 3 - ], - [ - 42, - 61, - 36, - 3, - 19, - 27 - ], - [ - 41, - 46, - 33, - 3, - 10, - 15 - ] - ], - [ - [ - 49, - 52, - 60, - 63, - 21, - 0 - ], - [ - 14, - 7, - 25, - 52, - 58, - 36 - ], - [ - 46, - 57, - 28, - 24, - 49, - 12 - ], - [ - 10, - 21, - 39, - 1, - 11, - 17 - ], - [ - 53, - 3, - 2, - 16, - 46, - 44 - ], - [ - 25, - 40, - 39, - 15, - 34, - 30 - ], - [ - 62, - 24, - 47, - 52, - 17, - 49 - ], - [ - 34, - 41, - 39, - 11, - 33, - 15 - ], - [ - 36, - 26, - 48, - 39, - 15, - 56 - ], - [ - 23, - 36, - 43, - 20, - 10, - 49 - ], - [ - 63, - 48, - 51, - 10, - 62, - 18 - ], - [ - 10, - 42, - 36, - 39, - 49, - 19 - ], - [ - 22, - 38, - 51, - 56, - 50, - 7 - ], - [ - 4, - 31, - 19, - 22, - 17, - 7 - ], - [ - 41, - 7, - 39, - 2, - 26, - 57 - ], - [ - 33, - 27, - 58, - 28, - 20, - 45 - ], - [ - 58, - 8, - 37, - 18, - 34, - 62 - ], - [ - 24, - 34, - 43, - 39, - 17, - 19 - ], - [ - 23, - 50, - 12, - 32, - 43, - 31 - ], - [ - 9, - 7, - 51, - 16, - 30, - 60 - ], - [ - 25, - 62, - 14, - 53, - 58, - 0 - ], - [ - 53, - 4, - 18, - 22, - 14, - 29 - ], - [ - 6, - 14, - 15, - 48, - 58, - 0 - ], - [ - 35, - 4, - 45, - 3, - 59, - 39 - ], - [ - 17, - 57, - 23, - 16, - 40, - 37 - ], - [ - 9, - 19, - 3, - 36, - 11, - 47 - ], - [ - 39, - 14, - 22, - 33, - 62, - 42 - ] - ], - [ - [ - 14, - 17, - 2, - 39, - 47, - 63 - ], - [ - 23, - 58, - 2, - 25, - 5, - 18 - ], - [ - 27, - 33, - 0, - 56, - 6, - 7 - ], - [ - 23, - 5, - 30, - 7, - 21, - 39 - ], - [ - 39, - 53, - 27, - 38, - 54, - 41 - ], - [ - 37, - 25, - 50, - 40, - 33, - 36 - ], - [ - 49, - 37, - 24, - 55, - 29, - 28 - ], - [ - 34, - 15, - 36, - 16, - 30, - 11 - ], - [ - 5, - 36, - 2, - 26, - 16, - 48 - ], - [ - 56, - 46, - 10, - 23, - 39, - 45 - ], - [ - 10, - 56, - 62, - 45, - 49, - 3 - ], - [ - 35, - 6, - 36, - 50, - 11, - 1 - ], - [ - 50, - 46, - 39, - 51, - 38, - 41 - ], - [ - 41, - 19, - 31, - 48, - 6, - 24 - ], - [ - 2, - 7, - 57, - 18, - 47, - 63 - ], - [ - 27, - 58, - 22, - 8, - 57, - 45 - ], - [ - 37, - 59, - 44, - 51, - 55, - 18 - ], - [ - 52, - 21, - 25, - 12, - 34, - 39 - ], - [ - 23, - 34, - 60, - 13, - 27, - 32 - ], - [ - 32, - 56, - 26, - 24, - 44, - 30 - ], - [ - 41, - 12, - 53, - 2, - 5, - 56 - ], - [ - 44, - 18, - 39, - 33, - 38, - 46 - ], - [ - 25, - 0, - 59, - 18, - 11, - 53 - ], - [ - 1, - 7, - 17, - 30, - 4, - 37 - ], - [ - 62, - 48, - 19, - 54, - 37, - 22 - ], - [ - 10, - 28, - 3, - 16, - 11, - 42 - ], - [ - 26, - 30, - 57, - 42, - 41, - 33 - ] - ], - [ - [ - 36, - 31, - 37, - 16, - 43, - 63 - ], - [ - 26, - 51, - 0, - 48, - 42, - 21 - ], - [ - 18, - 41, - 37, - 34, - 24, - 30 - ], - [ - 58, - 4, - 22, - 44, - 18, - 51 - ], - [ - 18, - 26, - 4, - 61, - 14, - 31 - ], - [ - 9, - 23, - 34, - 29, - 3, - 31 - ], - [ - 12, - 58, - 36, - 46, - 16, - 25 - ], - [ - 4, - 56, - 9, - 33, - 60, - 32 - ], - [ - 14, - 26, - 43, - 30, - 46, - 35 - ], - [ - 2, - 0, - 10, - 43, - 28, - 27 - ], - [ - 61, - 18, - 19, - 17, - 2, - 33 - ], - [ - 60, - 19, - 23, - 62, - 37, - 43 - ], - [ - 56, - 32, - 8, - 61, - 58, - 38 - ], - [ - 25, - 28, - 22, - 0, - 26, - 29 - ], - [ - 22, - 49, - 7, - 23, - 60, - 55 - ], - [ - 46, - 57, - 19, - 37, - 12, - 27 - ], - [ - 22, - 60, - 11, - 30, - 19, - 20 - ], - [ - 36, - 20, - 17, - 2, - 37, - 32 - ], - [ - 13, - 16, - 32, - 58, - 41, - 10 - ], - [ - 50, - 55, - 13, - 25, - 14, - 41 - ], - [ - 14, - 3, - 44, - 48, - 60, - 6 - ], - [ - 36, - 18, - 57, - 22, - 1, - 43 - ], - [ - 10, - 22, - 15, - 52, - 13, - 18 - ], - [ - 4, - 56, - 10, - 31, - 14, - 52 - ], - [ - 9, - 4, - 16, - 54, - 8, - 59 - ], - [ - 3, - 43, - 27, - 45, - 6, - 30 - ], - [ - 33, - 4, - 43, - 40, - 46, - 18 - ] - ], - [ - [ - 48, - 38, - 42, - 63, - 47, - 7 - ], - [ - 3, - 10, - 62, - 6, - 26, - 2 - ], - [ - 39, - 44, - 6, - 7, - 45, - 40 - ], - [ - 56, - 33, - 2, - 41, - 62, - 48 - ], - [ - 6, - 18, - 37, - 41, - 27, - 30 - ], - [ - 59, - 45, - 23, - 31, - 53, - 9 - ], - [ - 0, - 13, - 12, - 44, - 33, - 16 - ], - [ - 12, - 56, - 50, - 60, - 28, - 63 - ], - [ - 14, - 33, - 26, - 24, - 32, - 16 - ], - [ - 43, - 2, - 0, - 50, - 28, - 22 - ], - [ - 61, - 9, - 63, - 8, - 17, - 39 - ], - [ - 60, - 19, - 59, - 23, - 24, - 30 - ], - [ - 56, - 59, - 53, - 32, - 7, - 38 - ], - [ - 28, - 25, - 22, - 24, - 59, - 42 - ], - [ - 22, - 49, - 46, - 23, - 60, - 7 - ], - [ - 46, - 34, - 27, - 29, - 0, - 12 - ], - [ - 17, - 22, - 5, - 31, - 20, - 27 - ], - [ - 39, - 48, - 57, - 37, - 17, - 36 - ], - [ - 42, - 49, - 32, - 7, - 16, - 61 - ], - [ - 1, - 7, - 23, - 14, - 36, - 39 - ], - [ - 21, - 7, - 48, - 14, - 60, - 35 - ], - [ - 8, - 18, - 24, - 60, - 1, - 0 - ], - [ - 51, - 15, - 33, - 36, - 5, - 30 - ], - [ - 52, - 4, - 8, - 29, - 9, - 26 - ], - [ - 4, - 9, - 16, - 40, - 58, - 60 - ], - [ - 3, - 17, - 21, - 61, - 19, - 8 - ], - [ - 35, - 54, - 1, - 53, - 60, - 0 - ] - ], - [ - [ - 19, - 1, - 31, - 52, - 49, - 63 - ], - [ - 7, - 47, - 5, - 60, - 22, - 46 - ], - [ - 59, - 30, - 3, - 11, - 0, - 19 - ], - [ - 43, - 42, - 19, - 62, - 8, - 56 - ], - [ - 61, - 15, - 25, - 18, - 39, - 27 - ], - [ - 61, - 50, - 36, - 45, - 33, - 44 - ], - [ - 8, - 37, - 52, - 1, - 2, - 41 - ], - [ - 36, - 30, - 53, - 11, - 16, - 29 - ], - [ - 14, - 58, - 46, - 49, - 3, - 26 - ], - [ - 62, - 43, - 0, - 45, - 22, - 46 - ], - [ - 6, - 56, - 45, - 18, - 10, - 41 - ], - [ - 60, - 21, - 50, - 47, - 30, - 35 - ], - [ - 11, - 53, - 28, - 56, - 41, - 39 - ], - [ - 23, - 9, - 33, - 28, - 22, - 26 - ], - [ - 23, - 56, - 34, - 27, - 2, - 63 - ], - [ - 22, - 9, - 44, - 41, - 37, - 47 - ], - [ - 1, - 11, - 46, - 27, - 3, - 52 - ], - [ - 51, - 37, - 17, - 21, - 61, - 30 - ], - [ - 13, - 19, - 32, - 5, - 2, - 9 - ], - [ - 41, - 32, - 6, - 47, - 29, - 56 - ], - [ - 53, - 33, - 9, - 35, - 38, - 12 - ], - [ - 40, - 19, - 51, - 7, - 26, - 22 - ], - [ - 37, - 5, - 25, - 46, - 34, - 53 - ], - [ - 16, - 55, - 20, - 24, - 44, - 53 - ], - [ - 21, - 46, - 11, - 0, - 36, - 7 - ], - [ - 18, - 60, - 32, - 3, - 34, - 28 - ], - [ - 52, - 9, - 36, - 48, - 11, - 41 - ] - ], - [ - [ - 53, - 15, - 34, - 0, - 36, - 63 - ], - [ - 8, - 12, - 41, - 11, - 19, - 55 - ], - [ - 56, - 13, - 31, - 36, - 23, - 47 - ], - [ - 36, - 51, - 30, - 7, - 26, - 54 - ], - [ - 58, - 13, - 50, - 2, - 53, - 34 - ], - [ - 49, - 52, - 23, - 32, - 7, - 26 - ], - [ - 61, - 38, - 23, - 0, - 4, - 28 - ], - [ - 42, - 27, - 17, - 9, - 18, - 20 - ], - [ - 1, - 34, - 45, - 4, - 12, - 7 - ], - [ - 54, - 27, - 57, - 38, - 44, - 19 - ], - [ - 59, - 40, - 1, - 48, - 30, - 60 - ], - [ - 25, - 31, - 32, - 51, - 62, - 8 - ], - [ - 62, - 19, - 14, - 2, - 37, - 26 - ], - [ - 40, - 57, - 37, - 35, - 22, - 61 - ], - [ - 45, - 16, - 34, - 42, - 37, - 48 - ], - [ - 50, - 16, - 62, - 33, - 25, - 37 - ], - [ - 42, - 13, - 39, - 47, - 3, - 63 - ], - [ - 46, - 23, - 28, - 27, - 4, - 15 - ], - [ - 56, - 62, - 31, - 35, - 59, - 45 - ], - [ - 15, - 38, - 13, - 63, - 4, - 48 - ], - [ - 34, - 15, - 57, - 38, - 13, - 24 - ], - [ - 62, - 36, - 41, - 54, - 46, - 29 - ], - [ - 22, - 53, - 46, - 34, - 30, - 23 - ], - [ - 24, - 10, - 4, - 47, - 18, - 36 - ], - [ - 50, - 57, - 51, - 11, - 49, - 3 - ], - [ - 51, - 7, - 18, - 0, - 11, - 44 - ], - [ - 39, - 37, - 9, - 42, - 40, - 44 - ] - ], - [ - [ - 36, - 25, - 57, - 55, - 47, - 63 - ], - [ - 0, - 2, - 46, - 3, - 51, - 34 - ], - [ - 24, - 2, - 46, - 15, - 33, - 43 - ], - [ - 22, - 31, - 17, - 19, - 10, - 55 - ], - [ - 58, - 59, - 3, - 9, - 40, - 57 - ], - [ - 23, - 31, - 43, - 2, - 57, - 38 - ], - [ - 9, - 62, - 13, - 42, - 52, - 47 - ], - [ - 24, - 7, - 14, - 10, - 46, - 59 - ], - [ - 47, - 18, - 4, - 37, - 0, - 13 - ], - [ - 54, - 4, - 25, - 47, - 36, - 38 - ], - [ - 15, - 47, - 1, - 24, - 58, - 14 - ], - [ - 54, - 5, - 16, - 63, - 14, - 7 - ], - [ - 49, - 3, - 33, - 13, - 46, - 10 - ], - [ - 18, - 10, - 11, - 13, - 63, - 39 - ], - [ - 19, - 62, - 32, - 58, - 10, - 43 - ], - [ - 43, - 48, - 63, - 5, - 55, - 53 - ], - [ - 24, - 51, - 47, - 15, - 59, - 32 - ], - [ - 44, - 0, - 34, - 43, - 3, - 6 - ], - [ - 58, - 38, - 54, - 47, - 11, - 4 - ], - [ - 55, - 27, - 15, - 9, - 42, - 31 - ], - [ - 43, - 41, - 1, - 51, - 5, - 29 - ], - [ - 49, - 27, - 20, - 6, - 4, - 13 - ], - [ - 11, - 25, - 2, - 54, - 27, - 50 - ], - [ - 38, - 44, - 40, - 54, - 33, - 14 - ], - [ - 13, - 63, - 52, - 2, - 29, - 8 - ], - [ - 23, - 41, - 59, - 57, - 38, - 15 - ], - [ - 23, - 6, - 62, - 50, - 51, - 34 - ] - ], - [ - [ - 41, - 2, - 42, - 16, - 50, - 23 - ], - [ - 51, - 41, - 5, - 15, - 40, - 21 - ], - [ - 43, - 1, - 29, - 55, - 21, - 35 - ], - [ - 24, - 53, - 25, - 51, - 32, - 29 - ], - [ - 41, - 31, - 49, - 57, - 60, - 34 - ], - [ - 17, - 4, - 35, - 30, - 10, - 38 - ], - [ - 34, - 7, - 21, - 9, - 48, - 31 - ], - [ - 14, - 24, - 7, - 46, - 25, - 27 - ], - [ - 47, - 0, - 12, - 6, - 37, - 60 - ], - [ - 30, - 4, - 25, - 47, - 36, - 54 - ], - [ - 24, - 61, - 15, - 47, - 46, - 1 - ], - [ - 5, - 14, - 24, - 16, - 57, - 63 - ], - [ - 49, - 3, - 17, - 26, - 36, - 44 - ], - [ - 13, - 10, - 61, - 0, - 11, - 22 - ], - [ - 10, - 6, - 2, - 49, - 58, - 46 - ], - [ - 19, - 11, - 2, - 25, - 54, - 18 - ], - [ - 10, - 5, - 52, - 24, - 18, - 17 - ], - [ - 44, - 13, - 55, - 31, - 63, - 38 - ], - [ - 58, - 7, - 25, - 32, - 38, - 14 - ], - [ - 49, - 55, - 34, - 13, - 16, - 40 - ], - [ - 51, - 23, - 59, - 35, - 5, - 4 - ], - [ - 57, - 15, - 46, - 27, - 42, - 32 - ], - [ - 33, - 15, - 23, - 52, - 24, - 27 - ], - [ - 4, - 41, - 33, - 10, - 26, - 40 - ], - [ - 8, - 13, - 59, - 4, - 9, - 39 - ], - [ - 19, - 3, - 31, - 27, - 43, - 2 - ], - [ - 46, - 61, - 25, - 8, - 29, - 50 - ] - ], - [ - [ - 48, - 38, - 42, - 50, - 62, - 63 - ], - [ - 3, - 2, - 10, - 26, - 17, - 6 - ], - [ - 39, - 44, - 6, - 45, - 7, - 40 - ], - [ - 5, - 62, - 53, - 50, - 41, - 3 - ], - [ - 6, - 41, - 49, - 37, - 30, - 23 - ], - [ - 59, - 60, - 4, - 46, - 53, - 29 - ], - [ - 44, - 34, - 7, - 15, - 13, - 43 - ], - [ - 12, - 24, - 14, - 25, - 58, - 7 - ], - [ - 33, - 35, - 4, - 37, - 8, - 55 - ], - [ - 40, - 30, - 36, - 25, - 20, - 54 - ], - [ - 9, - 8, - 24, - 25, - 63, - 5 - ], - [ - 59, - 34, - 5, - 24, - 57, - 6 - ], - [ - 17, - 49, - 44, - 26, - 55, - 7 - ], - [ - 47, - 13, - 59, - 27, - 22, - 61 - ], - [ - 49, - 2, - 44, - 10, - 46, - 40 - ], - [ - 34, - 2, - 54, - 57, - 53, - 55 - ], - [ - 5, - 17, - 29, - 31, - 43, - 52 - ], - [ - 48, - 57, - 38, - 39, - 63, - 43 - ], - [ - 42, - 38, - 49, - 32, - 7, - 40 - ], - [ - 59, - 1, - 16, - 23, - 60, - 10 - ], - [ - 21, - 7, - 35, - 53, - 48, - 31 - ], - [ - 8, - 60, - 24, - 42, - 14, - 35 - ], - [ - 33, - 51, - 15, - 28, - 23, - 5 - ], - [ - 4, - 41, - 9, - 11, - 8, - 51 - ], - [ - 60, - 24, - 19, - 48, - 9, - 4 - ], - [ - 19, - 3, - 26, - 58, - 12, - 61 - ], - [ - 35, - 54, - 1, - 60, - 53, - 49 - ] - ], - [ - [ - 21, - 7, - 53, - 56, - 63, - 33 - ], - [ - 3, - 34, - 57, - 16, - 20, - 51 - ], - [ - 55, - 11, - 16, - 60, - 0, - 13 - ], - [ - 62, - 43, - 5, - 50, - 8, - 42 - ], - [ - 35, - 52, - 9, - 43, - 0, - 27 - ], - [ - 16, - 60, - 29, - 61, - 28, - 58 - ], - [ - 31, - 34, - 43, - 30, - 2, - 18 - ], - [ - 36, - 25, - 53, - 24, - 35, - 48 - ], - [ - 48, - 4, - 35, - 32, - 8, - 60 - ], - [ - 25, - 54, - 30, - 10, - 4, - 41 - ], - [ - 9, - 3, - 19, - 20, - 61, - 24 - ], - [ - 30, - 5, - 17, - 45, - 18, - 49 - ], - [ - 49, - 21, - 17, - 26, - 42, - 37 - ], - [ - 38, - 25, - 10, - 51, - 54, - 13 - ], - [ - 2, - 27, - 26, - 10, - 58, - 30 - ], - [ - 9, - 2, - 54, - 21, - 25, - 13 - ], - [ - 22, - 23, - 33, - 27, - 51, - 52 - ], - [ - 30, - 21, - 35, - 55, - 5, - 17 - ], - [ - 38, - 27, - 24, - 56, - 21, - 35 - ], - [ - 53, - 45, - 22, - 28, - 0, - 32 - ], - [ - 40, - 42, - 48, - 37, - 8, - 52 - ], - [ - 46, - 45, - 57, - 36, - 51, - 32 - ], - [ - 62, - 10, - 38, - 42, - 41, - 54 - ], - [ - 60, - 1, - 10, - 31, - 44, - 36 - ], - [ - 29, - 6, - 9, - 8, - 56, - 7 - ], - [ - 9, - 59, - 22, - 0, - 35, - 57 - ], - [ - 45, - 27, - 62, - 47, - 3, - 28 - ] - ], - [ - [ - 27, - 13, - 18, - 8, - 63, - 55 - ], - [ - 36, - 21, - 57, - 8, - 46, - 55 - ], - [ - 43, - 61, - 10, - 13, - 41, - 37 - ], - [ - 43, - 16, - 24, - 6, - 26, - 61 - ], - [ - 60, - 29, - 35, - 31, - 16, - 23 - ], - [ - 9, - 58, - 60, - 17, - 0, - 38 - ], - [ - 63, - 16, - 7, - 13, - 31, - 18 - ], - [ - 16, - 25, - 24, - 2, - 47, - 58 - ], - [ - 8, - 35, - 6, - 14, - 48, - 4 - ], - [ - 30, - 25, - 54, - 4, - 10, - 34 - ], - [ - 20, - 23, - 19, - 7, - 38, - 61 - ], - [ - 5, - 3, - 24, - 32, - 12, - 42 - ], - [ - 49, - 17, - 10, - 26, - 32, - 60 - ], - [ - 10, - 13, - 29, - 22, - 58, - 54 - ], - [ - 44, - 8, - 2, - 58, - 6, - 5 - ], - [ - 25, - 6, - 2, - 54, - 19, - 53 - ], - [ - 5, - 61, - 30, - 17, - 1, - 27 - ], - [ - 22, - 55, - 63, - 57, - 19, - 33 - ], - [ - 35, - 46, - 6, - 32, - 14, - 7 - ], - [ - 45, - 13, - 51, - 14, - 7, - 5 - ], - [ - 15, - 23, - 50, - 51, - 13, - 59 - ], - [ - 57, - 36, - 53, - 61, - 6, - 14 - ], - [ - 10, - 23, - 15, - 33, - 27, - 38 - ], - [ - 4, - 10, - 33, - 41, - 26, - 36 - ], - [ - 8, - 4, - 33, - 9, - 47, - 20 - ], - [ - 3, - 43, - 6, - 19, - 30, - 2 - ], - [ - 55, - 4, - 46, - 40, - 18, - 20 - ] - ], - [ - [ - 48, - 38, - 63, - 42, - 47, - 50 - ], - [ - 3, - 10, - 26, - 6, - 35, - 2 - ], - [ - 39, - 44, - 7, - 45, - 6, - 42 - ], - [ - 56, - 23, - 61, - 16, - 33, - 41 - ], - [ - 6, - 37, - 44, - 16, - 30, - 19 - ], - [ - 59, - 17, - 60, - 19, - 5, - 52 - ], - [ - 44, - 55, - 33, - 63, - 13, - 31 - ], - [ - 12, - 25, - 63, - 2, - 24, - 54 - ], - [ - 33, - 8, - 42, - 4, - 35, - 41 - ], - [ - 30, - 40, - 25, - 21, - 48, - 51 - ], - [ - 8, - 23, - 34, - 5, - 19, - 22 - ], - [ - 34, - 59, - 42, - 11, - 5, - 3 - ], - [ - 10, - 26, - 50, - 55, - 53, - 59 - ], - [ - 59, - 54, - 13, - 10, - 47, - 25 - ], - [ - 24, - 2, - 49, - 46, - 38, - 8 - ], - [ - 34, - 2, - 53, - 54, - 40, - 57 - ], - [ - 17, - 29, - 5, - 27, - 18, - 43 - ], - [ - 48, - 57, - 63, - 39, - 38, - 32 - ], - [ - 42, - 49, - 7, - 32, - 38, - 61 - ], - [ - 1, - 59, - 23, - 14, - 16, - 10 - ], - [ - 21, - 7, - 35, - 53, - 13, - 11 - ], - [ - 8, - 60, - 24, - 25, - 55, - 42 - ], - [ - 33, - 5, - 15, - 51, - 28, - 44 - ], - [ - 4, - 9, - 51, - 8, - 41, - 39 - ], - [ - 60, - 4, - 19, - 48, - 9, - 24 - ], - [ - 19, - 3, - 26, - 12, - 53, - 21 - ], - [ - 53, - 35, - 1, - 54, - 60, - 20 - ] - ], - [ - [ - 17, - 37, - 31, - 32, - 63, - 50 - ], - [ - 12, - 2, - 9, - 32, - 47, - 17 - ], - [ - 3, - 57, - 56, - 50, - 33, - 38 - ], - [ - 43, - 42, - 19, - 52, - 8, - 17 - ], - [ - 61, - 39, - 27, - 12, - 15, - 57 - ], - [ - 6, - 33, - 36, - 44, - 29, - 61 - ], - [ - 2, - 41, - 42, - 15, - 52, - 5 - ], - [ - 36, - 37, - 15, - 53, - 18, - 62 - ], - [ - 50, - 58, - 3, - 5, - 16, - 4 - ], - [ - 16, - 22, - 55, - 32, - 41, - 26 - ], - [ - 3, - 41, - 56, - 45, - 38, - 34 - ], - [ - 21, - 53, - 50, - 34, - 38, - 35 - ], - [ - 35, - 11, - 37, - 16, - 53, - 42 - ], - [ - 9, - 15, - 54, - 38, - 12, - 52 - ], - [ - 56, - 2, - 24, - 23, - 9, - 5 - ], - [ - 2, - 44, - 9, - 53, - 35, - 54 - ], - [ - 27, - 46, - 1, - 5, - 60, - 50 - ], - [ - 51, - 61, - 50, - 54, - 33, - 44 - ], - [ - 2, - 38, - 9, - 19, - 5, - 32 - ], - [ - 32, - 28, - 6, - 15, - 0, - 33 - ], - [ - 26, - 53, - 27, - 13, - 5, - 4 - ], - [ - 34, - 40, - 45, - 55, - 62, - 53 - ], - [ - 41, - 5, - 46, - 4, - 45, - 3 - ], - [ - 20, - 4, - 22, - 47, - 59, - 58 - ], - [ - 62, - 36, - 25, - 28, - 53, - 33 - ], - [ - 22, - 33, - 7, - 3, - 54, - 0 - ], - [ - 58, - 27, - 52, - 48, - 45, - 17 - ] - ], - [ - [ - 3, - 43, - 17, - 42, - 35, - 55 - ], - [ - 0, - 42, - 36, - 34, - 32, - 40 - ], - [ - 43, - 12, - 19, - 20, - 2, - 4 - ], - [ - 4, - 28, - 54, - 22, - 58, - 23 - ], - [ - 12, - 60, - 52, - 57, - 46, - 7 - ], - [ - 43, - 2, - 1, - 63, - 20, - 11 - ], - [ - 42, - 19, - 57, - 31, - 15, - 21 - ], - [ - 35, - 6, - 46, - 25, - 36, - 33 - ], - [ - 11, - 4, - 35, - 50, - 32, - 52 - ], - [ - 21, - 25, - 38, - 47, - 31, - 58 - ], - [ - 3, - 2, - 22, - 45, - 42, - 15 - ], - [ - 2, - 21, - 5, - 7, - 43, - 38 - ], - [ - 33, - 10, - 41, - 42, - 60, - 16 - ], - [ - 44, - 56, - 30, - 54, - 9, - 43 - ], - [ - 9, - 5, - 33, - 28, - 58, - 49 - ], - [ - 8, - 53, - 54, - 59, - 31, - 2 - ], - [ - 47, - 0, - 48, - 27, - 24, - 51 - ], - [ - 0, - 60, - 21, - 12, - 56, - 28 - ], - [ - 38, - 13, - 53, - 51, - 9, - 7 - ], - [ - 19, - 28, - 56, - 17, - 21, - 26 - ], - [ - 12, - 43, - 26, - 4, - 0, - 5 - ], - [ - 43, - 1, - 55, - 12, - 4, - 21 - ], - [ - 41, - 5, - 21, - 45, - 25, - 12 - ], - [ - 4, - 20, - 1, - 22, - 30, - 42 - ], - [ - 5, - 62, - 25, - 54, - 48, - 47 - ], - [ - 10, - 28, - 55, - 23, - 0, - 53 - ], - [ - 30, - 57, - 8, - 29, - 17, - 5 - ] - ], - [ - [ - 11, - 16, - 31, - 0, - 46, - 35 - ], - [ - 13, - 49, - 31, - 50, - 16, - 34 - ], - [ - 36, - 13, - 53, - 27, - 4, - 18 - ], - [ - 24, - 7, - 29, - 26, - 12, - 32 - ], - [ - 17, - 35, - 2, - 44, - 10, - 48 - ], - [ - 8, - 7, - 23, - 5, - 51, - 26 - ], - [ - 58, - 15, - 61, - 29, - 38, - 62 - ], - [ - 20, - 9, - 42, - 35, - 3, - 6 - ], - [ - 4, - 47, - 25, - 11, - 1, - 52 - ], - [ - 54, - 25, - 55, - 38, - 27, - 21 - ], - [ - 1, - 60, - 14, - 59, - 30, - 22 - ], - [ - 51, - 31, - 5, - 25, - 14, - 52 - ], - [ - 34, - 2, - 10, - 26, - 52, - 47 - ], - [ - 40, - 57, - 13, - 54, - 9, - 6 - ], - [ - 8, - 44, - 58, - 5, - 16, - 1 - ], - [ - 8, - 53, - 59, - 25, - 52, - 24 - ], - [ - 13, - 47, - 45, - 0, - 42, - 8 - ], - [ - 23, - 44, - 55, - 33, - 38, - 7 - ], - [ - 53, - 38, - 11, - 1, - 8, - 24 - ], - [ - 15, - 38, - 14, - 28, - 19, - 0 - ], - [ - 41, - 27, - 8, - 42, - 40, - 57 - ], - [ - 11, - 62, - 55, - 16, - 10, - 41 - ], - [ - 31, - 20, - 46, - 34, - 37, - 41 - ], - [ - 47, - 4, - 55, - 33, - 49, - 22 - ], - [ - 11, - 45, - 47, - 48, - 54, - 36 - ], - [ - 50, - 10, - 23, - 51, - 3, - 18 - ], - [ - 17, - 31, - 5, - 36, - 4, - 20 - ] - ], - [ - [ - 22, - 6, - 39, - 57, - 29, - 47 - ], - [ - 27, - 6, - 14, - 17, - 51, - 55 - ], - [ - 1, - 11, - 29, - 26, - 47, - 4 - ], - [ - 14, - 38, - 31, - 22, - 29, - 6 - ], - [ - 14, - 59, - 61, - 16, - 1, - 19 - ], - [ - 30, - 8, - 23, - 21, - 47, - 1 - ], - [ - 58, - 4, - 15, - 61, - 27, - 31 - ], - [ - 20, - 42, - 3, - 9, - 35, - 6 - ], - [ - 47, - 4, - 25, - 8, - 36, - 0 - ], - [ - 54, - 55, - 21, - 19, - 33, - 25 - ], - [ - 14, - 4, - 60, - 20, - 40, - 24 - ], - [ - 51, - 0, - 5, - 32, - 52, - 3 - ], - [ - 2, - 36, - 10, - 52, - 26, - 32 - ], - [ - 40, - 13, - 54, - 36, - 57, - 46 - ], - [ - 44, - 8, - 5, - 37, - 58, - 2 - ], - [ - 6, - 53, - 24, - 2, - 54, - 37 - ], - [ - 13, - 47, - 61, - 5, - 19, - 17 - ], - [ - 58, - 55, - 44, - 38, - 63, - 6 - ], - [ - 35, - 46, - 31, - 1, - 19, - 32 - ], - [ - 15, - 13, - 63, - 45, - 9, - 55 - ], - [ - 27, - 15, - 23, - 6, - 35, - 63 - ], - [ - 36, - 62, - 57, - 41, - 10, - 16 - ], - [ - 10, - 33, - 41, - 20, - 5, - 46 - ], - [ - 47, - 4, - 26, - 55, - 10, - 49 - ], - [ - 11, - 4, - 8, - 48, - 36, - 33 - ], - [ - 18, - 51, - 43, - 33, - 50, - 6 - ], - [ - 4, - 55, - 9, - 36, - 5, - 43 - ] - ], - [ - [ - 48, - 38, - 63, - 42, - 47, - 7 - ], - [ - 3, - 26, - 10, - 6, - 42, - 2 - ], - [ - 39, - 44, - 6, - 7, - 45, - 8 - ], - [ - 60, - 31, - 22, - 0, - 54, - 45 - ], - [ - 6, - 59, - 14, - 16, - 37, - 44 - ], - [ - 59, - 30, - 8, - 47, - 60, - 17 - ], - [ - 44, - 56, - 4, - 13, - 15, - 9 - ], - [ - 12, - 24, - 20, - 58, - 61, - 28 - ], - [ - 47, - 33, - 8, - 4, - 36, - 42 - ], - [ - 40, - 54, - 55, - 33, - 21, - 51 - ], - [ - 8, - 14, - 22, - 39, - 31, - 7 - ], - [ - 34, - 59, - 14, - 5, - 52, - 0 - ], - [ - 36, - 10, - 52, - 26, - 44, - 59 - ], - [ - 59, - 44, - 13, - 1, - 36, - 22 - ], - [ - 24, - 5, - 46, - 2, - 44, - 38 - ], - [ - 34, - 53, - 6, - 40, - 30, - 2 - ], - [ - 17, - 29, - 50, - 5, - 47, - 27 - ], - [ - 48, - 38, - 57, - 63, - 32, - 60 - ], - [ - 42, - 7, - 49, - 46, - 32, - 1 - ], - [ - 59, - 1, - 23, - 14, - 10, - 16 - ], - [ - 21, - 35, - 7, - 53, - 6, - 17 - ], - [ - 8, - 24, - 60, - 13, - 55, - 14 - ], - [ - 33, - 5, - 51, - 15, - 3, - 23 - ], - [ - 4, - 9, - 51, - 26, - 41, - 28 - ], - [ - 60, - 4, - 19, - 47, - 40, - 48 - ], - [ - 19, - 3, - 26, - 21, - 53, - 8 - ], - [ - 53, - 1, - 35, - 54, - 60, - 20 - ] - ], - [ - [ - 37, - 46, - 39, - 54, - 27, - 55 - ], - [ - 34, - 5, - 16, - 47, - 6, - 42 - ], - [ - 32, - 38, - 16, - 42, - 3, - 20 - ], - [ - 43, - 19, - 60, - 42, - 52, - 11 - ], - [ - 7, - 61, - 39, - 57, - 12, - 46 - ], - [ - 50, - 36, - 44, - 24, - 28, - 31 - ], - [ - 15, - 42, - 41, - 2, - 40, - 32 - ], - [ - 36, - 35, - 38, - 53, - 58, - 51 - ], - [ - 50, - 4, - 16, - 3, - 36, - 58 - ], - [ - 59, - 16, - 32, - 38, - 26, - 56 - ], - [ - 3, - 45, - 37, - 41, - 21, - 33 - ], - [ - 21, - 53, - 34, - 35, - 5, - 39 - ], - [ - 35, - 42, - 63, - 16, - 11, - 46 - ], - [ - 23, - 9, - 51, - 54, - 15, - 38 - ], - [ - 2, - 5, - 20, - 43, - 24, - 29 - ], - [ - 53, - 44, - 9, - 2, - 54, - 20 - ], - [ - 27, - 46, - 1, - 47, - 50, - 5 - ], - [ - 61, - 54, - 57, - 44, - 51, - 43 - ], - [ - 19, - 9, - 33, - 38, - 61, - 37 - ], - [ - 0, - 33, - 15, - 32, - 6, - 9 - ], - [ - 26, - 27, - 53, - 5, - 47, - 54 - ], - [ - 55, - 34, - 12, - 62, - 3, - 4 - ], - [ - 41, - 5, - 46, - 40, - 4, - 32 - ], - [ - 55, - 20, - 44, - 26, - 4, - 40 - ], - [ - 62, - 12, - 28, - 34, - 23, - 33 - ], - [ - 18, - 7, - 22, - 3, - 54, - 14 - ], - [ - 36, - 9, - 27, - 52, - 48, - 11 - ] - ], - [ - [ - 46, - 37, - 61, - 18, - 36, - 63 - ], - [ - 22, - 34, - 28, - 59, - 24, - 56 - ], - [ - 32, - 15, - 17, - 60, - 38, - 20 - ], - [ - 28, - 4, - 58, - 16, - 30, - 35 - ], - [ - 7, - 36, - 9, - 57, - 33, - 23 - ], - [ - 43, - 63, - 2, - 30, - 11, - 19 - ], - [ - 19, - 42, - 57, - 15, - 3, - 22 - ], - [ - 35, - 46, - 47, - 6, - 58, - 8 - ], - [ - 32, - 4, - 37, - 36, - 35, - 57 - ], - [ - 21, - 47, - 55, - 58, - 38, - 54 - ], - [ - 3, - 42, - 2, - 38, - 5, - 17 - ], - [ - 43, - 18, - 2, - 21, - 5, - 10 - ], - [ - 10, - 49, - 24, - 32, - 25, - 4 - ], - [ - 30, - 21, - 63, - 49, - 16, - 51 - ], - [ - 62, - 2, - 13, - 5, - 29, - 40 - ], - [ - 53, - 2, - 9, - 63, - 46, - 54 - ], - [ - 27, - 0, - 63, - 47, - 5, - 14 - ], - [ - 60, - 57, - 43, - 44, - 0, - 50 - ], - [ - 38, - 9, - 61, - 33, - 60, - 13 - ], - [ - 25, - 0, - 15, - 34, - 35, - 11 - ], - [ - 26, - 54, - 28, - 47, - 53, - 37 - ], - [ - 55, - 1, - 34, - 4, - 21, - 22 - ], - [ - 41, - 5, - 40, - 21, - 17, - 23 - ], - [ - 25, - 43, - 52, - 26, - 4, - 55 - ], - [ - 52, - 29, - 35, - 17, - 45, - 60 - ], - [ - 38, - 10, - 15, - 7, - 50, - 3 - ], - [ - 23, - 6, - 19, - 56, - 41, - 15 - ] - ], - [ - [ - 44, - 14, - 20, - 47, - 19, - 56 - ], - [ - 28, - 34, - 2, - 56, - 0, - 11 - ], - [ - 46, - 15, - 61, - 14, - 22, - 60 - ], - [ - 15, - 50, - 36, - 47, - 25, - 21 - ], - [ - 25, - 12, - 13, - 36, - 23, - 57 - ], - [ - 22, - 43, - 1, - 37, - 36, - 30 - ], - [ - 28, - 42, - 19, - 31, - 14, - 21 - ], - [ - 35, - 51, - 47, - 33, - 7, - 46 - ], - [ - 32, - 4, - 16, - 11, - 19, - 35 - ], - [ - 21, - 38, - 16, - 47, - 62, - 15 - ], - [ - 3, - 45, - 39, - 50, - 21, - 2 - ], - [ - 21, - 5, - 43, - 56, - 53, - 51 - ], - [ - 10, - 41, - 33, - 11, - 63, - 37 - ], - [ - 56, - 16, - 9, - 33, - 63, - 54 - ], - [ - 58, - 52, - 2, - 5, - 30, - 56 - ], - [ - 53, - 59, - 8, - 55, - 2, - 15 - ], - [ - 0, - 47, - 51, - 45, - 14, - 37 - ], - [ - 0, - 43, - 21, - 12, - 60, - 53 - ], - [ - 38, - 53, - 60, - 34, - 9, - 36 - ], - [ - 27, - 9, - 56, - 53, - 0, - 11 - ], - [ - 28, - 12, - 43, - 54, - 5, - 62 - ], - [ - 50, - 55, - 34, - 16, - 4, - 21 - ], - [ - 4, - 21, - 5, - 40, - 32, - 54 - ], - [ - 25, - 57, - 49, - 1, - 44, - 43 - ], - [ - 5, - 35, - 42, - 25, - 2, - 22 - ], - [ - 15, - 28, - 38, - 55, - 35, - 37 - ], - [ - 19, - 30, - 6, - 33, - 57, - 39 - ] - ], - [ - [ - 11, - 31, - 46, - 49, - 0, - 16 - ], - [ - 13, - 49, - 50, - 16, - 31, - 19 - ], - [ - 36, - 13, - 27, - 34, - 4, - 53 - ], - [ - 24, - 32, - 7, - 59, - 13, - 15 - ], - [ - 17, - 35, - 2, - 44, - 10, - 63 - ], - [ - 8, - 7, - 23, - 26, - 56, - 42 - ], - [ - 58, - 61, - 29, - 38, - 62, - 50 - ], - [ - 20, - 42, - 3, - 35, - 61, - 47 - ], - [ - 4, - 10, - 7, - 47, - 25, - 34 - ], - [ - 54, - 55, - 18, - 38, - 27, - 28 - ], - [ - 60, - 1, - 14, - 59, - 3, - 30 - ], - [ - 51, - 31, - 5, - 21, - 25, - 52 - ], - [ - 34, - 10, - 2, - 26, - 11, - 47 - ], - [ - 16, - 40, - 23, - 34, - 9, - 33 - ], - [ - 34, - 8, - 1, - 31, - 5, - 32 - ], - [ - 25, - 59, - 8, - 52, - 53, - 27 - ], - [ - 13, - 47, - 48, - 45, - 42, - 0 - ], - [ - 4, - 23, - 53, - 10, - 3, - 25 - ], - [ - 53, - 8, - 38, - 11, - 24, - 63 - ], - [ - 38, - 15, - 0, - 11, - 21, - 8 - ], - [ - 27, - 23, - 62, - 8, - 41, - 42 - ], - [ - 55, - 11, - 10, - 41, - 16, - 34 - ], - [ - 31, - 21, - 54, - 34, - 41, - 46 - ], - [ - 55, - 25, - 22, - 33, - 44, - 47 - ], - [ - 47, - 45, - 35, - 20, - 12, - 56 - ], - [ - 50, - 10, - 47, - 35, - 57, - 53 - ], - [ - 17, - 31, - 5, - 37, - 36, - 20 - ] - ], - [ - [ - 22, - 6, - 29, - 39, - 57, - 44 - ], - [ - 27, - 6, - 14, - 17, - 55, - 57 - ], - [ - 1, - 11, - 29, - 26, - 47, - 4 - ], - [ - 14, - 38, - 31, - 22, - 36, - 29 - ], - [ - 14, - 61, - 59, - 16, - 44, - 63 - ], - [ - 30, - 8, - 47, - 23, - 21, - 0 - ], - [ - 58, - 4, - 54, - 61, - 15, - 62 - ], - [ - 20, - 42, - 58, - 3, - 35, - 0 - ], - [ - 10, - 47, - 4, - 8, - 41, - 19 - ], - [ - 54, - 55, - 33, - 11, - 38, - 21 - ], - [ - 14, - 1, - 4, - 60, - 20, - 40 - ], - [ - 51, - 5, - 14, - 0, - 32, - 62 - ], - [ - 2, - 10, - 36, - 49, - 32, - 52 - ], - [ - 13, - 40, - 54, - 36, - 22, - 11 - ], - [ - 2, - 44, - 37, - 58, - 5, - 8 - ], - [ - 6, - 24, - 2, - 53, - 19, - 52 - ], - [ - 13, - 47, - 61, - 5, - 50, - 30 - ], - [ - 55, - 58, - 4, - 63, - 22, - 33 - ], - [ - 35, - 46, - 32, - 14, - 7, - 58 - ], - [ - 15, - 13, - 45, - 0, - 51, - 60 - ], - [ - 15, - 27, - 23, - 50, - 6, - 51 - ], - [ - 57, - 36, - 10, - 55, - 14, - 16 - ], - [ - 10, - 23, - 33, - 41, - 30, - 54 - ], - [ - 33, - 26, - 4, - 10, - 49, - 55 - ], - [ - 4, - 8, - 33, - 9, - 20, - 48 - ], - [ - 43, - 6, - 3, - 50, - 18, - 38 - ], - [ - 4, - 55, - 36, - 43, - 46, - 5 - ] - ], - [ - [ - 48, - 38, - 63, - 47, - 7, - 42 - ], - [ - 3, - 10, - 26, - 6, - 2, - 38 - ], - [ - 39, - 44, - 6, - 7, - 45, - 8 - ], - [ - 60, - 31, - 22, - 0, - 54, - 27 - ], - [ - 6, - 59, - 16, - 14, - 37, - 44 - ], - [ - 59, - 8, - 30, - 47, - 48, - 3 - ], - [ - 44, - 54, - 4, - 56, - 15, - 13 - ], - [ - 12, - 24, - 20, - 58, - 31, - 61 - ], - [ - 47, - 33, - 10, - 8, - 4, - 36 - ], - [ - 54, - 40, - 55, - 33, - 11, - 51 - ], - [ - 8, - 14, - 23, - 29, - 22, - 31 - ], - [ - 34, - 59, - 14, - 5, - 57, - 50 - ], - [ - 44, - 36, - 10, - 59, - 52, - 9 - ], - [ - 59, - 44, - 13, - 9, - 54, - 1 - ], - [ - 24, - 2, - 38, - 46, - 44, - 47 - ], - [ - 34, - 53, - 6, - 2, - 57, - 40 - ], - [ - 17, - 29, - 47, - 5, - 50, - 34 - ], - [ - 48, - 63, - 57, - 38, - 60, - 32 - ], - [ - 42, - 7, - 49, - 32, - 46, - 1 - ], - [ - 1, - 59, - 23, - 14, - 16, - 10 - ], - [ - 21, - 35, - 7, - 53, - 6, - 17 - ], - [ - 8, - 24, - 60, - 14, - 13, - 55 - ], - [ - 33, - 5, - 51, - 15, - 23, - 3 - ], - [ - 9, - 4, - 51, - 26, - 41, - 8 - ], - [ - 4, - 60, - 19, - 47, - 40, - 9 - ], - [ - 19, - 3, - 26, - 21, - 17, - 8 - ], - [ - 1, - 35, - 53, - 54, - 60, - 20 - ] - ], - [ - [ - 12, - 41, - 14, - 62, - 24, - 10 - ], - [ - 10, - 53, - 39, - 35, - 41, - 58 - ], - [ - 33, - 32, - 50, - 31, - 3, - 34 - ], - [ - 43, - 10, - 42, - 11, - 17, - 47 - ], - [ - 42, - 12, - 11, - 19, - 58, - 54 - ], - [ - 36, - 50, - 55, - 61, - 25, - 56 - ], - [ - 41, - 22, - 16, - 52, - 2, - 15 - ], - [ - 26, - 36, - 62, - 53, - 15, - 51 - ], - [ - 58, - 16, - 5, - 53, - 3, - 49 - ], - [ - 32, - 46, - 26, - 45, - 16, - 62 - ], - [ - 41, - 45, - 56, - 49, - 11, - 3 - ], - [ - 52, - 34, - 35, - 50, - 21, - 53 - ], - [ - 59, - 53, - 46, - 30, - 39, - 37 - ], - [ - 20, - 9, - 52, - 2, - 7, - 33 - ], - [ - 20, - 50, - 24, - 29, - 23, - 2 - ], - [ - 53, - 2, - 44, - 41, - 9, - 13 - ], - [ - 47, - 27, - 1, - 5, - 45, - 46 - ], - [ - 61, - 7, - 51, - 30, - 35, - 9 - ], - [ - 19, - 34, - 32, - 17, - 2, - 14 - ], - [ - 15, - 32, - 6, - 45, - 9, - 11 - ], - [ - 35, - 27, - 6, - 53, - 13, - 60 - ], - [ - 26, - 62, - 16, - 28, - 41, - 3 - ], - [ - 5, - 20, - 46, - 37, - 11, - 55 - ], - [ - 55, - 47, - 4, - 16, - 14, - 27 - ], - [ - 36, - 11, - 27, - 62, - 33, - 7 - ], - [ - 33, - 7, - 54, - 3, - 32, - 12 - ], - [ - 58, - 52, - 27, - 26, - 48, - 38 - ] - ], - [ - [ - 6, - 52, - 19, - 63, - 46, - 38 - ], - [ - 8, - 42, - 4, - 47, - 57, - 56 - ], - [ - 31, - 46, - 32, - 4, - 14, - 10 - ], - [ - 28, - 27, - 4, - 37, - 58, - 20 - ], - [ - 57, - 59, - 60, - 62, - 22, - 14 - ], - [ - 30, - 2, - 9, - 57, - 11, - 13 - ], - [ - 20, - 19, - 57, - 42, - 51, - 27 - ], - [ - 46, - 35, - 47, - 32, - 7, - 0 - ], - [ - 12, - 4, - 35, - 10, - 50, - 47 - ], - [ - 15, - 47, - 54, - 25, - 38, - 51 - ], - [ - 18, - 42, - 5, - 15, - 38, - 61 - ], - [ - 18, - 22, - 8, - 16, - 5, - 7 - ], - [ - 5, - 3, - 24, - 35, - 4, - 30 - ], - [ - 17, - 13, - 20, - 55, - 36, - 22 - ], - [ - 22, - 10, - 21, - 54, - 6, - 47 - ], - [ - 30, - 53, - 6, - 19, - 2, - 54 - ], - [ - 22, - 26, - 5, - 7, - 47, - 21 - ], - [ - 4, - 41, - 13, - 46, - 55, - 43 - ], - [ - 17, - 56, - 32, - 45, - 14, - 6 - ], - [ - 5, - 49, - 53, - 28, - 34, - 60 - ], - [ - 42, - 55, - 57, - 17, - 28, - 22 - ], - [ - 40, - 23, - 28, - 57, - 21, - 16 - ], - [ - 51, - 21, - 35, - 24, - 44, - 10 - ], - [ - 38, - 25, - 4, - 14, - 62, - 31 - ], - [ - 31, - 50, - 13, - 56, - 39, - 8 - ], - [ - 6, - 51, - 55, - 8, - 0, - 21 - ], - [ - 29, - 46, - 18, - 55, - 37, - 50 - ] - ], - [ - [ - 31, - 46, - 49, - 59, - 35, - 14 - ], - [ - 13, - 16, - 31, - 50, - 33, - 19 - ], - [ - 13, - 36, - 27, - 52, - 3, - 19 - ], - [ - 9, - 24, - 29, - 12, - 55, - 61 - ], - [ - 35, - 17, - 2, - 10, - 41, - 48 - ], - [ - 51, - 23, - 30, - 57, - 52, - 17 - ], - [ - 34, - 48, - 0, - 38, - 27, - 51 - ], - [ - 47, - 32, - 2, - 35, - 58, - 55 - ], - [ - 15, - 12, - 35, - 45, - 52, - 4 - ], - [ - 36, - 15, - 4, - 59, - 11, - 54 - ], - [ - 60, - 5, - 59, - 50, - 29, - 18 - ], - [ - 18, - 8, - 31, - 23, - 24, - 3 - ], - [ - 34, - 4, - 5, - 58, - 14, - 30 - ], - [ - 17, - 57, - 36, - 55, - 51, - 6 - ], - [ - 10, - 45, - 8, - 54, - 22, - 47 - ], - [ - 30, - 50, - 14, - 37, - 6, - 16 - ], - [ - 26, - 0, - 22, - 5, - 13, - 4 - ], - [ - 4, - 46, - 23, - 43, - 60, - 34 - ], - [ - 45, - 8, - 56, - 62, - 17, - 51 - ], - [ - 4, - 38, - 53, - 15, - 58, - 5 - ], - [ - 57, - 24, - 34, - 15, - 55, - 42 - ], - [ - 23, - 37, - 40, - 41, - 54, - 11 - ], - [ - 48, - 29, - 51, - 31, - 34, - 9 - ], - [ - 62, - 14, - 18, - 4, - 31, - 59 - ], - [ - 50, - 31, - 49, - 45, - 56, - 57 - ], - [ - 51, - 50, - 56, - 8, - 12, - 52 - ], - [ - 17, - 37, - 5, - 39, - 44, - 20 - ] - ], - [ - [ - 45, - 13, - 63, - 37, - 38, - 56 - ], - [ - 63, - 6, - 12, - 18, - 27, - 51 - ], - [ - 3, - 21, - 4, - 48, - 17, - 27 - ], - [ - 14, - 55, - 9, - 37, - 29, - 26 - ], - [ - 35, - 57, - 2, - 13, - 41, - 10 - ], - [ - 39, - 30, - 57, - 13, - 53, - 23 - ], - [ - 34, - 48, - 38, - 27, - 56, - 51 - ], - [ - 47, - 35, - 46, - 2, - 32, - 7 - ], - [ - 12, - 56, - 4, - 35, - 52, - 50 - ], - [ - 15, - 36, - 14, - 30, - 47, - 48 - ], - [ - 5, - 60, - 59, - 22, - 15, - 18 - ], - [ - 8, - 22, - 23, - 47, - 5, - 26 - ], - [ - 4, - 5, - 30, - 58, - 26, - 8 - ], - [ - 17, - 29, - 13, - 50, - 14, - 34 - ], - [ - 54, - 10, - 41, - 14, - 21, - 5 - ], - [ - 50, - 6, - 54, - 30, - 16, - 56 - ], - [ - 0, - 47, - 7, - 4, - 5, - 34 - ], - [ - 4, - 46, - 23, - 41, - 59, - 55 - ], - [ - 62, - 45, - 1, - 57, - 47, - 32 - ], - [ - 4, - 53, - 15, - 50, - 60, - 1 - ], - [ - 34, - 57, - 24, - 31, - 15, - 53 - ], - [ - 23, - 41, - 45, - 12, - 57, - 14 - ], - [ - 51, - 29, - 49, - 48, - 18, - 52 - ], - [ - 14, - 4, - 62, - 18, - 26, - 28 - ], - [ - 50, - 49, - 7, - 24, - 9, - 3 - ], - [ - 56, - 20, - 51, - 3, - 26, - 14 - ], - [ - 55, - 37, - 50, - 14, - 42, - 20 - ] - ], - [ - [ - 51, - 43, - 27, - 30, - 5, - 55 - ], - [ - 24, - 16, - 48, - 15, - 7, - 30 - ], - [ - 26, - 21, - 50, - 52, - 4, - 56 - ], - [ - 19, - 17, - 2, - 14, - 57, - 22 - ], - [ - 3, - 35, - 37, - 45, - 1, - 6 - ], - [ - 13, - 30, - 23, - 39, - 57, - 51 - ], - [ - 9, - 34, - 48, - 17, - 27, - 26 - ], - [ - 47, - 32, - 59, - 2, - 28, - 57 - ], - [ - 18, - 12, - 31, - 20, - 52, - 36 - ], - [ - 14, - 15, - 36, - 30, - 58, - 31 - ], - [ - 5, - 4, - 26, - 19, - 22, - 59 - ], - [ - 22, - 8, - 34, - 26, - 42, - 52 - ], - [ - 20, - 5, - 52, - 4, - 30, - 33 - ], - [ - 34, - 17, - 2, - 5, - 39, - 20 - ], - [ - 54, - 61, - 30, - 14, - 20, - 25 - ], - [ - 56, - 41, - 2, - 5, - 54, - 44 - ], - [ - 12, - 34, - 47, - 7, - 54, - 59 - ], - [ - 9, - 11, - 2, - 43, - 33, - 50 - ], - [ - 8, - 10, - 19, - 20, - 32, - 18 - ], - [ - 17, - 38, - 6, - 29, - 49, - 41 - ], - [ - 38, - 45, - 52, - 57, - 63, - 27 - ], - [ - 26, - 28, - 3, - 40, - 5, - 47 - ], - [ - 60, - 49, - 35, - 34, - 38, - 55 - ], - [ - 61, - 24, - 4, - 55, - 45, - 16 - ], - [ - 46, - 11, - 27, - 0, - 56, - 48 - ], - [ - 60, - 45, - 44, - 25, - 32, - 3 - ], - [ - 11, - 38, - 52, - 48, - 9, - 21 - ] - ], - [ - [ - 22, - 19, - 46, - 31, - 3, - 23 - ], - [ - 32, - 62, - 15, - 54, - 10, - 55 - ], - [ - 47, - 30, - 38, - 5, - 7, - 60 - ], - [ - 15, - 13, - 1, - 8, - 25, - 2 - ], - [ - 13, - 59, - 5, - 6, - 62, - 52 - ], - [ - 27, - 63, - 62, - 45, - 12, - 56 - ], - [ - 50, - 9, - 8, - 51, - 48, - 18 - ], - [ - 59, - 57, - 28, - 2, - 61, - 6 - ], - [ - 18, - 59, - 6, - 52, - 39, - 57 - ], - [ - 14, - 23, - 11, - 36, - 15, - 32 - ], - [ - 26, - 5, - 42, - 25, - 22, - 23 - ], - [ - 33, - 22, - 55, - 28, - 16, - 24 - ], - [ - 6, - 20, - 33, - 14, - 52, - 15 - ], - [ - 17, - 6, - 5, - 39, - 2, - 34 - ], - [ - 54, - 62, + 15, + 30, 25, - 61, - 21, - 14 - ], - [ - 41, - 45, - 14, - 5, - 2, - 54 - ], - [ - 47, - 34, - 10, - 31, - 5, - 41 - ], - [ - 9, - 2, - 36, - 6, - 43, 38 ], [ - 57, - 20, - 41, - 10, - 32, - 29 - ], - [ - 47, - 6, - 49, - 15, - 13, - 34 - ], - [ - 3, - 57, - 44, - 38, - 50, - 53 - ], - [ - 47, - 57, - 28, - 26, - 19, - 22 - ], - [ - 11, - 1, - 18, - 5, - 46, - 53 - ], - [ - 9, - 55, 4, - 3, - 25, - 10 - ], - [ - 45, - 11, - 0, - 63, - 48, - 57 - ], - [ - 42, - 10, - 43, - 3, - 49, - 39 - ], - [ - 38, - 15, - 17, - 10, - 41, - 8 - ] - ], - [ - [ 19, - 18, - 51, - 25, - 60, - 55 - ], - [ - 56, - 27, - 61, - 42, - 55, - 23 + 24, + 35, + 31, + 48 ], [ - 32, - 39, - 37, + 7, 46, - 20, - 52 - ], - [ - 41, - 21, - 37, - 13, - 57, - 2 - ], - [ - 10, - 9, 3, - 46, 58, - 32 - ], - [ - 62, - 27, - 57, - 43, - 2, - 53 - ], - [ - 50, - 21, - 19, - 48, - 15, - 6 - ], - [ - 14, - 21, - 54, - 8, - 57, - 28 + 30, + 41 ], [ - 6, - 18, - 4, - 52, + 58, + 9, 39, - 24 - ], - [ 32, - 38, - 23, - 53, - 25, - 17 - ], - [ - 26, - 22, - 25, - 5, - 42, - 33 - ], - [ - 5, - 22, - 16, - 9, - 61, - 55 + 29, + 40 ], [ - 49, - 33, - 30, - 25, + 40, + 37, 20, - 0 - ], - [ - 18, - 17, + 8, 25, - 63, - 39, - 11 - ], - [ - 62, - 54, - 10, - 5, - 58, - 37 - ], - [ - 42, - 41, - 5, - 24, - 37, - 54 - ], - [ - 47, - 34, - 27, - 10, - 22, 55 ], [ - 60, - 43, - 44, - 7, - 52, - 37 - ], - [ - 12, - 32, - 39, - 38, - 1, - 20 + 19, + 0, + 54, + 52, + 17, + 39 ], [ - 34, - 52, - 49, - 15, - 63, - 28 + 25, + 43, + 12, + 61, + 14, + 11 ], [ + 23, + 4, + 54, + 36, 28, - 39, - 3, - 26, - 22, - 30 + 33 ], [ - 29, - 1, 40, - 22, - 19, - 63 + 2, + 25, + 58, + 36, + 53 ], [ - 41, - 5, + 18, + 46, + 35, + 22, 53, - 33, - 26, - 39 + 16 ], [ - 25, - 38, - 4, - 34, - 49, - 51 + 2, + 6, + 63, + 14, + 42, + 11 ], [ - 29, + 35, + 7, 52, - 48, - 47, - 20, - 35 + 40, + 29, + 57 ], [ - 50, - 3, - 38, - 16, - 53, - 15 + 40, + 15, + 19, + 57, + 17, + 23 ], [ - 19, - 62, - 6, - 23, - 10, - 36 + 9, + 11, + 47, + 22, + 49, + 1 + ], + [ + 24, + 39, + 42, + 2, + 16, + 0 ] ], [ [ - 5, - 14, - 17, - 57, - 10, - 27 + 18, + 8, + 20, + 49, + 30, + 23 ], [ - 43, - 9, - 56, 1, - 14, - 33 + 27, + 26, + 22, + 59, + 36 ], [ - 63, - 35, 43, - 1, - 10, - 27 - ], - [ - 51, - 50, - 57, - 41, - 20, - 54 + 26, + 15, + 58, + 0, + 46 ], [ - 11, - 43, - 40, - 54, - 30, - 33 + 55, + 1, + 35, + 28, + 16, + 32 ], [ - 27, + 59, + 9, + 10, 53, - 63, - 30, - 15, + 12, 58 ], [ - 51, - 48, - 21, - 57, 9, - 50 + 2, + 27, + 11, + 61, + 43 ], [ - 21, - 14, - 59, - 52, - 28, - 8 + 16, + 57, + 63, + 23, + 19, + 12 ], [ - 6, - 18, + 46, + 45, + 26, 4, - 0, - 12, - 59 + 30, + 37 ], [ + 43, + 44, + 20, + 16, 14, - 53, - 30, - 4, - 42, - 36 + 9 ], [ - 26, + 34, + 47, 42, - 5, - 22, - 58, - 19 + 43, + 26, + 51 ], [ - 22, - 5, - 34, + 42, + 2, 38, - 52, - 29 - ], - [ - 49, - 33, - 0, + 45, 20, - 26, - 8 + 36 ], [ - 17, + 18, + 7, + 12, 2, - 39, - 13, - 44, - 63 + 43, + 60 ], [ - 10, - 54, - 20, - 37, - 5, - 59 + 1, + 28, + 12, + 3, + 29, + 33 + ], + [ + 25, + 13, + 0, + 63, + 2, + 62 + ], + [ + 18, + 36, + 6, + 29, + 19, + 15 ], [ + 1, + 42, + 63, 41, - 43, - 24, - 5, - 53, - 37 + 57, + 19 ], [ - 12, - 47, - 34, 57, + 54, + 5, 27, - 5 + 18, + 31 ], [ - 9, - 33, - 7, - 38, - 43, - 31 + 50, + 6, + 13, + 32, + 17, + 20 ], [ - 19, - 10, - 20, + 17, + 5, + 27, 32, - 18, - 1 + 1, + 55 ], [ - 36, - 63, - 15, 49, - 40, - 50 + 0, + 61, + 5, + 10, + 30 ], [ - 39, - 3, - 38, - 27, - 36, - 35 + 29, + 53, + 51, + 13, + 33, + 46 ], [ 29, - 19, - 40, - 47, - 62, - 14 + 17, + 21, + 30, + 14, + 40 ], [ 5, - 46, + 17, 33, - 53, - 49, - 21 + 32, + 18, + 28 ], [ - 55, - 4, - 27, - 61, 51, - 34 + 4, + 20, + 54, + 58, + 41 ], [ - 11, - 0, - 59, + 47, + 4, + 27, 48, - 14, - 27 + 37, + 60 ], [ - 7, - 32, 3, - 51, - 18, - 14 + 26, + 12, + 59, + 2, + 48 ], [ - 11, + 46, + 43, + 18, + 20, 9, - 36, - 48, - 0, - 46 + 53 ] ], [ [ - 63, - 62, - 60, + 57, + 9, 19, - 23, - 56 + 51, + 18, + 41 ], [ + 28, + 57, + 36, + 8, 48, - 32, - 1, - 35, - 5, - 21 + 60 ], [ - 22, - 24, - 46, - 58, + 2, + 51, 59, - 60 + 5, + 34, + 9 ], [ - 27, - 37, - 50, - 28, - 61, - 6 + 9, + 55, + 59, + 26, + 4, + 2 ], [ - 10, - 12, - 15, - 58, + 49, + 56, 35, + 42, + 30, 23 ], [ - 2, - 43, - 36, - 57, + 18, 30, - 20 - ], - [ - 21, - 14, + 22, + 29, 19, - 41, - 63, - 42 - ], - [ - 46, - 7, - 35, - 43, - 21, - 36 + 52 ], [ - 6, - 18, - 0, - 35, - 40, - 4 + 39, + 34, + 33, + 51, + 56, + 3 ], [ - 47, - 53, 32, - 38, + 21, 1, - 58 - ], - [ - 26, - 18, - 22, - 15, - 5, - 46 + 7, + 46, + 49 ], [ - 7, - 22, - 24, - 63, - 53, - 5 + 33, + 54, + 23, + 21, + 12, + 11 ], [ - 11, - 3, - 10, - 18, - 37, - 24 + 5, + 30, + 60, + 47, + 15, + 18 ], [ - 63, + 4, 18, - 13, - 39, - 17, - 0 + 46, + 27, + 20, + 22 ], [ - 62, + 22, + 59, 54, - 5, - 18, - 6, - 14 + 48, + 19, + 4 ], [ - 19, - 43, + 17, + 5, + 56, 31, - 30, - 57, - 42 + 49, + 4 ], [ - 24, - 5, - 51, - 34, + 29, 47, - 55 + 55, + 2, + 53, + 60 ], [ - 59, - 13, - 33, - 44, + 8, + 22, 11, - 41 + 44, + 36, + 15 ], [ - 58, - 32, - 1, - 7, - 39, - 38 + 60, + 44, + 30, + 57, + 54, + 39 ], [ - 49, - 13, - 28, - 9, - 34, - 7 + 7, + 44, + 27, + 20, + 2, + 61 ], [ + 48, + 17, + 21, + 37, 32, - 51, - 3, - 53, + 57 + ], + [ 48, - 13 + 32, + 46, + 6, + 61, + 42 ], [ - 40, - 15, - 37, + 4, 57, 1, - 23 + 36, + 0, + 30 ], [ - 5, + 7, + 17, + 61, + 53, + 21, + 63 + ], + [ + 60, + 14, 53, 35, + 18, + 55 + ], + [ + 10, + 15, + 33, 51, - 22, - 38 + 36, + 5 ], [ - 4, - 44, - 14, - 41, 11, - 38 + 4, + 19, + 51, + 21, + 52 ], [ 47, + 19, + 43, 48, - 8, - 61, - 13, - 9 + 58, + 4 ], [ - 23, 3, - 27, - 43, - 0, - 61 + 33, + 26, + 21, + 52, + 19 ], [ - 46, - 23, - 59, - 62, - 18, - 43 + 24, + 45, + 60, + 35, + 49, + 1 ] ], [ [ - 62, - 0, - 9, - 61, + 23, 54, - 32 + 53, + 58, + 11, + 8 ], [ - 45, + 11, + 30, + 15, + 59, + 63, + 55 + ], + [ + 20, + 58, 29, - 7, - 35, - 22, - 62 + 17, + 42, + 30 ], [ - 56, - 31, - 23, - 53, - 28, - 2 + 18, + 1, + 43, + 15, + 8, + 3 ], [ - 36, - 2, - 5, - 4, - 48, - 41 + 59, + 55, + 13, + 28, + 26, + 63 ], [ - 18, - 0, - 15, - 23, - 16, - 11 + 33, + 45, + 27, + 53, + 63, + 19 ], [ - 4, - 36, + 28, 57, - 56, - 14, - 5 + 51, + 54, + 34, + 53 ], [ - 59, - 56, - 21, - 8, - 7, - 33 + 24, + 13, + 3, + 47, + 45, + 50 ], [ - 12, - 23, - 7, - 2, - 28, - 34 + 47, + 59, + 33, + 57, + 37, + 35 ], [ - 17, - 6, 23, - 19, - 62, - 27 - ], - [ - 53, - 32, - 51, - 38, + 42, + 33, 41, - 58 + 48, + 30 ], [ - 18, + 42, 63, - 19, - 22, - 26, - 5 + 23, + 25, + 17, + 34 ], [ - 22, - 7, - 60, - 11, - 12, - 19 + 14, + 62, + 2, + 19, + 45, + 43 ], [ - 24, - 7, - 11, - 17, + 12, + 6, + 20, 30, - 37 + 29, + 17 ], [ - 47, - 13, - 63, - 50, - 39, - 45 + 6, + 3, + 17, + 8, + 27, + 31 ], [ - 62, + 19, + 59, + 2, + 10, 54, - 18, - 14, - 29, - 56 + 55 ], [ - 56, - 30, - 0, - 55, - 51, - 10 + 45, + 61, + 19, + 57, + 42, + 17 ], [ - 12, - 5, - 21, + 55, + 60, + 41, 34, - 63, - 29 + 35, + 53 ], [ - 42, + 36, + 2, 8, + 31, + 14, + 5 + ], + [ + 36, + 45, + 16, 38, + 51, + 21 + ], + [ 57, - 41, - 44 + 39, + 48, + 6, + 19, + 30 ], [ - 32, - 54, - 1, + 39, 37, - 49, + 59, + 48, + 42, 40 ], [ - 5, - 57, - 49, + 52, 28, - 34, - 10 - ], - [ + 33, 46, - 32, - 13, - 7, - 61, - 48 + 18, + 55 ], [ - 40, - 3, - 30, - 60, - 39, - 1 + 1, + 15, + 28, + 13, + 52, + 18 ], [ - 5, - 44, - 33, - 36, - 28, + 10, + 21, + 4, + 2, + 23, 31 ], [ - 4, - 11, - 28, + 45, + 9, 41, - 51, - 5 + 18, + 54, + 3 ], [ - 48, - 47, + 42, 61, - 28, - 60, + 36, + 3, + 19, 27 ], [ - 14, + 41, + 46, + 33, 3, - 26, - 12, - 53, - 61 - ], - [ - 54, - 60, - 49, - 35, 10, - 62 + 15 ] ], [ [ - 27, - 62, - 63, - 23, - 47, - 56 + 19, + 1, + 31, + 52, + 49, + 63 ], [ 7, - 4, - 2, - 10, - 35, - 36 + 47, + 5, + 60, + 22, + 46 ], [ + 59, + 30, 3, + 11, 0, - 27, - 62, - 50, - 60 + 19 ], [ - 36, - 3, + 43, 42, - 18, - 2, - 48 + 19, + 62, + 8, + 56 ], [ 61, - 12, - 27, - 10, 15, - 14 + 25, + 18, + 39, + 27 ], [ + 61, + 50, 36, + 45, 33, - 50, - 6, - 29, - 16 + 44 ], [ - 2, - 41, 8, - 43, - 40, - 59 + 37, + 52, + 1, + 2, + 41 ], [ 36, + 30, 53, - 15, - 7, - 37, - 2 + 11, + 16, + 29 ], [ - 3, + 14, 58, - 4, - 19, - 5, - 27 + 46, + 49, + 3, + 26 ], [ - 22, - 26, - 53, + 62, + 43, + 0, 45, - 25, - 38 + 22, + 46 ], [ + 6, + 56, + 45, 18, - 41, - 22, - 3, - 34, - 45 + 10, + 41 ], [ + 60, 21, - 53, - 5, - 22, - 28, - 9 + 50, + 47, + 30, + 35 ], [ 11, - 24, + 53, 28, - 16, - 8, - 35 + 56, + 41, + 39 ], [ - 38, + 23, 9, - 21, - 52, - 41, - 39 + 33, + 28, + 22, + 26 ], [ + 23, + 56, + 34, 27, - 54, - 14, - 5, - 8, + 2, 63 ], [ - 56, - 55, - 43, - 30, - 35, - 51 + 22, + 9, + 44, + 41, + 37, + 47 ], [ - 51, + 1, + 11, + 46, + 27, 3, - 14, - 60, - 5, - 26 + 52 ], [ - 62, - 30, - 8, + 51, + 37, + 17, 21, - 44, - 35 + 61, + 30 ], [ - 38, - 34, - 27, + 13, + 19, 32, - 22, - 8 + 5, + 2, + 9 ], [ - 22, - 53, - 28, - 60, - 12, - 49 + 41, + 32, + 6, + 47, + 29, + 56 ], [ - 49, - 32, - 41, 53, - 12, - 33 + 33, + 9, + 35, + 38, + 12 ], [ 40, - 45, - 22, - 44, - 1, - 34 + 19, + 51, + 7, + 26, + 22 ], [ + 37, 5, - 4, - 53, - 25, - 41, - 63 - ], - [ - 4, - 11, 25, - 43, - 31, - 14 - ], - [ - 31, - 52, - 5, - 58, - 48, - 43 - ], - [ - 23, - 1, - 28, - 3, - 0, - 10 + 46, + 34, + 53 ], [ - 27, - 45, - 32, - 4, - 30, - 6 - ] - ], - [ - [ - 41, - 2, - 42, 16, - 50, - 32 - ], - [ - 51, - 5, - 41, - 40, + 55, + 20, + 24, 44, - 21 + 53 ], [ - 43, - 1, - 29, - 55, 21, - 35 - ], - [ + 46, + 11, + 0, 36, - 58, - 25, - 3, - 18, - 54 + 7 ], [ - 31, + 18, 60, - 24, - 12, - 61, - 41 + 32, + 3, + 34, + 28 ], [ - 4, - 10, - 35, + 52, + 9, 36, - 0, - 43 + 48, + 11, + 41 + ] + ], + [ + [ + 17, + 37, + 31, + 32, + 63, + 50 ], [ - 45, - 43, - 63, - 35, - 36, - 48 + 12, + 2, + 9, + 32, + 47, + 17 ], [ 3, - 9, - 8, - 7, - 43, - 27 + 57, + 56, + 50, + 33, + 38 ], [ - 0, 43, - 4, - 40, - 18, - 44 + 42, + 19, + 52, + 8, + 17 ], [ - 34, + 61, + 39, + 27, + 12, 15, - 50, - 38, - 53, - 25 + 57 + ], + [ + 6, + 33, + 36, + 44, + 29, + 61 ], [ - 24, - 46, - 26, - 13, 2, - 18 + 41, + 42, + 15, + 52, + 5 ], [ - 57, + 36, 37, + 15, + 53, + 18, + 62 + ], + [ + 50, + 58, + 3, 5, + 16, + 4 + ], + [ + 16, 22, - 7, - 53 + 55, + 32, + 41, + 26 ], [ 3, - 9, + 41, + 56, + 45, 38, - 26, - 37, - 23 - ], - [ - 60, - 26, - 0, - 39, - 13, - 55 + 34 ], [ - 54, 21, - 58, - 6, - 55, - 14 + 53, + 50, + 34, + 38, + 35 ], [ + 35, 11, - 19, - 43, - 57, - 56, - 37 + 37, + 16, + 53, + 42 ], [ - 10, + 9, + 15, + 54, + 38, + 12, + 52 + ], + [ + 56, + 2, 24, - 60, - 21, - 5, - 58 + 23, + 9, + 5 ], [ - 31, - 20, - 63, + 2, 44, - 55, - 33 + 9, + 53, + 35, + 54 ], [ - 7, - 25, - 40, - 32, - 54, - 58 + 27, + 46, + 1, + 5, + 60, + 50 ], [ - 49, - 30, + 51, + 61, 50, - 13, - 28, - 55 + 54, + 33, + 44 ], [ - 30, - 3, - 51, - 61, - 0, - 16 + 2, + 38, + 9, + 19, + 5, + 32 ], [ - 57, - 46, - 43, - 40, - 30, - 22 + 32, + 28, + 6, + 15, + 0, + 33 ], [ - 52, - 33, - 51, - 19, + 26, + 53, 27, - 29 + 13, + 5, + 4 ], [ - 14, - 4, + 34, 40, + 45, + 55, + 62, + 53 + ], + [ 41, - 0, - 34 + 5, + 46, + 4, + 45, + 3 ], [ + 20, 4, - 13, - 8, - 61, + 22, 47, - 9 + 59, + 58 ], [ - 31, - 27, - 43, - 1, + 62, + 36, + 25, + 28, + 53, + 33 + ], + [ + 22, + 33, + 7, 3, + 54, 0 ], [ - 8, - 4, - 46, - 9, - 3, - 50 + 58, + 27, + 52, + 48, + 45, + 17 ] ] + ], + "routing_indices_token_indices": [ + 3, + 13, + 14, + 17, + 28, + 31, + 35, + 81, + 86, + 94 ] } } \ No newline at end of file From d4cacef876fe654629bec1b882841e8e0415bc2a Mon Sep 17 00:00:00 2001 From: Dmytro Pykhtar <37850217+dimapihtar@users.noreply.github.com> Date: Fri, 24 Apr 2026 13:56:45 +0300 Subject: [PATCH 018/105] SafeUnpickler class for safe pickle usage (#4319) Signed-off-by: dimapihtar --- megatron/core/__init__.py | 8 ++- .../core/extensions/transformer_engine.py | 4 +- megatron/core/safe_globals.py | 56 +++++++++++++++++++ .../dist_checkpointing/test_safe_globals.py | 17 +++++- 4 files changed, 82 insertions(+), 3 deletions(-) diff --git a/megatron/core/__init__.py b/megatron/core/__init__.py index b9668b2ce66..4c6dbbad5ea 100644 --- a/megatron/core/__init__.py +++ b/megatron/core/__init__.py @@ -1,5 +1,7 @@ # Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +import torch + import megatron.core.tensor_parallel import megatron.core.utils from megatron.core import parallel_state @@ -46,7 +48,11 @@ "__version__", ] -from .safe_globals import register_safe_globals +from .safe_globals import register_safe_globals, safe_load_from_bytes if is_torch_min_version("2.6a0"): register_safe_globals() + +# Avoid direct usage of unsafe `torch.storage._load_from_bytes` (weights_only=False) +# Use safe implementation with weights_only=True +torch.storage._load_from_bytes = safe_load_from_bytes diff --git a/megatron/core/extensions/transformer_engine.py b/megatron/core/extensions/transformer_engine.py index 419c85b404d..869e0099fce 100644 --- a/megatron/core/extensions/transformer_engine.py +++ b/megatron/core/extensions/transformer_engine.py @@ -1924,11 +1924,13 @@ def _encode_extra_state(self, state): return state_serialized def _decode_extra_state(self, state): + from megatron.core.safe_globals import SafeUnpickler + if isinstance(state, torch.Tensor): # No FP8 is indicated by an empty tensor we don't need to unpickle. if state.numel() == 0: return - return pickle.loads(state.detach().cpu().numpy().tobytes()) + return SafeUnpickler(io.BytesIO(state.detach().cpu().numpy().tobytes())).load() elif isinstance(state, io.BytesIO): state.seek(0) return torch.load(state, map_location="cuda") diff --git a/megatron/core/safe_globals.py b/megatron/core/safe_globals.py index 790050749cd..9241405876a 100755 --- a/megatron/core/safe_globals.py +++ b/megatron/core/safe_globals.py @@ -1,5 +1,7 @@ # Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. +import io +import pickle from argparse import Namespace from io import BytesIO from pathlib import PosixPath @@ -41,3 +43,57 @@ def register_safe_globals(): """Register megatron-core safe classes with torch serialization.""" for cls in SAFE_GLOBALS: torch.serialization.add_safe_globals([cls]) + + +def safe_load_from_bytes(b): + """Safe version (weights_only=True) of `torch.storage._load_from_bytes`.""" + return torch.load(io.BytesIO(b), weights_only=True) + + +class SafeUnpickler(pickle.Unpickler): + """Restricted unpickler for FP8 extra-state checkpoints. + Only allows the narrow set of types that ``_encode_extra_state`` can + produce: plain Python containers, numeric scalars, and the PyTorch + tensor/storage primitives used by `pickle.dumps(tensor)`. Any attempt + to instantiate a class outside this allowlist raises + `pickle.UnpicklingError`, preventing arbitrary code execution via a + crafted checkpoint. + """ + + _SAFE_CLASSES: frozenset = frozenset( + { + ("builtins", "dict"), + ("builtins", "list"), + ("builtins", "tuple"), + ("builtins", "int"), + ("builtins", "float"), + ("builtins", "bool"), + ("builtins", "bytes"), + ("builtins", "str"), + ("collections", "OrderedDict"), + ("torch", "Size"), + ("torch._utils", "_rebuild_tensor_v2"), + ("torch._tensor", "_rebuild_from_type_v2"), + ("torch.storage", "UntypedStorage"), + ("torch.storage", "_load_from_bytes"), + ("transformer_engine.common.recipe", "DelayedScaling"), + ("transformer_engine.common.recipe", "Float8CurrentScaling"), + ("transformer_engine.common.recipe", "Float8BlockScaling"), + ("transformer_engine.common.recipe", "MXFP8BlockScaling"), + ("transformer_engine.common.recipe", "NVFP4BlockScaling"), + ("transformer_engine.common.recipe", "Format"), + ("transformer_engine.common.recipe", "_FormatHelper"), + ("transformer_engine.common.recipe", "MMParams"), + ("transformer_engine.common.recipe", "QParams"), + ("megatron.core.extensions.transformer_engine", "TEDelayedScaling"), + ("megatron.core.safe_globals", "safe_load_from_bytes"), + } + ) + + def find_class(self, module: str, name: str): + if (module, name) not in self._SAFE_CLASSES: + raise pickle.UnpicklingError( + f"Refusing to unpickle disallowed class '{module}.{name}' " + "in FP8 extra-state checkpoint." + ) + return super().find_class(module, name) diff --git a/tests/unit_tests/dist_checkpointing/test_safe_globals.py b/tests/unit_tests/dist_checkpointing/test_safe_globals.py index dc09b2a292e..6034648b600 100755 --- a/tests/unit_tests/dist_checkpointing/test_safe_globals.py +++ b/tests/unit_tests/dist_checkpointing/test_safe_globals.py @@ -1,6 +1,7 @@ # Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. -import os +import io +import pickle from argparse import Namespace from collections import OrderedDict from pickle import UnpicklingError @@ -8,6 +9,7 @@ import pytest import torch +from megatron.core.safe_globals import SafeUnpickler from megatron.core.utils import is_torch_min_version @@ -48,3 +50,16 @@ def test_unsafe_globals(self, tmp_path_dist_ckpt): # add class to safe globals torch.serialization.add_safe_globals([UnsafeClass]) torch.load(ckpt_path) + + +class TestSafeUnpickler: + def test_safe_types(self): + data = {"key": [1, 2.0, True, "s"], "od": OrderedDict(a=1)} + raw = pickle.dumps(data) + result = SafeUnpickler(io.BytesIO(raw)).load() + assert result == data + + def test_unsafe_types(self): + raw = pickle.dumps(UnsafeClass(123)) + with pytest.raises(pickle.UnpicklingError, match="Refusing to unpickle"): + SafeUnpickler(io.BytesIO(raw)).load() From 109feda4d4bd7d241ff2547824aee18c33c5dc48 Mon Sep 17 00:00:00 2001 From: Dmytro Pykhtar <37850217+dimapihtar@users.noreply.github.com> Date: Fri, 24 Apr 2026 18:08:26 +0300 Subject: [PATCH 019/105] get rid of weights_only=False (#4434) Signed-off-by: dimapihtar --- megatron/core/dist_checkpointing/strategies/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/megatron/core/dist_checkpointing/strategies/common.py b/megatron/core/dist_checkpointing/strategies/common.py index 0ae800e46f8..3fdab41b4b0 100644 --- a/megatron/core/dist_checkpointing/strategies/common.py +++ b/megatron/core/dist_checkpointing/strategies/common.py @@ -42,9 +42,9 @@ def load_common(checkpoint_dir: str): try: if MultiStorageClientFeature.is_enabled(): msc = MultiStorageClientFeature.import_package() - return msc.torch.load(load_path, map_location='cpu', weights_only=False) + return msc.torch.load(load_path, map_location='cpu') else: - return torch.load(load_path, map_location='cpu', weights_only=False) + return torch.load(load_path, map_location='cpu') except FileNotFoundError as e: err_msg = f'Common file {load_path} does not exist' if MultiStorageClientFeature.is_enabled(): From 64870c14577a84267b9c89dc130124ddf8a72d42 Mon Sep 17 00:00:00 2001 From: Lawrence McAfee <85179052+lmcafee-nvidia@users.noreply.github.com> Date: Fri, 24 Apr 2026 11:29:54 -0400 Subject: [PATCH 020/105] Inference | Per-block MoE routing storage for prefix caching (#4301) Co-authored-by: Siddharth Singh Co-authored-by: Claude Opus 4.6 (1M context) --- .../inference/contexts/kv_block_allocator.py | 131 +++++++++++ .../core/inference/engines/dynamic_engine.py | 43 ++-- megatron/core/inference/inference_request.py | 35 ++- .../text_generation_controller.py | 75 ++++--- .../contexts/test_dynamic_prefix_caching.py | 211 ++++++++++++++++++ 5 files changed, 432 insertions(+), 63 deletions(-) diff --git a/megatron/core/inference/contexts/kv_block_allocator.py b/megatron/core/inference/contexts/kv_block_allocator.py index 87039835c7f..4f2744d3582 100644 --- a/megatron/core/inference/contexts/kv_block_allocator.py +++ b/megatron/core/inference/contexts/kv_block_allocator.py @@ -3,6 +3,7 @@ from collections import deque from typing import Callable, Dict, Optional +import numpy as np import torch from torch import Tensor @@ -73,6 +74,9 @@ def __init__( (self.total_count,), dtype=torch.int64, device=torch.cuda.current_device() ) + # Per-block MoE routing storage (populated when routing replay is enabled) + self.block_routing: Dict[int, np.ndarray] = {} + def __str__(self): return ( f"using: total {self.get_total_used()}/{self.total_count - 1}" @@ -183,6 +187,10 @@ def allocate_memory_blocks(self, num_blocks: int) -> Optional[Tensor]: if self.prefix_caching_eviction_policy == PrefixCachingEvictionPolicy.LRU: self.update_timestamps(block_ids) + # Clear stale routing data for re-allocated blocks + for bid in block_ids.tolist(): + self.block_routing.pop(bid, None) + return block_ids def release_memory_blocks(self, blocks: Tensor) -> None: @@ -255,6 +263,9 @@ def reset(self) -> None: if self.prefix_caching_eviction_policy == PrefixCachingEvictionPolicy.LRU: self.block_timestamps.fill_(0) + # Clear per-block routing storage + self.block_routing.clear() + # ========================================================================= # Prefix caching methods # ========================================================================= @@ -358,3 +369,123 @@ def evict_lru_blocks(self, num_blocks_needed: int) -> bool: self._deregister_blocks(blocks_to_evict) return True + + # ========================================================================= + # Per-block routing storage methods (for MoE routing replay) + # ========================================================================= + + def store_routing_per_block(self, flat_routing: Optional[np.ndarray]) -> None: + """Scatter flat routing indices into per-block storage. + + Uses the context's token-to-block mapping to distribute each token's + routing data into the appropriate block. Matched (prefix-cached) blocks + already have routing from the original request and are not overwritten + here since their tokens are not in the active token layout. + + Args: + flat_routing: ndarray of shape [active_token_count, num_layers, topk] + aligned with the context's active-token layout, or None. + """ + if flat_routing is None: + return + + context = self.context + token_count = context.active_token_count + if token_count == 0: + return + + assert ( + flat_routing.shape[0] == token_count + ), f"Routing token count {flat_routing.shape[0]} != active token count {token_count}" + + # Token-to-block mapping for all active tokens + block_ids_np = context.token_to_block_idx[:token_count].cpu().numpy() + positions_np = context.token_to_local_position_within_kv_block[:token_count].cpu().numpy() + + dummy = self.dummy_block_idx + + # Group tokens by block_id using sort for efficient scatter + unique_blocks, inverse, counts = np.unique( + block_ids_np, return_inverse=True, return_counts=True + ) + sorted_indices = np.argsort(inverse, kind='stable') + sorted_positions = positions_np[sorted_indices] + sorted_routing = flat_routing[sorted_indices] + + offset = 0 + for bid, count in zip(unique_blocks, counts): + bid = int(bid) + count = int(count) + if bid == dummy: + offset += count + continue + block_pos = sorted_positions[offset : offset + count] + block_rout = sorted_routing[offset : offset + count] + self.store_block_routing(bid, block_pos, block_rout) + offset += count + + def reconstruct_routing_from_blocks( + self, block_ids: list[int], total_routing_tokens: int + ) -> Optional[np.ndarray]: + """Reconstruct routing indices from per-block storage. + + Concatenates per-block routing ndarrays in block order, trimming the + last block to exactly ``total_routing_tokens`` entries. + + Args: + block_ids: Ordered list of block IDs for the request. + total_routing_tokens: Expected number of routing tokens + (total_tokens - 1, since the last generated token has no + forward-pass routing). + + Returns: + ndarray [total_routing_tokens, num_layers, topk] or None if any + block is missing routing data. + """ + block_size = self.context.block_size_tokens + routing_parts = [] + tokens_collected = 0 + + for bid in block_ids: + routing = self.get_block_routing(bid) + if routing is None: + return None # Missing routing data for this block + remaining = total_routing_tokens - tokens_collected + if remaining <= 0: + break + take = min(block_size, remaining) + routing_parts.append(routing[:take]) + tokens_collected += take + + if not routing_parts or tokens_collected != total_routing_tokens: + return None + + return np.concatenate(routing_parts, axis=0) + + def store_block_routing( + self, block_id: int, positions: np.ndarray, routing: np.ndarray + ) -> None: + """Store routing indices for specific token positions in a block. + + Args: + block_id: The block ID. + positions: ndarray of token positions within the block (1D, int). + routing: ndarray of routing data [num_positions, num_layers, topk]. + """ + if block_id not in self.block_routing: + self.block_routing[block_id] = np.zeros( + (self.context.block_size_tokens, routing.shape[-2], routing.shape[-1]), + dtype=routing.dtype, + ) + self.block_routing[block_id][positions] = routing + + def get_block_routing(self, block_id: int) -> Optional[np.ndarray]: + """Get routing indices for a block. + + Args: + block_id: The block ID. + + Returns: + ndarray [block_size_tokens, num_layers, topk] or None if not stored. + """ + return self.block_routing.get(block_id) diff --git a/megatron/core/inference/engines/dynamic_engine.py b/megatron/core/inference/engines/dynamic_engine.py index ec2bc5b630d..97f3e098c2b 100644 --- a/megatron/core/inference/engines/dynamic_engine.py +++ b/megatron/core/inference/engines/dynamic_engine.py @@ -1035,9 +1035,9 @@ def post_process_requests( accepted_tokens: torch.Tensor, log_probs: torch.Tensor, top_n_logprobs: Optional[Dict[int, List[Tuple[torch.Tensor, torch.Tensor]]]] = None, - routing_indices_per_request: Optional[Dict[int, torch.Tensor]] = None, pre_fwd_active_token_count: Optional[int] = None, pre_fwd_step_count: Optional[int] = None, + finished_routing_block_ids: Optional[Dict[int, list[int]]] = None, ) -> Tuple[List[DynamicInferenceRequest], List[DynamicInferenceRequest]]: """ Handles post-processing for requests after a step. @@ -1052,9 +1052,9 @@ def post_process_requests( log_probs: (List): Log probs for each request top_n_logprobs: (Dict): Top-n log probs for each request. Maps request_idx to list of (top_n_logprobs, top_n_indices) tuples. - routing_indices_per_request: (Dict[int, Tensor]): MoE routing indices - pre-mapped by request_id. Each value is a tensor of shape - [num_tokens_this_step, num_layers, topk]. + finished_routing_block_ids: (Dict[int, List[int]]): Block IDs for + finished requests, saved before update_requests released them. + Used for per-block routing reconstruction. Returns: A list of active requests and completed requests as `DynamicInferenceRequest` objects @@ -1183,6 +1183,20 @@ def post_process_requests( self._spec_tokens_accepted += actual_accepted if request_id in finished_request_ids: + # Reconstruct routing from per-block storage before popping. + if ( + finished_routing_block_ids + and request_id in finished_routing_block_ids + and len(self.requests[request_id].record.requests) == 1 + ): + block_ids = finished_routing_block_ids[request_id] + total_tokens = len(request.prompt_tokens) + len(request.generated_tokens) + request.routing_indices = ( + self.context.kv_block_allocator.reconstruct_routing_from_blocks( + block_ids, total_tokens - 1 + ) + ) + # Request finished by normal means (termination_id, max_length, or stop word from previous step) request.generated_length = len(request.generated_tokens) request.status = Status.COMPLETED @@ -1292,23 +1306,6 @@ def post_process_requests( else: request.generated_top_n_logprobs.append(logit_dict) - # Process routing indices if available (keyed by request_id) - # Each step's routing is a tensor of shape [num_tokens_this_step, num_layers, topk] - # We concatenate along dim=0 to accumulate: [total_tokens, num_layers, topk] - if ( - routing_indices_per_request is not None - and request_id in routing_indices_per_request - ): - step_routing = routing_indices_per_request[ - request_id - ] # [num_tokens, num_layers, topk] - if request.routing_indices is None: - request.routing_indices = step_routing.clone() - else: - request.routing_indices = torch.cat( - [request.routing_indices, step_routing], dim=0 - ) - # Handle evicted requests. if evict_request_ids is not None and evict_request_ids.numel() > 0: @@ -1728,7 +1725,7 @@ async def async_bookkeep( accepted_tokens = step_result["accepted_tokens"] log_probs = step_result["log_probs"] top_n_logprobs = step_result.get("top_n_logprobs", None) - routing_indices_per_request = step_result.get("routing_indices_per_request", None) + finished_routing_block_ids = step_result.get("finished_routing_block_ids", None) cuda_graph_request_count = step_result["cuda_graph_request_count"] # Add paused events. @@ -1746,9 +1743,9 @@ async def async_bookkeep( accepted_tokens, log_probs, top_n_logprobs, - routing_indices_per_request, pre_fwd_active_token_count=context_state.get("active_token_count"), pre_fwd_step_count=context_state.get("step_count"), + finished_routing_block_ids=finished_routing_block_ids, ) else: diff --git a/megatron/core/inference/inference_request.py b/megatron/core/inference/inference_request.py index 4c93b9024d5..50099695120 100644 --- a/megatron/core/inference/inference_request.py +++ b/megatron/core/inference/inference_request.py @@ -8,6 +8,7 @@ from itertools import accumulate from typing import Any, Dict, List, Optional, Tuple +import numpy as np import torch from megatron.core.inference.sampling_params import SamplingParams @@ -46,6 +47,16 @@ def deserialize_tensor(tensor_as_list: List) -> torch.Tensor: return tensor +def serialize_ndarray(arr: np.ndarray) -> dict: + """Serialize numpy array to a JSON-compatible dict.""" + return {"data": arr.tolist(), "dtype": str(arr.dtype)} + + +def deserialize_ndarray(obj: dict) -> np.ndarray: + """Deserialize numpy array from dict.""" + return np.array(obj["data"], dtype=np.dtype(obj["dtype"])) + + def unwrap_serialized_tensors(serialized_request: dict) -> dict: """Unwrap ("tensor", [...]) tuples produced by serialize() into plain lists. @@ -180,9 +191,13 @@ def serialize(self) -> dict: self.inference_parameters.serialize() if self.inference_parameters else None ) - # Serialize tensors. + # Serialize tensors and numpy arrays. obj = { - k: (("tensor", serialize_tensor(v)) if isinstance(v, torch.Tensor) else v) + k: ( + ("tensor", serialize_tensor(v)) + if isinstance(v, torch.Tensor) + else ("ndarray", serialize_ndarray(v)) if isinstance(v, np.ndarray) else v + ) for k, v in obj.items() } return obj @@ -221,10 +236,12 @@ def _post_deserialize(self, obj: dict): else SamplingParams.deserialize(obj["inference_parameters"]) ) - # Deserialize tensors and sampling params. + # Deserialize tensors, numpy arrays, and sampling params. for k, v in obj.items(): if isinstance(v, list) and len(v) == 2 and v[0] == "tensor": setattr(self, k, deserialize_tensor(v[1])) + elif isinstance(v, list) and len(v) == 2 and v[0] == "ndarray": + setattr(self, k, deserialize_ndarray(v[1])) class DynamicInferenceEventType(Enum): @@ -361,9 +378,8 @@ class DynamicInferenceRequest(InferenceRequest): policy_epoch: Optional[list[tuple[int, int]]] = None kv_cache_epoch: Optional[list[tuple[int, int]]] = None latency: Optional[float] = None - # routing_indices stores MoE routing decisions for all tokens generated so far. - # Shape: [total_tokens, num_layers, topk] - accumulated across all generation steps - routing_indices: Optional[torch.Tensor] = None + # routing_indices is reconstructed from per-block storage when a request finishes. + routing_indices: Optional[np.ndarray] = None finished_chunk_token_count: int = 0 stop_word_ids: Optional[List[List[int]]] = None # Tokenized stop words (populated internally) @@ -434,7 +450,7 @@ def serialize(self): obj["events"] = [e.serialize() for e in self.events] obj.pop("event_add_engine", None) - # Sanity check routing_indices: Tensor [total_tokens - 1, num_layers, topk] + # Sanity check routing_indices: ndarray [total_tokens - 1, num_layers, topk] if self.routing_indices is not None: total_tokens = len(self.prompt_tokens) + len(self.generated_tokens) # the last generated token does not undergo a forward pass @@ -695,8 +711,9 @@ def merge_lists(key): prompt_tokens = self.requests[0].prompt_tokens prompt_text = self.requests[0].prompt routing_indices = None - if self.requests[0].routing_indices is not None: - routing_indices = torch.cat([r.routing_indices for r in self.requests]) + routing_parts = [r.routing_indices for r in self.requests if r.routing_indices is not None] + if routing_parts: + routing_indices = np.concatenate(routing_parts) generated_tokens = merge_lists("generated_tokens") try: generated_text = "".join(r.generated_text for r in self.requests) diff --git a/megatron/core/inference/text_generation_controllers/text_generation_controller.py b/megatron/core/inference/text_generation_controllers/text_generation_controller.py index abf1bbf585b..2798533e783 100644 --- a/megatron/core/inference/text_generation_controllers/text_generation_controller.py +++ b/megatron/core/inference/text_generation_controllers/text_generation_controller.py @@ -7,6 +7,7 @@ from collections import defaultdict from typing import Any, Dict, List, Optional, OrderedDict, Tuple, Union +import numpy as np import torch import torch.nn.functional as F from torch import Tensor @@ -1227,18 +1228,20 @@ def _dynamic_step_log_probs_bookkeeping(self) -> Tuple[bool, bool]: return return_log_probs.any(), top_n_log_probs.any() - def _router_record_bookkeeping(self) -> Optional[Dict[int, Tensor]]: - """Collect and map routing indices per request for MoE router recording. + def _router_record_bookkeeping(self) -> Optional[np.ndarray]: + """Collect flat routing indices for MoE router recording. - This method retrieves recorded routing decisions and maps them to individual - requests using the context's request_ids and query_lengths. Uses the context's - routing_metadata when available (which handles CUDA graph static buffers automatically). - Must be called while context attributes are still valid (before request transitions). + Retrieves recorded routing decisions via the context's routing_metadata + (which handles CUDA graph static buffers), performs the TP all-gather + when sequence parallelism is active, strips CUDA padding, and returns + a flat CPU numpy array aligned with the context's active-token layout. + Must be called while context attributes are still valid (before request + transitions). Returns: - Optional[Dict[int, Tensor]]: A dictionary mapping request_id to a tensor of - shape [num_tokens, num_layers, topk]. Returns None if routing replay is - disabled or no routing data was recorded. + Optional[np.ndarray]: Flat routing array of shape + [active_token_count, num_layers, topk], or None if routing + replay is disabled or no routing data was recorded. """ config = self.inference_wrapped_model.model.config if not config.moe_enable_routing_replay: @@ -1254,10 +1257,6 @@ def _router_record_bookkeeping(self) -> Optional[Dict[int, Tensor]]: if stacked_routing is None: return None - # Get active request info from context - active_request_slice = slice(context.paused_request_count, context.total_request_count) - active_request_ids = context.request_ids[active_request_slice].tolist() - active_query_lengths = context.request_query_lengths[active_request_slice].tolist() active_token_count = context.active_token_count # Get TP group for all-gather if using sequence parallelism @@ -1268,24 +1267,25 @@ def _router_record_bookkeeping(self) -> Optional[Dict[int, Tensor]]: # All-gather across TP group if using sequence parallelism (tp_size > 1) if tp_size > 1 and get_model_config(self.inference_wrapped_model.model).sequence_parallel: + # With SP, the model processes padded_active_token_count tokens total, + # scattered evenly across TP ranks. Each rank routes + # padded_active_token_count // tp_size tokens through MoE layers. + # + # The CUDA-graph static buffer path in get_routing_indices() may return + # a tensor sliced to active_token_count (the global unpadded count), + # which can be larger than the per-rank valid count. Truncate to the + # true per-rank count before the all-gather so we only gather valid + # routing data and reconstruct the full sequence in the correct order. + local_token_count = context.padded_active_token_count // tp_size + + stacked_routing = stacked_routing[:local_token_count] # gather_from_sequence_parallel_region gathers along dim 0 - # [local_token_count, num_layers, topk] -> [global_token_count, num_layers, topk] + # [local_token_count, num_layers, topk] -> [padded_token_count, num_layers, topk] stacked_routing = gather_from_sequence_parallel_region(stacked_routing, group=tp_group) - # Slice to real tokens (remove CUDA padding) - stacked_routing = stacked_routing[:active_token_count] - - # Split by request along token dimension - # stacked_routing has shape [active_token_count, num_layers, topk] - routing_splits = stacked_routing.split(active_query_lengths, dim=0) - - # Map to request IDs - routing_indices_per_request = {} - for req_id, routing_split in zip(active_request_ids, routing_splits): - # routing_split has shape [num_tokens_for_request, num_layers, topk] - routing_indices_per_request[req_id] = routing_split - - return routing_indices_per_request + # Slice to real tokens (remove CUDA padding), move to CPU as numpy with target dtype + _ri_dtype = np.int16 if (config.num_moe_experts or 0) <= 32768 else np.int32 + return stacked_routing[:active_token_count].cpu().numpy().astype(_ri_dtype) def _dynamic_step_calculate_log_probs(self, logits: Tensor) -> Optional[Tensor]: """Calculate log probs from logits.""" @@ -1785,6 +1785,17 @@ def _dynamic_step_context_bookkeeping(self) -> Dict[str, Tensor]: ) finished_request_ids = context.request_ids[finished_idxs] + # Save block IDs for finished requests before update_requests releases them. + # Needed for per-block routing reconstruction in the engine. + finished_routing_block_ids = {} + if context.kv_block_allocator.block_routing and finished_idxs.numel() > 0: + for fidx in finished_idxs.tolist(): + req_id = int(context.request_ids[fidx].item()) + blocks = context.request_to_kv_block_ids[fidx] + valid = blocks[blocks >= 0].tolist() + if valid: + finished_routing_block_ids[req_id] = valid + # Clone needed: update_requests mutates next_tokens in-place via tensor_swap, # which would corrupt the reused _sampled_tokens_cuda buffer. new_sample_copy = self._sampled_tokens_cuda[:active_request_count].clone() @@ -1802,6 +1813,7 @@ def _dynamic_step_context_bookkeeping(self) -> Dict[str, Tensor]: return { "active_request_ids": active_request_ids, "finished_request_ids": finished_request_ids, + "finished_routing_block_ids": finished_routing_block_ids, **(update_result or {}), } @@ -1854,8 +1866,10 @@ async def async_generate_output_tokens_dynamic_batch( if context.is_hybrid_model and context.mamba_slot_allocator is not None: context.mamba_slot_allocator.commit_intermediate_states() - # Collect routing indices per request (must be done before context transitions) - routing_indices_per_request = self._router_record_bookkeeping() + # Collect flat routing indices and scatter them into per-block storage. + # Must be done before update_requests while token-to-block mappings are valid. + # Reconstruction happens from blocks at request completion. + context.kv_block_allocator.store_routing_per_block(self._router_record_bookkeeping()) # This is the best place to yield control back to event loop. # At this point we have enqueued FW pass GPU kernels asynchronously. @@ -1921,7 +1935,6 @@ async def async_generate_output_tokens_dynamic_batch( ), "log_probs": log_probs, "top_n_logprobs": top_n_logprobs, - "routing_indices_per_request": routing_indices_per_request, "cuda_graph_request_count": cuda_graph_request_count, } if self.num_speculative_tokens > 0: diff --git a/tests/unit_tests/inference/contexts/test_dynamic_prefix_caching.py b/tests/unit_tests/inference/contexts/test_dynamic_prefix_caching.py index 049c0e5e040..9fc9ac9aac3 100644 --- a/tests/unit_tests/inference/contexts/test_dynamic_prefix_caching.py +++ b/tests/unit_tests/inference/contexts/test_dynamic_prefix_caching.py @@ -3,6 +3,7 @@ import asyncio from collections import deque +import numpy as np import pytest import torch @@ -1084,3 +1085,213 @@ def test_commit_intermediate_states_batched(self): # Verify _has_intermediates cleared assert not msa._has_intermediates + + +class TestPerBlockRouting(PrefixCachingTestBase): + """Tests for per-block routing storage and reconstruction.""" + + @pytest.mark.internal + def test_store_and_get_block_routing(self): + """Verify store_block_routing / get_block_routing round-trip.""" + ctx = self._ctx() + alloc = ctx.kv_block_allocator + bs = ctx.block_size_tokens + num_layers, topk = 4, 2 + + # Allocate a block + block_ids = alloc.allocate_memory_blocks(1) + bid = block_ids[0].item() + + # Store routing for some positions + positions = np.array([0, 1, 2]) + routing = np.random.randint(-100, 100, size=(3, num_layers, topk), dtype=np.int16) + alloc.store_block_routing(bid, positions, routing) + + # Retrieve and verify + stored = alloc.get_block_routing(bid) + assert stored is not None + assert isinstance(stored, np.ndarray) + assert stored.shape == (bs, num_layers, topk) + assert np.allclose(stored[:3], routing) + # Remaining positions should be zero + assert (stored[3:] == 0).all() + + @pytest.mark.internal + def test_routing_cleared_on_allocate(self): + """Routing data is cleared when a block is re-allocated.""" + ctx = self._ctx(enable_prefix_caching=False) + alloc = ctx.kv_block_allocator + + # Allocate, store routing, release, re-allocate + block_ids = alloc.allocate_memory_blocks(1) + bid = block_ids[0].item() + positions = np.array([0]) + routing = np.random.randint(-100, 100, size=(1, 4, 2), dtype=np.int16) + alloc.store_block_routing(bid, positions, routing) + assert alloc.get_block_routing(bid) is not None + + alloc.release_memory_blocks(block_ids) + # After release, routing still present (persists until re-alloc) + assert alloc.get_block_routing(bid) is not None + + # Re-allocate the same block + new_ids = alloc.allocate_memory_blocks(1) + new_bid = new_ids[0].item() + # The re-allocated block should have routing cleared + assert alloc.get_block_routing(new_bid) is None + + @pytest.mark.internal + def test_routing_cleared_on_reset(self): + """Routing data is cleared on allocator reset.""" + ctx = self._ctx() + alloc = ctx.kv_block_allocator + + block_ids = alloc.allocate_memory_blocks(1) + bid = block_ids[0].item() + alloc.store_block_routing( + bid, np.array([0]), np.random.randint(-100, 100, size=(1, 4, 2), dtype=np.int16) + ) + assert alloc.get_block_routing(bid) is not None + + alloc.reset() + assert alloc.get_block_routing(bid) is None + assert len(alloc.block_routing) == 0 + + @pytest.mark.internal + def test_routing_persists_through_deregister(self): + """Routing data persists through block deregister (needed for reconstruction).""" + ctx = self._ctx(prefix_caching_eviction_policy=PrefixCachingEvictionPolicy.REF_ZERO) + alloc = ctx.kv_block_allocator + bs = ctx.block_size_tokens + + # Add a request so blocks get allocated and registered + prompt = self._prompt(bs * 2) + req = self._req(ctx, prompt) + ctx.add_request(req) + b0, b1 = self._block_ids(ctx, 0, 2) + + # Store routing for both blocks + for bid in [b0, b1]: + alloc.store_block_routing( + bid, np.arange(bs), np.random.randint(-100, 100, size=(bs, 4, 2), dtype=np.int16) + ) + + # Release blocks (REF_ZERO deregisters immediately) + blocks = ctx.request_to_kv_block_ids[0] + valid_blocks = blocks[blocks >= 0] + alloc.release_memory_blocks(valid_blocks) + + # Routing data should still be present + assert alloc.get_block_routing(b0) is not None + assert alloc.get_block_routing(b1) is not None + + @pytest.mark.internal + def test_reconstruct_routing_from_blocks(self): + """Test reconstruction of routing indices from per-block storage.""" + ctx = self._ctx() + alloc = ctx.kv_block_allocator + bs = ctx.block_size_tokens + num_layers, topk = 4, 2 + + # Allocate 3 blocks + block_ids = alloc.allocate_memory_blocks(3) + bids = block_ids.tolist() + + # Store routing for all positions in first two blocks (full) + for bid in bids[:2]: + alloc.store_block_routing( + bid, + np.arange(bs), + np.arange(bs * num_layers * topk, dtype=np.int16).reshape(bs, num_layers, topk) + + bid, + ) + + # Store routing for partial last block (e.g., 5 tokens) + partial = 5 + alloc.store_block_routing( + bids[2], + np.arange(partial), + np.arange(partial * num_layers * topk, dtype=np.int16).reshape( + partial, num_layers, topk + ) + + bids[2], + ) + + # total_routing_tokens = 2 full blocks + 5 partial = 2*bs + 5 + total_routing_tokens = 2 * bs + partial + + result = alloc.reconstruct_routing_from_blocks(bids, total_routing_tokens) + + assert result is not None + assert isinstance(result, np.ndarray) + assert result.shape == (total_routing_tokens, num_layers, topk) + + # Verify content: first block + expected_b0 = ( + np.arange(bs * num_layers * topk, dtype=np.int16).reshape(bs, num_layers, topk) + + bids[0] + ) + assert np.allclose(result[:bs], expected_b0) + + # Verify content: partial last block + expected_partial = ( + np.arange(partial * num_layers * topk, dtype=np.int16).reshape( + partial, num_layers, topk + ) + + bids[2] + ) + assert np.allclose(result[2 * bs :], expected_partial) + + @pytest.mark.internal + def test_reconstruct_returns_none_for_missing_block(self): + """Reconstruction returns None if a block has no routing data.""" + ctx = self._ctx() + alloc = ctx.kv_block_allocator + bs = ctx.block_size_tokens + + block_ids = alloc.allocate_memory_blocks(2) + bids = block_ids.tolist() + + # Only store routing for the first block + alloc.store_block_routing( + bids[0], np.arange(bs), np.random.randint(-100, 100, size=(bs, 4, 2), dtype=np.int16) + ) + + result = alloc.reconstruct_routing_from_blocks(bids, 2 * bs) + assert result is None + + @pytest.mark.internal + def test_routing_survives_prefix_match_lru(self): + """In LRU mode, matched blocks' routing persists for the new request.""" + ctx = self._ctx(prefix_caching_eviction_policy=PrefixCachingEvictionPolicy.LRU) + alloc = ctx.kv_block_allocator + bs = ctx.block_size_tokens + + # First request: 2 full blocks + prompt = self._prompt(bs * 2) + req1 = self._req(ctx, prompt, request_id=1) + ctx.add_request(req1) + b0, b1 = self._block_ids(ctx, 0, 2) + + # Store routing for both blocks + routing_b0 = np.random.randint(-100, 100, size=(bs, 4, 2), dtype=np.int16) + routing_b1 = np.random.randint(-100, 100, size=(bs, 4, 2), dtype=np.int16) + alloc.store_block_routing(b0, np.arange(bs), routing_b0) + alloc.store_block_routing(b1, np.arange(bs), routing_b1) + + # Release first request's blocks (LRU: blocks stay cached) + blocks = ctx.request_to_kv_block_ids[0] + valid_blocks = blocks[blocks >= 0] + active_mask = torch.zeros(1, device=torch.cuda.current_device(), dtype=torch.int32) + new_tokens = torch.tensor([100], device=torch.cuda.current_device()) + ctx.update_requests(active_mask, new_tokens) + + # Second request with same prefix should match + req2 = self._req(ctx, prompt.clone(), request_id=2) + ctx.add_request(req2) + + # The matched blocks should still have routing data + assert alloc.get_block_routing(b0) is not None + assert np.allclose(alloc.get_block_routing(b0), routing_b0) + assert alloc.get_block_routing(b1) is not None + assert np.allclose(alloc.get_block_routing(b1), routing_b1) From 017e684c722948078a6b78e9c2a5ca85b99f3fcd Mon Sep 17 00:00:00 2001 From: Ajay <11775318+balasaajay@users.noreply.github.com> Date: Fri, 24 Apr 2026 09:24:47 -0700 Subject: [PATCH 021/105] Add troubleshooting tip for 'access forbidden' (#4449) --- skills/build-and-dependency/SKILL.md | 1 + 1 file changed, 1 insertion(+) diff --git a/skills/build-and-dependency/SKILL.md b/skills/build-and-dependency/SKILL.md index c7dc89ec1ad..7c8dc252e54 100644 --- a/skills/build-and-dependency/SKILL.md +++ b/skills/build-and-dependency/SKILL.md @@ -209,3 +209,4 @@ files before committing (repo CLAUDE.md requirement). | `No space left on device` during uv ops | Cache fills container's `/root/.cache/` | Mount a host cache dir via `-v $HOME/.cache/uv:/root/.cache/uv` | | Pre-commit fails with linting errors | Code style violations | Run `BASE_REF=main CHECK_ONLY=false bash tools/autoformat.sh` | | `docker build` fails with secret-related error | `Dockerfile.ci.dev` has a `jet` stage that requires an internal secret | Add `--target main` to stop before the `jet` stage | +| `access forbidden` when pulling | Registry URL includes an explicit port (e.g. `:5005`) | Use `${GITLAB_HOST}/adlr/...` with no port — the sed extracts the hostname only | From 3d7bcd3a373f9068959de30078abf2526a7883d2 Mon Sep 17 00:00:00 2001 From: Fei <33940270+YangFei1990@users.noreply.github.com> Date: Fri, 24 Apr 2026 11:46:09 -0700 Subject: [PATCH 022/105] Fix checkpoint loading with rerun state machine (#4448) --- megatron/core/rerun_state_machine.py | 43 ++++- .../test_rerun_state_machine_ckpt.py | 157 ++++++++++++++++++ 2 files changed, 192 insertions(+), 8 deletions(-) create mode 100644 tests/unit_tests/dist_checkpointing/test_rerun_state_machine_ckpt.py diff --git a/megatron/core/rerun_state_machine.py b/megatron/core/rerun_state_machine.py index 77a973401d5..c928adef7e8 100644 --- a/megatron/core/rerun_state_machine.py +++ b/megatron/core/rerun_state_machine.py @@ -774,8 +774,13 @@ def state_dict( data_iterator: the data iterator that needs to be checkpointed (or None if this checkpoint is not requested by the rerun state machine). ckpt_format: the checkpoint format to use. + force: if True, emit the full state dict even when the machine is + disabled or no rerun is pending (used on the load path to + build a matching template). Returns: - A state dict representing the rerun state machine. + A state dict representing the rerun state machine, or None if + rerun checkpointing is not applicable (mode is DISABLED, or the + checkpoint format does not support ShardedObject). Example usage: @@ -790,11 +795,18 @@ def save_my_model_checkpoint(data_iterator, ...): return checkpoint """ - # Only save a checkpoint if a step needs to be rerun. + # Short-circuits only apply on the save path. On the load path + # (``force=True``) we build a template that mirrors whatever the + # checkpoint happens to contain, regardless of the current mode or + # ckpt_format -- this preserves the behavior the load path in + # ``checkpointing.py`` has always relied on. if not force: - if self.state == RerunState.NOT_RUNNING_YET: + # Disabled mode never triggers a rerun workflow, so there's + # nothing to persist across a restart; keep returning None to + # avoid bloating the checkpoint. + if self.mode == RerunMode.DISABLED: return None - + # ShardedObject is only supported by the torch_dist format. if ckpt_format != "torch_dist": log_single_rank( logger, @@ -804,11 +816,28 @@ def save_my_model_checkpoint(data_iterator, ...): ) return None - data_iterators: list[RerunDataIterator] = self._sanitize_data_iterators(data_iterator) + # Data-iterator buffers only need to be checkpointed when a rerun is + # actually pending. In steady state we skip sanitization so the + # caller isn't required to have wrapped its iterator in + # ``RerunDataIterator`` -- that requirement only applies when a + # fault is mid-flight. + if self.state == RerunState.NOT_RUNNING_YET and not force: + data_iterator_checkpoints = None + else: + data_iterators: list[RerunDataIterator] = self._sanitize_data_iterators(data_iterator) + data_iterator_checkpoints = ( + [d.state_dict() for d in data_iterators] if data_iterators else None + ) # When saving a step to re-run, the RerunStateMachine state is different across all ranks. # We keep the common state in the non-sharded (common) checkpoint and move the rank-level # state to a sharded object. + # In NOT_RUNNING_YET this is all zero/None defaults (a sentinel); + # in WILL_RERUN_FROM_CHECKPOINT it carries the real fault context. The + # ShardedObject key/shape/offset are identical in both cases. This keeps the + # checkpoint's sharded structure constant across saves (a + # requirement of ``--ckpt-assume-constant-structure``) + # For details, see GitHub issue NVIDIA/Megatron-LM#4378. sharded_dict = { "rerun_requested": self.rerun_requested, "checkpoint_requested": self.checkpoint_requested, @@ -822,9 +851,7 @@ def save_my_model_checkpoint(data_iterator, ...): "suspicious_node": self.suspicious_node, "suspicious_device": self.suspicious_device, # No need to save saved_state (RNG state already captured in checkpoint). - "data_iterator_checkpoints": ( - [d.state_dict() for d in data_iterators] if data_iterators else None - ), + "data_iterator_checkpoints": data_iterator_checkpoints, "large_value_counts": self.large_value_counts, "max_values": self.max_values, # No need to save saved_results and stats (resets when job resumes). diff --git a/tests/unit_tests/dist_checkpointing/test_rerun_state_machine_ckpt.py b/tests/unit_tests/dist_checkpointing/test_rerun_state_machine_ckpt.py new file mode 100644 index 00000000000..07a571e7d1b --- /dev/null +++ b/tests/unit_tests/dist_checkpointing/test_rerun_state_machine_ckpt.py @@ -0,0 +1,157 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +import torch + +from megatron.core.dist_checkpointing.mapping import ShardedObject +from megatron.core.rerun_state_machine import RerunMode, RerunState, RerunStateMachine +from tests.unit_tests.test_utilities import Utils + + +class TestRerunStateMachineCheckpointContract: + """Unit tests for ``RerunStateMachine.state_dict()``. + + These are fast, single-process tests that directly exercise the + ``state_dict()`` contract. They do not require a full save/load round + trip because the bug lives entirely in the structure returned by + ``state_dict()`` on the *first* save, which is what gets cached by + ``TorchDistSaveShardedStrategy`` when + ``--ckpt-assume-constant-structure`` is set. + """ + + def setup_method(self, method): + Utils.initialize_distributed() + + def teardown_method(self, method): + pass + + def _assert_sharded_object_shape(self, sh_obj): + """The ShardedObject must be uniquely keyed per rank with a shape + equal to the world size; this is what lets torch DCP reconstruct + the global object on load.""" + assert isinstance(sh_obj, ShardedObject) + assert sh_obj.key == "rerun_state_machine_state" + assert sh_obj.global_shape == (torch.distributed.get_world_size(),) + assert sh_obj.global_offset == (torch.distributed.get_rank(),) + + def test_steady_state_emits_sharded_object(self): + """Regression test for issue #4378. + + In the steady state (no pending rerun), ``state_dict()`` used to + return ``None`` which left the cached SavePlan with no entry for + ``rerun_state_machine_state``. After the fix, ``state_dict()`` must + emit a ShardedObject sentinel so the plan always includes the + rerun shard from the first save onwards. + """ + machine = RerunStateMachine(mode=RerunMode.VALIDATE_RESULTS) + assert machine.state == RerunState.NOT_RUNNING_YET + + sd = machine.state_dict(data_iterator=None, ckpt_format="torch_dist") + + assert sd is not None, ( + "state_dict() must not return None in the steady state when rerun is" + " enabled; otherwise --ckpt-assume-constant-structure caches a plan" + " that is missing the rerun_state_machine_state shard and the fault" + " save silently drops it (issue #4378)." + ) + assert set(sd.keys()) >= {"mode", "state", "current_iteration", "sharded"} + assert sd["state"] == RerunState.NOT_RUNNING_YET + self._assert_sharded_object_shape(sd["sharded"]) + + def test_fault_state_emits_sharded_object(self): + """When a fault is in flight, ``state_dict()`` continues to emit + the same ShardedObject structure, now carrying the real fault + payload instead of the sentinel values.""" + machine = RerunStateMachine(mode=RerunMode.VALIDATE_RESULTS) + machine.state = RerunState.WILL_RERUN_FROM_CHECKPOINT + machine.rerun_requested = True + machine.checkpoint_requested = True + + sd = machine.state_dict(data_iterator=None, ckpt_format="torch_dist") + + assert sd is not None + assert sd["state"] == RerunState.WILL_RERUN_FROM_CHECKPOINT + self._assert_sharded_object_shape(sd["sharded"]) + + def test_structure_constant_across_rerun_transition(self): + """This is the core invariant the Option-2 fix establishes: the + ShardedObject's key / global_shape / global_offset are identical + across the steady-state save and a subsequent fault save, so the + cached SavePlan built on the first save remains valid when a fault + triggers the second save.""" + machine = RerunStateMachine(mode=RerunMode.VALIDATE_RESULTS) + + # Save #1: steady state (mirrors every normal checkpoint during a + # healthy run). + sd_steady = machine.state_dict(data_iterator=None, ckpt_format="torch_dist") + + # Simulate the transition performed by should_run_forward_backward + # when a mismatching rerun result demands a fault checkpoint. + machine.state = RerunState.WILL_RERUN_FROM_CHECKPOINT + machine.rerun_requested = True + machine.checkpoint_requested = True + + # Save #2: fault save, performed in the same process with the same + # (already-cached) TorchDistSaveShardedStrategy. + sd_fault = machine.state_dict(data_iterator=None, ckpt_format="torch_dist") + + steady_obj = sd_steady["sharded"] + fault_obj = sd_fault["sharded"] + assert steady_obj.key == fault_obj.key + assert steady_obj.global_shape == fault_obj.global_shape + assert steady_obj.global_offset == fault_obj.global_offset + # Sanity: the payloads do differ between the two saves (sentinel vs + # real fault context). torch DCP caches structure, not contents, so + # this is fine. + assert steady_obj.data["rerun_requested"] is False + assert fault_obj.data["rerun_requested"] is True + + def test_steady_state_does_not_require_wrapped_data_iterator(self): + """In the steady state we skip ``_sanitize_data_iterators`` so the + caller isn't forced to wrap its training iterator in + ``RerunDataIterator`` just to satisfy checkpointing. The + requirement to wrap only kicks in once a rerun is pending.""" + machine = RerunStateMachine(mode=RerunMode.VALIDATE_RESULTS) + + # An unwrapped iterator would assert inside + # _sanitize_data_iterators. In steady state it must be accepted. + sd = machine.state_dict(data_iterator=iter([1, 2, 3]), ckpt_format="torch_dist") + + assert sd is not None + assert sd["sharded"].data["data_iterator_checkpoints"] is None + + def test_disabled_mode_returns_none(self): + """When the rerun state machine is disabled, ``state_dict()`` + returns ``None`` so disabled jobs don't pay any checkpoint + overhead.""" + machine = RerunStateMachine(mode=RerunMode.DISABLED) + + sd = machine.state_dict(data_iterator=None, ckpt_format="torch_dist") + + assert sd is None + + def test_non_torch_dist_format_returns_none(self): + """``ShardedObject`` is only supported by the ``torch_dist`` + format; for other formats ``state_dict()`` returns ``None`` + regardless of machine state.""" + machine = RerunStateMachine(mode=RerunMode.VALIDATE_RESULTS) + assert machine.state_dict(data_iterator=None, ckpt_format="torch") is None + + machine.state = RerunState.WILL_RERUN_FROM_CHECKPOINT + assert machine.state_dict(data_iterator=None, ckpt_format="torch") is None + + def test_force_overrides_short_circuits(self): + """``force=True`` is used on the load path to build a template + that mirrors whatever the checkpoint happens to contain. It + bypasses both the DISABLED short-circuit and the ckpt_format + short-circuit, matching the pre-existing load-side contract in + ``megatron/training/checkpointing.py``.""" + machine = RerunStateMachine(mode=RerunMode.DISABLED) + + sd = machine.state_dict(data_iterator=None, ckpt_format="torch_dist", force=True) + assert sd is not None + self._assert_sharded_object_shape(sd["sharded"]) + + # For legacy formats used on the load path (e.g. fsdp_dtensor), + # force=True still produces the template. + sd = machine.state_dict(data_iterator=None, ckpt_format="fsdp_dtensor", force=True) + assert sd is not None + self._assert_sharded_object_shape(sd["sharded"]) From 9b022065a85e5d1ed82b863018a5cd93fa812940 Mon Sep 17 00:00:00 2001 From: Teodor-Dumitru Ene <34819528+tdene@users.noreply.github.com> Date: Fri, 24 Apr 2026 14:36:27 -0500 Subject: [PATCH 023/105] Add misc CUDA graph sugar to CudaGraphManager (#4425) --- megatron/core/transformer/cuda_graphs.py | 70 +++++---- .../inference/engines/test_dynamic_engine.py | 2 +- .../engines/test_hybrid_prefix_caching_e2e.py | 2 +- .../test_prefix_caching_cuda_graphs.py | 4 +- .../transformer/test_cuda_graphs.py | 141 ++++++++++++++++++ 5 files changed, 189 insertions(+), 30 deletions(-) diff --git a/megatron/core/transformer/cuda_graphs.py b/megatron/core/transformer/cuda_graphs.py index 067f6055015..08d29a601b7 100644 --- a/megatron/core/transformer/cuda_graphs.py +++ b/megatron/core/transformer/cuda_graphs.py @@ -707,7 +707,10 @@ def __init__( self.backward_retain_grad = False self.fp8_enabled = False self.fp4_enabled = False + self.fp8_runtime_enabled = None + self.fp4_runtime_enabled = None self.deallocate_pipeline_outputs = False + self.num_warmup_steps = 0 self.grad_enabled = need_backward and torch.is_grad_enabled() self.func = super(MegatronModule, self.base_module).__call__ if func is None else func @@ -1415,6 +1418,8 @@ def __init__( function_name=None, need_backward=True, pg_collection=None, + inline_capture=False, + num_warmup_steps=None, ): super().__init__() """Creates a CudaGraphManager to manage CUDA graphs for a Megatron module. @@ -1422,7 +1427,13 @@ def __init__( Args: config: TransformerConfig object containing CUDA graph settings for memory pooling, graph retention, gradient accumulation, FP8/FP4, and warmup steps. + inline_capture: Normally, whether the inline capture path is taken depends on whether + `inference_context` is present in the kwargs of the forward call. + Setting this argument to True always forces the inline capture path to be taken. + num_warmup_steps: If set, overrides the per-runner warmup step count. """ + self._inline_capture = inline_capture + self._num_warmup_steps = num_warmup_steps if pg_collection is None: pg_collection = ProcessGroupCollection.use_mpu_process_groups() self.pg_collection = pg_collection @@ -1432,8 +1443,13 @@ def __init__( if function_name is not None: func = getattr(base_module, function_name) - def wrapped_func(*args, **kwargs): - out = self(base_module, args, kwargs) + def wrapped_func(*args, eager=False, cache_key=None, **kwargs): + if eager: + return func(*args, **kwargs) + out = self(base_module, args, kwargs, cache_key=cache_key) + # Unwrap single-element tuple to match the original function's return type. + if isinstance(out, tuple) and len(out) == 1: + return out[0] return out setattr(base_module, function_name, wrapped_func) @@ -1465,7 +1481,7 @@ def wrapped_func(*args, **kwargs): ) self.cudagraph_runners: list[_CudaGraphRunner] = [] - self.inference_cudagraphs_lookup_table: dict = defaultdict(lambda: None) + self.custom_cudagraphs_lookup_table: dict = defaultdict(lambda: None) self.is_first_microbatch = False # Without pipeline parallelism, microbatches execute one at a time. @@ -1493,7 +1509,7 @@ def call_ddp_preforward_hook(self, module): # Only hooks from Mcore DDP, which take no args, should be called at this point. hook(module) - def get_cudagraph_runner(self, megatron_module, args, kwargs, reuse_cudagraphs): + def get_cudagraph_runner(self, megatron_module, args, kwargs, reuse_cudagraphs, cache_key=None): '''Returns a valid cudagraph runner for the current forward call. The cudagraph corresponding to this call is the first element of 'self.cudagraph_runners'. We iterate through the list by 1 for each call, and the number of calls is equal to the @@ -1501,16 +1517,8 @@ def get_cudagraph_runner(self, megatron_module, args, kwargs, reuse_cudagraphs): Otherwise, we assign a mempool per microbatch, which allows cudagraphs to be reused over different microbatches by tracking their respective fwd and bwd passes.''' if reuse_cudagraphs: - is_inference_mode = 'inference_context' in kwargs.keys() and kwargs['inference_context'] - if is_inference_mode: - is_static_batching = kwargs['inference_context'].is_static_batching() - if is_static_batching: - batch_size = kwargs['hidden_states'].shape[0] - is_decode_only = kwargs["inference_context"].is_decode_only() - runner = self.inference_cudagraphs_lookup_table[(batch_size, is_decode_only)] - else: - padded_batch_dimensions = kwargs['inference_context'].padded_batch_dimensions - runner = self.inference_cudagraphs_lookup_table[padded_batch_dimensions] + if cache_key is not None: + runner = self.custom_cudagraphs_lookup_table[cache_key] else: # Todo: For training, we could also cache runners based on input shape. # If autograd is currently disabled, it doesnt matter if a runner was created @@ -1544,15 +1552,11 @@ def is_valid(r): self.func, self.need_backward, ) + if self._num_warmup_steps is not None: + runner.num_warmup_steps = self._num_warmup_steps self.cudagraph_runners.append(runner) - if is_inference_mode: - # Cache the newly created runner in the inference lookup table. - if is_static_batching: - self.inference_cudagraphs_lookup_table[(batch_size, is_decode_only)] = ( - runner - ) - else: - self.inference_cudagraphs_lookup_table[padded_batch_dimensions] = runner + if cache_key is not None: + self.custom_cudagraphs_lookup_table[cache_key] = runner else: # Create cudagraphs for every microbatch if _CudagraphGlobalRecord.cudagraph_created: @@ -1572,7 +1576,7 @@ def is_valid(r): return runner - def __call__(self, megatron_module, args, kwargs): + def __call__(self, megatron_module, args, kwargs, cache_key=None): """Calls the forward pass of the cudagraphed module. Args: @@ -1581,8 +1585,18 @@ def __call__(self, megatron_module, args, kwargs): args (tuple): The positional args to be passed to the module. kwargs (dict): The keyword args to be passed to the module. + + cache_key: Optional hashable key for O(1) runner lookup. + If `inference_context` is provided, this gets set to the correct value. """ is_inference_mode = 'inference_context' in kwargs.keys() and kwargs['inference_context'] + if cache_key is None and is_inference_mode: + inference_context = kwargs['inference_context'] + if inference_context.is_static_batching(): + batch_size = kwargs['hidden_states'].shape[0] + cache_key = (batch_size, inference_context.is_decode_only()) + else: + cache_key = inference_context.padded_batch_dimensions is_in_checkpoint_fwd = is_checkpointing() if HAVE_TE_GRAPHS: is_in_checkpoint_fwd = is_in_checkpoint_fwd or is_fp8_activation_recompute_enabled() @@ -1594,12 +1608,16 @@ def __call__(self, megatron_module, args, kwargs): for module in megatron_module.modules(): self.call_ddp_preforward_hook(module) - runner = self.get_cudagraph_runner(megatron_module, args, kwargs, self.reuse_cudagraphs) + runner = self.get_cudagraph_runner( + megatron_module, args, kwargs, self.reuse_cudagraphs, cache_key=cache_key + ) out = runner.replay_graph_capture(self.is_first_microbatch, args, kwargs) else: - if is_inference_mode: + if is_inference_mode or self._inline_capture: # Inference generation mode creates graphs immediately - runner = self.get_cudagraph_runner(megatron_module, args, kwargs, True) + runner = self.get_cudagraph_runner( + megatron_module, args, kwargs, True, cache_key=cache_key + ) if not runner.fwd_graph_recorded: # Reuse graph input-output buffers for inference diff --git a/tests/unit_tests/inference/engines/test_dynamic_engine.py b/tests/unit_tests/inference/engines/test_dynamic_engine.py index b23e9562242..0c38894cd0b 100644 --- a/tests/unit_tests/inference/engines/test_dynamic_engine.py +++ b/tests/unit_tests/inference/engines/test_dynamic_engine.py @@ -4503,7 +4503,7 @@ def _reset_cuda_graph_state(self, model): for module in model.modules(): if isinstance(module, CudaGraphManager): module.cudagraph_runners.clear() - module.inference_cudagraphs_lookup_table.clear() + module.custom_cudagraphs_lookup_table.clear() def _build_engine(self, model, enable_chunked_prefill, num_cuda_graphs, context_max_tokens): """Build an engine with the given chunked prefill / CUDA graph config.""" diff --git a/tests/unit_tests/inference/engines/test_hybrid_prefix_caching_e2e.py b/tests/unit_tests/inference/engines/test_hybrid_prefix_caching_e2e.py index 303cf76d122..d3f1930561b 100644 --- a/tests/unit_tests/inference/engines/test_hybrid_prefix_caching_e2e.py +++ b/tests/unit_tests/inference/engines/test_hybrid_prefix_caching_e2e.py @@ -226,7 +226,7 @@ def _build_engine( for module in model.modules(): if isinstance(module, CudaGraphManager): module.cudagraph_runners.clear() - module.inference_cudagraphs_lookup_table.clear() + module.custom_cudagraphs_lookup_table.clear() return DynamicInferenceEngine(controller, context) def _make_request(self, req_id, prompt, enable_pc, num_tokens=NUM_TOKENS_TO_GENERATE): diff --git a/tests/unit_tests/inference/engines/test_prefix_caching_cuda_graphs.py b/tests/unit_tests/inference/engines/test_prefix_caching_cuda_graphs.py index 26a81c5baef..c89209699b3 100644 --- a/tests/unit_tests/inference/engines/test_prefix_caching_cuda_graphs.py +++ b/tests/unit_tests/inference/engines/test_prefix_caching_cuda_graphs.py @@ -147,7 +147,7 @@ def _reset_cuda_graph_state(self, model): for module in model.modules(): if isinstance(module, CudaGraphManager): module.cudagraph_runners.clear() - module.inference_cudagraphs_lookup_table.clear() + module.custom_cudagraphs_lookup_table.clear() def _build_engine(self, model, mamba_config, num_cuda_graphs): """Build an engine with prefix caching and optional CUDA graphs.""" @@ -367,7 +367,7 @@ def _reset_cuda_graph_state(self, model): for module in model.modules(): if isinstance(module, CudaGraphManager): module.cudagraph_runners.clear() - module.inference_cudagraphs_lookup_table.clear() + module.custom_cudagraphs_lookup_table.clear() def _build_engine( self, diff --git a/tests/unit_tests/transformer/test_cuda_graphs.py b/tests/unit_tests/transformer/test_cuda_graphs.py index ee4ff7d152d..4d29f080d24 100644 --- a/tests/unit_tests/transformer/test_cuda_graphs.py +++ b/tests/unit_tests/transformer/test_cuda_graphs.py @@ -35,6 +35,7 @@ _CudagraphGlobalRecord, ) from megatron.core.transformer.enums import CudaGraphScope +from megatron.core.transformer.module import MegatronModule from megatron.core.transformer.moe.fused_a2a import reset_hybrid_ep_buffer from megatron.core.transformer.transformer_block import TransformerBlock from megatron.core.transformer.transformer_config import TransformerConfig @@ -1088,6 +1089,146 @@ def test_moe_partial_cudagraph(self, ep_size, moe_dropless_dispatcher, moe_dispa Utils.destroy_model_parallel() +class _SimpleModule(MegatronModule): + """Minimal MegatronModule for testing CudaGraphManager with function_name.""" + + def __init__(self, config): + super().__init__(config) + self.linear = torch.nn.Linear(config.hidden_size, config.hidden_size) + + def my_op(self, x): + return self.linear(x) + + +class TestInlineCaptureManager: + """Tests for CudaGraphManager with inline_capture, function_name, eager, and cache_key.""" + + def _make_config(self): + return TransformerConfig( + num_layers=1, + hidden_size=32, + num_attention_heads=1, + use_cpu_initialization=True, + cuda_graph_impl="local", + inference_rng_tracker=True, + ) + + def setup_method(self, method): + Utils.initialize_model_parallel() + model_parallel_cuda_manual_seed( + seed=123, inference_rng_tracker=True, use_cudagraphable_rng=False, force_reset_rng=True + ) + + def teardown_method(self, method): + _CudagraphGlobalRecord.cudagraph_created = False + _CudagraphGlobalRecord.cudagraph_record = [] + _CudagraphGlobalRecord.cudagraph_inference_record = [] + CudaGraphManager.global_mempool = None + Utils.destroy_model_parallel() + + @torch.inference_mode() + def test_inline_capture_matches_eager(self): + """Inline-captured graph output must match eager execution.""" + config = self._make_config() + module = _SimpleModule(config).cuda().eval() + + # Get eager reference before wrapping + x = torch.randn(4, config.hidden_size, device="cuda") + eager_out = module.my_op(x).clone() + + mgr = CudaGraphManager( + config, + base_module=module, + function_name="my_op", + inline_capture=True, + num_warmup_steps=0, + need_backward=False, + ) + + # First call captures, second replays + graph_out_1 = module.my_op(x) + graph_out_2 = module.my_op(x) + assert torch.equal(eager_out, graph_out_1) + assert torch.equal(eager_out, graph_out_2) + assert len(mgr.cudagraph_runners) == 1 + assert mgr.cudagraph_runners[0].fwd_graph_recorded + + @torch.inference_mode() + def test_eager_bypass(self): + """eager=True must bypass graph capture entirely.""" + config = self._make_config() + module = _SimpleModule(config).cuda().eval() + + mgr = CudaGraphManager( + config, + base_module=module, + function_name="my_op", + inline_capture=True, + num_warmup_steps=0, + need_backward=False, + ) + + x = torch.randn(4, config.hidden_size, device="cuda") + _ = module.my_op(x, eager=True) + _ = module.my_op(x, eager=True) + assert len(mgr.cudagraph_runners) == 0, "eager=True should not create runners" + + @torch.inference_mode() + def test_cache_key_routing(self): + """Different cache_keys must create separate runners.""" + config = self._make_config() + module = _SimpleModule(config).cuda().eval() + + mgr = CudaGraphManager( + config, + base_module=module, + function_name="my_op", + inline_capture=True, + num_warmup_steps=0, + need_backward=False, + ) + + x = torch.randn(4, config.hidden_size, device="cuda") + module.my_op(x, cache_key="key_a") + module.my_op(x, cache_key="key_b") + + assert len(mgr.cudagraph_runners) == 2 + assert mgr.custom_cudagraphs_lookup_table["key_a"] is not None + assert mgr.custom_cudagraphs_lookup_table["key_b"] is not None + assert ( + mgr.custom_cudagraphs_lookup_table["key_a"] + is not mgr.custom_cudagraphs_lookup_table["key_b"] + ) + + # Same key reuses the runner + module.my_op(x, cache_key="key_a") + assert len(mgr.cudagraph_runners) == 2 + + @torch.inference_mode() + def test_num_warmup_steps_override(self): + """num_warmup_steps on the manager must override the config value on runners.""" + config = self._make_config() + config.cuda_graph_warmup_steps = 3 + module = _SimpleModule(config).cuda().eval() + + mgr = CudaGraphManager( + config, + base_module=module, + function_name="my_op", + inline_capture=True, + num_warmup_steps=0, + need_backward=False, + ) + + x = torch.randn(4, config.hidden_size, device="cuda") + module.my_op(x, cache_key="test") + + runner = mgr.cudagraph_runners[0] + assert ( + runner.num_warmup_steps == 0 + ), f"Expected 0 warmup steps (manager override), got {runner.num_warmup_steps}" + + if __name__ == "__main__": test = TestParallelTransformerBlockCudagraphs() From 35f76df3f2c2fe4f676d242c1d0a4a41bd6f4271 Mon Sep 17 00:00:00 2001 From: Siddharth Singh Date: Fri, 24 Apr 2026 12:48:26 -0700 Subject: [PATCH 024/105] Inference: Add the embedding and output layer in the full_iteration_inference cuda graph scope for hybrid models (#4440) --- megatron/core/models/hybrid/hybrid_block.py | 37 +---------------- megatron/core/models/hybrid/hybrid_model.py | 41 ++++++++++++++++++- .../inference/engines/test_dynamic_engine.py | 7 +++- 3 files changed, 46 insertions(+), 39 deletions(-) diff --git a/megatron/core/models/hybrid/hybrid_block.py b/megatron/core/models/hybrid/hybrid_block.py index 5494d531e52..6d20bcdd6e5 100644 --- a/megatron/core/models/hybrid/hybrid_block.py +++ b/megatron/core/models/hybrid/hybrid_block.py @@ -25,7 +25,7 @@ from megatron.core.transformer import TransformerConfig from megatron.core.transformer.enums import CudaGraphScope from megatron.core.transformer.identity_op import IdentityOp -from megatron.core.transformer.module import GraphableMegatronModule, MegatronModule +from megatron.core.transformer.module import MegatronModule from megatron.core.transformer.spec_utils import ModuleSpec, build_module from megatron.core.transformer.transformer_layer import TransformerLayer from megatron.core.transformer.utils import sharded_state_dict_default @@ -47,7 +47,7 @@ class HybridStackSubmodules: mtp_block_spec: Optional[ModuleSpec] = None -class HybridStack(GraphableMegatronModule, MegatronModule): +class HybridStack(MegatronModule): """ Constructor for the HybridStack class. @@ -206,39 +206,6 @@ def mamba_state_shapes_per_request(self) -> Optional[Tuple[Tuple[int], Tuple[int return layer.mamba_state_shapes_per_request() return None - def _should_call_local_cudagraph(self, *args, **kwargs): - """ - Check if we should call the local cudagraph path. - """ - if ( - not self.training - and hasattr(self, 'cudagraph_manager') - and kwargs['attention_mask'] is None - and ( - kwargs.get('inference_context') is not None - or kwargs.get('inference_params') is not None - ) - and CudaGraphScope.full_iteration_inference in self.config.cuda_graph_scope - ): - if kwargs['inference_context'].is_static_batching(): - using_cuda_graph = kwargs['inference_context'].is_decode_only() - else: - using_cuda_graph = kwargs['inference_context'].using_cuda_graph_this_step() - - if using_cuda_graph: - return True - return False - - def __call__(self, *args, **kwargs): - if self._should_call_local_cudagraph(*args, **kwargs): - kwargs['hidden_states'] = ( - kwargs['hidden_states'].unwrap() - if isinstance(kwargs['hidden_states'], WrappedTensor) - else kwargs['hidden_states'] - ) - return super().__call__(*args, **kwargs)[0] - return super().__call__(*args, **kwargs) - def forward( self, hidden_states: Union[Tensor, WrappedTensor], diff --git a/megatron/core/models/hybrid/hybrid_model.py b/megatron/core/models/hybrid/hybrid_model.py index 88a97ec777f..b575792e81b 100644 --- a/megatron/core/models/hybrid/hybrid_model.py +++ b/megatron/core/models/hybrid/hybrid_model.py @@ -20,7 +20,8 @@ from megatron.core.quantization.utils import get_quant_config_or_none from megatron.core.tensor_parallel import gather_from_sequence_parallel_region from megatron.core.transformer import TransformerConfig -from megatron.core.transformer.enums import ModelType +from megatron.core.transformer.enums import CudaGraphScope, ModelType +from megatron.core.transformer.module import GraphableMegatronModule from megatron.core.transformer.multi_token_prediction import ( MultiTokenPredictionBlock, mtp_on_this_rank, @@ -37,7 +38,7 @@ logger = logging.getLogger(__name__) -class HybridModel(LanguageModule): +class HybridModel(LanguageModule, GraphableMegatronModule): """Hybrid language model. Args: @@ -340,6 +341,42 @@ def preprocess_for_fine_grained_offloading(self): off_interface.mark_not_offloadable(param) self.disable_param_offloading = False + def _should_call_local_cudagraph(self, *args, **kwargs): + """ + Check if we should call the local cudagraph path. + """ + if ( + not self.training + and hasattr(self, 'cudagraph_manager') + and ( + kwargs.get('inference_context') is not None + or kwargs.get('inference_params') is not None + ) + and CudaGraphScope.full_iteration_inference in self.config.cuda_graph_scope + ): + if kwargs['inference_context'].is_static_batching(): + using_cuda_graph = kwargs['inference_context'].is_decode_only() + else: + using_cuda_graph = kwargs['inference_context'].using_cuda_graph_this_step() + + if using_cuda_graph: + return True + return False + + def __call__(self, *args, **kwargs): + if self._should_call_local_cudagraph(*args, **kwargs): + return super().__call__(*args, **kwargs)[0] + return super().__call__(*args, **kwargs) + + def create_mcore_cudagraph_manager(self, config): + """ + Create the cudagraph manager for the full iteration inference scope + """ + if CudaGraphScope.full_iteration_inference in config.cuda_graph_scope: + from megatron.core.transformer.cuda_graphs import CudaGraphManager + + self.cudagraph_manager = CudaGraphManager(config) + def forward( self, input_ids: Tensor, diff --git a/tests/unit_tests/inference/engines/test_dynamic_engine.py b/tests/unit_tests/inference/engines/test_dynamic_engine.py index 0c38894cd0b..e39a58736a5 100644 --- a/tests/unit_tests/inference/engines/test_dynamic_engine.py +++ b/tests/unit_tests/inference/engines/test_dynamic_engine.py @@ -600,8 +600,11 @@ def test_simple(self, model_provider, num_cuda_graphs, cuda_graph_scope) -> None assert env.engine.context.cuda_graph_batch_dimensions_list model = env.engine.controller.inference_wrapped_model.model if cuda_graph_scope == [CudaGraphScope.full_iteration_inference]: - # check if cudagraph runners are created at the decoder level - assert model.decoder.cudagraph_manager.cudagraph_runners + # hybrid models attach cudagraph_manager to the model; others attach to the decoder + if model_provider == "hybrid": + assert model.cudagraph_manager.cudagraph_runners + else: + assert model.decoder.cudagraph_manager.cudagraph_runners else: # check if cudagraph runners are created at the layer level for layer in model.decoder.layers: From 481efd020e08e30a21c70501ece8bbee6c4ca567 Mon Sep 17 00:00:00 2001 From: Jimmy Zhang <133159885+jiemingz@users.noreply.github.com> Date: Fri, 24 Apr 2026 16:09:00 -0400 Subject: [PATCH 025/105] Important bugfixes in local CG implementation that were leading to loss curve gaps for latent MoE models (#4433) Signed-off-by: root --- megatron/core/transformer/cuda_graphs.py | 31 ++++++++++-------------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/megatron/core/transformer/cuda_graphs.py b/megatron/core/transformer/cuda_graphs.py index 08d29a601b7..866f6676d92 100644 --- a/megatron/core/transformer/cuda_graphs.py +++ b/megatron/core/transformer/cuda_graphs.py @@ -799,7 +799,14 @@ def create_fwd_graph(self, args, kwargs, outputs=None, clone_inputs=True): self.kwargs = kwargs self.outputs = outputs - # save grads and other variables that may be affected by graph warmup + # Save buffers, grads, and other variables that may be affected by graph warmup. + # For example, megatron/core/transformer/moe/router.py's expert_bias is a persistent + # buffer updated each forward pass by '_apply_expert_bias()'. So we need to ensure + # graph capture's forward passes do not corrupt its value. + buffer_backup = [] + for buf in self.base_module.buffers(): + buffer_backup.append(buf.clone()) + if self.training and torch.is_grad_enabled(): grad_backup = [] for param in self.base_module.parameters(): @@ -845,7 +852,6 @@ def create_fwd_graph(self, args, kwargs, outputs=None, clone_inputs=True): def _resolve_input_buffer(ten): if not isinstance(ten, ArgMetadata): return ten - # the input tensor is resued from another cudagraph's input or output if ( hasattr(ten, "cg_buffer_metadata") @@ -916,7 +922,7 @@ def _resolve_input_buffer(ten): def clone_ten(ten): if not torch.is_tensor(ten): return ten - return torch.zeros_like(ten).requires_grad_(ten.requires_grad) + return torch.clone(ten).detach().requires_grad_(ten.requires_grad) warmup_args = tree_map(clone_ten, self.fwd_graph_input_args) warmup_kwargs = tree_map(clone_ten, self.fwd_graph_input_kwargs) @@ -989,17 +995,6 @@ def clone_ten(ten): o.cg_buffer_metadata.fwd_cudagraph_buffer = fwd_graph_out fwd_buffer_reuse_ref_count += 1 - # if an input buffer requires a copy, and does not have metadata attached to it at this - # point, it will not be reused after this forward pass, so return it to the pool - for buf in self.fwd_graph_input_surface: - if ( - hasattr(buf, "can_skip_replay_copy") - and not buf.can_skip_replay_copy - and not hasattr(buf, "cg_buffer_metadata") - ): - assert _CudagraphGlobalRecord.tensor_reuse_pool.owns(buf) - _CudagraphGlobalRecord.tensor_reuse_pool.insert(buf) - if self.training and torch.is_grad_enabled(): assert ( len(self.fwd_graph_output_surface) > 0 @@ -1019,6 +1014,10 @@ def clone_ten(ten): if main_grad_copy is not None: param.main_grad.copy_(main_grad_copy) + # restore cached buffers + for buf_copy, buf in zip(buffer_backup, self.base_module.buffers()): + buf.copy_(buf_copy) + if is_moe: for name in tracker: tracker[name]["values"].copy_(cached_aux_losses[name]) @@ -1661,10 +1660,6 @@ def __call__(self, megatron_module, args, kwargs, cache_key=None): runner = self.get_cudagraph_runner( megatron_module, args, kwargs, self.reuse_cudagraphs ) - # check if a layer is frozen during training. - if not torch.is_grad_enabled(): - # If the layer is frozen, we need to set the runner to eval mode. - runner.eval() out = runner.record_graph_capture(args, kwargs) else: # No cudagraphs were found in training mode with grad disabled, so fallback to From e9abb6cb9898c3a4b82eaa7d148f717cd55866d3 Mon Sep 17 00:00:00 2001 From: Lawrence McAfee <85179052+lmcafee-nvidia@users.noreply.github.com> Date: Fri, 24 Apr 2026 17:27:37 -0400 Subject: [PATCH 026/105] fix: Replace polynomial rolling hash with SHA-256 for prefix caching (#4158) Co-authored-by: Claude Opus 4.6 (1M context) --- megatron/core/inference/inference_request.py | 53 ++++++++----------- .../contexts/test_dynamic_prefix_caching.py | 44 ++++++++++++++- 2 files changed, 64 insertions(+), 33 deletions(-) diff --git a/megatron/core/inference/inference_request.py b/megatron/core/inference/inference_request.py index 50099695120..3917bbba720 100644 --- a/megatron/core/inference/inference_request.py +++ b/megatron/core/inference/inference_request.py @@ -1,11 +1,11 @@ # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. import copy +import hashlib import time import warnings from dataclasses import asdict, dataclass, field from enum import Enum, auto -from itertools import accumulate from typing import Any, Dict, List, Optional, Tuple import numpy as np @@ -87,53 +87,44 @@ class Status(Enum): # Hash computation for prefix caching # ========================================================================= -# Constants for hash computation -# Using 2^61 - 1 (Mersenne prime) for ~10^18 hash space, reducing collision probability -# from ~10^-9 to ~10^-18 compared to the previous prime (1000000007). -HASH_PRIME = 2305843009213693951 -HASH_BASE = 31 - -_hash_powers: Optional[torch.Tensor] = None - def compute_block_hashes_batched(prompt_tokens: torch.Tensor, block_size: int) -> List[int]: - """Compute hashes for all complete blocks in a prompt in one batched operation. + """Compute SHA-256 based hashes for all complete blocks in a prompt. - Reshapes prompt tokens into [num_blocks, block_size], computes all per-block - token hashes via a single GPU matmul, transfers results with one .tolist() call, - and chains parent hashes on CPU. + Each block hash is computed as SHA-256(parent_digest || block_bytes), where + parent_digest chains from the previous block (starting from a zero digest). + This provides cryptographic collision resistance with no exploitable algebraic + structure. Args: prompt_tokens: All prompt token IDs, shape [seq_len]. block_size: Number of tokens per block. Returns: - List of positive integer hash values (1 to HASH_PRIME), one per complete block. + List of positive integer hash values in [1, 2^63-1], one per complete block. """ num_complete_blocks = len(prompt_tokens) // block_size if num_complete_blocks == 0: return [] - global _hash_powers - if _hash_powers is None or _hash_powers.shape[0] != block_size: - positions = torch.arange(block_size, device=prompt_tokens.device, dtype=torch.int64) - _hash_powers = torch.pow(HASH_BASE, positions).to(torch.int64) % HASH_PRIME + # Single GPU->CPU transfer, get contiguous bytes + tokens_cpu = prompt_tokens[: num_complete_blocks * block_size].to(torch.int64).cpu() + tokens_bytes = tokens_cpu.numpy().tobytes() + block_byte_size = block_size * tokens_cpu.element_size() # 8 bytes per int64 - # Reshape to [num_blocks, block_size] (zero-copy view) and compute all token hashes - blocks = prompt_tokens[: num_complete_blocks * block_size].view(num_complete_blocks, block_size) - token_hashes = (blocks.to(torch.int64) * _hash_powers).sum(dim=1) % HASH_PRIME + hashes = [] + parent_digest = b'\x00' * 32 # SHA-256 digest size - # Single GPU→CPU transfer - token_hashes_list = token_hashes.tolist() + for i in range(num_complete_blocks): + block_bytes = tokens_bytes[i * block_byte_size : (i + 1) * block_byte_size] + digest = hashlib.sha256(parent_digest + block_bytes).digest() - # Chain parent hashes on CPU (C-level accumulate, no Python loop) - hashes = list( - accumulate( - token_hashes_list, - lambda parent, th: (parent * HASH_BASE + th) % HASH_PRIME + 1, - initial=0, - ) - )[1:] + # Map to positive int64 range [1, 2^63-1], avoiding sentinels -1 and 0 + raw = int.from_bytes(digest[:8], byteorder='little', signed=False) + hash_val = (raw % (2**63 - 1)) + 1 + + hashes.append(hash_val) + parent_digest = digest # Full 32-byte digest chains into next block return hashes diff --git a/tests/unit_tests/inference/contexts/test_dynamic_prefix_caching.py b/tests/unit_tests/inference/contexts/test_dynamic_prefix_caching.py index 9fc9ac9aac3..c2deb6d74c7 100644 --- a/tests/unit_tests/inference/contexts/test_dynamic_prefix_caching.py +++ b/tests/unit_tests/inference/contexts/test_dynamic_prefix_caching.py @@ -11,7 +11,6 @@ from megatron.core.inference.contexts.dynamic_context import DynamicInferenceContext from megatron.core.inference.engines.dynamic_engine import DynamicInferenceEngine from megatron.core.inference.inference_request import ( - HASH_PRIME, DynamicInferenceRequest, DynamicInferenceRequestRecord, Status, @@ -142,7 +141,7 @@ def test_hash_computation(self): tokens = self._prompt(32) h1 = compute_block_hashes_batched(tokens, 32) h2 = compute_block_hashes_batched(tokens, 32) - assert h1 == h2 and len(h1) == 1 and 1 <= h1[0] <= HASH_PRIME + assert h1 == h2 and len(h1) == 1 and h1[0] >= 1 assert compute_block_hashes_batched(self._prompt(32, offset=1), 32)[0] != h1[0] # parent chaining: 4 blocks of all-zero tokens produce distinct hashes @@ -161,6 +160,47 @@ def test_hash_computation(self): ) assert len(long_h) == 120 and all(v > 0 for v in long_h) + @pytest.mark.internal + def test_hash_collision_resistance(self): + """Regression tests: old polynomial collision attacks must fail with SHA-256.""" + bs = 32 + + # V2 regression: algebraic attack (token[j] += 31, token[j+1] -= 1) + # This was a zero-delta exploit against the old polynomial hash. + tokens = self._prompt(bs) + collision = tokens.clone() + collision[0] += 31 + collision[1] -= 1 + h_orig = compute_block_hashes_batched(tokens, bs) + h_coll = compute_block_hashes_batched(collision, bs) + assert h_orig != h_coll, "V2 algebraic collision: token[j]+=31, token[j+1]-=1" + + # V2 at different positions within the block + for j in range(bs - 1): + c = tokens.clone() + c[j] += 31 + c[j + 1] -= 1 + assert compute_block_hashes_batched(c, bs) != h_orig, f"V2 at position {j}" + + # V2 across multiple blocks: modify one block, verify all downstream hashes change + tokens_multi = self._prompt(bs * 4) + h_multi = compute_block_hashes_batched(tokens_multi, bs) + modified = tokens_multi.clone() + modified[0] += 31 + modified[1] -= 1 + h_mod = compute_block_hashes_batched(modified, bs) + assert h_mod[0] != h_multi[0], "modified block hash must differ" + # Parent chaining: all subsequent blocks must also differ + for i in range(1, 4): + assert h_mod[i] != h_multi[i], f"parent chain: block {i} must differ" + + # V2 generalized: arbitrary linear combinations (token[j] += k*31, token[j+1] -= k) + for k in [1, 2, 5, 100]: + c = tokens.clone() + c[0] += k * 31 + c[1] -= k + assert compute_block_hashes_batched(c, bs) != h_orig, f"V2 generalized k={k}" + @pytest.mark.internal def test_registration_and_discovery(self): ctx = self._ctx() From 377af02ad911ae0933a1e5f25de7b40ef78fc1a9 Mon Sep 17 00:00:00 2001 From: Antoni-Joan Solergibert Date: Fri, 24 Apr 2026 23:33:19 +0200 Subject: [PATCH 027/105] feat(ckpt): expose validate_access_integrity knob on dist-ckpt load (#4422) Co-authored-by: Claude Opus 4.7 (1M context) --- megatron/training/checkpointing.py | 1 + megatron/training/config/training_config.py | 5 +++++ .../dist_checkpointing/test_pipeline_parallel_layout.py | 1 + tests/unit_tests/dist_checkpointing/utils.py | 1 + tests/unit_tests/pipeline_parallel/test_pipeline_layout.py | 1 + tests/unit_tests/test_checkpointing.py | 1 + 6 files changed, 10 insertions(+) diff --git a/megatron/training/checkpointing.py b/megatron/training/checkpointing.py index 1441a71518d..04a93e9f376 100644 --- a/megatron/training/checkpointing.py +++ b/megatron/training/checkpointing.py @@ -1247,6 +1247,7 @@ def _load_global_dist_base_checkpoint( sharded_state_dict, checkpoint_name, load_strategy, + validate_access_integrity=args.ckpt_load_validate_sharding_integrity, strict=args.dist_ckpt_strictness, ) return state_dict, checkpoint_name, release, CheckpointType.GLOBAL diff --git a/megatron/training/config/training_config.py b/megatron/training/config/training_config.py index 27cffb837f4..b44a6472872 100644 --- a/megatron/training/config/training_config.py +++ b/megatron/training/config/training_config.py @@ -529,6 +529,11 @@ class CheckpointConfig: ckpt_assume_constant_structure: bool = False """Assume the checkpoint structure is constant across saves to enable optimizations.""" + ckpt_load_validate_sharding_integrity: bool = True + """Whether to validate sharding access integrity when loading a distributed checkpoint. + When True (default), each tensor shard is checked to be accessed exactly once as main + replica by some rank. Disabling skips this validation""" + strict_fsdp_dtensor_load: bool = True """Whether to enforce strict loading for FSDP DTensor checkpoints. When False, allows partial loading.""" diff --git a/tests/unit_tests/dist_checkpointing/test_pipeline_parallel_layout.py b/tests/unit_tests/dist_checkpointing/test_pipeline_parallel_layout.py index 5f9c617893c..f8aace0105d 100644 --- a/tests/unit_tests/dist_checkpointing/test_pipeline_parallel_layout.py +++ b/tests/unit_tests/dist_checkpointing/test_pipeline_parallel_layout.py @@ -140,6 +140,7 @@ def create_args(): args.vocab_file = None args.add_position_embedding = False args.ckpt_assume_constant_structure = True + args.ckpt_load_validate_sharding_integrity = True args.dist_ckpt_strictness = "assume_ok_unexpected" args.fp16 = False args.bf16 = True diff --git a/tests/unit_tests/dist_checkpointing/utils.py b/tests/unit_tests/dist_checkpointing/utils.py index 0aadaee3b29..8a9df54ddc8 100644 --- a/tests/unit_tests/dist_checkpointing/utils.py +++ b/tests/unit_tests/dist_checkpointing/utils.py @@ -150,6 +150,7 @@ def init_checkpointing_mock_args(args, ckpt_dir, fully_parallel=False): args.no_save_optim = False args.no_save_rng = False args.ckpt_assume_constant_structure = False + args.ckpt_load_validate_sharding_integrity = True args.log_progress = False args.auto_detect_ckpt_format = False args.exit_on_missing_checkpoint = False diff --git a/tests/unit_tests/pipeline_parallel/test_pipeline_layout.py b/tests/unit_tests/pipeline_parallel/test_pipeline_layout.py index a6afabe8817..f871938b218 100644 --- a/tests/unit_tests/pipeline_parallel/test_pipeline_layout.py +++ b/tests/unit_tests/pipeline_parallel/test_pipeline_layout.py @@ -135,6 +135,7 @@ def create_args(): args.vocab_file = None args.add_position_embedding = False args.ckpt_assume_constant_structure = False + args.ckpt_load_validate_sharding_integrity = True args.dist_ckpt_strictness = "assume_ok_unexpected" args.fp16 = False args.bf16 = True diff --git a/tests/unit_tests/test_checkpointing.py b/tests/unit_tests/test_checkpointing.py index 16bac10566d..61cda3d91d2 100644 --- a/tests/unit_tests/test_checkpointing.py +++ b/tests/unit_tests/test_checkpointing.py @@ -140,6 +140,7 @@ def create_ckpt_load_args(create_args): args.ckpt_assume_constant_structure = False args.ckpt_fully_parallel_save = False args.ckpt_fully_parallel_load = False + args.ckpt_load_validate_sharding_integrity = True args.dist_ckpt_strictness = 'assume_ok_unexpected' args.use_megatron_fsdp = False args.strict_fsdp_dtensor_load = True From 241a5ca3f9b5321e0f3cf4ddcc83ef7648931a82 Mon Sep 17 00:00:00 2001 From: Ryan Prenger Date: Sat, 25 Apr 2026 12:34:13 -0700 Subject: [PATCH 028/105] Fix multivalidation (#3388) Signed-off-by: rprenger --- megatron/training/datasets/data_samplers.py | 51 +++++++++++++++++++-- megatron/training/training.py | 46 +++++++++++++------ tests/unit_tests/test_training.py | 36 +++++++++++++++ 3 files changed, 115 insertions(+), 18 deletions(-) diff --git a/megatron/training/datasets/data_samplers.py b/megatron/training/datasets/data_samplers.py index 7a31d846a49..296acc97941 100644 --- a/megatron/training/datasets/data_samplers.py +++ b/megatron/training/datasets/data_samplers.py @@ -41,13 +41,10 @@ def build_pretraining_data_loader(dataset, consumed_samples): global_batch_size = getattr(args, 'eval_global_batch_size', args.global_batch_size) if is_eval else args.global_batch_size if split == Split.valid and args.full_validation: - batch_sampler = MegatronPretrainingSampler( + batch_sampler = MegatronFullValidationSampler( total_samples=len(dataset), - consumed_samples=0, - micro_batch_size=micro_batch_size, data_parallel_rank=mpu.get_data_parallel_rank(), - data_parallel_size=mpu.get_data_parallel_world_size(), - ) + data_parallel_size=mpu.get_data_parallel_world_size()) elif args.dataloader_type == 'single': if args.hybrid_context_parallel: batch_sampler = HybridCPMegatronPretrainingSampler( @@ -228,6 +225,50 @@ def __iter__(self): global_batch_idx.extend(batch[start_idx[i]:end_idx[i]]) yield global_batch_idx + +class MegatronFullValidationSampler: + """Sampler for full validation that handles small datasets gracefully. + + This sampler is designed for validation datasets that may be smaller than + data_parallel_size * micro_batch_size. It uses micro_batch_size=1 to minimize + the samples needed per batch and properly handles partial batches where some + ranks may not have data. + """ + + def __init__(self, total_samples, data_parallel_rank, data_parallel_size): + self.total_samples = total_samples + self.data_parallel_rank = data_parallel_rank + self.data_parallel_size = data_parallel_size + self.micro_batch_size = 1 # Always use 1 for small dataset support + + # Sanity checks + assert self.total_samples > 0, f'no sample to consume: {self.total_samples}' + assert data_parallel_size > 0 + assert self.data_parallel_rank < data_parallel_size, \ + f'data_parallel_rank should be smaller than data size: {self.data_parallel_rank}, {data_parallel_size}' + + def __len__(self): + """Returns the number of batches this rank will yield.""" + # Each batch takes data_parallel_size samples (1 per rank) + # This rank gets samples at indices: data_parallel_rank, data_parallel_rank + data_parallel_size, ... + num_batches = 0 + for batch_idx in range(0, self.total_samples, self.data_parallel_size): + # Check if this rank has data in this batch + sample_idx = batch_idx + self.data_parallel_rank + if sample_idx < self.total_samples: + num_batches += 1 + return num_batches + + def __iter__(self): + """Yield batches for this data parallel rank.""" + for batch_idx in range(0, self.total_samples, self.data_parallel_size): + # Check if this rank has data in this batch + sample_idx = batch_idx + self.data_parallel_rank + if sample_idx < self.total_samples: + # Yield a batch with a single sample index for this rank + yield [sample_idx] + + class RandomSeedDataset(Dataset): """ A dataset wrapper that resets the random seed before each sample. diff --git a/megatron/training/training.py b/megatron/training/training.py index a16b7d9fb18..b2f7e52f3cc 100644 --- a/megatron/training/training.py +++ b/megatron/training/training.py @@ -3620,10 +3620,20 @@ def evaluate_and_print_results( print_rank_last('-' * length) -def cyclic_iter(iter): +def cyclic_iter(iterable): while True: - for x in iter: + iterator = iter(iterable) + count = 0 + for x in iterator: + count += 1 yield x + if count == 0: + # No data was yielded, the iterable is empty + raise RuntimeError( + "cyclic_iter: iterable produced no data. " + "This may indicate the validation dataloader is empty or eval_iters is incorrectly set. " + "Check that your validation dataset has data and that the dataloader is properly configured." + ) def get_train_valid_test_num_samples(): @@ -3798,32 +3808,42 @@ def _get_iterator(dataloader_type, dataloader): train_data_iterator = None if valid_dataloaders is not None: - # when using full validation, we need to override eval iters with the correct - # number of iterations on tp rank 0 so that it can be distributed to the other - # ranks later + # when using full validation, we need to override eval iters with the + # MAX length across DP ranks so all ranks run the same number of steps if args.full_validation: if args.multiple_validation_sets: if valid_dataloaders[0] is None: - args.eval_iters = [None]*len(valid_dataloaders) + args.eval_iters = [None] * len(valid_dataloaders) else: - args.eval_iters = [len(dl) for dl in valid_dataloaders] + local_eval_iters = [len(dl) for dl in valid_dataloaders] + eval_iters_tensor = torch.tensor(local_eval_iters, dtype=torch.long, device='cuda') + torch.distributed.all_reduce( + eval_iters_tensor, + op=torch.distributed.ReduceOp.MAX, + group=mpu.get_data_parallel_group(with_context_parallel=True), + ) + args.eval_iters = eval_iters_tensor.tolist() else: - args.eval_iters = len(valid_dataloaders[0]) + local_eval_iters = len(valid_dataloaders[0]) + eval_iters_tensor = torch.tensor([local_eval_iters], dtype=torch.long, device='cuda') + torch.distributed.all_reduce( + eval_iters_tensor, + op=torch.distributed.ReduceOp.MAX, + group=mpu.get_data_parallel_group(with_context_parallel=True), + ) + args.eval_iters = eval_iters_tensor.item() if args.multiple_validation_sets: if valid_dataloaders[0] is None: valid_data_iterators = [None] * len(valid_dataloaders) else: valid_dl_type = "cyclic" if args.full_validation else dl_type - print( - f"[VALID DATA LOADER LENGTHS] " - ", ".join(f"{idx}: {len(dl)}" for idx, dl in enumerate(valid_dataloaders)) - ) valid_data_iterators = [ _get_iterator(valid_dl_type, dl) for dl in valid_dataloaders ] elif valid_dataloaders[0] is not None: - valid_data_iterators = _get_iterator(dl_type, valid_dataloaders[0]) + valid_dl_type = "cyclic" if args.full_validation else dl_type + valid_data_iterators = _get_iterator(valid_dl_type, valid_dataloaders[0]) else: valid_data_iterators = None else: diff --git a/tests/unit_tests/test_training.py b/tests/unit_tests/test_training.py index a893734bd89..838b963778c 100644 --- a/tests/unit_tests/test_training.py +++ b/tests/unit_tests/test_training.py @@ -18,6 +18,24 @@ def mock_train_valid_test_datasets_provider(train_val_test_num_samples): return iter([1]), iter([2]), iter([3]) +class _LenDataloader: + """Fake dataloader with __len__ (required by the full_validation path) + and __iter__ (consumed via cyclic_iter).""" + + def __init__(self, data): + self._data = list(data) + + def __len__(self): + return len(self._data) + + def __iter__(self): + return iter(self._data) + + +def mock_multi_valid_full_datasets_provider(train_val_test_num_samples): + return (iter([1]), [_LenDataloader([2, 2]), _LenDataloader([20, 20, 20])], iter([3])) + + def create_test_args(): # Set dummy values for the args. args = SimpleNamespace() @@ -55,6 +73,24 @@ def test_build_train_valid_test_data_iterators(self): test_data = next(test_iter) assert (train_data, valid_data, test_data) == (1, 2, 3) + def test_build_train_valid_test_data_iterators_multi_full_validation(self): + """multiple_validation_sets + full_validation builds a list of iterators + (one per validation set) and sets args.eval_iters to the per-loader + lengths MAX-reduced across DP ranks.""" + args = create_test_args() + args.multiple_validation_sets = True + args.full_validation = True + set_args(args) + _, valid_iters, _ = build_train_valid_test_data_iterators( + mock_multi_valid_full_datasets_provider + ) + assert isinstance(valid_iters, list) + assert len(valid_iters) == 2 + assert next(valid_iters[0]) == 2 + assert next(valid_iters[1]) == 20 + # data_parallel_size=1, so MAX across DP ranks equals the local lengths + assert args.eval_iters == [2, 3] + def test_closed_formula_vocab_size_with_padding(self): def old_round_impl(after, multiple): while (after % multiple) != 0: From f2dcd421b2addb98360b7c751e721b02ba4f2955 Mon Sep 17 00:00:00 2001 From: Qiyu Wan <39144338+WanZzzzzz@users.noreply.github.com> Date: Sat, 25 Apr 2026 14:07:32 -0700 Subject: [PATCH 029/105] Add missing knob for reduce_scatter_with_fp32_accumulation (#4410) Signed-off-by: qiyuw Co-authored-by: Antoni-Joan Solergibert --- .../core/distributed/distributed_data_parallel.py | 12 ++++++++++-- megatron/core/fp4_utils.py | 3 ++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/megatron/core/distributed/distributed_data_parallel.py b/megatron/core/distributed/distributed_data_parallel.py index 06f3fb32238..a711f1405d1 100644 --- a/megatron/core/distributed/distributed_data_parallel.py +++ b/megatron/core/distributed/distributed_data_parallel.py @@ -265,10 +265,18 @@ def __init__( # If bucketing is explicitly disabled, then put all buckets in a buffer into a single # bucket group. self.bucket_groups = partition_buckets( - self.buffers, force_single_bucket_group=disable_bucketing + self.buffers, + force_single_bucket_group=disable_bucketing, + reduce_scatter_with_fp32_accumulation=( + self.ddp_config.reduce_scatter_with_fp32_accumulation + ), ) self.expert_parallel_bucket_groups = partition_buckets( - self.expert_parallel_buffers, force_single_bucket_group=disable_bucketing + self.expert_parallel_buffers, + force_single_bucket_group=disable_bucketing, + reduce_scatter_with_fp32_accumulation=( + self.ddp_config.reduce_scatter_with_fp32_accumulation + ), ) if self.ddp_config.num_distributed_optimizer_instances > 1: diff --git a/megatron/core/fp4_utils.py b/megatron/core/fp4_utils.py index 245a04eb39e..45e57285a8d 100644 --- a/megatron/core/fp4_utils.py +++ b/megatron/core/fp4_utils.py @@ -81,7 +81,8 @@ def modify_nvfp4_rowwise_storage(fp4_tensor: torch.Tensor, new_rowwise_data: tor ), "Rowwise NVFP4 storage must be uint8" # Preserve existing values and then swap storage new_rowwise_data.detach().copy_(old_rowwise) - setattr(fp4_tensor, "_rowwise_data", new_rowwise_data) + fp4_tensor._rowwise_data = new_rowwise_data + del old_rowwise def quantize_nvfp4_param_shard( From 03f4111bf8b67a76beae3cfa7baa9dc8f2d3eef3 Mon Sep 17 00:00:00 2001 From: Keshav Santhanam Date: Sun, 26 Apr 2026 12:07:54 -0700 Subject: [PATCH 030/105] Enable CUDA graphs for MTP inference (#4260) Signed-off-by: Keshav Santhanam --- .../core/inference/batch_dimensions_utils.py | 12 +- .../core/inference/engines/dynamic_engine.py | 46 +- .../mtp_utils_pytorch.py | 244 +++++ .../mtp_utils_triton.py | 456 ++++++++ .../text_generation_controller.py | 579 +++++----- .../common/language_module/language_module.py | 29 +- megatron/core/models/gpt/gpt_model.py | 2 + megatron/core/models/hybrid/hybrid_model.py | 1 + .../core/tensor_parallel/inference_layers.py | 76 ++ megatron/core/tensor_parallel/layers.py | 35 +- megatron/core/transformer/cuda_graphs.py | 84 +- .../transformer/multi_token_prediction.py | 19 +- megatron/core/utils.py | 5 + .../inference/engines/test_dynamic_engine.py | 322 +++--- .../engines/test_mtp_cuda_graph_inference.py | 991 ++++++++++++++++++ .../inference/engines/test_static_engine.py | 66 +- .../inference/test_batch_dimension_utils.py | 8 +- .../inference/test_communication_utils.py | 11 +- .../inference/test_moe_inference.py | 6 + .../test_mtp_utils.py | 694 ++++++++++++ .../test_text_generation_controller.py | 602 +++++++---- tests/unit_tests/test_utilities.py | 7 + 22 files changed, 3600 insertions(+), 695 deletions(-) create mode 100644 megatron/core/inference/text_generation_controllers/mtp_utils_pytorch.py create mode 100644 megatron/core/inference/text_generation_controllers/mtp_utils_triton.py create mode 100644 tests/unit_tests/inference/engines/test_mtp_cuda_graph_inference.py create mode 100644 tests/unit_tests/inference/text_generation_controllers/test_mtp_utils.py diff --git a/megatron/core/inference/batch_dimensions_utils.py b/megatron/core/inference/batch_dimensions_utils.py index 4e23151c533..c9474eac5a6 100644 --- a/megatron/core/inference/batch_dimensions_utils.py +++ b/megatron/core/inference/batch_dimensions_utils.py @@ -14,7 +14,7 @@ import torch -from megatron.core.utils import get_pg_size +from megatron.core.utils import get_pg_size, round_up_to_nearest_multiple @dataclass(order=True, frozen=True) @@ -85,6 +85,10 @@ def is_valid( Returns: True if the config is valid, False otherwise """ + # A dimension with no tokens serves no requests. + if self.token_count <= 0: + return False + # Check if total requests exceed maximum if self.prefill_req_count + self.decode_req_count > max_requests: return False @@ -269,7 +273,9 @@ def _calculate_cuda_graph_token_counts( ) # Align each entry to TP size cuda_graph_token_counts = list( - dict.fromkeys(math.ceil(s / tp_size) * tp_size for s in cuda_graph_token_counts) + dict.fromkeys( + round_up_to_nearest_multiple(s, tp_size) for s in cuda_graph_token_counts + ) ) # Clamp to max tokens cuda_graph_token_counts = [ @@ -291,7 +297,7 @@ def _calculate_cuda_graph_token_counts( math.ceil(int(cuda_graph_step_size) / CUDAGraphBatchDimensionBuilder.CUDA_GRAPH_ROUNDER) ) # Make sure divisible by TP size - cuda_graph_step_size = math.ceil(cuda_graph_step_size / tp_size) * tp_size + cuda_graph_step_size = round_up_to_nearest_multiple(cuda_graph_step_size, tp_size) # round down cuda graph max tokens to be multiple of TP size cuda_graph_max_tokens = (cuda_graph_max_tokens // tp_size) * tp_size diff --git a/megatron/core/inference/engines/dynamic_engine.py b/megatron/core/inference/engines/dynamic_engine.py index 97f3e098c2b..f96235db0c0 100644 --- a/megatron/core/inference/engines/dynamic_engine.py +++ b/megatron/core/inference/engines/dynamic_engine.py @@ -50,7 +50,7 @@ unset_inference_cuda_graphed_iteration_for_ep_inference, ) from megatron.core.process_groups_config import ProcessGroupCollection -from megatron.core.transformer.cuda_graphs import delete_cuda_graphs +from megatron.core.transformer.cuda_graphs import delete_cuda_graphs, graph_capture from megatron.core.transformer.enums import CudaGraphScope from megatron.core.transformer.moe.router_replay import RouterReplay, RouterReplayAction from megatron.core.utils import ( @@ -63,7 +63,9 @@ internal_api, nvtx_range_pop, nvtx_range_push, + round_up_to_nearest_multiple, trace_async_exceptions, + unwrap_model, ) from .async_zmq_communicator import AsyncZMQCommunicator @@ -365,6 +367,21 @@ def create_cuda_graphs(self, reset_context: bool = True): unwrapped_model = controller.inference_wrapped_model.model set_inference_cuda_graphed_iteration_for_ep_inference(unwrapped_model) + # MTP warmup preparation: capture MTP CUDA graphs alongside the + # decoder graphs within the same loop rather than in a separate pass. + unwrapped = unwrap_model(controller.inference_wrapped_model.model) + mtp_warmup_enabled = ( + controller.num_mtp_heads > 0 + and (controller.num_speculative_tokens or 0) > 0 + and hasattr(unwrapped, 'mtp') + ) + if mtp_warmup_enabled: + tp_size = get_pg_size(controller.inference_wrapped_model.tp_group) + sp_enabled = model_config.sequence_parallel and tp_size > 1 + mtp_pass_depth = not unwrapped.mtp.mtp_use_repeated_layer + mtp_warmup_depths = range(controller._num_mtp_depths) if mtp_pass_depth else [None] + mtp_seen_batch_sizes = set() + tbar = enumerate(context.cuda_graph_batch_dimensions_list) if HAVE_TQDM: tbar = tqdm(tbar, total=len(context.cuda_graph_batch_dimensions_list)) @@ -390,12 +407,39 @@ def create_cuda_graphs(self, reset_context: bool = True): # Forward pass -> logits. controller._dynamic_step_forward_logits(input_ids, position_ids) + # MTP CUDA graph warmup for this batch dimension. + if mtp_warmup_enabled: + n = cuda_graph_batch_dimension.req_count + if sp_enabled: + n = round_up_to_nearest_multiple(n, tp_size) + if n > 0 and n not in mtp_seen_batch_sizes: + mtp_seen_batch_sizes.add(n) + device = torch.cuda.current_device() + batch_dim = n // tp_size if sp_enabled else n + # Use zeros (not empty) — garbage token IDs cause OOB embedding lookups during graph capture/replay. + for depth in mtp_warmup_depths: + with graph_capture(): + unwrapped.compute_mtp_single_step( + hidden_states=torch.zeros( + (batch_dim, 1, model_config.hidden_size), + device=device, + dtype=model_config.params_dtype, + ), + next_token_ids=torch.zeros((1, n), device=device, dtype=torch.long), + position_ids=torch.zeros((1, n), device=device, dtype=torch.int64), + depth=depth, + ) + context.reset() # Disable inference dispatcher after graph capture if is_inference_optimized_ep: unset_inference_cuda_graphed_iteration_for_ep_inference(unwrapped_model) + if mtp_warmup_enabled and mtp_seen_batch_sizes: + controller.has_mtp_cuda_graphs = True + logging.info("> MTP CUDA graph warmup: %d batch size(s)", len(mtp_seen_batch_sizes)) + # Memory usage. time_end = time.time() mem_stats_end = torch.cuda.memory_stats() diff --git a/megatron/core/inference/text_generation_controllers/mtp_utils_pytorch.py b/megatron/core/inference/text_generation_controllers/mtp_utils_pytorch.py new file mode 100644 index 00000000000..59bad67d70a --- /dev/null +++ b/megatron/core/inference/text_generation_controllers/mtp_utils_pytorch.py @@ -0,0 +1,244 @@ +# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +import torch + + +def rewind_kv_cache( + accepted_counts, + prefill_status, + last_kv_block_offset, + kv_length_offsets, + kv_block_counts, + last_kv_block_id, + kv_block_ids, + num_speculative_tokens, + block_size_tokens, + num_active_requests=None, +): + """Update the KV cache bookkeeping for speculative decoding. + + After forward pass with speculative tokens, some tokens may be rejected. + This function "rewinds" the KV cache bookkeeping to reflect only the accepted tokens. + + When speculative tokens are rejected, we need to: + 1. Update kv_length_offsets (total sequence length) + 2. Update last_kv_block_offset (position within last block) + 3. If rewinding crosses a block boundary: + - Reduce kv_block_counts + - Update last_kv_block_id to point to the previous block + - Clear the entry in kv_block_ids for the released block + + Mutates the input tensors in-place. + + Returns (blocks_to_release, remove_mask). + """ + N = accepted_counts.shape[0] + if num_active_requests is None: + num_active_requests = N + + blocks_to_release = torch.empty_like(last_kv_block_id) + remove_mask = torch.empty(N, device=accepted_counts.device, dtype=torch.bool) + + for i in range(N): + if i >= num_active_requests: + blocks_to_release[i] = 0 + remove_mask[i] = False + continue + + accepted = accepted_counts[i].item() + prefill = prefill_status[i].item() + last_offset = last_kv_block_offset[i].item() + kv_length = kv_length_offsets[i].item() + block_count = kv_block_counts[i].item() + last_block = last_kv_block_id[i].item() + + # Number of tokens to rewind (rejected speculative tokens). + # For prefill requests, no speculative tokens were forwarded through the model, + # so there is nothing to rewind. + num_to_rewind = 0 if prefill == 1 else num_speculative_tokens - accepted + + # Save the original offset BEFORE modifying to correctly detect block boundary crossing. + # A request crosses back to a previous block if: original_offset - num_to_rewind < 0 + diff = last_offset - num_to_rewind + remove = diff < 0 + + # Update the offsets + new_offset = diff % block_size_tokens + last_kv_block_offset[i] = new_offset + kv_length_offsets[i] = kv_length - num_to_rewind + + # For requests that crossed back to a previous block, we need to: + # 1. Reduce the block count by 1 + # 2. Get the block ID to release (current last_kv_block_id) + # 3. Update last_kv_block_id to point to the previous block + # 4. Clear the entry in kv_block_ids for the released block + # 5. Release the block back to the allocator + blocks_to_release[i] = last_block + + # Reduce block counts for requests that crossed back + new_block_count = block_count - 1 if remove else block_count + kv_block_counts[i] = new_block_count + + # Update last_kv_block_id to point to the previous block (at index new_count - 1) + prev_idx = max(new_block_count - 1, 0) + prev_block_id = kv_block_ids[i, prev_idx].item() + last_kv_block_id[i] = prev_block_id if remove else last_block + + # Clear the released block entry (at index new_count, which was the old last block) + scatter_idx = min(new_block_count, kv_block_ids.shape[1] - 1) + if remove: + kv_block_ids[i, scatter_idx] = -1 + + remove_mask[i] = remove + + return blocks_to_release, remove_mask + + +# pylint: disable=line-too-long +def verify_speculative_tokens( + input_tokens, output_tokens, num_decode_requests, num_prefill_requests, num_speculative_tokens +): + """Verify speculative tokens against input tokens and compute acceptance. + + Creates an accepted tokens mask where: + - For prefill requests, the token is always accepted. + - For decode requests, the first token (base token) is always accepted, then we compare + sampled tokens with input tokens and accept consecutive matches. + Then finds the index of the last accepted token per request. + + Example (assume 1, 2, and 0 spec tokens are accepted in the first 3 decode requests): + input_tokens_required: [ a5 a6s a7s | b3 b4s b5s | c6 c7s c8s | d2 | e4 ] # Size 11 + Output tokens [ a6o a7o a8o | b40 b5o b6o | c7o c8o c9o | d3o | e5o ] + Output tokens right shift [ d3o a6o a7o | a8o b40 b5o | b6o c7o c8o | c9o | d3o ] + Accepted tokens mask [ 1 1 0 | 1 1 1 | 1 0 0 | 1 | 1 ] + Last one indices [ 1 | 5 | 6 | 9 | 10 ] + + Returns: + tuple: (last_one_indices, accepted_tokens_mask, input_tokens) where + last_one_indices contains the index of the last accepted token per request. + """ + if input_tokens.ndim == 2: + input_tokens = input_tokens.squeeze(0) + + stride = num_speculative_tokens + 1 + active_request_count = num_decode_requests + num_prefill_requests + decode_len = num_decode_requests * stride + + # Initialize mask with False to prevent boundary bleed + accepted_tokens_mask = torch.zeros_like(input_tokens, dtype=torch.bool) + + # Safe decode token verification without cross-batch boundary contamination + decode_mask_2d = None + if num_decode_requests > 0: + decode_inputs = input_tokens[:decode_len].reshape(num_decode_requests, stride) + decode_outputs = output_tokens[:decode_len].reshape(num_decode_requests, stride) + + # Shift outputs right by 1 *within* each request to align sampled tokens with input targets + decode_outputs_shifted = decode_outputs.roll(1, dims=1) + decode_mask_2d = decode_inputs == decode_outputs_shifted + # The first token (base token) is always accepted + decode_mask_2d[:, 0] = True + # Enforce consecutive acceptance: cummin propagates False to the right + decode_mask_2d = decode_mask_2d.cummin(dim=1).values + accepted_tokens_mask[:decode_len] = decode_mask_2d.flatten() + + # Make all prefill tokens accepted + if num_prefill_requests > 0: + accepted_tokens_mask[decode_len:] = True + + last_one_indices = torch.full( + (active_request_count,), -1, device=input_tokens.device, dtype=torch.long + ) + + if num_decode_requests > 0: + # Summing the consecutive mask gives the count; subtract 1 for the local index + local_last_indices = decode_mask_2d.sum(dim=1) - 1 + row_offsets = torch.arange(num_decode_requests, device=input_tokens.device) * stride + last_one_indices[:num_decode_requests] = row_offsets + local_last_indices + + if num_prefill_requests > 0: + prefill_valid = torch.nonzero(accepted_tokens_mask[decode_len:]).squeeze(-1) + decode_len + last_one_indices[num_decode_requests:] = prefill_valid + + return last_one_indices, accepted_tokens_mask, input_tokens + + +# pylint: disable=line-too-long +def prepare_next_forward_pass( + num_decode_requests, + output_tokens, + required_logit_indices, + last_one_indices, + accepted_tokens_mask, + input_tokens, + sampled_tokens_buf, + last_accepted_seq_buf, + accepted_tokens_per_request, + accepted_token_counts, + num_speculative_tokens, +): + """Prepare data for the next forward pass after speculative token verification. + + For each active request: + - Store the final sampled tokens for the next forward pass. + - Store the last accepted positions in the packed sequence for serial + MTP computation after verification. + + For decode requests, extract accepted tokens and counts: + input_tokens_required: [ a5 a6s a7s | b3 b4s b5s | c6 c7s c8s | d2 | e4 ] + Accepted tokens mask [ 1 1 0 | 1 1 1 | 1 0 0 | 1 | 1 ] + Accepted tokens [ [a6s -1] | [b4s b5s] | [-1 -1] ] # Only decode requests (prefill defaults to -1) + Accepted token counts [ 1 | 2 | 0 ] # Prefill defaults to 0 + + Writes results into the pre-allocated buffers provided by the caller. + """ + active_request_count = last_one_indices.shape[0] + stride = num_speculative_tokens + 1 + + for pid in range(active_request_count): + idx = last_one_indices[pid].item() + + # Store the final sampled tokens for the next forward pass. + sampled_tokens_buf[pid] = output_tokens[idx] + + # Store the last accepted positions in the packed sequence for serial + # MTP computation after verification. + last_accepted_seq_buf[pid] = required_logit_indices[idx] + + # Extract accepted tokens and counts for decode requests. + # For prefill it is always set to 1. For decode, the first token is always accepted, + # then we compare with input tokens and accept the next tokens if its a match. + if pid < num_decode_requests: + base = pid * stride + # Skip the first token of every decode request (i.e a5, b3, c6) + for s in range(num_speculative_tokens): + pos = base + 1 + s + if accepted_tokens_mask[pos]: + accepted_tokens_per_request[pid, s] = input_tokens[pos] + else: + accepted_tokens_per_request[pid, s] = -1 + + count = 0 + for s in range(num_speculative_tokens): + if accepted_tokens_per_request[pid, s].item() != -1: + count += 1 + accepted_token_counts[pid] = count + + +def mamba_state_selective_copy( + intermediate_states, current_states, prefill_status, state_idx, accepted_counts, num_layers +): + """Mamba speculative rewind state update. + + For each decode request, copies + `intermediate[layer, slot, accepted_count, ...]` → + `current[layer, slot, ...]` for every Mamba layer. + """ + N = prefill_status.shape[0] + for i in range(N): + if prefill_status[i].item() == 1: + continue + slot = state_idx[i].item() + accepted = accepted_counts[i].item() + for layer in range(num_layers): + current_states[layer, slot] = intermediate_states[layer, slot, accepted] diff --git a/megatron/core/inference/text_generation_controllers/mtp_utils_triton.py b/megatron/core/inference/text_generation_controllers/mtp_utils_triton.py new file mode 100644 index 00000000000..37ff55c1e99 --- /dev/null +++ b/megatron/core/inference/text_generation_controllers/mtp_utils_triton.py @@ -0,0 +1,456 @@ +# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +import math + +import torch + +try: + import triton + import triton.language as tl + + HAVE_TRITON = True +except ImportError: + from unittest.mock import MagicMock + + from megatron.core.utils import null_decorator + + triton = MagicMock() + triton.jit = null_decorator + tl = MagicMock() + HAVE_TRITON = False + + +# --------------------------------------------------------------------------- +# Kernel 1: KV-cache rewind for speculative decoding +# --------------------------------------------------------------------------- +@triton.jit +def _rewind_kv_cache_kernel( + # Per-request input (read-only) + ACCEPTED_COUNTS_PTR, + PREFILL_STATUS_PTR, + # Per-request state (read-write, updated in-place) + LAST_KV_BLOCK_OFFSET_PTR, + KV_LENGTH_OFFSETS_PTR, + KV_BLOCK_COUNTS_PTR, + LAST_KV_BLOCK_ID_PTR, + # 2-D table [N, max_blocks] (read-write) + KV_BLOCK_IDS_PTR, + # Per-request outputs + BLOCKS_TO_RELEASE_PTR, + REMOVE_MASK_PTR, + # Strides / limits + kv_block_ids_stride, + max_blocks_minus_1, + num_active_requests, + # Compile-time constants + NUM_SPEC_TOKENS: tl.constexpr, + BLOCK_SIZE_TOKENS: tl.constexpr, +): + """Rewind KV-cache bookkeeping for one request after speculative verification. + + Grid: may be padded beyond active requests for CUDA-graph compatibility. + Each program handles exactly one request. Programs with + `pid >= num_active_requests` are padding and produce safe no-op outputs. + """ + pid = tl.program_id(0) + + # Padding programs: write safe defaults and skip all state mutation. + if pid >= num_active_requests: + tl.store(BLOCKS_TO_RELEASE_PTR + pid, 0) + tl.store(REMOVE_MASK_PTR + pid, False) + return + + # --- Load per-request scalars --- + accepted = tl.load(ACCEPTED_COUNTS_PTR + pid) + prefill = tl.load(PREFILL_STATUS_PTR + pid) + last_offset = tl.load(LAST_KV_BLOCK_OFFSET_PTR + pid) + kv_length = tl.load(KV_LENGTH_OFFSETS_PTR + pid) + block_count = tl.load(KV_BLOCK_COUNTS_PTR + pid) + last_block_id = tl.load(LAST_KV_BLOCK_ID_PTR + pid) + + # --- Compute rewind (zero for prefill requests) --- + num_to_rewind = tl.where(prefill == 1, 0, NUM_SPEC_TOKENS - accepted) + diff = last_offset - num_to_rewind + remove = diff < 0 + + # Python-style modulo: ((diff % M) + M) % M to handle negative diff + new_offset = ((diff % BLOCK_SIZE_TOKENS) + BLOCK_SIZE_TOKENS) % BLOCK_SIZE_TOKENS + tl.store(LAST_KV_BLOCK_OFFSET_PTR + pid, new_offset) + tl.store(KV_LENGTH_OFFSETS_PTR + pid, kv_length - num_to_rewind) + + # Save current last block id (will be released by caller if remove is True) + tl.store(BLOCKS_TO_RELEASE_PTR + pid, last_block_id) + + # Decrement block count when a block boundary was crossed + new_block_count = tl.where(remove, block_count - 1, block_count) + tl.store(KV_BLOCK_COUNTS_PTR + pid, new_block_count) + + # Gather previous block id from the 2-D table + kv_row_base = pid.to(tl.int64) * kv_block_ids_stride + prev_idx = tl.maximum(new_block_count - 1, 0) + prev_block_id = tl.load(KV_BLOCK_IDS_PTR + kv_row_base + prev_idx) + + # Conditionally update last block id + tl.store(LAST_KV_BLOCK_ID_PTR + pid, tl.where(remove, prev_block_id, last_block_id)) + + # Clear released block entry via scatter + scatter_idx = tl.minimum(new_block_count, max_blocks_minus_1) + current_val = tl.load(KV_BLOCK_IDS_PTR + kv_row_base + scatter_idx) + tl.store(KV_BLOCK_IDS_PTR + kv_row_base + scatter_idx, tl.where(remove, -1, current_val)) + + # Output remove mask for the caller (to release blocks outside this kernel) + tl.store(REMOVE_MASK_PTR + pid, remove) + + +def rewind_kv_cache( + accepted_counts, + prefill_status, + last_kv_block_offset, + kv_length_offsets, + kv_block_counts, + last_kv_block_id, + kv_block_ids, + num_speculative_tokens, + block_size_tokens, + num_active_requests=None, +): + """Launch the KV-cache rewind Triton kernel. + + Args: + num_active_requests: Number of real (non-padding) requests. When the + grid is padded beyond this count, the kernel skips padding + programs so stale data in padding slots cannot corrupt + bookkeeping. Defaults to `accepted_counts.shape[0]` (no + padding). + + Returns: + (blocks_to_release, remove_mask) — same semantics as the original + torch.compile'd `_rewind_kv_cache` (KV-cache portion only; Mamba + state updates are handled separately by the caller). + """ + N = accepted_counts.shape[0] + if num_active_requests is None: + num_active_requests = N + if N == 0: + return ( + torch.empty(0, device=accepted_counts.device, dtype=last_kv_block_id.dtype), + torch.empty(0, device=accepted_counts.device, dtype=torch.bool), + ) + + blocks_to_release = torch.empty_like(last_kv_block_id) + remove_mask = torch.empty(N, device=accepted_counts.device, dtype=torch.bool) + + _rewind_kv_cache_kernel[(N,)]( + accepted_counts, + prefill_status, + last_kv_block_offset, + kv_length_offsets, + kv_block_counts, + last_kv_block_id, + kv_block_ids, + blocks_to_release, + remove_mask, + kv_block_ids_stride=kv_block_ids.stride(0), + max_blocks_minus_1=kv_block_ids.shape[1] - 1, + num_active_requests=num_active_requests, + NUM_SPEC_TOKENS=num_speculative_tokens, + BLOCK_SIZE_TOKENS=block_size_tokens, + ) + return blocks_to_release, remove_mask + + +# --------------------------------------------------------------------------- +# Kernel 2: Verify speculative tokens +# --------------------------------------------------------------------------- +@triton.jit +def _verify_speculative_tokens_kernel( + INPUT_TOKENS_PTR, + OUTPUT_TOKENS_PTR, + # Outputs + ACCEPTED_MASK_PTR, + LAST_ONE_INDICES_PTR, + # Runtime scalars + num_decode_requests, + decode_len, + # Compile-time constants + STRIDE: tl.constexpr, # num_speculative_tokens + 1 + BLOCK_SIZE: tl.constexpr, # next_power_of_2(STRIDE) +): + """Verify speculative tokens for one request. + + Grid: (active_request_count,) + Programs 0..num_decode_requests-1 handle decode requests. + Programs num_decode_requests..end handle prefill requests. + """ + pid = tl.program_id(0) + + if pid < num_decode_requests: + base = pid * STRIDE + offsets = tl.arange(0, BLOCK_SIZE) + valid = offsets < STRIDE + + input_toks = tl.load(INPUT_TOKENS_PTR + base + offsets, mask=valid, other=0) + + # Build shifted output: shifted[i] = output[i-1]. + # Position 0 uses a dummy load (always accepted regardless). + safe_shifted = tl.where(offsets > 0, offsets - 1, 0) + shifted_output = tl.load(OUTPUT_TOKENS_PTR + base + safe_shifted, mask=valid, other=0) + + # First token is always accepted; rest must match shifted output. + match = tl.where(offsets == 0, 1, (input_toks == shifted_output).to(tl.int32)) + match = tl.where(valid, match, 0) + + # Consecutive acceptance via cumulative-sum trick: + # accepted[i] iff cumsum(match)[i] == i + 1 + cumsum = tl.cumsum(match, axis=0) + accepted = (cumsum == (offsets + 1)) & valid + + tl.store(ACCEPTED_MASK_PTR + base + offsets, accepted, mask=valid) + + accepted_count = tl.sum(accepted.to(tl.int32)) + tl.store(LAST_ONE_INDICES_PTR + pid, (base + accepted_count - 1).to(tl.int64)) + else: + # Prefill request — single token, always accepted + prefill_idx = decode_len + (pid - num_decode_requests) + tl.store(ACCEPTED_MASK_PTR + prefill_idx, 1) + tl.store(LAST_ONE_INDICES_PTR + pid, prefill_idx.to(tl.int64)) + + +def verify_speculative_tokens( + input_tokens, output_tokens, num_decode_requests, num_prefill_requests, num_speculative_tokens +): + """Launch the speculative-token verification Triton kernel. + + Returns: + (last_one_indices, accepted_tokens_mask, input_tokens) + matching the original `_verify_speculative_tokens` signature. + """ + if input_tokens.ndim == 2: + input_tokens = input_tokens.squeeze(0) + + device = input_tokens.device + active_request_count = num_decode_requests + num_prefill_requests + stride = num_speculative_tokens + 1 + decode_len = num_decode_requests * stride + + accepted_tokens_mask = torch.zeros_like(input_tokens, dtype=torch.bool) + last_one_indices = torch.full((active_request_count,), -1, device=device, dtype=torch.long) + + if active_request_count > 0: + block_size = triton.next_power_of_2(stride) + _verify_speculative_tokens_kernel[(active_request_count,)]( + input_tokens, + output_tokens, + accepted_tokens_mask, + last_one_indices, + num_decode_requests=num_decode_requests, + decode_len=decode_len, + STRIDE=stride, + BLOCK_SIZE=block_size, + ) + + return last_one_indices, accepted_tokens_mask, input_tokens + + +# --------------------------------------------------------------------------- +# Kernel 3: Prepare speculative tokens for next forward pass +# --------------------------------------------------------------------------- +@triton.jit +def _prepare_next_forward_pass_kernel( + OUTPUT_TOKENS_PTR, + REQUIRED_LOGIT_INDICES_PTR, + LAST_ONE_INDICES_PTR, + INPUT_TOKENS_PTR, + ACCEPTED_MASK_PTR, + # Outputs + SAMPLED_TOKENS_OUT_PTR, + LAST_ACCEPTED_SEQ_OUT_PTR, + ACCEPTED_TOKENS_OUT_PTR, + ACCEPTED_COUNTS_OUT_PTR, + # Strides + accepted_tokens_out_stride, + # Runtime scalars + num_decode_requests, + # Compile-time constants + STRIDE: tl.constexpr, # num_speculative_tokens + 1 + NUM_SPEC_TOKENS: tl.constexpr, + SPEC_BLOCK_SIZE: tl.constexpr, # next_power_of_2(NUM_SPEC_TOKENS) +): + """Gather final tokens and extract accepted speculative tokens per request. + + Grid: (active_request_count,) + """ + pid = tl.program_id(0) + + # --- Gather final sampled token and sequence index for every request --- + idx = tl.load(LAST_ONE_INDICES_PTR + pid) + tl.store(SAMPLED_TOKENS_OUT_PTR + pid, tl.load(OUTPUT_TOKENS_PTR + idx)) + tl.store(LAST_ACCEPTED_SEQ_OUT_PTR + pid, tl.load(REQUIRED_LOGIT_INDICES_PTR + idx)) + + # --- For decode requests: extract accepted tokens and count --- + if pid < num_decode_requests: + base = pid * STRIDE + spec_offsets = tl.arange(0, SPEC_BLOCK_SIZE) + spec_valid = spec_offsets < NUM_SPEC_TOKENS + token_positions = base + 1 + spec_offsets # skip first (base) token + + tokens = tl.load(INPUT_TOKENS_PTR + token_positions, mask=spec_valid, other=0) + mask_val = tl.load(ACCEPTED_MASK_PTR + token_positions, mask=spec_valid, other=0) + accepted = mask_val != 0 + + result = tl.where(accepted & spec_valid, tokens, -1) + + out_base = pid.to(tl.int64) * accepted_tokens_out_stride + tl.store(ACCEPTED_TOKENS_OUT_PTR + out_base + spec_offsets, result, mask=spec_valid) + + count = tl.sum((accepted & spec_valid).to(tl.int64)) + tl.store(ACCEPTED_COUNTS_OUT_PTR + pid, count) + + +def prepare_next_forward_pass( + num_decode_requests, + output_tokens, + required_logit_indices, + last_one_indices, + accepted_tokens_mask, + input_tokens, + sampled_tokens_buf, + last_accepted_seq_buf, + accepted_tokens_per_request, + accepted_token_counts, + num_speculative_tokens, +): + """Launch the prepare-next-forward-pass Triton kernel. + + Writes results into the pre-allocated buffers provided by the caller. + """ + active_request_count = last_one_indices.shape[0] + if active_request_count == 0: + return + + stride = num_speculative_tokens + 1 + spec_block_size = triton.next_power_of_2(num_speculative_tokens) + + _prepare_next_forward_pass_kernel[(active_request_count,)]( + output_tokens, + required_logit_indices, + last_one_indices, + input_tokens, + accepted_tokens_mask, + sampled_tokens_buf, + last_accepted_seq_buf, + accepted_tokens_per_request, + accepted_token_counts, + accepted_tokens_out_stride=accepted_tokens_per_request.stride(0), + num_decode_requests=num_decode_requests, + STRIDE=stride, + NUM_SPEC_TOKENS=num_speculative_tokens, + SPEC_BLOCK_SIZE=spec_block_size, + ) + + +# --------------------------------------------------------------------------- +# Kernel 4: Mamba state selective copy (eliminates temporary allocations) +# --------------------------------------------------------------------------- +@triton.jit +def _mamba_state_selective_copy_kernel( + # Source: intermediate states [L, M, S+1, *state_shape] + SRC_PTR, + # Destination: current states [L, M, *state_shape] + DST_PTR, + # Per-request index arrays + PREFILL_STATUS_PTR, # [N] 0=decode, 1=prefill + STATE_IDX_PTR, # [N] maps request → mamba state slot + ACCEPTED_PTR, # [N] accepted token index per request + # Strides (in elements) + src_stride_layer, + src_stride_slot, + src_stride_spec, + dst_stride_layer, + dst_stride_slot, + # Data size + STATE_SIZE, + # Compile-time + BLOCK_SIZE: tl.constexpr, +): + """Copy intermediate Mamba state to current state for decode requests. + + Grid: (N, L, num_chunks) + - dim 0: active request index + - dim 1: mamba layer index + - dim 2: chunk of the flattened state vector + + No-op for prefill requests. + """ + pid_req = tl.program_id(0) + pid_layer = tl.program_id(1) + pid_chunk = tl.program_id(2) + + # Skip prefill requests immediately. + prefill = tl.load(PREFILL_STATUS_PTR + pid_req) + if prefill == 1: + return + + state_idx = tl.load(STATE_IDX_PTR + pid_req).to(tl.int64) + accepted = tl.load(ACCEPTED_PTR + pid_req).to(tl.int64) + + chunk_start = pid_chunk * BLOCK_SIZE + offsets = tl.arange(0, BLOCK_SIZE) + elem_offsets = chunk_start + offsets + mask = elem_offsets < STATE_SIZE + + src_base = ( + pid_layer.to(tl.int64) * src_stride_layer + + state_idx * src_stride_slot + + accepted * src_stride_spec + ) + dst_base = pid_layer.to(tl.int64) * dst_stride_layer + state_idx * dst_stride_slot + + data = tl.load(SRC_PTR + src_base + elem_offsets, mask=mask) + tl.store(DST_PTR + dst_base + elem_offsets, data, mask=mask) + + +def mamba_state_selective_copy( + intermediate_states, current_states, prefill_status, state_idx, accepted_counts, num_layers +): + """Copy accepted intermediate Mamba states to current states in-place. + + For each decode request, copies + `intermediate[layer, slot, accepted_count, ...]` → + `current[layer, slot, ...]` for every Mamba layer. + + Args: + intermediate_states: `(L, M, S+1, *state_shape)` — intermediate buffer. + current_states: `(L, M, *state_shape)` — current state buffer (updated in-place). + prefill_status: `(N,)` int tensor — 0 for decode, 1 for prefill. + state_idx: `(N,)` int tensor — mamba state slot index per request. + accepted_counts: `(N,)` int tensor — accepted token index per request. + num_layers: number of Mamba layers (first dim of the state tensors). + """ + N = prefill_status.shape[0] + if N == 0: + return + + # The state vector to copy per (layer, request) is the product of all + # trailing dimensions after the speculative-token axis. + # intermediate shape: (L, M, S+1, *state_shape) → state_size = prod(state_shape) + state_size = math.prod(intermediate_states.shape[3:]) + + BLOCK_SIZE = 1024 + num_chunks = triton.cdiv(state_size, BLOCK_SIZE) + grid = (N, num_layers, num_chunks) + + _mamba_state_selective_copy_kernel[grid]( + intermediate_states, + current_states, + prefill_status, + state_idx, + accepted_counts, + src_stride_layer=intermediate_states.stride(0), + src_stride_slot=intermediate_states.stride(1), + src_stride_spec=intermediate_states.stride(2), + dst_stride_layer=current_states.stride(0), + dst_stride_slot=current_states.stride(1), + STATE_SIZE=state_size, + BLOCK_SIZE=BLOCK_SIZE, + ) diff --git a/megatron/core/inference/text_generation_controllers/text_generation_controller.py b/megatron/core/inference/text_generation_controllers/text_generation_controller.py index 2798533e783..993d05afbe0 100644 --- a/megatron/core/inference/text_generation_controllers/text_generation_controller.py +++ b/megatron/core/inference/text_generation_controllers/text_generation_controller.py @@ -40,6 +40,9 @@ get_asyncio_loop, get_model_config, get_pg_size, + nvtx_range_pop, + nvtx_range_push, + round_up_to_nearest_multiple, unwrap_model, ) @@ -52,6 +55,12 @@ HAVE_TE = False from megatron.core.inference.batch_dimensions_utils import InferenceBatchDimensions +from megatron.core.inference.text_generation_controllers.mtp_utils_triton import ( + mamba_state_selective_copy, + prepare_next_forward_pass, + rewind_kv_cache, + verify_speculative_tokens, +) # pylint: disable=line-too-long @@ -91,6 +100,7 @@ def __init__(self, inference_wrapped_model: AbstractModelInferenceWrapper, token self.sampling_rng = torch.Generator(device=torch.cuda.current_device()) self.num_mtp_heads = self._get_mtp_num_heads() + self.has_mtp_cuda_graphs = False self.sampling_rng.manual_seed(self.model_config.inference_sampling_seed) if ( @@ -137,12 +147,6 @@ def _init_dynamic_sampling_tensors(self): self._sampling_backend = "torch" self._sampled_tokens_cuda = torch.empty(max_requests, dtype=torch.int64, device=device) - # Speculative tokens tensor will be allocated later when num_speculative_tokens is set by the engine - self._accepted_tokens_per_request = None - # MTP tensor will be allocated later when num_speculative_tokens is set by the engine - self._sampled_mtp_tokens_cuda = None - # Last accepted sequence indices for serial MTP computation - self._last_accepted_seq_indices = None # Keep track of request metadata. self._request_metadata: Dict[str, Tensor] = {} @@ -158,23 +162,49 @@ def _init_dynamic_sampling_tensors(self): if self._sampling_backend == "torch": self._torch_sampling_buckets: List[Tuple] = [] - self._init_mtp_sampling_tensor() + # Cache values that are constant across inference steps. + self._unwrapped_model = unwrap_model(self.inference_wrapped_model.model) + self._is_last_pp_stage = is_pipeline_last_stage(self.pp_group) + self._tp_size = get_pg_size(self.inference_wrapped_model.tp_group) + self._sp_enabled = self.model_config.sequence_parallel and self._tp_size > 1 - def _init_mtp_sampling_tensor(self): - """Initialize the MTP sampling tensor after num_speculative_tokens is set.""" - if self.num_speculative_tokens is not None and self.num_speculative_tokens > 0: - context = self.inference_wrapped_model.inference_context - max_requests = context.max_requests - device = torch.cuda.current_device() - self._sampled_mtp_tokens_cuda = torch.empty( - [self.num_speculative_tokens, max_requests], dtype=torch.int64, device=device - ) - self._accepted_tokens_per_request = ( - torch.ones( - [max_requests, self.num_speculative_tokens], dtype=torch.int64, device=device - ) - * -1 + self._init_mtp_sampling_tensors() + + def _init_mtp_sampling_tensors(self): + """Pre-allocate MTP sampling tensors. + + Addresses must be stable across steps for CUDA graph capture. + """ + if not self.num_speculative_tokens: + self._sampled_mtp_tokens_cuda = None + self._accepted_tokens_per_request = None + self._last_accepted_seq_indices = None + return + + context = self.inference_wrapped_model.inference_context + max_requests = context.max_requests + device = torch.cuda.current_device() + self._sampled_mtp_tokens_cuda = torch.empty( + [self.num_speculative_tokens, max_requests], dtype=torch.int64, device=device + ) + self._accepted_tokens_per_request = ( + torch.ones( + [max_requests, self.num_speculative_tokens], dtype=torch.int64, device=device ) + * -1 + ) + self._accepted_token_counts_per_request = torch.zeros( + max_requests, dtype=torch.int64, device=device + ) + self._last_accepted_seq_indices_buf = torch.empty( + max_requests, dtype=torch.int64, device=device + ) + self._last_accepted_seq_indices = None + self._num_mtp_depths = min(self.num_speculative_tokens, self.num_mtp_heads) + self._mtp_token_ids_buf = torch.empty([1, max_requests], dtype=torch.int64, device=device) + self._mtp_position_ids_buf = torch.empty( + [1, max_requests], dtype=torch.int64, device=device + ) @staticmethod def tokenize_prompt(tokenizer, prompt: str, add_BOS: bool = False) -> List[int]: @@ -582,6 +612,25 @@ def _dynamic_step_context_init( is_expert_parallel_dummy_cuda_graph_step=is_dummy_forward, ) + # Derive the MTP padded batch size from the existing padded graph dimensions. + # For MoE models this is post EP sync. In eager mode MTP uses locally SP-aligned + # batch size instead. + if self.has_mtp_cuda_graphs and context.using_cuda_graph_this_step(): + self._mtp_resolved_padded_count = context.padded_batch_dimensions.req_count + if self._sp_enabled: + self._mtp_resolved_padded_count = round_up_to_nearest_multiple( + self._mtp_resolved_padded_count, self._tp_size + ) + else: + self._mtp_resolved_padded_count = None + + # Tell the model whether to use MTP CUDA graphs this step. When the + # main model falls back to eager mode, MTP must also run eagerly across + # all EP ranks — otherwise some ranks may replay a captured graph while + # others run eagerly, causing EP collectives to hang. + if self.has_mtp_cuda_graphs: + unwrapped_model.use_mtp_cuda_graphs = context.using_cuda_graph_this_step() + # If using symmetric kernels and we are using using nccl # for prefill turn off symmetric kernels symmetric_ar_type = self.model_config.symmetric_ar_type @@ -697,118 +746,77 @@ def _dynamic_step_sample_bookkeeping(self): bucket_map[sampling_params].append(request_index) # Just unpack the key directly! + device = torch.cuda.current_device() self._torch_sampling_buckets = [ (indices, *sampling_params) for sampling_params, indices in bucket_map.items() ] + # Pre-compute index tensors on GPU to avoid per-step H2D copies. + self._torch_sampling_bucket_index_tensors = [ + torch.tensor(indices, device=device, dtype=torch.long) + for indices, *_ in self._torch_sampling_buckets + ] - def _rewind_kv_cache(self): + def _rewind_kv_cache(self) -> tuple: """Update the KV cache bookkeeping for speculative decoding. After forward pass with speculative tokens, some tokens may be rejected. - This function "rewinds" the KV cache bookkeeping to reflect only the accepted tokens. - - When speculative tokens are rejected, we need to: - 1. Update request_kv_length_offsets (total sequence length) - 2. Update request_last_kv_block_offset (position within last block) - 3. If rewinding crosses a block boundary: - - Reduce request_kv_block_counts - - Update request_last_kv_block_id to point to the previous block - - Clear the entry in request_to_kv_block_ids for the released block - - Release the block back to the allocator + This function "rewinds" the KV cache bookkeeping to reflect only the accepted + tokens. The core bookkeeping is handled by a Triton kernel (one thread per + request). Mamba hybrid-model state updates remain in PyTorch. + + Returns (blocks_to_release, remove_mask) for the caller to release blocks + back to the allocator outside the compiled graph. """ context = self.inference_wrapped_model.inference_context active_request_count = context.total_request_count - context.paused_request_count active_request_slice = slice(context.paused_request_count, context.total_request_count) - # Get the accepted token counts for each request - # Note: _accepted_token_counts is indexed from 0 to active_request_count-1 accepted_tokens_per_request = self._accepted_token_counts_per_request[:active_request_count] - # Number of tokens to rewind (rejected speculative tokens) - num_tokens_to_rewind = self.num_speculative_tokens - accepted_tokens_per_request - - # For prefill requests, no speculative tokens were forwarded through the model, - # so there is nothing to rewind. request_in_prefill_status = context.request_in_prefill_status_tensor[active_request_slice] - num_tokens_to_rewind[request_in_prefill_status == 1] = 0 - - # Save the original offset BEFORE modifying to correctly detect block boundary crossing - original_offset = context.request_last_kv_block_offset[active_request_slice].clone() - - # Check which requests need to rewind to a previous block BEFORE modifying - # A request crosses back to a previous block if: original_offset - num_tokens_to_rewind < 0 - remove_allocated_blocks_mask = (original_offset - num_tokens_to_rewind) < 0 - - # Update the offsets - context.request_last_kv_block_offset[active_request_slice] = ( - original_offset - num_tokens_to_rewind - ) % context.block_size_tokens - - context.request_kv_length_offsets[active_request_slice] = ( - context.request_kv_length_offsets[active_request_slice] - num_tokens_to_rewind + request_last_kv_block_offset = context.request_last_kv_block_offset[active_request_slice] + request_kv_length_offsets = context.request_kv_length_offsets[active_request_slice] + request_kv_block_counts = context.request_kv_block_counts[active_request_slice] + request_last_kv_block_id = context.request_last_kv_block_id[active_request_slice] + request_to_kv_block_ids = context.request_to_kv_block_ids[active_request_slice] + + # --- Triton kernel: core KV-cache rewind --- + blocks_to_release, remove_mask = rewind_kv_cache( + accepted_counts=accepted_tokens_per_request, + prefill_status=request_in_prefill_status, + last_kv_block_offset=request_last_kv_block_offset, + kv_length_offsets=request_kv_length_offsets, + kv_block_counts=request_kv_block_counts, + last_kv_block_id=request_last_kv_block_id, + kv_block_ids=request_to_kv_block_ids, + num_speculative_tokens=self.num_speculative_tokens, + block_size_tokens=context.block_size_tokens, + num_active_requests=active_request_count, ) - # No need to update request_query_lengths (It will be set correctly in the next iteration) - - # For requests that crossed back to a previous block, we need to: - # 1. Reduce the block count by 1 - # 2. Get the block ID to release (current request_last_kv_block_id) - # 3. Update request_last_kv_block_id to point to the previous block - # 4. Clear the entry in request_to_kv_block_ids for the released block - # 5. Release the block back to the allocator - if remove_allocated_blocks_mask.any(): - # Get indices of requests that need to release a block (relative to active requests) - requests_needing_release = torch.nonzero(remove_allocated_blocks_mask, as_tuple=True)[0] - # Convert to absolute indices in the context tensors - absolute_indices = requests_needing_release + context.paused_request_count - - # No clone needed: advanced (fancy) indexing with a tensor already returns - # a copy, not a view. - blocks_to_release = context.request_last_kv_block_id[absolute_indices] - - # Reduce block counts for requests that crossed back - context.request_kv_block_counts[absolute_indices] -= 1 - - # Get the new block counts after decrement - new_block_counts = context.request_kv_block_counts[absolute_indices] - - # Update request_last_kv_block_id to point to the previous block - # and clear the released block entry in request_to_kv_block_ids - # Vectorized implementation using advanced indexing: - # Note: new_block_counts is guaranteed to be > 0 for all requests here, since - # crossing back to a previous block implies the request had at least 2 blocks. - - # Update request_last_kv_block_id to point to the previous block (at index new_count - 1) - context.request_last_kv_block_id[absolute_indices] = context.request_to_kv_block_ids[ - absolute_indices, new_block_counts - 1 - ] - - # Clear the released block entry (at index new_count, which was the old last block) - context.request_to_kv_block_ids[absolute_indices, new_block_counts] = -1 - - # Release the blocks back to the allocator - context.kv_block_allocator.release_memory_blocks(blocks_to_release) - - # Mamba speculative rewind state update + # Mamba speculative rewind: copy accepted intermediate states in-place. if context.is_hybrid_model: - active_mamba_indices = context.mamba_metadata.request_to_mamba_state_idx[ + mamba_state_idx = context.mamba_metadata.request_to_mamba_state_idx[ active_request_slice ] - is_decode_mask = context.request_in_prefill_status_tensor[active_request_slice] == 0 - decode_mamba_indices = active_mamba_indices[is_decode_mask] - accepted_tokens_per_decode_request = accepted_tokens_per_request[is_decode_mask] - - if decode_mamba_indices.numel() > 0: - context.mamba_conv_states[:, decode_mamba_indices] = ( - context.mamba_intermediate_conv_states[ - :, decode_mamba_indices, accepted_tokens_per_decode_request - ] - ) - context.mamba_ssm_states[:, decode_mamba_indices] = ( - context.mamba_intermediate_ssm_states[ - :, decode_mamba_indices, accepted_tokens_per_decode_request - ] - ) + mamba_state_selective_copy( + intermediate_states=context.mamba_intermediate_conv_states, + current_states=context.mamba_conv_states, + prefill_status=request_in_prefill_status, + state_idx=mamba_state_idx, + accepted_counts=accepted_tokens_per_request, + num_layers=context.num_mamba_layers, + ) + mamba_state_selective_copy( + intermediate_states=context.mamba_intermediate_ssm_states, + current_states=context.mamba_ssm_states, + prefill_status=request_in_prefill_status, + state_idx=mamba_state_idx, + accepted_counts=accepted_tokens_per_request, + num_layers=context.num_mamba_layers, + ) + + return blocks_to_release, remove_mask def _sample_from_logits_2d(self, logits_2d: Tensor) -> Tensor: """Sample tokens from 2D logits using existing sampling parameters. @@ -820,18 +828,15 @@ def _sample_from_logits_2d(self, logits_2d: Tensor) -> Tensor: Tensor: Sampled tokens of shape [num_requests]. """ spec_token_list = [] - indices_list = [] - for request_indices, temp, top_k, top_p in self._torch_sampling_buckets: - request_indices_tensor = torch.tensor( - request_indices, device=logits_2d.device, dtype=torch.long - ) + for idx_tensor, (_, temp, top_k, top_p) in zip( + self._torch_sampling_bucket_index_tensors, self._torch_sampling_buckets + ): spec_token_list.append( - self._torch_sampling_func(logits_2d[request_indices_tensor, :], temp, top_k, top_p) + self._torch_sampling_func(logits_2d[idx_tensor, :], temp, top_k, top_p) ) - indices_list.append(request_indices_tensor) spec_tokens = torch.empty(logits_2d.shape[0], device=logits_2d.device, dtype=torch.int64) - for tokens, indices in zip(spec_token_list, indices_list): + for tokens, indices in zip(spec_token_list, self._torch_sampling_bucket_index_tensors): spec_tokens[indices] = tokens return spec_tokens @@ -847,14 +852,15 @@ def _compute_serial_mtp_and_sample(self): (scattered along the first dimension) between MTP depths to avoid a redundant gather + scatter round-trip per depth. """ + nvtx_range_push("mtp-spec-decoding/serial-mtp-init") context = self.inference_wrapped_model.inference_context active_request_count = context.total_request_count - context.paused_request_count active_slice = slice(context.paused_request_count, context.total_request_count) - unwrapped_model = unwrap_model(self.inference_wrapped_model.model) + unwrapped_model = self._unwrapped_model # On non-last pipeline stages, the model won't have decoder hidden states. - has_mtp = is_pipeline_last_stage(self.pp_group) and hasattr( + has_mtp = self._is_last_pp_stage and hasattr( unwrapped_model, '_decoder_hidden_states_cache' ) @@ -865,7 +871,7 @@ def _compute_serial_mtp_and_sample(self): # When SP is active the decoder output is in scattered format # [S/TP, B, H], but _last_accepted_seq_indices are indices into # the full (gathered) sequence. - if self.model_config.sequence_parallel: + if self._sp_enabled: hidden_states = gather_from_sequence_parallel_region( hidden_states, group=self.inference_wrapped_model.tp_group ) @@ -880,72 +886,88 @@ def _compute_serial_mtp_and_sample(self): # The next position to predict starts at that cache length. adjusted_offsets = context.request_kv_length_offsets[active_slice] processed_tokens = context.request_query_lengths[active_slice] - base_position = adjusted_offsets + processed_tokens + # Cast to int64 to match CUDA graph capture dtype expectations. + base_position = (adjusted_offsets + processed_tokens).to(torch.int64) # Start with the freshly sampled base token. next_token_ids = self._sampled_tokens_cuda[:active_request_count].clone() current_hidden = last_accepted_hidden if has_mtp else None - # Compute padding needed to make batch a multiple of tp_size for SP compatibility. - tp_size = get_pg_size(self.inference_wrapped_model.tp_group) - sp_enabled = self.model_config.sequence_parallel and tp_size > 1 - if sp_enabled: - pad_count = (tp_size - active_request_count % tp_size) % tp_size - padded_count = active_request_count + pad_count + # Compute padding needed to make batch compatible with SP and CUDA graphs. + if getattr(self, '_mtp_resolved_padded_count', None) is not None: + # CUDA-graph path: use the EP-synced padded count. + padded_count = self._mtp_resolved_padded_count + assert not self._sp_enabled or padded_count % self._tp_size == 0 + elif has_mtp: + # Eager path: pad only for SP alignment. + padded_count = active_request_count + if self._sp_enabled: + padded_count = round_up_to_nearest_multiple(padded_count, self._tp_size) else: - pad_count = 0 + padded_count = active_request_count + pad_count = padded_count - active_request_count + + # Pad hidden states and scatter for sequence parallelism. + if has_mtp: + current_hidden = F.pad(current_hidden, (0, 0, 0, 0, 0, pad_count)) + if self._sp_enabled: + current_hidden = scatter_to_sequence_parallel_region( + current_hidden, group=self.inference_wrapped_model.tp_group + ) - # Pad hidden states to align with the tensor parallel size. - if has_mtp and sp_enabled: - if pad_count > 0: - current_hidden = F.pad(current_hidden, (0, 0, 0, 0, 0, pad_count)) + token_ids_buf = self._mtp_token_ids_buf[:, :padded_count] + position_ids_buf = self._mtp_position_ids_buf[:, :padded_count] - current_hidden = scatter_to_sequence_parallel_region( - current_hidden, group=self.inference_wrapped_model.tp_group - ) + # Zero-fill padding slots so the embedding layer never sees out-of-range IDs. + token_ids_buf[0, active_request_count:] = 0 + position_ids_buf[0, active_request_count:] = 0 - num_depths = min(self.num_speculative_tokens, self.num_mtp_heads) - for depth in range(num_depths): - position_ids = (base_position + depth).unsqueeze(0) # [1, active_request_count] - token_ids = next_token_ids.unsqueeze(0) # [1, active_request_count] + nvtx_range_pop("mtp-spec-decoding/serial-mtp-init") + for depth in range(self._num_mtp_depths): + nvtx_range_push(f"mtp-spec-decoding/depth-{depth}") + + token_ids_buf[0, :active_request_count] = next_token_ids + position_ids_buf[0, :active_request_count] = base_position + depth mtp_logits_2d = None if has_mtp: - # Pad token_ids and position_ids each iteration (they change per depth). - if pad_count > 0: - token_ids = F.pad(token_ids, (0, pad_count)) - position_ids = F.pad(position_ids, (0, pad_count)) - + nvtx_range_push(f"mtp-spec-decoding/depth-{depth}/forward") + mtp_depth = None if unwrapped_model.mtp.mtp_use_repeated_layer else depth current_hidden, mtp_logits = unwrapped_model.compute_mtp_single_step( hidden_states=current_hidden, - next_token_ids=token_ids, - position_ids=position_ids, - depth=depth, + next_token_ids=token_ids_buf, + position_ids=position_ids_buf, + depth=mtp_depth, ) + nvtx_range_pop(f"mtp-spec-decoding/depth-{depth}/forward") - # Strip padding from logits only. Hidden states stay padded+SP + # Strip padding from logits only. Hidden states stay padded+SP # between depths to avoid redundant gather/scatter round-trips. - if pad_count > 0: - mtp_logits = mtp_logits[:active_request_count] + mtp_logits = mtp_logits[:active_request_count] # mtp_logits: [active_request_count, 1, vocab_size] mtp_logits_2d = mtp_logits.squeeze(1) # [active_request_count, vocab_size] # Broadcast MTP logits across pipeline stages. if self.model_is_pipeline_parallel: + nvtx_range_push(f"mtp-spec-decoding/depth-{depth}/pp-broadcast") mtp_logits_2d = broadcast_from_last_pipeline_stage( [active_request_count, self.vocab_size], dtype=self.model_config.params_dtype, tensor=mtp_logits_2d, pp_group=self.pp_group, ) + nvtx_range_pop(f"mtp-spec-decoding/depth-{depth}/pp-broadcast") # Sample speculative token using the same sampling parameters. + nvtx_range_push(f"mtp-spec-decoding/depth-{depth}/sample") spec_tokens = self._sample_from_logits_2d(mtp_logits_2d) self._sampled_mtp_tokens_cuda[depth, :active_request_count] = spec_tokens + nvtx_range_pop(f"mtp-spec-decoding/depth-{depth}/sample") # Use sampled token as input for the next depth. next_token_ids = spec_tokens + nvtx_range_pop(f"mtp-spec-decoding/depth-{depth}") # Clean up cached hidden states. if has_mtp: @@ -984,13 +1006,10 @@ def _sample_speculative_logits( output_tokens_jumbled_list = [] token_order_list = [] - for request_indices, temp, top_k, top_p in self._torch_sampling_buckets: - request_indices_tensor = torch.tensor( - request_indices, device=token_to_request_index.device - ) - required_indices = torch.where( - torch.isin(token_to_request_index, request_indices_tensor) - )[0] + for idx_tensor, (_, temp, top_k, top_p) in zip( + self._torch_sampling_bucket_index_tensors, self._torch_sampling_buckets + ): + required_indices = torch.where(torch.isin(token_to_request_index, idx_tensor))[0] output_tokens_jumbled_list.append( self._torch_sampling_func(required_logits[required_indices, :], temp, top_k, top_p) ) @@ -1012,86 +1031,19 @@ def _verify_speculative_tokens( self, output_tokens: Tensor, input_tokens_required: Tensor, - request_in_prefill_status_tensor: Tensor, - repeats: Tensor, num_decode_requests: int, num_prefill_requests: int, active_request_count: int, ) -> tuple: - """Verify speculative tokens against input tokens and compute acceptance. - - Creates an accepted tokens mask where: - - For prefill requests, the token is always accepted. - - For decode requests, the first token (base token) is always accepted, then we compare - sampled tokens with input tokens and accept consecutive matches. - Then finds the index of the last accepted token per request. - - Example (assume 1, 2, and 0 spec tokens are accepted in the first 3 decode requests): - input_tokens_required: [ a5 a6s a7s | b3 b4s b5s | c6 c7s c8s | d2 | e4 ] # Size 11 - Output tokens [ a6o a7o a8o | b40 b5o b6o | c7o c8o c9o | d3o | e5o ] - Output tokens right shift [ d3o a6o a7o | a8o b40 b5o | b6o c7o c8o | c9o | d3o ] - Accepted tokens mask [ 1 1 0 | 1 1 1 | 1 0 0 | 1 | 1 ] - Last one indices [ 1 | 5 | 6 | 9 | 10 ] - - Returns: - tuple: (last_one_indices, accepted_tokens_mask, input_tokens_required) where - last_one_indices contains the index of the last accepted token per request. - """ - if input_tokens_required.ndim == 2: - assert ( - input_tokens_required.shape[0] == 1 - ), f"Expected input_tokens_required to have 1 row, but got {input_tokens_required.shape}" - input_tokens_required = input_tokens_required.squeeze(0) - - # Initialize mask with False to prevent boundary bleed - accepted_tokens_mask = torch.zeros_like(input_tokens_required, dtype=torch.bool) - - # Make all prefill tokens accepted - token_to_prefill_idx = torch.repeat_interleave(request_in_prefill_status_tensor, repeats) - accepted_tokens_mask[token_to_prefill_idx == 1] = True - - # Safe decode token verification without cross-batch boundary contamination - decode_mask_2d = None - if num_decode_requests > 0: - decode_len = num_decode_requests * (self.num_speculative_tokens + 1) - - decode_inputs = input_tokens_required[:decode_len].reshape( - num_decode_requests, self.num_speculative_tokens + 1 - ) - decode_outputs = output_tokens[:decode_len].reshape( - num_decode_requests, self.num_speculative_tokens + 1 - ) - - # Shift outputs right by 1 *within* each request to align sampled tokens with input targets - decode_outputs_shifted = decode_outputs.roll(1, dims=1) - decode_mask_2d = decode_inputs == decode_outputs_shifted - # The first token (base token) is always accepted - decode_mask_2d[:, 0] = True - # Enforce consecutive acceptance: cummin propagates False to the right - decode_mask_2d = decode_mask_2d.cummin(dim=1).values - accepted_tokens_mask[:decode_len] = decode_mask_2d.flatten() - - last_one_indices = torch.full( - (active_request_count,), -1, device=input_tokens_required.device + """Verify speculative tokens against input tokens (Triton kernel).""" + return verify_speculative_tokens( + input_tokens=input_tokens_required, + output_tokens=output_tokens, + num_decode_requests=num_decode_requests, + num_prefill_requests=num_prefill_requests, + num_speculative_tokens=self.num_speculative_tokens, ) - if num_decode_requests > 0: - # Summing the consecutive mask gives the count; subtract 1 for the local index - local_last_indices = decode_mask_2d.sum(dim=1) - 1 - row_offsets = torch.arange(num_decode_requests, device=last_one_indices.device) * ( - self.num_speculative_tokens + 1 - ) - last_one_indices[:num_decode_requests] = row_offsets + local_last_indices - - if num_prefill_requests > 0: - decode_len = num_decode_requests * (self.num_speculative_tokens + 1) - prefill_valid = ( - torch.nonzero(accepted_tokens_mask[decode_len:]).squeeze(-1) + decode_len - ) - last_one_indices[num_decode_requests:] = prefill_valid - - return last_one_indices, accepted_tokens_mask, input_tokens_required - def _dynamic_step_sample_logits_and_verify_tokens(self, logits: Tensor, input_ids: Tensor): """ Sample tokens from logits for dynamic batching with speculative tokens and verify the tokens. @@ -1102,16 +1054,11 @@ def _dynamic_step_sample_logits_and_verify_tokens(self, logits: Tensor, input_id request_in_prefill_status_tensor = context.request_in_prefill_status_tensor[ context.paused_request_count : context.total_request_count ] - request_query_lengths = context.request_query_lengths[ - context.paused_request_count : context.total_request_count - ] - - num_prefill_requests = request_in_prefill_status_tensor.sum().item() - num_decode_requests = active_request_count - num_prefill_requests # Get the logit indices for tokens that need sampling. # These indices are always needed for input_ids slicing and tracking # accepted sequence positions, even when logits are pre-sliced. + nvtx_range_push("mtp-spec-decoding/verify/logit-indices") required_logit_indices = context.speculative_required_logit_indices(logits.device) if context.config.materialize_only_last_token_logits: @@ -1121,57 +1068,76 @@ def _dynamic_step_sample_logits_and_verify_tokens(self, logits: Tensor, input_id required_logits = logits.squeeze(0)[ required_logit_indices, : ] # Shape [num_required, vocab_size] + nvtx_range_pop("mtp-spec-decoding/verify/logit-indices") # Sample tokens from logits + nvtx_range_push("mtp-spec-decoding/verify/sample") output_tokens, repeats = self._sample_speculative_logits( required_logits, request_in_prefill_status_tensor ) + nvtx_range_pop("mtp-spec-decoding/verify/sample") + + num_prefill_requests = context.num_prefill_requests + num_decode_requests = active_request_count - num_prefill_requests # Verify speculative tokens against input tokens. + nvtx_range_push("mtp-spec-decoding/verify/verify-tokens") input_tokens_required = input_ids[0, required_logit_indices] last_one_indices, accepted_tokens_mask, input_tokens_required = ( self._verify_speculative_tokens( output_tokens, input_tokens_required, - request_in_prefill_status_tensor, - repeats, num_decode_requests, num_prefill_requests, active_request_count, ) ) + nvtx_range_pop("mtp-spec-decoding/verify/verify-tokens") + + nvtx_range_push("mtp-spec-decoding/verify/prepare-next") + self._prepare_speculative_tokens_for_next_forward_pass( + num_decode_requests, + output_tokens, + required_logit_indices, + last_one_indices, + accepted_tokens_mask, + input_tokens_required, + ) + nvtx_range_pop("mtp-spec-decoding/verify/prepare-next") - # Store the final sampled tokens for the next forward pass. - final_sampled_tokens = output_tokens[last_one_indices] - self._sampled_tokens_cuda[: len(final_sampled_tokens)] = final_sampled_tokens - - # Store the last accepted positions in the packed sequence for serial - # MTP computation after verification. - self._last_accepted_seq_indices = required_logit_indices[last_one_indices] - - # Extract accepted tokens and counts for decode requests. - # For prefill it is always set to 1. For decode, the first token is always accepted, - # then we compare with input tokens and accept the next tokens if its a match. - # - # Example (continuing from above): - # input_tokens_required: [ a5 a6s a7s | b3 b4s b5s | c6 c7s c8s | d2 | e4 ] - # Accepted tokens mask [ 1 1 0 | 1 1 1 | 1 0 0 | 1 | 1 ] - # Accepted tokens [ [a6s -1] | [b4s b5s] | [-1 -1] ] # Only decode requests (prefill defaults to -1) - # Accepted token counts [ 1 | 2 | 0 ] # Prefill defaults to 0 - input_tokens_required[accepted_tokens_mask == 0] = -1 # Mask out non-accepted tokens - input_tokens_decode_mode = input_tokens_required[ - : num_decode_requests * (self.num_speculative_tokens + 1) - ] - input_tokens_reshaped = input_tokens_decode_mode.reshape( - -1, self.num_speculative_tokens + 1 - ) # shape: [num_decode_requests, num_speculative_tokens + 1] - - # Skip the first token of every decode request (i.e a5, b3, c6) - accepted_tokens = input_tokens_reshaped[:, 1:] - self._accepted_tokens_per_request[: accepted_tokens.shape[0], :] = accepted_tokens - self._accepted_token_counts_per_request = (self._accepted_tokens_per_request != -1).sum( - dim=1 + def _prepare_speculative_tokens_for_next_forward_pass( + self, + num_decode_requests: int, + output_tokens: torch.Tensor, + required_logit_indices: torch.Tensor, + last_one_indices: torch.Tensor, + accepted_tokens_mask: torch.Tensor, + input_tokens_required: torch.Tensor, + ): + """Prepare accepted speculative tokens for the next forward pass (Triton kernel). + + Example: + input_tokens_required: [ a5 a6s a7s | b3 b4s b5s | c6 c7s c8s | d2 | e4 ] + Accepted tokens mask [ 1 1 0 | 1 1 1 | 1 0 0 | 1 | 1 ] + Accepted tokens [ [a6s -1] | [b4s b5s] | [-1 -1] ] (decode only; prefill → -1) + Accepted token counts [ 1 | 2 | 0 ] (prefill defaults to 0) + """ + active_request_count = last_one_indices.shape[0] + prepare_next_forward_pass( + num_decode_requests=num_decode_requests, + output_tokens=output_tokens, + required_logit_indices=required_logit_indices, + last_one_indices=last_one_indices, + accepted_tokens_mask=accepted_tokens_mask, + input_tokens=input_tokens_required, + sampled_tokens_buf=self._sampled_tokens_cuda, + last_accepted_seq_buf=self._last_accepted_seq_indices_buf, + accepted_tokens_per_request=self._accepted_tokens_per_request, + accepted_token_counts=self._accepted_token_counts_per_request, + num_speculative_tokens=self.num_speculative_tokens, ) + # Expose the active slice so downstream code sees the right length. + self._last_accepted_seq_indices = self._last_accepted_seq_indices_buf[:active_request_count] def _dynamic_step_sample_logits(self, logits: Tensor): """Sample tokens from logits for dynamic batching. @@ -1628,7 +1594,8 @@ def dummy_forward(self): if not context.cuda_graph_batch_dimensions_list: self.inference_wrapped_model.dummy_forward() - # Disable MoE padding for MTP computation + # Disable MoE padding for MTP computation. + # No CUDA graphs in this path (cuda_graph_batch_dimensions_list is empty). if self.model_config.moe_pad_experts_for_cuda_graph_inference: unwrapped_model = unwrap_model(self.inference_wrapped_model.model) set_decode_expert_padding(unwrapped_model, False) @@ -1651,10 +1618,12 @@ def dummy_forward(self): # fallback to eager dummy forward self.inference_wrapped_model.dummy_forward() - # Disable MoE padding for MTP computation + # Disable MoE padding for MTP computation, unless CUDA graphs + # are active (the graphs were captured with padding enabled). if self.model_config.moe_pad_experts_for_cuda_graph_inference: - unwrapped_model = unwrap_model(self.inference_wrapped_model.model) - set_decode_expert_padding(unwrapped_model, False) + if not context.using_cuda_graph_this_step(): + unwrapped_model = unwrap_model(self.inference_wrapped_model.model) + set_decode_expert_padding(unwrapped_model, False) # When speculative decoding is active, the real EP ranks perform serial # MTP forward passes after the main forward pass. MTP layers may contain @@ -1686,10 +1655,11 @@ def _dummy_serial_mtp_forward(self): if self.model_config.expert_model_parallel_size <= 1: return - unwrapped_model = unwrap_model(self.inference_wrapped_model.model) + unwrapped_model = self._unwrapped_model - is_last_stage = is_pipeline_last_stage(self.pp_group) - has_mtp = is_last_stage and hasattr(unwrapped_model, '_decoder_hidden_states_cache') + has_mtp = self._is_last_pp_stage and hasattr( + unwrapped_model, '_decoder_hidden_states_cache' + ) if not has_mtp and not self.model_is_pipeline_parallel: # No MTP on this rank and no PP broadcast to participate in. return @@ -1697,31 +1667,38 @@ def _dummy_serial_mtp_forward(self): device = torch.cuda.current_device() dtype = self.model_config.params_dtype hidden_size = self.model_config.hidden_size - num_depths = min(self.num_speculative_tokens, self.num_mtp_heads) - # Pad token_ids/position_ids to nearest multiple of tp_size so that the - # embedding can reduce-scatter evenly across TP ranks. - tp_size = get_pg_size(self.inference_wrapped_model.tp_group) - sp_enabled = self.model_config.sequence_parallel and tp_size > 1 - padded_count = tp_size if sp_enabled else 1 + # Use precomputed MTP CUDA graph batch size when available; + # otherwise use minimal SP-compatible size. + if getattr(self, '_mtp_resolved_padded_count', None) is not None: + padded_count = self._mtp_resolved_padded_count + assert not self._sp_enabled or padded_count % self._tp_size == 0 + elif has_mtp: + # Eager path: use TP-aligned minimum size for dummy tensors. + padded_count = self._tp_size if self._sp_enabled else 1 dummy_hidden = None if has_mtp: - # Minimal dummy tensors — just enough to drive the MTP layer forward + # Minimal dummy tensors to drive the MTP layer forward # so that the MoE all-to-all collectives are issued. - # Depth 0 uses full-format hidden; subsequent depths use SP format. - dummy_hidden = torch.zeros((1, 1, hidden_size), device=device, dtype=dtype) + dummy_hidden = torch.zeros((padded_count, 1, hidden_size), device=device, dtype=dtype) + if self._sp_enabled: + dummy_hidden = scatter_to_sequence_parallel_region( + dummy_hidden, group=self.inference_wrapped_model.tp_group + ) dummy_token_ids = torch.zeros((1, padded_count), device=device, dtype=torch.long) dummy_position_ids = torch.zeros((1, padded_count), device=device, dtype=torch.long) - for depth in range(num_depths): + for depth in range(self._num_mtp_depths): + nvtx_range_push(f"mtp-spec-decoding/dummy-depth-{depth}") mtp_logits_2d = None if has_mtp: + mtp_depth = None if unwrapped_model.mtp.mtp_use_repeated_layer else depth dummy_hidden, mtp_logits = unwrapped_model.compute_mtp_single_step( hidden_states=dummy_hidden, next_token_ids=dummy_token_ids, position_ids=dummy_position_ids, - depth=depth, + depth=mtp_depth, ) mtp_logits_2d = mtp_logits.squeeze(1) # [padded_count, vocab_size] @@ -1733,6 +1710,7 @@ def _dummy_serial_mtp_forward(self): tensor=mtp_logits_2d, pp_group=self.pp_group, ) + nvtx_range_pop(f"mtp-spec-decoding/dummy-depth-{depth}") def _dynamic_step_context_bookkeeping(self) -> Dict[str, Tensor]: """Update the dynamic inference context after sampling. @@ -1887,17 +1865,28 @@ async def async_generate_output_tokens_dynamic_batch( if self.num_speculative_tokens > 0: # Phase 1: Verify speculative tokens using base logits only. + nvtx_range_push("mtp-spec-decoding/verify") self._dynamic_step_sample_logits_and_verify_tokens(logits, input_ids) + nvtx_range_pop("mtp-spec-decoding/verify") # Phase 2: Rewind KV cache for rejected tokens. - self._rewind_kv_cache() + nvtx_range_push("mtp-spec-decoding/rewind-kv-cache") + blocks_to_release, remove_mask = self._rewind_kv_cache() + nvtx_range_pop("mtp-spec-decoding/rewind-kv-cache") - # Disable MoE padding for MTP computation + # Disable MoE padding for MTP computation, unless CUDA graphs + # are active (the graphs were captured with padding enabled). if self.model_config.moe_pad_experts_for_cuda_graph_inference: - unwrapped_model = unwrap_model(self.inference_wrapped_model.model) - set_decode_expert_padding(unwrapped_model, False) + if not context.using_cuda_graph_this_step(): + set_decode_expert_padding(self._unwrapped_model, False) # Phase 3: Compute MTP serially with correct (verified) inputs. + nvtx_range_push("mtp-spec-decoding/serial-mtp") self._compute_serial_mtp_and_sample() + nvtx_range_pop("mtp-spec-decoding/serial-mtp") + + # Phase 4: Release freed blocks. Deferred from Phase 2 so the + # data-dependent boolean-mask sync overlaps with MTP GPU work. + context.kv_block_allocator.release_memory_blocks(blocks_to_release[remove_mask]) else: self._dynamic_step_sample_logits(logits) diff --git a/megatron/core/models/common/language_module/language_module.py b/megatron/core/models/common/language_module/language_module.py index e8bb564e759..85870726269 100644 --- a/megatron/core/models/common/language_module/language_module.py +++ b/megatron/core/models/common/language_module/language_module.py @@ -8,6 +8,7 @@ from megatron.core import parallel_state, tensor_parallel from megatron.core.dist_checkpointing.mapping import ShardedStateDict +from megatron.core.transformer.cuda_graphs import CudaGraphManager try: from megatron.core.extensions.transformer_engine import te_parallel_cross_entropy @@ -63,6 +64,20 @@ def __init__( self.vp_stage = None self.vp_size = self.config.virtual_pipeline_model_parallel_size + def _setup_mtp_cuda_graphs(self): + """Wrap `compute_mtp_single_step` with a CudaGraphManager. + + Must be called by subclasses after `self.mtp` is created. + """ + if self.config.cuda_graph_impl == "local": + self._mtp_cudagraph_manager = CudaGraphManager( + self.config, + base_module=self, + function_name="compute_mtp_single_step", + need_backward=False, + is_mtp_inference=True, + ) + def _is_in_embd_group(self): if self.embd_group is None: return False @@ -325,7 +340,11 @@ def shared_embedding_or_output_weight(self) -> Tensor: @torch.inference_mode() def compute_mtp_single_step( - self, hidden_states: Tensor, next_token_ids: Tensor, position_ids: Tensor, depth: int + self, + hidden_states: Tensor, + next_token_ids: Tensor, + position_ids: Tensor, + depth: Optional[int] = None, ) -> tuple: """Compute a single MTP depth for speculative decoding. @@ -336,13 +355,15 @@ def compute_mtp_single_step( hidden_states (Tensor): Hidden states at last accepted positions. next_token_ids (Tensor): Correct next token IDs [1, N]. position_ids (Tensor): Position IDs for the next tokens [1, N]. - depth (int): MTP depth index (0-indexed). + depth (int, optional): MTP depth index. Only needed when + ``mtp_use_repeated_layer`` is False (each depth uses a + distinct layer). Omit for repeated-layer models so that a + single CUDA graph can serve all depths. Returns: tuple: (new_hidden_states, logits [N, 1, vocab_size]). """ - layer_idx = 0 if self.mtp.mtp_use_repeated_layer else depth - + layer_idx = 0 if depth is None else depth mtp_hidden = self.mtp.layers[layer_idx].forward_single_position( hidden_states=hidden_states, next_token_ids=next_token_ids, diff --git a/megatron/core/models/gpt/gpt_model.py b/megatron/core/models/gpt/gpt_model.py index 4fe641bb17b..dedada837b7 100644 --- a/megatron/core/models/gpt/gpt_model.py +++ b/megatron/core/models/gpt/gpt_model.py @@ -223,6 +223,8 @@ def __init__( pg_collection=self.pg_collection, ) + self._setup_mtp_cuda_graphs() + # Output if self.post_process: diff --git a/megatron/core/models/hybrid/hybrid_model.py b/megatron/core/models/hybrid/hybrid_model.py index b575792e81b..4399c6984a7 100644 --- a/megatron/core/models/hybrid/hybrid_model.py +++ b/megatron/core/models/hybrid/hybrid_model.py @@ -279,6 +279,7 @@ def __init__( mtp_num_depths=self.mtp_num_depths, hybrid_submodules=hybrid_submodules, ) + self._setup_mtp_cuda_graphs() # Output if post_process or self.mtp_process: diff --git a/megatron/core/tensor_parallel/inference_layers.py b/megatron/core/tensor_parallel/inference_layers.py index 80aa754dd50..14ac28fbefa 100644 --- a/megatron/core/tensor_parallel/inference_layers.py +++ b/megatron/core/tensor_parallel/inference_layers.py @@ -20,6 +20,10 @@ from megatron.core.inference.quantization.utils import mm_mxfp8 from megatron.core.inference.symmetric_memory import SymmetricMemoryManager from megatron.core.model_parallel_config import ModelParallelConfig +from megatron.core.tensor_parallel.mappings import ( + gather_from_tensor_model_parallel_region, + reduce_scatter_to_sequence_parallel_region, +) from megatron.core.transformer.transformer_config import TransformerConfig from megatron.core.utils import get_tensor_model_parallel_group_if_none @@ -473,3 +477,75 @@ def forward( else: x = self._matmul_reduce_scatter(x) return x, None + + +def inference_all_gather_from_tensor_model_parallel_region( + x: torch.Tensor, tp_group: torch.distributed.ProcessGroup, config: TransformerConfig +) -> torch.Tensor: + """NVLS-optimized all-gather along the last dimension, with NCCL fallback. + + Replaces `gather_from_tensor_model_parallel_region` in inference paths + where autograd is not needed and NVLS symmetric-memory is available. + + The NVLS path performs a flat all-gather into symmetric memory (concatenating + along dim-0), then rearranges the result to the last dimension — the same + semantics as `_gather_along_last_dim` but using hardware multicast when + possible. + """ + tp_size = dist.get_world_size(tp_group) + if tp_size == 1: + return x + + triton_nvls_kernels_allowed = not getattr( + config, 'inference_disable_triton_nvls_kernels', False + ) + + if triton_nvls_kernels_allowed and SymmetricMemoryManager.is_initialized("tp"): + ag_buffer_dims = list(x.size()) + ag_buffer_dims[0] *= tp_size + buf = SymmetricMemoryManager.get_buffer("tp", process_group=tp_group) + symm_mem_buffer = buf.maybe_get_tensor(ag_buffer_dims, dtype=x.dtype) + + if are_tensors_nvls_eligible(x) and symm_mem_buffer["handle"] is not None: + multimem_all_gather(symm_mem_buffer["tensor"], x, symm_mem_buffer["handle"]) + tensor_list = symm_mem_buffer["tensor"].chunk(tp_size, dim=0) + return torch.cat(tensor_list, dim=-1).contiguous() + + return gather_from_tensor_model_parallel_region(x, group=tp_group) + + +def inference_reduce_scatter_to_sequence_parallel_region( + x: torch.Tensor, tp_group: torch.distributed.ProcessGroup, config: TransformerConfig +) -> torch.Tensor: + """NVLS-optimized reduce-scatter along the first dimension, with NCCL fallback. + + Replaces `reduce_scatter_to_sequence_parallel_region` in inference paths + where autograd is not needed and NVLS symmetric-memory is available. + """ + # TODO(ksanthanam): Refactor InferenceRowParallelLinear._matmul_reduce_scatter + # to use this function for its non-fused NVLS reduce-scatter path. + tp_size = dist.get_world_size(tp_group) + if tp_size == 1: + return x + + triton_nvls_kernels_allowed = not getattr( + config, 'inference_disable_triton_nvls_kernels', False + ) + + if triton_nvls_kernels_allowed and SymmetricMemoryManager.is_initialized("tp"): + buf = SymmetricMemoryManager.get_buffer("tp", process_group=tp_group) + symm_mem_buffer = buf.maybe_get_tensor(list(x.size()), dtype=x.dtype) + + if ( + x.dtype == torch.bfloat16 + and are_tensors_nvls_eligible(x) + and symm_mem_buffer["handle"] is not None + ): + symm_mem_buffer["tensor"].copy_(x) + output_dims = list(x.size()) + output_dims[0] = x.size(0) // tp_size + output = torch.empty(output_dims, dtype=x.dtype, device=x.device) + multimem_reduce_scatter(output, symm_mem_buffer["tensor"], symm_mem_buffer["handle"]) + return output + + return reduce_scatter_to_sequence_parallel_region(x, group=tp_group) diff --git a/megatron/core/tensor_parallel/layers.py b/megatron/core/tensor_parallel/layers.py index 610700f0a95..4ab2aa0f639 100644 --- a/megatron/core/tensor_parallel/layers.py +++ b/megatron/core/tensor_parallel/layers.py @@ -235,6 +235,11 @@ def __init__( ) self.num_embeddings_per_partition = self.vocab_end_index - self.vocab_start_index self.deterministic_mode = config.deterministic_mode + self.config = config + + self.use_inference_optimized_reduce_scatter = ( + getattr(config, 'transformer_impl', None) == 'inference_optimized' + ) # Allocate weights and initialize. if config.use_cpu_initialization: @@ -302,9 +307,17 @@ def forward(self, input_): if self.reduce_scatter_embeddings: # Data format change to avoid explicit tranposes : [b s h] --> [s b h]. output_parallel = output_parallel.transpose(0, 1).contiguous() - output = reduce_scatter_to_sequence_parallel_region( - output_parallel, group=self.tp_group - ) + if self.use_inference_optimized_reduce_scatter and not self.training: + # Deferred to avoid circular import: inference_layers → TE → layers. + from .inference_layers import inference_reduce_scatter_to_sequence_parallel_region + + output = inference_reduce_scatter_to_sequence_parallel_region( + output_parallel, self.tp_group, self.config + ) + else: + output = reduce_scatter_to_sequence_parallel_region( + output_parallel, group=self.tp_group + ) else: # Reduce across all the model parallel GPUs. output = reduce_from_tensor_model_parallel_region(output_parallel, group=self.tp_group) @@ -921,6 +934,10 @@ def __init__( else: self.register_parameter("bias", None) + self.use_inference_optimized_all_gather = ( + getattr(config, 'transformer_impl', None) == 'inference_optimized' + ) + self.sequence_parallel = config.sequence_parallel if self.sequence_parallel and world_size <= 1: warnings.warn( @@ -1056,7 +1073,17 @@ def forward( if gather_output: # All-gather across the partitions. - output = gather_from_tensor_model_parallel_region(output_parallel, group=self.tp_group) + if self.use_inference_optimized_all_gather and not self.training: + # Deferred to avoid circular import: inference_layers → TE → layers. + from .inference_layers import inference_all_gather_from_tensor_model_parallel_region + + output = inference_all_gather_from_tensor_model_parallel_region( + output_parallel, self.tp_group, self.config + ) + else: + output = gather_from_tensor_model_parallel_region( + output_parallel, group=self.tp_group + ) else: output = output_parallel output_bias = self.bias if self.skip_bias_add else None diff --git a/megatron/core/transformer/cuda_graphs.py b/megatron/core/transformer/cuda_graphs.py index 866f6676d92..c2ebe655955 100644 --- a/megatron/core/transformer/cuda_graphs.py +++ b/megatron/core/transformer/cuda_graphs.py @@ -8,7 +8,7 @@ import os import time from collections import defaultdict -from contextlib import nullcontext +from contextlib import contextmanager, nullcontext from copy import deepcopy from dataclasses import dataclass, is_dataclass from enum import Enum @@ -98,6 +98,16 @@ def _set_capture_end(): _IS_GRAPH_CAPTURING = False +@contextmanager +def graph_capture(): + """Context manager that brackets a graph-capture region.""" + _set_capture_start() + try: + yield + finally: + _set_capture_end() + + def is_graph_warmup(): """Query if currently warming up for graph capture.""" return _IS_GRAPH_WARMUP @@ -331,6 +341,10 @@ class _CudagraphGlobalRecord: cudagraph_record: list[tuple] = [] cudagraph_inference_record: list[tuple] = [] + # MTP CudaGraphManagers registered at construction time so that + # delete_cuda_graphs() can clear their lookup tables. + mtp_cudagraph_managers: list = [] + """A pool-like data structure to reuse input and output buffers across cudagraph.""" tensor_reuse_pool = TensorReusePool() @@ -506,6 +520,19 @@ def delete_cuda_graphs(): runner.bwd_graph = None runner.mempool = None + # Reset MTP runners (excluded from the global inference record). + for mgr in _CudagraphGlobalRecord.mtp_cudagraph_managers: + for runner in mgr.cudagraph_runners: + runner.cudagraph_created = False + runner.fwd_graph_recorded = False + runner.bwd_graph_recorded = False + runner.fwd_graph = None + runner.bwd_graph = None + runner.mempool = None + mgr.cudagraph_runners.clear() + mgr.custom_cudagraphs_lookup_table.clear() + _CudagraphGlobalRecord.mtp_cudagraph_managers.clear() + # Reset global tracking state _CudagraphGlobalRecord.cudagraph_created = False _CudagraphGlobalRecord.cudagraph_record = [] @@ -1417,6 +1444,7 @@ def __init__( function_name=None, need_backward=True, pg_collection=None, + is_mtp_inference=False, inline_capture=False, num_warmup_steps=None, ): @@ -1426,6 +1454,7 @@ def __init__( Args: config: TransformerConfig object containing CUDA graph settings for memory pooling, graph retention, gradient accumulation, FP8/FP4, and warmup steps. + is_mtp_inference: Whether this manager wraps an MTP inference forward pass. inline_capture: Normally, whether the inline capture path is taken depends on whether `inference_context` is present in the kwargs of the forward call. Setting this argument to True always forces the inline capture path to be taken. @@ -1438,6 +1467,7 @@ def __init__( self.pg_collection = pg_collection rng_tracker = get_cuda_rng_tracker() self.need_backward = need_backward + self.is_mtp_inference = is_mtp_inference if function_name is not None: func = getattr(base_module, function_name) @@ -1483,6 +1513,10 @@ def wrapped_func(*args, eager=False, cache_key=None, **kwargs): self.custom_cudagraphs_lookup_table: dict = defaultdict(lambda: None) self.is_first_microbatch = False + if is_mtp_inference: + # Registered so delete_cuda_graphs() can clear the lookup table. + _CudagraphGlobalRecord.mtp_cudagraph_managers.append(self) + # Without pipeline parallelism, microbatches execute one at a time. # Therefore modules will always execute in the same order, so cudagraphs # can both be reused and share a single mempool. @@ -1588,14 +1622,19 @@ def __call__(self, megatron_module, args, kwargs, cache_key=None): cache_key: Optional hashable key for O(1) runner lookup. If `inference_context` is provided, this gets set to the correct value. """ - is_inference_mode = 'inference_context' in kwargs.keys() and kwargs['inference_context'] + is_inference_mode = ( + 'inference_context' in kwargs.keys() and kwargs['inference_context'] + ) or self.is_mtp_inference if cache_key is None and is_inference_mode: - inference_context = kwargs['inference_context'] - if inference_context.is_static_batching(): - batch_size = kwargs['hidden_states'].shape[0] - cache_key = (batch_size, inference_context.is_decode_only()) - else: - cache_key = inference_context.padded_batch_dimensions + if 'inference_context' in kwargs and kwargs['inference_context']: + inference_context = kwargs['inference_context'] + if inference_context.is_static_batching(): + batch_size = kwargs['hidden_states'].shape[0] + cache_key = (batch_size, inference_context.is_decode_only()) + else: + cache_key = inference_context.padded_batch_dimensions + elif self.is_mtp_inference: + cache_key = ('mtp', kwargs['hidden_states'].shape, kwargs.get('depth')) is_in_checkpoint_fwd = is_checkpointing() if HAVE_TE_GRAPHS: is_in_checkpoint_fwd = is_in_checkpoint_fwd or is_fp8_activation_recompute_enabled() @@ -1613,11 +1652,28 @@ def __call__(self, megatron_module, args, kwargs, cache_key=None): out = runner.replay_graph_capture(self.is_first_microbatch, args, kwargs) else: if is_inference_mode or self._inline_capture: + # MTP must match the main model's eager/graph mode so all EP + # ranks take the same code path. Skip during graph capture. + if ( + self.is_mtp_inference + and not getattr(megatron_module, 'use_mtp_cuda_graphs', False) + and not is_graph_capturing() + ): + return self.func(*args, **kwargs) + # Inference generation mode creates graphs immediately runner = self.get_cudagraph_runner( megatron_module, args, kwargs, True, cache_key=cache_key ) + if ( + not runner.fwd_graph_recorded + and self.is_mtp_inference + and not is_graph_capturing() + ): + # No pre-warmed graph for this batch size — run eagerly. + return self.func(*args, **kwargs) + if not runner.fwd_graph_recorded: # Reuse graph input-output buffers for inference local_args, local_kwargs = args, kwargs @@ -1649,10 +1705,14 @@ def __call__(self, megatron_module, args, kwargs, cache_key=None): runner.cudagraph_created = True runner = runner.eval() - # Record this to the global execution record - _CudagraphGlobalRecord.cudagraph_inference_record.append( - (runner, "fwd", args, kwargs) - ) + # Record to the global execution record. MTP runners are + # excluded — they don't chain with decoder layers (the + # previous-layer lookup expects layer_number) and are + # cleaned up via mtp_cudagraph_managers instead. + if not self.is_mtp_inference: + _CudagraphGlobalRecord.cudagraph_inference_record.append( + (runner, "fwd", args, kwargs) + ) # Now replay the graph out = runner.replay_graph_capture(self.is_first_microbatch, args, kwargs) diff --git a/megatron/core/transformer/multi_token_prediction.py b/megatron/core/transformer/multi_token_prediction.py index b70fdc3f28b..2e0461e365c 100755 --- a/megatron/core/transformer/multi_token_prediction.py +++ b/megatron/core/transformer/multi_token_prediction.py @@ -22,6 +22,9 @@ gather_from_tensor_model_parallel_region, scatter_to_sequence_parallel_region, ) +from megatron.core.tensor_parallel.inference_layers import ( + inference_all_gather_from_tensor_model_parallel_region, +) from megatron.core.transformer.enums import AttnMaskType, LayerType from megatron.core.transformer.module import MegatronModule from megatron.core.transformer.spec_utils import ModuleSpec, build_module @@ -918,11 +921,15 @@ def _concat_embeddings(self, hidden_states: torch.Tensor, decoder_input: torch.T hidden_states = torch.cat((decoder_input, hidden_states), -1) hidden_states, _ = self.eh_proj(hidden_states) # For tensor parallel we need to gather the tensor across the model-parallel - # ranks after the linear projection. This used to call - # `all_gather_last_dim_from_tensor_parallel_region`, but that utility reduces - # the gradient in backward pass and was therefore incorrect in this context. - # It has been replaced with the correct `gather_from_tensor_model_parallel_region`. - hidden_states = gather_from_tensor_model_parallel_region(hidden_states, group=self.tp_group) + # ranks after the linear projection. + if not self.training: + hidden_states = inference_all_gather_from_tensor_model_parallel_region( + hidden_states, self.tp_group, self.config + ) + else: + hidden_states = gather_from_tensor_model_parallel_region( + hidden_states, group=self.tp_group + ) # For sequence parallel, scatter after linear_fc and before transformer layer. if self.sequence_parallel: hidden_states = scatter_to_sequence_parallel_region(hidden_states, group=self.tp_group) @@ -1021,7 +1028,6 @@ def forward_single_position( rotary_pos_emb: Optional[Tensor] = None, rotary_pos_cos: Optional[Tensor] = None, rotary_pos_sin: Optional[Tensor] = None, - inference_params=None, packed_seq_params: Optional[PackedSeqParams] = None, sequence_len_offset: Optional[Tensor] = None, ) -> Tensor: @@ -1052,7 +1058,6 @@ def forward_single_position( rotary_pos_emb=rotary_pos_emb, rotary_pos_cos=rotary_pos_cos, rotary_pos_sin=rotary_pos_sin, - inference_params=inference_params, packed_seq_params=packed_seq_params, sequence_len_offset=sequence_len_offset, ) diff --git a/megatron/core/utils.py b/megatron/core/utils.py index 39ebf9a5044..f0d00cd40bb 100644 --- a/megatron/core/utils.py +++ b/megatron/core/utils.py @@ -506,6 +506,11 @@ def divide(numerator, denominator): return numerator // denominator +def round_up_to_nearest_multiple(value: int, multiple: int) -> int: + """Round *value* up to the nearest multiple of *multiple*.""" + return math.ceil(value / multiple) * multiple + + def get_tensor_model_parallel_group_if_none(tp_group, is_expert=False, check_initialized=True): """Issue a deprecation warning if tp_group is None and return the default tp group.""" # TODO(zijiey): remove this function later. diff --git a/tests/unit_tests/inference/engines/test_dynamic_engine.py b/tests/unit_tests/inference/engines/test_dynamic_engine.py index e39a58736a5..4f02369d0ed 100644 --- a/tests/unit_tests/inference/engines/test_dynamic_engine.py +++ b/tests/unit_tests/inference/engines/test_dynamic_engine.py @@ -50,11 +50,11 @@ from megatron.core.models.hybrid.hybrid_model import HybridModel from megatron.core.ssm.mamba_mixer import _check_mamba_sequence_packing_support from megatron.core.tensor_parallel.random import model_parallel_cuda_manual_seed -from megatron.core.transformer.cuda_graphs import CudaGraphManager, _CudagraphGlobalRecord +from megatron.core.transformer.cuda_graphs import delete_cuda_graphs from megatron.core.transformer.enums import CudaGraphScope from megatron.core.transformer.transformer_config import TransformerConfig from megatron.core.utils import is_fa_min_version, is_te_min_version -from tests.unit_tests.test_utilities import Utils +from tests.unit_tests.test_utilities import Utils, clear_nvte_env_vars try: from torch_memory_saver import torch_memory_saver # noqa: F401 @@ -178,7 +178,7 @@ class DynamicEngineTestEnv: ) -class TestDynamicInferenceEngine: +class DynamicInferenceEngineTestBase: @classmethod def _build_requests(cls, test_config: DynamicEngineTestConfig) -> List[DynamicInferenceRequest]: @@ -281,11 +281,7 @@ def _build_inference_context( @classmethod @torch.inference_mode() def _build_test_env(cls, test_config): - Utils.initialize_model_parallel( - tensor_model_parallel_size=test_config.tensor_model_parallel_size, - pipeline_model_parallel_size=test_config.pipeline_model_parallel_size, - ) - + clear_nvte_env_vars() set_rounder(4) # Random state. @@ -398,12 +394,18 @@ def _build_test_env(cls, test_config): ), sequence_parallel=test_config.sequence_parallel, pipeline_dtype=torch.bfloat16, - add_bias_linear=test_config.expert_model_parallel_size == 1, + add_bias_linear=test_config.expert_model_parallel_size == 1 + and not (test_config.transformer_impl == "inference_optimized"), fp8="hybrid" if test_config.fp8 else None, fp8_recipe="tensorwise" if test_config.fp8 else None, inference_sampling_seed=test_config.random_seed, cuda_graph_scope=test_config.cuda_graph_scope, transformer_impl=test_config.transformer_impl, + normalization=( + "RMSNorm" + if test_config.transformer_impl == "inference_optimized" + else "LayerNorm" + ), is_hybrid_model=True, # Needs to be set for correct out_proj init ) @@ -459,10 +461,7 @@ def _build_test_env(cls, test_config): ), ) - # Reset global cuda graph state. - _CudagraphGlobalRecord.cudagraph_created = False - _CudagraphGlobalRecord.cudagraph_record = [] - CudaGraphManager.global_mempool = None + delete_cuda_graphs() # Inference engine. engine = DynamicInferenceEngine(text_generation_controller, inference_context) @@ -565,8 +564,21 @@ def _run_test(cls, **test_config_kwargs): return env + +class TestDynamicInferenceEngine(DynamicInferenceEngineTestBase): + + @classmethod + def setup_class(cls): + Utils.initialize_model_parallel( + tensor_model_parallel_size=1, + pipeline_model_parallel_size=1, + expert_model_parallel_size=1, + expert_tensor_parallel_size=1, + ) + @classmethod def teardown_class(cls): + delete_cuda_graphs() set_rounder(64) Utils.destroy_model_parallel() @@ -1086,88 +1098,6 @@ def test_log_probs_token_correspondence(self): assert not math.isnan(log_prob) and not math.isinf(log_prob) assert -100.0 <= log_prob <= 0.0 - @pytest.mark.internal - @pytest.mark.skipif( - not is_fa_min_version("2.7.3"), reason="need latest flash attn for dynamic batching" - ) - @pytest.mark.parametrize("materialize_only_last_token_logits", [False, True]) - @pytest.mark.parametrize("sequence_parallel", [False, True]) - @pytest.mark.parametrize("ep_size", [1, 2]) - @pytest.mark.parametrize("pp_size", [1, 2]) - @pytest.mark.parametrize("tp_size", [1, 2]) - @pytest.mark.parametrize("model_provider", ["gpt", "hybrid"]) - @pytest.mark.parametrize("transformer_impl", ["local", "inference_optimized"]) - @torch.inference_mode() - def test_parallel_inference( - self, - model_provider, - tp_size, - pp_size, - ep_size, - sequence_parallel, - materialize_only_last_token_logits, - transformer_impl, - ): - skip_if_mamba_sequence_packing_not_available(model_provider) - - if tp_size == 1 and pp_size == 1 and ep_size == 1: - pytest.skip(reason="Test requires tp_size > 1 or pp_size > 1 or ep_size > 1") - elif not torch.distributed.is_initialized(): - pytest.skip("Distributed not initialized") - world_size = torch.distributed.get_world_size() - min_world_size = tp_size * pp_size * ep_size - if world_size < min_world_size: - pytest.skip(f"Test requires at least {min_world_size} GPUs") - elif tp_size == 1 and sequence_parallel: - pytest.skip(reason="Sequence parallelism requires tp_size > 1") - elif tp_size > 1 and ep_size > 1 and not sequence_parallel: - pytest.skip(reason="Sequence parallelism must be used with tp_size > 1 and ep_size > 1") - elif transformer_impl == "inference_optimized": - if ep_size > 1: - pytest.skip( - reason="MoE models are not supported with the inference optimized transformer." - ) - if tp_size > 1 and not sequence_parallel: - pytest.skip( - reason=( - "The inference optimized transformer requires sequence parallelism " - "when tp_size > 1." - ) - ) - if model_provider == "hybrid": - pytest.skip( - reason="Mamba model is not supported with the inference optimized transformer." - ) - - env = self._run_test( - model_provider=model_provider, - tensor_model_parallel_size=tp_size, - pipeline_model_parallel_size=pp_size, - expert_model_parallel_size=ep_size, - sequence_parallel=sequence_parallel, - materialize_only_last_token_logits=materialize_only_last_token_logits, - transformer_impl=transformer_impl, - ) - - @pytest.mark.internal - @pytest.mark.skipif( - not is_fa_min_version("2.7.3"), reason="need latest flash attn for dynamic batching" - ) - @pytest.mark.parametrize("materialize_only_last_token_logits", [False, True]) - def test_sequence_parallel_fp8_inference(self, materialize_only_last_token_logits: bool): - fp8_available, reason_for_no_fp8 = check_fp8_support() - if not fp8_available: - pytest.skip(reason_for_no_fp8) - - self._run_test( - min_prompt_length=19, - max_prompt_length=19, - tensor_model_parallel_size=4, - sequence_parallel=True, - materialize_only_last_token_logits=True, - fp8=True, - ) - @pytest.mark.internal @pytest.mark.skipif( not is_fa_min_version("2.7.3"), reason="need latest flash attn for dynamic batching" @@ -2364,7 +2294,7 @@ def mock_mtp_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth): + def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): n = hidden_states.size(0) logits = torch.zeros( n, 1, test_config.vocab_size, device=hidden_states.device, dtype=torch.bfloat16 @@ -2487,7 +2417,7 @@ def mock_deterministic_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth): + def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): n = hidden_states.size(0) # Predict next_token_ids + 1 (continuing the ascending sequence) pred_toks = (next_token_ids + 1).clamp(max=test_config.vocab_size - 1) @@ -2571,7 +2501,7 @@ def mock_deterministic_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth): + def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): n = hidden_states.size(0) # Predict next_token_ids + 1 (continuing the ascending sequence) pred_toks = (next_token_ids + 1).clamp(max=test_config.vocab_size - 1) @@ -2656,7 +2586,7 @@ def mock_deterministic_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth): + def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): n = hidden_states.size(0) # Predict next_token_ids + 1 (continuing the ascending sequence) pred_toks = (next_token_ids + 1).clamp(max=test_config.vocab_size - 1) @@ -3010,7 +2940,7 @@ def mock_safe_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth): + def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): n = hidden_states.size(0) logits = torch.zeros( n, 1, test_config.vocab_size, device=hidden_states.device, dtype=torch.bfloat16 @@ -3223,7 +3153,7 @@ def mock_deterministic_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth): + def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): n = hidden_states.size(0) logits = torch.randn( n, 1, test_config.vocab_size, device=hidden_states.device, dtype=torch.bfloat16 @@ -3343,7 +3273,7 @@ def mock_deterministic_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth): + def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): n = hidden_states.size(0) logits = torch.randn( n, 1, test_config.vocab_size, device=hidden_states.device, dtype=torch.bfloat16 @@ -3473,7 +3403,7 @@ def mock_deterministic_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth): + def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): n = hidden_states.size(0) logits = torch.randn( n, 1, test_config.vocab_size, device=hidden_states.device, dtype=torch.bfloat16 @@ -3822,7 +3752,7 @@ def mock_deterministic_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth): + def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): n = hidden_states.size(0) pred_toks = (next_token_ids + 1).clamp(max=test_config.vocab_size - 1) logits = torch.zeros( @@ -4260,40 +4190,6 @@ def deterministic_mtp(hidden_states, next_token_ids, position_ids, depth): assert isinstance(lp, float) assert -0.1 < lp <= 0.0, f"Token {j}: expected log prob near 0.0, got {lp}" - @pytest.mark.internal - @pytest.mark.skipif( - not is_fa_min_version("2.7.3"), reason="need latest flash attn for dynamic batching" - ) - @torch.inference_mode() - def test_speculative_decoding_pipeline_parallel(self): - """Test speculative decoding with pipeline parallelism (pp_size=2). - - Verifies that MTP logit broadcasts across pipeline stages don't hang - or produce incorrect results. Each PP stage must participate in the - same number of MTP broadcast rounds. - """ - if not torch.distributed.is_initialized(): - pytest.skip("Distributed not initialized") - world_size = torch.distributed.get_world_size() - pp_size = 2 - if world_size < pp_size: - pytest.skip(f"Test requires at least {pp_size} GPUs") - - env = self._run_test( - model_provider="gpt", - pipeline_model_parallel_size=pp_size, - num_speculative_tokens=2, - num_tokens_to_generate=6, - materialize_only_last_token_logits=False, - ) - - for request in env.requests: - assert ( - request.status == Status.COMPLETED - ), f"Request {request.request_id}: status={request.status}" - num_expected = request.sampling_params.num_tokens_to_generate - assert len(request.generated_tokens) <= num_expected - @pytest.mark.internal @pytest.mark.skipif( not is_fa_min_version("2.7.3"), reason="need latest flash attn for dynamic batching" @@ -4413,6 +4309,137 @@ def mtp_with_rejection(hidden_states, next_token_ids, position_ids, depth): assert env.engine.context.total_request_count == 0 +class TestDynamicInferenceEngineParallel(DynamicInferenceEngineTestBase): + """Tests that require non-default parallel configs (tp>1, pp>1, or ep>1). + + Each test initializes its own parallel state and tears it down afterward, + so these are separated from TestDynamicInferenceEngine to avoid accumulating + NCCL communicator memory from repeated init/destroy cycles. + """ + + def teardown_method(self, method): + delete_cuda_graphs() + Utils.destroy_model_parallel() + + @classmethod + @torch.inference_mode() + def _build_test_env(cls, test_config): + Utils.initialize_model_parallel( + tensor_model_parallel_size=test_config.tensor_model_parallel_size, + pipeline_model_parallel_size=test_config.pipeline_model_parallel_size, + expert_model_parallel_size=test_config.expert_model_parallel_size, + expert_tensor_parallel_size=1, + ) + return super()._build_test_env(test_config) + + @pytest.mark.internal + @pytest.mark.skipif( + not is_fa_min_version("2.7.3"), reason="need latest flash attn for dynamic batching" + ) + @pytest.mark.parametrize("materialize_only_last_token_logits", [False, True]) + @pytest.mark.parametrize("sequence_parallel", [False, True]) + @pytest.mark.parametrize("ep_size", [1, 2]) + @pytest.mark.parametrize("pp_size", [1, 2]) + @pytest.mark.parametrize("tp_size", [1, 2]) + @pytest.mark.parametrize("model_provider", ["gpt", "hybrid"]) + @pytest.mark.parametrize("transformer_impl", ["local", "inference_optimized"]) + @torch.inference_mode() + def test_parallel_inference( + self, + model_provider, + tp_size, + pp_size, + ep_size, + sequence_parallel, + materialize_only_last_token_logits, + transformer_impl, + ): + skip_if_mamba_sequence_packing_not_available(model_provider) + + if tp_size == 1 and pp_size == 1 and ep_size == 1: + pytest.skip(reason="Test requires tp_size > 1 or pp_size > 1 or ep_size > 1") + elif not torch.distributed.is_initialized(): + pytest.skip("Distributed not initialized") + world_size = torch.distributed.get_world_size() + min_world_size = tp_size * pp_size * ep_size + if world_size < min_world_size: + pytest.skip(f"Test requires at least {min_world_size} GPUs") + elif tp_size == 1 and sequence_parallel: + pytest.skip(reason="Sequence parallelism requires tp_size > 1") + elif tp_size > 1 and ep_size > 1 and not sequence_parallel: + pytest.skip(reason="Sequence parallelism must be used with tp_size > 1 and ep_size > 1") + elif transformer_impl == "inference_optimized": + if ep_size > 1: + pytest.skip( + reason="MoE models are not supported with the inference optimized transformer." + ) + if tp_size > 1 and not sequence_parallel: + pytest.skip( + reason=( + "The inference optimized transformer requires sequence parallelism " + "when tp_size > 1." + ) + ) + + env = self._run_test( + model_provider=model_provider, + tensor_model_parallel_size=tp_size, + pipeline_model_parallel_size=pp_size, + expert_model_parallel_size=ep_size, + sequence_parallel=sequence_parallel, + materialize_only_last_token_logits=materialize_only_last_token_logits, + transformer_impl=transformer_impl, + ) + + @pytest.mark.internal + @pytest.mark.skipif( + not is_fa_min_version("2.7.3"), reason="need latest flash attn for dynamic batching" + ) + @pytest.mark.parametrize("materialize_only_last_token_logits", [False, True]) + def test_sequence_parallel_fp8_inference(self, materialize_only_last_token_logits: bool): + fp8_available, reason_for_no_fp8 = check_fp8_support() + if not fp8_available: + pytest.skip(reason_for_no_fp8) + + self._run_test( + min_prompt_length=19, + max_prompt_length=19, + tensor_model_parallel_size=4, + sequence_parallel=True, + materialize_only_last_token_logits=True, + fp8=True, + ) + + @pytest.mark.internal + @pytest.mark.skipif( + not is_fa_min_version("2.7.3"), reason="need latest flash attn for dynamic batching" + ) + @torch.inference_mode() + def test_speculative_decoding_pipeline_parallel(self): + """Test speculative decoding with pipeline parallelism (pp_size=2).""" + if not torch.distributed.is_initialized(): + pytest.skip("Distributed not initialized") + world_size = torch.distributed.get_world_size() + pp_size = 2 + if world_size < pp_size: + pytest.skip(f"Test requires at least {pp_size} GPUs") + + env = self._run_test( + model_provider="gpt", + pipeline_model_parallel_size=pp_size, + num_speculative_tokens=2, + num_tokens_to_generate=6, + materialize_only_last_token_logits=False, + ) + + for request in env.requests: + assert ( + request.status == Status.COMPLETED + ), f"Request {request.request_id}: status={request.status}" + num_expected = request.sampling_params.num_tokens_to_generate + assert len(request.generated_tokens) <= num_expected + + CHUNKED_CG_BLOCK_SIZE = 256 CHUNKED_CG_VOCAB_SIZE = 10000 CHUNKED_CG_MAX_SEQ_LEN = 2048 @@ -4433,6 +4460,7 @@ def setup_class(cls): @classmethod def teardown_class(cls): + delete_cuda_graphs() set_rounder(64) Utils.destroy_model_parallel() @@ -4497,17 +4525,6 @@ def _create_model(self, model_provider, num_cuda_graphs): model.eval() return model - def _reset_cuda_graph_state(self, model): - """Reset all CUDA graph global and per-module state.""" - _CudagraphGlobalRecord.cudagraph_created = False - _CudagraphGlobalRecord.cudagraph_record = [] - _CudagraphGlobalRecord.cudagraph_inference_record = [] - CudaGraphManager.global_mempool = None - for module in model.modules(): - if isinstance(module, CudaGraphManager): - module.cudagraph_runners.clear() - module.custom_cudagraphs_lookup_table.clear() - def _build_engine(self, model, enable_chunked_prefill, num_cuda_graphs, context_max_tokens): """Build an engine with the given chunked prefill / CUDA graph config.""" set_rounder(4) @@ -4540,7 +4557,7 @@ def _build_engine(self, model, enable_chunked_prefill, num_cuda_graphs, context_ vocab_size=CHUNKED_CG_VOCAB_SIZE, detokenize=lambda tokens: "tokenized_prompt" ), ) - self._reset_cuda_graph_state(model) + delete_cuda_graphs() return DynamicInferenceEngine(controller, context) def _run_to_completion(self, engine, prompts, num_tokens_to_generate): @@ -4575,10 +4592,7 @@ def test_chunked_prefill_cuda_graphs(self, model_provider, chunked_prefill, num_ """Verify generated tokens match across chunked prefill and CUDA graph configs.""" skip_if_mamba_sequence_packing_not_available(model_provider) - # Clear NVTE env vars set by conftest set_env fixture. - os.environ.pop('NVTE_FLASH_ATTN', None) - os.environ.pop('NVTE_FUSED_ATTN', None) - os.environ.pop('NVTE_UNFUSED_ATTN', None) + clear_nvte_env_vars() random.seed(123) torch.manual_seed(123) diff --git a/tests/unit_tests/inference/engines/test_mtp_cuda_graph_inference.py b/tests/unit_tests/inference/engines/test_mtp_cuda_graph_inference.py new file mode 100644 index 00000000000..d6605e88d0d --- /dev/null +++ b/tests/unit_tests/inference/engines/test_mtp_cuda_graph_inference.py @@ -0,0 +1,991 @@ +# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +"""Tests for CUDA-graphed MTP (Multi-Token Prediction) inference. + +Verifies that: +1. CUDA graph replay produces the same output as eager execution (no extra + padding in the CUDA graphed case). +2. CUDA graphs work correctly with sequence parallelism (padding is applied + to make batch sizes divisible by TP). +3. CUDA graphs work correctly with expert parallelism and dummy ranks. + +Uses DynamicInferenceEngine for CUDA graph warmup so MTP graph capture +logic matches production code exactly. +""" + +import itertools +from unittest import mock + +import pytest +import torch +import torch.distributed as dist + +from megatron.core import parallel_state +from megatron.core.inference.batch_dimensions_utils import InferenceBatchDimensions +from megatron.core.inference.config import InferenceConfig +from megatron.core.inference.contexts.dynamic_context import DynamicInferenceContext +from megatron.core.inference.engines.dynamic_engine import DynamicInferenceEngine +from megatron.core.inference.model_inference_wrappers.gpt.gpt_inference_wrapper import ( + GPTInferenceWrapper, +) +from megatron.core.inference.text_generation_controllers.text_generation_controller import ( + TextGenerationController, +) +from megatron.core.models.gpt.gpt_layer_specs import ( + get_gpt_layer_local_spec, + get_gpt_mtp_block_spec, +) +from megatron.core.models.gpt.gpt_model import GPTModel +from megatron.core.tensor_parallel.mappings import scatter_to_sequence_parallel_region +from megatron.core.tensor_parallel.random import model_parallel_cuda_manual_seed +from megatron.core.transformer import TransformerConfig +from megatron.core.transformer.cuda_graphs import _CudagraphGlobalRecord, delete_cuda_graphs +from megatron.core.transformer.enums import AttnBackend +from megatron.core.utils import unwrap_model +from tests.unit_tests.test_utilities import Utils + +# --------------------------------------------------------------------------- # +# TestMTPCudaGraphInference (TP = 2) +# --------------------------------------------------------------------------- # + + +class TestMTPCudaGraphInference: + """Tests for MTP CUDA-graphed inference with tensor parallelism. + + All tests require at least 2 GPUs (TP = 2). Uses DynamicInferenceEngine + for CUDA graph warmup so MTP graph capture matches production code. + """ + + HIDDEN_SIZE = 32 + VOCAB_SIZE = 100 + MAX_SEQ_LEN = 64 + NUM_LAYERS = 4 + NUM_ATTN_HEADS = 4 + TP_SIZE = 2 + + @classmethod + def setup_class(cls): + if Utils.world_size < cls.TP_SIZE: + pytest.skip(f"Need at least {cls.TP_SIZE} GPUs") + Utils.initialize_model_parallel( + tensor_model_parallel_size=cls.TP_SIZE, pipeline_model_parallel_size=1 + ) + + @classmethod + def teardown_class(cls): + delete_cuda_graphs() + Utils.destroy_model_parallel() + + def teardown_method(self): + delete_cuda_graphs() + + # ---- helpers ---------------------------------------------------------- # + + def _build_model( + self, *, sequence_parallel=False, mtp_num_layers=2, mtp_use_repeated_layer=False + ): + """Build a GPT model with MTP layers and local CUDA graph support.""" + model_parallel_cuda_manual_seed(123, inference_rng_tracker=True, force_reset_rng=True) + config = TransformerConfig( + num_layers=self.NUM_LAYERS, + hidden_size=self.HIDDEN_SIZE, + num_attention_heads=self.NUM_ATTN_HEADS, + use_cpu_initialization=True, + attention_backend=AttnBackend.local, + params_dtype=torch.bfloat16, + tensor_model_parallel_size=self.TP_SIZE, + pipeline_model_parallel_size=1, + pipeline_dtype=torch.bfloat16, + mtp_num_layers=mtp_num_layers, + mtp_use_repeated_layer=mtp_use_repeated_layer, + sequence_parallel=sequence_parallel, + cuda_graph_impl="local", + ) + layer_spec = get_gpt_layer_local_spec() + mtp_block_spec = get_gpt_mtp_block_spec( + config=config, spec=layer_spec, use_transformer_engine=False + ) + model = GPTModel( + config=config, + transformer_layer_spec=layer_spec, + vocab_size=self.VOCAB_SIZE, + max_sequence_length=self.MAX_SEQ_LEN, + parallel_output=True, + pre_process=True, + post_process=True, + mtp_block_spec=mtp_block_spec, + ).cuda() + for param in model.parameters(): + param.data = param.data.to(config.params_dtype) + model.eval() + return model + + def _build_engine( + self, + *, + sequence_parallel=False, + mtp_num_layers=2, + mtp_use_repeated_layer=False, + num_speculative_tokens=2, + max_requests=16, + ): + """Build a DynamicInferenceEngine with automatic MTP CUDA graph warmup. + + The engine's `__init__` calls `create_cuda_graphs()` which captures + both decoder and MTP CUDA graphs, matching production warmup exactly. + """ + delete_cuda_graphs() + model = self._build_model( + sequence_parallel=sequence_parallel, + mtp_num_layers=mtp_num_layers, + mtp_use_repeated_layer=mtp_use_repeated_layer, + ) + config = model.config + context = DynamicInferenceContext( + model_config=config, + inference_config=InferenceConfig( + max_sequence_length=self.MAX_SEQ_LEN, + buffer_size_gb=0.5, + materialize_only_last_token_logits=False, + num_speculative_tokens=num_speculative_tokens, + block_size_tokens=256, + max_requests=max_requests, + num_cuda_graphs=-1, + ), + ) + wrapped = GPTInferenceWrapper(model, context) + wrapped.model_is_pipeline_parallel = False + mock_tokenizer = mock.Mock() + ctrl = TextGenerationController(inference_wrapped_model=wrapped, tokenizer=mock_tokenizer) + engine = DynamicInferenceEngine(ctrl, context) + return engine + + @staticmethod + def _get_mtp_warmed_batch_sizes(engine): + """Return the MTP batch sizes (padded req_counts) warmed by the engine. + + These are the `n` values for which MTP CUDA graphs were captured. + Hidden states shape is `[n // tp, 1, H]` with SP, `[n, 1, H]` without. + Token/position IDs are always `[1, n]`. + """ + context = engine.context + model_config = engine.controller.inference_wrapped_model.model.config + tp_size = parallel_state.get_tensor_model_parallel_world_size() + sp_enabled = model_config.sequence_parallel and tp_size > 1 + sizes = set() + for dim in context.cuda_graph_batch_dimensions_list: + n = dim.req_count + if sp_enabled: + n += (tp_size - n % tp_size) % tp_size + if n > 0: + sizes.add(n) + return sorted(sizes) + + @staticmethod + def _set_mtp_cuda_graph_flag(model, enabled): + """Set `use_mtp_cuda_graphs` on the model.""" + unwrapped = unwrap_model(model) + unwrapped.use_mtp_cuda_graphs = enabled + + @staticmethod + def _assert_mtp_cuda_graphs_were_replayed(model, expect_replayed): + """Assert that MTP CUDA graphs were (or were not) replayed. + + MTP runners are stored in the CudaGraphManager's lookup table + rather than the global inference record. A runner with + `fwd_graph_recorded=True` confirms the graph was captured and + replayed. + """ + unwrapped = unwrap_model(model) + manager = getattr(unwrapped, '_mtp_cudagraph_manager', None) + if manager is None: + assert not expect_replayed, "No MTP CudaGraphManager found on the model" + return + table = manager.custom_cudagraphs_lookup_table + mtp_runners = [v for k, v in table.items() if isinstance(k, tuple) and k[0] == 'mtp'] + if expect_replayed: + assert ( + len(mtp_runners) > 0 + ), "Expected MTP CUDA graphs to be replayed, but no MTP runners found" + for runner in mtp_runners: + assert runner.fwd_graph_recorded, ( + "Expected MTP CUDA graph to be recorded and replayed, " + f"but runner for {runner.base_module.__class__.__name__} " + "has fwd_graph_recorded=False" + ) + else: + recorded = [r for r in mtp_runners if r.fwd_graph_recorded] + assert len(recorded) == 0, ( + f"Expected no MTP CUDA graph replay, but {len(recorded)} " + "runners have fwd_graph_recorded=True" + ) + + # ---- Test 1: graph output matches eager (no additional padding) ------- # + + @pytest.mark.parametrize("mtp_use_repeated_layer", [False, True]) + @torch.inference_mode() + def test_cuda_graph_output_matches_eager(self, mtp_use_repeated_layer): + """CUDA graph replay produces the same output as eager execution. + + The batch sizes exactly match warmed-up graphs (from the engine's + CUDA graph warmup), so there is no additional padding. Both paths + must produce identical hidden states and logits. + """ + engine = self._build_engine(mtp_use_repeated_layer=mtp_use_repeated_layer) + model = engine.controller.inference_wrapped_model.model + unwrapped = unwrap_model(model) + batch_sizes = self._get_mtp_warmed_batch_sizes(engine) + assert len(batch_sizes) > 0, "Engine did not warm up any MTP CUDA graphs" + + mtp_depth = None if unwrapped.mtp.mtp_use_repeated_layer else 0 + + for batch_size in batch_sizes[:3]: + hidden = torch.randn( + batch_size, 1, self.HIDDEN_SIZE, device='cuda', dtype=torch.bfloat16 + ) + dist.broadcast(hidden, src=0) + token_ids = torch.randint(0, self.VOCAB_SIZE, (1, batch_size), device='cuda') + dist.broadcast(token_ids, src=0) + position_ids = torch.arange(batch_size, device='cuda', dtype=torch.int64).unsqueeze(0) + + self._set_mtp_cuda_graph_flag(model, True) + h_graph, logits_graph = unwrapped.compute_mtp_single_step( + hidden_states=hidden.clone(), + next_token_ids=token_ids.clone(), + position_ids=position_ids.clone(), + depth=mtp_depth, + ) + h_graph = h_graph.clone() + logits_graph = logits_graph.clone() + + self._set_mtp_cuda_graph_flag(model, False) + h_eager, logits_eager = unwrapped.compute_mtp_single_step( + hidden_states=hidden.clone(), + next_token_ids=token_ids.clone(), + position_ids=position_ids.clone(), + depth=mtp_depth, + ) + + torch.testing.assert_close( + h_graph, h_eager, msg=f"Hidden mismatch at batch_size={batch_size}" + ) + torch.testing.assert_close( + logits_graph, logits_eager, msg=f"Logits mismatch at batch_size={batch_size}" + ) + + self._assert_mtp_cuda_graphs_were_replayed(model, True) + + # ---- Test 2: graph matches eager with sequence parallelism ------------ # + + @pytest.mark.parametrize("mtp_use_repeated_layer", [False, True]) + @torch.inference_mode() + def test_cuda_graph_output_matches_eager_with_sp(self, mtp_use_repeated_layer): + """CUDA graph replay matches eager with sequence parallelism. + + Hidden states are in scattered SP format `[batch_size/TP, 1, H]`. + Token/position IDs remain at full `[1, batch_size]`. Both paths + must produce identical outputs. + """ + engine = self._build_engine( + sequence_parallel=True, mtp_use_repeated_layer=mtp_use_repeated_layer + ) + model = engine.controller.inference_wrapped_model.model + unwrapped = unwrap_model(model) + tp_group = parallel_state.get_tensor_model_parallel_group() + batch_sizes = self._get_mtp_warmed_batch_sizes(engine) + assert len(batch_sizes) > 0, "Engine did not warm up any MTP CUDA graphs" + + mtp_depth = None if unwrapped.mtp.mtp_use_repeated_layer else 0 + + for batch_size in batch_sizes[:3]: + hidden = torch.randn( + batch_size, 1, self.HIDDEN_SIZE, device='cuda', dtype=torch.bfloat16 + ) + dist.broadcast(hidden, src=0) + hidden_sp = scatter_to_sequence_parallel_region(hidden, group=tp_group) + + token_ids = torch.randint(0, self.VOCAB_SIZE, (1, batch_size), device='cuda') + dist.broadcast(token_ids, src=0) + position_ids = torch.arange(batch_size, device='cuda', dtype=torch.int64).unsqueeze(0) + + self._set_mtp_cuda_graph_flag(model, True) + h_graph, logits_graph = unwrapped.compute_mtp_single_step( + hidden_states=hidden_sp.clone(), + next_token_ids=token_ids.clone(), + position_ids=position_ids.clone(), + depth=mtp_depth, + ) + h_graph = h_graph.clone() + logits_graph = logits_graph.clone() + + self._set_mtp_cuda_graph_flag(model, False) + h_eager, logits_eager = unwrapped.compute_mtp_single_step( + hidden_states=hidden_sp.clone(), + next_token_ids=token_ids.clone(), + position_ids=position_ids.clone(), + depth=mtp_depth, + ) + + torch.testing.assert_close( + h_graph, h_eager, msg=f"Hidden mismatch at batch_size={batch_size}" + ) + torch.testing.assert_close( + logits_graph, logits_eager, msg=f"Logits mismatch at batch_size={batch_size}" + ) + + self._assert_mtp_cuda_graphs_were_replayed(model, True) + + # ---- Test 3: end-to-end _compute_serial_mtp_and_sample with SP ------- # + + @pytest.mark.parametrize("mtp_use_repeated_layer", [False, True]) + @torch.inference_mode() + def test_cuda_graph_sp_padding_end_to_end(self, mtp_use_repeated_layer): + """Full `_compute_serial_mtp_and_sample` with CUDA graphs and SP. + + Active request counts that are not multiples of TP are padded. + The engine's CUDA graph warmup pre-captures MTP graphs for the + padded batch sizes. Verifies that padding, SP scatter/gather, and + MTP forward all work correctly through the CUDA graph path. + """ + tp_size = self.TP_SIZE + num_spec = 2 + max_requests = 16 + engine = self._build_engine( + sequence_parallel=True, + mtp_num_layers=num_spec, + mtp_use_repeated_layer=mtp_use_repeated_layer, + num_speculative_tokens=num_spec, + max_requests=max_requests, + ) + ctrl = engine.controller + context = engine.context + model = ctrl.inference_wrapped_model.model + unwrapped = unwrap_model(model) + + mtp_sizes = self._get_mtp_warmed_batch_sizes(engine) + + # Find active_request_counts whose TP-padded values match warmed MTP sizes. + active_counts = [] + for n in mtp_sizes: + for active in range(n, 0, -1): + padded = active + (tp_size - active % tp_size) % tp_size + if padded == n and active <= max_requests: + active_counts.append(active) + break + assert len(active_counts) > 0, "No valid active request counts found" + + for active_request_count in active_counts[:4]: + padded_count = ( + active_request_count + (tp_size - active_request_count % tp_size) % tp_size + ) + + context.reset() + context.total_request_count = active_request_count + context.paused_request_count = 0 + context.request_kv_length_offsets[:active_request_count] = torch.arange( + active_request_count, dtype=torch.int32, device='cuda' + ) + context.request_query_lengths[:active_request_count] = torch.ones( + active_request_count, dtype=torch.int32, device='cuda' + ) + + ctrl.num_speculative_tokens = num_spec + ctrl.num_mtp_heads = num_spec + ctrl._init_mtp_sampling_tensors() + ctrl._mtp_token_ids_buf.zero_() + ctrl._mtp_position_ids_buf.zero_() + ctrl._sampled_tokens_cuda[:active_request_count] = torch.remainder( + torch.arange(active_request_count, device='cuda'), self.VOCAB_SIZE + ) + + tp_rank = parallel_state.get_tensor_model_parallel_rank() + + torch.manual_seed(42) + full_hidden = torch.randn( + padded_count, 1, self.HIDDEN_SIZE, device='cuda', dtype=torch.bfloat16 + ) + dist.broadcast(full_hidden, src=0) + local_hidden = full_hidden.chunk(tp_size)[tp_rank].contiguous() + unwrapped._decoder_hidden_states_cache = local_hidden + + ctrl._last_accepted_seq_indices = torch.arange(active_request_count, device='cuda') + ctrl._mtp_resolved_padded_count = padded_count + self._set_mtp_cuda_graph_flag(model, True) + + ctrl._torch_sampling_buckets = [(list(range(active_request_count)), 1.0, 1, 0.0)] + ctrl._torch_sampling_bucket_index_tensors = [ + torch.arange(active_request_count, device='cuda', dtype=torch.long) + ] + + ctrl._compute_serial_mtp_and_sample() + + for depth in range(num_spec): + sampled = ctrl._sampled_mtp_tokens_cuda[depth, :active_request_count] + assert sampled.shape == ( + active_request_count, + ), f"active={active_request_count}, depth={depth}" + assert sampled.dtype == torch.int64 + assert torch.all(sampled >= 0) and torch.all(sampled < self.VOCAB_SIZE) + + assert not hasattr(unwrapped, '_decoder_hidden_states_cache') + + self._assert_mtp_cuda_graphs_were_replayed(model, True) + + # ---- Test 4: SP padding graph vs eager produces same MTP tokens ------- # + + @pytest.mark.parametrize("mtp_use_repeated_layer", [False, True]) + @torch.inference_mode() + def test_cuda_graph_sp_padding_matches_eager(self, mtp_use_repeated_layer): + """With SP padding, CUDA graph path produces the same MTP tokens as eager. + + Uses a single engine (shared model weights) and toggles the CUDA + graph flag between runs. Both paths receive identical inputs and + must produce the same sampled MTP tokens. + """ + tp_size = self.TP_SIZE + num_spec = 2 + max_requests = 16 + engine = self._build_engine( + sequence_parallel=True, + mtp_num_layers=num_spec, + mtp_use_repeated_layer=mtp_use_repeated_layer, + num_speculative_tokens=num_spec, + max_requests=max_requests, + ) + ctrl = engine.controller + context = engine.context + model = ctrl.inference_wrapped_model.model + + mtp_sizes = self._get_mtp_warmed_batch_sizes(engine) + + # Find active counts that require TP padding (active % tp != 0). + active_counts = [] + for n in mtp_sizes: + for active in range(n, 0, -1): + padded = active + (tp_size - active % tp_size) % tp_size + if padded == n and active % tp_size != 0 and active <= max_requests: + active_counts.append(active) + break + assert len(active_counts) > 0, "No active counts with TP padding found" + + for active_request_count in active_counts[:2]: + padded_count = ( + active_request_count + (tp_size - active_request_count % tp_size) % tp_size + ) + + def _run_mtp(use_cuda_graph): + """Set up state and run MTP, returning sampled tokens.""" + unwrapped = unwrap_model(model) + context.reset() + context.total_request_count = active_request_count + context.paused_request_count = 0 + context.request_kv_length_offsets[:active_request_count] = torch.arange( + active_request_count, dtype=torch.int32, device='cuda' + ) + context.request_query_lengths[:active_request_count] = torch.ones( + active_request_count, dtype=torch.int32, device='cuda' + ) + + ctrl.num_speculative_tokens = num_spec + ctrl.num_mtp_heads = num_spec + ctrl._init_mtp_sampling_tensors() + ctrl._mtp_token_ids_buf.zero_() + ctrl._mtp_position_ids_buf.zero_() + ctrl._sampled_tokens_cuda[:active_request_count] = torch.remainder( + torch.arange(active_request_count, device='cuda'), self.VOCAB_SIZE + ) + + if use_cuda_graph: + ctrl.has_mtp_cuda_graphs = True + ctrl._mtp_resolved_padded_count = padded_count + self._set_mtp_cuda_graph_flag(model, True) + else: + ctrl.has_mtp_cuda_graphs = False + ctrl._mtp_resolved_padded_count = None + self._set_mtp_cuda_graph_flag(model, False) + + tp_rank = parallel_state.get_tensor_model_parallel_rank() + + torch.manual_seed(42) + full_hidden = torch.randn( + padded_count, 1, self.HIDDEN_SIZE, device='cuda', dtype=torch.bfloat16 + ) + dist.broadcast(full_hidden, src=0) + local_hidden = full_hidden.chunk(tp_size)[tp_rank].contiguous() + unwrapped._decoder_hidden_states_cache = local_hidden + + ctrl._last_accepted_seq_indices = torch.arange(active_request_count, device='cuda') + ctrl._torch_sampling_buckets = [(list(range(active_request_count)), 1.0, 1, 0.0)] + ctrl._torch_sampling_bucket_index_tensors = [ + torch.arange(active_request_count, device='cuda', dtype=torch.long) + ] + + ctrl._compute_serial_mtp_and_sample() + + return [ + ctrl._sampled_mtp_tokens_cuda[d, :active_request_count].clone() + for d in range(num_spec) + ] + + graph_tokens = _run_mtp(use_cuda_graph=True) + self._assert_mtp_cuda_graphs_were_replayed(model, True) + eager_tokens = _run_mtp(use_cuda_graph=False) + + for depth in range(num_spec): + assert torch.equal(graph_tokens[depth], eager_tokens[depth]), ( + f"active={active_request_count}, depth={depth}: " + f"graph tokens {graph_tokens[depth].tolist()} != " + f"eager tokens {eager_tokens[depth].tolist()}" + ) + + # ---- Test 5: multiple MTP depths with CUDA graphs --------------------- # + + @pytest.mark.parametrize("mtp_use_repeated_layer", [False, True]) + @torch.inference_mode() + def test_cuda_graph_multi_depth(self, mtp_use_repeated_layer): + """Run multiple MTP depths with CUDA graphs enabled. + + Verifies that the hidden output from one depth feeds correctly into + the next depth through the same CUDA graph, producing valid outputs + at every depth. + """ + num_depths = 2 + engine = self._build_engine( + mtp_num_layers=num_depths, mtp_use_repeated_layer=mtp_use_repeated_layer + ) + model = engine.controller.inference_wrapped_model.model + unwrapped = unwrap_model(model) + batch_sizes = self._get_mtp_warmed_batch_sizes(engine) + assert len(batch_sizes) > 0, "Engine did not warm up any MTP CUDA graphs" + + use_repeated = unwrapped.mtp.mtp_use_repeated_layer + + batch_size = batch_sizes[0] + self._set_mtp_cuda_graph_flag(model, True) + + hidden = torch.randn(batch_size, 1, self.HIDDEN_SIZE, device='cuda', dtype=torch.bfloat16) + dist.broadcast(hidden, src=0) + token_ids = torch.randint(0, self.VOCAB_SIZE, (1, batch_size), device='cuda') + dist.broadcast(token_ids, src=0) + position_ids = torch.arange(batch_size, device='cuda', dtype=torch.int64).unsqueeze(0) + + current_hidden = hidden.clone() + for depth in range(num_depths): + mtp_depth = None if use_repeated else depth + current_hidden, logits = unwrapped.compute_mtp_single_step( + hidden_states=current_hidden, + next_token_ids=token_ids.clone(), + position_ids=position_ids.clone(), + depth=mtp_depth, + ) + current_hidden = current_hidden.clone() + + assert current_hidden.shape == (batch_size, 1, self.HIDDEN_SIZE), ( + f"Depth {depth}: expected hidden shape ({batch_size}, 1, {self.HIDDEN_SIZE}), " + f"got {current_hidden.shape}" + ) + assert logits.shape == (batch_size, 1, self.VOCAB_SIZE), ( + f"Depth {depth}: expected logits shape ({batch_size}, 1, {self.VOCAB_SIZE}), " + f"got {logits.shape}" + ) + assert torch.all( + torch.isfinite(logits) + ), f"Depth {depth}: logits contain non-finite values" + + self._assert_mtp_cuda_graphs_were_replayed(model, True) + + # ---- Test 6: eager fallback when no matching graph exists ------------- # + + @pytest.mark.parametrize("mtp_use_repeated_layer", [False, True]) + @torch.inference_mode() + def test_eager_fallback_no_matching_graph(self, mtp_use_repeated_layer): + """When `use_mtp_cuda_graphs` is True but no warmed graph matches the + batch size, `compute_mtp_single_step` falls back to eager execution. + The system should produce valid outputs without errors. + """ + engine = self._build_engine(mtp_use_repeated_layer=mtp_use_repeated_layer) + model = engine.controller.inference_wrapped_model.model + unwrapped = unwrap_model(model) + warmed_sizes = set(self._get_mtp_warmed_batch_sizes(engine)) + + # Find a batch size with no matching CUDA graph. + fallback_size = None + for candidate in range(1, 32): + if candidate not in warmed_sizes: + fallback_size = candidate + break + assert fallback_size is not None, "Could not find a non-warmed batch size" + + mtp_depth = None if unwrapped.mtp.mtp_use_repeated_layer else 0 + + self._set_mtp_cuda_graph_flag(model, True) + hidden = torch.randn( + fallback_size, 1, self.HIDDEN_SIZE, device='cuda', dtype=torch.bfloat16 + ) + dist.broadcast(hidden, src=0) + token_ids = torch.randint(0, self.VOCAB_SIZE, (1, fallback_size), device='cuda') + dist.broadcast(token_ids, src=0) + position_ids = torch.arange(fallback_size, device='cuda', dtype=torch.int64).unsqueeze(0) + + h_out, logits = unwrapped.compute_mtp_single_step( + hidden_states=hidden.clone(), + next_token_ids=token_ids.clone(), + position_ids=position_ids.clone(), + depth=mtp_depth, + ) + + assert h_out.shape == (fallback_size, 1, self.HIDDEN_SIZE) + assert logits.shape == (fallback_size, 1, self.VOCAB_SIZE) + assert torch.all(torch.isfinite(logits)) + + # ---- Test 7: graph flag propagation matches main model ---------------- # + + @torch.inference_mode() + def test_mtp_graph_flag_propagation(self): + """`use_mtp_cuda_graphs` is correctly toggled via the helper.""" + model = self._build_model(mtp_num_layers=2) + unwrapped = unwrap_model(model) + + self._set_mtp_cuda_graph_flag(model, True) + assert unwrapped.use_mtp_cuda_graphs is True + + self._set_mtp_cuda_graph_flag(model, False) + assert unwrapped.use_mtp_cuda_graphs is False + + # ---- Test 8: delete_cuda_graphs resets MTP runners -------------------- # + + @torch.inference_mode() + def test_delete_cuda_graphs_resets_mtp_runners(self): + """`delete_cuda_graphs()` resets MTP CUDA graph runners. + + MTP runners are excluded from the global inference record, so they + require special handling in `delete_cuda_graphs()`. After deletion, + no MTP runners should have `fwd_graph_recorded=True` and the global + manager list should be cleared. + """ + engine = self._build_engine() + model = engine.controller.inference_wrapped_model.model + + self._assert_mtp_cuda_graphs_were_replayed(model, True) + + unwrapped = unwrap_model(model) + manager = getattr(unwrapped, '_mtp_cudagraph_manager', None) + assert manager is not None + assert len(manager.custom_cudagraphs_lookup_table) > 0 + + delete_cuda_graphs() + + assert len(manager.custom_cudagraphs_lookup_table) == 0 + assert len(_CudagraphGlobalRecord.mtp_cudagraph_managers) == 0 + + +# --------------------------------------------------------------------------- # +# TestMTPCudaGraphExpertParallel (EP = 2) +# --------------------------------------------------------------------------- # + +_EP_SIZE = 2 + +# Request state constants for parametrized tests. +NONE = "none" +DECODE = "decode" +PREFILL = "prefill" +MIXED = "mixed" + +ALL_STATES = [NONE, DECODE, PREFILL, MIXED] + +# Combinatorial sweep: C(4+2-1, 2) = 10 test cases. +_STATE_COMBOS = list(itertools.combinations_with_replacement(ALL_STATES, _EP_SIZE)) + +# Batch dimensions for each non-dummy state. +_STATE_DIMS = { + DECODE: InferenceBatchDimensions(token_count=2, prefill_req_count=0, decode_req_count=2), + PREFILL: InferenceBatchDimensions(token_count=16, prefill_req_count=2, decode_req_count=0), + MIXED: InferenceBatchDimensions(token_count=32, prefill_req_count=1, decode_req_count=2), +} + + +@pytest.mark.internal +class TestMTPCudaGraphExpertParallel: + """Tests for MTP CUDA-graphed inference with expert parallelism. + + Follows the test pattern from `test_mamba_model_expert_parallel_inference.py`. + All tests require at least `_EP_SIZE` GPUs. + """ + + HIDDEN_SIZE = 32 + VOCAB_SIZE = 100 + MAX_SEQ_LEN = 128 + NUM_LAYERS = 2 + NUM_ATTN_HEADS = 4 + NUM_MOE_EXPERTS = 2 + + @classmethod + def setup_class(cls): + if Utils.world_size < _EP_SIZE: + pytest.skip(f"EP test requires at least {_EP_SIZE} GPUs") + if Utils.world_size % _EP_SIZE != 0: + pytest.skip( + f"world_size ({Utils.world_size}) must be divisible by EP size ({_EP_SIZE})" + ) + Utils.initialize_model_parallel( + tensor_model_parallel_size=1, + pipeline_model_parallel_size=1, + expert_model_parallel_size=_EP_SIZE, + ) + + @classmethod + def teardown_class(cls): + delete_cuda_graphs() + Utils.destroy_model_parallel() + + def teardown_method(self): + delete_cuda_graphs() + + # ---- helpers ---------------------------------------------------------- # + + def _build_model(self): + """Build a GPT model with MTP + MoE + local CUDA graphs.""" + model_parallel_cuda_manual_seed(123, inference_rng_tracker=True, force_reset_rng=True) + config = TransformerConfig( + num_layers=self.NUM_LAYERS, + hidden_size=self.HIDDEN_SIZE, + num_attention_heads=self.NUM_ATTN_HEADS, + use_cpu_initialization=True, + attention_backend=AttnBackend.local, + params_dtype=torch.bfloat16, + expert_model_parallel_size=_EP_SIZE, + num_moe_experts=self.NUM_MOE_EXPERTS, + moe_token_dispatcher_type="alltoall", + add_bias_linear=False, + mtp_num_layers=2, + cuda_graph_impl="local", + moe_pad_experts_for_cuda_graph_inference=True, + ) + layer_spec = get_gpt_layer_local_spec(num_experts=self.NUM_MOE_EXPERTS) + mtp_block_spec = get_gpt_mtp_block_spec( + config=config, spec=layer_spec, use_transformer_engine=False + ) + model = GPTModel( + config=config, + transformer_layer_spec=layer_spec, + vocab_size=self.VOCAB_SIZE, + max_sequence_length=self.MAX_SEQ_LEN, + parallel_output=True, + pre_process=True, + post_process=True, + mtp_block_spec=mtp_block_spec, + ).cuda() + for param in model.parameters(): + param.data = param.data.to(config.params_dtype) + model.eval() + return model + + def _build_context( + self, + model, + *, + num_cuda_graphs=16, + use_cuda_graphs_for_non_decode_steps=True, + max_requests=None, + ): + """Build a DynamicInferenceContext for the model.""" + return DynamicInferenceContext( + model_config=model.config, + inference_config=InferenceConfig( + max_sequence_length=self.MAX_SEQ_LEN, + buffer_size_gb=0.5, + block_size_tokens=256, + materialize_only_last_token_logits=False, + num_cuda_graphs=num_cuda_graphs, + use_cuda_graphs_for_non_decode_steps=use_cuda_graphs_for_non_decode_steps, + max_requests=max_requests, + ), + ) + + # ---- Test 1: all EP ranks run MTP eager forward ----------------------- # + + @pytest.mark.parametrize("batch_size", [2, 4, 8]) + @pytest.mark.internal + @torch.inference_mode() + def test_ep_mtp_eager_forward(self, batch_size): + """All EP ranks can run MTP forward in eager mode. + + The MoE all-to-all collectives must match across EP ranks. Verifies + that all ranks complete without hanging and produce valid shapes. + """ + model = self._build_model() + unwrapped = unwrap_model(model) + + # Broadcast identical inputs so all EP ranks see the same data. + hidden = torch.randn(batch_size, 1, self.HIDDEN_SIZE, device='cuda', dtype=torch.bfloat16) + dist.broadcast(hidden, src=0) + token_ids = torch.randint(0, self.VOCAB_SIZE, (1, batch_size), device='cuda') + dist.broadcast(token_ids, src=0) + position_ids = torch.arange(batch_size, device='cuda', dtype=torch.int64).unsqueeze(0) + + h_out, logits = unwrapped.compute_mtp_single_step( + hidden_states=hidden.clone(), + next_token_ids=token_ids.clone(), + position_ids=position_ids.clone(), + depth=0, + ) + + assert h_out.shape == (batch_size, 1, self.HIDDEN_SIZE) + assert logits.shape == (batch_size, 1, self.VOCAB_SIZE) + assert torch.all(torch.isfinite(logits)) + + # ---- Test 2: dummy ranks + real ranks in eager mode ------------------- # + + @pytest.mark.internal + @torch.inference_mode() + def test_ep_mtp_eager_dummy_and_real_ranks(self): + """Even EP ranks run as dummy (with zeros), odd ranks run with real data. + + Both must issue matching MoE all-to-all collectives via the + MTP eager forward to avoid hangs. + """ + batch_size = 4 + model = self._build_model() + unwrapped = unwrap_model(model) + + ep_rank = parallel_state.get_expert_model_parallel_rank() + is_dummy = ep_rank % 2 == 0 + + if is_dummy: + hidden = torch.zeros( + batch_size, 1, self.HIDDEN_SIZE, device='cuda', dtype=torch.bfloat16 + ) + token_ids = torch.zeros(1, batch_size, device='cuda', dtype=torch.long) + else: + hidden = torch.randn( + batch_size, 1, self.HIDDEN_SIZE, device='cuda', dtype=torch.bfloat16 + ) + token_ids = torch.randint(0, self.VOCAB_SIZE, (1, batch_size), device='cuda') + position_ids = torch.arange(batch_size, device='cuda', dtype=torch.int64).unsqueeze(0) + + # All ranks must complete without hanging. + h_out, logits = unwrapped.compute_mtp_single_step( + hidden_states=hidden, next_token_ids=token_ids, position_ids=position_ids, depth=0 + ) + + assert h_out.shape == (batch_size, 1, self.HIDDEN_SIZE) + assert logits.shape == (batch_size, 1, self.VOCAB_SIZE) + + # ---- Test 3: EP state cross product with DynamicInferenceContext ------- # + + @pytest.mark.parametrize("rank_states", _STATE_COMBOS, ids=[",".join(s) for s in _STATE_COMBOS]) + @pytest.mark.internal + @torch.inference_mode() + def test_ep_state_cross_product(self, rank_states): + """Test combinatorial assignments of request states across EP ranks. + + Verifies that: + - All EP ranks agree on CUDA graph usage (on or off). + - When CUDA graphs are used, all ranks agree on the padded batch size + (which would be used as the MTP batch dimension). + """ + ep_rank = parallel_state.get_expert_model_parallel_rank() + my_state = rank_states[ep_rank] + is_dummy = my_state == NONE + + model = self._build_model() + ctx = self._build_context(model) + + # Phase 1: Set up each rank's request state. + if not is_dummy: + ctx.add_dummy_requests_for_cudagraph_capture(_STATE_DIMS[my_state]) + + # Phase 2: Initialize attention state (EP collective). + if is_dummy: + ctx.initialize_attention_state(is_expert_parallel_dummy_cuda_graph_step=True) + else: + ctx.initialize_attention_state() + + # Phase 3: Verify EP agreement on CUDA graph usage. + uses_graph = ctx.using_cuda_graph_this_step() + ep_group = parallel_state.get_expert_model_parallel_group() + uses_graph_t = torch.tensor([int(uses_graph)], device='cuda', dtype=torch.int32) + graph_min = uses_graph_t.clone() + graph_max = uses_graph_t.clone() + dist.all_reduce(graph_min, op=dist.ReduceOp.MIN, group=ep_group) + dist.all_reduce(graph_max, op=dist.ReduceOp.MAX, group=ep_group) + assert graph_min.item() == graph_max.item(), ( + f"CUDA graph usage disagrees across EP ranks: " + f"min={graph_min.item()}, max={graph_max.item()} " + f"(rank_states={rank_states})" + ) + + if not uses_graph: + return + + # Phase 4: Derive MTP padded batch size from EP-synced dimensions. + mtp_padded = ctx.padded_batch_dimensions.req_count + + # Verify MTP padded count agrees across EP ranks. + padded_t = torch.tensor([mtp_padded], dtype=torch.int32, device='cuda') + padded_max = padded_t.clone() + padded_min = padded_t.clone() + dist.all_reduce(padded_max, op=dist.ReduceOp.MAX, group=ep_group) + dist.all_reduce(padded_min, op=dist.ReduceOp.MIN, group=ep_group) + assert padded_max.item() == padded_min.item(), ( + f"MTP padded batch size mismatch across EP ranks: " + f"min={padded_min.item()}, max={padded_max.item()} " + f"(rank_states={rank_states})" + ) + + # ---- Test 4: dummy EP rank bail-out with decode-only CUDA graphs ------ # + + @pytest.mark.parametrize( + "peer_state", [PREFILL, MIXED], ids=[f"peer={s}" for s in [PREFILL, MIXED]] + ) + @pytest.mark.internal + @torch.inference_mode() + def test_ep_dummy_bailout_with_decode_only_cuda_graphs(self, peer_state): + """Verify the dummy-rank bail-out path when only decode CUDA graphs + are available. + + With `use_cuda_graphs_for_non_decode_steps=False`, only decode-only + graphs exist. When any EP rank has prefill requests, no graph matches + and all ranks fall back to eager mode. The MTP forward for the dummy + rank must use eager execution without hanging. + """ + ep_rank = parallel_state.get_expert_model_parallel_rank() + is_even = ep_rank % 2 == 0 + + model = self._build_model() + ctx = self._build_context(model, use_cuda_graphs_for_non_decode_steps=False) + + # Even ranks are dummy; odd ranks have the peer_state. + if not is_even: + ctx.add_dummy_requests_for_cudagraph_capture(_STATE_DIMS[peer_state]) + + if is_even: + ctx.initialize_attention_state(is_expert_parallel_dummy_cuda_graph_step=True) + else: + ctx.initialize_attention_state() + + # No rank should match a CUDA graph. + assert not ctx.using_cuda_graph_this_step(), ( + f"EP rank {ep_rank}: expected no CUDA graph match with " + f"decode-only graphs and peer_state={peer_state}" + ) + + # MTP eager forward should still work on all ranks. + unwrapped = unwrap_model(model) + + tp_size = parallel_state.get_tensor_model_parallel_world_size() + dummy_hidden = torch.zeros( + (tp_size, 1, self.HIDDEN_SIZE), device='cuda', dtype=torch.bfloat16 + ) + dummy_tokens = torch.zeros((1, tp_size), device='cuda', dtype=torch.long) + dummy_positions = torch.zeros((1, tp_size), device='cuda', dtype=torch.long) + + h_out, logits = unwrapped.compute_mtp_single_step( + hidden_states=dummy_hidden, + next_token_ids=dummy_tokens, + position_ids=dummy_positions, + depth=0, + ) + + assert h_out.shape == (tp_size, 1, self.HIDDEN_SIZE) + assert logits.shape == (tp_size, 1, self.VOCAB_SIZE) diff --git a/tests/unit_tests/inference/engines/test_static_engine.py b/tests/unit_tests/inference/engines/test_static_engine.py index 483a21d13bd..0067ff6e9bc 100644 --- a/tests/unit_tests/inference/engines/test_static_engine.py +++ b/tests/unit_tests/inference/engines/test_static_engine.py @@ -27,9 +27,10 @@ from megatron.core.models.gpt.gpt_layer_specs import get_gpt_layer_local_spec from megatron.core.models.gpt.gpt_model import GPTModel from megatron.core.tensor_parallel.random import model_parallel_cuda_manual_seed +from megatron.core.transformer.cuda_graphs import delete_cuda_graphs from megatron.core.transformer.transformer_config import TransformerConfig from megatron.core.utils import is_fa_min_version -from tests.unit_tests.test_utilities import Utils +from tests.unit_tests.test_utilities import Utils, clear_nvte_env_vars class StaticInferenceEngineTestHarness: @@ -45,11 +46,7 @@ def setup_engine( buffer_size_gb=10, inference_config_params_dtype=torch.float, ): - Utils.initialize_model_parallel( - tensor_model_parallel_size=tensor_model_parallel_size, - pipeline_model_parallel_size=pipeline_model_parallel_size, - ) - + clear_nvte_env_vars() model_parallel_cuda_manual_seed(123) self.batch_size = 4 self.hidden_size = 32 @@ -111,11 +108,23 @@ def setup_engine( buffer_size_gb=buffer_size_gb, ) + +class TestStaticInferenceEngine(StaticInferenceEngineTestHarness): + + @classmethod + def setup_class(cls): + Utils.initialize_model_parallel( + tensor_model_parallel_size=1, pipeline_model_parallel_size=1 + ) + def teardown_method(self, method): - Utils.destroy_model_parallel() + delete_cuda_graphs() + @classmethod + def teardown_class(cls): + delete_cuda_graphs() + Utils.destroy_model_parallel() -class TestStaticInferenceEngine(StaticInferenceEngineTestHarness): @pytest.mark.parametrize( "batch_size,num_trials,empty_prompt", [(4, 1, False), (4, 1, True), (4, 3, False), (2, 1, False), (8, 1, False)], @@ -294,6 +303,47 @@ async def collect_stream(stream_generator, num_tokens_to_generate): f"final_streamed_token.generated_log_probs={final_streamed_token.generated_log_probs}" ) + +class TestStaticInferenceEngineParallel(StaticInferenceEngineTestHarness): + """Tests that require non-default parallel configs (varying tp/pp/ep). + + Each test initializes its own parallel state and tears it down afterward, + so these are separated from TestStaticInferenceEngine to avoid + accumulating NCCL communicator memory from repeated init/destroy cycles. + """ + + def teardown_method(self, method): + delete_cuda_graphs() + Utils.destroy_model_parallel() + + def setup_engine( + self, + engine_max_batch_size=None, + vocab_size=100, + tensor_model_parallel_size=1, + pipeline_model_parallel_size=1, + expert_model_parallel_size=1, + sequence_parallel=False, + legacy=False, + buffer_size_gb=10, + inference_config_params_dtype=torch.float, + ): + Utils.initialize_model_parallel( + tensor_model_parallel_size=tensor_model_parallel_size, + pipeline_model_parallel_size=pipeline_model_parallel_size, + ) + super().setup_engine( + engine_max_batch_size=engine_max_batch_size, + vocab_size=vocab_size, + tensor_model_parallel_size=tensor_model_parallel_size, + pipeline_model_parallel_size=pipeline_model_parallel_size, + expert_model_parallel_size=expert_model_parallel_size, + sequence_parallel=sequence_parallel, + legacy=legacy, + buffer_size_gb=buffer_size_gb, + inference_config_params_dtype=inference_config_params_dtype, + ) + @pytest.mark.parametrize("sequence_parallel", [False, True]) @pytest.mark.parametrize("ep_size", [1, 2]) @pytest.mark.parametrize("pp_size", [1, 2]) diff --git a/tests/unit_tests/inference/test_batch_dimension_utils.py b/tests/unit_tests/inference/test_batch_dimension_utils.py index f520c2441d7..f35be897e3a 100644 --- a/tests/unit_tests/inference/test_batch_dimension_utils.py +++ b/tests/unit_tests/inference/test_batch_dimension_utils.py @@ -122,6 +122,7 @@ class TestMatchGraphConfigWithEP: Uses the world group as the EP group (all 8 GPUs form one EP group). """ + @classmethod def setup_class(cls): Utils.initialize_model_parallel( tensor_model_parallel_size=1, @@ -129,6 +130,7 @@ def setup_class(cls): expert_model_parallel_size=Utils.world_size, ) + @classmethod def teardown_class(cls): Utils.destroy_model_parallel() @@ -352,14 +354,16 @@ def test_one_rank_oversized_forces_no_match(self, num_cuda_graphs): class TestSpeculativeDecodingBatchDimensions: """Tests for batch dimensions specifically handling speculative decoding.""" - def setup_method(self, method): + @classmethod + def setup_class(cls): Utils.initialize_model_parallel( tensor_model_parallel_size=1, pipeline_model_parallel_size=1, expert_model_parallel_size=Utils.world_size, ) - def teardown_method(self, method): + @classmethod + def teardown_class(cls): Utils.destroy_model_parallel() @staticmethod diff --git a/tests/unit_tests/inference/test_communication_utils.py b/tests/unit_tests/inference/test_communication_utils.py index 95de6c70560..e0c5a9f734d 100644 --- a/tests/unit_tests/inference/test_communication_utils.py +++ b/tests/unit_tests/inference/test_communication_utils.py @@ -1,3 +1,5 @@ +# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + import pytest import torch import torch.distributed as dist @@ -22,6 +24,9 @@ def setup(self): self.size = [16, 8] self.dtype = torch.float32 + def teardown_method(self, method): + Utils.destroy_model_parallel() + @pytest.mark.skipif( not is_torch_min_version("2.4.0"), reason="torch.distributed.init_device_mesh requires torch >= 2.4.0", @@ -65,7 +70,8 @@ def test_broadcast_comparison(self, tp_size, pp_size): assert torch.allclose( tensor_received_global, tensor_received_custom ), "broadcast_from_last_pipeline_stage should be the same with or without custom pp_group" - Utils.destroy_model_parallel() + + grid.destroy() @pytest.mark.skipif( not is_torch_min_version("2.4.0"), @@ -126,4 +132,5 @@ def test_send_recv(self, tp_size, pp_size): assert torch.allclose( local_recv_buffer_global, local_recv_buffer_custom ), "Custom and global recv buffers should be the same." - Utils.destroy_model_parallel() + + grid.destroy() diff --git a/tests/unit_tests/inference/test_moe_inference.py b/tests/unit_tests/inference/test_moe_inference.py index b762b5e638c..209eab3dd83 100644 --- a/tests/unit_tests/inference/test_moe_inference.py +++ b/tests/unit_tests/inference/test_moe_inference.py @@ -10,6 +10,8 @@ - shared experts """ +import gc + import pytest import torch @@ -204,6 +206,10 @@ def teardown_class(cls): SymmetricMemoryManager.destroy() Utils.destroy_model_parallel() + def teardown_method(self, method): + gc.collect() + torch.cuda.empty_cache() + def _make_dispatcher(self, **config_overrides): from megatron.core.transformer.moe.moe_utils import get_default_pg_collection from megatron.core.transformer.moe.token_dispatcher_inference import ( diff --git a/tests/unit_tests/inference/text_generation_controllers/test_mtp_utils.py b/tests/unit_tests/inference/text_generation_controllers/test_mtp_utils.py new file mode 100644 index 00000000000..16d9d901624 --- /dev/null +++ b/tests/unit_tests/inference/text_generation_controllers/test_mtp_utils.py @@ -0,0 +1,694 @@ +# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +"""Unit tests for MTP Triton kernels. + +Each test runs both the pure-PyTorch reference (from mtp_utils_pytorch) and +the Triton kernel (from mtp_utils_triton) on the same inputs, and asserts +that the outputs match exactly. +""" + +import pytest +import torch + +from megatron.core.inference.text_generation_controllers.mtp_utils_pytorch import ( + mamba_state_selective_copy as mamba_state_selective_copy_pytorch, +) +from megatron.core.inference.text_generation_controllers.mtp_utils_pytorch import ( + prepare_next_forward_pass as prepare_next_forward_pass_pytorch, +) +from megatron.core.inference.text_generation_controllers.mtp_utils_pytorch import ( + rewind_kv_cache as rewind_kv_cache_pytorch, +) +from megatron.core.inference.text_generation_controllers.mtp_utils_pytorch import ( + verify_speculative_tokens as verify_speculative_tokens_pytorch, +) +from megatron.core.inference.text_generation_controllers.mtp_utils_triton import ( + mamba_state_selective_copy, + prepare_next_forward_pass, + rewind_kv_cache, + verify_speculative_tokens, +) + +# --------------------------------------------------------------------------- +# Test helpers +# --------------------------------------------------------------------------- + +DEVICE = "cuda" + + +def _clone_tensors(*tensors): + """Return a tuple of cloned tensors (for running reference vs kernel on the same data).""" + return tuple(t.clone() for t in tensors) + + +# --------------------------------------------------------------------------- +# Tests +# --------------------------------------------------------------------------- + + +class TestRewindKvCache: + """Tests for the rewind_kv_cache Triton kernel.""" + + @pytest.mark.parametrize("num_requests", [1, 4, 16]) + @pytest.mark.parametrize("num_speculative_tokens", [1, 2, 4]) + @pytest.mark.parametrize("block_size_tokens", [8, 16, 64]) + def test_basic(self, num_requests, num_speculative_tokens, block_size_tokens): + N = num_requests + max_blocks = 8 + + accepted_counts = torch.randint(0, num_speculative_tokens + 1, (N,), device=DEVICE) + prefill_status = torch.zeros(N, dtype=torch.int32, device=DEVICE) + + last_kv_block_offset = torch.randint(0, block_size_tokens, (N,), device=DEVICE) + kv_length_offsets = torch.randint( + block_size_tokens, block_size_tokens * 4, (N,), device=DEVICE + ) + kv_block_counts = torch.randint(2, max_blocks, (N,), device=DEVICE) + last_kv_block_id = torch.randint(0, 100, (N,), device=DEVICE) + kv_block_ids = torch.randint(0, 100, (N, max_blocks), device=DEVICE) + + ref_offset, ref_kv_len, ref_block_counts, ref_last_block, ref_block_ids = _clone_tensors( + last_kv_block_offset, kv_length_offsets, kv_block_counts, last_kv_block_id, kv_block_ids + ) + tri_offset, tri_kv_len, tri_block_counts, tri_last_block, tri_block_ids = _clone_tensors( + last_kv_block_offset, kv_length_offsets, kv_block_counts, last_kv_block_id, kv_block_ids + ) + + ref_release, ref_mask = rewind_kv_cache_pytorch( + accepted_counts.clone(), + prefill_status.clone(), + ref_offset, + ref_kv_len, + ref_block_counts, + ref_last_block, + ref_block_ids, + num_speculative_tokens, + block_size_tokens, + ) + + tri_release, tri_mask = rewind_kv_cache( + accepted_counts.clone(), + prefill_status.clone(), + tri_offset, + tri_kv_len, + tri_block_counts, + tri_last_block, + tri_block_ids, + num_speculative_tokens, + block_size_tokens, + ) + + torch.testing.assert_close(tri_offset, ref_offset) + torch.testing.assert_close(tri_kv_len, ref_kv_len) + torch.testing.assert_close(tri_block_counts, ref_block_counts) + torch.testing.assert_close(tri_last_block, ref_last_block) + torch.testing.assert_close(tri_block_ids, ref_block_ids) + torch.testing.assert_close(tri_release, ref_release) + torch.testing.assert_close(tri_mask, ref_mask) + + def test_prefill_requests_skip_rewind(self): + N = 4 + num_spec = 3 + block_size = 16 + + accepted_counts = torch.tensor([1, 0, 2, 0], device=DEVICE) + prefill_status = torch.tensor([0, 1, 0, 1], dtype=torch.int32, device=DEVICE) + last_kv_block_offset = torch.tensor([5, 10, 2, 7], device=DEVICE) + kv_length_offsets = torch.tensor([100, 200, 300, 400], device=DEVICE) + kv_block_counts = torch.tensor([3, 4, 2, 5], device=DEVICE) + last_kv_block_id = torch.tensor([10, 20, 30, 40], device=DEVICE) + kv_block_ids = torch.randint(0, 50, (N, 8), device=DEVICE) + + ref_offset, ref_kv_len, ref_block_counts, ref_last_block, ref_block_ids = _clone_tensors( + last_kv_block_offset, kv_length_offsets, kv_block_counts, last_kv_block_id, kv_block_ids + ) + tri_offset, tri_kv_len, tri_block_counts, tri_last_block, tri_block_ids = _clone_tensors( + last_kv_block_offset, kv_length_offsets, kv_block_counts, last_kv_block_id, kv_block_ids + ) + + ref_release, ref_mask = rewind_kv_cache_pytorch( + accepted_counts.clone(), + prefill_status.clone(), + ref_offset, + ref_kv_len, + ref_block_counts, + ref_last_block, + ref_block_ids, + num_spec, + block_size, + ) + tri_release, tri_mask = rewind_kv_cache( + accepted_counts.clone(), + prefill_status.clone(), + tri_offset, + tri_kv_len, + tri_block_counts, + tri_last_block, + tri_block_ids, + num_spec, + block_size, + ) + + # Prefill requests (indices 1, 3) should be unchanged. + for idx in [1, 3]: + assert ref_kv_len[idx] == kv_length_offsets[idx] + assert ref_offset[idx] == last_kv_block_offset[idx] + + torch.testing.assert_close(tri_offset, ref_offset) + torch.testing.assert_close(tri_kv_len, ref_kv_len) + torch.testing.assert_close(tri_block_counts, ref_block_counts) + torch.testing.assert_close(tri_last_block, ref_last_block) + torch.testing.assert_close(tri_block_ids, ref_block_ids) + torch.testing.assert_close(tri_mask, ref_mask) + + def test_block_boundary_crossing(self): + """When offset - rewind < 0, a block boundary is crossed.""" + N = 2 + num_spec = 3 + block_size = 16 + + accepted_counts = torch.tensor([0, 0], device=DEVICE) + prefill_status = torch.zeros(N, dtype=torch.int32, device=DEVICE) + last_kv_block_offset = torch.tensor([1, 10], device=DEVICE) + kv_length_offsets = torch.tensor([100, 200], device=DEVICE) + kv_block_counts = torch.tensor([3, 4], device=DEVICE) + last_kv_block_id = torch.tensor([50, 60], device=DEVICE) + kv_block_ids = torch.tensor( + [[10, 20, 50, -1, -1, -1, -1, -1], [15, 25, 35, 60, -1, -1, -1, -1]], device=DEVICE + ) + + ref_offset, ref_kv_len, ref_block_counts, ref_last_block, ref_block_ids = _clone_tensors( + last_kv_block_offset, kv_length_offsets, kv_block_counts, last_kv_block_id, kv_block_ids + ) + tri_offset, tri_kv_len, tri_block_counts, tri_last_block, tri_block_ids = _clone_tensors( + last_kv_block_offset, kv_length_offsets, kv_block_counts, last_kv_block_id, kv_block_ids + ) + + rewind_kv_cache_pytorch( + accepted_counts.clone(), + prefill_status.clone(), + ref_offset, + ref_kv_len, + ref_block_counts, + ref_last_block, + ref_block_ids, + num_spec, + block_size, + ) + rewind_kv_cache( + accepted_counts.clone(), + prefill_status.clone(), + tri_offset, + tri_kv_len, + tri_block_counts, + tri_last_block, + tri_block_ids, + num_spec, + block_size, + ) + + # Request 0: offset 1 - 3 = -2 → crosses boundary. + assert ref_block_counts[0] == 2 + assert tri_block_counts[0] == 2 + assert ref_last_block[0] == 20 # previous block + assert tri_last_block[0] == 20 + + # Request 1: offset 10 - 3 = 7 → no crossing. + assert ref_block_counts[1] == 4 + assert tri_block_counts[1] == 4 + + torch.testing.assert_close(tri_offset, ref_offset) + torch.testing.assert_close(tri_kv_len, ref_kv_len) + torch.testing.assert_close(tri_block_ids, ref_block_ids) + + def test_padding_programs(self): + """Padding slots (pid >= num_active_requests) must produce safe no-ops.""" + N = 8 # grid size + active = 3 + num_spec = 2 + block_size = 16 + + accepted_counts = torch.randint(0, num_spec + 1, (N,), device=DEVICE) + prefill_status = torch.zeros(N, dtype=torch.int32, device=DEVICE) + last_kv_block_offset = torch.randint(0, block_size, (N,), device=DEVICE) + kv_length_offsets = torch.randint(block_size, block_size * 4, (N,), device=DEVICE) + kv_block_counts = torch.randint(2, 6, (N,), device=DEVICE) + last_kv_block_id = torch.randint(0, 100, (N,), device=DEVICE) + kv_block_ids = torch.randint(0, 100, (N, 8), device=DEVICE) + + ref_offset, ref_kv_len, ref_block_counts, ref_last_block, ref_block_ids = _clone_tensors( + last_kv_block_offset, kv_length_offsets, kv_block_counts, last_kv_block_id, kv_block_ids + ) + tri_offset, tri_kv_len, tri_block_counts, tri_last_block, tri_block_ids = _clone_tensors( + last_kv_block_offset, kv_length_offsets, kv_block_counts, last_kv_block_id, kv_block_ids + ) + + rewind_kv_cache_pytorch( + accepted_counts.clone(), + prefill_status.clone(), + ref_offset, + ref_kv_len, + ref_block_counts, + ref_last_block, + ref_block_ids, + num_spec, + block_size, + num_active_requests=active, + ) + tri_release, tri_mask = rewind_kv_cache( + accepted_counts.clone(), + prefill_status.clone(), + tri_offset, + tri_kv_len, + tri_block_counts, + tri_last_block, + tri_block_ids, + num_spec, + block_size, + num_active_requests=active, + ) + + # Active slots should match. + torch.testing.assert_close(tri_offset[:active], ref_offset[:active]) + torch.testing.assert_close(tri_kv_len[:active], ref_kv_len[:active]) + torch.testing.assert_close(tri_block_counts[:active], ref_block_counts[:active]) + torch.testing.assert_close(tri_last_block[:active], ref_last_block[:active]) + torch.testing.assert_close(tri_block_ids[:active], ref_block_ids[:active]) + + # Padding slots: release=0, mask=False. + assert (tri_release[active:] == 0).all() + assert (~tri_mask[active:]).all() + + def test_empty(self): + N = 0 + blocks_to_release, remove_mask = rewind_kv_cache( + torch.empty(0, device=DEVICE, dtype=torch.int64), + torch.empty(0, device=DEVICE, dtype=torch.int32), + torch.empty(0, device=DEVICE, dtype=torch.int64), + torch.empty(0, device=DEVICE, dtype=torch.int64), + torch.empty(0, device=DEVICE, dtype=torch.int64), + torch.empty(0, device=DEVICE, dtype=torch.int64), + torch.empty(0, 8, device=DEVICE, dtype=torch.int64), + num_speculative_tokens=2, + block_size_tokens=16, + ) + assert blocks_to_release.shape[0] == 0 + assert remove_mask.shape[0] == 0 + + +class TestVerifySpeculativeTokens: + """Tests for the verify_speculative_tokens Triton kernel.""" + + def _make_scenario(self, num_decode, num_prefill, num_spec, *, match_pattern=None): + """Build input/output token tensors for testing. + + Args: + match_pattern: list of ints per decode request indicating how many + speculative tokens should match (0 means only base accepted). + If None, generates random matches. + """ + stride = num_spec + 1 + decode_len = num_decode * stride + total_len = decode_len + num_prefill + + input_tokens = torch.randint(1, 1000, (total_len,), device=DEVICE) + output_tokens = torch.randint(1, 1000, (total_len,), device=DEVICE) + + if match_pattern is not None: + assert len(match_pattern) == num_decode + for req_idx, num_match in enumerate(match_pattern): + base = req_idx * stride + for s in range(num_match): + output_tokens[base + s] = input_tokens[base + s + 1] + + return input_tokens, output_tokens + + @pytest.mark.parametrize( + "num_decode,num_prefill,num_spec", [(1, 0, 2), (3, 0, 2), (3, 2, 2), (0, 3, 2), (5, 3, 4)] + ) + def test_basic(self, num_decode, num_prefill, num_spec): + input_tokens, output_tokens = self._make_scenario(num_decode, num_prefill, num_spec) + + ref_last, ref_mask, ref_input = verify_speculative_tokens_pytorch( + input_tokens.clone(), output_tokens.clone(), num_decode, num_prefill, num_spec + ) + tri_last, tri_mask, tri_input = verify_speculative_tokens( + input_tokens.clone(), output_tokens.clone(), num_decode, num_prefill, num_spec + ) + + torch.testing.assert_close(tri_mask, ref_mask) + torch.testing.assert_close(tri_last, ref_last) + + def test_all_accepted(self): + """All speculative tokens match → all accepted.""" + num_decode, num_prefill, num_spec = 3, 0, 3 + input_tokens, output_tokens = self._make_scenario( + num_decode, num_prefill, num_spec, match_pattern=[3, 3, 3] + ) + + ref_last, ref_mask, _ = verify_speculative_tokens_pytorch( + input_tokens.clone(), output_tokens.clone(), num_decode, num_prefill, num_spec + ) + tri_last, tri_mask, _ = verify_speculative_tokens( + input_tokens.clone(), output_tokens.clone(), num_decode, num_prefill, num_spec + ) + + assert ref_mask.all() + torch.testing.assert_close(tri_mask, ref_mask) + torch.testing.assert_close(tri_last, ref_last) + + def test_none_accepted(self): + """No speculative tokens match → only base tokens accepted.""" + num_decode, num_prefill, num_spec = 3, 0, 3 + input_tokens, output_tokens = self._make_scenario( + num_decode, num_prefill, num_spec, match_pattern=[0, 0, 0] + ) + + ref_last, ref_mask, _ = verify_speculative_tokens_pytorch( + input_tokens.clone(), output_tokens.clone(), num_decode, num_prefill, num_spec + ) + tri_last, tri_mask, _ = verify_speculative_tokens( + input_tokens.clone(), output_tokens.clone(), num_decode, num_prefill, num_spec + ) + + stride = num_spec + 1 + for req in range(num_decode): + base = req * stride + assert ref_mask[base].item() is True + assert not ref_mask[base + 1 : base + stride].any() + + torch.testing.assert_close(tri_mask, ref_mask) + torch.testing.assert_close(tri_last, ref_last) + + def test_mixed_match_pattern(self): + """Different acceptance counts per request.""" + num_decode, num_prefill, num_spec = 3, 1, 3 + input_tokens, output_tokens = self._make_scenario( + num_decode, num_prefill, num_spec, match_pattern=[1, 3, 0] + ) + + ref_last, ref_mask, _ = verify_speculative_tokens_pytorch( + input_tokens.clone(), output_tokens.clone(), num_decode, num_prefill, num_spec + ) + tri_last, tri_mask, _ = verify_speculative_tokens( + input_tokens.clone(), output_tokens.clone(), num_decode, num_prefill, num_spec + ) + + torch.testing.assert_close(tri_mask, ref_mask) + torch.testing.assert_close(tri_last, ref_last) + + def test_2d_input(self): + """Input tokens with shape [1, total_len] should be squeezed.""" + num_decode, num_prefill, num_spec = 2, 1, 2 + input_tokens, output_tokens = self._make_scenario(num_decode, num_prefill, num_spec) + input_2d = input_tokens.unsqueeze(0) + + ref_last, ref_mask, _ = verify_speculative_tokens_pytorch( + input_2d.clone(), output_tokens.clone(), num_decode, num_prefill, num_spec + ) + tri_last, tri_mask, _ = verify_speculative_tokens( + input_2d.clone(), output_tokens.clone(), num_decode, num_prefill, num_spec + ) + + torch.testing.assert_close(tri_mask, ref_mask) + torch.testing.assert_close(tri_last, ref_last) + + +class TestPrepareNextForwardPass: + """Tests for the prepare_next_forward_pass Triton kernel.""" + + def _setup(self, num_decode, num_prefill, num_spec): + stride = num_spec + 1 + active = num_decode + num_prefill + decode_len = num_decode * stride + total_len = decode_len + num_prefill + + output_tokens = torch.randint(1, 1000, (total_len,), device=DEVICE, dtype=torch.int64) + required_logit_indices = torch.arange(total_len, device=DEVICE, dtype=torch.int64) + input_tokens = torch.randint(1, 1000, (total_len,), device=DEVICE, dtype=torch.int64) + + accepted_mask = torch.zeros(total_len, device=DEVICE, dtype=torch.bool) + last_one_indices = torch.empty(active, device=DEVICE, dtype=torch.int64) + + for req in range(num_decode): + base = req * stride + num_match = torch.randint(0, num_spec + 1, (1,)).item() + for j in range(stride): + if j <= num_match: + accepted_mask[base + j] = True + last_one_indices[req] = base + num_match + + for p in range(num_prefill): + idx = decode_len + p + accepted_mask[idx] = True + last_one_indices[num_decode + p] = idx + + return output_tokens, required_logit_indices, input_tokens, accepted_mask, last_one_indices + + @pytest.mark.parametrize( + "num_decode,num_prefill,num_spec", [(1, 0, 2), (3, 0, 2), (3, 2, 2), (0, 3, 2), (5, 3, 4)] + ) + def test_basic(self, num_decode, num_prefill, num_spec): + (output_tokens, required_logit_indices, input_tokens, accepted_mask, last_one_indices) = ( + self._setup(num_decode, num_prefill, num_spec) + ) + + active = num_decode + num_prefill + + ref_sampled = torch.zeros(active, device=DEVICE, dtype=torch.int64) + ref_last_seq = torch.zeros(active, device=DEVICE, dtype=torch.int64) + ref_accepted = torch.full((num_decode, num_spec), -1, device=DEVICE, dtype=torch.int64) + ref_counts = torch.zeros(num_decode, device=DEVICE, dtype=torch.int64) + + tri_sampled = torch.zeros(active, device=DEVICE, dtype=torch.int64) + tri_last_seq = torch.zeros(active, device=DEVICE, dtype=torch.int64) + tri_accepted = torch.full( + (max(num_decode, 1), num_spec), -1, device=DEVICE, dtype=torch.int64 + ) + tri_counts = torch.zeros(max(num_decode, 1), device=DEVICE, dtype=torch.int64) + + prepare_next_forward_pass_pytorch( + num_decode, + output_tokens, + required_logit_indices, + last_one_indices, + accepted_mask, + input_tokens, + ref_sampled, + ref_last_seq, + ref_accepted, + ref_counts, + num_spec, + ) + + prepare_next_forward_pass( + num_decode, + output_tokens, + required_logit_indices, + last_one_indices, + accepted_mask, + input_tokens, + tri_sampled, + tri_last_seq, + tri_accepted, + tri_counts, + num_spec, + ) + + torch.testing.assert_close(tri_sampled, ref_sampled) + torch.testing.assert_close(tri_last_seq, ref_last_seq) + if num_decode > 0: + torch.testing.assert_close(tri_accepted[:num_decode], ref_accepted[:num_decode]) + torch.testing.assert_close(tri_counts[:num_decode], ref_counts[:num_decode]) + + def test_empty(self): + """Zero active requests should be a no-op.""" + last_one_indices = torch.empty(0, device=DEVICE, dtype=torch.int64) + prepare_next_forward_pass( + num_decode_requests=0, + output_tokens=torch.empty(0, device=DEVICE, dtype=torch.int64), + required_logit_indices=torch.empty(0, device=DEVICE, dtype=torch.int64), + last_one_indices=last_one_indices, + accepted_tokens_mask=torch.empty(0, device=DEVICE, dtype=torch.bool), + input_tokens=torch.empty(0, device=DEVICE, dtype=torch.int64), + sampled_tokens_buf=torch.empty(0, device=DEVICE, dtype=torch.int64), + last_accepted_seq_buf=torch.empty(0, device=DEVICE, dtype=torch.int64), + accepted_tokens_per_request=torch.empty(0, 2, device=DEVICE, dtype=torch.int64), + accepted_token_counts=torch.empty(0, device=DEVICE, dtype=torch.int64), + num_speculative_tokens=2, + ) + + +class TestMambaStateSelectiveCopy: + """Tests for the mamba_state_selective_copy Triton kernel.""" + + @pytest.mark.parametrize("num_requests", [1, 4, 8]) + @pytest.mark.parametrize("num_layers", [1, 3]) + def test_basic(self, num_requests, num_layers): + N = num_requests + M = N # 1:1 request-to-slot mapping for simplicity + S = 4 # speculative tokens + 1 + state_shape = (16, 32) # arbitrary state dimensions + + intermediate = torch.randn(num_layers, M, S, *state_shape, device=DEVICE) + current_ref = torch.randn(num_layers, M, *state_shape, device=DEVICE) + current_tri = current_ref.clone() + + prefill_status = torch.zeros(N, dtype=torch.int32, device=DEVICE) + state_idx = torch.arange(N, device=DEVICE, dtype=torch.int64) + accepted_counts = torch.randint(0, S, (N,), device=DEVICE, dtype=torch.int64) + + mamba_state_selective_copy_pytorch( + intermediate, current_ref, prefill_status, state_idx, accepted_counts, num_layers + ) + mamba_state_selective_copy( + intermediate, current_tri, prefill_status, state_idx, accepted_counts, num_layers + ) + + torch.testing.assert_close(current_tri, current_ref) + + def test_prefill_skipped(self): + N = 4 + num_layers = 2 + M = N + S = 3 + state_shape = (8,) + + intermediate = torch.randn(num_layers, M, S, *state_shape, device=DEVICE) + current_ref = torch.randn(num_layers, M, *state_shape, device=DEVICE) + current_tri = current_ref.clone() + current_orig = current_ref.clone() + + prefill_status = torch.tensor([0, 1, 0, 1], dtype=torch.int32, device=DEVICE) + state_idx = torch.arange(N, device=DEVICE, dtype=torch.int64) + accepted_counts = torch.tensor([1, 0, 2, 0], device=DEVICE, dtype=torch.int64) + + mamba_state_selective_copy_pytorch( + intermediate, current_ref, prefill_status, state_idx, accepted_counts, num_layers + ) + mamba_state_selective_copy( + intermediate, current_tri, prefill_status, state_idx, accepted_counts, num_layers + ) + + # Prefill slots should be unchanged from original. + for layer in range(num_layers): + for slot in [1, 3]: + torch.testing.assert_close(current_ref[layer, slot], current_orig[layer, slot]) + torch.testing.assert_close(current_tri[layer, slot], current_orig[layer, slot]) + + torch.testing.assert_close(current_tri, current_ref) + + def test_noncontiguous_state_idx(self): + """state_idx does not have to be a simple arange.""" + N = 3 + num_layers = 2 + M = 6 # more slots than requests + S = 3 + state_shape = (8, 4) + + intermediate = torch.randn(num_layers, M, S, *state_shape, device=DEVICE) + current_ref = torch.randn(num_layers, M, *state_shape, device=DEVICE) + current_tri = current_ref.clone() + + prefill_status = torch.zeros(N, dtype=torch.int32, device=DEVICE) + state_idx = torch.tensor([1, 4, 0], device=DEVICE, dtype=torch.int64) + accepted_counts = torch.tensor([2, 0, 1], device=DEVICE, dtype=torch.int64) + + mamba_state_selective_copy_pytorch( + intermediate, current_ref, prefill_status, state_idx, accepted_counts, num_layers + ) + mamba_state_selective_copy( + intermediate, current_tri, prefill_status, state_idx, accepted_counts, num_layers + ) + + torch.testing.assert_close(current_tri, current_ref) + + def test_empty(self): + """Zero requests should be a no-op.""" + num_layers = 2 + state_shape = (8,) + intermediate = torch.randn(num_layers, 4, 3, *state_shape, device=DEVICE) + current = torch.randn(num_layers, 4, *state_shape, device=DEVICE) + current_before = current.clone() + + mamba_state_selective_copy( + intermediate, + current, + torch.empty(0, dtype=torch.int32, device=DEVICE), + torch.empty(0, dtype=torch.int64, device=DEVICE), + torch.empty(0, dtype=torch.int64, device=DEVICE), + num_layers, + ) + + torch.testing.assert_close(current, current_before) + + +class TestStressRandom: + """Randomized stress tests running all four kernels with varied inputs.""" + + @pytest.mark.parametrize("trial", range(5)) + def test_rewind_random(self, trial): + torch.manual_seed(42 + trial) + N = torch.randint(1, 32, (1,)).item() + num_spec = torch.randint(1, 6, (1,)).item() + block_size = 2 ** torch.randint(3, 7, (1,)).item() + max_blocks = torch.randint(4, 16, (1,)).item() + + accepted_counts = torch.randint(0, num_spec + 1, (N,), device=DEVICE) + prefill_status = (torch.rand(N, device=DEVICE) > 0.7).to(torch.int32) + last_kv_block_offset = torch.randint(0, block_size, (N,), device=DEVICE) + kv_length_offsets = torch.randint(block_size, block_size * 4, (N,), device=DEVICE) + kv_block_counts = torch.randint(2, max_blocks, (N,), device=DEVICE) + last_kv_block_id = torch.randint(0, 200, (N,), device=DEVICE) + kv_block_ids = torch.randint(0, 200, (N, max_blocks), device=DEVICE) + + ref_args = _clone_tensors( + last_kv_block_offset, kv_length_offsets, kv_block_counts, last_kv_block_id, kv_block_ids + ) + tri_args = _clone_tensors( + last_kv_block_offset, kv_length_offsets, kv_block_counts, last_kv_block_id, kv_block_ids + ) + + ref_release, ref_mask = rewind_kv_cache_pytorch( + accepted_counts.clone(), prefill_status.clone(), *ref_args, num_spec, block_size + ) + tri_release, tri_mask = rewind_kv_cache( + accepted_counts.clone(), prefill_status.clone(), *tri_args, num_spec, block_size + ) + + for r, t in zip(ref_args, tri_args): + torch.testing.assert_close(t, r) + torch.testing.assert_close(tri_release, ref_release) + torch.testing.assert_close(tri_mask, ref_mask) + + @pytest.mark.parametrize("trial", range(5)) + def test_verify_random(self, trial): + torch.manual_seed(42 + trial) + num_decode = torch.randint(0, 16, (1,)).item() + num_prefill = torch.randint(0, 8, (1,)).item() + if num_decode == 0 and num_prefill == 0: + num_prefill = 1 + num_spec = torch.randint(1, 6, (1,)).item() + + stride = num_spec + 1 + total_len = num_decode * stride + num_prefill + + input_tokens = torch.randint(1, 500, (total_len,), device=DEVICE) + output_tokens = torch.randint(1, 500, (total_len,), device=DEVICE) + + # Randomly make some speculative tokens match. + for req in range(num_decode): + base = req * stride + num_match = torch.randint(0, num_spec + 1, (1,)).item() + for s in range(num_match): + output_tokens[base + s] = input_tokens[base + s + 1] + + ref_last, ref_mask, _ = verify_speculative_tokens_pytorch( + input_tokens.clone(), output_tokens.clone(), num_decode, num_prefill, num_spec + ) + tri_last, tri_mask, _ = verify_speculative_tokens( + input_tokens.clone(), output_tokens.clone(), num_decode, num_prefill, num_spec + ) + + torch.testing.assert_close(tri_mask, ref_mask) + torch.testing.assert_close(tri_last, ref_last) diff --git a/tests/unit_tests/inference/text_generation_controllers/test_text_generation_controller.py b/tests/unit_tests/inference/text_generation_controllers/test_text_generation_controller.py index 9c6564f6989..dd4764ee92d 100644 --- a/tests/unit_tests/inference/text_generation_controllers/test_text_generation_controller.py +++ b/tests/unit_tests/inference/text_generation_controllers/test_text_generation_controller.py @@ -14,7 +14,7 @@ from transformer_engine.pytorch.fp8 import check_fp8_support from megatron.core import parallel_state -from megatron.core.inference.config import InferenceConfig +from megatron.core.inference.config import InferenceConfig, MambaInferenceStateConfig from megatron.core.inference.contexts import DynamicInferenceContext, StaticInferenceContext from megatron.core.inference.contexts.dynamic_context import MaxSequenceLengthOverflowError from megatron.core.inference.inference_request import ( @@ -34,6 +34,8 @@ get_gpt_mtp_block_spec, ) from megatron.core.models.gpt.gpt_model import GPTModel +from megatron.core.models.hybrid.hybrid_layer_specs import hybrid_stack_spec +from megatron.core.models.hybrid.hybrid_model import HybridModel from megatron.core.tensor_parallel.random import model_parallel_cuda_manual_seed from megatron.core.transformer.enums import AttnBackend from megatron.core.transformer.module import Float16Module @@ -43,7 +45,7 @@ from tests.unit_tests.test_utilities import Utils -class TestTextGenerationController: +class TextGenerationControllerTestBase: def setup_model( self, @@ -64,11 +66,8 @@ def setup_model( sequence_parallel: bool = False, expert_model_parallel_size: int = 1, num_moe_experts: int = None, + hybrid_layer_pattern: str = None, ): - Utils.initialize_model_parallel( - tensor_model_parallel_size=tensor_model_parallel_size, - pipeline_model_parallel_size=pipeline_model_parallel_size, - ) if use_training_random_init: # This is necessary to induce the training behavior which permutes the random seed # for every rank; otherwise, every rank will have the same seed. @@ -98,31 +97,51 @@ def setup_model( expert_model_parallel_size=expert_model_parallel_size, num_moe_experts=num_moe_experts, add_bias_linear=num_moe_experts is None, + **( + dict(is_hybrid_model=True, mamba_num_heads=2, mamba_head_dim=16, mamba_num_groups=2) + if hybrid_layer_pattern + else {} + ), ) if dtype == torch.bfloat16: transformer_config.bf16 = True - layer_spec = get_gpt_layer_local_spec() + mamba_inference_state_config = None + if hybrid_layer_pattern: + model = HybridModel( + config=transformer_config, + hybrid_stack_spec=hybrid_stack_spec, + vocab_size=self.vocab_size, + max_sequence_length=self.sequence_length, + parallel_output=True, + hybrid_layer_pattern=hybrid_layer_pattern, + pre_process=parallel_state.is_pipeline_first_stage(), + post_process=parallel_state.is_pipeline_last_stage(), + ).cuda() + mamba_inference_state_config = MambaInferenceStateConfig.from_model(model) + else: + layer_spec = get_gpt_layer_local_spec() + + mtp_block_spec = None + if mtp_num_layers > 0: + mtp_block_spec = get_gpt_mtp_block_spec( + config=transformer_config, spec=layer_spec, use_transformer_engine=False + ) - mtp_block_spec = None - if mtp_num_layers > 0: - mtp_block_spec = get_gpt_mtp_block_spec( - config=transformer_config, spec=layer_spec, use_transformer_engine=False - ) + model = GPTModel( + config=transformer_config, + transformer_layer_spec=layer_spec, + vocab_size=self.vocab_size, + max_sequence_length=self.sequence_length, + parallel_output=True, + pre_process=parallel_state.is_pipeline_first_stage(), + post_process=parallel_state.is_pipeline_last_stage(), + mtp_block_spec=mtp_block_spec, + ).cuda() - gpt_model = GPTModel( - config=transformer_config, - transformer_layer_spec=layer_spec, - vocab_size=self.vocab_size, - max_sequence_length=self.sequence_length, - parallel_output=True, - pre_process=parallel_state.is_pipeline_first_stage(), - post_process=parallel_state.is_pipeline_last_stage(), - mtp_block_spec=mtp_block_spec, - ).cuda() - gpt_model.eval() + model.eval() if dtype == torch.bfloat16: - gpt_model = Float16Module(gpt_model.config, gpt_model) + model = Float16Module(model.config, model) if static: inference_context = StaticInferenceContext( @@ -142,10 +161,11 @@ def setup_model( block_size_tokens=block_size_tokens, enable_prefix_caching=enable_prefix_caching, max_requests=max_requests, + mamba_inference_state_config=mamba_inference_state_config, ), ) - inference_wrapped_model = GPTInferenceWrapper(gpt_model, inference_context) + inference_wrapped_model = GPTInferenceWrapper(model, inference_context) inference_wrapped_model.model_is_pipeline_parallel = not ( parallel_state.is_pipeline_first_stage() and parallel_state.is_pipeline_last_stage() @@ -157,6 +177,15 @@ def setup_model( inference_wrapped_model=inference_wrapped_model, tokenizer=self.mock_tokenizer ) + +class TestTextGenerationController(TextGenerationControllerTestBase): + + @classmethod + def setup_class(cls): + Utils.initialize_model_parallel( + tensor_model_parallel_size=2, pipeline_model_parallel_size=1 + ) + @classmethod def teardown_class(cls): Utils.destroy_model_parallel() @@ -931,164 +960,12 @@ def test_dynamic_top_n_logprobs_calculation( top_n_indices.shape[0] == top_n ), f"Request {req_idx}, token {token_idx}: expected {top_n} indices" - @pytest.mark.parametrize("static", [True, False]) - @pytest.mark.parametrize("tp_size", [1, 2]) - @pytest.mark.parametrize("pp_size", [1, 2]) - def test_sampled_tokens_match_with_parallelism(self, static, tp_size, pp_size): - """ - Verify that sampled tokens match across all parallel ranks. - """ - if tp_size == 1 and pp_size == 1: - pytest.skip(reason="Test requires model parallel size > 1.") - - if not static and not is_fa_min_version("2.7.3"): - pytest.skip(reason="Need latest flash attn for dynamic batching") - - # Ensure that we are using the training setup for random seed initialization - # so that every rank has a different seed - self.setup_model( - dtype=torch.bfloat16, - tensor_model_parallel_size=tp_size, - pipeline_model_parallel_size=pp_size, - static=static, - use_training_random_init=True, - ) - - self.mock_tokenizer.vocab_size = self.vocab_size - self.mock_tokenizer.eod = self.vocab_size - 1 - self.mock_tokenizer.detokenize.side_effect = lambda x, skip_special_tokens=False: ' '.join( - [ - ''.join(random.choices(string.ascii_letters, k=random.randint(4, 10))) - for _ in range(len(x)) - ] - ) - self.mock_tokenizer.offsets.side_effect = lambda _, s: [ - i for i, c in enumerate(s) if c == ' ' - ] + [len(s)] - - # Prepare requests. - active_requests: Dict[str, InferenceRequest] = OrderedDict() - for i in range(self.batch_size): - prompt = "sample" * (i + 1) - prompt_tokens = torch.randint( - low=0, high=self.vocab_size - 1, size=(len(prompt),) - ).tolist() - request_id = str(i) - inference_request = InferenceRequest( - request_id=request_id, - prompt=prompt, - sampling_params=SamplingParams( - top_k=10, num_tokens_to_generate=25, return_log_probs=True - ), - arrival_time=time.time(), - prompt_tokens=prompt_tokens, - status=Status.ACTIVE_BUT_NOT_GENERATING_TOKENS, - ) - active_requests[request_id] = inference_request - - # Generate tokens. - if static: - requests = self.text_generation_controller.generate_all_output_tokens_static_batch( - active_requests - ) - all_generated_tokens = [req.generated_tokens.tolist() for req in requests.values()] - else: - all_generated_tokens = [[] for _ in range(len(active_requests))] - context = self.text_generation_controller.inference_wrapped_model.inference_context - for request_id, request in active_requests.items(): - context.add_request( - DynamicInferenceRequest( - request_id=int(request_id), - prompt_tokens=torch.tensor( - request.prompt_tokens, - dtype=torch.long, - device=torch.cuda.current_device(), - ), - sampling_params=SamplingParams( - top_k=10, return_log_probs=True, num_tokens_to_generate=25 - ), - ) - ) - expected_active_requests = set(int(x) for x in active_requests.keys()) - while context.has_unfinished_requests(): - result = self.text_generation_controller.generate_output_tokens_dynamic_batch() - new_tokens = result["sample"] - active_ids = result["active_request_ids"].tolist() - finished_ids = result["finished_request_ids"].tolist() - assert len(new_tokens) == len(expected_active_requests) - assert set(active_ids) == expected_active_requests - expected_active_requests -= set(finished_ids) - for i, token in enumerate(new_tokens.tolist()): - all_generated_tokens[i].append(token) - - # Wait for all communication to complete before proceeding. - torch.distributed.barrier() - - # Collect all the generated tokens for each request from each rank in the - # model parallel group. - mp_group = parallel_state.get_model_parallel_group() - mp_ranks = torch.distributed.get_process_group_ranks(mp_group) - local_rank = torch.distributed.get_rank() - tokens_per_rank = {} - tokens_per_rank[local_rank] = all_generated_tokens - - for i in mp_ranks: - # Start by communicating the batch size so each rank knows how many requests to expect. - if i == local_rank: - batch_size = torch.tensor( - len(tokens_per_rank[local_rank]), - dtype=torch.long, - device=torch.cuda.current_device(), - ) - else: - tokens_per_rank[i] = [] - batch_size = torch.empty(1, dtype=torch.long, device=torch.cuda.current_device()) - torch.distributed.broadcast(batch_size, group=mp_group, src=i) - - for j in range(batch_size.item()): - # For each request, communicate the sequence length followed by the actual tokens. - if i == local_rank: - sequence_length = torch.tensor( - len(tokens_per_rank[local_rank][j]), - dtype=torch.int32, - device=torch.cuda.current_device(), - ) - else: - sequence_length = torch.empty( - 1, dtype=torch.int32, device=torch.cuda.current_device() - ) - torch.distributed.broadcast(sequence_length, group=mp_group, src=i) - - if i == local_rank: - generated_tokens = torch.tensor( - tokens_per_rank[local_rank][j], - dtype=torch.long, - device=torch.cuda.current_device(), - ) - else: - generated_tokens = torch.empty( - sequence_length.item(), dtype=torch.long, device=torch.cuda.current_device() - ) - torch.distributed.broadcast(generated_tokens, group=mp_group, src=i) - - if i != local_rank: - tokens_per_rank[i].append(generated_tokens.tolist()) - - # Ensure that every rank in the model parallel group produced the same tokens. - for i in mp_ranks: - if i == local_rank: - continue - for j, (expected, actual) in enumerate( - zip(tokens_per_rank[local_rank], tokens_per_rank[i]) - ): - assert ( - expected == actual - ), f"Rank {i} tokens differ from rank {local_rank} tokens for request {j}" - @pytest.mark.internal def test_speculative_verify_tokens(self): """Test consecutive token acceptance logic for speculative decoding.""" - self.setup_model(torch.float32, static=False, num_speculative_tokens=2, max_requests=2) + self.setup_model( + torch.float32, static=False, num_speculative_tokens=2, max_requests=2, mtp_num_layers=2 + ) # Enable speculative decoding self.text_generation_controller.num_speculative_tokens = 2 @@ -1103,7 +980,7 @@ def test_speculative_verify_tokens(self): ) # 1 sampled + 2 spec # Init accepted tokens tensors - self.text_generation_controller._init_mtp_sampling_tensor() + self.text_generation_controller._init_mtp_sampling_tensors() # Mock inputs: [Req 1 sampled, Req 1 spec1, Req 1 spec2, Req 2 sampled, Req 2 spec1, Req 2 spec2] # Target tokens (what the model was fed): [T0, T1, T2, T3, T4, T5] @@ -1124,6 +1001,9 @@ def mock_sampling_func(logits, *args, **kwargs): # Override sampling to return our predictable mock outputs self.text_generation_controller._torch_sampling_buckets = [([0, 1], 1.0, 1, 0.0)] + self.text_generation_controller._torch_sampling_bucket_index_tensors = [ + torch.tensor([0, 1], device='cuda', dtype=torch.long) + ] self.text_generation_controller._torch_sampling_func = mock.MagicMock( side_effect=mock_sampling_func ) @@ -1156,6 +1036,7 @@ def test_rewind_kv_cache(self, is_hybrid_model): num_speculative_tokens=3, block_size_tokens=4, max_requests=16, + hybrid_layer_pattern="***M" if is_hybrid_model else None, ) self.text_generation_controller.num_speculative_tokens = 3 ctx = self.text_generation_controller.inference_wrapped_model.inference_context @@ -1177,21 +1058,22 @@ def test_rewind_kv_cache(self, is_hybrid_model): ) if is_hybrid_model: - ctx.is_hybrid_model = True - ctx.mamba_metadata = mock.MagicMock() - ctx.mamba_metadata.request_to_mamba_state_idx = torch.tensor([0, 1], device='cuda') - ctx.mamba_ssm_states = torch.zeros((1, 2, 16), device='cuda') - ctx.mamba_intermediate_ssm_states = torch.ones((1, 2, 4, 16), device='cuda') * 99 - ctx.mamba_conv_states = torch.zeros((1, 2, 8), device='cuda') - ctx.mamba_intermediate_conv_states = torch.ones((1, 2, 4, 8), device='cuda') * 77 + ctx.mamba_metadata.request_to_mamba_state_idx[:2] = torch.tensor( + [0, 1], dtype=torch.int32, device='cuda' + ) + ctx.mamba_ssm_states.zero_() + ctx.mamba_intermediate_ssm_states.fill_(99) + ctx.mamba_conv_states.zero_() + ctx.mamba_intermediate_conv_states.fill_(77) # Mock accepted token counts: Req 0 accepts 1 (rejects 2), Req 1 accepts 0 (rejects 3) - self.text_generation_controller._init_mtp_sampling_tensor() + self.text_generation_controller._init_mtp_sampling_tensors() self.text_generation_controller._accepted_token_counts_per_request = torch.tensor( [1, 0], device='cuda' ) - self.text_generation_controller._rewind_kv_cache() + blocks_to_release, remove_mask = self.text_generation_controller._rewind_kv_cache() + ctx.kv_block_allocator.release_memory_blocks(blocks_to_release[remove_mask]) # Assert offsets updated assert torch.equal( @@ -1221,6 +1103,108 @@ def test_rewind_kv_cache(self, is_hybrid_model): assert torch.all(ctx.mamba_conv_states[:, 0] == 77) # Req 0 accepted 1, loaded index 1 assert torch.all(ctx.mamba_conv_states[:, 1] == 77) # Req 1 accepted 0, loaded index 0 + @pytest.mark.internal + def test_rewind_kv_cache_stale_padding_is_safe(self): + """Padding slots with stale data must not corrupt active requests or + release junk blocks when the rewind kernel grid is padded beyond the + active request count. + + Without the num_active_requests guard in the kernel, padding slots + whose stale request_last_kv_block_offset < num_speculative_tokens + would produce remove_mask=True, causing the block allocator to free + block IDs that belong to other active requests. + """ + from megatron.core.inference.text_generation_controllers.mtp_utils_triton import ( + rewind_kv_cache, + ) + + num_spec = 3 + block_size = 4 + active = 2 + padded = 4 + max_blocks = 10 + dev = 'cuda' + + # --- Active requests (slots 0-1): identical to test_rewind_kv_cache --- + # Req 0: accepted 1, last_offset 2 → rewind 2 → offset 0, no release + # Req 1: accepted 0, last_offset 1 → rewind 3 → crosses block, release block 60 + accepted = torch.zeros(padded, device=dev, dtype=torch.int64) + accepted[0] = 1 + accepted[1] = 0 + + prefill = torch.zeros(padded, device=dev, dtype=torch.int64) + + last_offset = torch.zeros(padded, device=dev, dtype=torch.int64) + last_offset[0] = 2 + last_offset[1] = 1 + + kv_length = torch.zeros(padded, device=dev, dtype=torch.int64) + kv_length[0] = 10 + kv_length[1] = 15 + + block_counts = torch.zeros(padded, device=dev, dtype=torch.int64) + block_counts[0] = 3 + block_counts[1] = 4 + + last_block_id = torch.zeros(padded, device=dev, dtype=torch.int64) + last_block_id[0] = 50 + last_block_id[1] = 60 + + block_ids = torch.full((padded, max_blocks), -1, device=dev, dtype=torch.int64) + block_ids[0, :3] = torch.tensor([48, 49, 50]) + block_ids[1, :4] = torch.tensor([57, 58, 59, 60]) + + # --- Padding slots (2-3): stale data from completed requests --- + # Crucially, last_offset values < num_spec would trigger remove=True + # without the kernel guard, releasing stale block IDs. + last_offset[2] = 1 + last_offset[3] = 2 + kv_length[2] = 9999 + kv_length[3] = 9999 + block_counts[2] = 5 + block_counts[3] = 7 + last_block_id[2] = 777 + last_block_id[3] = 888 + block_ids[2, :5] = torch.arange(100, 105, device=dev) + block_ids[3, :5] = torch.arange(200, 205, device=dev) + + blocks_to_release, remove_mask = rewind_kv_cache( + accepted_counts=accepted, + prefill_status=prefill, + last_kv_block_offset=last_offset, + kv_length_offsets=kv_length, + kv_block_counts=block_counts, + last_kv_block_id=last_block_id, + kv_block_ids=block_ids, + num_speculative_tokens=num_spec, + block_size_tokens=block_size, + num_active_requests=active, + ) + + # --- Active request 0: rewind 2, no block release --- + assert remove_mask[0].item() is False + assert last_offset[0].item() == 0 + assert kv_length[0].item() == 8 + assert block_counts[0].item() == 3 + assert last_block_id[0].item() == 50 + + # --- Active request 1: rewind 3, crosses block boundary --- + assert remove_mask[1].item() is True + assert last_offset[1].item() == 2 # (1 - 3) % 4 = 2 + assert kv_length[1].item() == 12 + assert block_counts[1].item() == 3 + assert last_block_id[1].item() == 59 + assert blocks_to_release[1].item() == 60 + + # --- Padding slots 2-3: must be no-ops, no blocks released --- + assert remove_mask[2].item() is False + assert remove_mask[3].item() is False + # Stale state must be untouched (kernel skipped these programs). + assert kv_length[2].item() == 9999 + assert kv_length[3].item() == 9999 + assert block_counts[2].item() == 5 + assert block_counts[3].item() == 7 + @pytest.mark.internal def test_speculative_multinomial_sampling(self): """Test that speculative decoding can successfully use non-greedy sampling @@ -1251,6 +1235,9 @@ def test_speculative_multinomial_sampling(self): # Set up a bucket that forces multinomial sampling (top_p = 0.9, top_k = 0) # _torch_sampling_buckets format: (indices, temp, top_k, top_p) self.text_generation_controller._torch_sampling_buckets = [([0, 1], 1.0, 0, 0.9)] + self.text_generation_controller._torch_sampling_bucket_index_tensors = [ + torch.tensor([0, 1], device='cuda', dtype=torch.long) + ] # Since we are actually testing the internal math of `_torch_sampling_func` handling the shapes, # we DO NOT mock `_torch_sampling_func` here. We want it to run natively to prove it doesn't crash. @@ -1310,12 +1297,13 @@ def test_rewind_kv_cache_with_prefix_caching_ref_counts(self): initial_avail = ctx.kv_block_allocator.total_avail # Req 0 accepts 1 (rewinds 1), Req 1 accepts 0 (rewinds 2, crosses boundary). - self.text_generation_controller._init_mtp_sampling_tensor() + self.text_generation_controller._init_mtp_sampling_tensors() self.text_generation_controller._accepted_token_counts_per_request = torch.tensor( [1, 0], device='cuda' ) - self.text_generation_controller._rewind_kv_cache() + blocks_to_release, remove_mask = self.text_generation_controller._rewind_kv_cache() + ctx.kv_block_allocator.release_memory_blocks(blocks_to_release[remove_mask]) # Req 1 should have released block 20 (ref count decremented). assert ctx.kv_block_allocator.block_ref_counts[20].item() == 1 @@ -1350,12 +1338,13 @@ def test_rewind_kv_cache_does_not_release_shared_prefix_blocks(self): # Blocks 10, 20 are shared prefix blocks. Block 30, 40 are exclusive. ctx.kv_block_allocator.total_avail = 50 - self.text_generation_controller._init_mtp_sampling_tensor() + self.text_generation_controller._init_mtp_sampling_tensors() self.text_generation_controller._accepted_token_counts_per_request = torch.tensor( [0], device='cuda' ) - self.text_generation_controller._rewind_kv_cache() + blocks_to_release, remove_mask = self.text_generation_controller._rewind_kv_cache() + ctx.kv_block_allocator.release_memory_blocks(blocks_to_release[remove_mask]) # Only block 40 should be released, not blocks 10, 20, or 30. assert ctx.request_kv_block_counts[0].item() == 3 @@ -1372,7 +1361,9 @@ def test_rewind_kv_cache_does_not_release_shared_prefix_blocks(self): def test_speculative_mtp_position_ids_with_prefill(self): """Test that _compute_serial_mtp_and_sample uses the correct position IDs for a mixed batch of prefill and decode requests.""" - self.setup_model(torch.float32, static=False, num_speculative_tokens=2, max_requests=2) + self.setup_model( + torch.float32, static=False, num_speculative_tokens=2, max_requests=2, mtp_num_layers=2 + ) self.text_generation_controller.num_speculative_tokens = 2 self.text_generation_controller.num_mtp_heads = 2 @@ -1388,7 +1379,7 @@ def test_speculative_mtp_position_ids_with_prefill(self): ctx.request_kv_length_offsets[:2] = torch.tensor([10, 0], dtype=torch.int32, device='cuda') ctx.request_query_lengths[:2] = torch.tensor([3, 15], dtype=torch.int32, device='cuda') - self.text_generation_controller._init_mtp_sampling_tensor() + self.text_generation_controller._init_mtp_sampling_tensors() # Mock base token sampling (the first tokens fed into MTP) self.text_generation_controller._sampled_tokens_cuda[:2] = torch.tensor( [100, 200], device='cuda' @@ -1403,7 +1394,7 @@ def test_speculative_mtp_position_ids_with_prefill(self): captured_position_ids = [] - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth): + def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): captured_position_ids.append(position_ids.clone()) return hidden_states, torch.randn(2, 1, self.vocab_size, device='cuda') @@ -1463,7 +1454,7 @@ def test_mtp_sp_padding_real_ranks(self, active_request_count): active_request_count, dtype=torch.int32, device='cuda' ) - ctrl._init_mtp_sampling_tensor() + ctrl._init_mtp_sampling_tensors() ctrl._sampled_tokens_cuda[:active_request_count] = torch.remainder( torch.arange(active_request_count, device='cuda'), self.vocab_size ) @@ -1488,6 +1479,9 @@ def test_mtp_sp_padding_real_ranks(self, active_request_count): # Greedy sampling: top_k=1 selects the argmax token deterministically. ctrl._torch_sampling_buckets = [(list(range(active_request_count)), 1.0, 1, 0.0)] + ctrl._torch_sampling_bucket_index_tensors = [ + torch.arange(active_request_count, device='cuda', dtype=torch.long) + ] # Run the MTP forward pass ctrl._compute_serial_mtp_and_sample() @@ -1539,10 +1533,7 @@ def test_mtp_sp_padding_dummy_ranks(self): dummy_positions = torch.zeros((1, tp_size), device='cuda', dtype=torch.long) hidden_out, logits_out = unwrapped_model.compute_mtp_single_step( - hidden_states=dummy_hidden, - next_token_ids=dummy_tokens, - position_ids=dummy_positions, - depth=0, + hidden_states=dummy_hidden, next_token_ids=dummy_tokens, position_ids=dummy_positions ) # Hidden output is in SP format: [padded_count/tp_size, 1, H] = [1, 1, H]. @@ -1585,7 +1576,6 @@ def test_mtp_sp_dummy_hidden_uses_full_seq_len(self): hidden_states=current_hidden, next_token_ids=dummy_tokens, position_ids=dummy_positions, - depth=depth, ) # Hidden stays in SP format across all depths. @@ -1598,3 +1588,209 @@ def test_mtp_sp_dummy_hidden_uses_full_seq_len(self): f"Depth {depth}: expected logits shape ({tp_size}, 1, {self.vocab_size}), " f"got {logits.shape}" ) + + +class TestTextGenerationControllerParallel(TextGenerationControllerTestBase): + """Tests that require non-default parallel configs (varying tp/pp). + + Each test initializes its own parallel state and tears it down afterward, + so these are separated from TestTextGenerationController to avoid + accumulating NCCL communicator memory from repeated init/destroy cycles. + """ + + def teardown_method(self, method): + Utils.destroy_model_parallel() + + def setup_model( + self, + dtype, + symmetric_ar_type=None, + fp8: bool = False, + tensor_model_parallel_size: int = 2, + pipeline_model_parallel_size: int = 1, + batch_size: int = 4, + static: bool = True, + use_training_random_init: bool = False, + materialize_only_last_token_logits: bool = False, + num_speculative_tokens: int = 0, + block_size_tokens: int = 256, + enable_prefix_caching: bool = False, + max_requests: int = None, + mtp_num_layers: int = 0, + sequence_parallel: bool = False, + expert_model_parallel_size: int = 1, + num_moe_experts: int = None, + hybrid_layer_pattern: str = None, + ): + Utils.initialize_model_parallel( + tensor_model_parallel_size=tensor_model_parallel_size, + pipeline_model_parallel_size=pipeline_model_parallel_size, + ) + super().setup_model( + dtype, + symmetric_ar_type=symmetric_ar_type, + fp8=fp8, + tensor_model_parallel_size=tensor_model_parallel_size, + pipeline_model_parallel_size=pipeline_model_parallel_size, + batch_size=batch_size, + static=static, + use_training_random_init=use_training_random_init, + materialize_only_last_token_logits=materialize_only_last_token_logits, + num_speculative_tokens=num_speculative_tokens, + block_size_tokens=block_size_tokens, + enable_prefix_caching=enable_prefix_caching, + max_requests=max_requests, + mtp_num_layers=mtp_num_layers, + sequence_parallel=sequence_parallel, + expert_model_parallel_size=expert_model_parallel_size, + num_moe_experts=num_moe_experts, + hybrid_layer_pattern=hybrid_layer_pattern, + ) + + @pytest.mark.parametrize("static", [True, False]) + @pytest.mark.parametrize("tp_size", [1, 2]) + @pytest.mark.parametrize("pp_size", [1, 2]) + def test_sampled_tokens_match_with_parallelism(self, static, tp_size, pp_size): + """Verify that sampled tokens match across all parallel ranks.""" + if tp_size == 1 and pp_size == 1: + pytest.skip(reason="Test requires model parallel size > 1.") + + if not static and not is_fa_min_version("2.7.3"): + pytest.skip(reason="Need latest flash attn for dynamic batching") + + self.setup_model( + dtype=torch.bfloat16, + tensor_model_parallel_size=tp_size, + pipeline_model_parallel_size=pp_size, + static=static, + use_training_random_init=True, + ) + + self.mock_tokenizer.vocab_size = self.vocab_size + self.mock_tokenizer.eod = self.vocab_size - 1 + self.mock_tokenizer.detokenize.side_effect = lambda x, skip_special_tokens=False: ' '.join( + [ + ''.join(random.choices(string.ascii_letters, k=random.randint(4, 10))) + for _ in range(len(x)) + ] + ) + self.mock_tokenizer.offsets.side_effect = lambda _, s: [ + i for i, c in enumerate(s) if c == ' ' + ] + [len(s)] + + # Prepare requests. + active_requests: Dict[str, InferenceRequest] = OrderedDict() + for i in range(self.batch_size): + prompt = "sample" * (i + 1) + prompt_tokens = torch.randint( + low=0, high=self.vocab_size - 1, size=(len(prompt),) + ).tolist() + request_id = str(i) + inference_request = InferenceRequest( + request_id=request_id, + prompt=prompt, + sampling_params=SamplingParams( + top_k=10, num_tokens_to_generate=25, return_log_probs=True + ), + arrival_time=time.time(), + prompt_tokens=prompt_tokens, + status=Status.ACTIVE_BUT_NOT_GENERATING_TOKENS, + ) + active_requests[request_id] = inference_request + + # Generate tokens. + if static: + requests = self.text_generation_controller.generate_all_output_tokens_static_batch( + active_requests + ) + all_generated_tokens = [req.generated_tokens.tolist() for req in requests.values()] + else: + all_generated_tokens = [[] for _ in range(len(active_requests))] + context = self.text_generation_controller.inference_wrapped_model.inference_context + for request_id, request in active_requests.items(): + context.add_request( + DynamicInferenceRequest( + request_id=int(request_id), + prompt_tokens=torch.tensor( + request.prompt_tokens, + dtype=torch.long, + device=torch.cuda.current_device(), + ), + sampling_params=SamplingParams( + top_k=10, return_log_probs=True, num_tokens_to_generate=25 + ), + ) + ) + expected_active_requests = set(int(x) for x in active_requests.keys()) + while context.has_unfinished_requests(): + result = self.text_generation_controller.generate_output_tokens_dynamic_batch() + new_tokens = result["sample"] + active_ids = result["active_request_ids"].tolist() + finished_ids = result["finished_request_ids"].tolist() + assert len(new_tokens) == len(expected_active_requests) + assert set(active_ids) == expected_active_requests + expected_active_requests -= set(finished_ids) + for i, token in enumerate(new_tokens.tolist()): + all_generated_tokens[i].append(token) + + # Wait for all communication to complete before proceeding. + torch.distributed.barrier() + + # Collect all the generated tokens for each request from each rank in the + # model parallel group. + mp_group = parallel_state.get_model_parallel_group() + mp_ranks = torch.distributed.get_process_group_ranks(mp_group) + local_rank = torch.distributed.get_rank() + tokens_per_rank = {} + tokens_per_rank[local_rank] = all_generated_tokens + + for i in mp_ranks: + if i == local_rank: + batch_size = torch.tensor( + len(tokens_per_rank[local_rank]), + dtype=torch.long, + device=torch.cuda.current_device(), + ) + else: + tokens_per_rank[i] = [] + batch_size = torch.empty(1, dtype=torch.long, device=torch.cuda.current_device()) + torch.distributed.broadcast(batch_size, group=mp_group, src=i) + + for j in range(batch_size.item()): + if i == local_rank: + sequence_length = torch.tensor( + len(tokens_per_rank[local_rank][j]), + dtype=torch.int32, + device=torch.cuda.current_device(), + ) + else: + sequence_length = torch.empty( + 1, dtype=torch.int32, device=torch.cuda.current_device() + ) + torch.distributed.broadcast(sequence_length, group=mp_group, src=i) + + if i == local_rank: + generated_tokens = torch.tensor( + tokens_per_rank[local_rank][j], + dtype=torch.long, + device=torch.cuda.current_device(), + ) + else: + generated_tokens = torch.empty( + sequence_length.item(), dtype=torch.long, device=torch.cuda.current_device() + ) + torch.distributed.broadcast(generated_tokens, group=mp_group, src=i) + + if i != local_rank: + tokens_per_rank[i].append(generated_tokens.tolist()) + + # Ensure that every rank in the model parallel group produced the same tokens. + for i in mp_ranks: + if i == local_rank: + continue + for j, (expected, actual) in enumerate( + zip(tokens_per_rank[local_rank], tokens_per_rank[i]) + ): + assert ( + expected == actual + ), f"Rank {i} tokens differ from rank {local_rank} tokens for request {j}" diff --git a/tests/unit_tests/test_utilities.py b/tests/unit_tests/test_utilities.py index f8fad3325f5..0ddfef4dc67 100644 --- a/tests/unit_tests/test_utilities.py +++ b/tests/unit_tests/test_utilities.py @@ -26,6 +26,13 @@ def __init__( self.layers[-1].weight.shared_embedding = True +def clear_nvte_env_vars(): + """Clear NVTE env vars set by conftest set_env fixture.""" + os.environ.pop('NVTE_FLASH_ATTN', None) + os.environ.pop('NVTE_FUSED_ATTN', None) + os.environ.pop('NVTE_UNFUSED_ATTN', None) + + class Utils: world_size = int(os.environ.get('WORLD_SIZE', '1')) From 1879dc2c6d271684c6118f2f5939001283c9b5e7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 27 Apr 2026 11:13:14 +0000 Subject: [PATCH 031/105] =?UTF-8?q?chore(beep=20boop=20=F0=9F=A4=96):=20Bu?= =?UTF-8?q?mp=20=20(main)=20(2026-04-27)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- uv.lock | 824 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 427 insertions(+), 397 deletions(-) diff --git a/uv.lock b/uv.lock index f69541db539..16d960dcc2b 100644 --- a/uv.lock +++ b/uv.lock @@ -78,7 +78,7 @@ dependencies = [ { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "psutil" }, { name = "pyyaml" }, { name = "safetensors" }, @@ -103,7 +103,7 @@ wheels = [ [[package]] name = "aiobotocore" -version = "3.4.0" +version = "3.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -114,9 +114,9 @@ dependencies = [ { name = "python-dateutil" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b8/50/a48ed11b15f926ce3dbb33e7fb0f25af17dbb99bcb7ae3b30c763723eca7/aiobotocore-3.4.0.tar.gz", hash = "sha256:a918b5cb903f81feba7e26835aed4b5e6bb2d0149d7f42bb2dd7d8089e3d9000", size = 122360, upload-time = "2026-04-07T06:12:24.884Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/89/9533b377e9412013cc43a539d81bc5f8feeb4b6830643821ad612f78b09b/aiobotocore-3.5.0.tar.gz", hash = "sha256:d45d1c4659ad0e48b694a5aa4ff18829100386f7de96c8d146ec7757a6f12918", size = 123061, upload-time = "2026-04-21T07:25:26.993Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/df/d8/ce9386e6d76ea79e61dee15e62aa48cff6be69e89246b0ac4a11857cb02c/aiobotocore-3.4.0-py3-none-any.whl", hash = "sha256:26290eb6830ea92d8a6f5f90b56e9f5cedd6d126074d5db63b195e281d982465", size = 88018, upload-time = "2026-04-07T06:12:22.684Z" }, + { url = "https://files.pythonhosted.org/packages/2d/05/6eeeadef45c24630af0ceae4d038b883e9a394786300529286ba8cc1e62d/aiobotocore-3.5.0-py3-none-any.whl", hash = "sha256:49ce35bb8b96b85d3251c2cbbb2ed7a028dc0cb0d0d0801f9ccca1ccd0d41ded", size = 88281, upload-time = "2026-04-21T07:25:25.258Z" }, ] [[package]] @@ -546,7 +546,7 @@ dependencies = [ { name = "click" }, { name = "mypy-extensions" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "pathspec" }, { name = "platformdirs" }, ] @@ -570,16 +570,16 @@ wheels = [ [[package]] name = "botocore" -version = "1.42.84" +version = "1.42.91" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b4/b7/1c03423843fb0d1795b686511c00ee63fed1234c2400f469aeedfd42212f/botocore-1.42.84.tar.gz", hash = "sha256:234064604c80d9272a5e9f6b3566d260bcaa053a5e05246db90d7eca1c2cf44b", size = 15148615, upload-time = "2026-04-06T19:38:56.673Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/bc/a4b7c46471c2e789ad8c4c7acfd7f302fdb481d93ff870f441249b924ae6/botocore-1.42.91.tar.gz", hash = "sha256:d252e27bc454afdbf5ed3dc617aa423f2c855c081e98b7963093399483ecc698", size = 15213010, upload-time = "2026-04-17T19:30:50.793Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/37/0c0c90361c8a1b9e6c75222ca24ae12996a298c0e18822a72ab229c37207/botocore-1.42.84-py3-none-any.whl", hash = "sha256:15f3fe07dfa6545e46a60c4b049fe2bdf63803c595ae4a4eec90e8f8172764f3", size = 14827061, upload-time = "2026-04-06T19:38:53.613Z" }, + { url = "https://files.pythonhosted.org/packages/b1/fc/24cc0a47c824f13933e210e9ad034b4fba22f7185b8d904c0fbf5a3b2be8/botocore-1.42.91-py3-none-any.whl", hash = "sha256:7a28c3cc6bfab5724ad18899d52402b776a0de7d87fa20c3c5270bcaaf199ce8", size = 14897344, upload-time = "2026-04-17T19:30:44.245Z" }, ] [[package]] @@ -625,18 +625,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ninja" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts'" }, { name = "torch", marker = "sys_platform == 'never'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/63/15/ec51d77a2df03ee93410f8ee97fceeb7181da213813c51243e9dd6d7e144/causal_conv1d-1.6.1.tar.gz", hash = "sha256:e4a697ec2db3906f012e675125569f8b510b4559bc53e3095143d91369e1221b", size = 29426, upload-time = "2026-03-10T08:56:35.305Z" } [[package]] name = "certifi" -version = "2026.2.25" +version = "2026.4.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, + { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" }, ] [[package]] @@ -771,14 +771,14 @@ wheels = [ [[package]] name = "click" -version = "8.3.2" +version = "8.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/57/75/31212c6bf2503fdf920d87fee5d7a86a2e3bcf444984126f13d8e4016804/click-8.3.2.tar.gz", hash = "sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5", size = 302856, upload-time = "2026-04-03T19:14:45.118Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/20/71885d8b97d4f3dde17b1fdb92dbd4908b00541c5a3379787137285f602e/click-8.3.2-py3-none-any.whl", hash = "sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d", size = 108379, upload-time = "2026-04-03T19:14:43.505Z" }, + { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, ] [[package]] @@ -906,55 +906,55 @@ wheels = [ [[package]] name = "cryptography" -version = "46.0.7" +version = "47.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/47/93/ac8f3d5ff04d54bc814e961a43ae5b0b146154c89c61b47bb07557679b18/cryptography-46.0.7.tar.gz", hash = "sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5", size = 750652, upload-time = "2026-04-08T01:57:54.692Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/5d/4a8f770695d73be252331e60e526291e3df0c9b27556a90a6b47bccca4c2/cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4", size = 7179869, upload-time = "2026-04-08T01:56:17.157Z" }, - { url = "https://files.pythonhosted.org/packages/5f/45/6d80dc379b0bbc1f9d1e429f42e4cb9e1d319c7a8201beffd967c516ea01/cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325", size = 4275492, upload-time = "2026-04-08T01:56:19.36Z" }, - { url = "https://files.pythonhosted.org/packages/4a/9a/1765afe9f572e239c3469f2cb429f3ba7b31878c893b246b4b2994ffe2fe/cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308", size = 4426670, upload-time = "2026-04-08T01:56:21.415Z" }, - { url = "https://files.pythonhosted.org/packages/8f/3e/af9246aaf23cd4ee060699adab1e47ced3f5f7e7a8ffdd339f817b446462/cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77", size = 4280275, upload-time = "2026-04-08T01:56:23.539Z" }, - { url = "https://files.pythonhosted.org/packages/0f/54/6bbbfc5efe86f9d71041827b793c24811a017c6ac0fd12883e4caa86b8ed/cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1", size = 4928402, upload-time = "2026-04-08T01:56:25.624Z" }, - { url = "https://files.pythonhosted.org/packages/2d/cf/054b9d8220f81509939599c8bdbc0c408dbd2bdd41688616a20731371fe0/cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef", size = 4459985, upload-time = "2026-04-08T01:56:27.309Z" }, - { url = "https://files.pythonhosted.org/packages/f9/46/4e4e9c6040fb01c7467d47217d2f882daddeb8828f7df800cb806d8a2288/cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de", size = 3990652, upload-time = "2026-04-08T01:56:29.095Z" }, - { url = "https://files.pythonhosted.org/packages/36/5f/313586c3be5a2fbe87e4c9a254207b860155a8e1f3cca99f9910008e7d08/cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83", size = 4279805, upload-time = "2026-04-08T01:56:30.928Z" }, - { url = "https://files.pythonhosted.org/packages/69/33/60dfc4595f334a2082749673386a4d05e4f0cf4df8248e63b2c3437585f2/cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb", size = 4892883, upload-time = "2026-04-08T01:56:32.614Z" }, - { url = "https://files.pythonhosted.org/packages/c7/0b/333ddab4270c4f5b972f980adef4faa66951a4aaf646ca067af597f15563/cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b", size = 4459756, upload-time = "2026-04-08T01:56:34.306Z" }, - { url = "https://files.pythonhosted.org/packages/d2/14/633913398b43b75f1234834170947957c6b623d1701ffc7a9600da907e89/cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85", size = 4410244, upload-time = "2026-04-08T01:56:35.977Z" }, - { url = "https://files.pythonhosted.org/packages/10/f2/19ceb3b3dc14009373432af0c13f46aa08e3ce334ec6eff13492e1812ccd/cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e", size = 4674868, upload-time = "2026-04-08T01:56:38.034Z" }, - { url = "https://files.pythonhosted.org/packages/1a/bb/a5c213c19ee94b15dfccc48f363738633a493812687f5567addbcbba9f6f/cryptography-46.0.7-cp311-abi3-win32.whl", hash = "sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457", size = 3026504, upload-time = "2026-04-08T01:56:39.666Z" }, - { url = "https://files.pythonhosted.org/packages/2b/02/7788f9fefa1d060ca68717c3901ae7fffa21ee087a90b7f23c7a603c32ae/cryptography-46.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b", size = 3488363, upload-time = "2026-04-08T01:56:41.893Z" }, - { url = "https://files.pythonhosted.org/packages/7b/56/15619b210e689c5403bb0540e4cb7dbf11a6bf42e483b7644e471a2812b3/cryptography-46.0.7-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842", size = 7119671, upload-time = "2026-04-08T01:56:44Z" }, - { url = "https://files.pythonhosted.org/packages/74/66/e3ce040721b0b5599e175ba91ab08884c75928fbeb74597dd10ef13505d2/cryptography-46.0.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c", size = 4268551, upload-time = "2026-04-08T01:56:46.071Z" }, - { url = "https://files.pythonhosted.org/packages/03/11/5e395f961d6868269835dee1bafec6a1ac176505a167f68b7d8818431068/cryptography-46.0.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902", size = 4408887, upload-time = "2026-04-08T01:56:47.718Z" }, - { url = "https://files.pythonhosted.org/packages/40/53/8ed1cf4c3b9c8e611e7122fb56f1c32d09e1fff0f1d77e78d9ff7c82653e/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d", size = 4271354, upload-time = "2026-04-08T01:56:49.312Z" }, - { url = "https://files.pythonhosted.org/packages/50/46/cf71e26025c2e767c5609162c866a78e8a2915bbcfa408b7ca495c6140c4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022", size = 4905845, upload-time = "2026-04-08T01:56:50.916Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ea/01276740375bac6249d0a971ebdf6b4dc9ead0ee0a34ef3b5a88c1a9b0d4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce", size = 4444641, upload-time = "2026-04-08T01:56:52.882Z" }, - { url = "https://files.pythonhosted.org/packages/3d/4c/7d258f169ae71230f25d9f3d06caabcff8c3baf0978e2b7d65e0acac3827/cryptography-46.0.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f", size = 3967749, upload-time = "2026-04-08T01:56:54.597Z" }, - { url = "https://files.pythonhosted.org/packages/b5/2a/2ea0767cad19e71b3530e4cad9605d0b5e338b6a1e72c37c9c1ceb86c333/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99", size = 4270942, upload-time = "2026-04-08T01:56:56.416Z" }, - { url = "https://files.pythonhosted.org/packages/41/3d/fe14df95a83319af25717677e956567a105bb6ab25641acaa093db79975d/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1", size = 4871079, upload-time = "2026-04-08T01:56:58.31Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/4a479e0f36f8f378d397f4eab4c850b4ffb79a2f0d58704b8fa0703ddc11/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2", size = 4443999, upload-time = "2026-04-08T01:57:00.508Z" }, - { url = "https://files.pythonhosted.org/packages/28/17/b59a741645822ec6d04732b43c5d35e4ef58be7bfa84a81e5ae6f05a1d33/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e", size = 4399191, upload-time = "2026-04-08T01:57:02.654Z" }, - { url = "https://files.pythonhosted.org/packages/59/6a/bb2e166d6d0e0955f1e9ff70f10ec4b2824c9cfcdb4da772c7dd69cc7d80/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee", size = 4655782, upload-time = "2026-04-08T01:57:04.592Z" }, - { url = "https://files.pythonhosted.org/packages/95/b6/3da51d48415bcb63b00dc17c2eff3a651b7c4fed484308d0f19b30e8cb2c/cryptography-46.0.7-cp314-cp314t-win32.whl", hash = "sha256:fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298", size = 3002227, upload-time = "2026-04-08T01:57:06.91Z" }, - { url = "https://files.pythonhosted.org/packages/32/a8/9f0e4ed57ec9cebe506e58db11ae472972ecb0c659e4d52bbaee80ca340a/cryptography-46.0.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb", size = 3475332, upload-time = "2026-04-08T01:57:08.807Z" }, - { url = "https://files.pythonhosted.org/packages/a7/7f/cd42fc3614386bc0c12f0cb3c4ae1fc2bbca5c9662dfed031514911d513d/cryptography-46.0.7-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4", size = 7165618, upload-time = "2026-04-08T01:57:10.645Z" }, - { url = "https://files.pythonhosted.org/packages/a5/d0/36a49f0262d2319139d2829f773f1b97ef8aef7f97e6e5bd21455e5a8fb5/cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7", size = 4270628, upload-time = "2026-04-08T01:57:12.885Z" }, - { url = "https://files.pythonhosted.org/packages/8a/6c/1a42450f464dda6ffbe578a911f773e54dd48c10f9895a23a7e88b3e7db5/cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832", size = 4415405, upload-time = "2026-04-08T01:57:14.923Z" }, - { url = "https://files.pythonhosted.org/packages/9a/92/4ed714dbe93a066dc1f4b4581a464d2d7dbec9046f7c8b7016f5286329e2/cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163", size = 4272715, upload-time = "2026-04-08T01:57:16.638Z" }, - { url = "https://files.pythonhosted.org/packages/b7/e6/a26b84096eddd51494bba19111f8fffe976f6a09f132706f8f1bf03f51f7/cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2", size = 4918400, upload-time = "2026-04-08T01:57:19.021Z" }, - { url = "https://files.pythonhosted.org/packages/c7/08/ffd537b605568a148543ac3c2b239708ae0bd635064bab41359252ef88ed/cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067", size = 4450634, upload-time = "2026-04-08T01:57:21.185Z" }, - { url = "https://files.pythonhosted.org/packages/16/01/0cd51dd86ab5b9befe0d031e276510491976c3a80e9f6e31810cce46c4ad/cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0", size = 3985233, upload-time = "2026-04-08T01:57:22.862Z" }, - { url = "https://files.pythonhosted.org/packages/92/49/819d6ed3a7d9349c2939f81b500a738cb733ab62fbecdbc1e38e83d45e12/cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba", size = 4271955, upload-time = "2026-04-08T01:57:24.814Z" }, - { url = "https://files.pythonhosted.org/packages/80/07/ad9b3c56ebb95ed2473d46df0847357e01583f4c52a85754d1a55e29e4d0/cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006", size = 4879888, upload-time = "2026-04-08T01:57:26.88Z" }, - { url = "https://files.pythonhosted.org/packages/b8/c7/201d3d58f30c4c2bdbe9b03844c291feb77c20511cc3586daf7edc12a47b/cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0", size = 4449961, upload-time = "2026-04-08T01:57:29.068Z" }, - { url = "https://files.pythonhosted.org/packages/a5/ef/649750cbf96f3033c3c976e112265c33906f8e462291a33d77f90356548c/cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85", size = 4401696, upload-time = "2026-04-08T01:57:31.029Z" }, - { url = "https://files.pythonhosted.org/packages/41/52/a8908dcb1a389a459a29008c29966c1d552588d4ae6d43f3a1a4512e0ebe/cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e", size = 4664256, upload-time = "2026-04-08T01:57:33.144Z" }, - { url = "https://files.pythonhosted.org/packages/4b/fa/f0ab06238e899cc3fb332623f337a7364f36f4bb3f2534c2bb95a35b132c/cryptography-46.0.7-cp38-abi3-win32.whl", hash = "sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246", size = 3013001, upload-time = "2026-04-08T01:57:34.933Z" }, - { url = "https://files.pythonhosted.org/packages/d2/f1/00ce3bde3ca542d1acd8f8cfa38e446840945aa6363f9b74746394b14127/cryptography-46.0.7-cp38-abi3-win_amd64.whl", hash = "sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3", size = 3472985, upload-time = "2026-04-08T01:57:36.714Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/ef/b2/7ffa7fe8207a8c42147ffe70c3e360b228160c1d85dc3faff16aaa3244c0/cryptography-47.0.0.tar.gz", hash = "sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb", size = 830863, upload-time = "2026-04-24T19:54:57.056Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/98/40dfe932134bdcae4f6ab5927c87488754bf9eb79297d7e0070b78dd58e9/cryptography-47.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:160ad728f128972d362e714054f6ba0067cab7fb350c5202a9ae8ae4ce3ef1a0", size = 7912214, upload-time = "2026-04-24T19:53:03.864Z" }, + { url = "https://files.pythonhosted.org/packages/34/c6/2733531243fba725f58611b918056b277692f1033373dcc8bd01af1c05d4/cryptography-47.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b9a8943e359b7615db1a3ba587994618e094ff3d6fa5a390c73d079ce18b3973", size = 4644617, upload-time = "2026-04-24T19:53:06.909Z" }, + { url = "https://files.pythonhosted.org/packages/00/e3/b27be1a670a9b87f855d211cf0e1174a5d721216b7616bd52d8581d912ed/cryptography-47.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f5c15764f261394b22aef6b00252f5195f46f2ca300bec57149474e2538b31f8", size = 4668186, upload-time = "2026-04-24T19:53:09.053Z" }, + { url = "https://files.pythonhosted.org/packages/81/b9/8443cfe5d17d482d348cee7048acf502bb89a51b6382f06240fd290d4ca3/cryptography-47.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9c59ab0e0fa3a180a5a9c59f3a5abe3ef90d474bc56d7fadfbe80359491b615b", size = 4651244, upload-time = "2026-04-24T19:53:11.217Z" }, + { url = "https://files.pythonhosted.org/packages/5d/5e/13ed0cdd0eb88ba159d6dd5ebfece8cb901dbcf1ae5ac4072e28b55d3153/cryptography-47.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:34b4358b925a5ea3e14384ca781a2c0ef7ac219b57bb9eacc4457078e2b19f92", size = 5252906, upload-time = "2026-04-24T19:53:13.532Z" }, + { url = "https://files.pythonhosted.org/packages/64/16/ed058e1df0f33d440217cd120d41d5dda9dd215a80b8187f68483185af82/cryptography-47.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0024b87d47ae2399165a6bfb20d24888881eeab83ae2566d62467c5ff0030ce7", size = 4701842, upload-time = "2026-04-24T19:53:15.618Z" }, + { url = "https://files.pythonhosted.org/packages/02/e0/3d30986b30fdbd9e969abbdf8ba00ed0618615144341faeb57f395a084fe/cryptography-47.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:1e47422b5557bb82d3fff997e8d92cff4e28b9789576984f08c248d2b3535d93", size = 4289313, upload-time = "2026-04-24T19:53:17.755Z" }, + { url = "https://files.pythonhosted.org/packages/df/fd/32db38e3ad0cb331f0691cb4c7a8a6f176f679124dee746b3af6633db4d9/cryptography-47.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:6f29f36582e6151d9686235e586dd35bb67491f024767d10b842e520dc6a07ac", size = 4650964, upload-time = "2026-04-24T19:53:20.062Z" }, + { url = "https://files.pythonhosted.org/packages/86/53/5395d944dfd48cb1f67917f533c609c34347185ef15eb4308024c876f274/cryptography-47.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a9b761f012a943b7de0e828843c5688d0de94a0578d44d6c85a1bae32f87791f", size = 5207817, upload-time = "2026-04-24T19:53:22.498Z" }, + { url = "https://files.pythonhosted.org/packages/34/4f/e5711b28e1901f7d480a2b1b688b645aa4c77c73f10731ed17e7f7db3f0d/cryptography-47.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4e1de79e047e25d6e9f8cea71c86b4a53aced64134f0f003bbcbf3655fd172c8", size = 4701544, upload-time = "2026-04-24T19:53:24.356Z" }, + { url = "https://files.pythonhosted.org/packages/22/22/c8ddc25de3010fc8da447648f5a092c40e7a8fadf01dd6d255d9c0b9373d/cryptography-47.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef6b3634087f18d2155b1e8ce264e5345a753da2c5fa9815e7d41315c90f8318", size = 4783536, upload-time = "2026-04-24T19:53:26.665Z" }, + { url = "https://files.pythonhosted.org/packages/66/b6/d4a68f4ea999c6d89e8498579cba1c5fcba4276284de7773b17e4fa69293/cryptography-47.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:11dbb9f50a0f1bb9757b3d8c27c1101780efb8f0bdecfb12439c22a74d64c001", size = 4926106, upload-time = "2026-04-24T19:53:28.686Z" }, + { url = "https://files.pythonhosted.org/packages/54/ed/5f524db1fade9c013aa618e1c99c6ed05e8ffc9ceee6cda22fed22dda3f4/cryptography-47.0.0-cp311-abi3-win32.whl", hash = "sha256:7fda2f02c9015db3f42bb8a22324a454516ed10a8c29ca6ece6cdbb5efe2a203", size = 3258581, upload-time = "2026-04-24T19:53:31.058Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dc/1b901990b174786569029f67542b3edf72ac068b6c3c8683c17e6a2f5363/cryptography-47.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:f5c3296dab66202f1b18a91fa266be93d6aa0c2806ea3d67762c69f60adc71aa", size = 3775309, upload-time = "2026-04-24T19:53:33.054Z" }, + { url = "https://files.pythonhosted.org/packages/14/88/7aa18ad9c11bc87689affa5ce4368d884b517502d75739d475fc6f4a03c7/cryptography-47.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:be12cb6a204f77ed968bcefe68086eb061695b540a3dd05edac507a3111b25f0", size = 7904299, upload-time = "2026-04-24T19:53:35.003Z" }, + { url = "https://files.pythonhosted.org/packages/07/55/c18f75724544872f234678fdedc871391722cb34a2aee19faa9f63100bb2/cryptography-47.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2ebd84adf0728c039a3be2700289378e1c164afc6748df1a5ed456767bef9ba7", size = 4631180, upload-time = "2026-04-24T19:53:37.517Z" }, + { url = "https://files.pythonhosted.org/packages/ee/65/31a5cc0eaca99cec5bafffe155d407115d96136bb161e8b49e0ef73f09a7/cryptography-47.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f68d6fbc7fbbcfb0939fea72c3b96a9f9a6edfc0e1b1d29778a2066030418b1", size = 4653529, upload-time = "2026-04-24T19:53:39.775Z" }, + { url = "https://files.pythonhosted.org/packages/e5/bc/641c0519a495f3bfd0421b48d7cd325c4336578523ccd76ea322b6c29c7a/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:6651d32eff255423503aa276739da98c30f26c40cbeffcc6048e0d54ef704c0c", size = 4638570, upload-time = "2026-04-24T19:53:42.129Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f2/300327b0a47f6dc94dd8b71b57052aefe178bb51745073d73d80604f11ab/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3fb8fa48075fad7193f2e5496135c6a76ac4b2aa5a38433df0a539296b377829", size = 5238019, upload-time = "2026-04-24T19:53:44.577Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5a/5b5cf994391d4bf9d9c7efd4c66aabe4d95227256627f8fea6cff7dfadbd/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:11438c7518132d95f354fa01a4aa2f806d172a061a7bed18cf18cbdacdb204d7", size = 4686832, upload-time = "2026-04-24T19:53:47.015Z" }, + { url = "https://files.pythonhosted.org/packages/dc/2c/ae950e28fd6475c852fc21a44db3e6b5bcc1261d1e370f2b6e42fa800fef/cryptography-47.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8c1a736bbb3288005796c3f7ccb9453360d7fed483b13b9f468aea5171432923", size = 4269301, upload-time = "2026-04-24T19:53:48.97Z" }, + { url = "https://files.pythonhosted.org/packages/67/fb/6a39782e150ffe5cc1b0018cb6ddc48bf7ca62b498d7539ffc8a758e977d/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:f1557695e5c2b86e204f6ce9470497848634100787935ab7adc5397c54abd7ab", size = 4638110, upload-time = "2026-04-24T19:53:51.011Z" }, + { url = "https://files.pythonhosted.org/packages/8e/d7/0b3c71090a76e5c203164a47688b697635ece006dcd2499ab3a4dbd3f0bd/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:f9a034b642b960767fb343766ae5ba6ad653f2e890ddd82955aef288ffea8736", size = 5194988, upload-time = "2026-04-24T19:53:52.962Z" }, + { url = "https://files.pythonhosted.org/packages/63/33/63a961498a9df51721ab578c5a2622661411fc520e00bd83b0cc64eb20c4/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:b1c76fca783aa7698eb21eb14f9c4aa09452248ee54a627d125025a43f83e7a7", size = 4686563, upload-time = "2026-04-24T19:53:55.274Z" }, + { url = "https://files.pythonhosted.org/packages/b7/bf/5ee5b145248f92250de86145d1c1d6edebbd57a7fe7caa4dedb5d4cf06a1/cryptography-47.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4f7722c97826770bab8ae92959a2e7b20a5e9e9bf4deae68fd86c3ca457bab52", size = 4770094, upload-time = "2026-04-24T19:53:57.753Z" }, + { url = "https://files.pythonhosted.org/packages/92/43/21d220b2da5d517773894dacdcdb5c682c28d3fffce65548cb06e87d5501/cryptography-47.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:09f6d7bf6724f8db8b32f11eccf23efc8e759924bc5603800335cf8859a3ddbd", size = 4913811, upload-time = "2026-04-24T19:54:00.236Z" }, + { url = "https://files.pythonhosted.org/packages/31/98/dc4ad376ac5f1a1a7d4a83f7b0c6f2bcad36b5d2d8f30aeb482d3a7d9582/cryptography-47.0.0-cp314-cp314t-win32.whl", hash = "sha256:6eebcaf0df1d21ce1f90605c9b432dd2c4f4ab665ac29a40d5e3fc68f51b5e63", size = 3237158, upload-time = "2026-04-24T19:54:02.606Z" }, + { url = "https://files.pythonhosted.org/packages/bc/da/97f62d18306b5133468bc3f8cc73a3111e8cdc8cf8d3e69474d6e5fd2d1b/cryptography-47.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:51c9313e90bd1690ec5a75ed047c27c0b8e6c570029712943d6116ef9a90620b", size = 3758706, upload-time = "2026-04-24T19:54:04.433Z" }, + { url = "https://files.pythonhosted.org/packages/e0/34/a4fae8ae7c3bc227460c9ae43f56abf1b911da0ec29e0ebac53bb0a4b6b7/cryptography-47.0.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:14432c8a9bcb37009784f9594a62fae211a2ae9543e96c92b2a8e4c3cd5cd0c4", size = 7904072, upload-time = "2026-04-24T19:54:06.411Z" }, + { url = "https://files.pythonhosted.org/packages/01/64/d7b1e54fdb69f22d24a64bb3e88dc718b31c7fb10ef0b9691a3cf7eeea6e/cryptography-47.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:07efe86201817e7d3c18781ca9770bc0db04e1e48c994be384e4602bc38f8f27", size = 4635767, upload-time = "2026-04-24T19:54:08.519Z" }, + { url = "https://files.pythonhosted.org/packages/8b/7b/cca826391fb2a94efdcdfe4631eb69306ee1cff0b22f664a412c90713877/cryptography-47.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b45761c6ec22b7c726d6a829558777e32d0f1c8be7c3f3480f9c912d5ee8a10", size = 4654350, upload-time = "2026-04-24T19:54:10.795Z" }, + { url = "https://files.pythonhosted.org/packages/4c/65/4b57bcc823f42a991627c51c2f68c9fd6eb1393c1756aac876cba2accae2/cryptography-47.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:edd4da498015da5b9f26d38d3bfc2e90257bfa9cbed1f6767c282a0025ae649b", size = 4643394, upload-time = "2026-04-24T19:54:13.275Z" }, + { url = "https://files.pythonhosted.org/packages/f4/c4/2c5fbeea70adbbca2bbae865e1d605d6a4a7f8dbd9d33eaf69645087f06c/cryptography-47.0.0-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9af828c0d5a65c70ec729cd7495a4bf1a67ecb66417b8f02ff125ab8a6326a74", size = 5225777, upload-time = "2026-04-24T19:54:15.18Z" }, + { url = "https://files.pythonhosted.org/packages/7e/b8/ac57107ef32749d2b244e36069bb688792a363aaaa3acc9e3cf84c130315/cryptography-47.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:256d07c78a04d6b276f5df935a9923275f53bd1522f214447fdf365494e2d515", size = 4688771, upload-time = "2026-04-24T19:54:17.835Z" }, + { url = "https://files.pythonhosted.org/packages/56/fc/9f1de22ff8be99d991f240a46863c52d475404c408886c5a38d2b5c3bb26/cryptography-47.0.0-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:5d0e362ff51041b0c0d219cc7d6924d7b8996f57ce5712bdcef71eb3c65a59cc", size = 4270753, upload-time = "2026-04-24T19:54:19.963Z" }, + { url = "https://files.pythonhosted.org/packages/00/68/d70c852797aa68e8e48d12e5a87170c43f67bb4a59403627259dd57d15de/cryptography-47.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:1581aef4219f7ca2849d0250edaa3866212fb74bf5667284f46aa92f9e65c1ca", size = 4642911, upload-time = "2026-04-24T19:54:21.818Z" }, + { url = "https://files.pythonhosted.org/packages/a5/51/661cbee74f594c5d97ff82d34f10d5551c085ca4668645f4606ebd22bd5d/cryptography-47.0.0-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a49a3eb5341b9503fa3000a9a0db033161db90d47285291f53c2a9d2cd1b7f76", size = 5181411, upload-time = "2026-04-24T19:54:24.376Z" }, + { url = "https://files.pythonhosted.org/packages/94/87/f2b6c374a82cf076cfa1416992ac8e8ec94d79facc37aec87c1a5cb72352/cryptography-47.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2207a498b03275d0051589e326b79d4cf59985c99031b05bb292ac52631c37fe", size = 4688262, upload-time = "2026-04-24T19:54:26.946Z" }, + { url = "https://files.pythonhosted.org/packages/14/e2/8b7462f4acf21ec509616f0245018bb197194ab0b65c2ea21a0bdd53c0eb/cryptography-47.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7a02675e2fabd0c0fc04c868b8781863cbf1967691543c22f5470500ff840b31", size = 4775506, upload-time = "2026-04-24T19:54:28.926Z" }, + { url = "https://files.pythonhosted.org/packages/70/75/158e494e4c08dc05e039da5bb48553826bd26c23930cf8d3cd5f21fa8921/cryptography-47.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80887c5cbd1774683cb126f0ab4184567f080071d5acf62205acb354b4b753b7", size = 4912060, upload-time = "2026-04-24T19:54:30.869Z" }, + { url = "https://files.pythonhosted.org/packages/06/bd/0a9d3edbf5eadbac926d7b9b3cd0c4be584eeeae4a003d24d9eda4affbbd/cryptography-47.0.0-cp38-abi3-win32.whl", hash = "sha256:ed67ea4e0cfb5faa5bc7ecb6e2b8838f3807a03758eec239d6c21c8769355310", size = 3248487, upload-time = "2026-04-24T19:54:33.494Z" }, + { url = "https://files.pythonhosted.org/packages/60/80/5681af756d0da3a599b7bdb586fac5a1540f1bcefd2717a20e611ddade45/cryptography-47.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:835d2d7f47cdc53b3224e90810fb1d36ca94ea29cc1801fb4c1bc43876735769", size = 3755737, upload-time = "2026-04-24T19:54:35.408Z" }, ] [[package]] @@ -1089,7 +1089,7 @@ dependencies = [ { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.14' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-dev') or (python_full_version < '3.14' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.14' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-lts') or (python_full_version < '3.14' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.14' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-dev') or (python_full_version < '3.14' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.14' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-lts') or (python_full_version < '3.14' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.14' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-lts') or (python_full_version < '3.14' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.14' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-dev') or (python_full_version < '3.14' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "pandas", version = "3.0.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.14' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-lts') or (python_full_version < '3.14' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "pyarrow", marker = "python_full_version >= '3.14' and sys_platform == 'win32'" }, @@ -1135,7 +1135,7 @@ dependencies = [ { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.14' and extra == 'extra-13-megatron-core-dev') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.14' and extra == 'extra-13-megatron-core-lts') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-lts') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.14' and extra == 'extra-13-megatron-core-dev') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.14' and extra == 'extra-13-megatron-core-lts') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-lts') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.14' and extra == 'extra-13-megatron-core-lts') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-lts') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.14' and extra == 'extra-13-megatron-core-dev') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "pandas", version = "3.0.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.14' and extra == 'extra-13-megatron-core-lts') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-lts') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "pyarrow", marker = "python_full_version < '3.14' or sys_platform != 'win32'" }, @@ -1304,7 +1304,7 @@ wheels = [ [[package]] name = "fastapi" -version = "0.136.0" +version = "0.136.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -1313,9 +1313,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4e/d9/e66315807e41e69e7f6a1b42a162dada2f249c5f06ad3f1a95f84ab336ef/fastapi-0.136.0.tar.gz", hash = "sha256:cf08e067cc66e106e102d9ba659463abfac245200752f8a5b7b1e813de4ff73e", size = 396607, upload-time = "2026-04-16T11:47:13.623Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5d/45/c130091c2dfa061bbfe3150f2a5091ef1adf149f2a8d2ae769ecaf6e99a2/fastapi-0.136.1.tar.gz", hash = "sha256:7af665ad7acfa0a3baf8983d393b6b471b9da10ede59c60045f49fbc89a0fa7f", size = 397448, upload-time = "2026-04-23T16:49:44.046Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/26/a3/0bd5f0cdb0bbc92650e8dc457e9250358411ee5d1b65e42b6632387daf81/fastapi-0.136.0-py3-none-any.whl", hash = "sha256:8793d44ec7378e2be07f8a013cf7f7aa47d6327d0dfe9804862688ec4541a6b4", size = 117556, upload-time = "2026-04-16T11:47:11.922Z" }, + { url = "https://files.pythonhosted.org/packages/5a/ff/2e4eca3ade2c22fe1dea7043b8ee9dabe47753349eb1b56a202de8af6349/fastapi-0.136.1-py3-none-any.whl", hash = "sha256:a6e9d7eeada96c93a4d69cb03836b44fa34e2854accb7244a1ece36cd4781c3f", size = 117683, upload-time = "2026-04-23T16:49:42.437Z" }, ] [[package]] @@ -1411,7 +1411,7 @@ dependencies = [ { name = "nvidia-cutlass-dsl" }, { name = "nvidia-ml-py" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts'" }, { name = "requests" }, { name = "tabulate" }, { name = "torch", marker = "sys_platform == 'never'" }, @@ -1634,14 +1634,14 @@ wheels = [ [[package]] name = "gitpython" -version = "3.1.46" +version = "3.1.47" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "gitdb" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/b5/59d16470a1f0dfe8c793f9ef56fd3826093fc52b3bd96d6b9d6c26c7e27b/gitpython-3.1.46.tar.gz", hash = "sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f", size = 215371, upload-time = "2026-01-01T15:37:32.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/bd/50db468e9b1310529a19fce651b3b0e753b5c07954d486cba31bbee9a5d5/gitpython-3.1.47.tar.gz", hash = "sha256:dba27f922bd2b42cb54c87a8ab3cb6beb6bf07f3d564e21ac848913a05a8a3cd", size = 216978, upload-time = "2026-04-22T02:44:44.059Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", size = 208620, upload-time = "2026-01-01T15:37:30.574Z" }, + { url = "https://files.pythonhosted.org/packages/f2/c5/a1bc0996af85757903cf2bf444a7824e68e0035ce63fb41d6f76f9def68b/gitpython-3.1.47-py3-none-any.whl", hash = "sha256:489f590edfd6d20571b2c0e72c6a6ac6915ee8b8cd04572330e3842207a78905", size = 209547, upload-time = "2026-04-22T02:44:41.271Z" }, ] [[package]] @@ -1853,7 +1853,7 @@ version = "1.29.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "pathspec" }, { name = "pluggy" }, { name = "trove-classifiers" }, @@ -1970,7 +1970,7 @@ dependencies = [ { name = "fsspec", version = "2026.3.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.14' and sys_platform == 'win32') or (python_full_version < '3.14' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "pyyaml" }, { name = "requests" }, { name = "tqdm" }, @@ -2007,11 +2007,11 @@ wheels = [ [[package]] name = "idna" -version = "3.11" +version = "3.13" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/cc/762dfb036166873f0059f3b7de4565e1b5bc3d6f28a414c13da27e442f99/idna-3.13.tar.gz", hash = "sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242", size = 194210, upload-time = "2026-04-22T16:42:42.314Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/5d/13/ad7d7ca3808a898b4612b6fe93cde56b53f3034dcde235acb1f0e1df24c6/idna-3.13-py3-none-any.whl", hash = "sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3", size = 68629, upload-time = "2026-04-22T16:42:40.909Z" }, ] [[package]] @@ -2322,7 +2322,7 @@ wheels = [ [[package]] name = "langsmith" -version = "0.7.32" +version = "0.7.37" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -2335,9 +2335,9 @@ dependencies = [ { name = "xxhash" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2f/b4/a0b4a501bee6b8a741ce29f8c48155b132118483cddc6f9247735ddb38fa/langsmith-0.7.32.tar.gz", hash = "sha256:b59b8e106d0e4c4842e158229296086e2aa7c561e3f602acda73d3ad0062e915", size = 1184518, upload-time = "2026-04-15T23:42:41.885Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/38/092f99a3326f0f6bb6ea62f388b16611d9cb619869ed7b0f3dae6c21c331/langsmith-0.7.37.tar.gz", hash = "sha256:e15ab27f5febbcfbaec4e6fa74ab71f0284f4c5965249cc732fe9344844290cb", size = 4433170, upload-time = "2026-04-26T21:36:41.314Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/bc/148f98ac7dad73ac5e1b1c985290079cfeeb9ba13d760a24f25002beb2c9/langsmith-0.7.32-py3-none-any.whl", hash = "sha256:e1fde928990c4c52f47dc5132708cec674355d9101723d564183e965f383bf5f", size = 378272, upload-time = "2026-04-15T23:42:39.905Z" }, + { url = "https://files.pythonhosted.org/packages/c6/76/fa99559d23ec9a39e1153f317a5ec99e7b967aec08b5faac04f8da603dd3/langsmith-0.7.37-py3-none-any.whl", hash = "sha256:64fc5fbf223fcdcc6ee44b08a5df4b2ab8a55e4d968e850c86b6b69fe0c258e3", size = 385948, upload-time = "2026-04-26T21:36:39.09Z" }, ] [[package]] @@ -2480,7 +2480,7 @@ dependencies = [ { name = "einops" }, { name = "ninja" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts'" }, { name = "setuptools" }, { name = "torch", marker = "sys_platform == 'never'" }, { name = "transformers" }, @@ -2634,7 +2634,7 @@ dependencies = [ { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "torch", marker = "sys_platform == 'never' or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, ] @@ -2656,7 +2656,7 @@ dev = [ { name = "nvidia-modelopt", marker = "(sys_platform != 'darwin' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "nvidia-resiliency-ext" }, { name = "onnxscript", version = "0.5.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, - { name = "onnxscript", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, + { name = "onnxscript", version = "0.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "openai", extra = ["aiohttp"], marker = "extra == 'extra-13-megatron-core-dev'" }, { name = "opentelemetry-api", version = "1.33.1", source = { registry = "https://pypi.org/simple" } }, { name = "orjson" }, @@ -2679,7 +2679,7 @@ lts = [ { name = "mamba-ssm" }, { name = "megatron-energon", extra = ["av-decode"], marker = "extra == 'extra-13-megatron-core-lts'" }, { name = "multi-storage-client" }, - { name = "onnxscript", version = "0.6.2", source = { registry = "https://pypi.org/simple" } }, + { name = "onnxscript", version = "0.7.0", source = { registry = "https://pypi.org/simple" } }, { name = "opentelemetry-api", version = "1.33.1", source = { registry = "https://pypi.org/simple" } }, { name = "tensorstore", version = "0.1.82", source = { registry = "https://pypi.org/simple" } }, { name = "tqdm" }, @@ -2708,7 +2708,7 @@ build = [ { name = "hatchling" }, { name = "nvidia-mathdx" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "pybind11" }, { name = "setuptools" }, { name = "torch", marker = "sys_platform == 'never' or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, @@ -3047,7 +3047,7 @@ wheels = [ [[package]] name = "multi-storage-client" -version = "0.46.0" +version = "0.47.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -3065,12 +3065,12 @@ dependencies = [ { name = "xattr" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/da/1294bebc3a3a842ab084a3638a04b36dd094a7e5717573048b55551270fe/multi_storage_client-0.46.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:e6dedd4e93e1f6d7328cc616fbc4ddeb5289d4390a10783863f6ccfe00cb56dc", size = 9514199, upload-time = "2026-04-10T20:57:31.185Z" }, - { url = "https://files.pythonhosted.org/packages/ac/dc/ec67ba28c744f80cfba44225f19ac56b0fb804cdaf8fcf4ac8c925da47e2/multi_storage_client-0.46.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f210266ab0d118161fb79f66d1f6027b52e10c63a41af27a3300dfaea6bb1a7", size = 5765225, upload-time = "2026-04-10T20:58:19.728Z" }, - { url = "https://files.pythonhosted.org/packages/c6/56/f979a4f7496843328d6cf4e09b1cfa76293fe25fb7f78a60aef2dc5ecfc2/multi_storage_client-0.46.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdedff4ebc431264494fe8fe0b7ab6b4ca971f5687772adace1604788034cf75", size = 5965749, upload-time = "2026-04-10T20:56:19.951Z" }, - { url = "https://files.pythonhosted.org/packages/3f/71/c5fe5fbfc47218127c05e7d42511e90fe1b7c47ca3801b8339820df74388/multi_storage_client-0.46.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4b4817b0ed092ec536fdd89fbd1519e024054a66c340273bf2ad13d1f67d2f3f", size = 9509882, upload-time = "2026-04-10T20:57:07.779Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ce/25cfb4a854c841305a66ca3735ad1ea13c0947b963297f03afaf67f8aa42/multi_storage_client-0.46.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f47b7d64f00be68a320665d316ec0559b63b080351a7fd0c2900762bb824cfe", size = 5765904, upload-time = "2026-04-10T21:16:14.759Z" }, - { url = "https://files.pythonhosted.org/packages/c6/3b/0a7703ad775b2b9c41bb5560650f69d2e883f082163b0f46427826337a86/multi_storage_client-0.46.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51ff5e4313ec6df7d94dbffc3dded21cadd40293cb821efd50b4669b6498490c", size = 5965829, upload-time = "2026-04-10T20:56:42.944Z" }, + { url = "https://files.pythonhosted.org/packages/cc/06/04c531ee992ad3d5da4372f081e308b13fa859701d97221551a68cb95a5d/multi_storage_client-0.47.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:fe6307f6752806c28c8bb1938fb664ca81f9a15cb6d5019d2fc6353b650f2745", size = 9523431, upload-time = "2026-04-23T22:17:24.888Z" }, + { url = "https://files.pythonhosted.org/packages/c3/e2/f20fd1fd6783e92a3feabcd47fd9f6416a59c7fbb9de441e79c33817523a/multi_storage_client-0.47.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573e4443a4d619025c6bb337f39b237af2a7c7e2f63d9eceba67086af7bd265", size = 5777105, upload-time = "2026-04-23T22:17:50.943Z" }, + { url = "https://files.pythonhosted.org/packages/99/6a/34e25493f21c368f568408f545f15a4e1e1613a8909cec3c2ae7a4592e91/multi_storage_client-0.47.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6345af5a1f1ad41e3e1958086eda75cddf29a48ba59a958e14a6b0c84318335c", size = 5976329, upload-time = "2026-04-23T22:19:49.54Z" }, + { url = "https://files.pythonhosted.org/packages/66/80/7c842e62a252b353800114654a9173e0b984733b8a69449cbaad7590e9e0/multi_storage_client-0.47.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:3f13642799639a75270c79545c8c2be5dcb8e685ff36417deeae5ff4d7c84ca1", size = 9519686, upload-time = "2026-04-23T22:18:42.524Z" }, + { url = "https://files.pythonhosted.org/packages/66/7c/1ee11cc5acdfedb3f0d1745201ca174c8cb516288b1e2138acd0350cefa3/multi_storage_client-0.47.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd84264e71abc987aee0631d6854cc8345b1bd00731d90a6adae907ba75fda0d", size = 5776772, upload-time = "2026-04-23T22:21:16.566Z" }, + { url = "https://files.pythonhosted.org/packages/7c/6a/7097e3077c6b921ccc7effe9bb872fee7cef4ed42c482a69f5f0339f77c3/multi_storage_client-0.47.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c93c5ac4f36125ba27e68a267a6d90945f432f24b10af25142a39d3b5c5fb29", size = 5976253, upload-time = "2026-04-23T22:23:05.257Z" }, ] [[package]] @@ -3229,7 +3229,7 @@ dependencies = [ { name = "networkx" }, { name = "omegaconf" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "rich" }, { name = "toml" }, { name = "torchx" }, @@ -3247,36 +3247,36 @@ wheels = [ [[package]] name = "nh3" -version = "0.3.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/86/f8d3a7c9bd1bbaa181f6312c757e0b74d25f71ecf84ea3c0dc5e0f01840d/nh3-0.3.4.tar.gz", hash = "sha256:96709a379997c1b28c8974146ca660b0dcd3794f4f6d50c1ea549bab39ac6ade", size = 19520, upload-time = "2026-03-25T10:57:30.789Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/5e/c400663d14be2216bc084ed2befc871b7b12563f85d40904f2a4bf0dd2b7/nh3-0.3.4-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8b61058f34c2105d44d2a4d4241bacf603a1ef5c143b08766bbd0cf23830118f", size = 1417991, upload-time = "2026-03-25T10:56:59.13Z" }, - { url = "https://files.pythonhosted.org/packages/36/f5/109526f5002ec41322ac8cafd50f0f154bae0c26b9607c0fcb708bdca8ec/nh3-0.3.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:554cc2bab281758e94d770c3fb0bf2d8be5fb403ef6b2e8841dd7c1615df7a0f", size = 790566, upload-time = "2026-03-25T10:57:00.445Z" }, - { url = "https://files.pythonhosted.org/packages/7b/66/38950f2b4b316ffd82ee51ed8f9143d1f56fdd620312cacc91613b77b3e7/nh3-0.3.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dbe76feaa44e2ef9436f345016012a591550e77818876a8de5c8bc2a248e08df", size = 837538, upload-time = "2026-03-25T10:57:01.848Z" }, - { url = "https://files.pythonhosted.org/packages/d8/9f/9d6da970e9524fe360ea02a2082856390c2c8ba540409d1be6e5851887b3/nh3-0.3.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:87dac8d611b4a478400e0821a13b35770e88c266582f065e7249d6a37b0f86e8", size = 1012154, upload-time = "2026-03-25T10:57:03.592Z" }, - { url = "https://files.pythonhosted.org/packages/54/92/7c85c33c241e9dd51dda115bd3f765e940446588cdaaca62ef8edffe675f/nh3-0.3.4-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8d697e19f2995b337f648204848ac3a528eaafffc39e7ce4ac6b7a2fbe6c84af", size = 1092516, upload-time = "2026-03-25T10:57:04.726Z" }, - { url = "https://files.pythonhosted.org/packages/16/0f/597842bdb2890999a3faa2f3fcb02db8aa6ad09320d3d843ff6d0a1f737b/nh3-0.3.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:7cae217f031809321db962cd7e092bda8d4e95a87f78c0226628fa6c2ea8ebc5", size = 1053793, upload-time = "2026-03-25T10:57:06.171Z" }, - { url = "https://files.pythonhosted.org/packages/7d/32/669da65147bc10746d2e1d7a8a3dbfbffe0315f419e74b559e2ee3471a01/nh3-0.3.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:07999b998bf89692738f15c0eac76a416382932f855709e0b7488b595c30ec89", size = 1035975, upload-time = "2026-03-25T10:57:07.292Z" }, - { url = "https://files.pythonhosted.org/packages/a1/7e/9e97a8b3c5161c79b4bf21cc54e9334860a52cc54ede15bf2239ef494b73/nh3-0.3.4-cp314-cp314t-win32.whl", hash = "sha256:ca90397c8d36c1535bf1988b2bed006597337843a164c7ec269dc8813f37536b", size = 600419, upload-time = "2026-03-25T10:57:08.342Z" }, - { url = "https://files.pythonhosted.org/packages/e0/c7/6849d8d4295d3997d148eacb2d4b1c9faada4895ee3c1b1e12e72f4611e2/nh3-0.3.4-cp314-cp314t-win_amd64.whl", hash = "sha256:41e46b3499918ab6128b6421677b316e79869d0c140da24069d220a94f4e72d1", size = 613342, upload-time = "2026-03-25T10:57:09.593Z" }, - { url = "https://files.pythonhosted.org/packages/8b/0e/14a3f510f36c20b922c123a2730f071f938d006fb513aacfd46d6cbc03a7/nh3-0.3.4-cp314-cp314t-win_arm64.whl", hash = "sha256:80b955d802bf365bd42e09f6c3d64567dce777d20e97968d94b3e9d9e99b265e", size = 607025, upload-time = "2026-03-25T10:57:10.959Z" }, - { url = "https://files.pythonhosted.org/packages/4a/57/a97955bc95960cfb1f0517043d60a121f4ba93fde252d4d9ffd3c2a9eead/nh3-0.3.4-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d8bebcb20ab4b91858385cd98fe58046ec4a624275b45ef9b976475604f45b49", size = 1439519, upload-time = "2026-03-25T10:57:12.019Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/c9a33361da8cde7c7760f091cd10467bc470634e4eea31c8bb70935b00a4/nh3-0.3.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d825722a1e8cbc87d7ca1e47ffb1d2a6cf343ad4c1b8465becf7cadcabcdfd0", size = 833798, upload-time = "2026-03-25T10:57:13.264Z" }, - { url = "https://files.pythonhosted.org/packages/6b/19/9487790780b8c94eacca37866c1270b747a4af8e244d43b3b550fddbbf62/nh3-0.3.4-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4aa8b43e68c26b68069a3b6cef09de166d1d7fa140cf8d77e409a46cbf742e44", size = 820414, upload-time = "2026-03-25T10:57:14.236Z" }, - { url = "https://files.pythonhosted.org/packages/6b/b4/c6a340dd321d20b1e4a663307032741da045685c87403926c43656f6f5ec/nh3-0.3.4-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f5f214618ad5eff4f2a6b13a8d4da4d9e7f37c569d90a13fb9f0caaf7d04fe21", size = 1061531, upload-time = "2026-03-25T10:57:15.384Z" }, - { url = "https://files.pythonhosted.org/packages/c4/49/f6b4b474e0032e4bcbb7174b44e4cf6915670e09c62421deb06ccfcb88b8/nh3-0.3.4-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3390e4333883673a684ce16c1716b481e91782d6f56dec5c85fed9feedb23382", size = 1021889, upload-time = "2026-03-25T10:57:16.454Z" }, - { url = "https://files.pythonhosted.org/packages/43/da/e52a6941746d1f974752af3fc8591f1dbcdcf7fd8c726c7d99f444ba820e/nh3-0.3.4-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18a2e44ccb29cbb45071b8f3f2dab9ebfb41a6516f328f91f1f1fd18196239a4", size = 912965, upload-time = "2026-03-25T10:57:17.624Z" }, - { url = "https://files.pythonhosted.org/packages/d6/b7/ec1cbc6b297a808c513f59f501656389623fc09ad6a58c640851289c7854/nh3-0.3.4-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0961a27dc2057c38d0364cb05880e1997ae1c80220cbc847db63213720b8f304", size = 804975, upload-time = "2026-03-25T10:57:18.994Z" }, - { url = "https://files.pythonhosted.org/packages/a9/56/b1275aa2c6510191eed76178da4626b0900402439cb9f27d6b9bf7c6d5e9/nh3-0.3.4-cp38-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:9337517edb7c10228252cce2898e20fb3d77e32ffaccbb3c66897927d74215a0", size = 833400, upload-time = "2026-03-25T10:57:20.086Z" }, - { url = "https://files.pythonhosted.org/packages/7c/a5/5d574ffa3c6e49a5364d1b25ebad165501c055340056671493beb467a15e/nh3-0.3.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d866701affe67a5171b916b5c076e767a74c6a9efb7fb2006eb8d3c5f9a293d5", size = 854277, upload-time = "2026-03-25T10:57:21.433Z" }, - { url = "https://files.pythonhosted.org/packages/79/36/8aeb2ab21517cefa212db109e41024e02650716cb42bf293d0a88437a92d/nh3-0.3.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:47d749d99ae005ab19517224140b280dd56e77b33afb82f9b600e106d0458003", size = 1022021, upload-time = "2026-03-25T10:57:22.433Z" }, - { url = "https://files.pythonhosted.org/packages/9c/95/9fd860997685e64abe2d5a995ca2eb5004c0fb6d6585429612a7871548b9/nh3-0.3.4-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:f987cb56458323405e8e5ea827e1befcf141ffa0c0ac797d6d02e6b646056d9a", size = 1103526, upload-time = "2026-03-25T10:57:23.487Z" }, - { url = "https://files.pythonhosted.org/packages/7d/0d/df545070614c1007f0109bb004230226c9000e7857c9785583ec25cda9d7/nh3-0.3.4-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:883d5a6d6ee8078c4afc8e96e022fe579c4c265775ff6ee21e39b8c542cabab3", size = 1068050, upload-time = "2026-03-25T10:57:24.624Z" }, - { url = "https://files.pythonhosted.org/packages/94/d5/17b016df52df052f714c53be71df26a1943551d9931e9383b92c998b88f8/nh3-0.3.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:75643c22f5092d8e209f766ee8108c400bc1e44760fc94d2d638eb138d18f853", size = 1046037, upload-time = "2026-03-25T10:57:25.799Z" }, - { url = "https://files.pythonhosted.org/packages/51/39/49f737907e6ab2b4ca71855d3bd63dd7958862e9c8b94fb4e5b18ccf6988/nh3-0.3.4-cp38-abi3-win32.whl", hash = "sha256:72e4e9ca1c4bd41b4a28b0190edc2e21e3f71496acd36a0162858e1a28db3d7e", size = 609542, upload-time = "2026-03-25T10:57:27.112Z" }, - { url = "https://files.pythonhosted.org/packages/73/4f/af8e9071d7464575a7316831938237ffc9d92d27f163dbdd964b1309cd9b/nh3-0.3.4-cp38-abi3-win_amd64.whl", hash = "sha256:c10b1f0c741e257a5cb2978d6bac86e7c784ab20572724b20c6402c2e24bce75", size = 624244, upload-time = "2026-03-25T10:57:28.302Z" }, - { url = "https://files.pythonhosted.org/packages/44/0c/37695d6b0168f6714b5c492331636a9e6123d6ec22d25876c68d06eab1b8/nh3-0.3.4-cp38-abi3-win_arm64.whl", hash = "sha256:43ad4eedee7e049b9069bc015b7b095d320ed6d167ecec111f877de1540656e9", size = 616649, upload-time = "2026-03-25T10:57:29.623Z" }, +version = "0.3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/5f/1d19bdc7d27238e37f3672cdc02cb77c56a4a86d140cd4f4f23c90df6e16/nh3-0.3.5.tar.gz", hash = "sha256:45855e14ff056064fec77133bfcf7cd691838168e5e17bbef075394954dc9dc8", size = 20743, upload-time = "2026-04-25T10:44:16.066Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/b0/8587ac42a9627ab88e7e221601f1dfccbf4db80b2a29222ea63266dc9abc/nh3-0.3.5-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:23a312224875f72cd16bde417f49071451877e29ef646a60e50fcb69407cc18a", size = 1420126, upload-time = "2026-04-25T10:43:39.834Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1b/1dbc4d0c43f12e8c1784ede17eaee6f061d4fbe5505757c65c49b2ceab95/nh3-0.3.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:387abd011e81959d5a35151a11350a0795c6edeb53ebfa02d2e882dc01299263", size = 793943, upload-time = "2026-04-25T10:43:41.363Z" }, + { url = "https://files.pythonhosted.org/packages/47/9f/d6758d7a14ee964bf439cc35ae4fa24a763a93399c8ef6f22bd11d532d29/nh3-0.3.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:48f45e3e914be93a596431aa143dedf1582557bf41a58153c296048d6e3798c9", size = 841150, upload-time = "2026-04-25T10:43:43.007Z" }, + { url = "https://files.pythonhosted.org/packages/b6/36/d5d1ae8374612c98f390e1ea7c610fa6c9716259a03bbf4d15b269f40073/nh3-0.3.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0a09f51806fd51b4fedbf9ea2b61fef388f19aef0d62fe51199d41648be14588", size = 1008415, upload-time = "2026-04-25T10:43:44.324Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8f/d13a9c3fd2d9c131a2a281737380e9379eb0f8c33fea24c2b923aaafbb15/nh3-0.3.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c357f1d042c67f135a5e6babb2b0e3b9d9224ff4a3543240f597767b01384ffd", size = 1092706, upload-time = "2026-04-25T10:43:45.653Z" }, + { url = "https://files.pythonhosted.org/packages/bb/57/2f3add7f8680fcc896afa6a675cb2bab09982853ee8af40bad621f6b61c4/nh3-0.3.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:38748140bf76383ab7ce2dce0ad4cb663855d8fbc9098f7f3483673d09616a17", size = 1048346, upload-time = "2026-04-25T10:43:46.974Z" }, + { url = "https://files.pythonhosted.org/packages/c1/c3/2f9e4ffa82863074d1361bfe949bc46393d91b3411579dfbbd090b24cac5/nh3-0.3.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:84bdeb082544fbcb77a12c034dd77d7da0556fdc0727b787eb6214b958c15e29", size = 1029038, upload-time = "2026-04-25T10:43:48.569Z" }, + { url = "https://files.pythonhosted.org/packages/e8/10/2804deb3f3315184c9cae41702e293c87524b5a21f766b07d7fe3ffbcfbb/nh3-0.3.5-cp314-cp314t-win32.whl", hash = "sha256:c3aae321f67ae66cff2a627115f106a377d4475d10b0e13d97959a13486b9a88", size = 603263, upload-time = "2026-04-25T10:43:49.851Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a2/f6685248b49f7548fc9a8c335ab3a52f68610b72e8a61576447151e4e2e6/nh3-0.3.5-cp314-cp314t-win_amd64.whl", hash = "sha256:c88605d8d468f7fc1b31e06129bc91d6c96f6c621776c9b504a0da9beac9df5f", size = 616866, upload-time = "2026-04-25T10:43:51.005Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b6/d8c9018635d4acfefde6b68470daa510eed715a350cbaa2f928ba0609f81/nh3-0.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:72c5bdedec27fa33de6a5326346ea8aa3fe54f6ac294d54c4b204fb66a9f1e79", size = 602566, upload-time = "2026-04-25T10:43:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/85/30/d162e99746a2fb1d98bb0ef23af3e201b156cf09f7de867c7390c8fe1c06/nh3-0.3.5-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:3bb854485c9b33e5bb143ff3e49e577073bc6bc320f0ff8fc316dd89c0d3c101", size = 1442393, upload-time = "2026-04-25T10:43:53.556Z" }, + { url = "https://files.pythonhosted.org/packages/25/8c/072120d506978ab053e1732d0efa7c86cb478fee0ee098fda0ac0d31cb34/nh3-0.3.5-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50d401ab2d8e86d59e2126e3ab2a2f45840c405842b626d9a51624b3a33b6878", size = 837722, upload-time = "2026-04-25T10:43:55.073Z" }, + { url = "https://files.pythonhosted.org/packages/52/86/d4e06e28c5ad1c4b065f89737d02631bd49f1660b6ebcf17a87ffcd201da/nh3-0.3.5-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:acfd354e61accbe4c74f8017c6e397a776916dfe47c48643cf7fd84ade826f93", size = 822872, upload-time = "2026-04-25T10:43:56.581Z" }, + { url = "https://files.pythonhosted.org/packages/0a/62/50659255213f241ec5797ae7427464c969397373e83b3659372b341ae869/nh3-0.3.5-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:52d877980d7ca01dc3baf3936bf844828bc6f332962227a684ed79c18cce14c3", size = 1100031, upload-time = "2026-04-25T10:43:58.098Z" }, + { url = "https://files.pythonhosted.org/packages/00/7a/a12ae77593b2fcf3be25df7bc1c01967d0de448bdb4b6c7ec80fe4f5a74f/nh3-0.3.5-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:207c01801d3e9bb8ec08f08689346bdd30ce15b8bf60013a925d08b5388962a4", size = 1057669, upload-time = "2026-04-25T10:43:59.328Z" }, + { url = "https://files.pythonhosted.org/packages/2d/71/5647dc04c0233192a3956fc91708822b21403a06508cacf78083c68e7bf0/nh3-0.3.5-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea232933394d1d58bf7c4bb348dc4660eae6604e1ae81cd2ba6d9ed80d390f3b", size = 914795, upload-time = "2026-04-25T10:44:00.52Z" }, + { url = "https://files.pythonhosted.org/packages/1b/0e/bf298920729f216adcb002acf7ea01b90842603d2e4e2ce9b900d9ee8fab/nh3-0.3.5-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe3a787dc76b50de6bee54ef242f26c41dfe47654428e3e94f0fae5bb6dd2cc1", size = 806976, upload-time = "2026-04-25T10:44:01.743Z" }, + { url = "https://files.pythonhosted.org/packages/85/01/26761e1dc2b848e65a62c19e5d39ad446283287cd4afddc89f364ab86bc9/nh3-0.3.5-cp38-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:488928988caad25ba14b1eb5bc74e25e21f3b5e40341d956f3ce4a8bc19460dc", size = 834904, upload-time = "2026-04-25T10:44:03.454Z" }, + { url = "https://files.pythonhosted.org/packages/33/53/0766113e679540ac1edc1b82b1295aecd321eeb75d6fead70109a838b6ee/nh3-0.3.5-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c069570b06aa848457713ad7af4a9905691291548c4466a9ad78ee95808382b", size = 857159, upload-time = "2026-04-25T10:44:05.003Z" }, + { url = "https://files.pythonhosted.org/packages/58/36/734d353dfaf292fed574b8b3092f0ef79dc6404f3879f7faaa61a4701fad/nh3-0.3.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:eeedc90ed8c42c327e8e10e621ccfa314fc6cce35d5929f4297ff1cdb89667c4", size = 1018600, upload-time = "2026-04-25T10:44:06.18Z" }, + { url = "https://files.pythonhosted.org/packages/6b/aa/d9c59c1b49669fcb7bababa55df82385f029ad5c2651f583c3a1141cfdd1/nh3-0.3.5-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:de8e8621853b6470fe928c684ee0d3f39ea8086cebafe4c416486488dea7b68d", size = 1103530, upload-time = "2026-04-25T10:44:07.68Z" }, + { url = "https://files.pythonhosted.org/packages/90/b0/cdd210bfb8d9d43fb02fc3c868336b9955934d8e15e66eb1d15a147b8af0/nh3-0.3.5-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:6ea58cc44d274c643b83547ca9654a0b1a817609b160601356f76a2b744c49ad", size = 1061754, upload-time = "2026-04-25T10:44:09.362Z" }, + { url = "https://files.pythonhosted.org/packages/ce/cb/7a39e72e668c8445bdd95e494b3e21cfdddc68329be8ea3522c8befb46c4/nh3-0.3.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e49c9b564e6bcb03ecd2f057213df9a0de15a95812ac9db9600b590db23d3ae9", size = 1040938, upload-time = "2026-04-25T10:44:10.775Z" }, + { url = "https://files.pythonhosted.org/packages/af/4c/fc2f9ed208a3801a319f59b5fea03cdc20cf3bd8af14be930d3a8de01224/nh3-0.3.5-cp38-abi3-win32.whl", hash = "sha256:559e4c73b689e9a7aa97ac9760b1bc488038d7c1a575aa4ab5a0e19ee9630c0f", size = 611445, upload-time = "2026-04-25T10:44:12.317Z" }, + { url = "https://files.pythonhosted.org/packages/db/1a/e4c9b5e2ae13e6092c9ec16d8ca30646cb01fcdea245f36c5b08fd21fbd5/nh3-0.3.5-cp38-abi3-win_amd64.whl", hash = "sha256:45e6a65dc88a300a2e3502cb9c8e6d1d6b831d6fba7470643333609c6aab1f30", size = 626502, upload-time = "2026-04-25T10:44:13.682Z" }, + { url = "https://files.pythonhosted.org/packages/80/7c/19cd0671d1ba2762fb388fc149697d20d0568ccfeef833b11280a619e526/nh3-0.3.5-cp38-abi3-win_arm64.whl", hash = "sha256:8f85285700a18e9f3fc5bff41fe573fa84f81542ef13b48a89f9fecca0474d3b", size = 611069, upload-time = "2026-04-25T10:44:14.934Z" }, ] [[package]] @@ -3878,7 +3878,7 @@ wheels = [ [[package]] name = "onnx-ir" -version = "0.2.0" +version = "0.2.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32'", @@ -3908,9 +3908,9 @@ dependencies = [ { name = "sympy" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b2/a5/acc43c8fa6edbc584d127fb6bbd13ae9ebfc01b9675c74e0da2de15fa4a6/onnx_ir-0.2.0.tar.gz", hash = "sha256:8bad3906691987290789b26d05e0dbff467029a0b1e411e12e4cae02e43503e4", size = 141693, upload-time = "2026-02-24T02:31:10.998Z" } +sdist = { url = "https://files.pythonhosted.org/packages/35/e6/672fefb2f108d077f58181a7babf4c0f8d1182a30353ffc9c79c63afc5ee/onnx_ir-0.2.1.tar.gz", hash = "sha256:8b8b10a93f43e65962104de6070c43c5dacb0e3cdfefc7c8059dd83c9db64f35", size = 144279, upload-time = "2026-04-20T20:21:47.735Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/df/a99736bcca6b16e36c687ce4996abcf4ce73c514fddd9e730cfcb6a334f2/onnx_ir-0.2.0-py3-none-any.whl", hash = "sha256:eb14d1399c2442bd1ff702719e70074e9cedfa3af5729416a32752c9e0f82591", size = 164100, upload-time = "2026-02-24T02:31:09.454Z" }, + { url = "https://files.pythonhosted.org/packages/8c/aa/f7a53321c60b9ad9ee184b6018292ed6b5389947592a2c8c09c736bb7f9e/onnx_ir-0.2.1-py3-none-any.whl", hash = "sha256:c7285da889312f91882de2092e298a9eeeefbfc1d1951c49d983992967eb09a7", size = 166792, upload-time = "2026-04-20T20:21:46.357Z" }, ] [[package]] @@ -3946,7 +3946,7 @@ wheels = [ [[package]] name = "onnxscript" -version = "0.6.2" +version = "0.7.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32'", @@ -3973,14 +3973,14 @@ dependencies = [ { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts'" }, { name = "onnx", version = "1.21.0", source = { registry = "https://pypi.org/simple" } }, - { name = "onnx-ir", version = "0.2.0", source = { registry = "https://pypi.org/simple" } }, + { name = "onnx-ir", version = "0.2.1", source = { registry = "https://pypi.org/simple" } }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/2b/538fdeb0e25bed5d7e0f954af5710543e2629499fb74381afc3333f8a8ae/onnxscript-0.6.2.tar.gz", hash = "sha256:abb2e6f464db40c9b8c7fbb3e64cca04cf3f4495e67c4eda5eac17b784191ce3", size = 590865, upload-time = "2026-02-10T22:53:39.638Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/99/fd948eba63ba65b52265a4cd09a14f96bb9f5b730fcef58876c4358bf406/onnxscript-0.7.0.tar.gz", hash = "sha256:c95ed7b339b02cface56ee27689565c46612e1fc542c562298dddfdad5268dc5", size = 612032, upload-time = "2026-04-20T17:09:19.775Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/66/56/e6b179397497ab93266b6eb00743403a6a699a29063a423c4a14595d3db9/onnxscript-0.6.2-py3-none-any.whl", hash = "sha256:20e3c3fd1da19b3655549d5455a2df719db47374fe430e01e865ae69127c37b9", size = 689064, upload-time = "2026-02-10T22:53:41.663Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ce/2ed92575cc3be4ea1db5f38f16f20765f9b20b69b14d6c1d9972658a8ee9/onnxscript-0.7.0-py3-none-any.whl", hash = "sha256:5b356907d4501e9919f8599c91d8da967406a37b1fac2b40caa55a49acf242ea", size = 714842, upload-time = "2026-04-20T17:09:22.089Z" }, ] [[package]] @@ -4066,7 +4066,7 @@ wheels = [ [[package]] name = "opentelemetry-api" -version = "1.41.0" +version = "1.41.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'win32'", @@ -4083,9 +4083,9 @@ dependencies = [ { name = "importlib-metadata", marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, { name = "typing-extensions", marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/47/8e/3778a7e87801d994869a9396b9fc2a289e5f9be91ff54a27d41eace494b0/opentelemetry_api-1.41.0.tar.gz", hash = "sha256:9421d911326ec12dee8bc933f7839090cad7a3f13fcfb0f9e82f8174dc003c09", size = 71416, upload-time = "2026-04-09T14:38:34.544Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/fc/b7564cbef36601aef0d6c9bc01f7badb64be8e862c2e1c3c5c3b43b53e4f/opentelemetry_api-1.41.1.tar.gz", hash = "sha256:0ad1814d73b875f84494387dae86ce0b12c68556331ce6ce8fe789197c949621", size = 71416, upload-time = "2026-04-24T13:15:38.262Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/ee/99ab786653b3bda9c37ade7e24a7b607a1b1f696063172768417539d876d/opentelemetry_api-1.41.0-py3-none-any.whl", hash = "sha256:0e77c806e6a89c9e4f8d372034622f3e1418a11bdbe1c80a50b3d3397ad0fa4f", size = 69007, upload-time = "2026-04-09T14:38:11.833Z" }, + { url = "https://files.pythonhosted.org/packages/29/59/3e7118ed140f76b0982ba4321bdaed1997a0473f9720de2d10788a577033/opentelemetry_api-1.41.1-py3-none-any.whl", hash = "sha256:a22df900e75c76dc08440710e51f52f1aa6b451b429298896023e60db5b3139f", size = 69007, upload-time = "2026-04-24T13:15:15.662Z" }, ] [[package]] @@ -4124,7 +4124,7 @@ wheels = [ [[package]] name = "opentelemetry-exporter-prometheus" -version = "0.62b0" +version = "0.62b1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'win32'", @@ -4138,25 +4138,25 @@ resolution-markers = [ "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "opentelemetry-api", version = "1.41.0", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, - { name = "opentelemetry-sdk", version = "1.41.0", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, + { name = "opentelemetry-api", version = "1.41.1", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, + { name = "opentelemetry-sdk", version = "1.41.1", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, { name = "prometheus-client", marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0f/ec/fa8a722199dc2e75dc582779d62207b00b0bdb014b5635594afa0cf3ee43/opentelemetry_exporter_prometheus-0.62b0.tar.gz", hash = "sha256:4d1106566a9b3e8dff028e69e9f2dc90723e6b431c900ff8c72982fcf11dbae5", size = 15441, upload-time = "2026-04-09T14:38:38.934Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/03/e1fbf14386ef171b949b71fba7c18643691a9390ab38c221df582e916569/opentelemetry_exporter_prometheus-0.62b1.tar.gz", hash = "sha256:7ecbac9aa76e7abb44082ab0ff2983e0a573e4091c4653f7db483b02bae03506", size = 15446, upload-time = "2026-04-24T13:15:43.783Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/1e/43645fadd561471af2aec95906a3dd54af1a8e7782322310e802a810ad3a/opentelemetry_exporter_prometheus-0.62b0-py3-none-any.whl", hash = "sha256:cd7e8acae3be5f425ffa2e0864eea474fa7a40706f786de7a2d23846573d8f75", size = 13278, upload-time = "2026-04-09T14:38:19.367Z" }, + { url = "https://files.pythonhosted.org/packages/c8/d2/ee4002b88e20c59fae52bed008a63c6f7eff7d498f302032f6b0434a6de7/opentelemetry_exporter_prometheus-0.62b1-py3-none-any.whl", hash = "sha256:7a0b8a6402e107e1f93e38f074a668797e1103936b189561959531a67ffeba55", size = 13278, upload-time = "2026-04-24T13:15:22.485Z" }, ] [[package]] name = "opentelemetry-proto" -version = "1.41.0" +version = "1.41.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e0/d9/08e3dc6156878713e8c811682bc76151f5fe1a3cb7f3abda3966fd56e71e/opentelemetry_proto-1.41.0.tar.gz", hash = "sha256:95d2e576f9fb1800473a3e4cfcca054295d06bdb869fda4dc9f4f779dc68f7b6", size = 45669, upload-time = "2026-04-09T14:38:45.978Z" } +sdist = { url = "https://files.pythonhosted.org/packages/99/e8/633c6d8a9c8840338b105907e55c32d3da1983abab5e52f899f72a82c3d1/opentelemetry_proto-1.41.1.tar.gz", hash = "sha256:4b9d2eb631237ea43b80e16c073af438554e32bc7e9e3f8ca4a9582f900020e5", size = 45670, upload-time = "2026-04-24T13:15:49.768Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/49/8c/65ef7a9383a363864772022e822b5d5c6988e6f9dabeebb9278f5b86ebc3/opentelemetry_proto-1.41.0-py3-none-any.whl", hash = "sha256:b970ab537309f9eed296be482c3e7cca05d8aca8165346e929f658dbe153b247", size = 72074, upload-time = "2026-04-09T14:38:29.38Z" }, + { url = "https://files.pythonhosted.org/packages/e4/1e/5cd77035e3e82070e2265a63a760f715aacd3cb16dddc7efee913f297fcc/opentelemetry_proto-1.41.1-py3-none-any.whl", hash = "sha256:0496713b804d127a4147e32849fbaf5683fac8ee98550e8e7679cd706c289720", size = 72076, upload-time = "2026-04-24T13:15:32.542Z" }, ] [[package]] @@ -4195,7 +4195,7 @@ wheels = [ [[package]] name = "opentelemetry-sdk" -version = "1.41.0" +version = "1.41.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'win32'", @@ -4209,13 +4209,13 @@ resolution-markers = [ "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "opentelemetry-api", version = "1.41.0", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, - { name = "opentelemetry-semantic-conventions", version = "0.62b0", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, + { name = "opentelemetry-api", version = "1.41.1", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, + { name = "opentelemetry-semantic-conventions", version = "0.62b1", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, { name = "typing-extensions", marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/0e/a586df1186f9f56b5a0879d52653effc40357b8e88fc50fe300038c3c08b/opentelemetry_sdk-1.41.0.tar.gz", hash = "sha256:7bddf3961131b318fc2d158947971a8e37e38b1cd23470cfb72b624e7cc108bd", size = 230181, upload-time = "2026-04-09T14:38:47.225Z" } +sdist = { url = "https://files.pythonhosted.org/packages/58/d0/54ee30dab82fb0acda23d144502771ff76ef8728459c83c3e89ef9fb1825/opentelemetry_sdk-1.41.1.tar.gz", hash = "sha256:724b615e1215b5aeacda0abb8a6a8922c9a1853068948bd0bd225a56d0c792e6", size = 230180, upload-time = "2026-04-24T13:15:50.991Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/13/a7825118208cb32e6a4edcd0a99f925cbef81e77b3b0aedfd9125583c543/opentelemetry_sdk-1.41.0-py3-none-any.whl", hash = "sha256:a596f5687964a3e0d7f8edfdcf5b79cbca9c93c7025ebf5fb00f398a9443b0bd", size = 180214, upload-time = "2026-04-09T14:38:30.657Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e7/a1420b698aad018e1cf60fdbaaccbe49021fb415e2a0d81c242f4c518f54/opentelemetry_sdk-1.41.1-py3-none-any.whl", hash = "sha256:edee379c126c1bce952b0c812b48fe8ff35b30df0eecf17e98afa4d598b7d85d", size = 180213, upload-time = "2026-04-24T13:15:33.767Z" }, ] [[package]] @@ -4253,7 +4253,7 @@ wheels = [ [[package]] name = "opentelemetry-semantic-conventions" -version = "0.62b0" +version = "0.62b1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'win32'", @@ -4267,12 +4267,12 @@ resolution-markers = [ "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "opentelemetry-api", version = "1.41.0", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, + { name = "opentelemetry-api", version = "1.41.1", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, { name = "typing-extensions", marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/b0/c14f723e86c049b7bf8ff431160d982519b97a7be2857ed2247377397a24/opentelemetry_semantic_conventions-0.62b0.tar.gz", hash = "sha256:cbfb3c8fc259575cf68a6e1b94083cc35adc4a6b06e8cf431efa0d62606c0097", size = 145753, upload-time = "2026-04-09T14:38:48.274Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/de/911ac9e309052aca1b20b2d5549d3db45d1011e1a610e552c6ccdd1b64f8/opentelemetry_semantic_conventions-0.62b1.tar.gz", hash = "sha256:c5cc6e04a7f8c7cdd30be2ed81499fa4e75bfbd52c9cb70d40af1f9cd3619802", size = 145750, upload-time = "2026-04-24T13:15:52.236Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/6c/5e86fa1759a525ef91c2d8b79d668574760ff3f900d114297765eb8786cb/opentelemetry_semantic_conventions-0.62b0-py3-none-any.whl", hash = "sha256:0ddac1ce59eaf1a827d9987ab60d9315fb27aea23304144242d1fcad9e16b489", size = 231619, upload-time = "2026-04-09T14:38:32.394Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a6/83dc2ab6fa397ee66fba04fe2e74bdf7be3b3870005359ceb7689103c058/opentelemetry_semantic_conventions-0.62b1-py3-none-any.whl", hash = "sha256:cf506938103d331fbb78eded0d9788095f7fd59016f2bda813c3324e5a74a93c", size = 231620, upload-time = "2026-04-24T13:15:35.454Z" }, ] [[package]] @@ -4359,7 +4359,7 @@ wheels = [ [[package]] name = "packaging" -version = "26.1" +version = "26.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", @@ -4390,9 +4390,9 @@ resolution-markers = [ "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", ] -sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519, upload-time = "2026-04-14T21:12:49.362Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" }, + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, ] [[package]] @@ -4560,11 +4560,11 @@ wheels = [ [[package]] name = "pathspec" -version = "1.0.4" +version = "1.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, ] [[package]] @@ -4868,60 +4868,60 @@ wheels = [ [[package]] name = "py-spy" -version = "0.4.1" +version = "0.4.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/e2/ff811a367028b87e86714945bb9ecb5c1cc69114a8039a67b3a862cef921/py_spy-0.4.1.tar.gz", hash = "sha256:e53aa53daa2e47c2eef97dd2455b47bb3a7e7f962796a86cc3e7dbde8e6f4db4", size = 244726, upload-time = "2025-07-31T19:33:25.172Z" } +sdist = { url = "https://files.pythonhosted.org/packages/93/d8/5b71371f50cf153b1307e5a11ac8a4ce4d85651dae946bd7e9a064146545/py_spy-0.4.2.tar.gz", hash = "sha256:90e600b27bb6bb40479637baca5a5b4bc2ba3395c93d889e672315d93042c4ae", size = 286374, upload-time = "2026-04-24T22:08:54.906Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/e3/3a32500d845bdd94f6a2b4ed6244982f42ec2bc64602ea8fcfe900678ae7/py_spy-0.4.1-py2.py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:809094208c6256c8f4ccadd31e9a513fe2429253f48e20066879239ba12cd8cc", size = 3682508, upload-time = "2025-07-31T19:33:13.753Z" }, - { url = "https://files.pythonhosted.org/packages/4f/bf/e4d280e9e0bec71d39fc646654097027d4bbe8e04af18fb68e49afcff404/py_spy-0.4.1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:1fb8bf71ab8df95a95cc387deed6552934c50feef2cf6456bc06692a5508fd0c", size = 1796395, upload-time = "2025-07-31T19:33:15.325Z" }, - { url = "https://files.pythonhosted.org/packages/df/79/9ed50bb0a9de63ed023aa2db8b6265b04a7760d98c61eb54def6a5fddb68/py_spy-0.4.1-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee776b9d512a011d1ad3907ed53ae32ce2f3d9ff3e1782236554e22103b5c084", size = 2034938, upload-time = "2025-07-31T19:33:17.194Z" }, - { url = "https://files.pythonhosted.org/packages/53/a5/36862e3eea59f729dfb70ee6f9e14b051d8ddce1aa7e70e0b81d9fe18536/py_spy-0.4.1-py2.py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:532d3525538254d1859b49de1fbe9744df6b8865657c9f0e444bf36ce3f19226", size = 2658968, upload-time = "2025-07-31T19:33:18.916Z" }, - { url = "https://files.pythonhosted.org/packages/08/f8/9ea0b586b065a623f591e5e7961282ec944b5fbbdca33186c7c0296645b3/py_spy-0.4.1-py2.py3-none-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4972c21890b6814017e39ac233c22572c4a61fd874524ebc5ccab0f2237aee0a", size = 2147541, upload-time = "2025-07-31T19:33:20.565Z" }, - { url = "https://files.pythonhosted.org/packages/68/fb/bc7f639aed026bca6e7beb1e33f6951e16b7d315594e7635a4f7d21d63f4/py_spy-0.4.1-py2.py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6a80ec05eb8a6883863a367c6a4d4f2d57de68466f7956b6367d4edd5c61bb29", size = 2763338, upload-time = "2025-07-31T19:33:22.202Z" }, - { url = "https://files.pythonhosted.org/packages/e1/da/fcc9a9fcd4ca946ff402cff20348e838b051d69f50f5d1f5dca4cd3c5eb8/py_spy-0.4.1-py2.py3-none-win_amd64.whl", hash = "sha256:d92e522bd40e9bf7d87c204033ce5bb5c828fca45fa28d970f58d71128069fdc", size = 1818784, upload-time = "2025-07-31T19:33:23.802Z" }, + { url = "https://files.pythonhosted.org/packages/ef/21/ec030145a0c7992bd4b9eafb2f06f56358b3a5339eab4a16534baf3c69aa/py_spy-0.4.2-py2.py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:1ccf688393105111684435f035bc14ec3f22117dd2b85b2414612cf27a22755a", size = 3743992, upload-time = "2026-04-24T22:08:45.438Z" }, + { url = "https://files.pythonhosted.org/packages/50/80/de5fd27243c2be03692ecd317bf0dbe24b4c6f78f689ce111e7277a7cb09/py_spy-0.4.2-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:a0e6f6810ccf0fc5e64e85e0182a5b626c4496eec01b14fb8755154b363a4831", size = 1859057, upload-time = "2026-04-24T22:08:46.946Z" }, + { url = "https://files.pythonhosted.org/packages/89/23/3eb4c23c684ebd667674ce1d076ae855e0621d1d9bd5e052aa3f7982f757/py_spy-0.4.2-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:142887e984a4e541071c99a4401ff8c3770f255d329dbd0f64e8c1dd51882cce", size = 2828136, upload-time = "2026-04-24T22:08:48.519Z" }, + { url = "https://files.pythonhosted.org/packages/ca/01/6314152cf9ad3310ebacbf2c47b5ed858086530f8e12b1a665725ca5e0f4/py_spy-0.4.2-py2.py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f1c6d9b0e2379ead5bf792df43f4cf36153aa79e6dda4fb8ac7740cf8017110", size = 2857707, upload-time = "2026-04-24T22:08:49.677Z" }, + { url = "https://files.pythonhosted.org/packages/cc/1f/0960a129d504728d28a51dbd5a04ce94031eb75bac676341da7aefdd8232/py_spy-0.4.2-py2.py3-none-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:24720573f95230653b457671a1dcc3c5a381fcf4e92677761e328a430ad251b2", size = 2301852, upload-time = "2026-04-24T22:08:51.152Z" }, + { url = "https://files.pythonhosted.org/packages/f9/34/dd7d3c763a00b7b965e25a5eab0acd1a345dbaf0f45fffe595278873a1c0/py_spy-0.4.2-py2.py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:aeb0323409199c785f730645e9f4bb7a7b9ca2c481f2c331a55642b5d13fa52f", size = 2936518, upload-time = "2026-04-24T22:08:52.264Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ed/1409cdb557e558a6c98003ab12fdd4284699e158c167c187cb0f124eea4c/py_spy-0.4.2-py2.py3-none-win_amd64.whl", hash = "sha256:8b06a353c177677e4e1701b288d8c58e2f8d4208ee81a8048d9f72ba800918f8", size = 1894002, upload-time = "2026-04-24T22:08:53.811Z" }, ] [[package]] name = "pyarrow" -version = "23.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/88/22/134986a4cc224d593c1afde5494d18ff629393d74cc2eddb176669f234a4/pyarrow-23.0.1.tar.gz", hash = "sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019", size = 1167336, upload-time = "2026-02-16T10:14:12.39Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/4b/4166bb5abbfe6f750fc60ad337c43ecf61340fa52ab386da6e8dbf9e63c4/pyarrow-23.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f", size = 34214575, upload-time = "2026-02-16T10:09:56.225Z" }, - { url = "https://files.pythonhosted.org/packages/e1/da/3f941e3734ac8088ea588b53e860baeddac8323ea40ce22e3d0baa865cc9/pyarrow-23.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7", size = 35832540, upload-time = "2026-02-16T10:10:03.428Z" }, - { url = "https://files.pythonhosted.org/packages/88/7c/3d841c366620e906d54430817531b877ba646310296df42ef697308c2705/pyarrow-23.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9", size = 44470940, upload-time = "2026-02-16T10:10:10.704Z" }, - { url = "https://files.pythonhosted.org/packages/2c/a5/da83046273d990f256cb79796a190bbf7ec999269705ddc609403f8c6b06/pyarrow-23.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05", size = 47586063, upload-time = "2026-02-16T10:10:17.95Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/b7d2ebcff47a514f47f9da1e74b7949138c58cfeb108cdd4ee62f43f0cf3/pyarrow-23.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67", size = 48173045, upload-time = "2026-02-16T10:10:25.363Z" }, - { url = "https://files.pythonhosted.org/packages/43/b2/b40961262213beaba6acfc88698eb773dfce32ecdf34d19291db94c2bd73/pyarrow-23.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730", size = 50621741, upload-time = "2026-02-16T10:10:33.477Z" }, - { url = "https://files.pythonhosted.org/packages/f6/70/1fdda42d65b28b078e93d75d371b2185a61da89dda4def8ba6ba41ebdeb4/pyarrow-23.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0", size = 27620678, upload-time = "2026-02-16T10:10:39.31Z" }, - { url = "https://files.pythonhosted.org/packages/47/10/2cbe4c6f0fb83d2de37249567373d64327a5e4d8db72f486db42875b08f6/pyarrow-23.0.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8", size = 34210066, upload-time = "2026-02-16T10:10:45.487Z" }, - { url = "https://files.pythonhosted.org/packages/cb/4f/679fa7e84dadbaca7a65f7cdba8d6c83febbd93ca12fa4adf40ba3b6362b/pyarrow-23.0.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f", size = 35825526, upload-time = "2026-02-16T10:10:52.266Z" }, - { url = "https://files.pythonhosted.org/packages/f9/63/d2747d930882c9d661e9398eefc54f15696547b8983aaaf11d4a2e8b5426/pyarrow-23.0.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677", size = 44473279, upload-time = "2026-02-16T10:11:01.557Z" }, - { url = "https://files.pythonhosted.org/packages/b3/93/10a48b5e238de6d562a411af6467e71e7aedbc9b87f8d3a35f1560ae30fb/pyarrow-23.0.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2", size = 47585798, upload-time = "2026-02-16T10:11:09.401Z" }, - { url = "https://files.pythonhosted.org/packages/5c/20/476943001c54ef078dbf9542280e22741219a184a0632862bca4feccd666/pyarrow-23.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37", size = 48179446, upload-time = "2026-02-16T10:11:17.781Z" }, - { url = "https://files.pythonhosted.org/packages/4b/b6/5dd0c47b335fcd8edba9bfab78ad961bd0fd55ebe53468cc393f45e0be60/pyarrow-23.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2", size = 50623972, upload-time = "2026-02-16T10:11:26.185Z" }, - { url = "https://files.pythonhosted.org/packages/d5/09/a532297c9591a727d67760e2e756b83905dd89adb365a7f6e9c72578bcc1/pyarrow-23.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a", size = 27540749, upload-time = "2026-02-16T10:12:23.297Z" }, - { url = "https://files.pythonhosted.org/packages/a5/8e/38749c4b1303e6ae76b3c80618f84861ae0c55dd3c2273842ea6f8258233/pyarrow-23.0.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1", size = 34471544, upload-time = "2026-02-16T10:11:32.535Z" }, - { url = "https://files.pythonhosted.org/packages/a3/73/f237b2bc8c669212f842bcfd842b04fc8d936bfc9d471630569132dc920d/pyarrow-23.0.1-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500", size = 35949911, upload-time = "2026-02-16T10:11:39.813Z" }, - { url = "https://files.pythonhosted.org/packages/0c/86/b912195eee0903b5611bf596833def7d146ab2d301afeb4b722c57ffc966/pyarrow-23.0.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41", size = 44520337, upload-time = "2026-02-16T10:11:47.764Z" }, - { url = "https://files.pythonhosted.org/packages/69/c2/f2a717fb824f62d0be952ea724b4f6f9372a17eed6f704b5c9526f12f2f1/pyarrow-23.0.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07", size = 47548944, upload-time = "2026-02-16T10:11:56.607Z" }, - { url = "https://files.pythonhosted.org/packages/84/a7/90007d476b9f0dc308e3bc57b832d004f848fd6c0da601375d20d92d1519/pyarrow-23.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83", size = 48236269, upload-time = "2026-02-16T10:12:04.47Z" }, - { url = "https://files.pythonhosted.org/packages/b0/3f/b16fab3e77709856eb6ac328ce35f57a6d4a18462c7ca5186ef31b45e0e0/pyarrow-23.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125", size = 50604794, upload-time = "2026-02-16T10:12:11.797Z" }, - { url = "https://files.pythonhosted.org/packages/e9/a1/22df0620a9fac31d68397a75465c344e83c3dfe521f7612aea33e27ab6c0/pyarrow-23.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8", size = 27660642, upload-time = "2026-02-16T10:12:17.746Z" }, - { url = "https://files.pythonhosted.org/packages/8d/1b/6da9a89583ce7b23ac611f183ae4843cd3a6cf54f079549b0e8c14031e73/pyarrow-23.0.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:5df1161da23636a70838099d4aaa65142777185cc0cdba4037a18cee7d8db9ca", size = 34238755, upload-time = "2026-02-16T10:12:32.819Z" }, - { url = "https://files.pythonhosted.org/packages/ae/b5/d58a241fbe324dbaeb8df07be6af8752c846192d78d2272e551098f74e88/pyarrow-23.0.1-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1", size = 35847826, upload-time = "2026-02-16T10:12:38.949Z" }, - { url = "https://files.pythonhosted.org/packages/54/a5/8cbc83f04aba433ca7b331b38f39e000efd9f0c7ce47128670e737542996/pyarrow-23.0.1-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:0b95a3994f015be13c63148fef8832e8a23938128c185ee951c98908a696e0eb", size = 44536859, upload-time = "2026-02-16T10:12:45.467Z" }, - { url = "https://files.pythonhosted.org/packages/36/2e/c0f017c405fcdc252dbccafbe05e36b0d0eb1ea9a958f081e01c6972927f/pyarrow-23.0.1-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:4982d71350b1a6e5cfe1af742c53dfb759b11ce14141870d05d9e540d13bc5d1", size = 47614443, upload-time = "2026-02-16T10:12:55.525Z" }, - { url = "https://files.pythonhosted.org/packages/af/6b/2314a78057912f5627afa13ba43809d9d653e6630859618b0fd81a4e0759/pyarrow-23.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c250248f1fe266db627921c89b47b7c06fee0489ad95b04d50353537d74d6886", size = 48232991, upload-time = "2026-02-16T10:13:04.729Z" }, - { url = "https://files.pythonhosted.org/packages/40/f2/1bcb1d3be3460832ef3370d621142216e15a2c7c62602a4ea19ec240dd64/pyarrow-23.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f4763b83c11c16e5f4c15601ba6dfa849e20723b46aa2617cb4bffe8768479f", size = 50645077, upload-time = "2026-02-16T10:13:14.147Z" }, - { url = "https://files.pythonhosted.org/packages/eb/3f/b1da7b61cd66566a4d4c8383d376c606d1c34a906c3f1cb35c479f59d1aa/pyarrow-23.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:3a4c85ef66c134161987c17b147d6bffdca4566f9a4c1d81a0a01cdf08414ea5", size = 28234271, upload-time = "2026-02-16T10:14:09.397Z" }, - { url = "https://files.pythonhosted.org/packages/b5/78/07f67434e910a0f7323269be7bfbf58699bd0c1d080b18a1ab49ba943fe8/pyarrow-23.0.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:17cd28e906c18af486a499422740298c52d7c6795344ea5002a7720b4eadf16d", size = 34488692, upload-time = "2026-02-16T10:13:21.541Z" }, - { url = "https://files.pythonhosted.org/packages/50/76/34cf7ae93ece1f740a04910d9f7e80ba166b9b4ab9596a953e9e62b90fe1/pyarrow-23.0.1-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:76e823d0e86b4fb5e1cf4a58d293036e678b5a4b03539be933d3b31f9406859f", size = 35964383, upload-time = "2026-02-16T10:13:28.63Z" }, - { url = "https://files.pythonhosted.org/packages/46/90/459b827238936d4244214be7c684e1b366a63f8c78c380807ae25ed92199/pyarrow-23.0.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a62e1899e3078bf65943078b3ad2a6ddcacf2373bc06379aac61b1e548a75814", size = 44538119, upload-time = "2026-02-16T10:13:35.506Z" }, - { url = "https://files.pythonhosted.org/packages/28/a1/93a71ae5881e99d1f9de1d4554a87be37da11cd6b152239fb5bd924fdc64/pyarrow-23.0.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:df088e8f640c9fae3b1f495b3c64755c4e719091caf250f3a74d095ddf3c836d", size = 47571199, upload-time = "2026-02-16T10:13:42.504Z" }, - { url = "https://files.pythonhosted.org/packages/88/a3/d2c462d4ef313521eaf2eff04d204ac60775263f1fb08c374b543f79f610/pyarrow-23.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:46718a220d64677c93bc243af1d44b55998255427588e400677d7192671845c7", size = 48259435, upload-time = "2026-02-16T10:13:49.226Z" }, - { url = "https://files.pythonhosted.org/packages/cc/f1/11a544b8c3d38a759eb3fbb022039117fd633e9a7b19e4841cc3da091915/pyarrow-23.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a09f3876e87f48bc2f13583ab551f0379e5dfb83210391e68ace404181a20690", size = 50629149, upload-time = "2026-02-16T10:13:57.238Z" }, - { url = "https://files.pythonhosted.org/packages/50/f2/c0e76a0b451ffdf0cf788932e182758eb7558953f4f27f1aff8e2518b653/pyarrow-23.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:527e8d899f14bd15b740cd5a54ad56b7f98044955373a17179d5956ddb93d9ce", size = 28365807, upload-time = "2026-02-16T10:14:03.892Z" }, +version = "24.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/13/13e1069b351bdc3881266e11147ffccf687505dbb0ea74036237f5d454a5/pyarrow-24.0.0.tar.gz", hash = "sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83", size = 1180261, upload-time = "2026-04-21T10:51:25.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/a9/9686d9f07837f91f775e8932659192e02c74f9d8920524b480b85212cc68/pyarrow-24.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810", size = 34981559, upload-time = "2026-04-21T10:47:22.17Z" }, + { url = "https://files.pythonhosted.org/packages/80/b6/0ddf0e9b6ead3474ab087ae598c76b031fc45532bf6a63f3a553440fb258/pyarrow-24.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a", size = 36663654, upload-time = "2026-04-21T10:47:28.315Z" }, + { url = "https://files.pythonhosted.org/packages/7c/3b/926382efe8ce27ba729071d3566ade6dfb86bdf112f366000196b2f5780a/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66", size = 45679394, upload-time = "2026-04-21T10:47:34.821Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7a/829f7d9dfd37c207206081d6dad474d81dde29952401f07f2ba507814818/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb", size = 48863122, upload-time = "2026-04-21T10:47:42.056Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e8/f88ce625fe8babaae64e8db2d417c7653adb3019b08aae85c5ed787dc816/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e", size = 49376032, upload-time = "2026-04-21T10:47:48.967Z" }, + { url = "https://files.pythonhosted.org/packages/36/7a/82c363caa145fff88fb475da50d3bf52bb024f61917be5424c3392eaf878/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6", size = 51929490, upload-time = "2026-04-21T10:47:55.981Z" }, + { url = "https://files.pythonhosted.org/packages/66/1c/e3e72c8014ad2743ca64a701652c733cc5cbcee15c0463a32a8c55518d9e/pyarrow-24.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826", size = 27355660, upload-time = "2026-04-21T10:48:01.718Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d3/a1abf004482026ddc17f4503db227787fa3cfe41ec5091ff20e4fea55e57/pyarrow-24.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba", size = 34976759, upload-time = "2026-04-21T10:48:07.258Z" }, + { url = "https://files.pythonhosted.org/packages/4f/4a/34f0a36d28a2dd32225301b79daad44e243dc1a2bb77d43b60749be255c4/pyarrow-24.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68", size = 36658471, upload-time = "2026-04-21T10:48:13.347Z" }, + { url = "https://files.pythonhosted.org/packages/1f/78/543b94712ae8bb1a6023bcc1acf1a740fbff8286747c289cd9468fced2a5/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2", size = 45675981, upload-time = "2026-04-21T10:48:20.201Z" }, + { url = "https://files.pythonhosted.org/packages/84/9f/8fb7c222b100d314137fa40ec050de56cd8c6d957d1cfff685ce72f15b17/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0", size = 48859172, upload-time = "2026-04-21T10:48:27.541Z" }, + { url = "https://files.pythonhosted.org/packages/a7/d3/1ea72538e6c8b3b475ed78d1049a2c518e655761ea50fe1171fc855fcab7/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495", size = 49385733, upload-time = "2026-04-21T10:48:34.7Z" }, + { url = "https://files.pythonhosted.org/packages/c3/be/c3d8b06a1ba35f2260f8e1f771abbee7d5e345c0937aab90675706b1690a/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f", size = 51934335, upload-time = "2026-04-21T10:48:42.099Z" }, + { url = "https://files.pythonhosted.org/packages/9c/62/89e07a1e7329d2cde3e3c6994ba0839a24977a2beda8be6005ea3d860b99/pyarrow-24.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91", size = 27271748, upload-time = "2026-04-21T10:49:42.532Z" }, + { url = "https://files.pythonhosted.org/packages/17/1a/cff3a59f80b5b1658549d46611b67163f65e0664431c076ad728bf9d5af4/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275", size = 35238554, upload-time = "2026-04-21T10:48:48.526Z" }, + { url = "https://files.pythonhosted.org/packages/a8/99/cce0f42a327bfef2c420fb6078a3eb834826e5d6697bf3009fe11d2ad051/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b", size = 36782301, upload-time = "2026-04-21T10:48:55.181Z" }, + { url = "https://files.pythonhosted.org/packages/2a/66/8e560d5ff6793ca29aca213c53eec0dd482dd46cb93b2819e5aab52e4252/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42", size = 45721929, upload-time = "2026-04-21T10:49:03.676Z" }, + { url = "https://files.pythonhosted.org/packages/27/0c/a26e25505d030716e078d9f16eb74973cbf0b33b672884e9f9da1c83b871/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b", size = 48825365, upload-time = "2026-04-21T10:49:11.714Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/771f9ecb0c65e73fe9dccdd1717901b9594f08c4515d000c7c62df573811/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37", size = 49451819, upload-time = "2026-04-21T10:49:21.474Z" }, + { url = "https://files.pythonhosted.org/packages/48/da/61ae89a88732f5a785646f3ec6125dbb640fa98a540eb2b9889caa561403/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca", size = 51909252, upload-time = "2026-04-21T10:49:31.164Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1a/8dd5cafab7b66573fa91c03d06d213356ad4edd71813aa75e08ce2b3a844/pyarrow-24.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d", size = 27388127, upload-time = "2026-04-21T10:49:37.334Z" }, + { url = "https://files.pythonhosted.org/packages/ad/80/d022a34ff05d2cbedd8ccf841fc1f532ecfa9eb5ed1711b56d0e0ea71fc9/pyarrow-24.0.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838", size = 35007997, upload-time = "2026-04-21T10:49:48.796Z" }, + { url = "https://files.pythonhosted.org/packages/1a/ff/f01485fda6f4e5d441afb8dd5e7681e4db18826c1e271852f5d3957d6a80/pyarrow-24.0.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b", size = 36678720, upload-time = "2026-04-21T10:49:55.858Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c2/2d2d5fea814237923f71b36495211f20b43a1576f9a4d6da7e751a64ec6f/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795", size = 45741852, upload-time = "2026-04-21T10:50:04.624Z" }, + { url = "https://files.pythonhosted.org/packages/8e/3a/28ba9c1c1ebdbb5f1b94dfebb46f207e52e6a554b7fe4132540fde29a3a0/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26", size = 48889852, upload-time = "2026-04-21T10:50:12.293Z" }, + { url = "https://files.pythonhosted.org/packages/df/51/4a389acfd31dca009f8fb82d7f510bb4130f2b3a8e18cf00194d0687d8ac/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde", size = 49445207, upload-time = "2026-04-21T10:50:20.677Z" }, + { url = "https://files.pythonhosted.org/packages/19/4b/0bab2b23d2ae901b1b9a03c0efd4b2d070256f8ce3fc43f6e58c167b2081/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76", size = 51954117, upload-time = "2026-04-21T10:50:29.14Z" }, + { url = "https://files.pythonhosted.org/packages/29/88/f4e9145da0417b3d2c12035a8492b35ff4a3dbc653e614fcfb51d9dedb38/pyarrow-24.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e", size = 28001155, upload-time = "2026-04-21T10:51:22.337Z" }, + { url = "https://files.pythonhosted.org/packages/79/4f/46a49a63f43526da895b1a45bbb51d5baf8e4d77159f8528fc3e5490007f/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05", size = 35250387, upload-time = "2026-04-21T10:50:35.552Z" }, + { url = "https://files.pythonhosted.org/packages/a0/da/d5e0cd5ef00796922404806d5f00325cdadc3441ce2c13fe7115f2df9a64/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a", size = 36797102, upload-time = "2026-04-21T10:50:42.417Z" }, + { url = "https://files.pythonhosted.org/packages/34/c7/5904145b0a593a05236c882933d439b5720f0a145381179063722fbfc123/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072", size = 45745118, upload-time = "2026-04-21T10:50:49.324Z" }, + { url = "https://files.pythonhosted.org/packages/13/d3/cca42fe166d1c6e4d5b80e530b7949104d10e17508a90ae202dac205ce2a/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931", size = 48844765, upload-time = "2026-04-21T10:50:55.579Z" }, + { url = "https://files.pythonhosted.org/packages/b0/49/942c3b79878ba928324d1e17c274ed84581db8c0a749b24bcf4cbdf15bd3/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699", size = 49471890, upload-time = "2026-04-21T10:51:02.439Z" }, + { url = "https://files.pythonhosted.org/packages/76/97/ff71431000a75d84135a1ace5ca4ba11726a231a8007bbb320a4c54075d5/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136", size = 51932250, upload-time = "2026-04-21T10:51:10.576Z" }, + { url = "https://files.pythonhosted.org/packages/51/be/6f79d55816d5c22557cf27533543d5d70dfe692adfbee4b99f2760674f38/pyarrow-24.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19", size = 28131282, upload-time = "2026-04-21T10:51:16.815Z" }, ] [[package]] @@ -4974,7 +4974,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.13.2" +version = "2.13.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -4982,103 +4982,103 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/09/e5/06d23afac9973109d1e3c8ad38e1547a12e860610e327c05ee686827dc37/pydantic-2.13.2.tar.gz", hash = "sha256:b418196607e61081c3226dcd4f0672f2a194828abb9109e9cfb84026564df2d1", size = 843836, upload-time = "2026-04-17T09:31:59.636Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/e4/40d09941a2cebcb20609b86a559817d5b9291c49dd6f8c87e5feffbe703a/pydantic-2.13.3.tar.gz", hash = "sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d", size = 844068, upload-time = "2026-04-20T14:46:43.632Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/ca/b45c378e6e8d0b90577288b533e04e95b7afd61bb1d51b6c263176435489/pydantic-2.13.2-py3-none-any.whl", hash = "sha256:a525087f4c03d7e7456a3de89b64cd693d2229933bb1068b9af6befd5563694e", size = 471947, upload-time = "2026-04-17T09:31:57.541Z" }, + { url = "https://files.pythonhosted.org/packages/f3/0a/fd7d723f8f8153418fb40cf9c940e82004fce7e987026b08a68a36dd3fe7/pydantic-2.13.3-py3-none-any.whl", hash = "sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927", size = 471981, upload-time = "2026-04-20T14:46:41.402Z" }, ] [[package]] name = "pydantic-core" -version = "2.46.2" +version = "2.46.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/bb/4742f05b739b2478459bb16fa8470549518c802e06ddcf3f106c5081315e/pydantic_core-2.46.2.tar.gz", hash = "sha256:37bb079f9ee3f1a519392b73fda2a96379b31f2013c6b467fe693e7f2987f596", size = 471269, upload-time = "2026-04-17T09:10:07.017Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/97/ec/2fafa4c86f5d2a69372c7cddef30925fd0e370b1efaf556609c1a0196d8a/pydantic_core-2.46.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ea1ad8c89da31512fe2d249cf0638fb666925bda341901541bc5f3311c6fcc9e", size = 2101729, upload-time = "2026-04-17T09:12:30.042Z" }, - { url = "https://files.pythonhosted.org/packages/cf/55/be5386c2c4b49af346e8a26b748194ff25757bbb6cf544130854e997af7a/pydantic_core-2.46.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b308da17b92481e0587244631c5529e5d91d04cb2b08194825627b1eca28e21e", size = 1951546, upload-time = "2026-04-17T09:10:10.585Z" }, - { url = "https://files.pythonhosted.org/packages/29/92/89e273a055ce440e6636c756379af35ad86da9d336a560049c3ba5e41c80/pydantic_core-2.46.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d333a50bdd814a917d8d6a7ee35ba2395d53ddaa882613bc24e54a9d8b129095", size = 1976178, upload-time = "2026-04-17T09:11:49.619Z" }, - { url = "https://files.pythonhosted.org/packages/91/b3/e4664469cf70c0cb0f7b2f5719d64e5968bb6f38217042c2afa3d3c4ba17/pydantic_core-2.46.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d00b99590c5bd1fabbc5d28b170923e32c1b1071b1f1de1851a4d14d89eb192", size = 2051697, upload-time = "2026-04-17T09:12:04.917Z" }, - { url = "https://files.pythonhosted.org/packages/98/58/dbf68213ee06ce51cdd6d8c95f97980e646858c45bd96bd2dfb40433be73/pydantic_core-2.46.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f0e686960ffe9e65066395af856ac2d52c159043144433602c50c221d81c1ba", size = 2233160, upload-time = "2026-04-17T09:12:00.956Z" }, - { url = "https://files.pythonhosted.org/packages/f5/d3/68092aa0ee6c60ff4de4740eb82db3d4ce338ec89b3cecb978c532472f12/pydantic_core-2.46.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d1128da41c9cb474e0a4701f9c363ec645c9d1a02229904c76bf4e0a194fde2", size = 2298398, upload-time = "2026-04-17T09:10:29.694Z" }, - { url = "https://files.pythonhosted.org/packages/e4/51/5d6155eb737db55b0ad354ca5f333ef009f75feb67df2d79a84bace45af6/pydantic_core-2.46.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48649cf2d8c358d79586e9fb2f8235902fcaa2d969ec1c5301f2d1873b2f8321", size = 2094058, upload-time = "2026-04-17T09:12:10.995Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f3/eb4a986197d71319430464ff181226c95adc8f06d932189b158bae5a82f5/pydantic_core-2.46.2-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:b902f0fc7c2cf503865a05718b68147c6cd5d0a3867af38c527be574a9fa6e9d", size = 2130388, upload-time = "2026-04-17T09:12:41.159Z" }, - { url = "https://files.pythonhosted.org/packages/56/00/44a9c4fe6d0f64b5786d6a8c649d6f0e34ba6c89b3663add1066e54451a2/pydantic_core-2.46.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e80011f808b03d1d87a8f1e76ae3da19a18eb706c823e17981dcf1fae43744fc", size = 2184245, upload-time = "2026-04-17T09:12:36.532Z" }, - { url = "https://files.pythonhosted.org/packages/78/6b/685b98a834d5e3d1c34a1bde1627525559dd223b75075bc7490cdb24eb33/pydantic_core-2.46.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b839d5c802e31348b949b6473f8190cddbf7d47475856d8ac995a373ee16ec59", size = 2186842, upload-time = "2026-04-17T09:13:04.054Z" }, - { url = "https://files.pythonhosted.org/packages/22/64/caa2f5a2ac8b6113adaa410ccdf31ba7f54897a6e54cd0d726fc7e780c88/pydantic_core-2.46.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:c6b1064f3f9cf9072e1d59dd2936f9f3b668bec1c37039708c9222db703c0d5b", size = 2336066, upload-time = "2026-04-17T09:12:13.006Z" }, - { url = "https://files.pythonhosted.org/packages/ee/f9/7d2701bf82945b5b9e7df8347be97ef6a36da2846bfe5b4afec299ffe27b/pydantic_core-2.46.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:37a68e6f2ac95578ce3c0564802404b27b24988649616e556c07e77111ed3f1d", size = 2363691, upload-time = "2026-04-17T09:13:42.972Z" }, - { url = "https://files.pythonhosted.org/packages/3b/65/0dab11574101522941055109419db3cc09db871643dc3fc74e2413215e5b/pydantic_core-2.46.2-cp312-cp312-win32.whl", hash = "sha256:d9ffa75a7ef4b97d6e5e205fabd4304ef01fec09e6f1bdde04b9ad1b07d20289", size = 1958801, upload-time = "2026-04-17T09:11:31.981Z" }, - { url = "https://files.pythonhosted.org/packages/13/2b/df84baa609c676f6450b8ecad44ea59146c805e3371b7b52443c0899f989/pydantic_core-2.46.2-cp312-cp312-win_amd64.whl", hash = "sha256:0551f2d2ddb68af5a00e26497f8025c538f73ef3cb698f8e5a487042cd2792a8", size = 2072634, upload-time = "2026-04-17T09:11:02.407Z" }, - { url = "https://files.pythonhosted.org/packages/d1/4e/e1ce8029fc438086a946739bf9d596f70ff470aad4a8345555920618cabe/pydantic_core-2.46.2-cp312-cp312-win_arm64.whl", hash = "sha256:83aef30f106edcc21a6a4cc44b82d3169a1dbe255508db788e778f3c804d3583", size = 2026188, upload-time = "2026-04-17T09:13:11.083Z" }, - { url = "https://files.pythonhosted.org/packages/07/2b/662e48254479a2d3450ba24b1e25061108b64339794232f503990c519144/pydantic_core-2.46.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:d26e9eea3715008a09a74585fe9becd0c67fbb145dc4df9756d597d7230a652c", size = 2101762, upload-time = "2026-04-17T09:10:13.87Z" }, - { url = "https://files.pythonhosted.org/packages/73/ab/bafd7c7503757ccc8ec4d1911e106fe474c629443648c51a88f08b0fe91a/pydantic_core-2.46.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:48b36e3235140510dc7861f0cd58b714b1cdd3d48f75e10ce52e69866b746f10", size = 1951814, upload-time = "2026-04-17T09:12:25.934Z" }, - { url = "https://files.pythonhosted.org/packages/92/cc/7549c2d57ba2e9a42caa5861a2d398dbe31c02c6aca783253ace59ce84f8/pydantic_core-2.46.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36b1f99dc451f1a3981f236151465bcf995bbe712d0727c9f7b236fe228a8133", size = 1977329, upload-time = "2026-04-17T09:13:37.605Z" }, - { url = "https://files.pythonhosted.org/packages/18/50/7ed4a8a0d478a4dca8f0134a5efa7193f03cc8520dd4c9509339fb2e5002/pydantic_core-2.46.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8641c8d535c2d95b45c2e19b646ecd23ebba35d461e0ae48a3498277006250ab", size = 2051832, upload-time = "2026-04-17T09:12:49.771Z" }, - { url = "https://files.pythonhosted.org/packages/dc/16/bb35b193741c0298ddc5f5e4234269efdc0c65e2bcd198aa0de9b68845e4/pydantic_core-2.46.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:20fb194788a0a50993e87013e693494ba183a2af5b44e99cf060bbae10912b11", size = 2233127, upload-time = "2026-04-17T09:11:04.449Z" }, - { url = "https://files.pythonhosted.org/packages/91/a5/98f4b637149185addea19e1785ea20c373cca31b202f589111d8209d9873/pydantic_core-2.46.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9262d11d0cd11ee3303a95156939402bed6cedfe5ed0e331b95a283a4da6eb8b", size = 2297418, upload-time = "2026-04-17T09:11:25.929Z" }, - { url = "https://files.pythonhosted.org/packages/36/90/93a5d21990b152da7b7507b7fddb0b935f6a0984d57ac3ec45a6e17777a2/pydantic_core-2.46.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac204542736aa295fa25f713b7fad6fc50b46ab7764d16087575c85f085174f3", size = 2093735, upload-time = "2026-04-17T09:12:06.908Z" }, - { url = "https://files.pythonhosted.org/packages/14/22/b8b1ffdddf08b4e84380bcb67f41dbbf4c171377c1d36fc6290794bb2094/pydantic_core-2.46.2-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9a7c43a0584742dface3ca0daf6f719d46c1ac2f87cf080050f9ae052c75e1b2", size = 2127570, upload-time = "2026-04-17T09:11:53.906Z" }, - { url = "https://files.pythonhosted.org/packages/c6/26/e60d72b4e2d0ce1fa811044a974412ac1c567fe067d97b3e6b290530786e/pydantic_core-2.46.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fd05e1edb6a90ad446fa268ab09e59202766b837597b714b2492db11ee87fab9", size = 2183524, upload-time = "2026-04-17T09:11:30.092Z" }, - { url = "https://files.pythonhosted.org/packages/35/32/36bec7584a1eefb17dec4dfa1c946d3fe4440f466c5705b8adfda69c9a9f/pydantic_core-2.46.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:91155b110788b5501abc7ea954f1d08606219e4e28e3c73a94124307c06efb80", size = 2185408, upload-time = "2026-04-17T09:10:57.228Z" }, - { url = "https://files.pythonhosted.org/packages/fc/d6/1a5689d873620efd67d6b163db0c444c056adb0849b5bc33e2b9f09665a6/pydantic_core-2.46.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:e4e2c72a529fa03ff228be1d2b76944013f428220b764e03cc50ada67e17a42c", size = 2335171, upload-time = "2026-04-17T09:11:43.369Z" }, - { url = "https://files.pythonhosted.org/packages/3e/8e/675104802abe8ef502b072050ee5f2e915251aa1a3af87e1015ce31ec42d/pydantic_core-2.46.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:56291ec1a11c3499890c99a8fd9053b47e60fe837a77ec72c0671b1b8b3dce24", size = 2362743, upload-time = "2026-04-17T09:10:18.333Z" }, - { url = "https://files.pythonhosted.org/packages/8d/bc/86c5dde4fa6e24467680eef5047da3c1a19be0a527d0d8e14aa76b39307c/pydantic_core-2.46.2-cp313-cp313-win32.whl", hash = "sha256:b50f9c5f826ddca1246f055148df939f5f3f2d0d96db73de28e2233f22210d4c", size = 1958074, upload-time = "2026-04-17T09:12:38.622Z" }, - { url = "https://files.pythonhosted.org/packages/2a/97/2537e8c1282b2c4eb062580c0d7a4339e10b072b803d1ee0b7f1f0a5c22c/pydantic_core-2.46.2-cp313-cp313-win_amd64.whl", hash = "sha256:251a57788823230ca8cbc99e6245d1a2ed6e180ec4864f251c94182c580c7f2e", size = 2071741, upload-time = "2026-04-17T09:13:32.405Z" }, - { url = "https://files.pythonhosted.org/packages/da/aa/2ee75798706f9dbc4e76dbe59e41a396c5c311e3d6223b9cf6a5fa7780be/pydantic_core-2.46.2-cp313-cp313-win_arm64.whl", hash = "sha256:315d32d1a71494d6b4e1e14a9fa7a4329597b4c4340088ad7e1a9dafbeed92a9", size = 2025955, upload-time = "2026-04-17T09:10:15.567Z" }, - { url = "https://files.pythonhosted.org/packages/d0/96/a50ccb6b539ae780f73cea74905468777680e30c6c3bdf714b9d4c116ea0/pydantic_core-2.46.2-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:4f59b45f3ef8650c0c736a57f59031d47ed9df4c0a64e83796849d7d14863a2d", size = 2097111, upload-time = "2026-04-17T09:10:49.617Z" }, - { url = "https://files.pythonhosted.org/packages/34/5f/fdead7b3afa822ab6e5a18ee0ecffd54937de1877c01ed13a342e0fb3f07/pydantic_core-2.46.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3a075a29ebef752784a91532a1a85be6b234ccffec0a9d7978a92696387c3da6", size = 1951904, upload-time = "2026-04-17T09:12:32.062Z" }, - { url = "https://files.pythonhosted.org/packages/95/e0/1c5d547e550cdab1bec737492aa08865337af6fe7fc9b96f7f45f17d9519/pydantic_core-2.46.2-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d12d786e30c04a9d307c5d7080bf720d9bac7f1668191d8e37633a9562749e2", size = 1978667, upload-time = "2026-04-17T09:11:35.589Z" }, - { url = "https://files.pythonhosted.org/packages/0e/cb/665ce629e218c8228302cb94beff4f6531082a2c87d3ecc3d5e63a26f392/pydantic_core-2.46.2-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0d5e6d6343b0b5dcacb3503b5de90022968da8ed0ab9ab39d3eda71c20cbf84e", size = 2046721, upload-time = "2026-04-17T09:11:47.725Z" }, - { url = "https://files.pythonhosted.org/packages/77/e9/6cb2cf60f54c1472bbdfce19d957553b43dbba79d1d7b2930a195c594785/pydantic_core-2.46.2-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:233eebac0999b6b9ba76eb56f3ec8fce13164aa16b6d2225a36a79e0f95b5973", size = 2228483, upload-time = "2026-04-17T09:12:08.837Z" }, - { url = "https://files.pythonhosted.org/packages/0d/2a/93e018dd5571f781ebaeda8c0cf65398489d5bee9b1f484df0b6149b43b9/pydantic_core-2.46.2-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9cc0eee720dd2f14f3b7c349469402b99ad81a174ab49d3533974529e9d93992", size = 2294663, upload-time = "2026-04-17T09:12:52.053Z" }, - { url = "https://files.pythonhosted.org/packages/5e/4f/49e57ca55c770c93d9bb046666a54949b42e3c9099a0c5fe94557873fe30/pydantic_core-2.46.2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83ee76bf2c9910513dbc19e7d82367131fa7508dedd6186a462393071cc11059", size = 2098742, upload-time = "2026-04-17T09:13:45.472Z" }, - { url = "https://files.pythonhosted.org/packages/c6/b0/6e46b5cd3332af665f794b8cdeea206618a8630bd9e7bcc36864518fce81/pydantic_core-2.46.2-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:d61db38eb4ee5192f0c261b7f2d38e420b554df8912245e3546aee5c45e2fd78", size = 2125922, upload-time = "2026-04-17T09:12:54.304Z" }, - { url = "https://files.pythonhosted.org/packages/06/d1/40850c81585be443a2abfdf7f795f8fae831baf8e2f9b2133c8246ac671c/pydantic_core-2.46.2-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8f09a713d17bcd55da8ab02ebd9110c5246a49c44182af213b5212800af8bc83", size = 2183000, upload-time = "2026-04-17T09:10:59.027Z" }, - { url = "https://files.pythonhosted.org/packages/04/af/8493d7dfa03ebb7866909e577c6aa65ea0de7377b86023cc51d0c8e11db3/pydantic_core-2.46.2-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:30cacc5fb696e64b8ef6fd31d9549d394dd7d52760db072eecb98e37e3af1677", size = 2180335, upload-time = "2026-04-17T09:12:57.01Z" }, - { url = "https://files.pythonhosted.org/packages/72/5b/1f6a344c4ffdf284da41c6067b82d5ebcbd11ce1b515ae4b662d4adb6f61/pydantic_core-2.46.2-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:7ccfb105fcfe91a22bbb5563ad3dc124bc1aa75bfd2e53a780ab05f78cdf6108", size = 2330002, upload-time = "2026-04-17T09:12:02.958Z" }, - { url = "https://files.pythonhosted.org/packages/25/ff/9a694126c12d6d2f48a0cafa6f8eef88ef0d8825600e18d03ff2e896c3b2/pydantic_core-2.46.2-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:13ffef637dc8370c249e5b26bd18e9a80a4fca3d809618c44e18ec834a7ca7a8", size = 2359920, upload-time = "2026-04-17T09:10:27.764Z" }, - { url = "https://files.pythonhosted.org/packages/51/c8/3a35c763d68a9cb2675eb10ef242cf66c5d4701b28ae12e688d67d2c180e/pydantic_core-2.46.2-cp314-cp314-win32.whl", hash = "sha256:1b0ab6d756ca2704a938e6c31b53f290c2f9c10d3914235410302a149de1a83e", size = 1953701, upload-time = "2026-04-17T09:13:30.021Z" }, - { url = "https://files.pythonhosted.org/packages/1a/6a/f2726a780365f7dfd89d62036f984f7acb99978c60c5e1fa7c0cb898ed11/pydantic_core-2.46.2-cp314-cp314-win_amd64.whl", hash = "sha256:99ebade8c9ada4df975372d8dd25883daa0e379a05f1cd0c99aa0c04368d01a6", size = 2071867, upload-time = "2026-04-17T09:10:39.205Z" }, - { url = "https://files.pythonhosted.org/packages/e1/79/76baacb9feba3d7c399b245ca1a29c74ea0db04ea693811374827eec2290/pydantic_core-2.46.2-cp314-cp314-win_arm64.whl", hash = "sha256:de87422197cf7f83db91d89c86a21660d749b3cd76cd8a45d115b8e675670f02", size = 2017252, upload-time = "2026-04-17T09:10:26.175Z" }, - { url = "https://files.pythonhosted.org/packages/f1/3b/77c26938f817668d9ad9bab1a905cb23f11d9a3d4bf724d429b3e55a8eaf/pydantic_core-2.46.2-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:236f22b4a206b5b61db955396b7cf9e2e1ff77f372efe9570128ccfcd6a525eb", size = 2094545, upload-time = "2026-04-17T09:12:19.339Z" }, - { url = "https://files.pythonhosted.org/packages/fe/de/42c13f590e3c260966aa49bcdb1674774f975467c49abd51191e502bea28/pydantic_core-2.46.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c2012f64d2cd7cca50f49f22445aa5a88691ac2b4498ee0a9a977f8ca4f7289f", size = 1933953, upload-time = "2026-04-17T09:09:55.889Z" }, - { url = "https://files.pythonhosted.org/packages/4e/84/ebe3ebb3e2d8db656937cfa6f97f544cb7132f2307a4a7dfdcd0ea102a12/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d07d6c63106d3a9c9a333e2636f9c82c703b1a9e3b079299e58747964e4fdb72", size = 1974435, upload-time = "2026-04-17T09:10:12.371Z" }, - { url = "https://files.pythonhosted.org/packages/b9/15/0bf51ca6709477cd4ef86148b6d7844f3308f029eac361dd0383f1e17b1a/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c326a2b4b85e959d9a1fc3a11f32f84611b6ec07c053e1828a860edf8d068208", size = 2031113, upload-time = "2026-04-17T09:10:00.752Z" }, - { url = "https://files.pythonhosted.org/packages/02/ae/b7b5af9b79db036d9e61a44c481c17a213dc8fc4b8b71fe6875a72fc778b/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac8a65e798f2462552c00d2e013d532c94d646729dda98458beaf51f9ec7b120", size = 2236325, upload-time = "2026-04-17T09:10:33.227Z" }, - { url = "https://files.pythonhosted.org/packages/a6/ae/ecef7477b5a03d4a499708f7e75d2836452ebb70b776c2d64612b334f57a/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a3c2bc1cc8164bedbc160b7bb1e8cc1e8b9c27f69ae4f9ae2b976cdae02b2dd", size = 2278135, upload-time = "2026-04-17T09:10:23.287Z" }, - { url = "https://files.pythonhosted.org/packages/db/e4/2f9d82faa47af6c39fc3f120145fd915971e1e0cb6b55b494fad9fdf8275/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e69aa5e10b7e8b1bb4a6888650fd12fcbf11d396ca11d4a44de1450875702830", size = 2109071, upload-time = "2026-04-17T09:11:06.149Z" }, - { url = "https://files.pythonhosted.org/packages/f1/9c/677cf10873fbd0b116575ab7b97c90482b21564f8a8040beb18edef7a577/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:4e6df5c3301e65fb42bc5338bf9a1027a02b0a31dc7f54c33775229af474daf0", size = 2106028, upload-time = "2026-04-17T09:10:51.525Z" }, - { url = "https://files.pythonhosted.org/packages/d6/53/6a06183544daba51c059123a2064a99039df25f115a06bdb26f2ea177038/pydantic_core-2.46.2-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c2f6e32548ac8d559b47944effcf8ae4d81c161f6b6c885edc53bc08b8f192d", size = 2164816, upload-time = "2026-04-17T09:11:56.187Z" }, - { url = "https://files.pythonhosted.org/packages/57/6f/10fcdd9e3eca66fc828eef0f6f5850f2dd3bca2c59e6e041fb8bc3da39be/pydantic_core-2.46.2-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:b089a81c58e6ea0485562bbbbbca4f65c0549521606d5ef27fba217aac9b665a", size = 2166130, upload-time = "2026-04-17T09:10:03.804Z" }, - { url = "https://files.pythonhosted.org/packages/29/83/92d3fd0e0156cad2e3cb5c26de73794af78ac9fa0c22ab666e566dd67061/pydantic_core-2.46.2-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:7f700a6d6f64112ae9193709b84303bbab84424ad4b47d0253301aabce9dfc70", size = 2316605, upload-time = "2026-04-17T09:12:45.249Z" }, - { url = "https://files.pythonhosted.org/packages/97/f1/facffdb970981068219582e499b8d0871ed163ffcc6b347de5c412669e4c/pydantic_core-2.46.2-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:67db6814beaa5fefe91101ec7eb9efda613795767be96f7cf58b1ca8c9ca9972", size = 2358385, upload-time = "2026-04-17T09:09:54.657Z" }, - { url = "https://files.pythonhosted.org/packages/8b/a1/b8160b2f22b2199467bc68581a4ed380643c16b348a27d6165c6c242d694/pydantic_core-2.46.2-cp314-cp314t-win32.whl", hash = "sha256:32fbc7447be8e3be99bf7869f7066308f16be55b61f9882c2cefc7931f5c7664", size = 1942373, upload-time = "2026-04-17T09:12:59.594Z" }, - { url = "https://files.pythonhosted.org/packages/0d/90/db89acabe5b150e11d1b59fe3d947dda2ef6abbfef5c82f056ff63802f5d/pydantic_core-2.46.2-cp314-cp314t-win_amd64.whl", hash = "sha256:b317a2b97019c0b95ce99f4f901ae383f40132da6706cdf1731066a73394c25c", size = 2052078, upload-time = "2026-04-17T09:10:19.96Z" }, - { url = "https://files.pythonhosted.org/packages/97/32/e19b83ceb07a3f1bb21798407790bbc9a31740158fd132b94139cb84e16c/pydantic_core-2.46.2-cp314-cp314t-win_arm64.whl", hash = "sha256:7dcb9d40930dfad7ab6b20bcc6ca9d2b030b0f347a0cd9909b54bd53ead521b1", size = 2016941, upload-time = "2026-04-17T09:12:34.447Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/66c146f421178641bda880b0267c0d57dd84f5fec9ecc8e46be17b480742/pydantic_core-2.46.2-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:e9fcabd1857492b5bf16f90258babde50f618f55d046b1309972da2396321ff9", size = 2091621, upload-time = "2026-04-17T09:12:47.501Z" }, - { url = "https://files.pythonhosted.org/packages/ee/b2/c28419aa9fc8055f4ac8e801d1d11c6357351bfa4321ed9bafab3eb98087/pydantic_core-2.46.2-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:fb3ec2c7f54c07b30d89983ce78dc32c37dd06a972448b8716d609493802d628", size = 1937059, upload-time = "2026-04-17T09:10:53.554Z" }, - { url = "https://files.pythonhosted.org/packages/30/ce/cd0824a2db213dc17113291b7a09b9b0ccd9fbf97daa4b81548703341baf/pydantic_core-2.46.2-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130a6c837d819ef33e8c2bf702ed2c3429237ea69807f1140943d6f4bdaf52fa", size = 1997278, upload-time = "2026-04-17T09:12:23.784Z" }, - { url = "https://files.pythonhosted.org/packages/c9/69/47283fe3c0c967d3e9e9cd6c42b70907610c8a6f8d6e8381f1bb55f8006c/pydantic_core-2.46.2-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2e25417cec5cd9bddb151e33cb08c50160f317479ecc02b22a95ec18f8fe004", size = 2147096, upload-time = "2026-04-17T09:12:43.124Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/2a/ef/f7abb56c49382a246fd2ce9c799691e3c3e7175ec74b14d99e798bcddb1a/pydantic_core-2.46.3.tar.gz", hash = "sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c", size = 471412, upload-time = "2026-04-20T14:40:56.672Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/cb/5b47425556ecc1f3fe18ed2a0083188aa46e1dd812b06e406475b3a5d536/pydantic_core-2.46.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67", size = 2101946, upload-time = "2026-04-20T14:40:52.581Z" }, + { url = "https://files.pythonhosted.org/packages/a1/4f/2fb62c2267cae99b815bbf4a7b9283812c88ca3153ef29f7707200f1d4e5/pydantic_core-2.46.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089", size = 1951612, upload-time = "2026-04-20T14:42:42.996Z" }, + { url = "https://files.pythonhosted.org/packages/50/6e/b7348fd30d6556d132cddd5bd79f37f96f2601fe0608afac4f5fb01ec0b3/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0", size = 1977027, upload-time = "2026-04-20T14:42:02.001Z" }, + { url = "https://files.pythonhosted.org/packages/82/11/31d60ee2b45540d3fb0b29302a393dbc01cd771c473f5b5147bcd353e593/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789", size = 2063008, upload-time = "2026-04-20T14:44:17.952Z" }, + { url = "https://files.pythonhosted.org/packages/8a/db/3a9d1957181b59258f44a2300ab0f0be9d1e12d662a4f57bb31250455c52/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d", size = 2233082, upload-time = "2026-04-20T14:40:57.934Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e1/3277c38792aeb5cfb18c2f0c5785a221d9ff4e149abbe1184d53d5f72273/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c", size = 2304615, upload-time = "2026-04-20T14:42:12.584Z" }, + { url = "https://files.pythonhosted.org/packages/5e/d5/e3d9717c9eba10855325650afd2a9cba8e607321697f18953af9d562da2f/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395", size = 2094380, upload-time = "2026-04-20T14:43:05.522Z" }, + { url = "https://files.pythonhosted.org/packages/a1/20/abac35dedcbfd66c6f0b03e4e3564511771d6c9b7ede10a362d03e110d9b/pydantic_core-2.46.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396", size = 2135429, upload-time = "2026-04-20T14:41:55.549Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a5/41bfd1df69afad71b5cf0535055bccc73022715ad362edbc124bc1e021d7/pydantic_core-2.46.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d", size = 2174582, upload-time = "2026-04-20T14:41:45.96Z" }, + { url = "https://files.pythonhosted.org/packages/79/65/38d86ea056b29b2b10734eb23329b7a7672ca604df4f2b6e9c02d4ee22fe/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca", size = 2187533, upload-time = "2026-04-20T14:40:55.367Z" }, + { url = "https://files.pythonhosted.org/packages/b6/55/a1129141678a2026badc539ad1dee0a71d06f54c2f06a4bd68c030ac781b/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976", size = 2332985, upload-time = "2026-04-20T14:44:13.05Z" }, + { url = "https://files.pythonhosted.org/packages/d7/60/cb26f4077719f709e54819f4e8e1d43f4091f94e285eb6bd21e1190a7b7c/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b", size = 2373670, upload-time = "2026-04-20T14:41:53.421Z" }, + { url = "https://files.pythonhosted.org/packages/6b/7e/c3f21882bdf1d8d086876f81b5e296206c69c6082551d776895de7801fa0/pydantic_core-2.46.3-cp312-cp312-win32.whl", hash = "sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4", size = 1966722, upload-time = "2026-04-20T14:44:30.588Z" }, + { url = "https://files.pythonhosted.org/packages/57/be/6b5e757b859013ebfbd7adba02f23b428f37c86dcbf78b5bb0b4ffd36e99/pydantic_core-2.46.3-cp312-cp312-win_amd64.whl", hash = "sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1", size = 2072970, upload-time = "2026-04-20T14:42:54.248Z" }, + { url = "https://files.pythonhosted.org/packages/bf/f8/a989b21cc75e9a32d24192ef700eea606521221a89faa40c919ce884f2b1/pydantic_core-2.46.3-cp312-cp312-win_arm64.whl", hash = "sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72", size = 2035963, upload-time = "2026-04-20T14:44:20.4Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3c/9b5e8eb9821936d065439c3b0fb1490ffa64163bfe7e1595985a47896073/pydantic_core-2.46.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37", size = 2102109, upload-time = "2026-04-20T14:41:24.219Z" }, + { url = "https://files.pythonhosted.org/packages/91/97/1c41d1f5a19f241d8069f1e249853bcce378cdb76eec8ab636d7bc426280/pydantic_core-2.46.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f", size = 1951820, upload-time = "2026-04-20T14:42:14.236Z" }, + { url = "https://files.pythonhosted.org/packages/30/b4/d03a7ae14571bc2b6b3c7b122441154720619afe9a336fa3a95434df5e2f/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8", size = 1977785, upload-time = "2026-04-20T14:42:31.648Z" }, + { url = "https://files.pythonhosted.org/packages/ae/0c/4086f808834b59e3c8f1aa26df8f4b6d998cdcf354a143d18ef41529d1fe/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad", size = 2062761, upload-time = "2026-04-20T14:40:37.093Z" }, + { url = "https://files.pythonhosted.org/packages/fa/71/a649be5a5064c2df0db06e0a512c2281134ed2fcc981f52a657936a7527c/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c", size = 2232989, upload-time = "2026-04-20T14:42:59.254Z" }, + { url = "https://files.pythonhosted.org/packages/a2/84/7756e75763e810b3a710f4724441d1ecc5883b94aacb07ca71c5fb5cfb69/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f", size = 2303975, upload-time = "2026-04-20T14:41:32.287Z" }, + { url = "https://files.pythonhosted.org/packages/6c/35/68a762e0c1e31f35fa0dac733cbd9f5b118042853698de9509c8e5bf128b/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35", size = 2095325, upload-time = "2026-04-20T14:42:47.685Z" }, + { url = "https://files.pythonhosted.org/packages/77/bf/1bf8c9a8e91836c926eae5e3e51dce009bf495a60ca56060689d3df3f340/pydantic_core-2.46.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687", size = 2133368, upload-time = "2026-04-20T14:41:22.766Z" }, + { url = "https://files.pythonhosted.org/packages/e5/50/87d818d6bab915984995157ceb2380f5aac4e563dddbed6b56f0ed057aba/pydantic_core-2.46.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3", size = 2173908, upload-time = "2026-04-20T14:42:52.044Z" }, + { url = "https://files.pythonhosted.org/packages/91/88/a311fb306d0bd6185db41fa14ae888fb81d0baf648a761ae760d30819d33/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022", size = 2186422, upload-time = "2026-04-20T14:43:29.55Z" }, + { url = "https://files.pythonhosted.org/packages/8f/79/28fd0d81508525ab2054fef7c77a638c8b5b0afcbbaeee493cf7c3fef7e1/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23", size = 2332709, upload-time = "2026-04-20T14:42:16.134Z" }, + { url = "https://files.pythonhosted.org/packages/b3/21/795bf5fe5c0f379308b8ef19c50dedab2e7711dbc8d0c2acf08f1c7daa05/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7", size = 2372428, upload-time = "2026-04-20T14:41:10.974Z" }, + { url = "https://files.pythonhosted.org/packages/45/b3/ed14c659cbe7605e3ef063077680a64680aec81eb1a04763a05190d49b7f/pydantic_core-2.46.3-cp313-cp313-win32.whl", hash = "sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13", size = 1965601, upload-time = "2026-04-20T14:41:42.128Z" }, + { url = "https://files.pythonhosted.org/packages/ef/bb/adb70d9a762ddd002d723fbf1bd492244d37da41e3af7b74ad212609027e/pydantic_core-2.46.3-cp313-cp313-win_amd64.whl", hash = "sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0", size = 2071517, upload-time = "2026-04-20T14:43:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/52/eb/66faefabebfe68bd7788339c9c9127231e680b11906368c67ce112fdb47f/pydantic_core-2.46.3-cp313-cp313-win_arm64.whl", hash = "sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec", size = 2035802, upload-time = "2026-04-20T14:43:38.507Z" }, + { url = "https://files.pythonhosted.org/packages/7f/db/a7bcb4940183fda36022cd18ba8dd12f2dff40740ec7b58ce7457befa416/pydantic_core-2.46.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b", size = 2097614, upload-time = "2026-04-20T14:44:38.374Z" }, + { url = "https://files.pythonhosted.org/packages/24/35/e4066358a22e3e99519db370494c7528f5a2aa1367370e80e27e20283543/pydantic_core-2.46.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018", size = 1951896, upload-time = "2026-04-20T14:40:53.996Z" }, + { url = "https://files.pythonhosted.org/packages/87/92/37cf4049d1636996e4b888c05a501f40a43ff218983a551d57f9d5e14f0d/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34", size = 1979314, upload-time = "2026-04-20T14:41:49.446Z" }, + { url = "https://files.pythonhosted.org/packages/d8/36/9ff4d676dfbdfb2d591cf43f3d90ded01e15b1404fd101180ed2d62a2fd3/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7", size = 2056133, upload-time = "2026-04-20T14:42:23.574Z" }, + { url = "https://files.pythonhosted.org/packages/bc/f0/405b442a4d7ba855b06eec8b2bf9c617d43b8432d099dfdc7bf999293495/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2", size = 2228726, upload-time = "2026-04-20T14:44:22.816Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f8/65cd92dd5a0bd89ba277a98ecbfaf6fc36bbd3300973c7a4b826d6ab1391/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba", size = 2301214, upload-time = "2026-04-20T14:44:48.792Z" }, + { url = "https://files.pythonhosted.org/packages/fd/86/ef96a4c6e79e7a2d0410826a68fbc0eccc0fd44aa733be199d5fcac3bb87/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f", size = 2099927, upload-time = "2026-04-20T14:41:40.196Z" }, + { url = "https://files.pythonhosted.org/packages/6d/53/269caf30e0096e0a8a8f929d1982a27b3879872cca2d917d17c2f9fdf4fe/pydantic_core-2.46.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22", size = 2128789, upload-time = "2026-04-20T14:41:15.868Z" }, + { url = "https://files.pythonhosted.org/packages/00/b0/1a6d9b6a587e118482910c244a1c5acf4d192604174132efd12bf0ac486f/pydantic_core-2.46.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f", size = 2173815, upload-time = "2026-04-20T14:44:25.152Z" }, + { url = "https://files.pythonhosted.org/packages/87/56/e7e00d4041a7e62b5a40815590114db3b535bf3ca0bf4dca9f16cef25246/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127", size = 2181608, upload-time = "2026-04-20T14:41:28.933Z" }, + { url = "https://files.pythonhosted.org/packages/e8/22/4bd23c3d41f7c185d60808a1de83c76cf5aeabf792f6c636a55c3b1ec7f9/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c", size = 2326968, upload-time = "2026-04-20T14:42:03.962Z" }, + { url = "https://files.pythonhosted.org/packages/24/ac/66cd45129e3915e5ade3b292cb3bc7fd537f58f8f8dbdaba6170f7cabb74/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1", size = 2369842, upload-time = "2026-04-20T14:41:35.52Z" }, + { url = "https://files.pythonhosted.org/packages/a2/51/dd4248abb84113615473aa20d5545b7c4cd73c8644003b5259686f93996c/pydantic_core-2.46.3-cp314-cp314-win32.whl", hash = "sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505", size = 1959661, upload-time = "2026-04-20T14:41:00.042Z" }, + { url = "https://files.pythonhosted.org/packages/20/eb/59980e5f1ae54a3b86372bd9f0fa373ea2d402e8cdcd3459334430f91e91/pydantic_core-2.46.3-cp314-cp314-win_amd64.whl", hash = "sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e", size = 2071686, upload-time = "2026-04-20T14:43:16.471Z" }, + { url = "https://files.pythonhosted.org/packages/8c/db/1cf77e5247047dfee34bc01fa9bca134854f528c8eb053e144298893d370/pydantic_core-2.46.3-cp314-cp314-win_arm64.whl", hash = "sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df", size = 2026907, upload-time = "2026-04-20T14:43:31.732Z" }, + { url = "https://files.pythonhosted.org/packages/57/c0/b3df9f6a543276eadba0a48487b082ca1f201745329d97dbfa287034a230/pydantic_core-2.46.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf", size = 2095047, upload-time = "2026-04-20T14:42:37.982Z" }, + { url = "https://files.pythonhosted.org/packages/66/57/886a938073b97556c168fd99e1a7305bb363cd30a6d2c76086bf0587b32a/pydantic_core-2.46.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee", size = 1934329, upload-time = "2026-04-20T14:43:49.655Z" }, + { url = "https://files.pythonhosted.org/packages/0b/7c/b42eaa5c34b13b07ecb51da21761297a9b8eb43044c864a035999998f328/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a", size = 1974847, upload-time = "2026-04-20T14:42:10.737Z" }, + { url = "https://files.pythonhosted.org/packages/e6/9b/92b42db6543e7de4f99ae977101a2967b63122d4b6cf7773812da2d7d5b5/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c", size = 2041742, upload-time = "2026-04-20T14:40:44.262Z" }, + { url = "https://files.pythonhosted.org/packages/0f/19/46fbe1efabb5aa2834b43b9454e70f9a83ad9c338c1291e48bdc4fecf167/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1", size = 2236235, upload-time = "2026-04-20T14:41:27.307Z" }, + { url = "https://files.pythonhosted.org/packages/77/da/b3f95bc009ad60ec53120f5d16c6faa8cabdbe8a20d83849a1f2b8728148/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64", size = 2282633, upload-time = "2026-04-20T14:44:33.271Z" }, + { url = "https://files.pythonhosted.org/packages/cc/6e/401336117722e28f32fb8220df676769d28ebdf08f2f4469646d404c43a3/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb", size = 2109679, upload-time = "2026-04-20T14:44:41.065Z" }, + { url = "https://files.pythonhosted.org/packages/fc/53/b289f9bc8756a32fe718c46f55afaeaf8d489ee18d1a1e7be1db73f42cc4/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6", size = 2108342, upload-time = "2026-04-20T14:42:50.144Z" }, + { url = "https://files.pythonhosted.org/packages/10/5b/8292fc7c1f9111f1b2b7c1b0dcf1179edcd014fc3ea4517499f50b829d71/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c", size = 2157208, upload-time = "2026-04-20T14:42:08.133Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9e/f80044e9ec07580f057a89fc131f78dda7a58751ddf52bbe05eaf31db50f/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47", size = 2167237, upload-time = "2026-04-20T14:42:25.412Z" }, + { url = "https://files.pythonhosted.org/packages/f8/84/6781a1b037f3b96be9227edbd1101f6d3946746056231bf4ac48cdff1a8d/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab", size = 2312540, upload-time = "2026-04-20T14:40:40.313Z" }, + { url = "https://files.pythonhosted.org/packages/3e/db/19c0839feeb728e7df03255581f198dfdf1c2aeb1e174a8420b63c5252e5/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba", size = 2369556, upload-time = "2026-04-20T14:41:09.427Z" }, + { url = "https://files.pythonhosted.org/packages/e0/15/3228774cb7cd45f5f721ddf1b2242747f4eb834d0c491f0c02d606f09fed/pydantic_core-2.46.3-cp314-cp314t-win32.whl", hash = "sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56", size = 1949756, upload-time = "2026-04-20T14:41:25.717Z" }, + { url = "https://files.pythonhosted.org/packages/b8/2a/c79cf53fd91e5a87e30d481809f52f9a60dd221e39de66455cf04deaad37/pydantic_core-2.46.3-cp314-cp314t-win_amd64.whl", hash = "sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8", size = 2051305, upload-time = "2026-04-20T14:43:18.627Z" }, + { url = "https://files.pythonhosted.org/packages/0b/db/d8182a7f1d9343a032265aae186eb063fe26ca4c40f256b21e8da4498e89/pydantic_core-2.46.3-cp314-cp314t-win_arm64.whl", hash = "sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374", size = 2026310, upload-time = "2026-04-20T14:41:01.778Z" }, + { url = "https://files.pythonhosted.org/packages/34/42/f426db557e8ab2791bc7562052299944a118655496fbff99914e564c0a94/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803", size = 2091877, upload-time = "2026-04-20T14:43:27.091Z" }, + { url = "https://files.pythonhosted.org/packages/5c/4f/86a832a9d14df58e663bfdf4627dc00d3317c2bd583c4fb23390b0f04b8e/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3", size = 1932428, upload-time = "2026-04-20T14:40:45.781Z" }, + { url = "https://files.pythonhosted.org/packages/11/1a/fe857968954d93fb78e0d4b6df5c988c74c4aaa67181c60be7cfe327c0ca/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5", size = 1997550, upload-time = "2026-04-20T14:44:02.425Z" }, + { url = "https://files.pythonhosted.org/packages/17/eb/9d89ad2d9b0ba8cd65393d434471621b98912abb10fbe1df08e480ba57b5/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4", size = 2137657, upload-time = "2026-04-20T14:42:45.149Z" }, ] [[package]] name = "pydantic-settings" -version = "2.13.1" +version = "2.14.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/6d/fffca34caecc4a3f97bda81b2098da5e8ab7efc9a66e819074a11955d87e/pydantic_settings-2.13.1.tar.gz", hash = "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025", size = 223826, upload-time = "2026-02-19T13:45:08.055Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/98/c8345dccdc31de4228c039a98f6467a941e39558da41c1744fbe29fa5666/pydantic_settings-2.14.0.tar.gz", hash = "sha256:24285fd4b0e0c06507dd9fdfd331ee23794305352aaec8fc4eb92d4047aeb67d", size = 235709, upload-time = "2026-04-20T13:37:40.293Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/4b/ccc026168948fec4f7555b9164c724cf4125eac006e176541483d2c959be/pydantic_settings-2.13.1-py3-none-any.whl", hash = "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237", size = 58929, upload-time = "2026-02-19T13:45:06.034Z" }, + { url = "https://files.pythonhosted.org/packages/01/dd/bebff3040138f00ae8a102d426b27349b9a49acc310fcae7f92112d867e3/pydantic_settings-2.14.0-py3-none-any.whl", hash = "sha256:fc8d5d692eb7092e43c8647c1c35a3ecd00e040fcf02ed86f4cb5458ca62182e", size = 60940, upload-time = "2026-04-20T13:37:38.586Z" }, ] [[package]] name = "pydata-sphinx-theme" -version = "0.17.0" +version = "0.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "accessible-pygments" }, @@ -5089,9 +5089,9 @@ dependencies = [ { name = "sphinx" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/bb/4a97aaa840b26601d6d04deca1389c35025336428706a4a732051187fbd3/pydata_sphinx_theme-0.17.0.tar.gz", hash = "sha256:529c5631582cb3328cf4814fb9eb80611d1704c854406d282a75c9c86e3a1955", size = 4990605, upload-time = "2026-04-03T13:02:20.091Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/f7/c74c7100a7f4c0f77b5dcacb7dfdb8fee774fb70e487dd97acba2b930774/pydata_sphinx_theme-0.17.1.tar.gz", hash = "sha256:2cfc1d926c753c77039b7ee53f0ccebcbee5e81f0db61432b01cbb10ad7fd0af", size = 4991415, upload-time = "2026-04-21T13:00:34.263Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/b7/a2bae25aae3568fe9f17040b31f9c190b4c5d86856d8869d0a30364a2567/pydata_sphinx_theme-0.17.0-py3-none-any.whl", hash = "sha256:cec5c92f41f4a11541b6df8210c446b4aa9c3badb7fcf2db7893405b786d5c99", size = 6820685, upload-time = "2026-04-03T13:02:18.09Z" }, + { url = "https://files.pythonhosted.org/packages/e2/bc/2cb8c78300ce1ace4eeac3b3522218cea2c2053bfa6b4e32cc972a477f9a/pydata_sphinx_theme-0.17.1-py3-none-any.whl", hash = "sha256:320b022d7808bdf5920d9a28e573f27aace9b23e1af6ca103eecc752411df492", size = 6823346, upload-time = "2026-04-21T13:00:31.978Z" }, ] [[package]] @@ -5216,7 +5216,7 @@ dependencies = [ { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "iniconfig" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "pluggy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload-time = "2025-03-02T12:54:54.503Z" } @@ -5448,7 +5448,7 @@ wheels = [ [[package]] name = "ray" -version = "2.55.0" +version = "2.55.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -5456,22 +5456,22 @@ dependencies = [ { name = "jsonschema" }, { name = "msgpack" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "protobuf" }, { name = "pyyaml" }, { name = "requests" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/d4/586e6a696004262b74a7a53b3860c3178b9940ed3c5ee5d909cb7f8f3149/ray-2.55.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6f0b8dfa3716cc9be5fce3b53e9bfdb73cea36025bfe6f1d27928d0f84cfd695", size = 65822329, upload-time = "2026-04-15T04:32:14.218Z" }, - { url = "https://files.pythonhosted.org/packages/4a/6a/0c1a1179832b9dd93c615289ab92eefd5d844f6e6cea313db09bd55b84da/ray-2.55.0-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:b77f406072ac0ce90431ac436828f364c183ab57ba15c3a0e688a74ae3c2d3f3", size = 72910696, upload-time = "2026-04-15T04:32:19.915Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a5/2a17fd0aed4f053462eeabfeb6b3ff1b46a85ac9a7da1ebf99d60683f3c2/ray-2.55.0-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:cd460bdbf8a8a4bb768a20c38b1c534d84fe63bc0e5f3580c5c0ef7302b986b3", size = 73765215, upload-time = "2026-04-15T04:32:25.486Z" }, - { url = "https://files.pythonhosted.org/packages/ea/ac/40ee9b4a514366a1dc8ed50a16a4fc095192c89f2eccb30b15ab710addf3/ray-2.55.0-cp312-cp312-win_amd64.whl", hash = "sha256:5da06d27358d38c30a723a617bf9b7df138f4d90e8046f1fa51d9b8c7473b64a", size = 27865785, upload-time = "2026-04-15T04:32:31.587Z" }, - { url = "https://files.pythonhosted.org/packages/a5/47/c57556d981a7ed0fe1438f9ff1ecd601bf7d5c704e7698bf181536470acc/ray-2.55.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:1da8b8755b6e4fde03db78b6ce2bbcecfcfbd20d39b93833d246c515daeedf3c", size = 65766784, upload-time = "2026-04-15T04:32:36.85Z" }, - { url = "https://files.pythonhosted.org/packages/00/e8/09ebc53f76800130da0a38bb28e14924ec28daa0a9f41b75146056b52f7c/ray-2.55.0-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:ceee87a884602aab34db109415e6839a6e9169f4750cab727b7ea1610df5b91f", size = 72818556, upload-time = "2026-04-15T04:32:42.275Z" }, - { url = "https://files.pythonhosted.org/packages/72/6c/97bd20bc62e5dc1b40784261e38f5fce9aee9765c51332a41e95083507a1/ray-2.55.0-cp313-cp313-manylinux2014_x86_64.whl", hash = "sha256:f16dea32e5cc58ed406c0ef0dd4be69d60ce77a075edb5f0380356a48bf85ab3", size = 73678945, upload-time = "2026-04-15T04:32:49.379Z" }, - { url = "https://files.pythonhosted.org/packages/9b/90/2b71910c00372634c73a15d5252a09999b1a806301e9759c90dc71479247/ray-2.55.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:d48bc4533b3b76d59ed3f9eab1e6b7322a53a7cdefb8f657d9b46eebad56dbee", size = 65774400, upload-time = "2026-04-15T04:32:55.279Z" }, - { url = "https://files.pythonhosted.org/packages/b8/72/0283b4d2289567511918de44a890d3dc3f5da529112481b75402bb67550d/ray-2.55.0-cp314-cp314-manylinux2014_aarch64.whl", hash = "sha256:b74390f201f28f05c8f250069dfed54d6d6a0109ffe482425d76c11be820e309", size = 72813920, upload-time = "2026-04-15T04:33:00.899Z" }, - { url = "https://files.pythonhosted.org/packages/a8/da/f701527fe5e3b84afbdffe206b39d2929013301ef15660b99794debdf2d3/ray-2.55.0-cp314-cp314-manylinux2014_x86_64.whl", hash = "sha256:eb0a6179641bc420a66ee85cc9b382e58f22effbd36297e3683a793e5cdc0898", size = 73644251, upload-time = "2026-04-15T04:33:07.754Z" }, + { url = "https://files.pythonhosted.org/packages/ac/3a/4d34f471a68b958b7f94c974c19ad6836a61a2dc16393df4294169a2e4b0/ray-2.55.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:137f9006eee28caab8260803cca314f37bbda3fc94fdfa31c770b5d019626ad8", size = 65822379, upload-time = "2026-04-22T20:09:58.064Z" }, + { url = "https://files.pythonhosted.org/packages/f1/13/0db535102d0256b350ca116d8987588aca1a1f9ebb4638e1e1ff88bbcef8/ray-2.55.1-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:26541f69bb55607ef8335baac75b2ed12ff2ce02d56313219b29eda003039221", size = 72910802, upload-time = "2026-04-22T20:10:04.382Z" }, + { url = "https://files.pythonhosted.org/packages/4c/f8/fffadf3f4285eebd460e4d7f2ed1c0cd641ed89613c3f49eb881ee9fa7e2/ray-2.55.1-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:263705f6bab29e7622a94f82da25fd7f9cead76cdf89a07aab28f79cdf8f9d95", size = 73765203, upload-time = "2026-04-22T20:10:10.495Z" }, + { url = "https://files.pythonhosted.org/packages/10/f7/5acb86fc9625a0e6bbc40e1c7d42c60770e78585439a921c32738b6d675a/ray-2.55.1-cp312-cp312-win_amd64.whl", hash = "sha256:9ad56704c8bd7e92130162f9c58e4ef473609515637673d5a36e761f95335206", size = 27865547, upload-time = "2026-04-22T20:10:15.364Z" }, + { url = "https://files.pythonhosted.org/packages/d5/95/898699cc1a6a5f304ea95376d079843b5c05f4c8c1ec7e55a5cc7ffcea50/ray-2.55.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:f9844a9272ef2e6eb5771025866072cf4234cf4c7cc1a31e235b7de7111864be", size = 65766823, upload-time = "2026-04-22T20:10:20.786Z" }, + { url = "https://files.pythonhosted.org/packages/c9/13/87deecc090c672e45a0cf6f5eef511de448b93f37ef18fd10eb8e8557a0d/ray-2.55.1-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:b415d590e062f248907e0fe42994943f11726b7178fcf4b1cf5546721fb1a5f8", size = 72818676, upload-time = "2026-04-22T20:10:26.705Z" }, + { url = "https://files.pythonhosted.org/packages/71/d7/fc95d3b8824c62105c64aa1b59c59600b581f608d78a2af753e010936dc9/ray-2.55.1-cp313-cp313-manylinux2014_x86_64.whl", hash = "sha256:1380e043eb57cde69b7e9199c6f2558ceeb8f0fc41c97d1d5e50ea042115f302", size = 73678908, upload-time = "2026-04-22T20:10:32.795Z" }, + { url = "https://files.pythonhosted.org/packages/a9/03/7e552325572e067b23a4584bda8dc6a67af8bd7e03c424d2610bfa93112d/ray-2.55.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:b062045c64c2bce39a51661624f7292c7bbf30f2a9d878627aae31d46da5712d", size = 65774106, upload-time = "2026-04-22T20:10:39.885Z" }, + { url = "https://files.pythonhosted.org/packages/94/62/607a8859520ce350861425f11f8e15d66c15ee33e6aac812f9e2889b5df4/ray-2.55.1-cp314-cp314-manylinux2014_aarch64.whl", hash = "sha256:4e618d61e1b14b6fde9a586151f3fd9d435b0b85048b997bcaa7f4a533747b2b", size = 72814044, upload-time = "2026-04-22T20:10:46.985Z" }, + { url = "https://files.pythonhosted.org/packages/04/5a/0699bef04a72d7dc54462960d07ef7a19cd8b1e09979880aba2b6d13cca2/ray-2.55.1-cp314-cp314-manylinux2014_x86_64.whl", hash = "sha256:156ed3e72ad95b645d2006cd71a8dddbcc89b56bfc00027f6225adf78bd9cb74", size = 73644244, upload-time = "2026-04-22T20:10:52.973Z" }, ] [package.optional-dependencies] @@ -5482,10 +5482,10 @@ default = [ { name = "grpcio" }, { name = "opencensus" }, { name = "opentelemetry-exporter-prometheus", version = "0.54b1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev' or extra == 'extra-13-megatron-core-lts'" }, - { name = "opentelemetry-exporter-prometheus", version = "0.62b0", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, + { name = "opentelemetry-exporter-prometheus", version = "0.62b1", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, { name = "opentelemetry-proto" }, { name = "opentelemetry-sdk", version = "1.33.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev' or extra == 'extra-13-megatron-core-lts'" }, - { name = "opentelemetry-sdk", version = "1.41.0", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, + { name = "opentelemetry-sdk", version = "1.41.1", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, { name = "prometheus-client" }, { name = "py-spy" }, { name = "pydantic" }, @@ -6141,7 +6141,7 @@ dependencies = [ { name = "imagesize" }, { name = "jinja2" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "pygments" }, { name = "requests" }, { name = "roman-numerals" }, @@ -6302,15 +6302,15 @@ wheels = [ [[package]] name = "sse-starlette" -version = "3.3.4" +version = "3.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "starlette" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/26/8c/f9290339ef6d79badbc010f067cd769d6601ec11a57d78569c683fb4dd87/sse_starlette-3.3.4.tar.gz", hash = "sha256:aaf92fc067af8a5427192895ac028e947b484ac01edbc3caf00e7e7137c7bef1", size = 32427, upload-time = "2026-03-29T09:00:23.307Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/9a/f35932a8c0eb6b2287b66fa65a0321df8c84e4e355a659c1841a37c39fdb/sse_starlette-3.4.1.tar.gz", hash = "sha256:f780bebcf6c8997fe514e3bd8e8c648d8284976b391c8bed0bcb1f611632b555", size = 35127, upload-time = "2026-04-26T13:32:32.292Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/7f/3de5402f39890ac5660b86bcf5c03f9d855dad5c4ed764866d7b592b46fd/sse_starlette-3.3.4-py3-none-any.whl", hash = "sha256:84bb06e58939a8b38d8341f1bc9792f06c2b53f48c608dd207582b664fc8f3c1", size = 14330, upload-time = "2026-03-29T09:00:21.846Z" }, + { url = "https://files.pythonhosted.org/packages/ff/07/45c21ed03d708c477367305726b89919b020a3a2a01f72aaf5ad941caf35/sse_starlette-3.4.1-py3-none-any.whl", hash = "sha256:6b43cf21f1d574d582a6e1b0cfbde1c94dc86a32a701a7168c99c4475c6bd1d0", size = 16487, upload-time = "2026-04-26T13:32:30.819Z" }, ] [[package]] @@ -6367,7 +6367,7 @@ dependencies = [ { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "pillow" }, { name = "protobuf" }, { name = "setuptools" }, @@ -6693,7 +6693,7 @@ dependencies = [ { name = "onnx", version = "1.19.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "onnx", version = "1.21.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "onnxscript", version = "0.5.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, - { name = "onnxscript", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, + { name = "onnxscript", version = "0.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" } }, { name = "pydantic" }, { name = "torch", marker = "sys_platform == 'never'" }, @@ -6709,7 +6709,7 @@ dependencies = [ { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "pyyaml" }, { name = "regex" }, { name = "requests" }, @@ -6750,7 +6750,7 @@ wheels = [ [[package]] name = "typer" -version = "0.24.1" +version = "0.25.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -6758,9 +6758,9 @@ dependencies = [ { name = "rich" }, { name = "shellingham" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613, upload-time = "2026-02-21T16:54:40.609Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/27/ede8cec7596e0041ba7e7b80b47d132562f56ff454313a16f6084e555c9f/typer-0.25.0.tar.gz", hash = "sha256:123eaf9f19bb40fd268310e12a542c0c6b4fab9c98d9d23342a01ff95e3ce930", size = 120150, upload-time = "2026-04-26T08:46:14.767Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085, upload-time = "2026-02-21T16:54:41.616Z" }, + { url = "https://files.pythonhosted.org/packages/9a/72/193d4e586ec5a4db834a36bbeb47641a62f951f114ffd0fe5b1b46e8d56f/typer-0.25.0-py3-none-any.whl", hash = "sha256:ac01b48823d3db9a83c9e164338057eadbb1c9957a2a6b4eeb486669c560b5dc", size = 55993, upload-time = "2026-04-26T08:46:15.889Z" }, ] [[package]] @@ -6839,15 +6839,15 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.44.0" +version = "0.46.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/da/6eee1ff8b6cbeed47eeb5229749168e81eb4b7b999a1a15a7176e51410c9/uvicorn-0.44.0.tar.gz", hash = "sha256:6c942071b68f07e178264b9152f1f16dfac5da85880c4ce06366a96d70d4f31e", size = 86947, upload-time = "2026-04-06T09:23:22.826Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/93/041fca8274050e40e6791f267d82e0e2e27dd165627bd640d3e0e378d877/uvicorn-0.46.0.tar.gz", hash = "sha256:fb9da0926999cc6cb22dc7cd71a94a632f078e6ae47ff683c5c420750fb7413d", size = 88758, upload-time = "2026-04-23T07:16:00.151Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/23/a5bbd9600dd607411fa644c06ff4951bec3a4d82c4b852374024359c19c0/uvicorn-0.44.0-py3-none-any.whl", hash = "sha256:ce937c99a2cc70279556967274414c087888e8cec9f9c94644dfca11bd3ced89", size = 69425, upload-time = "2026-04-06T09:23:21.524Z" }, + { url = "https://files.pythonhosted.org/packages/31/a3/5b1562db76a5a488274b2332a97199b32d0442aca0ed193697fd47786316/uvicorn-0.46.0-py3-none-any.whl", hash = "sha256:bbebbcbed972d162afca128605223022bedd345b7bc7855ce66deb31487a9048", size = 70926, upload-time = "2026-04-23T07:15:58.355Z" }, ] [[package]] @@ -6867,13 +6867,13 @@ wheels = [ [[package]] name = "wandb" -version = "0.26.0" +version = "0.26.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "gitpython" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.1", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "platformdirs" }, { name = "protobuf" }, { name = "pydantic" }, @@ -6882,17 +6882,17 @@ dependencies = [ { name = "sentry-sdk" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/93/82/911948663ddf9e5ec6bc5cde19b0fffcb23c4b64a546bf5c084fde76c4cb/wandb-0.26.0.tar.gz", hash = "sha256:0356853895b53fe110e2ed17a1d49c15405498f08e5fbc339deab384f2df45f1", size = 42120837, upload-time = "2026-04-13T19:42:47.282Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/a4/72a6640e1f566e81f184a426e3e45298d4c6672664de41adb7eb6f64370a/wandb-0.26.1.tar.gz", hash = "sha256:eef2dbaea06f0b1c0cdc5d76f544ae4c2b8848fc512442a00bd59f0502fc8aa1", size = 42159814, upload-time = "2026-04-23T16:27:34.033Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/16/c0ea55323be74da9b4297d934b8e787251ae944d5776340c5498871927f1/wandb-0.26.0-py3-none-macosx_12_0_arm64.whl", hash = "sha256:1ece94a2a5eda1d0e3a2d8a2fd28aa0187705d6efa5ac4c0b8680083583b7ec1", size = 24800103, upload-time = "2026-04-13T19:42:23.946Z" }, - { url = "https://files.pythonhosted.org/packages/29/b8/4d38b43747616c4a9304be38b6e78526814deb5c1e01b3b6ebac82ce1cb5/wandb-0.26.0-py3-none-macosx_12_0_x86_64.whl", hash = "sha256:92f6f303fe2af50e3f711833a835150f9b4d8082874bfd9868cf15491ea2947e", size = 25956473, upload-time = "2026-04-13T19:42:26.772Z" }, - { url = "https://files.pythonhosted.org/packages/45/a6/940ca459d70c7cce7a6f7b395809f8ed051a25b2ce696fb93694c77f065f/wandb-0.26.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:a17aae051a31831388cff880251c1b5bc38fbf6a283a0ee7c543709e8e9633d1", size = 25352442, upload-time = "2026-04-13T19:42:29.438Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a8/55325da4b240d07ba2a8e1949a05b5942dd3346e14f7fd5e3cc72e46a648/wandb-0.26.0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:28a14ffc014e523498d077dfde12839b7be586ca8c3190e72e7167c1aea6ee4c", size = 27177821, upload-time = "2026-04-13T19:42:32.055Z" }, - { url = "https://files.pythonhosted.org/packages/c4/84/e4b0636a3e921e2cffb159b57b5a83787475993e2b5adb6181fbf7712a59/wandb-0.26.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fb9a63babeee044fecf65a4675f7dfb0efaea4986e498a3bc8f948558af877e7", size = 25522688, upload-time = "2026-04-13T19:42:34.625Z" }, - { url = "https://files.pythonhosted.org/packages/dd/f0/821a451110dd5f5c39358752abbdcb56c4fcebcc41039c7dcd4b024d2e27/wandb-0.26.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3eb88fb556a64bf4492cf571bb851d47871901c096f0540d841ccb50f5cbeb66", size = 27486467, upload-time = "2026-04-13T19:42:37.119Z" }, - { url = "https://files.pythonhosted.org/packages/24/c5/a4eb8fb6e7527584c6ccdf5c9b265283ed0c9d94d26d5eb28f9b48cd5779/wandb-0.26.0-py3-none-win32.whl", hash = "sha256:362828d48d21dd4877e28fdce40421ebdfc16d1fe0b59e8371b12d75bbc3f1e7", size = 24908555, upload-time = "2026-04-13T19:42:39.416Z" }, - { url = "https://files.pythonhosted.org/packages/35/3d/bf182f3af977e6297fc05bc3fd9bd51feacfe4d2c4ce83c90eb2ad7ce59b/wandb-0.26.0-py3-none-win_amd64.whl", hash = "sha256:21a8346434fd30e1bc13a26b226fc29b6f33a1cb346d610cbcb4040c3b0e1f63", size = 24908559, upload-time = "2026-04-13T19:42:42.019Z" }, - { url = "https://files.pythonhosted.org/packages/1a/3e/344cb29b593f8e7abc14cc268dafde1974bae3f073b4885476f4fbba3cb8/wandb-0.26.0-py3-none-win_arm64.whl", hash = "sha256:99bd11974e9005d3a3f82e1fabfc4909ffa1fdede23a8839f5fbaea2f5be9033", size = 22936140, upload-time = "2026-04-13T19:42:44.715Z" }, + { url = "https://files.pythonhosted.org/packages/8c/09/3296235f3906e904f06f2df29eed4d672fb23c0932c9486e2af64f2f2a66/wandb-0.26.1-py3-none-macosx_12_0_arm64.whl", hash = "sha256:2955fe190c005fb83ee6d73f066c8a33f09f3212a1f2eb53faa6581440e456be", size = 24857204, upload-time = "2026-04-23T16:26:58.576Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ad/e39ca3086534129e42208ba00ed2c6247ce425f890219eeec33b4f162864/wandb-0.26.1-py3-none-macosx_12_0_x86_64.whl", hash = "sha256:55d91cabde98162d7116a5e19ddd052bd9848556243f1da4cbb9ffb7ad435bfc", size = 26014649, upload-time = "2026-04-23T16:27:02.559Z" }, + { url = "https://files.pythonhosted.org/packages/56/af/400d84a3bdce0b062b4baa70acb6becd2c8018697f4fbf5af9a9e1e406e5/wandb-0.26.1-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:7c78bc2454cfe1ffa1c3a256060a387356eed8a4488e024d9d2eba8f2b5bd51d", size = 25421317, upload-time = "2026-04-23T16:27:06.411Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e9/b4bf8f3509dcea1cec52233a38991459654635b5a8e6a494eb912e1b9cfb/wandb-0.26.1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:a2c8eeec8706dcd2872e69c3b4d20ec523082fdb4440295491556e219ad2aa67", size = 27192831, upload-time = "2026-04-23T16:27:10.308Z" }, + { url = "https://files.pythonhosted.org/packages/62/cf/4a6dce0c782223ef0eeea7139daee73418a7322befcf083512c31cebaa18/wandb-0.26.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2fa768ee0636a569afb7541cf996e56309c47070566a38916823f94e02afe586", size = 25593326, upload-time = "2026-04-23T16:27:14.259Z" }, + { url = "https://files.pythonhosted.org/packages/df/99/58c3d8c36ae8e2b7d70bf6493eb5daa1cca0231a04b025717b4cd1a78f1e/wandb-0.26.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5854928725cfeff1f284d5c043cd353f810e5da02eead2c120ef5056ad026fea", size = 27535542, upload-time = "2026-04-23T16:27:18.473Z" }, + { url = "https://files.pythonhosted.org/packages/7c/d0/4e846ffc1d0cc435518dfa581ce73ac82cfd0ebbf35f3853c9277f632e5f/wandb-0.26.1-py3-none-win32.whl", hash = "sha256:5c2bd44e575ae9944e2764d1aaa031461178276bf2636d5558399c2816ef5cfe", size = 24968151, upload-time = "2026-04-23T16:27:22.086Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9b/487413eaccefdb58799a226726e24b486e9192d2671c75a4550c160aba23/wandb-0.26.1-py3-none-win_amd64.whl", hash = "sha256:5817785467d3f1676f1812ec19a89f77f6e56dfe67d9f47080075af95f705d3e", size = 24968155, upload-time = "2026-04-23T16:27:25.731Z" }, + { url = "https://files.pythonhosted.org/packages/04/dc/5baf3e99b3eeb709d6f75124b5bec8cb73d4b38d2b10df7fdcfde4966200/wandb-0.26.1-py3-none-win_arm64.whl", hash = "sha256:f848b7744f896bc04cabbb28360a2814d1551a91fa2c456243e06435729c8a2e", size = 22912416, upload-time = "2026-04-23T16:27:29.456Z" }, ] [[package]] @@ -7190,85 +7190,115 @@ wheels = [ [[package]] name = "xxhash" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz", hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160, upload-time = "2025-10-02T14:37:08.097Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/07/d9412f3d7d462347e4511181dea65e47e0d0e16e26fbee2ea86a2aefb657/xxhash-3.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:01362c4331775398e7bb34e3ab403bc9ee9f7c497bc7dee6272114055277dd3c", size = 32744, upload-time = "2025-10-02T14:34:34.622Z" }, - { url = "https://files.pythonhosted.org/packages/79/35/0429ee11d035fc33abe32dca1b2b69e8c18d236547b9a9b72c1929189b9a/xxhash-3.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b7b2df81a23f8cb99656378e72501b2cb41b1827c0f5a86f87d6b06b69f9f204", size = 30816, upload-time = "2025-10-02T14:34:36.043Z" }, - { url = "https://files.pythonhosted.org/packages/b7/f2/57eb99aa0f7d98624c0932c5b9a170e1806406cdbcdb510546634a1359e0/xxhash-3.6.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:dc94790144e66b14f67b10ac8ed75b39ca47536bf8800eb7c24b50271ea0c490", size = 194035, upload-time = "2025-10-02T14:34:37.354Z" }, - { url = "https://files.pythonhosted.org/packages/4c/ed/6224ba353690d73af7a3f1c7cdb1fc1b002e38f783cb991ae338e1eb3d79/xxhash-3.6.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93f107c673bccf0d592cdba077dedaf52fe7f42dcd7676eba1f6d6f0c3efffd2", size = 212914, upload-time = "2025-10-02T14:34:38.6Z" }, - { url = "https://files.pythonhosted.org/packages/38/86/fb6b6130d8dd6b8942cc17ab4d90e223653a89aa32ad2776f8af7064ed13/xxhash-3.6.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aa5ee3444c25b69813663c9f8067dcfaa2e126dc55e8dddf40f4d1c25d7effa", size = 212163, upload-time = "2025-10-02T14:34:39.872Z" }, - { url = "https://files.pythonhosted.org/packages/ee/dc/e84875682b0593e884ad73b2d40767b5790d417bde603cceb6878901d647/xxhash-3.6.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7f99123f0e1194fa59cc69ad46dbae2e07becec5df50a0509a808f90a0f03f0", size = 445411, upload-time = "2025-10-02T14:34:41.569Z" }, - { url = "https://files.pythonhosted.org/packages/11/4f/426f91b96701ec2f37bb2b8cec664eff4f658a11f3fa9d94f0a887ea6d2b/xxhash-3.6.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49e03e6fe2cac4a1bc64952dd250cf0dbc5ef4ebb7b8d96bce82e2de163c82a2", size = 193883, upload-time = "2025-10-02T14:34:43.249Z" }, - { url = "https://files.pythonhosted.org/packages/53/5a/ddbb83eee8e28b778eacfc5a85c969673e4023cdeedcfcef61f36731610b/xxhash-3.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bd17fede52a17a4f9a7bc4472a5867cb0b160deeb431795c0e4abe158bc784e9", size = 210392, upload-time = "2025-10-02T14:34:45.042Z" }, - { url = "https://files.pythonhosted.org/packages/1e/c2/ff69efd07c8c074ccdf0a4f36fcdd3d27363665bcdf4ba399abebe643465/xxhash-3.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6fb5f5476bef678f69db04f2bd1efbed3030d2aba305b0fc1773645f187d6a4e", size = 197898, upload-time = "2025-10-02T14:34:46.302Z" }, - { url = "https://files.pythonhosted.org/packages/58/ca/faa05ac19b3b622c7c9317ac3e23954187516298a091eb02c976d0d3dd45/xxhash-3.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:843b52f6d88071f87eba1631b684fcb4b2068cd2180a0224122fe4ef011a9374", size = 210655, upload-time = "2025-10-02T14:34:47.571Z" }, - { url = "https://files.pythonhosted.org/packages/d4/7a/06aa7482345480cc0cb597f5c875b11a82c3953f534394f620b0be2f700c/xxhash-3.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7d14a6cfaf03b1b6f5f9790f76880601ccc7896aff7ab9cd8978a939c1eb7e0d", size = 414001, upload-time = "2025-10-02T14:34:49.273Z" }, - { url = "https://files.pythonhosted.org/packages/23/07/63ffb386cd47029aa2916b3d2f454e6cc5b9f5c5ada3790377d5430084e7/xxhash-3.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:418daf3db71e1413cfe211c2f9a528456936645c17f46b5204705581a45390ae", size = 191431, upload-time = "2025-10-02T14:34:50.798Z" }, - { url = "https://files.pythonhosted.org/packages/0f/93/14fde614cadb4ddf5e7cebf8918b7e8fac5ae7861c1875964f17e678205c/xxhash-3.6.0-cp312-cp312-win32.whl", hash = "sha256:50fc255f39428a27299c20e280d6193d8b63b8ef8028995323bf834a026b4fbb", size = 30617, upload-time = "2025-10-02T14:34:51.954Z" }, - { url = "https://files.pythonhosted.org/packages/13/5d/0d125536cbe7565a83d06e43783389ecae0c0f2ed037b48ede185de477c0/xxhash-3.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:c0f2ab8c715630565ab8991b536ecded9416d615538be8ecddce43ccf26cbc7c", size = 31534, upload-time = "2025-10-02T14:34:53.276Z" }, - { url = "https://files.pythonhosted.org/packages/54/85/6ec269b0952ec7e36ba019125982cf11d91256a778c7c3f98a4c5043d283/xxhash-3.6.0-cp312-cp312-win_arm64.whl", hash = "sha256:eae5c13f3bc455a3bbb68bdc513912dc7356de7e2280363ea235f71f54064829", size = 27876, upload-time = "2025-10-02T14:34:54.371Z" }, - { url = "https://files.pythonhosted.org/packages/33/76/35d05267ac82f53ae9b0e554da7c5e281ee61f3cad44c743f0fcd354f211/xxhash-3.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:599e64ba7f67472481ceb6ee80fa3bd828fd61ba59fb11475572cc5ee52b89ec", size = 32738, upload-time = "2025-10-02T14:34:55.839Z" }, - { url = "https://files.pythonhosted.org/packages/31/a8/3fbce1cd96534a95e35d5120637bf29b0d7f5d8fa2f6374e31b4156dd419/xxhash-3.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7d8b8aaa30fca4f16f0c84a5c8d7ddee0e25250ec2796c973775373257dde8f1", size = 30821, upload-time = "2025-10-02T14:34:57.219Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ea/d387530ca7ecfa183cb358027f1833297c6ac6098223fd14f9782cd0015c/xxhash-3.6.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d597acf8506d6e7101a4a44a5e428977a51c0fadbbfd3c39650cca9253f6e5a6", size = 194127, upload-time = "2025-10-02T14:34:59.21Z" }, - { url = "https://files.pythonhosted.org/packages/ba/0c/71435dcb99874b09a43b8d7c54071e600a7481e42b3e3ce1eb5226a5711a/xxhash-3.6.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:858dc935963a33bc33490128edc1c12b0c14d9c7ebaa4e387a7869ecc4f3e263", size = 212975, upload-time = "2025-10-02T14:35:00.816Z" }, - { url = "https://files.pythonhosted.org/packages/84/7a/c2b3d071e4bb4a90b7057228a99b10d51744878f4a8a6dd643c8bd897620/xxhash-3.6.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba284920194615cb8edf73bf52236ce2e1664ccd4a38fdb543506413529cc546", size = 212241, upload-time = "2025-10-02T14:35:02.207Z" }, - { url = "https://files.pythonhosted.org/packages/81/5f/640b6eac0128e215f177df99eadcd0f1b7c42c274ab6a394a05059694c5a/xxhash-3.6.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b54219177f6c6674d5378bd862c6aedf64725f70dd29c472eaae154df1a2e89", size = 445471, upload-time = "2025-10-02T14:35:03.61Z" }, - { url = "https://files.pythonhosted.org/packages/5e/1e/3c3d3ef071b051cc3abbe3721ffb8365033a172613c04af2da89d5548a87/xxhash-3.6.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:42c36dd7dbad2f5238950c377fcbf6811b1cdb1c444fab447960030cea60504d", size = 193936, upload-time = "2025-10-02T14:35:05.013Z" }, - { url = "https://files.pythonhosted.org/packages/2c/bd/4a5f68381939219abfe1c22a9e3a5854a4f6f6f3c4983a87d255f21f2e5d/xxhash-3.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f22927652cba98c44639ffdc7aaf35828dccf679b10b31c4ad72a5b530a18eb7", size = 210440, upload-time = "2025-10-02T14:35:06.239Z" }, - { url = "https://files.pythonhosted.org/packages/eb/37/b80fe3d5cfb9faff01a02121a0f4d565eb7237e9e5fc66e73017e74dcd36/xxhash-3.6.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b45fad44d9c5c119e9c6fbf2e1c656a46dc68e280275007bbfd3d572b21426db", size = 197990, upload-time = "2025-10-02T14:35:07.735Z" }, - { url = "https://files.pythonhosted.org/packages/d7/fd/2c0a00c97b9e18f72e1f240ad4e8f8a90fd9d408289ba9c7c495ed7dc05c/xxhash-3.6.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6f2580ffab1a8b68ef2b901cde7e55fa8da5e4be0977c68f78fc80f3c143de42", size = 210689, upload-time = "2025-10-02T14:35:09.438Z" }, - { url = "https://files.pythonhosted.org/packages/93/86/5dd8076a926b9a95db3206aba20d89a7fc14dd5aac16e5c4de4b56033140/xxhash-3.6.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40c391dd3cd041ebc3ffe6f2c862f402e306eb571422e0aa918d8070ba31da11", size = 414068, upload-time = "2025-10-02T14:35:11.162Z" }, - { url = "https://files.pythonhosted.org/packages/af/3c/0bb129170ee8f3650f08e993baee550a09593462a5cddd8e44d0011102b1/xxhash-3.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f205badabde7aafd1a31e8ca2a3e5a763107a71c397c4481d6a804eb5063d8bd", size = 191495, upload-time = "2025-10-02T14:35:12.971Z" }, - { url = "https://files.pythonhosted.org/packages/e9/3a/6797e0114c21d1725e2577508e24006fd7ff1d8c0c502d3b52e45c1771d8/xxhash-3.6.0-cp313-cp313-win32.whl", hash = "sha256:2577b276e060b73b73a53042ea5bd5203d3e6347ce0d09f98500f418a9fcf799", size = 30620, upload-time = "2025-10-02T14:35:14.129Z" }, - { url = "https://files.pythonhosted.org/packages/86/15/9bc32671e9a38b413a76d24722a2bf8784a132c043063a8f5152d390b0f9/xxhash-3.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:757320d45d2fbcce8f30c42a6b2f47862967aea7bf458b9625b4bbe7ee390392", size = 31542, upload-time = "2025-10-02T14:35:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/39/c5/cc01e4f6188656e56112d6a8e0dfe298a16934b8c47a247236549a3f7695/xxhash-3.6.0-cp313-cp313-win_arm64.whl", hash = "sha256:457b8f85dec5825eed7b69c11ae86834a018b8e3df5e77783c999663da2f96d6", size = 27880, upload-time = "2025-10-02T14:35:16.315Z" }, - { url = "https://files.pythonhosted.org/packages/f3/30/25e5321c8732759e930c555176d37e24ab84365482d257c3b16362235212/xxhash-3.6.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a42e633d75cdad6d625434e3468126c73f13f7584545a9cf34e883aa1710e702", size = 32956, upload-time = "2025-10-02T14:35:17.413Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3c/0573299560d7d9f8ab1838f1efc021a280b5ae5ae2e849034ef3dee18810/xxhash-3.6.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:568a6d743219e717b07b4e03b0a828ce593833e498c3b64752e0f5df6bfe84db", size = 31072, upload-time = "2025-10-02T14:35:18.844Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1c/52d83a06e417cd9d4137722693424885cc9878249beb3a7c829e74bf7ce9/xxhash-3.6.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bec91b562d8012dae276af8025a55811b875baace6af510412a5e58e3121bc54", size = 196409, upload-time = "2025-10-02T14:35:20.31Z" }, - { url = "https://files.pythonhosted.org/packages/e3/8e/c6d158d12a79bbd0b878f8355432075fc82759e356ab5a111463422a239b/xxhash-3.6.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78e7f2f4c521c30ad5e786fdd6bae89d47a32672a80195467b5de0480aa97b1f", size = 215736, upload-time = "2025-10-02T14:35:21.616Z" }, - { url = "https://files.pythonhosted.org/packages/bc/68/c4c80614716345d55071a396cf03d06e34b5f4917a467faf43083c995155/xxhash-3.6.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3ed0df1b11a79856df5ffcab572cbd6b9627034c1c748c5566fa79df9048a7c5", size = 214833, upload-time = "2025-10-02T14:35:23.32Z" }, - { url = "https://files.pythonhosted.org/packages/7e/e9/ae27c8ffec8b953efa84c7c4a6c6802c263d587b9fc0d6e7cea64e08c3af/xxhash-3.6.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0e4edbfc7d420925b0dd5e792478ed393d6e75ff8fc219a6546fb446b6a417b1", size = 448348, upload-time = "2025-10-02T14:35:25.111Z" }, - { url = "https://files.pythonhosted.org/packages/d7/6b/33e21afb1b5b3f46b74b6bd1913639066af218d704cc0941404ca717fc57/xxhash-3.6.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fba27a198363a7ef87f8c0f6b171ec36b674fe9053742c58dd7e3201c1ab30ee", size = 196070, upload-time = "2025-10-02T14:35:26.586Z" }, - { url = "https://files.pythonhosted.org/packages/96/b6/fcabd337bc5fa624e7203aa0fa7d0c49eed22f72e93229431752bddc83d9/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:794fe9145fe60191c6532fa95063765529770edcdd67b3d537793e8004cabbfd", size = 212907, upload-time = "2025-10-02T14:35:28.087Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d3/9ee6160e644d660fcf176c5825e61411c7f62648728f69c79ba237250143/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:6105ef7e62b5ac73a837778efc331a591d8442f8ef5c7e102376506cb4ae2729", size = 200839, upload-time = "2025-10-02T14:35:29.857Z" }, - { url = "https://files.pythonhosted.org/packages/0d/98/e8de5baa5109394baf5118f5e72ab21a86387c4f89b0e77ef3e2f6b0327b/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f01375c0e55395b814a679b3eea205db7919ac2af213f4a6682e01220e5fe292", size = 213304, upload-time = "2025-10-02T14:35:31.222Z" }, - { url = "https://files.pythonhosted.org/packages/7b/1d/71056535dec5c3177eeb53e38e3d367dd1d16e024e63b1cee208d572a033/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d706dca2d24d834a4661619dcacf51a75c16d65985718d6a7d73c1eeeb903ddf", size = 416930, upload-time = "2025-10-02T14:35:32.517Z" }, - { url = "https://files.pythonhosted.org/packages/dc/6c/5cbde9de2cd967c322e651c65c543700b19e7ae3e0aae8ece3469bf9683d/xxhash-3.6.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5f059d9faeacd49c0215d66f4056e1326c80503f51a1532ca336a385edadd033", size = 193787, upload-time = "2025-10-02T14:35:33.827Z" }, - { url = "https://files.pythonhosted.org/packages/19/fa/0172e350361d61febcea941b0cc541d6e6c8d65d153e85f850a7b256ff8a/xxhash-3.6.0-cp313-cp313t-win32.whl", hash = "sha256:1244460adc3a9be84731d72b8e80625788e5815b68da3da8b83f78115a40a7ec", size = 30916, upload-time = "2025-10-02T14:35:35.107Z" }, - { url = "https://files.pythonhosted.org/packages/ad/e6/e8cf858a2b19d6d45820f072eff1bea413910592ff17157cabc5f1227a16/xxhash-3.6.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b1e420ef35c503869c4064f4a2f2b08ad6431ab7b229a05cce39d74268bca6b8", size = 31799, upload-time = "2025-10-02T14:35:36.165Z" }, - { url = "https://files.pythonhosted.org/packages/56/15/064b197e855bfb7b343210e82490ae672f8bc7cdf3ddb02e92f64304ee8a/xxhash-3.6.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ec44b73a4220623235f67a996c862049f375df3b1052d9899f40a6382c32d746", size = 28044, upload-time = "2025-10-02T14:35:37.195Z" }, - { url = "https://files.pythonhosted.org/packages/7e/5e/0138bc4484ea9b897864d59fce9be9086030825bc778b76cb5a33a906d37/xxhash-3.6.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a40a3d35b204b7cc7643cbcf8c9976d818cb47befcfac8bbefec8038ac363f3e", size = 32754, upload-time = "2025-10-02T14:35:38.245Z" }, - { url = "https://files.pythonhosted.org/packages/18/d7/5dac2eb2ec75fd771957a13e5dda560efb2176d5203f39502a5fc571f899/xxhash-3.6.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a54844be970d3fc22630b32d515e79a90d0a3ddb2644d8d7402e3c4c8da61405", size = 30846, upload-time = "2025-10-02T14:35:39.6Z" }, - { url = "https://files.pythonhosted.org/packages/fe/71/8bc5be2bb00deb5682e92e8da955ebe5fa982da13a69da5a40a4c8db12fb/xxhash-3.6.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:016e9190af8f0a4e3741343777710e3d5717427f175adfdc3e72508f59e2a7f3", size = 194343, upload-time = "2025-10-02T14:35:40.69Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3b/52badfb2aecec2c377ddf1ae75f55db3ba2d321c5e164f14461c90837ef3/xxhash-3.6.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f6f72232f849eb9d0141e2ebe2677ece15adfd0fa599bc058aad83c714bb2c6", size = 213074, upload-time = "2025-10-02T14:35:42.29Z" }, - { url = "https://files.pythonhosted.org/packages/a2/2b/ae46b4e9b92e537fa30d03dbc19cdae57ed407e9c26d163895e968e3de85/xxhash-3.6.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:63275a8aba7865e44b1813d2177e0f5ea7eadad3dd063a21f7cf9afdc7054063", size = 212388, upload-time = "2025-10-02T14:35:43.929Z" }, - { url = "https://files.pythonhosted.org/packages/f5/80/49f88d3afc724b4ac7fbd664c8452d6db51b49915be48c6982659e0e7942/xxhash-3.6.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cd01fa2aa00d8b017c97eb46b9a794fbdca53fc14f845f5a328c71254b0abb7", size = 445614, upload-time = "2025-10-02T14:35:45.216Z" }, - { url = "https://files.pythonhosted.org/packages/ed/ba/603ce3961e339413543d8cd44f21f2c80e2a7c5cfe692a7b1f2cccf58f3c/xxhash-3.6.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0226aa89035b62b6a86d3c68df4d7c1f47a342b8683da2b60cedcddb46c4d95b", size = 194024, upload-time = "2025-10-02T14:35:46.959Z" }, - { url = "https://files.pythonhosted.org/packages/78/d1/8e225ff7113bf81545cfdcd79eef124a7b7064a0bba53605ff39590b95c2/xxhash-3.6.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c6e193e9f56e4ca4923c61238cdaced324f0feac782544eb4c6d55ad5cc99ddd", size = 210541, upload-time = "2025-10-02T14:35:48.301Z" }, - { url = "https://files.pythonhosted.org/packages/6f/58/0f89d149f0bad89def1a8dd38feb50ccdeb643d9797ec84707091d4cb494/xxhash-3.6.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9176dcaddf4ca963d4deb93866d739a343c01c969231dbe21680e13a5d1a5bf0", size = 198305, upload-time = "2025-10-02T14:35:49.584Z" }, - { url = "https://files.pythonhosted.org/packages/11/38/5eab81580703c4df93feb5f32ff8fa7fe1e2c51c1f183ee4e48d4bb9d3d7/xxhash-3.6.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c1ce4009c97a752e682b897aa99aef84191077a9433eb237774689f14f8ec152", size = 210848, upload-time = "2025-10-02T14:35:50.877Z" }, - { url = "https://files.pythonhosted.org/packages/5e/6b/953dc4b05c3ce678abca756416e4c130d2382f877a9c30a20d08ee6a77c0/xxhash-3.6.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:8cb2f4f679b01513b7adbb9b1b2f0f9cdc31b70007eaf9d59d0878809f385b11", size = 414142, upload-time = "2025-10-02T14:35:52.15Z" }, - { url = "https://files.pythonhosted.org/packages/08/a9/238ec0d4e81a10eb5026d4a6972677cbc898ba6c8b9dbaec12ae001b1b35/xxhash-3.6.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:653a91d7c2ab54a92c19ccf43508b6a555440b9be1bc8be553376778be7f20b5", size = 191547, upload-time = "2025-10-02T14:35:53.547Z" }, - { url = "https://files.pythonhosted.org/packages/f1/ee/3cf8589e06c2164ac77c3bf0aa127012801128f1feebf2a079272da5737c/xxhash-3.6.0-cp314-cp314-win32.whl", hash = "sha256:a756fe893389483ee8c394d06b5ab765d96e68fbbfe6fde7aa17e11f5720559f", size = 31214, upload-time = "2025-10-02T14:35:54.746Z" }, - { url = "https://files.pythonhosted.org/packages/02/5d/a19552fbc6ad4cb54ff953c3908bbc095f4a921bc569433d791f755186f1/xxhash-3.6.0-cp314-cp314-win_amd64.whl", hash = "sha256:39be8e4e142550ef69629c9cd71b88c90e9a5db703fecbcf265546d9536ca4ad", size = 32290, upload-time = "2025-10-02T14:35:55.791Z" }, - { url = "https://files.pythonhosted.org/packages/b1/11/dafa0643bc30442c887b55baf8e73353a344ee89c1901b5a5c54a6c17d39/xxhash-3.6.0-cp314-cp314-win_arm64.whl", hash = "sha256:25915e6000338999236f1eb68a02a32c3275ac338628a7eaa5a269c401995679", size = 28795, upload-time = "2025-10-02T14:35:57.162Z" }, - { url = "https://files.pythonhosted.org/packages/2c/db/0e99732ed7f64182aef4a6fb145e1a295558deec2a746265dcdec12d191e/xxhash-3.6.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c5294f596a9017ca5a3e3f8884c00b91ab2ad2933cf288f4923c3fd4346cf3d4", size = 32955, upload-time = "2025-10-02T14:35:58.267Z" }, - { url = "https://files.pythonhosted.org/packages/55/f4/2a7c3c68e564a099becfa44bb3d398810cc0ff6749b0d3cb8ccb93f23c14/xxhash-3.6.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1cf9dcc4ab9cff01dfbba78544297a3a01dafd60f3bde4e2bfd016cf7e4ddc67", size = 31072, upload-time = "2025-10-02T14:35:59.382Z" }, - { url = "https://files.pythonhosted.org/packages/c6/d9/72a29cddc7250e8a5819dad5d466facb5dc4c802ce120645630149127e73/xxhash-3.6.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:01262da8798422d0685f7cef03b2bd3f4f46511b02830861df548d7def4402ad", size = 196579, upload-time = "2025-10-02T14:36:00.838Z" }, - { url = "https://files.pythonhosted.org/packages/63/93/b21590e1e381040e2ca305a884d89e1c345b347404f7780f07f2cdd47ef4/xxhash-3.6.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51a73fb7cb3a3ead9f7a8b583ffd9b8038e277cdb8cb87cf890e88b3456afa0b", size = 215854, upload-time = "2025-10-02T14:36:02.207Z" }, - { url = "https://files.pythonhosted.org/packages/ce/b8/edab8a7d4fa14e924b29be877d54155dcbd8b80be85ea00d2be3413a9ed4/xxhash-3.6.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b9c6df83594f7df8f7f708ce5ebeacfc69f72c9fbaaababf6cf4758eaada0c9b", size = 214965, upload-time = "2025-10-02T14:36:03.507Z" }, - { url = "https://files.pythonhosted.org/packages/27/67/dfa980ac7f0d509d54ea0d5a486d2bb4b80c3f1bb22b66e6a05d3efaf6c0/xxhash-3.6.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:627f0af069b0ea56f312fd5189001c24578868643203bca1abbc2c52d3a6f3ca", size = 448484, upload-time = "2025-10-02T14:36:04.828Z" }, - { url = "https://files.pythonhosted.org/packages/8c/63/8ffc2cc97e811c0ca5d00ab36604b3ea6f4254f20b7bc658ca825ce6c954/xxhash-3.6.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa912c62f842dfd013c5f21a642c9c10cd9f4c4e943e0af83618b4a404d9091a", size = 196162, upload-time = "2025-10-02T14:36:06.182Z" }, - { url = "https://files.pythonhosted.org/packages/4b/77/07f0e7a3edd11a6097e990f6e5b815b6592459cb16dae990d967693e6ea9/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b465afd7909db30168ab62afe40b2fcf79eedc0b89a6c0ab3123515dc0df8b99", size = 213007, upload-time = "2025-10-02T14:36:07.733Z" }, - { url = "https://files.pythonhosted.org/packages/ae/d8/bc5fa0d152837117eb0bef6f83f956c509332ce133c91c63ce07ee7c4873/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a881851cf38b0a70e7c4d3ce81fc7afd86fbc2a024f4cfb2a97cf49ce04b75d3", size = 200956, upload-time = "2025-10-02T14:36:09.106Z" }, - { url = "https://files.pythonhosted.org/packages/26/a5/d749334130de9411783873e9b98ecc46688dad5db64ca6e04b02acc8b473/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9b3222c686a919a0f3253cfc12bb118b8b103506612253b5baeaac10d8027cf6", size = 213401, upload-time = "2025-10-02T14:36:10.585Z" }, - { url = "https://files.pythonhosted.org/packages/89/72/abed959c956a4bfc72b58c0384bb7940663c678127538634d896b1195c10/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:c5aa639bc113e9286137cec8fadc20e9cd732b2cc385c0b7fa673b84fc1f2a93", size = 417083, upload-time = "2025-10-02T14:36:12.276Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b3/62fd2b586283b7d7d665fb98e266decadf31f058f1cf6c478741f68af0cb/xxhash-3.6.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5c1343d49ac102799905e115aee590183c3921d475356cb24b4de29a4bc56518", size = 193913, upload-time = "2025-10-02T14:36:14.025Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9a/c19c42c5b3f5a4aad748a6d5b4f23df3bed7ee5445accc65a0fb3ff03953/xxhash-3.6.0-cp314-cp314t-win32.whl", hash = "sha256:5851f033c3030dd95c086b4a36a2683c2ff4a799b23af60977188b057e467119", size = 31586, upload-time = "2025-10-02T14:36:15.603Z" }, - { url = "https://files.pythonhosted.org/packages/03/d6/4cc450345be9924fd5dc8c590ceda1db5b43a0a889587b0ae81a95511360/xxhash-3.6.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0444e7967dac37569052d2409b00a8860c2135cff05502df4da80267d384849f", size = 32526, upload-time = "2025-10-02T14:36:16.708Z" }, - { url = "https://files.pythonhosted.org/packages/0f/c9/7243eb3f9eaabd1a88a5a5acadf06df2d83b100c62684b7425c6a11bcaa8/xxhash-3.6.0-cp314-cp314t-win_arm64.whl", hash = "sha256:bb79b1e63f6fd84ec778a4b1916dfe0a7c3fdb986c06addd5db3a0d413819d95", size = 28898, upload-time = "2025-10-02T14:36:17.843Z" }, +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/2f/e183a1b407002f5af81822bee18b61cdb94b8670208ef34734d8d2b8ebe9/xxhash-3.7.0.tar.gz", hash = "sha256:6cc4eefbb542a5d6ffd6d70ea9c502957c925e800f998c5630ecc809d6702bae", size = 82022, upload-time = "2026-04-25T11:10:32.553Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/8a/51a14cdef4728c6c2337db8a7d8704422cc65676d9199d77215464c880af/xxhash-3.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:082c87bfdd2b9f457606c7a4a53457f4c4b48b0cdc48de0277f4349d79bb3d7a", size = 33357, upload-time = "2026-04-25T11:06:20.44Z" }, + { url = "https://files.pythonhosted.org/packages/b9/1b/0c2c933809421ffd9bf42b59315552c143c755db5d9a816b2f1ae273e884/xxhash-3.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5e7ce913b61f35b0c1c839a49ac9c8e75dd8d860150688aed353b0ce1bf409d8", size = 30869, upload-time = "2026-04-25T11:06:21.989Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/89d5fdd6ee12d70ba99451de46dd0e8010167468dcd913ec855653f4dd50/xxhash-3.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3beb1de3b1e9694fcdd853e570ee64c631c7062435d2f8c69c1adf809bc086f0", size = 194100, upload-time = "2026-04-25T11:06:23.586Z" }, + { url = "https://files.pythonhosted.org/packages/87/ee/2f9f2ed993e77206d1e66991290a1ebe22e843351ca3ebec8e49e01ba186/xxhash-3.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3e7b689c3bce16699efcf736066f5c6cc4472c3840fe4b22bd8279daf4abdac", size = 212977, upload-time = "2026-04-25T11:06:25.019Z" }, + { url = "https://files.pythonhosted.org/packages/de/60/5a91644615a9e9d4e42c2e9925f1908e3a24e4e691d9de7340d565bea024/xxhash-3.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a6545e6b409e3d5cbafc850fb84c55a1ca26ed15a6b11e3bf07a0e0cd84517c8", size = 236373, upload-time = "2026-04-25T11:06:26.482Z" }, + { url = "https://files.pythonhosted.org/packages/22/c0/f3a9384eaaed9d14d4d062a5d953aa0da489bfe9747877aa994caa87cd0b/xxhash-3.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:31ab1461c77a11461d703c88eb949e132a1c6515933cf675d97ec680f4bd18de", size = 212229, upload-time = "2026-04-25T11:06:28.065Z" }, + { url = "https://files.pythonhosted.org/packages/2e/67/02f07a9fd79726804190f2172c4894c3ed9a4ebccaca05653c84beb58025/xxhash-3.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c4d596b7676f811172687ec567cbafb9e4dea2f9be1bbb4f622410cb7f40f40", size = 445462, upload-time = "2026-04-25T11:06:30.048Z" }, + { url = "https://files.pythonhosted.org/packages/40/37/558f5a90c0672fc9b4402dc25d87ac5b7406616e8969430c9ca4e52ee74d/xxhash-3.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13805f0461cba0a857924e70ff91ae6d52d2598f79a884e788db80532614a4a1", size = 193932, upload-time = "2026-04-25T11:06:31.857Z" }, + { url = "https://files.pythonhosted.org/packages/d5/90/aaa09cd58661d32044dbbad7df55bbe22a623032b810e7ed3b8c569a2a6f/xxhash-3.7.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d398f372496152f1c6933a33566373f8d1b37b98b8c9d608fa6edc0976f23b2", size = 284807, upload-time = "2026-04-25T11:06:33.697Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f3/53df3719ab127a02c174f0c1c74924fcd110866e89c966bc7909cfa8fa84/xxhash-3.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d610aa62cdb7d4d497740741772a24a794903bf3e79eaa51d2e800082abe11e5", size = 210445, upload-time = "2026-04-25T11:06:35.488Z" }, + { url = "https://files.pythonhosted.org/packages/72/33/d219975c0e8b6fa2eb9ccd486fe47e21bf1847985b878dd2fbc3126e0d5c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:073c23900a9fbf3d26616c17c830db28af9803677cd5b33aea3224d824111514", size = 241273, upload-time = "2026-04-25T11:06:37.24Z" }, + { url = "https://files.pythonhosted.org/packages/3e/50/49b1afe610eb3964cedcb90a4d4c3d46a261ee8669cbd4f060652619ae3c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:418a463c3e6a590c0cdc890f8be19adb44a8c8acd175ca5b2a6de77e61d0b386", size = 197950, upload-time = "2026-04-25T11:06:39.148Z" }, + { url = "https://files.pythonhosted.org/packages/c6/75/5f42a1a4c78717d906a4b6a140c6dbf837ab1f547a54d23c4e2903310936/xxhash-3.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:03f8ff4474ee61c845758ce00711d7087a770d77efb36f7e74a6e867301000b8", size = 210709, upload-time = "2026-04-25T11:06:40.958Z" }, + { url = "https://files.pythonhosted.org/packages/8a/85/237e446c25abced71e9c53d269f2cef5bab8a82b3f88a12e00c5368e7368/xxhash-3.7.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:44fba4a5f1d179b7ddc7b3dc40f56f9209046421679b57025d4d8821b376fd8d", size = 275345, upload-time = "2026-04-25T11:06:42.525Z" }, + { url = "https://files.pythonhosted.org/packages/62/34/c2c26c0a6a9cc739bc2a5f0ae03ba8b87deb12b8bce35f7ac495e790dc6d/xxhash-3.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31e3516a0f829d06ded4a2c0f3c7c5561993256bfa1c493975fb9dc7bfa828a1", size = 414056, upload-time = "2026-04-25T11:06:44.343Z" }, + { url = "https://files.pythonhosted.org/packages/a0/aa/5c58e9bc8071b8afd8dcf297ff362f723c4892168faba149f19904132bf4/xxhash-3.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b59ee2ac81de57771a09ecad09191e840a1d2fae1ef684208320591055768f83", size = 191485, upload-time = "2026-04-25T11:06:46.262Z" }, + { url = "https://files.pythonhosted.org/packages/d4/69/a929cf9d1e2e65a48b818cdce72cb6b69eab2e6877f21436d0a1942aff43/xxhash-3.7.0-cp312-cp312-win32.whl", hash = "sha256:74bbd92f8c7fcc397ba0a11bfdc106bc72ad7f11e3a60277753f87e7532b4d81", size = 30671, upload-time = "2026-04-25T11:06:48.039Z" }, + { url = "https://files.pythonhosted.org/packages/b9/1b/104b41a8947f4e1d4a66ce1e628eea752f37d1890bfd7453559ca7a3d950/xxhash-3.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:7bd7bc82dd4f185f28f35193c2e968ef46131628e3cac62f639dadf321cba4d1", size = 31514, upload-time = "2026-04-25T11:06:49.279Z" }, + { url = "https://files.pythonhosted.org/packages/98/a0/1fd0ea1f1b886d9e7c73f0397571e22333a7d79e31da6d7127c2a4a71d75/xxhash-3.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:7d7148180ec99ba36585b42c8c5de25e9b40191613bc4be68909b4d25a77a852", size = 27761, upload-time = "2026-04-25T11:06:50.448Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ca/d5174b4c36d10f64d4ca7050563138c5a599efb01a765858ddefc9c1202a/xxhash-3.7.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:4b6d6b33f141158692bd4eafbb96edbc5aa0dabdb593a962db01a91983d4f8fa", size = 36813, upload-time = "2026-04-25T11:06:51.73Z" }, + { url = "https://files.pythonhosted.org/packages/41/d0/abc6c9d347ba1f1e1e1d98125d0881a0452c7f9a76a9dd03a7b5d2197f23/xxhash-3.7.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:845d347df254d6c619f616afa921331bada8614b8d373d58725c663ba97c3605", size = 35121, upload-time = "2026-04-25T11:06:53.048Z" }, + { url = "https://files.pythonhosted.org/packages/bf/11/4cc834eb3d79f2f2b3a6ef7324195208bcdfbdcf7534d2b17267aa5f3a8f/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:fddbbb69a6fff4f421e7a0d1fa28f894b20112e9e3fab306af451e2dfd0e459b", size = 29624, upload-time = "2026-04-25T11:06:54.311Z" }, + { url = "https://files.pythonhosted.org/packages/23/83/e97d3e7b635fe73a1dfb1e91f805324dd6d930bb42041cbf18f183bc0b6d/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:54876a4e45101cec2bf8f31a973cda073a23e2e108538dad224ba07f85f22487", size = 30638, upload-time = "2026-04-25T11:06:55.864Z" }, + { url = "https://files.pythonhosted.org/packages/f4/40/d84951d80c35db1f4c40a29a64a8520eea5d56e764c603906b4fe763580f/xxhash-3.7.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:0c72fe9c7e3d6dfd7f1e21e224a877917fa09c465694ba4e06464b9511b65544", size = 33323, upload-time = "2026-04-25T11:06:57.336Z" }, + { url = "https://files.pythonhosted.org/packages/89/cc/c7dc6558d97e9ab023f663d69ab28b340ed9bf4d2d94f2c259cf896bb354/xxhash-3.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a6d73a830b17ef49bc04e00182bd839164c1b3c59c127cd7c54fcb10c7ed8ee8", size = 33362, upload-time = "2026-04-25T11:06:58.656Z" }, + { url = "https://files.pythonhosted.org/packages/2a/6e/46b84017b1301d54091430353d4ad5901654a3e0871649877a416f7f1644/xxhash-3.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:91c3b07cf3362086d8f126c6aecd8e5e9396ad8b2f2219ea7e49a8250c318acd", size = 30874, upload-time = "2026-04-25T11:06:59.834Z" }, + { url = "https://files.pythonhosted.org/packages/df/5e/8f9158e3ab906ad3fec51e09b5ea0093e769f12207bfa42a368ca204e7ab/xxhash-3.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:50e879ebbac351c81565ca108db766d7832f5b8b6a5b14b8c0151f7190028e3d", size = 194185, upload-time = "2026-04-25T11:07:01.658Z" }, + { url = "https://files.pythonhosted.org/packages/f3/29/a804ded9f5d3d3758292678d23e7528b08fda7b7e750688d08b052322475/xxhash-3.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:921c14e93817842dd0dd9f372890a0f0c72e534650b6ab13c5be5cd0db11d47e", size = 213033, upload-time = "2026-04-25T11:07:03.606Z" }, + { url = "https://files.pythonhosted.org/packages/8b/91/1ce5a7d2fdc975267320e2c78fc1cecfe7ab735ccbcf6993ec5dd541cb2c/xxhash-3.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e64a7c9d7dfca3e0fafcbc5e455519090706a3e36e95d655cec3e04e79f95aaa", size = 236140, upload-time = "2026-04-25T11:07:05.396Z" }, + { url = "https://files.pythonhosted.org/packages/34/04/fd595a4fd8617b05fa27bd9b684ecb4985bfed27917848eea85d54036d06/xxhash-3.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2220af08163baf5fa36c2b8af079dc2cbe6e66ae061385267f9472362dfd53c6", size = 212291, upload-time = "2026-04-25T11:07:06.966Z" }, + { url = "https://files.pythonhosted.org/packages/03/fb/f1a379cbc372ae5b9f4ab36154c48a849ca6ebe3ac477067a57865bf3bc6/xxhash-3.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f14bb8b22a4a91325813e3d553b8963c10cf8c756cff65ee50c194431296c655", size = 445532, upload-time = "2026-04-25T11:07:08.525Z" }, + { url = "https://files.pythonhosted.org/packages/65/59/172424b79f8cfd4b6d8a122b2193e6b8ad4b11f7159bb3b6f9b3191329bb/xxhash-3.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:496736f86a9bedaf64b0dc70e3539d0766df01c71ea22032698e88f3f04a1ce9", size = 193990, upload-time = "2026-04-25T11:07:10.315Z" }, + { url = "https://files.pythonhosted.org/packages/b9/19/aeac22161d953f139f07ba5586cb4a17c5b7b6dff985122803bb12933500/xxhash-3.7.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0ff71596bd79816975b3de7130ab1ff4541410285a3c084584eeb1c8239996fd", size = 284876, upload-time = "2026-04-25T11:07:12.15Z" }, + { url = "https://files.pythonhosted.org/packages/77/d5/4fd0b59e7a02242953da05ff679fbb961b0a4368eac97a217e11dae110c1/xxhash-3.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1ad86695c19b1d46fe106925db3c7a37f16be37669dcf58dcc70a9dd6e324676", size = 210495, upload-time = "2026-04-25T11:07:13.952Z" }, + { url = "https://files.pythonhosted.org/packages/aa/fb/976a3165c728c7faf74aa1b5ab3cf6a85e6d731612894741840524c7d28c/xxhash-3.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:970f9f8c50961d639cbd0d988c96f80ddf66006de93641719282c4fe7a87c5e6", size = 241331, upload-time = "2026-04-25T11:07:15.557Z" }, + { url = "https://files.pythonhosted.org/packages/4a/2c/6763d5901d53ac9e6ba296e5717ae599025c9d268396e8faa8b4b0a8e0ac/xxhash-3.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5886ad85e9e347911783760a1d16cb6b393e8f9e3b52c982568226cb56927bdc", size = 198037, upload-time = "2026-04-25T11:07:17.563Z" }, + { url = "https://files.pythonhosted.org/packages/61/2b/876e722d533833f5f9a83473e6ba993e48745701096944e77bbecf29b2c3/xxhash-3.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6e934bbae1e0ec74e27d5f0d7f37ef547ce5ff9f0a7e63fb39e559fc99526734", size = 210744, upload-time = "2026-04-25T11:07:19.055Z" }, + { url = "https://files.pythonhosted.org/packages/21/e6/d7e7baef7ce24166b4668d3c48557bb35a23b92ecadcac7e7718d099ab69/xxhash-3.7.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:3b6b3d28228af044ebcded71c4a3dd86e1dbd7e2f4645bf40f7b5da65bb5fb5a", size = 275406, upload-time = "2026-04-25T11:07:20.908Z" }, + { url = "https://files.pythonhosted.org/packages/92/fe/198b3763b2e01ca908f2154969a2352ec99bda892b574a11a9a151c5ede4/xxhash-3.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:6be4d70d9ab76c9f324ead9c01af6ff52c324745ea0c3731682a0cf99720f1fe", size = 414125, upload-time = "2026-04-25T11:07:23.037Z" }, + { url = "https://files.pythonhosted.org/packages/3a/6d/019a11affd5a5499137cacca53808659964785439855b5aa40dfd3412916/xxhash-3.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:151d7520838d4465461a0b7f4ae488b3b00de16183dd3214c1a6b14bf89d7fb6", size = 191555, upload-time = "2026-04-25T11:07:24.991Z" }, + { url = "https://files.pythonhosted.org/packages/76/21/b96d58568df2d01533244c3e0e5cbdd0c8b2b25c4bec4d72f19259a292d7/xxhash-3.7.0-cp313-cp313-win32.whl", hash = "sha256:d798c1e291bffb8e37b5bbe0dda77fc767cd19e89cadaf66e6ed5d0ff88c9fe6", size = 30668, upload-time = "2026-04-25T11:07:26.665Z" }, + { url = "https://files.pythonhosted.org/packages/99/57/d849a8d3afa1f8f4bc6a831cd89f49f9706fbbad94d2975d6140a171988c/xxhash-3.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:875811ba23c543b1a1c3143c926e43996eb27ebb8f52d3500744aa608c275aed", size = 31524, upload-time = "2026-04-25T11:07:27.92Z" }, + { url = "https://files.pythonhosted.org/packages/81/52/bacc753e92dee78b058af8dcef0a50815f5f860986c664a92d75f965b6a5/xxhash-3.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:54a675cb300dda83d71daae2a599389d22db8021a0f8db0dd659e14626eb3ecc", size = 27768, upload-time = "2026-04-25T11:07:29.113Z" }, + { url = "https://files.pythonhosted.org/packages/1c/47/ddbd683b7fc7e592c1a8d9d65f73ce9ab513f082b3967eee2baf549b8fc6/xxhash-3.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a3b19a42111c4057c1547a4a1396a53961dca576a0f6b82bfa88a2d1561764b2", size = 33576, upload-time = "2026-04-25T11:07:30.469Z" }, + { url = "https://files.pythonhosted.org/packages/07/f2/36d3310161db7f72efb4562aadde0ed429f1d0531782dd6345b12d2da527/xxhash-3.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8f4608a06e4d61b7a3425665a46d00e0579122e1a2fae97a0c52953a3aad9aa3", size = 31123, upload-time = "2026-04-25T11:07:31.989Z" }, + { url = "https://files.pythonhosted.org/packages/0d/3f/75937a5c69556ed213021e43cbedd84c8e0279d0d74e7d41a255d84ba4b1/xxhash-3.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ad37c7792479e49cf96c1ab25517d7003fe0d93687a772ba19a097d235bbe41e", size = 196491, upload-time = "2026-04-25T11:07:33.358Z" }, + { url = "https://files.pythonhosted.org/packages/22/29/f10d7ff8c7a733d4403a43b9de18c8fabc005f98cec054644f04418659ee/xxhash-3.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc026e3b89d98e30a8288c95cb696e77d150b3f0fb7a51f73dcd49ee6b5577fa", size = 215793, upload-time = "2026-04-25T11:07:34.919Z" }, + { url = "https://files.pythonhosted.org/packages/8b/fd/778f60aa295f58907938f030a8b514611f391405614a525cccd2ffc00eb5/xxhash-3.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c9b31ab1f28b078a6a1ac1a54eb35e7d5390deddd56870d0be3a0a733d1c321c", size = 237993, upload-time = "2026-04-25T11:07:36.638Z" }, + { url = "https://files.pythonhosted.org/packages/70/f5/736db5de387b4a540e37a05b84b40dc58a1ce974bfd2b4e5754ce29b68c3/xxhash-3.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3bb5fd680c038fd5229e44e9c493782f90df9bef632fd0499d442374688ff70b", size = 214887, upload-time = "2026-04-25T11:07:38.564Z" }, + { url = "https://files.pythonhosted.org/packages/4d/aa/09a095f22fdb9a27fbb716841fbff52119721f9ca4261952d07a912f7839/xxhash-3.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:030c0fd688fce3569fbb49a2feefd4110cbb0b650186fb4610759ecfac677548", size = 448407, upload-time = "2026-04-25T11:07:40.552Z" }, + { url = "https://files.pythonhosted.org/packages/74/8a/b745efeeca9e34a91c26fdc97ad8514c43d5a81ac78565cba80a1353870a/xxhash-3.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b1bde10324f4c31812ae0d0502e92d916ae8917cad7209353f122b8b8f610c3", size = 196119, upload-time = "2026-04-25T11:07:42.101Z" }, + { url = "https://files.pythonhosted.org/packages/8a/5c/0cfceb024af90c191f665c7933b1f318ee234f4797858383bebd1881d52f/xxhash-3.7.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:503722d52a615f2604f5e7611de7d43878df010dc0053094ef91cb9a9ac3d987", size = 286751, upload-time = "2026-04-25T11:07:43.568Z" }, + { url = "https://files.pythonhosted.org/packages/0b/0a/0793e405dc3cf8f4ebe2c1acec1e4e4608cd9e7e50ea691dabbc2a95ccbb/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c72500a3b6d6c30ebfc135035bcace9eb5884f2dc220804efcaaba43e9f611dd", size = 212961, upload-time = "2026-04-25T11:07:45.388Z" }, + { url = "https://files.pythonhosted.org/packages/0c/7e/721118ffc63bfff94aa565bcf2555a820f9f4bdb0f001e0d609bdfad70de/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:43475925a766d01ca8cd9a857fd87f3d50406983c8506a4c07c4df12adcc867f", size = 243703, upload-time = "2026-04-25T11:07:47.053Z" }, + { url = "https://files.pythonhosted.org/packages/6e/18/16f6267160488b8276fd3d449d425712512add292ba545c1b6946bfdb7dd/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8d09dfd2ab135b985daf868b594315ebe11ad86cd9fea46e6c69f19b28f7d25a", size = 200894, upload-time = "2026-04-25T11:07:48.657Z" }, + { url = "https://files.pythonhosted.org/packages/2d/94/80ba841287fd97e3e9cac1d228788c8ef623746f570404961eec748ecb5c/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c50269d0055ac1faecfd559886d2cbe4b730de236585aba0e873f9d9dadbe585", size = 213357, upload-time = "2026-04-25T11:07:50.257Z" }, + { url = "https://files.pythonhosted.org/packages/a1/7e/106d4067130c59f1e18a55ffadcd876d8c68534883a1e02685b29d3d8153/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:1910df4756a5ab58cfad8744fc2d0f23926e3efcc346ee76e87b974abab922f4", size = 277600, upload-time = "2026-04-25T11:07:51.745Z" }, + { url = "https://files.pythonhosted.org/packages/c5/86/a081dd30da71d720b2612a792bfd55e45fa9a07ac76a0507f60487473c25/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d006faf3b491957efcb433489be3c149efe4787b7063d5cddb8ddaefdc60e0c1", size = 416980, upload-time = "2026-04-25T11:07:53.504Z" }, + { url = "https://files.pythonhosted.org/packages/35/29/1a95221a029a3c1293773869e1ab47b07cbbdd82444a42809e8c60156626/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:abb65b4e947e958f7b3b0d71db3ce447d1bc5f37f5eab871ce7223bda8768a04", size = 193840, upload-time = "2026-04-25T11:07:55.103Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e0/db909dd0823285de2286f67e10ee4d81e96ad35d7d8e964ecb07fccd8af9/xxhash-3.7.0-cp313-cp313t-win32.whl", hash = "sha256:178959906cb1716a1ce08e0d69c82886c70a15a6f2790fc084fdd146ca30cd49", size = 30966, upload-time = "2026-04-25T11:07:56.524Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ff/d705b15b22f21ee106adce239cb65d35067a158c630b240270f09b17c2e6/xxhash-3.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2524a1e20d4c231d13b50f7cf39e44265b055669a64a7a4b9a2a44faa03f19b6", size = 31784, upload-time = "2026-04-25T11:07:57.758Z" }, + { url = "https://files.pythonhosted.org/packages/a2/1f/b2cf83c3638fd0588e0b17f22e5a9400bdfb1a3e3755324ac0aee2250b88/xxhash-3.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:37d994d0ffe81ef087bb330d392caa809bb5853c77e22ea3f71db024a0543dba", size = 27932, upload-time = "2026-04-25T11:07:59.109Z" }, + { url = "https://files.pythonhosted.org/packages/0e/cc/431db584f6fbb9312e40a173af027644e5580d39df1f73603cbb9dca4d6b/xxhash-3.7.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:8c5fcfd806c335bfa2adf1cd0b3110a44fc7b6995c3a648c27489bae85801465", size = 36644, upload-time = "2026-04-25T11:08:00.658Z" }, + { url = "https://files.pythonhosted.org/packages/bc/01/255ec513e0a705d1f9a61413e78dfce4e3235203f0ed525a24c2b4b56345/xxhash-3.7.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:506a0b488f190f0a06769575e30caf71615c898ed93ab18b0dbcb6dec5c3713c", size = 35003, upload-time = "2026-04-25T11:08:02.338Z" }, + { url = "https://files.pythonhosted.org/packages/68/70/c55fc33c93445b44d8fc5a17b41ed99e3cebe92bcf8396809e63fc9a1165/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:ec68dbba21532c0173a9872298e65c89749f7c9d21538c3a78b5bb6105871568", size = 29655, upload-time = "2026-04-25T11:08:03.701Z" }, + { url = "https://files.pythonhosted.org/packages/c2/72/ff8de73df000d74467d12a59ce6d6e2b2a368b978d41ab7b1fba5ed442be/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:fa77e7ec1450d415d20129961814787c9abd9a07f98872f070b1fe96c5084611", size = 30664, upload-time = "2026-04-25T11:08:05.011Z" }, + { url = "https://files.pythonhosted.org/packages/b6/91/08416d9bd9bc3bf39d831abe8a5631ac2db5141dfd6fe81c3fe59a1f9264/xxhash-3.7.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:fe32736295ea38e43e7d9424053c8c47c9f64fecfc7c895fb3da9b30b131c9ee", size = 33317, upload-time = "2026-04-25T11:08:06.413Z" }, + { url = "https://files.pythonhosted.org/packages/0e/3b/86b1caa4dee10a99f4bf9521e623359341c5e50d05158fa10c275b2bd079/xxhash-3.7.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ab9dd2c83c4bbd63e422181a76f13502d049d3ddcac9a1bdc29196263d692bb8", size = 33457, upload-time = "2026-04-25T11:08:08.099Z" }, + { url = "https://files.pythonhosted.org/packages/ed/38/98ea14ad1517e1461292a65906951458d520689782bfbae111050145bdba/xxhash-3.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3afec3a336a2286601a437cb07562ab0227685e6fbb9ec17e8c18457ff348ecf", size = 30894, upload-time = "2026-04-25T11:08:09.429Z" }, + { url = "https://files.pythonhosted.org/packages/61/a2/074654d0b893606541199993c7db70067d9fc63b748e0d60020a52a1bd36/xxhash-3.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:565df64437a9390f84465dcca33e7377114c7ede8d05cd2cf20081f831ea788e", size = 194409, upload-time = "2026-04-25T11:08:10.91Z" }, + { url = "https://files.pythonhosted.org/packages/e2/26/6d2a1afc468189f77ca28c32e1c83e1b9da1178231e05641dbc1b350e332/xxhash-3.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12eca820a5d558633d423bf8bb78ce72a55394823f64089247f788a7e0ae691e", size = 213135, upload-time = "2026-04-25T11:08:12.575Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0e/d8aecf95e09c42547453137be74d2f7b8b14e08f5177fa2fab6144a19061/xxhash-3.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f262b8f7599516567e070abf607b9af649052b2c4bd6f9be02b0cb41b7024805", size = 236379, upload-time = "2026-04-25T11:08:14.206Z" }, + { url = "https://files.pythonhosted.org/packages/f2/74/8140e8210536b3dd0cc816c4faaeb5ba6e63e8125ab25af4bcddd6a037b3/xxhash-3.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1598916cb197681e03e601901e4ab96a9a963de398c59d0964f8a6f44a2b361", size = 212447, upload-time = "2026-04-25T11:08:15.79Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d2/462001d2903b4bee5a5689598a0a55e5e7cd1ac7f4247a5545cff10d3ebb/xxhash-3.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:322b2f0622230f526aeb1738149948a7ae357a9e2ceb1383c6fd1fdaecdafa16", size = 445660, upload-time = "2026-04-25T11:08:17.441Z" }, + { url = "https://files.pythonhosted.org/packages/23/09/2bd1ed7f8689b20e51727952cac8329d50c694dc32b2eba06ba5bc742b37/xxhash-3.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cc22070880cc57b830a65cde4e65fa884c6d9b28ae4803b5ee05911e7bafba", size = 194076, upload-time = "2026-04-25T11:08:19.134Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6e/692302cd0a5f4ac4e6289f37fa888dc2e1e07750b68fe3e4bfe939b8cea3/xxhash-3.7.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb5a888a968b2434abf9ecda357b5d43f10d7b5a6da6fdbbe036208473aff0e2", size = 284990, upload-time = "2026-04-25T11:08:20.618Z" }, + { url = "https://files.pythonhosted.org/packages/05/d9/e54b159b3d9df7999d2a7c676ce7b323d1b5588a64f8f51ed8172567bd87/xxhash-3.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a999771ff97bec27d18341be4f3a36b163bb1ac41ec17bef6d2dabd84acd33c7", size = 210590, upload-time = "2026-04-25T11:08:22.24Z" }, + { url = "https://files.pythonhosted.org/packages/50/93/0e0df1a3a196ced4ca71de76d65ead25d8e87bbfb87b64306ea47a40c00d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ed4a6efe2dee1655adb73e7ad40c6aa955a6892422b1e3b95de6a34de56e3cbb", size = 241442, upload-time = "2026-04-25T11:08:23.844Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a9/d917a7a814e90b218f8a0d37967105eea91bf752c3303683c99a1f7bfc1f/xxhash-3.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9fd17f14ac0faa12126c2f9ca774a8cf342957265ec3c8669c144e5e6cdb478c", size = 198356, upload-time = "2026-04-25T11:08:25.99Z" }, + { url = "https://files.pythonhosted.org/packages/89/5e/f2ba1877c39469abbefc72991d6ebdcbd4c0880db01ae8cb1f553b0c537d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:05fd1254268c59b5cb2a029dfc204275e9fc52de2913f1e53aa8d01442c96b4d", size = 210898, upload-time = "2026-04-25T11:08:27.608Z" }, + { url = "https://files.pythonhosted.org/packages/90/c6/be56b58e73de531f39a10de1355bb77ceb663900dc4bf2d6d3002a9c3f9e/xxhash-3.7.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a2eae53197c6276d5b317f75a1be226bbf440c20b58bf525f36b5d0e1f657ca6", size = 275519, upload-time = "2026-04-25T11:08:29.301Z" }, + { url = "https://files.pythonhosted.org/packages/92/e2/17ddc85d5765b9c709f192009ed8f5a1fc876f4eb35bba7c307b5b1169f9/xxhash-3.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:bfe6f92e3522dcbe8c4281efd74fa7542a336cb00b0e3272c4ec0edabeaeaf67", size = 414191, upload-time = "2026-04-25T11:08:31.16Z" }, + { url = "https://files.pythonhosted.org/packages/9c/42/85f5b79f4bf1ec7ba052491164adfd4f4e9519f5dc7246de4fbd64a1bd56/xxhash-3.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7ab9a49c410d8c6c786ab99e79c529938d894c01433130353dd0fe999111077a", size = 191604, upload-time = "2026-04-25T11:08:32.862Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d0/6127b623aa4cca18d8b7743592b048d689fd6c6e37ff26a22cddf6cd9d7f/xxhash-3.7.0-cp314-cp314-win32.whl", hash = "sha256:040ea63668f9185b92bc74942df09c7e65703deed71431333678fc6e739a9955", size = 31271, upload-time = "2026-04-25T11:08:34.651Z" }, + { url = "https://files.pythonhosted.org/packages/64/4f/44fc4788568004c43921701cbc127f48218a1eede2c9aea231115323564d/xxhash-3.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2a61e2a3fb23c892496d587b470dee7fa1b58b248a187719c65ea8e94ec13257", size = 32284, upload-time = "2026-04-25T11:08:35.987Z" }, + { url = "https://files.pythonhosted.org/packages/6d/77/18bb895eb60a49453d16e17d67990e5caff557c78eafc90ad4e2eabf4570/xxhash-3.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:c7741c7524961d8c0cb4d4c21b28957ff731a3fd5b5cd8b856dc80a40e9e5acc", size = 28701, upload-time = "2026-04-25T11:08:37.767Z" }, + { url = "https://files.pythonhosted.org/packages/45/a0/46f72244570c550fbbb7db1ef554183dd5ebe9136385f30e032b781ae8f6/xxhash-3.7.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:fc84bf7aa7592f31ec63a3e7b11d624f468a3f19f5238cec7282a42e838ab1d7", size = 33646, upload-time = "2026-04-25T11:08:39.109Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3a/453846a7eceea11e75def361eed01ec6a0205b9822c19927ed364ccae7cc/xxhash-3.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9f1563fdc8abfc389748e6932c7e4e99c89a53e4ec37d4563c24fc06f5e5644b", size = 31125, upload-time = "2026-04-25T11:08:40.467Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3e/49434aba738885d512f9e486db1bdd19db28dfa40372b56da26ef7a4e738/xxhash-3.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2d415f18becf6f153046ab6adc97da77e3643a0ee205dae61c4012604113a020", size = 196633, upload-time = "2026-04-25T11:08:41.943Z" }, + { url = "https://files.pythonhosted.org/packages/a4/e9/006cb6127baeb9f8abe6d15e62faa01349f09b34e2bfd65175b2422d026b/xxhash-3.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bb16aa13ed175bc9be5c2491ba031b85a9b51c4ed90e0b3d4ebe63cf3fb54f8e", size = 215899, upload-time = "2026-04-25T11:08:43.645Z" }, + { url = "https://files.pythonhosted.org/packages/27/e4/cc57d72e66df0ae29b914335f1c6dcf61e8f3746ddf0ae3c471aa4f15e00/xxhash-3.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f9fd595f1e5941b3d7863e4774e4b30caa6731fc34b9277da032295aa5656ee5", size = 238116, upload-time = "2026-04-25T11:08:45.698Z" }, + { url = "https://files.pythonhosted.org/packages/af/78/3531d4a3fd8a0038cc6be1f265a69c1b3587f557a10b677dd736de2202c1/xxhash-3.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1295325c5a98d552333fa53dc2b026b0ef0ec9c8e73ca3a952990b4c7d65d459", size = 215012, upload-time = "2026-04-25T11:08:47.355Z" }, + { url = "https://files.pythonhosted.org/packages/b4/f6/259fb1eaaec921f59b17203b0daee69829761226d3b980d5191d7723dd83/xxhash-3.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3573a651d146912da9daa9e29e5fbc45994420daaa9ef1e2fa5823e1dc485513", size = 448534, upload-time = "2026-04-25T11:08:49.149Z" }, + { url = "https://files.pythonhosted.org/packages/7b/16/a66d0eaf6a7e68532c07714361ddc904c663ec940f3b028c1ae4a21a7b9d/xxhash-3.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ec1e080a3d02d94ea9335bfab0e3374b877e25411422c18f51a943fa4b46381", size = 196217, upload-time = "2026-04-25T11:08:50.805Z" }, + { url = "https://files.pythonhosted.org/packages/8d/ef/d2efc7fc51756dc52509109d1a25cefc859d74bc4b19a167b12dbd8c2786/xxhash-3.7.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:84415265192072d8638a3afc3c1bc5995e310570cd9acb54dc46d3939e364fe0", size = 286906, upload-time = "2026-04-25T11:08:52.418Z" }, + { url = "https://files.pythonhosted.org/packages/fc/67/25decd1d4a4018582ec4db2a868a2b7e40640f4adb20dfeb19ac923aa825/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d4dea659b57443989ef32f4295104fd6912c73d0bf26d1d148bb88a9f159b02", size = 213057, upload-time = "2026-04-25T11:08:54.105Z" }, + { url = "https://files.pythonhosted.org/packages/0d/5d/17651eb29d06786cdc40c60ae3d27d645aa5d61d2eca6237a7ba0b94789b/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:05ece0fe4d9c9c2728912d1981ae1566cfc83a011571b24732cbf76e1fb70dca", size = 243886, upload-time = "2026-04-25T11:08:56.109Z" }, + { url = "https://files.pythonhosted.org/packages/8a/d4/174d9cf7502243d586e6a9ae842b1ae23026620995114f85f1380e588bc9/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fd880353cf1ffaf321bc18dd663e111976dbd0d3bbd8a66d58d2b470dfa7f396", size = 201015, upload-time = "2026-04-25T11:08:57.777Z" }, + { url = "https://files.pythonhosted.org/packages/91/8c/2254e2d06c3ac5e6fe22eaf3da791b87ea823ae9f2c17b4af66755c5752d/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4e15cc9e2817f6481160f930c62842b3ff419e20e13072bcbab12230943092bc", size = 213457, upload-time = "2026-04-25T11:08:59.826Z" }, + { url = "https://files.pythonhosted.org/packages/79/a2/e3daa762545921173e3360f3b4ff7fc63c2d27359f7230ec1a7a74e117f6/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:90b9d1a8bd37d768ffc92a1f651ec69afc532a96fa1ac2ea7abbed5d630b3237", size = 277738, upload-time = "2026-04-25T11:09:01.423Z" }, + { url = "https://files.pythonhosted.org/packages/e1/4c/e186da2c46b87f5204640e008d42730bf3c1ee9f0efb71ae1ebcdfeac681/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:157c49475b34ecea8809e51123d9769a534e139d1247942f7a4bc67710bb2533", size = 417127, upload-time = "2026-04-25T11:09:03.592Z" }, + { url = "https://files.pythonhosted.org/packages/17/28/3798e15007a3712d0da3d3fe70f8e11916569858b5cc371053bc26270832/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5a6ddec83325685e729ca119d1f5c518ec39294212ecd770e60693cdc5f7eb79", size = 193962, upload-time = "2026-04-25T11:09:06.228Z" }, + { url = "https://files.pythonhosted.org/packages/ad/95/a26baa93b5241fd7630998816a4ec47a5a0bad193b3f8fc8f3593e1a4a67/xxhash-3.7.0-cp314-cp314t-win32.whl", hash = "sha256:a04a6cab47e2166435aaf5b9e5ee41d1532cc8300efdef87f2a4d0acb7db19ed", size = 31643, upload-time = "2026-04-25T11:09:08.153Z" }, + { url = "https://files.pythonhosted.org/packages/44/36/5454f13c447e395f9b06a3e91274c59f503d31fad84e1836efe3bdb71f6a/xxhash-3.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8653dd7c2eda020545bb2c71c7f7039b53fe7434d0fc1a0a9deb79ab3f1a4fc1", size = 32522, upload-time = "2026-04-25T11:09:09.534Z" }, + { url = "https://files.pythonhosted.org/packages/74/35/698e7e3ff38e22992ea24870a511d8762474fb6783627a2910ff22a185c2/xxhash-3.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:468f0fc114faaa4b36699f8e328bbc3bb11dc418ba94ac52c26dd736d4b6c637", size = 28807, upload-time = "2026-04-25T11:09:11.234Z" }, ] [[package]] From 970c2540aa8d7576e0e3ecde2a26f4bba6bffe10 Mon Sep 17 00:00:00 2001 From: Dmytro Pykhtar <37850217+dimapihtar@users.noreply.github.com> Date: Mon, 27 Apr 2026 14:42:39 +0300 Subject: [PATCH 032/105] checkpoint integrity verification (#4305) Signed-off-by: dimapihtar --- .../core/dist_checkpointing/serialization.py | 24 +++ .../core/dist_checkpointing/validation.py | 155 +++++++++++++++++- megatron/training/checkpointing.py | 4 +- megatron/training/config/training_config.py | 7 + .../model_config.yaml | 1 + .../model_config.yaml | 1 + .../model_config.yaml | 1 + .../model_config.yaml | 1 + .../model_config.yaml | 1 + .../model_config.yaml | 1 + .../dist_checkpointing/test_integrity.py | 106 ++++++++++++ .../test_pipeline_parallel_layout.py | 1 + .../pipeline_parallel/test_pipeline_layout.py | 1 + tests/unit_tests/test_checkpointing.py | 1 + 14 files changed, 303 insertions(+), 2 deletions(-) create mode 100644 tests/unit_tests/dist_checkpointing/test_integrity.py diff --git a/megatron/core/dist_checkpointing/serialization.py b/megatron/core/dist_checkpointing/serialization.py index 2ee7970f143..82ee869576d 100644 --- a/megatron/core/dist_checkpointing/serialization.py +++ b/megatron/core/dist_checkpointing/serialization.py @@ -36,8 +36,10 @@ StrictHandling, determine_global_metadata, parse_strict_flag, + save_integrity_manifest, validate_integrity_and_strict_load, verify_checkpoint, + verify_integrity_manifest, ) logger = logging.getLogger(__name__) @@ -56,6 +58,7 @@ def load( common_strategy: None = None, validate_access_integrity: bool = True, strict: Union[str, StrictHandling] = StrictHandling.ASSUME_OK_UNEXPECTED, + verify_integrity: bool = False, ) -> Union[StateDict, Tuple[StateDict, Set[str], Set[str]]]: """Loading entrypoint. @@ -89,6 +92,10 @@ def load( incur any performance overhead. Other recommended values are: `False` (StrictHandling.LOG_UNEXPECTED) which logs only unexpected keys or `StrictHandling.RETURN_ALL` which returns all mismatch keys. + verify_integrity (bool, optional): if True, re-hashes every checkpoint file + and compares against the SHA-256 manifest. Raises `CheckpointingException` on any + mismatch. Requires that the checkpoint was previously saved with + `verify_integrity=True`. Returns: StateDict or Tuple[StateDict, Set[str], Set[str]]: in most cases only @@ -97,6 +104,8 @@ def load( assert common_strategy is None verify_checkpoint(checkpoint_dir) + if verify_integrity: + verify_integrity_manifest(checkpoint_dir) if sharded_strategy is None: sharded_strategy = TorchDistLoadShardedStrategy() @@ -295,6 +304,7 @@ def save( ] = None, content_metadata: Optional[dict] = None, async_strategy: Optional[str] = "nvrx", + verify_integrity: bool = False, ) -> Optional[AsyncRequest]: """Saving entrypoint. @@ -340,6 +350,11 @@ def save( modify the original state dict content_metadata (dict, optional): metadata to identify the checkpoint content. Useful for framework specific versioning. + verify_integrity (bool, optional): if True, compute SHA-256 hashes for every + file in the checkpoint directory after all data has been written. This manifest can + later be verified on load with `load(..., verify_integrity=True)`. + Adds I/O overhead proportional to the total checkpoint size (one extra + read pass over all files on rank 0). Returns: AsyncRequest (optional): if `async_sharded_save` is True, returns @@ -386,13 +401,22 @@ def metadata_finalize_fn(): ) torch.distributed.barrier() + def integrity_finalize_fn(): + if torch.distributed.get_rank() == 0: + save_integrity_manifest(checkpoint_dir) + torch.distributed.barrier() + if not async_sharded_save: sharded_strategy.save(sharded_state_dict, checkpoint_dir) metadata_finalize_fn() + if verify_integrity: + integrity_finalize_fn() return None async_request = sharded_strategy.async_save(sharded_state_dict, checkpoint_dir, async_strategy) async_request.finalize_fns.append(metadata_finalize_fn) + if verify_integrity: + async_request.finalize_fns.append(integrity_finalize_fn) return async_request diff --git a/megatron/core/dist_checkpointing/validation.py b/megatron/core/dist_checkpointing/validation.py index 89ecba1a968..5c9e6ba3caa 100644 --- a/megatron/core/dist_checkpointing/validation.py +++ b/megatron/core/dist_checkpointing/validation.py @@ -1,10 +1,13 @@ # Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +import hashlib +import json import logging +import os from collections import Counter, defaultdict from enum import Enum from pathlib import Path -from typing import TYPE_CHECKING, List, Optional, Set, Tuple, Union +from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Union import numpy as np import torch @@ -22,6 +25,7 @@ ShardedStateDict, is_main_replica, ) +from megatron.core.msc_utils import MultiStorageClientFeature if TYPE_CHECKING: from megatron.core.dist_checkpointing.serialization import CkptShardedMetadata @@ -34,6 +38,10 @@ # list of lists of global saved/loaded ShardedBase objects (each element corresponds to global rank) _GlobalMetadata = List[_LocalMetadata] +INTEGRITY_FNAME = 'integrity.json' +_HASH_ALGORITHM = 'sha256' +_READ_CHUNK_SIZE = 1 << 20 # 1 MiB + class StrictHandling(Enum): """Determines handling of load mismatch (non-empty "unexpected" or "missing" keys). @@ -483,3 +491,148 @@ def determine_global_metadata( global_metadata = [None] * torch.distributed.get_world_size() torch.distributed.all_gather_object(global_metadata, local_metadata) return local_metadata, global_metadata # type: ignore[return-value] + + +def _compute_file_hash(file_path: str) -> str: + """Return the SHA-256 hex digest of `file_path`, read in streaming chunks. + Args: + file_path: absolute path to the file to hash. + Returns: + Lowercase hex-encoded SHA-256 digest string. + """ + h = hashlib.sha256() + if MultiStorageClientFeature.is_enabled(): + msc = MultiStorageClientFeature.import_package() + with msc.open(file_path, 'rb') as f: + for chunk in iter(lambda: f.read(_READ_CHUNK_SIZE), b''): + h.update(chunk) + else: + with open(file_path, 'rb') as f: + for chunk in iter(lambda: f.read(_READ_CHUNK_SIZE), b''): + h.update(chunk) + return h.hexdigest() + + +def save_integrity_manifest(checkpoint_dir: str) -> None: + """Hash every file in `heckpoint_dir` and write an integrity manifest. + The manifest lists each filename (relative to `checkpoint_dir`) + together with its SHA-256 digest. The manifest file itself is excluded + from the listing. + Args: + checkpoint_dir: directory that contains the checkpoint files. + """ + manifest: Dict[str, str] = {} + + if MultiStorageClientFeature.is_enabled(): + msc = MultiStorageClientFeature.import_package() + ckpt_path = msc.Path(checkpoint_dir) + for entry in sorted(ckpt_path.iterdir(), key=lambda p: str(p)): + if entry.name != INTEGRITY_FNAME: + manifest[entry.name] = _compute_file_hash(str(entry)) + else: + ckpt_path = Path(checkpoint_dir) + for entry in sorted(ckpt_path.iterdir()): + if entry.is_file() and entry.name != INTEGRITY_FNAME: + manifest[entry.name] = _compute_file_hash(str(entry)) + + integrity_path = os.path.join(checkpoint_dir, INTEGRITY_FNAME) + payload = {'algorithm': _HASH_ALGORITHM, 'files': manifest} + + if MultiStorageClientFeature.is_enabled(): + msc = MultiStorageClientFeature.import_package() + with msc.open(integrity_path, 'w') as f: + json.dump(payload, f, indent=2) + else: + with open(integrity_path, 'w') as f: + json.dump(payload, f, indent=2) + + logger.info("Saved integrity manifest with %d file(s) to %s", len(manifest), integrity_path) + + +def _verify_integrity_manifest_impl(checkpoint_dir: str) -> None: + """Single-process implementation of integrity verification. + Reads ``integrity.json``, recomputes each file's hash, and raises + `megatron.core.dist_checkpointing.core.CheckpointingException` + on any mismatch or missing file. + Args: + checkpoint_dir: checkpoint directory to verify. + Raises: + CheckpointingException: if the manifest is absent, uses an unsupported + algorithm, or any file's hash does not match. + """ + integrity_path = os.path.join(checkpoint_dir, INTEGRITY_FNAME) + + if MultiStorageClientFeature.is_enabled(): + msc = MultiStorageClientFeature.import_package() + if not msc.os.path.exists(integrity_path): + raise CheckpointingException( + f'Integrity manifest not found at {integrity_path}. ' + 'The checkpoint must be saved with integrity verification enabled ' + '(save_integrity=True) before it can be verified on load.' + ) + with msc.open(integrity_path) as f: + manifest_data = json.load(f) + else: + if not os.path.exists(integrity_path): + raise CheckpointingException( + f'Integrity manifest not found at {integrity_path}. ' + 'The checkpoint must be saved with integrity verification enabled ' + '(save_integrity=True) before it can be verified on load.' + ) + with open(integrity_path) as f: + manifest_data = json.load(f) + + algorithm = manifest_data.get('algorithm', _HASH_ALGORITHM) + if algorithm != _HASH_ALGORITHM: + raise CheckpointingException( + f'Unsupported hash algorithm in integrity manifest: {algorithm!r}. ' + f'Expected: {_HASH_ALGORITHM!r}.' + ) + + manifest: Dict[str, str] = manifest_data['files'] + mismatches = [] + + for filename, expected_hash in manifest.items(): + full_path = os.path.join(checkpoint_dir, filename) + try: + actual_hash = _compute_file_hash(full_path) + except (FileNotFoundError, OSError) as exc: + mismatches.append(f' {filename}: file missing or unreadable ({exc})') + continue + if actual_hash != expected_hash: + mismatches.append( + f' {filename}: hash mismatch ' + f'(expected {expected_hash[:16]}..., got {actual_hash[:16]}...)' + ) + + if mismatches: + raise CheckpointingException( + f'Checkpoint integrity verification failed for {len(mismatches)} ' + f'file(s) in {checkpoint_dir}:\n' + '\n'.join(mismatches) + ) + + logger.info("Checkpoint integrity verified: %d file(s) OK in %s", len(manifest), checkpoint_dir) + + +def verify_integrity_manifest(checkpoint_dir: str) -> None: + """Verify checkpoint files against their recorded SHA-256 hashes. + Args: + checkpoint_dir: checkpoint directory to verify. + Raises: + CheckpointingException: if ``integrity.json`` is absent or any file's + hash no longer matches the stored value. + """ + import torch + + if torch.distributed.is_initialized() and torch.distributed.get_world_size() > 1: + error_payload = [None] + if torch.distributed.get_rank() == 0: + try: + _verify_integrity_manifest_impl(checkpoint_dir) + except CheckpointingException as exc: + error_payload = [str(exc)] + torch.distributed.broadcast_object_list(error_payload, src=0) + if error_payload[0] is not None: + raise CheckpointingException(error_payload[0]) + else: + _verify_integrity_manifest_impl(checkpoint_dir) diff --git a/megatron/training/checkpointing.py b/megatron/training/checkpointing.py index 04a93e9f376..b4b91acd8b8 100644 --- a/megatron/training/checkpointing.py +++ b/megatron/training/checkpointing.py @@ -678,7 +678,8 @@ def save_checkpoint(iteration, model, optimizer, opt_param_scheduler, num_floati validate_access_integrity=validate_sharding_integrity, preprocess_common_before_consistancy_check=preprocess_common_state_dict_fn, content_metadata=_clean_metadata_for_serialization(sharded_sd_metadata), - async_strategy=args.async_strategy) + async_strategy=args.async_strategy, + verify_integrity=args.verify_integrity) # [ModelOpt]: save sharded modelopt_state if has_nvidia_modelopt: save_sharded_modelopt_state(model, checkpoint_name, (args.ckpt_format, 1)) @@ -1249,6 +1250,7 @@ def _load_global_dist_base_checkpoint( load_strategy, validate_access_integrity=args.ckpt_load_validate_sharding_integrity, strict=args.dist_ckpt_strictness, + verify_integrity=args.verify_integrity, ) return state_dict, checkpoint_name, release, CheckpointType.GLOBAL diff --git a/megatron/training/config/training_config.py b/megatron/training/config/training_config.py index b44a6472872..692eeb4a8d0 100644 --- a/megatron/training/config/training_config.py +++ b/megatron/training/config/training_config.py @@ -579,6 +579,9 @@ class CheckpointConfig: replication_factor: int = 2 """Number of machines storing the replica of a given rank's data.""" + verify_integrity: bool = False + """Whether to hash checkpointing files during save and validate their integrity during load.""" + def __post_init__(self): from megatron.training.utils import has_nvrx_installed @@ -590,3 +593,7 @@ def __post_init__(self): "nvidia-resiliency-ext is not installed. " "Please, install nvidia-resiliency-ext to enable async save." ) + + if self.verify_integrity: + assert self.ckpt_format == "torch_dist", \ + f"`verify_integrity` is only supported with torch_dist checkpoint format." diff --git a/tests/functional_tests/test_cases/gpt/gpt3_mcore_te_tp2_pp2_resume_torch_dist/model_config.yaml b/tests/functional_tests/test_cases/gpt/gpt3_mcore_te_tp2_pp2_resume_torch_dist/model_config.yaml index 9436fa2a5e6..14e40a430d5 100644 --- a/tests/functional_tests/test_cases/gpt/gpt3_mcore_te_tp2_pp2_resume_torch_dist/model_config.yaml +++ b/tests/functional_tests/test_cases/gpt/gpt3_mcore_te_tp2_pp2_resume_torch_dist/model_config.yaml @@ -51,4 +51,5 @@ MODEL_ARGS: --log-memory-to-tensorboard: true --async-save: true --use-persistent-ckpt-worker: true + --verify-integrity: true TEST_TYPE: ckpt-resume diff --git a/tests/functional_tests/test_cases/gpt/gpt3_mcore_te_tp2_pp2_resume_torch_dist_cp2_nondeterministic/model_config.yaml b/tests/functional_tests/test_cases/gpt/gpt3_mcore_te_tp2_pp2_resume_torch_dist_cp2_nondeterministic/model_config.yaml index 2fe3d590c1b..29a9bbef0c1 100644 --- a/tests/functional_tests/test_cases/gpt/gpt3_mcore_te_tp2_pp2_resume_torch_dist_cp2_nondeterministic/model_config.yaml +++ b/tests/functional_tests/test_cases/gpt/gpt3_mcore_te_tp2_pp2_resume_torch_dist_cp2_nondeterministic/model_config.yaml @@ -53,4 +53,5 @@ MODEL_ARGS: --log-memory-to-tensorboard: true --async-save: true --use-persistent-ckpt-worker: true + --verify-integrity: true TEST_TYPE: ckpt-resume diff --git a/tests/functional_tests/test_cases/gpt/gpt3_mcore_te_tp2_pp2_resume_torch_dist_reshard_1x4xNone/model_config.yaml b/tests/functional_tests/test_cases/gpt/gpt3_mcore_te_tp2_pp2_resume_torch_dist_reshard_1x4xNone/model_config.yaml index a413b7ebb0c..ec29ea58ca6 100644 --- a/tests/functional_tests/test_cases/gpt/gpt3_mcore_te_tp2_pp2_resume_torch_dist_reshard_1x4xNone/model_config.yaml +++ b/tests/functional_tests/test_cases/gpt/gpt3_mcore_te_tp2_pp2_resume_torch_dist_reshard_1x4xNone/model_config.yaml @@ -49,4 +49,5 @@ MODEL_ARGS: --log-memory-to-tensorboard: true --async-save: true --use-persistent-ckpt-worker: true + --verify-integrity: true TEST_TYPE: ckpt-resume diff --git a/tests/functional_tests/test_cases/gpt/gpt3_mcore_tp1_pp1_resume_torch_dist_dist_optimizer_overlap_grad_reduce_param_gather/model_config.yaml b/tests/functional_tests/test_cases/gpt/gpt3_mcore_tp1_pp1_resume_torch_dist_dist_optimizer_overlap_grad_reduce_param_gather/model_config.yaml index 823312a21df..6588160cc67 100644 --- a/tests/functional_tests/test_cases/gpt/gpt3_mcore_tp1_pp1_resume_torch_dist_dist_optimizer_overlap_grad_reduce_param_gather/model_config.yaml +++ b/tests/functional_tests/test_cases/gpt/gpt3_mcore_tp1_pp1_resume_torch_dist_dist_optimizer_overlap_grad_reduce_param_gather/model_config.yaml @@ -55,4 +55,5 @@ MODEL_ARGS: --log-memory-to-tensorboard: true --async-save: true --use-persistent-ckpt-worker: true + --verify-integrity: true TEST_TYPE: ckpt-resume diff --git a/tests/functional_tests/test_cases/moe/gpt3_mcore_te_tp1_pp2_resume_torch_dist_reshard_2x1x4_te_8experts2parallel_dist_optimizer/model_config.yaml b/tests/functional_tests/test_cases/moe/gpt3_mcore_te_tp1_pp2_resume_torch_dist_reshard_2x1x4_te_8experts2parallel_dist_optimizer/model_config.yaml index e885bc255aa..1c0875aa0d0 100644 --- a/tests/functional_tests/test_cases/moe/gpt3_mcore_te_tp1_pp2_resume_torch_dist_reshard_2x1x4_te_8experts2parallel_dist_optimizer/model_config.yaml +++ b/tests/functional_tests/test_cases/moe/gpt3_mcore_te_tp1_pp2_resume_torch_dist_reshard_2x1x4_te_8experts2parallel_dist_optimizer/model_config.yaml @@ -56,5 +56,6 @@ MODEL_ARGS: --disable-bias-linear: true --no-bias-gelu-fusion: true --log-memory-to-tensorboard: true + --verify-integrity: true TEST_TYPE: ckpt-resume LAUNCHER: ft_launcher diff --git a/tests/functional_tests/test_cases/moe/gpt3_mcore_te_tp2_pp1_resume_torch_dist_te_8experts2parallel_dist_optimizer/model_config.yaml b/tests/functional_tests/test_cases/moe/gpt3_mcore_te_tp2_pp1_resume_torch_dist_te_8experts2parallel_dist_optimizer/model_config.yaml index 764c576645e..2a8a2a5d72b 100644 --- a/tests/functional_tests/test_cases/moe/gpt3_mcore_te_tp2_pp1_resume_torch_dist_te_8experts2parallel_dist_optimizer/model_config.yaml +++ b/tests/functional_tests/test_cases/moe/gpt3_mcore_te_tp2_pp1_resume_torch_dist_te_8experts2parallel_dist_optimizer/model_config.yaml @@ -62,4 +62,5 @@ MODEL_ARGS: --log-memory-to-tensorboard: true --async-save: true --use-persistent-ckpt-worker: true + --verify-integrity: true TEST_TYPE: ckpt-resume diff --git a/tests/unit_tests/dist_checkpointing/test_integrity.py b/tests/unit_tests/dist_checkpointing/test_integrity.py new file mode 100644 index 00000000000..e87af62af93 --- /dev/null +++ b/tests/unit_tests/dist_checkpointing/test_integrity.py @@ -0,0 +1,106 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. + +import json +import os +from pathlib import Path + +import pytest +import torch + +from megatron.core.dist_checkpointing import ShardedTensor, load, save +from megatron.core.dist_checkpointing.core import CheckpointingException +from megatron.core.dist_checkpointing.validation import ( + save_integrity_manifest, + verify_integrity_manifest, +) +from tests.unit_tests.dist_checkpointing import TempNamedDir +from tests.unit_tests.test_utilities import Utils + + +@pytest.fixture +def init_model_parallel(): + """Init torch distributed.""" + Utils.initialize_model_parallel(1, 1) + yield # Run the actual test. + Utils.destroy_model_parallel() + + +class TestIntegrity: + def test_save_verify_integrity_manifest_with_ckpt(self, tmp_path_dist_ckpt): + Utils.initialize_model_parallel(1, 1) + state_dict = { + 'sd_keyA': ShardedTensor.from_rank_offsets( + 'keyA', torch.ones(1, 1), replica_id=Utils.rank + ), + 'rank': 0, + } + load_state_dict = { + 'sd_keyA': ShardedTensor.from_rank_offsets( + 'keyA', torch.empty(1, 1), replica_id=Utils.rank + ) + } + + with TempNamedDir( + tmp_path_dist_ckpt / 'test_save_integrity_manifest', sync=True + ) as ckpt_dir: + save(state_dict, ckpt_dir, verify_integrity=True) + + integrity_file = Path(ckpt_dir / "integrity.json") + assert integrity_file.is_file(), "integrity.json doesn't exist." + + with open(integrity_file, "r") as f: + data = json.load(f) + files = list(data["files"].keys()) + + assert "__0_0.distcp" in files + assert len(data["files"]["common.pt"]) == 64 + + loaded_state_dict = load(load_state_dict, ckpt_dir, verify_integrity=True) + + Utils.destroy_model_parallel() + + def test_save_verify_integrity_manifest_directly(self, init_model_parallel, tmp_path_dist_ckpt): + with TempNamedDir( + tmp_path_dist_ckpt / 'test_save_integrity_manifest_directly', sync=True + ) as ckpt_dir: + metadata_file = Path(ckpt_dir / "metadata.json") + with open(metadata_file, "w") as f: + data = {"test_metadata": 1} + json.dump(data, f) + + if torch.distributed.get_rank() == 0: + save_integrity_manifest(ckpt_dir) + torch.distributed.barrier() + integrity_file = Path(ckpt_dir / "integrity.json") + assert integrity_file.is_file(), "integrity.json doesn't exist." + + with open(integrity_file, "r") as f: + data = json.load(f) + files = list(data["files"].keys()) + + assert len(files) == 1 + assert len(data["files"]["metadata.json"]) == 64 + + verify_integrity_manifest(ckpt_dir) + + def test_save_verify_integrity_manifest_error(self, init_model_parallel, tmp_path_dist_ckpt): + with TempNamedDir( + tmp_path_dist_ckpt / 'test_save_integrity_manifest_error', sync=True + ) as ckpt_dir: + metadata_file = Path(ckpt_dir / "metadata.json") + + with open(metadata_file, "w") as f: + data = {"test_metadata": 1} + json.dump(data, f) + + if torch.distributed.get_rank() == 0: + save_integrity_manifest(ckpt_dir) + torch.distributed.barrier() + + with open(metadata_file, "w") as f: + data = {"test_metadata": 11} + json.dump(data, f) + + # CheckpointingException, hash mismatch + with pytest.raises(CheckpointingException): + verify_integrity_manifest(ckpt_dir) diff --git a/tests/unit_tests/dist_checkpointing/test_pipeline_parallel_layout.py b/tests/unit_tests/dist_checkpointing/test_pipeline_parallel_layout.py index f8aace0105d..3e493539b1b 100644 --- a/tests/unit_tests/dist_checkpointing/test_pipeline_parallel_layout.py +++ b/tests/unit_tests/dist_checkpointing/test_pipeline_parallel_layout.py @@ -153,6 +153,7 @@ def create_args(): args.dist_ckpt_optim_fully_reshardable = False args.distrib_optim_fully_reshardable_mem_efficient = False args.phase_transition_iterations = None + args.verify_integrity = False yield args diff --git a/tests/unit_tests/pipeline_parallel/test_pipeline_layout.py b/tests/unit_tests/pipeline_parallel/test_pipeline_layout.py index f871938b218..9441996ec49 100644 --- a/tests/unit_tests/pipeline_parallel/test_pipeline_layout.py +++ b/tests/unit_tests/pipeline_parallel/test_pipeline_layout.py @@ -149,6 +149,7 @@ def create_args(): args.distrib_optim_fully_reshardable_mem_efficient = False args.phase_transition_iterations = None args.async_strategy = "nvrx" + args.verify_integrity = False yield args diff --git a/tests/unit_tests/test_checkpointing.py b/tests/unit_tests/test_checkpointing.py index 61cda3d91d2..c778d58fb7f 100644 --- a/tests/unit_tests/test_checkpointing.py +++ b/tests/unit_tests/test_checkpointing.py @@ -118,6 +118,7 @@ def create_args(): args.ckpt_step = None args.swiglu = True args.num_experts = 1 + args.verify_integrity = False yield args From ebd70d3125bf12bcdff0ad3c0073a03a8c0dc917 Mon Sep 17 00:00:00 2001 From: wdykas <73254672+wdykas@users.noreply.github.com> Date: Mon, 27 Apr 2026 16:24:59 -0400 Subject: [PATCH 033/105] Fix cache gating (#4455) Co-authored-by: Siddharth Singh --- megatron/core/ssm/mamba_mixer.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/megatron/core/ssm/mamba_mixer.py b/megatron/core/ssm/mamba_mixer.py index 69c6d89c286..727c6ef5fd6 100644 --- a/megatron/core/ssm/mamba_mixer.py +++ b/megatron/core/ssm/mamba_mixer.py @@ -1035,7 +1035,7 @@ def _get_decode_A_neg_exp(self) -> torch.Tensor: base = -torch.exp(self.A_log.float()) return base.view(-1, 1, 1).expand(-1, self.headdim, self.d_state) # Inference path. Refill when stale - if torch.cuda.is_current_stream_capturing() or self._A_neg_exp_cache_stale: + if self._A_neg_exp_cache_stale: with torch.no_grad(): self._A_neg_exp_cache.copy_(-torch.exp(self.A_log.float())) self._A_neg_exp_cache_stale = False @@ -1043,7 +1043,10 @@ def _get_decode_A_neg_exp(self) -> torch.Tensor: def train(self, mode: bool = True): """Mark the decode cache stale; weights may have updated.""" - self._A_neg_exp_cache_stale = True + if mode: + # only mark stale when switching to training mode. + # otherwise retain the staleness state. + self._A_neg_exp_cache_stale = True return super().train(mode) def _ssm_decode( From 0447347ce897766d37ffe7673309b7209aac1228 Mon Sep 17 00:00:00 2001 From: Cory Ye <44509866+cspades@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:12:01 -0700 Subject: [PATCH 034/105] [Main] Fix FusedAdam.use_decoupled_grad mis-set for Megatron-FSDP. (#4427) Signed-off-by: Cory Ye --- megatron/core/optimizer/__init__.py | 2 ++ .../test_mcore_fully_sharded_data_parallel.py | 24 +++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/megatron/core/optimizer/__init__.py b/megatron/core/optimizer/__init__.py index 4f69a9efd55..c6d3e41aed5 100644 --- a/megatron/core/optimizer/__init__.py +++ b/megatron/core/optimizer/__init__.py @@ -975,6 +975,8 @@ def get_megatron_optimizer( # applied to the Megatron-FSDP main weight and extended to FusedAdam # main weights. Override this here. setattr(optimizer_part.optimizer, "master_weights", False) + # Megatron-FSDP always uses a decoupled gradient when using FusedAdam. + setattr(optimizer_part.optimizer, "use_decoupled_grad", True) optimizers.append(optimizer_part) model_chunk_offset += 1 diff --git a/tests/unit_tests/distributed/megatron_fsdp/test_mcore_fully_sharded_data_parallel.py b/tests/unit_tests/distributed/megatron_fsdp/test_mcore_fully_sharded_data_parallel.py index 04f7c5c6482..500045871e7 100644 --- a/tests/unit_tests/distributed/megatron_fsdp/test_mcore_fully_sharded_data_parallel.py +++ b/tests/unit_tests/distributed/megatron_fsdp/test_mcore_fully_sharded_data_parallel.py @@ -736,12 +736,21 @@ def _training_loop(seed=42, **kwargs): train_iters=NUM_TRAINING_STEPS, **kwargs, ) - if kwargs.get("use_megatron_fsdp", False) and kwargs.get( + megatron_fsdp_te_fused_adam = kwargs.get("use_megatron_fsdp", False) and kwargs.get( "use_precision_aware_optimizer", False - ): + ) + if megatron_fsdp_te_fused_adam: assert ( not optim.optimizer.master_weights ), "Megatron-FSDP should not use FusedAdam master weights." + assert ( + optim.optimizer.use_decoupled_grad + ), "Megatron-FSDP should be using a decoupled gradient with FusedAdam." + assert model_chunks[ + 0 + ].module.param_and_grad_buffer.use_decoupled_grad, ( + "Megatron-FSDP is installing gradients into param.decoupled_grad." + ) # Prepare data iterator data_iterator = make_gpt_mock_data_iterator( @@ -764,6 +773,17 @@ def _training_loop(seed=42, **kwargs): micro_batch_size=MICRO_BATCH_SIZE, num_micro_batches=GLOBAL_BATCH_SIZE // MICRO_BATCH_SIZE // DP_GROUP.size(), ) + # Check that at least one non-null / non-zero gradient + # exists when using Megatron-FSDP. + if kwargs.get("use_megatron_fsdp", False): + grad_attr = "decoupled_grad" if megatron_fsdp_te_fused_adam else "grad" + assert any( + [ + getattr(p, grad_attr, None) is not None + and getattr(p, grad_attr, None)._local_tensor.any() + for p in model_chunks[0].parameters() + ] + ), f"[Megatron-FSDP] Missing gradient in Parameter.{grad_attr}..." optim.step() # Collect loss From 8c5cf05ece83f0fcd13f6b78d4efd55dd421f04a Mon Sep 17 00:00:00 2001 From: Tong Liu Date: Tue, 28 Apr 2026 08:47:12 +0800 Subject: [PATCH 035/105] add permute fusion into hybrid ep (#4089) Co-authored-by: root Co-authored-by: root Co-authored-by: Dennis(Zhenhuan) Liu --- megatron/core/transformer/moe/fused_a2a.py | 110 +++++++++++++++--- .../core/transformer/moe/token_dispatcher.py | 6 +- .../core/transformer/transformer_config.py | 19 ++- .../models/test_hybrid_moe_model.py | 5 +- .../transformer/moe/test_token_dispatcher.py | 44 ++++++- 5 files changed, 157 insertions(+), 27 deletions(-) diff --git a/megatron/core/transformer/moe/fused_a2a.py b/megatron/core/transformer/moe/fused_a2a.py index 39f50a4a670..586d70c400f 100644 --- a/megatron/core/transformer/moe/fused_a2a.py +++ b/megatron/core/transformer/moe/fused_a2a.py @@ -3,6 +3,8 @@ # Copyright (c) 2025 DeepSeek # Licensed under the MIT License - https://github.com/deepseek-ai/DeepEP/blob/main/LICENSE +from typing import Optional + from megatron.core.utils import internal_api try: @@ -280,9 +282,11 @@ def init_hybrid_ep_buffer( hidden_dim: int, seq_len: int, num_local_experts: int, - num_sms_dispatch_api: int, - num_sms_combine_api: int, - fp8_dispatch: bool, + num_sms_dispatch_api: Optional[int] = None, + num_sms_combine_api: Optional[int] = None, + num_blocks_permute: Optional[int] = None, + num_blocks_unpermute: Optional[int] = None, + fp8_dispatch: bool = False, ) -> None: ''' Initialize the HybridEP buffer, including buffer allocation and metadata @@ -301,23 +305,35 @@ def init_hybrid_ep_buffer( Maximum sequence length of the input tensor. num_local_experts (int): Number of local experts. - num_sms_dispatch_api (int): + num_sms_dispatch_api (Optional[int]): Number of SMs used by the dispatch API. - num_sms_combine_api (int): + num_sms_combine_api (Optional[int]): Number of SMs used by the combine API. + num_blocks_permute (Optional[int]): + Number of blocks used by the permute part. + num_blocks_unpermute (Optional[int]): + Number of blocks used by the unpermute part. fp8_dispatch (bool): Whether to use FP8 communication during the dispatch phase. ''' assert not fp8_dispatch, "HybridEP dispatcher does not support fp8 dispatch now" global _hybrid_ep_buffer + kwargs = {} + if num_sms_dispatch_api is not None: + kwargs['num_sms_dispatch_api'] = num_sms_dispatch_api + if num_sms_combine_api is not None: + kwargs['num_sms_combine_api'] = num_sms_combine_api + if num_blocks_permute is not None: + kwargs['num_blocks_permute'] = num_blocks_permute + if num_blocks_unpermute is not None: + kwargs['num_blocks_unpermute'] = num_blocks_unpermute _hybrid_ep_buffer = HybridEPBuffer( group=group, hidden_dim=hidden_dim, max_num_of_tokens_per_rank=seq_len, num_local_experts=num_local_experts, use_fp8=fp8_dispatch, - num_sms_dispatch_api=num_sms_dispatch_api, - num_sms_combine_api=num_sms_combine_api, + **kwargs, ) @@ -342,14 +358,34 @@ def forward( probs, group, num_local_experts, - num_sms_dispatch_api=24, - num_sms_combine_api=24, + num_sms_dispatch_api=None, + num_sms_combine_api=None, + num_blocks_permute=None, + num_blocks_unpermute=None, + fused=False, num_permuted_tokens=None, pad_multiple=None, ): ''' Forward pass of fused dispatch of the HybridEP backend ''' + if fused or num_blocks_permute is not None or num_blocks_unpermute is not None: + import inspect + import warnings + + sig = inspect.signature(HybridEPBuffer.dispatch_with_permute) + if 'fuse_permute_dispatch' not in sig.parameters: + warnings.warn( + "Current DeepEP version does not support fused permute dispatch or " + "num_blocks_permute/num_blocks_unpermute. Falling back to unfused " + "HybridEP dispatch.", + UserWarning, + stacklevel=2, + ) + fused = False + num_blocks_permute = None + num_blocks_unpermute = None + if _hybrid_ep_buffer is None: seq_len, hidden_dim = x.shape[-2:] fp8_dispatch = False # Currently, we do not support fp8 dispatch @@ -360,6 +396,8 @@ def forward( num_local_experts, num_sms_dispatch_api, num_sms_combine_api, + num_blocks_permute, + num_blocks_unpermute, fp8_dispatch, ) # If we provide the num_permuted_tokens, we do not need to use sync to @@ -381,10 +419,12 @@ def forward( pad_multiple=pad_multiple, num_permuted_tokens=num_permuted_tokens, non_blocking=non_blocking, + **({"fuse_permute_dispatch": fused} if fused else {}), ) ctx.handle = handle ctx.pad_multiple = pad_multiple + ctx.fused = fused return ( dispatched_hidden, dispatched_probs, @@ -400,9 +440,26 @@ def backward(ctx, grad_x, grad_probs, grad_scaling_factor, grad_tokens_per_exper ''' handle = ctx.handle combined_hidden, combined_probs = _hybrid_ep_buffer.combine_with_unpermute( - hidden=grad_x, probs=grad_probs, handle=handle, pad_multiple=ctx.pad_multiple + hidden=grad_x, + probs=grad_probs, + handle=handle, + pad_multiple=ctx.pad_multiple, + **({"fuse_unpermute_combine": ctx.fused} if ctx.fused else {}), + ) + return ( + combined_hidden, + None, + combined_probs, + None, + None, + None, + None, + None, + None, + None, + None, + None, ) - return combined_hidden, None, combined_probs, None, None, None, None, None, None, None @internal_api @@ -412,16 +469,20 @@ class HybridEPCombine(torch.autograd.Function): ''' @staticmethod - def forward(ctx, x, handle, num_permuted_tokens=None, pad_multiple=None): + def forward(ctx, x, handle, num_permuted_tokens=None, pad_multiple=None, fused=False): ''' Forward pass of fused combine of the HybridEP backend ''' combined_hidden, _ = _hybrid_ep_buffer.combine_with_unpermute( - hidden=x, handle=handle, pad_multiple=pad_multiple + hidden=x, + handle=handle, + pad_multiple=pad_multiple, + **({"fuse_unpermute_combine": fused} if fused else {}), ) ctx.handle = handle ctx.pad_multiple = pad_multiple ctx.num_permuted_tokens = num_permuted_tokens + ctx.fused = fused return combined_hidden @staticmethod @@ -436,6 +497,7 @@ def backward(ctx, grad_x): handle=handle, pad_multiple=ctx.pad_multiple, num_permuted_tokens=ctx.num_permuted_tokens, + **({"fuse_permute_dispatch": ctx.fused} if ctx.fused else {}), ) return dispatched_hidden, None, None, None, None @@ -449,8 +511,11 @@ def hybrid_ep_dispatch( probs, group, num_local_experts, - num_sms_dispatch_api=24, - num_sms_combine_api=24, + num_sms_dispatch_api=None, + num_sms_combine_api=None, + num_blocks_permute=None, + num_blocks_unpermute=None, + fused=False, num_permuted_tokens=None, pad_multiple=None, ): @@ -469,10 +534,14 @@ def hybrid_ep_dispatch( Process group used for communication. num_local_experts (int): Number of local experts. - num_sms_dispatch_api (int): + num_sms_dispatch_api (Optional[int]): Number of SMs used by the dispatch API. - num_sms_combine_api (int): + num_sms_combine_api (Optional[int]): Number of SMs used by the combine API. + num_blocks_permute (Optional[int]): + Number of blocks used by the permute part. + num_blocks_unpermute (Optional[int]): + Number of blocks used by the unpermute part. num_permuted_tokens (int): Number of tokens after permute. HybridEP uses this to allocate buffers. If not provided, HybridEP obtains the size from a GPU tensor, @@ -489,12 +558,15 @@ def hybrid_ep_dispatch( num_local_experts, num_sms_dispatch_api, num_sms_combine_api, + num_blocks_permute, + num_blocks_unpermute, + fused, num_permuted_tokens, pad_multiple, ) @internal_api - def hybrid_ep_combine(x, handle, num_permuted_tokens, pad_multiple): + def hybrid_ep_combine(x, handle, num_permuted_tokens, pad_multiple, fused=False): ''' Perform fused combine operation for unpermute + combine a2a + unpermute using the HybridEP backend @@ -511,7 +583,7 @@ def hybrid_ep_combine(x, handle, num_permuted_tokens, pad_multiple): The alignment multiple required for FP8 GEMM. If not provided, no padding is performed. ''' - return HybridEPCombine.apply(x, handle, num_permuted_tokens, pad_multiple) + return HybridEPCombine.apply(x, handle, num_permuted_tokens, pad_multiple, fused) else: hybrid_ep_dispatch = None diff --git a/megatron/core/transformer/moe/token_dispatcher.py b/megatron/core/transformer/moe/token_dispatcher.py index 15fc8b984b3..717e285a249 100644 --- a/megatron/core/transformer/moe/token_dispatcher.py +++ b/megatron/core/transformer/moe/token_dispatcher.py @@ -1073,8 +1073,11 @@ def dispatch( num_local_experts=self.num_local_experts, num_sms_dispatch_api=self.config.moe_hybridep_num_sms, num_sms_combine_api=self.config.moe_hybridep_num_sms, + num_blocks_permute=self.config.moe_hybridep_num_blocks_permute, + num_blocks_unpermute=self.config.moe_hybridep_num_blocks_unpermute, num_permuted_tokens=self.num_permuted_tokens, pad_multiple=self.pad_multiple, + fused=self.config.moe_permute_fusion_into_hybridep, ) ) @@ -1096,6 +1099,7 @@ def combine( handle=self.handle, num_permuted_tokens=self.num_permuted_tokens, pad_multiple=self.pad_multiple, + fused=self.config.moe_permute_fusion_into_hybridep, ) # Release the used handle/num_permuted_tokens which could change in each iteration. # For drop_and_pad mode, we don't need to reset the num_permuted_tokens and @@ -1385,8 +1389,8 @@ def __init__( self.num_local_experts = num_local_experts self.local_expert_indices = local_expert_indices - assert self.tp_size * self.ep_size > 1, "Flex token dispatcher requires TPxEP > 1" if self.config.moe_flex_dispatcher_backend == "deepep": + assert self.tp_size * self.ep_size > 1, "DeepEP dispatcher requires TPxEP > 1" self._comm_manager = _DeepepManager( group=self.tp_ep_group, num_local_experts=self.num_local_experts, diff --git a/megatron/core/transformer/transformer_config.py b/megatron/core/transformer/transformer_config.py index 40c1a745493..1393da06c84 100644 --- a/megatron/core/transformer/transformer_config.py +++ b/megatron/core/transformer/transformer_config.py @@ -759,6 +759,9 @@ class TransformerConfig(ModelParallelConfig): Options are "deepep" and "hybridep". Currently only "hybridep" backend supports the MNNVL case.""" + moe_permute_fusion_into_hybridep: bool = False + """Fuse token rearrangement ops during token dispatching for HybridEP.""" + moe_per_layer_logging: bool = False """Enable per-layer logging for MoE, currently supports auxiliary loss and z loss.""" @@ -803,9 +806,19 @@ class TransformerConfig(ModelParallelConfig): moe_deepep_num_sms: int = 20 """Number of SMs to use for DeepEP.""" - moe_hybridep_num_sms: int = 16 - """Number of SMs to use for HybridEP. In pure NVL scenarios, - 16 SMs can generally achieve good bandwidth.""" + moe_hybridep_num_sms: Optional[int] = None + """Number of SMs to use for HybridEP. None uses the default from DeepEP. + In pure NVL scenarios, 16 SMs can generally achieve good bandwidth.""" + + moe_hybridep_num_blocks_permute: Optional[int] = None + """Number of CUDA thread blocks for the permute part in HybridEP. + When permute_fusion_into_hybridep is True, this sets the number + of SMs for the permute part (only 1 block per SM).""" + + moe_hybridep_num_blocks_unpermute: Optional[int] = None + """Number of CUDA thread blocks for the unpermute part in HybridEP. + When permute_fusion_into_hybridep is True, this sets the number + of SMs for the unpermute part (only 1 block per SM).""" ################## # Context Parallel diff --git a/tests/unit_tests/models/test_hybrid_moe_model.py b/tests/unit_tests/models/test_hybrid_moe_model.py index 01a46efe083..f2c275458a8 100644 --- a/tests/unit_tests/models/test_hybrid_moe_model.py +++ b/tests/unit_tests/models/test_hybrid_moe_model.py @@ -161,7 +161,9 @@ "moe_ffn_hidden_size": 1856, "moe_flex_dispatcher_backend": "deepep", "moe_grouped_gemm": True, - "moe_hybridep_num_sms": 16, + "moe_hybridep_num_sms": None, + "moe_hybridep_num_blocks_permute": None, + "moe_hybridep_num_blocks_unpermute": None, "moe_input_jitter_eps": None, "moe_latent_size": None, "moe_layer_freq": 1, @@ -170,6 +172,7 @@ "moe_pad_experts_for_cuda_graph_inference": False, "moe_per_layer_logging": False, "moe_permute_fusion": False, + "moe_permute_fusion_into_hybridep": False, "moe_router_bias_update_rate": 0.001, "moe_router_dtype": "fp64", "moe_router_enable_expert_bias": True, diff --git a/tests/unit_tests/transformer/moe/test_token_dispatcher.py b/tests/unit_tests/transformer/moe/test_token_dispatcher.py index 6ff8fcdc6e5..aef63b6e00e 100644 --- a/tests/unit_tests/transformer/moe/test_token_dispatcher.py +++ b/tests/unit_tests/transformer/moe/test_token_dispatcher.py @@ -430,11 +430,24 @@ def teardown_method(self, method): @pytest.mark.parametrize("tp_size,ep_size", [(1, 8), (8, 1), (4, 2)]) @pytest.mark.parametrize("permute_fusion", permute_fusion_params) @pytest.mark.parametrize("moe_flex_dispatcher_backend", ["deepep", "hybridep"]) - def test_forward_backward(self, tp_size, ep_size, permute_fusion, moe_flex_dispatcher_backend): + @pytest.mark.parametrize("moe_permute_fusion_into_hybridep", [True, False]) + def test_forward_backward( + self, + tp_size, + ep_size, + permute_fusion, + moe_flex_dispatcher_backend, + moe_permute_fusion_into_hybridep, + ): if moe_flex_dispatcher_backend == "deepep" and not is_deep_ep_available(): pytest.skip("Deep EP is not available") if moe_flex_dispatcher_backend == "hybridep" and not is_hybrid_ep_available(): pytest.skip("Hybrid EP is not available") + if moe_permute_fusion_into_hybridep: + if permute_fusion or moe_flex_dispatcher_backend != "hybridep": + pytest.skip( + "moe_permute_fusion_into_hybridep skipped because permute_fusion or hybridep is not set" + ) if permute_fusion: config.ENABLE_EXPERIMENTAL = True container = MoEModelTestContainer( @@ -448,6 +461,7 @@ def test_forward_backward(self, tp_size, ep_size, permute_fusion, moe_flex_dispa moe_permute_fusion=permute_fusion, hidden_size=1024, moe_flex_dispatcher_backend=moe_flex_dispatcher_backend, + moe_permute_fusion_into_hybridep=moe_permute_fusion_into_hybridep, test_dtype=torch.bfloat16, ) container.dispatcher_dropless_test() @@ -460,13 +474,24 @@ def test_forward_backward(self, tp_size, ep_size, permute_fusion, moe_flex_dispa @pytest.mark.parametrize("tp_size,ep_size", [(1, 8), (8, 1), (4, 2)]) @pytest.mark.parametrize("permute_fusion", permute_fusion_params) @pytest.mark.parametrize("moe_flex_dispatcher_backend", ["deepep", "hybridep"]) + @pytest.mark.parametrize("moe_permute_fusion_into_hybridep", [True, False]) def test_capacity_forward_backward( - self, tp_size, ep_size, permute_fusion, moe_flex_dispatcher_backend + self, + tp_size, + ep_size, + permute_fusion, + moe_flex_dispatcher_backend, + moe_permute_fusion_into_hybridep, ): if moe_flex_dispatcher_backend == "deepep" and not is_deep_ep_available(): pytest.skip("Deep EP is not available") if moe_flex_dispatcher_backend == "hybridep" and not is_hybrid_ep_available(): pytest.skip("Hybrid EP is not available") + if moe_permute_fusion_into_hybridep: + if permute_fusion or moe_flex_dispatcher_backend != "hybridep": + pytest.skip( + "moe_permute_fusion_into_hybridep skipped because permute_fusion or hybridep is not set" + ) if permute_fusion: config.ENABLE_EXPERIMENTAL = True container = MoEModelTestContainer( @@ -483,6 +508,7 @@ def test_capacity_forward_backward( moe_permute_fusion=permute_fusion, hidden_size=1024, moe_flex_dispatcher_backend=moe_flex_dispatcher_backend, + moe_permute_fusion_into_hybridep=moe_permute_fusion_into_hybridep, test_dtype=torch.bfloat16, ) container.dispatcher_capacity_test() @@ -497,13 +523,24 @@ def test_capacity_forward_backward( @pytest.mark.parametrize("tp_size,ep_size", [(1, 8), (8, 1), (4, 2)]) @pytest.mark.parametrize("permute_fusion", [True]) @pytest.mark.parametrize("moe_flex_dispatcher_backend", ["deepep", "hybridep"]) + @pytest.mark.parametrize("moe_permute_fusion_into_hybridep", [True, False]) def test_router_padding_for_fp8_forward_backward( - self, tp_size, ep_size, permute_fusion, moe_flex_dispatcher_backend + self, + tp_size, + ep_size, + permute_fusion, + moe_flex_dispatcher_backend, + moe_permute_fusion_into_hybridep, ): if moe_flex_dispatcher_backend == "deepep" and not is_deep_ep_available(): pytest.skip("Deep EP is not available") if moe_flex_dispatcher_backend == "hybridep" and not is_hybrid_ep_available(): pytest.skip("Hybrid EP is not available") + if moe_permute_fusion_into_hybridep: + if permute_fusion or moe_flex_dispatcher_backend != "hybridep": + pytest.skip( + "moe_permute_fusion_into_hybridep skipped because permute_fusion or hybridep is not set" + ) if permute_fusion: config.ENABLE_EXPERIMENTAL = True container = MoEModelTestContainer( @@ -518,6 +555,7 @@ def test_router_padding_for_fp8_forward_backward( moe_permute_fusion=permute_fusion, hidden_size=1024, moe_flex_dispatcher_backend=moe_flex_dispatcher_backend, + moe_permute_fusion_into_hybridep=moe_permute_fusion_into_hybridep, test_dtype=torch.bfloat16, ) container.dispatcher_router_padding_for_fp8_test() From 42e396ef5a7da3ae2f3284184ff6f3e4c0146a17 Mon Sep 17 00:00:00 2001 From: Yashaswi Karnati <144376261+yashaswikarnati@users.noreply.github.com> Date: Mon, 27 Apr 2026 18:29:07 -0700 Subject: [PATCH 036/105] Add ColocatedBridgeCommunicator for heterogeneous TP/DP MIMO training (NMFW-17) (#4368) Co-authored-by: Claude Opus 4.7 (1M context) --- megatron/core/models/mimo/comm/__init__.py | 1 + .../mimo/comm/colocated_communicator.py | 325 +++++ .../core/models/mimo/config/base_configs.py | 23 +- megatron/core/models/mimo/config/role.py | 77 +- megatron/core/models/mimo/model/base.py | 58 +- .../models/test_mimo_1f1b_schedule.py | 217 ++- .../test_mimo_colocated_communicator.py | 543 ++++++++ .../models/test_mimo_colocated_correctness.py | 1183 +++++++++++++++++ tests/unit_tests/models/test_mimo_model.py | 23 +- 9 files changed, 2337 insertions(+), 113 deletions(-) create mode 100644 megatron/core/models/mimo/comm/__init__.py create mode 100644 megatron/core/models/mimo/comm/colocated_communicator.py create mode 100644 tests/unit_tests/models/test_mimo_colocated_communicator.py create mode 100644 tests/unit_tests/models/test_mimo_colocated_correctness.py diff --git a/megatron/core/models/mimo/comm/__init__.py b/megatron/core/models/mimo/comm/__init__.py new file mode 100644 index 00000000000..26496bfed70 --- /dev/null +++ b/megatron/core/models/mimo/comm/__init__.py @@ -0,0 +1 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. diff --git a/megatron/core/models/mimo/comm/colocated_communicator.py b/megatron/core/models/mimo/comm/colocated_communicator.py new file mode 100644 index 00000000000..4c43dcdf3cd --- /dev/null +++ b/megatron/core/models/mimo/comm/colocated_communicator.py @@ -0,0 +1,325 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. + +import logging +from dataclasses import dataclass +from enum import Enum +from typing import Dict, List, Optional, Tuple + +import torch +import torch.distributed as dist + +from megatron.core.hyper_comm_grid import HyperCommGrid + + +@dataclass +class SliceInfo: + """Batch dimension slice information for a rank's data partition.""" + + start: int + size: int + + +class BridgeDirection(str, Enum): + """Which side of the bridge scales up, if any. + + ``FAN_IN`` — src has more DP replicas than dest; forward all-gathers + src outputs along the batch dim, backward narrows the sibling dest + gradient down to this src rank's slot. + + ``FAN_OUT`` — dest has more DP replicas; forward narrows, backward + all-gathers across the sibling dest DP ranks (the adjoint of narrow + is not zero-pad-and-scatter because every dest rank consumes a + different slice of the same src activation). + + ``EQUAL`` — matching DP; the bridge is a pure passthrough. + """ + + FAN_IN = "fan_in" + FAN_OUT = "fan_out" + EQUAL = "equal" + + +class ColocatedBridgeCommunicator: + """Bridges tensors between colocated modules with different TP/DP layouts. + + Default ``dim_mapping`` assumes 3D ``(b, s, h)``. Callers bridging + ``MimoModel``'s pre-flattened ``(s*b, h)`` encoder output should pass + ``dim_mapping={'b': 0, 'h': 1}``; this relies on a uniform token count per + sample so dim 0 divides evenly by the DP scale. + + Precondition: the input must be TP-replicated across the src TP group — + i.e. all TP ranks inside a src DP replica hold the same tensor on the + batch dim. The bridge never gathers along TP; violating this silently + produces wrong results. + """ + + def __init__( + self, + src_grid: HyperCommGrid, + dest_grid: HyperCommGrid, + src_module_name: str = "src", + dest_module_name: str = "dest", + dim_mapping: Optional[Dict[str, int]] = None, + ): + self.src_grid = src_grid + self.dest_grid = dest_grid + self.src_module_name = src_module_name + self.dest_module_name = dest_module_name + self.dim_mapping = dim_mapping or {'b': 0, 's': 1, 'h': 2} + self.current_rank = dist.get_rank() + + self._validate_grids() + self._extract_parallelism_info() + self._build_rank_mappings() + + # At most one direction is active; fan-in and fan-out are mutually + # exclusive (one of ``src_dp / dest_dp`` is >1, the other is 1). + # Equal DP uses no collective at all. Unify behind a single + # ``gather_pg`` + ``direction`` + ``scale`` rather than a fan-in + # and fan-out pair of attributes. + self.gather_pg: Optional[dist.ProcessGroup] = None + self.gather_group_ranks: List[List[int]] = [] + + if self.src_dp_size > self.dest_dp_size: + self.direction = BridgeDirection.FAN_IN + self.scale = self.src_dp_size // self.dest_dp_size + self.gather_group_ranks = self._build_gather_groups( + iter_size=self.dest_dp_size, + sibling_tp_size=self.src_tp_size, + scale=self.scale, + rank_to_pos=self.rank_to_src_pos, + ) + self.gather_pg, _ = dist.new_subgroups_by_enumeration( + self.gather_group_ranks, backend='nccl' + ) + elif self.dest_dp_size > self.src_dp_size: + self.direction = BridgeDirection.FAN_OUT + self.scale = self.dest_dp_size // self.src_dp_size + self.gather_group_ranks = self._build_gather_groups( + iter_size=self.src_dp_size, + sibling_tp_size=self.dest_tp_size, + scale=self.scale, + rank_to_pos=self.rank_to_dest_pos, + ) + self.gather_pg, _ = dist.new_subgroups_by_enumeration( + self.gather_group_ranks, backend='nccl' + ) + else: + self.direction = BridgeDirection.EQUAL + self.scale = 1 + + logging.info( + f"[Rank {self.current_rank}] ColocatedBridgeCommunicator: " + f"{src_module_name}({self.src_tp_size}TP/{self.src_dp_size}DP) -> " + f"{dest_module_name}({self.dest_tp_size}TP/{self.dest_dp_size}DP), " + f"direction={self.direction.value}, scale={self.scale}" + ) + + def _validate_grids(self): + if self.src_grid.size != self.dest_grid.size: + raise ValueError( + f"Grids must span same number of ranks: " + f"src={self.src_grid.size}, dest={self.dest_grid.size}" + ) + + if self.src_grid.rank_offset != self.dest_grid.rank_offset: + raise ValueError( + f"Grids must have same rank offset: " + f"src={self.src_grid.rank_offset}, dest={self.dest_grid.rank_offset}" + ) + + # Per-grid dim checks: tp/dp required; pp and cp (if present) must be 1. + # CP>1 also corrupts dp_idx when iterating get_rank_enum(['tp']) groups. + for name, grid in [("src", self.src_grid), ("dest", self.dest_grid)]: + for required in ('tp', 'dp'): + if required not in grid.dim_names: + raise ValueError( + f"{name} grid must have '{required}' dimension, " + f"got dim_names={grid.dim_names}" + ) + for singleton in ('pp', 'cp'): + if singleton in grid.dim_names: + size = grid.shape[grid.dim_names.index(singleton)] + if size != 1: + raise ValueError( + f"{name} {singleton.upper()} must be 1 for " + f"ColocatedBridgeCommunicator, got {size}" + ) + + src_dp = self.src_grid.shape[self.src_grid.dim_names.index('dp')] + dest_dp = self.dest_grid.shape[self.dest_grid.dim_names.index('dp')] + if src_dp % dest_dp != 0 and dest_dp % src_dp != 0: + raise ValueError( + f"DP sizes must be evenly divisible: src_dp={src_dp}, dest_dp={dest_dp}" + ) + + def _extract_parallelism_info(self): + self.src_tp_size = self.src_grid.shape[self.src_grid.dim_names.index('tp')] + self.src_dp_size = self.src_grid.shape[self.src_grid.dim_names.index('dp')] + self.dest_tp_size = self.dest_grid.shape[self.dest_grid.dim_names.index('tp')] + self.dest_dp_size = self.dest_grid.shape[self.dest_grid.dim_names.index('dp')] + + def _build_rank_mappings(self): + self.rank_to_src_pos: Dict[int, Tuple[int, int]] = {} + self.rank_to_dest_pos: Dict[int, Tuple[int, int]] = {} + + src_tp_groups = self.src_grid.get_rank_enum(['tp']) + for dp_idx, tp_group in enumerate(src_tp_groups): + for tp_idx, rank in enumerate(tp_group): + self.rank_to_src_pos[rank] = (dp_idx, tp_idx) + + dest_tp_groups = self.dest_grid.get_rank_enum(['tp']) + for dp_idx, tp_group in enumerate(dest_tp_groups): + for tp_idx, rank in enumerate(tp_group): + self.rank_to_dest_pos[rank] = (dp_idx, tp_idx) + + @staticmethod + def _build_gather_groups( + iter_size: int, sibling_tp_size: int, scale: int, rank_to_pos: Dict[int, Tuple[int, int]] + ) -> List[List[int]]: + """Build ``iter_size * sibling_tp_size`` gather groups of ``scale`` ranks. + + For each slot on the "iterating" side and each TP shard on the + sibling side, collect the ``scale`` sibling ranks whose DP indices + map into that slot. Append order equals group-local-rank order, + which ``all_gather_into_tensor`` uses to concatenate outputs — do + not sort. + """ + groups: List[List[int]] = [] + for iter_idx in range(iter_size): + sibling_dp_indices = range(iter_idx * scale, (iter_idx + 1) * scale) + for sibling_tp_idx in range(sibling_tp_size): + group_ranks = [] + for sibling_dp_idx in sibling_dp_indices: + for rank, (dp, tp) in rank_to_pos.items(): + if dp == sibling_dp_idx and tp == sibling_tp_idx: + group_ranks.append(rank) + break + groups.append(group_ranks) + return groups + + def is_fan_in(self) -> bool: + """True if src DP > dest DP (forward all-gathers).""" + return self.direction is BridgeDirection.FAN_IN + + def is_fan_out(self) -> bool: + """True if src DP < dest DP (forward narrows).""" + return self.direction is BridgeDirection.FAN_OUT + + def get_slice_info(self, batch_size: int) -> SliceInfo: + """Compute this rank's slice of ``batch_size`` on the narrowing side. + + For FAN_OUT this is the forward narrow; for FAN_IN it is the + backward narrow against the post-gather batch. EQUAL returns the + identity slice. + + Raises ``ValueError`` if ``batch_size`` is not divisible by ``scale``. + """ + if self.direction is BridgeDirection.EQUAL: + return SliceInfo(start=0, size=batch_size) + self._check_divisible(batch_size) + if self.direction is BridgeDirection.FAN_OUT: + dp_idx = self.rank_to_dest_pos[self.current_rank][0] + else: # FAN_IN + dp_idx = self.rank_to_src_pos[self.current_rank][0] + slot = dp_idx % self.scale + slice_size = batch_size // self.scale + return SliceInfo(start=slot * slice_size, size=slice_size) + + def _check_divisible(self, batch_size: int) -> None: + if batch_size % self.scale != 0: + raise ValueError( + f"ColocatedBridgeCommunicator: batch dim size {batch_size} is " + f"not divisible by {self.direction.value} scale={self.scale}." + ) + + def communicate(self, tensor: torch.Tensor) -> torch.Tensor: + """Transform ``tensor`` from src TP/DP layout to dest TP/DP layout. + + Raises ``ValueError`` when FAN_OUT and the batch dim is not + divisible by ``scale``; FAN_IN only slices on the backward pass + and re-checks via ``get_slice_info`` there. + """ + if self.direction is BridgeDirection.FAN_OUT: + self._check_divisible(tensor.shape[self.dim_mapping['b']]) + return _ColocatedCommunicate.apply(tensor, self) + + def destroy(self) -> None: + """Release the NCCL subgroup created by this communicator. + + NCCL caps concurrent communicators; long-lived or repeated + construction leaks PGs without this call. + """ + if self.gather_pg is not None: + dist.destroy_process_group(self.gather_pg) + self.gather_pg = None + + +class _ColocatedCommunicate(torch.autograd.Function): + """Autograd function for colocated communication with correct backward pass.""" + + @staticmethod + def forward(ctx, tensor: torch.Tensor, comm: ColocatedBridgeCommunicator) -> torch.Tensor: + """Reshape the batch dim across the bridge: narrow on fan-out, all-gather on fan-in.""" + ctx.comm = comm + ctx.batch_dim = comm.dim_mapping['b'] + + if comm.direction is BridgeDirection.FAN_OUT: + # Narrow this rank's slice out of the full src batch. + slice_info = comm.get_slice_info(tensor.shape[ctx.batch_dim]) + return tensor.narrow(ctx.batch_dim, slice_info.start, slice_info.size).contiguous() + + if comm.direction is BridgeDirection.FAN_IN: + # All-gather sibling src outputs into a single full-batch tensor. + return _all_gather_along_batch_dim(tensor, comm.gather_pg, ctx.batch_dim) + + # EQUAL: pure passthrough. + return tensor.contiguous() + + @staticmethod + def backward(ctx, grad_output: torch.Tensor) -> Tuple[torch.Tensor, None]: + """Adjoint of forward: narrow for fan-in, all-gather for fan-out. + + Fan-out's forward is ``narrow``, whose naive adjoint is zero-pad. + That would leave each src rank with only its own dest rank's + slice of the gradient, missing the contributions from every + other dest rank that consumed a different slice of the same src + activation. Instead we all-gather across the fan-out sibling + group, reconstructing the full src-batch gradient (symmetric + with the fan-in forward's all-gather). + """ + comm = ctx.comm + batch_dim = ctx.batch_dim + + if comm.direction is BridgeDirection.FAN_OUT: + return _all_gather_along_batch_dim(grad_output, comm.gather_pg, batch_dim), None + + if comm.direction is BridgeDirection.FAN_IN: + slice_info = comm.get_slice_info(grad_output.shape[batch_dim]) + return ( + grad_output.narrow(batch_dim, slice_info.start, slice_info.size).contiguous(), + None, + ) + + return grad_output.contiguous(), None + + +def _all_gather_along_batch_dim( + tensor: torch.Tensor, group: dist.ProcessGroup, batch_dim: int +) -> torch.Tensor: + """All-gather ``tensor`` along an arbitrary batch dim into a single tensor. + + ``all_gather_into_tensor`` concatenates along dim 0, so when the + batch dim is not 0 we move it, gather, then restore. + """ + world_size = dist.get_world_size(group) + src = tensor.contiguous() + if batch_dim != 0: + src = src.movedim(batch_dim, 0).contiguous() + out_shape = list(src.shape) + out_shape[0] *= world_size + out = torch.empty(out_shape, dtype=tensor.dtype, device=tensor.device) + dist.all_gather_into_tensor(out, src, group=group) + if batch_dim != 0: + out = out.movedim(0, batch_dim).contiguous() + return out diff --git a/megatron/core/models/mimo/config/base_configs.py b/megatron/core/models/mimo/config/base_configs.py index a92484a5a48..0eda09465e0 100644 --- a/megatron/core/models/mimo/config/base_configs.py +++ b/megatron/core/models/mimo/config/base_configs.py @@ -23,9 +23,11 @@ class MimoModelConfig: in the input_ids to insert the modality embeddings at the correct positions. module_to_grid_map (Optional[Dict[str, HyperCommGrid]]): Dictionary mapping module keys (e.g., "vision", "language") to their - corresponding HyperCommGrid configurations for non-colocated pipeline - parallelism. The language model must use the key MIMO_LANGUAGE_MODULE_KEY. - When None, all modules are assumed to be colocated on the same ranks. + corresponding HyperCommGrid configurations. The language model must use + the key MIMO_LANGUAGE_MODULE_KEY. + When grids span the same ranks → colocated (same or different TP/DP). + When grids span disjoint ranks → non-colocated (pipeline parallel). + When None → colocated with legacy global parallel_state. kv_format (str): Key-value format for attention: "sbhd" (seq-batch-head-dim) or "thd" (total-head-dim). Default is "sbhd". @@ -43,3 +45,18 @@ class MimoModelConfig: special_token_ids: Dict[str, int] = field(default_factory=dict) module_to_grid_map: Optional[Dict[str, HyperCommGrid]] = None kv_format: str = "sbhd" + + def __post_init__(self): + if not self.module_to_grid_map: + return + # Local import avoids circular imports at dataclass-module import time. + from megatron.core.models.mimo.config.role import MIMO_LANGUAGE_MODULE_KEY + + expected_keys = set(self.modality_submodules_spec.keys()) | {MIMO_LANGUAGE_MODULE_KEY} + grid_keys = set(self.module_to_grid_map.keys()) + if grid_keys != expected_keys: + raise ValueError( + f"module_to_grid_map keys must match modality module names + " + f"'{MIMO_LANGUAGE_MODULE_KEY}'. Missing: {expected_keys - grid_keys}, " + f"Extra: {grid_keys - expected_keys}" + ) diff --git a/megatron/core/models/mimo/config/role.py b/megatron/core/models/mimo/config/role.py index 77c2512e8e6..411791f1e5c 100644 --- a/megatron/core/models/mimo/config/role.py +++ b/megatron/core/models/mimo/config/role.py @@ -5,7 +5,7 @@ import logging from dataclasses import dataclass, field from enum import Enum -from typing import Dict, List +from typing import Dict, List, Optional import torch.distributed as dist @@ -24,22 +24,17 @@ class ModuleLayout(Enum): Determines how modules are distributed across ranks and which forward path is used. - UNIFIED: No module_to_grid_map. All modules share same ranks and - parallelism. Uses the unified forward path (_forward_all_modules). + COLOCATED: All modules share the same ranks. Covers both legacy + (no grid map, global parallel_state) and heterogeneous TP/DP + (grid map with overlapping ranks). Uses _forward_all_modules. NON_COLOCATED: module_to_grid_map is set with non-overlapping rank ranges. Each rank runs EITHER encoder(s) OR the language model. Uses role-based dispatch with separate forward paths. - - COLOCATED: (future) module_to_grid_map is set with overlapping rank - ranges. Encoder(s) and language model share ranks but have - different parallelism configs. Uses role-based dispatch but - allows both module types on the same rank. """ - UNIFIED = "unified" - NON_COLOCATED = "non_colocated" COLOCATED = "colocated" + NON_COLOCATED = "non_colocated" @dataclass @@ -70,50 +65,50 @@ class RankRole: """ modules: Dict[str, ModuleStageInfo] = field(default_factory=dict) - mode: ModuleLayout = ModuleLayout.UNIFIED + mode: ModuleLayout = ModuleLayout.COLOCATED + + @classmethod + def build( + cls, + modality_module_names: List[str], + module_to_grid_map: Optional[Dict[str, 'HyperCommGrid']] = None, + ) -> 'RankRole': + """Build a RankRole, dispatching by whether grids share ranks. + + No grid map or all grids span the same ranks → COLOCATED. + Grids differ → NON_COLOCATED with PP-stage info per module. + """ + if module_to_grid_map is None or cls._all_grids_colocated(module_to_grid_map): + return cls._colocated(modality_module_names) + return cls._from_grid_map(module_to_grid_map) + + @staticmethod + def _all_grids_colocated(module_to_grid_map: Dict[str, 'HyperCommGrid']) -> bool: + grids = list(module_to_grid_map.values()) + first = grids[0] + return all(g.rank_offset == first.rank_offset and g.size == first.size for g in grids[1:]) @classmethod - def unified(cls, module_names: List[str]) -> 'RankRole': - """Create a role for the unified case: every module, first+last stage.""" + def _colocated(cls, modality_module_names: List[str]) -> 'RankRole': + """Colocated layout: every module on every rank, PP=1.""" + all_module_names = list(modality_module_names) + [MIMO_LANGUAGE_MODULE_KEY] return cls( modules={ name: ModuleStageInfo(is_first_stage=True, is_last_stage=True) - for name in module_names + for name in all_module_names }, - mode=ModuleLayout.UNIFIED, + mode=ModuleLayout.COLOCATED, ) @classmethod - def from_grid_map( - cls, module_to_grid_map: Dict[str, HyperCommGrid], modality_module_names: List[str] - ) -> 'RankRole': - """Create a role from a module-to-grid mapping for non-colocated PP. - - Determines which modules the current rank participates in and its - pipeline stage position within each module. + def _from_grid_map(cls, module_to_grid_map: Dict[str, HyperCommGrid]) -> 'RankRole': + """Non-colocated role for this rank from a module-to-grid mapping. - Args: - module_to_grid_map: Dict mapping module names to HyperCommGrid objects. - Must contain keys matching modality_module_names + MIMO_LANGUAGE_MODULE_KEY. - modality_module_names: List of modality module names (e.g., ["images", "audio"]). - - Returns: - RankRole for the current rank. + Grid map keys are validated by ``MimoModelConfig.__post_init__``. Raises: - ValueError: If grid map keys don't match expected module names. RuntimeError: If current rank is not in any module grid. """ - # Validate keys - expected_keys = set(modality_module_names) | {MIMO_LANGUAGE_MODULE_KEY} - grid_keys = set(module_to_grid_map.keys()) - if grid_keys != expected_keys: - raise ValueError( - f"module_to_grid_map keys must match modality module names + " - f"'{MIMO_LANGUAGE_MODULE_KEY}'. Missing: {expected_keys - grid_keys}, " - f"Extra: {grid_keys - expected_keys}" - ) - current_rank = dist.get_rank() modules = {} @@ -131,7 +126,7 @@ def from_grid_map( is_first = pp_rank == 0 is_last = pp_rank == pp_size - 1 logger.info( - f"[RankRole.from_grid_map] Rank {current_rank}: module={module_name}, " + f"[RankRole._from_grid_map] Rank {current_rank}: module={module_name}, " f"pp_rank={pp_rank}/{pp_size}, is_first_stage={is_first}, is_last_stage={is_last}" ) modules[module_name] = ModuleStageInfo(is_first_stage=is_first, is_last_stage=is_last) diff --git a/megatron/core/models/mimo/model/base.py b/megatron/core/models/mimo/model/base.py index b1c12f521c3..bdfe4289dd0 100644 --- a/megatron/core/models/mimo/model/base.py +++ b/megatron/core/models/mimo/model/base.py @@ -7,6 +7,7 @@ import torch from megatron.core.distributed import DistributedDataParallel +from megatron.core.models.mimo.comm.colocated_communicator import ColocatedBridgeCommunicator from megatron.core.models.mimo.config import MimoModelConfig from megatron.core.models.mimo.config.role import MIMO_LANGUAGE_MODULE_KEY, ModuleLayout, RankRole from megatron.core.models.mimo.partition.utils import PartitionAdapter, PartitionConfig @@ -59,10 +60,12 @@ def __init__(self, mimo_config: MimoModelConfig, cp_group=None, tp_group=None) - self.mimo_config = mimo_config modality_names = list(mimo_config.modality_submodules_spec.keys()) - if mimo_config.module_to_grid_map: - self.role = RankRole.from_grid_map(mimo_config.module_to_grid_map, modality_names) - else: - self.role = RankRole.unified(modality_names + [MIMO_LANGUAGE_MODULE_KEY]) + self.colocated_comms = {} + self.role = RankRole.build(modality_names, mimo_config.module_to_grid_map) + if self.role.mode is ModuleLayout.COLOCATED and mimo_config.module_to_grid_map: + # Per-encoder bridge needed iff modules share ranks but may differ + # in TP/DP within those ranks. + self._build_colocated_communicators() # Use special token IDs from the config self.special_token_ids = ( @@ -358,7 +361,7 @@ def forward( # Get any tensors passed via set_input_tensor input_tensors = getattr(self, 'input_tensors', None) - if self.role.mode == ModuleLayout.UNIFIED: + if self.role.mode == ModuleLayout.COLOCATED: return self._forward_all_modules( input_ids, position_ids, @@ -491,6 +494,47 @@ def _forward_language_module( return lm_output + def _build_colocated_communicators(self): + grid_map = self.mimo_config.module_to_grid_map + if any( + 'tp' not in grid.dim_names or 'dp' not in grid.dim_names for grid in grid_map.values() + ): + logger.info( + "Skipping colocated communicator setup because module_to_grid_map " + "does not define TP/DP topology for every module." + ) + return + + lang_key = MIMO_LANGUAGE_MODULE_KEY + lang_grid = grid_map[lang_key] + for mod_name in self.mimo_config.modality_submodules_spec: + if mod_name == lang_key: + continue + self.colocated_comms[(mod_name, lang_key)] = ColocatedBridgeCommunicator( + src_grid=grid_map[mod_name], + dest_grid=lang_grid, + src_module_name=mod_name, + dest_module_name=lang_key, + dim_mapping={'b': 0, 'h': 1}, + ) + + def destroy(self) -> None: + """Release process groups owned by this MimoModel.""" + for comm in self.colocated_comms.values(): + comm.destroy() + self.colocated_comms.clear() + + def _apply_colocated_comms(self, modality_embeddings): + """Transform encoder embeddings from encoder TP/DP to LLM TP/DP layout.""" + lang_key = MIMO_LANGUAGE_MODULE_KEY + for modality_name in list(modality_embeddings.keys()): + comm = self.colocated_comms.get((modality_name, lang_key)) + if comm is not None: + modality_embeddings[modality_name] = comm.communicate( + modality_embeddings[modality_name] + ) + return modality_embeddings + def _forward_all_modules( self, input_ids: torch.Tensor, @@ -533,6 +577,10 @@ def _forward_all_modules( f"Generated embeddings for {modality_name} with shape {embeddings.shape}" ) + # Apply colocated communication if configured (no-op when colocated_comms is empty) + if self.colocated_comms: + modality_embeddings = self._apply_colocated_comms(modality_embeddings) + # Get text embeddings text_embeddings = self.get_text_embeddings(input_ids, position_ids, self.special_token_ids) logger.debug(f"Generated text embeddings with shape {text_embeddings.shape}") diff --git a/tests/unit_tests/models/test_mimo_1f1b_schedule.py b/tests/unit_tests/models/test_mimo_1f1b_schedule.py index 44be0c7911e..836382b21cc 100644 --- a/tests/unit_tests/models/test_mimo_1f1b_schedule.py +++ b/tests/unit_tests/models/test_mimo_1f1b_schedule.py @@ -60,6 +60,27 @@ _embedding_pg_cache: dict = {} +def build_no_sync_func(mimo_model): + """Build a no_sync_func that stacks DDP no_sync over each sub-module. + + Shared by 1F1B pipeline tests and colocated-correctness tests — both need + DDP's gradient sync disabled during microbatches and resumed via the + schedule's finalize_grads_func. + """ + + @contextmanager + def no_sync_func(): + with ExitStack() as stack: + if mimo_model.language_model is not None: + stack.enter_context(mimo_model.language_model.no_sync()) + for submodule in mimo_model.modality_submodules.values(): + if submodule is not None: + stack.enter_context(submodule.no_sync()) + yield + + return no_sync_func + + def create_hypercomm_grid(offset=0, tp=1, cp=1, pp=1, dp=1): """Create a HyperCommGrid with specified parallelism.""" grid = HyperCommGrid( @@ -183,13 +204,40 @@ def is_rank_in_grid(grid): def get_language_model_spec( - num_layers, hidden_size, num_attention_heads, vocab_size, seq_len, pg_collection + num_layers, + hidden_size, + num_attention_heads, + vocab_size, + seq_len, + pg_collection, + bf16=True, + bias=True, + dropout=True, + per_token_loss=False, ): - """Get the language model spec.""" + """Get the language model spec. + + ``bf16=False`` switches pipeline dtype and autocast to fp32. Correctness + tests also pass ``bias=False, dropout=False`` to remove bias-update and + stochastic noise from the cross-config diff signal. + + ``per_token_loss=True`` sets ``calculate_per_token_loss=True`` on the + TransformerConfig, which pins DDP's gradient_scaling_factor to 1.0 + (pure SUM reduction). Callers that flip this must supply a 3-tuple + loss_func and drive the external divide in their finalize hook. + """ pp_rank = dist.get_rank(pg_collection.pp) pp_size = dist.get_world_size(pg_collection.pp) tp_size = pg_collection.tp.size() if pg_collection.tp is not None else 1 + pipeline_dtype = torch.bfloat16 if bf16 else torch.float32 + extra_kwargs = {} + if not bias: + extra_kwargs['add_bias_linear'] = False + if not dropout: + extra_kwargs['attention_dropout'] = 0.0 + extra_kwargs['hidden_dropout'] = 0.0 + lm_config = TransformerConfig( num_layers=num_layers, hidden_size=hidden_size, @@ -199,10 +247,12 @@ def get_language_model_spec( moe_token_dispatcher_type='alltoall', tensor_model_parallel_size=tp_size, pipeline_model_parallel_size=pp_size, - pipeline_dtype=torch.bfloat16, - bf16=True, + pipeline_dtype=pipeline_dtype, + bf16=bf16, cross_entropy_loss_fusion=True, cross_entropy_fusion_impl='te', + calculate_per_token_loss=per_token_loss, + **extra_kwargs, ) return ModuleSpec( module=GPTModel, @@ -218,12 +268,12 @@ def get_language_model_spec( ) -def get_projection_config(hidden_size): +def get_projection_config(hidden_size, bias=True): """Return a TransformerConfig for the vision projection MLP.""" cfg = TransformerConfig(num_layers=1, hidden_size=hidden_size, num_attention_heads=1) cfg.ffn_hidden_size = hidden_size - cfg.bias_activation_fusion = True - cfg.add_bias_linear = True + cfg.bias_activation_fusion = bool(bias) + cfg.add_bias_linear = bool(bias) cfg.activation_func = torch.nn.functional.gelu return cfg @@ -239,15 +289,38 @@ def get_projection_layer_spec(): def get_vision_submodules_spec( - num_layers, hidden_size, num_attention_heads, language_hidden_size, pg_collection + num_layers, + hidden_size, + num_attention_heads, + language_hidden_size, + pg_collection, + bf16=True, + bias=True, + dropout=True, + per_token_loss=False, ): - """Get the submodule spec for the vision modality.""" + """Get the submodule spec for the vision modality. + + ``bias=False`` / ``dropout=False`` mirror the LM-spec kwargs for + correctness tests. ``per_token_loss=True`` sets + ``calculate_per_token_loss=True`` on the encoder's TransformerConfig so + the encoder DDP also pure-SUMs across DP (needed for the heterogeneous-DP + colocated path). + """ from megatron.core.transformer.transformer_block import TransformerBlock tp_size = pg_collection.tp.size() if pg_collection.tp is not None else 1 pp_size = pg_collection.pp.size() if pg_collection.pp is not None else 1 pp_rank = dist.get_rank(pg_collection.pp) + pipeline_dtype = torch.bfloat16 if bf16 else torch.float32 + extra_kwargs = {} + if not bias: + extra_kwargs['add_bias_linear'] = False + if not dropout: + extra_kwargs['attention_dropout'] = 0.0 + extra_kwargs['hidden_dropout'] = 0.0 + vision_config = TransformerConfig( num_layers=num_layers, hidden_size=hidden_size, @@ -257,8 +330,10 @@ def get_vision_submodules_spec( moe_token_dispatcher_type='alltoall', tensor_model_parallel_size=tp_size, pipeline_model_parallel_size=pp_size, - pipeline_dtype=torch.bfloat16, - bf16=True, + pipeline_dtype=pipeline_dtype, + bf16=bf16, + calculate_per_token_loss=per_token_loss, + **extra_kwargs, ) vision_encoder_spec = ModuleSpec( module=TransformerBlock, @@ -274,7 +349,7 @@ def get_vision_submodules_spec( vision_projection_spec = ModuleSpec( module=MultimodalProjector, params={ - "config": get_projection_config(hidden_size=language_hidden_size), + "config": get_projection_config(hidden_size=language_hidden_size, bias=bias), "submodules": get_projection_layer_spec().submodules, "projector_type": "mlp", "input_size": vision_config.hidden_size, @@ -293,9 +368,38 @@ def get_vision_submodules_spec( def get_mimo_model( - encoder_name, encoder_grid, llm_grid, hidden_size, num_layers, vocab_size, seq_len + encoder_name, + encoder_grid, + llm_grid, + hidden_size, + num_layers, + vocab_size, + seq_len, + ddp_config=None, + bf16=True, + bias=True, + dropout=True, + per_token_loss=False, ): - """Create MIMO model with TransformerBlock encoder and GPTModel LLM.""" + """Create MIMO model with TransformerBlock encoder and GPTModel LLM. + + Args: + ddp_config: Optional override for the Megatron DDP config. Default + matches the 1F1B schedule tests' config. + bf16: If True (default) build the model in bf16; if False build in + fp32 end-to-end for deterministic numerics in correctness tests. + bias: If False, disable ``add_bias_linear`` in LM/vision configs and + the projection MLP — removes bias-update noise from diffs. + dropout: If False, force attention/hidden dropout to 0.0. + per_token_loss: If True, set ``calculate_per_token_loss=True`` on + both sub-model configs. This pins the encoder and LLM DDP + gradient_scaling_factor to 1.0 (pure SUM across DP). The caller + MUST supply a 3-tuple loss_func ``(sum_loss, num_tokens, + log_dict)`` and a custom ``finalize_model_grads_func`` that + divides grads by the correct global divisor on both sides; + hetero-DP callers use this to land ``1/B_full`` on both encoder + and LLM without relying on the per-DDP built-in scaling. + """ language_pg = get_pg_collection_with_embedding_groups(llm_grid, is_language_model=True) vision_pg = get_pg_collection_with_embedding_groups(encoder_grid, is_language_model=False) @@ -306,6 +410,10 @@ def get_mimo_model( vocab_size=vocab_size, seq_len=seq_len, pg_collection=language_pg, + bf16=bf16, + bias=bias, + dropout=dropout, + per_token_loss=per_token_loss, ) vision_submodule_spec = get_vision_submodules_spec( num_layers=num_layers, @@ -313,6 +421,10 @@ def get_mimo_model( num_attention_heads=8, language_hidden_size=hidden_size, pg_collection=vision_pg, + bf16=bf16, + bias=bias, + dropout=dropout, + per_token_loss=per_token_loss, ) module_to_grid_map = {encoder_name: encoder_grid, MIMO_LANGUAGE_MODULE_KEY: llm_grid} @@ -326,12 +438,15 @@ def get_mimo_model( ) mimo_model = MimoModel(mimo_config) - mimo_model.to(torch.device("cuda")).to(torch.bfloat16) - - # Wrap with DDP - ddp_config = DistributedDataParallelConfig( - overlap_grad_reduce=True, bucket_size=10000, use_distributed_optimizer=True - ) + mimo_model.to(torch.device("cuda")) + if bf16: + mimo_model.to(torch.bfloat16) + + # Wrap with DDP (caller may override e.g. for heterogeneous-DP scaling). + if ddp_config is None: + ddp_config = DistributedDataParallelConfig( + overlap_grad_reduce=True, bucket_size=10000, use_distributed_optimizer=True + ) if mimo_model.language_model is not None: mimo_model.language_model = DistributedDataParallel( @@ -485,16 +600,7 @@ def run_mimo_1f1b_test( seq_len=seq_length, ) - # Build schedule functions using pre-created pg_collections (no leaks) - @contextmanager - def no_sync_func(): - with ExitStack() as stack: - if mimo_model.language_model is not None: - stack.enter_context(mimo_model.language_model.no_sync()) - for submodule in mimo_model.modality_submodules.values(): - if submodule is not None: - stack.enter_context(submodule.no_sync()) - yield + no_sync_func = build_no_sync_func(mimo_model) def finalize_grads_func(*args, **kwargs): if mimo_model.language_model is not None: @@ -595,30 +701,35 @@ def loss_func(loss_mask, output_tensor): optimizer.zero_grad() - losses = schedule.forward_backward_pipelining_without_interleaving( - forward_step_func=step_func, - data_iterator=data_iterator, - model=[mimo_model], - num_microbatches=num_microbatches, - seq_length=seq_length, - micro_batch_size=micro_batch_size, - forward_only=False, - p2p_communicator=communicator, - pg_collection=pg_collection, - ) - - # Optimizer step with global gradient clipping - success, grad_norm, num_zeros = optimizer.step() - assert success, "Optimizer step failed" - assert grad_norm is not None and grad_norm > 0, f"Expected positive grad norm, got {grad_norm}" - - # Verify results on last LLM stage - if is_rank_in_grid(llm_grid) and is_pp_last_stage(llm_grid.get_pg("pp")): - assert len(losses) > 0, "Expected losses on last LLM stage" - for loss_dict in losses: - assert 'loss_reduced' in loss_dict + try: + losses = schedule.forward_backward_pipelining_without_interleaving( + forward_step_func=step_func, + data_iterator=data_iterator, + model=[mimo_model], + num_microbatches=num_microbatches, + seq_length=seq_length, + micro_batch_size=micro_batch_size, + forward_only=False, + p2p_communicator=communicator, + pg_collection=pg_collection, + ) - return losses + # Optimizer step with global gradient clipping + success, grad_norm, num_zeros = optimizer.step() + assert success, "Optimizer step failed" + assert ( + grad_norm is not None and grad_norm > 0 + ), f"Expected positive grad norm, got {grad_norm}" + + # Verify results on last LLM stage + if is_rank_in_grid(llm_grid) and is_pp_last_stage(llm_grid.get_pg("pp")): + assert len(losses) > 0, "Expected losses on last LLM stage" + for loss_dict in losses: + assert 'loss_reduced' in loss_dict + + return losses + finally: + mimo_model.destroy() # ============================================================================ diff --git a/tests/unit_tests/models/test_mimo_colocated_communicator.py b/tests/unit_tests/models/test_mimo_colocated_communicator.py new file mode 100644 index 00000000000..67cee551a0f --- /dev/null +++ b/tests/unit_tests/models/test_mimo_colocated_communicator.py @@ -0,0 +1,543 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +import logging +import os +import sys + +import pytest +import torch +import torch.distributed as dist + +from megatron.core.hyper_comm_grid import HyperCommGrid +from megatron.core.models.mimo.comm.colocated_communicator import ColocatedBridgeCommunicator + +logging.basicConfig(level=logging.DEBUG, stream=sys.stderr) + +_active_grids: list = [] +_active_comms: list = [] + + +def create_hypercomm_grid(offset=0, tp=1, cp=1, pp=1, dp=1): + grid = HyperCommGrid( + shape=[tp, cp, pp, dp], + dim_names=["tp", "cp", "pp", "dp"], + rank_offset=offset, + backend="nccl", + ) + grid.create_pg(["tp"]) + grid.create_pg(["cp"]) + grid.create_pg(["pp"]) + grid.create_pg(["dp"]) + _active_grids.append(grid) + return grid + + +def make_comm(*args, **kwargs): + comm = ColocatedBridgeCommunicator(*args, **kwargs) + _active_comms.append(comm) + return comm + + +def destroy_all_grids(): + # Destroy communicators first so their NCCL subgroups are freed before we + # tear down the parent grids. NCCL caps concurrent communicators at ~500; + # leaked PGs from per-test fixtures blow that budget quickly. + for comm in _active_comms: + comm.destroy() + _active_comms.clear() + for grid in _active_grids: + grid.destroy() + _active_grids.clear() + + +# ── Test 1: Rank mappings ────────────────────────────────────────────────────── + + +class TestRankMappings: + + @classmethod + def setup_class(cls): + if not dist.is_initialized(): + dist.init_process_group(backend="nccl") + if torch.cuda.is_available(): + torch.cuda.set_device(int(os.environ.get("LOCAL_RANK", 0))) + + def teardown_method(self): + destroy_all_grids() + + @pytest.mark.parametrize( + "src_tp, src_dp, dest_tp, dest_dp, expected_src_pos, expected_dest_pos", + [ + # Fan-in: TP2/DP4 → TP4/DP2 + ( + 2, + 4, + 4, + 2, + { + 0: (0, 0), + 1: (0, 1), + 2: (1, 0), + 3: (1, 1), + 4: (2, 0), + 5: (2, 1), + 6: (3, 0), + 7: (3, 1), + }, + { + 0: (0, 0), + 1: (0, 1), + 2: (0, 2), + 3: (0, 3), + 4: (1, 0), + 5: (1, 1), + 6: (1, 2), + 7: (1, 3), + }, + ), + # Fan-out: TP4/DP2 → TP2/DP4 + ( + 4, + 2, + 2, + 4, + { + 0: (0, 0), + 1: (0, 1), + 2: (0, 2), + 3: (0, 3), + 4: (1, 0), + 5: (1, 1), + 6: (1, 2), + 7: (1, 3), + }, + { + 0: (0, 0), + 1: (0, 1), + 2: (1, 0), + 3: (1, 1), + 4: (2, 0), + 5: (2, 1), + 6: (3, 0), + 7: (3, 1), + }, + ), + ], + ids=["fan_in", "fan_out"], + ) + def test_rank_mappings( + self, src_tp, src_dp, dest_tp, dest_dp, expected_src_pos, expected_dest_pos + ): + src_grid = create_hypercomm_grid(tp=src_tp, dp=src_dp) + dest_grid = create_hypercomm_grid(tp=dest_tp, dp=dest_dp) + comm = make_comm(src_grid, dest_grid) + + assert comm.rank_to_src_pos == expected_src_pos + assert comm.rank_to_dest_pos == expected_dest_pos + + def test_rank_mappings_with_rank_offset(self): + # 4-rank grids at offset=4 (covering ranks 4-7). Exercises the + # rank_offset propagation that previously only ran with offset=0. + if dist.get_world_size() < 8: + pytest.skip("requires at least 8 ranks") + src_grid = create_hypercomm_grid(offset=4, tp=2, dp=2) + dest_grid = create_hypercomm_grid(offset=4, tp=1, dp=4) + comm = make_comm(src_grid, dest_grid) + + assert comm.rank_to_src_pos == {4: (0, 0), 5: (0, 1), 6: (1, 0), 7: (1, 1)} + assert comm.rank_to_dest_pos == {4: (0, 0), 5: (1, 0), 6: (2, 0), 7: (3, 0)} + + +# ── Test 2: All-gather groups ────────────────────────────────────────────────── + + +class TestAllGatherGroups: + + @classmethod + def setup_class(cls): + if not dist.is_initialized(): + dist.init_process_group(backend="nccl") + if torch.cuda.is_available(): + torch.cuda.set_device(int(os.environ.get("LOCAL_RANK", 0))) + + def teardown_method(self): + destroy_all_grids() + + def test_fan_in_all_gather_groups(self): + # Fan-in TP2/DP4 → TP4/DP2. Groups are keyed (dest_dp_idx, src_tp_idx) + # and members must appear in src_dp_idx order so all_gather_into_tensor + # concatenates in slot order on the backward path. + src_grid = create_hypercomm_grid(tp=2, dp=4) + dest_grid = create_hypercomm_grid(tp=4, dp=2) + comm = make_comm(src_grid, dest_grid) + + assert comm.gather_group_ranks == [[0, 2], [1, 3], [4, 6], [5, 7]] + assert comm.gather_pg is not None + + def test_fan_out_gather_groups(self): + # Fan-out TP4/DP2 → TP2/DP4. Groups are keyed (src_dp_idx, dest_tp_idx); + # membership order must track dest_dp_idx so the backward all-gather + # reconstructs the full-batch gradient in the correct layout. + src_grid = create_hypercomm_grid(tp=4, dp=2) + dest_grid = create_hypercomm_grid(tp=2, dp=4) + comm = make_comm(src_grid, dest_grid) + + assert comm.gather_group_ranks == [[0, 2], [1, 3], [4, 6], [5, 7]] + assert comm.gather_pg is not None + + +# ── Test 3b: _validate_grids negative tests ─────────────────────────────────── + + +class TestValidateGrids: + """One negative test per raise path in ColocatedBridgeCommunicator._validate_grids. + + Each case builds a pair of grids that violates exactly one invariant and + asserts that the constructor raises ValueError. + """ + + @classmethod + def setup_class(cls): + if not dist.is_initialized(): + dist.init_process_group(backend="nccl") + if torch.cuda.is_available(): + torch.cuda.set_device(int(os.environ.get("LOCAL_RANK", 0))) + + def teardown_method(self): + destroy_all_grids() + + def _grid_missing_tp(self, offset=0, dp=1): + # Build a grid without a 'tp' dim to exercise the "missing 'tp'" raise. + grid = HyperCommGrid(shape=[dp], dim_names=["dp"], rank_offset=offset, backend="nccl") + grid.create_pg(["dp"]) + _active_grids.append(grid) + return grid + + def test_missing_tp_dim(self): + src_grid = self._grid_missing_tp(dp=8) + dest_grid = create_hypercomm_grid(tp=4, dp=2) + with pytest.raises(ValueError, match="must have 'tp' dimension"): + make_comm(src_grid, dest_grid) + + def test_size_mismatch(self): + src_grid = create_hypercomm_grid(tp=2, dp=4) # 8 ranks + dest_grid = create_hypercomm_grid(offset=4, tp=2, dp=2) # 4 ranks + with pytest.raises(ValueError, match="span same number of ranks"): + make_comm(src_grid, dest_grid) + + def test_rank_offset_mismatch(self): + src_grid = create_hypercomm_grid(offset=0, tp=2, dp=2) + dest_grid = create_hypercomm_grid(offset=4, tp=2, dp=2) + with pytest.raises(ValueError, match="same rank offset"): + make_comm(src_grid, dest_grid) + + @pytest.mark.parametrize( + "side,dim,expected", + [ + ("src", "pp", "src PP must be 1"), + ("dest", "pp", "dest PP must be 1"), + ("src", "cp", "CP must be 1"), + ], + ) + def test_pp_or_cp_gt_one_rejected(self, side, dim, expected): + bad = {dim: 2, "tp": 2, "dp": 2} + good = {"tp": 4, "dp": 2} + if side == "src": + src_grid = create_hypercomm_grid(**bad) + dest_grid = create_hypercomm_grid(**good) + else: + src_grid = create_hypercomm_grid(**good) + dest_grid = create_hypercomm_grid(**bad) + with pytest.raises(ValueError, match=expected): + make_comm(src_grid, dest_grid) + + def test_dp_not_divisible(self): + # 6-rank grids with DP sizes (3 vs 2) that neither divides the other. + # Fits inside an 8-rank world (HyperCommGrid enforces size <= world - offset). + if dist.get_world_size() < 6: + pytest.skip("requires at least 6 ranks") + src_grid = HyperCommGrid( + shape=[2, 1, 1, 3], dim_names=["tp", "cp", "pp", "dp"], backend="nccl" + ) + dest_grid = HyperCommGrid( + shape=[3, 1, 1, 2], dim_names=["tp", "cp", "pp", "dp"], backend="nccl" + ) + for g in (src_grid, dest_grid): + _active_grids.append(g) + with pytest.raises(ValueError, match="evenly divisible"): + make_comm(src_grid, dest_grid) + + +# ── Test 3c: communicate() runtime preconditions ────────────────────────────── + + +class TestCommunicatePreconditions: + """Runtime-input checks enforced by ``communicate()``.""" + + @classmethod + def setup_class(cls): + if not dist.is_initialized(): + dist.init_process_group(backend="nccl") + if torch.cuda.is_available(): + torch.cuda.set_device(int(os.environ.get("LOCAL_RANK", 0))) + + def teardown_method(self): + destroy_all_grids() + + def test_non_divisible_batch_raises_fan_out(self): + # Fan-out: dest_dp=4, src_dp=2 → scale=2. Pass a batch dim of size 3 + # so 3 % 2 != 0 and the forward communicate() raises before slicing. + src_grid = create_hypercomm_grid(tp=4, dp=2) + dest_grid = create_hypercomm_grid(tp=2, dp=4) + comm = make_comm(src_grid, dest_grid, dim_mapping={'b': 0, 'h': 1}) + tensor = torch.zeros(3, 8, device='cuda') + with pytest.raises(ValueError, match="not divisible by fan_out"): + comm.communicate(tensor) + + def test_non_divisible_batch_raises_fan_in_backward_narrow(self): + # Fan-in forward all-gathers (no slice), so the forward path never + # divides. The backward path narrows the post-gather output via + # get_slice_info, which raises on a non-divisible size. Call + # get_slice_info directly with an odd size to exercise that path. + src_grid = create_hypercomm_grid(tp=2, dp=4) + dest_grid = create_hypercomm_grid(tp=4, dp=2) + comm = make_comm(src_grid, dest_grid) + with pytest.raises(ValueError, match="not divisible by fan_in"): + comm.get_slice_info(batch_size=3) + + +# ── Test 3d: destroy() releases PGs ────────────────────────────────────────── + + +class TestDestroy: + """``destroy()`` must null out both PG attributes.""" + + @classmethod + def setup_class(cls): + if not dist.is_initialized(): + dist.init_process_group(backend="nccl") + if torch.cuda.is_available(): + torch.cuda.set_device(int(os.environ.get("LOCAL_RANK", 0))) + + def teardown_method(self): + destroy_all_grids() + + def test_destroy_releases_fan_in_pg(self): + src_grid = create_hypercomm_grid(tp=2, dp=4) + dest_grid = create_hypercomm_grid(tp=4, dp=2) + # Don't track via make_comm — destroy() is exactly what we're testing. + comm = ColocatedBridgeCommunicator(src_grid, dest_grid) + assert comm.gather_pg is not None + comm.destroy() + assert comm.gather_pg is None + + def test_destroy_releases_fan_out_pg(self): + src_grid = create_hypercomm_grid(tp=4, dp=2) + dest_grid = create_hypercomm_grid(tp=2, dp=4) + comm = ColocatedBridgeCommunicator(src_grid, dest_grid) + assert comm.gather_pg is not None + comm.destroy() + assert comm.gather_pg is None + + def test_destroy_is_idempotent(self): + # Calling destroy twice must not raise — leftover test fixtures often + # double-destroy during exception cleanup. + src_grid = create_hypercomm_grid(tp=2, dp=4) + dest_grid = create_hypercomm_grid(tp=4, dp=2) + comm = ColocatedBridgeCommunicator(src_grid, dest_grid) + comm.destroy() + comm.destroy() + + +# ── Test 3e: Bridge gradient correctness (bitwise exact) ───────────────────── + + +def _shape_for_dim_mapping(dim_mapping, B, S, H): + s = [0, 0, 0] + s[dim_mapping['b']] = B + s[dim_mapping['s']] = S + s[dim_mapping['h']] = H + return s + + +# Parametrize dim_mapping for the fan-in tests (tests 1 & 2 per AXIOM spec). +_DIM_MAPPINGS = [{'s': 0, 'b': 1, 'h': 2}, {'b': 0, 's': 1, 'h': 2}] +_DIM_MAPPING_IDS = ["sbh", "bsh"] + + +class TestBridgeGradients: + """Bitwise-exact gradient tests for ``ColocatedBridgeCommunicator``. + + This class is **intentionally distinct** from the model-level correctness + tests in ``test_mimo_colocated_correctness.py`` (see PR review comment 19). + The bridge forward and backward are pure data + movement (``narrow`` / ``all_gather_into_tensor``) with no FP compute, so + the mathematical adjoint relationship can — and should — be asserted at + ``rtol=0, atol=0``: + + * fan-in forward == ``torch.cat`` of sibling inputs in slot order + * fan-in backward == ``grad_output.narrow`` at this rank's slot + * fan-out forward == ``input.narrow`` at this rank's slot + * fan-out backward == ``cat`` of every sibling's grad (catches + zero-pad-without-gather, wrong slot order, double-counting, + missing siblings — the four failure modes of the adjoint) + * equal-DP is a pure identity (forward + backward) + + The MimoModel-level tests validate the full training stack including GEMM + reduction order and DDP scaling, and can only assert approximate FP32 + closeness. These tests localise the bridge's own invariants and fail + first when one of them regresses. + """ + + S = 8 + B_PER_RANK = 2 + H = 128 + + @classmethod + def setup_class(cls): + if not dist.is_initialized(): + dist.init_process_group(backend="nccl") + if torch.cuda.is_available(): + torch.cuda.set_device(int(os.environ.get("LOCAL_RANK", 0))) + + def teardown_method(self): + destroy_all_grids() + + # ── Test 1: fan-in forward = torch.cat of sibling inputs ───────────────── + @pytest.mark.parametrize("src_tp,src_dp,dest_tp,dest_dp", [(2, 4, 4, 2)], ids=["2x_fan_in"]) + @pytest.mark.parametrize("dim_mapping", _DIM_MAPPINGS, ids=_DIM_MAPPING_IDS) + def test_fan_in_forward_equals_torch_cat(self, src_tp, src_dp, dest_tp, dest_dp, dim_mapping): + src_grid = create_hypercomm_grid(tp=src_tp, dp=src_dp) + dest_grid = create_hypercomm_grid(tp=dest_tp, dp=dest_dp) + comm = make_comm(src_grid, dest_grid, dim_mapping=dim_mapping) + + rank = dist.get_rank() + shape = _shape_for_dim_mapping(dim_mapping, self.B_PER_RANK, self.S, self.H) + + # Distinct inputs per rank so the cat reveals ordering bugs. + torch.manual_seed(1000 + rank) + local_input = torch.randn(*shape, device='cuda') + + actual = comm.communicate(local_input) + + # Expected: manual all_gather over the communicator's fan-in group, + # then cat along batch_dim. all_gather preserves group-local-rank + # order, which is the same order the communicator uses. + group = comm.gather_pg + gathered = [torch.empty_like(local_input) for _ in range(dist.get_world_size(group))] + dist.all_gather(gathered, local_input, group=group) + expected = torch.cat(gathered, dim=dim_mapping['b']) + + torch.testing.assert_close(actual, expected, rtol=0, atol=0) + + # ── Test 2: fan-in backward = grad_output.narrow for this rank's slot ──── + @pytest.mark.parametrize("src_tp,src_dp,dest_tp,dest_dp", [(2, 4, 4, 2)], ids=["2x_fan_in"]) + @pytest.mark.parametrize("dim_mapping", _DIM_MAPPINGS, ids=_DIM_MAPPING_IDS) + def test_fan_in_backward_equals_narrow(self, src_tp, src_dp, dest_tp, dest_dp, dim_mapping): + src_grid = create_hypercomm_grid(tp=src_tp, dp=src_dp) + dest_grid = create_hypercomm_grid(tp=dest_tp, dp=dest_dp) + comm = make_comm(src_grid, dest_grid, dim_mapping=dim_mapping) + + rank = dist.get_rank() + batch_dim = dim_mapping['b'] + b_local = self.B_PER_RANK + shape = _shape_for_dim_mapping(dim_mapping, b_local, self.S, self.H) + + torch.manual_seed(1000 + rank) + local_input = torch.randn(*shape, device='cuda', requires_grad=True) + out = comm.communicate(local_input) + + # grad_output is TP-replicated within the dest DP group: seed the same + # on every rank so every rank in the fan-in group backward-narrows the + # same upstream gradient. out shape is identical across group members, + # so seeded randn produces the same tensor on each. + torch.manual_seed(42) + grad_output = torch.randn_like(out) + out.backward(grad_output) + + slot = comm.rank_to_src_pos[rank][0] % comm.scale + expected = grad_output.narrow(batch_dim, slot * b_local, b_local).contiguous() + torch.testing.assert_close(local_input.grad, expected, rtol=0, atol=0) + + # ── Test 3: fan-out forward = input.narrow for this rank's slot ───────── + @pytest.mark.parametrize("src_tp,src_dp,dest_tp,dest_dp", [(4, 2, 2, 4)], ids=["2x_fan_out"]) + def test_fan_out_forward_equals_narrow(self, src_tp, src_dp, dest_tp, dest_dp): + dim_mapping = {'b': 0, 's': 1, 'h': 2} + src_grid = create_hypercomm_grid(tp=src_tp, dp=src_dp) + dest_grid = create_hypercomm_grid(tp=dest_tp, dp=dest_dp) + comm = make_comm(src_grid, dest_grid, dim_mapping=dim_mapping) + + rank = dist.get_rank() + batch_dim = dim_mapping['b'] + b_per_dest = self.B_PER_RANK + b_full = b_per_dest * comm.scale + shape = _shape_for_dim_mapping(dim_mapping, b_full, self.S, self.H) + + # Input is TP-replicated on the batch dim (bridge contract). Seed + # identically across all ranks to satisfy it. + torch.manual_seed(42) + input_tensor = torch.randn(*shape, device='cuda') + + actual = comm.communicate(input_tensor) + + slot = comm.rank_to_dest_pos[rank][0] % comm.scale + expected = input_tensor.narrow(batch_dim, slot * b_per_dest, b_per_dest).contiguous() + torch.testing.assert_close(actual, expected, rtol=0, atol=0) + + # ── Test 4 (CRITICAL): fan-out backward = concat of all sibling grads ── + @pytest.mark.parametrize("src_tp,src_dp,dest_tp,dest_dp", [(4, 2, 2, 4)], ids=["2x_fan_out"]) + def test_fan_out_backward_equals_concat_of_sibling_grads( + self, src_tp, src_dp, dest_tp, dest_dp + ): + """Fan-out backward must all-gather sibling grads in slot order. + + Catches four distinct regressions with a single assertion: + * zero-pad-without-gather (other slots would be zero), + * wrong slot order (values would be scrambled), + * double-counting (values would be multiplied), + * missing siblings (shape or zeros would diverge). + """ + dim_mapping = {'b': 0, 's': 1, 'h': 2} + src_grid = create_hypercomm_grid(tp=src_tp, dp=src_dp) + dest_grid = create_hypercomm_grid(tp=dest_tp, dp=dest_dp) + comm = make_comm(src_grid, dest_grid, dim_mapping=dim_mapping) + + rank = dist.get_rank() + batch_dim = dim_mapping['b'] + scale = comm.scale + b_per_dest = self.B_PER_RANK + b_full = b_per_dest * scale + shape = _shape_for_dim_mapping(dim_mapping, b_full, self.S, self.H) + + torch.manual_seed(42) # identical input across ranks (TP-replicated) + input_tensor = torch.randn(*shape, device='cuda', requires_grad=True) + out = comm.communicate(input_tensor) # narrowed to (b_per_dest, S, H) + + # Distinct grad per slot so the cat reveals both membership and order. + slot = comm.rank_to_dest_pos[rank][0] % scale + grad_output = (slot + 1) * torch.ones_like(out) + out.backward(grad_output) + + slot_shape = _shape_for_dim_mapping(dim_mapping, b_per_dest, self.S, self.H) + expected = torch.cat( + [(i + 1) * torch.ones(*slot_shape, device='cuda') for i in range(scale)], dim=batch_dim + ) + torch.testing.assert_close(input_tensor.grad, expected, rtol=0, atol=0) + + # ── Test 5: equal DP is a pure identity forward and backward ──────────── + @pytest.mark.parametrize("src_tp,src_dp,dest_tp,dest_dp", [(4, 2, 4, 2)], ids=["tp4_dp2"]) + def test_equal_dp_is_bitwise_identity_fwd_and_bwd(self, src_tp, src_dp, dest_tp, dest_dp): + dim_mapping = {'b': 0, 's': 1, 'h': 2} + src_grid = create_hypercomm_grid(tp=src_tp, dp=src_dp) + dest_grid = create_hypercomm_grid(tp=dest_tp, dp=dest_dp) + comm = make_comm(src_grid, dest_grid, dim_mapping=dim_mapping) + + shape = _shape_for_dim_mapping(dim_mapping, self.B_PER_RANK, self.S, self.H) + torch.manual_seed(1000 + dist.get_rank()) + x = torch.randn(*shape, device='cuda', requires_grad=True) + + out = comm.communicate(x) + torch.testing.assert_close(out, x, rtol=0, atol=0) + + grad_output = torch.randn_like(x) + out.backward(grad_output) + torch.testing.assert_close(x.grad, grad_output, rtol=0, atol=0) diff --git a/tests/unit_tests/models/test_mimo_colocated_correctness.py b/tests/unit_tests/models/test_mimo_colocated_correctness.py new file mode 100644 index 00000000000..e2d91bdf83e --- /dev/null +++ b/tests/unit_tests/models/test_mimo_colocated_correctness.py @@ -0,0 +1,1183 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +"""Gradient-scaling correctness for colocated MimoModel under heterogeneous DP. + +Verifies that a heterogeneous-DP MimoModel produces the same post-step +encoder weights as an **equal-DP** reference built on the SAME encoder +TP/DP layout as the dist model (so the bridge is the identity +passthrough — ``BridgeDirection.EQUAL`` in +``ColocatedBridgeCommunicator``). Under correct grad scaling, both +configs yield the DP=1 gradient on every encoder shard, so the Adam +update lands on identical values and the sharded post-step weights +compare directly. + +Why an equal-DP reference is the right oracle: + * Encoder sharding matches exactly — ref and dist both use + ``enc_tp=dist_enc_tp, enc_dp=dist_enc_dp``. Shards line up 1:1, + so there is no gather-and-slice in the weight comparison and no + TP=1-vs-TP>1 accumulation-order drift to contend with. + * ``enc_dp == llm_dp`` on the ref side → the bridge is identity and + every encoder rank feeds its colocated LLM rank with no + redistribution collective. + * Both sides set ``calculate_per_token_loss=True`` on their + TransformerConfigs, which pins DDP's ``gradient_scaling_factor=1.0`` + — pure SUM across DP. The custom + ``finalize_grads_func`` in ``_wire_training_hooks`` all-reduces + ``total_num_tokens`` over the LLM DP group, then calls + ``scale_gradients(1/N_global)`` on both encoder and LLM. This lands + the true global per-token mean on every shard without touching + ``DistributedDataParallel``. + +LLM TP differs between ref (``llm_tp=dist_enc_tp``) and dist +(``llm_tp=dist_llm_tp``), so ref's LLM weights are copied into dist via +all-gather-across-ref-TP + slice-for-dist-TP. The LLM forward then +diverges numerically by fp32 TP accumulation order, but the aggregate +gradient that flows back into the encoder remains the DP=1 gradient in +both models, which is what the post-step encoder weight oracle checks. +The test runs in fp32 with ``add_bias_linear=False`` and dropout +disabled to minimize non-bridge numerical noise — this keeps the +post-bridge hidden states bit-exact and surfaces only TP-shape drift +in the logits oracle. + +If the heterogeneous-DP scaling is wrong (e.g. dividing by encoder_dp +when it should be 1, or letting either DDP apply its default ``1/dp_size`` +on top of the per-token mean already delivered by the finalize hook), +the dist encoder's post-step weights diverge from the ref encoder's +weights — a single Adam step is enough to detect. + +Run with:: + + uv run python -m torch.distributed.run --nproc_per_node=8 \\ + -m pytest tests/unit_tests/models/test_mimo_colocated_correctness.py -v -s +""" + +import os +from functools import partial + +import pytest +import torch +import torch.distributed as dist +from packaging import version + +import megatron.core.pipeline_parallel.schedules as schedule +from megatron.core.distributed import DistributedDataParallelConfig +from megatron.core.distributed.finalize_model_grads import finalize_model_grads +from megatron.core.models.mimo.optimizer import get_mimo_optimizer +from megatron.core.optimizer.optimizer_config import OptimizerConfig +from megatron.core.transformer.enums import ModelType +from megatron.core.utils import unwrap_model +from tests.unit_tests.models.test_mimo_1f1b_schedule import ( + build_no_sync_func, + create_all_embedding_groups, + create_hypercomm_grid, + destroy_all_grids, + get_mimo_model, +) +from tests.unit_tests.test_utilities import Utils + + +def loss_func(loss_mask, output_tensor): + """Per-token-loss 3-tuple: raw local sum + local valid-token count. + + Returns ``(local_sum, local_num_tokens, log_dict)`` — the contract the + schedule expects when ``calculate_per_token_loss=True`` is set on the + TransformerConfig. No ``1/num_tokens`` or ``1/num_microbatches`` + division is applied here; the schedule skips the per-microbatch + division (see ``schedules.py:270-274``) and aggregates ``num_tokens`` + across microbatches for the finalize step. + + Paired with ``mimo_finalize_grads_func`` below, which all-reduces + ``total_num_tokens`` over the LLM DP group to obtain ``N_global`` and + then divides both encoder and LLM grads by ``1/N_global`` directly via + ``scale_gradients`` — landing the true global per-token mean on every + shard without touching DDP. + + ``output_tensor`` is per-token CE from + ``GPTModel.compute_language_model_loss`` with shape ``[b, s]``. + """ + if output_tensor is None: + zero_loss = torch.tensor(0.0, device='cuda', requires_grad=True) + zero_count = torch.tensor(0, device='cuda', dtype=torch.int) + return zero_loss, zero_count, {'loss_reduced': 0.0} + + masked = output_tensor.float() * loss_mask.float() + local_sum = masked.sum() + local_num_tokens = loss_mask.float().sum().to(torch.int) + return local_sum, local_num_tokens, {'loss_reduced': local_sum.detach().item()} + + +def forward_step(data_iterator, model, encoder_grid, llm_grid, encoder_name): + """Forward step with per-rank data slicing for heterogeneous DP.""" + batch = next(data_iterator) if data_iterator is not None else {'input_ids': None} + + if batch.get('input_ids') is None: + output_tensor, loss_mask = model(**batch) + return output_tensor, partial(loss_func, loss_mask) + + encoder_dp = encoder_grid.get_pg("dp").size() + llm_dp = llm_grid.get_pg("dp").size() + + if encoder_dp > llm_dp: + # Fan-in: input was pre-sliced to LLM-DP (larger per-rank batch). + # Narrow modality_inputs to the encoder's smaller per-rank slice. + scale = encoder_dp // llm_dp + encoder_dp_idx = encoder_grid.get_pg("dp").rank() + slot = encoder_dp_idx % scale + + if 'modality_inputs' in batch and batch['modality_inputs'] is not None: + for mod_name, mod_data in batch['modality_inputs'].items(): + for enc_name, enc_data in mod_data.items(): + for key, tensor in enc_data.items(): + if tensor is not None and isinstance(tensor, torch.Tensor): + batch_size = tensor.shape[1] # [seq, batch, hidden] + slice_size = batch_size // scale + start = slot * slice_size + enc_data[key] = tensor[:, start : start + slice_size, :].contiguous() + + elif llm_dp > encoder_dp: + # Fan-out: input was pre-sliced to encoder-DP (larger per-rank batch). + # Narrow the LLM-side tensors to this LLM-DP rank's slice. + scale = llm_dp // encoder_dp + llm_dp_idx = llm_grid.get_pg("dp").rank() + slot = llm_dp_idx % scale + + batch_size = batch['input_ids'].shape[0] + slice_size = batch_size // scale + start = slot * slice_size + + for key in ['input_ids', 'labels', 'loss_mask', 'position_ids']: + if key in batch and batch[key] is not None: + batch[key] = batch[key][start : start + slice_size].contiguous() + + output_tensor, loss_mask = model(**batch) + return output_tensor, partial(loss_func, loss_mask) + + +def _set_deterministic_env(): + for k, v in { + "NVTE_ALLOW_NONDETERMINISTIC_ALGO": "0", + "CUDA_DEVICE_MAX_CONNECTIONS": "1", + "CUBLAS_WORKSPACE_CONFIG": ":4096:8", + }.items(): + os.environ[k] = v + os.environ.pop('NVTE_FLASH_ATTN', None) + os.environ.pop('NVTE_FUSED_ATTN', None) + os.environ.pop('NVTE_UNFUSED_ATTN', None) + + +def _wire_training_hooks(mimo_model, language_pg, vision_pg): + """Attach no_sync / finalize_grads / grad_scale hooks to a MimoModel. + + The finalize hook implements the heterogeneous-DP grad-scaling story + without touching ``DistributedDataParallel``. Both sub-model configs + set ``calculate_per_token_loss=True``, so both DDPs pure-SUM across + their own DP group (``gradient_scaling_factor=1.0``). After backward + and DDP reduce, every rank's ``main_grad`` holds the un-normalized + full-batch sum of per-token gradients. + + This hook then: + 1. all-reduces the schedule's ``total_num_tokens`` across the LLM + DP group to obtain ``N_global`` (total valid tokens in the global + batch). Since ranks are colocated, every rank now knows + ``N_global``. + 2. Calls ``finalize_model_grads(num_tokens=None)`` per side — runs + the usual DDP grad finish + layernorm/embedding AR work without + letting the built-in divisor path fire. + 3. Calls ``scale_gradients(1/N_global)`` on each side — lands the + true global per-token mean uniformly on encoder and LLM grads. + + Note: encoder has no loss_func (so nothing emits a per-encoder-DP + ``num_tokens`` to feed ``finalize_model_grads``' internal all-reduce). + Doing the all-reduce once ourselves and calling ``scale_gradients`` + directly avoids engineering a fictitious per-encoder-rank count whose + sum happens to equal ``N_global``. + """ + + no_sync_func = build_no_sync_func(mimo_model) + + def finalize_grads_func(model_list, num_tokens, force_all_reduce=False, **kwargs): + # Schedule passes the per-rank sum-across-microbatches of what the + # loss_func returned. Because loss_func runs only on the LLM side, + # this is the LLM-local token count. + assert num_tokens is not None, ( + "finalize_grads_func expects calculate_per_token_loss=True on the " + "TransformerConfig so the schedule forwards total_num_tokens; got None." + ) + + # Phase 1: lift the all-reduce. After this, every rank (including + # encoder-only replicas) has N_global = total non-padded tokens in + # the global batch. + llm_dp_pg = language_pg.dp_cp if language_pg.dp_cp is not None else language_pg.dp + dist.all_reduce(num_tokens, group=llm_dp_pg, op=dist.ReduceOp.SUM) + n_global = num_tokens.item() + + # Phase 2: per-side DDP finish without built-in num_tokens scaling. + # Forward ``force_all_reduce`` so PP grad-sync semantics (if ever + # exercised here) aren't silently dropped. + if mimo_model.language_model is not None: + finalize_model_grads( + [mimo_model.language_model], + num_tokens=None, + pg_collection=language_pg, + force_all_reduce=force_all_reduce, + ) + for submodule in mimo_model.modality_submodules.values(): + if submodule is not None: + finalize_model_grads( + [submodule], + num_tokens=None, + pg_collection=vision_pg, + force_all_reduce=force_all_reduce, + ) + + # Phase 3: uniform divide by N_global. Guard div-by-zero for the + # degenerate fully-masked batch. + if n_global > 0: + inv = 1.0 / n_global + if mimo_model.language_model is not None: + mimo_model.language_model.scale_gradients(inv) + for submodule in mimo_model.modality_submodules.values(): + if submodule is not None: + submodule.scale_gradients(inv) + + mimo_model.config.no_sync_func = no_sync_func + mimo_model.config.finalize_model_grads_func = finalize_grads_func + mimo_model.config.grad_scale_func = lambda loss: ( + torch.tensor(loss, dtype=torch.float32, device='cuda', requires_grad=True) + if isinstance(loss, (int, float)) + else loss + ) + + +def _generate_and_broadcast_global_batches( + global_mbs, + seq_length, + hidden_size, + vocab_size, + encoder_name, + num_batches, + image_token_id=50257, + mask_pattern="uniform", +): + """Generate global batches on rank 0 and broadcast so every rank sees + identical data. Dist pre-slices per rank; ref consumes the full batch. + + ``mask_pattern``: + * ``"uniform"`` — every sample has the same valid-token count (image + tokens masked, text tokens all valid). Local/global denominators + coincide up to DP-rank partitioning. + * ``"asymmetric"`` — each sample zeros out an additional sample- + dependent number of trailing text tokens, so different samples + (and therefore different DP-rank slices) carry different valid- + token counts. This exercises the num+den global-mean CE path + where the old local-mean recipe would be only approximately + correct. + """ + if mask_pattern not in ("uniform", "asymmetric"): + raise ValueError(f"Unknown mask_pattern: {mask_pattern!r}") + + rank = dist.get_rank() + image_seq_length = seq_length // 2 + batches = [] + + for batch_idx in range(num_batches): + if rank == 0: + encoder_hidden_states = torch.randn( + image_seq_length, global_mbs, hidden_size, device='cuda', dtype=torch.float32 + ) + image_tokens = torch.full( + (global_mbs, image_seq_length), image_token_id, dtype=torch.long, device='cuda' + ) + text_tokens = torch.randint( + 1, vocab_size, (global_mbs, seq_length - image_seq_length), device='cuda' + ) + input_ids = torch.cat([image_tokens, text_tokens], dim=1) + else: + encoder_hidden_states = torch.empty( + image_seq_length, global_mbs, hidden_size, device='cuda', dtype=torch.float32 + ) + input_ids = torch.empty(global_mbs, seq_length, dtype=torch.long, device='cuda') + + dist.broadcast(encoder_hidden_states, src=0) + dist.broadcast(input_ids, src=0) + + labels = input_ids.clone() + labels[input_ids == image_token_id] = -100 + loss_mask = torch.ones(global_mbs, seq_length, device='cuda', dtype=torch.float32) + loss_mask[input_ids == image_token_id] = 0.0 + + if mask_pattern == "asymmetric": + # Zero out a sample-dependent trailing run of text tokens so + # each sample ends up with a different valid-token count. + # Counts are deterministic given (batch_idx, sample_idx) so the + # broadcast-on-rank-0 pattern is reproducible on every rank. + text_len = seq_length - image_seq_length + for sample_idx in range(global_mbs): + n_drop = ((batch_idx * 7 + sample_idx * 3) % (text_len - 1)) + 1 + loss_mask[sample_idx, seq_length - n_drop :] = 0.0 + labels[sample_idx, seq_length - n_drop :] = -100 + position_ids = ( + torch.arange(seq_length, device='cuda').unsqueeze(0).expand(global_mbs, -1).clone() + ) + + batches.append( + { + "input_ids": input_ids, + "labels": labels, + "loss_mask": loss_mask, + "position_ids": position_ids, + "modality_inputs": { + encoder_name: { + "clip_encoder": { + 'hidden_states': encoder_hidden_states, + 'attention_mask': None, + } + } + }, + } + ) + + return batches + + +def _slice_batch(global_batch, split_dp, split_rank): + """Return the ``split_rank``-th of ``split_dp`` slices along the batch dim.""" + batch_dim = global_batch['input_ids'].shape[0] + slice_size = batch_dim // split_dp + start = split_rank * slice_size + end = start + slice_size + + per_rank = {} + for key in ['input_ids', 'labels', 'loss_mask', 'position_ids']: + per_rank[key] = global_batch[key][start:end].contiguous() + + mod_inputs_new = {} + for mod_name, mod_data in global_batch['modality_inputs'].items(): + mod_inputs_new[mod_name] = {} + for enc_name, enc_data in mod_data.items(): + mod_inputs_new[mod_name][enc_name] = {} + for key, tensor in enc_data.items(): + if tensor is not None and isinstance(tensor, torch.Tensor): + # modality hidden_states is [seq, batch, hidden] — slice dim 1 + mod_inputs_new[mod_name][enc_name][key] = tensor[:, start:end, :].contiguous() + else: + mod_inputs_new[mod_name][enc_name][key] = tensor + per_rank['modality_inputs'] = mod_inputs_new + return per_rank + + +def _slice_global_batch_for_dist(global_batch, encoder_grid, llm_grid): + """Pre-slice a global batch to the per-rank batch that ``forward_step`` expects. + + ``forward_step`` assumes each rank already has its LLM-DP slice + (fan-in) or encoder-DP slice (fan-out); this helper performs that + slicing so both models can consume the same underlying global batch. + When ``enc_dp == llm_dp`` there is no fan-in/fan-out to pre-slice for + (``forward_step`` also skips slicing), and the full batch is returned. + """ + enc_dp = encoder_grid.get_pg("dp").size() + llm_dp = llm_grid.get_pg("dp").size() + + if enc_dp > llm_dp: + return _slice_batch(global_batch, llm_dp, llm_grid.get_pg("dp").rank()) + if llm_dp > enc_dp: + return _slice_batch(global_batch, enc_dp, encoder_grid.get_pg("dp").rank()) + return global_batch + + +def _slice_global_batch_by_dp(global_batch, dp_pg): + """Slice a global batch along the batch dim by ``dp_pg`` rank. + + For the equal-DP reference (``enc_dp == llm_dp``, bridge is identity), + each rank consumes 1/``dp_size`` of the global batch directly. + ``_slice_global_batch_for_dist`` returns the full batch in that case, + so this helper does the DP-rank split explicitly. + """ + dp_size = dist.get_world_size(dp_pg) + if dp_size <= 1: + return global_batch + return _slice_batch(global_batch, dp_size, dist.get_rank(dp_pg)) + + +def _copy_ref_params_to_dist(ref_module, dist_module, ref_tp_group, dist_tp_group): + """Copy ref params into dist, handling differing TP shardings. + + When ref and dist params have the same shape (same TP size and layout + at offset=0), shards align 1:1 and we copy directly. When shapes differ + (different TP sizes), we all-gather ref's shards across ``ref_tp_group`` + to reconstruct the full weight, then slice by the dist ``partition_dim`` + for this rank's dist TP shard. + + Must be called **before** constructing the distributed optimizer, which + clones current param data into fp32 master weights at __init__. + """ + ref_tp_size = dist.get_world_size(ref_tp_group) + dist_tp_rank = dist.get_rank(dist_tp_group) + dist_tp_size = dist.get_world_size(dist_tp_group) + ref_params = dict(ref_module.named_parameters()) + + with torch.no_grad(): + for name, dist_param in dist_module.named_parameters(): + assert name in ref_params, f"Param '{name}' in dist but not in ref" + ref_param = ref_params[name] + partition_dim = getattr(dist_param, 'partition_dim', -1) + + if ref_param.shape == dist_param.shape: + # Same shard size (same TP layout or both replicated). + dist_param.data.copy_(ref_param.data.to(dist_param.dtype)) + continue + + assert partition_dim >= 0, ( + f"Param '{name}': shapes differ " + f"(ref={tuple(ref_param.shape)}, dist={tuple(dist_param.shape)}) " + f"but partition_dim<0 — cannot reshard a replicated param." + ) + + # Different TP sizes: gather ref shards, then slice for dist. + shards = [torch.empty_like(ref_param.data) for _ in range(ref_tp_size)] + dist.all_gather(shards, ref_param.data.contiguous(), group=ref_tp_group) + full_weight = torch.cat(shards, dim=partition_dim) + dist_slice = torch.tensor_split(full_weight, dist_tp_size, dim=partition_dim)[ + dist_tp_rank + ] + + assert dist_slice.shape == dist_param.shape, ( + f"Param '{name}': sliced.shape={tuple(dist_slice.shape)} != " + f"dist.shape={tuple(dist_param.shape)} " + f"(ref_tp={ref_tp_size}, dist_tp={dist_tp_size}, " + f"partition_dim={partition_dim})" + ) + dist_param.data.copy_(dist_slice.to(dist_param.dtype)) + + +def _global_abs_diff_stats(a, b, pg=None): + """Absolute-diff stats plus reference-tensor magnitude stats, across ``pg``. + + Reports both the abs-diff distribution AND the magnitude of ``b`` (the + reference tensor) so the caller can judge scale: a max abs-diff of 1.0 + is catastrophic for values of O(1), but fine for values of O(100). The + relative-diff column (``rel_max = max_diff / ref_max``) gives a quick + percentage read. + + Useful when the per-rank tensors cover different shards — all-reducing + MAX/MIN (and MAX of per-rank p95/p99 as a conservative worst-case) lets + rank 0 print a global view of drift across every shard in ``pg``. Mean + is SUM/world_size, which is the true global mean when every rank holds + the same number of elements (true here — shards have the same shape). + """ + diff = (a.float() - b.float()).abs().flatten() + ref = b.float().abs().flatten() + n = diff.numel() + + if n == 0: + zero = torch.tensor(0.0, device='cuda') + local_min = local_max = local_mean = local_p50 = local_p95 = local_p99 = zero + local_ref_max = local_ref_p95 = local_ref_mean = zero + else: + local_min = diff.min() + local_max = diff.max() + local_mean = diff.mean() + local_p50 = diff.quantile(0.50) + local_p95 = diff.quantile(0.95) + local_p99 = diff.quantile(0.99) + local_ref_max = ref.max() + local_ref_p95 = ref.quantile(0.95) + local_ref_mean = ref.mean() + + world = dist.get_world_size(pg) if dist.is_initialized() else 1 + if world > 1: + g_min = local_min.clone() + g_max = local_max.clone() + g_mean = local_mean.clone() + g_p50 = local_p50.clone() + g_p95 = local_p95.clone() + g_p99 = local_p99.clone() + g_ref_max = local_ref_max.clone() + g_ref_p95 = local_ref_p95.clone() + g_ref_mean = local_ref_mean.clone() + dist.all_reduce(g_min, op=dist.ReduceOp.MIN, group=pg) + dist.all_reduce(g_max, op=dist.ReduceOp.MAX, group=pg) + dist.all_reduce(g_mean, op=dist.ReduceOp.SUM, group=pg) + dist.all_reduce(g_p50, op=dist.ReduceOp.MAX, group=pg) + dist.all_reduce(g_p95, op=dist.ReduceOp.MAX, group=pg) + dist.all_reduce(g_p99, op=dist.ReduceOp.MAX, group=pg) + dist.all_reduce(g_ref_max, op=dist.ReduceOp.MAX, group=pg) + dist.all_reduce(g_ref_p95, op=dist.ReduceOp.MAX, group=pg) + dist.all_reduce(g_ref_mean, op=dist.ReduceOp.SUM, group=pg) + g_mean = g_mean / world + g_ref_mean = g_ref_mean / world + return { + 'min': g_min.item(), + 'max': g_max.item(), + 'mean': g_mean.item(), + 'p50_worst': g_p50.item(), + 'p95_worst': g_p95.item(), + 'p99_worst': g_p99.item(), + 'ref_max': g_ref_max.item(), + 'ref_p95': g_ref_p95.item(), + 'ref_mean': g_ref_mean.item(), + 'numel_per_rank': n, + 'ranks': world, + } + return { + 'min': local_min.item(), + 'max': local_max.item(), + 'mean': local_mean.item(), + 'p50_worst': local_p50.item(), + 'p95_worst': local_p95.item(), + 'p99_worst': local_p99.item(), + 'ref_max': local_ref_max.item(), + 'ref_p95': local_ref_p95.item(), + 'ref_mean': local_ref_mean.item(), + 'numel_per_rank': n, + 'ranks': 1, + } + + +def _fmt_diff_stats(s): + ref_max = s.get('ref_max', 0.0) + rel_max = (s['max'] / ref_max) if ref_max > 0 else float('inf') + return ( + f"min={s['min']:.2e} p50={s['p50_worst']:.2e} mean={s['mean']:.2e} " + f"p95={s['p95_worst']:.2e} p99={s['p99_worst']:.2e} " + f"max={s['max']:.2e} | ref_max={ref_max:.2e} ref_p95={s.get('ref_p95', 0.0):.2e} " + f"ref_mean={s.get('ref_mean', 0.0):.2e} rel_max={rel_max:.1%} " + f"(n_per_rank={s['numel_per_rank']}, ranks={s['ranks']})" + ) + + +def _print_from_rank0(msg): + if not dist.is_initialized() or dist.get_rank() == 0: + print(msg, flush=True) + + +def _register_logits_capture(mimo_model): + """Forward hook on the LLM ``output_layer``; captures per-microbatch logits. + + The hook runs on every microbatch forward. ``output`` from + ``ColumnParallelLinear`` is ``(logits, bias)`` with logits shape + ``[s, b, v/tp]`` — this rank's per-DP-slot, per-TP-vocab-shard slice + of the global logits tensor. Cloning so backward doesn't mutate. + + Returns ``(captures, handle)``; caller must ``handle.remove()`` after + the schedule completes. + """ + gpt = unwrap_model(mimo_model.language_model) + captures = [] + + def hook(_module, _inputs, output): + logits = output[0] if isinstance(output, tuple) else output + captures.append(logits.detach().clone()) + + handle = gpt.output_layer.register_forward_hook(hook) + return captures, handle + + +def _register_llm_input_capture(mimo_model): + """Forward pre-hook on the GPT ``decoder``; captures post-bridge hidden states. + + This is the activation entering the transformer block AFTER embedding + (skipped when MIMO passes ``decoder_input``) AND after the bridge has + moved the encoder output into the LLM's TP/DP layout. Shape is + ``[s, b_local, h_full]`` — hidden dim is NOT TP-sharded at this point. + + Comparing dist vs ref at this capture isolates "does the bridge deliver + mathematically equivalent inputs to the LLM?" from downstream LLM TP + forward drift. If this oracle passes but ``llm_logits`` fails, the + divergence is inside the LLM TP forward; if this fails, the bridge + (fan_in/fan_out vs equal) is not equivalent. + """ + gpt = unwrap_model(mimo_model.language_model) + captures = [] + + def pre_hook(_module, args, kwargs): + hidden = kwargs.get('hidden_states', None) + if hidden is None and args: + hidden = args[0] + if hidden is not None: + captures.append(hidden.detach().clone()) + + handle = gpt.decoder.register_forward_pre_hook(pre_hook, with_kwargs=True) + return captures, handle + + +def _gather_bs_dp(local_tensor, llm_dp_pg): + """All-gather ``[s, b, h]`` across LLM DP along the batch dim.""" + dp_size = dist.get_world_size(llm_dp_pg) + if dp_size <= 1: + return local_tensor.contiguous() + contig = local_tensor.contiguous() + shards = [torch.empty_like(contig) for _ in range(dp_size)] + dist.all_gather(shards, contig, group=llm_dp_pg) + return torch.cat(shards, dim=1) + + +def _assert_llm_input_match( + ref_captures, dist_captures, ref_llm_grid, dist_llm_grid, rtol=1e-3, atol=1e-3 +): + """Post-bridge oracle: hidden states entering the LLM decoder match. + + Hidden dim is not TP-sharded at the decoder input, so only DP-gather + across the LLM DP group is needed to reconstruct the full-batch tensor. + """ + assert len(ref_captures) == len(dist_captures), ( + f"Microbatch count mismatch: ref={len(ref_captures)}, " f"dist={len(dist_captures)}" + ) + ref_dp_pg = ref_llm_grid.get_pg("dp") + dist_dp_pg = dist_llm_grid.get_pg("dp") + + mismatches = [] + for mbs_idx, (ref_local, dist_local) in enumerate(zip(ref_captures, dist_captures)): + ref_full = _gather_bs_dp(ref_local, ref_dp_pg) + dist_full = _gather_bs_dp(dist_local, dist_dp_pg) + assert ref_full.shape == dist_full.shape, ( + f"mbs[{mbs_idx}]: gathered llm-input shape mismatch — " + f"ref={tuple(ref_full.shape)}, dist={tuple(dist_full.shape)}" + ) + stats = _global_abs_diff_stats(dist_full, ref_full, pg=dist.group.WORLD) + _print_from_rank0( + f"[llm-input-diff] mbs[{mbs_idx}] shape={tuple(ref_full.shape)} " + f"{_fmt_diff_stats(stats)}" + ) + try: + torch.testing.assert_close(dist_full, ref_full, rtol=rtol, atol=atol) + except AssertionError as e: + mismatches.append((mbs_idx, str(e))) + + if mismatches: + rank = dist.get_rank() + details = "\n".join(f" mbs[{i}]: {msg}" for i, msg in mismatches) + raise AssertionError( + f"Rank {rank}: llm-input diverged on {len(mismatches)} microbatch(es):\n" f"{details}" + ) + + +def _gather_logits_full_batch(local_logits, llm_tp_pg, llm_dp_pg): + """All-gather ``[s, b, v/tp]`` across LLM TP (vocab dim) then DP (batch dim). + + Returns ``[s, b * dp_size, v]`` — the full global-batch logits, + identical on every rank of the LLM grid. Used to compare dist vs ref + on the same global slots regardless of how TP/DP slices them. + """ + tp_size = dist.get_world_size(llm_tp_pg) + dp_size = dist.get_world_size(llm_dp_pg) + + vocab_full = local_logits.contiguous() + if tp_size > 1: + shards = [torch.empty_like(vocab_full) for _ in range(tp_size)] + dist.all_gather(shards, vocab_full, group=llm_tp_pg) + vocab_full = torch.cat(shards, dim=-1) + + batch_full = vocab_full.contiguous() + if dp_size > 1: + shards = [torch.empty_like(batch_full) for _ in range(dp_size)] + dist.all_gather(shards, batch_full, group=llm_dp_pg) + batch_full = torch.cat(shards, dim=1) + + return batch_full + + +def _assert_llm_logits_match( + ref_captures, dist_captures, ref_llm_grid, dist_llm_grid, rtol=1e-2, atol=1e-2 +): + """Logits oracle: TP+DP-gathered full-batch logits match microbatch-by-microbatch. + + Dist and ref share the same global batch on every rank (broadcast from + rank 0), and with the HyperCommGrid layout both reconstruct global + batch rows 0..N in the same order after TP+DP all-gather (see + ``_slice_global_batch_*`` helpers for how the slicing lines up). + The only numerical difference between the two gathered logits is + fp32 accumulation order across a different LLM TP shape — hence the + loose ``rtol=atol=1e-2`` default. + """ + assert len(ref_captures) == len(dist_captures), ( + f"Microbatch count mismatch: ref={len(ref_captures)}, " f"dist={len(dist_captures)}" + ) + ref_tp_pg = ref_llm_grid.get_pg("tp") + ref_dp_pg = ref_llm_grid.get_pg("dp") + dist_tp_pg = dist_llm_grid.get_pg("tp") + dist_dp_pg = dist_llm_grid.get_pg("dp") + + mismatches = [] + for mbs_idx, (ref_local, dist_local) in enumerate(zip(ref_captures, dist_captures)): + ref_full = _gather_logits_full_batch(ref_local, ref_tp_pg, ref_dp_pg) + dist_full = _gather_logits_full_batch(dist_local, dist_tp_pg, dist_dp_pg) + assert ref_full.shape == dist_full.shape, ( + f"mbs[{mbs_idx}]: gathered logits shape mismatch — " + f"ref={tuple(ref_full.shape)}, dist={tuple(dist_full.shape)}" + ) + # Gathered full-batch logits are identical on every LLM-grid rank, + # so stats at rank 0 represent the tensor globally — no reduction + # needed across other ranks. + stats = _global_abs_diff_stats(dist_full, ref_full, pg=dist.group.WORLD) + _print_from_rank0( + f"[logits-diff] mbs[{mbs_idx}] shape={tuple(ref_full.shape)} " + f"{_fmt_diff_stats(stats)}" + ) + try: + torch.testing.assert_close(dist_full, ref_full, rtol=rtol, atol=atol) + except AssertionError as e: + mismatches.append((mbs_idx, str(e))) + + if mismatches: + rank = dist.get_rank() + details = "\n".join(f" mbs[{i}]: {msg}" for i, msg in mismatches) + raise AssertionError( + f"Rank {rank}: logits diverged on {len(mismatches)} microbatch(es):\n" f"{details}" + ) + + +def _snapshot_first_layer_encoder_grads(mimo_model, encoder_name): + """Clone ``param.main_grad`` for every ``.layers.0.`` encoder param. + + ``main_grad`` holds the post-DDP-reduction gradient (reduced across + encoder DP), populated by the backward pass and consumed by + ``optimizer.step()``. Snapshot between backward and step so the values + aren't yet zeroed. + """ + encoder = mimo_model.modality_submodules[encoder_name].module + snap = {} + for name, param in encoder.named_parameters(): + if '.layers.0.' not in name: + continue + grad = getattr(param, 'main_grad', None) + if grad is None: + continue + snap[name] = grad.detach().clone() + return snap + + +def _assert_first_layer_grads_match(ref_snap, dist_snap, rtol=1e-3, atol=1e-3): + """First-layer encoder grad oracle: shard-to-shard match between ref and dist. + + Ref and dist use identical encoder TP/DP layout, so for every + ``layers.0.*`` encoder parameter their local shards line up 1:1. + Under correct grad scaling both main_grads equal the DP=1 gradient, + so the per-shard values must match within fp32 precision. Tighter + tolerances than the logits oracle are possible because the encoder + forward is identical on both sides — only the LLM TP layout differs, + and that noise enters via the gradient flowing back into the encoder. + """ + assert set(ref_snap.keys()) == set(dist_snap.keys()), ( + f"First-layer param name mismatch — " + f"ref-only: {set(ref_snap) - set(dist_snap)}, " + f"dist-only: {set(dist_snap) - set(ref_snap)}" + ) + mismatches = [] + for name in sorted(ref_snap): + ref_g = ref_snap[name] + dist_g = dist_snap[name] + assert ref_g.shape == dist_g.shape, ( + f"Param '{name}': grad shape {tuple(ref_g.shape)} != " + f"{tuple(dist_g.shape)} — caller must match encoder TP." + ) + # Every rank holds its own TP shard of this param; all-reduce + # across the full world so rank 0 prints the worst-case drift + # across all shards. + stats = _global_abs_diff_stats(dist_g, ref_g, pg=dist.group.WORLD) + _print_from_rank0( + f"[grad-diff] {name} shape={tuple(ref_g.shape)} " f"{_fmt_diff_stats(stats)}" + ) + try: + torch.testing.assert_close(dist_g, ref_g, rtol=rtol, atol=atol) + except AssertionError as e: + mismatches.append((name, str(e))) + + if mismatches: + rank = dist.get_rank() + details = "\n".join(f" {n}: {msg}" for n, msg in mismatches) + raise AssertionError( + f"Rank {rank}: {len(mismatches)} first-layer encoder grad(s) " + f"diverged between dist and ref:\n{details}" + ) + + +def _assert_encoder_weights_match(ref_module, dist_module, rtol=1e-3, atol=1e-3): + """Assert every dist encoder shard matches the ref encoder shard. + + Caller is responsible for ensuring ref and dist have the same encoder TP + layout (same ``enc_tp`` and ``enc_dp``), so each rank's shards line up + 1:1 and can be compared directly. Under correct grad scaling and + identical initial state, one Adam step yields shard-wise equal post-step + weights — modulo fp32 TP accumulation-order drift from the LLM TP + layout differing between the two models. + """ + ref_params = dict(ref_module.named_parameters()) + + mismatches = [] + for name, dist_param in dist_module.named_parameters(): + ref_param = ref_params[name] + assert ref_param.shape == dist_param.shape, ( + f"Param '{name}': ref.shape={tuple(ref_param.shape)} != " + f"dist.shape={tuple(dist_param.shape)} — caller must match encoder TP." + ) + stats = _global_abs_diff_stats(dist_param.data, ref_param.data, pg=dist.group.WORLD) + _print_from_rank0( + f"[weight-diff] {name} shape={tuple(ref_param.shape)} " f"{_fmt_diff_stats(stats)}" + ) + try: + torch.testing.assert_close(dist_param.data, ref_param.data, rtol=rtol, atol=atol) + except AssertionError as e: + mismatches.append((name, str(e))) + + if mismatches: + rank = dist.get_rank() + details = "\n".join(f" {n}: {msg}" for n, msg in mismatches) + raise AssertionError( + f"Rank {rank}: {len(mismatches)} encoder param(s) diverged between " + f"heterogeneous-DP dist model and equal-DP reference:\n{details}" + ) + + +class _BatchIterator: + """Minimal iterator over a pre-generated list of batches.""" + + def __init__(self, batches): + self.batches = batches + self.idx = 0 + + def __iter__(self): + return self + + def __next__(self): + if self.idx >= len(self.batches): + raise StopIteration + b = self.batches[self.idx] + self.idx += 1 + return b + + +def _run_forward_backward( + mimo_model, + batches, + enc_grid, + llm_grid, + encoder_name, + language_pg, + micro_batch_size, + seq_length, + num_microbatches, +): + """One forward/backward pass through the mimo schedule.""" + return schedule.forward_backward_no_pipelining( + forward_step_func=partial( + forward_step, encoder_grid=enc_grid, llm_grid=llm_grid, encoder_name=encoder_name + ), + data_iterator=_BatchIterator(batches), + model=[mimo_model], + num_microbatches=num_microbatches, + seq_length=seq_length, + micro_batch_size=micro_batch_size, + forward_only=False, + pg_collection=language_pg, + ) + + +class TestColocatedGradientScalingCorrectness: + """Verify heterogeneous-DP encoder grad scaling against an equal-DP reference. + + The critical invariant: with ``calculate_per_token_loss=True`` on both + sub-model configs, DDP's ``gradient_scaling_factor`` is pinned to + 1.0 and each side's DDP reduction is a pure SUM. The custom + ``finalize_grads_func`` then divides both encoder and LLM grads by + ``1/N_global`` (true global valid-token count), so the aggregate + gradient on every encoder shard equals the DP=1 per-token-mean + gradient. The reference uses the same encoder TP/DP as dist but with + ``enc_tp == llm_tp`` and ``enc_dp == llm_dp`` (identity bridge), so + after one Adam step the dist model's sharded weights match the ref + model's sharded weights within fp32 precision. + + If the scaling were wrong (e.g., if either DDP applied its default + ``1/dp_size`` on top of the per-token mean, or if the custom finalize + used the encoder DP group's sum-of-local-counts instead of the + globally lifted ``N_global``), the encoder's reduced grad would be + skewed and post-step weights would diverge — a single optimizer step + is sufficient to detect. + """ + + @classmethod + def setup_class(cls): + Utils.initialize_distributed() + cls.world_size = dist.get_world_size() + + @classmethod + def teardown_class(cls): + Utils.destroy_model_parallel() + + def setup_method(self): + # Track MimoModels built by the test so teardown can release any + # ColocatedBridgeCommunicator subgroups before destroy_all_grids. + self._mimo_models = [] + + def teardown_method(self): + torch.use_deterministic_algorithms(False) + for model in self._mimo_models: + model.destroy() + self._mimo_models.clear() + destroy_all_grids() + + @pytest.mark.skipif( + version.parse(torch.__version__) < version.parse("2.3.0"), reason="Requires PyTorch 2.3+" + ) + @pytest.mark.parametrize( + "enc_tp,enc_dp,llm_tp,llm_dp", [(2, 4, 4, 2), (4, 2, 2, 4)], ids=["fan_in", "fan_out"] + ) + @pytest.mark.parametrize( + "mask_pattern", ["uniform", "asymmetric"], ids=["uniform", "asymmetric"] + ) + @pytest.mark.parametrize("num_microbatches", [1, 4], ids=["mbs1", "mbs4"]) + def test_dist_matches_dp1_reference_post_step_weights( + self, enc_tp, enc_dp, llm_tp, llm_dp, mask_pattern, num_microbatches + ): + """Heterogeneous-DP dist post-step encoder weights match equal-DP reference. + + Builds two MimoModels on every rank: + + * Dist: the heterogeneous TP/DP config under test, with + ``calculate_per_token_loss=True`` + custom finalize hook that + pure-SUMs DDP and externally divides by ``N_global``. + * Ref: equal-DP uniform with ``enc_tp=dist_enc_tp``, + ``enc_dp=dist_enc_dp``, ``llm_tp=dist_enc_tp``, + ``llm_dp=dist_enc_dp`` — bridge is + ``BridgeDirection.EQUAL`` (identity passthrough), and the + encoder TP sharding matches dist's exactly so shards line up + 1:1 for comparison. + + Both models run the same finalize wiring; both DDPs pure-SUM + across their own DP group, then divide uniformly by ``N_global``. + LLM TP differs between the two models, which introduces fp32 TP + accumulation-order drift in the gradient flowing back to the + encoder but does not change the per-token-mean invariant that the + post-step encoder oracle checks. + + Reference weights are copied into the distributed model so both + start from identical state. One Adam step later, the dist shards + should match the ref shards within fp32 precision. + """ + if self.world_size != 8: + pytest.skip(f"Requires 8 GPUs, got {self.world_size}") + + _set_deterministic_env() + torch.use_deterministic_algorithms(True) + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + + encoder_name = "images" + hidden_size, seq_length, vocab_size = 256, 64, 1000 + micro_batch_size = 2 + + # Global batch spans the larger DP side; dist pre-slices per rank + # before forward_step (which further slices encoder/LLM side). + global_batch_size = micro_batch_size * max(enc_dp, llm_dp) + + # Grids: dist is heterogeneous; ref is equal-DP uniform matching + # dist's encoder so the bridge is identity and encoder shards + # align 1:1 for direct comparison. + dist_enc_grid = create_hypercomm_grid(offset=0, tp=enc_tp, cp=1, pp=1, dp=enc_dp) + dist_llm_grid = create_hypercomm_grid(offset=0, tp=llm_tp, cp=1, pp=1, dp=llm_dp) + ref_enc_grid = create_hypercomm_grid(offset=0, tp=enc_tp, cp=1, pp=1, dp=enc_dp) + ref_llm_grid = create_hypercomm_grid(offset=0, tp=enc_tp, cp=1, pp=1, dp=enc_dp) + create_all_embedding_groups([dist_enc_grid, dist_llm_grid, ref_enc_grid, ref_llm_grid]) + + # Both sub-model TransformerConfigs set calculate_per_token_loss=True + # (via per_token_loss=True on get_mimo_model), which pins DDP's + # gradient_scaling_factor to 1.0 — pure SUM across DP on both sides. + # Under the 3-tuple loss_func + custom finalize_grads_func in + # _wire_training_hooks, grads are divided uniformly by N_global, + # which is the true global per-token mean on every shard. + ddp_config = DistributedDataParallelConfig( + overlap_grad_reduce=True, bucket_size=10000, use_distributed_optimizer=True + ) + + # Build dist first (heterogeneous TP/DP). + torch.manual_seed(12345) + dist_mimo, _, _, dist_language_pg, dist_vision_pg = get_mimo_model( + encoder_name=encoder_name, + encoder_grid=dist_enc_grid, + llm_grid=dist_llm_grid, + hidden_size=hidden_size, + num_layers=2, + vocab_size=vocab_size, + seq_len=seq_length, + ddp_config=ddp_config, + bf16=False, + bias=False, + dropout=False, + per_token_loss=True, + ) + dist_mimo.model_type = ModelType.encoder_or_decoder + self._mimo_models.append(dist_mimo) + + # Reference with equal-DP uniform (enc_tp == llm_tp, enc_dp == llm_dp). + torch.manual_seed(12345) + ref_mimo, _, _, ref_language_pg, ref_vision_pg = get_mimo_model( + encoder_name=encoder_name, + encoder_grid=ref_enc_grid, + llm_grid=ref_llm_grid, + hidden_size=hidden_size, + num_layers=2, + vocab_size=vocab_size, + seq_len=seq_length, + ddp_config=ddp_config, + bf16=False, + bias=False, + dropout=False, + per_token_loss=True, + ) + ref_mimo.model_type = ModelType.encoder_or_decoder + self._mimo_models.append(ref_mimo) + + # Force identical initial state: encoder shards already match + # (same TP layout), so the helper copies shard-to-shard. LLM + # shards don't match (ref_llm_tp=enc_tp, dist_llm_tp=llm_tp), so + # the helper all-gathers ref's shards across ref's TP group and + # re-slices for dist's TP group. + _copy_ref_params_to_dist( + ref_mimo.modality_submodules[encoder_name].module, + dist_mimo.modality_submodules[encoder_name].module, + ref_enc_grid.get_pg("tp"), + dist_enc_grid.get_pg("tp"), + ) + _copy_ref_params_to_dist( + ref_mimo.language_model.module, + dist_mimo.language_model.module, + ref_llm_grid.get_pg("tp"), + dist_llm_grid.get_pg("tp"), + ) + + _wire_training_hooks(dist_mimo, dist_language_pg, dist_vision_pg) + _wire_training_hooks(ref_mimo, ref_language_pg, ref_vision_pg) + + # Distributed optimizers snapshot current param.data into fp32 master + # weights at __init__, so both must be built AFTER the ref-to-dist + # param copy above. + opt_config = OptimizerConfig( + optimizer='adam', + lr=1e-4, + weight_decay=0.01, + clip_grad=1.0, + bf16=False, + use_distributed_optimizer=True, + ) + dist_optimizer = get_mimo_optimizer(dist_mimo, opt_config) + ref_optimizer = get_mimo_optimizer(ref_mimo, opt_config) + + # Data: one deterministic global batch, identical on every rank. + torch.manual_seed(99999) + global_batches = _generate_and_broadcast_global_batches( + global_mbs=global_batch_size, + seq_length=seq_length, + hidden_size=hidden_size, + vocab_size=vocab_size, + encoder_name=encoder_name, + num_batches=num_microbatches, + mask_pattern=mask_pattern, + ) + dist_batches = [ + _slice_global_batch_for_dist(b, dist_enc_grid, dist_llm_grid) for b in global_batches + ] + # Ref is uniform (enc_dp == llm_dp), so _slice_global_batch_for_dist + # returns the full batch; slice explicitly by enc_dp so each rank + # sees the same per-rank batch size as dist's encoder does. + ref_batches = [ + _slice_global_batch_by_dp(b, ref_enc_grid.get_pg("dp")) for b in global_batches + ] + ref_per_rank_batch_size = global_batch_size // enc_dp + + # Logits capture: hook fires on every microbatch forward. + # Registered before forward/backward, removed right after so the + # hook doesn't leak across the second model's run. + dist_logits, dist_logits_hook = _register_logits_capture(dist_mimo) + ref_logits, ref_logits_hook = _register_logits_capture(ref_mimo) + dist_llm_input, dist_input_hook = _register_llm_input_capture(dist_mimo) + ref_llm_input, ref_input_hook = _register_llm_input_capture(ref_mimo) + + try: + # One optimizer step on dist (heterogeneous forward_step slicing). + dist_optimizer.zero_grad() + _run_forward_backward( + mimo_model=dist_mimo, + batches=dist_batches, + enc_grid=dist_enc_grid, + llm_grid=dist_llm_grid, + encoder_name=encoder_name, + language_pg=dist_language_pg, + micro_batch_size=micro_batch_size, + seq_length=seq_length, + num_microbatches=num_microbatches, + ) + # Snapshot encoder first-layer grads AFTER backward and BEFORE + # optimizer.step() consumes/zeros the grad buffer. + dist_first_layer_grads = _snapshot_first_layer_encoder_grads(dist_mimo, encoder_name) + dist_success, dist_grad_norm, _ = dist_optimizer.step() + assert dist_success, "Dist optimizer step failed" + assert dist_grad_norm is not None and dist_grad_norm > 0, ( + f"Dist grad_norm={dist_grad_norm} — encoder grads may have been " + "silently zeroed by wrong scaling" + ) + + # One optimizer step on ref (enc_dp == llm_dp → forward_step skips slicing). + ref_optimizer.zero_grad() + _run_forward_backward( + mimo_model=ref_mimo, + batches=ref_batches, + enc_grid=ref_enc_grid, + llm_grid=ref_llm_grid, + encoder_name=encoder_name, + language_pg=ref_language_pg, + micro_batch_size=ref_per_rank_batch_size, + seq_length=seq_length, + num_microbatches=num_microbatches, + ) + ref_first_layer_grads = _snapshot_first_layer_encoder_grads(ref_mimo, encoder_name) + ref_success, ref_grad_norm, _ = ref_optimizer.step() + assert ref_success, "Ref optimizer step failed" + assert ref_grad_norm is not None and ref_grad_norm > 0, f"Ref grad_norm={ref_grad_norm}" + finally: + dist_logits_hook.remove() + ref_logits_hook.remove() + dist_input_hook.remove() + ref_input_hook.remove() + + # Run all three oracles regardless of individual failures so the + # diff-stats print covers every layer. Order: encoder weights / + # first-layer grads first (tightest — same encoder TP/DP layout + # → shards align 1:1), then LLM logits last (loosest — different + # LLM TP layout drives fp32 accumulation drift). Each oracle + # printed its own min/mean/p95/p99/max before its assertion ran, + # so the user sees the full drift distribution for every test. + failures = [] + + try: + _assert_encoder_weights_match( + ref_mimo.modality_submodules[encoder_name].module, + dist_mimo.modality_submodules[encoder_name].module, + rtol=1e-3, + atol=1e-3, + ) + except AssertionError as e: + failures.append(('encoder_weights', str(e))) + + try: + _assert_first_layer_grads_match( + ref_first_layer_grads, dist_first_layer_grads, rtol=1e-3, atol=1e-3 + ) + except AssertionError as e: + failures.append(('first_layer_grads', str(e))) + + try: + _assert_llm_input_match( + ref_llm_input, dist_llm_input, ref_llm_grid, dist_llm_grid, rtol=1e-3, atol=1e-3 + ) + except AssertionError as e: + failures.append(('llm_input', str(e))) + + try: + _assert_llm_logits_match( + ref_logits, dist_logits, ref_llm_grid, dist_llm_grid, rtol=1e-2, atol=1e-2 + ) + except AssertionError as e: + failures.append(('llm_logits', str(e))) + + if failures: + summary = "\n\n".join(f"== {oracle} ==\n{msg}" for oracle, msg in failures) + raise AssertionError(f"{len(failures)} oracle(s) failed:\n{summary}") diff --git a/tests/unit_tests/models/test_mimo_model.py b/tests/unit_tests/models/test_mimo_model.py index e1c4b6e89bf..0ef62ff570f 100644 --- a/tests/unit_tests/models/test_mimo_model.py +++ b/tests/unit_tests/models/test_mimo_model.py @@ -528,15 +528,13 @@ def test_grid_validation_rejects_mismatched_keys(self): self.hidden_size, self.img_h, self.img_w, self.patch_dim ) - mimo_config = MimoModelConfig( - language_model_spec=language_model_spec, - modality_submodules_spec={"images": vision_submodule_spec}, - special_token_ids={"images": 50257}, - module_to_grid_map={MIMO_LANGUAGE_MODULE_KEY: MockGrid()}, - ) - with pytest.raises(ValueError, match="module_to_grid_map keys must match"): - MimoModel(mimo_config) + MimoModelConfig( + language_model_spec=language_model_spec, + modality_submodules_spec={"images": vision_submodule_spec}, + special_token_ids={"images": 50257}, + module_to_grid_map={MIMO_LANGUAGE_MODULE_KEY: MockGrid()}, + ) def test_role_determination(self): """Test role correctly identifies modules and stage positions.""" @@ -550,7 +548,7 @@ def test_role_determination(self): self.patch_dim, {"images": 50257}, ) - assert model_no_grid.role.mode == ModuleLayout.UNIFIED + assert model_no_grid.role.mode == ModuleLayout.COLOCATED assert model_no_grid.role.has_language_module is True assert model_no_grid.role.has_modality_modules is True @@ -564,12 +562,15 @@ def test_role_determination(self): assert model_language.role.has_modality_modules is False assert model_language.role.has_language_module is True - # Stage info with PP + # Stage info with PP. language_in_grid=False so encoder and language + # grids have distinct rank_offsets and role.build dispatches to + # _from_grid_map (rather than collapsing to the COLOCATED path). model_pp = MimoModel( - self._make_config(encoder_in_grid=True, language_in_grid=True, pp_rank=1, pp_size=3) + self._make_config(encoder_in_grid=True, language_in_grid=False, pp_rank=1, pp_size=3) ) assert model_pp.role.is_first_stage("images") is False assert model_pp.role.is_last_stage("images") is False + assert model_pp.colocated_comms == {} def test_selective_init_encoder_only(self): """Test encoder-only rank initializes encoder but not language model.""" From 6fd6652af5158bf5899372b9b9078411e060b396 Mon Sep 17 00:00:00 2001 From: HelloWorldBeginner <294810125@qq.com> Date: Tue, 28 Apr 2026 13:51:03 +0800 Subject: [PATCH 037/105] Fix incorrect bias display in extra_repr of Column/RowParallelLinear (#4330) Co-authored-by: mhh111 Co-authored-by: Antoni-Joan Solergibert --- megatron/core/tensor_parallel/layers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/megatron/core/tensor_parallel/layers.py b/megatron/core/tensor_parallel/layers.py index 4ab2aa0f639..225cdb3b465 100644 --- a/megatron/core/tensor_parallel/layers.py +++ b/megatron/core/tensor_parallel/layers.py @@ -1119,7 +1119,7 @@ def get_extra_state(self) -> None: def extra_repr(self) -> str: """Extra context to add to the module's string representation.""" tp = self.output_size // self.output_size_per_partition - use_bias = self.bias is not None and self.bias is True + use_bias = self.bias is not None return ( f"in_features={self.input_size}, " f"out_features={self.output_size}, " @@ -1381,7 +1381,7 @@ def get_extra_state(self) -> None: def extra_repr(self) -> str: """Extra context to add to the module's string representation.""" tp = self.input_size // self.input_size_per_partition - use_bias = self.bias is not None and self.bias is True + use_bias = self.bias is not None return ( f"in_features={self.input_size}, " f"out_features={self.output_size}, " From c8a4bfd8df219de19cfb291fbb151f8c59cc8eed Mon Sep 17 00:00:00 2001 From: Hanpeng Date: Tue, 28 Apr 2026 14:23:45 +0800 Subject: [PATCH 038/105] Fix assertion logic in combined_1f1b_schedule_for_interleaved_pipelining (#4276) Co-authored-by: Hanpeng Hu Co-authored-by: Deepak Narayanan Co-authored-by: Antoni-Joan Solergibert --- megatron/core/pipeline_parallel/combined_1f1b.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/megatron/core/pipeline_parallel/combined_1f1b.py b/megatron/core/pipeline_parallel/combined_1f1b.py index fdd3b32201f..f4f222ad2a1 100644 --- a/megatron/core/pipeline_parallel/combined_1f1b.py +++ b/megatron/core/pipeline_parallel/combined_1f1b.py @@ -231,10 +231,15 @@ def combined_1f1b_schedule_for_interleaved_pipelining(): if f_model_chunk_id is not None: forward_step_helper_postprocess(f_model_chunk_id, output_tensor, num_tokens) # backward post process - if b_model_chunk_id: + if b_model_chunk_id is not None: # The same as the backward_step_helper backward_step_helper_postprocess(b_virtual_microbatch_id) - if input_tensor is not None: + # Verify backward grad: if backward microbatch received activation from upstream + # (b_input_tensor is not None), input_tensor_grad must be produced. + # Note: the original assert used forward's input_tensor, which is incorrect when + # forward and backward are on different VP stages (backward has chunk reversal: + # model_chunk_id = num_chunks - id - 1), causing false failures in interleaved PP. + if b_input_tensor is not None: assert input_tensor_grad is not None return output_tensor, input_tensor_grad From 374fa85b446671bc74c7ea3ed5c6132f69c74e18 Mon Sep 17 00:00:00 2001 From: Ajay <11775318+balasaajay@users.noreply.github.com> Date: Tue, 28 Apr 2026 01:55:03 -0700 Subject: [PATCH 039/105] ci: Fix event name reference in CI workflow condition for merge group (#4462) --- .github/workflows/cicd-main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd-main.yml b/.github/workflows/cicd-main.yml index e5bf1ac116c..9e5350b96c3 100644 --- a/.github/workflows/cicd-main.yml +++ b/.github/workflows/cicd-main.yml @@ -972,7 +972,7 @@ jobs: ( needs.pre-flight.outputs.docs_only == 'true' || needs.pre-flight.outputs.is_deployment_workflow == 'true' - || github.event == 'merge_group' + || github.event_name == 'merge_group' ) && needs.pre-flight.outputs.is_ci_workload == 'false' && !cancelled() From 9c152905ec9323727dd74b375649a2361dd844f9 Mon Sep 17 00:00:00 2001 From: Philip Petrakian Date: Tue, 28 Apr 2026 03:10:03 -0700 Subject: [PATCH 040/105] Add manual sync workflow from main to dev (#4165) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: oliver könig Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: Philip Petrakian <> Co-authored-by: oliver könig --- .../workflows/nightly-sync-main-to-dev.yml | 196 ++++++ skills/nightly-sync/SKILL.md | 563 ++++++++++++++++++ 2 files changed, 759 insertions(+) create mode 100644 .github/workflows/nightly-sync-main-to-dev.yml create mode 100644 skills/nightly-sync/SKILL.md diff --git a/.github/workflows/nightly-sync-main-to-dev.yml b/.github/workflows/nightly-sync-main-to-dev.yml new file mode 100644 index 00000000000..333f6dbfbd3 --- /dev/null +++ b/.github/workflows/nightly-sync-main-to-dev.yml @@ -0,0 +1,196 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Nightly Sync Main to Dev + +on: + workflow_dispatch: + schedule: + # 21:00 UTC = 2 PM PDT (1 PM PST during winter — GitHub Actions cron + # is UTC-only and does not follow DST). + - cron: '0 21 * * *' + +concurrency: + group: nightly-sync-main-to-dev + cancel-in-progress: false + +permissions: + contents: write + pull-requests: write + issues: write + id-token: write + +jobs: + sync-main-to-dev: + runs-on: ubuntu-latest + if: github.repository == 'NVIDIA/Megatron-LM' + timeout-minutes: 360 + env: + GH_TOKEN: ${{ secrets.PAT }} + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + token: ${{ secrets.PAT }} + + - name: Configure Git + run: | + git config user.name "svcnvidia-nemo-ci" + git config user.email "svcnvidia-nemo-ci@nvidia.com" + + - name: Compute branch name + id: vars + run: | + DATE=$(date -u +%d_%m_%Y) + BRANCH="main2dev/${DATE}" + echo "branch=$BRANCH" >> "$GITHUB_OUTPUT" + echo "date=$DATE" >> "$GITHUB_OUTPUT" + + - name: Close previous unmerged sync PRs + run: | + OPEN_PRS=$(gh pr list \ + --repo "${{ github.repository }}" \ + --base dev \ + --state open \ + --json number,headRefName \ + --jq '.[] | select(.headRefName | startswith("main2dev/")) | .number') + + for PR_NUM in $OPEN_PRS; do + echo "Closing stale sync PR #${PR_NUM}" + gh pr close "$PR_NUM" \ + --repo "${{ github.repository }}" \ + --comment "Superseded by today's nightly sync." + done + + - name: Check if sync is needed + id: check-sync + run: | + git fetch origin main dev + AHEAD_COUNT=$(git rev-list --count origin/dev..origin/main) + echo "main is $AHEAD_COUNT commit(s) ahead of dev" + if [ "$AHEAD_COUNT" -eq 0 ]; then + echo "skip=true" >> "$GITHUB_OUTPUT" + echo "No changes to sync." + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + - name: Run Claude Code to merge, fix, and iterate + if: steps.check-sync.outputs.skip != 'true' + uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + github_token: ${{ secrets.PAT }} + prompt: | + You are an automated sync bot. Merge `main` into `dev`, create a + PR, ensure CI passes (fixing failures), and mark the PR ready. + There are 4 phases. You are NOT done until Phase 4 completes. + + REPO: ${{ github.repository }} + BRANCH: ${{ steps.vars.outputs.branch }} + DATE: ${{ steps.vars.outputs.date }} + + Read `.claude/skills/nightly-sync/SKILL.md` for the detailed + merge strategy, CI architecture, failure investigation procedures, + and known issues. Also read `.claude/skills/build-and-test/SKILL.md` + and `CLAUDE.md` for general CI and contribution guidelines. + + ## Hard Constraints + + **Exit condition:** You MUST run `gh pr ready ` before + exiting. That command is Phase 4. Do NOT exit after Phase 1, 2, + or 3 — not even if CI is "still running" or "stuck in queue." + Keep polling until it resolves, then act. + + **NO background tasks. Ever.** + You are running inside a single GitHub Actions step. The step + process owns your shell. When you stop issuing tool calls, the + step ends and the runner container is DESTROYED — every + background process dies with it and cannot resume. There is no + "future session" to wake up into. + + The following are strictly forbidden: + - `Bash` with `run_in_background: true` + - `Agent` with `run_in_background: true` + - `ScheduleWakeup` (nothing will ever wake up) + - Any shell command ending in `&`, or using `nohup`, `disown`, + or `setsid` to detach a process + - `tail -f` on a log produced by a backgrounded task + + Required shape for every long wait: ONE foreground Bash tool + call containing an inline `while true; do ... sleep ; done` + or `until ...; do sleep ; done` loop that BLOCKS inside + that single tool call and only returns when the wait is + resolved (success, failure, or a clearly-classified terminal + state). Do NOT break a long wait into many short polls with + conversation in between — that wastes `--max-turns` and + creates windows where the agent could forget the loop. + + **Source of truth for CI status:** + `gh pr view --repo $REPO --json statusCheckRollup` + This lists every required check — GitHub Actions jobs AND + external contexts (GitLab CI, `copy-pr-bot`, etc.). The + `gh api .../actions/runs//jobs` endpoint alone is + NOT sufficient — it misses external contexts. + + **Pre-existing failures:** MUST verify against recent dev CI + before classifying any failure as pre-existing. Run + `gh pr checks` on a recently merged dev PR. If the test passes + on dev, the failure is sync-caused and you must fix it. A + check that has never completed on your PR cannot be + pre-existing — wait for it to finish first. + + **Phase 4 gate — strict "all terminal, all green":** + Do NOT run `gh pr ready` until every non-exempt required check + in `statusCheckRollup` satisfies BOTH: + - `status == "COMPLETED"` (NOT `QUEUED`, `IN_PROGRESS`, + `PENDING`, `WAITING`, or `REQUESTED`), AND + - `conclusion` ∈ {`SUCCESS`, `SKIPPED`, `NEUTRAL`}. + A check stuck in a runner queue is NOT complete. Never + classify queued/in-progress jobs as "infrastructure-blocked" + and ship anyway — wait for them to reach a terminal + conclusion, then act on that result. When a check fails, + loop: diagnose → fix → commit → push → `/ok to test ` → + poll. Only exit the loop when the gate is satisfied on the + LATEST CI run against the current HEAD SHA. + + **Exempt checks (may be ignored for the Phase 4 gate):** + These categories are pre-merge policy signals, not + correctness signals, so their failure must not block the + sync bot from marking the PR ready for human review. + + - Approval / code-review: `codeowners-approval`, + `check-approval`, `multi-approval-bot-summary`, + `is-not-external-contributor`, any check whose name + contains `review` or `approval`. + - Code coverage: `Coverage (unit-test)`, `Coverage_Fake`, + any check whose name contains `codecov` or `coverage` + (case-insensitive). + - Docs: `build-docs / Build docs`, `build-docs-summary`, + any check whose name contains `build-docs`, `doc-build`, + `readthedocs`, or `sphinx`. + + Everything else — unit tests (`tests/unit_tests/...`), + integration tests (`gpt/...`, `moe/...`, etc.), `linting`, + `cicd-container-build`, `cicd-mbridge-testing`, + `Nemo_CICD_Test`, `copyright-check`, `pre-flight`, wheel + builds, etc. — is NOT exempt and must reach a terminal + green conclusion. + show_full_output: true + claude_args: | + --allowedTools "Bash,Read,Edit,Write,Grep,Glob,Agent" + --model "opus[1m]" + --effort max + --max-turns 1500 diff --git a/skills/nightly-sync/SKILL.md b/skills/nightly-sync/SKILL.md new file mode 100644 index 00000000000..aed24c8fc8d --- /dev/null +++ b/skills/nightly-sync/SKILL.md @@ -0,0 +1,563 @@ +--- +name: nightly-sync +description: Domain knowledge for the nightly main-to-dev sync workflow. Covers merge strategy, CI architecture, failure investigation, and known issues. +--- + +# Nightly Sync: Main to Dev + +This skill is read by the automated sync bot during the nightly-sync-main-to-dev +workflow. It contains all domain knowledge for merging main into dev, resolving +conflicts, iterating on CI, and shipping the PR. + +--- + +## Phase 1: Create the Sync Branch and Merge + +### Branch Setup + +1. Create branch `$BRANCH` from `origin/dev` +2. Merge: `git merge origin/main -X theirs --no-edit` +3. If conflicts remain (e.g. add/add), resolve by favoring main + +### Preserving Dev-Only Additions + +Do NOT blanket-override all shared files with main's version. Dev has features +not yet in main (new classes, new modules, new tests). The merge preserves both +sides' non-conflicting additions — only intervene where there is an actual +conflict. + +### Squash-Merge Chain Detection + +Dev often develops features as a chain of PRs (PR1 → PR2 → PR3) where each +builds on the last. When PR1 is squash-merged to main, git sees main's squashed +version and dev's original commits as unrelated changes. `-X theirs` will pick +main's PR1 code and silently discard PR2/PR3's improvements on dev. + +After the merge, check for this pattern: + +1. For each file where `-X theirs` resolved a conflict, run + `git log --oneline origin/dev -- ` to see if dev has commits that + came AFTER the code main is bringing in. +2. If dev has follow-up commits (bug fixes, refactors, extensions), **favor + dev's version** for those sections. +3. If the conflict is just main bringing in a clean copy of what dev already + has (no follow-ups), main's version is fine. + +Practical check: run `git diff origin/dev -- ` on conflicted files. If +dev's code was removed or reverted, investigate whether dev's version is the +more evolved one. + +Real examples from PR #4291: +- `emerging_optimizers.py`: Main's version was MORE complete — it squash-merged + dev's PRs plus added more. `-X theirs` was correct. +- `distrib_optimizer.py`: Main overwrote dev's `GroupedQuantizedTensor` support. + Had to restore `_is_distopt_quantized_param` and the expanded + `_expand_quantized_param_shard_for_cast` loop while keeping main's NVFP4 + additions. This required a surgical merge combining sections from both. + +Key insight: squash-merge chains can go in EITHER direction. Sometimes main +is ahead (it squash-merged dev's work + more), sometimes dev is ahead (it has +follow-up PRs). Always diff both ways before deciding which version to favor. + +### Files to Override from Main + +These files have known semantic conflicts where dev's versions reference args +or APIs that main removed or renamed. Take main's version with +`git checkout origin/main -- `: + +- `megatron/training/training.py` — references dev-only args +- `megatron/training/initialize.py` — references dev-only args +- `megatron/training/utils.py` — references dev-only args +- `megatron/training/datasets/data_samplers.py` — references dev-only args +- `megatron/core/optimizer/layer_wise_optimizer.py` — constructor signature + +**Caveat for ALL overrides:** After taking main's version of any file, you +MUST run the API Mismatch Detection procedure (see below) on that file. +Taking main's caller code while keeping dev's callee implementations is the +#1 source of sync bugs. + +**IMPORTANT: Do NOT take main's `pyproject.toml`, `uv.lock`, or +`docker/Dockerfile.ci.dev`.** These three files are a tightly coupled +triple — the Dockerfile's `uv sync` command must match the dependency +groups in `pyproject.toml`, and `uv.lock` must be consistent with both. +Main's versions are missing dev-only dependencies (e.g. +`fast-hadamard-transform`, correct TransformerEngine revision) and the +`--group no_pypi_wheels` flag needed to install them. Keep dev's versions +of all three files. + +**IMPORTANT: `.github/CODEOWNERS` must NEVER be modified by the sync +bot under any circumstances.** Dev's CODEOWNERS is intentionally +different from main's — do not take main's version, do not merge them, +do not touch the file. If the merge produces a conflict or a non-zero +diff against `origin/dev` on this path, restore dev's version verbatim: + +``` +git checkout origin/dev -- .github/CODEOWNERS +``` + +Then verify with `git diff origin/dev -- .github/CODEOWNERS` — output +must be empty. Modifying CODEOWNERS triggers spurious reviewer +requests and conflicts with the dev team's governance; rolling back a +CODEOWNERS change after the PR lands is painful. + +**NEVER manually edit `uv.lock`.** It is a machine-generated lockfile. If +it needs to change, it must be regenerated with `uv lock` inside a CUDA +container (see `.claude/skills/build-and-test/SKILL.md`). + +### Git Source Reconciliation (pyproject.toml) + +After keeping dev's `pyproject.toml`, check whether main has added NEW git +sources to `[tool.uv.sources]` that don't exist in dev's version. Main's +merged code may import from packages only available at specific git revisions. + +1. Diff the `[tool.uv.sources]` sections: + `git show origin/main:pyproject.toml` vs `git show origin/dev:pyproject.toml` +2. For each git source in main but not dev, add it to dev's `pyproject.toml` +3. For sources in both but at different revisions, check whether dev's revision + works. If dev's revision is broken (TOML parse errors, missing classes main's + code imports), take main's revision instead. + +Real examples from PR #4291: +- `nvidia-resiliency-ext`: Main's `torch.py` imports `get_write_results_queue` + which only existed in main's pinned git revision, not on PyPI. Had to add + main's git source to dev's pyproject.toml. +- `nemo-run`: Dev's pinned revision had a TOML parse error with uv 0.7.2. + Had to swap to main's revision. + +After any changes to `pyproject.toml`, regenerate `uv.lock` inside a CUDA +container: +```bash +docker run --rm -v $(pwd):/workspace nvcr.io/nvidia/pytorch:26.02-py3 \ + bash -c "pip install uv==0.7.2 && cd /workspace && \ + uv venv .venv --system-site-packages && uv sync --only-group build && uv lock" +# Clean up root-owned .venv: +docker run --rm -v $(pwd):/workspace nvcr.io/nvidia/pytorch:26.02-py3 \ + bash -c "rm -rf /workspace/.venv" +``` + +### API Mismatch Detection (Post-Merge Audit) + +The merge can create "Frankenstein" code where main's callers use dev's +implementations (or vice versa) with different method signatures. This +compiles fine but fails at runtime. + +After the merge, audit cross-boundary call sites: + +1. Identify files where main's version was taken (`-X theirs` or explicit + `git checkout origin/main`) +2. For each, find all external call sites: classes it instantiates, methods + it calls on imported objects, functions from other modules it invokes +3. Verify method names, parameter counts, and signatures match between the + caller and the implementation in the merged tree +4. Pay special attention to "interface" modules (files defining base classes) + — if main and dev evolved the interface differently, every caller and + implementer must agree + +Real examples from PR #4291: +- `multi_latent_attention.py` (main) called `off_interface.group_commit()` + but dev's interface only had `group_offload()` — method renamed +- `mamba_model.py` (main) called `init_chunk_handler(3 params)` but dev's + interface required 6 params — signature expanded on dev +- `mamba_model.py` called `mark_not_offloadable()` but dev had + `mark_not_offload()` — method renamed +- `bulk_offload()` did `.remove()` after `bulk_offload_group()` already + `.pop()`d the same item — double-removal from a list + +Practical detection: +```bash +# For each file taken from main, find what it imports and calls +grep -rn "from import\|\." megatron/ +# Cross-reference with the actual implementations in the merged tree +``` + +### File-Specific Merge Lessons + +These lessons were learned from PR #4291. They may recur if the same files +continue to diverge: + +- `gated_delta_net.py`: If the merge creates code calling non-existent helper + methods (e.g. `_resolve_cu_seqlens`), take dev's version wholesale. +- `model_chunk_schedule_plan.py`: Watch for missing imports (e.g. + `CudaGraphScope`) silently dropped during conflict resolution. +- `fine_grained_activation_offload.py`: Critical interface file used by many + callers. If main and dev have divergent method names/signatures, prefer + dev's implementation and patch main-originated callers to match. +- `distrib_optimizer.py`: Dev may have broader type abstractions (e.g. + `_is_distopt_quantized_param` covering both FP8 and GroupedQuantizedTensor). + Main may simplify to explicit type checks. Restore dev's abstractions. + +### Special Handling: data_schedule.py + +Main and dev have completely different classes in this file: +- Main: `HybridCPDataLoaderWrapper` (imported by main's `training.py`) +- Dev: `BasePackingScheduler`, `DpBalancedScheduler`, + `DefaultDynamicCPScheduler`, `wrap_data_iterator`, + `get_batch_on_this_rank_for_sequence_packing` (imported by `pretrain_gpt.py` + and tests) + +**Do NOT take either version wholesale.** Keep dev's file and append main's +`HybridCPDataLoaderWrapper` class (plus any missing imports like +`BalancedCPScheduler`, `Any`, `List`) at the end. + +### Restore Deleted Files + +Compare `git ls-tree` between `origin/main` and HEAD to find files in main +that are missing from the merged tree. For each: +- **Restore** if main's code imports/references it and would break without it + (e.g. `hybrid_cp_schedule.py` if `data_schedule.py` imports from it) +- **Do NOT restore** if dev intentionally deleted it — check + `git log origin/dev -- ` for the deletion commit to understand intent +- When in doubt, check whether any file in the merged tree imports from the + missing file. If nothing imports it, skip it. + +### Formatting + +Run on ALL changed Python files (relative to `origin/dev`), in this order: + +1. `black` (version 24, `--config pyproject.toml`) +2. `isort` +3. Order matters: black first, then isort — reverse order can undo isort's work +4. `pylint` on changed `megatron/core/` files — fix missing-docstring and + line-too-long violations before pushing + +### Pre-push invariant checks + +Before every `git push` in this workflow (the initial push in Phase 1 +AND every fix-push in Phase 3), run these bash checks. If any fails, +fix the condition and re-check before pushing: + +```bash +# 1. CODEOWNERS must be identical to dev's. +if ! git diff --quiet origin/dev -- .github/CODEOWNERS; then + echo "ABORT: .github/CODEOWNERS differs from origin/dev. Restore with:" + echo " git checkout origin/dev -- .github/CODEOWNERS" + exit 1 +fi + +# 2. Dependency-management triple must be identical to dev's. +for f in pyproject.toml uv.lock docker/Dockerfile.ci.dev; do + if ! git diff --quiet origin/dev -- "$f"; then + # pyproject.toml is allowed to differ ONLY for git source reconciliation + # (new [tool.uv.sources] entries from main). If you intentionally edited + # it for that reason, bypass this check by re-running with $f skipped. + echo "WARNING: $f differs from origin/dev" + fi +done +``` + +The CODEOWNERS check is a HARD abort — never push if it fails. + +### Commit and Push + +After the pre-push invariant checks pass, commit everything and push +the branch. + +--- + +## Phase 2: Create the Draft PR + +- Title: `chore: nightly sync main into dev ($DATE)` +- Create as **draft**: `gh pr create --draft` +- Body should include: + 1. Summary of what was synced (number of commits from main) + 2. **Python-only line-change stats**, so reviewers can gauge the real + code surface (excluding golden-value JSON, uv.lock, etc.). Compute + with: + + ```bash + git diff --numstat origin/dev...HEAD -- '*.py' \ + | awk 'BEGIN{a=0;d=0} {a+=$1; d+=$2} END{ + printf "Python lines: +%d / -%d across %d files\n", a, d, NR + }' + ``` + + Include the exact line (e.g. `Python lines: +1234 / -567 across 42 files`) + in the PR body so reviewers see it at a glance. + 3. List of files where main's version was taken over the merge + 4. List of files that were deleted in dev but restored (and why) + 5. The remerge-diff output (`git show --remerge-diff HEAD` on the merge + commit) so reviewers can inspect ONLY the conflict resolutions. If the + output is very long, summarize conflicts by file and put the full diff + in a collapsed `
` block. If git is too old for `--remerge-diff`, + note the git version and describe the merge strategy used instead. +- Save the PR number for later phases +- **Add the `Run functional tests` label** to the PR immediately after + creation. This ensures `/ok to test` triggers the full CI suite (unit tests + + functional/integration tests with 100-step training and golden value + comparison). Without this label, only a lightweight subset runs. + ```bash + gh pr edit --repo $REPO --add-label "Run functional tests" + ``` + +--- + +## Phase 3: CI Iteration + +### CI Architecture + +- **`Nemo_CICD_Test`** is a downstream gate job aggregating unit test, + integration test, and other results. If it fails, investigate the upstream + jobs it depends on — do NOT debug the gate itself. +- **Integration tests** (H100, GB200) may be skipped for non-maintainer PRs. + This is expected; the `Nemo_CICD_Test` gate will fail as a result. +- **`tests/unit_tests/conftest.py`** imports from `megatron.training.training`, + so a broken import in `training.py` (or anything it transitively imports) + cascades to fail ALL test suites. If every test job fails with ImportError, + check the training.py import chain first. + +### Execution model: one step, no background + +You run inside ONE GitHub Actions step. The moment you stop emitting +tool calls, the step ends and the runner container is destroyed. Any +background process you started dies with it. There is NO persistent +session and NO future wakeup. See the workflow prompt's "NO background +tasks" block for the full ban list. + +Practical rule: every wait for CI to resolve is a SINGLE foreground Bash +tool call that blocks inline until the wait is resolved. + +### The Fix-Then-Retrigger Loop + +Two nested loops. Do NOT conflate them: + +- The **outer loop** is YOUR sequence of tool calls (each iteration: one + `/ok to test`, one blocking poll, maybe one fix-and-push). It is NOT a + Bash loop. It advances because you make new tool calls. +- The **inner loop** is a single blocking Bash tool call using + `while true; do ... sleep 120; done`. It runs during one iteration of + the outer loop and ends when CI reaches a terminal state for that + iteration. + +The outer loop terminates ONLY when Phase 4's gate is satisfied. + +**Source of truth:** `gh pr view --repo $REPO --json statusCheckRollup`. +This lists every required check, including external status contexts +(GitLab CI, `copy-pr-bot`, etc.) that `gh api .../actions/runs/.../jobs` +does NOT show. + +**Outer-loop iteration (each iteration is a few tool calls):** + +1. `latest_sha=$(git rev-parse HEAD)` (one Bash call). +2. Post `/ok to test $latest_sha` on the PR: + `gh pr comment --repo $REPO --body "/ok to test $latest_sha"` +3. ONE blocking Bash tool call. This is the inner loop. Copy this + template verbatim, only changing `REPO` and `PR`: + + ```bash + REPO='NVIDIA/Megatron-LM' + PR='' + # Names matched case-insensitively, anchored to the START of the name. + EXEMPT='copy-pr-bot|is-not-external-contributor|greptile|coderabbit|codeowners|.*review|.*approval|codecov|coverage|build-docs|doc-build|readthedocs|sphinx' + # Sentinel check that tells us CI has fully run. Update this if the + # aggregate gate job is renamed. + SENTINEL='Nemo_CICD_Test' + + while true; do + # Normalize both CheckRun (.status / .conclusion) and StatusContext + # (.state) entries into the same {name, status, conclusion} shape. + rollup=$(gh pr view "$PR" --repo "$REPO" --json statusCheckRollup --jq ' + .statusCheckRollup[] | [ + (.name // .context // "?"), + (if .__typename == "StatusContext" then + (if (.state == "PENDING" or .state == "EXPECTED") then "IN_PROGRESS" + else "COMPLETED" end) + else (.status // "UNKNOWN") end), + (if .__typename == "StatusContext" then + (if .state == "SUCCESS" then "SUCCESS" + elif (.state == "FAILURE" or .state == "ERROR") then "FAILURE" + else "NEUTRAL" end) + else (.conclusion // "UNKNOWN") end) + ] | @tsv') + + # Sentinel: do NOT declare green until the CI aggregate gate has + # reached a terminal state. Before /ok to test triggers the run, + # the sentinel is absent; while CI is running, it's IN_PROGRESS. + sentinel_line=$(printf '%s\n' "$rollup" | awk -F'\t' -v s="$SENTINEL" '$1 == s') + sentinel_status=$(printf '%s\n' "$sentinel_line" | awk -F'\t' 'NR==1 {print $2}') + if [ "$sentinel_status" != "COMPLETED" ]; then + echo "=== $(date -u) waiting for $SENTINEL (status: ${sentinel_status:-absent}) ===" + sleep 120 + continue + fi + + # Classify non-exempt checks (exempt list applied to the NAME only). + non_exempt=$(printf '%s\n' "$rollup" | awk -F'\t' -v p="^($EXEMPT)" 'tolower($1) !~ tolower(p)') + failed=$(printf '%s\n' "$non_exempt" | awk -F'\t' '$2 == "COMPLETED" && $3 !~ /^(SUCCESS|SKIPPED|NEUTRAL)$/') + pending=$(printf '%s\n' "$non_exempt" | awk -F'\t' '$2 != "COMPLETED"') + + if [ -n "$failed" ]; then + echo "=== NON-EXEMPT FAILURES ===" + printf '%s\n' "$failed" + echo "RESULT=FAILURE" + exit 0 + fi + if [ -n "$pending" ]; then + # Sentinel is COMPLETED but a non-exempt check is still pending — + # rare but possible. Keep waiting; do NOT ship. + echo "=== $(date -u) sentinel done but non-exempt checks still pending ===" + printf '%s\n' "$pending" + sleep 120 + continue + fi + + echo "=== ALL NON-EXEMPT CHECKS COMPLETED GREEN ===" + printf '%s\n' "$non_exempt" + echo "RESULT=GREEN" + exit 0 + done + ``` + + This Bash call blocks for as long as CI takes (minutes to hours). Do + NOT split it into many short polls interleaved with other tool calls + — that wastes `--max-turns` and creates windows where you could lose + track of the loop state. + +4. Read the tool output: + - If `RESULT=FAILURE`: use `gh api repos/$REPO/actions/jobs//logs` + (or the equivalent for external contexts) to diagnose, fix the code, + commit, push. Then start a NEW outer-loop iteration at step 1 with + the new HEAD SHA. + - If `RESULT=GREEN`: outer loop is done. Proceed to Phase 4. + +**Why not wait-for-run-to-register first?** `gh pr comment` with +`/ok to test ` is handled by `copy-pr-bot`, which takes a few +seconds to trigger the CI run. The `statusCheckRollup` poll in step 3 +will initially show checks in `PENDING` / `QUEUED`; that's fine — the +inner loop treats those as "keep waiting" and will see them advance as +CI progresses. No separate registration poll needed. + +### Anti-Patterns (what went wrong on run 24800621116) + +- **Do NOT classify a queued/in-progress job as "infrastructure- + blocked" and ship.** A stuck queue drains eventually — wait. If the + job eventually passes, great; if it fails, go fix it. +- **Do NOT mark ready while any required check is `PENDING` / + `QUEUED` / `IN_PROGRESS` on the HEAD SHA.** A push is not a pass; + only a `COMPLETED` + green status is. +- **Do NOT declare an untested job "pre-existing."** Pre-existing + means the test ran to completion and failed the same way on recent + dev CI. A job that never ran on your PR cannot be pre-existing. +- **Do NOT use `gh api .../actions/runs/.../jobs` alone** as the gate + signal. External status contexts (GitLab CI pipelines, copy-pr-bot + status, etc.) do NOT appear there. Use `statusCheckRollup`. +- **Do NOT start any background process.** No `&`, no `nohup`, no + `run_in_background: true`, no `ScheduleWakeup`. The GitHub Actions + step owns your shell; when the step ends, every background process + is killed and cannot resume. +- **Do NOT push directly to `pull-request/` branches.** + The community bot manages those branches when it processes + `/ok to test`. Pushing to them directly breaks the CI trigger + mechanism. Always push to your own sync branch (e.g. + `main2dev/`) instead. +- **Do NOT forget the `Run functional tests` label.** Without it, + the internal GitLab functional tests do not run. + +### Failure Investigation + +1. Fetch logs: `gh api repos/$REPO/actions/jobs//logs` +2. Grep for: `ImportError`, `ModuleNotFoundError`, `FAILED`, + `would reformat`, `line-too-long`, `Traceback` +3. Read the error, understand root cause, fix the code + +### Common Issues + +- **ImportError for a class/module:** Dev test imports a class from a file + where we took main's version. Restore only the missing class/function — + not the entire file. If a file's classes are completely different between + main and dev, keep both sets of code. +- **Formatting failures (black/pylint):** Run `black --config pyproject.toml` + on offending files. For pylint long-line or missing-docstring, edit directly. +- **Circular imports:** `isort` can reorder imports in a way that introduces + circular dependencies (e.g. `megatron/legacy/model/__init__.py`). Check + `git diff` on `__init__.py` files to see if import order changed. +- **Dependency version mismatches:** Taking main's `pyproject.toml`/`uv.lock` + can change library versions in the CI container. Dev-only code may depend on + newer versions (e.g. TransformerEngine's `single_grouped_weight`). If failures + trace to missing kwargs or changed APIs in third-party libs, this is the cause. +- **API mismatch (AttributeError / TypeError at runtime):** Main's callers + reference methods that don't exist (or have different signatures) in dev's + implementations. See "API Mismatch Detection" in Phase 1. Fix by adding + shims, renaming methods, or adjusting call signatures. +- **Infrastructure / network failures (apt-get, pip download):** Errors like + `archive.ubuntu.com unreachable` or `Connection timed out` during package + installation are transient CI infrastructure issues, not code problems. + Retry CI with the same SHA. Do not investigate as code failures. + +### Pre-Existing Failure Verification + +**You MUST empirically verify before classifying any failure as pre-existing.** + +1. `gh pr list --repo $REPO --base dev --state merged --limit 3` +2. `gh pr checks --repo $REPO` on a recently merged dev PR +3. If the same test bucket **passes on recent dev CI** → the failure is + sync-caused. You must fix it. +4. Only if the test **also fails on recent dev CI** can you classify it as + pre-existing. Document with the dev PR number and CI run as evidence. + +### Internal GitLab Functional Tests + +GitHub CI covers unit tests and some integration tests. Internal GitLab +(`gitlab-master.nvidia.com`) runs additional functional tests on +H100/GB200 hardware that may reveal issues GitHub CI does not catch. +These surface in `statusCheckRollup` as external status contexts (the +bash template already handles them via the `__typename == "StatusContext"` +branch). + +- Fine-grained activation offloading failures, for example, only showed + up in GitLab functional tests during PR #4291 +- If GitHub CI passes but a reviewer reports GitLab failures, + investigate with the same rigor as GitHub CI failures +- The sync PR should ideally pass both GitHub and GitLab CI before + merge, but GitHub CI passing (i.e. the Phase 4 gate above) is the + minimum before `gh pr ready` + +--- + +## Phase 4: Mark PR Ready — Strict Gate + +Run `gh pr ready` ONLY when every non-exempt required check on the latest +CI run (against the current HEAD SHA) satisfies BOTH: + +1. `status == "completed"` — NOT `queued`, `in_progress`, `pending`, + `waiting`, or `requested`. +2. `conclusion ∈ {"success", "skipped", "neutral"}`. + +If a non-exempt check is pending/queued/in-progress: keep polling; do not +run `gh pr ready`. If it fails: go back to Phase 3's loop. + +The exempt list (approval/coverage/docs) is defined in Phase 3; only those +checks may be ignored. + +A pre-existing failure (same test failing identically on recent dev CI) +may be accepted, but ONLY after it has fully run, been empirically +verified against dev, and documented in the PR body with evidence (dev PR +number + CI run URL). + +``` +gh pr ready --repo $REPO +``` + +Then comment on the PR confirming it is ready for human review. The +comment should include: +- Which non-exempt checks passed (summary from the bash template's + final `ALL NON-EXEMPT CHECKS COMPLETED GREEN` output) +- Any documented pre-existing failures with evidence (dev PR number + + CI run URL showing the same failure on recent dev CI) +- Which files were taken from main vs. merged manually +- Any API mismatches detected and fixed +- Any `pyproject.toml` git source reconciliation performed +- Links to the CI runs that validated the fixes + +--- + +## Rules + +- Prioritize main over dev on genuine conflicts. Preserve dev-only additions + that do not conflict. +- CI triggers via comment: `/ok to test ` +- CI runs appear on branch `pull-request/` +- Git committer identity: `svcnvidia-nemo-ci` +- After editing imports, run `isort` on those files +- **Push directly to NVIDIA/Megatron-LM** (not a fork). The bot uses a PAT + with write access. CLAUDE.md says "never push directly" but that rule is + for human contributors — the sync bot is an exception. From 98161402efde67c2e4f7cbbf6d2cb1b109032a2e Mon Sep 17 00:00:00 2001 From: "Chenhan D. Yu" <5185878+ChenhanYu@users.noreply.github.com> Date: Tue, 28 Apr 2026 10:51:07 -0700 Subject: [PATCH 041/105] fix: handle list-format quant_cfg from ModelOpt PR #1094 (#4187) Signed-off-by: chenhany Signed-off-by: Chenhan Yu Signed-off-by: Jennifer Chen Co-authored-by: Claude Sonnet 4.6 Co-authored-by: Jennifer Chen --- .../post_training/modelopt/convert_model.py | 9 ++- examples/post_training/modelopt/export.py | 9 ++- examples/post_training/modelopt/finetune.py | 8 ++- examples/post_training/modelopt/generate.py | 9 ++- examples/post_training/modelopt/mmlu.py | 9 ++- .../modelopt/offline_feature_extract.py | 8 +-- examples/post_training/modelopt/prune.py | 9 ++- examples/post_training/modelopt/quantize.py | 59 +++++++++---------- examples/post_training/modelopt/validate.py | 9 ++- 9 files changed, 62 insertions(+), 67 deletions(-) diff --git a/examples/post_training/modelopt/convert_model.py b/examples/post_training/modelopt/convert_model.py index 136b0273724..1ecbd3cc5f7 100644 --- a/examples/post_training/modelopt/convert_model.py +++ b/examples/post_training/modelopt/convert_model.py @@ -27,6 +27,7 @@ from megatron.training import get_args from megatron.training.checkpointing import save_checkpoint from megatron.training.initialize import initialize_megatron +from megatron.training.arguments import parse_and_validate_args from megatron.training.utils import print_rank_0, unwrap_model from model_provider import model_provider @@ -102,14 +103,12 @@ def check_arguments(): if __name__ == "__main__": - initialize_megatron( - extra_args_provider=add_convert_args, - args_defaults={ + parse_and_validate_args(extra_args_provider=add_convert_args, args_defaults={ 'tokenizer_type': 'HuggingFaceTokenizer', 'no_load_rng': True, 'no_load_optim': True, - }, - ) + }) + initialize_megatron() check_arguments() args = get_args() diff --git a/examples/post_training/modelopt/export.py b/examples/post_training/modelopt/export.py index 31c72d95eab..d426f578d63 100755 --- a/examples/post_training/modelopt/export.py +++ b/examples/post_training/modelopt/export.py @@ -18,6 +18,7 @@ from megatron.post_training.model_builder import modelopt_gpt_hybrid_builder from megatron.training import get_args, get_model from megatron.training.initialize import initialize_megatron +from megatron.training.arguments import parse_and_validate_args from megatron.training.utils import unwrap_model from model_provider import model_provider @@ -49,14 +50,12 @@ def add_modelopt_export_args(parser): if __name__ == "__main__": - initialize_megatron( - extra_args_provider=add_modelopt_export_args, - args_defaults={ + parse_and_validate_args(extra_args_provider=add_modelopt_export_args, args_defaults={ 'tokenizer_type': 'HuggingFaceTokenizer', 'no_load_rng': True, 'no_load_optim': True, - }, - ) + }) + initialize_megatron() args = get_args() diff --git a/examples/post_training/modelopt/finetune.py b/examples/post_training/modelopt/finetune.py index 2efd3cde6a4..41e82f05ad9 100755 --- a/examples/post_training/modelopt/finetune.py +++ b/examples/post_training/modelopt/finetune.py @@ -484,12 +484,16 @@ def forward_step(data_iterator, model: GPTModel): if __name__ == "__main__": + from megatron.training.arguments import parse_and_validate_args + + parse_and_validate_args( + extra_args_provider=add_finetune_args, + args_defaults={"tokenizer_type": "HuggingFaceTokenizer"}, + ) pretrain( train_valid_test_sft_datasets_provider, partial(model_provider, modelopt_gpt_hybrid_builder), ModelType.encoder_or_decoder, forward_step, - extra_args_provider=add_finetune_args, - args_defaults={"tokenizer_type": "HuggingFaceTokenizer"}, non_loss_data_func=non_loss_data_func, ) diff --git a/examples/post_training/modelopt/generate.py b/examples/post_training/modelopt/generate.py index cc4c4e37a80..de90ee4368a 100644 --- a/examples/post_training/modelopt/generate.py +++ b/examples/post_training/modelopt/generate.py @@ -17,6 +17,7 @@ from megatron.post_training.model_builder import modelopt_gpt_hybrid_builder from megatron.post_training.utils import report_current_memory_info, to_empty_if_meta from megatron.training import get_args, get_model, initialize_megatron +from megatron.training.arguments import parse_and_validate_args from utils import get_hf_tokenizer from megatron.training.utils import print_rank_0, unwrap_model from model_provider import model_provider @@ -73,14 +74,12 @@ def get_conversations(example): if __name__ == "__main__": - initialize_megatron( - extra_args_provider=add_generate_args, - args_defaults={ + parse_and_validate_args(extra_args_provider=add_generate_args, args_defaults={ 'tokenizer_type': 'HuggingFaceTokenizer', 'no_load_rng': True, 'no_load_optim': True, - }, - ) + }) + initialize_megatron() check_arguments() diff --git a/examples/post_training/modelopt/mmlu.py b/examples/post_training/modelopt/mmlu.py index 466d5052b50..61c02178906 100644 --- a/examples/post_training/modelopt/mmlu.py +++ b/examples/post_training/modelopt/mmlu.py @@ -20,6 +20,7 @@ from megatron.post_training.model_builder import modelopt_gpt_hybrid_builder from megatron.post_training.utils import report_current_memory_info from megatron.training import get_args, get_model, initialize_megatron +from megatron.training.arguments import parse_and_validate_args from utils import get_hf_tokenizer from megatron.training.utils import print_rank_0, unwrap_model import modelopt.torch.quantization as mtq @@ -133,14 +134,12 @@ def generate_prompt(test_example, dev_examples, few_shots=0, no_subject_prompt=F if __name__ == "__main__": - initialize_megatron( - extra_args_provider=add_mmlu_args, - args_defaults={ + parse_and_validate_args(extra_args_provider=add_mmlu_args, args_defaults={ 'tokenizer_type': 'HuggingFaceTokenizer', 'no_load_rng': True, 'no_load_optim': True, - }, - ) + }) + initialize_megatron() args = get_args() cache = Cache(args.cache_dir) diff --git a/examples/post_training/modelopt/offline_feature_extract.py b/examples/post_training/modelopt/offline_feature_extract.py index 92500b2950e..f9f59bfd91a 100644 --- a/examples/post_training/modelopt/offline_feature_extract.py +++ b/examples/post_training/modelopt/offline_feature_extract.py @@ -42,14 +42,12 @@ def extract_feature(dataset, model, output_dir, idx_start, idx_end): torch.distributed.barrier() if __name__ == "__main__": - initialize_megatron( - extra_args_provider=add_extract_args, - args_defaults={ + parse_and_validate_args(extra_args_provider=add_extract_args, args_defaults={ 'tokenizer_type': 'HuggingFaceTokenizer', 'no_load_rng': True, 'no_load_optim': True, - }, - ) + }) + initialize_megatron() args = get_args() tokenizer = get_tokenizer() diff --git a/examples/post_training/modelopt/prune.py b/examples/post_training/modelopt/prune.py index 99e351a6198..7dedd19a79a 100644 --- a/examples/post_training/modelopt/prune.py +++ b/examples/post_training/modelopt/prune.py @@ -33,6 +33,7 @@ report_current_memory_info, ) from megatron.training import get_args, get_model, initialize_megatron +from megatron.training.arguments import parse_and_validate_args from utils import get_hf_tokenizer from megatron.training.checkpointing import save_checkpoint from megatron.training.utils import print_rank_0, unwrap_model @@ -149,14 +150,12 @@ def get_params(model): if __name__ == "__main__": - initialize_megatron( - extra_args_provider=add_prune_args, - args_defaults={ + parse_and_validate_args(extra_args_provider=add_prune_args, args_defaults={ "tokenizer_type": "HuggingFaceTokenizer", "no_load_rng": True, "no_load_optim": True, - }, - ) + }) + initialize_megatron() args = get_args() check_arguments(args) diff --git a/examples/post_training/modelopt/quantize.py b/examples/post_training/modelopt/quantize.py index 0c10696df84..62ba81ff3c0 100644 --- a/examples/post_training/modelopt/quantize.py +++ b/examples/post_training/modelopt/quantize.py @@ -45,6 +45,7 @@ report_current_memory_info, ) from megatron.training import get_args, get_model, initialize_megatron +from megatron.training.arguments import parse_and_validate_args from utils import get_hf_tokenizer from megatron.training.checkpointing import save_checkpoint from megatron.training.utils import print_rank_0, unwrap_model @@ -188,13 +189,10 @@ def get_first_layers_disabled_config(config, num_layers: int = 1, num_layers_to_ """ config = copy.deepcopy(config) quant_cfg = config.get("quant_cfg", {}) - quant_cfg.update( - { - functools.partial( - _is_first_layers, num_layers=num_layers, num_layers_to_disable=num_layers_to_disable - ): {"enable": False} - } + predicate = functools.partial( + _is_first_layers, num_layers=num_layers, num_layers_to_disable=num_layers_to_disable ) + quant_cfg.append({"quantizer_name": predicate, "enable": False}) config["quant_cfg"] = quant_cfg return config @@ -206,13 +204,10 @@ def get_last_layers_disabled_config(config, num_layers: int = 1, num_layers_to_d """ config = copy.deepcopy(config) quant_cfg = config.get("quant_cfg", {}) - quant_cfg.update( - { - functools.partial( - _is_last_layers, num_layers=num_layers, num_layers_to_disable=num_layers_to_disable - ): {"enable": False} - } + predicate = functools.partial( + _is_last_layers, num_layers=num_layers, num_layers_to_disable=num_layers_to_disable ) + quant_cfg.append({"quantizer_name": predicate, "enable": False}) config["quant_cfg"] = quant_cfg return config @@ -224,28 +219,34 @@ def get_modelopt_torch_quantization_config(): raise ValueError(f"Unsupported quantization config {args.export_quant_cfg}.") mtq_config = QUANT_CFG_CHOICES[args.export_quant_cfg] - fp8_config = {"enable": True, "num_bits": (4, 3), "axis": None} + if isinstance(mtq_config["quant_cfg"], dict): + # Normalize old dict format to new list format + mtq_config["quant_cfg"] = mtq.normalize_quant_cfg_list(mtq_config["quant_cfg"]) + + fp8_config = {"enable": True, "cfg": {"num_bits": (4, 3), "axis": None}} fp4_config = { - "num_bits": (2, 1), - "block_sizes": {-1: 16, "type": "dynamic", "scale_bits": (4, 3)}, - "axis": None, "enable": True, + "cfg": { + "num_bits": (2, 1), + "block_sizes": {-1: 16, "type": "dynamic", "scale_bits": (4, 3)}, + "axis": None, + }, } if args.export_quant_cfg == "FP8_DEFAULT_CFG": # Enable Medusa heads and kv-cache quantization - mtq_config["quant_cfg"]["*medusa_heads**"] = fp8_config + mtq_config["quant_cfg"].append({"quantizer_name": "*medusa_heads**", **fp8_config}) if "FP4" in args.export_quant_cfg: # Enable Medusa heads and kv-cache quantization - mtq_config["quant_cfg"]["*medusa_heads**"] = fp4_config + mtq_config["quant_cfg"].append({"quantizer_name": "*medusa_heads**", **fp4_config}) if "AWQ" in args.export_quant_cfg: - weight_quantizer = mtq_config["quant_cfg"]["*weight_quantizer"] # type: ignore - if isinstance(weight_quantizer, list): - weight_quantizer = weight_quantizer[0] - weight_quantizer["block_sizes"][-1] = 128 - + try: + weight_quantizer = mtq.find_quant_cfg_entry_by_path(mtq_config["quant_cfg"], "*weight_quantizer") + weight_quantizer["block_sizes"][-1] = 128 + except KeyError: + weight_quantizer = None # Customization if args.disable_qkv_quant: - mtq_config["quant_cfg"]["*self_attention*"] = {"enable": False} + mtq_config["quant_cfg"].append({"quantizer_name": "*self_attention*", "enable": False}) # KV Cache Quantization enable_quant_kv_cache = args.export_kv_cache_quant != "none" @@ -257,7 +258,7 @@ def get_modelopt_torch_quantization_config(): # Weight Only Quantization if args.weight_only: - mtq_config["quant_cfg"]["*input_quantizer"] = {"enable": False} + mtq_config["quant_cfg"].append({"quantizer_name": "*input_quantizer", "enable": False}) if args.num_first_layers_to_skip_quant is not None: mtq_config = get_first_layers_disabled_config( mtq_config, @@ -346,14 +347,12 @@ def get_calib_dataloader( if __name__ == "__main__": - initialize_megatron( - extra_args_provider=add_text_generate_ptq_args, - args_defaults={ + parse_and_validate_args(extra_args_provider=add_text_generate_ptq_args, args_defaults={ "tokenizer_type": "HuggingFaceTokenizer", "no_load_rng": True, "no_load_optim": True, - }, - ) + }) + initialize_megatron() check_arguments() diff --git a/examples/post_training/modelopt/validate.py b/examples/post_training/modelopt/validate.py index 4d9757da00c..2e6816d699a 100644 --- a/examples/post_training/modelopt/validate.py +++ b/examples/post_training/modelopt/validate.py @@ -17,6 +17,7 @@ from megatron.post_training.model_builder import modelopt_gpt_hybrid_builder from megatron.post_training.utils import get_mtbench_chat_data from megatron.training import get_args, get_model, initialize_megatron +from megatron.training.arguments import parse_and_validate_args from utils import get_hf_tokenizer from megatron.training.utils import print_rank_0, unwrap_model from model_provider import model_provider @@ -89,14 +90,12 @@ def report_current_memory_info(): if __name__ == "__main__": - initialize_megatron( - extra_args_provider=add_ar_validation_args, - args_defaults={ + parse_and_validate_args(extra_args_provider=add_ar_validation_args, args_defaults={ 'tokenizer_type': 'HuggingFaceTokenizer', 'no_load_rng': True, 'no_load_optim': True, - }, - ) + }) + initialize_megatron() check_arguments() From 9e982593ea475a841259f6a909378142e2c8b041 Mon Sep 17 00:00:00 2001 From: Philip Petrakian Date: Tue, 28 Apr 2026 11:08:11 -0700 Subject: [PATCH 042/105] ci: also add Run MBridge tests label in nightly sync workflow (#4499) Co-authored-by: Claude Opus 4.7 (1M context) --- skills/nightly-sync/SKILL.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/skills/nightly-sync/SKILL.md b/skills/nightly-sync/SKILL.md index aed24c8fc8d..71c1a20c59e 100644 --- a/skills/nightly-sync/SKILL.md +++ b/skills/nightly-sync/SKILL.md @@ -281,12 +281,16 @@ the branch. in a collapsed `
` block. If git is too old for `--remerge-diff`, note the git version and describe the merge strategy used instead. - Save the PR number for later phases -- **Add the `Run functional tests` label** to the PR immediately after - creation. This ensures `/ok to test` triggers the full CI suite (unit tests - + functional/integration tests with 100-step training and golden value - comparison). Without this label, only a lightweight subset runs. +- **Add the `Run functional tests` and `Run MBridge tests` labels** to the + PR immediately after creation. The `Run functional tests` label ensures + `/ok to test` triggers the full CI suite (unit tests + functional/ + integration tests with 100-step training and golden value comparison). + The `Run MBridge tests` label triggers the MBridge test suite. Without + these labels, only a lightweight subset runs. ```bash - gh pr edit --repo $REPO --add-label "Run functional tests" + gh pr edit --repo $REPO \ + --add-label "Run functional tests" \ + --add-label "Run MBridge tests" ``` --- @@ -449,8 +453,10 @@ CI progresses. No separate registration poll needed. `/ok to test`. Pushing to them directly breaks the CI trigger mechanism. Always push to your own sync branch (e.g. `main2dev/`) instead. -- **Do NOT forget the `Run functional tests` label.** Without it, - the internal GitLab functional tests do not run. +- **Do NOT forget the `Run functional tests` and `Run MBridge tests` + labels.** Without `Run functional tests`, the internal GitLab + functional tests do not run; without `Run MBridge tests`, the + MBridge test suite does not run. ### Failure Investigation From 533dc75f7563a7a02c4de5a1ffb7861999698f4f Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 29 Apr 2026 00:28:19 +0000 Subject: [PATCH 043/105] Update copy-pr-bot.yaml [skip ci] --- .github/copy-pr-bot.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/copy-pr-bot.yaml b/.github/copy-pr-bot.yaml index f72c2fe51e5..39987c91ff8 100644 --- a/.github/copy-pr-bot.yaml +++ b/.github/copy-pr-bot.yaml @@ -1,4 +1,4 @@ enabled: true auto_sync_draft: false auto_sync_ready: true -trustees_override: ["AAnoosheh", "ArEsKay3", "Autumn1998", "BestJuly", "BoxiangW", "CarlosGomes98", "ChenhanYu", "Connor-XY", "FDecaYed", "HaochenYuan", "ISEEKYAN", "JRD971000", "Mellonta", "Phlip79", "QiZhangNV", "RPrenger", "ShriyaRishab", "Victarry", "WanZzzzzz", "Wohox", "YangFei1990", "ZhiyuLi-Nvidia", "ahmadki", "aklife97", "ananthsub", "aroshanghias-nvd", "asolergi-nv", "buptzyb", "chtruong814", "cjld", "cspades", "cuichenx", "deepakn94", "dimapihtar", "dingqingy-nv", "duncanriach", "erhoo82", "ericharper", "fanshiqing", "faradawn", "fitsumreda", "frsun-nvda", "gautham-kollu", "gdengk", "guihong-nv", "guyueh1", "hexinw-nvidia", "huvunvidia", "hxbai", "ilml", "jalbericiola", "janEbert", "jaredcasper", "jenchen13", "jiemingz", "jingqiny-99", "jkamalu", "jon-barker", "jstjohn", "kajalj22", "kanz-nv", "kevalmorabia97", "ko3n1g", "ksivaman", "kunlunl", "kvareddy", "kwyss-nvidia", "layalir", "lhb8125", "lmcafee-nvidia", "maanug-nv", "mathemakitten", "matthieule", "mchrzanowski", "mehraakash", "minitu", "mkhona-nvidia", "nanz-nv", "parthmannan", "prajwal1210", "pthombre", "rhewett-nv", "rogerwaleffe", "sajadn", "sanandaraj5597", "sancha", "santhnm2", "sbak5", "shanmugamr1992", "sharathts", "sheliang-nv", "shengf-nv", "shifangx", "shjwudp", "sidsingh-nvidia", "skyw", "sraman-rgb", "sudhakarsingh27", "tdene", "theothermike", "thomasdhc", "tomlifu", "trintamaki", "tylerpoon", "wdykas", "wplf", "wujingyue", "xiaoyao0115", "xuwchen", "yanring", "yaox12", "yaoyu-33", "yashaswikarnati", "yeyu-nvidia", "yobibyte", "youngeunkwon0405", "yueshen2016", "yuzhongw-nvidia", "zhongbozhu"] +trustees_override: ["AAnoosheh", "ArEsKay3", "Autumn1998", "BestJuly", "BoxiangW", "CarlosGomes98", "ChenhanYu", "Connor-XY", "FDecaYed", "HaochenYuan", "ISEEKYAN", "JRD971000", "Mellonta", "Phlip79", "QiZhangNV", "RPrenger", "ShriyaRishab", "Victarry", "WanZzzzzz", "Wohox", "YangFei1990", "ZhiyuLi-Nvidia", "ahmadki", "aklife97", "ananthsub", "aroshanghias-nvd", "asolergi-nv", "buptzyb", "chtruong814", "cjld", "cspades", "cuichenx", "deepakn94", "dimapihtar", "dingqingy-nv", "duncanriach", "erhoo82", "ericharper", "fanshiqing", "faradawn", "fitsumreda", "frsun-nvda", "gautham-kollu", "gdengk", "guihong-nv", "guyueh1", "hexinw-nvidia", "huvunvidia", "hxbai", "ilml", "jalbericiola", "janEbert", "jaredcasper", "jenchen13", "jiemingz", "jingqiny-99", "jkamalu", "jon-barker", "jstjohn", "kajalj22", "kanz-nv", "kevalmorabia97", "ko3n1g", "ksivaman", "kunlunl", "kvareddy", "kwyss-nvidia", "layalir", "lhb8125", "lmcafee-nvidia", "maanug-nv", "mathemakitten", "matthieule", "mchrzanowski", "mehraakash", "minitu", "mkhona-nvidia", "nanz-nv", "ntajbakhsh", "parthmannan", "prajwal1210", "pthombre", "rhewett-nv", "rogerwaleffe", "sajadn", "sanandaraj5597", "sancha", "santhnm2", "sbak5", "shanmugamr1992", "sharathts", "sheliang-nv", "shengf-nv", "shifangx", "shjwudp", "sidsingh-nvidia", "skyw", "sraman-rgb", "sudhakarsingh27", "tdene", "theothermike", "thomasdhc", "tomlifu", "trintamaki", "tylerpoon", "wdykas", "wplf", "wujingyue", "xiaoyao0115", "xuantengh", "xuwchen", "yanring", "yaox12", "yaoyu-33", "yashaswikarnati", "yeyu-nvidia", "yobibyte", "youngeunkwon0405", "yueshen2016", "yuzhongw-nvidia", "zhongbozhu"] From 1c4e537698b8d8302cd82946191fad7904f70994 Mon Sep 17 00:00:00 2001 From: Maanu Grover Date: Tue, 28 Apr 2026 17:33:55 -0700 Subject: [PATCH 044/105] [training migration] Add serialization features to config container (#4309) Signed-off-by: Maanu Grover --- megatron/training/config/__init__.py | 1 + megatron/training/config/container.py | 209 ++++- megatron/training/config/instantiate_utils.py | 542 +++++++++++ megatron/training/config/utils.py | 105 +++ megatron/training/config/yaml_utils.py | 185 ++++ .../training/config/test_container_base.py | 850 ++++++++++++++++++ .../training/config/test_instantiate_utils.py | 580 ++++++++++++ .../training/config/test_target_allowlist.py | 221 +++++ .../unit_tests/training/config/test_utils.py | 266 ++++++ .../training/config/test_yaml_utils.py | 336 +++++++ 10 files changed, 3293 insertions(+), 2 deletions(-) create mode 100644 megatron/training/config/instantiate_utils.py create mode 100644 megatron/training/config/utils.py create mode 100644 megatron/training/config/yaml_utils.py create mode 100644 tests/unit_tests/training/config/test_container_base.py create mode 100644 tests/unit_tests/training/config/test_instantiate_utils.py create mode 100644 tests/unit_tests/training/config/test_target_allowlist.py create mode 100644 tests/unit_tests/training/config/test_utils.py create mode 100644 tests/unit_tests/training/config/test_yaml_utils.py diff --git a/megatron/training/config/__init__.py b/megatron/training/config/__init__.py index 2d25388afc9..97b13b4883a 100644 --- a/megatron/training/config/__init__.py +++ b/megatron/training/config/__init__.py @@ -18,3 +18,4 @@ ) from megatron.training.config.container import PretrainConfigContainer +from megatron.training.config.instantiate_utils import TargetAllowlist, target_allowlist diff --git a/megatron/training/config/container.py b/megatron/training/config/container.py index 1ac8c4f2531..d22627bbab6 100644 --- a/megatron/training/config/container.py +++ b/megatron/training/config/container.py @@ -1,13 +1,218 @@ # Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. -from dataclasses import dataclass, field +import copy +import os +from dataclasses import dataclass, field, fields as dataclass_fields, is_dataclass +from typing import Any, Type, TypeVar +import yaml +from omegaconf import OmegaConf from megatron.training.config.common_config import RNGConfig, DistributedInitConfig, ProfilingConfig from megatron.training.config.training_config import TrainingConfig, ValidationConfig, SchedulerConfig, LoggerConfig, CheckpointConfig from megatron.core.optimizer import OptimizerConfig +from megatron.core.msc_utils import MultiStorageClientFeature from megatron.core.distributed.distributed_data_parallel_config import DistributedDataParallelConfig from megatron.training.config.resilience_config import RerunStateMachineConfig, StragglerDetectionConfig +from megatron.training.config.utils import sanitize_dataclass_config +from megatron.training.config.instantiate_utils import InstantiationMode, instantiate +from megatron.training.config.yaml_utils import safe_yaml_representers + +T = TypeVar("T", bound="ConfigContainerBase") + +@dataclass(kw_only=True) +class ConfigContainerBase: + """ + Configuration container base class for Megatron configurations. + + Provides YAML/Dict serialization and deserialization. + """ + + @classmethod + def from_dict( + cls: Type[T], + config_dict: dict[str, Any], + mode: InstantiationMode = InstantiationMode.STRICT, + ) -> T: + """ + Create a config container from a dictionary. + + Args: + config_dict: Dictionary containing configuration + mode: Serialization mode (strict or lenient) + + Returns: + A new instance of this class initialized with the dictionary values + """ + # Make a copy to avoid modifying the input + config_dict = copy.deepcopy(config_dict) + + assert "_target_" in config_dict + + # Apply backward compatibility: remove init=False fields that may have been + # serialized by older versions (these are computed in __post_init__) + config_dict = sanitize_dataclass_config(config_dict) + + # Check for extra keys in strict mode + expected_fields = {f.name for f in dataclass_fields(cls) if not f.name.startswith("_")} + expected_fields.add("_target_") # Add _target_ as a valid field + extra_keys = set(config_dict.keys()) - expected_fields + + if extra_keys: + if mode == InstantiationMode.STRICT: + raise ValueError(f"Dictionary contains extra keys not in {cls.__qualname__}: {extra_keys}") + else: + # In lenient mode, remove extra keys + for key in extra_keys: + config_dict.pop(key) + + # Use instantiate to create the object + instance = instantiate(config_dict, mode=mode) + + return instance + + @classmethod + def from_yaml(cls: Type[T], yaml_path: str, mode: InstantiationMode = InstantiationMode.LENIENT) -> T: + """ + Create a config container from a YAML file. + + Args: + yaml_path: Path to the YAML file + mode: Serialization mode (strict or lenient) + + Returns: + A new instance of this class initialized with the YAML file values + """ + if MultiStorageClientFeature.is_enabled(): + msc = MultiStorageClientFeature.import_package() + yaml_path_exists = msc.os.path.exists(yaml_path) + else: + yaml_path_exists = os.path.exists(yaml_path) + + if not yaml_path_exists: + raise FileNotFoundError(f"YAML file not found: {yaml_path}") + + if MultiStorageClientFeature.is_enabled(): + msc = MultiStorageClientFeature.import_package() + with msc.open(yaml_path, "r") as f: + config_dict = yaml.safe_load(f) + else: + with open(yaml_path, "r") as f: + config_dict = yaml.safe_load(f) + + # Convert to OmegaConf first for better compatibility with instantiate + conf = OmegaConf.create(config_dict) + + return cls.from_dict(OmegaConf.to_container(conf, resolve=True), mode=mode) + + def to_dict(self) -> dict[str, Any]: + """ + Convert the config container to a dictionary. + + Also converts any nested dataclasses (both ConfigContainer and regular dataclasses) + to dictionaries recursively. + + Returns: + Dictionary representation of this config + """ + result = {} + result["_target_"] = f"{self.__class__.__module__}.{self.__class__.__qualname__}" + + for f in dataclass_fields(self): + if f.name.startswith("_"): + continue + + value = getattr(self, f.name) + result[f.name] = self._convert_value_to_dict(value) + + return result + + @classmethod + def _convert_value_to_dict(cls, value: Any) -> Any: + """ + Recursively convert a value to a dictionary representation. + + Handles: + - ConfigContainer instances (using to_dict) + - Serializable instances (using as_dict) + - Classes which implement a to_cfg_dict method + - Regular dataclasses (converting each non-private field) + - Lists and tuples (converting each element) + - Dictionaries (converting each value) + - Other types (kept as-is) + + Args: + value: The value to convert + + Returns: + The converted value + """ + if isinstance(value, ConfigContainerBase): + return value.to_dict() + # elif isinstance(value, Serializable): # TODO (@maanug): re-enable after upstreaming ModelConfig+Serializable + # return value.as_dict() + elif hasattr(value, "to_cfg_dict"): + # Allow non-Container classes to implement own custom method + return value.to_cfg_dict() + elif is_dataclass(value) and not isinstance(value, type): + # Handle regular dataclasses + result = {} + + # Add _target_ field for instantiation + result["_target_"] = f"{value.__class__.__module__}.{value.__class__.__qualname__}" + + # Convert each field, handling nested dataclasses properly + for field in dataclass_fields(value): + if field.name.startswith("_"): + continue + + field_value = getattr(value, field.name) + result[field.name] = cls._convert_value_to_dict(field_value) + + return result + elif isinstance(value, (list, tuple)): + return [cls._convert_value_to_dict(item) for item in value] + elif isinstance(value, dict): + return {k: cls._convert_value_to_dict(v) for k, v in value.items()} + else: + return value + + def to_yaml(self, yaml_path: str) -> None: + """ + Save the config container to a YAML file. + + Args: + yaml_path: Path where to save the YAML file. + """ + config_dict = self.to_dict() + + with safe_yaml_representers(): + if MultiStorageClientFeature.is_enabled(): + msc = MultiStorageClientFeature.import_package() + with msc.open(yaml_path, "w") as f: + yaml.safe_dump(config_dict, f, default_flow_style=False) + else: + with open(yaml_path, "w") as f: + yaml.safe_dump(config_dict, f, default_flow_style=False) + + def print_yaml(self) -> None: + """ + Print the config container to the console in YAML format. + """ + config_dict = self.to_dict() + with safe_yaml_representers(): + print(yaml.safe_dump(config_dict, default_flow_style=False)) + + def __deepcopy__(self, memo): + """Support for deep copying.""" + cls = self.__class__ + result = cls.__new__(cls) + memo[id(self)] = result + + for f in dataclass_fields(self): + setattr(result, f.name, copy.deepcopy(getattr(self, f.name), memo)) + + return result @dataclass(kw_only=True) -class PretrainConfigContainer: +class PretrainConfigContainer(ConfigContainerBase): """Top-level container holding all configuration objects.""" train: TrainingConfig diff --git a/megatron/training/config/instantiate_utils.py b/megatron/training/config/instantiate_utils.py new file mode 100644 index 00000000000..362d8574690 --- /dev/null +++ b/megatron/training/config/instantiate_utils.py @@ -0,0 +1,542 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +import copy +import functools +import inspect +import logging +from enum import Enum +from textwrap import dedent +from typing import Any, Callable, Sequence + +from omegaconf import OmegaConf +from omegaconf._utils import is_structured_config + + +class InstantiationException(Exception): + """Custom exception type for instantiation errors.""" + + ... + + +class InstantiationMode(Enum): + """Enum for instantiation modes.""" + + STRICT = "strict" + LENIENT = "lenient" + + +class _Keys(str, Enum): + """Special keys in configs used by instantiate.""" + + TARGET = "_target_" + PARTIAL = "_partial_" + CALL = "_call_" + ARGS = "_args_" + NAME = "_name_" + + +_DEFAULT_ALLOWED_PREFIXES: tuple[str, ...] = ( + "megatron.training.", + "megatron.core.", + "torch.", + "transformers.", + "signal.", +) + +_DEFAULT_ALLOWED_EXACT: frozenset[str] = frozenset({ + "functools.partial", +}) + + +class TargetAllowlist: + """Controls which ``_target_`` strings are permitted for instantiation. + + Security: prevents arbitrary code execution from untrusted YAML configs + by gating which module paths can be imported and called. + """ + + def __init__(self) -> None: + self._allowed_prefixes: list[str] = list(_DEFAULT_ALLOWED_PREFIXES) + self._allowed_exact: set[str] = set(_DEFAULT_ALLOWED_EXACT) + self._enabled: bool = True + + def is_allowed(self, target: str) -> bool: + """Check whether *target* is permitted by the allowlist.""" + if not self._enabled: + return True + if target in self._allowed_exact: + return True + return any(target.startswith(prefix) for prefix in self._allowed_prefixes) + + def add_prefix(self, prefix: str) -> None: + """Add an allowed module prefix (must end with ``'.'``).""" + if not prefix.endswith("."): + raise ValueError(f"Prefix must end with '.': got '{prefix}'") + if prefix not in self._allowed_prefixes: + self._allowed_prefixes.append(prefix) + + def remove_prefix(self, prefix: str) -> None: + """Remove an allowed module prefix.""" + self._allowed_prefixes.remove(prefix) + + def add_exact(self, target: str) -> None: + """Add an exact target string to the allowlist.""" + self._allowed_exact.add(target) + + def remove_exact(self, target: str) -> None: + """Remove an exact target string from the allowlist.""" + self._allowed_exact.discard(target) + + def disable(self) -> None: + """Disable the allowlist check (allows all targets).""" + logging.warning( + "Target allowlist has been disabled. " + "Arbitrary _target_ values will be permitted." + ) + self._enabled = False + + def enable(self) -> None: + """Re-enable the allowlist check.""" + self._enabled = True + + @property + def enabled(self) -> bool: + return self._enabled + + @property + def allowed_prefixes(self) -> tuple[str, ...]: + return tuple(self._allowed_prefixes) + + @property + def allowed_exact(self) -> frozenset[str]: + return frozenset(self._allowed_exact) + + +target_allowlist = TargetAllowlist() + + +def instantiate( + config: Any, + *args: Any, + mode: InstantiationMode = InstantiationMode.LENIENT, + **kwargs: Any, +) -> Any: + """Instantiate an object or callable from a config object. + + This function takes a configuration object (dictionary, list, OmegaConf config, + or Structured Config instance) and instantiates the target specified within it. + + The config object must contain: + _target_ (str): The fully qualified name of the class or callable to instantiate. + + The config object may also contain: + _args_ (list): Positional arguments for the target. + _partial_ (bool): If True, return a functools.partial object instead of calling + the target. Defaults to False. + _call_ (bool): If False, simply resolves and returns the target without calling it. + Defaults to True. + Additional keyword arguments to pass to the target. + + Args: + config: The configuration object describing the target and its parameters. + *args: Optional positional arguments that will override _args_ in the config + if provided. + mode: Instantiation mode (STRICT or LENIENT). Controls how config keys that + do not match the target's signature are handled: LENIENT (default) + drops them with a warning, STRICT raises ``InstantiationException``. + Errors resolving a ``_target_`` propagate in both modes. + **kwargs: Optional keyword arguments that will override parameters in the config. + Note: Dataclass instances in kwargs are treated as nested configs. + + Returns: + The instantiated object or the return value of the callable. + If config._partial_ is True, returns a functools.partial object. + If config._call_ is False, returns the resolved target callable/class itself. + Returns None if the input config is None. + + Raises: + InstantiationException: If the config is invalid, the target cannot be resolved, + or instantiation fails in STRICT mode. + TypeError: If the _partial_ flag is not a boolean. + """ + + # Return None if config is None + if config is None: + return None + + if isinstance(config, (dict, list)): + config = _prepare_input_dict_or_list(config) + + kwargs = _prepare_input_dict_or_list(kwargs) + + # Structured Config always converted first to OmegaConf + if is_structured_config(config) or isinstance(config, (dict, list)): + config = OmegaConf.structured(config, flags={"allow_objects": True}) + + if OmegaConf.is_dict(config): + # Finalize config (convert targets to strings, merge with kwargs) + config_copy = copy.deepcopy(config) + config_copy._set_flag(flags=["allow_objects", "struct", "readonly"], values=[True, False, False]) + config_copy._set_parent(config._get_parent()) + config = config_copy + + if kwargs: + config = OmegaConf.merge(config, kwargs) + + OmegaConf.resolve(config) + + _partial_ = config.pop(_Keys.PARTIAL, False) + + return instantiate_node(config, *args, partial=_partial_, mode=mode) + elif OmegaConf.is_list(config): + # Finalize config (convert targets to strings, merge with kwargs) + config_copy = copy.deepcopy(config) + config_copy._set_flag(flags=["allow_objects", "struct", "readonly"], values=[True, False, False]) + config_copy._set_parent(config._get_parent()) + config = config_copy + + OmegaConf.resolve(config) + + _partial_ = kwargs.pop(_Keys.PARTIAL, False) + + if _partial_: + raise InstantiationException("The _partial_ keyword is not compatible with top-level list instantiation") + + return instantiate_node(config, *args, partial=_partial_, mode=mode) + else: + raise InstantiationException( + dedent( + f"""\ + Cannot instantiate config of type {type(config).__name__}. + Top level config must be an OmegaConf DictConfig/ListConfig object, + a plain dict/list, or a Structured Config class or instance.""" + ) + ) + + +def instantiate_node( + node: Any, + *args: Any, + partial: bool = False, + mode: InstantiationMode = InstantiationMode.LENIENT, +) -> Any: + """Recursively instantiates a node within a configuration structure. + + This function handles the instantiation of individual nodes (dictionaries, + lists, or primitive values) within a larger configuration tree, typically + managed by OmegaConf. + + If the node is a dictionary containing a `_target_` key, it resolves and + instantiates the target callable/class using the other items in the + dictionary as keyword arguments. Nested nodes are recursively instantiated. + + If the node is a list, it recursively instantiates each item in the list. + + If the node is not an OmegaConf config node (e.g., a primitive type), it's + returned directly. + + Args: + node: The configuration node to instantiate (can be DictConfig, ListConfig, + or a primitive type). + *args: Positional arguments passed down from the top-level `instantiate` call, + used primarily for the final target call if the node is a dictionary + with `_target_`. + partial: Boolean flag indicating whether to return a `functools.partial` object + instead of calling the target. This can be overridden by a + `_partial_` key within the node itself. + mode: Instantiation mode (STRICT or LENIENT). Determines error handling + behavior for nested instantiations. + + Returns: + The instantiated object, list, or the original node if it wasn't a config. + Returns None if the input node is None or represents a None value in OmegaConf. + + Raises: + InstantiationException: If instantiation fails in STRICT mode, or if there are + issues like incompatible arguments or non-callable targets. + TypeError: If a `_partial_` flag within the config is not a boolean. + """ + # Return None if config is None + if node is None or (OmegaConf.is_config(node) and node._is_none()): + return None + + if not OmegaConf.is_config(node): + return node + + if OmegaConf.is_dict(node): + partial = node[_Keys.PARTIAL] if _Keys.PARTIAL in node else partial + + full_key = node._get_full_key(None) + + if not isinstance(partial, bool): + msg = f"Instantiation: _partial_ flag must be a bool, got {type(partial)}" + if node and full_key: + msg += f"\nfull_key: {full_key}" + raise TypeError(msg) + + if OmegaConf.is_list(node): + items = [instantiate_node(item, mode=mode) for item in node._iter_ex(resolve=True)] + + return items + elif OmegaConf.is_dict(node): + exclude_keys = set(item.value for item in _Keys if item != _Keys.ARGS) + if _is_target(node): + should_call_target = node.get(_Keys.CALL, True) + _target_ = _resolve_target(node.get(_Keys.TARGET), full_key, check_callable=should_call_target) + kwargs = {} + is_partial = node.get(_Keys.PARTIAL, False) or partial + + if not should_call_target: + if len(set(node.keys()) - {_Keys.TARGET, _Keys.CALL}) != 0: + extra_keys = set(node.keys()) - {_Keys.TARGET, _Keys.CALL} + raise InstantiationException( + f"_call_ was set to False for target {_convert_target_to_string(_target_)}," + f" but extra keys were found: {extra_keys}" + ) + else: + return _target_ + + for key in node.keys(): + if key not in exclude_keys: + if OmegaConf.is_missing(node, key) and is_partial: + continue + value = node[key] + value = instantiate_node(value, mode=mode) + kwargs[key] = _convert_node(value) + + assert callable(_target_) + # Drop unexpected kwargs in lenient mode or raise in strict mode + kwargs = _filter_kwargs_for_target(_target_, kwargs, full_key, mode) + return _call_target(_target_, partial, args, kwargs, full_key) + else: + dict_items = {} + for key, value in node.items(): + dict_items[key] = instantiate_node(value, mode=mode) + return dict_items + + else: + raise InstantiationException(f"Unexpected config type: {type(node).__name__}") + + +def _locate(path: str) -> Any: + """ + Locate an object by name or dotted path, importing as necessary. + This function attempts to import modules starting from the most specific path + (back to front), making it possible to import objects where the final component + could be either a module or an attribute of the previous module. + """ + if path == "": + raise ImportError("Empty path") + from importlib import import_module + + parts = [part for part in path.split(".")] + for part in parts: + if not len(part): + raise ValueError(f"Error loading '{path}': invalid dotstring." + "\nRelative imports are not supported.") + assert len(parts) > 0 + + # Try importing from the most specific path first (back to front) + for i in range(len(parts), 0, -1): + module_path = ".".join(parts[:i]) + try: + obj = import_module(module_path) + + # If this isn't the full path, get the remaining attributes + remaining_parts = parts[i:] + for part in remaining_parts: + try: + obj = getattr(obj, part) + except AttributeError as exc_attr: + raise ImportError( + f"Error loading '{path}':\n{repr(exc_attr)}" + + f"\nAre you sure that '{part}' is an attribute of '{module_path}'?" + ) from exc_attr + + # Successfully found the object + return obj + + except ModuleNotFoundError: + # Module not found, try a less specific path + continue + except Exception as exc_import: + # If we hit a different exception, it's likely an issue with the module itself + raise ImportError(f"Error loading '{path}':\n{repr(exc_import)}") from exc_import + + # If we've tried all paths and nothing worked, report failure with the base module + raise ImportError( + f"Error loading '{path}': Unable to import any module in the path. " + f"Are you sure that module '{parts[0]}' is installed?" + ) + + +def _is_target(x: Any) -> bool: + if isinstance(x, dict): + return _Keys.TARGET in x + if OmegaConf.is_dict(x): + return _Keys.TARGET in x + return False + + +def _call_target( + _target_: Callable[..., Any], + _partial_: bool, + args: tuple[Any, ...], + kwargs: dict[str, Any], + full_key: str, +) -> Any: + """Call target (type) with args and kwargs.""" + args, kwargs = _extract_pos_args(args, kwargs) + if _partial_: + try: + return functools.partial(_target_, *args, **kwargs) + except Exception as e: + msg = f"Error in creating partial({_convert_target_to_string(_target_)}, ...) object:" + f"\n{repr(e)}" + if full_key: + msg += f"\nfull_key: {full_key}" + raise InstantiationException(msg) from e + else: + try: + return _target_(*args, **kwargs) + except Exception as e: + msg = f"Error in call to target '{_convert_target_to_string(_target_)}':\n{repr(e)}" + if full_key: + msg += f"\nfull_key: {full_key}" + raise InstantiationException(msg) from e + + +def _convert_target_to_string(t: Any) -> Any: + if callable(t): + return f"{t.__module__}.{t.__qualname__}" + else: + return t + + +def _filter_kwargs_for_target( + target: Callable[..., Any] | type, + kwargs: dict[str, Any], + full_key: str, + mode: InstantiationMode, +) -> dict[str, Any]: + """Drop unexpected keyword arguments for a target and warn. + + If the target accepts ``**kwargs`` we forward everything. Otherwise we + inspect the signature and remove keys not present as keyword-capable + parameters, emitting a warning with the dropped keys. + """ + try: + signature = inspect.signature(target) + except (TypeError, ValueError): + # Some builtins or C-extensions may not have an inspectable signature. + return kwargs + + parameters = signature.parameters + if any(p.kind == inspect.Parameter.VAR_KEYWORD for p in parameters.values()): + return kwargs + + allowed_keys = { + name + for name, param in parameters.items() + if param.kind in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY) + } + + unexpected = set(kwargs.keys()) - allowed_keys + if _Keys.ARGS in unexpected: + unexpected.remove(_Keys.ARGS) + + if not unexpected: + return kwargs + + target_str = _convert_target_to_string(target) + if mode == InstantiationMode.LENIENT: + # Warn and drop the unexpected keys + warning_msg = f"Dropping unexpected config keys for target '{target_str}': {sorted(unexpected)}" + if full_key: + warning_msg += f"\nfull_key: {full_key}" + logging.warning(warning_msg) + filtered = {k: v for k, v in kwargs.items() if k in allowed_keys} + if _Keys.ARGS in kwargs: + filtered[_Keys.ARGS] = kwargs[_Keys.ARGS] + return filtered + else: + msg = f"Unexpected config keys for target '{target_str}': {sorted(unexpected)}" + if full_key: + msg += f"\nfull_key: {full_key}" + raise InstantiationException(msg) + + +def _prepare_input_dict_or_list(d: dict[Any, Any] | list[Any]) -> Any: + res: Any + if isinstance(d, dict): + res = {} + for k, v in d.items(): + if k == _Keys.TARGET: + v = _convert_target_to_string(d[_Keys.TARGET]) + elif isinstance(v, (dict, list)): + v = _prepare_input_dict_or_list(v) + res[k] = v + elif isinstance(d, list): + res = [] + for v in d: + if isinstance(v, (list, dict)): + v = _prepare_input_dict_or_list(v) + res.append(v) + else: + raise InstantiationException(f"Expected a dict or list, got {type(d).__name__}") + return res + + +def _resolve_target( + target: str | type | Callable[..., Any], + full_key: str, + check_callable: bool = True, +) -> type | Callable[..., Any] | object: + """Resolve target string, type or callable into type or callable.""" + if isinstance(target, str): + # Security: check allowlist BEFORE importing to prevent + # arbitrary code execution from untrusted _target_ strings. + if not target_allowlist.is_allowed(target): + msg = ( + f"Target '{target}' is not in the allowlist for _target_ instantiation.\n" + f"Allowed module prefixes: {', '.join(target_allowlist.allowed_prefixes)}\n" + f"Allowed exact targets: {', '.join(sorted(target_allowlist.allowed_exact))}\n" + f"To allow this target, call:\n" + f" target_allowlist.add_prefix('{target.rsplit('.', 1)[0] + '.'}')\n" + f" or: target_allowlist.add_exact('{target}')" + ) + if full_key: + msg += f"\nfull_key: {full_key}" + raise InstantiationException(msg) + try: + target = _locate(target) + except Exception as e: + msg = f"Error locating target '{target}'." + if full_key: + msg += f"\nfull_key: {full_key}" + raise InstantiationException(msg) from e + if check_callable and not callable(target): + msg = f"Expected a callable target, got '{target}' of type '{type(target).__name__}'" + if full_key: + msg += f"\nfull_key: {full_key}" + raise InstantiationException(msg) + return target + + +def _extract_pos_args(input_args: Any, kwargs: Any) -> tuple[Any, Any]: + config_args = kwargs.pop(_Keys.ARGS, ()) + output_args = config_args + + if isinstance(config_args, Sequence): + if len(input_args) > 0: + output_args = input_args + else: + raise InstantiationException( + f"Unsupported _args_ type: '{type(config_args).__name__}'. value: '{config_args}'" + ) + + return output_args, kwargs + + +def _convert_node(node: Any) -> Any: + if OmegaConf.is_config(node): + node = OmegaConf.to_container(node, resolve=True) + + return node diff --git a/megatron/training/config/utils.py b/megatron/training/config/utils.py new file mode 100644 index 00000000000..3b00c734d69 --- /dev/null +++ b/megatron/training/config/utils.py @@ -0,0 +1,105 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. + +import importlib +import logging +from functools import lru_cache +from dataclasses import is_dataclass +from dataclasses import fields as dataclass_fields +from typing import Any + +logger = logging.getLogger(__name__) + + +def sanitize_dataclass_config(config: dict[str, Any], _visited: set | None = None) -> dict[str, Any]: + """Remove init=False fields from a dataclass config dict for backward compatibility. + + This function automatically detects fields with init=False by inspecting the + target class specified in the config's _target_ field. This handles cases where + older checkpoints serialized computed fields that should not be passed to __init__. + + The function recursively processes nested dicts that may also be dataclass configs. + + Args: + config: A configuration dictionary, potentially with a _target_ field. + _visited: Internal set to track visited objects and prevent infinite recursion. + + Returns: + The sanitized configuration with init=False fields removed. + """ + if not isinstance(config, dict): + return config + + if _visited is None: + _visited = set() + config_id = id(config) + if config_id in _visited: + return config + _visited.add(config_id) + + target = config.get("_target_") + init_false_fields: frozenset[str] = frozenset() + + if isinstance(target, str): + target_class = _resolve_target_class(target) + if target_class is not None: + init_false_fields = _get_init_false_fields(target_class) + + # Process all values, filtering init=False fields and recursing into nested dicts + sanitized = {} + for key, value in config.items(): + if key in init_false_fields: + if target_class is not None: + logger.debug( + f"Removing init=False field '{key}' from {target_class.__name__} config for backward compatibility" + ) + continue + + # Recursively sanitize nested dicts (which may be nested dataclass configs) + if isinstance(value, dict): + value = sanitize_dataclass_config(value, _visited) + elif isinstance(value, list): + value = [sanitize_dataclass_config(item, _visited) if isinstance(item, dict) else item for item in value] + + sanitized[key] = value + + return sanitized + + +def _resolve_target_class(target: str) -> type | None: + """Resolve a _target_ string to a class. + + Args: + target: A fully qualified class path (e.g., "module.submodule.ClassName"). + + Returns: + The resolved class, or None if resolution fails. + """ + from megatron.training.config.instantiate_utils import target_allowlist + + if not target_allowlist.is_allowed(target): + logger.warning(f"Target '{target}' is not in the allowlist. Skipping resolution.") + return None + + try: + module_path, class_name = target.rsplit(".", 1) + module = importlib.import_module(module_path) + return getattr(module, class_name, None) + except (ValueError, ImportError, AttributeError) as e: + logger.warning(f"Could not resolve target '{target}': {e}") + return None + + +@lru_cache(maxsize=128) +def _get_init_false_fields(target_class: type) -> frozenset[str]: + """Get the set of field names with init=False for a dataclass. + + Args: + target_class: A dataclass type to inspect. + + Returns: + A frozenset of field names that have init=False. + """ + if not is_dataclass(target_class): + return frozenset() + + return frozenset(f.name for f in dataclass_fields(target_class) if not f.init) diff --git a/megatron/training/config/yaml_utils.py b/megatron/training/config/yaml_utils.py new file mode 100644 index 00000000000..f088a8ba484 --- /dev/null +++ b/megatron/training/config/yaml_utils.py @@ -0,0 +1,185 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. + +import enum +import functools +import inspect +from contextlib import contextmanager +from typing import Generator + +import yaml + + +@contextmanager +def safe_yaml_representers() -> Generator[None, None, None]: + """ + Context manager for safely adding and removing custom YAML representers. + + Temporarily adds custom representers for functions, classes, and other objects + to the YAML SafeDumper, and restores the original representers when exiting + the context. + + Usage: + with safe_yaml_representers(): + yaml_str = yaml.safe_dump(my_complex_object) + """ + # Save original representers + original_representers = yaml.SafeDumper.yaml_representers.copy() + original_multi_representers = yaml.SafeDumper.yaml_multi_representers.copy() + + try: + # Register custom representers + + # Partial representer + yaml.SafeDumper.add_representer(functools.partial, _partial_representer) + + # Enum representer + yaml.SafeDumper.add_multi_representer(enum.Enum, _enum_representer) + + # Function representer + yaml.SafeDumper.add_representer(type(lambda: ...), _function_representer) + yaml.SafeDumper.add_representer(type(object), _function_representer) + + # Try to add torch dtype representer if available + try: + import torch + + yaml.SafeDumper.add_representer(torch.dtype, _torch_dtype_representer) + except ModuleNotFoundError: + pass + + # Try to add GenerationConfig representer if available + try: + from transformers import GenerationConfig + + yaml.SafeDumper.add_representer(GenerationConfig, _generation_config_representer) + except ModuleNotFoundError: + pass + + # Try to add PretrainedConfig representer if available (generic for HF configs) + try: + from transformers import PretrainedConfig + + # Use multi-representer so subclasses of PretrainedConfig are also handled + yaml.SafeDumper.add_multi_representer(PretrainedConfig, _pretrained_config_representer) + except ModuleNotFoundError: + pass + + # General object representer + yaml.SafeDumper.add_multi_representer(object, _safe_object_representer) + + yield + finally: + # Restore original representers + yaml.SafeDumper.yaml_representers = original_representers + yaml.SafeDumper.yaml_multi_representers = original_multi_representers + + +def _function_representer(dumper, data): + """Represent functions in YAML.""" + value = { + "_target_": f"{inspect.getmodule(data).__name__}.{data.__qualname__}", # type: ignore + "_call_": False, + } + return dumper.represent_data(value) + + +def _torch_dtype_representer(dumper, data): + """Represent torch dtypes in YAML.""" + value = { + "_target_": str(data), + "_call_": False, + } + return dumper.represent_data(value) + + +def _safe_object_representer(dumper, data): + """ + General object representer for YAML. + + This function is a fallback for objects that don't have specific representers. + If the object has __qualname__ attr, + the _target_ is set to f"{inspect.getmodule(obj).__name__}.{obj.__qualname__}". + If the object does not have a __qualname__ attr, the _target_ is set from its __class__ attr. + The _call_ key is used to indicate whether the target should be called to create an instance. + + Args: + dumper (yaml.Dumper): The YAML dumper to use for serialization. + data (Any): The data to serialize. + + Returns: + The YAML representation of the data. + """ + try: + obj = data + target = f"{inspect.getmodule(obj).__name__}.{obj.__qualname__}" + call = False + except AttributeError: + obj = data.__class__ + target = f"{inspect.getmodule(obj).__name__}.{obj.__qualname__}" + call = True + + value = { + "_target_": target, # type: ignore + "_call_": call, + } + return dumper.represent_data(value) + + +def _partial_representer(dumper, data): + """Represent functools.partial objects in YAML.""" + # Get the underlying function + func = data.func + + # Create a dictionary representation + value = { + "_target_": f"{inspect.getmodule(func).__name__}.{func.__qualname__}", + "_partial_": True, + "_args_": list(data.args) if data.args else [], + } + + # Add keyword arguments if any exist + if data.keywords: + for k, v in data.keywords.items(): + value[k] = v + + return dumper.represent_data(value) + + +def _enum_representer(dumper, data): + """Represent enums in YAML.""" + # Create a dictionary representation + enum_class = data.__class__ + value = { + "_target_": f"{inspect.getmodule(enum_class).__name__}.{enum_class.__qualname__}", + "_call_": True, + "_args_": [data.value], + "_name_": data.name, + } + + return dumper.represent_data(value) + + +def _generation_config_representer(dumper, data): + """Represent transformers GenerationConfig objects in YAML.""" + cls = data.__class__ + value = { + "_target_": f"{inspect.getmodule(cls).__name__}.{cls.__qualname__}.from_dict", + "_call_": True, + "config_dict": data.to_dict(), + } + + return dumper.represent_data(value) + + +def _pretrained_config_representer(dumper, data): + """Represent transformers PretrainedConfig objects in YAML generically. + + Uses the class's from_dict/to_dict methods to ensure full round-trip of all fields. + """ + cls = data.__class__ + value = { + "_target_": f"{inspect.getmodule(cls).__name__}.{cls.__qualname__}.from_dict", + "_call_": True, + "config_dict": data.to_dict(), + } + return dumper.represent_data(value) diff --git a/tests/unit_tests/training/config/test_container_base.py b/tests/unit_tests/training/config/test_container_base.py new file mode 100644 index 00000000000..2b87c69679f --- /dev/null +++ b/tests/unit_tests/training/config/test_container_base.py @@ -0,0 +1,850 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. + +import copy +import functools +import os +import tempfile +from dataclasses import dataclass +from unittest.mock import MagicMock, mock_open, patch + +import pytest +import torch +import yaml + +from megatron.core.msc_utils import MultiStorageClientFeature +from megatron.training.config.container import ConfigContainerBase +from megatron.training.config.instantiate_utils import InstantiationMode + + +@pytest.fixture(autouse=True) +def _disable_allowlist(): + """Temporarily disable allowlist to fully test container logic with local test targets.""" + from megatron.training.config.instantiate_utils import target_allowlist + + target_allowlist.disable() + yield + target_allowlist.enable() + + +def _target_qualname(obj) -> str: + return f"{obj.__module__}.{obj.__qualname__}" + + +# Test functions for callable testing +def activation_function(x): + """Test activation function.""" + return x * 2 + + +def loss_function(pred, target, reduction="mean"): + """Test loss function with parameters.""" + return abs(pred - target) + + +# Test dataclasses for testing +@dataclass +class SimpleDataclass: + """Simple dataclass for testing.""" + + name: str = "test" + value: int = 42 + + +@dataclass +class NestedDataclass: + """Nested dataclass for testing.""" + + simple: SimpleDataclass + description: str = "nested" + + +@dataclass +class CallableDataclass: + """Dataclass with callable and partial fields for testing.""" + + name: str = "callable_test" + activation_func: callable = activation_function + loss_func: callable = functools.partial(loss_function, reduction="sum") + torch_func: callable = torch.nn.functional.relu + lambda_func: callable = lambda x: x + 1 + regular_value: int = 100 + + +@dataclass +class TestConfigContainer(ConfigContainerBase): + """Test configuration container.""" + + name: str = "test_config" + value: int = 100 + description: str = "A test configuration" + + +@dataclass +class ComplexConfigContainer(ConfigContainerBase): + """Complex configuration container for testing.""" + + simple_config: TestConfigContainer + nested_data: NestedDataclass + items: list[str] + metadata: dict[str, int] + + +@dataclass +class CallableConfigContainer(ConfigContainerBase): + """Configuration container with callable fields for testing.""" + + name: str = "callable_config" + callable_data: CallableDataclass = None # Will be set in tests + activation: callable = activation_function + partial_loss: callable = functools.partial(loss_function, reduction="none") + torch_activation: callable = torch.nn.functional.gelu + + def __post_init__(self): + """Initialize callable_data if not provided.""" + if self.callable_data is None: + self.callable_data = CallableDataclass() + + +class TestConfigContainer_FromDict: + """Test ConfigContainer.from_dict method.""" + + @patch("megatron.training.config.container.instantiate") + def test_from_dict_basic(self, mock_instantiate): + """Test basic from_dict functionality.""" + config_dict = { + "_target_": _target_qualname(TestConfigContainer), + "name": "from_dict", + "value": 300, + } + + expected_config = TestConfigContainer(name="from_dict", value=300) + mock_instantiate.return_value = expected_config + + result = TestConfigContainer.from_dict(config_dict) + + mock_instantiate.assert_called_once_with(config_dict, mode=InstantiationMode.STRICT) + assert result.name == "from_dict" + assert result.value == 300 + + @patch("megatron.training.config.container.instantiate") + def test_from_dict_with_mode(self, mock_instantiate): + """Test from_dict with different instantiation modes.""" + config_dict = {"_target_": _target_qualname(TestConfigContainer), "name": "lenient"} + + expected_config = TestConfigContainer(name="lenient") + mock_instantiate.return_value = expected_config + + result = TestConfigContainer.from_dict(config_dict, mode=InstantiationMode.LENIENT) + + mock_instantiate.assert_called_once_with(config_dict, mode=InstantiationMode.LENIENT) + assert result.name == "lenient" + + def test_from_dict_missing_target(self): + """Test from_dict raises error when _target_ is missing.""" + config_dict = {"name": "test"} + + with pytest.raises(AssertionError): + TestConfigContainer.from_dict(config_dict) + + def test_from_dict_extra_keys_strict_mode(self): + """Test from_dict raises error for extra keys in strict mode.""" + config_dict = { + "_target_": _target_qualname(TestConfigContainer), + "name": "test", + "extra_key": "should_fail", + } + + with pytest.raises(ValueError, match="Dictionary contains extra keys"): + TestConfigContainer.from_dict(config_dict, mode=InstantiationMode.STRICT) + + @patch("megatron.training.config.container.instantiate") + def test_from_dict_extra_keys_lenient_mode(self, mock_instantiate): + """Test from_dict removes extra keys in lenient mode.""" + config_dict = { + "_target_": _target_qualname(TestConfigContainer), + "name": "test", + "extra_key": "should_be_removed", + } + + expected_config = TestConfigContainer(name="test") + mock_instantiate.return_value = expected_config + + TestConfigContainer.from_dict(config_dict, mode=InstantiationMode.LENIENT) + + # Verify that extra_key was removed from the dict passed to instantiate + called_dict = mock_instantiate.call_args[0][0] + assert "extra_key" not in called_dict + assert called_dict["name"] == "test" + assert called_dict["_target_"] == _target_qualname(TestConfigContainer) + + def test_from_dict_preserves_original(self): + """Test that from_dict doesn't modify the original dictionary.""" + original_dict = { + "_target_": _target_qualname(TestConfigContainer), + "name": "original", + "extra_key": "should_be_preserved_in_original", + } + + original_copy = copy.deepcopy(original_dict) + + with pytest.raises(ValueError): # This will fail in strict mode + TestConfigContainer.from_dict(original_dict, mode=InstantiationMode.STRICT) + + # Original dict should be unchanged + assert original_dict == original_copy + + +class TestConfigContainer_FromYaml: + """Test ConfigContainer.from_yaml method.""" + + def test_from_yaml_file_not_found(self): + """Test from_yaml raises FileNotFoundError for missing file.""" + with pytest.raises(FileNotFoundError, match="YAML file not found"): + TestConfigContainer.from_yaml("non_existent_file.yaml") + + @patch("megatron.training.config.container.MultiStorageClientFeature.is_enabled") + @patch("megatron.training.config.container.OmegaConf") + @patch("builtins.open", new_callable=mock_open) + @patch("os.path.exists") + def test_from_yaml_success(self, mock_exists, mock_file, mock_omegaconf, mock_msc): + """Test successful YAML loading.""" + mock_msc.return_value = False + mock_exists.return_value = True + yaml_content = f""" + _target_: {_target_qualname(TestConfigContainer)} + name: yaml_config + value: 500 + """ + mock_file.return_value.read.return_value = yaml_content + + # Mock yaml.safe_load to return parsed content + with patch("yaml.safe_load") as mock_yaml_load: + config_dict = { + "_target_": _target_qualname(TestConfigContainer), + "name": "yaml_config", + "value": 500, + } + mock_yaml_load.return_value = config_dict + + # Mock OmegaConf methods + mock_conf = MagicMock() + mock_omegaconf.create.return_value = mock_conf + mock_omegaconf.to_container.return_value = config_dict + + result = TestConfigContainer.from_yaml("test.yaml") + + mock_exists.assert_called_once_with("test.yaml") + mock_file.assert_called_once_with("test.yaml", "r") + mock_yaml_load.assert_called_once() + mock_omegaconf.create.assert_called_once_with(config_dict) + mock_omegaconf.to_container.assert_called_once_with(mock_conf, resolve=True) + + assert result.name == "yaml_config" + assert result.value == 500 + + @patch("megatron.training.config.container.MultiStorageClientFeature.is_enabled") + @patch("os.path.exists") + def test_from_yaml_with_mode(self, mock_exists, mock_msc): + """Test from_yaml with different instantiation modes.""" + mock_msc.return_value = False + mock_exists.return_value = True + + with patch("builtins.open", mock_open()): + with patch("yaml.safe_load", return_value={}): + with patch("megatron.training.config.container.OmegaConf") as mock_omegaconf: + # Mock OmegaConf methods to return expected values + mock_conf = MagicMock() + mock_omegaconf.create.return_value = mock_conf + mock_omegaconf.to_container.return_value = {} # Return actual empty dict + + with patch.object(TestConfigContainer, "from_dict") as mock_from_dict: + TestConfigContainer.from_yaml("test.yaml", mode=InstantiationMode.STRICT) + mock_from_dict.assert_called_once_with({}, mode=InstantiationMode.STRICT) + + +class TestConfigContainer_ToDict: + """Test ConfigContainer.to_dict method.""" + + def test_to_dict_basic(self): + """Test basic to_dict functionality.""" + config = TestConfigContainer(name="test", value=123, description="test desc") + result = config.to_dict() + + expected = { + "_target_": _target_qualname(TestConfigContainer), + "name": "test", + "value": 123, + "description": "test desc", + } + + assert result == expected + + def test_to_dict_with_nested_config_container(self): + """Test to_dict with nested ConfigContainer.""" + simple_config = TestConfigContainer(name="nested", value=456) + nested_data = NestedDataclass(simple=SimpleDataclass(name="inner", value=789)) + + complex_config = ComplexConfigContainer( + simple_config=simple_config, + nested_data=nested_data, + items=["a", "b", "c"], + metadata={"key1": 1, "key2": 2}, + ) + + result = complex_config.to_dict() + + # Check the structure + assert "_target_" in result + assert result["_target_"] == _target_qualname(ComplexConfigContainer) + + # Check nested ConfigContainer + assert result["simple_config"]["_target_"] == _target_qualname(TestConfigContainer) + assert result["simple_config"]["name"] == "nested" + assert result["simple_config"]["value"] == 456 + + # Check nested regular dataclass + assert result["nested_data"]["_target_"] == _target_qualname(NestedDataclass) + assert result["nested_data"]["simple"]["_target_"] == _target_qualname(SimpleDataclass) + assert result["nested_data"]["simple"]["name"] == "inner" + assert result["nested_data"]["simple"]["value"] == 789 + + # Check lists and dicts + assert result["items"] == ["a", "b", "c"] + assert result["metadata"] == {"key1": 1, "key2": 2} + + # TODO (@maanug): reenable after migrating model config+builder + # def test_convert_serializable_nested_in_config(self): + # """Test that a Serializable nested inside a ConfigContainer is serialized via as_dict().""" + + # class NestedSerializable: + # def __init__(self, value): + # self.value = value + + # def as_dict(self) -> dict: + # return {"_target_": "my.module.NestedSerializable", "value": self.value} + + # @classmethod + # def from_dict(cls, data): + # return cls(data["value"]) + + # @dataclass + # class ConfigWithSerializable(ConfigContainerBase): + # name: str = "ser_test" + # nested: object = None + + # def __post_init__(self): + # if self.nested is None: + # self.nested = NestedSerializable(99) + + # config = ConfigWithSerializable() + # result = config.to_dict() + + # assert result["name"] == "ser_test" + # assert result["nested"] == {"_target_": "my.module.NestedSerializable", "value": 99} + + def test_to_dict_excludes_private_fields(self): + """Test that to_dict excludes fields starting with underscore.""" + config = TestConfigContainer() + result = config.to_dict() + + # Should include _target_ but exclude __version__ + assert "_target_" in result + assert "__version__" not in result + + +class TestConfigContainer_ConvertValueToDict: + """Test ConfigContainer._convert_value_to_dict method.""" + + def test_convert_config_container(self): + """Test converting ConfigContainer instance.""" + config = TestConfigContainer(name="convert_test", value=999) + result = TestConfigContainer._convert_value_to_dict(config) + + expected = { + "_target_": _target_qualname(TestConfigContainer), + "name": "convert_test", + "value": 999, + "description": "A test configuration", + } + + assert result == expected + + def test_convert_regular_dataclass(self): + """Test converting regular dataclass.""" + simple = SimpleDataclass(name="simple_test", value=555) + result = TestConfigContainer._convert_value_to_dict(simple) + + expected = { + "_target_": _target_qualname(SimpleDataclass), + "name": "simple_test", + "value": 555, + } + + assert result == expected + + def test_convert_list(self): + """Test converting list with nested dataclasses.""" + items = [SimpleDataclass(name="item1", value=1), "string_item", 42] + result = TestConfigContainer._convert_value_to_dict(items) + + assert len(result) == 3 + assert result[0]["_target_"] == _target_qualname(SimpleDataclass) + assert result[0]["name"] == "item1" + assert result[1] == "string_item" + assert result[2] == 42 + + def test_convert_tuple(self): + """Test converting tuple.""" + items = (SimpleDataclass(name="tuple_item"), "string") + result = TestConfigContainer._convert_value_to_dict(items) + + assert len(result) == 2 + assert result[0]["_target_"] == _target_qualname(SimpleDataclass) + assert result[1] == "string" + + def test_convert_dict(self): + """Test converting dictionary with nested dataclasses.""" + data = { + "config": SimpleDataclass(name="dict_config"), + "value": 123, + "nested": {"inner": SimpleDataclass(name="inner_config")}, + } + result = TestConfigContainer._convert_value_to_dict(data) + + assert result["config"]["_target_"] == _target_qualname(SimpleDataclass) + assert result["value"] == 123 + assert result["nested"]["inner"]["_target_"] == _target_qualname(SimpleDataclass) + + # TODO (@maanug): reenable after migrating model config+builder + # def test_convert_serializable(self): + # """Test converting a Serializable instance uses as_dict().""" + + # class MySerializable: + # def as_dict(self) -> dict: + # return {"_target_": "my.module.MySerializable", "x": 42} + + # @classmethod + # def from_dict(cls, data): + # return cls() + + # obj = MySerializable() + # assert isinstance(obj, Serializable) # runtime_checkable sanity check + + # result = TestConfigContainer._convert_value_to_dict(obj) + + # assert result == {"_target_": "my.module.MySerializable", "x": 42} + + def test_convert_primitive_types(self): + """Test converting primitive types.""" + assert TestConfigContainer._convert_value_to_dict(42) == 42 + assert TestConfigContainer._convert_value_to_dict("string") == "string" + assert TestConfigContainer._convert_value_to_dict(True) is True + assert TestConfigContainer._convert_value_to_dict(None) is None + assert TestConfigContainer._convert_value_to_dict(3.14) == 3.14 + + def test_convert_excludes_private_fields_in_dataclass(self): + """Test that private fields are excluded from dataclass conversion.""" + + @dataclass + class DataclassWithPrivate: + public_field: str = "public" + _private_field: str = "private" + + obj = DataclassWithPrivate() + result = TestConfigContainer._convert_value_to_dict(obj) + + assert "public_field" in result + assert "_private_field" not in result + assert "_target_" in result + + +class TestConfigContainer_ToYaml: + """Test ConfigContainer.to_yaml method.""" + + def test_to_yaml_save_to_file(self): + """Test to_yaml writes valid YAML to disk matching to_dict().""" + config = TestConfigContainer(name="file_test", value=888) + + with tempfile.TemporaryDirectory() as tmp_dir: + tmp_path = os.path.join(tmp_dir, "test_output.yaml") + config.to_yaml(tmp_path) + + assert os.path.exists(tmp_path) + with open(tmp_path, "r") as f: + parsed = yaml.safe_load(f) + + assert parsed == config.to_dict() + + def test_to_yaml_with_msc_url(self): + """Test to_yaml with MSC URL.""" + config = TestConfigContainer(name="msc_test", value=999) + + MultiStorageClientFeature.enable() + + # Verify that the file is created in the temporary directory + with tempfile.TemporaryDirectory() as temp_dir: + config.to_yaml(f"msc://default{temp_dir}/test_output.yaml") + assert os.path.exists(f"{temp_dir}/test_output.yaml") + + loaded_config = TestConfigContainer.from_yaml( + f"msc://default{temp_dir}/test_output.yaml" + ) + assert config.to_dict() == loaded_config.to_dict() + + +class TestConfigContainer_PrintYaml: + """Test ConfigContainer.print_yaml method.""" + + def test_print_yaml_basic(self, capsys): + """Test print_yaml outputs valid YAML with the correct field values.""" + config = TestConfigContainer(name="print_test", value=555, description="test print") + + config.print_yaml() + + captured = capsys.readouterr() + parsed = yaml.safe_load(captured.out) + + assert parsed["_target_"] == _target_qualname(TestConfigContainer) + assert parsed["name"] == "print_test" + assert parsed["value"] == 555 + assert parsed["description"] == "test print" + + def test_print_yaml_with_complex_config(self, capsys): + """Test print_yaml with complex nested configuration.""" + simple_config = TestConfigContainer(name="nested", value=123) + nested_data = NestedDataclass(simple=SimpleDataclass(name="inner", value=456)) + + complex_config = ComplexConfigContainer( + simple_config=simple_config, + nested_data=nested_data, + items=["a", "b", "c"], + metadata={"key1": 10, "key2": 20}, + ) + + complex_config.print_yaml() + + captured = capsys.readouterr() + parsed = yaml.safe_load(captured.out) + + assert parsed["_target_"] == _target_qualname(ComplexConfigContainer) + assert parsed["simple_config"]["name"] == "nested" + assert parsed["nested_data"]["simple"]["value"] == 456 + assert parsed["items"] == ["a", "b", "c"] + assert parsed["metadata"] == {"key1": 10, "key2": 20} + + def test_print_yaml_output_matches_to_dict(self, capsys): + """Test that the YAML output exactly round-trips through to_dict.""" + config = TestConfigContainer(name="to_dict_test", value=999) + + config.print_yaml() + + captured = capsys.readouterr() + parsed = yaml.safe_load(captured.out) + + assert parsed == config.to_dict() + + +class TestConfigContainer_DeepCopy: + """Test ConfigContainer.__deepcopy__ method.""" + + def test_deepcopy_basic(self): + """Test basic deep copy functionality.""" + config = TestConfigContainer(name="original", value=100) + copied_config = copy.deepcopy(config) + + assert copied_config is not config + assert copied_config.name == config.name + assert copied_config.value == config.value + assert copied_config.description == config.description + + # Modify original to verify they're independent + config.name = "modified" + assert copied_config.name == "original" + + def test_deepcopy_with_nested_structures(self): + """Test deep copy with nested dataclasses and containers.""" + simple_config = TestConfigContainer(name="nested", value=456) + nested_data = NestedDataclass(simple=SimpleDataclass(name="inner", value=789)) + + complex_config = ComplexConfigContainer( + simple_config=simple_config, + nested_data=nested_data, + items=["a", "b", "c"], + metadata={"key1": 1, "key2": 2}, + ) + + copied_config = copy.deepcopy(complex_config) + + # Verify it's a deep copy + assert copied_config is not complex_config + assert copied_config.simple_config is not complex_config.simple_config + assert copied_config.nested_data is not complex_config.nested_data + assert copied_config.items is not complex_config.items + assert copied_config.metadata is not complex_config.metadata + + # Verify values are preserved + assert copied_config.simple_config.name == "nested" + assert copied_config.nested_data.simple.name == "inner" + assert copied_config.items == ["a", "b", "c"] + assert copied_config.metadata == {"key1": 1, "key2": 2} + + # Verify independence + complex_config.simple_config.name = "modified" + complex_config.items.append("d") + + assert copied_config.simple_config.name == "nested" + assert len(copied_config.items) == 3 + + +class TestConfigContainer_Integration: + """Integration tests for ConfigContainer.""" + + def test_roundtrip_dict_conversion(self): + """Test that converting to dict and back preserves data.""" + simple_config = TestConfigContainer(name="roundtrip", value=999) + nested_data = NestedDataclass( + simple=SimpleDataclass(name="nested", value=888), description="roundtrip test" + ) + + original_config = ComplexConfigContainer( + simple_config=simple_config, + nested_data=nested_data, + items=["x", "y", "z"], + metadata={"test": 42}, + ) + + config_dict = original_config.to_dict() + + reconstructed_config = ComplexConfigContainer.from_dict(config_dict) + + assert reconstructed_config.simple_config.name == original_config.simple_config.name + assert reconstructed_config.simple_config.value == original_config.simple_config.value + assert ( + reconstructed_config.nested_data.description == original_config.nested_data.description + ) + assert ( + reconstructed_config.nested_data.simple.name == original_config.nested_data.simple.name + ) + assert ( + reconstructed_config.nested_data.simple.value + == original_config.nested_data.simple.value + ) + assert reconstructed_config.items == original_config.items + assert reconstructed_config.metadata == original_config.metadata + + def test_yaml_roundtrip_structure(self): + """Test that converting to YAML and back preserves data.""" + config = TestConfigContainer(name="yaml_roundtrip", value=1234) + + with tempfile.TemporaryDirectory() as tmp_dir: + tmp_path = os.path.join(tmp_dir, "test_config.yaml") + config.to_yaml(tmp_path) + + loaded_config = TestConfigContainer.from_yaml(tmp_path) + + assert loaded_config.name == config.name + assert loaded_config.value == config.value + assert loaded_config.description == config.description + + +class TestConfigContainer_EdgeCases: + """Test edge cases for ConfigContainer.""" + + def test_empty_config_container(self): + """Test ConfigContainer with minimal fields.""" + + @dataclass + class MinimalConfig(ConfigContainerBase): + pass + + config = MinimalConfig() + result = config.to_dict() + + assert "_target_" in result + # The actual path will be generated based on the local class + assert "MinimalConfig" in result["_target_"] + + def test_config_with_none_values(self): + """Test ConfigContainer with None values.""" + + @dataclass + class ConfigWithNone(ConfigContainerBase): + optional_field: str = None + required_field: str = "required" + + config = ConfigWithNone() + result = config.to_dict() + + assert result["optional_field"] is None + assert result["required_field"] == "required" + + def test_config_with_complex_nested_types(self): + """Test ConfigContainer with complex nested types.""" + + @dataclass + class ComplexConfig(ConfigContainerBase): + nested_list: list[dict[str, SimpleDataclass]] + nested_dict: dict[str, list[SimpleDataclass]] + + nested_list = [ + {"item1": SimpleDataclass(name="list_item1", value=1)}, + {"item2": SimpleDataclass(name="list_item2", value=2)}, + ] + + nested_dict = { + "group1": [SimpleDataclass(name="group1_item1", value=10)], + "group2": [SimpleDataclass(name="group2_item1", value=20)], + } + + config = ComplexConfig(nested_list=nested_list, nested_dict=nested_dict) + result = config.to_dict() + + # Verify complex nested structure conversion + assert len(result["nested_list"]) == 2 + assert result["nested_list"][0]["item1"]["_target_"] == _target_qualname(SimpleDataclass) + assert result["nested_dict"]["group1"][0]["name"] == "group1_item1" + + +class TestConfigContainer_CallablesAndPartials: + """Test ConfigContainer handling of callables and partial functions.""" + + def test_dataclass_with_callables_to_dict(self): + """Test converting dataclass with callables to dict.""" + callable_data = CallableDataclass() + result = TestConfigContainer._convert_value_to_dict(callable_data) + + assert result["_target_"] == _target_qualname(CallableDataclass) + assert result["name"] == "callable_test" + assert result["regular_value"] == 100 + + # Callables are not dataclasses/lists/dicts, so they pass through as-is + assert result["activation_func"] is activation_function + assert isinstance(result["loss_func"], functools.partial) + assert result["loss_func"].func is loss_function + assert result["loss_func"].keywords == {"reduction": "sum"} + assert result["torch_func"] is torch.nn.functional.relu + assert callable(result["lambda_func"]) + assert result["lambda_func"](5) == 6 + + def test_config_container_with_callables_to_dict(self): + """Test ConfigContainer with callable fields converted to dict.""" + config = CallableConfigContainer() + result = config.to_dict() + + assert result["_target_"] == _target_qualname(CallableConfigContainer) + assert result["name"] == "callable_config" + + # Nested CallableDataclass hits the is_dataclass branch and becomes a dict + assert result["callable_data"]["_target_"] == _target_qualname(CallableDataclass) + assert result["callable_data"]["name"] == "callable_test" + assert result["callable_data"]["regular_value"] == 100 + + # Top-level callable fields pass through as-is + assert result["activation"] is activation_function + assert isinstance(result["partial_loss"], functools.partial) + assert result["partial_loss"].func is loss_function + assert result["partial_loss"].keywords == {"reduction": "none"} + assert result["torch_activation"] is torch.nn.functional.gelu + + def test_partial_function_handling(self): + """Test that partial objects pass through _convert_value_to_dict unchanged.""" + partial_func = functools.partial(loss_function, reduction="sum") + result = TestConfigContainer._convert_value_to_dict(partial_func) + + assert result is partial_func + + def test_various_callable_types(self): + """Test that all callable types pass through _convert_value_to_dict unchanged.""" + # Plain function + assert ( + TestConfigContainer._convert_value_to_dict(activation_function) is activation_function + ) + + # Partial — not a dataclass/list/dict so falls through as-is + partial_func = functools.partial(loss_function, reduction="mean") + assert TestConfigContainer._convert_value_to_dict(partial_func) is partial_func + + # Torch built-in function + assert ( + TestConfigContainer._convert_value_to_dict(torch.nn.functional.relu) + is torch.nn.functional.relu + ) + + # Lambda + fn = lambda x: x * 2 + assert TestConfigContainer._convert_value_to_dict(fn) is fn + + # Callable nn.Module instance — not a dataclass, falls through as-is + relu_instance = torch.nn.ReLU() + assert TestConfigContainer._convert_value_to_dict(relu_instance) is relu_instance + + def test_config_with_callables_roundtrip_behavior(self): + """Test that to_dict/from_dict roundtrip preserves all fields for callable configs.""" + config = CallableConfigContainer(name="roundtrip_test") + config_dict = config.to_dict() + + reconstructed = CallableConfigContainer.from_dict(config_dict) + + assert reconstructed.name == config.name + assert reconstructed.callable_data.name == config.callable_data.name + assert reconstructed.callable_data.regular_value == config.callable_data.regular_value + # Callables pass through as-is in to_dict, so they come back with the same identity + assert reconstructed.activation is config.activation + assert reconstructed.partial_loss.func is config.partial_loss.func + assert reconstructed.partial_loss.keywords == config.partial_loss.keywords + assert reconstructed.torch_activation is config.torch_activation + + def test_mixed_container_with_callables_and_regular_data(self): + """Test container mixing callable and regular data.""" + + @dataclass + class MixedConfig(ConfigContainerBase): + name: str = "mixed" + regular_list: list[str] = None + callable_func: callable = activation_function + nested_data: SimpleDataclass = None + + def __post_init__(self): + if self.regular_list is None: + self.regular_list = ["a", "b", "c"] + if self.nested_data is None: + self.nested_data = SimpleDataclass(name="nested", value=999) + + config = MixedConfig() + result = config.to_dict() + + # Verify mixed content handling + assert result["name"] == "mixed" + assert result["regular_list"] == ["a", "b", "c"] + assert result["nested_data"]["name"] == "nested" + assert result["nested_data"]["value"] == 999 + + # Callable fields pass through as-is + assert result["callable_func"] is activation_function + + def test_deepcopy_with_callables(self): + """Test deep copying ConfigContainer with callable fields.""" + config = CallableConfigContainer(name="deepcopy_test") + + # Verify original works + assert config.name == "deepcopy_test" + assert callable(config.activation) + assert callable(config.partial_loss) + + # Test deep copy + copied_config = copy.deepcopy(config) + + # Verify copy independence + assert copied_config is not config + assert copied_config.name == "deepcopy_test" + + # Verify callable fields are handled properly + assert callable(copied_config.activation) + assert callable(copied_config.partial_loss) + + # Test that functions still work + assert copied_config.activation(5) == 10 # test_activation_function multiplies by 2 + + # Modify original to verify independence + config.name = "modified" + assert copied_config.name == "deepcopy_test" diff --git a/tests/unit_tests/training/config/test_instantiate_utils.py b/tests/unit_tests/training/config/test_instantiate_utils.py new file mode 100644 index 00000000000..a1316b8064b --- /dev/null +++ b/tests/unit_tests/training/config/test_instantiate_utils.py @@ -0,0 +1,580 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. + +import enum +import functools +import logging +from unittest.mock import MagicMock, patch + +import pytest +from omegaconf import OmegaConf + +from megatron.training.config.instantiate_utils import ( + InstantiationException, + InstantiationMode, + _call_target, + _convert_node, + _convert_target_to_string, + _extract_pos_args, + _is_target, + _Keys, + _locate, + _prepare_input_dict_or_list, + _resolve_target, + instantiate, + instantiate_node, +) + + +@pytest.fixture(autouse=True) +def _disable_allowlist(): + """Temporarily disable allowlist to fully test instantiate logic with local test targets.""" + from megatron.training.config.instantiate_utils import target_allowlist + + target_allowlist.disable() + yield + target_allowlist.enable() + + +def _target_qualname(obj) -> str: + return f"{obj.__module__}.{obj.__qualname__}" + + +# Test classes and functions for instantiation testing +class TestClass: + """Test class for instantiation.""" + + def __init__(self, arg1=None, arg2=None, **kwargs): + self.arg1 = arg1 + self.arg2 = arg2 + self.kwargs = kwargs + + +def test_function(arg1=None, arg2=None, **kwargs): + """Test function for instantiation.""" + return {"arg1": arg1, "arg2": arg2, "kwargs": kwargs} + + +class TestInstantiationException: + """Test InstantiationException class.""" + + def test_instantiation_exception_creation(self): + """Test creating InstantiationException.""" + msg = "Test error message" + exc = InstantiationException(msg) + assert str(exc) == msg + assert isinstance(exc, Exception) + + +class TestInstantiationMode: + """Test InstantiationMode enum.""" + + def test_instantiation_mode_values(self): + """Test InstantiationMode enum values.""" + assert InstantiationMode.STRICT.value == "strict" + assert InstantiationMode.LENIENT.value == "lenient" + + +class TestKeys: + """Test _Keys enum.""" + + def test_keys_values(self): + """Test _Keys enum values.""" + assert _Keys.TARGET == "_target_" + assert _Keys.PARTIAL == "_partial_" + assert _Keys.CALL == "_call_" + assert _Keys.ARGS == "_args_" + assert _Keys.NAME == "_name_" + + +class TestInstantiate: + """Test instantiate function.""" + + def test_instantiate_none(self): + """Test instantiate with None config.""" + result = instantiate(None) + assert result is None + + def test_instantiate_simple_class(self): + """Test instantiating a simple class.""" + config = {"_target_": _target_qualname(TestClass), "arg1": "value1", "arg2": "value2"} + result = instantiate(config) + assert isinstance(result, TestClass) + assert result.arg1 == "value1" + assert result.arg2 == "value2" + + def test_instantiate_function(self): + """Test instantiating a function.""" + config = {"_target_": _target_qualname(test_function), "arg1": "value1", "arg2": "value2"} + result = instantiate(config) + expected = {"arg1": "value1", "arg2": "value2", "kwargs": {}} + assert result == expected + + def test_instantiate_with_args(self): + """Test instantiate with positional args.""" + config = {"_target_": _target_qualname(test_function), "_args_": ["pos1", "pos2"]} + result = instantiate(config) + expected = {"arg1": "pos1", "arg2": "pos2", "kwargs": {}} + assert result == expected + + def test_instantiate_with_partial(self): + """Test instantiate with partial=True.""" + config = {"_target_": _target_qualname(test_function), "_partial_": True, "arg1": "value1"} + result = instantiate(config) + assert isinstance(result, functools.partial) + actual_result = result(arg2="value2") + expected = {"arg1": "value1", "arg2": "value2", "kwargs": {}} + assert actual_result == expected + + def test_instantiate_with_call_false(self): + """Test instantiate with _call_=False.""" + config = {"_target_": _target_qualname(test_function), "_call_": False} + result = instantiate(config) + assert callable(result) + assert result == test_function + + def test_instantiate_with_call_false_and_extra_keys(self): + """Test instantiate with _call_=False and extra keys raises error.""" + config = { + "_target_": _target_qualname(test_function), + "_call_": False, + "extra_key": "value", + } + with pytest.raises(InstantiationException, match="_call_ was set to False"): + instantiate(config) + + def test_instantiate_with_kwargs_override(self): + """Test instantiate with kwargs override.""" + config = {"_target_": _target_qualname(test_function), "arg1": "original"} + result = instantiate(config, arg1="override", arg2="new") + expected = {"arg1": "override", "arg2": "new", "kwargs": {}} + assert result == expected + + def test_instantiate_list_config(self): + """Test instantiate with list config.""" + config = [ + {"_target_": _target_qualname(test_function), "arg1": "item1"}, + {"_target_": _target_qualname(test_function), "arg1": "item2"}, + ] + result = instantiate(config) + assert len(result) == 2 + assert result[0] == {"arg1": "item1", "arg2": None, "kwargs": {}} + assert result[1] == {"arg1": "item2", "arg2": None, "kwargs": {}} + + def test_instantiate_list_with_partial_raises_error(self): + """Test instantiate list with _partial_=True raises error.""" + config = ["item1", "item2"] + with pytest.raises(InstantiationException, match="_partial_ keyword is not compatible"): + instantiate(config, _partial_=True) + + def test_instantiate_invalid_config_type(self): + """Test instantiate with invalid config type.""" + with pytest.raises(InstantiationException, match="Cannot instantiate config of type"): + instantiate("invalid_config") + + def test_instantiate_strict_mode_error(self): + """Test instantiate in strict mode with error.""" + config = { + "_target_": _target_qualname(TestClass), + "nested": {"_target_": "non.existent.module.Class"}, + } + with pytest.raises(InstantiationException): + instantiate(config, mode=InstantiationMode.STRICT) + + def test_instantiate_lenient_mode_error(self): + """In lenient mode, nested resolution errors now propagate (no auto-None).""" + config = { + "_target_": _target_qualname(TestClass), + "nested": {"_target_": "non.existent.module.Class"}, + } + with pytest.raises(InstantiationException, match="Error locating target"): + instantiate(config, mode=InstantiationMode.LENIENT) + + def test_instantiate_with_omegaconf_dict(self): + """Test instantiate with OmegaConf DictConfig.""" + config = OmegaConf.create({"_target_": _target_qualname(TestClass), "arg1": "value1"}) + result = instantiate(config) + assert isinstance(result, TestClass) + assert result.arg1 == "value1" + + def test_instantiate_with_omegaconf_list(self): + """Test instantiate with OmegaConf ListConfig.""" + config = OmegaConf.create([{"_target_": _target_qualname(test_function), "arg1": "item1"}]) + result = instantiate(config) + assert len(result) == 1 + assert result[0] == {"arg1": "item1", "arg2": None, "kwargs": {}} + + +class TestInstantiateNode: + """Test instantiate_node function.""" + + def test_instantiate_node_none(self): + """Test instantiate_node with None.""" + result = instantiate_node(None) + assert result is None + + def test_instantiate_node_non_config(self): + """Test instantiate_node with non-config value.""" + result = instantiate_node("simple_string") + assert result == "simple_string" + + def test_instantiate_node_dict_without_target(self): + """Test instantiate_node with dict without _target_.""" + config = OmegaConf.create({"key1": "value1", "key2": "value2"}) + result = instantiate_node(config) + assert result == {"key1": "value1", "key2": "value2"} + + def test_instantiate_node_list(self): + """Test instantiate_node with list.""" + config = OmegaConf.create(["item1", "item2"]) + result = instantiate_node(config) + assert result == ["item1", "item2"] + + def test_instantiate_node_partial_not_bool_raises_error(self): + """Test instantiate_node with non-bool partial raises error.""" + config = OmegaConf.create({"_partial_": "not_bool"}) + with pytest.raises(TypeError, match="_partial_ flag must be a bool"): + instantiate_node(config) + + +class TestLocate: + """Test _locate function.""" + + def test_locate_valid_path(self): + """Test _locate with valid path.""" + result = _locate("builtins.str") + assert result == str + + def test_locate_empty_path(self): + """Test _locate with empty path.""" + with pytest.raises(ImportError, match="Empty path"): + _locate("") + + def test_locate_invalid_path(self): + """Test _locate with invalid path.""" + with pytest.raises(ImportError, match="Unable to import any module"): + _locate("non.existent.module") + + def test_locate_invalid_dotstring(self): + """Test _locate with invalid dotstring.""" + with pytest.raises(ValueError, match="invalid dotstring"): + _locate("invalid..path") + + def test_locate_relative_import(self): + """Test _locate with relative import.""" + with pytest.raises(ValueError, match="Relative imports are not supported"): + _locate(".relative.import") + + def test_locate_attribute_error(self): + """Test _locate with attribute that doesn't exist.""" + with pytest.raises(ImportError, match="Are you sure that"): + _locate("builtins.nonexistent_attribute") + + +class TestIsTarget: + """Test _is_target function.""" + + def test_is_target_dict_with_target(self): + """Test _is_target with dict containing _target_.""" + config = {"_target_": "some.target"} + assert _is_target(config) is True + + def test_is_target_dict_without_target(self): + """Test _is_target with dict not containing _target_.""" + config = {"other_key": "value"} + assert _is_target(config) is False + + def test_is_target_omegaconf_with_target(self): + """Test _is_target with OmegaConf containing _target_.""" + config = OmegaConf.create({"_target_": "some.target"}) + assert _is_target(config) is True + + def test_is_target_omegaconf_without_target(self): + """Test _is_target with OmegaConf not containing _target_.""" + config = OmegaConf.create({"other_key": "value"}) + assert _is_target(config) is False + + def test_is_target_non_dict(self): + """Test _is_target with non-dict value.""" + assert _is_target("string") is False + assert _is_target(123) is False + assert _is_target([]) is False + + +class TestCallTarget: + """Test _call_target function.""" + + def test_call_target_normal(self): + """Test _call_target with normal call.""" + result = _call_target(test_function, False, (), {"arg1": "value1"}, "test_key") + expected = {"arg1": "value1", "arg2": None, "kwargs": {}} + assert result == expected + + def test_call_target_partial(self): + """Test _call_target with partial=True.""" + result = _call_target(test_function, True, (), {"arg1": "value1"}, "test_key") + assert isinstance(result, functools.partial) + + def test_call_target_with_args(self): + """Test _call_target with positional args.""" + result = _call_target(test_function, False, ("pos1", "pos2"), {}, "test_key") + expected = {"arg1": "pos1", "arg2": "pos2", "kwargs": {}} + assert result == expected + + def test_call_target_error_normal(self): + """Test _call_target with error in normal call.""" + + def failing_function(): + raise ValueError("Test error") + + with pytest.raises(InstantiationException, match="Error in call to target"): + _call_target(failing_function, False, (), {}, "test_key") + + def test_call_target_error_partial(self): + """Test _call_target with error in partial creation.""" + # Create a mock that raises an error when used with functools.partial + mock_target = MagicMock() + mock_target.__module__ = "test_module" + mock_target.__qualname__ = "test_function" + + with patch("functools.partial", side_effect=ValueError("Partial error")): + with pytest.raises(InstantiationException, match="Error in creating partial"): + _call_target(mock_target, True, (), {}, "test_key") + + +class TestConvertTargetToString: + """Test _convert_target_to_string function.""" + + def test_convert_callable_to_string(self): + """Test converting callable to string.""" + result = _convert_target_to_string(test_function) + assert "test_function" in result + + def test_convert_non_callable_to_string(self): + """Test converting non-callable to string.""" + result = _convert_target_to_string("already_string") + assert result == "already_string" + + +class TestPrepareInputDictOrList: + """Test _prepare_input_dict_or_list function.""" + + def test_prepare_dict(self): + """Test preparing input dict.""" + input_dict = {"_target_": test_function, "key1": "value1", "nested": {"key2": "value2"}} + result = _prepare_input_dict_or_list(input_dict) + assert "_target_" in result + assert "test_function" in result["_target_"] + assert result["key1"] == "value1" + assert result["nested"]["key2"] == "value2" + + def test_prepare_list(self): + """Test preparing input list.""" + input_list = [{"_target_": test_function, "key1": "value1"}, ["nested_item"]] + result = _prepare_input_dict_or_list(input_list) + assert len(result) == 2 + assert "test_function" in result[0]["_target_"] + assert result[0]["key1"] == "value1" + assert result[1] == ["nested_item"] + + +class TestResolveTarget: + """Test _resolve_target function.""" + + def test_resolve_string_target(self): + """Test resolving string target.""" + result = _resolve_target("builtins.str", "test_key") + assert result == str + + def test_resolve_callable_target(self): + """Test resolving already callable target.""" + result = _resolve_target(test_function, "test_key") + assert result == test_function + + def test_resolve_invalid_string_target(self): + """Test resolving invalid string target.""" + with pytest.raises(InstantiationException, match="Error locating target"): + _resolve_target("invalid.target", "test_key") + + def test_resolve_non_callable_target(self): + """Test resolving non-callable target with check_callable=True.""" + with pytest.raises(InstantiationException, match="Expected a callable target"): + _resolve_target("builtins.__name__", "test_key", check_callable=True) + + def test_resolve_non_callable_target_no_check(self): + """Test resolving non-callable target with check_callable=False.""" + result = _resolve_target("builtins.__name__", "test_key", check_callable=False) + assert result == "builtins" + + +class TestExtractPosArgs: + """Test _extract_pos_args function.""" + + def test_extract_pos_args_no_input_args(self): + """Test extracting pos args with no input args.""" + kwargs = {"_args_": ["arg1", "arg2"], "key1": "value1"} + args, remaining_kwargs = _extract_pos_args((), kwargs) + assert args == ["arg1", "arg2"] + assert remaining_kwargs == {"key1": "value1"} + + def test_extract_pos_args_with_input_args(self): + """Test extracting pos args with input args override.""" + kwargs = {"_args_": ["config_arg1", "config_arg2"], "key1": "value1"} + input_args = ["input_arg1", "input_arg2"] + args, remaining_kwargs = _extract_pos_args(input_args, kwargs) + assert args == input_args + assert remaining_kwargs == {"key1": "value1"} + + def test_extract_pos_args_no_args_key(self): + """Test extracting pos args with no _args_ key.""" + kwargs = {"key1": "value1"} + args, remaining_kwargs = _extract_pos_args((), kwargs) + assert args == () + assert remaining_kwargs == {"key1": "value1"} + + def test_extract_pos_args_invalid_type(self): + """Test extracting pos args with invalid _args_ type.""" + kwargs = {"_args_": 123} # Integer is not a sequence + with pytest.raises(InstantiationException, match="Unsupported _args_ type"): + _extract_pos_args((), kwargs) + + +class TestConvertNode: + """Test _convert_node function.""" + + def test_convert_omegaconf_node(self): + """Test converting OmegaConf node.""" + config = OmegaConf.create({"key1": "value1", "key2": 2}) + result = _convert_node(config) + assert result == {"key1": "value1", "key2": 2} + assert not OmegaConf.is_config(result) + + def test_convert_non_config_node(self): + """Test converting non-config node.""" + value = {"key1": "value1"} + result = _convert_node(value) + assert result == value + + +class TestComplexScenarios: + """Test complex instantiation scenarios.""" + + def test_nested_instantiation(self): + """Test nested instantiation scenario.""" + config = { + "_target_": _target_qualname(TestClass), + "arg1": {"_target_": _target_qualname(test_function), "arg1": "nested_value"}, + "arg2": "simple_value", + } + result = instantiate(config) + assert isinstance(result, TestClass) + assert result.arg1 == {"arg1": "nested_value", "arg2": None, "kwargs": {}} + assert result.arg2 == "simple_value" + + def test_list_with_nested_targets(self): + """Test list with nested target instantiation.""" + config = [ + { + "_target_": _target_qualname(TestClass), + "arg1": {"_target_": _target_qualname(test_function), "arg1": "item1"}, + }, + "simple_item", + ] + result = instantiate(config) + assert len(result) == 2 + assert isinstance(result[0], TestClass) + assert result[0].arg1 == {"arg1": "item1", "arg2": None, "kwargs": {}} + assert result[1] == "simple_item" + + def test_missing_values_with_partial(self): + """Test missing values with partial instantiation.""" + config = OmegaConf.create( + { + "_target_": _target_qualname(test_function), + "_partial_": True, + "arg1": "value1", + "missing_arg": "???", # OmegaConf missing value + } + ) + OmegaConf.set_struct(config, True) + + result = instantiate(config) + assert isinstance(result, functools.partial) + # The missing value should be skipped in partial mode + actual_result = result(arg2="value2") + expected = {"arg1": "value1", "arg2": "value2", "kwargs": {}} + assert actual_result == expected + + +class DummyTarget: + def __init__(self, a: int, b: int = 0) -> None: + self.a = a + self.b = b + + +class KwTarget: + def __init__(self, **kwargs) -> None: + self.kwargs = dict(kwargs) + + +def test_drops_unexpected_kwargs_and_warns(caplog: pytest.LogCaptureFixture) -> None: + config = { + "_target_": _target_qualname(DummyTarget), + "a": 10, + "foo": 123, # unexpected key that should be dropped + } + + with caplog.at_level(logging.WARNING): + obj = instantiate(config) + + assert isinstance(obj, DummyTarget) + assert obj.a == 10 + # 'foo' is dropped; 'b' remains default + assert obj.b == 0 + + # Ensure a warning was emitted mentioning the dropped key + warnings = [rec.getMessage() for rec in caplog.records if rec.levelno == logging.WARNING] + assert any("Dropping unexpected config keys" in m for m in warnings) + assert any("foo" in m for m in warnings) + + +def test_allows_kwargs_when_target_accepts_var_kwargs(caplog: pytest.LogCaptureFixture) -> None: + config = {"_target_": _target_qualname(KwTarget), "foo": 1, "bar": 2} + + with caplog.at_level(logging.WARNING): + obj = instantiate(config) + + assert isinstance(obj, KwTarget) + assert obj.kwargs == {"foo": 1, "bar": 2} + + # No warning should be emitted for **kwargs targets + warnings = [rec.getMessage() for rec in caplog.records if rec.levelno == logging.WARNING] + assert not any("Dropping unexpected config keys" in m for m in warnings) + + +def test_raises_on_unexpected_kwargs_in_strict_mode() -> None: + config = {"_target_": _target_qualname(DummyTarget), "a": 10, "foo": 123} + + with pytest.raises(InstantiationException): + instantiate(config, mode=InstantiationMode.STRICT) + + +class TestEnum(enum.Enum): + A = 1 + B = 2 + + +class TestInstantiateEnum: + """Test instantiation of Enums.""" + + def test_instantiate_enum_with_args(self): + """Test instantiating an Enum with _args_.""" + config = {"_target_": _target_qualname(TestEnum), "_args_": [1]} + result = instantiate(config) + assert result == TestEnum.A + + def test_instantiate_enum_with_args_lenient(self): + """Test instantiating an Enum with _args_ in lenient mode (default).""" + config = {"_target_": _target_qualname(TestEnum), "_args_": [2]} + # This previously failed because _args_ was dropped in lenient mode + result = instantiate(config) + assert result == TestEnum.B diff --git a/tests/unit_tests/training/config/test_target_allowlist.py b/tests/unit_tests/training/config/test_target_allowlist.py new file mode 100644 index 00000000000..3be5f8c87e3 --- /dev/null +++ b/tests/unit_tests/training/config/test_target_allowlist.py @@ -0,0 +1,221 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. + +import pytest + +from megatron.training.config.instantiate_utils import ( + InstantiationException, + TargetAllowlist, + _resolve_target, + target_allowlist, +) + + +class TestTargetAllowlistIsAllowed: + """Tests for the TargetAllowlist.is_allowed() method.""" + + def test_allows_megatron_training_targets(self): + al = TargetAllowlist() + assert al.is_allowed("megatron.training.config.training_config.TrainingConfig") + assert al.is_allowed("megatron.training.config.container.PretrainConfigContainer") + + def test_allows_megatron_core_targets(self): + al = TargetAllowlist() + assert al.is_allowed("megatron.core.optimizer.OptimizerConfig") + assert al.is_allowed( + "megatron.core.distributed.distributed_data_parallel_config.DistributedDataParallelConfig" + ) + + def test_allows_torch_targets(self): + al = TargetAllowlist() + assert al.is_allowed("torch.float16") + assert al.is_allowed("torch.bfloat16") + assert al.is_allowed("torch.float32") + + def test_allows_transformers_targets(self): + al = TargetAllowlist() + assert al.is_allowed("transformers.GenerationConfig.from_dict") + assert al.is_allowed("transformers.LlamaConfig.from_dict") + + def test_allows_signal_targets(self): + al = TargetAllowlist() + assert al.is_allowed("signal.Signals") + + def test_allows_exact_functools_partial(self): + al = TargetAllowlist() + assert al.is_allowed("functools.partial") + + def test_blocks_os_system(self): + al = TargetAllowlist() + assert not al.is_allowed("os.system") + + def test_blocks_subprocess(self): + al = TargetAllowlist() + assert not al.is_allowed("subprocess.call") + assert not al.is_allowed("subprocess.Popen") + + def test_blocks_builtins(self): + al = TargetAllowlist() + assert not al.is_allowed("builtins.eval") + assert not al.is_allowed("builtins.exec") + assert not al.is_allowed("builtins.__import__") + + def test_blocks_shutil(self): + al = TargetAllowlist() + assert not al.is_allowed("shutil.rmtree") + + def test_blocks_importlib(self): + al = TargetAllowlist() + assert not al.is_allowed("importlib.import_module") + + def test_blocks_empty_string(self): + al = TargetAllowlist() + assert not al.is_allowed("") + + def test_blocks_partial_prefix_match(self): + """Ensure prefix matching doesn't match partial module names.""" + al = TargetAllowlist() + # "torchvision" starts with "torch" but not "torch." + assert not al.is_allowed("torchvision.models.resnet50") + + +class TestTargetAllowlistAddRemove: + """Tests for add/remove prefix and exact.""" + + def test_add_prefix(self): + al = TargetAllowlist() + assert not al.is_allowed("custom_lib.MyClass") + al.add_prefix("custom_lib.") + assert al.is_allowed("custom_lib.MyClass") + + def test_add_prefix_requires_trailing_dot(self): + al = TargetAllowlist() + with pytest.raises(ValueError, match="Prefix must end with '.'"): + al.add_prefix("custom_lib") + + def test_add_prefix_is_idempotent(self): + al = TargetAllowlist() + al.add_prefix("custom_lib.") + al.add_prefix("custom_lib.") + assert al.allowed_prefixes.count("custom_lib.") == 1 + + def test_remove_prefix(self): + al = TargetAllowlist() + al.add_prefix("custom_lib.") + assert al.is_allowed("custom_lib.MyClass") + al.remove_prefix("custom_lib.") + assert not al.is_allowed("custom_lib.MyClass") + + def test_remove_prefix_not_found_raises(self): + al = TargetAllowlist() + with pytest.raises(ValueError): + al.remove_prefix("nonexistent.") + + def test_add_exact(self): + al = TargetAllowlist() + assert not al.is_allowed("os.getcwd") + al.add_exact("os.getcwd") + assert al.is_allowed("os.getcwd") + # Other os.* targets still blocked + assert not al.is_allowed("os.system") + + def test_remove_exact(self): + al = TargetAllowlist() + al.add_exact("os.getcwd") + assert al.is_allowed("os.getcwd") + al.remove_exact("os.getcwd") + assert not al.is_allowed("os.getcwd") + + def test_remove_exact_nonexistent_is_noop(self): + al = TargetAllowlist() + al.remove_exact("nonexistent.target") # Should not raise + + +class TestTargetAllowlistEnableDisable: + """Tests for enable/disable and env var override.""" + + def test_disable_allows_everything(self): + al = TargetAllowlist() + al.disable() + assert al.is_allowed("os.system") + assert al.is_allowed("subprocess.call") + assert not al.enabled + + def test_enable_after_disable(self): + al = TargetAllowlist() + al.disable() + assert al.is_allowed("os.system") + al.enable() + assert not al.is_allowed("os.system") + assert al.enabled + + def test_properties(self): + al = TargetAllowlist() + assert isinstance(al.allowed_prefixes, tuple) + assert isinstance(al.allowed_exact, frozenset) + assert "functools.partial" in al.allowed_exact + assert "megatron.training." in al.allowed_prefixes + + +class TestResolveTargetAllowlistEnforcement: + """Tests that _resolve_target() enforces the allowlist.""" + + def test_blocked_string_target_raises(self): + with pytest.raises(InstantiationException, match="not in the allowlist"): + _resolve_target("os.system", "", check_callable=True) + + def test_blocked_target_error_message_contains_target_name(self): + with pytest.raises(InstantiationException, match="os.system"): + _resolve_target("os.system", "", check_callable=True) + + def test_blocked_target_error_message_contains_prefixes(self): + with pytest.raises(InstantiationException, match="megatron.training."): + _resolve_target("os.system", "", check_callable=True) + + def test_blocked_target_error_message_contains_remediation(self): + with pytest.raises(InstantiationException, match="add_prefix"): + _resolve_target("os.system", "", check_callable=True) + + def test_blocked_target_error_message_includes_full_key(self): + with pytest.raises(InstantiationException, match="full_key: my.config.key"): + _resolve_target("os.system", "my.config.key", check_callable=True) + + def test_nonstring_target_bypasses_allowlist(self): + """Already-resolved callables should not be blocked.""" + result = _resolve_target(int, "", check_callable=True) + assert result is int + + def test_allowed_target_resolves(self): + """Allowed targets should be resolved normally.""" + result = _resolve_target("functools.partial", "", check_callable=True) + import functools + + assert result is functools.partial + + +class TestResolveTargetClassAllowlistEnforcement: + """Tests that _resolve_target_class() in utils.py respects the allowlist.""" + + def test_blocked_target_returns_none(self): + from megatron.training.config.utils import _resolve_target_class + + result = _resolve_target_class("os.system") + assert result is None + + def test_allowed_target_resolves(self): + from megatron.training.config.utils import _resolve_target_class + + # This should resolve to the actual class + result = _resolve_target_class("megatron.training.config.instantiate_utils.TargetAllowlist") + assert result is TargetAllowlist + + +class TestModuleLevelSingleton: + """Tests for the module-level target_allowlist singleton.""" + + def test_singleton_is_enabled_by_default(self): + assert target_allowlist.enabled + + def test_singleton_has_default_prefixes(self): + assert "megatron.training." in target_allowlist.allowed_prefixes + assert "megatron.core." in target_allowlist.allowed_prefixes + assert "torch." in target_allowlist.allowed_prefixes diff --git a/tests/unit_tests/training/config/test_utils.py b/tests/unit_tests/training/config/test_utils.py new file mode 100644 index 00000000000..53c476268d2 --- /dev/null +++ b/tests/unit_tests/training/config/test_utils.py @@ -0,0 +1,266 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. + +from dataclasses import dataclass, field + +import pytest + +from megatron.training.config.container import ConfigContainerBase + + +@pytest.fixture(autouse=True) +def _disable_allowlist(): + """Temporarily disable allowlist to fully test utils logic with local test targets.""" + from megatron.training.config.instantiate_utils import target_allowlist + + target_allowlist.disable() + yield + target_allowlist.enable() + + +# Test dataclasses for testing +@dataclass +class SimpleDataclass: + """Simple dataclass for testing.""" + + name: str = "test" + value: int = 42 + + +@dataclass +class DataclassWithInitFalse: + """Dataclass with init=False field for testing backward compatibility.""" + + name: str = "test" + value: int = 42 + computed_field: str = field(init=False, default="computed") + + def __post_init__(self): + self.computed_field = f"computed_{self.name}" + + +@dataclass +class NestedDataclassWithInitFalse: + """Nested dataclass with init=False field.""" + + inner: DataclassWithInitFalse = None + metadata: dict = field(default_factory=dict) + cached_result: list = field(init=False, default_factory=list) + + +class TestBackwardCompatibility: + """Test suite for backward compatibility functions.""" + + def test_get_init_false_fields_with_init_false(self): + """Test _get_init_false_fields correctly identifies init=False fields.""" + from megatron.training.config.utils import _get_init_false_fields + + result = _get_init_false_fields(DataclassWithInitFalse) + assert "computed_field" in result + assert "name" not in result + assert "value" not in result + + def test_get_init_false_fields_no_init_false(self): + """Test _get_init_false_fields returns empty set for normal dataclass.""" + from megatron.training.config.utils import _get_init_false_fields + + result = _get_init_false_fields(SimpleDataclass) + assert result == frozenset() + + def test_get_init_false_fields_non_dataclass(self): + """Test _get_init_false_fields returns empty set for non-dataclass.""" + from megatron.training.config.utils import _get_init_false_fields + + result = _get_init_false_fields(str) + assert result == frozenset() + + def test_resolve_target_class_valid(self): + """Test _resolve_target_class resolves valid class path.""" + from megatron.training.config.utils import _resolve_target_class + + result = _resolve_target_class("megatron.training.config.container.ConfigContainerBase") + assert result is ConfigContainerBase + + def test_resolve_target_class_invalid(self): + """Test _resolve_target_class returns None for invalid path.""" + from megatron.training.config.utils import _resolve_target_class + + result = _resolve_target_class("nonexistent.module.ClassName") + assert result is None + + def test_resolve_target_class_malformed(self): + """Test _resolve_target_class handles malformed paths gracefully.""" + from megatron.training.config.utils import _resolve_target_class + + result = _resolve_target_class("no_dots") + assert result is None + + def test_sanitize_dataclass_config_removes_init_false_fields(self): + """Test sanitize_dataclass_config removes init=False fields.""" + from megatron.training.config.utils import sanitize_dataclass_config + + config = { + "_target_": "tests.unit_tests.training.config.test_utils.DataclassWithInitFalse", + "name": "test_name", + "value": 123, + "computed_field": "should_be_removed", + } + + result = sanitize_dataclass_config(config) + + assert "name" in result + assert "value" in result + assert "_target_" in result + assert "computed_field" not in result + + def test_sanitize_dataclass_config_preserves_normal_fields(self): + """Test sanitize_dataclass_config preserves fields without init=False.""" + from megatron.training.config.utils import sanitize_dataclass_config + + config = { + "_target_": "tests.unit_tests.training.config.test_utils.SimpleDataclass", + "name": "preserved", + "value": 999, + } + + result = sanitize_dataclass_config(config) + + assert result["name"] == "preserved" + assert result["value"] == 999 + assert result["_target_"] == config["_target_"] + + def test_sanitize_dataclass_config_handles_nested_configs(self): + """Test sanitize_dataclass_config recursively processes nested configs.""" + from megatron.training.config.utils import sanitize_dataclass_config + + config = { + "_target_": "tests.unit_tests.training.config.test_utils.NestedDataclassWithInitFalse", + "inner": { + "_target_": "tests.unit_tests.training.config.test_utils.DataclassWithInitFalse", + "name": "inner_test", + "value": 42, + "computed_field": "nested_computed_should_be_removed", + }, + "metadata": {"key": "value"}, + "cached_result": ["should", "be", "removed"], + } + + result = sanitize_dataclass_config(config) + + # Top-level init=False field removed + assert "cached_result" not in result + # Nested init=False field removed + assert "computed_field" not in result["inner"] + # Normal fields preserved + assert result["inner"]["name"] == "inner_test" + assert result["metadata"] == {"key": "value"} + + def test_sanitize_dataclass_config_handles_lists_of_configs(self): + """Test sanitize_dataclass_config processes lists containing configs.""" + from megatron.training.config.utils import sanitize_dataclass_config + + config = { + "_target_": "some.module.ListContainer", + "items": [ + { + "_target_": "tests.unit_tests.training.config.test_utils.DataclassWithInitFalse", + "name": "item1", + "computed_field": "remove_me", + }, + { + "_target_": "tests.unit_tests.training.config.test_utils.DataclassWithInitFalse", + "name": "item2", + "computed_field": "remove_me_too", + }, + ], + } + + result = sanitize_dataclass_config(config) + + assert "computed_field" not in result["items"][0] + assert "computed_field" not in result["items"][1] + assert result["items"][0]["name"] == "item1" + assert result["items"][1]["name"] == "item2" + + def test_sanitize_dataclass_config_no_target(self): + """Test sanitize_dataclass_config handles dicts without _target_.""" + from megatron.training.config.utils import sanitize_dataclass_config + + config = {"key": "value", "number": 42} + result = sanitize_dataclass_config(config) + + assert result == config + + def test_sanitize_dataclass_config_non_dict_input(self): + """Test sanitize_dataclass_config handles non-dict input.""" + from megatron.training.config.utils import sanitize_dataclass_config + + assert sanitize_dataclass_config("string") == "string" + assert sanitize_dataclass_config(42) == 42 + assert sanitize_dataclass_config(None) is None + + def test_sanitize_dataclass_config_unresolvable_target(self): + """Test sanitize_dataclass_config handles unresolvable _target_.""" + from megatron.training.config.utils import sanitize_dataclass_config + + config = {"_target_": "nonexistent.module.Class", "field1": "value1", "field2": "value2"} + + result = sanitize_dataclass_config(config) + + # All fields preserved when target can't be resolved + assert result == config + + def test_sanitize_dataclass_config_sanitizes_model(self): + """Test sanitize_dataclass_config sanitizes model section with init=False fields.""" + from megatron.training.config.utils import sanitize_dataclass_config + + run_config = { + "model": { + "_target_": "tests.unit_tests.training.config.test_utils.DataclassWithInitFalse", + "name": "model_name", + "value": 100, + "computed_field": "should_be_removed", + }, + "training": {"lr": 0.001}, + "tokenizer": {"type": "sentencepiece"}, + } + + result = sanitize_dataclass_config(run_config) + + assert "computed_field" not in result["model"] + assert result["model"]["name"] == "model_name" + assert result["training"] == {"lr": 0.001} + assert result["tokenizer"] == {"type": "sentencepiece"} + + def test_sanitize_dataclass_config_sanitizes_all_sections(self): + """Test sanitize_dataclass_config sanitizes all sections, not just model.""" + from megatron.training.config.utils import sanitize_dataclass_config + + run_config = { + "model": { + "_target_": "tests.unit_tests.training.config.test_utils.DataclassWithInitFalse", + "name": "model_name", + "computed_field": "should_be_removed_from_model", + }, + "training": { + "_target_": "tests.unit_tests.training.config.test_utils.DataclassWithInitFalse", + "name": "training_config", + "computed_field": "should_be_removed_from_training", + }, + "data": { + "_target_": "tests.unit_tests.training.config.test_utils.DataclassWithInitFalse", + "name": "data_config", + "computed_field": "should_be_removed_from_data", + }, + } + + result = sanitize_dataclass_config(run_config) + + # All sections should have init=False fields removed + assert "computed_field" not in result["model"] + assert "computed_field" not in result["training"] + assert "computed_field" not in result["data"] + + # Regular fields should be preserved + assert result["model"]["name"] == "model_name" + assert result["training"]["name"] == "training_config" + assert result["data"]["name"] == "data_config" diff --git a/tests/unit_tests/training/config/test_yaml_utils.py b/tests/unit_tests/training/config/test_yaml_utils.py new file mode 100644 index 00000000000..12526eff2d6 --- /dev/null +++ b/tests/unit_tests/training/config/test_yaml_utils.py @@ -0,0 +1,336 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. + +import enum +import functools +import os +import tempfile +from dataclasses import dataclass +from unittest.mock import Mock + +import pytest +import yaml + +from megatron.training.config.yaml_utils import ( + _enum_representer, + _function_representer, + _generation_config_representer, + _partial_representer, + _safe_object_representer, + _torch_dtype_representer, + safe_yaml_representers, +) + + +class TestEnum(enum.Enum): + """Test enum""" + + VALUE1 = "test_value1" + VALUE2 = "test_value2" + + +@dataclass +class TestDataclass: + """Test dataclass""" + + name: str + value: int + + +class TestClass: + def __init__(self, name: str = "test"): + """Test class""" + self.name = name + + +def test_function(): + """Test function""" + return "test" + + +class TestSafeYamlRepresenters: + """Test the safe_yaml_representers context manager.""" + + def test_context_manager_adds_and_removes_representers(self): + """Test that representers are properly added and removed.""" + # Save original state + original_representers = yaml.SafeDumper.yaml_representers.copy() + original_multi_representers = yaml.SafeDumper.yaml_multi_representers.copy() + + # Use context manager + with safe_yaml_representers(): + # Check that new representers were added + assert functools.partial in yaml.SafeDumper.yaml_representers + assert enum.Enum in yaml.SafeDumper.yaml_multi_representers + assert type(lambda: ...) in yaml.SafeDumper.yaml_representers + + # Check that original representers were restored + assert yaml.SafeDumper.yaml_representers == original_representers + assert yaml.SafeDumper.yaml_multi_representers == original_multi_representers + + def test_context_manager_handles_exceptions(self): + """Test that representers are restored even if an exception occurs.""" + original_representers = yaml.SafeDumper.yaml_representers.copy() + original_multi_representers = yaml.SafeDumper.yaml_multi_representers.copy() + + try: + with safe_yaml_representers(): + raise ValueError("Test exception") + except ValueError: + pass + + # Check that original representers were still restored + assert yaml.SafeDumper.yaml_representers == original_representers + assert yaml.SafeDumper.yaml_multi_representers == original_multi_representers + + +class TestFunctionRepresenter: + """Test the _function_representer function.""" + + def test_function_representation(self): + """Test representing a function in YAML.""" + dumper = yaml.SafeDumper("") + result = _function_representer(dumper, test_function) + + # The result should be a MappingNode + assert hasattr(result, "value") + + # Parse the represented data using the context manager + with safe_yaml_representers(): + data = yaml.safe_load(yaml.safe_dump({"test": test_function})) + assert "_target_" in data["test"] + assert "_call_" in data["test"] + assert data["test"]["_call_"] is False + assert "test_function" in data["test"]["_target_"] + + +class TestPartialRepresenter: + """Test the _partial_representer function.""" + + def test_partial_without_keywords(self): + """Test representing a partial function without keyword arguments.""" + partial_func = functools.partial(test_function) + dumper = yaml.SafeDumper("") + _ = _partial_representer(dumper, partial_func) + + # Parse the represented data + with safe_yaml_representers(): + data = yaml.safe_load(yaml.safe_dump({"test": partial_func})) + assert "_target_" in data["test"] + assert "_partial_" in data["test"] + assert data["test"]["_partial_"] is True + assert "_args_" in data["test"] + assert data["test"]["_args_"] == [] + + def test_partial_with_args_and_kwargs(self): + """Test representing a partial function with arguments and keyword arguments.""" + + def example_func(a, b, c=None): + return a + b + (c or 0) + + partial_func = functools.partial(example_func, 1, c=10) + dumper = yaml.SafeDumper("") + _ = _partial_representer(dumper, partial_func) + + # Parse the represented data + with safe_yaml_representers(): + data = yaml.safe_load(yaml.safe_dump({"test": partial_func})) + assert data["test"]["_args_"] == [1] + assert data["test"]["c"] == 10 + + +class TestEnumRepresenter: + """Test the _enum_representer function.""" + + def test_enum_representation(self): + """Test representing an enum value in YAML.""" + enum_value = TestEnum.VALUE1 + dumper = yaml.SafeDumper("") + _ = _enum_representer(dumper, enum_value) + + # Parse the represented data + with safe_yaml_representers(): + data = yaml.safe_load(yaml.safe_dump({"test": enum_value})) + assert "_target_" in data["test"] + assert "_call_" in data["test"] + assert data["test"]["_call_"] is True + assert "_args_" in data["test"] + assert data["test"]["_args_"] == ["test_value1"] + assert "_name_" in data["test"] + assert data["test"]["_name_"] == "VALUE1" + assert "TestEnum" in data["test"]["_target_"] + + +class TestSafeObjectRepresenter: + """Test the _safe_object_representer function.""" + + def test_object_with_qualname(self): + """Test representing an object that has __qualname__ attribute.""" + obj = test_function + dumper = yaml.SafeDumper("") + _ = _safe_object_representer(dumper, obj) + + # Parse the represented data + with safe_yaml_representers(): + data = yaml.safe_load(yaml.safe_dump({"test": obj})) + assert "_target_" in data["test"] + assert "_call_" in data["test"] + assert data["test"]["_call_"] is False + + def test_object_without_qualname(self): + """Test representing an object that doesn't have __qualname__ attribute.""" + obj = TestClass("test") + dumper = yaml.SafeDumper("") + _ = _safe_object_representer(dumper, obj) + + # Parse the represented data + with safe_yaml_representers(): + data = yaml.safe_load(yaml.safe_dump({"test": obj})) + assert "_target_" in data["test"] + assert "_call_" in data["test"] + assert data["test"]["_call_"] is True + assert "TestClass" in data["test"]["_target_"] + + +class TestTorchDtypeRepresenter: + """Test the _torch_dtype_representer function.""" + + def test_torch_dtype_representation(self): + """Test representing a torch dtype in YAML.""" + import torch + + dtype = torch.float32 + dumper = yaml.SafeDumper("") + _ = _torch_dtype_representer(dumper, dtype) + + # Parse the represented data + with safe_yaml_representers(): + data = yaml.safe_load(yaml.safe_dump({"test": dtype})) + assert "_target_" in data["test"] + assert "_call_" in data["test"] + assert data["test"]["_call_"] is False + assert "float32" in data["test"]["_target_"] + + def test_torch_dtype_representer_function(self): + """Test the torch dtype representer function directly.""" + # Create a mock torch dtype + mock_dtype = Mock() + mock_dtype.__str__ = Mock(return_value="torch.float32") + + dumper = yaml.SafeDumper("") + result = _torch_dtype_representer(dumper, mock_dtype) + + # Test the direct result from the representer function + # The result should be a MappingNode + assert hasattr(result, "value") + + # Parse the result from the direct function call + # We need to manually construct the data that would be generated + test_data = {"_target_": "torch.float32", "_call_": False} + yaml_result = yaml.safe_dump(test_data) + data = yaml.safe_load(yaml_result) + + assert "_target_" in data + assert "_call_" in data + assert data["_call_"] is False + + +class TestGenerationConfigRepresenter: + """Test the _generation_config_representer function.""" + + def test_generation_config_representation(self): + """Test representing a GenerationConfig object in YAML.""" + try: + from transformers import GenerationConfig + + config = GenerationConfig(max_length=100, temperature=0.8) + dumper = yaml.SafeDumper("") + _ = _generation_config_representer(dumper, config) + + # Parse the represented data + with safe_yaml_representers(): + data = yaml.safe_load(yaml.safe_dump({"test": config})) + assert "_target_" in data["test"] + assert "_call_" in data["test"] + assert data["test"]["_call_"] is True + assert "config_dict" in data["test"] + assert "from_dict" in data["test"]["_target_"] + except ImportError: + pytest.skip("Transformers not available") + + def test_generation_config_representer_function(self): + """Test the generation config representer function directly.""" + # Create a mock GenerationConfig + mock_config = Mock() + mock_config.__class__.__qualname__ = "GenerationConfig" + mock_config.__class__.__module__ = "transformers.generation.configuration_utils" + mock_config.to_dict = Mock(return_value={"max_length": 100, "temperature": 0.8}) + + dumper = yaml.SafeDumper("") + result = _generation_config_representer(dumper, mock_config) + + # Test the direct result from the representer function + # The result should be a MappingNode + assert hasattr(result, "value") + + # Parse the result from the direct function call + # We need to manually construct the data that would be generated + test_data = { + "_target_": "transformers.generation.configuration_utils.GenerationConfig.from_dict", + "_call_": True, + "config_dict": {"max_length": 100, "temperature": 0.8}, + } + yaml_result = yaml.safe_dump(test_data) + data = yaml.safe_load(yaml_result) + + assert "_target_" in data + assert "_call_" in data + assert data["_call_"] is True + assert "config_dict" in data + assert "from_dict" in data["_target_"] + + +class TestIntegration: + """Integration tests for the YAML utils functionality.""" + + def test_complex_object_serialization(self): + """Test serializing a complex object with multiple types.""" + complex_obj = { + "function": test_function, + "enum": TestEnum.VALUE1, + "partial": functools.partial(test_function), + "dataclass": TestDataclass("test", 42), + "regular_data": {"key": "value", "number": 123}, + } + + with safe_yaml_representers(): + result = yaml.safe_dump(complex_obj) + + assert isinstance(result, str) + + # Verify all components are serialized + assert "_target_:" in result + assert "test_function" in result + assert "TestEnum" in result + assert "_partial_:" in result + assert "TestDataclass" in result + assert "_call_:" in result + assert "key: value" in result + + def test_roundtrip_with_simple_objects(self): + """Test that simple objects can be serialized and deserialized.""" + simple_obj = { + "string": "test", + "number": 42, + "list": [1, 2, 3], + "dict": {"nested": "value"}, + } + + with safe_yaml_representers(): + yaml_str = yaml.safe_dump(simple_obj) + + reconstructed = yaml.safe_load(yaml_str) + + assert reconstructed["string"] == "test" + assert reconstructed["number"] == 42 + assert reconstructed["list"] == [1, 2, 3] + assert reconstructed["dict"]["nested"] == "value" From f4a49cf0b6e775a81f3f83eec318d2d14d66f01d Mon Sep 17 00:00:00 2001 From: Teodor-Dumitru Ene <34819528+tdene@users.noreply.github.com> Date: Tue, 28 Apr 2026 20:42:58 -0500 Subject: [PATCH 045/105] Fix conflict with inference graphs (#4504) --- megatron/core/transformer/cuda_graphs.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/megatron/core/transformer/cuda_graphs.py b/megatron/core/transformer/cuda_graphs.py index c2ebe655955..06e1d8958fe 100644 --- a/megatron/core/transformer/cuda_graphs.py +++ b/megatron/core/transformer/cuda_graphs.py @@ -829,12 +829,14 @@ def create_fwd_graph(self, args, kwargs, outputs=None, clone_inputs=True): # Save buffers, grads, and other variables that may be affected by graph warmup. # For example, megatron/core/transformer/moe/router.py's expert_bias is a persistent # buffer updated each forward pass by '_apply_expert_bias()'. So we need to ensure - # graph capture's forward passes do not corrupt its value. - buffer_backup = [] - for buf in self.base_module.buffers(): - buffer_backup.append(buf.clone()) - + # graph capture's forward passes do not corrupt its value. Inference is not affected + # (no known buffer mutators) and would add new buffers (lazy MoE _fc1_weight/ + # _fc2_weight) that misalign the positional restore. if self.training and torch.is_grad_enabled(): + buffer_backup = [] + for buf in self.base_module.buffers(): + buffer_backup.append(buf.clone()) + grad_backup = [] for param in self.base_module.parameters(): grad_backup.append(param.main_grad.clone() if hasattr(param, "main_grad") else None) @@ -1041,9 +1043,9 @@ def clone_ten(ten): if main_grad_copy is not None: param.main_grad.copy_(main_grad_copy) - # restore cached buffers - for buf_copy, buf in zip(buffer_backup, self.base_module.buffers()): - buf.copy_(buf_copy) + # restore cached buffers + for buf_copy, buf in zip(buffer_backup, self.base_module.buffers()): + buf.copy_(buf_copy) if is_moe: for name in tracker: From 251c6e9def3845c0dee269939391146a3514114d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 29 Apr 2026 09:43:46 +0000 Subject: [PATCH 046/105] chore: rotate oncall schedule --- .github/oncall_schedule.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/oncall_schedule.json b/.github/oncall_schedule.json index 86b1bc73cfb..1e546d239b9 100644 --- a/.github/oncall_schedule.json +++ b/.github/oncall_schedule.json @@ -1,8 +1,4 @@ [ - { - "user": "asolergi-nv", - "date": "2026-04-22" - }, { "user": "maanug-nv", "date": "2026-04-29" @@ -46,5 +42,9 @@ { "user": "wujingyue", "date": "2026-07-08" + }, + { + "user": "Connor-XY", + "date": "2026-07-15" } ] From c5201a026ac27e42d0633ff36d2f1c56e1585009 Mon Sep 17 00:00:00 2001 From: Antoni-Joan Solergibert Date: Wed, 29 Apr 2026 19:32:46 +0200 Subject: [PATCH 047/105] Add tools/prepare_cache.py for offline GPT dataset cache preparation (#4080) Co-authored-by: Claude Opus 4.6 (1M context) --- docs/user-guide/data-loading.md | 2 + megatron/core/datasets/blended_dataset.py | 5 +- megatron/core/datasets/readme.md | 16 +- pretrain_gpt.py | 8 +- pretrain_hybrid.py | 8 +- tests/unit_tests/data/test_prepare_cache.py | 288 ++++++++++++++++++++ tools/prepare_cache.py | 214 +++++++++++++++ 7 files changed, 531 insertions(+), 10 deletions(-) create mode 100644 tests/unit_tests/data/test_prepare_cache.py create mode 100644 tools/prepare_cache.py diff --git a/docs/user-guide/data-loading.md b/docs/user-guide/data-loading.md index 1f0d544317c..b60cd685cf2 100644 --- a/docs/user-guide/data-loading.md +++ b/docs/user-guide/data-loading.md @@ -72,6 +72,8 @@ If your later training job does not set `--global-batch-size`, or you are prepar This keeps the prepared cache aligned with the sample counts expected by training. +> **Unsupported configurations:** `tools/prepare_cache.py` does not support `--mock-data`, `--sft`, `--fim-data`, or `--step-batch-size-schedule`. Using any of these will cause the script to exit with an error. + ### Step 3: Optionally pre-build per-dataset metadata When blending many datasets, generate the `--per-dataset-sequences-path` JSON ahead of time to avoid one metadata read per file prefix at startup: diff --git a/megatron/core/datasets/blended_dataset.py b/megatron/core/datasets/blended_dataset.py index 802a9770506..9b642ee1ff3 100644 --- a/megatron/core/datasets/blended_dataset.py +++ b/megatron/core/datasets/blended_dataset.py @@ -150,7 +150,10 @@ def _build_indices(self) -> Tuple[numpy.ndarray, numpy.ndarray]: else: cache_hit = False - if not path_to_cache or (not cache_hit and torch.distributed.get_rank() == 0): + if not path_to_cache or ( + not cache_hit + and (not torch.distributed.is_initialized() or torch.distributed.get_rank() == 0) + ): log_single_rank( logger, logging.INFO, f"Build and save the {type(self).__name__} indices" ) diff --git a/megatron/core/datasets/readme.md b/megatron/core/datasets/readme.md index 452bf24e4a2..58721b7471b 100644 --- a/megatron/core/datasets/readme.md +++ b/megatron/core/datasets/readme.md @@ -192,12 +192,24 @@ To query the `BlendedDataset` for the _k_-th sample we do the following To save time during initialization, each index is built/cached sequentially on one process rank and subsequently loaded in parallel on other process ranks. The cached indices are unique to a hash generated in the `BlendedDataset.__init__` function. +## Offline cache preparation + +For GPT-style training, the dataset caches described above can be prepared ahead of time with `tools/prepare_cache.py` instead of waiting for rank 0 to build them during training startup. + +The script reuses the normal dataset construction path used by `pretrain_gpt.py` and `pretrain_mamba.py`, including `GPTDataset`, `BlendedDataset`, and `BlendedMegatronDatasetBuilder`. It accepts the usual dataset arguments, supports blends and per-split dataset definitions, and requires `--data-cache-path` so the generated cache can later be reused by training. + +This is especially useful for large blends or many file prefixes, where building the document, sample, and shuffle indices can take several minutes and leave all GPUs idle while rank 0 performs CPU-only work. + +If the later training job does not specify `--global-batch-size` (which is needed to determine the dataset size and splits), you should specify `--prepare-cache-world-size` to explicitly set the world size used during cache preparation. + +`tools/prepare_cache.py` does not support `--mock-data`, `--sft`, `--fim-data`, or `--step-batch-size-schedule`. + ## Fast DataLoader initialization -Especially for large-scale runs, DataLoader initialization can take several minutes, since it involves opening and memory-mapping multiple files and can significantly stress the filesystem. To speed up this process, we have developed the following three optimizations, controlled by configuration flags": +Especially for large-scale runs, DataLoader initialization can take several minutes, since it involves opening and memory-mapping multiple files and can significantly stress the filesystem. To speed up this process, we have developed the following three optimizations, controlled by configuration flags: - `--dataloader-fast-cache-load`: This option assumes that the dataset cache already exists in the specified `--data-cache-path`. When enabled, it speeds up the creation process by removing synchronization points and file check assertions. - `--dataloader-defer-npy-index-mmap`: This option also assumes that the dataset cache already exists in the specified `--data-cache-path`. When enabled, it defers the memory mapping of the dataset indexes (.npy files) until their first access. We recommend using this configuration together with `--num-workers` > 0 so that the DataLoader prefetches the next batches of data, thereby hiding the cost of index memory mapping. - - `--per-dataset-sequences-path`: With this configuration, we specify the JSON file generated by the `tools/build_sequences_per_dataset.py` script. This script generates a single file containing the required metadata from all the specified file prefixes. This configuration is especially useful when dealing with hundreds to thousands of file prefixes, since it requires only a single `open` operation instead of one per file prefix. \ No newline at end of file + - `--per-dataset-sequences-path`: With this configuration, we specify the JSON file generated by the `tools/build_sequences_per_dataset.py` script. This script generates a single file containing the required metadata from all the specified file prefixes. This configuration is especially useful when dealing with hundreds to thousands of file prefixes, since it requires only a single `open` operation instead of one per file prefix. diff --git a/pretrain_gpt.py b/pretrain_gpt.py index e9f6987f3f0..6cff899009a 100644 --- a/pretrain_gpt.py +++ b/pretrain_gpt.py @@ -17,7 +17,7 @@ warnings.filterwarnings("ignore", category=FutureWarning) from functools import partial -from typing import List, Optional, Tuple +from typing import Any, List, Optional, Tuple import torch @@ -282,7 +282,7 @@ def is_dataset_built_on_rank(vp_stage=None, is_packed_sequence=False): ) -def core_gpt_dataset_config_from_args(args): +def core_gpt_dataset_config_from_args(args: Any) -> GPTDatasetConfig: tokenizer = build_tokenizer(args) # Sometimes --data-path is too long, instead we parse it from a file. @@ -319,7 +319,7 @@ def core_gpt_dataset_config_from_args(args): "defer_npy_index_mmap": args.dataloader_defer_npy_index_mmap, "context_parallel_size": args.context_parallel_size, "data_parallel_size": args.data_parallel_size, - "sequence_parallel_size": args.tensor_model_parallel_size*args.sequence_parallel, + "sequence_parallel_size": args.tensor_model_parallel_size * args.sequence_parallel, "hybrid_context_parallel": args.hybrid_context_parallel, } @@ -405,7 +405,7 @@ def get_embedding_ranks(pp_ranks: List[int]): set_startup_timestamps(program_start=_PROGRAM_START_TIME, main_entry=_MAIN_ENTRY_TIME) # Temporary for transition to core datasets - train_valid_test_datasets_provider.is_distributed = True + setattr(train_valid_test_datasets_provider, "is_distributed", True) # Optionally enable inprocess restart on pretrain pretrain, store = inprocess_restart.maybe_wrap_for_inprocess_restart(pretrain) diff --git a/pretrain_hybrid.py b/pretrain_hybrid.py index 293b8c76935..9ab52ed11ab 100644 --- a/pretrain_hybrid.py +++ b/pretrain_hybrid.py @@ -16,7 +16,7 @@ warnings.filterwarnings("ignore", category=FutureWarning) from functools import partial -from typing import List, Optional, Tuple +from typing import Any, List, Optional, Tuple import torch @@ -275,7 +275,7 @@ def is_dataset_built_on_rank(vp_stage=None, is_packed_sequence=False): return is_first_or_last_pipeline_stage(vp_stage) -def core_gpt_dataset_config_from_args(args): +def core_gpt_dataset_config_from_args(args: Any) -> GPTDatasetConfig: tokenizer = build_tokenizer(args) # Sometimes --data-path is too long, instead we parse it from a file. @@ -294,6 +294,8 @@ def core_gpt_dataset_config_from_args(args): blend=blend, blend_per_split=blend_per_split, split=args.split, + multiple_validation_sets=args.multiple_validation_sets, + full_validation=args.full_validation, num_dataset_builder_threads=args.num_dataset_builder_threads, path_to_cache=args.data_cache_path, mmap_bin_files=args.mmap_bin_files, @@ -353,7 +355,7 @@ def train_valid_test_datasets_provider(train_val_test_num_samples, vp_stage=None set_startup_timestamps(program_start=_PROGRAM_START_TIME, main_entry=_MAIN_ENTRY_TIME) # Temporary for transition to core datasets - train_valid_test_datasets_provider.is_distributed = True + setattr(train_valid_test_datasets_provider, "is_distributed", True) # Optionally enable inprocess restart on pretrain pretrain, store = inprocess_restart.maybe_wrap_for_inprocess_restart(pretrain) diff --git a/tests/unit_tests/data/test_prepare_cache.py b/tests/unit_tests/data/test_prepare_cache.py new file mode 100644 index 00000000000..164001121b2 --- /dev/null +++ b/tests/unit_tests/data/test_prepare_cache.py @@ -0,0 +1,288 @@ +# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +import os +import random +from argparse import Namespace + +import pytest +import torch + +from megatron.core.datasets.blended_dataset import BlendedDataset +from megatron.core.datasets.blended_megatron_dataset_builder import BlendedMegatronDatasetBuilder +from megatron.core.datasets.gpt_dataset import GPTDataset +from megatron.core.datasets.indexed_dataset import DType, IndexedDatasetBuilder +from megatron.core.datasets.utils import compile_helpers +from megatron.core.tokenizers.utils.build_tokenizer import build_tokenizer +from tests.unit_tests.dist_checkpointing import TempNamedDir +from tests.unit_tests.test_utilities import Utils +from tools.prepare_cache import ( + _normalize_prepare_cache_args, + build_dataset_caches, + core_gpt_dataset_config_from_args, +) + + +def _build_null_tokenizer(vocab_size: int = 2048): + return build_tokenizer( + Namespace( + vocab_size=vocab_size, + tokenizer_type="NullTokenizer", + padded_vocab_size=None, + rank=0, + make_vocab_size_divisible_by=128, + tensor_model_parallel_size=1, + ) + ) + + +def _initialize_test_environment() -> None: + if torch.distributed.is_available(): + Utils.initialize_distributed() + if torch.distributed.get_rank() == 0: + compile_helpers() + torch.distributed.barrier() + else: + compile_helpers() + + +def _create_file_prefixes(tokenizer, dataset_dir, number_of_files: int = 4) -> list[str]: + os.makedirs(dataset_dir, exist_ok=True) + + file_prefixes = [] + for i in range(number_of_files): + file_prefix = os.path.join(dataset_dir, f"file_{i}") + builder = IndexedDatasetBuilder( + file_prefix + ".bin", dtype=DType.optimal_dtype(tokenizer.vocab_size) + ) + + for j in range(32): + tokens = [int((i * 97 + j * 13 + k) % tokenizer.vocab_size) for k in range(64)] + builder.add_document(tokens, [len(tokens)]) + + builder.finalize(file_prefix + ".idx") + file_prefixes.append(file_prefix) + + return file_prefixes + + +def _create_shared_file_prefixes(tokenizer, dataset_dir, number_of_files: int = 4) -> list[str]: + if not torch.distributed.is_initialized() or torch.distributed.get_rank() == 0: + file_prefixes = _create_file_prefixes(tokenizer, dataset_dir, number_of_files) + else: + file_prefixes = [os.path.join(dataset_dir, f"file_{i}") for i in range(number_of_files)] + + if torch.distributed.is_initialized(): + torch.distributed.barrier() + + random.seed(1234) # NOTE(asolergi-nv): re-sync random state across all ranks + + return file_prefixes + + +def _build_prepare_cache_args(file_prefixes, data_cache_path, **overrides): + args = dict( + seed=1234, + seq_length=16, + split="70,20,10", + data_path=file_prefixes, + train_data_path=None, + valid_data_path=None, + test_data_path=None, + per_split_data_args_path=None, + data_args_path=None, + per_dataset_sequences_path=None, + data_cache_path=str(data_cache_path), + mmap_bin_files=True, + reset_position_ids=False, + reset_attention_mask=False, + eod_mask_loss=False, + create_attention_mask_in_dataloader=False, + object_storage_cache_path=None, + mid_level_dataset_surplus=0.005, + allow_ambiguous_pad_tokens=False, + dataloader_fast_cache_load=True, + dataloader_defer_npy_index_mmap=True, + context_parallel_size=1, + data_parallel_size=4, + tensor_model_parallel_size=1, + sequence_parallel=False, + hybrid_context_parallel=False, + multiple_validation_sets=False, + full_validation=False, + num_dataset_builder_threads=1, + tokenizer_type="NullTokenizer", + vocab_size=2048, + padded_vocab_size=None, + make_vocab_size_divisible_by=128, + rank=0, + world_size=4, + train_samples=None, + train_iters=4, + skip_train=False, + eval_iters=2, + eval_interval=2, + start_eval_at_iter=None, + global_batch_size=8, + phase_transition_iterations=None, + iteration=0, + mock_data=False, + sft=False, + fim_data=False, + step_batch_size_schedule=None, + ) + args.update(overrides) + return Namespace(**args) + + +def test_prepare_cache_builds_blended_dataset_cache(tmp_path_dist_ckpt): + _initialize_test_environment() + + tokenizer = _build_null_tokenizer() + + with TempNamedDir( + tmp_path_dist_ckpt / "test_prepare_cache_builds_blended_dataset_cache", sync=True + ) as temp_dir: + file_prefixes = _create_shared_file_prefixes(tokenizer, os.path.join(temp_dir, "dataset")) + args = _build_prepare_cache_args(file_prefixes, temp_dir / "cache") + + summary = build_dataset_caches(args) + + assert args.dataloader_fast_cache_load is False + assert args.dataloader_defer_npy_index_mmap is False + assert summary["train_valid_test_num_samples"] == (32, 48, 16) + assert summary["train_dataset_length"] == 32 + assert summary["valid_dataset_length"] == 48 + assert summary["test_dataset_length"] == 16 + assert list((temp_dir / "cache").glob("*document_index.npy")) + assert list((temp_dir / "cache").glob("*dataset_index.npy")) + + +def test_prepare_cache_world_size_override(): + args = Namespace(rank=11, world_size=1, prepare_cache_world_size=8) + + _normalize_prepare_cache_args(args) + + assert args.rank == 0 + assert args.world_size == 8 + + +def test_prepare_cache_builds_and_hits_per_split_dataset_cache(tmp_path_dist_ckpt): + _initialize_test_environment() + + tokenizer = _build_null_tokenizer() + + with TempNamedDir( + tmp_path_dist_ckpt / "test_prepare_cache_builds_and_hits_per_split_dataset_cache", sync=True + ) as temp_dir: + file_prefixes = _create_shared_file_prefixes(tokenizer, os.path.join(temp_dir, "dataset")) + args = _build_prepare_cache_args( + None, + temp_dir / "cache", + split=None, + data_path=None, + train_data_path=[50, file_prefixes[0], 50, file_prefixes[1]], + valid_data_path=[file_prefixes[2]], + test_data_path=[file_prefixes[3]], + ) + + summary = build_dataset_caches(args) + + assert summary["train_valid_test_num_samples"] == (32, 48, 16) + assert list((temp_dir / "cache").glob("*description.txt")) + + slow_args = _build_prepare_cache_args( + None, + temp_dir / "cache", + split=None, + data_path=None, + train_data_path=[50, file_prefixes[0], 50, file_prefixes[1]], + valid_data_path=[file_prefixes[2]], + test_data_path=[file_prefixes[3]], + dataloader_fast_cache_load=False, + dataloader_defer_npy_index_mmap=False, + ) + slow_config = core_gpt_dataset_config_from_args(slow_args) + train_slow, valid_slow, test_slow = BlendedMegatronDatasetBuilder( + GPTDataset, list(summary["train_valid_test_num_samples"]), lambda: True, slow_config + ).build() + + fast_args = _build_prepare_cache_args( + None, + temp_dir / "cache", + split=None, + data_path=None, + train_data_path=[50, file_prefixes[0], 50, file_prefixes[1]], + valid_data_path=[file_prefixes[2]], + test_data_path=[file_prefixes[3]], + dataloader_fast_cache_load=True, + dataloader_defer_npy_index_mmap=True, + ) + fast_config = core_gpt_dataset_config_from_args(fast_args) + train_fast, valid_fast, test_fast = BlendedMegatronDatasetBuilder( + GPTDataset, list(summary["train_valid_test_num_samples"]), lambda: True, fast_config + ).build() + + assert isinstance(train_fast, BlendedDataset) + assert train_fast.dataset_index is None + assert train_fast.dataset_sample_index is None + assert isinstance(valid_fast, GPTDataset) + assert valid_fast.document_index is None + assert valid_fast.sample_index is None + assert valid_fast.shuffle_index is None + assert isinstance(test_fast, GPTDataset) + assert test_fast.document_index is None + assert test_fast.sample_index is None + assert test_fast.shuffle_index is None + + assert summary["train_dataset_length"] == len(train_slow) == len(train_fast) == 32 + assert summary["valid_dataset_length"] == len(valid_slow) == len(valid_fast) + assert summary["test_dataset_length"] == len(test_slow) == len(test_fast) + assert summary["valid_dataset_length"] >= summary["train_valid_test_num_samples"][1] + assert summary["test_dataset_length"] >= summary["train_valid_test_num_samples"][2] + assert torch.all(train_slow[0]["tokens"] == train_fast[0]["tokens"]) + assert torch.all(valid_slow[0]["tokens"] == valid_fast[0]["tokens"]) + assert torch.all(test_slow[0]["tokens"] == test_fast[0]["tokens"]) + + assert train_fast.dataset_index is not None + assert train_fast.dataset_sample_index is not None + assert valid_fast.document_index is not None + assert valid_fast.sample_index is not None + assert valid_fast.shuffle_index is not None + assert test_fast.document_index is not None + assert test_fast.sample_index is not None + assert test_fast.shuffle_index is not None + + +@pytest.mark.parametrize( + ("flag_name", "flag_value", "message"), + [ + ("mock_data", True, "--mock-data"), + ("sft", True, "--sft"), + ("fim_data", True, "--fim-data"), + ("step_batch_size_schedule", [(0, 8)], "--step-batch-size-schedule"), + ], +) +def test_prepare_cache_rejects_unsupported_modes(tmp_path, flag_name, flag_value, message): + args = _build_prepare_cache_args([], tmp_path / "cache", **{flag_name: flag_value}) + + with pytest.raises(ValueError, match=message): + build_dataset_caches(args) + + +def test_prepare_cache_builds_with_train_samples(tmp_path_dist_ckpt): + _initialize_test_environment() + + tokenizer = _build_null_tokenizer() + + with TempNamedDir( + tmp_path_dist_ckpt / "test_prepare_cache_builds_with_train_samples", sync=True + ) as temp_dir: + file_prefixes = _create_shared_file_prefixes(tokenizer, os.path.join(temp_dir, "dataset")) + args = _build_prepare_cache_args( + file_prefixes, temp_dir / "cache", train_iters=None, train_samples=32 + ) + + summary = build_dataset_caches(args) + + assert args.train_iters == 32 // args.global_batch_size + assert summary["train_valid_test_num_samples"][0] == 32 diff --git a/tools/prepare_cache.py b/tools/prepare_cache.py new file mode 100644 index 00000000000..98d57db498a --- /dev/null +++ b/tools/prepare_cache.py @@ -0,0 +1,214 @@ +# Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +"""Prepare GPT dataset caches ahead of training. + +Unsupported configurations: + --mock-data, --sft, --fim-data, --step-batch-size-schedule +""" + +import argparse +import json +from typing import Any, Dict, List, Optional, Tuple + +from megatron.core.datasets.blended_megatron_dataset_builder import BlendedMegatronDatasetBuilder +from megatron.core.datasets.gpt_dataset import GPTDataset, GPTDatasetConfig +from megatron.core.datasets.utils import compile_helpers +from megatron.core.tokenizers.utils.build_tokenizer import build_tokenizer +from megatron.training import get_train_valid_test_num_samples +from megatron.training.arguments import parse_args, validate_args +from megatron.training.global_vars import set_args, unset_global_variables +from megatron.training.training import update_train_iters +from megatron.training.utils import get_blend_and_blend_per_split + +try: + from megatron.post_training.arguments import add_modelopt_args + + has_nvidia_modelopt = True +except ImportError: + has_nvidia_modelopt = False + + +def add_prepare_cache_args(parser: argparse.ArgumentParser) -> argparse.ArgumentParser: + """Add cache-preparation specific arguments.""" + + group = parser.add_argument_group(title="prepare cache") + group.add_argument( + "--prepare-cache-world-size", + type=int, + default=None, + help=( + "Optional override for the effective world size used to derive data-parallel size and " + "dataset sample counts during cache preparation." + ), + ) + return parser + + +def _extra_args_provider(parser: argparse.ArgumentParser) -> argparse.ArgumentParser: + parser = add_prepare_cache_args(parser) + if has_nvidia_modelopt: + parser = add_modelopt_args(parser) + return parser + + +def _normalize_prepare_cache_args(args: Any) -> None: + """Apply cache-preparation specific argument normalization.""" + + args.rank = 0 + + if args.prepare_cache_world_size is not None: + if args.prepare_cache_world_size <= 0: + raise ValueError("--prepare-cache-world-size must be positive") + args.world_size = args.prepare_cache_world_size + + +def _validate_prepare_cache_args(args: Any) -> None: + """Validate options that are intentionally unsupported for offline cache prep.""" + + if args.data_cache_path is None: + raise ValueError("--data-cache-path must be provided for cache preparation") + if args.mock_data: + raise ValueError("--mock-data is not supported by tools/prepare_cache.py") + if getattr(args, "sft", False): + raise ValueError("--sft is not supported by tools/prepare_cache.py") + if getattr(args, "fim_data", False): + raise ValueError("--fim-data is not supported by tools/prepare_cache.py") + if getattr(args, "step_batch_size_schedule", None) is not None: + raise ValueError( + "--step-batch-size-schedule is not supported by tools/prepare_cache.py" + ) + + +def _disable_cache_load_only_flags(args: Any) -> Dict[str, bool]: + """Disable flags that only make sense when consuming an existing cache.""" + + ignored = { + "dataloader_fast_cache_load": bool(args.dataloader_fast_cache_load), + "dataloader_defer_npy_index_mmap": bool(args.dataloader_defer_npy_index_mmap), + } + args.dataloader_fast_cache_load = False + args.dataloader_defer_npy_index_mmap = False + return ignored + + +def _get_dataset_length(dataset: Optional[Any]) -> Optional[Any]: + if dataset is None: + return None + if isinstance(dataset, list): + return [len(ds) if ds is not None else None for ds in dataset] + return len(dataset) + + +def _print_effective_configuration( + args: Any, train_valid_test_num_samples: Any, ignored_flags: Dict[str, bool] +) -> None: + print("> preparing dataset cache with the following effective values:") + print(f" world size: {args.world_size}") + print(f" data parallel size: {args.data_parallel_size}") + print(f" global batch size: {args.global_batch_size}") + print(f" cache path: {args.data_cache_path}") + print(" > datasets target sizes (minimum size):") + print(f" train: {train_valid_test_num_samples[0]}") + print(f" validation: {train_valid_test_num_samples[1]}") + print(f" test: {train_valid_test_num_samples[2]}") + if ignored_flags["dataloader_fast_cache_load"]: + print("> ignoring --dataloader-fast-cache-load during cache preparation") + if ignored_flags["dataloader_defer_npy_index_mmap"]: + print("> ignoring --dataloader-defer-npy-index-mmap during cache preparation") + + +def core_gpt_dataset_config_from_args(args: Any) -> GPTDatasetConfig: + """Build the explicit GPTDatasetConfig used for offline cache preparation.""" + + tokenizer = build_tokenizer(args) + + blend: Optional[Tuple[List[str], Optional[List[float]]]] + blend_per_split: Optional[List[Optional[Tuple[List[str], Optional[List[float]]]]]] + blend, blend_per_split = get_blend_and_blend_per_split(args) + + sequences_per_dataset = None + if args.per_dataset_sequences_path is not None: + with open(args.per_dataset_sequences_path, "r") as f: + sequences_per_dataset = json.load(f) + + return GPTDatasetConfig( + random_seed=args.seed, + sequence_length=args.seq_length, + blend=blend, + blend_per_split=blend_per_split, + split=args.split, + multiple_validation_sets=args.multiple_validation_sets, + full_validation=args.full_validation, + num_dataset_builder_threads=args.num_dataset_builder_threads, + path_to_cache=args.data_cache_path, + mmap_bin_files=args.mmap_bin_files, + tokenizer=tokenizer, + reset_position_ids=args.reset_position_ids, + reset_attention_mask=args.reset_attention_mask, + eod_mask_loss=args.eod_mask_loss, + create_attention_mask=args.create_attention_mask_in_dataloader, + object_storage_cache_path=args.object_storage_cache_path, + mid_level_dataset_surplus=args.mid_level_dataset_surplus, + allow_ambiguous_pad_tokens=args.allow_ambiguous_pad_tokens, + fast_cache_load=args.dataloader_fast_cache_load, + sequences_per_dataset=sequences_per_dataset, + defer_npy_index_mmap=args.dataloader_defer_npy_index_mmap, + context_parallel_size=args.context_parallel_size, + data_parallel_size=args.data_parallel_size, + sequence_parallel_size=args.tensor_model_parallel_size * args.sequence_parallel, + hybrid_context_parallel=args.hybrid_context_parallel, + ) + + +def build_dataset_caches(args: Any) -> Dict[str, Any]: + """Build the dataset caches for the plain GPTDataset path.""" + + _validate_prepare_cache_args(args) + ignored_flags = _disable_cache_load_only_flags(args) + + unset_global_variables() + set_args(args) + + try: + # Derive train_iters from --train-samples when needed (pretrain() does the same). + update_train_iters(args) + train_valid_test_num_samples = get_train_valid_test_num_samples() + _print_effective_configuration(args, train_valid_test_num_samples, ignored_flags) + + compile_helpers() + + config = core_gpt_dataset_config_from_args(args) + train_ds, valid_ds, test_ds = BlendedMegatronDatasetBuilder( + GPTDataset, train_valid_test_num_samples, lambda: True, config + ).build() + + print("> finished preparing dataset cache") + print(f" train dataset length: {_get_dataset_length(train_ds)}") + print(f" validation dataset length: {_get_dataset_length(valid_ds)}") + print(f" test dataset length: {_get_dataset_length(test_ds)}") + + return { + "world_size": args.world_size, + "data_parallel_size": args.data_parallel_size, + "global_batch_size": args.global_batch_size, + "train_valid_test_num_samples": tuple(train_valid_test_num_samples), + "train_dataset_length": _get_dataset_length(train_ds), + "valid_dataset_length": _get_dataset_length(valid_ds), + "test_dataset_length": _get_dataset_length(test_ds), + } + finally: + unset_global_variables() + + +def main() -> Dict[str, Any]: + args = parse_args( + extra_args_provider=_extra_args_provider, + ignore_unknown_args=False, + ) + _normalize_prepare_cache_args(args) + validate_args(args, defaults={"tokenizer_type": "GPT2BPETokenizer"}) + return build_dataset_caches(args) + + +if __name__ == "__main__": + main() From cb3d5d9c40eac51fb641446cc246a5efcc66c949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?oliver=20k=C3=B6nig?= Date: Wed, 29 Apr 2026 19:48:45 +0200 Subject: [PATCH 048/105] [build] fix: move mamba-ssm and causal-conv1d to optional [ssm] extra (#4517) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: oliver könig Signed-off-by: Oliver Koenig Co-authored-by: Claude Sonnet 4.6 --- docker/Dockerfile.ci.dev | 2 +- pyproject.toml | 14 +- uv.lock | 499 ++++++++++++++++++--------------------- 3 files changed, 237 insertions(+), 278 deletions(-) diff --git a/docker/Dockerfile.ci.dev b/docker/Dockerfile.ci.dev index 5e3d7419b3a..bcb2e5940a9 100644 --- a/docker/Dockerfile.ci.dev +++ b/docker/Dockerfile.ci.dev @@ -41,7 +41,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \ export NVTE_CUDA_ARCHS="80;90;100" uv venv ${UV_PROJECT_ENVIRONMENT} --system-site-packages uv sync --only-group build - uv sync --extra ${IMAGE_TYPE} --extra mlm --link-mode copy --locked \ + uv sync --extra ${IMAGE_TYPE} --extra mlm --extra ssm --extra te --link-mode copy --locked \ --no-install-package torch \ --no-install-package torchvision \ --no-install-package triton \ diff --git a/pyproject.toml b/pyproject.toml index f7611078b9e..05a1843222e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -89,15 +89,12 @@ mlm = [ dev = [ "nvidia-modelopt[torch]; sys_platform != 'darwin'", - "transformer-engine[pytorch,core_cu13]", "nvidia-resiliency-ext", "tqdm", "einops~=0.8", "tensorstore~=0.1,!=0.1.46,!=0.1.72", "multi-storage-client~=0.27", "opentelemetry-api~=1.33.1", - "mamba-ssm~=2.2", - "causal-conv1d~=1.5", "flash-linear-attention~=0.4.0", "megatron-energon[av_decode]~=6.0", "av", @@ -119,8 +116,6 @@ lts = [ "tensorstore~=0.1,!=0.1.46,!=0.1.72", "multi-storage-client~=0.27", "opentelemetry-api~=1.33.1", - "mamba-ssm~=2.2", - "causal-conv1d~=1.5", "megatron-energon[av_decode]~=6.0", "av", "flashinfer-python~=0.5.0", @@ -131,6 +126,15 @@ lts = [ "emerging_optimizers", ] +te = [ + "transformer-engine[pytorch,core_cu13]", +] + +ssm = [ + "mamba-ssm~=2.2", + "causal-conv1d~=1.5", +] + [dependency-groups] test = [ "coverage", diff --git a/uv.lock b/uv.lock index 16d960dcc2b..59527b68746 100644 --- a/uv.lock +++ b/uv.lock @@ -2,51 +2,24 @@ version = 1 revision = 2 requires-python = ">=3.12" resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] conflicts = [[ { package = "megatron-core", extra = "dev" }, @@ -625,8 +598,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ninja" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts'" }, - { name = "torch", marker = "sys_platform == 'never'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, + { name = "torch", marker = "sys_platform == 'never' or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/63/15/ec51d77a2df03ee93410f8ee97fceeb7181da213813c51243e9dd6d7e144/causal_conv1d-1.6.1.tar.gz", hash = "sha256:e4a697ec2db3906f012e675125569f8b510b4559bc53e3095143d91369e1221b", size = 29426, upload-time = "2026-03-10T08:56:35.305Z" } @@ -1009,37 +982,37 @@ wheels = [ [package.optional-dependencies] cublas = [ - { name = "nvidia-cublas", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nvidia-cublas", marker = "sys_platform == 'linux'" }, ] cudart = [ - { name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux'" }, ] cufft = [ - { name = "nvidia-cufft", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nvidia-cufft", marker = "sys_platform == 'linux'" }, ] cufile = [ { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, ] cupti = [ - { name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux'" }, ] curand = [ - { name = "nvidia-curand", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nvidia-curand", marker = "sys_platform == 'linux'" }, ] cusolver = [ - { name = "nvidia-cusolver", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nvidia-cusolver", marker = "sys_platform == 'linux'" }, ] cusparse = [ - { name = "nvidia-cusparse", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nvidia-cusparse", marker = "sys_platform == 'linux'" }, ] nvjitlink = [ - { name = "nvidia-nvjitlink", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nvidia-nvjitlink", marker = "sys_platform == 'linux'" }, ] nvrtc = [ - { name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux'" }, ] nvtx = [ - { name = "nvidia-nvtx", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "nvidia-nvtx", marker = "sys_platform == 'linux'" }, ] [[package]] @@ -1548,46 +1521,22 @@ name = "fsspec" version = "2026.2.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] sdist = { url = "https://files.pythonhosted.org/packages/51/7c/f60c259dcbf4f0c47cc4ddb8f7720d2dcdc8888c8e5ad84c73ea4531cc5b/fsspec-2026.2.0.tar.gz", hash = "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff", size = 313441, upload-time = "2026-02-05T21:50:53.743Z" } wheels = [ @@ -1604,11 +1553,8 @@ name = "fsspec" version = "2026.3.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32'", ] sdist = { url = "https://files.pythonhosted.org/packages/e1/cf/b50ddf667c15276a9ab15a70ef5f257564de271957933ffea49d2cdbcdfb/fsspec-2026.3.0.tar.gz", hash = "sha256:1ee6a0e28677557f8c2f994e3eea77db6392b4de9cd1f5d7a9e87a0ae9d01b41", size = 313547, upload-time = "2026-03-27T19:11:14.892Z" } wheels = [ @@ -2480,11 +2426,11 @@ dependencies = [ { name = "einops" }, { name = "ninja" }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, - { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "setuptools" }, - { name = "torch", marker = "sys_platform == 'never'" }, + { name = "torch", marker = "sys_platform == 'never' or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "transformers" }, - { name = "triton", marker = "sys_platform == 'never'" }, + { name = "triton", marker = "sys_platform == 'never' or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/34/67/ec89aa703da194a813e35d2ea2de8f74a7ce6991a120a29f3a0c5e30d4b9/mamba_ssm-2.3.1.tar.gz", hash = "sha256:4d529477ad94753962216d583fc8f1c127c717b7d7c875d6bbb9376366d0d761", size = 121707, upload-time = "2026-03-10T09:27:34.798Z" } @@ -2641,7 +2587,6 @@ dependencies = [ [package.optional-dependencies] dev = [ { name = "av" }, - { name = "causal-conv1d" }, { name = "datasets", version = "2.2.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.14' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-dev') or (python_full_version < '3.14' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "datasets", version = "4.8.4", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.14' and extra == 'extra-13-megatron-core-dev') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "einops" }, @@ -2650,7 +2595,6 @@ dev = [ { name = "flash-linear-attention" }, { name = "flashinfer-python" }, { name = "hypercorn" }, - { name = "mamba-ssm" }, { name = "megatron-energon", extra = ["av-decode"], marker = "extra == 'extra-13-megatron-core-dev'" }, { name = "multi-storage-client" }, { name = "nvidia-modelopt", marker = "(sys_platform != 'darwin' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, @@ -2664,19 +2608,16 @@ dev = [ { name = "tensorstore", version = "0.1.74", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "tensorstore", version = "0.1.82", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "tqdm" }, - { name = "transformer-engine", marker = "extra == 'extra-13-megatron-core-dev'" }, { name = "wget" }, ] lts = [ { name = "av" }, - { name = "causal-conv1d" }, { name = "datasets", version = "2.2.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.14' and sys_platform == 'win32' and extra == 'extra-13-megatron-core-lts') or (python_full_version < '3.14' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "datasets", version = "4.8.4", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.14' and extra == 'extra-13-megatron-core-lts') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-lts') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "einops" }, { name = "emerging-optimizers" }, { name = "fastapi" }, { name = "flashinfer-python" }, - { name = "mamba-ssm" }, { name = "megatron-energon", extra = ["av-decode"], marker = "extra == 'extra-13-megatron-core-lts'" }, { name = "multi-storage-client" }, { name = "onnxscript", version = "0.7.0", source = { registry = "https://pypi.org/simple" } }, @@ -2693,6 +2634,13 @@ mlm = [ { name = "transformers" }, { name = "wandb" }, ] +ssm = [ + { name = "causal-conv1d" }, + { name = "mamba-ssm" }, +] +te = [ + { name = "transformer-engine" }, +] training = [ { name = "accelerate" }, { name = "flask-restful" }, @@ -2761,8 +2709,7 @@ requires-dist = [ { name = "accelerate", marker = "extra == 'training'" }, { name = "av", marker = "extra == 'dev'" }, { name = "av", marker = "extra == 'lts'" }, - { name = "causal-conv1d", marker = "extra == 'dev'", specifier = "~=1.5" }, - { name = "causal-conv1d", marker = "extra == 'lts'", specifier = "~=1.5" }, + { name = "causal-conv1d", marker = "extra == 'ssm'", specifier = "~=1.5" }, { name = "datasets", marker = "extra == 'dev'" }, { name = "datasets", marker = "extra == 'lts'" }, { name = "einops", marker = "extra == 'dev'", specifier = "~=0.8" }, @@ -2777,8 +2724,7 @@ requires-dist = [ { name = "flask-restful", marker = "extra == 'mlm'" }, { name = "flask-restful", marker = "extra == 'training'" }, { name = "hypercorn", marker = "extra == 'dev'" }, - { name = "mamba-ssm", marker = "extra == 'dev'", specifier = "~=2.2" }, - { name = "mamba-ssm", marker = "extra == 'lts'", specifier = "~=2.2" }, + { name = "mamba-ssm", marker = "extra == 'ssm'", specifier = "~=2.2" }, { name = "megatron-energon", extras = ["av-decode"], marker = "extra == 'dev'", specifier = "~=6.0" }, { name = "megatron-energon", extras = ["av-decode"], marker = "extra == 'lts'", specifier = "~=6.0" }, { name = "multi-storage-client", marker = "extra == 'dev'", specifier = "~=0.27" }, @@ -2803,7 +2749,7 @@ requires-dist = [ { name = "torch", specifier = ">=2.6.0" }, { name = "tqdm", marker = "extra == 'dev'" }, { name = "tqdm", marker = "extra == 'lts'" }, - { name = "transformer-engine", extras = ["core-cu13", "pytorch"], marker = "extra == 'dev'", git = "https://github.com/NVIDIA/TransformerEngine.git?rev=f031cf87bd054c7558b887df7bed93975456667f" }, + { name = "transformer-engine", extras = ["core-cu13", "pytorch"], marker = "extra == 'te'", git = "https://github.com/NVIDIA/TransformerEngine.git?rev=f031cf87bd054c7558b887df7bed93975456667f" }, { name = "transformers", marker = "extra == 'mlm'" }, { name = "transformers", marker = "extra == 'training'" }, { name = "wandb", marker = "extra == 'mlm'" }, @@ -2811,7 +2757,7 @@ requires-dist = [ { name = "wget", marker = "extra == 'dev'" }, { name = "wget", marker = "extra == 'lts'" }, ] -provides-extras = ["training", "mlm", "dev", "lts"] +provides-extras = ["training", "mlm", "dev", "lts", "te", "ssm"] [package.metadata.requires-dev] build = [ @@ -2916,7 +2862,7 @@ resolution-markers = [ "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fd/15/76f86faa0902836cc133939732f7611ace68cf54148487a99c539c272dc8/ml_dtypes-0.4.1.tar.gz", hash = "sha256:fad5f2de464fd09127e49b7fd1252b9006fb43d2edc1ff112d390c324af5ca7a", size = 692594, upload-time = "2024-09-13T19:07:11.624Z" } wheels = [ @@ -2952,7 +2898,7 @@ resolution-markers = [ ] dependencies = [ { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0e/4a/c27b42ed9b1c7d13d9ba8b6905dece787d6259152f2309338aed29b2447b/ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453", size = 692314, upload-time = "2025-11-17T22:32:31.031Z" } wheels = [ @@ -3363,33 +3309,24 @@ name = "numpy" version = "2.4.4" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587, upload-time = "2026-03-29T13:22:01.298Z" } wheels = [ @@ -3454,7 +3391,7 @@ version = "0.2.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml" }, - { name = "torch", marker = "sys_platform == 'never'" }, + { name = "torch", marker = "sys_platform == 'never' or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/8a/86/94188e03e5d4dd7b73c390b0cddcde5618b3799c18e327b2bf15763f6137/nvdlfw_inspect-0.2.2-py3-none-any.whl", hash = "sha256:8a4dc2814c5a4cd19ae304170b9bfa514538ef3c3eb243a45a82404ec3cb279d", size = 30964, upload-time = "2025-12-03T10:52:01.933Z" }, @@ -3505,7 +3442,7 @@ name = "nvidia-cudnn-cu13" version = "9.19.0.56" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas" }, + { name = "nvidia-cublas", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4", size = 433781201, upload-time = "2026-02-03T20:40:53.805Z" }, @@ -3534,7 +3471,7 @@ name = "nvidia-cufft" version = "12.0.0.61" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink" }, + { name = "nvidia-nvjitlink", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, @@ -3566,9 +3503,9 @@ name = "nvidia-cusolver" version = "12.0.4.66" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas" }, - { name = "nvidia-cusparse" }, - { name = "nvidia-nvjitlink" }, + { name = "nvidia-cublas", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "nvidia-cusparse", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "nvidia-nvjitlink", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, @@ -3581,7 +3518,7 @@ name = "nvidia-cusparse" version = "12.6.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink" }, + { name = "nvidia-nvjitlink", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, @@ -3770,10 +3707,10 @@ resolution-markers = [ "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "protobuf", marker = "python_full_version >= '3.13'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.13'" }, + { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, + { name = "protobuf", marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, + { name = "typing-extensions", marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5b/bf/b0a63ee9f3759dcd177b28c6f2cb22f2aecc6d9b3efecaabc298883caa5f/onnx-1.19.0.tar.gz", hash = "sha256:aa3f70b60f54a29015e41639298ace06adf1dd6b023b9b30f1bca91bb0db9473", size = 11949859, upload-time = "2025-08-27T02:34:27.107Z" } wheels = [ @@ -3823,7 +3760,7 @@ resolution-markers = [ dependencies = [ { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" } }, { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "protobuf" }, { name = "typing-extensions" }, ] @@ -3866,10 +3803,10 @@ resolution-markers = [ "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "onnx", version = "1.19.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.13'" }, + { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, + { name = "onnx", version = "1.19.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, + { name = "typing-extensions", marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/af/4a/7ea3952e556e7281b8bfe7f7fce016a13fdac85544d6d6af8ebca5cae160/onnx_ir-0.1.8.tar.gz", hash = "sha256:85ea59eaf165b2b107788193480a260e2723cfc7a1dac1bde7085fd0b7e380d7", size = 108961, upload-time = "2025-09-05T15:45:33.887Z" } wheels = [ @@ -3903,7 +3840,7 @@ resolution-markers = [ dependencies = [ { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" } }, { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "onnx", version = "1.21.0", source = { registry = "https://pypi.org/simple" } }, { name = "sympy" }, { name = "typing-extensions" }, @@ -3932,12 +3869,12 @@ resolution-markers = [ "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "onnx", version = "1.19.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "onnx-ir", version = "0.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.13'" }, + { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, + { name = "onnx", version = "1.19.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, + { name = "onnx-ir", version = "0.1.8", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, + { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, + { name = "typing-extensions", marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f5/2f/0bb2b6ca727e4d5173f640527f402ab4225def4bc8d667269b83047be8c4/onnxscript-0.5.0.tar.gz", hash = "sha256:4aba215e1f80fbcd07ba0d97d6bca96797fc3e9639eacb5434d35317ce1406aa", size = 588762, upload-time = "2025-09-12T16:57:46.484Z" } wheels = [ @@ -3971,11 +3908,11 @@ resolution-markers = [ dependencies = [ { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" } }, { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "onnx", version = "1.21.0", source = { registry = "https://pypi.org/simple" } }, { name = "onnx-ir", version = "0.2.1", source = { registry = "https://pypi.org/simple" } }, { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, - { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9b/99/fd948eba63ba65b52265a4cd09a14f96bb9f5b730fcef58876c4358bf406/onnxscript-0.7.0.tar.gz", hash = "sha256:c95ed7b339b02cface56ee27689565c46612e1fc542c562298dddfdad5268dc5", size = 612032, upload-time = "2026-04-20T17:09:19.775Z" } @@ -4069,15 +4006,24 @@ name = "opentelemetry-api" version = "1.41.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version < '3.13' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version < '3.13' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ { name = "importlib-metadata", marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, @@ -4127,15 +4073,24 @@ name = "opentelemetry-exporter-prometheus" version = "0.62b1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version < '3.13' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version < '3.13' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ { name = "opentelemetry-api", version = "1.41.1", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, @@ -4198,15 +4153,24 @@ name = "opentelemetry-sdk" version = "1.41.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version < '3.13' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version < '3.13' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ { name = "opentelemetry-api", version = "1.41.1", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, @@ -4256,15 +4220,24 @@ name = "opentelemetry-semantic-conventions" version = "0.62b1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'win32'", - "python_full_version < '3.13' and sys_platform == 'win32'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten'", - "python_full_version < '3.13' and sys_platform == 'emscripten'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ { name = "opentelemetry-api", version = "1.41.1", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts')" }, @@ -4362,33 +4335,24 @@ name = "packaging" version = "26.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } wheels = [ @@ -4467,33 +4431,24 @@ name = "pandas" version = "3.0.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and sys_platform == 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and sys_platform == 'emscripten' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", - "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-13-megatron-core-dev' and extra != 'extra-13-megatron-core-lts'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.14' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine != 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and platform_machine == 's390x' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, @@ -6612,19 +6567,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, { name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux' or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, - { name = "filelock" }, - { name = "fsspec", version = "2026.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14' or sys_platform != 'win32' or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, - { name = "fsspec", version = "2026.3.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.14' and sys_platform == 'win32') or (python_full_version < '3.14' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts') or (sys_platform != 'win32' and extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, - { name = "jinja2" }, - { name = "networkx" }, + { name = "filelock", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "fsspec", version = "2026.2.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "jinja2", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "networkx", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, - { name = "setuptools" }, - { name = "sympy" }, + { name = "setuptools", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "sympy", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, { name = "triton", marker = "sys_platform == 'never'" }, - { name = "typing-extensions" }, + { name = "typing-extensions", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/6f/8b/69e3008d78e5cee2b30183340cc425081b78afc5eff3d080daab0adda9aa/torch-2.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b5866312ee6e52ea625cd211dcb97d6a2cdc1131a5f15cc0d87eec948f6dd34", size = 80606338, upload-time = "2026-03-23T18:11:34.781Z" }, @@ -6691,12 +6645,13 @@ dependencies = [ { name = "importlib-metadata" }, { name = "nvdlfw-inspect" }, { name = "onnx", version = "1.19.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, - { name = "onnx", version = "1.21.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, + { name = "onnx", version = "1.21.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' or extra != 'extra-13-megatron-core-dev' or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, { name = "onnxscript", version = "0.5.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, - { name = "onnxscript", version = "0.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-13-megatron-core-dev') or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, - { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" } }, + { name = "onnxscript", version = "0.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' or extra != 'extra-13-megatron-core-dev' or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, + { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-dev'" }, + { name = "packaging", version = "26.2", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-13-megatron-core-lts' or extra != 'extra-13-megatron-core-dev'" }, { name = "pydantic" }, - { name = "torch", marker = "sys_platform == 'never'" }, + { name = "torch", marker = "sys_platform == 'never' or (extra == 'extra-13-megatron-core-dev' and extra == 'extra-13-megatron-core-lts')" }, ] [[package]] From 4e208a8d260c5cf6dc6f5a62dcc985b81d5f1689 Mon Sep 17 00:00:00 2001 From: wdykas <73254672+wdykas@users.noreply.github.com> Date: Wed, 29 Apr 2026 14:20:15 -0400 Subject: [PATCH 049/105] mamba: avoid redundant HBM reloads in causal_conv1d_update shift loop (#4460) Co-authored-by: Claude Opus 4.7 (1M context) Co-authored-by: root Co-authored-by: William Dykas --- megatron/core/ssm/ops/causal_conv1d_triton.py | 68 +++++++++++++++---- .../ssm/test_causal_conv1d_triton.py | 44 ++++++++++++ 2 files changed, 97 insertions(+), 15 deletions(-) diff --git a/megatron/core/ssm/ops/causal_conv1d_triton.py b/megatron/core/ssm/ops/causal_conv1d_triton.py index f57f5d94cea..4ff7ddcf933 100644 --- a/megatron/core/ssm/ops/causal_conv1d_triton.py +++ b/megatron/core/ssm/ops/causal_conv1d_triton.py @@ -139,33 +139,71 @@ def causal_conv1d_update_kernel( conv_state_ptrs + (state_len - WIDTH + 3) * conv_state_l_stride, mask=mask ).to(tl.float32) - # Shift the linear state buffer left by 1 - i = 0 - while i < state_len - 1: - val = tl.load(conv_state_ptrs + (i + 1) * conv_state_l_stride, mask=mask) - tl.store(conv_state_ptrs + i * conv_state_l_stride, val, mask=mask) - i += 1 - # Process the single token for the current sequence step x_val = tl.load(x_ptrs, mask=mask) + # Shift the linear state buffer left by 1. When state_len == WIDTH (the + # common case: conv_state dim == conv kernel width) the shifted values + # are already resident in the x_val_* registers from the loads above, so + # we can write them back without a second HBM read per position. For + # state_len > WIDTH the leading positions are untouched by compute; fall + # through to the explicit load+store shift. + if state_len == WIDTH: + out_dtype = conv_state_ptrs.dtype.element_ty + if WIDTH >= 2: + tl.store( + conv_state_ptrs + 0 * conv_state_l_stride, x_val_0.to(out_dtype), mask=mask + ) + if WIDTH >= 3: + tl.store( + conv_state_ptrs + 1 * conv_state_l_stride, x_val_1.to(out_dtype), mask=mask + ) + if WIDTH >= 4: + tl.store( + conv_state_ptrs + 2 * conv_state_l_stride, x_val_2.to(out_dtype), mask=mask + ) + else: + i = 0 + while i < state_len - 1: + val = tl.load(conv_state_ptrs + (i + 1) * conv_state_l_stride, mask=mask) + tl.store(conv_state_ptrs + i * conv_state_l_stride, val, mask=mask) + i += 1 + # Store the new token at the end of the linear state buffer tl.store(conv_state_ptrs + (state_len - 1) * conv_state_l_stride, x_val, mask=mask) - # Write out to the intermediate state buffer if requested + # Write out to the intermediate state buffer if requested. Reuse the + # register values from the shift above (and x_val for the new tail + # position) when state_len == WIDTH, instead of re-reading from HBM. if HAS_INT_STATE: - i = 0 - while i < state_len: - val = tl.load(conv_state_ptrs + i * conv_state_l_stride, mask=mask) - int_ptr = ( + if state_len == WIDTH: + int_base = ( int_state_ptr + state_batch_coord * int_state_b_stride + s * int_state_s_stride + channel_offsets * int_state_c_stride - + i * int_state_l_stride ) - tl.store(int_ptr, val, mask=mask) - i += 1 + out_dtype = int_base.dtype.element_ty + if WIDTH >= 2: + tl.store(int_base + 0 * int_state_l_stride, x_val_0.to(out_dtype), mask=mask) + if WIDTH >= 3: + tl.store(int_base + 1 * int_state_l_stride, x_val_1.to(out_dtype), mask=mask) + if WIDTH >= 4: + tl.store(int_base + 2 * int_state_l_stride, x_val_2.to(out_dtype), mask=mask) + tl.store(int_base + (state_len - 1) * int_state_l_stride, x_val, mask=mask) + else: + i = 0 + while i < state_len: + val = tl.load(conv_state_ptrs + i * conv_state_l_stride, mask=mask) + int_ptr = ( + int_state_ptr + + state_batch_coord * int_state_b_stride + + s * int_state_s_stride + + channel_offsets * int_state_c_stride + + i * int_state_l_stride + ) + tl.store(int_ptr, val, mask=mask) + i += 1 # Advance registers for calculation x_val_f32 = x_val.to(tl.float32) diff --git a/tests/unit_tests/ssm/test_causal_conv1d_triton.py b/tests/unit_tests/ssm/test_causal_conv1d_triton.py index 3015f5ed989..624cd8c048b 100644 --- a/tests/unit_tests/ssm/test_causal_conv1d_triton.py +++ b/tests/unit_tests/ssm/test_causal_conv1d_triton.py @@ -221,6 +221,50 @@ def test_intermediate_state(self, width): conv_state_ref[:, :, -1] = x[:, s, :] torch.testing.assert_close(int_states[:, s, :, :], conv_state_ref, atol=1e-5, rtol=1e-5) + @pytest.mark.parametrize("width", [2, 3, 4]) + def test_state_len_eq_width_fast_path(self, width): + """Cover the ``state_len == WIDTH`` fast path (the common Mamba + configuration where d_conv == width). + + The other tests use ``state_len = 8`` so they always fall through to + the explicit shift loop. Here ``state_len = width`` exercises the + register-resident shift and the matching ``HAS_INT_STATE`` branch. + """ + torch.manual_seed(42) + B, seq_len, D = 2, 4, 64 + state_len = width + x = torch.randn(B, seq_len, D, device="cuda", dtype=torch.float32) + conv_state_initial = torch.randn(B, D, state_len, device="cuda", dtype=torch.float32) + weight = torch.randn(D, width, device="cuda", dtype=torch.float32) + int_states = torch.zeros(B, seq_len, D, state_len, device="cuda", dtype=torch.float32) + + conv_state_triton = conv_state_initial.clone() + conv_state_ref = conv_state_initial.clone() + + result = causal_conv1d_update( + x, + conv_state_triton, + weight, + bias=None, + silu_activation=False, + conv_state_indices=None, + intermediate_conv_states=int_states, + ) + expected = causal_conv1d_update_ref( + x, conv_state_ref, weight, bias=None, silu_activation=False + ) + + # Output and final state match the reference. + torch.testing.assert_close(result, expected, atol=1e-5, rtol=1e-5) + torch.testing.assert_close(conv_state_triton, conv_state_ref, atol=1e-5, rtol=1e-5) + + # Per-step intermediate states match a manual replay. + replay_state = conv_state_initial.clone() + for s in range(seq_len): + replay_state[:, :, :-1] = replay_state[:, :, 1:].clone() + replay_state[:, :, -1] = x[:, s, :] + torch.testing.assert_close(int_states[:, s, :, :], replay_state, atol=1e-5, rtol=1e-5) + def test_intermediate_state_with_indices(self): """Test intermediate states work correctly with conv_state_indices mapping.""" torch.manual_seed(42) From 3f59bbb1f44c54becbe5fb9f6012cd4121a55f50 Mon Sep 17 00:00:00 2001 From: Teodor-Dumitru Ene <34819528+tdene@users.noreply.github.com> Date: Wed, 29 Apr 2026 14:23:54 -0500 Subject: [PATCH 050/105] Standardize misc graph interface (#4485) Co-authored-by: mathemakitten --- .../core/inference/engines/dynamic_engine.py | 27 ++--- .../text_generation_controller.py | 24 +++-- .../common/language_module/language_module.py | 15 ++- megatron/core/transformer/cuda_graphs.py | 93 ++++------------ .../inference/engines/test_dynamic_engine.py | 101 +++++++++++++----- .../engines/test_mtp_cuda_graph_inference.py | 83 +++++++------- .../test_text_generation_controller.py | 4 +- .../transformer/test_cuda_graphs.py | 29 ++++- 8 files changed, 205 insertions(+), 171 deletions(-) diff --git a/megatron/core/inference/engines/dynamic_engine.py b/megatron/core/inference/engines/dynamic_engine.py index f96235db0c0..2a40937ae6b 100644 --- a/megatron/core/inference/engines/dynamic_engine.py +++ b/megatron/core/inference/engines/dynamic_engine.py @@ -50,7 +50,7 @@ unset_inference_cuda_graphed_iteration_for_ep_inference, ) from megatron.core.process_groups_config import ProcessGroupCollection -from megatron.core.transformer.cuda_graphs import delete_cuda_graphs, graph_capture +from megatron.core.transformer.cuda_graphs import delete_cuda_graphs from megatron.core.transformer.enums import CudaGraphScope from megatron.core.transformer.moe.router_replay import RouterReplay, RouterReplayAction from megatron.core.utils import ( @@ -410,25 +410,27 @@ def create_cuda_graphs(self, reset_context: bool = True): # MTP CUDA graph warmup for this batch dimension. if mtp_warmup_enabled: n = cuda_graph_batch_dimension.req_count + # pylint: disable-next=possibly-used-before-assignment if sp_enabled: n = round_up_to_nearest_multiple(n, tp_size) + # pylint: disable-next=possibly-used-before-assignment if n > 0 and n not in mtp_seen_batch_sizes: mtp_seen_batch_sizes.add(n) device = torch.cuda.current_device() batch_dim = n // tp_size if sp_enabled else n # Use zeros (not empty) — garbage token IDs cause OOB embedding lookups during graph capture/replay. for depth in mtp_warmup_depths: - with graph_capture(): - unwrapped.compute_mtp_single_step( - hidden_states=torch.zeros( - (batch_dim, 1, model_config.hidden_size), - device=device, - dtype=model_config.params_dtype, - ), - next_token_ids=torch.zeros((1, n), device=device, dtype=torch.long), - position_ids=torch.zeros((1, n), device=device, dtype=torch.int64), - depth=depth, - ) + unwrapped.compute_mtp_single_step( + hidden_states=torch.zeros( + (batch_dim, 1, model_config.hidden_size), + device=device, + dtype=model_config.params_dtype, + ), + next_token_ids=torch.zeros((1, n), device=device, dtype=torch.long), + position_ids=torch.zeros((1, n), device=device, dtype=torch.int64), + depth=depth, + cache_key=("mtp", n, depth), + ) context.reset() @@ -437,7 +439,6 @@ def create_cuda_graphs(self, reset_context: bool = True): unset_inference_cuda_graphed_iteration_for_ep_inference(unwrapped_model) if mtp_warmup_enabled and mtp_seen_batch_sizes: - controller.has_mtp_cuda_graphs = True logging.info("> MTP CUDA graph warmup: %d batch size(s)", len(mtp_seen_batch_sizes)) # Memory usage. diff --git a/megatron/core/inference/text_generation_controllers/text_generation_controller.py b/megatron/core/inference/text_generation_controllers/text_generation_controller.py index 993d05afbe0..e66591edad0 100644 --- a/megatron/core/inference/text_generation_controllers/text_generation_controller.py +++ b/megatron/core/inference/text_generation_controllers/text_generation_controller.py @@ -100,7 +100,6 @@ def __init__(self, inference_wrapped_model: AbstractModelInferenceWrapper, token self.sampling_rng = torch.Generator(device=torch.cuda.current_device()) self.num_mtp_heads = self._get_mtp_num_heads() - self.has_mtp_cuda_graphs = False self.sampling_rng.manual_seed(self.model_config.inference_sampling_seed) if ( @@ -615,7 +614,7 @@ def _dynamic_step_context_init( # Derive the MTP padded batch size from the existing padded graph dimensions. # For MoE models this is post EP sync. In eager mode MTP uses locally SP-aligned # batch size instead. - if self.has_mtp_cuda_graphs and context.using_cuda_graph_this_step(): + if context.using_cuda_graph_this_step(): self._mtp_resolved_padded_count = context.padded_batch_dimensions.req_count if self._sp_enabled: self._mtp_resolved_padded_count = round_up_to_nearest_multiple( @@ -624,13 +623,6 @@ def _dynamic_step_context_init( else: self._mtp_resolved_padded_count = None - # Tell the model whether to use MTP CUDA graphs this step. When the - # main model falls back to eager mode, MTP must also run eagerly across - # all EP ranks — otherwise some ranks may replay a captured graph while - # others run eagerly, causing EP collectives to hang. - if self.has_mtp_cuda_graphs: - unwrapped_model.use_mtp_cuda_graphs = context.using_cuda_graph_this_step() - # If using symmetric kernels and we are using using nccl # for prefill turn off symmetric kernels symmetric_ar_type = self.model_config.symmetric_ar_type @@ -938,6 +930,12 @@ def _compute_serial_mtp_and_sample(self): next_token_ids=token_ids_buf, position_ids=position_ids_buf, depth=mtp_depth, + eager=not context.using_cuda_graph_this_step(), + cache_key=( + ("mtp", padded_count, mtp_depth) + if context.using_cuda_graph_this_step() + else None + ), ) nvtx_range_pop(f"mtp-spec-decoding/depth-{depth}/forward") @@ -1689,6 +1687,8 @@ def _dummy_serial_mtp_forward(self): dummy_token_ids = torch.zeros((1, padded_count), device=device, dtype=torch.long) dummy_position_ids = torch.zeros((1, padded_count), device=device, dtype=torch.long) + context = self.inference_wrapped_model.inference_context + for depth in range(self._num_mtp_depths): nvtx_range_push(f"mtp-spec-decoding/dummy-depth-{depth}") mtp_logits_2d = None @@ -1699,6 +1699,12 @@ def _dummy_serial_mtp_forward(self): next_token_ids=dummy_token_ids, position_ids=dummy_position_ids, depth=mtp_depth, + eager=not context.using_cuda_graph_this_step(), + cache_key=( + ("mtp", padded_count, mtp_depth) + if context.using_cuda_graph_this_step() + else None + ), ) mtp_logits_2d = mtp_logits.squeeze(1) # [padded_count, vocab_size] diff --git a/megatron/core/models/common/language_module/language_module.py b/megatron/core/models/common/language_module/language_module.py index 85870726269..84b0ca2fea3 100644 --- a/megatron/core/models/common/language_module/language_module.py +++ b/megatron/core/models/common/language_module/language_module.py @@ -75,7 +75,7 @@ def _setup_mtp_cuda_graphs(self): base_module=self, function_name="compute_mtp_single_step", need_backward=False, - is_mtp_inference=True, + inline_capture=True, ) def _is_in_embd_group(self): @@ -345,6 +345,8 @@ def compute_mtp_single_step( next_token_ids: Tensor, position_ids: Tensor, depth: Optional[int] = None, + eager: bool = False, + cache_key=None, ) -> tuple: """Compute a single MTP depth for speculative decoding. @@ -355,14 +357,19 @@ def compute_mtp_single_step( hidden_states (Tensor): Hidden states at last accepted positions. next_token_ids (Tensor): Correct next token IDs [1, N]. position_ids (Tensor): Position IDs for the next tokens [1, N]. - depth (int, optional): MTP depth index. Only needed when - ``mtp_use_repeated_layer`` is False (each depth uses a - distinct layer). Omit for repeated-layer models so that a + depth (int, optional): MTP depth index. Only needed when `mtp_use_repeated_layer` is + False (each depth uses a distinct layer). Omit for repeated-layer models so that a single CUDA graph can serve all depths. + eager, cache_key: The `CudaGraphManager` works by monkey-patching this argument onto the + function signature. Explictly including them removes the need for a monkey-patch, + and makes it straightforward to call the same method with and without eager mode. + These arguments are consumed by `CudaGraphManager`, if it exists. Returns: tuple: (new_hidden_states, logits [N, 1, vocab_size]). """ + # CudaGraphManager consumes these args, if it exists + del eager, cache_key layer_idx = 0 if depth is None else depth mtp_hidden = self.mtp.layers[layer_idx].forward_single_position( hidden_states=hidden_states, diff --git a/megatron/core/transformer/cuda_graphs.py b/megatron/core/transformer/cuda_graphs.py index 06e1d8958fe..9cd8cd2ffb6 100644 --- a/megatron/core/transformer/cuda_graphs.py +++ b/megatron/core/transformer/cuda_graphs.py @@ -8,7 +8,7 @@ import os import time from collections import defaultdict -from contextlib import contextmanager, nullcontext +from contextlib import nullcontext from copy import deepcopy from dataclasses import dataclass, is_dataclass from enum import Enum @@ -98,16 +98,6 @@ def _set_capture_end(): _IS_GRAPH_CAPTURING = False -@contextmanager -def graph_capture(): - """Context manager that brackets a graph-capture region.""" - _set_capture_start() - try: - yield - finally: - _set_capture_end() - - def is_graph_warmup(): """Query if currently warming up for graph capture.""" return _IS_GRAPH_WARMUP @@ -341,10 +331,6 @@ class _CudagraphGlobalRecord: cudagraph_record: list[tuple] = [] cudagraph_inference_record: list[tuple] = [] - # MTP CudaGraphManagers registered at construction time so that - # delete_cuda_graphs() can clear their lookup tables. - mtp_cudagraph_managers: list = [] - """A pool-like data structure to reuse input and output buffers across cudagraph.""" tensor_reuse_pool = TensorReusePool() @@ -520,19 +506,6 @@ def delete_cuda_graphs(): runner.bwd_graph = None runner.mempool = None - # Reset MTP runners (excluded from the global inference record). - for mgr in _CudagraphGlobalRecord.mtp_cudagraph_managers: - for runner in mgr.cudagraph_runners: - runner.cudagraph_created = False - runner.fwd_graph_recorded = False - runner.bwd_graph_recorded = False - runner.fwd_graph = None - runner.bwd_graph = None - runner.mempool = None - mgr.cudagraph_runners.clear() - mgr.custom_cudagraphs_lookup_table.clear() - _CudagraphGlobalRecord.mtp_cudagraph_managers.clear() - # Reset global tracking state _CudagraphGlobalRecord.cudagraph_created = False _CudagraphGlobalRecord.cudagraph_record = [] @@ -832,6 +805,7 @@ def create_fwd_graph(self, args, kwargs, outputs=None, clone_inputs=True): # graph capture's forward passes do not corrupt its value. Inference is not affected # (no known buffer mutators) and would add new buffers (lazy MoE _fc1_weight/ # _fc2_weight) that misalign the positional restore. + if self.training and torch.is_grad_enabled(): buffer_backup = [] for buf in self.base_module.buffers(): @@ -1446,7 +1420,6 @@ def __init__( function_name=None, need_backward=True, pg_collection=None, - is_mtp_inference=False, inline_capture=False, num_warmup_steps=None, ): @@ -1456,7 +1429,6 @@ def __init__( Args: config: TransformerConfig object containing CUDA graph settings for memory pooling, graph retention, gradient accumulation, FP8/FP4, and warmup steps. - is_mtp_inference: Whether this manager wraps an MTP inference forward pass. inline_capture: Normally, whether the inline capture path is taken depends on whether `inference_context` is present in the kwargs of the forward call. Setting this argument to True always forces the inline capture path to be taken. @@ -1469,7 +1441,6 @@ def __init__( self.pg_collection = pg_collection rng_tracker = get_cuda_rng_tracker() self.need_backward = need_backward - self.is_mtp_inference = is_mtp_inference if function_name is not None: func = getattr(base_module, function_name) @@ -1515,10 +1486,6 @@ def wrapped_func(*args, eager=False, cache_key=None, **kwargs): self.custom_cudagraphs_lookup_table: dict = defaultdict(lambda: None) self.is_first_microbatch = False - if is_mtp_inference: - # Registered so delete_cuda_graphs() can clear the lookup table. - _CudagraphGlobalRecord.mtp_cudagraph_managers.append(self) - # Without pipeline parallelism, microbatches execute one at a time. # Therefore modules will always execute in the same order, so cudagraphs # can both be reused and share a single mempool. @@ -1624,19 +1591,14 @@ def __call__(self, megatron_module, args, kwargs, cache_key=None): cache_key: Optional hashable key for O(1) runner lookup. If `inference_context` is provided, this gets set to the correct value. """ - is_inference_mode = ( - 'inference_context' in kwargs.keys() and kwargs['inference_context'] - ) or self.is_mtp_inference + is_inference_mode = 'inference_context' in kwargs.keys() and kwargs['inference_context'] if cache_key is None and is_inference_mode: - if 'inference_context' in kwargs and kwargs['inference_context']: - inference_context = kwargs['inference_context'] - if inference_context.is_static_batching(): - batch_size = kwargs['hidden_states'].shape[0] - cache_key = (batch_size, inference_context.is_decode_only()) - else: - cache_key = inference_context.padded_batch_dimensions - elif self.is_mtp_inference: - cache_key = ('mtp', kwargs['hidden_states'].shape, kwargs.get('depth')) + inference_context = kwargs['inference_context'] + if inference_context.is_static_batching(): + batch_size = kwargs['hidden_states'].shape[0] + cache_key = (batch_size, inference_context.is_decode_only()) + else: + cache_key = inference_context.padded_batch_dimensions is_in_checkpoint_fwd = is_checkpointing() if HAVE_TE_GRAPHS: is_in_checkpoint_fwd = is_in_checkpoint_fwd or is_fp8_activation_recompute_enabled() @@ -1654,39 +1616,26 @@ def __call__(self, megatron_module, args, kwargs, cache_key=None): out = runner.replay_graph_capture(self.is_first_microbatch, args, kwargs) else: if is_inference_mode or self._inline_capture: - # MTP must match the main model's eager/graph mode so all EP - # ranks take the same code path. Skip during graph capture. - if ( - self.is_mtp_inference - and not getattr(megatron_module, 'use_mtp_cuda_graphs', False) - and not is_graph_capturing() - ): - return self.func(*args, **kwargs) - # Inference generation mode creates graphs immediately runner = self.get_cudagraph_runner( megatron_module, args, kwargs, True, cache_key=cache_key ) - if ( - not runner.fwd_graph_recorded - and self.is_mtp_inference - and not is_graph_capturing() - ): - # No pre-warmed graph for this batch size — run eagerly. - return self.func(*args, **kwargs) - if not runner.fwd_graph_recorded: # Reuse graph input-output buffers for inference local_args, local_kwargs = args, kwargs if not runner.is_first_layer: - # Find previous layer's runner in the global record + # Find previous layer's runner in the global record. + # Method-wrapped managers (e.g. the MTP wrapper around + # `compute_mtp_single_step`) have a base_module without + # `layer_number`; `getattr(..., None)` makes those rows + # harmlessly skipped by the predicate. try: previous_runner = next( r for r in _CudagraphGlobalRecord.cudagraph_inference_record if ( - r[0].base_module.layer_number + getattr(r[0].base_module, 'layer_number', None) == runner.base_module.layer_number - 1 and r[0].fwd_graph is not None and ArgMetadata(r[3]['hidden_states']) @@ -1707,14 +1656,10 @@ def __call__(self, megatron_module, args, kwargs, cache_key=None): runner.cudagraph_created = True runner = runner.eval() - # Record to the global execution record. MTP runners are - # excluded — they don't chain with decoder layers (the - # previous-layer lookup expects layer_number) and are - # cleaned up via mtp_cudagraph_managers instead. - if not self.is_mtp_inference: - _CudagraphGlobalRecord.cudagraph_inference_record.append( - (runner, "fwd", args, kwargs) - ) + # Record this to the global execution record + _CudagraphGlobalRecord.cudagraph_inference_record.append( + (runner, "fwd", args, kwargs) + ) # Now replay the graph out = runner.replay_graph_capture(self.is_first_microbatch, args, kwargs) diff --git a/tests/unit_tests/inference/engines/test_dynamic_engine.py b/tests/unit_tests/inference/engines/test_dynamic_engine.py index 4f02369d0ed..e88924f1af0 100644 --- a/tests/unit_tests/inference/engines/test_dynamic_engine.py +++ b/tests/unit_tests/inference/engines/test_dynamic_engine.py @@ -2294,7 +2294,9 @@ def mock_mtp_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): + def mock_compute_mtp_single_step( + hidden_states, next_token_ids, position_ids, depth=None, eager=False, cache_key=None + ): n = hidden_states.size(0) logits = torch.zeros( n, 1, test_config.vocab_size, device=hidden_states.device, dtype=torch.bfloat16 @@ -2417,7 +2419,9 @@ def mock_deterministic_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): + def mock_compute_mtp_single_step( + hidden_states, next_token_ids, position_ids, depth=None, eager=False, cache_key=None + ): n = hidden_states.size(0) # Predict next_token_ids + 1 (continuing the ascending sequence) pred_toks = (next_token_ids + 1).clamp(max=test_config.vocab_size - 1) @@ -2501,7 +2505,9 @@ def mock_deterministic_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): + def mock_compute_mtp_single_step( + hidden_states, next_token_ids, position_ids, depth=None, eager=False, cache_key=None + ): n = hidden_states.size(0) # Predict next_token_ids + 1 (continuing the ascending sequence) pred_toks = (next_token_ids + 1).clamp(max=test_config.vocab_size - 1) @@ -2586,7 +2592,9 @@ def mock_deterministic_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): + def mock_compute_mtp_single_step( + hidden_states, next_token_ids, position_ids, depth=None, eager=False, cache_key=None + ): n = hidden_states.size(0) # Predict next_token_ids + 1 (continuing the ascending sequence) pred_toks = (next_token_ids + 1).clamp(max=test_config.vocab_size - 1) @@ -2709,8 +2717,12 @@ def deterministic_forward(*args, **kwargs): # Wrap the real MTP step similarly. real_mtp = unwrapped_model.compute_mtp_single_step - def deterministic_mtp(hidden_states, next_token_ids, position_ids, depth): - hidden_states, logits = real_mtp(hidden_states, next_token_ids, position_ids, depth) + def deterministic_mtp( + hidden_states, next_token_ids, position_ids, depth, eager=False, cache_key=None + ): + hidden_states, logits = real_mtp( + hidden_states, next_token_ids, position_ids, depth, eager=eager, cache_key=cache_key + ) logits.zero_() logits[..., 0] = 100.0 return hidden_states, logits @@ -2863,8 +2875,17 @@ def deterministic_forward(*args, **kwargs): real_mtp = model.compute_mtp_single_step - def deterministic_mtp(hidden_states, next_token_ids, position_ids, depth): - hidden_states, logits = real_mtp(hidden_states, next_token_ids, position_ids, depth) + def deterministic_mtp( + hidden_states, next_token_ids, position_ids, depth, eager=False, cache_key=None + ): + hidden_states, logits = real_mtp( + hidden_states, + next_token_ids, + position_ids, + depth, + eager=eager, + cache_key=cache_key, + ) logits.zero_() logits[..., 0] = 100.0 return hidden_states, logits @@ -2940,7 +2961,9 @@ def mock_safe_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): + def mock_compute_mtp_single_step( + hidden_states, next_token_ids, position_ids, depth=None, eager=False, cache_key=None + ): n = hidden_states.size(0) logits = torch.zeros( n, 1, test_config.vocab_size, device=hidden_states.device, dtype=torch.bfloat16 @@ -3153,7 +3176,9 @@ def mock_deterministic_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): + def mock_compute_mtp_single_step( + hidden_states, next_token_ids, position_ids, depth=None, eager=False, cache_key=None + ): n = hidden_states.size(0) logits = torch.randn( n, 1, test_config.vocab_size, device=hidden_states.device, dtype=torch.bfloat16 @@ -3273,7 +3298,9 @@ def mock_deterministic_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): + def mock_compute_mtp_single_step( + hidden_states, next_token_ids, position_ids, depth=None, eager=False, cache_key=None + ): n = hidden_states.size(0) logits = torch.randn( n, 1, test_config.vocab_size, device=hidden_states.device, dtype=torch.bfloat16 @@ -3403,7 +3430,9 @@ def mock_deterministic_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): + def mock_compute_mtp_single_step( + hidden_states, next_token_ids, position_ids, depth=None, eager=False, cache_key=None + ): n = hidden_states.size(0) logits = torch.randn( n, 1, test_config.vocab_size, device=hidden_states.device, dtype=torch.bfloat16 @@ -3656,7 +3685,9 @@ def mock_deterministic_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_wrong(hidden_states, next_token_ids, position_ids, depth): + def mock_compute_mtp_wrong( + hidden_states, next_token_ids, position_ids, depth, eager=False, cache_key=None + ): n = hidden_states.size(0) wrong_toks = (next_token_ids + 5).clamp(max=test_config.vocab_size - 1) logits = torch.zeros( @@ -3752,7 +3783,9 @@ def mock_deterministic_forward(*args, **kwargs): ) return base_logits - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): + def mock_compute_mtp_single_step( + hidden_states, next_token_ids, position_ids, depth=None, eager=False, cache_key=None + ): n = hidden_states.size(0) pred_toks = (next_token_ids + 1).clamp(max=test_config.vocab_size - 1) logits = torch.zeros( @@ -3864,8 +3897,12 @@ def deterministic_forward(*args, **kwargs): real_mtp = unwrapped_model.compute_mtp_single_step - def deterministic_mtp(hidden_states, next_token_ids, position_ids, depth): - hidden_states, logits = real_mtp(hidden_states, next_token_ids, position_ids, depth) + def deterministic_mtp( + hidden_states, next_token_ids, position_ids, depth, eager=False, cache_key=None + ): + hidden_states, logits = real_mtp( + hidden_states, next_token_ids, position_ids, depth, eager=eager, cache_key=cache_key + ) logits.zero_() logits[..., 0] = 100.0 return hidden_states, logits @@ -3973,8 +4010,12 @@ def deterministic_forward(*args, **kwargs): # Deterministic MTP: also predict token 0 → all speculative tokens accepted. real_mtp = unwrapped_model.compute_mtp_single_step - def deterministic_mtp(hidden_states, next_token_ids, position_ids, depth): - hidden_states, logits = real_mtp(hidden_states, next_token_ids, position_ids, depth) + def deterministic_mtp( + hidden_states, next_token_ids, position_ids, depth, eager=False, cache_key=None + ): + hidden_states, logits = real_mtp( + hidden_states, next_token_ids, position_ids, depth, eager=eager, cache_key=cache_key + ) logits.zero_() logits[..., 0] = 100.0 return hidden_states, logits @@ -4045,8 +4086,12 @@ def deterministic_forward(*args, **kwargs): # During prefill, no MTP runs, so request 2 is unaffected. real_mtp = unwrapped_model.compute_mtp_single_step - def heterogeneous_mtp(hidden_states, next_token_ids, position_ids, depth): - hidden_states, logits = real_mtp(hidden_states, next_token_ids, position_ids, depth) + def heterogeneous_mtp( + hidden_states, next_token_ids, position_ids, depth, eager=False, cache_key=None + ): + hidden_states, logits = real_mtp( + hidden_states, next_token_ids, position_ids, depth, eager=eager, cache_key=cache_key + ) n = logits.size(0) logits.zero_() if n >= 2: @@ -4144,8 +4189,12 @@ def deterministic_forward(*args, **kwargs): real_mtp = unwrapped_model.compute_mtp_single_step - def deterministic_mtp(hidden_states, next_token_ids, position_ids, depth): - hidden_states, logits = real_mtp(hidden_states, next_token_ids, position_ids, depth) + def deterministic_mtp( + hidden_states, next_token_ids, position_ids, depth, eager=False, cache_key=None + ): + hidden_states, logits = real_mtp( + hidden_states, next_token_ids, position_ids, depth, eager=eager, cache_key=cache_key + ) logits.zero_() logits[..., 0] = 100.0 return hidden_states, logits @@ -4247,9 +4296,13 @@ def deterministic_forward(*args, **kwargs): real_mtp = unwrapped_model.compute_mtp_single_step - def mtp_with_rejection(hidden_states, next_token_ids, position_ids, depth): + def mtp_with_rejection( + hidden_states, next_token_ids, position_ids, depth, eager=False, cache_key=None + ): # Run real MTP to exercise Mamba intermediate state saving. - hidden_states, logits = real_mtp(hidden_states, next_token_ids, position_ids, depth) + hidden_states, logits = real_mtp( + hidden_states, next_token_ids, position_ids, depth, eager=eager, cache_key=cache_key + ) logits.zero_() if rejection_mode == "all_accepted": # Predict token 0 (same as base) → accepted. diff --git a/tests/unit_tests/inference/engines/test_mtp_cuda_graph_inference.py b/tests/unit_tests/inference/engines/test_mtp_cuda_graph_inference.py index d6605e88d0d..d266e2ef1ed 100644 --- a/tests/unit_tests/inference/engines/test_mtp_cuda_graph_inference.py +++ b/tests/unit_tests/inference/engines/test_mtp_cuda_graph_inference.py @@ -39,7 +39,7 @@ from megatron.core.tensor_parallel.mappings import scatter_to_sequence_parallel_region from megatron.core.tensor_parallel.random import model_parallel_cuda_manual_seed from megatron.core.transformer import TransformerConfig -from megatron.core.transformer.cuda_graphs import _CudagraphGlobalRecord, delete_cuda_graphs +from megatron.core.transformer.cuda_graphs import delete_cuda_graphs from megatron.core.transformer.enums import AttnBackend from megatron.core.utils import unwrap_model from tests.unit_tests.test_utilities import Utils @@ -182,10 +182,16 @@ def _get_mtp_warmed_batch_sizes(engine): return sorted(sizes) @staticmethod - def _set_mtp_cuda_graph_flag(model, enabled): - """Set `use_mtp_cuda_graphs` on the model.""" - unwrapped = unwrap_model(model) - unwrapped.use_mtp_cuda_graphs = enabled + def _mtp_kwargs(use_graph, batch_size, mtp_depth): + """Construct call-site kwargs that route `compute_mtp_single_step` to + either CUDA graph replay or eager execution. + + The wrapped `compute_mtp_single_step` honors `eager=True` to bypass the + manager and `cache_key=...` for O(1) runner lookup. + """ + if use_graph: + return {"cache_key": ("mtp", batch_size, mtp_depth)} + return {"eager": True} @staticmethod def _assert_mtp_cuda_graphs_were_replayed(model, expect_replayed): @@ -248,22 +254,22 @@ def test_cuda_graph_output_matches_eager(self, mtp_use_repeated_layer): dist.broadcast(token_ids, src=0) position_ids = torch.arange(batch_size, device='cuda', dtype=torch.int64).unsqueeze(0) - self._set_mtp_cuda_graph_flag(model, True) h_graph, logits_graph = unwrapped.compute_mtp_single_step( hidden_states=hidden.clone(), next_token_ids=token_ids.clone(), position_ids=position_ids.clone(), depth=mtp_depth, + **self._mtp_kwargs(use_graph=True, batch_size=batch_size, mtp_depth=mtp_depth), ) h_graph = h_graph.clone() logits_graph = logits_graph.clone() - self._set_mtp_cuda_graph_flag(model, False) h_eager, logits_eager = unwrapped.compute_mtp_single_step( hidden_states=hidden.clone(), next_token_ids=token_ids.clone(), position_ids=position_ids.clone(), depth=mtp_depth, + **self._mtp_kwargs(use_graph=False, batch_size=batch_size, mtp_depth=mtp_depth), ) torch.testing.assert_close( @@ -308,22 +314,22 @@ def test_cuda_graph_output_matches_eager_with_sp(self, mtp_use_repeated_layer): dist.broadcast(token_ids, src=0) position_ids = torch.arange(batch_size, device='cuda', dtype=torch.int64).unsqueeze(0) - self._set_mtp_cuda_graph_flag(model, True) h_graph, logits_graph = unwrapped.compute_mtp_single_step( hidden_states=hidden_sp.clone(), next_token_ids=token_ids.clone(), position_ids=position_ids.clone(), depth=mtp_depth, + **self._mtp_kwargs(use_graph=True, batch_size=batch_size, mtp_depth=mtp_depth), ) h_graph = h_graph.clone() logits_graph = logits_graph.clone() - self._set_mtp_cuda_graph_flag(model, False) h_eager, logits_eager = unwrapped.compute_mtp_single_step( hidden_states=hidden_sp.clone(), next_token_ids=token_ids.clone(), position_ids=position_ids.clone(), depth=mtp_depth, + **self._mtp_kwargs(use_graph=False, batch_size=batch_size, mtp_depth=mtp_depth), ) torch.testing.assert_close( @@ -410,7 +416,7 @@ def test_cuda_graph_sp_padding_end_to_end(self, mtp_use_repeated_layer): ctrl._last_accepted_seq_indices = torch.arange(active_request_count, device='cuda') ctrl._mtp_resolved_padded_count = padded_count - self._set_mtp_cuda_graph_flag(model, True) + context._using_cuda_graph_this_step = True ctrl._torch_sampling_buckets = [(list(range(active_request_count)), 1.0, 1, 0.0)] ctrl._torch_sampling_bucket_index_tensors = [ @@ -496,13 +502,11 @@ def _run_mtp(use_cuda_graph): ) if use_cuda_graph: - ctrl.has_mtp_cuda_graphs = True ctrl._mtp_resolved_padded_count = padded_count - self._set_mtp_cuda_graph_flag(model, True) + context._using_cuda_graph_this_step = True else: - ctrl.has_mtp_cuda_graphs = False ctrl._mtp_resolved_padded_count = None - self._set_mtp_cuda_graph_flag(model, False) + context._using_cuda_graph_this_step = False tp_rank = parallel_state.get_tensor_model_parallel_rank() @@ -561,7 +565,6 @@ def test_cuda_graph_multi_depth(self, mtp_use_repeated_layer): use_repeated = unwrapped.mtp.mtp_use_repeated_layer batch_size = batch_sizes[0] - self._set_mtp_cuda_graph_flag(model, True) hidden = torch.randn(batch_size, 1, self.HIDDEN_SIZE, device='cuda', dtype=torch.bfloat16) dist.broadcast(hidden, src=0) @@ -577,6 +580,7 @@ def test_cuda_graph_multi_depth(self, mtp_use_repeated_layer): next_token_ids=token_ids.clone(), position_ids=position_ids.clone(), depth=mtp_depth, + **self._mtp_kwargs(use_graph=True, batch_size=batch_size, mtp_depth=mtp_depth), ) current_hidden = current_hidden.clone() @@ -594,14 +598,14 @@ def test_cuda_graph_multi_depth(self, mtp_use_repeated_layer): self._assert_mtp_cuda_graphs_were_replayed(model, True) - # ---- Test 6: eager fallback when no matching graph exists ------------- # + # ---- Test 6: caller-driven eager bypass for non-warmed shapes --------- # @pytest.mark.parametrize("mtp_use_repeated_layer", [False, True]) @torch.inference_mode() - def test_eager_fallback_no_matching_graph(self, mtp_use_repeated_layer): - """When `use_mtp_cuda_graphs` is True but no warmed graph matches the - batch size, `compute_mtp_single_step` falls back to eager execution. - The system should produce valid outputs without errors. + def test_eager_bypass_for_non_warmed_shape(self, mtp_use_repeated_layer): + """Passing `eager=True` runs `compute_mtp_single_step` outside the + CudaGraphManager wrapper. This is the canonical caller-side fallback + for a shape that warmup did not capture. """ engine = self._build_engine(mtp_use_repeated_layer=mtp_use_repeated_layer) model = engine.controller.inference_wrapped_model.model @@ -618,7 +622,6 @@ def test_eager_fallback_no_matching_graph(self, mtp_use_repeated_layer): mtp_depth = None if unwrapped.mtp.mtp_use_repeated_layer else 0 - self._set_mtp_cuda_graph_flag(model, True) hidden = torch.randn( fallback_size, 1, self.HIDDEN_SIZE, device='cuda', dtype=torch.bfloat16 ) @@ -632,36 +635,21 @@ def test_eager_fallback_no_matching_graph(self, mtp_use_repeated_layer): next_token_ids=token_ids.clone(), position_ids=position_ids.clone(), depth=mtp_depth, + eager=True, ) assert h_out.shape == (fallback_size, 1, self.HIDDEN_SIZE) assert logits.shape == (fallback_size, 1, self.VOCAB_SIZE) assert torch.all(torch.isfinite(logits)) - # ---- Test 7: graph flag propagation matches main model ---------------- # - - @torch.inference_mode() - def test_mtp_graph_flag_propagation(self): - """`use_mtp_cuda_graphs` is correctly toggled via the helper.""" - model = self._build_model(mtp_num_layers=2) - unwrapped = unwrap_model(model) - - self._set_mtp_cuda_graph_flag(model, True) - assert unwrapped.use_mtp_cuda_graphs is True - - self._set_mtp_cuda_graph_flag(model, False) - assert unwrapped.use_mtp_cuda_graphs is False - - # ---- Test 8: delete_cuda_graphs resets MTP runners -------------------- # + # ---- Test 7: delete_cuda_graphs resets MTP runners -------------------- # @torch.inference_mode() def test_delete_cuda_graphs_resets_mtp_runners(self): """`delete_cuda_graphs()` resets MTP CUDA graph runners. - MTP runners are excluded from the global inference record, so they - require special handling in `delete_cuda_graphs()`. After deletion, - no MTP runners should have `fwd_graph_recorded=True` and the global - manager list should be cleared. + MTP runners join the standard `cudagraph_inference_record`, so the + standard cleanup loop resets their `fwd_graph_recorded` flag. """ engine = self._build_engine() model = engine.controller.inference_wrapped_model.model @@ -671,12 +659,13 @@ def test_delete_cuda_graphs_resets_mtp_runners(self): unwrapped = unwrap_model(model) manager = getattr(unwrapped, '_mtp_cudagraph_manager', None) assert manager is not None - assert len(manager.custom_cudagraphs_lookup_table) > 0 + assert len(manager.cudagraph_runners) > 0 + assert all(r.fwd_graph_recorded for r in manager.cudagraph_runners) delete_cuda_graphs() - assert len(manager.custom_cudagraphs_lookup_table) == 0 - assert len(_CudagraphGlobalRecord.mtp_cudagraph_managers) == 0 + assert all(not r.fwd_graph_recorded for r in manager.cudagraph_runners) + assert all(r.fwd_graph is None for r in manager.cudagraph_runners) # --------------------------------------------------------------------------- # @@ -828,6 +817,7 @@ def test_ep_mtp_eager_forward(self, batch_size): next_token_ids=token_ids.clone(), position_ids=position_ids.clone(), depth=0, + eager=True, ) assert h_out.shape == (batch_size, 1, self.HIDDEN_SIZE) @@ -865,7 +855,11 @@ def test_ep_mtp_eager_dummy_and_real_ranks(self): # All ranks must complete without hanging. h_out, logits = unwrapped.compute_mtp_single_step( - hidden_states=hidden, next_token_ids=token_ids, position_ids=position_ids, depth=0 + hidden_states=hidden, + next_token_ids=token_ids, + position_ids=position_ids, + depth=0, + eager=True, ) assert h_out.shape == (batch_size, 1, self.HIDDEN_SIZE) @@ -985,6 +979,7 @@ def test_ep_dummy_bailout_with_decode_only_cuda_graphs(self, peer_state): next_token_ids=dummy_tokens, position_ids=dummy_positions, depth=0, + eager=True, ) assert h_out.shape == (tp_size, 1, self.HIDDEN_SIZE) diff --git a/tests/unit_tests/inference/text_generation_controllers/test_text_generation_controller.py b/tests/unit_tests/inference/text_generation_controllers/test_text_generation_controller.py index dd4764ee92d..f0c41ca7d83 100644 --- a/tests/unit_tests/inference/text_generation_controllers/test_text_generation_controller.py +++ b/tests/unit_tests/inference/text_generation_controllers/test_text_generation_controller.py @@ -1394,7 +1394,9 @@ def test_speculative_mtp_position_ids_with_prefill(self): captured_position_ids = [] - def mock_compute_mtp_single_step(hidden_states, next_token_ids, position_ids, depth=None): + def mock_compute_mtp_single_step( + hidden_states, next_token_ids, position_ids, depth=None, eager=False, cache_key=None + ): captured_position_ids.append(position_ids.clone()) return hidden_states, torch.randn(2, 1, self.vocab_size, device='cuda') diff --git a/tests/unit_tests/transformer/test_cuda_graphs.py b/tests/unit_tests/transformer/test_cuda_graphs.py index 4d29f080d24..fe3fe287622 100644 --- a/tests/unit_tests/transformer/test_cuda_graphs.py +++ b/tests/unit_tests/transformer/test_cuda_graphs.py @@ -1100,6 +1100,24 @@ def my_op(self, x): return self.linear(x) +class _SimpleNonModule: + """non-nn.Module base_module for testing the function_name= form of `CudaGraphManager`.""" + + def __init__(self, config): + self.weight = torch.randn(config.hidden_size, config.hidden_size, device="cuda") + + def my_op(self, x): + return x @ self.weight + + +def _make_simple_module(config): + return _SimpleModule(config).cuda().eval() + + +def _make_simple_non_module(config): + return _SimpleNonModule(config) + + class TestInlineCaptureManager: """Tests for CudaGraphManager with inline_capture, function_name, eager, and cache_key.""" @@ -1126,11 +1144,18 @@ def teardown_method(self, method): CudaGraphManager.global_mempool = None Utils.destroy_model_parallel() + @pytest.mark.parametrize( + "make_module", + [ + pytest.param(_make_simple_module, id="nn_module"), + pytest.param(_make_simple_non_module, id="plain_class"), + ], + ) @torch.inference_mode() - def test_inline_capture_matches_eager(self): + def test_inline_capture_matches_eager(self, make_module): """Inline-captured graph output must match eager execution.""" config = self._make_config() - module = _SimpleModule(config).cuda().eval() + module = make_module(config) # Get eager reference before wrapping x = torch.randn(4, config.hidden_size, device="cuda") From 29864b2ea4cbc3c0e1f0ffd33a0d7e58c330a793 Mon Sep 17 00:00:00 2001 From: Teodor-Dumitru Ene <34819528+tdene@users.noreply.github.com> Date: Wed, 29 Apr 2026 14:55:45 -0500 Subject: [PATCH 051/105] Fix inference graph override in RL flow (#4323) --- megatron/rl/rl_utils.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/megatron/rl/rl_utils.py b/megatron/rl/rl_utils.py index 8a564315dc3..4d018217cec 100644 --- a/megatron/rl/rl_utils.py +++ b/megatron/rl/rl_utils.py @@ -1973,8 +1973,8 @@ def megatron_rl_inference_mode( logger.debug(f"[{dist.get_rank()}] Entering inference mode") - # Change cudagraph scope for inference (empty list = full-layer capture) - model[0].config.cuda_graph_scope = [] + # Set cudagraph scope for inference. + model[0].config.cuda_graph_scope = args.cuda_graph_scope model[0].config.cuda_graph_impl = "local" # If we get a lower precision wrapper, we go one object deeper. @@ -2031,7 +2031,8 @@ def megatron_rl_inference_mode( # Reset drop_and_pad leaked from inference decode set_decode_expert_padding(unwrap_model(model[0]), set_to=False) - # Restore partial capture cudagraph scope for training if this is MoE + # Restore cudagraph scope for training. + # MoE partial capture requires specific scopes that aren't user-facing. if args.num_experts is not None: model[0].config.cuda_graph_scope = [ CudaGraphScope.mamba, @@ -2039,6 +2040,10 @@ def megatron_rl_inference_mode( CudaGraphScope.moe_router, CudaGraphScope.moe_preprocess, ] + else: + model[0].config.cuda_graph_scope = [ + s for s in args.cuda_graph_scope if s != CudaGraphScope.full_iteration_inference + ] # Switch MoE layers to partial CUDA graph capture for training if args.rl_training_cuda_graphs and args.num_experts is not None: From b23aa3f855ed1e9c74f47fbcfe00f99c6f4729a3 Mon Sep 17 00:00:00 2001 From: Cory Ye <44509866+cspades@users.noreply.github.com> Date: Wed, 29 Apr 2026 13:29:11 -0700 Subject: [PATCH 052/105] Unify and refactor Megatron-FSDP documentation. (#4418) Signed-off-by: Cory Ye Co-authored-by: megnvidia --- docs/discussions/README.md | 6 +- .../megatron-fsdp-user-guide.md | 129 ---- docs/images/custom_fsdp/FSDP_workflow.png | Bin 568617 -> 0 bytes .../MCore_Custom_FSDP_Class_Diagram.png | Bin 510903 -> 0 bytes docs/images/megatron_fsdp/DDP_vs_FSDP.png | Bin 0 -> 36542 bytes .../FSDP_Allreduce.png | Bin .../megatron_fsdp/fsdp_double_buffer.png | Bin 0 -> 102094 bytes docs/images/megatron_fsdp/fsdp_streams.png | Bin 0 -> 593828 bytes .../megatron_fsdp/fsdp_v_hfsdp_streams.png | Bin 0 -> 64401 bytes docs/images/megatron_fsdp/hfsdp.png | Bin 0 -> 201310 bytes docs/images/megatron_fsdp/lcm_dim0_shard.png | Bin 0 -> 38723 bytes docs/images/megatron_fsdp/mixed_sharding.png | Bin 0 -> 25786 bytes .../megatron_fsdp/quantized_param_gather.png | Bin 0 -> 114906 bytes .../megatron_fsdp/sharded_quantization.png | Bin 0 -> 187339 bytes docs/images/megatron_fsdp/uneven_sharding.png | Bin 0 -> 349418 bytes .../megatron_fsdp/zero3_model_state.png | Bin 0 -> 59949 bytes docs/index.md | 2 +- docs/user-guide/features/custom_fsdp.md | 196 ------ docs/user-guide/features/index.md | 2 +- docs/user-guide/features/megatron_fsdp.md | 608 ++++++++++++++++++ docs/user-guide/parallelism-guide.md | 77 ++- examples/megatron_fsdp/README.md | 157 +++++ .../sbatch_checkpoint_convert.sh | 0 .../sbatch_mfsdp_deepseek_v3.sh | 2 +- .../train_llama3_8b_fsdp_h100_fp8.sh | 212 ++++++ megatron/core/distributed/README.md | 24 +- megatron/core/distributed/fsdp/src/README.md | 303 ++------- 27 files changed, 1105 insertions(+), 613 deletions(-) delete mode 100644 docs/discussions/megatron-fsdp-user-guide/megatron-fsdp-user-guide.md delete mode 100644 docs/images/custom_fsdp/FSDP_workflow.png delete mode 100644 docs/images/custom_fsdp/MCore_Custom_FSDP_Class_Diagram.png create mode 100644 docs/images/megatron_fsdp/DDP_vs_FSDP.png rename docs/images/{custom_fsdp => megatron_fsdp}/FSDP_Allreduce.png (100%) create mode 100644 docs/images/megatron_fsdp/fsdp_double_buffer.png create mode 100644 docs/images/megatron_fsdp/fsdp_streams.png create mode 100644 docs/images/megatron_fsdp/fsdp_v_hfsdp_streams.png create mode 100644 docs/images/megatron_fsdp/hfsdp.png create mode 100644 docs/images/megatron_fsdp/lcm_dim0_shard.png create mode 100644 docs/images/megatron_fsdp/mixed_sharding.png create mode 100644 docs/images/megatron_fsdp/quantized_param_gather.png create mode 100644 docs/images/megatron_fsdp/sharded_quantization.png create mode 100644 docs/images/megatron_fsdp/uneven_sharding.png create mode 100644 docs/images/megatron_fsdp/zero3_model_state.png delete mode 100644 docs/user-guide/features/custom_fsdp.md create mode 100644 docs/user-guide/features/megatron_fsdp.md create mode 100644 examples/megatron_fsdp/README.md rename {docs/discussions/megatron-fsdp-user-guide/example-scripts => examples/megatron_fsdp}/sbatch_checkpoint_convert.sh (100%) rename {docs/discussions/megatron-fsdp-user-guide/example-scripts => examples/megatron_fsdp}/sbatch_mfsdp_deepseek_v3.sh (99%) create mode 100644 examples/megatron_fsdp/train_llama3_8b_fsdp_h100_fp8.sh diff --git a/docs/discussions/README.md b/docs/discussions/README.md index e791ed57cd8..aab65fc65ca 100644 --- a/docs/discussions/README.md +++ b/docs/discussions/README.md @@ -19,13 +19,9 @@ This directory contains in-depth guides, tutorials, and discussions about optimi ### Training Guides -- **[Megatron-FSDP User Guide](megatron-fsdp-user-guide/megatron-fsdp-user-guide.md)** - - A practical guide to enable Megatron-FSDP training, including a quick-start example for DeepSeek-V3, required and recommended configurations, and instructions for checkpoint conversion from torch_dist to fsdp_dtensor. - ## Contributing -If you'd like to contribute a guide or tutorial, please follow this structure: +To contribute a guide or tutorial, follow this structure: 1. Create a new directory: `docs/discussions/your-guide-name/` 2. Add your main guide: `docs/discussions/your-guide-name/your-guide-name.md` diff --git a/docs/discussions/megatron-fsdp-user-guide/megatron-fsdp-user-guide.md b/docs/discussions/megatron-fsdp-user-guide/megatron-fsdp-user-guide.md deleted file mode 100644 index b5de090ab46..00000000000 --- a/docs/discussions/megatron-fsdp-user-guide/megatron-fsdp-user-guide.md +++ /dev/null @@ -1,129 +0,0 @@ ---- -orphan: true ---- - - - -# Megatron-FSDP User Guide - -## Table of Contents - -- [Megatron-FSDP Quick Start](#megatron-fsdp-quick-start) -- [Checkpoint Conversion from 3D-Parallel to Megatron-FSDP](#checkpoint-conversion-from-3d-parallel-to-megatron-fsdp) - -## Megatron-FSDP Quick Start - -We recommend using the latest [NVIDIA NeMo Framework Container](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/nemo/tags), which provides a tested software stack and optimized performance. - -For your reference, we provide an example launch script for DeepSeek-V3: [`sbatch_mfsdp_deepseek_v3.sh`](./example-scripts/sbatch_mfsdp_deepseek_v3.sh). - -### Required Configurations - -To enable Megatron-FSDP, add the following required flags to your training script: - -```bash ---use-megatron-fsdp ---data-parallel-sharding-strategy optim_grads_params ---no-gradient-accumulation-fusion ---use-distributed-optimizer ---ckpt-format fsdp_dtensor -``` - -### Recommended Configurations - -We also recommend adding the following configurations to further improve performance: - -```bash -unset CUDA_DEVICE_MAX_CONNECTIONS -``` -```bash ---calculate-per-token-loss ---init-model-with-meta-device ---grad-reduce-in-bf16 ---fsdp-double-buffer ---use-nccl-ub -``` - -💡 **Detailed explanations of these configurations are provided below.** - -#### 1. Disable `CUDA_DEVICE_MAX_CONNECTIONS` - -To ensure full parallelization of FSDP communication and computation, disable the CUDA_DEVICE_MAX_CONNECTIONS environment variable. This step avoids potential bubbles in the CUDA stream. (But it may slow down TP and CP to some extent.) - -#### 2. Add `--calculate-per-token-loss` - -For gradients sharding mode optimization, include the `--calculate-per-token-loss` flag in your training script. This improves performance by reducing the frequency of gradient scaling, which is also a sizable drain on SM resources. - -#### 3. Add `--init-model-with-meta-device` - -Allows model initialization using meta device, followed by layer-by-layer initialization of distributed model weight buffers via the `Module.reset_parameters` API, facilitating the initialization of extremely large models. - -#### 4. Add `--grad-reduce-in-bf16` - -Enables gradient reduction in BF16 precision instead of FP32, reducing communication volume and accelerating the backward pass. - -#### 5. Add `--fsdp-double-buffer` - -Uses persistently allocated double buffers for temporarily-defined memory needed in `MegatronFSDP` communications. While having persistent double buffers may increase peak VRAM utilization, it is necessary to register NCCL user buffers (`nccl_ub=True`) for `MegatronFSDP`. Currently, this is supported only for simple repetitive model structures such as GPT. - -- **Only effective when using Megatron-LM.** -- Defaults to `False`. Automatically overridden to `True` when `nccl_ub` is enabled. - -#### 6. Add `--use-nccl-ub` - -Allocates and [registers NCCL user buffers](https://docs.nvidia.com/deeplearning/nccl/user-guide/docs/usage/bufferreg.html#) for param and grad buffers. This option enables an SM-efficient NCCL algorithm that could improve the performance of overlapped computations. This flag will be much more effective when used together with [SHARP](https://docs.nvidia.com/networking/display/sharpv3130) if the FSDP communication includes both NVL and IB domains. Enabling this option will cause additional memory overhead due to the requirement to enable the `fsdp_double_buffer` option. - -- **Only effective when using Megatron-LM.** -- Defaults to `False`. -- By default we try to use NCCL window (symmetric) registration if it is available. If not it falls back to conventional local registration. -- **Incompatible with PyTorch's segmentable allocator:** Do not set `PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True` when using `--use-nccl-ub`, as this will cause a runtime error due to compatibility issues with the `torch.cuda.MemPool` API. - -## Checkpoint Conversion from 3D-Parallel to Megatron-FSDP - -Megatron-FSDP introduces `fsdp_dtensor`, a DTensor-based distributed checkpoint format that serves as its standard. To help you smoothly transition from 3D-Parallel to Megatron-FSDP, we provide a script for converting checkpoints from the `torch_dist` format to the `fsdp_dtensor` format. Using DeepSeek-V3 as an example, the detailed conversion process is described below. - -### Step 1: Generate 3D-Parallel Checkpoint with `param_to_param_group_map` - -Run your 3D-parallel + EP training script to generate a `torch_dist` checkpoint along with a directory containing `param_to_param_group_map` files. Add the following flag to your training script: - -```bash ---dump-param-to-param-group-map /path/to/param_to_param_group_map -``` - -If you already have a `torch_dist` checkpoint, simply specify the `--dump-param-to-param-group-map /path/to/param_to_param_group_map` flag and run a very short experiment-this will create the `param_to_param_group_map` you need without full pretraining. - -### Step 2: Export `param_to_param_group_map` to a JSON File - -Convert the `param_to_param_group_map` into a JSON file for easier processing by running: - -```bash -python tools/checkpoint/checkpoint_inspector.py print-torch-dcp-in-json /path/to/param_to_param_group_map -``` - -This will create a `param_to_param_group_map.json` file in the `/path/to/param_to_param_group_map` directory. - -### Step 3: Convert Checkpoint from `torch_dist` to `fsdp_dtensor` - -Convert your `torch_dist` checkpoint to the `fsdp_dtensor` format using the parameter to `param_to_param_group_map` JSON file: - -```bash -torchrun --nproc_per_node=8 --nnodes=1 \ - tools/checkpoint/checkpoint_inspector.py \ - convert-torch-dist-to-fsdp-dtensor --swiglu \ - /path/to/input_torch_dist_checkpoint \ - /path/to/output_fsdp_dtensor_checkpoint \ - --param-to-param-group-map-json /path/to/param_to_param_group_map.json -``` - -**Note:** For multi-node conversion tasks, please refer to the example script: [`sbatch_checkpoint_convert.sh`](./example-scripts/sbatch_checkpoint_convert.sh). - -### Step 4: Launch Megatron-FSDP Training - -Start your Megatron-FSDP training job using the converted `fsdp_dtensor` checkpoint. \ No newline at end of file diff --git a/docs/images/custom_fsdp/FSDP_workflow.png b/docs/images/custom_fsdp/FSDP_workflow.png deleted file mode 100644 index 588b6f220a375163b34a9ae17254b42678e445b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 568617 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

_whnroH} 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℘c<{P0-qVZ=%GC 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

-WrpltxB>%lC3o?B9lCcllD} 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

@G5%rOA8*}#?k*& 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

2-jXy@IFPZsa@~~X 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

4&*Y) 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¥swy)y3I13I`_?fl%09sgMoH}r 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�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�Kv?b}_4nkU9Q|(s_s|#@Y;t(xIc< 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 literal 0 HcmV?d00001 diff --git a/docs/images/custom_fsdp/FSDP_Allreduce.png b/docs/images/megatron_fsdp/FSDP_Allreduce.png similarity index 100% rename from docs/images/custom_fsdp/FSDP_Allreduce.png rename to docs/images/megatron_fsdp/FSDP_Allreduce.png diff --git a/docs/images/megatron_fsdp/fsdp_double_buffer.png b/docs/images/megatron_fsdp/fsdp_double_buffer.png new file mode 100644 index 0000000000000000000000000000000000000000..fbfbcef9b28dc0da679bb51bc123c3c31c470361 GIT binary patch literal 102094 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 literal 0 HcmV?d00001 diff --git a/docs/images/megatron_fsdp/fsdp_streams.png b/docs/images/megatron_fsdp/fsdp_streams.png new file mode 100644 index 0000000000000000000000000000000000000000..6b8840783c8f2c7018d72dd5932bf3ccc672d7bd GIT binary patch literal 593828 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ǠpA&2jK9^iAA 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͔($g2$zU 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�vbR!r(s(SU9&-H9Xo$vO@fv@!?5;U30 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

R=fa}P<1i+g8tUKdM>)SDrRk&0JT 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�uF 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