From 932861eafbd85a85c58c11c23bd6f1f6632375ec Mon Sep 17 00:00:00 2001 From: Wonhyuk Yang Date: Fri, 22 May 2026 21:33:31 +0900 Subject: [PATCH 1/2] [Tests] Extract shared test_result helper into tests/_pytorchsim_utils.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces 48 near-duplicate ~12-line `test_result(name, out, ...)` defs (two signature variants, three slightly divergent bodies — one had a 'pass message only' bug) with a single canonical helper that prints framed pass/fail messages and exits 1 on mismatch. Positional-argument compatible, so caller sites are unchanged. Each migrated file replaces its local def with: import os, sys sys.path.insert(0, os.path.join( os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) from _pytorchsim_utils import test_result The module name is deliberately unique rather than `tests._utils`: ultralytics ships its own top-level `tests` package in site-packages, which shadows any generic `tests` import. `insert(0, .../tests)` puts the repo's tests dir ahead of site-packages so the helper resolves regardless of installed packages. Net diff: 50 files changed, 220 insertions(+), 729 deletions(-). Co-Authored-By: Claude Opus 4.7 (1M context) --- tests/DeepSeek/test_deepseek_v3_base.py | 24 ++------------ tests/Diffusion/test_diffusion.py | 20 ++--------- tests/Fusion/test_addmm_residual.py | 19 +++-------- tests/Fusion/test_attention_fusion.py | 19 +++-------- tests/Fusion/test_bmm_reduction.py | 19 +++-------- tests/Fusion/test_conv_fusion.py | 14 +++----- tests/Fusion/test_matmul_activation.py | 19 +++-------- tests/Fusion/test_matmul_reduction.py | 19 +++-------- tests/Fusion/test_matmul_scalar.py | 19 +++-------- tests/Fusion/test_matmul_vector.py | 19 +++-------- tests/Fusion/test_prologue_fusion.py | 19 +++-------- tests/Fusion/test_transformer_fusion.py | 19 +++-------- tests/Llama/test_llama.py | 15 ++------- tests/MLP/test_mlp.py | 19 ++--------- tests/MLP/test_mlp_cpu.py | 18 ++-------- tests/Mixtral_8x7B/test_attention.py | 19 +++-------- tests/MoE/test_moe.py | 19 ++--------- tests/MobileNet/test_mobilenet.py | 18 ++-------- tests/Yolov5/test_yolov5.py | 18 ++-------- tests/__init__.py | 0 tests/_pytorchsim_utils.py | 44 +++++++++++++++++++++++++ tests/test_activation.py | 19 +++-------- tests/test_add.py | 19 +++-------- tests/test_batchnorm.py | 19 +++-------- tests/test_bmm.py | 19 +++-------- tests/test_cnn.py | 19 +++-------- tests/test_conv2d.py | 19 +++-------- tests/test_expert_mask.py | 19 +++-------- tests/test_exponent.py | 19 +++-------- tests/test_gqa.py | 18 ++-------- tests/test_group_conv.py | 19 +++-------- tests/test_indirect_access.py | 19 +++-------- tests/test_layernorm.py | 19 +++-------- tests/test_matmul.py | 19 +++-------- tests/test_mlp.py | 19 +++-------- tests/test_pool.py | 19 +++-------- tests/test_reduce.py | 19 +++-------- tests/test_resnet.py | 20 +++-------- tests/test_single_perceptron.py | 19 +++-------- tests/test_softmax.py | 15 +++------ tests/test_sort.py | 19 +++-------- tests/test_sparse_core.py | 20 +++-------- tests/test_stonne.py | 20 +++-------- tests/test_topk.py | 19 +++-------- tests/test_transcendental.py | 19 +++-------- tests/test_transformer.py | 19 +++-------- tests/test_transpose2D.py | 19 +++-------- tests/test_transpose3D.py | 19 +++-------- tests/test_view3D_2D.py | 19 +++-------- tests/test_vit.py | 20 +++-------- 50 files changed, 220 insertions(+), 729 deletions(-) delete mode 100644 tests/__init__.py create mode 100644 tests/_pytorchsim_utils.py diff --git a/tests/DeepSeek/test_deepseek_v3_base.py b/tests/DeepSeek/test_deepseek_v3_base.py index ade787c5..5005b70b 100644 --- a/tests/DeepSeek/test_deepseek_v3_base.py +++ b/tests/DeepSeek/test_deepseek_v3_base.py @@ -4,6 +4,8 @@ import copy from pathlib import Path import torch +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result # recursive compile for some ops that are caused by graph break torch.npu.register_eager_to_compile([ @@ -18,28 +20,6 @@ ]) -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - out_cpu = out.cpu() - max_diff = (out_cpu - cpu_out).abs().max().item() - mean_diff = (out_cpu - cpu_out).abs().mean().item() - if torch.allclose(out_cpu, cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print(f"Max absolute difference: {max_diff:.6f}") - print(f"Mean absolute difference: {mean_diff:.6f}") - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("NPU out: ", out_cpu) - print("CPU out: ", cpu_out) - print(f"Max absolute difference: {max_diff:.6f}") - print(f"Mean absolute difference: {mean_diff:.6f}") - exit(1) - def _extract_logits(output): if isinstance(output, torch.Tensor): diff --git a/tests/Diffusion/test_diffusion.py b/tests/Diffusion/test_diffusion.py index 85eaba9f..7ad5a759 100644 --- a/tests/Diffusion/test_diffusion.py +++ b/tests/Diffusion/test_diffusion.py @@ -9,23 +9,8 @@ from diffusers.models.upsampling import Upsample2D from diffusers.models.resnet import ResnetBlock2D from diffusers.models.embeddings import Timesteps - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - diff = torch.max(torch.abs(out.cpu() - cpu_out)).item() - print(f"Max abs diff: {diff}") - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result @torch.no_grad() def test_unet_conditional( @@ -636,7 +621,6 @@ def test_timesteps( parser.add_argument("--prompt", type=str, default="a cat in a hat") args = parser.parse_args() - sys.path.append(os.environ.get("TORCHSIM_DIR", "/workspace/PyTorchSim")) device = torch.device("npu:0") #test_upsample2d(device) diff --git a/tests/Fusion/test_addmm_residual.py b/tests/Fusion/test_addmm_residual.py index a2c17207..e2fd97f1 100644 --- a/tests/Fusion/test_addmm_residual.py +++ b/tests/Fusion/test_addmm_residual.py @@ -1,19 +1,8 @@ +import os +import sys import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_addmm_residual(device, input_size=128, hidden_size=128, output_size=128): def addmm_residual(a, b, c, d): diff --git a/tests/Fusion/test_attention_fusion.py b/tests/Fusion/test_attention_fusion.py index 93a17347..3edb589b 100644 --- a/tests/Fusion/test_attention_fusion.py +++ b/tests/Fusion/test_attention_fusion.py @@ -1,20 +1,9 @@ +import os +import sys import copy import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def clones(module, N): "Produce N identical layers." diff --git a/tests/Fusion/test_bmm_reduction.py b/tests/Fusion/test_bmm_reduction.py index 45e31dab..a9e02725 100644 --- a/tests/Fusion/test_bmm_reduction.py +++ b/tests/Fusion/test_bmm_reduction.py @@ -1,19 +1,8 @@ +import os +import sys import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_bmm_reduce(device, batch=12, size=512): def bmm(a, b): diff --git a/tests/Fusion/test_conv_fusion.py b/tests/Fusion/test_conv_fusion.py index bc200ff2..eb168086 100644 --- a/tests/Fusion/test_conv_fusion.py +++ b/tests/Fusion/test_conv_fusion.py @@ -1,15 +1,9 @@ +import os +import sys import torch +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - message = f"|{name} Test Passed|" - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - print("Failed") - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) # exit(1) def test_conv_residual(device, batch_size=1, in_channels=8, out_channels=16, input_size=64, kernel_size=3, stride=1, padding=0): diff --git a/tests/Fusion/test_matmul_activation.py b/tests/Fusion/test_matmul_activation.py index 232ec98d..60261ab9 100644 --- a/tests/Fusion/test_matmul_activation.py +++ b/tests/Fusion/test_matmul_activation.py @@ -1,20 +1,9 @@ +import os +import sys import copy import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result class Matmul_ActivationFn(torch.nn.Module): def __init__(self, input_size, output_size, activation_fn): diff --git a/tests/Fusion/test_matmul_reduction.py b/tests/Fusion/test_matmul_reduction.py index 9b09214a..d5dde2af 100644 --- a/tests/Fusion/test_matmul_reduction.py +++ b/tests/Fusion/test_matmul_reduction.py @@ -1,21 +1,10 @@ +import os +import sys import torch import torch._dynamo import torch.utils.cpp_extension - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_matmul_reduce(device, M=512, N=512, K=512): def matmul_fused(a, b): diff --git a/tests/Fusion/test_matmul_scalar.py b/tests/Fusion/test_matmul_scalar.py index d5a159ed..19a55518 100644 --- a/tests/Fusion/test_matmul_scalar.py +++ b/tests/Fusion/test_matmul_scalar.py @@ -1,21 +1,10 @@ +import os +import sys import torch import torch._dynamo import torch.utils.cpp_extension - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_matmul_scalar(device): def matmul_fused(a, b, c): diff --git a/tests/Fusion/test_matmul_vector.py b/tests/Fusion/test_matmul_vector.py index f87f9432..c7e55272 100644 --- a/tests/Fusion/test_matmul_vector.py +++ b/tests/Fusion/test_matmul_vector.py @@ -1,19 +1,8 @@ +import os +import sys import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_matmul_vector(device, size=[56, 78, 239], dim=0): def matmul_fused(a, b, c, d): diff --git a/tests/Fusion/test_prologue_fusion.py b/tests/Fusion/test_prologue_fusion.py index ecfd5fbf..05d89a39 100644 --- a/tests/Fusion/test_prologue_fusion.py +++ b/tests/Fusion/test_prologue_fusion.py @@ -1,19 +1,8 @@ +import os +import sys import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_elem_broadcast_fusion(device): def matmul_fused(a, b, c): diff --git a/tests/Fusion/test_transformer_fusion.py b/tests/Fusion/test_transformer_fusion.py index 1581cd97..b6eb274f 100644 --- a/tests/Fusion/test_transformer_fusion.py +++ b/tests/Fusion/test_transformer_fusion.py @@ -1,21 +1,10 @@ +import os +import sys import math import copy import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def clones(module, N): "Produce N identical layers." diff --git a/tests/Llama/test_llama.py b/tests/Llama/test_llama.py index 5e87b8e7..a4f2f1f2 100644 --- a/tests/Llama/test_llama.py +++ b/tests/Llama/test_llama.py @@ -5,19 +5,8 @@ import torch from transformers.models.llama.configuration_llama import LlamaConfig from transformers.models.llama.modeling_llama import LlamaForCausalLM, LlamaDecoderLayer, LlamaRMSNorm, LlamaRotaryEmbedding, LlamaModel - -def test_result(name, out, ref, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), ref.cpu(), rtol=rtol, atol=atol): - msg = f"|{name} Test Passed|" - print("-" * len(msg)); print(msg); print("-" * len(msg)) - else: - msg = f"|{name} Test Failed|" - print("-" * len(msg)); print(msg); print("-" * len(msg)) - diff = (out.cpu().int() - ref.cpu().int()).abs().max().item() - print("device out:", out.detach().cpu()) - print("cpu ref :", ref.detach().cpu()) - print(f"Max abs diff: {diff}") - sys.exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result @torch.no_grad() def run_rmsnorm_test( diff --git a/tests/MLP/test_mlp.py b/tests/MLP/test_mlp.py index c910729e..ac3d03e0 100644 --- a/tests/MLP/test_mlp.py +++ b/tests/MLP/test_mlp.py @@ -19,23 +19,8 @@ import torch.utils.cpp_extension from torch._inductor import config -sys.path.append(os.environ.get('TORCHSIM_DIR', default='/workspace/PyTorchSim')) - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - pass_message = f"|{name} Test Passed|" - fail_message = f"|{name} Test Failed|" - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - print("-" * len(pass_message)) - print(pass_message) - print("-" * len(pass_message)) - else: - print("-" * len(fail_message)) - print(fail_message) - print("-" * len(fail_message)) - - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get('TORCHSIM_DIR', default='/workspace/PyTorchSim'), 'tests')) +from _pytorchsim_utils import test_result class MLP(nn.Module): def __init__(self, input_size, output_size, hidden_size): diff --git a/tests/MLP/test_mlp_cpu.py b/tests/MLP/test_mlp_cpu.py index 112f5d07..620d6cd2 100644 --- a/tests/MLP/test_mlp_cpu.py +++ b/tests/MLP/test_mlp_cpu.py @@ -20,22 +20,8 @@ import torch.utils.cpp_extension from torch._inductor import config -sys.path.append(os.environ.get('TORCHSIM_DIR', default='/workspace/PyTorchSim')) - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - pass_message = f"|{name} Test Passed|" - fail_message = f"|{name} Test Failed|" - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - print("-" * len(pass_message)) - print(pass_message) - print("-" * len(pass_message)) - else: - print("-" * len(fail_message)) - print(fail_message) - print("-" * len(fail_message)) - - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) +sys.path.insert(0, os.path.join(os.environ.get('TORCHSIM_DIR', default='/workspace/PyTorchSim'), 'tests')) +from _pytorchsim_utils import test_result class MLP(nn.Module): def __init__(self, input_size, output_size, hidden_size): diff --git a/tests/Mixtral_8x7B/test_attention.py b/tests/Mixtral_8x7B/test_attention.py index 57760370..c993c2ec 100644 --- a/tests/Mixtral_8x7B/test_attention.py +++ b/tests/Mixtral_8x7B/test_attention.py @@ -1,21 +1,10 @@ +import os +import sys import copy import torch from model import Transformer, TransformerBlock, ModelArgs, Attention, FeedForward, KVCache, RMSNorm, precompute_freqs_cis, sample - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_decode(device, prompt_length, nr_tokens): # Setup model & model args diff --git a/tests/MoE/test_moe.py b/tests/MoE/test_moe.py index d4cd98f1..6df06803 100644 --- a/tests/MoE/test_moe.py +++ b/tests/MoE/test_moe.py @@ -14,7 +14,8 @@ import torch.utils.cpp_extension from torch._inductor import config -sys.path.append(os.environ.get('TORCHSIM_DIR', default='/workspace/PyTorchSim')) +sys.path.insert(0, os.path.join(os.environ.get('TORCHSIM_DIR', default='/workspace/PyTorchSim'), 'tests')) +from _pytorchsim_utils import test_result # FIXME. This is a Dynamo bug. Solution to avoid is_forward conflict during backward def patch_metrics_context_update(): @@ -30,22 +31,6 @@ def patched_update(values, overwrite=True): # Patch the method get_metrics_context().update = patched_update -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - pass_message = f"|{name} Test Passed|" - fail_message = f"|{name} Test Failed|" - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - print("-" * len(pass_message)) - print(pass_message) - print("-" * len(pass_message)) - else: - print("-" * len(fail_message)) - print(fail_message) - print("-" * len(fail_message)) - - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) - class SparseDispatcher(object): """Helper for implementing a mixture of experts. The purpose of this class is to create input minibatches for the diff --git a/tests/MobileNet/test_mobilenet.py b/tests/MobileNet/test_mobilenet.py index 966d479a..36ab41b5 100644 --- a/tests/MobileNet/test_mobilenet.py +++ b/tests/MobileNet/test_mobilenet.py @@ -1,3 +1,4 @@ +import sys import argparse import copy import os @@ -6,23 +7,10 @@ import torch._dynamo import torch.utils.cpp_extension from torchvision.models import mobilenet_v2 +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) - def _mobilenet_v2(): try: diff --git a/tests/Yolov5/test_yolov5.py b/tests/Yolov5/test_yolov5.py index d98828bd..9e93fd50 100644 --- a/tests/Yolov5/test_yolov5.py +++ b/tests/Yolov5/test_yolov5.py @@ -11,22 +11,10 @@ from torchvision import transforms import os +import sys import shutil - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def run_yolo(batch, config): import copy diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/_pytorchsim_utils.py b/tests/_pytorchsim_utils.py new file mode 100644 index 00000000..923f4bb7 --- /dev/null +++ b/tests/_pytorchsim_utils.py @@ -0,0 +1,44 @@ +"""Shared helpers for PyTorchSim test files. + +Module name is unique (not ``tests._utils``) because ``ultralytics`` +ships a top-level ``tests`` package in site-packages that would shadow it. + +Import with: + + import os, sys + sys.path.insert(0, os.path.join( + os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) + from _pytorchsim_utils import test_result +""" + +import sys + +import torch + + +def test_result(name, out, expected, rtol=1e-4, atol=1e-4): + """Compare ``out`` to ``expected``; exit 1 on mismatch.""" + out_cpu = out.cpu() if hasattr(out, "cpu") else out + expected_cpu = expected.cpu() if hasattr(expected, "cpu") else expected + + if torch.allclose(out_cpu, expected_cpu, rtol=rtol, atol=atol): + msg = f"|{name} Test Passed|" + bar = "-" * len(msg) + print(bar) + print(msg) + print(bar) + return + + msg = f"|{name} Test Failed|" + bar = "-" * len(msg) + print(bar) + print(msg) + print(bar) + print("custom out: ", out_cpu) + print("cpu out: ", expected_cpu) + try: + max_diff = (out_cpu - expected_cpu).abs().max().item() + print(f"Max abs diff: {max_diff}") + except Exception: + pass + sys.exit(1) diff --git a/tests/test_activation.py b/tests/test_activation.py index dacc102e..f1c5de96 100644 --- a/tests/test_activation.py +++ b/tests/test_activation.py @@ -1,22 +1,11 @@ +import os +import sys import torch import torch._dynamo import torch.utils.cpp_extension import torch.nn.functional as F - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_ReLU(device, size=(128, 128)): torch.manual_seed(0) diff --git a/tests/test_add.py b/tests/test_add.py index 7a0d23d9..b3c94653 100644 --- a/tests/test_add.py +++ b/tests/test_add.py @@ -1,21 +1,10 @@ +import os +import sys import torch import torch._dynamo import torch.utils.cpp_extension - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_vectoradd(device, size=(128, 128)): def vectoradd(a, b): diff --git a/tests/test_batchnorm.py b/tests/test_batchnorm.py index 065c0870..1c34942d 100644 --- a/tests/test_batchnorm.py +++ b/tests/test_batchnorm.py @@ -1,19 +1,8 @@ +import os +import sys import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_BatchNorm(device, size=(1, 16, 64, 64)): torch.manual_seed(0) diff --git a/tests/test_bmm.py b/tests/test_bmm.py index 02a6460e..5f0ae9c9 100644 --- a/tests/test_bmm.py +++ b/tests/test_bmm.py @@ -1,19 +1,8 @@ +import os +import sys import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_BMM(device, batch_size=1, m=32, n=16, k=64): def bmm(a, b): diff --git a/tests/test_cnn.py b/tests/test_cnn.py index e6b01bbd..dcb4e14c 100644 --- a/tests/test_cnn.py +++ b/tests/test_cnn.py @@ -1,19 +1,8 @@ +import os +import sys import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result class CNN(torch.nn.Module): def __init__(self): diff --git a/tests/test_conv2d.py b/tests/test_conv2d.py index 313003b1..820e990f 100644 --- a/tests/test_conv2d.py +++ b/tests/test_conv2d.py @@ -1,20 +1,9 @@ +import os +import sys import torch import torch._dynamo - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_conv2d(device, batch_size=1, in_channels=8, out_channels=16, input_size=64, kernel_size=3, stride=1, padding=0): def custom_conv2d(a, b, bias): diff --git a/tests/test_expert_mask.py b/tests/test_expert_mask.py index 4d240206..9da3b9ac 100644 --- a/tests/test_expert_mask.py +++ b/tests/test_expert_mask.py @@ -1,23 +1,12 @@ +import os +import sys import torch import torch._dynamo import torch.utils.cpp_extension +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) - def test_expert_mask(device, batch=4, num_experts=8): # Regression test for issue #228: diff --git a/tests/test_exponent.py b/tests/test_exponent.py index 20f0a143..5bc57841 100644 --- a/tests/test_exponent.py +++ b/tests/test_exponent.py @@ -1,19 +1,8 @@ +import os +import sys import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_exponent(device, size=(128, 128)): def exponent(a): diff --git a/tests/test_gqa.py b/tests/test_gqa.py index ba262fa6..038861c8 100644 --- a/tests/test_gqa.py +++ b/tests/test_gqa.py @@ -5,23 +5,10 @@ import torch.nn.functional as F import torch._dynamo import argparse +sys.path.insert(0, os.path.join(os.environ.get('TORCHSIM_DIR', default='/workspace/PyTorchSim'), 'tests')) +from _pytorchsim_utils import test_result -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) - class GQAMultiheadAttention(nn.Module): """ @@ -300,7 +287,6 @@ def scaled_dot_product_attention(query, key, value, attn_mask=None, dropout_p=0. args = parser.parse_args() - sys.path.append(os.environ.get('TORCHSIM_DIR', default='/workspace/PyTorchSim')) device = torch.device("npu:0") test_repeat_interleave_compilation( diff --git a/tests/test_group_conv.py b/tests/test_group_conv.py index 4f97cff6..e9becfe3 100644 --- a/tests/test_group_conv.py +++ b/tests/test_group_conv.py @@ -1,21 +1,10 @@ +import os +import sys import torch import torch._dynamo from Simulator.simulator import TOGSimulator - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_group_convolution( device, diff --git a/tests/test_indirect_access.py b/tests/test_indirect_access.py index 95167d1e..f64fe50d 100644 --- a/tests/test_indirect_access.py +++ b/tests/test_indirect_access.py @@ -1,20 +1,9 @@ +import os +import sys import torch import copy - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_indirect_vectoradd(device, size=(128, 128)): def vectoradd(a, idx, b): diff --git a/tests/test_layernorm.py b/tests/test_layernorm.py index 3db27dc5..553ce7e3 100644 --- a/tests/test_layernorm.py +++ b/tests/test_layernorm.py @@ -1,21 +1,10 @@ +import os +import sys import torch import torch._dynamo import torch.utils.cpp_extension - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_LayerNorm(device, size=(64, 64)): torch.manual_seed(0) diff --git a/tests/test_matmul.py b/tests/test_matmul.py index a5bdf422..756b99a2 100644 --- a/tests/test_matmul.py +++ b/tests/test_matmul.py @@ -1,19 +1,8 @@ +import os +import sys import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_matmul(device, input_size=128, hidden_size=128, output_size=128): def custom_matmul(a, b): diff --git a/tests/test_mlp.py b/tests/test_mlp.py index e3f79561..cb27fe76 100644 --- a/tests/test_mlp.py +++ b/tests/test_mlp.py @@ -1,20 +1,9 @@ +import os +import sys import copy import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result class MLP(torch.nn.Module): def __init__(self, input_size=28*28, hidden_size=64, output_size=8): diff --git a/tests/test_pool.py b/tests/test_pool.py index 2848e04b..91d8fce1 100644 --- a/tests/test_pool.py +++ b/tests/test_pool.py @@ -1,19 +1,8 @@ +import os +import sys import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_maxpool(device, b=1, c=64, h=112, w=112): torch.manual_seed(0) diff --git a/tests/test_reduce.py b/tests/test_reduce.py index 07f8fef2..d80e17b9 100644 --- a/tests/test_reduce.py +++ b/tests/test_reduce.py @@ -1,21 +1,10 @@ +import os +import sys import torch import torch._dynamo import torch.utils.cpp_extension - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_reduce_sum(device, size, dim, keepdim=False): def reduce_sum(a, b, dim, keepdim): diff --git a/tests/test_resnet.py b/tests/test_resnet.py index 2459cd58..f9f13bd4 100644 --- a/tests/test_resnet.py +++ b/tests/test_resnet.py @@ -3,21 +3,10 @@ import torch._dynamo import torch.utils.cpp_extension from torchvision.models import resnet18, resnet50 - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +import os +import sys +sys.path.insert(0, os.path.join(os.environ.get('TORCHSIM_DIR', default='/workspace/PyTorchSim'), 'tests')) +from _pytorchsim_utils import test_result def test_resnet(device, batch=1, model_type='resnet18'): from torchvision.models import resnet @@ -47,7 +36,6 @@ def test_resnet(device, batch=1, model_type='resnet18'): args = argparse.ArgumentParser() args.add_argument('--model_type', type=str, default="resnet18", help='ex) resnet18') args = args.parse_args() - sys.path.append(os.environ.get('TORCHSIM_DIR', default='/workspace/PyTorchSim')) device = torch.device("npu:0") test_resnet(device, model_type=args.model_type) diff --git a/tests/test_single_perceptron.py b/tests/test_single_perceptron.py index 7d3401a3..3b262132 100644 --- a/tests/test_single_perceptron.py +++ b/tests/test_single_perceptron.py @@ -1,20 +1,9 @@ +import os +import sys import copy import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_single_perceptron(device): def perceptron(a, b, c): diff --git a/tests/test_softmax.py b/tests/test_softmax.py index 2dca97b7..2e5e6422 100644 --- a/tests/test_softmax.py +++ b/tests/test_softmax.py @@ -1,17 +1,10 @@ +import os +import sys import torch import torch._dynamo import torch.utils.cpp_extension - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - message = f"|{name} Test Passed|" - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_softmax(device, size=(128, 128), dim=1): torch.manual_seed(0) diff --git a/tests/test_sort.py b/tests/test_sort.py index 5bce2532..d9b51087 100644 --- a/tests/test_sort.py +++ b/tests/test_sort.py @@ -1,20 +1,9 @@ +import os +import sys import argparse import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out:", out.cpu()) - print("cpu out:", cpu_out) - raise SystemExit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_equal(name, out, cpu_out): diff --git a/tests/test_sparse_core.py b/tests/test_sparse_core.py index bb4ff630..21cd9344 100644 --- a/tests/test_sparse_core.py +++ b/tests/test_sparse_core.py @@ -3,21 +3,10 @@ import torch._dynamo import torch.utils.cpp_extension import torch.nn.utils.prune as prune - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +import os +import sys +sys.path.insert(0, os.path.join(os.environ.get('TORCHSIM_DIR', default='/root/workspace/PyTorchSim'), 'tests')) +from _pytorchsim_utils import test_result class MLP(nn.Module): def __init__(self, input_size=16, hidden_size=16, output_size=16, sparsity_fc1=0, sparsity_fc2=0): @@ -79,7 +68,6 @@ def test_sparse_mlp(device, batch_size=32, input_size=128, hidden_size=128, outp if __name__ == "__main__": import os import sys - sys.path.append(os.environ.get('TORCHSIM_DIR', default='/root/workspace/PyTorchSim')) device = torch.device("npu:0") test_sparse_mlp(device, batch_size=8, input_size=16, hidden_size=32, output_size=64) diff --git a/tests/test_stonne.py b/tests/test_stonne.py index ac26c273..4febafd0 100644 --- a/tests/test_stonne.py +++ b/tests/test_stonne.py @@ -4,6 +4,10 @@ import random import numpy as np import argparse +import os +import sys +sys.path.insert(0, os.path.join(os.environ.get('TORCHSIM_DIR', default='/root/workspace/PyTorchSim'), 'tests')) +from _pytorchsim_utils import test_result random.seed(0) np.random.seed(0) @@ -13,21 +17,6 @@ def apply_pruning(tensor, sparsity): mask = torch.rand_like(tensor) >= sparsity tensor *= mask -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) - def sparse_matmul(a, b): return torch.sparse.mm(a, b) @@ -52,7 +41,6 @@ def test_sparse_mm(device, input_size=128, hidden_size=128, output_size=128, spa parser.add_argument("sparsity", nargs="?", type=float, help="%% of zero", default=0.0) args = parser.parse_args() - sys.path.append(os.environ.get('TORCHSIM_DIR', default='/root/workspace/PyTorchSim')) device = torch.device("npu:0") test_sparse_mm(device, args.sz, args.sz, args.sz, args.sparsity) \ No newline at end of file diff --git a/tests/test_topk.py b/tests/test_topk.py index caf56779..e27c199d 100644 --- a/tests/test_topk.py +++ b/tests/test_topk.py @@ -1,21 +1,10 @@ +import os +import sys import torch import torch._dynamo import torch.utils.cpp_extension - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_topk(device, size=(128, 128), k=5, dim=-1, largest=True, sorted=True): # dim 해석을 위해 양수 인덱스로 변환 diff --git a/tests/test_transcendental.py b/tests/test_transcendental.py index 34546539..c3a2ee0f 100644 --- a/tests/test_transcendental.py +++ b/tests/test_transcendental.py @@ -1,21 +1,10 @@ +import os +import sys import torch import torch._dynamo import torch.utils.cpp_extension - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_tanh(device, size=(128, 128)): def tanh(a): diff --git a/tests/test_transformer.py b/tests/test_transformer.py index 2b7f308c..2eaa9fd0 100644 --- a/tests/test_transformer.py +++ b/tests/test_transformer.py @@ -1,21 +1,10 @@ +import os +import sys import math import copy import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def clones(module, N): "Produce N identical layers." diff --git a/tests/test_transpose2D.py b/tests/test_transpose2D.py index 4e9807ce..dd83e9ed 100644 --- a/tests/test_transpose2D.py +++ b/tests/test_transpose2D.py @@ -1,19 +1,8 @@ +import os +import sys import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_Transpose2D(device, size=(16, 32)): def transpose(a): diff --git a/tests/test_transpose3D.py b/tests/test_transpose3D.py index e4d4e952..f121c713 100644 --- a/tests/test_transpose3D.py +++ b/tests/test_transpose3D.py @@ -1,19 +1,8 @@ +import os +import sys import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_Transpose3D_1(device, size=(4, 16, 32)): def transpose(a, b): diff --git a/tests/test_view3D_2D.py b/tests/test_view3D_2D.py index cc7b5e41..b6e5ffff 100644 --- a/tests/test_view3D_2D.py +++ b/tests/test_view3D_2D.py @@ -1,19 +1,8 @@ +import os +import sys import torch - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +sys.path.insert(0, os.path.join(os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim"), "tests")) +from _pytorchsim_utils import test_result def test_view3D_2D(device, size=(16, 8, 16), t_x=0, t_y=1): def view3D_2D(a): diff --git a/tests/test_vit.py b/tests/test_vit.py index 6149166d..9fa8a2f0 100644 --- a/tests/test_vit.py +++ b/tests/test_vit.py @@ -4,21 +4,10 @@ import argparse from torchvision import models from torchvision.models.vision_transformer import _vision_transformer, EncoderBlock - -def test_result(name, out, cpu_out, rtol=1e-4, atol=1e-4): - if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - message = f"|{name} Test Passed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - else: - message = f"|{name} Test Failed|" - print("-" * len(message)) - print(message) - print("-" * len(message)) - print("custom out: ", out.cpu()) - print("cpu out: ", cpu_out) - exit(1) +import os +import sys +sys.path.insert(0, os.path.join(os.environ.get('TORCHSIM_DIR', default='/workspace/PyTorchSim'), 'tests')) +from _pytorchsim_utils import test_result def init_vit_weights(m): if isinstance(m, torch.nn.Linear): @@ -201,7 +190,6 @@ def test_encoder_block_with_class_token( shape = tuple(map(int, args.shape.strip('()').split(','))) - sys.path.append(os.environ.get('TORCHSIM_DIR', default='/workspace/PyTorchSim')) device = torch.device("npu:0") #test_multihead_attention(device) #test_encoder_block(device, seq_len=197) From dff1f10976197b82d9036ad9c2de7c0ea0f6e403 Mon Sep 17 00:00:00 2001 From: Wonhyuk Yang Date: Sat, 23 May 2026 01:13:48 +0900 Subject: [PATCH 2/2] [Tests] Reorganize tests/ into ops/, models, system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tests/ was a flat mix of op-level files, single-file model tests, and inconsistently-cased model directories. Adds a hierarchy: tests/ _pytorchsim_utils.py ops/ elementwise/ reduce/ gemm/ conv/ attention/ view/ sort/ sparsity/ misc/ fusion/ models/ DeepSeek/ Diffusion/ Llama/ MLP/ MoE/ MobileNet/ Mixtral8x7B/ Yolov5/ test_mlp.py test_resnet.py test_single_perceptron.py test_transformer.py test_vit.py system/ test_eager.py test_hetro.py test_scheduler.py test_stonne.py test_vectorops.py Mixtral_8x7B → Mixtral8x7B for consistency with the other PascalCase model dirs. Existing single-file model dirs are kept as dirs (they may grow companion files like the Mixtral model.py). All file moves use `git mv` to preserve history. External path references rewritten across .github/workflows/pytorchsim_test.yml, README.md, CLAUDE.md, .github/ISSUE_TEMPLATE/bug_report.md, and scripts/{sparsity,stonne}_experiment/. Cross-test imports drop the `tests.` prefix (the prior commit puts `/tests` on sys.path[0]). `__init__.py` added to each new subdir so e.g. `from ops.elementwise.test_add import test_vectoradd` resolves. Sample-verified locally on tests/ops/elementwise/test_add.py and tests/ops/fusion/test_matmul_vector.py. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .github/workflows/pytorchsim_test.yml | 90 +++++++++---------- CLAUDE.md | 18 ++-- README.md | 16 ++-- scripts/sparsity_experiment/run.sh | 60 ++++++------- scripts/stonne_experiment/run.sh | 6 +- scripts/stonne_experiment/run_trace.sh | 2 +- .../DeepSeek/test_deepseek_v3_base.py | 0 .../{ => models}/Diffusion/test_diffusion.py | 0 tests/{ => models}/Llama/test_llama.py | 0 tests/{ => models}/MLP/test_mlp.py | 0 tests/{ => models}/MLP/test_mlp_cpu.py | 0 .../Mixtral8x7B}/model.py | 0 .../Mixtral8x7B}/test_attention.py | 0 tests/{ => models}/MoE/test_moe.py | 0 tests/{ => models}/MoE/test_moe_cpu.py | 0 .../{ => models}/MobileNet/test_mobilenet.py | 0 tests/{ => models}/Yolov5/test_yolov5.py | 0 tests/{Fusion => models}/__init__.py | 0 tests/{ => models}/test_mlp.py | 0 tests/{ => models}/test_resnet.py | 0 tests/{ => models}/test_single_perceptron.py | 0 tests/{ => models}/test_transformer.py | 0 tests/{ => models}/test_vit.py | 0 tests/ops/__init__.py | 0 tests/ops/attention/__init__.py | 0 tests/{ => ops/attention}/test_gqa.py | 0 tests/{ => ops/attention}/test_gqa_decode.py | 0 tests/{ => ops/attention}/test_sdpa.py | 0 tests/ops/conv/__init__.py | 0 tests/{ => ops/conv}/test_cnn.py | 0 tests/{ => ops/conv}/test_conv2d.py | 0 tests/{ => ops/conv}/test_group_conv.py | 0 tests/{ => ops/conv}/test_pool.py | 0 tests/ops/elementwise/__init__.py | 0 .../{ => ops/elementwise}/test_activation.py | 0 tests/{ => ops/elementwise}/test_add.py | 0 tests/{ => ops/elementwise}/test_exponent.py | 0 .../elementwise}/test_transcendental.py | 0 tests/ops/fusion/__init__.py | 0 .../fusion}/test_addmm_residual.py | 0 .../fusion}/test_attention_fusion.py | 0 .../fusion}/test_bmm_reduction.py | 0 .../fusion}/test_conv_fusion.py | 0 .../fusion}/test_matmul_activation.py | 0 .../fusion}/test_matmul_reduction.py | 0 .../fusion}/test_matmul_scalar.py | 0 .../fusion}/test_matmul_vector.py | 0 .../fusion}/test_prologue_fusion.py | 0 .../fusion}/test_transformer_fusion.py | 0 tests/ops/gemm/__init__.py | 0 tests/{ => ops/gemm}/test_bmm.py | 0 tests/{ => ops/gemm}/test_matmul.py | 0 tests/ops/misc/__init__.py | 0 tests/{ => ops/misc}/test_expert_mask.py | 0 tests/{ => ops/misc}/test_indirect_access.py | 0 tests/ops/reduce/__init__.py | 0 tests/{ => ops/reduce}/test_batchnorm.py | 0 tests/{ => ops/reduce}/test_layernorm.py | 0 tests/{ => ops/reduce}/test_reduce.py | 0 tests/{ => ops/reduce}/test_softmax.py | 0 tests/ops/sort/__init__.py | 0 tests/{ => ops/sort}/test_sort.py | 0 tests/{ => ops/sort}/test_topk.py | 0 tests/ops/sparsity/__init__.py | 0 tests/{ => ops/sparsity}/test_sparse_core.py | 0 tests/{ => ops/sparsity}/test_sparsity.py | 7 +- tests/ops/view/__init__.py | 0 tests/{ => ops/view}/test_cat.py | 0 tests/{ => ops/view}/test_transpose2D.py | 0 tests/{ => ops/view}/test_transpose3D.py | 0 tests/{ => ops/view}/test_view3D_2D.py | 0 tests/system/__init__.py | 0 tests/{ => system}/test_eager.py | 0 tests/{ => system}/test_hetro.py | 4 +- tests/{ => system}/test_scheduler.py | 8 +- tests/{ => system}/test_stonne.py | 0 tests/{ => system}/test_vectorops.py | 17 ++-- 78 files changed, 120 insertions(+), 110 deletions(-) rename tests/{ => models}/DeepSeek/test_deepseek_v3_base.py (100%) rename tests/{ => models}/Diffusion/test_diffusion.py (100%) rename tests/{ => models}/Llama/test_llama.py (100%) rename tests/{ => models}/MLP/test_mlp.py (100%) rename tests/{ => models}/MLP/test_mlp_cpu.py (100%) rename tests/{Mixtral_8x7B => models/Mixtral8x7B}/model.py (100%) rename tests/{Mixtral_8x7B => models/Mixtral8x7B}/test_attention.py (100%) rename tests/{ => models}/MoE/test_moe.py (100%) rename tests/{ => models}/MoE/test_moe_cpu.py (100%) rename tests/{ => models}/MobileNet/test_mobilenet.py (100%) rename tests/{ => models}/Yolov5/test_yolov5.py (100%) rename tests/{Fusion => models}/__init__.py (100%) rename tests/{ => models}/test_mlp.py (100%) rename tests/{ => models}/test_resnet.py (100%) rename tests/{ => models}/test_single_perceptron.py (100%) rename tests/{ => models}/test_transformer.py (100%) rename tests/{ => models}/test_vit.py (100%) create mode 100644 tests/ops/__init__.py create mode 100644 tests/ops/attention/__init__.py rename tests/{ => ops/attention}/test_gqa.py (100%) rename tests/{ => ops/attention}/test_gqa_decode.py (100%) rename tests/{ => ops/attention}/test_sdpa.py (100%) create mode 100644 tests/ops/conv/__init__.py rename tests/{ => ops/conv}/test_cnn.py (100%) rename tests/{ => ops/conv}/test_conv2d.py (100%) rename tests/{ => ops/conv}/test_group_conv.py (100%) rename tests/{ => ops/conv}/test_pool.py (100%) create mode 100644 tests/ops/elementwise/__init__.py rename tests/{ => ops/elementwise}/test_activation.py (100%) rename tests/{ => ops/elementwise}/test_add.py (100%) rename tests/{ => ops/elementwise}/test_exponent.py (100%) rename tests/{ => ops/elementwise}/test_transcendental.py (100%) create mode 100644 tests/ops/fusion/__init__.py rename tests/{Fusion => ops/fusion}/test_addmm_residual.py (100%) rename tests/{Fusion => ops/fusion}/test_attention_fusion.py (100%) rename tests/{Fusion => ops/fusion}/test_bmm_reduction.py (100%) rename tests/{Fusion => ops/fusion}/test_conv_fusion.py (100%) rename tests/{Fusion => ops/fusion}/test_matmul_activation.py (100%) rename tests/{Fusion => ops/fusion}/test_matmul_reduction.py (100%) rename tests/{Fusion => ops/fusion}/test_matmul_scalar.py (100%) rename tests/{Fusion => ops/fusion}/test_matmul_vector.py (100%) rename tests/{Fusion => ops/fusion}/test_prologue_fusion.py (100%) rename tests/{Fusion => ops/fusion}/test_transformer_fusion.py (100%) create mode 100644 tests/ops/gemm/__init__.py rename tests/{ => ops/gemm}/test_bmm.py (100%) rename tests/{ => ops/gemm}/test_matmul.py (100%) create mode 100644 tests/ops/misc/__init__.py rename tests/{ => ops/misc}/test_expert_mask.py (100%) rename tests/{ => ops/misc}/test_indirect_access.py (100%) create mode 100644 tests/ops/reduce/__init__.py rename tests/{ => ops/reduce}/test_batchnorm.py (100%) rename tests/{ => ops/reduce}/test_layernorm.py (100%) rename tests/{ => ops/reduce}/test_reduce.py (100%) rename tests/{ => ops/reduce}/test_softmax.py (100%) create mode 100644 tests/ops/sort/__init__.py rename tests/{ => ops/sort}/test_sort.py (100%) rename tests/{ => ops/sort}/test_topk.py (100%) create mode 100644 tests/ops/sparsity/__init__.py rename tests/{ => ops/sparsity}/test_sparse_core.py (100%) rename tests/{ => ops/sparsity}/test_sparsity.py (94%) create mode 100644 tests/ops/view/__init__.py rename tests/{ => ops/view}/test_cat.py (100%) rename tests/{ => ops/view}/test_transpose2D.py (100%) rename tests/{ => ops/view}/test_transpose3D.py (100%) rename tests/{ => ops/view}/test_view3D_2D.py (100%) create mode 100644 tests/system/__init__.py rename tests/{ => system}/test_eager.py (100%) rename tests/{ => system}/test_hetro.py (94%) rename tests/{ => system}/test_scheduler.py (87%) rename tests/{ => system}/test_stonne.py (100%) rename tests/{ => system}/test_vectorops.py (64%) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 7022ebba..36a5f4f5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -19,7 +19,7 @@ If the issue occurs while running a Python workload or involves a simulator cras For example: ``` -python3 tests/test_add.py +python3 tests/ops/elementwise/test_add.py ... [SpikeSimulator] cmd> spike --isa rv64gcv --varch=vlen:256,elen:64 --vectorlane-size=128 \ -m0x80000000:0x1900000000,0x2000000000:0x1000000 \ diff --git a/.github/workflows/pytorchsim_test.yml b/.github/workflows/pytorchsim_test.yml index e7ae6385..da8366af 100644 --- a/.github/workflows/pytorchsim_test.yml +++ b/.github/workflows/pytorchsim_test.yml @@ -33,7 +33,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_add.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/elementwise/test_add.py test_transcendental: name: Run test_transcendental.py @@ -52,7 +52,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_transcendental.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/elementwise/test_transcendental.py test_activation: name: Run test_activation.py @@ -71,7 +71,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_activation.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/elementwise/test_activation.py test_batchnorm: name: Run test_batchnorm.py @@ -90,7 +90,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_batchnorm.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/reduce/test_batchnorm.py test_bmm: name: Run test_bmm.py @@ -109,7 +109,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_bmm.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/gemm/test_bmm.py test_cnn: name: Run test_cnn.py @@ -128,7 +128,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_cnn.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/conv/test_cnn.py test_conv2d: name: Run test_conv2d.py @@ -147,7 +147,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_conv2d.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/conv/test_conv2d.py test_cat: name: Run test_cat.py @@ -166,7 +166,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_cat.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/view/test_cat.py test_matmul: name: Run test_matmul.py @@ -185,7 +185,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_matmul.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/gemm/test_matmul.py test_reduce: name: Run test_reduce.py @@ -204,7 +204,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_reduce.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/reduce/test_reduce.py test_softmax: name: Run test_softmax.py @@ -223,7 +223,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_softmax.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/reduce/test_softmax.py test_transpose2D: name: Run test_transpose2D.py @@ -242,7 +242,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_transpose2D.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/view/test_transpose2D.py test_view3D_2D: name: Run test_view3D_2D.py @@ -261,7 +261,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_view3D_2D.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/view/test_view3D_2D.py test_layernorm: name: Run test_layernorm.py @@ -280,7 +280,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_layernorm.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/reduce/test_layernorm.py test_mlp: name: Run test_mlp.py @@ -299,7 +299,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_mlp.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/models/test_mlp.py test_resnet: name: Run test_resnet.py @@ -318,7 +318,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_resnet.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/models/test_resnet.py - name: Run test_resnet50.py run: | @@ -326,7 +326,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_resnet.py --model_type resnet50 + ${{ inputs.image_name }} python3 PyTorchSim/tests/models/test_resnet.py --model_type resnet50 test_mobilenet: name: Run test_mobilenet.py @@ -345,7 +345,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/MobileNet/test_mobilenet.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/models/MobileNet/test_mobilenet.py test_transformer: name: Run test_transformer.py @@ -364,7 +364,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_transformer.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/models/test_transformer.py test_transpose3D: name: Run test_transpose3D.py @@ -383,7 +383,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_transpose3D.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/view/test_transpose3D.py test_sparsity: name: Run test_sparsity.py @@ -402,7 +402,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_sparsity.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/sparsity/test_sparsity.py test_pool: name: Run test_pool.py @@ -421,7 +421,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_pool.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/conv/test_pool.py test_perceptron: name: Run test_perceptron.py @@ -440,7 +440,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_single_perceptron.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/models/test_single_perceptron.py test_fusion: name: Run test_fusion @@ -459,7 +459,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/Fusion/test_addmm_residual.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/fusion/test_addmm_residual.py - name: Run test_matmul_activation.py run: | @@ -467,7 +467,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/Fusion/test_matmul_activation.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/fusion/test_matmul_activation.py - name: Run test_matmul_scalar.py run: | @@ -475,7 +475,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/Fusion/test_matmul_scalar.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/fusion/test_matmul_scalar.py - name: Run test_matmul_reduction.py run: | @@ -483,7 +483,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/Fusion/test_matmul_reduction.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/fusion/test_matmul_reduction.py - name: Run test_bmm_reduction.py run: | @@ -491,7 +491,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/Fusion/test_bmm_reduction.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/fusion/test_bmm_reduction.py - name: Run test_prologue_fusion.py run: | @@ -499,7 +499,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/Fusion/test_prologue_fusion.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/fusion/test_prologue_fusion.py - name: Run test_transformer_fusion.py run: | @@ -507,7 +507,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/Fusion/test_transformer_fusion.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/fusion/test_transformer_fusion.py - name: Run test_conv_fusion.py run: | @@ -515,7 +515,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/Fusion/test_conv_fusion.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/fusion/test_conv_fusion.py - name: Run test_attention_fusion.py run: | @@ -523,7 +523,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/Fusion/test_attention_fusion.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/fusion/test_attention_fusion.py - name: Run test_matmul_vector.py run: | @@ -531,7 +531,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/Fusion/test_matmul_vector.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/fusion/test_matmul_vector.py test_moe: name: Run test_moe @@ -550,7 +550,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/MoE/test_moe.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/models/MoE/test_moe.py test_mistral: name: Run test_mistral @@ -569,7 +569,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/Mixtral_8x7B/test_attention.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/models/Mixtral8x7B/test_attention.py test_vit: name: Run test_vit @@ -588,7 +588,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_vit.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/models/test_vit.py test_diffusion: name: Run test_diffusion @@ -607,7 +607,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/Diffusion/test_diffusion.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/models/Diffusion/test_diffusion.py test_indirect: name: Run test_indirect @@ -626,7 +626,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_indirect_access.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/misc/test_indirect_access.py test_scheduler: name: Run test_scheduler @@ -645,7 +645,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_scheduler.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/system/test_scheduler.py test_llama: name: Run test_llama1&2 @@ -664,7 +664,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/Llama/test_llama.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/models/Llama/test_llama.py test_yolov5: name: Run test_yolov5 @@ -683,7 +683,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/Yolov5/test_yolov5.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/models/Yolov5/test_yolov5.py test_deepseek: name: Run test_deepseek @@ -702,7 +702,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/DeepSeek/test_deepseek_v3_base.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/models/DeepSeek/test_deepseek_v3_base.py test_eager: name: Run test_eager.py @@ -721,7 +721,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_eager.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/system/test_eager.py test_exponent: name: Run test_exponent.py @@ -740,7 +740,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_exponent.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/elementwise/test_exponent.py test_sort: name: Run test_sort.py @@ -759,7 +759,7 @@ jobs: docker run --rm \ -e vpu_num_lanes="${{ inputs.vector_lane }}" \ -e vpu_spad_size_kb_per_lane="${{ inputs.spad_size }}" \ - ${{ inputs.image_name }} python3 PyTorchSim/tests/test_sort.py + ${{ inputs.image_name }} python3 PyTorchSim/tests/ops/sort/test_sort.py test_accuracy: name: Run test_accuracy and test_speedup diff --git a/CLAUDE.md b/CLAUDE.md index 8eb99c93..4a4e7424 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -23,7 +23,7 @@ The pipeline runs in that order on every `torch.compile` invocation; you'll see | `TOGSim/` | C++ TOGSim source. `src/Simulator.cc`, `Core.cc`, `Dram.cc`, `Interconnect.cc`, `L2Cache.cc`, `Tile.cc`, `TileGraph.cc` are the core models. Externals: ramulator2, booksim, stonneCore, onnx, protobuf, spdlog, yaml-cpp | | `AsmParser/` | `tog_generator.py`, `onnx_utility.py` — TOG generation from ONNX/ASM | | `configs/` | TOGSim hardware configs (YAML). The default is `systolic_ws_128x128_c1_simple_noc_tpuv3.yml`. Naming pattern: `systolic_ws__c__.yml` | -| `tests/` | ~36 op- and model-level tests. Subdirs `DeepSeek/`, `Diffusion/`, `Llama/`, `MLP/`, `Mixtral_8x7B/`, `MoE/`, `Yolov5/`, `Fusion/` for whole-model workloads | +| `tests/` | Op- and model-level tests organized under `ops//` (elementwise, reduce, gemm, conv, attention, view, sort, sparsity, misc, fusion), `models//` (Llama, Mixtral8x7B, DeepSeek, Diffusion, MoE, MLP, MobileNet, Yolov5) plus single-file model tests (test_resnet, test_transformer, test_vit, test_mlp, test_single_perceptron), and `system/` (scheduler, eager, hetro, stonne, vectorops). Shared helper: `tests/_utils.py` | | `experiments/artifact/` | Paper reproduction scripts (`cycle_validation/run_cycle.sh`, `speedup/run_speedup.sh`) | | `scripts/` | One-off experiment runners (CompilerOpt, ILS, batch, chiplet, sparsity, stonne, end2end). `build_from_source.sh` builds gem5/llvm/spike | | `gem5_script/` | gem5 wrapper scripts called by `CycleSimulator` | @@ -36,16 +36,16 @@ The pipeline runs in that order on every `torch.compile` invocation; you'll see Most tests follow the same pattern: build CPU reference, compile via `torch.compile` on `npu:0`, compare with `torch.allclose` (rtol=atol=1e-4). They all have `if __name__ == "__main__"` blocks. ```bash -python tests/test_add.py # vector add (smoke test, fastest) -python tests/test_matmul.py # GEMM -python tests/test_mlp.py # MLP forward + backward (training path) -python tests/test_scheduler.py # multi-tenant launch_model -python tests/test_eager.py # eager-fallback registration +python tests/ops/elementwise/test_add.py # vector add (smoke test, fastest) +python tests/ops/gemm/test_matmul.py # GEMM +python tests/models/test_mlp.py # MLP forward + backward (training path) +python tests/system/test_scheduler.py # multi-tenant launch_model +python tests/system/test_eager.py # eager-fallback registration ``` -Run a model from `tests/Llama/`, `tests/DeepSeek/`, etc. similarly. +Run a model from `tests/models/Llama/`, `tests/models/DeepSeek/`, etc. similarly. -**CI coverage:** the GitHub Actions workflow `.github/workflows/pytorchsim_test.yml` runs an **explicit allowlist** of `tests/*.py` files (~40 jobs, one Docker container per test). Adding a new file under `tests/` does *not* automatically gate PRs — register it in `pytorchsim_test.yml` if you want CI to exercise it. Conversely, files like `tests/test_gqa.py`, `tests/test_gqa_decode.py`, and `tests/test_eager.py` exist in the repo but are *not* in CI, so local validation is the only safety net for them. +**CI coverage:** the GitHub Actions workflow `.github/workflows/pytorchsim_test.yml` runs an **explicit allowlist** of `tests/*.py` files (~40 jobs, one Docker container per test). Adding a new file under `tests/` does *not* automatically gate PRs — register it in `pytorchsim_test.yml` if you want CI to exercise it. Conversely, files like `tests/ops/attention/test_gqa.py`, `tests/ops/attention/test_gqa_decode.py`, and `tests/system/test_eager.py` exist in the repo but are *not* in CI, so local validation is the only safety net for them. **For fast iteration** (skip functional check): ```bash @@ -123,7 +123,7 @@ Conan deps for TOGSim: `boost/1.79.0`, `robin-hood-hashing/3.11.5`, `spdlog/1.11 - **Adding a PyTorch device op:** `PyTorchSimDevice/csrc/aten/native/*` (Minimal/Extra split mirrors `torch_openreg`). - **TOGSim hardware model changes:** `TOGSim/src/{Core,Dram,Interconnect,L2Cache,Tile,TileGraph}.cc` + matching `include/*.h`. - **TOG generation:** `AsmParser/tog_generator.py` builds the raw graph and serializes it via `AsmParser/onnx_utility.py` to **ONNX, which is the on-disk TOG format** consumed by TOGSim. -- **Eager fallback registration:** `torch.npu.register_eager_to_compile([...])` — see `tests/test_eager.py`. +- **Eager fallback registration:** `torch.npu.register_eager_to_compile([...])` — see `tests/system/test_eager.py`. - **Per-run results:** `togsim_results/>.log` (stats) and `.trace` (instruction trace). The path is also printed at the end of every run. - **Wrapper codegen path:** printed as `Wrapper Codegen Path = /tmp/torchinductor_//...py` — useful for inspecting generated kernel code and tensor names for `SRAM_BUFFER_PLAN_PATH`. diff --git a/README.md b/README.md index 800f4761..f0bdc772 100644 --- a/README.md +++ b/README.md @@ -40,15 +40,15 @@ PyTorchSim **supports**: |---|:-:|:-:|---| | ResNet-18 | | ✅ | channel last format | | ResNet-50 | | ✅ | channel last format | -| MobileNet-v2 | | ✅ | `tests/MobileNet/` (torchvision) | -| YOLOv5 | | ✅ | `tests/Yolov5/` | +| MobileNet-v2 | | ✅ | `tests/models/MobileNet/` (torchvision) | +| YOLOv5 | | ✅ | `tests/models/Yolov5/` | | BERT | | ✅ | | | GPT-2 | | ✅ | | -| ViT | | ✅ | `tests/test_vit.py` | +| ViT | | ✅ | `tests/models/test_vit.py` | | Mistral | | ✅ | | | Stable-diffusion v1 | 🤗 | ✅ | | -| Llama 2/3 | 🤗 | ✅ | `tests/Llama/` (blocks & decode-style paths) | -| DeepSeek-V3 (base) | 🤗 | ✅ | `tests/DeepSeek/` — several ops(e.g., gate ops) are not cycle-modeled | +| Llama 2/3 | 🤗 | ✅ | `tests/models/Llama/` (blocks & decode-style paths) | +| DeepSeek-V3 (base) | 🤗 | ✅ | `tests/models/DeepSeek/` — several ops(e.g., gate ops) are not cycle-modeled | | Llama-4 | 🤗 | ⏳ | In development | | Broader model support | — | ⏳ | In development |