From e1286014c12409b7ab047638b8c8ec4dca31a071 Mon Sep 17 00:00:00 2001 From: Stefanos Laskaridis Date: Fri, 19 Jul 2024 16:35:50 +0100 Subject: [PATCH] Initial commit --- .clang-format | 8 + .github/ISSUE_TEMPLATE/bug-report.md | 43 + .github/ISSUE_TEMPLATE/config.yml | 9 + .github/ISSUE_TEMPLATE/documentation.md | 17 + .github/ISSUE_TEMPLATE/feature-request.md | 23 + .github/ISSUE_TEMPLATE/general.md | 13 + .github/ISSUE_TEMPLATE/model-request.md | 17 + .github/ISSUE_TEMPLATE/speed-report.md | 24 + .github/ISSUE_TEMPLATE/tracking.md | 40 + .github/workflows/documentation.yml | 39 + .github/workflows/update-relax.yml | 32 + .gitignore | 324 + .gitmodules | 12 + 0001-Add-events-timing-to-MLCChat.patch | 145 + 0002-Parse-stats-into-json-format.patch | 60 + 3rdparty/argparse | 1 + 3rdparty/googletest | 1 + 3rdparty/tokenizers-cpp | 1 + 3rdparty/tvm | 1 + CMakeLists.txt | 177 + CONTRIBUTORS.md | 6 + LICENSE | 213 + README.md | 220 + android/.gitignore | 19 + android/README.md | 3 + android/app/.gitignore | 2 + android/app/build.gradle | 73 + android/app/proguard-rules.pro | 21 + android/app/src/main/AndroidManifest.xml | 42 + .../app/src/main/ic_launcher-playstore.png | Bin 0 -> 47710 bytes .../main/java/ai/mlc/mlcchat/AppViewModel.kt | 912 ++ .../src/main/java/ai/mlc/mlcchat/ChatView.kt | 236 + .../mlc/mlcchat/ConversationsRecordManager.kt | 89 + .../main/java/ai/mlc/mlcchat/MainActivity.kt | 29 + .../src/main/java/ai/mlc/mlcchat/NavView.kt | 18 + .../main/java/ai/mlc/mlcchat/RestAwaitLib.kt | 25 + .../src/main/java/ai/mlc/mlcchat/StartView.kt | 251 + .../java/ai/mlc/mlcchat/ui/theme/Color.kt | 44 + .../java/ai/mlc/mlcchat/ui/theme/Theme.kt | 107 + .../main/java/ai/mlc/mlcchat/ui/theme/Type.kt | 34 + .../res/drawable/ic_android_black_24dp.xml | 5 + .../src/main/res/drawable/mlc_logo_108.xml | 11 + android/app/src/main/res/values/colors.xml | 10 + android/app/src/main/res/values/strings.xml | 3 + android/app/src/main/res/values/themes.xml | 6 + android/app/src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + android/build.gradle | 5 + android/docs/setup-notes.md | 56 + android/gradle.properties | 23 + android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + android/gradlew | 185 + android/gradlew.bat | 89 + android/library/.gitignore | 1 + android/library/CMakeLists.txt | 74 + android/library/build.gradle | 38 + android/library/prepare_libs.sh | 33 + android/library/prepare_model_lib.py | 24 + android/library/src/cpp/tvm_runtime.h | 28 + android/library/src/main/AndroidManifest.xml | 4 + .../library/src/main/assets/app-config.json | 48 + android/library/src/main/assets/input.json | 10 + .../main/java/ai/mlc/mlcllm/ChatModule.java | 157 + android/settings.gradle | 17 + build.py | 4 + build_scripts/README.md | 95 + build_scripts/build_android.sh | 24 + build_scripts/build_ios.sh | 26 + build_scripts/build_mlc.sh | 75 + build_scripts/build_tvm_unity.sh | 79 + build_scripts/install_android_apk.sh | 10 + build_scripts/install_ios_ipa.sh | 12 + build_scripts/push_android_models.sh | 30 + build_scripts/push_ios_models.sh | 38 + build_scripts/run_jetson_docker.sh | 3 + ci/bash.sh | 72 + ci/jenkinsfile.groovy | 256 + ci/task/black.sh | 10 + ci/task/build_clean.sh | 11 + ci/task/build_lib.sh | 40 + ci/task/build_wheel.sh | 27 + ci/task/clang-format.sh | 63 + ci/task/isort.sh | 10 + ci/task/mypy.sh | 6 + ci/task/pylint.sh | 13 + ci/task/test_model_compile.sh | 37 + cmake/gen_cmake_config.py | 73 + cpp/README.md | 3 + cpp/base.h | 16 + cpp/conv_templates.cc | 896 ++ cpp/conversation.cc | 185 + cpp/conversation.h | 298 + cpp/image_embed.cc | 209 + cpp/image_embed.h | 18 + cpp/llm_chat.cc | 1969 ++++ cpp/llm_chat.h | 20 + cpp/loader/multi_gpu_loader.cc | 255 + cpp/metadata/json_parser.h | 147 + cpp/metadata/model.cc | 100 + cpp/metadata/model.h | 60 + cpp/random.h | 37 + cpp/serve/async_threaded_engine.cc | 136 + cpp/serve/async_threaded_engine.h | 54 + cpp/serve/config.cc | 293 + cpp/serve/config.h | 112 + cpp/serve/data.cc | 164 + cpp/serve/data.h | 163 + cpp/serve/engine.cc | 343 + cpp/serve/engine.h | 122 + cpp/serve/engine_actions/action.cc | 16 + cpp/serve/engine_actions/action.h | 118 + cpp/serve/engine_actions/action_commons.cc | 127 + cpp/serve/engine_actions/action_commons.h | 63 + cpp/serve/engine_actions/batch_decode.cc | 150 + cpp/serve/engine_actions/batch_draft.cc | 171 + cpp/serve/engine_actions/batch_verify.cc | 257 + .../engine_actions/new_request_prefill.cc | 230 + cpp/serve/engine_state.cc | 58 + cpp/serve/engine_state.h | 125 + cpp/serve/event_trace_recorder.cc | 161 + cpp/serve/event_trace_recorder.h | 73 + cpp/serve/function_table.cc | 252 + cpp/serve/function_table.h | 94 + cpp/serve/grammar/grammar.cc | 125 + cpp/serve/grammar/grammar.h | 201 + cpp/serve/grammar/grammar_builder.h | 212 + cpp/serve/grammar/grammar_parser.cc | 437 + cpp/serve/grammar/grammar_parser.h | 67 + cpp/serve/grammar/grammar_serializer.cc | 156 + cpp/serve/grammar/grammar_serializer.h | 115 + cpp/serve/grammar/grammar_simplifier.cc | 219 + cpp/serve/grammar/grammar_simplifier.h | 184 + cpp/serve/grammar/grammar_state_matcher.cc | 517 + cpp/serve/grammar/grammar_state_matcher.h | 125 + .../grammar/grammar_state_matcher_base.h | 236 + .../grammar/grammar_state_matcher_preproc.h | 315 + .../grammar/grammar_state_matcher_state.h | 442 + cpp/serve/grammar/support.h | 123 + cpp/serve/logit_processor.cc | 404 + cpp/serve/logit_processor.h | 94 + cpp/serve/model.cc | 472 + cpp/serve/model.h | 165 + cpp/serve/request.cc | 83 + cpp/serve/request.h | 83 + cpp/serve/request_state.cc | 162 + cpp/serve/request_state.h | 162 + cpp/serve/sampler.cc | 447 + cpp/serve/sampler.h | 99 + cpp/streamer.cc | 283 + cpp/streamer.h | 138 + cpp/support/encoding.cc | 184 + cpp/support/encoding.h | 77 + cpp/support/load_bytes_from_file.h | 32 + cpp/support/progress_bar.h | 49 + cpp/tokenizers.cc | 152 + cpp/tokenizers.h | 76 + docs/.gitignore | 1 + docs/Makefile | 20 + docs/README.md | 30 + .../img/mlc-logo-with-text-landscape.svg | 87 + docs/_static/img/project-structure.svg | 1189 +++ docs/community/faq.rst | 16 + docs/community/guideline.rst | 125 + docs/compilation/compile_models.rst | 1056 +++ docs/compilation/configure_quantization.rst | 22 + docs/compilation/convert_weights.rst | 183 + docs/compilation/define_new_models.rst | 25 + docs/compilation/get-vicuna-weight.rst | 68 + docs/conf.py | 103 + docs/deploy/android.rst | 187 + docs/deploy/cli.rst | 106 + docs/deploy/ios.rst | 491 + docs/deploy/javascript.rst | 360 + docs/deploy/python.rst | 363 + docs/deploy/rest.rst | 394 + docs/get_started/mlc_chat_config.rst | 254 + docs/get_started/project_overview.rst | 88 + docs/index.rst | 233 + docs/install/conda.rst | 66 + docs/install/emcc.rst | 64 + docs/install/gpu.rst | 201 + docs/install/mlc_llm.rst | 240 + docs/install/tvm.rst | 313 + docs/make.bat | 35 + docs/prebuilt_models.rst | 773 ++ docs/prebuilt_models_deprecated.rst | 845 ++ docs/privacy.rst | 5 + docs/requirements.txt | 10 + examples/python/benchmark.py | 11 + examples/python/run_llama_batched_vllm.py | 448 + examples/python/sample_chat_stream.py | 30 + examples/python/sample_mlc_chat.py | 39 + examples/rest/nodejs/README.MD | 21 + examples/rest/nodejs/dotenv.example | 2 + examples/rest/nodejs/package.json | 40 + examples/rest/nodejs/sample_client.js | 74 + examples/rest/nodejs/sample_langchain.ts | 75 + examples/rest/nodejs/sample_openai.js | 77 + examples/rest/nodejs/tsconfig.json | 14 + examples/rest/python/sample_client.py | 46 + examples/rest/python/sample_langchain.py | 156 + examples/rest/python/sample_openai.py | 43 + examples/rest/resources/linux.txt | 23 + .../rest/resources/state_of_the_union.txt | 723 ++ ios/.gitignore | 2 + ios/MLCChat copy-Info.plist | 8 + ios/MLCChat.xcodeproj/project.pbxproj | 769 ++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 5 + .../xcshareddata/xcschemes/MLCChat.xcscheme | 81 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 14 + .../AppIcon.appiconset/mlc-logo.png | Bin 0 -> 71139 bytes ios/MLCChat/Assets.xcassets/Contents.json | 6 + ios/MLCChat/Common/Constants.swift | 11 + ios/MLCChat/Info.plist | 8 + ios/MLCChat/MLCChat.entitlements | 10 + ios/MLCChat/MLCChatApp.swift | 28 + ios/MLCChat/Models/AppConfig.swift | 28 + ios/MLCChat/Models/ModelConfig.swift | 18 + ios/MLCChat/Models/ParamsConfig.swift | 12 + ios/MLCChat/PerformanceMetrics.swift | 61 + .../Preview Assets.xcassets/Contents.json | 6 + ios/MLCChat/RestAwaitLib.swift | 47 + ios/MLCChat/States/AppState.swift | 274 + ios/MLCChat/States/ChatState.swift | 499 + ios/MLCChat/States/ModelState.swift | 414 + ios/MLCChat/Views/ChatView.swift | 193 + ios/MLCChat/Views/ImageProcessing.swift | 66 + ios/MLCChat/Views/MessageView.swift | 66 + ios/MLCChat/Views/ModelView.swift | 97 + ios/MLCChat/Views/StartView.swift | 47 + ios/MLCChat/app-config.json | 27 + ios/MLCSwift/Package.swift | 32 + ios/MLCSwift/README.md | 4 + ios/MLCSwift/Sources/ObjC/LLMChat.mm | 333 + ios/MLCSwift/Sources/ObjC/include/LLMChat.h | 132 + ios/MLCSwift/Sources/Swift/LLMChat.swift | 1 + ios/MLCSwift/Sources/Swift/ThreadWorker.swift | 31 + ios/MLCSwift/tvm_home | 1 + ios/README.md | 3 + ios/prepare_libs.sh | 74 + ios/prepare_model_lib.py | 29 + ios/prepare_params.sh | 32 + mlc_llm/__init__.py | 7 + mlc_llm/build.py | 47 + mlc_llm/core.py | 1015 ++ mlc_llm/dispatch/__init__.py | 2 + mlc_llm/dispatch/dispatch_tir_operator.py | 53 + .../dispatch/dispatch_tir_operator_adreno.py | 8356 +++++++++++++++++ mlc_llm/dispatch/gpt_neox/__init__.py | 13 + mlc_llm/dispatch/gpt_neox/dolly_v2_3b.py | 1034 ++ mlc_llm/dispatch/gpt_neox/dolly_v2_3b_mod.py | 511 + .../gpt_neox/redpajama_incite_chat_3b_v1.py | 972 ++ .../redpajama_incite_chat_3b_v1_mod.py | 722 ++ .../redpajama_incite_chat_3b_v1_tune.py | 1010 ++ mlc_llm/dispatch/gpt_neox/redpajama_q4f32.py | 840 ++ .../dispatch/gpt_neox/redpajama_q4f32_mod.py | 577 ++ .../dispatch/gpt_neox/redpajama_q4f32_tune.py | 743 ++ mlc_llm/dispatch/llama/__init__.py | 1 + mlc_llm/dispatch/llama/main.py | 6712 +++++++++++++ mlc_llm/quantization/__init__.py | 232 + mlc_llm/quantization/autogptq_quantization.py | 193 + mlc_llm/quantization/ft_quantization.py | 219 + mlc_llm/quantization/group_quantization.py | 214 + mlc_llm/quantization/quantization.py | 217 + mlc_llm/quantization/tir_utils.py | 106 + mlc_llm/relax_model/__init__.py | 1 + mlc_llm/relax_model/chatglm.py | 797 ++ mlc_llm/relax_model/commons.py | 363 + mlc_llm/relax_model/gpt_bigcode.py | 661 ++ mlc_llm/relax_model/gpt_neox.py | 733 ++ mlc_llm/relax_model/gptj.py | 688 ++ mlc_llm/relax_model/llama.py | 1507 +++ mlc_llm/relax_model/llama_batched_vllm.py | 658 ++ mlc_llm/relax_model/minigpt.py | 627 ++ mlc_llm/relax_model/mistral.py | 1125 +++ mlc_llm/relax_model/modules.py | 280 + mlc_llm/relax_model/param_manager.py | 1209 +++ mlc_llm/relax_model/rwkv.py | 641 ++ mlc_llm/relax_model/stablelm_3b.py | 913 ++ mlc_llm/transform/__init__.py | 9 + mlc_llm/transform/clean_up_tir_attrs.py | 25 + mlc_llm/transform/decode_matmul_ewise.py | 84 + mlc_llm/transform/decode_take.py | 71 + mlc_llm/transform/decode_transpose.py | 113 + .../transform/fuse_split_rotary_embedding.py | 284 + .../transform/lift_tir_global_buffer_alloc.py | 197 + mlc_llm/transform/reorder_transform_func.py | 231 + mlc_llm/transform/rewrite_attention.py | 46 + mlc_llm/transform/transpose_matmul.py | 349 + mlc_llm/utils.py | 741 ++ pyproject.toml | 37 + python/README.md | 5 + python/mlc_chat/__init__.py | 7 + python/mlc_chat/__main__.py | 47 + python/mlc_chat/_ffi_api.py | 6 + python/mlc_chat/base.py | 28 + python/mlc_chat/callback.py | 141 + python/mlc_chat/chat_module.py | 1234 +++ python/mlc_chat/cli/__init__.py | 0 python/mlc_chat/cli/bench.py | 62 + python/mlc_chat/cli/benchmark.py | 86 + python/mlc_chat/cli/chat.py | 54 + python/mlc_chat/cli/check_device.py | 30 + python/mlc_chat/cli/compile.py | 142 + python/mlc_chat/cli/convert_weight.py | 95 + python/mlc_chat/cli/delivery.py | 280 + python/mlc_chat/cli/gen_config.py | 106 + python/mlc_chat/cli/model_metadata.py | 182 + python/mlc_chat/cli/worker.py | 53 + python/mlc_chat/compiler_pass/__init__.py | 2 + .../compiler_pass/attach_to_ir_module.py | 159 + .../compiler_pass/clean_up_tir_attrs.py | 30 + .../mlc_chat/compiler_pass/cublas_dispatch.py | 31 + .../compiler_pass/estimate_memory_usage.py | 84 + .../mlc_chat/compiler_pass/fuse_add_norm.py | 211 + .../fuse_dequantize_matmul_ewise.py | 86 + .../compiler_pass/fuse_dequantize_take.py | 90 + .../fuse_dequantize_transpose.py | 106 + .../fuse_ft_dequantize_matmul_epilogue.py | 322 + .../compiler_pass/fuse_transpose_matmul.py | 151 + .../compiler_pass/lift_global_buffer_alloc.py | 195 + python/mlc_chat/compiler_pass/pipeline.py | 160 + .../rewrite_kv_cache_creation.py | 154 + .../compiler_pass/scatter_tuple_get_item.py | 51 + python/mlc_chat/conversation_template.py | 77 + python/mlc_chat/embeddings/__init__.py | 0 python/mlc_chat/embeddings/openai.py | 245 + python/mlc_chat/gradio.py | 247 + python/mlc_chat/help.py | 142 + python/mlc_chat/interface/__init__.py | 0 python/mlc_chat/interface/bench.py | 28 + python/mlc_chat/interface/chat.py | 196 + python/mlc_chat/interface/compile.py | 230 + python/mlc_chat/interface/compiler_flags.py | 158 + python/mlc_chat/interface/convert_weight.py | 169 + python/mlc_chat/interface/gen_config.py | 232 + python/mlc_chat/interface/jit.py | 128 + python/mlc_chat/interface/openai_api.py | 183 + python/mlc_chat/libinfo.py | 70 + python/mlc_chat/loader/__init__.py | 7 + python/mlc_chat/loader/huggingface_loader.py | 222 + python/mlc_chat/loader/loader.py | 12 + python/mlc_chat/loader/mapping.py | 101 + python/mlc_chat/loader/stats.py | 95 + python/mlc_chat/loader/utils.py | 66 + python/mlc_chat/model/__init__.py | 3 + python/mlc_chat/model/baichuan/__init__.py | 0 .../model/baichuan/baichuan_loader.py | 70 + .../mlc_chat/model/baichuan/baichuan_model.py | 252 + .../model/baichuan/baichuan_quantization.py | 53 + python/mlc_chat/model/gemma/__init__.py | 0 python/mlc_chat/model/gemma/gemma_loader.py | 121 + python/mlc_chat/model/gemma/gemma_model.py | 386 + .../model/gemma/gemma_quantization.py | 38 + python/mlc_chat/model/gpt2/__init__.py | 0 python/mlc_chat/model/gpt2/gpt2_loader.py | 79 + python/mlc_chat/model/gpt2/gpt2_model.py | 433 + .../mlc_chat/model/gpt2/gpt2_quantization.py | 69 + python/mlc_chat/model/gpt_bigcode/__init__.py | 0 .../model/gpt_bigcode/gpt_bigcode_loader.py | 49 + .../model/gpt_bigcode/gpt_bigcode_model.py | 289 + .../gpt_bigcode/gpt_bigcode_quantization.py | 70 + python/mlc_chat/model/gpt_neox/__init__.py | 0 .../model/gpt_neox/gpt_neox_loader.py | 89 + .../mlc_chat/model/gpt_neox/gpt_neox_model.py | 456 + .../model/gpt_neox/gpt_neox_quantization.py | 53 + python/mlc_chat/model/llama/__init__.py | 0 python/mlc_chat/model/llama/llama_loader.py | 171 + python/mlc_chat/model/llama/llama_model.py | 400 + .../model/llama/llama_quantization.py | 69 + python/mlc_chat/model/mistral/__init__.py | 0 .../mlc_chat/model/mistral/mistral_loader.py | 165 + .../mlc_chat/model/mistral/mistral_model.py | 528 ++ .../model/mistral/mistral_quantization.py | 69 + python/mlc_chat/model/mixtral/__init__.py | 0 .../mlc_chat/model/mixtral/mixtral_loader.py | 129 + .../mlc_chat/model/mixtral/mixtral_model.py | 176 + .../model/mixtral/mixtral_quantization.py | 61 + python/mlc_chat/model/model.py | 251 + python/mlc_chat/model/model_preset.py | 495 + python/mlc_chat/model/phi/__init__.py | 0 python/mlc_chat/model/phi/phi_loader.py | 162 + python/mlc_chat/model/phi/phi_model.py | 404 + python/mlc_chat/model/phi/phi_quantization.py | 53 + python/mlc_chat/model/qwen/__init__.py | 0 python/mlc_chat/model/qwen/qwen_loader.py | 70 + python/mlc_chat/model/qwen/qwen_model.py | 254 + .../mlc_chat/model/qwen/qwen_quantization.py | 53 + python/mlc_chat/model/qwen2/__init__.py | 0 python/mlc_chat/model/qwen2/qwen2_loader.py | 88 + python/mlc_chat/model/qwen2/qwen2_model.py | 271 + .../model/qwen2/qwen2_quantization.py | 54 + python/mlc_chat/model/stable_lm/__init__.py | 0 .../model/stable_lm/stablelm_loader.py | 104 + .../model/stable_lm/stablelm_model.py | 266 + .../model/stable_lm/stablelm_quantization.py | 53 + python/mlc_chat/nn/__init__.py | 3 + python/mlc_chat/nn/expert.py | 26 + python/mlc_chat/nn/kv_cache.py | 1435 +++ python/mlc_chat/op/__init__.py | 6 + python/mlc_chat/op/attention.py | 182 + python/mlc_chat/op/extern.py | 65 + python/mlc_chat/op/ft_gemm.py | 135 + python/mlc_chat/op/moe_matmul.py | 555 ++ python/mlc_chat/op/moe_misc.py | 408 + python/mlc_chat/op/position_embedding.py | 376 + python/mlc_chat/protocol/__init__.py | 4 + .../protocol/conversation_protocol.py | 136 + .../mlc_chat/protocol/openai_api_protocol.py | 329 + python/mlc_chat/protocol/protocol_utils.py | 58 + python/mlc_chat/quantization/__init__.py | 6 + .../mlc_chat/quantization/awq_quantization.py | 271 + .../mlc_chat/quantization/ft_quantization.py | 396 + .../quantization/group_quantization.py | 664 ++ .../mlc_chat/quantization/no_quantization.py | 14 + python/mlc_chat/quantization/quantization.py | 120 + python/mlc_chat/quantization/utils.py | 47 + python/mlc_chat/rest.py | 492 + python/mlc_chat/serve/__init__.py | 11 + python/mlc_chat/serve/_ffi_api.py | 6 + python/mlc_chat/serve/async_engine.py | 330 + python/mlc_chat/serve/config.py | 155 + python/mlc_chat/serve/data.py | 117 + python/mlc_chat/serve/engine.py | 494 + python/mlc_chat/serve/entrypoints/__init__.py | 2 + .../serve/entrypoints/debug_entrypoints.py | 48 + .../serve/entrypoints/entrypoint_utils.py | 92 + .../serve/entrypoints/openai_entrypoints.py | 537 ++ python/mlc_chat/serve/event_trace_recorder.py | 41 + python/mlc_chat/serve/grammar.py | 242 + python/mlc_chat/serve/request.py | 58 + python/mlc_chat/serve/server/__init__.py | 3 + python/mlc_chat/serve/server/__main__.py | 71 + python/mlc_chat/serve/server/popen_server.py | 119 + .../mlc_chat/serve/server/server_context.py | 47 + python/mlc_chat/streamer.py | 84 + python/mlc_chat/support/__init__.py | 4 + python/mlc_chat/support/argparse.py | 15 + python/mlc_chat/support/auto_config.py | 192 + python/mlc_chat/support/auto_device.py | 87 + python/mlc_chat/support/auto_target.py | 398 + python/mlc_chat/support/auto_weight.py | 177 + python/mlc_chat/support/config.py | 113 + python/mlc_chat/support/constants.py | 55 + python/mlc_chat/support/convert_tiktoken.py | 163 + python/mlc_chat/support/download.py | 147 + python/mlc_chat/support/logging.py | 20 + python/mlc_chat/support/preshard.py | 126 + python/mlc_chat/support/random.py | 16 + python/mlc_chat/support/style.py | 62 + python/mlc_chat/support/tensor_parallel.py | 97 + python/mlc_chat/support/tqdm.py | 38 + python/mlc_chat/tokenizer.py | 55 + python/setup.py | 131 + run_scripts/README.md | 36 + run_scripts/run-mlc-chat-cli.exp | 142 + run_scripts/run-mlc.sh | 40 + run_scripts/run_expect_all.sh | 30 + rust/.gitignore | 20 + rust/Cargo.toml | 22 + rust/README.md | 25 + rust/build.rs | 6 + rust/examples/mlc_chat.rs | 28 + rust/rustfmt.toml | 9 + rust/src/chat_module.rs | 601 ++ rust/src/config.rs | 273 + rust/src/lib.rs | 23 + scripts/build_site.sh | 9 + scripts/check_url_validity.py | 44 + scripts/gh_deploy_site.sh | 20 + scripts/local_deploy_site.sh | 8 + scripts/prep_emcc_deps.sh | 19 + setup.py | 47 + site/.gitignore | 4 + site/CNAME | 1 + site/_config.yml | 42 + site/gif/android-demo.gif | Bin 0 -> 855466 bytes site/gif/ios-demo.gif | Bin 0 -> 6795718 bytes site/gif/linux-demo.gif | Bin 0 -> 416279 bytes site/img/android/android-diagram.png | Bin 0 -> 468100 bytes site/img/android/android-studio.png | Bin 0 -> 1195564 bytes site/img/android/android-vs-ios.png | Bin 0 -> 184026 bytes site/img/android/local-advantage.png | Bin 0 -> 269011 bytes site/img/diag.svg | 1 + site/img/multi-gpu/figure-1.svg | 247 + site/img/multi-gpu/figure-2.svg | 418 + site/img/multi-gpu/figure-3.svg | 167 + site/index.md | 68 + site/privacy.md | 10 + tests/cpp/conv_unittest.cc | 27 + tests/legacy-python/compare_lib.py | 213 + tests/legacy-python/dump_intermediate.py | 172 + tests/legacy-python/evaluate.py | 202 + tests/legacy-python/module_intercept.py | 147 + tests/legacy-python/test_batching_llama.py | 160 + tests/legacy-python/test_build_args.py | 175 + .../test_build_model_from_args.py | 142 + .../legacy-python/test_sliding_window_mask.py | 338 + tests/python/__init__.py | 0 tests/python/api/test_python.py | 45 + tests/python/api/test_rest.py | 105 + ...test_fuse_ft_dequantize_matmul_epilogue.py | 342 + tests/python/conftest.py | 21 + .../python/integration/test_model_compile.py | 155 + tests/python/loader/test_awq.py | 40 + tests/python/loader/test_huggingface.py | 69 + tests/python/model/test_gpt2.py | 21 + tests/python/model/test_gptNeox.py | 21 + tests/python/model/test_kv_cache.py | 207 + tests/python/model/test_llama.py | 23 + tests/python/model/test_llama_quantization.py | 73 + tests/python/model/test_mistral.py | 21 + tests/python/model/test_phi.py | 22 + .../quantization/test_awq_quantization.py | 89 + .../quantization/test_group_quantization.py | 189 + tests/python/serve/benchmark.py | 159 + tests/python/serve/evaluate_engine.py | 76 + tests/python/serve/json.ebnf | 22 + tests/python/serve/server/conftest.py | 35 + tests/python/serve/server/test_server.py | 1036 ++ .../serve/server/test_server_function_call.py | 210 + .../python/serve/test_event_trace_recorder.py | 44 + tests/python/serve/test_grammar_parser.py | 260 + .../serve/test_grammar_state_matcher.py | 387 + tests/python/serve/test_serve_async_engine.py | 72 + .../serve/test_serve_async_engine_spec.py | 82 + tests/python/serve/test_serve_engine.py | 392 + tests/python/serve/test_serve_engine_spec.py | 372 + tests/python/support/test_auto_config.py | 41 + tests/python/support/test_auto_weight.py | 118 + tests/python/support/test_streamer.py | 198 + version.py | 145 + 536 files changed, 101760 insertions(+) create mode 100644 .clang-format create mode 100644 .github/ISSUE_TEMPLATE/bug-report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/documentation.md create mode 100644 .github/ISSUE_TEMPLATE/feature-request.md create mode 100644 .github/ISSUE_TEMPLATE/general.md create mode 100644 .github/ISSUE_TEMPLATE/model-request.md create mode 100644 .github/ISSUE_TEMPLATE/speed-report.md create mode 100644 .github/ISSUE_TEMPLATE/tracking.md create mode 100644 .github/workflows/documentation.yml create mode 100644 .github/workflows/update-relax.yml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 0001-Add-events-timing-to-MLCChat.patch create mode 100644 0002-Parse-stats-into-json-format.patch create mode 160000 3rdparty/argparse create mode 160000 3rdparty/googletest create mode 160000 3rdparty/tokenizers-cpp create mode 160000 3rdparty/tvm create mode 100644 CMakeLists.txt create mode 100644 CONTRIBUTORS.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 android/.gitignore create mode 100644 android/README.md create mode 100644 android/app/.gitignore create mode 100644 android/app/build.gradle create mode 100644 android/app/proguard-rules.pro create mode 100644 android/app/src/main/AndroidManifest.xml create mode 100644 android/app/src/main/ic_launcher-playstore.png create mode 100644 android/app/src/main/java/ai/mlc/mlcchat/AppViewModel.kt create mode 100644 android/app/src/main/java/ai/mlc/mlcchat/ChatView.kt create mode 100644 android/app/src/main/java/ai/mlc/mlcchat/ConversationsRecordManager.kt create mode 100644 android/app/src/main/java/ai/mlc/mlcchat/MainActivity.kt create mode 100644 android/app/src/main/java/ai/mlc/mlcchat/NavView.kt create mode 100644 android/app/src/main/java/ai/mlc/mlcchat/RestAwaitLib.kt create mode 100644 android/app/src/main/java/ai/mlc/mlcchat/StartView.kt create mode 100644 android/app/src/main/java/ai/mlc/mlcchat/ui/theme/Color.kt create mode 100644 android/app/src/main/java/ai/mlc/mlcchat/ui/theme/Theme.kt create mode 100644 android/app/src/main/java/ai/mlc/mlcchat/ui/theme/Type.kt create mode 100644 android/app/src/main/res/drawable/ic_android_black_24dp.xml create mode 100644 android/app/src/main/res/drawable/mlc_logo_108.xml create mode 100644 android/app/src/main/res/values/colors.xml create mode 100644 android/app/src/main/res/values/strings.xml create mode 100644 android/app/src/main/res/values/themes.xml create mode 100644 android/app/src/main/res/xml/backup_rules.xml create mode 100644 android/app/src/main/res/xml/data_extraction_rules.xml create mode 100644 android/build.gradle create mode 100644 android/docs/setup-notes.md create mode 100644 android/gradle.properties create mode 100644 android/gradle/wrapper/gradle-wrapper.jar create mode 100644 android/gradle/wrapper/gradle-wrapper.properties create mode 100755 android/gradlew create mode 100644 android/gradlew.bat create mode 100644 android/library/.gitignore create mode 100644 android/library/CMakeLists.txt create mode 100644 android/library/build.gradle create mode 100755 android/library/prepare_libs.sh create mode 100644 android/library/prepare_model_lib.py create mode 100644 android/library/src/cpp/tvm_runtime.h create mode 100644 android/library/src/main/AndroidManifest.xml create mode 100644 android/library/src/main/assets/app-config.json create mode 100644 android/library/src/main/assets/input.json create mode 100644 android/library/src/main/java/ai/mlc/mlcllm/ChatModule.java create mode 100644 android/settings.gradle create mode 100644 build.py create mode 100644 build_scripts/README.md create mode 100755 build_scripts/build_android.sh create mode 100755 build_scripts/build_ios.sh create mode 100755 build_scripts/build_mlc.sh create mode 100755 build_scripts/build_tvm_unity.sh create mode 100755 build_scripts/install_android_apk.sh create mode 100755 build_scripts/install_ios_ipa.sh create mode 100755 build_scripts/push_android_models.sh create mode 100755 build_scripts/push_ios_models.sh create mode 100755 build_scripts/run_jetson_docker.sh create mode 100755 ci/bash.sh create mode 100644 ci/jenkinsfile.groovy create mode 100755 ci/task/black.sh create mode 100755 ci/task/build_clean.sh create mode 100755 ci/task/build_lib.sh create mode 100755 ci/task/build_wheel.sh create mode 100755 ci/task/clang-format.sh create mode 100755 ci/task/isort.sh create mode 100755 ci/task/mypy.sh create mode 100755 ci/task/pylint.sh create mode 100755 ci/task/test_model_compile.sh create mode 100644 cmake/gen_cmake_config.py create mode 100644 cpp/README.md create mode 100644 cpp/base.h create mode 100644 cpp/conv_templates.cc create mode 100644 cpp/conversation.cc create mode 100644 cpp/conversation.h create mode 100644 cpp/image_embed.cc create mode 100644 cpp/image_embed.h create mode 100644 cpp/llm_chat.cc create mode 100644 cpp/llm_chat.h create mode 100644 cpp/loader/multi_gpu_loader.cc create mode 100644 cpp/metadata/json_parser.h create mode 100644 cpp/metadata/model.cc create mode 100644 cpp/metadata/model.h create mode 100644 cpp/random.h create mode 100644 cpp/serve/async_threaded_engine.cc create mode 100644 cpp/serve/async_threaded_engine.h create mode 100644 cpp/serve/config.cc create mode 100644 cpp/serve/config.h create mode 100644 cpp/serve/data.cc create mode 100644 cpp/serve/data.h create mode 100644 cpp/serve/engine.cc create mode 100644 cpp/serve/engine.h create mode 100644 cpp/serve/engine_actions/action.cc create mode 100644 cpp/serve/engine_actions/action.h create mode 100644 cpp/serve/engine_actions/action_commons.cc create mode 100644 cpp/serve/engine_actions/action_commons.h create mode 100644 cpp/serve/engine_actions/batch_decode.cc create mode 100644 cpp/serve/engine_actions/batch_draft.cc create mode 100644 cpp/serve/engine_actions/batch_verify.cc create mode 100644 cpp/serve/engine_actions/new_request_prefill.cc create mode 100644 cpp/serve/engine_state.cc create mode 100644 cpp/serve/engine_state.h create mode 100644 cpp/serve/event_trace_recorder.cc create mode 100644 cpp/serve/event_trace_recorder.h create mode 100644 cpp/serve/function_table.cc create mode 100644 cpp/serve/function_table.h create mode 100644 cpp/serve/grammar/grammar.cc create mode 100644 cpp/serve/grammar/grammar.h create mode 100644 cpp/serve/grammar/grammar_builder.h create mode 100644 cpp/serve/grammar/grammar_parser.cc create mode 100644 cpp/serve/grammar/grammar_parser.h create mode 100644 cpp/serve/grammar/grammar_serializer.cc create mode 100644 cpp/serve/grammar/grammar_serializer.h create mode 100644 cpp/serve/grammar/grammar_simplifier.cc create mode 100644 cpp/serve/grammar/grammar_simplifier.h create mode 100644 cpp/serve/grammar/grammar_state_matcher.cc create mode 100644 cpp/serve/grammar/grammar_state_matcher.h create mode 100644 cpp/serve/grammar/grammar_state_matcher_base.h create mode 100644 cpp/serve/grammar/grammar_state_matcher_preproc.h create mode 100644 cpp/serve/grammar/grammar_state_matcher_state.h create mode 100644 cpp/serve/grammar/support.h create mode 100644 cpp/serve/logit_processor.cc create mode 100644 cpp/serve/logit_processor.h create mode 100644 cpp/serve/model.cc create mode 100644 cpp/serve/model.h create mode 100644 cpp/serve/request.cc create mode 100644 cpp/serve/request.h create mode 100644 cpp/serve/request_state.cc create mode 100644 cpp/serve/request_state.h create mode 100644 cpp/serve/sampler.cc create mode 100644 cpp/serve/sampler.h create mode 100644 cpp/streamer.cc create mode 100644 cpp/streamer.h create mode 100644 cpp/support/encoding.cc create mode 100644 cpp/support/encoding.h create mode 100644 cpp/support/load_bytes_from_file.h create mode 100644 cpp/support/progress_bar.h create mode 100644 cpp/tokenizers.cc create mode 100644 cpp/tokenizers.h create mode 100644 docs/.gitignore create mode 100644 docs/Makefile create mode 100644 docs/README.md create mode 100644 docs/_static/img/mlc-logo-with-text-landscape.svg create mode 100644 docs/_static/img/project-structure.svg create mode 100644 docs/community/faq.rst create mode 100644 docs/community/guideline.rst create mode 100644 docs/compilation/compile_models.rst create mode 100644 docs/compilation/configure_quantization.rst create mode 100644 docs/compilation/convert_weights.rst create mode 100644 docs/compilation/define_new_models.rst create mode 100644 docs/compilation/get-vicuna-weight.rst create mode 100644 docs/conf.py create mode 100644 docs/deploy/android.rst create mode 100644 docs/deploy/cli.rst create mode 100644 docs/deploy/ios.rst create mode 100644 docs/deploy/javascript.rst create mode 100644 docs/deploy/python.rst create mode 100644 docs/deploy/rest.rst create mode 100644 docs/get_started/mlc_chat_config.rst create mode 100644 docs/get_started/project_overview.rst create mode 100644 docs/index.rst create mode 100644 docs/install/conda.rst create mode 100644 docs/install/emcc.rst create mode 100644 docs/install/gpu.rst create mode 100644 docs/install/mlc_llm.rst create mode 100644 docs/install/tvm.rst create mode 100644 docs/make.bat create mode 100644 docs/prebuilt_models.rst create mode 100644 docs/prebuilt_models_deprecated.rst create mode 100644 docs/privacy.rst create mode 100644 docs/requirements.txt create mode 100644 examples/python/benchmark.py create mode 100644 examples/python/run_llama_batched_vllm.py create mode 100644 examples/python/sample_chat_stream.py create mode 100644 examples/python/sample_mlc_chat.py create mode 100755 examples/rest/nodejs/README.MD create mode 100755 examples/rest/nodejs/dotenv.example create mode 100755 examples/rest/nodejs/package.json create mode 100755 examples/rest/nodejs/sample_client.js create mode 100644 examples/rest/nodejs/sample_langchain.ts create mode 100755 examples/rest/nodejs/sample_openai.js create mode 100755 examples/rest/nodejs/tsconfig.json create mode 100644 examples/rest/python/sample_client.py create mode 100644 examples/rest/python/sample_langchain.py create mode 100644 examples/rest/python/sample_openai.py create mode 100644 examples/rest/resources/linux.txt create mode 100644 examples/rest/resources/state_of_the_union.txt create mode 100644 ios/.gitignore create mode 100644 ios/MLCChat copy-Info.plist create mode 100644 ios/MLCChat.xcodeproj/project.pbxproj create mode 100644 ios/MLCChat.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 ios/MLCChat.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/MLCChat.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 ios/MLCChat.xcodeproj/xcshareddata/xcschemes/MLCChat.xcscheme create mode 100644 ios/MLCChat/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 ios/MLCChat/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 ios/MLCChat/Assets.xcassets/AppIcon.appiconset/mlc-logo.png create mode 100644 ios/MLCChat/Assets.xcassets/Contents.json create mode 100644 ios/MLCChat/Common/Constants.swift create mode 100644 ios/MLCChat/Info.plist create mode 100644 ios/MLCChat/MLCChat.entitlements create mode 100644 ios/MLCChat/MLCChatApp.swift create mode 100644 ios/MLCChat/Models/AppConfig.swift create mode 100644 ios/MLCChat/Models/ModelConfig.swift create mode 100644 ios/MLCChat/Models/ParamsConfig.swift create mode 100644 ios/MLCChat/PerformanceMetrics.swift create mode 100644 ios/MLCChat/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 ios/MLCChat/RestAwaitLib.swift create mode 100644 ios/MLCChat/States/AppState.swift create mode 100644 ios/MLCChat/States/ChatState.swift create mode 100644 ios/MLCChat/States/ModelState.swift create mode 100644 ios/MLCChat/Views/ChatView.swift create mode 100644 ios/MLCChat/Views/ImageProcessing.swift create mode 100644 ios/MLCChat/Views/MessageView.swift create mode 100644 ios/MLCChat/Views/ModelView.swift create mode 100644 ios/MLCChat/Views/StartView.swift create mode 100644 ios/MLCChat/app-config.json create mode 100644 ios/MLCSwift/Package.swift create mode 100644 ios/MLCSwift/README.md create mode 100644 ios/MLCSwift/Sources/ObjC/LLMChat.mm create mode 100644 ios/MLCSwift/Sources/ObjC/include/LLMChat.h create mode 100644 ios/MLCSwift/Sources/Swift/LLMChat.swift create mode 100644 ios/MLCSwift/Sources/Swift/ThreadWorker.swift create mode 120000 ios/MLCSwift/tvm_home create mode 100644 ios/README.md create mode 100755 ios/prepare_libs.sh create mode 100644 ios/prepare_model_lib.py create mode 100755 ios/prepare_params.sh create mode 100644 mlc_llm/__init__.py create mode 100644 mlc_llm/build.py create mode 100644 mlc_llm/core.py create mode 100644 mlc_llm/dispatch/__init__.py create mode 100644 mlc_llm/dispatch/dispatch_tir_operator.py create mode 100644 mlc_llm/dispatch/dispatch_tir_operator_adreno.py create mode 100644 mlc_llm/dispatch/gpt_neox/__init__.py create mode 100644 mlc_llm/dispatch/gpt_neox/dolly_v2_3b.py create mode 100644 mlc_llm/dispatch/gpt_neox/dolly_v2_3b_mod.py create mode 100644 mlc_llm/dispatch/gpt_neox/redpajama_incite_chat_3b_v1.py create mode 100644 mlc_llm/dispatch/gpt_neox/redpajama_incite_chat_3b_v1_mod.py create mode 100644 mlc_llm/dispatch/gpt_neox/redpajama_incite_chat_3b_v1_tune.py create mode 100644 mlc_llm/dispatch/gpt_neox/redpajama_q4f32.py create mode 100644 mlc_llm/dispatch/gpt_neox/redpajama_q4f32_mod.py create mode 100644 mlc_llm/dispatch/gpt_neox/redpajama_q4f32_tune.py create mode 100644 mlc_llm/dispatch/llama/__init__.py create mode 100644 mlc_llm/dispatch/llama/main.py create mode 100644 mlc_llm/quantization/__init__.py create mode 100644 mlc_llm/quantization/autogptq_quantization.py create mode 100644 mlc_llm/quantization/ft_quantization.py create mode 100644 mlc_llm/quantization/group_quantization.py create mode 100644 mlc_llm/quantization/quantization.py create mode 100644 mlc_llm/quantization/tir_utils.py create mode 100644 mlc_llm/relax_model/__init__.py create mode 100644 mlc_llm/relax_model/chatglm.py create mode 100644 mlc_llm/relax_model/commons.py create mode 100644 mlc_llm/relax_model/gpt_bigcode.py create mode 100644 mlc_llm/relax_model/gpt_neox.py create mode 100644 mlc_llm/relax_model/gptj.py create mode 100644 mlc_llm/relax_model/llama.py create mode 100644 mlc_llm/relax_model/llama_batched_vllm.py create mode 100644 mlc_llm/relax_model/minigpt.py create mode 100644 mlc_llm/relax_model/mistral.py create mode 100644 mlc_llm/relax_model/modules.py create mode 100644 mlc_llm/relax_model/param_manager.py create mode 100644 mlc_llm/relax_model/rwkv.py create mode 100644 mlc_llm/relax_model/stablelm_3b.py create mode 100644 mlc_llm/transform/__init__.py create mode 100644 mlc_llm/transform/clean_up_tir_attrs.py create mode 100644 mlc_llm/transform/decode_matmul_ewise.py create mode 100644 mlc_llm/transform/decode_take.py create mode 100644 mlc_llm/transform/decode_transpose.py create mode 100644 mlc_llm/transform/fuse_split_rotary_embedding.py create mode 100644 mlc_llm/transform/lift_tir_global_buffer_alloc.py create mode 100644 mlc_llm/transform/reorder_transform_func.py create mode 100644 mlc_llm/transform/rewrite_attention.py create mode 100644 mlc_llm/transform/transpose_matmul.py create mode 100644 mlc_llm/utils.py create mode 100644 pyproject.toml create mode 100644 python/README.md create mode 100644 python/mlc_chat/__init__.py create mode 100644 python/mlc_chat/__main__.py create mode 100644 python/mlc_chat/_ffi_api.py create mode 100644 python/mlc_chat/base.py create mode 100644 python/mlc_chat/callback.py create mode 100644 python/mlc_chat/chat_module.py create mode 100644 python/mlc_chat/cli/__init__.py create mode 100644 python/mlc_chat/cli/bench.py create mode 100644 python/mlc_chat/cli/benchmark.py create mode 100644 python/mlc_chat/cli/chat.py create mode 100644 python/mlc_chat/cli/check_device.py create mode 100644 python/mlc_chat/cli/compile.py create mode 100644 python/mlc_chat/cli/convert_weight.py create mode 100644 python/mlc_chat/cli/delivery.py create mode 100644 python/mlc_chat/cli/gen_config.py create mode 100644 python/mlc_chat/cli/model_metadata.py create mode 100644 python/mlc_chat/cli/worker.py create mode 100644 python/mlc_chat/compiler_pass/__init__.py create mode 100644 python/mlc_chat/compiler_pass/attach_to_ir_module.py create mode 100644 python/mlc_chat/compiler_pass/clean_up_tir_attrs.py create mode 100644 python/mlc_chat/compiler_pass/cublas_dispatch.py create mode 100644 python/mlc_chat/compiler_pass/estimate_memory_usage.py create mode 100644 python/mlc_chat/compiler_pass/fuse_add_norm.py create mode 100644 python/mlc_chat/compiler_pass/fuse_dequantize_matmul_ewise.py create mode 100644 python/mlc_chat/compiler_pass/fuse_dequantize_take.py create mode 100644 python/mlc_chat/compiler_pass/fuse_dequantize_transpose.py create mode 100644 python/mlc_chat/compiler_pass/fuse_ft_dequantize_matmul_epilogue.py create mode 100644 python/mlc_chat/compiler_pass/fuse_transpose_matmul.py create mode 100644 python/mlc_chat/compiler_pass/lift_global_buffer_alloc.py create mode 100644 python/mlc_chat/compiler_pass/pipeline.py create mode 100644 python/mlc_chat/compiler_pass/rewrite_kv_cache_creation.py create mode 100644 python/mlc_chat/compiler_pass/scatter_tuple_get_item.py create mode 100644 python/mlc_chat/conversation_template.py create mode 100644 python/mlc_chat/embeddings/__init__.py create mode 100644 python/mlc_chat/embeddings/openai.py create mode 100644 python/mlc_chat/gradio.py create mode 100644 python/mlc_chat/help.py create mode 100644 python/mlc_chat/interface/__init__.py create mode 100644 python/mlc_chat/interface/bench.py create mode 100644 python/mlc_chat/interface/chat.py create mode 100644 python/mlc_chat/interface/compile.py create mode 100644 python/mlc_chat/interface/compiler_flags.py create mode 100644 python/mlc_chat/interface/convert_weight.py create mode 100644 python/mlc_chat/interface/gen_config.py create mode 100644 python/mlc_chat/interface/jit.py create mode 100644 python/mlc_chat/interface/openai_api.py create mode 100644 python/mlc_chat/libinfo.py create mode 100644 python/mlc_chat/loader/__init__.py create mode 100644 python/mlc_chat/loader/huggingface_loader.py create mode 100644 python/mlc_chat/loader/loader.py create mode 100644 python/mlc_chat/loader/mapping.py create mode 100644 python/mlc_chat/loader/stats.py create mode 100644 python/mlc_chat/loader/utils.py create mode 100644 python/mlc_chat/model/__init__.py create mode 100644 python/mlc_chat/model/baichuan/__init__.py create mode 100644 python/mlc_chat/model/baichuan/baichuan_loader.py create mode 100644 python/mlc_chat/model/baichuan/baichuan_model.py create mode 100644 python/mlc_chat/model/baichuan/baichuan_quantization.py create mode 100644 python/mlc_chat/model/gemma/__init__.py create mode 100644 python/mlc_chat/model/gemma/gemma_loader.py create mode 100644 python/mlc_chat/model/gemma/gemma_model.py create mode 100644 python/mlc_chat/model/gemma/gemma_quantization.py create mode 100644 python/mlc_chat/model/gpt2/__init__.py create mode 100644 python/mlc_chat/model/gpt2/gpt2_loader.py create mode 100644 python/mlc_chat/model/gpt2/gpt2_model.py create mode 100644 python/mlc_chat/model/gpt2/gpt2_quantization.py create mode 100644 python/mlc_chat/model/gpt_bigcode/__init__.py create mode 100644 python/mlc_chat/model/gpt_bigcode/gpt_bigcode_loader.py create mode 100644 python/mlc_chat/model/gpt_bigcode/gpt_bigcode_model.py create mode 100644 python/mlc_chat/model/gpt_bigcode/gpt_bigcode_quantization.py create mode 100644 python/mlc_chat/model/gpt_neox/__init__.py create mode 100644 python/mlc_chat/model/gpt_neox/gpt_neox_loader.py create mode 100644 python/mlc_chat/model/gpt_neox/gpt_neox_model.py create mode 100644 python/mlc_chat/model/gpt_neox/gpt_neox_quantization.py create mode 100644 python/mlc_chat/model/llama/__init__.py create mode 100644 python/mlc_chat/model/llama/llama_loader.py create mode 100644 python/mlc_chat/model/llama/llama_model.py create mode 100644 python/mlc_chat/model/llama/llama_quantization.py create mode 100644 python/mlc_chat/model/mistral/__init__.py create mode 100644 python/mlc_chat/model/mistral/mistral_loader.py create mode 100644 python/mlc_chat/model/mistral/mistral_model.py create mode 100644 python/mlc_chat/model/mistral/mistral_quantization.py create mode 100644 python/mlc_chat/model/mixtral/__init__.py create mode 100644 python/mlc_chat/model/mixtral/mixtral_loader.py create mode 100644 python/mlc_chat/model/mixtral/mixtral_model.py create mode 100644 python/mlc_chat/model/mixtral/mixtral_quantization.py create mode 100644 python/mlc_chat/model/model.py create mode 100644 python/mlc_chat/model/model_preset.py create mode 100644 python/mlc_chat/model/phi/__init__.py create mode 100644 python/mlc_chat/model/phi/phi_loader.py create mode 100644 python/mlc_chat/model/phi/phi_model.py create mode 100644 python/mlc_chat/model/phi/phi_quantization.py create mode 100644 python/mlc_chat/model/qwen/__init__.py create mode 100644 python/mlc_chat/model/qwen/qwen_loader.py create mode 100644 python/mlc_chat/model/qwen/qwen_model.py create mode 100644 python/mlc_chat/model/qwen/qwen_quantization.py create mode 100644 python/mlc_chat/model/qwen2/__init__.py create mode 100644 python/mlc_chat/model/qwen2/qwen2_loader.py create mode 100644 python/mlc_chat/model/qwen2/qwen2_model.py create mode 100644 python/mlc_chat/model/qwen2/qwen2_quantization.py create mode 100644 python/mlc_chat/model/stable_lm/__init__.py create mode 100644 python/mlc_chat/model/stable_lm/stablelm_loader.py create mode 100644 python/mlc_chat/model/stable_lm/stablelm_model.py create mode 100644 python/mlc_chat/model/stable_lm/stablelm_quantization.py create mode 100644 python/mlc_chat/nn/__init__.py create mode 100644 python/mlc_chat/nn/expert.py create mode 100644 python/mlc_chat/nn/kv_cache.py create mode 100644 python/mlc_chat/op/__init__.py create mode 100644 python/mlc_chat/op/attention.py create mode 100644 python/mlc_chat/op/extern.py create mode 100644 python/mlc_chat/op/ft_gemm.py create mode 100644 python/mlc_chat/op/moe_matmul.py create mode 100644 python/mlc_chat/op/moe_misc.py create mode 100644 python/mlc_chat/op/position_embedding.py create mode 100644 python/mlc_chat/protocol/__init__.py create mode 100644 python/mlc_chat/protocol/conversation_protocol.py create mode 100644 python/mlc_chat/protocol/openai_api_protocol.py create mode 100644 python/mlc_chat/protocol/protocol_utils.py create mode 100644 python/mlc_chat/quantization/__init__.py create mode 100644 python/mlc_chat/quantization/awq_quantization.py create mode 100644 python/mlc_chat/quantization/ft_quantization.py create mode 100644 python/mlc_chat/quantization/group_quantization.py create mode 100644 python/mlc_chat/quantization/no_quantization.py create mode 100644 python/mlc_chat/quantization/quantization.py create mode 100644 python/mlc_chat/quantization/utils.py create mode 100644 python/mlc_chat/rest.py create mode 100644 python/mlc_chat/serve/__init__.py create mode 100644 python/mlc_chat/serve/_ffi_api.py create mode 100644 python/mlc_chat/serve/async_engine.py create mode 100644 python/mlc_chat/serve/config.py create mode 100644 python/mlc_chat/serve/data.py create mode 100644 python/mlc_chat/serve/engine.py create mode 100644 python/mlc_chat/serve/entrypoints/__init__.py create mode 100644 python/mlc_chat/serve/entrypoints/debug_entrypoints.py create mode 100644 python/mlc_chat/serve/entrypoints/entrypoint_utils.py create mode 100644 python/mlc_chat/serve/entrypoints/openai_entrypoints.py create mode 100644 python/mlc_chat/serve/event_trace_recorder.py create mode 100644 python/mlc_chat/serve/grammar.py create mode 100644 python/mlc_chat/serve/request.py create mode 100644 python/mlc_chat/serve/server/__init__.py create mode 100644 python/mlc_chat/serve/server/__main__.py create mode 100644 python/mlc_chat/serve/server/popen_server.py create mode 100644 python/mlc_chat/serve/server/server_context.py create mode 100644 python/mlc_chat/streamer.py create mode 100644 python/mlc_chat/support/__init__.py create mode 100644 python/mlc_chat/support/argparse.py create mode 100644 python/mlc_chat/support/auto_config.py create mode 100644 python/mlc_chat/support/auto_device.py create mode 100644 python/mlc_chat/support/auto_target.py create mode 100644 python/mlc_chat/support/auto_weight.py create mode 100644 python/mlc_chat/support/config.py create mode 100644 python/mlc_chat/support/constants.py create mode 100644 python/mlc_chat/support/convert_tiktoken.py create mode 100644 python/mlc_chat/support/download.py create mode 100644 python/mlc_chat/support/logging.py create mode 100644 python/mlc_chat/support/preshard.py create mode 100644 python/mlc_chat/support/random.py create mode 100644 python/mlc_chat/support/style.py create mode 100644 python/mlc_chat/support/tensor_parallel.py create mode 100644 python/mlc_chat/support/tqdm.py create mode 100644 python/mlc_chat/tokenizer.py create mode 100644 python/setup.py create mode 100644 run_scripts/README.md create mode 100755 run_scripts/run-mlc-chat-cli.exp create mode 100755 run_scripts/run-mlc.sh create mode 100755 run_scripts/run_expect_all.sh create mode 100644 rust/.gitignore create mode 100644 rust/Cargo.toml create mode 100644 rust/README.md create mode 100644 rust/build.rs create mode 100644 rust/examples/mlc_chat.rs create mode 100644 rust/rustfmt.toml create mode 100644 rust/src/chat_module.rs create mode 100644 rust/src/config.rs create mode 100644 rust/src/lib.rs create mode 100755 scripts/build_site.sh create mode 100644 scripts/check_url_validity.py create mode 100755 scripts/gh_deploy_site.sh create mode 100755 scripts/local_deploy_site.sh create mode 100755 scripts/prep_emcc_deps.sh create mode 100644 setup.py create mode 100644 site/.gitignore create mode 100644 site/CNAME create mode 100644 site/_config.yml create mode 100644 site/gif/android-demo.gif create mode 100644 site/gif/ios-demo.gif create mode 100644 site/gif/linux-demo.gif create mode 100644 site/img/android/android-diagram.png create mode 100644 site/img/android/android-studio.png create mode 100644 site/img/android/android-vs-ios.png create mode 100644 site/img/android/local-advantage.png create mode 100644 site/img/diag.svg create mode 100644 site/img/multi-gpu/figure-1.svg create mode 100644 site/img/multi-gpu/figure-2.svg create mode 100644 site/img/multi-gpu/figure-3.svg create mode 100644 site/index.md create mode 100644 site/privacy.md create mode 100644 tests/cpp/conv_unittest.cc create mode 100644 tests/legacy-python/compare_lib.py create mode 100644 tests/legacy-python/dump_intermediate.py create mode 100644 tests/legacy-python/evaluate.py create mode 100644 tests/legacy-python/module_intercept.py create mode 100644 tests/legacy-python/test_batching_llama.py create mode 100644 tests/legacy-python/test_build_args.py create mode 100644 tests/legacy-python/test_build_model_from_args.py create mode 100644 tests/legacy-python/test_sliding_window_mask.py create mode 100644 tests/python/__init__.py create mode 100644 tests/python/api/test_python.py create mode 100644 tests/python/api/test_rest.py create mode 100644 tests/python/compiler_pass/test_fuse_ft_dequantize_matmul_epilogue.py create mode 100644 tests/python/conftest.py create mode 100644 tests/python/integration/test_model_compile.py create mode 100644 tests/python/loader/test_awq.py create mode 100644 tests/python/loader/test_huggingface.py create mode 100644 tests/python/model/test_gpt2.py create mode 100644 tests/python/model/test_gptNeox.py create mode 100644 tests/python/model/test_kv_cache.py create mode 100644 tests/python/model/test_llama.py create mode 100644 tests/python/model/test_llama_quantization.py create mode 100644 tests/python/model/test_mistral.py create mode 100644 tests/python/model/test_phi.py create mode 100644 tests/python/quantization/test_awq_quantization.py create mode 100644 tests/python/quantization/test_group_quantization.py create mode 100644 tests/python/serve/benchmark.py create mode 100644 tests/python/serve/evaluate_engine.py create mode 100644 tests/python/serve/json.ebnf create mode 100644 tests/python/serve/server/conftest.py create mode 100644 tests/python/serve/server/test_server.py create mode 100644 tests/python/serve/server/test_server_function_call.py create mode 100644 tests/python/serve/test_event_trace_recorder.py create mode 100644 tests/python/serve/test_grammar_parser.py create mode 100644 tests/python/serve/test_grammar_state_matcher.py create mode 100644 tests/python/serve/test_serve_async_engine.py create mode 100644 tests/python/serve/test_serve_async_engine_spec.py create mode 100644 tests/python/serve/test_serve_engine.py create mode 100644 tests/python/serve/test_serve_engine_spec.py create mode 100644 tests/python/support/test_auto_config.py create mode 100644 tests/python/support/test_auto_weight.py create mode 100644 tests/python/support/test_streamer.py create mode 100644 version.py diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..9d622b9 --- /dev/null +++ b/.clang-format @@ -0,0 +1,8 @@ +# Run the following command to reformat a file: +# clang-format -i -style=Google +# Or use clang-format-diff to only reformat the changed lines: +# https://clang.llvm.org/docs/ClangFormat.html +BasedOnStyle: Google +DerivePointerAlignment: false +ColumnLimit: 100 +PointerAlignment: Left diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 0000000..87bbc4a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,43 @@ +--- +name: "🐛 Bug Report" +about: Submit a bug report to help us improve MLC-LLM +title: '[Bug] ' +labels: ['bug'] +assignees: '' + +--- + +## 🐛 Bug + + + +## To Reproduce + +Steps to reproduce the behavior: + +1. +1. +1. + + + +## Expected behavior + + + +## Environment + + - Platform (e.g. WebGPU/Vulkan/IOS/Android/CUDA): + - Operating system (e.g. Ubuntu/Windows/MacOS/...): + - Device (e.g. iPhone 12 Pro, PC+RTX 3090, ...) + - How you installed MLC-LLM (`conda`, source): + - How you installed TVM-Unity (`pip`, source): + - Python version (e.g. 3.10): + - GPU driver version (if applicable): + - CUDA/cuDNN version (if applicable): + - TVM Unity Hash Tag (`python -c "import tvm; print('\n'.join(f'{k}: {v}' for k, v in tvm.support.libinfo().items()))"`, applicable if you compile models): + - Any other relevant information: + +## Additional context + + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..6e88ecf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,9 @@ +blank_issues_enabled: false + +contact_links: + - name: Check the MLC-LLM Documentation + url: https://llm.mlc.ai/docs/ + about: Our documentation might provide answers to your questions. + - name: Chat on Discord + url: https://discord.gg/9Xpy2HGBuD + about: Join the Discord Server to live chat with the community. diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 0000000..58aba64 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -0,0 +1,17 @@ +--- +name: "\U0001F4DA Documentation" +about: Report an issue related to https://llm.mlc.ai/docs/ +title: '[Doc] ' +labels: ['documentation'] +assignees: '' + +--- + +## 📚 Documentation + +### Suggestion + + +### Bug +- Link to the buggy documentation/tutorial: +- Description of the bug: diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 0000000..5d92d35 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,23 @@ +--- +name: "\U0001F680 Feature Request" +about: Submit a proposal/request for a new MLC-LLM feature, or an enhancement on existing features. +title: '[Feature Request] ' +labels: ['feature request'] +assignees: '' + +--- + +## 🚀 Feature + + +## Motivation + + + +## Alternatives + + + +## Additional context + + diff --git a/.github/ISSUE_TEMPLATE/general.md b/.github/ISSUE_TEMPLATE/general.md new file mode 100644 index 0000000..f441937 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/general.md @@ -0,0 +1,13 @@ +--- +name: "❓ General Questions" +about: General questions you have about MLC-LLM. +title: '[Question] ' +labels: ['question'] +assignees: '' + +--- + +## ❓ General Questions + + + diff --git a/.github/ISSUE_TEMPLATE/model-request.md b/.github/ISSUE_TEMPLATE/model-request.md new file mode 100644 index 0000000..fb48ce5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/model-request.md @@ -0,0 +1,17 @@ +--- +name: "️️⚙️ Model Request" +about: Request a new model in MLC-LLM +title: '[Model Request] ' +labels: ['new-models'] +assignees: '' + +--- + +## ⚙️ Request New Models + +- Link to an existing implementation (e.g. Hugging Face/Github): +- Is this model architecture supported by MLC-LLM? (the list of [supported models](https://llm.mlc.ai/docs/prebuilt_models.html)) + +## Additional context + + diff --git a/.github/ISSUE_TEMPLATE/speed-report.md b/.github/ISSUE_TEMPLATE/speed-report.md new file mode 100644 index 0000000..d84a41b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/speed-report.md @@ -0,0 +1,24 @@ +--- +name: " 🏎️ Speed Report" +about: Submit a speed report of an model running in MLC-LLM +title: '[Speed] ' +labels: ['performance'] +assignees: '' + +--- + +# 🏎️ Speed Report + + + +- The model code: + + +- The model configuration (e.g. quantization mode, running data type, etc.): +- Device (e.g. MacBook Pro M2, PC+RTX 3080): +- OS (if applicable): +- Encode speed (Token/s): +- Decode speed (Token/s): +- Memory usage (if applicable): + + \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/tracking.md b/.github/ISSUE_TEMPLATE/tracking.md new file mode 100644 index 0000000..d84b745 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/tracking.md @@ -0,0 +1,40 @@ +--- +name: "Tracking" +about: A tracking issue that tracks ongoing item in the project +title: '[Tracking] ' +labels: ['status: tracking'] +assignees: '' + +--- + + + + +## Overview + + + + +## Action Items + + +- [ ] + + +## Links to Related Issues and PRs + + + diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 0000000..644df9c --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,39 @@ +name: Build Docs + +on: + push: + branches: + - main + +jobs: + test_linux: + name: Deploy Docs + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Configuring build Environment + run: | + sudo apt-get update + python -m pip install -U pip wheel + + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.0' + + - name: Installing dependencies + run: | + python -m pip install -r docs/requirements.txt + gem install jekyll jekyll-remote-theme + + - name: Deploying on GitHub Pages + if: github.ref == 'refs/heads/main' + run: | + git remote set-url origin https://x-access-token:${{ secrets.MLC_GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY + git config --global user.email "mlc-gh-actions-bot@nomail" + git config --global user.name "mlc-gh-actions-bot" + ./scripts/gh_deploy_site.sh diff --git a/.github/workflows/update-relax.yml b/.github/workflows/update-relax.yml new file mode 100644 index 0000000..ccd5dcb --- /dev/null +++ b/.github/workflows/update-relax.yml @@ -0,0 +1,32 @@ +name: 'Relax Submodule Sync' + +on: + workflow_dispatch: + +jobs: + sync: + name: 'Relax Submodule Sync' + runs-on: ubuntu-latest + + defaults: + run: + shell: bash + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: true + + - name: Git Sumbodule Update + run: | + git submodule update --remote 3rdparty/tvm + + - name: Commit update + env: + GITHUB_TOKEN: ${{ secrets.MLC_GITHUB_TOKEN }} + run: | + git config --global user.name 'Git bot' + git config --global user.email 'bot@noreply.github.com' + git remote set-url origin https://$GITHUB_TOKEN@github.com/mlc-ai/mlc-llm + git commit -am "Auto updated submodule references" && git push || echo "No changes to commit" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f9454e5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,324 @@ +tmp/ +dist/ +params/ +debug/ +*.bak +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +.DS_Store + +*.S +# C extensions +*.so + +build/ + +*.ll +.npm +# Distribution / packaging +.Python +env/ +build/ +build-*/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +.conda/ +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Generated by python/gen_requirements.py +python/requirements/*.txt + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +docs/_staging/ + +# PyBuilder +target/ +/target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject +*~ +*.pyc +*~ +config.mk +config.cmake +Win32 +*.dir +perf +*.wasm +.emscripten + +## IOS +DerivedData/ + +## Java +*.class +jvm/*/target/ +jvm/*/*/target/ +jvm/native/*/generated +jvm/native/src/main/native/org_apache_tvm_native_c_api.h +*.worksheet +*.idea +*.iml +*.classpath +*.project +*.settings +*/node_modules/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ +.pkl_memoize_* + +.emscripten* +.m2 + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +## Other +*.moved-aside +*.xccheckout +*.xcscmblueprint +.DS_Store +tags +cscope* +*.lock + +# vim temporary files +*.swp +*.swo + +# TVM generated code +perf +.bash_history +# *.json +*.params +*.ro +*.onnx +*.h5 +synset.txt +cat.jpg +cat.png +docs.tgz +cat.png +*.mlmodel +tvm_u.* +tvm_t.* +# Mac OS X +.DS_Store + +# Jetbrain +.idea +.ipython +.jupyter +.nv +.pylint.d +.python_history +.pytest_cache +.local +cmake-build-debug + +# Visual Studio +.vs + +# Visual Studio Code +.vscode + +# tmp file +.nfs* + +# keys +*.pem +*.p12 +*.pfx +*.cer +*.crt +*.der + +# patch sentinel +patched.txt + +# Python type checking +.mypy_cache/ +.pyre/ + +# pipenv files +Pipfile +Pipfile.lock + +# conda package artifacts +conda/Dockerfile.cuda* +conda/pkg +.node_repl_history +# nix files +.envrc +*.nix + +# Docker files +.sudo_as_admin_successful + +# Downloaded models/datasets +.tvm_test_data +.dgl +.caffe2 + +# Local docs build +_docs/ +jvm/target +.config/configstore/ +.ci-py-scripts/ + +# Generated Hexagon files +src/runtime/hexagon/rpc/hexagon_rpc.h +src/runtime/hexagon/rpc/hexagon_rpc_skel.c +src/runtime/hexagon/rpc/hexagon_rpc_stub.c + +# Local tvm-site checkout +tvm-site/ + +# Generated docs files +gallery/how_to/work_with_microtvm/micro_tvmc.py + +# Test sample data files +!tests/python/ci/sample_prs/*.json + +# Used in CI to communicate between Python and Jenkins +.docker-image-names/ + +# Printed TIR code on disk +*.tir + +# GDB history file +.gdb_history + +dist diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..10ef4b2 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,12 @@ +[submodule "3rdparty/argparse"] + path = 3rdparty/argparse + url = https://github.com/p-ranav/argparse +[submodule "3rdparty/tokenizers-cpp"] + path = 3rdparty/tokenizers-cpp + url = https://github.com/mlc-ai/tokenizers-cpp +[submodule "3rdparty/googletest"] + path = 3rdparty/googletest + url = https://github.com/google/googletest.git +[submodule "3rdparty/tvm"] + path = 3rdparty/tvm + url = https://github.com/mlc-ai/relax.git diff --git a/0001-Add-events-timing-to-MLCChat.patch b/0001-Add-events-timing-to-MLCChat.patch new file mode 100644 index 0000000..01aad40 --- /dev/null +++ b/0001-Add-events-timing-to-MLCChat.patch @@ -0,0 +1,145 @@ +From c9950224e21153b59d0e610ab06bd5bfedf98a26 Mon Sep 17 00:00:00 2001 +From: Stefanos Laskaridis +Date: Sun, 3 Mar 2024 17:18:06 +0000 +Subject: [PATCH] Add events timing to MLCChat++ + +--- + python/mlc_chat/chat_module.py | 24 +++++++++++++++++++++++- + python/mlc_chat/cli/chat.py | 7 +++++++ + python/mlc_chat/interface/chat.py | 6 +++++- + 3 files changed, 35 insertions(+), 2 deletions(-) + +diff --git a/python/mlc_chat/chat_module.py b/python/mlc_chat/chat_module.py +index 62ca0135..79de7756 100644 +--- a/python/mlc_chat/chat_module.py ++++ b/python/mlc_chat/chat_module.py +@@ -7,6 +7,7 @@ import json + import os + import subprocess + import sys ++import time + import warnings + from dataclasses import asdict, dataclass, fields + from enum import Enum +@@ -719,6 +720,9 @@ class ChatModule: # pylint: disable=too-many-instance-attributes + device_type = self.device.device_type + device_id = self.device.device_id + ++ self.energy_events = {} ++ self.generate_counter = 0 ++ + # 1. Populate chat module and their functions + fcreate_chat_mod = tvm.get_global_func("mlc.llm_chat_create") + assert fcreate_chat_mod is not None +@@ -844,23 +848,35 @@ class ChatModule: # pylint: disable=too-many-instance-attributes + num_return_sequences = generation_config.n + return_str = False + +- for _ in range(num_return_sequences): ++ for idx in range(num_return_sequences): + if stateless: + self.reset_chat() ++ self.energy_events[f"chat.{self.generate_counter}.{idx}.prefill.start"] = time.time_ns() + self._prefill(prompt, generation_config=generation_config) ++ self.energy_events[f"chat.{self.generate_counter}.{idx}.prefill.end"] = time.time_ns() + + if not progress_callback: ++ decode_counter = 0 + while not self._stopped(): ++ self.energy_events[f"chat.{self.generate_counter}.{idx}.decode.{decode_counter}.start"] = time.time_ns() + self._decode(generation_config=generation_config) ++ self.energy_events[f"chat.{self.generate_counter}.{idx}.decode.{decode_counter}.end"] = time.time_ns() ++ self.energy_events[f"chat.{self.generate_counter}.{idx}.get_message.start"] = time.time_ns() + new_msg = self._get_message() ++ self.energy_events[f"chat.{self.generate_counter}.{idx}.get_message.end"] = time.time_ns() + new_msgs.append(new_msg) + else: + # apply callback with a rate of callback_interval + i, new_msg = 0, "" ++ decode_counter = 0 + while not self._stopped(): ++ self.energy_events[f"chat.{self.generate_counter}.{idx}.decode.{decode_counter}.start"] = time.time_ns() + self._decode(generation_config=generation_config) ++ self.energy_events[f"chat.{self.generate_counter}.{idx}.decode.{decode_counter}.end"] = time.time_ns() + if i % progress_callback.callback_interval == 0 or self._stopped(): ++ self.energy_events[f"chat.{self.generate_counter}.{idx}.get_message.start"] = time.time_ns() + new_msg = self._get_message() ++ self.energy_events[f"chat.{self.generate_counter}.{idx}.get_message.end"] = time.time_ns() + progress_callback(new_msg) + i += 1 + progress_callback(stopped=True) +@@ -999,11 +1015,15 @@ class ChatModule: # pylint: disable=too-many-instance-attributes + app_config_json: str + The partial config that is used to partially override the model configuration. + """ ++ self.energy_events[f"load_model.start"] = time.time_ns() + self._reload_func(lib, model_path, app_config_json) ++ self.energy_events[f"load_model.end"] = time.time_ns() + + def _unload(self): + r"""Unload the chat module and clear memory of all loaded models.""" ++ self.energy_events[f"unload_model.start"] = time.time_ns() + self._unload_func() ++ self.energy_events[f"unload_model.end"] = time.time_ns() + + def _prefill( + self, +@@ -1209,4 +1229,6 @@ class ChatModule: # pylint: disable=too-many-instance-attributes + + def _process_system_prompts(self): + r"""Pre-process by prefilling the system prompts, running prior to any user input.""" ++ self.energy_events["prompt.system.start"] = time.time_ns() + self._process_system_prompts_func() ++ self.energy_events["prompt.system.end"] = time.time_ns() +diff --git a/python/mlc_chat/cli/chat.py b/python/mlc_chat/cli/chat.py +index 7ec6efb2..96edef2d 100644 +--- a/python/mlc_chat/cli/chat.py ++++ b/python/mlc_chat/cli/chat.py +@@ -37,6 +37,12 @@ def main(argv): + default=None, + help=HELP["model_lib_path"] + ' (default: "%(default)s")', + ) ++ parser.add_argument( ++ "--energy-events", ++ type=str, ++ default="energy_events.txt", ++ help="Energy events file to use for energy profiling (default: energy_events.txt)" ++ ) + parsed = parser.parse_args(argv) + chat( + model=parsed.model, +@@ -44,4 +50,5 @@ def main(argv): + opt=parsed.opt, + overrides=parsed.overrides, + model_lib_path=parsed.model_lib_path, ++ energy_events_filename=parsed.energy_events, + ) +diff --git a/python/mlc_chat/interface/chat.py b/python/mlc_chat/interface/chat.py +index cd473f79..3d23df40 100644 +--- a/python/mlc_chat/interface/chat.py ++++ b/python/mlc_chat/interface/chat.py +@@ -122,6 +122,7 @@ def chat( + opt: str, + overrides: ChatConfigOverride, + model_lib_path: Optional[str], ++ energy_events_filename: str, + ): + """chat with a model.""" + # Set up chat config and generate config +@@ -146,9 +147,12 @@ def chat( + if prompt[:6] == "/reset": + cm.reset_chat() + elif prompt[:5] == "/exit": ++ with open(energy_events_filename, 'w', encoding='utf-8') as f: ++ for event_key, event_value in cm.energy_events.items(): ++ f.write(f"{event_key} {event_value}\n") + break + elif prompt[:6] == "/stats": +- print(cm.stats(), flush=True) ++ print(cm.stats(verbose=True), flush=True) + elif prompt[:4] == "/set": + gen_config_overrides = GenerationConfigOverride.from_str(prompt.split()[1]) + generate_config = gen_config_overrides.apply(generate_config) +-- +2.43.0 + diff --git a/0002-Parse-stats-into-json-format.patch b/0002-Parse-stats-into-json-format.patch new file mode 100644 index 0000000..78d638b --- /dev/null +++ b/0002-Parse-stats-into-json-format.patch @@ -0,0 +1,60 @@ +From 9b3d6f2fc6e84ec29afe501611708da488950081 Mon Sep 17 00:00:00 2001 +From: Stefanos Laskaridis +Date: Mon, 4 Mar 2024 16:56:11 +0000 +Subject: [PATCH] Parse stats into json format + +--- + python/mlc_chat/interface/chat.py | 31 ++++++++++++++++++++++++++++++- + 1 file changed, 30 insertions(+), 1 deletion(-) + +diff --git a/python/mlc_chat/interface/chat.py b/python/mlc_chat/interface/chat.py +index 3d23df40..0df8bb15 100644 +--- a/python/mlc_chat/interface/chat.py ++++ b/python/mlc_chat/interface/chat.py +@@ -1,5 +1,7 @@ + """Python entrypoint of chat.""" + import dataclasses ++import re ++import json + from typing import List, Optional, Union + + from prompt_toolkit import prompt as get_prompt # pylint: disable=import-error +@@ -152,7 +154,34 @@ def chat( + f.write(f"{event_key} {event_value}\n") + break + elif prompt[:6] == "/stats": +- print(cm.stats(verbose=True), flush=True) ++ # print(cm.stats(verbose=True), flush=True) ++ # ----------- prefill ----------- ++ # throughput: 87.899 tok/s ++ # total tokens: 10 tok ++ # total time: 0.114 s ++ # ------------ decode ------------ ++ # throughput: 54.603 tok/s ++ # total tokens: 18 tok ++ # total time: 0.330 s ++ # Parse the above metrics into json format ++ stats = cm.stats(verbose=True) ++ if stats.startswith("{"): # This is already handled by the backend ++ print(stats, flush=True) ++ else: # This is in case the backend has not been changed ++ stats = stats.strip().split("\n") ++ float_re = re.compile(r"\d+\.\d+") ++ int_re = re.compile(r"\d+") ++ stats_dict = {} ++ try: ++ for i in range(0, len(stats), 4): ++ stats_dict[stats[i].strip('-').strip()] = { ++ "throughput": f"{float(re.findall(float_re, stats[i + 1])[0])} tok/s", ++ "total_tokens": f"{int(re.findall(int_re, stats[i + 2])[0])} tok", ++ "total_time": f"{float(re.findall(float_re, stats[i + 3])[0])} s", ++ } ++ print(json.dumps(stats_dict, indent=4), flush=True) ++ except IndexError: ++ print(stats, flush=True) + elif prompt[:4] == "/set": + gen_config_overrides = GenerationConfigOverride.from_str(prompt.split()[1]) + generate_config = gen_config_overrides.apply(generate_config) +-- +2.43.0 + diff --git a/3rdparty/argparse b/3rdparty/argparse new file mode 160000 index 0000000..557948f --- /dev/null +++ b/3rdparty/argparse @@ -0,0 +1 @@ +Subproject commit 557948f1236db9e27089959de837cc23de6c6bbd diff --git a/3rdparty/googletest b/3rdparty/googletest new file mode 160000 index 0000000..4580469 --- /dev/null +++ b/3rdparty/googletest @@ -0,0 +1 @@ +Subproject commit 45804691223635953f311cf31a10c632553bbfc3 diff --git a/3rdparty/tokenizers-cpp b/3rdparty/tokenizers-cpp new file mode 160000 index 0000000..27dbe17 --- /dev/null +++ b/3rdparty/tokenizers-cpp @@ -0,0 +1 @@ +Subproject commit 27dbe17d7268801ec720569167af905c88d3db50 diff --git a/3rdparty/tvm b/3rdparty/tvm new file mode 160000 index 0000000..59c3556 --- /dev/null +++ b/3rdparty/tvm @@ -0,0 +1 @@ +Subproject commit 59c3556043abdc88f3ed98e07aa6176ac9a3f0cd diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..81ded0a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,177 @@ +cmake_minimum_required(VERSION 3.18) +project(mlc_llm C CXX) + +include(CheckCXXCompilerFlag) +if(MSVC) + set(CMAKE_CXX_FLAGS "/fp:fast ${CMAKE_CXX_FLAGS}") +else() + set(CMAKE_CXX_FLAGS "-ffast-math ${CMAKE_CXX_FLAGS}") +endif() + +if(EXISTS ${CMAKE_BINARY_DIR}/config.cmake) + include(${CMAKE_BINARY_DIR}/config.cmake) +else() + if(EXISTS ${CMAKE_SOURCE_DIR}/config.cmake) + include(${CMAKE_SOURCE_DIR}/config.cmake) + endif() +endif() + +if(NOT CMAKE_BUILD_TYPE) + set( + CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type" FORCE + ) + message(STATUS "Setting default build type to " ${CMAKE_BUILD_TYPE}) +endif(NOT CMAKE_BUILD_TYPE) + +option(MLC_HIDE_PRIVATE_SYMBOLS "Hide private symbols" ON) + +if (MLC_LLM_INSTALL_STATIC_LIB) + set(BUILD_STATIC_RUNTIME ON) +endif() + +set(MLC_VISIBILITY_FLAG "") +if (MLC_HIDE_PRIVATE_SYMBOLS) + set(HIDE_PRIVATE_SYMBOLS ON) + if (NOT MSVC) + set(MLC_VISIBILITY_FLAG "-fvisibility=hidden") + endif() + message(STATUS "Hide private symbols") +endif() + +option(BUILD_CPP_TEST "Build cpp unittests" OFF) + +set(CMAKE_CUDA_STANDARD 17) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +# tvm runtime config: minimize runtime components +set(USE_RPC OFF) +set(USE_MICRO OFF) +#set(USE_GRAPH_EXECUTOR OFF) +set(USE_GRAPH_EXECUTOR_DEBUG OFF) +set(USE_AOT_EXECUTOR OFF) +set(USE_PROFILER OFF) +set(USE_GTEST OFF) +set(USE_LIBBACKTRACE OFF) +#set(BUILD_DUMMY_LIBTVM ON) +if (NOT DEFINED TVM_HOME) + set(TVM_HOME 3rdparty/tvm) +endif (NOT DEFINED TVM_HOME) +message(STATUS "TVM_HOME: ${TVM_HOME}") +add_subdirectory(${TVM_HOME} tvm EXCLUDE_FROM_ALL) + +set(MLC_LLM_RUNTIME_LINKER_LIB "") +set(TOKENZIER_CPP_PATH 3rdparty/tokenizers-cpp) +add_subdirectory(${TOKENZIER_CPP_PATH} tokenizers EXCLUDE_FROM_ALL) + +if (DEFINED BENCHMARK_PER_LAYER) + message(STATUS "BENCHMARK_PER_LAYER: ${BENCHMARK_PER_LAYER}") +endif() + + +tvm_file_glob(GLOB_RECURSE MLC_LLM_SRCS cpp/*.cc) +add_library(mlc_llm_objs OBJECT ${MLC_LLM_SRCS}) + +set( + MLC_LLM_INCLUDES + ${TVM_HOME}/include + ${TVM_HOME}/3rdparty/dlpack/include + ${TVM_HOME}/3rdparty/dmlc-core/include + ${TVM_HOME}/3rdparty/picojson +) + +if (BENCHMARK_PER_LAYER) + set(MLC_LLM_COMPILE_DEFS ${MLC_LLM_COMPILE_DEFS} DMLC_USE_LOGGING_LIBRARY= BENCHMARK_PER_LAYER=${BENCHMARK_PER_LAYER}) +else() + set(MLC_LLM_COMPILE_DEFS ${MLC_LLM_COMPILE_DEFS} DMLC_USE_LOGGING_LIBRARY=) +endif() +set(MLC_LLM_COMPILE_DEFS ${MLC_LLM_COMPILE_DEFS} __STDC_FORMAT_MACROS=1) +set(MLC_LLM_COMPILE_DEFS ${MLC_LLM_COMPILE_DEFS} PICOJSON_USE_INT64) + +target_include_directories(mlc_llm_objs PRIVATE ${MLC_LLM_INCLUDES}) +target_compile_definitions(mlc_llm_objs PRIVATE ${MLC_LLM_COMPILE_DEFS}) +target_include_directories(mlc_llm_objs PRIVATE ${TOKENZIER_CPP_PATH}/include) +target_compile_definitions(mlc_llm_objs PRIVATE -DMLC_LLM_EXPORTS) + +add_library(mlc_llm SHARED $) +add_library(mlc_llm_static STATIC $) +add_dependencies(mlc_llm_static tokenizers_cpp sentencepiece-static tokenizers_c tvm_runtime) +set_target_properties(mlc_llm_static PROPERTIES OUTPUT_NAME mlc_llm) + +target_link_libraries(mlc_llm PUBLIC tvm_runtime) +target_link_libraries(mlc_llm PRIVATE tokenizers_cpp) + +find_library(FLASH_ATTN_LIBRARY flash_attn) + +if (FLASH_ATTN_LIBRARY STREQUAL "FLASH_ATTN_LIBRARY-NOTFOUND") + message(WARNING "Cannot find libflash_attn. The model must not have been built with --use-flash-attn-mqa option.") +else () + target_link_libraries(mlc_llm PUBLIC -Wl,--no-as-needed ${FLASH_ATTN_LIBRARY}) +endif() + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_definitions(mlc_llm PRIVATE "TVM_LOG_DEBUG") + target_compile_definitions(mlc_llm_objs PRIVATE "TVM_LOG_DEBUG") + target_compile_definitions(mlc_llm_static PRIVATE "TVM_LOG_DEBUG") +endif() + +if (BUILD_CPP_TEST) + message(STATUS "Building cpp unittests") + add_subdirectory(3rdparty/googletest) + file(GLOB_RECURSE MLC_LLM_TEST_SRCS ${PROJECT_SOURCE_DIR}/tests/cpp/*unittest.cc) + add_executable(mlc_llm_cpp_tests ${MLC_LLM_TEST_SRCS}) + target_include_directories(mlc_llm_cpp_tests PRIVATE ${MLC_LLM_INCLUDES}) + target_include_directories(mlc_llm_cpp_tests PRIVATE ${PROJECT_SOURCE_DIR}/cpp) + target_include_directories(mlc_llm_cpp_tests PRIVATE ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) + target_link_libraries(mlc_llm_cpp_tests PUBLIC mlc_llm gtest gtest_main) +endif(BUILD_CPP_TEST) + +if (CMAKE_SYSTEM_NAME STREQUAL "Android") + target_link_libraries(mlc_llm PRIVATE log) + target_link_libraries(tokenizers_cpp PRIVATE log) +endif() + +add_library(mlc_llm_module SHARED $) +target_link_libraries(mlc_llm_module PUBLIC tvm) +target_link_libraries(mlc_llm_module PRIVATE tokenizers_cpp) + + +set_property(TARGET mlc_llm_module APPEND PROPERTY LINK_OPTIONS "${MLC_VISIBILITY_FLAG}") +set_property(TARGET mlc_llm APPEND PROPERTY LINK_OPTIONS "${MLC_VISIBILITY_FLAG}") + +find_program(CARGO_EXECUTABLE cargo) + +if(NOT CARGO_EXECUTABLE) + message(FATAL_ERROR "Cargo is not found! Please install cargo.") +endif() + +# when this option is on, +# we install all static lib deps into lib +if (MLC_LLM_INSTALL_STATIC_LIB) + install(TARGETS + mlc_llm_static + tokenizers_cpp + sentencepiece-static + tvm_runtime + LIBRARY DESTINATION lib${LIB_SUFFIX} + ) + # tokenizers need special handling as it builds from rust + if(MSVC) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/tokenizers/libtokenizers_c.lib + DESTINATION lib${LIB_SUFFIX} + ) + else() + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/tokenizers/libtokenizers_c.a + DESTINATION lib${LIB_SUFFIX} + ) + endif() +else() + install(TARGETS tvm_runtime mlc_llm mlc_llm_module + mlc_llm_static + tokenizers_cpp + sentencepiece-static + RUNTIME_DEPENDENCY_SET tokenizers_c + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LIB_SUFFIX} + ) +endif() diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 0000000..3f70fac --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,6 @@ +MLC LLM Contributors +==================== + + +## List of Contributors +- [Full List of Contributors](https://github.com/mlc-ai/mlc-llm/graphs/contributors) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..586f143 --- /dev/null +++ b/LICENSE @@ -0,0 +1,213 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + +/* + * Modifications Copyright (c) 2024 Brave Software + * + * The MLC-LLM repository has been forked from https://github.com/mlc-ai/mlc-llm. + * Our modifications relate to: + * - enabling per operation profiling in the backend + * - streamlining the build process + * - changing the mobile application to streamline automation + * - integrating event and operation tracing + */ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9bea5cc --- /dev/null +++ b/README.md @@ -0,0 +1,220 @@ +[discord-url]: https://discord.gg/9Xpy2HGBuD + +# MLC LLM + +[Documentation](https://llm.mlc.ai/docs) | [Blog](https://blog.mlc.ai/) | [Discord][discord-url] + +**M**achine **L**earning **C**ompilation for **L**arge **L**anguage **M**odels (MLC LLM) is a high-performance universal deployment solution that allows native deployment of any large language models with native APIs with compiler acceleration. The mission of this project is to enable everyone to develop, optimize and deploy AI models natively on everyone's devices with ML compilation techniques. + +**Universal deployment.** MLC LLM supports the following platforms and hardware: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AMD GPUNVIDIA GPUApple GPUIntel GPU
Linux / Win✅ Vulkan, ROCm✅ Vulkan, CUDAN/A✅ Vulkan
macOS✅ Metal (dGPU)N/A✅ Metal✅ Metal (iGPU)
Web Browser✅ WebGPU and WASM
iOS / iPadOS✅ Metal on Apple A-series GPU
Android✅ OpenCL on Adreno GPU✅ OpenCL on Mali GPU
+ + +**Scalable.** MLC LLM scales universally on NVIDIA and AMD GPUs, cloud and gaming GPUs. Below +showcases our single batch decoding performance with prefilling = 1 and decoding = 256. + +Performance of 4-bit CodeLlama-34B and Llama2-70B on two NVIDIA RTX 4090 and two AMD Radeon 7900 XTX: +

+ + +

+ +Scaling of fp16 and 4-bit CodeLlama-34 and Llama2-70B on A100-80G-PCIe and A10G-24G-PCIe, up to 8 GPUs: +

+ +

+ +## News + +* [10/18/2023] [[Post]](https://blog.mlc.ai/2023/10/19/Scalable-Language-Model-Inference-on-Multiple-NVDIA-AMD-GPUs) Scalable multi-GPU support for CUDA and ROCm are official. +* [09/02/2023] Prebuilt ROCm 5.7 and CUDA 12.2 package is [available](https://llm.mlc.ai/docs/install/tvm.html#option-1-prebuilt-package). +* [08/25/2023] CodeLlama support is up. +* [08/14/2023] [[Post]](https://blog.mlc.ai/2023/08/09/GPU-Accelerated-LLM-on-Orange-Pi) Mali GPU support is up on Orange Pi. +* [08/09/2023] [[Post]](https://blog.mlc.ai/2023/08/09/Making-AMD-GPUs-competitive-for-LLM-inference) ROCm backend is mature to use. +* [08/02/2023] [Dockerfile](https://github.com/mlc-ai/llm-perf-bench/) is released for CUDA performance benchmarking. +* [07/19/2023] Support for Llama2-7B/13B/70B is up. +* [05/22/2023] [[Post]](https://blog.mlc.ai/2023/05/22/bringing-open-large-language-models-to-consumer-devices) RedPajama support is up. +* [05/08/2023] [[Post]](https://blog.mlc.ai/2023/05/08/bringing-hardware-accelerated-language-models-to-android-devices) MLC LLM is now available on Android. +* [05/01/2023] [[Post]](https://blog.mlc.ai/2023/05/01/bringing-accelerated-llm-to-consumer-hardware) MLC LLM is released with Metal, Vulkan and CUDA backends. +* [04/14/2023] [WebLLM](https://github.com/mlc-ai/web-llm) is released prior to MLC LLM with WebGPU and WebAssembly backend. + +## Getting Started + +Please visit our [documentation](https://llm.mlc.ai/docs/index.html#getting-started) for detailed instructions. + +## Model Support + +MLC LLM supports a wide range of model architectures and variants. We have the following prebuilts which you can +use off-the-shelf. Visit [Prebuilt Models](https://llm.mlc.ai/docs/prebuilt_models.html) to see the full list, and [Compile Models via MLC](https://llm.mlc.ai/docs/compilation/compile_models.html) to see how to use models not on this list. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ArchitecturePrebuilt Model Variants
LlamaLlama-2, Code Llama, Vicuna, WizardLM, WizardMath, OpenOrca Platypus2, FlagAlpha Llama-2 Chinese, georgesung Llama-2 Uncensored
GPT-NeoXRedPajama
GPT-J
RWKVRWKV-raven
MiniGPT
GPTBigCodeWizardCoder
ChatGLM
StableLM
Mistral
Phi
+ +## Universal Deployment APIs + +MLC LLM provides multiple sets of APIs across platforms and environments. These include +* [Python API](https://llm.mlc.ai/docs/deploy/python.html) +* [OpenAI-compatible Rest-API](https://llm.mlc.ai/docs/deploy/rest.html) +* [C++ API](https://llm.mlc.ai/docs/deploy/cli.html) +* [JavaScript API](https://llm.mlc.ai/docs/deploy/javascript.html) and [Web LLM](https://github.com/mlc-ai/web-llm) +* [Swift API for iOS App](https://llm.mlc.ai/docs/deploy/ios.html) +* [Java API and Android App](https://llm.mlc.ai/docs/deploy/android.html) + +## Citation + +Please consider citing our project if you find it useful: + +```bibtex +@software{mlc-llm, + author = {MLC team}, + title = {{MLC-LLM}}, + url = {https://github.com/mlc-ai/mlc-llm}, + year = {2023} +} +``` + +The underlying techniques of MLC LLM include: + +
+ References (Click to expand) + + ```bibtex + @inproceedings{tensorir, + author = {Feng, Siyuan and Hou, Bohan and Jin, Hongyi and Lin, Wuwei and Shao, Junru and Lai, Ruihang and Ye, Zihao and Zheng, Lianmin and Yu, Cody Hao and Yu, Yong and Chen, Tianqi}, + title = {TensorIR: An Abstraction for Automatic Tensorized Program Optimization}, + year = {2023}, + isbn = {9781450399166}, + publisher = {Association for Computing Machinery}, + address = {New York, NY, USA}, + url = {https://doi.org/10.1145/3575693.3576933}, + doi = {10.1145/3575693.3576933}, + booktitle = {Proceedings of the 28th ACM International Conference on Architectural Support for Programming Languages and Operating Systems, Volume 2}, + pages = {804–817}, + numpages = {14}, + keywords = {Tensor Computation, Machine Learning Compiler, Deep Neural Network}, + location = {Vancouver, BC, Canada}, + series = {ASPLOS 2023} + } + + @inproceedings{metaschedule, + author = {Shao, Junru and Zhou, Xiyou and Feng, Siyuan and Hou, Bohan and Lai, Ruihang and Jin, Hongyi and Lin, Wuwei and Masuda, Masahiro and Yu, Cody Hao and Chen, Tianqi}, + booktitle = {Advances in Neural Information Processing Systems}, + editor = {S. Koyejo and S. Mohamed and A. Agarwal and D. Belgrave and K. Cho and A. Oh}, + pages = {35783--35796}, + publisher = {Curran Associates, Inc.}, + title = {Tensor Program Optimization with Probabilistic Programs}, + url = {https://proceedings.neurips.cc/paper_files/paper/2022/file/e894eafae43e68b4c8dfdacf742bcbf3-Paper-Conference.pdf}, + volume = {35}, + year = {2022} + } + + @inproceedings{tvm, + author = {Tianqi Chen and Thierry Moreau and Ziheng Jiang and Lianmin Zheng and Eddie Yan and Haichen Shen and Meghan Cowan and Leyuan Wang and Yuwei Hu and Luis Ceze and Carlos Guestrin and Arvind Krishnamurthy}, + title = {{TVM}: An Automated {End-to-End} Optimizing Compiler for Deep Learning}, + booktitle = {13th USENIX Symposium on Operating Systems Design and Implementation (OSDI 18)}, + year = {2018}, + isbn = {978-1-939133-08-3}, + address = {Carlsbad, CA}, + pages = {578--594}, + url = {https://www.usenix.org/conference/osdi18/presentation/chen}, + publisher = {USENIX Association}, + month = oct, + } + ``` +
+ +## Links + +- You might want to check out our online public [Machine Learning Compilation course](https://mlc.ai) for a systematic +walkthrough of our approaches. +- [WebLLM](https://webllm.mlc.ai/) is a companion project using MLC LLM's WebGPU and WebAssembly backend. +- [WebStableDiffusion](https://websd.mlc.ai/) is a companion project for diffusion models with the WebGPU backend. + diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..002b05d --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,19 @@ +app/src/main/jni/*.h +app/src/main/jni/*.cc +app/src/main/obj + +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/android/README.md b/android/README.md new file mode 100644 index 0000000..502eb53 --- /dev/null +++ b/android/README.md @@ -0,0 +1,3 @@ +# MLC-LLM Android + +[Documentation page](https://llm.mlc.ai/docs/deploy/android.html) diff --git a/android/app/.gitignore b/android/app/.gitignore new file mode 100644 index 0000000..558f311 --- /dev/null +++ b/android/app/.gitignore @@ -0,0 +1,2 @@ +/build +/src/main/libs \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..debbb90 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,73 @@ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'ai.mlc.mlcchat' + compileSdk 34 + + defaultConfig { + applicationId "ai.mlc.mlcchat32" + minSdk 26 + targetSdk 33 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary true + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } + buildFeatures { + compose true + } + composeOptions { + kotlinCompilerExtensionVersion '1.4.3' + } + packagingOptions { + resources { + excludes += '/META-INF/{AL2.0,LGPL2.1}' + } + } +} + +dependencies { + implementation project(":library") + implementation 'androidx.core:core-ktx:1.10.1' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1' + implementation 'androidx.activity:activity-compose:1.7.1' + implementation platform('androidx.compose:compose-bom:2022.10.00') + implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1' + implementation 'androidx.compose.ui:ui' + implementation 'androidx.compose.ui:ui-graphics' + implementation 'androidx.compose.ui:ui-tooling-preview' + implementation 'androidx.compose.material3:material3:1.1.0' + implementation 'androidx.compose.material:material-icons-extended' + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'androidx.navigation:navigation-compose:2.5.3' + implementation 'com.google.code.gson:gson:2.10.1' + implementation fileTree(dir: 'src/main/libs', include: ['*.aar', '*.jar'], exclude: []) + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + androidTestImplementation platform('androidx.compose:compose-bom:2022.10.00') + androidTestImplementation 'androidx.compose.ui:ui-test-junit4' + debugImplementation 'androidx.compose.ui:ui-tooling' + debugImplementation 'androidx.compose.ui:ui-test-manifest' + +} \ No newline at end of file diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..e25d837 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/ic_launcher-playstore.png b/android/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..3c16fd65fd66c5db7ab493bcb80b8f1ead7c8ef1 GIT binary patch literal 47710 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 j_-xe5^8e={+As0etSRBcmM().toMutableStateList() + val chatState = ChatState() + val modelSampleList = emptyList().toMutableStateList() + private var showAlert = mutableStateOf(false) + private var alertMessage = mutableStateOf("") + private var appConfig = AppConfig( + emptyList().toMutableList(), + emptyList().toMutableList() + ) + private val application = getApplication() + private val appDirFile = application.getExternalFilesDir("") + private val gson = Gson() + private val modelIdSet = emptySet().toMutableSet() + + companion object { + const val AppConfigFilename = "app-config.json" + const val ModelConfigFilename = "mlc-chat-config.json" + const val ParamsConfigFilename = "ndarray-cache.json" + const val ModelUrlSuffix = "resolve/main/" + } + + init { + loadAppConfig() + } + + fun isShowingAlert(): Boolean { + return showAlert.value + } + + fun errorMessage(): String { + return alertMessage.value + } + + fun dismissAlert() { + require(showAlert.value) + showAlert.value = false + } + + fun copyError() { + require(showAlert.value) + val clipboard = + application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + clipboard.setPrimaryClip(ClipData.newPlainText("MLCChat", errorMessage())) + } + + private fun issueAlert(error: String) { + showAlert.value = true + alertMessage.value = error + } + + fun requestDeleteModel(modelId: String) { + deleteModel(modelId) + issueAlert("Model: $modelId has been deleted") + } + + + private fun loadAppConfig() { + val appConfigFile = File(appDirFile, AppConfigFilename) + val jsonString: String = if (!appConfigFile.exists()) { + application.assets.open(AppConfigFilename).bufferedReader().use { it.readText() } + } else { + appConfigFile.readText() + } + appConfig = gson.fromJson(jsonString, AppConfig::class.java) + appConfig.modelLibs = emptyList().toMutableList() + modelList.clear() + modelIdSet.clear() + modelSampleList.clear() + for (modelRecord in appConfig.modelList) { + appConfig.modelLibs.add(modelRecord.modelLib) + val modelDirFile = File(appDirFile, modelRecord.modelId) + val modelConfigFile = File(modelDirFile, ModelConfigFilename) + if (modelConfigFile.exists()) { + val modelConfigString = modelConfigFile.readText() + val modelConfig = gson.fromJson(modelConfigString, ModelConfig::class.java) + modelConfig.modelId = modelRecord.modelId + modelConfig.modelLib = modelRecord.modelLib + modelConfig.estimatedVramBytes = modelRecord.estimatedVramBytes + addModelConfig(modelConfig, modelRecord.modelUrl, true) + } else { + downloadModelConfig( + if (modelRecord.modelUrl.endsWith("/")) modelRecord.modelUrl else "${modelRecord.modelUrl}/", + modelRecord, + true + ) + } + } + } + + private fun readInputFile(): Array>? { + val inputFile = File(appDirFile, "input.json") + val jsonString: String = if (!inputFile.exists()) { + application.assets.open("input.json").bufferedReader().use { it.readText() } + } else { + inputFile.readText() + } + return gson.fromJson(jsonString, Array>::class.java) + } + + private fun updateAppConfig(action: () -> Unit) { + action() + val jsonString = gson.toJson(appConfig) + val appConfigFile = File(appDirFile, AppConfigFilename) + appConfigFile.writeText(jsonString) + } + + private fun addModelConfig(modelConfig: ModelConfig, modelUrl: String, isBuiltin: Boolean) { + require(!modelIdSet.contains(modelConfig.modelId)) + modelIdSet.add(modelConfig.modelId) + modelList.add( + ModelState( + modelConfig, + modelUrl + if (modelUrl.endsWith("/")) "" else "/", + File(appDirFile, modelConfig.modelId) + ) + ) + if (!isBuiltin) { + updateAppConfig { + appConfig.modelList.add( + ModelRecord( + modelUrl, + modelConfig.modelId, + modelConfig.estimatedVramBytes, + modelConfig.modelLib + ) + ) + } + } + } + + private fun deleteModel(modelId: String) { + val modelDirFile = File(appDirFile, modelId) + modelDirFile.deleteRecursively() + require(!modelDirFile.exists()) + modelIdSet.remove(modelId) + modelList.removeIf { modelState -> modelState.modelConfig.modelId == modelId } + updateAppConfig { + appConfig.modelList.removeIf { modelRecord -> modelRecord.modelId == modelId } + } + } + + private fun isModelConfigAllowed(modelConfig: ModelConfig): Boolean { + if (appConfig.modelLibs.contains(modelConfig.modelLib)) return true + viewModelScope.launch { + issueAlert("Model lib ${modelConfig.modelLib} is not supported.") + } + return false + } + + + private fun downloadModelConfig( + modelUrl: String, + modelRecord: ModelRecord, + isBuiltin: Boolean + ) { + thread(start = true) { + try { + val url = URL("${modelUrl}${ModelUrlSuffix}${ModelConfigFilename}") + val tempId = UUID.randomUUID().toString() + val tempFile = File( + application.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), + tempId + ) + url.openStream().use { + Channels.newChannel(it).use { src -> + FileOutputStream(tempFile).use { fileOutputStream -> + fileOutputStream.channel.transferFrom(src, 0, Long.MAX_VALUE) + } + } + } + require(tempFile.exists()) + viewModelScope.launch { + try { + val modelConfigString = tempFile.readText() + val modelConfig = gson.fromJson(modelConfigString, ModelConfig::class.java) + modelConfig.modelId = modelRecord.modelId + modelConfig.modelLib = modelRecord.modelLib + modelConfig.estimatedVramBytes = modelRecord.estimatedVramBytes + if (modelIdSet.contains(modelConfig.modelId)) { + tempFile.delete() + issueAlert("${modelConfig.modelId} has been used, please consider another local ID") + return@launch + } + if (!isModelConfigAllowed(modelConfig)) { + tempFile.delete() + return@launch + } + val modelDirFile = File(appDirFile, modelConfig.modelId) + val modelConfigFile = File(modelDirFile, ModelConfigFilename) + tempFile.copyTo(modelConfigFile, overwrite = true) + tempFile.delete() + require(modelConfigFile.exists()) + addModelConfig(modelConfig, modelUrl, isBuiltin) + } catch (e: Exception) { + viewModelScope.launch { + Toast.makeText( + application, + "Add model failed: ${e.localizedMessage}", + Toast.LENGTH_LONG + ).show() + Log.e("mlc-llm", e.localizedMessage) + } + } + } + } catch (e: Exception) { + viewModelScope.launch { + Toast.makeText( + application, + "Download model config failed: ${e.localizedMessage}", + Toast.LENGTH_LONG + ).show() + Log.e("mlc-llm", e.localizedMessage) + } + } + + } + } + + inner class ModelState( + val modelConfig: ModelConfig, + private val modelUrl: String, + private val modelDirFile: File + ) { + var modelInitState = mutableStateOf(ModelInitState.Initializing) + private var paramsConfig = ParamsConfig(emptyList()) + val progress = mutableStateOf(0) + val total = mutableStateOf(1) + val id: UUID = UUID.randomUUID() + private val remainingTasks = emptySet().toMutableSet() + private val downloadingTasks = emptySet().toMutableSet() + private val maxDownloadTasks = 3 + private val gson = Gson() + + + init { + switchToInitializing() + } + + private fun switchToInitializing() { + val paramsConfigFile = File(modelDirFile, ParamsConfigFilename) + if (paramsConfigFile.exists()) { + loadParamsConfig() + switchToIndexing() + } else { + downloadParamsConfig() + } + } + + private fun loadParamsConfig() { + val paramsConfigFile = File(modelDirFile, ParamsConfigFilename) + require(paramsConfigFile.exists()) + val jsonString = paramsConfigFile.readText() + paramsConfig = gson.fromJson(jsonString, ParamsConfig::class.java) + } + + private fun downloadParamsConfig() { + thread(start = true) { + val url = URL("${modelUrl}${ModelUrlSuffix}${ParamsConfigFilename}") + val tempId = UUID.randomUUID().toString() + val tempFile = File(modelDirFile, tempId) + url.openStream().use { + Channels.newChannel(it).use { src -> + FileOutputStream(tempFile).use { fileOutputStream -> + fileOutputStream.channel.transferFrom(src, 0, Long.MAX_VALUE) + } + } + } + require(tempFile.exists()) + val paramsConfigFile = File(modelDirFile, ParamsConfigFilename) + tempFile.renameTo(paramsConfigFile) + require(paramsConfigFile.exists()) + viewModelScope.launch { + loadParamsConfig() + switchToIndexing() + } + } + } + + fun handleStart() { + switchToDownloading() + } + + fun handlePause() { + switchToPausing() + } + + fun handleClear() { + require( + modelInitState.value == ModelInitState.Downloading || + modelInitState.value == ModelInitState.Paused || + modelInitState.value == ModelInitState.Finished + ) + switchToClearing() + } + + private fun switchToClearing() { + if (modelInitState.value == ModelInitState.Paused) { + modelInitState.value = ModelInitState.Clearing + clear() + } else if (modelInitState.value == ModelInitState.Finished) { + modelInitState.value = ModelInitState.Clearing + if (chatState.modelName.value == modelConfig.modelId) { + chatState.requestTerminateChat { clear() } + } else { + clear() + } + } else { + modelInitState.value = ModelInitState.Clearing + } + } + + fun handleDelete() { + require( + modelInitState.value == ModelInitState.Downloading || + modelInitState.value == ModelInitState.Paused || + modelInitState.value == ModelInitState.Finished + ) + switchToDeleting() + } + + private fun switchToDeleting() { + if (modelInitState.value == ModelInitState.Paused) { + modelInitState.value = ModelInitState.Deleting + delete() + } else if (modelInitState.value == ModelInitState.Finished) { + modelInitState.value = ModelInitState.Deleting + if (chatState.modelName.value == modelConfig.modelId) { + chatState.requestTerminateChat { delete() } + } else { + delete() + } + } else { + modelInitState.value = ModelInitState.Deleting + } + } + + private fun switchToIndexing() { + modelInitState.value = ModelInitState.Indexing + progress.value = 0 + total.value = modelConfig.tokenizerFiles.size + paramsConfig.paramsRecords.size + for (tokenizerFilename in modelConfig.tokenizerFiles) { + val file = File(modelDirFile, tokenizerFilename) + if (file.exists()) { + ++progress.value + } else { + remainingTasks.add( + DownloadTask( + URL("${modelUrl}${ModelUrlSuffix}${tokenizerFilename}"), + file + ) + ) + } + } + for (paramsRecord in paramsConfig.paramsRecords) { + val file = File(modelDirFile, paramsRecord.dataPath) + if (file.exists()) { + ++progress.value + } else { + remainingTasks.add( + DownloadTask( + URL("${modelUrl}${ModelUrlSuffix}${paramsRecord.dataPath}"), + file + ) + ) + } + } + if (progress.value < total.value) { + switchToPaused() + } else { + switchToFinished() + } + } + + private fun switchToDownloading() { + modelInitState.value = ModelInitState.Downloading + for (downloadTask in remainingTasks) { + if (downloadingTasks.size < maxDownloadTasks) { + handleNewDownload(downloadTask) + } else { + return + } + } + } + + private fun handleNewDownload(downloadTask: DownloadTask) { + require(modelInitState.value == ModelInitState.Downloading) + require(!downloadingTasks.contains(downloadTask)) + downloadingTasks.add(downloadTask) + thread(start = true) { + val tempId = UUID.randomUUID().toString() + val tempFile = File(modelDirFile, tempId) + downloadTask.url.openStream().use { + Channels.newChannel(it).use { src -> + FileOutputStream(tempFile).use { fileOutputStream -> + fileOutputStream.channel.transferFrom(src, 0, Long.MAX_VALUE) + } + } + } + require(tempFile.exists()) + tempFile.renameTo(downloadTask.file) + require(downloadTask.file.exists()) + viewModelScope.launch { + handleFinishDownload(downloadTask) + } + } + } + + private fun handleNextDownload() { + require(modelInitState.value == ModelInitState.Downloading) + for (downloadTask in remainingTasks) { + if (!downloadingTasks.contains(downloadTask)) { + handleNewDownload(downloadTask) + break + } + } + } + + private fun handleFinishDownload(downloadTask: DownloadTask) { + remainingTasks.remove(downloadTask) + downloadingTasks.remove(downloadTask) + ++progress.value + require( + modelInitState.value == ModelInitState.Downloading || + modelInitState.value == ModelInitState.Pausing || + modelInitState.value == ModelInitState.Clearing || + modelInitState.value == ModelInitState.Deleting + ) + if (modelInitState.value == ModelInitState.Downloading) { + if (remainingTasks.isEmpty()) { + if (downloadingTasks.isEmpty()) { + switchToFinished() + } + } else { + handleNextDownload() + } + } else if (modelInitState.value == ModelInitState.Pausing) { + if (downloadingTasks.isEmpty()) { + switchToPaused() + } + } else if (modelInitState.value == ModelInitState.Clearing) { + if (downloadingTasks.isEmpty()) { + clear() + } + } else if (modelInitState.value == ModelInitState.Deleting) { + if (downloadingTasks.isEmpty()) { + delete() + } + } + } + + private fun clear() { + val files = modelDirFile.listFiles { dir, name -> + !(dir == modelDirFile && name == ModelConfigFilename) + } + require(files != null) + for (file in files) { + file.deleteRecursively() + require(!file.exists()) + } + val modelConfigFile = File(modelDirFile, ModelConfigFilename) + require(modelConfigFile.exists()) + switchToIndexing() + } + + private fun delete() { + modelDirFile.deleteRecursively() + require(!modelDirFile.exists()) + requestDeleteModel(modelConfig.modelId) + } + + private fun switchToPausing() { + modelInitState.value = ModelInitState.Pausing + } + + private fun switchToPaused() { + modelInitState.value = ModelInitState.Paused + } + + + private fun switchToFinished() { + modelInitState.value = ModelInitState.Finished + } + + fun startChat() { + chatState.requestReloadChat( + modelConfig, + modelDirFile.absolutePath, + ) + } + + } + + inner class ChatState { + val messages = emptyList().toMutableStateList() + val report = mutableStateOf("") + val modelName = mutableStateOf("") + lateinit var modelLoadTime: TimeRecord + + private var modelChatState = mutableStateOf(ModelChatState.Ready) + @Synchronized get + @Synchronized set + private val backend = ChatModule() + private var modelLib = "" + private var modelPath = "" + private val executorService = Executors.newSingleThreadExecutor() + + private fun mainResetChat() { + executorService.submit { + callBackend { backend.resetChat() } + viewModelScope.launch { + clearHistory() + switchToReady() + } + } + } + + private fun clearHistory() { + messages.clear() + report.value = "" + } + + + private fun switchToResetting() { + modelChatState.value = ModelChatState.Resetting + } + + private fun switchToGenerating() { + modelChatState.value = ModelChatState.Generating + } + + private fun switchToReloading() { + modelChatState.value = ModelChatState.Reloading + } + + private fun switchToReady() { + modelChatState.value = ModelChatState.Ready + } + + private fun switchToFailed() { + modelChatState.value = ModelChatState.Falied + } + + private fun callBackend(callback: () -> Unit): Boolean { + try { + callback() + } catch (e: Exception) { + viewModelScope.launch { + val stackTrace = e.stackTraceToString() + val errorMessage = e.localizedMessage + appendMessage( + MessageRole.Bot, + "MLCChat failed\n\nStack trace:\n$stackTrace\n\nError message:\n$errorMessage" + ) + switchToFailed() + } + return false + } + return true + } + + fun requestResetChat() { + require(interruptable()) + interruptChat( + prologue = { + switchToResetting() + }, + epilogue = { + mainResetChat() + } + ) + } + + private fun interruptChat(prologue: () -> Unit, epilogue: () -> Unit) { + // prologue runs before interruption + // epilogue runs after interruption + require(interruptable()) + if (modelChatState.value == ModelChatState.Ready) { + prologue() + epilogue() + } else if (modelChatState.value == ModelChatState.Generating) { + prologue() + executorService.submit { + viewModelScope.launch { epilogue() } + } + } else { + require(false) + } + } + + fun requestTerminateChat(callback: () -> Unit) { + require(interruptable()) + interruptChat( + prologue = { + switchToTerminating() + }, + epilogue = { + mainTerminateChat(callback) + } + ) + } + + private fun mainTerminateChat(callback: () -> Unit) { + executorService.submit { + callBackend { backend.unload() } + viewModelScope.launch { + clearHistory() + switchToReady() + callback() + } + } + } + + private fun switchToTerminating() { + modelChatState.value = ModelChatState.Terminating + } + + + fun requestReloadChat(modelConfig: ModelConfig, modelPath: String) { + + if (this.modelName.value == modelConfig.modelId && this.modelLib == modelConfig.modelLib && this.modelPath == modelPath) { + return + } + require(interruptable()) + interruptChat( + prologue = { + switchToReloading() + }, + epilogue = { + mainReloadChat(modelConfig, modelPath) + } + ) + } + + private fun mainReloadChat(modelConfig: ModelConfig, modelPath: String) { + val timeStart = Date() + clearHistory() + this.modelName.value = modelConfig.modelId + this.modelLib = modelConfig.modelLib + this.modelPath = modelPath + executorService.submit { + viewModelScope.launch { + Toast.makeText(application, "Initialize...", Toast.LENGTH_SHORT).show() + } + if (!callBackend { + backend.unload() + backend.reload( + modelConfig.modelLib, + modelPath + ) + }) return@submit + viewModelScope.launch { + Toast.makeText(application, "Ready to chat", Toast.LENGTH_SHORT).show() + switchToReady() + } + + val duration = Date().time - timeStart.time + this.modelLoadTime = TimeRecord(timeStart, duration) + } + } + + fun requestGenerate(prompt: String) { + require(chatable()) + switchToGenerating() + executorService.submit { + appendMessage(MessageRole.User, prompt) + appendMessage(MessageRole.Bot, "") + if (!callBackend { backend.prefill(prompt) }) return@submit + while (!backend.stopped()) { + if (!callBackend { + backend.decode() + val newText = backend.message + viewModelScope.launch { updateMessage(MessageRole.Bot, newText) } + }) return@submit + if (modelChatState.value != ModelChatState.Generating) return@submit + } + val runtimeStats = backend.runtimeStatsText() + viewModelScope.launch { + report.value = runtimeStats + if (modelChatState.value == ModelChatState.Generating) switchToReady() + } + } + } + + private fun appendMessage(role: MessageRole, text: String) { + messages.add(MessageData(role, text)) + } + + + private fun updateMessage(role: MessageRole, text: String) { + messages[messages.size - 1] = MessageData(role, text) + } + + fun chatable(): Boolean { + return modelChatState.value == ModelChatState.Ready + } + + fun interruptable(): Boolean { + return modelChatState.value == ModelChatState.Ready + || modelChatState.value == ModelChatState.Generating + || modelChatState.value == ModelChatState.Falied + } + + fun requestAutomation(measurementFilename: String) { + + val conversationsRecordManager = ConversationsRecordManager() + val conversations = readInputFile() + + if (conversations == null) { + Log.e("AppViewModel", "Couldn't not load input.json") + return + } + + require(chatable()) + switchToGenerating() + + executorService.submit { + + // per conversation + conversations.forEachIndexed { c_idx, conversation -> + + val conversationRecord = ConversationRecord(this.modelName.value, this.modelLoadTime) + + conversation.forEachIndexed { q_idx, question -> + + appendMessage(MessageRole.User, "{$c_idx}_{$q_idx}: $question") + //Log.d("AppViewModel", "Prompt: $question") + + val timeStart = Date() + + if (!callBackend { backend.prefill(question) }) return@submit + while (!backend.stopped()) { + if (!callBackend { + backend.decode() + //val newText = backend.message + }) return@submit + if (modelChatState.value != ModelChatState.Generating) return@submit + } + + val runtimeStatsText = backend.verboseRuntimeStatsText() + val jsonResult = parseJSON(runtimeStatsText) + + val originalSessionTokens = -1 + var inputTokens = -1 + var outputTokens = -1 + + jsonResult?.let { + inputTokens = it["prefill"]?.get("total tokens")?.split(" ")?.first()?.toInt()!! + outputTokens = it["decode"]?.get("total tokens")?.split(" ")?.first()?.toInt()!! + } + + //Log.d("AppViewModel", "Answer: " + backend.message) + + val duration = Date().time - timeStart.time + val questionRecord = QuestionRecord( + TimeRecord(timeStart, duration), + question, + backend.message, + originalSessionTokens, + inputTokens, + outputTokens, + runtimeStatsText + ) + conversationRecord.questionRecords.add(questionRecord) + val message = backend.message + appendMessage(MessageRole.Bot, "{$c_idx}_{$q_idx}: $message") + sleep(5000) + } + + // Save energy events for particular session + val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).toString() + File.separator + "melt_measurements", + "${measurementFilename}_conv$c_idx.csv" + ) + + if (!file.parentFile.exists()) { + file.parentFile.mkdirs() + } + + backend.saveEnergyEventsToCSV(file) + + // add metrics + conversationsRecordManager.addConversationRecord(conversationRecord) + + // clear context + backend.resetChat() + backend.resetEnergyEvents() + + appendMessage(MessageRole.Bot, "--sleep--") + sleep(60 * 1000) + } + + conversationsRecordManager.saveToFile(measurementFilename) + + // Notify BladeRunner that task is complete + val client = RestAwaitLib("192.168.1.42", 5100) + val response = client.continueExecution() + Log.d("AppViewModel", response) + } + } + } + + fun parseJSON(jsonString: String): Map>? { + return try { + val jsonObject = JSONObject(jsonString) + val result = mutableMapOf>() + + jsonObject.keys().forEach { key -> + val innerJson = jsonObject.getJSONObject(key) + val innerMap = mutableMapOf() + + innerJson.keys().forEach { innerKey -> + innerMap[innerKey] = innerJson.getString(innerKey) + } + + result[key] = innerMap + } + + result + } catch (e: Exception) { + println("Error parsing JSON: $e") + null + } + } +} + +enum class ModelInitState { + Initializing, + Indexing, + Paused, + Downloading, + Pausing, + Clearing, + Deleting, + Finished +} + +enum class ModelChatState { + Generating, + Resetting, + Reloading, + Terminating, + Ready, + Falied +} + +enum class MessageRole { + Bot, + User +} + +data class DownloadTask(val url: URL, val file: File) + +data class MessageData(val role: MessageRole, val text: String, val id: UUID = UUID.randomUUID()) + +data class AppConfig( + @SerializedName("model_libs") var modelLibs: MutableList, + @SerializedName("model_list") val modelList: MutableList, +) + +data class ModelRecord( + @SerializedName("model_url") val modelUrl: String, + @SerializedName("model_id") val modelId: String, + @SerializedName("estimated_vram_bytes") val estimatedVramBytes: Long?, + @SerializedName("model_lib") val modelLib: String +) + +data class ModelConfig( + @SerializedName("model_lib") var modelLib: String, + @SerializedName("model_id") var modelId: String, + @SerializedName("estimated_vram_bytes") var estimatedVramBytes: Long?, + @SerializedName("tokenizer_files") val tokenizerFiles: List, + @SerializedName("context_window_size") val contextWindowSize: Int, + @SerializedName("prefill_chunk_size") val prefillChunkSize: Int, +) + +data class ParamsRecord( + @SerializedName("dataPath") val dataPath: String +) + +data class ParamsConfig( + @SerializedName("records") val paramsRecords: List +) \ No newline at end of file diff --git a/android/app/src/main/java/ai/mlc/mlcchat/ChatView.kt b/android/app/src/main/java/ai/mlc/mlcchat/ChatView.kt new file mode 100644 index 0000000..61351f1 --- /dev/null +++ b/android/app/src/main/java/ai/mlc/mlcchat/ChatView.kt @@ -0,0 +1,236 @@ +package ai.mlc.mlcchat + +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Replay +import androidx.compose.material.icons.filled.Send +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import kotlinx.coroutines.launch + +@ExperimentalMaterial3Api +@Composable +fun ChatView( + navController: NavController, chatState: AppViewModel.ChatState +) { + val localFocusManager = LocalFocusManager.current + var isDialogOpen by remember { mutableStateOf(false) } + var filenameInput by remember { mutableStateOf("") } + + Scaffold(topBar = { + TopAppBar( + title = { + Text( + text = "MLCChat: " + chatState.modelName.value.split("-")[0], + color = MaterialTheme.colorScheme.onPrimary + ) + }, + colors = TopAppBarDefaults.topAppBarColors(containerColor = MaterialTheme.colorScheme.primary), + navigationIcon = { + IconButton( + onClick = { navController.popBackStack() }, + enabled = chatState.interruptable() + ) { + Icon( + imageVector = Icons.Filled.ArrowBack, + contentDescription = "back home page", + tint = MaterialTheme.colorScheme.onPrimary + ) + } + }, + actions = { + IconButton( + onClick = { + //chatState.requestAutomation() + isDialogOpen = true + }, + enabled = chatState.interruptable() + ) { + Icon( + imageVector = Icons.Filled.Replay, + contentDescription = "reset the chat", + tint = MaterialTheme.colorScheme.onPrimary + ) + } + }) + }, modifier = Modifier.pointerInput(Unit) { + detectTapGestures(onTap = { + localFocusManager.clearFocus() + }) + }) { paddingValues -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .padding(horizontal = 10.dp) + ) { + val lazyColumnListState = rememberLazyListState() + val coroutineScope = rememberCoroutineScope() + Text( + text = chatState.report.value, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight() + .padding(top = 5.dp) + ) + Divider(thickness = 1.dp, modifier = Modifier.padding(vertical = 5.dp)) + LazyColumn( + modifier = Modifier.weight(9f), + verticalArrangement = Arrangement.spacedBy(5.dp, alignment = Alignment.Bottom), + state = lazyColumnListState + ) { + coroutineScope.launch { + lazyColumnListState.animateScrollToItem(chatState.messages.size) + } + items( + items = chatState.messages, + key = { message -> message.id }, + ) { message -> + MessageView(messageData = message) + } + item { + // place holder item for scrolling to the bottom + } + } + Divider(thickness = 1.dp, modifier = Modifier.padding(top = 5.dp)) + SendMessageView(chatState = chatState) + } + } + + if (isDialogOpen) { + AlertDialog( + onDismissRequest = { isDialogOpen = false }, + title = { Text("Enter filename prefix") }, + text = { + OutlinedTextField( + value = filenameInput, + onValueChange = { filenameInput = it }, + label = { Text("Filename prefix") } + ) + }, + confirmButton = { + Button( + onClick = { + if (filenameInput.isNotBlank()) { + chatState.requestAutomation(filenameInput) + isDialogOpen = false + filenameInput = "" + } + } + ) { + Text("Run") + } + }, + dismissButton = { + Button( + onClick = { isDialogOpen = false } + ) { + Text("Cancel") + } + } + ) + } +} + +@Composable +fun MessageView(messageData: MessageData) { + SelectionContainer { + if (messageData.role == MessageRole.Bot) { + Row( + horizontalArrangement = Arrangement.Start, + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = messageData.text, + textAlign = TextAlign.Left, + color = MaterialTheme.colorScheme.onSecondaryContainer, + modifier = Modifier + .wrapContentWidth() + .background( + color = MaterialTheme.colorScheme.secondaryContainer, + shape = RoundedCornerShape(5.dp) + ) + .padding(5.dp) + .widthIn(max = 300.dp) + ) + + } + } else { + Row( + horizontalArrangement = Arrangement.End, + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = messageData.text, + textAlign = TextAlign.Right, + color = MaterialTheme.colorScheme.onPrimaryContainer, + modifier = Modifier + .wrapContentWidth() + .background( + color = MaterialTheme.colorScheme.primaryContainer, + shape = RoundedCornerShape(5.dp) + ) + .padding(5.dp) + .widthIn(max = 300.dp) + ) + + } + } + } +} + +@ExperimentalMaterial3Api +@Composable +fun SendMessageView(chatState: AppViewModel.ChatState) { + val localFocusManager = LocalFocusManager.current + Row( + horizontalArrangement = Arrangement.spacedBy(5.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .height(IntrinsicSize.Max) + .fillMaxWidth() + .padding(bottom = 5.dp) + ) { + var text by rememberSaveable { mutableStateOf("") } + OutlinedTextField( + value = text, + onValueChange = { text = it }, + label = { Text(text = "Input") }, + modifier = Modifier + .weight(9f), + ) + IconButton( + onClick = { + localFocusManager.clearFocus() + chatState.requestGenerate(text) + text = "" + }, + modifier = Modifier + .aspectRatio(1f) + .weight(1f), + enabled = (text != "" && chatState.chatable()) + ) { + Icon( + imageVector = Icons.Filled.Send, + contentDescription = "send message", + ) + } + } +} diff --git a/android/app/src/main/java/ai/mlc/mlcchat/ConversationsRecordManager.kt b/android/app/src/main/java/ai/mlc/mlcchat/ConversationsRecordManager.kt new file mode 100644 index 0000000..4cf28bb --- /dev/null +++ b/android/app/src/main/java/ai/mlc/mlcchat/ConversationsRecordManager.kt @@ -0,0 +1,89 @@ +package ai.mlc.mlcchat + +import android.os.Environment +import android.util.Log +import org.json.JSONArray +import org.json.JSONObject +import java.io.File +import java.io.IOException +import java.util.* + +data class ConversationRecord( + val modelName: String, + val modelLoadTime: TimeRecord, + val questionRecords: MutableList = mutableListOf() +) + +data class QuestionRecord( + val time: TimeRecord, + val input: String, + val output: String, + val original_session_tokens: Int, + val input_tokens: Int, + val output_tokens: Int, + val runtimeStats: String +) + +data class TimeRecord( + val start: Date, + val duration: Long +) + +class ConversationsRecordManager { + private val conversations: ArrayList = ArrayList() + + fun addConversationRecord(conversation: ConversationRecord) { + conversations.add(conversation) + } + + fun saveToFile(fileName: String) { + val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).toString() + File.separator + "melt_measurements", + "$fileName.json" + ) + + if (!file.parentFile.exists()) { + file.parentFile.mkdirs() + } + + try { + val jsonArray = JSONArray() + for (session in conversations) { + val sessionObject = JSONObject() + sessionObject.put("modelName", session.modelName) + + val modelLoadTime = JSONObject() + val startEpoch = session.modelLoadTime.start.time / 1000.0 + modelLoadTime.put("start", startEpoch) + modelLoadTime.put("duration", session.modelLoadTime.duration / 1000.0) + sessionObject.put("modelLoadTime", modelLoadTime) + + val questionRecordsArray = JSONArray() + for (questionRecord in session.questionRecords) { + val chatRecordObject = JSONObject() + val timeRecordObject = JSONObject() + + val questionStartEpoch = questionRecord.time.start.time / 1000.0 + timeRecordObject.put("start", questionStartEpoch) + timeRecordObject.put("duration", questionRecord.time.duration / 1000.0) + + chatRecordObject.put("time", timeRecordObject) + chatRecordObject.put("input", questionRecord.input) + chatRecordObject.put("output", questionRecord.output) + chatRecordObject.put("original_session_tokens", questionRecord.original_session_tokens) + chatRecordObject.put("input_tokens", questionRecord.input_tokens) + chatRecordObject.put("output_tokens", questionRecord.output_tokens) + chatRecordObject.put("runtimeStats", questionRecord.runtimeStats) + + questionRecordsArray.put(chatRecordObject) + } + sessionObject.put("questionRecords", questionRecordsArray) + + jsonArray.put(sessionObject) + } + file.writeText(jsonArray.toString(4)) + Log.d("SessionRecordManager", "JSON data successfully saved at: " + file.absolutePath) + } catch (e: IOException) { + Log.e("SessionRecordManager", "Failed to write JSON data: ${e.localizedMessage}") + } + } +} diff --git a/android/app/src/main/java/ai/mlc/mlcchat/MainActivity.kt b/android/app/src/main/java/ai/mlc/mlcchat/MainActivity.kt new file mode 100644 index 0000000..c586869 --- /dev/null +++ b/android/app/src/main/java/ai/mlc/mlcchat/MainActivity.kt @@ -0,0 +1,29 @@ +package ai.mlc.mlcchat + +import ai.mlc.mlcchat.ui.theme.MLCChatTheme +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Surface +import androidx.compose.ui.Modifier + + +class MainActivity : ComponentActivity() { + + @ExperimentalMaterial3Api + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + Surface( + modifier = Modifier + .fillMaxSize() + ) { + MLCChatTheme { + NavView() + } + } + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/ai/mlc/mlcchat/NavView.kt b/android/app/src/main/java/ai/mlc/mlcchat/NavView.kt new file mode 100644 index 0000000..fe897ce --- /dev/null +++ b/android/app/src/main/java/ai/mlc/mlcchat/NavView.kt @@ -0,0 +1,18 @@ +package ai.mlc.mlcchat + +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController + +@ExperimentalMaterial3Api +@Composable +fun NavView(appViewModel: AppViewModel = viewModel()) { + val navController = rememberNavController() + NavHost(navController = navController, startDestination = "home") { + composable("home") { StartView(navController, appViewModel) } + composable("chat") { ChatView(navController, appViewModel.chatState) } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/ai/mlc/mlcchat/RestAwaitLib.kt b/android/app/src/main/java/ai/mlc/mlcchat/RestAwaitLib.kt new file mode 100644 index 0000000..409b72f --- /dev/null +++ b/android/app/src/main/java/ai/mlc/mlcchat/RestAwaitLib.kt @@ -0,0 +1,25 @@ +package ai.mlc.mlcchat + +import java.io.BufferedReader +import java.io.InputStreamReader +import java.net.HttpURLConnection +import java.net.URL + +class RestAwaitLib(private val host: String, private val port: Int) { + + fun continueExecution(): String { + val url = URL("http://$host:$port/continue") + val connection = url.openConnection() as HttpURLConnection + connection.requestMethod = "GET" + + val responseCode = connection.responseCode + if (responseCode == HttpURLConnection.HTTP_OK) { + val reader = BufferedReader(InputStreamReader(connection.inputStream)) + val response = reader.readText() + reader.close() + return response + } else { + throw RuntimeException("GET request failed with response code: $responseCode") + } + } +} diff --git a/android/app/src/main/java/ai/mlc/mlcchat/StartView.kt b/android/app/src/main/java/ai/mlc/mlcchat/StartView.kt new file mode 100644 index 0000000..a58129e --- /dev/null +++ b/android/app/src/main/java/ai/mlc/mlcchat/StartView.kt @@ -0,0 +1,251 @@ +package ai.mlc.mlcchat + +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Chat +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material.icons.outlined.Download +import androidx.compose.material.icons.outlined.Pause +import androidx.compose.material.icons.outlined.Schedule +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Divider +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController + + +@ExperimentalMaterial3Api +@Composable +fun StartView( + navController: NavController, + appViewModel: AppViewModel +) { + val localFocusManager = LocalFocusManager.current + Scaffold( + topBar = { + TopAppBar( + title = { Text(text = "MLCChat", color = MaterialTheme.colorScheme.onPrimary) }, + colors = TopAppBarDefaults.topAppBarColors(containerColor = MaterialTheme.colorScheme.primary) + ) + }, + modifier = Modifier.pointerInput(Unit) { + detectTapGestures(onTap = { + localFocusManager.clearFocus() + }) + } + ) + { paddingValues -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .padding(horizontal = 10.dp) + ) { + Text(text = "Model List", modifier = Modifier.padding(top = 10.dp)) + LazyColumn() { + items(items = appViewModel.modelList, + key = { modelState -> modelState.id } + ) { modelState -> + ModelView( + navController = navController, + modelState = modelState, + appViewModel = appViewModel + ) + } + } + } + if (appViewModel.isShowingAlert()) { + AlertDialog( + onDismissRequest = { appViewModel.dismissAlert() }, + onConfirmation = { appViewModel.copyError() }, + error = appViewModel.errorMessage() + ) + } + } +} + +@ExperimentalMaterial3Api +@Composable +fun AlertDialog( + onDismissRequest: () -> Unit, + onConfirmation: () -> Unit, + error: String, +) { + AlertDialog( + title = { Text(text = "Error") }, + text = { Text(text = error) }, + onDismissRequest = { onDismissRequest() }, + confirmButton = { + TextButton(onClick = { onConfirmation() }) { Text("Copy") } + }, + dismissButton = { + TextButton(onClick = { onDismissRequest() }) { Text("Dismiss") } + } + ) +} + +@Composable +fun ModelView( + navController: NavController, + modelState: AppViewModel.ModelState, + appViewModel: AppViewModel +) { + var isDeletingModel by rememberSaveable { mutableStateOf(false) } + Column( + verticalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .wrapContentHeight() + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(5.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight() + ) { + Text( + text = modelState.modelConfig.modelId, + textAlign = TextAlign.Left, + modifier = Modifier + .wrapContentHeight() + .weight(8f) + ) + Divider( + modifier = Modifier + .height(20.dp) + .width(1.dp) + ) + if (modelState.modelInitState.value == ModelInitState.Paused) { + IconButton( + onClick = { modelState.handleStart() }, modifier = Modifier + .aspectRatio(1f) + .weight(1f) + ) { + Icon( + imageVector = Icons.Outlined.Download, + contentDescription = "start downloading", + ) + } + + } else if (modelState.modelInitState.value == ModelInitState.Downloading) { + IconButton( + onClick = { modelState.handlePause() }, modifier = Modifier + .aspectRatio(1f) + .weight(1f) + ) { + Icon( + imageVector = Icons.Outlined.Pause, + contentDescription = "pause downloading", + ) + } + } else if (modelState.modelInitState.value == ModelInitState.Finished) { + IconButton( + onClick = { + modelState.startChat() + navController.navigate("chat") + }, + enabled = appViewModel.chatState.interruptable(), + modifier = Modifier + .aspectRatio(1f) + .weight(1f) + ) { + Icon( + imageVector = Icons.Outlined.Chat, + contentDescription = "start chatting", + ) + } + } else { + IconButton( + enabled = false, onClick = {}, modifier = Modifier + .aspectRatio(1f) + .weight(1f) + ) { + Icon( + imageVector = Icons.Outlined.Schedule, + contentDescription = "pending", + ) + } + } + if (modelState.modelInitState.value == ModelInitState.Downloading || + modelState.modelInitState.value == ModelInitState.Paused || + modelState.modelInitState.value == ModelInitState.Finished + ) { + IconButton( + onClick = { isDeletingModel = true }, + modifier = Modifier + .aspectRatio(1f) + .weight(1f) + ) { + Icon( + imageVector = Icons.Outlined.Delete, + contentDescription = "start downloading", + tint = MaterialTheme.colorScheme.error + ) + } + } + } + LinearProgressIndicator( + progress = modelState.progress.value.toFloat() / modelState.total.value, + modifier = Modifier.fillMaxWidth() + ) + if (isDeletingModel) { + Row( + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight() + ) { + TextButton(onClick = { isDeletingModel = false }) { + Text(text = "cancel") + } + TextButton(onClick = { + isDeletingModel = false + modelState.handleClear() + }) { + Text(text = "clear data", color = MaterialTheme.colorScheme.error) + } + TextButton(onClick = { + isDeletingModel = false + modelState.handleDelete() + }) { + Text(text = "delete model", color = MaterialTheme.colorScheme.error) + } + } + } + } +} + diff --git a/android/app/src/main/java/ai/mlc/mlcchat/ui/theme/Color.kt b/android/app/src/main/java/ai/mlc/mlcchat/ui/theme/Color.kt new file mode 100644 index 0000000..75a3557 --- /dev/null +++ b/android/app/src/main/java/ai/mlc/mlcchat/ui/theme/Color.kt @@ -0,0 +1,44 @@ +package ai.mlc.mlcchat.ui.theme + +import androidx.compose.ui.graphics.Color + +val Blue10 = Color(0xFF000F5E) +val Blue20 = Color(0xFF001E92) +val Blue30 = Color(0xFF002ECC) +val Blue40 = Color(0xFF1546F6) +val Blue80 = Color(0xFFB8C3FF) +val Blue90 = Color(0xFFDDE1FF) + +val DarkBlue10 = Color(0xFF00036B) +val DarkBlue20 = Color(0xFF000BA6) +val DarkBlue30 = Color(0xFF1026D3) +val DarkBlue40 = Color(0xFF3648EA) +val DarkBlue80 = Color(0xFFBBC2FF) +val DarkBlue90 = Color(0xFFDEE0FF) + +val Yellow10 = Color(0xFF261900) +val Yellow20 = Color(0xFF402D00) +val Yellow30 = Color(0xFF5C4200) +val Yellow40 = Color(0xFF7A5900) +val Yellow80 = Color(0xFFFABD1B) +val Yellow90 = Color(0xFFFFDE9C) + +val Red10 = Color(0xFF410001) +val Red20 = Color(0xFF680003) +val Red30 = Color(0xFF930006) +val Red40 = Color(0xFFBA1B1B) +val Red80 = Color(0xFFFFB4A9) +val Red90 = Color(0xFFFFDAD4) + +val Grey10 = Color(0xFF191C1D) +val Grey20 = Color(0xFF2D3132) +val Grey80 = Color(0xFFC4C7C7) +val Grey90 = Color(0xFFE0E3E3) +val Grey95 = Color(0xFFEFF1F1) +val Grey99 = Color(0xFFFBFDFD) + +val BlueGrey30 = Color(0xFF45464F) +val BlueGrey50 = Color(0xFF767680) +val BlueGrey60 = Color(0xFF90909A) +val BlueGrey80 = Color(0xFFC6C5D0) +val BlueGrey90 = Color(0xFFE2E1EC) \ No newline at end of file diff --git a/android/app/src/main/java/ai/mlc/mlcchat/ui/theme/Theme.kt b/android/app/src/main/java/ai/mlc/mlcchat/ui/theme/Theme.kt new file mode 100644 index 0000000..cbc6156 --- /dev/null +++ b/android/app/src/main/java/ai/mlc/mlcchat/ui/theme/Theme.kt @@ -0,0 +1,107 @@ +package ai.mlc.mlcchat.ui.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat + +private val DarkColorScheme = darkColorScheme( + primary = Blue80, + onPrimary = Blue20, + primaryContainer = Blue30, + onPrimaryContainer = Blue90, + inversePrimary = Blue40, + secondary = DarkBlue80, + onSecondary = DarkBlue20, + secondaryContainer = DarkBlue30, + onSecondaryContainer = DarkBlue90, + tertiary = Yellow80, + onTertiary = Yellow20, + tertiaryContainer = Yellow30, + onTertiaryContainer = Yellow90, + error = Red80, + onError = Red20, + errorContainer = Red30, + onErrorContainer = Red90, + background = Grey10, + onBackground = Grey90, + surface = Grey10, + onSurface = Grey80, + inverseSurface = Grey90, + inverseOnSurface = Grey20, + surfaceVariant = BlueGrey30, + onSurfaceVariant = BlueGrey80, + outline = BlueGrey60 +) + +private val LightColorScheme = lightColorScheme( + primary = Blue40, + onPrimary = Color.White, + primaryContainer = Blue90, + onPrimaryContainer = Blue10, + inversePrimary = Blue80, + secondary = DarkBlue40, + onSecondary = Color.White, + secondaryContainer = DarkBlue90, + onSecondaryContainer = DarkBlue10, + tertiary = Yellow40, + onTertiary = Color.White, + tertiaryContainer = Yellow90, + onTertiaryContainer = Yellow10, + error = Red40, + onError = Color.White, + errorContainer = Red90, + onErrorContainer = Red10, + background = Grey99, + onBackground = Grey10, + surface = Grey99, + onSurface = Grey10, + inverseSurface = Grey20, + inverseOnSurface = Grey95, + surfaceVariant = BlueGrey90, + onSurfaceVariant = BlueGrey30, + outline = BlueGrey50 +) + +@Composable +fun MLCChatTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + window.statusBarColor = colorScheme.primary.toArgb() + WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme + } + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/android/app/src/main/java/ai/mlc/mlcchat/ui/theme/Type.kt b/android/app/src/main/java/ai/mlc/mlcchat/ui/theme/Type.kt new file mode 100644 index 0000000..30e70c2 --- /dev/null +++ b/android/app/src/main/java/ai/mlc/mlcchat/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package ai.mlc.mlcchat.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/android/app/src/main/res/drawable/ic_android_black_24dp.xml b/android/app/src/main/res/drawable/ic_android_black_24dp.xml new file mode 100644 index 0000000..fe51230 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_android_black_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/android/app/src/main/res/drawable/mlc_logo_108.xml b/android/app/src/main/res/drawable/mlc_logo_108.xml new file mode 100644 index 0000000..d5307e0 --- /dev/null +++ b/android/app/src/main/res/drawable/mlc_logo_108.xml @@ -0,0 +1,11 @@ + + + diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..5a127eb --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + MLCChat++ + \ No newline at end of file diff --git a/android/app/src/main/res/values/themes.xml b/android/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..a16e9d4 --- /dev/null +++ b/android/app/src/main/res/values/themes.xml @@ -0,0 +1,6 @@ + + + + + + + + + + + + + + + + + + + diff --git a/docs/_static/img/project-structure.svg b/docs/_static/img/project-structure.svg new file mode 100644 index 0000000..e4ad7db --- /dev/null +++ b/docs/_static/img/project-structure.svg @@ -0,0 +1,1189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/community/faq.rst b/docs/community/faq.rst new file mode 100644 index 0000000..3913dd9 --- /dev/null +++ b/docs/community/faq.rst @@ -0,0 +1,16 @@ +.. _FAQ: + +Frequently Asked Questions +========================== + +This is a list of Frequently Asked Questions (FAQ) about the MLC-LLM. Feel free to suggest new entries! + +... How can I customize the temperature, and repetition penalty of models? + Please check our :doc:`/get_started/mlc_chat_config` tutorial. + +... What's the quantization algorithm MLC-LLM using? + Please check our :doc:`/compilation/configure_quantization` tutorial. + +... Why do I encounter an error ``free(): invalid pointer, Aborted (core dumped)`` at the end of model compilation? + This happens if you compiled TVM-Unity from source and didn't hide LLVM symbols in cmake configurations. + Please follow our instructions in :ref:`Building TVM Unity from Source ` tutorial to compile TVM-Unity which hides LLVM symbols, or use our pre-built MLC-LLM :doc:`pip wheels <../install/mlc_llm>`. diff --git a/docs/community/guideline.rst b/docs/community/guideline.rst new file mode 100644 index 0000000..33e8982 --- /dev/null +++ b/docs/community/guideline.rst @@ -0,0 +1,125 @@ +.. _community_guide: + +Community Guideline +=================== + +.. contents:: + :depth: 2 + :local: + +Welcome to the MLC-LLM community! Just like you, all of us are in awe of the immense power of large language models. +Our goal for MLC-LLM is to foster a project that is driven by an open-source community, working together to democratize +this technology and make it accessible across various devices. We are thrilled to have you as part of our +community and eagerly anticipate your valuable contributions. + + +.. _community_discussion: + +Participate in Community Discussions +------------------------------------ + +We encourage open discussions. If you encounter a bug or have a feature request, please file an issue in MLC-LLM's +GitHub `issue tracker `__. You are encouraged to tag the issue with labels +such as "bug," "feature request," or "iOS" so that the relevant developers can quickly notice your concern. + +Additionally, we have set up a `discord server `__ for online discussions. +While we encourage participation in the Discord server, we also recommend creating a GitHub issue even if the +topic has been discussed there. This ensures that the discussion is archived and searchable for future reference. + +Before submitting an issue, we kindly ask you to check our :doc:`/community/faq` to see if your question has already been answered. + +.. _contribute-to-mlc-llm: + +Contribute to MLC-LLM +--------------------- + +.. _fork-and-create-pull-requests: + +Fork and Create Pull Requests +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Ready to contribute to MLC-LLM? Awesome! We are excited to see you are ready to contribute your code. +The standard way to make changes to MLC-LLM code base is through creating a `pull-request `__, +and we will review your code and merge it to the code base when it is ready. + +The first step to becoming a developer is to `fork `__ the repository to your own +github account, you will notice a repository under ``https://github.com/username/mlc-llm`` where ``username`` is your github user name. + +You can clone your fork to your local machine and commit changes, or edit the contents of your fork (in the case you are just fixing typos) +on GitHub directly. Once your update is complete, you can click the ``contribute`` button and open a pull request to the main repository. + +.. _contribute-new-models: + +Contribute New Models to MLC-LLM +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* If you have compiled a model using our :doc:`/compilation/compile_models` tutorial for an existing model architecture, please upload your models to the internet (e.g., Hugging Face) by following :ref:`distribute-compiled-models` tutorial. Once you have done that, you can create a pull request to add an entry in the :doc:`/prebuilt_models` page. Additionally, you have the option to `create a speed report issue `__ to track the speed and memory consumption of your model. You don't need to test it on all devices; let the community collaborate on building it together! + +* If you add a new model variant to MLC-LLM by following our :doc:`/compilation/define_new_models` tutorial. + Please create a pull request to add your model architecture (currently model architectures are placed under + `relax_models `__ folder). + +.. _coding-styles: + +Coding Styles +^^^^^^^^^^^^^ + +For python codes, we generally follow the `PEP8 style guide `__. +The python comments follow `NumPy style `__ python docstrings. +To make things easy, you can use `black `__ to automatically format your python code. + +.. code:: bash + + pip install black + black your_python_file.py + +For C++ codes, we generally follow the `Google C++ style guide `__. +The C++ comments should be `Doxygen compatible `__. +Fo your convenience, you can use `clang-format `__ to automatically format your C++ code. + +.. code:: bash + + clang-format -i your_cpp_file.cpp + +.. _general-development-process: + +General Development Process +--------------------------- + +Everyone in the community is welcome to send patches, documents, and propose new directions to the project. +The key guideline here is to enable everyone in the community to get involved and participate in the decision and development. +We encourage public discussion in different channels, so that everyone in the community can participate +and get informed in developments. + +Code reviews are one of the key ways to ensure the quality of the code. High-quality code reviews prevent technical debt +for long-term and are crucial to the success of the project. A pull request needs to be reviewed before it gets merged. +A committer who has the expertise of the corresponding area would moderate the pull request and merge the code when +it is ready. The corresponding committer could request multiple reviewers who are familiar with the area of the code. +We encourage contributors to request code reviews themselves and help review each other's code -- remember everyone +is volunteering their time to the community, high-quality code review itself costs as much as the actual code +contribution, you could get your code quickly reviewed if you do others the same favor. + +The community should strive to reach a consensus on technical decisions through discussion. We expect committers to +moderate technical discussions in a diplomatic way, and provide suggestions with clear technical reasoning when necessary. + + +.. _roles-committers: + +Committers +^^^^^^^^^^ + +Committers are individuals who are granted with write access to the project. A committer is usually responsible for +a certain area or several areas of the code where they oversee the code review process. +The area of contribution can take all forms, including code contributions and code reviews, documents, education, and outreach. +The review of pull requests will be assigned to the committers who recently contribute to the area this PR belongs to. +Committers are essential for a high quality and healthy project. The community actively looks for new committers +from contributors. Each existing committer can nominate new committers to MLC projects. + +.. _roles-contributors: + +Contributors +^^^^^^^^^^^^ +We also welcome contributors if you are not ready to be a committer yet. Everyone who contributes to +the project (in the form of code, bugfix, documentation, tutorials, etc) is a contributor. +We maintain a `page `__ to acknowledge contributors, +please let us know if you contribute to the project and if your name is not included in the list. diff --git a/docs/compilation/compile_models.rst b/docs/compilation/compile_models.rst new file mode 100644 index 0000000..e9a3d63 --- /dev/null +++ b/docs/compilation/compile_models.rst @@ -0,0 +1,1056 @@ +.. _compile-model-libraries: + +Compile Model Libraries +======================= + +To run a model with MLC LLM in any platform, you need: + +1. **Model weights** converted to MLC format (e.g. `RedPajama-INCITE-Chat-3B-v1-MLC + `_.) +2. **Model library** that comprises the inference logic (see repo `binary-mlc-llm-libs `__). + +If you are simply adding a model variant, follow :ref:`convert-weights-via-MLC` suffices. + +This page describes how to compile a model library with MLC LLM. Model compilation optimizes +the model inference for a given platform, allowing users bring their own new model +architecture, use different quantization modes, and customize the overall model +optimization flow. + +We compile ``RedPajama-INCITE-Chat-3B-v1`` with ``q4f16_1`` as an example for all platforms. + +.. note:: + Before you proceed, make sure you followed :ref:`install-tvm-unity`, a required + backend to compile models with MLC LLM. + + Please also follow the instructions in :ref:`deploy-cli` / :ref:`deploy-python` to obtain + the CLI app / Python API that can be used to chat with the compiled model. + Finally, we strongly recommend you to read :ref:`project-overview` first to get + familiarized with the high-level terminologies. + +.. contents:: Table of Contents + :depth: 1 + :local: + +0. Verify Installation +---------------------- + +**Step 1. Verify mlc_chat** + +We use the python package ``mlc_chat`` to compile models. This can be installed by +following :ref:`install-mlc-packages`, either by building from source, or by +installing the prebuilt package. Verify ``mlc_chat`` installation in command line via: + +.. code:: bash + + $ mlc_chat --help + # You should see help information with this line + usage: MLC LLM Command Line Interface. [-h] {compile,convert_weight,gen_config} + +.. note:: + If it runs into error ``command not found: mlc_chat``, try ``python -m mlc_chat --help``. + +**Step 2. Verify TVM** + +To compile models, you also need to follow :ref:`install-tvm-unity`. +Here we verify ``tvm`` quickly with command line (for full verification, see :ref:`tvm-unity-validate`): + +.. code:: bash + + $ python -c "import tvm; print(tvm.__file__)" + /some-path/lib/python3.11/site-packages/tvm/__init__.py + +1. Clone from HF and convert_weight +----------------------------------- + +This replicates :ref:`convert-weights-via-MLC`, see that page for more details. + +You can be under the mlc-llm repo, or your own working directory. Note that all platforms +can share the same compiled/quantized weights. + +.. code:: shell + + # Create directory + mkdir -p dist/models && cd dist/models + # Clone HF weights + git lfs install + git clone https://huggingface.co/togethercomputer/RedPajama-INCITE-Chat-3B-v1 + cd ../.. + # Convert weight + mlc_chat convert_weight ./dist/models/RedPajama-INCITE-Chat-3B-v1/ \ + --quantization q4f16_1 \ + -o dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC + +2. Generate mlc-chat-config and compile +--------------------------------------- + +A model library is specified by: + + - The model architecture (e.g. ``llama-2``, ``gpt-neox``) + - Quantization (e.g. ``q4f16_1``, ``q0f32``) + - Metadata (e.g. ``context_window_size``, ``sliding_window_size``, ``prefill-chunk-size``), which affects memory planning + - Platform (e.g. ``cuda``, ``webgpu``, ``iOS``) + +All these knobs are specified in ``mlc-chat-config.json`` generated by ``gen_config``. + +.. code:: shell + + # Create output directory for the model library compiled + mkdir dist/libs + +.. tabs:: + + .. group-tab:: Linux - CUDA + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/RedPajama-INCITE-Chat-3B-v1/ \ + --quantization q4f16_1 --conv-template redpajama_chat \ + -o dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/mlc-chat-config.json \ + --device cuda -o dist/libs/RedPajama-INCITE-Chat-3B-v1-q4f16_1-cuda.so + + + .. group-tab:: Metal + + For M-chip Mac: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/RedPajama-INCITE-Chat-3B-v1/ \ + --quantization q4f16_1 --conv-template redpajama_chat \ + -o dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/mlc-chat-config.json \ + --device metal -o dist/libs/RedPajama-INCITE-Chat-3B-v1-q4f16_1-metal.so + + Cross-Compiling for Intel Mac on M-chip Mac: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/RedPajama-INCITE-Chat-3B-v1/ \ + --quantization q4f16_1 --conv-template redpajama_chat \ + -o dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/mlc-chat-config.json \ + --device metal:x86-64 -o dist/libs/RedPajama-INCITE-Chat-3B-v1-q4f16_1-metal_x86_64.dylib + + For Intel Mac: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/RedPajama-INCITE-Chat-3B-v1/ \ + --quantization q4f16_1 --conv-template redpajama_chat \ + -o dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/mlc-chat-config.json \ + --device metal -o dist/libs/RedPajama-INCITE-Chat-3B-v1-q4f16_1-metal_x86_64.dylib + + + .. group-tab:: Vulkan + + For Linux: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/RedPajama-INCITE-Chat-3B-v1/ \ + --quantization q4f16_1 --conv-template redpajama_chat \ + -o dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/mlc-chat-config.json \ + --device vulkan -o dist/libs/RedPajama-INCITE-Chat-3B-v1-q4f16_1-vulkan.so + + For Windows: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/RedPajama-INCITE-Chat-3B-v1/ \ + --quantization q4f16_1 --conv-template redpajama_chat \ + -o dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/mlc-chat-config.json \ + --device vulkan -o dist/libs/RedPajama-INCITE-Chat-3B-v1-q4f16_1-vulkan.dll + + .. group-tab:: iOS/iPadOS + + You need a Mac to compile models for it. + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/RedPajama-INCITE-Chat-3B-v1/ --quantization q4f16_1 \ + --conv-template redpajama_chat --context-window-size 768 \ + -o dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/mlc-chat-config.json \ + --device iphone -o dist/libs/RedPajama-INCITE-Chat-3B-v1-q4f16_1-iphone.tar + + .. note:: + If it runs into error + + .. code:: text + + Compilation error: + xcrun: error: unable to find utility "metal", not a developer tool or in PATH + xcrun: error: unable to find utility "metallib", not a developer tool or in PATH + + , please check and make sure you have Command Line Tools for Xcode installed correctly. + You can use ``xcrun metal`` to validate: when it prints ``metal: error: no input files``, it means the Command Line Tools for Xcode is installed and can be found, and you can proceed with the model compiling. + + .. group-tab:: Android + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/RedPajama-INCITE-Chat-3B-v1/ --quantization q4f16_1 \ + --conv-template redpajama_chat --context-window-size 768 \ + -o dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/mlc-chat-config.json \ + --device android -o dist/libs/RedPajama-INCITE-Chat-3B-v1-q4f16_1-android.tar + + .. group-tab:: WebGPU + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/RedPajama-INCITE-Chat-3B-v1/ \ + --quantization q4f16_1 --conv-template redpajama_chat \ + -o dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/mlc-chat-config.json \ + --device webgpu -o dist/libs/RedPajama-INCITE-Chat-3B-v1-q4f16_1-webgpu.wasm + + .. note:: + To compile for webgpu, you need to build from source when installing ``mlc_chat``. Besides, you also need to follow :ref:`install-web-build`. + Otherwise, it would run into error + + .. code:: text + + RuntimeError: Cannot find libraries: wasm_runtime.bc + + .. note:: + For webgpu, when compiling larger models like ``Llama-2-7B``, you may want to add ``--prefill_chunk_size 1024`` or lower ``context_window_size`` to decrease memory usage. + Otherwise, you may run into issues like: + + .. code:: text + + TypeError: Failed to execute 'createBuffer' on 'GPUDevice': Failed to read the 'size' property from + 'GPUBufferDescriptor': Value is outside the 'unsigned long long' value range. + +.. note:: + + For the ``conv-template``, `conv_template.cc `__ + contains a full list of conversation templates that MLC provides. If the model you are adding + requires a new conversation template, you would need to add your own. + Follow `this PR `__ as an example. + However, adding your own template would require you :ref:`build mlc_chat from source ` + in order for it to be recognized by the runtime. + + For more details, please see :ref:`configure-mlc-chat-json`. + +3. Verify output and chat +------------------------- + +By executing the compile command above, we generate the model weights, model lib, and a chat config. +We can check the output with the commands below: + +.. tabs:: + + .. group-tab:: Linux - CUDA + + .. code:: shell + + ~/mlc-llm > ls dist/libs + RedPajama-INCITE-Chat-3B-v1-q4f16_1-cuda.so # ===> the model library + + ~/mlc-llm > ls dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC + mlc-chat-config.json # ===> the chat config + ndarray-cache.json # ===> the model weight info + params_shard_0.bin # ===> the model weights + params_shard_1.bin + ... + tokenizer.json # ===> the tokenizer files + tokenizer_config.json + + We can now chat with the model using the command line interface (CLI) app or the Python API. + + .. code:: shell + + python + >>> from mlc_chat import ChatModule + >>> cm = ChatModule(model="./dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC", \ + model_lib_path="./dist/libs/RedPajama-INCITE-Chat-3B-v1-q4f16_1-cuda.so") + >>> cm.generate("hi") + 'Hi! How can I assist you today?' + + .. group-tab:: Metal + + .. code:: shell + + ~/mlc-llm > ls dist/libs + RedPajama-INCITE-Chat-3B-v1-q4f16_1-metal.so # ===> the model library (will be -metal_x86_64.dylib for Intel Mac) + + ~/mlc-llm > ls dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC + mlc-chat-config.json # ===> the chat config + ndarray-cache.json # ===> the model weight info + params_shard_0.bin # ===> the model weights + params_shard_1.bin + ... + tokenizer.json # ===> the tokenizer files + tokenizer_config.json + + We can now chat with the model using the command line interface (CLI) app or the Python API. + + .. code:: shell + + python + >>> from mlc_chat import ChatModule + >>> cm = ChatModule(model="./dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC", \ + model_lib_path="./dist/libs/RedPajama-INCITE-Chat-3B-v1-q4f16_1-metal.so") + >>> cm.generate("hi") + 'Hi! How can I assist you today?' + + + .. group-tab:: Vulkan + + .. code:: shell + + ~/mlc-llm > ls dist/libs + RedPajama-INCITE-Chat-3B-v1-q4f16_1-vulkan.so # ===> the model library (will be .dll for Windows) + + ~/mlc-llm > ls dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC + mlc-chat-config.json # ===> the chat config + ndarray-cache.json # ===> the model weight info + params_shard_0.bin # ===> the model weights + params_shard_1.bin + ... + tokenizer.json # ===> the tokenizer files + tokenizer_config.json + + We can now chat with the model using the command line interface (CLI) app or the Python API. + + .. code:: shell + + python + >>> from mlc_chat import ChatModule + >>> cm = ChatModule(model="./dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC", \ + model_lib_path="./dist/libs/RedPajama-INCITE-Chat-3B-v1-q4f16_1-vulkan.so", device="vulkan") + >>> cm.generate("hi") + 'Hi! How can I assist you today?' + + .. group-tab:: iOS/iPadOS + + .. code:: shell + + ~/mlc-llm > ls dist/libs + RedPajama-INCITE-Chat-3B-v1-q4f16_1-iphone.tar # ===> the model library + + ~/mlc-llm > ls dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC + mlc-chat-config.json # ===> the chat config + ndarray-cache.json # ===> the model weight info + params_shard_0.bin # ===> the model weights + params_shard_1.bin + ... + tokenizer.json # ===> the tokenizer files + tokenizer_config.json + + The model lib ``dist/libs/RedPajama-INCITE-Chat-3B-v1-q4f16_1-iphone.tar`` + will be packaged as a static library into the iOS app. Checkout :ref:`deploy-ios` for more details. + + .. group-tab:: Android + + .. code:: shell + + ~/mlc-llm > ls dist/libs + RedPajama-INCITE-Chat-3B-v1-q4f16_1-android.tar # ===> the model library + + ~/mlc-llm > ls dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC + mlc-chat-config.json # ===> the chat config + ndarray-cache.json # ===> the model weight info + params_shard_0.bin # ===> the model weights + params_shard_1.bin + ... + tokenizer.json # ===> the tokenizer files + tokenizer_config.json + + The model lib ``dist/libs/RedPajama-INCITE-Chat-3B-v1-q4f16_1-android.tar`` + will be packaged as a static library into the android app. Checkout :ref:`deploy-android` for more details. + + .. group-tab:: WebGPU + + .. code:: shell + + ~/mlc-llm > ls dist/libs + RedPajama-INCITE-Chat-3B-v1-q4f16_1-webgpu.wasm # ===> the model library + + ~/mlc-llm > ls dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC + mlc-chat-config.json # ===> the chat config + ndarray-cache.json # ===> the model weight info + params_shard_0.bin # ===> the model weights + params_shard_1.bin + ... + tokenizer.json # ===> the tokenizer files + tokenizer_config.json + + To use this in WebGPU runtime, checkout :ref:`webllm-runtime`. + +Compile Commands for More Models +-------------------------------- + +This section lists compile commands for more models that you can try out. Note that this can be easily +generalized to any model variant, as long as mlc-llm supports the architecture. + +.. tabs:: + + .. tab:: Model: Llama-2-7B + + Please `request for access `_ to the Llama-2 weights from Meta first. + After granted access, first create directory ``dist/models`` and download the model to the directory. + For example, you can run the following code: + + .. code:: shell + + mkdir -p dist/models && cd dist/models + git lfs install + git clone https://huggingface.co/meta-llama/Llama-2-7b-chat-hf + cd ../.. + + Then convert the HF weights into MLC-compatible weights. Note that all platforms + can share the same compiled/quantized weights. + + .. code:: shell + + mlc_chat convert_weight ./dist/models/Llama-2-7b-chat-hf/ --quantization q4f16_1 -o dist/Llama-2-7b-chat-hf-q4f16_1-MLC + + Afterwards, run the following command to generate mlc config and compile the model. + + .. code:: shell + + # Create output directory for the model library compiled + mkdir dist/libs + + .. tabs:: + + .. tab:: Target: CUDA + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/Llama-2-7b-chat-hf/ --quantization q4f16_1 \ + --conv-template llama-2 -o dist/Llama-2-7b-chat-hf-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/Llama-2-7b-chat-hf-q4f16_1-MLC/mlc-chat-config.json \ + --device cuda -o dist/libs/Llama-2-7b-chat-hf-q4f16_1-cuda.so + + .. tab:: Metal + + For M-chip Mac: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/Llama-2-7b-chat-hf/ --quantization q4f16_1 \ + --conv-template llama-2 -o dist/Llama-2-7b-chat-hf-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/Llama-2-7b-chat-hf-q4f16_1-MLC/mlc-chat-config.json \ + --device metal -o dist/libs/Llama-2-7b-chat-hf-q4f16_1-metal.so + + Cross-Compiling for Intel Mac on M-chip Mac: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/RedPajama-INCITE-Chat-3B-v1/ \ + --quantization q4f16_1 --conv-template redpajama_chat \ + -o dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/mlc-chat-config.json \ + --device metal:x86-64 -o dist/libs/RedPajama-INCITE-Chat-3B-v1-q4f16_1-metal_x86_64.dylib + + For Intel Mac: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/Llama-2-7b-chat-hf/ --quantization q4f16_1 \ + --conv-template llama-2 -o dist/Llama-2-7b-chat-hf-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/Llama-2-7b-chat-hf-q4f16_1-MLC/mlc-chat-config.json \ + --device metal -o dist/libs/Llama-2-7b-chat-hf-q4f16_1-metal_x86_64.dylib + + .. tab:: Vulkan + + For Linux: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/Llama-2-7b-chat-hf/ --quantization q4f16_1 \ + --conv-template llama-2 -o dist/Llama-2-7b-chat-hf-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/Llama-2-7b-chat-hf-q4f16_1-MLC/mlc-chat-config.json \ + --device vulkan -o dist/libs/Llama-2-7b-chat-hf-q4f16_1-vulkan.so + + For Windows: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/Llama-2-7b-chat-hf/ --quantization q4f16_1 \ + --conv-template llama-2 -o dist/Llama-2-7b-chat-hf-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/Llama-2-7b-chat-hf-q4f16_1-MLC/mlc-chat-config.json \ + --device vulkan -o dist/libs/Llama-2-7b-chat-hf-q4f16_1-vulkan.dll + + .. tab:: WebGPU + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/Llama-2-7b-chat-hf/ --quantization q4f16_1 \ + --context-window-size 2048 --conv-template llama-2 -o dist/Llama-2-7b-chat-hf-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/Llama-2-7b-chat-hf-q4f16_1-MLC/mlc-chat-config.json \ + --device webgpu -o dist/libs/Llama-2-7b-chat-hf-q4f16_1-webgpu.wasm + + .. note:: + To compile for webgpu, you need to build from source when installing ``mlc_chat``. Besides, you also need to follow :ref:`install-web-build`. + Otherwise, it would run into error + + .. code:: text + + RuntimeError: Cannot find libraries: wasm_runtime.bc + + .. tab:: iPhone/iPad + + You need a Mac to compile models for it. + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/Llama-2-7b-chat-hf/ --quantization q4f16_1 \ + --conv-template llama-2 --context-window-size 768 -o dist/Llama-2-7b-chat-hf-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/Llama-2-7b-chat-hf-q4f16_1-MLC/mlc-chat-config.json \ + --device iphone -o dist/libs/Llama-2-7b-chat-hf-q4f16_1-iphone.tar + + .. tab:: Android + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/Llama-2-7b-chat-hf/ --quantization q4f16_1 \ + --conv-template llama-2 --context-window-size 768 -o dist/Llama-2-7b-chat-hf-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/Llama-2-7b-chat-hf-q4f16_1-MLC/mlc-chat-config.json \ + --device android -o dist/libs/Llama-2-7b-chat-hf-q4f16_1-android.tar + + .. tab:: Mistral-7B-Instruct-v0.2 + + Note that Mistral uses sliding window attention (SWA). Thus, instead of specifying + ``context-window-size``, we specify ``sliding-window-size``. + + First create directory ``dist/models`` and download the model to the directory. + For example, you can run the following code: + + .. code:: shell + + mkdir -p dist/models && cd dist/models + git lfs install + git clone https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.2 + cd ../.. + + Then convert the HF weights into MLC-compatible weights. Note that all platforms + can share the same compiled/quantized weights. + + .. code:: shell + + mlc_chat convert_weight ./dist/models/Mistral-7B-Instruct-v0.2/ --quantization q4f16_1 \ + -o dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC + + Afterwards, run the following command to generate mlc config and compile the model. + + .. code:: shell + + # Create output directory for the model library compiled + mkdir dist/libs + + .. tabs:: + + .. tab:: Target: CUDA + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/Mistral-7B-Instruct-v0.2/ --quantization q4f16_1 \ + --conv-template mistral_default -o dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/mlc-chat-config.json \ + --device cuda -o dist/libs/Mistral-7B-Instruct-v0.2-q4f16_1-cuda.so + + .. tab:: Metal + + For M-chip Mac: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/Mistral-7B-Instruct-v0.2/ --quantization q4f16_1 \ + --conv-template mistral_default -o dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/mlc-chat-config.json \ + --device metal -o dist/libs/Mistral-7B-Instruct-v0.2-q4f16_1-metal.so + + + For Intel Mac: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/Mistral-7B-Instruct-v0.2/ --quantization q4f16_1 \ + --conv-template mistral_default -o dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/mlc-chat-config.json \ + --device metal -o dist/libs/Mistral-7B-Instruct-v0.2-q4f16_1-metal_x86_64.dylib + + .. tab:: Vulkan + + For Linux: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/Mistral-7B-Instruct-v0.2/ --quantization q4f16_1 \ + --conv-template mistral_default -o dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/mlc-chat-config.json \ + --device vulkan -o dist/libs/Mistral-7B-Instruct-v0.2-q4f16_1-vulkan.so + + For Windows: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/Mistral-7B-Instruct-v0.2/ --quantization q4f16_1 \ + --conv-template mistral_default -o dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/mlc-chat-config.json \ + --device vulkan -o dist/libs/Mistral-7B-Instruct-v0.2-q4f16_1-vulkan.dll + + .. tab:: WebGPU + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/Mistral-7B-Instruct-v0.2/ --quantization q4f16_1 \ + --prefill-chunk-size 1024 --conv-template mistral_default \ + -o dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/mlc-chat-config.json \ + --device webgpu -o dist/libs/Mistral-7B-Instruct-v0.2-q4f16_1-webgpu.wasm + + .. note:: + To compile for webgpu, you need to build from source when installing ``mlc_chat``. Besides, you also need to follow :ref:`install-web-build`. + Otherwise, it would run into error + + .. code:: text + + RuntimeError: Cannot find libraries: wasm_runtime.bc + + .. note:: + For webgpu, when compiling larger models like ``Llama-2-7B``, you may want to add ``--prefill_chunk_size 1024`` or lower ``context_window_size`` to decrease memory usage. + Otherwise, you may run into issues like: + + .. code:: text + + TypeError: Failed to execute 'createBuffer' on 'GPUDevice': Failed to read the 'size' property from + 'GPUBufferDescriptor': Value is outside the 'unsigned long long' value range. + + .. tab:: iPhone/iPad + + You need a Mac to compile models for it. + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/Mistral-7B-Instruct-v0.2/ --quantization q4f16_1 \ + --conv-template mistral_default --sliding-window-size 1024 --prefill-chunk-size 128 \ + -o dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/mlc-chat-config.json \ + --device iphone -o dist/libs/Mistral-7B-Instruct-v0.2-q4f16_1-iphone.tar + + .. tab:: Android + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/Mistral-7B-Instruct-v0.2/ --quantization q4f16_1 \ + --conv-template mistral_default --sliding-window-size 1024 --prefill-chunk-size 128 -o dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/mlc-chat-config.json \ + --device android -o dist/libs/Mistral-7B-Instruct-v0.2-q4f16_1-android.tar + + .. tab:: Other models + + First create directory ``dist/models`` and download the model to the directory. + For example, you can run the following code: + + .. code:: shell + + mkdir -p dist/models && cd dist/models + git lfs install + git clone https://huggingface.co/DISTRIBUTOR/HF_MODEL + cd ../.. + + Then convert the HF weights into MLC-compatible weights. Note that all platforms + can share the same compiled/quantized weights. + + .. code:: shell + + mlc_chat convert_weight ./dist/models/HF_MODEL/ --quantization q4f16_1 -o dist/OUTPUT-MLC + + Afterwards, run the following command to generate mlc config and compile the model. + + .. code:: shell + + # Create output directory for the model library compiled + mkdir dist/libs + + .. tabs:: + + .. tab:: Target: CUDA + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/HF_MODEL/ --quantization q4f16_1 --conv-template CONV_TEMPLATE -o dist/OUTPUT-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/OUTPUT-MLC/mlc-chat-config.json --device cuda -o dist/libs/OUTPUT-cuda.so + + .. tab:: Metal + + For M-chip Mac: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/HF_MODEL/ --quantization q4f16_1 --conv-template CONV_TEMPLATE -o dist/OUTPUT-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/OUTPUT-MLC/mlc-chat-config.json --device metal -o dist/libs/OUTPUT-metal.so + + + For Intel Mac: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/HF_MODEL/ --quantization q4f16_1 --conv-template CONV_TEMPLATE -o dist/OUTPUT-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/OUTPUT-MLC/mlc-chat-config.json --device metal -o dist/libs/OUTPUT-metal_x86_64.dylib + + .. tab:: Vulkan + + For Linux: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/HF_MODEL/ --quantization q4f16_1 --conv-template CONV_TEMPLATE -o dist/OUTPUT-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/OUTPUT-MLC/mlc-chat-config.json --device vulkan -o dist/libs/OUTPUT-vulkan.so + + For Windows: + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/HF_MODEL/ --quantization q4f16_1 --conv-template CONV_TEMPLATE -o dist/OUTPUT-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/OUTPUT-MLC/mlc-chat-config.json --device vulkan -o dist/libs/OUTPUT-vulkan.dll + + .. tab:: WebGPU + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/HF_MODEL/ --quantization q4f16_1 --conv-template CONV_TEMPLATE -o dist/OUTPUT-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/OUTPUT-MLC/mlc-chat-config.json --device webgpu -o dist/libs/OUTPUT-webgpu.wasm + + .. note:: + To compile for webgpu, you need to build from source when installing ``mlc_chat``. Besides, you also need to follow :ref:`install-web-build`. + Otherwise, it would run into error + + .. code:: text + + RuntimeError: Cannot find libraries: wasm_runtime.bc + + .. note:: + For webgpu, when compiling larger models like ``Llama-2-7B``, you may want to add ``--prefill_chunk_size 1024`` or lower ``context_window_size`` to decrease memory usage. + Otherwise, you may run into issues like: + + .. code:: text + + TypeError: Failed to execute 'createBuffer' on 'GPUDevice': Failed to read the 'size' property from + 'GPUBufferDescriptor': Value is outside the 'unsigned long long' value range. + + .. tab:: iPhone/iPad + + You need a Mac to compile models for it. + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/HF_MODEL/ --quantization q4f16_1 --conv-template CONV_TEMPLATE \ + --context-window-size 768 -o dist/OUTPUT-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/OUTPUT-MLC/mlc-chat-config.json --device iphone -o dist/libs/OUTPUT-iphone.tar + + .. tab:: Android + + .. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/HF_MODEL/ --quantization q4f16_1 --conv-template CONV_TEMPLATE \ + --context-window-size 768 -o dist/OUTPUT-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/OUTPUT-MLC/mlc-chat-config.json --device android -o dist/libs/OUTPUT-android.tar + +For each model and each backend, the above only provides the most recommended build command (which is the most optimized). +You can also try with different argument values (e.g., different quantization modes, context window size, etc.), +whose build results affect runtime memory requirement, and it is possible that they may not run as +fast and robustly as the provided one when running the model. + +.. note:: + Uing 3-bit quantization usually can be overly aggressive and only works for limited settings. + If you encounter issues where the compiled model does not perform as expected, + consider utilizing a higher number of bits for quantization (e.g., 4-bit quantization). + +If you are interested in distributing the model besides local execution, please checkout :ref:`distribute-compiled-models`. + + +.. _compile-command-specification: + +Compile Command Specification +----------------------------- + +As you have seen in the section above, the model compilation is split into three steps: convert weights, generate +``mlc-chat-config.json``, and compile the model. This section describes the list of options that can be used +during compilation. + +1. Convert Weight +^^^^^^^^^^^^^^^^^ + +Weight conversion command follows the pattern below: + +.. code:: text + + mlc_chat convert_weight \ + CONFIG \ + --quantization QUANTIZATION_MODE \ + [--model-type MODEL_TYPE] \ + [--device DEVICE] \ + [--source SOURCE] \ + [--source-format SOURCE_FORMAT] \ + --output OUTPUT + +Note that ``CONFIG`` is a positional argument. Arguments wrapped with ``[ ]`` are optional. + +--CONFIG It can be one of the following: + + 1. Path to a HuggingFace model directory that contains a ``config.json`` or + 2. Path to ``config.json`` in HuggingFace format, or + 3. The name of a pre-defined model architecture. + + A ``config.json`` file in HuggingFace format defines the model architecture, including the vocabulary + size, the number of layers, the hidden size, number of attention heads, etc. + Example: https://huggingface.co/codellama/CodeLlama-7b-hf/blob/main/config.json. + + A HuggingFace directory often contains a ``config.json`` which defines the model architecture, + the non-quantized model weights in PyTorch or SafeTensor format, tokenizer configurations, + as well as an optional ``generation_config.json`` provides additional default configuration for + text generation. + Example: https://huggingface.co/codellama/CodeLlama-7b-hf/tree/main. + + For existing pre-defined model architecture, see ``MODEL_PRESETS`` + `here `_. + +--quantization QUANTIZATION_MODE The quantization mode we use to compile. + + See :ref:`quantization_mode` for more information. + Available options are: ``q0f16``, ``q0f32``, ``q3f16_1``, ``q4f16_1``, ``q4f32_1``, and + ``q4f16_awq``. + + We encourage you to use 4-bit quantization, as the text generated by 3-bit + quantized models may have bad quality depending on the model. + +--model-type MODEL_TYPE Model architecture such as "llama". If not set, it is inferred from ``config.json``. + +--device DEVICE The device used to do quantization such as "cuda" or "cuda:0". Will detect from + local available GPUs if not specified. + +--source SOURCE The path to original model weight, infer from ``config`` if missing. + +--source-format SOURCE_FORMAT The format of source model weight, infer from ``config`` if missing. + +--output OUTPUT The output directory to save the quantized model weight. + Will create ``params_shard_*.bin`` and ```ndarray-cache.json``` in this directory. + +2. Generate MLC Chat Config +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In order to compile a model, we first need to generate the ``mlc-chat-config.json``. This file contains specifications +like ``context-window-size`` and ``sliding-window-size``, among others that can alter the model compiled. We also process +tokenizers in this step. + +Config generation command follows the pattern below: + +.. code:: text + + mlc_chat gen_config \ + CONFIG \ + --quantization QUANTIZATION_MODE \ + [--model-type MODEL_TYPE] \ + --conv-template CONV_TEMPLATE \ + [--context-window-size CONTEXT_WINDOW_SIZE] \ + [--sliding-window-size SLIDING_WINDOW_SIZE] \ + [--prefill-chunk-size PREFILL_CHUNK_SIZE] \ + [--tensor-parallel-shard TENSOR_PARALLEL_SHARDS] \ + --output OUTPUT + +Note that ``CONFIG`` is a positional argument. Arguments wrapped with ``[ ]`` are optional. + +--CONFIG It can be one of the following: + + 1. Path to a HuggingFace model directory that contains a ``config.json`` or + 2. Path to ``config.json`` in HuggingFace format, or + 3. The name of a pre-defined model architecture. + + A ``config.json`` file in HuggingFace format defines the model architecture, including the vocabulary + size, the number of layers, the hidden size, number of attention heads, etc. + Example: https://huggingface.co/codellama/CodeLlama-7b-hf/blob/main/config.json. + + A HuggingFace directory often contains a ``config.json`` which defines the model architecture, + the non-quantized model weights in PyTorch or SafeTensor format, tokenizer configurations, + as well as an optional ``generation_config.json`` provides additional default configuration for + text generation. + Example: https://huggingface.co/codellama/CodeLlama-7b-hf/tree/main. + + For existing pre-defined model architecture, see ``MODEL_PRESETS`` + `here `_. + +--quantization QUANTIZATION_MODE The quantization mode we use to compile. + + See :ref:`quantization_mode` for more information. + Available options are: ``q0f16``, ``q0f32``, ``q3f16_1``, ``q4f16_1``, ``q4f32_1``, and + ``q4f16_awq``. + + We encourage you to use 4-bit quantization, as the text generated by 3-bit + quantized models may have bad quality depending on the model. + +--model-type MODEL_TYPE Model architecture such as "llama". If not set, it is inferred from ``config.json``. + +--conv-template CONV_TEMPLATE Conversation template. It depends on how the model is tuned. Use "LM" for vanilla base model + For existing pre-defined templates, see ``CONV_TEMPLATES`` + `here `_. + +--context-window-size CONTEXT_WINDOW_SIZE Option to provide the maximum sequence length supported by the model. + This is usually explicitly shown as context length or context window in the model card. + If this option is not set explicitly, by default, + it will be determined by ``context_window_size`` or ``max_position_embeddings`` in ``config.json``, + and the latter is usually inaccurate for some models. + +--sliding-window-size SLIDING_WINDOW (Experimental) The sliding window size in sliding window attention (SWA). + This optional field overrides the ``sliding_window`` in ``config.json`` for + those models that use SWA. Currently only useful when compiling mistral-based models. + This flag subjects to future refactoring. + +--prefill-chunk-size PREFILL_CHUNK_SIZE (Experimental) The chunk size during prefilling. By default, + the chunk size is the same as ``context_window_size`` or ``sliding_window_size``. + This flag subjects to future refactoring. + +--tensor-parallel-shard TENSOR_PARALLEL_SHARDS Number of shards to split the model into in tensor parallelism multi-gpu inference. + +--output OUTPUT The output directory for generated configurations, including `mlc-chat-config.json` and tokenizer configuration. + +3. Compile Model Library +^^^^^^^^^^^^^^^^^^^^^^^^ + +After generating ``mlc-chat-config.json``, we can compile the model into a model library (files ending in ``.so``, ``.tar``, etc. that contains +the inference logic of a model). + +Model compilation command follows the pattern below: + +.. code:: text + + mlc_chat compile \ + MODEL \ + [--quantization QUANTIZATION_MODE] \ + [--model-type MODEL_TYPE] \ + [--device DEVICE] \ + [--host HOST] \ + [--opt OPT] \ + [--system-lib-prefix SYSTEM_LIB_PREFIX] \ + --output OUTPUT \ + [--overrides OVERRIDES] + +Note that ``MODEL`` is a positional argument. Arguments wrapped with ``[ ]`` are optional. + +--MODEL A path to ``mlc-chat-config.json``, or an MLC model directory that contains ``mlc-chat-config.json``. + +--quantization QUANTIZATION_MODE The quantization mode we use to compile. If unprovided, will infer from ``MODEL``. + + See :ref:`quantization_mode` for more information. + Available options are: ``q0f16``, ``q0f32``, ``q3f16_1``, ``q4f16_1``, ``q4f32_1``, and + ``q4f16_awq``. + + We encourage you to use 4-bit quantization, as the text generated by 3-bit + quantized models may have bad quality depending on the model. + +--model-type MODEL_TYPE Model architecture such as "llama". If not set, it is inferred from ``mlc-chat-config.json``. + +--device DEVICE The GPU device to compile the model to. If not set, it is inferred from GPUs available locally. + +--host HOST The host LLVM triple to compile the model to. If not set, it is inferred from the local CPU and OS. + Examples of the LLVM triple: + + 1) iPhones: arm64-apple-ios; + 2) ARM64 Android phones: aarch64-linux-android; + 3) WebAssembly: wasm32-unknown-unknown-wasm; + 4) Windows: x86_64-pc-windows-msvc; + 5) ARM macOS: arm64-apple-darwin. + +--opt OPT Optimization flags. MLC LLM maintains a predefined set of optimization flags, + denoted as ``O0``, ``O1``, ``O2``, ``O3``, where ``O0`` means no optimization, ``O2`` + means majority of them, and ``O3`` represents extreme optimization that could + potentially break the system. + + Meanwhile, optimization flags could be explicitly specified via details knobs, e.g. + ``--opt="cutlass_attn=1;cutlass_norm=0;cublas_gemm=0;cudagraph=0"``. + +--system-lib-prefix SYSTEM_LIB_PREFIX Adding a prefix to all symbols exported. Similar to ``objcopy --prefix-symbols``. + This is useful when compiling multiple models into a single library to avoid symbol + conflicts. Different from objcopy, this takes no effect for shared library. + + +--output OUTPUT The path to the output file. The suffix determines if the output file is a shared library or + objects. Available suffixes: + + 1) Linux: .so (shared), .tar (objects); + 2) macOS: .dylib (shared), .tar (objects); + 3) Windows: .dll (shared), .tar (objects); + 4) Android, iOS: .tar (objects); + 5) Web: .wasm (web assembly). + +--overrides OVERRIDES Model configuration override. Configurations to override ``mlc-chat-config.json``. Supports + ``context_window_size``, ``prefill_chunk_size``, ``sliding_window``, ``max_batch_size`` and + ``tensor_parallel_shards``. Meanwhile, model config could be explicitly specified via details + knobs, e.g. ``--overrides "context_window_size=1024;prefill_chunk_size=128"``. diff --git a/docs/compilation/configure_quantization.rst b/docs/compilation/configure_quantization.rst new file mode 100644 index 0000000..d66f841 --- /dev/null +++ b/docs/compilation/configure_quantization.rst @@ -0,0 +1,22 @@ +🚧 Configure Quantization +========================= + +Quantization Algorithm +---------------------- + +The default quantization algorithm used in MLC-LLM is grouping quantization method discussed in the papers `The case for 4-bit precision: k-bit Inference Scaling Laws `__ and `LUT-GEMM: Quantized Matrix Multiplication based on LUTs for Efficient Inference in Large-Scale Generative Language Models `__. + +.. _quantization_mode: + +Quantization Mode +----------------- + +In MLC-LLM we use a short code that indicates the quantization mode to use. + +The format of the code is ``qAfB(_id)``, where ``A`` represents the number +of bits for storing weights and ``B`` represents the number of bits for storing activations. +The ``_id`` is an integer identifier to distinguish different quantization algorithms (e.g. symmetric, non-symmetric, AWQ, etc). + +Currently, available options are: ``q0f16``, ``q0f32``, ``q3f16_1``, ``q4f16_1``, ``q4f32_1``, and ``q4f16_awq`` (not stable). + +More details to come. \ No newline at end of file diff --git a/docs/compilation/convert_weights.rst b/docs/compilation/convert_weights.rst new file mode 100644 index 0000000..6b39cf8 --- /dev/null +++ b/docs/compilation/convert_weights.rst @@ -0,0 +1,183 @@ +.. _convert-weights-via-MLC: + +Convert Weights via MLC +======================= + +To run a model with MLC LLM in any platform, you need: + +1. **Model weights** converted to MLC format (e.g. `RedPajama-INCITE-Chat-3B-v1-MLC + `_.) +2. **Model library** that comprises the inference logic (see repo `binary-mlc-llm-libs `__). + +In many cases, we only need to convert weights and reuse existing model library. +This page demonstrates adding a model variant with ``mlc_chat convert_weight``, which +takes a hugginface model as input and converts/quantizes into MLC-compatible weights. + +Specifically, we add RedPjama-INCITE-**Instruct**-3B-v1, while MLC already +provides a model library for RedPjama-INCITE-**Chat**-3B-v1, which we can reuse. + +This can be extended to, e.g.: + +- Add ``OpenHermes-Mistral`` when MLC already supports Mistral +- Add ``Llama-2-uncensored`` when MLC already supports Llama-2 + +.. note:: + Before you proceed, make sure you followed :ref:`install-tvm-unity`, a required + backend to compile models with MLC LLM. + + Please also follow the instructions in :ref:`deploy-cli` / :ref:`deploy-python` to obtain + the CLI app / Python API that can be used to chat with the compiled model. + Finally, we strongly recommend you to read :ref:`project-overview` first to get + familiarized with the high-level terminologies. + +.. contents:: Table of Contents + :depth: 1 + :local: + +.. _verify_installation_for_compile: + +0. Verify installation +---------------------- + +**Step 1. Verify mlc_chat** + +We use the python package ``mlc_chat`` to compile models. This can be installed by +following :ref:`install-mlc-packages`, either by building from source, or by +installing the prebuilt package. Verify ``mlc_chat`` installation in command line via: + +.. code:: bash + + $ mlc_chat --help + # You should see help information with this line + usage: MLC LLM Command Line Interface. [-h] {compile,convert_weight,gen_config} + +.. note:: + If it runs into error ``command not found: mlc_chat``, try ``python -m mlc_chat --help``. + +**Step 2. Verify TVM** + +To compile models, you also need to follow :ref:`install-tvm-unity`. +Here we verify ``tvm`` quickly with command line (for full verification, see :ref:`tvm-unity-validate`): + +.. code:: bash + + $ python -c "import tvm; print(tvm.__file__)" + /some-path/lib/python3.11/site-packages/tvm/__init__.py + + +1. Clone from HF and convert_weight +----------------------------------- + +You can be under the mlc-llm repo, or your own working directory. Note that all platforms +can share the same compiled/quantized weights. See :ref:`compile-command-specification` +for specification of ``convert_weight``. + +.. code:: shell + + # Create directory + mkdir -p dist/models && cd dist/models + # Clone HF weights + git lfs install + git clone https://huggingface.co/togethercomputer/RedPajama-INCITE-Instruct-3B-v1 + cd ../.. + # Convert weight + mlc_chat convert_weight ./dist/models/RedPajama-INCITE-Instruct-3B-v1/ \ + --quantization q4f16_1 \ + -o dist/RedPajama-INCITE-Instruct-3B-v1-q4f16_1-MLC + +.. _generate_mlc_chat_config: + +2. Generate MLC Chat Config +--------------------------- + +Use ``mlc_chat gen_config`` to generate ``mlc-chat-config.json`` and process tokenizers. +See :ref:`compile-command-specification` for specification of ``gen_config``. + +.. code:: shell + + mlc_chat gen_config ./dist/models/RedPajama-INCITE-Instruct-3B-v1/ \ + --quantization q4f16_1 --conv-template redpajama_chat \ + -o dist/RedPajama-INCITE-Instruct-3B-v1-q4f16_1-MLC/ + + +.. note:: + The file ``mlc-chat-config.json`` is crucial in both model compilation + and runtime chatting. Here we only care about the latter case. + + You can **optionally** customize + ``dist/RedPajama-INCITE-Instruct-3B-v1-q4f16_1-MLC/mlc-chat-config.json`` (checkout :ref:`configure-mlc-chat-json` for more detailed instructions). + You can also simply use the default configuration. + + `conv_template.cc `__ + contains a full list of conversation templates that MLC provides. If the model you are adding + requires a new conversation template, you would need to add your own. + Follow `this PR `__ as an example. However, + adding your own template would require you :ref:`build mlc_chat from source ` in order for it + to be recognized by the runtime. + +By now, you should have the following files. + +.. code:: shell + + ~/mlc-llm > ls dist/RedPajama-INCITE-Instruct-3B-v1-q4f16_1-MLC + mlc-chat-config.json # ===> the chat config + ndarray-cache.json # ===> the model weight info + params_shard_0.bin # ===> the model weights + params_shard_1.bin + ... + tokenizer.json # ===> the tokenizer files + tokenizer_config.json + +.. _distribute-compiled-models: + +(Optional) 3. Upload weights to HF +---------------------------------- + +Optionally, you can upload what we have to huggingface. + +.. code:: shell + + # First, please create a repository on Hugging Face. + # With the repository created, run + git lfs install + git clone https://huggingface.co/my-huggingface-account/my-redpajama3b-weight-huggingface-repo + cd my-redpajama3b-weight-huggingface-repo + cp path/to/mlc-llm/dist/RedPajama-INCITE-Instruct-3B-v1-q4f16_1-MLC/* . + git add . && git commit -m "Add redpajama-3b instruct model weights" + git push origin main + +This would result in something like `RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC +`_, but +for **Instruct** instead of **Chat**. + +Good job, you have successfully distributed the model you compiled. +Next, we will talk about how we can consume the model weights in applications. + +Download the Distributed Models and Run in Python +------------------------------------------------- + +Running the distributed models are similar to running prebuilt model weights and libraries in :ref:`Model Prebuilts`. + +.. code:: shell + + # Clone prebuilt libs so we can reuse them: + mkdir -p dist/ + git clone https://github.com/mlc-ai/binary-mlc-llm-libs.git dist/prebuilt_libs + + # Or download the model library (only needed if we do not reuse the model lib): + cd dist/prebuilt_libs + wget url-to-my-model-lib + cd ../.. + + # Download the model weights + cd dist + git clone https://huggingface.co/my-huggingface-account/my-redpajama3b-weight-huggingface-repo RedPajama-INCITE-Instruct-3B-v1-q4f16_1-MLC + cd .. + + # Run the model in Python; note that we reuse `-Chat` model library + python + >>> from mlc_chat import ChatModule + >>> cm = ChatModule(model="dist/RedPajama-INCITE-Instruct-3B-v1-q4f16_1-MLC", \ + model_lib_path="dist/prebuilt_libs/RedPajama-INCITE-Chat-3B-v1-q4f16_1-cuda.so") # Adjust based on backend + >>> cm.generate("hi") + 'Hi! How can I assist you today?' diff --git a/docs/compilation/define_new_models.rst b/docs/compilation/define_new_models.rst new file mode 100644 index 0000000..4c73864 --- /dev/null +++ b/docs/compilation/define_new_models.rst @@ -0,0 +1,25 @@ +Define New Model Architectures +============================== + +This page guides you how to add a new model architecture in MLC. + +This notebook (runnable in Colab) should contain all necessary information to add a model in +MLC LLM: +https://github.com/mlc-ai/notebooks/blob/main/mlc-llm/tutorial_add_new_model_architecture_in_tvm_nn_module.ipynb + +In the notebook, we leverage ``tvm.nn.module`` to define a model in MLC LLM. We also use ``JIT`` +(just-in-time compilation) to debug the implementation. + +You can also refer to the PRs below on specific examples of adding a model architecture in MLC LLM: + +- `GPTNeoX PR `_ +- `GPT-2 PR `_ +- `Mistral PR `_ + +.. note:: + + As mentioned in :ref:`Model Prebuilts`, when adding a model variant that has + its architecture already supported in mlc-llm , you **only need to convert weights** + (e.g. adding ``CodeLlama`` when MLC supports ``llama-2``; adding ``OpenHermes Mistral`` + when MLC supports ``mistral``). On the other hand, a new model architecture + (or inference logic) requires more work (following the tutorial above). \ No newline at end of file diff --git a/docs/compilation/get-vicuna-weight.rst b/docs/compilation/get-vicuna-weight.rst new file mode 100644 index 0000000..2ea4ba5 --- /dev/null +++ b/docs/compilation/get-vicuna-weight.rst @@ -0,0 +1,68 @@ +Getting Vicuna Weights +====================== + +.. contents:: Table of Contents + :local: + :depth: 2 + +`Vicuna `_ is an open-source chatbot trained by fine-tuning `LLaMA `_ on `ShartGPT `_ data. + +Please note that the official Vicuna weights are delta weights applied to the LLaMA weights in order to comply with the LLaMA license. Users are responsible for applying these delta weights themselves. + +In this tutorial, we will show how to apply the delta weights to LLaMA weights to get Vicuna weights. + +Install FastChat +---------------- + +FastChat offers convenient utility functions for applying the delta to LLaMA weights. You can easily install it using pip. + +.. code-block:: bash + + pip install fschat + +Download HuggingFace LLaMA Weights +---------------------------------- + +The HuggingFace LLaMA weights are hosted using Git-LFS. Therefore, it is necessary to install Git-LFS first (you can ignore this step if git-lfs is already installed). + +.. code-block:: bash + + conda install git-lfs + git lfs install + +Then download the weights (both the LLaMA weight and Vicuna delta weight): + +.. code-block:: bash + + git clone https://huggingface.co/decapoda-research/llama-7b-hf + git clone https://huggingface.co/lmsys/vicuna-7b-delta-v1.1 + + +There is a name misalignment issue in the LLaMA weights and Vicuna delta weights. +Please follow these steps to modify the content of the "config.json" file: + +.. code-block:: bash + + sed -i 's/LLaMAForCausalLM/LlamaForCausalLM/g' llama-7b-hf/config.json + +Then use ``fschat`` to apply the delta to LLaMA weights + +.. code-block:: bash + + python3 -m fastchat.model.apply_delta \ + --base-model-path llama-7b-hf \ + --target-model-path vicuna-7b-v1.1 \ + --delta-path vicuna-7b-delta-v1.1 + +You will get the Vicuna weights in ``vicuna-7b-v1.1`` folder, which can be used as input of MLC-LLM to further compile models. + + +(Optional) Move Vicuna Weights to dist folder +--------------------------------------------- + +The default model path of MLC-LLM is ``dist`` folder. Therefore, it is recommended to move the Vicuna weights to ``dist`` folder. + +.. code-block:: bash + + mkdir -p dist/models + mv vicuna-7b-v1.1 dist/models/vicuna-7b-v1.1 diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..0f7ed19 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +import os +import sys + +import tlcpack_sphinx_addon + +# -- General configuration ------------------------------------------------ + +sys.path.insert(0, os.path.abspath("../python")) +sys.path.insert(0, os.path.abspath("../")) +autodoc_mock_imports = ["torch"] +# do not load mlc-llm.so in docs +os.environ["SKIP_LOADING_MLCLLM_SO"] = "1" + +# General information about the project. +project = "mlc-llm" +author = "MLC LLM Contributors" +copyright = "2023, %s" % author + +# Version information. + +version = "0.1.0" +release = "0.1.0" + +extensions = [ + "sphinx_tabs.tabs", + "sphinx_toolbox.collapse", + "sphinxcontrib.httpdomain", + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx_reredirects", +] + +redirects = {"get_started/try_out": "../index.html#getting-started"} + +source_suffix = [".rst"] + +language = "en" + +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = "sphinx" + +# A list of ignored prefixes for module index sorting. +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + +# -- Options for HTML output ---------------------------------------------- + +# The theme is set by the make target +import sphinx_rtd_theme + +html_theme = "sphinx_rtd_theme" +html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + +templates_path = [] + +html_static_path = [] + +footer_copyright = "© 2023 MLC LLM" +footer_note = " " + +html_logo = "_static/img/mlc-logo-with-text-landscape.svg" + +html_theme_options = { + "logo_only": True, +} + +header_links = [ + ("Home", "https://llm.mlc.ai/"), + ("Github", "https://github.com/mlc-ai/mlc-llm"), + ("Discord Server", "https://discord.gg/9Xpy2HGBuD"), +] + +header_dropdown = { + "name": "Other Resources", + "items": [ + ("MLC Course", "https://mlc.ai/"), + ("MLC Blog", "https://blog.mlc.ai/"), + ("Web LLM", "https://webllm.mlc.ai/"), + ], +} + +html_context = { + "footer_copyright": footer_copyright, + "footer_note": footer_note, + "header_links": header_links, + "header_dropdown": header_dropdown, + "display_github": True, + "github_user": "mlc-ai", + "github_repo": "mlc-llm", + "github_version": "main/docs/", + "theme_vcs_pageview_mode": "edit", + # "header_logo": "/path/to/logo", + # "header_logo_link": "", + # "version_selecter": "", +} + + +# add additional overrides +templates_path += [tlcpack_sphinx_addon.get_templates_path()] +html_static_path += [tlcpack_sphinx_addon.get_static_path()] diff --git a/docs/deploy/android.rst b/docs/deploy/android.rst new file mode 100644 index 0000000..7bcda64 --- /dev/null +++ b/docs/deploy/android.rst @@ -0,0 +1,187 @@ +.. _deploy-android: + +Android App +=========== + +.. contents:: Table of Contents + :local: + :depth: 2 + +Demo App +-------- + +The demo APK below is built for Samsung S23 with Snapdragon 8 Gen 2 chip. + +.. image:: https://seeklogo.com/images/D/download-android-apk-badge-logo-D074C6882B-seeklogo.com.png + :width: 135 + :target: https://github.com/mlc-ai/binary-mlc-llm-libs/releases/download/Android/mlc-chat.apk + +Prerequisite +------------ + +**Rust** (`install `__) is needed to cross-compile HuggingFace tokenizers to Android. Make sure rustc, cargo, and rustup are available in ``$PATH``. + +**Android Studio** (`install `__) with NDK and CMake. To install NDK and CMake, in the Android Studio welcome page, click "Projects → SDK Manager → SDK Tools". Set up the following environment variables: + +- ``ANDROID_NDK`` so that ``$ANDROID_NDK/build/cmake/android.toolchain.cmake`` is available. +- ``TVM_NDK_CC`` that points to NDK's clang compiler. + +.. code-block:: bash + + # Example on macOS + ANDROID_NDK: $HOME/Library/Android/sdk/ndk/25.2.9519653 + TVM_NDK_CC: $ANDROID_NDK/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android24-clang + # Example on Windows + ANDROID_NDK: $HOME/Library/Android/sdk/ndk/25.2.9519653 + TVM_NDK_CC: $ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang + +**JDK**, such as OpenJDK >= 17, to compile Java bindings of TVM Unity runtime. It could be installed via Homebrew on macOS, apt on Ubuntu or other package managers. Set up the following environment variable: + +- ``JAVA_HOME`` so that Java is available in ``$JAVA_HOME/bin/java``. + +Please ensure that the JDK versions for Android Studio and JAVA_HOME are the same. We recommended setting the `JAVA_HOME` to the JDK bundled with Android Studio. e.g. `export JAVA_HOME=/Applications/Android\ Studio.app/Contents/jbr/Contents/Home` for macOS. + +**TVM Unity runtime** is placed under `3rdparty/tvm `__ in MLC LLM, so there is no need to install anything extra. Set up the following environment variable: + +- ``TVM_HOME`` so that its headers are available under ``$TVM_HOME/include/tvm/runtime``. + +(Optional) **TVM Unity compiler** Python package (:ref:`install ` or :ref:`build from source `). It is *NOT* required if models are prebuilt, but to compile PyTorch models from HuggingFace in the following section, the compiler is a must-dependency. + +.. note:: + ❗ Whenever using Python, it is highly recommended to use **conda** to manage an isolated Python environment to avoid missing dependencies, incompatible versions, and package conflicts. + +Check if **environment variable** are properly set as the last check. One way to ensure this is to place them in ``$HOME/.zshrc``, ``$HOME/.bashrc`` or environment management tools. + +.. code-block:: bash + + source $HOME/.cargo/env # Rust + export ANDROID_NDK=... # Android NDK toolchain + export TVM_NDK_CC=... # Android NDK clang + export JAVA_HOME=... # Java + export TVM_HOME=... # TVM Unity runtime + +Compile PyTorch Models from HuggingFace +--------------------------------------- + +To deploy models on Android with reasonable performance, one has to cross-compile to and fully utilize mobile GPUs using TVM Unity. MLC provides a few pre-compiled models, or one could compile the models on their own. + +**Cloning MLC LLM from GitHub**. Download MLC LLM via the following command: + +.. code-block:: bash + + git clone --recursive https://github.com/mlc-ai/mlc-llm/ + ^^^^^^^^^^^ + cd ./mlc-llm/ + +.. note:: + ❗ The ``--recursive`` flag is necessary to download submodules like `3rdparty/tvm `__. If you see any file missing during compilation, please double check if git submodules are properly cloned. + +**Download the PyTorch model** using Git Large File Storage (LFS), and by default, under ``./dist/models/``: + +.. code-block:: bash + + MODEL_NAME=Llama-2-7b-chat-hf + QUANTIZATION=q4f16_1 + + git lfs install + git clone https://huggingface.co/meta-llama/$MODEL_NAME \ + ./dist/models/ + +**Compile Android-capable models**. Install TVM Unity compiler as a Python package, and then compile the model for android using the following commands: + +.. code-block:: bash + + # convert weights + mlc_chat convert_weight ./dist/models/$MODEL_NAME/ --quantization $QUANTIZATION -o dist/$MODEL_NAME-$QUANTIZATION-MLC/ + + # create mlc-chat-config.json + mlc_chat gen_config ./dist/models/$MODEL_NAME/ --quantization $QUANTIZATION \ + --conv-template llama-2 --context-window-size 768 -o dist/${MODEL_NAME}-${QUANTIZATION}-MLC/ + + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/${MODEL_NAME}-${QUANTIZATION}-MLC/mlc-chat-config.json \ + --device android -o ./dist/${MODEL_NAME}-${QUANTIZATION}-MLC/${MODEL_NAME}-${QUANTIZATION}-android.tar + +This generates the directory ``./dist/$MODEL_NAME-$QUANTIZATION-MLC`` which contains the necessary components to run the model, as explained below. + +.. note:: + ❗ To run 7B models like llama-2-7B, Mistral-7B, it is recommended to use smaller values of parameter ``--context-window-size`` (``--sliding-window-size`` and ``--prefill-chunk-size`` for sliding window attention) to reduce the memory footprint of the model. Default configurations for certains models can be found under the Android tab in the `Compile Models `_ section. + +**Expected output format**. By default models are placed under ``./dist/${MODEL_NAME}-${QUANTIZATION}-MLC``, and the result consists of 3 major components: + +- Runtime configuration: It configures conversation templates including system prompts, repetition penalty, sampling including temperature and top-p probability, maximum sequence length, etc. It is usually named as ``mlc-chat-config.json`` alongside with tokenizer configurations. +- Model lib: The compiled library that uses mobile GPU. It is usually named as ``${MODEL_NAME}-${QUANTIZATION}-android.tar``, for example, ``Llama-2-7b-chat-hf-q4f16_1-android.tar``. +- Model weights: the model weights are sharded as ``params_shard_*.bin`` and the metadata is stored in ``ndarray-cache.json`` + +Create Android Project using Compiled Models +-------------------------------------------- + +The source code for MLC LLM is available under ``android/``, including scripts to build dependencies. Enter the directory first: + +.. code-block:: bash + + cd ./android/library + +**Build necessary dependencies.** Configure the list of models the app comes with using the JSON file ``app-config.json`` which contains two properties `model_list` and `model_lib_path_for_prepare_libs` ``model_lib_path_for_prepare_libs`` contains list of model library paths under `./dist/` that will be bundled with the apk. The ``model_list`` property contains data for models that are not bundled with the apk, but downloaded from the internet at run-time. Each model defined in `model_list` contain the following fields: + +``model_url`` + (Required) URL to the repo containing the weights. + +``model_id`` + (Required) Unique local identifier to identify the model. + +``model_lib`` + (Required) Matches the system-lib-prefix, generally set during ``mlc_chat compile`` which can be specified using + ``--system-lib-prefix`` argument. By default, it is set to ``"${model_type}_${quantization}"`` e.g. ``gpt_neox_q4f16_1`` for the RedPajama-INCITE-Chat-3B-v1 model. If the ``--system-lib-prefix`` argument is manually specified during ``mlc_chat compile``, the ``model_lib`` field should be updated accordingly. + +``estimated_vram_bytes`` + (Optional) Estimated requirements of VRAM to run the model. + +To change the configuration, edit ``app-config.json``: + +.. code-block:: bash + + vim ./src/main/assets/app-config.json + +Then bundle the android library ``${MODEL_NAME}-${QUANTIZATION}-android.tar`` compiled from ``mlc_chat compile`` in the previous steps, with TVM Unity's Java runtime by running the commands below: + +.. code-block:: bash + + ./prepare_libs.sh + +which generates the two files below: + +.. code-block:: bash + + >>> find ./build/output -type f + ./build/output/arm64-v8a/libtvm4j_runtime_packed.so + ./build/output/tvm4j_core.jar + +The model execution logic in mobile GPUs is incorporated into ``libtvm4j_runtime_packed.so``, while ``tvm4j_core.jar`` is a lightweight (~60 kb) `Java binding `_ to it. + +**Build the Android app**. Open folder ``./android`` as an Android Studio Project. Connect your Android device to your machine. In the menu bar of Android Studio, click "Build → Make Project". Once the build is finished, click "Run → Run 'app'" and you will see the app launched on your phone. + +.. note:: + ❗ This app cannot be run in an emulator and thus a physical phone is required, because MLC LLM needs an actual mobile GPU to meaningfully run at an accelerated speed. + +Incorporate Model Weights +------------------------- + +Instructions have been provided to build an Android App with MLC LLM in previous sections, but it requires run-time weight downloading from HuggingFace, as configured in `app-config.json` in previous steps under `model_url`. However, it could be desirable to bundle weights together into the app to avoid downloading over the network. In this section, we provide a simple ADB-based walkthrough that hopefully helps with further development. + +**Generating APK**. Enter Android Studio, and click "Build → Generate Signed Bundle/APK" to build an APK for release. If it is the first time you generate an APK, you will need to create a key according to `the official guide from Android `_. This APK will be placed under ``android/app/release/app-release.apk``. + +**Install ADB and USB debugging**. Enable "USB debugging" in the developer mode in your phone settings. In SDK manager, install `Android SDK Platform-Tools `_. Add the path to platform-tool path to the environment variable ``PATH``. Run the following commands, and if ADB is installed correctly, your phone will appear as a device: + +.. code-block:: bash + + adb devices + +**Install the APK and weights to your phone**. Run the commands below replacing ``${MODEL_NAME}`` and ``${QUANTIZATION}`` with the actual model name (e.g. Llama-2-7b-chat-hf) and quantization format (e.g. q4f16_1). + +.. code-block:: bash + + adb install android/app/release/app-release.apk + adb push dist/${MODEL_NAME}-${QUANTIZATION}-MLC /data/local/tmp/${MODEL_NAME}-${QUANTIZATION}/ + adb shell "mkdir -p /storage/emulated/0/Android/data/ai.mlc.mlcchat/files/" + adb shell "mv /data/local/tmp/${MODEL_NAME}-${QUANTIZATION} /storage/emulated/0/Android/data/ai.mlc.mlcchat/files/" diff --git a/docs/deploy/cli.rst b/docs/deploy/cli.rst new file mode 100644 index 0000000..83a2a9d --- /dev/null +++ b/docs/deploy/cli.rst @@ -0,0 +1,106 @@ +.. _deploy-cli: + +CLI +=============== + +MLCChat CLI is the command line tool to run MLC-compiled LLMs out of the box. + +.. contents:: Table of Contents + :local: + :depth: 2 + +Option 1. Conda Prebuilt +~~~~~~~~~~~~~~~~~~~~~~~~ + +The prebuilt package supports Metal on macOS and Vulkan on Linux and Windows, and can be installed via Conda one-liner. + +To use other GPU runtimes, e.g. CUDA, please instead :ref:`build it from source `. + +.. code:: shell + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-chat-nightly mlc-ai-nightly + mlc_chat chat -h + +.. note:: + The prebuilt package supports **Metal** on macOS and **Vulkan** on Linux and Windows. It is possible to use other GPU runtimes such as **CUDA** by compiling MLCChat CLI from the source. + + +Option 2. Build MLC Runtime from Source +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We also provide options to build mlc runtime libraries and ``mlc_chat`` from source. +This step is useful if the prebuilt is unavailable on your platform, or if you would like to build a runtime +that supports other GPU runtime than the prebuilt version. We can build a customized version +of mlc chat runtime. You only need to do this if you choose not to use the prebuilt. + +First, make sure you install TVM unity (following the instruction in :ref:`install-tvm-unity`). +Then please follow the instructions in :ref:`mlcchat_build_from_source` to build the necessary libraries. + +.. `|` adds a blank line + +| + +Run Models through MLCChat CLI +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once ``mlc_chat`` is installed, you are able to run any MLC-compiled model on the command line. + +To run a model with MLC LLM in any platform, you can either: + +- Use off-the-shelf model prebuilts from the MLC Huggingface repo (see :ref:`Model Prebuilts` for details). +- Use locally compiled model weights and libraries following :doc:`the model compilation page `. + +**Option 1: Use model prebuilts** + +To run ``mlc_chat``, you can specify the Huggingface MLC prebuilt model repo path with the prefix ``HF://``. +For example, to run the MLC Llama 2 7B Q4F16_1 model (`Repo link `_), +simply use ``HF://mlc-ai/Llama-2-7b-chat-hf-q4f16_1-MLC``. The model weights and library will be downloaded +automatically from Huggingface. + +.. code:: shell + + mlc_chat chat HF://mlc-ai/Llama-2-7b-chat-hf-q4f16_1-MLC --device "cuda:0" --overrides context_window_size=1024 + +.. code:: shell + + You can use the following special commands: + /help print the special commands + /exit quit the cli + /stats print out the latest stats (token/sec) + /reset restart a fresh chat + /set [overrides] override settings in the generation config. For example, + `/set temperature=0.5;max_gen_len=100;stop=end,stop` + Note: Separate stop words in the `stop` option with commas (,). + Multi-line input: Use escape+enter to start a new line. + + [INST]: What's the meaning of life + [/INST]: + Ah, a question that has puzzled philosophers and theologians for centuries! The meaning + of life is a deeply personal and subjective topic, and there are many different + perspectives on what it might be. However, here are some possible answers that have been + proposed by various thinkers and cultures: + ... + + +**Option 2: Use locally compiled model weights and libraries** + +For models other than the prebuilt ones we provided: + +1. If the model is a variant to an existing model library (e.g. ``WizardMathV1.1`` and ``OpenHermes`` are variants of ``Mistral``), + follow :ref:`convert-weights-via-MLC` to convert the weights and reuse existing model libraries. +2. Otherwise, follow :ref:`compile-model-libraries` to compile both the model library and weights. + +Once you have the model locally compiled with a model library and model weights, to run ``mlc_chat``, simply + +- Specify the path to ``mlc-chat-config.json`` and the converted model weights to ``--model`` +- Specify the path to the compiled model library (e.g. a .so file) to ``--model-lib-path`` + +.. code:: shell + + mlc_chat chat dist/Llama-2-7b-chat-hf-q4f16_1-MLC \ + --device "cuda:0" --overrides context_window_size=1024 \ + --model-lib-path dist/prebuilt_libs/Llama-2-7b-chat-hf/Llama-2-7b-chat-hf-q4f16_1-vulkan.so + # CUDA on Linux: dist/prebuilt_libs/Llama-2-7b-chat-hf/Llama-2-7b-chat-hf-q4f16_1-cuda.so + # Metal on macOS: dist/prebuilt_libs/Llama-2-7b-chat-hf/Llama-2-7b-chat-hf-q4f16_1-metal.so + # Same rule applies for other platforms diff --git a/docs/deploy/ios.rst b/docs/deploy/ios.rst new file mode 100644 index 0000000..0d3b4f6 --- /dev/null +++ b/docs/deploy/ios.rst @@ -0,0 +1,491 @@ +.. _deploy-ios: + +iOS App and Swift API +===================== + +.. contents:: Table of Contents + :local: + :depth: 2 + +The MLC LLM iOS app can be installed in two ways: through the pre-built package or by building from the source. +If you are an iOS user looking to try out the models, the pre-built package is recommended. If you are a +developer seeking to integrate new features into the package, building the iOS package from the source is required. + +Use Pre-built iOS App +--------------------- +The MLC Chat app is now available in App Store at no cost. You can download and explore it by simply clicking the button below: + + .. image:: https://developer.apple.com/assets/elements/badges/download-on-the-app-store.svg + :width: 135 + :target: https://apps.apple.com/us/app/mlc-chat/id6448482937 + + +Build iOS App from Source +------------------------- + +This section shows how we can build the app from the source. + +Step 1. Install Build Dependencies +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +First and foremost, please clone the `MLC LLM GitHub repository `_. + +Please follow :doc:`/install/tvm` to install TVM Unity. +Note that we **do not** have to run `build.py` since we can use prebuilt weights. +We only need TVM Unity's utility to combine the libraries (`local-id-iphone.tar`) into a single library. + +We also need to have the following build dependencies: + +* CMake >= 3.24, +* Git and Git-LFS, +* `Rust and Cargo `_, which are required by Hugging Face's tokenizer. + + +Step 2. Download Prebuilt Weights and Library +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You also need to obtain a copy of the MLC-LLM source code +by cloning the `MLC LLM GitHub repository `_. +To simplify the build, we will use prebuilt model +weights and libraries here. Run the following command +in the root directory of the MLC-LLM. + +.. code:: bash + + mkdir -p dist/prebuilt + git clone https://github.com/mlc-ai/binary-mlc-llm-libs.git dist/prebuilt/lib + + cd dist/prebuilt + git lfs install + git clone https://huggingface.co/mlc-ai/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC + cd ../.. + +Validate that the files and directories exist: + +.. code:: bash + + >>> ls -l ./dist/prebuilt/lib/*/*-iphone.tar + ./dist/prebuilt/lib/RedPajama-INCITE-Chat-3B-v1/RedPajama-INCITE-Chat-3B-v1-q4f16_1-iphone.tar + ./dist/prebuilt/lib/Mistral-7B-Instruct-v0.2/Mistral-7B-Instruct-v0.2-q3f16_1-iphone.tar + ... + + >>> ls -l ./dist/prebuilt/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC + # chat config: + mlc-chat-config.json + # model weights: + ndarray-cache.json + params_shard_*.bin + ... + + +Step 3. Build Auxiliary Components +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +**Tokenizer and runtime** + +In addition to the model itself, a lightweight runtime and tokenizer are +required to actually run the LLM. You can build and organize these +components by following these steps: + +.. code:: bash + + git submodule update --init --recursive + cd ./ios + ./prepare_libs.sh + +This will create a ``./build`` folder that contains the following files. +Please make sure all the following files exist in ``./build/``. + +.. code:: bash + + >>> ls ./build/lib/ + libmlc_llm.a # A lightweight interface to interact with LLM, tokenizer, and TVM Unity runtime + libmodel_iphone.a # The compiled model lib + libsentencepiece.a # SentencePiece tokenizer + libtokenizers_cpp.a # Huggingface tokenizer + libtvm_runtime.a # TVM Unity runtime + +**Add prepackage model** + +We can also *optionally* add prepackage weights into the app, +run the following command under the ``./ios`` directory: + +.. code:: bash + + cd ./ios + open ./prepare_params.sh # make sure builtin_list only contains "RedPajama-INCITE-Chat-3B-v1-q4f16_1" + ./prepare_params.sh + +The outcome should be as follows: + +.. code:: bash + + >>> ls ./dist/ + RedPajama-INCITE-Chat-3B-v1-q4f16_1 + +Step 4. Build iOS App +^^^^^^^^^^^^^^^^^^^^^ + +Open ``./ios/MLCChat.xcodeproj`` using Xcode. Note that you will need an +Apple Developer Account to use Xcode, and you may be prompted to use +your own developer team credential and product bundle identifier. + +Ensure that all the necessary dependencies and configurations are +correctly set up in the Xcode project. + +Once you have made the necessary changes, build the iOS app using Xcode. +If you have an Apple Silicon Mac, you can select target "My Mac (designed for iPad)" +to run on your Mac. You can also directly run it on your iPad or iPhone. + +.. image:: https://raw.githubusercontent.com/mlc-ai/web-data/main/images/mlc-llm/tutorials/xcode-build.jpg + :align: center + :width: 60% + +| + +Customize the App +----------------- + +We can customize the iOS app in several ways. +`MLCChat/app-config.json `_ +controls the list of local and remote models to be packaged into the app, given a local path or a URL respectively. Only models in ``model_list`` will have their libraries brought into the app when running `./prepare_libs` to package them into ``libmodel_iphone.a``. Each model defined in `app-config.json` contain the following fields: + +``model_path`` + (Required if local model) Name of the local folder containing the weights. + +``model_url`` + (Required if remote model) URL to the repo containing the weights. + +``model_id`` + (Required) Unique local identifier to identify the model. + +``model_lib`` + (Required) Matches the system-lib-prefix, generally set during ``mlc_chat compile`` which can be specified using + ``--system-lib-prefix`` argument. By default, it is set to ``"${model_type}_${quantization}"`` e.g. ``gpt_neox_q4f16_1`` + for the RedPajama-INCITE-Chat-3B-v1 model. If the ``--system-lib-prefix`` argument is manually specified during + ``mlc_chat compile``, the ``model_lib`` field should be updated accordingly. + +``required_vram_bytes`` + (Required) Estimated requirements of VRAM to run the model. + +``model_lib_path_for_prepare_libs`` + (Required) List of paths to the model libraries in the app (respective ``.tar`` file in the ``binary-mlc-llm-libs`` + repo, relative path in the ``dist`` artifact folder or full path to the library). Only used while running + ``prepare_libs.sh`` to determine which model library to use during runtime. Useful when selecting a library with + different settings (e.g. ``prefill_chunk_size``, ``context_window_size``, and ``sliding_window_size``). + +Additionally, the app prepackages the models under ``./ios/dist``. +This built-in list can be controlled by editing ``prepare_params.sh``. +You can package new prebuilt models or compiled models by changing the above fields and then repeating the steps above. + + +Bring Your Own Model Variant +---------------------------- + +In cases where the model you are adding is simply a variant of an existing +model, we only need to convert weights and reuse existing model library. For instance: + +- Adding ``NeuralHermes`` when MLC already supports the ``Mistral`` architecture + + +In this section, we walk you through adding ``NeuralHermes-2.5-Mistral-7B-q3f16_1-MLC`` to the MLC iOS app. +According to the model's ``config.json`` on `its Huggingface repo `_, +it reuses the Mistral model architecture. + +.. note:: + + This section largely replicates :ref:`convert-weights-via-MLC`. + See that page for more details. Note that the weights are shared across + all platforms in MLC. + +**Step 1 Clone from HF and convert_weight** + +You can be under the mlc-llm repo, or your own working directory. Note that all platforms +can share the same compiled/quantized weights. See :ref:`compile-command-specification` +for specification of ``convert_weight``. + +.. code:: shell + + # Create directory + mkdir -p dist/models && cd dist/models + # Clone HF weights + git lfs install + git clone https://huggingface.co/mlabonne/NeuralHermes-2.5-Mistral-7B + cd ../.. + # Convert weight + mlc_chat convert_weight ./dist/models/NeuralHermes-2.5-Mistral-7B/ \ + --quantization q4f16_1 \ + -o dist/NeuralHermes-2.5-Mistral-7B-q3f16_1-MLC + +**Step 2 Generate MLC Chat Config** + +Use ``mlc_chat gen_config`` to generate ``mlc-chat-config.json`` and process tokenizers. +See :ref:`compile-command-specification` for specification of ``gen_config``. + +.. code:: shell + + mlc_chat gen_config ./dist/models/NeuralHermes-2.5-Mistral-7B/ \ + --quantization q3f16_1 --conv-template neural_hermes_mistral \ + -o dist/NeuralHermes-2.5-Mistral-7B-q3f16_1-MLC + +For the ``conv-template``, `conv_template.cc `__ +contains a full list of conversation templates that MLC provides. + +If the model you are adding requires a new conversation template, you would need to add your own. +Follow `this PR `__ as an example. +We look up the template to use with the ``conv_template`` field in ``mlc-chat-config.json``. + +For more details, please see :ref:`configure-mlc-chat-json`. + +**Step 3 Upload weights to HF** + +.. code:: shell + + # First, please create a repository on Hugging Face. + # With the repository created, run + git lfs install + git clone https://huggingface.co/my-huggingface-account/my-mistral-weight-huggingface-repo + cd my-mistral-weight-huggingface-repo + cp path/to/mlc-llm/dist/NeuralHermes-2.5-Mistral-7B-q3f16_1-MLC/* . + git add . && git commit -m "Add mistral model weights" + git push origin main + +After successfully following all steps, you should end up with a Huggingface repo similar to +`NeuralHermes-2.5-Mistral-7B-q3f16_1-MLC `__, +which includes the converted/quantized weights, the ``mlc-chat-config.json``, and tokenizer files. + + +**Step 4 Register as a ModelRecord** + +Finally, we modify the code snippet for +`app-config.json `__ +pasted above. + +We simply specify the Huggingface link as ``model_url``, while reusing the ``model_lib`` for +``Mistral-7B``. + +.. code:: javascript + + "model_list": [ + // Other records here omitted... + { + // Substitute model_url with the one you created `my-huggingface-account/my-mistral-weight-huggingface-repo` + "model_url": "https://huggingface.co/mlc-ai/NeuralHermes-2.5-Mistral-7B-q3f16_1-MLC", + "model_id": "Mistral-7B-Instruct-v0.2-q3f16_1", + "model_lib": "mistral_q3f16_1", + "model_lib_path": "lib/Mistral-7B-Instruct-v0.2/Mistral-7B-Instruct-v0.2-q3f16_1-iphone.tar", + "estimated_vram_bytes": 3316000000 + } + ] + + +Now, the app will use the ``NeuralHermes-Mistral`` model you just added. + + +Bring Your Own Model Library +---------------------------- + +A model library is specified by: + + - The model architecture (e.g. ``mistral``, ``phi-msft``) + - Quantization Scheme (e.g. ``q3f16_1``, ``q0f32``) + - Metadata (e.g. ``context_window_size``, ``sliding_window_size``, ``prefill_chunk_size``), which affects memory planning + - Platform (e.g. ``cuda``, ``webgpu``, ``iphone``, ``android``) + +In cases where the model you want to run is not compatible with the provided MLC +prebuilt model libraries (e.g. having a different quantization, a different +metadata spec, or even a different model architecture), you need to build your +own model library. + +In this section, we walk you through adding ``phi-2`` to the iOS app. + +This section largely replicates :ref:`compile-model-libraries`. See that page for +more details, specifically the ``iOS`` option. + +**Step 0. Install dependencies** + +To compile model libraries for iOS, you need to :ref:`build mlc_chat from source `. + +**Step 1. Clone from HF and convert_weight** + +You can be under the mlc-llm repo, or your own working directory. Note that all platforms +can share the same compiled/quantized weights. + +.. code:: shell + + # Create directory + mkdir -p dist/models && cd dist/models + # Clone HF weights + git lfs install + git clone https://huggingface.co/microsoft/phi-2 + cd ../.. + # Convert weight + mlc_chat convert_weight ./dist/models/phi-2/ \ + --quantization q4f16_1 \ + -o dist/phi-2-q4f16_1-MLC + +**Step 2. Generate mlc-chat-config and compile** + +A model library is specified by: + + - The model architecture (e.g. ``mistral``, ``phi-msft``) + - Quantization Scheme (e.g. ``q3f16_1``, ``q0f32``) + - Metadata (e.g. ``context_window_size``, ``sliding_window_size``, ``prefill_chunk_size``), which affects memory planning + - Platform (e.g. ``cuda``, ``webgpu``, ``iphone``, ``android``) + +All these knobs are specified in ``mlc-chat-config.json`` generated by ``gen_config``. + +.. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/phi-2/ \ + --quantization q4f16_1 --conv-template phi-2 \ + -o dist/phi-2-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/phi-2-q4f16_1-MLC/mlc-chat-config.json \ + --device iphone -o dist/libs/phi-2-q4f16_1-iphone.tar + +.. note:: + When compiling larger models like ``Llama-2-7B``, you may want to add a lower chunk size + while prefilling prompts ``--prefill_chunk_size 128`` or even lower ``context_window_size``\ + to decrease memory usage. Otherwise, during runtime, you may run out of memory. + + +**Step 3. Distribute model library and model weights** + +After following the steps above, you should end up with: + +.. code:: shell + + ~/mlc-llm > ls dist/libs + phi-2-q4f16_1-iphone.tar # ===> the model library + + ~/mlc-llm > ls dist/phi-2-q4f16_1-MLC + mlc-chat-config.json # ===> the chat config + ndarray-cache.json # ===> the model weight info + params_shard_0.bin # ===> the model weights + params_shard_1.bin + ... + tokenizer.json # ===> the tokenizer files + tokenizer_config.json + +Upload the ``phi-2-q4f16_1-iphone.tar`` to a github repository (for us, +it is in `binary-mlc-llm-libs `__). Then +upload the weights ``phi-2-q4f16_1-MLC`` to a Huggingface repo: + +.. code:: shell + + # First, please create a repository on Hugging Face. + # With the repository created, run + git lfs install + git clone https://huggingface.co/my-huggingface-account/my-phi-weight-huggingface-repo + cd my-phi-weight-huggingface-repo + cp path/to/mlc-llm/dist/phi-2-q4f16_1-MLC/* . + git add . && git commit -m "Add phi-2 model weights" + git push origin main + +This would result in something like `phi-2-q4f16_1-MLC +`_. + + +**Step 4. Calculate estimated VRAM usage** + +Given the compiled library, it is possible to calculate an upper bound for the VRAM +usage during runtime. This useful to better understand if a model is able to fit particular +hardware. We can calculate this estimate using the following command: + +.. code:: shell + + ~/mlc-llm > python -m mlc_chat.cli.model_metadata ./dist/libs/phi-2-q4f16_1-iphone.tar \ + > --memory-only --mlc-chat-config ./dist/phi-2-q4f16_1-MLC/mlc-chat-config.json + INFO model_metadata.py:90: Total memory usage: 3042.96 MB (Parameters: 1492.45 MB. KVCache: 640.00 MB. Temporary buffer: 910.51 MB) + INFO model_metadata.py:99: To reduce memory usage, tweak `prefill_chunk_size`, `context_window_size` and `sliding_window_size` + + +**Step 5. Register as a ModelRecord** + +Finally, we update the code snippet for +`app-config.json `__ +pasted above. + +We simply specify the Huggingface link as ``model_url``, while using the new ``model_lib`` for +``phi-2``. Regarding the field ``estimated_vram_bytes``, we can use the output of the last step +rounded up to MB. + +.. code:: javascript + + "model_list": [ + // Other records here omitted... + { + // Substitute model_url with the one you created `my-huggingface-account/my-phi-weight-huggingface-repo` + "model_url": "https://huggingface.co/mlc-ai/phi-2-q4f16_1-MLC", + "model_id": "phi-2-q4f16_1", + "model_lib": "phi_msft_q4f16_1", + "model_lib_path": "lib/phi-2/phi-2-q4f16_1-iphone.tar", + "estimated_vram_bytes": 3043000000 + } + ] + + +Now, the app will use the ``phi-2`` model library you just added. + + +Build Apps with MLC Swift API +----------------------------- + +We also provide a Swift package that you can use to build +your own app. The package is located under `ios/MLCSwift`. + +- First make sure you have run the same steps listed + in the previous section. This will give us the necessary libraries + under ``/path/to/ios/build/lib``. +- Then you can add ``ios/MLCSwift`` package to your app in Xcode. + Under "Frameworks, Libraries, and Embedded Content", click add package dependencies + and add local package that points to ``ios/MLCSwift``. +- Finally, we need to add the libraries dependencies. Under build settings: + + - Add library search path ``/path/to/ios/build/lib``. + - Add the following items to "other linker flags". + + .. code:: + + -Wl,-all_load + -lmodel_iphone + -lmlc_llm -ltvm_runtime + -ltokenizers_cpp + -lsentencepiece + -ltokenizers_c + + +You can then import the `MLCSwift` package into your app. +The following code shows an illustrative example of how to use the chat module. + +.. code:: swift + + import MLCSwift + + let threadWorker = ThreadWorker() + let chat = ChatModule() + + threadWorker.push { + let modelLib = "model-lib-name" + let modelPath = "/path/to/model/weights" + let input = "What is the capital of Canada?" + chat.reload(modelLib, modelPath: modelPath) + + chat.prefill(input) + while (!chat.stopped()) { + displayReply(chat.getMessage()) + chat.decode() + } + } + +.. note:: + + Because the chat module makes heavy use of GPU and thread-local + resources, it needs to run on a dedicated background thread. + Therefore, **avoid using** `DispatchQueue`, which can cause context switching to + different threads and segfaults due to thread-safety issues. + Use the `ThreadWorker` class to launch all the jobs related + to the chat module. You can check out the source code of + the MLCChat app for a complete example. diff --git a/docs/deploy/javascript.rst b/docs/deploy/javascript.rst new file mode 100644 index 0000000..06a1d3f --- /dev/null +++ b/docs/deploy/javascript.rst @@ -0,0 +1,360 @@ +.. _webllm-runtime: + +WebLLM and Javascript API +========================= + +.. contents:: Table of Contents + :local: + :depth: 2 + +`WebLLM `_ is an MLC chat web runtime +that allows you to build chat applications directly in the browser, leveraging +`WebGPU `_ and providing users a natural layer of abstraction. + +Try out the Prebuilt Webpage +---------------------------- + +To get started, you can try out `WebLLM prebuilt webpage `__. + +A WebGPU-compatible browser and a local GPU are needed to run WebLLM. +You can download the latest Google Chrome and use `WebGPU Report `__ +to verify the functionality of WebGPU on your browser. + + +Use WebLLM NPM Package +---------------------- + +WebLLM is available as an `npm package `_. +The source code is available in `the WebLLM repo `_, +where you can make your own modifications and build from source. + +Note that the `WebLLM prebuilt webpage `__ above +is powered by the WebLLM npm package, specifically with the code in +the `simple-chat `__ example. + +Each of the model in the `WebLLM prebuilt webpage `__ +is registered as an instance of ``ModelRecord``. Looking at the most straightforward example +`get-started `__, +we see the code snippet: + +.. code:: typescript + + const myAppConfig: AppConfig = { + model_list: [ + { + "model_url": "https://huggingface.co/mlc-ai/Llama-2-7b-chat-hf-q4f32_1-MLC/resolve/main/", + "local_id": "Llama-2-7b-chat-hf-q4f32_1", + "model_lib_url": "https://raw.githubusercontent.com/mlc-ai/binary-mlc-llm-libs/main/Llama-2-7b-chat-hf/Llama-2-7b-chat-hf-q4f32_1-ctx4k_cs1k-webgpu.wasm", + }, + { + "model_url": "https://huggingface.co/mlc-ai/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/resolve/main/", + "local_id": "Mistral-7B-Instruct-v0.2-q4f16_1", + "model_lib_url": "https://raw.githubusercontent.com/mlc-ai/binary-mlc-llm-libs/main/Mistral-7B-Instruct-v0.2/Mistral-7B-Instruct-v0.2-q4f16_1-sw4k_cs1k-webgpu.wasm", + "required_features": ["shader-f16"], + }, + // Add your own models here... + ] + } + const selectedModel = "Llama-2-7b-chat-hf-q4f32_1" + // const selectedModel = "Mistral-7B-Instruct-v0.1-q4f16_1" + await chat.reload(selectedModel, undefined, myAppConfig); + +Just like any other platforms, to run a model with on WebLLM, you need: + +1. **Model weights** converted to MLC format (e.g. `Llama-2-7b-hf-q4f32_1-MLC + `_.): downloaded through ``model_url`` +2. **Model library** that comprises the inference logic (see repo `binary-mlc-llm-libs `__): downloaded through ``model_lib_url``. + +Verify Installation for Adding Models +------------------------------------- + +In sections below, we walk you through two examples of adding models to WebLLM. Before proceeding, +please verify installation of ``mlc_chat`` and ``tvm``: + +**Step 1. Verify mlc_chat** + +We use the python package ``mlc_chat`` to compile models. This can be installed by +following :ref:`install-mlc-packages`, either by building from source, or by +installing the prebuilt package. Verify ``mlc_chat`` installation in command line via: + +.. code:: bash + + $ mlc_chat --help + # You should see help information with this line + usage: MLC LLM Command Line Interface. [-h] {compile,convert_weight,gen_config} + +.. note:: + If it runs into error ``command not found: mlc_chat``, try ``python -m mlc_chat --help``. + +**Step 2. Verify TVM** + +To compile models, you also need to follow :ref:`install-tvm-unity`. +Here we verify ``tvm`` quickly with command line (for full verification, see :ref:`tvm-unity-validate`): + +.. code:: bash + + $ python -c "import tvm; print(tvm.__file__)" + /some-path/lib/python3.11/site-packages/tvm/__init__.py + + +.. _webllm-add-model-variant: + +Bring Your Own Model Variant +---------------------------- + +In cases where the model you are adding is simply a variant of an existing +model, we only need to convert weights and reuse existing model library. For instance: + +- Adding ``OpenMistral`` when MLC supports ``Mistral`` +- Adding ``Llama2-uncensored`` when MLC supports ``Llama2`` + + +In this section, we walk you through adding ``WizardMath-7B-V1.1-q4f16_1`` to the +`get-started `__ example. +According to the model's ``config.json`` on `its Huggingface repo `_, +it reuses the Mistral model architecture. + +.. note:: + + This section largely replicates :ref:`convert-weights-via-MLC`. + See that page for more details. Note that the weights are shared across + all platforms in MLC. + +**Step 1 Clone from HF and convert_weight** + +You can be under the mlc-llm repo, or your own working directory. Note that all platforms +can share the same compiled/quantized weights. See :ref:`compile-command-specification` +for specification of ``convert_weight``. + +.. code:: shell + + # Create directory + mkdir -p dist/models && cd dist/models + # Clone HF weights + git lfs install + git clone https://huggingface.co/WizardLM/WizardMath-7B-V1.1 + cd ../.. + # Convert weight + mlc_chat convert_weight ./dist/models/WizardMath-7B-V1.1/ \ + --quantization q4f16_1 \ + -o dist/WizardMath-7B-V1.1-q4f16_1-MLC + +**Step 2 Generate MLC Chat Config** + +Use ``mlc_chat gen_config`` to generate ``mlc-chat-config.json`` and process tokenizers. +See :ref:`compile-command-specification` for specification of ``gen_config``. + +.. code:: shell + + mlc_chat gen_config ./dist/models/WizardMath-7B-V1.1/ \ + --quantization q4f16_1 --conv-template wizard_coder_or_math \ + -o dist/WizardMath-7B-V1.1-q4f16_1-MLC/ + +For the ``conv-template``, `conv_template.cc `__ +contains a full list of conversation templates that MLC provides. + +If the model you are adding requires a new conversation template, you would need to add your own. +Follow `this PR `__ as an example. Besides, you also need to add the new template to ``/path/to/web-llm/src/conversation.ts``. +We look up the template to use with the ``conv_template`` field in ``mlc-chat-config.json``. + +For more details, please see :ref:`configure-mlc-chat-json`. + +.. note:: + + If you added your conversation template in ``src/conversation.ts``, you need to build WebLLM + from source following the instruction in + `the WebLLM repo's README `_. + + Alternatively, you could use the ``"custom"`` conversation template so that you can pass in + your own ``ConvTemplateConfig`` in runtime without having to build the package from source. + +**Step 3 Upload weights to HF** + +.. code:: shell + + # First, please create a repository on Hugging Face. + # With the repository created, run + git lfs install + git clone https://huggingface.co/my-huggingface-account/my-wizardMath-weight-huggingface-repo + cd my-wizardMath-weight-huggingface-repo + cp path/to/mlc-llm/dist/WizardMath-7B-V1.1-q4f16_1-MLC/* . + git add . && git commit -m "Add wizardMath model weights" + git push origin main + +After successfully following all steps, you should end up with a Huggingface repo similar to +`WizardMath-7B-V1.1-q4f16_1-MLC `__, +which includes the converted/quantized weights, the ``mlc-chat-config.json``, and tokenizer files. + + +**Step 4 Register as a ModelRecord** + +Finally, we modify the code snippet for +`get-started `__ +pasted above. + +We simply specify the Huggingface link as ``model_url``, while reusing the ``model_lib_url`` for +``Mistral-7B``. Note that we need the suffix to be ``/resolve/main/``. + +.. code:: typescript + + const myAppConfig: AppConfig = { + model_list: [ + // Other records here omitted... + { + // Substitute model_url with the one you created `my-huggingface-account/my-wizardMath-weight-huggingface-repo` + "model_url": "https://huggingface.co/mlc-ai/WizardMath-7B-V1.1-q4f16_1-MLC/resolve/main/", + "local_id": "WizardMath-7B-V1.1-q4f16_1", + "model_lib_url": "https://raw.githubusercontent.com/mlc-ai/binary-mlc-llm-libs/main/Mistral-7B-Instruct-v0.2/Mistral-7B-Instruct-v0.2-q4f16_1-sw4k_cs1k-webgpu.wasm", + "required_features": ["shader-f16"], + }, + ] + } + + const selectedModel = "WizardMath-7B-V1.1-q4f16_1" + await chat.reload(selectedModel, undefined, myAppConfig); + +Now, running the ``get-started`` example will use the ``WizardMath`` model you just added. +See `get-started's README `__ +on how to run it. + + +Bring Your Own Model Library +---------------------------- + +A model library is specified by: + + - The model architecture (e.g. ``llama-2``, ``gpt-neox``) + - Quantization (e.g. ``q4f16_1``, ``q0f32``) + - Metadata (e.g. ``context_window_size``, ``sliding_window_size``, ``prefill-chunk-size``), which affects memory planning + - Platform (e.g. ``cuda``, ``webgpu``, ``iOS``) + +In cases where the model you want to run is not compatible with the provided MLC +prebuilt model libraries (e.g. having a different quantization, a different +metadata spec, or even a different model architecture), you need to build your +own model library. + +In this section, we walk you through adding ``RedPajama-INCITE-Chat-3B-v1`` to the +`get-started `__ example. + +This section largely replicates :ref:`compile-model-libraries`. See that page for +more details, specifically the ``WebGPU`` option. + +**Step 0. Install dependencies** + +To compile model libraries for webgpu, you need to :ref:`build mlc_chat from source `. +Besides, you also need to follow :ref:`install-web-build`. Otherwise, it would run into error: + +.. code:: text + + RuntimeError: Cannot find libraries: wasm_runtime.bc + +**Step 1. Clone from HF and convert_weight** + +You can be under the mlc-llm repo, or your own working directory. Note that all platforms +can share the same compiled/quantized weights. + +.. code:: shell + + # Create directory + mkdir -p dist/models && cd dist/models + # Clone HF weights + git lfs install + git clone https://huggingface.co/togethercomputer/RedPajama-INCITE-Chat-3B-v1 + cd ../.. + # Convert weight + mlc_chat convert_weight ./dist/models/RedPajama-INCITE-Chat-3B-v1/ \ + --quantization q4f16_1 \ + -o dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC + +**Step 2. Generate mlc-chat-config and compile** + +A model library is specified by: + + - The model architecture (e.g. ``llama-2``, ``gpt-neox``) + - Quantization (e.g. ``q4f16_1``, ``q0f32``) + - Metadata (e.g. ``context_window_size``, ``sliding_window_size``, ``prefill-chunk-size``), which affects memory planning + - Platform (e.g. ``cuda``, ``webgpu``, ``iOS``) + +All these knobs are specified in ``mlc-chat-config.json`` generated by ``gen_config``. + +.. code:: shell + + # 1. gen_config: generate mlc-chat-config.json and process tokenizers + mlc_chat gen_config ./dist/models/RedPajama-INCITE-Chat-3B-v1/ \ + --quantization q4f16_1 --conv-template redpajama_chat \ + -o dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/ + # 2. compile: compile model library with specification in mlc-chat-config.json + mlc_chat compile ./dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC/mlc-chat-config.json \ + --device webgpu -o dist/libs/RedPajama-INCITE-Chat-3B-v1-q4f16_1-webgpu.wasm + +.. note:: + When compiling larger models like ``Llama-2-7B``, you may want to add ``--prefill_chunk_size 1024`` or + lower ``context_window_size`` to decrease memory usage. Otherwise, during runtime, + you may run into issues like: + + .. code:: text + + TypeError: Failed to execute 'createBuffer' on 'GPUDevice': Failed to read the 'size' property from + 'GPUBufferDescriptor': Value is outside the 'unsigned long long' value range. + + +**Step 3. Distribute model library and model weights** + +After following the steps above, you should end up with: + +.. code:: shell + + ~/mlc-llm > ls dist/libs + RedPajama-INCITE-Chat-3B-v1-q4f16_1-webgpu.wasm # ===> the model library + + ~/mlc-llm > ls dist/RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC + mlc-chat-config.json # ===> the chat config + ndarray-cache.json # ===> the model weight info + params_shard_0.bin # ===> the model weights + params_shard_1.bin + ... + tokenizer.json # ===> the tokenizer files + tokenizer_config.json + +Upload the ``RedPajama-INCITE-Chat-3B-v1-q4f16_1-webgpu.wasm`` to a github repository (for us, +it is in `binary-mlc-llm-libs `__). Then +upload the ``RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC`` to a Huggingface repo: + +.. code:: shell + + # First, please create a repository on Hugging Face. + # With the repository created, run + git lfs install + git clone https://huggingface.co/my-huggingface-account/my-redpajama3b-weight-huggingface-repo + cd my-redpajama3b-weight-huggingface-repo + cp path/to/mlc-llm/dist/RedPajama-INCITE-Instruct-3B-v1-q4f16_1-MLC/* . + git add . && git commit -m "Add redpajama-3b instruct model weights" + git push origin main + +This would result in something like `RedPajama-INCITE-Chat-3B-v1-q4f16_1-MLC +`_. + +**Step 4. Register as a ModelRecord** + +Finally, we are able to run the model we added in WebLLM's `get-started `__: + +.. code:: typescript + + const myAppConfig: AppConfig = { + model_list: [ + // Other records here omitted... + { + "model_url": "https://huggingface.co/my-hf-account/my-redpajama3b-weight-huggingface-repo/resolve/main/", + "local_id": "RedPajama-INCITE-Instruct-3B-v1", + "model_lib_url": "https://raw.githubusercontent.com/my-gh-account/my-repo/main/RedPajama-INCITE-Chat-3B-v1-q4f16_1-webgpu.wasm", + "required_features": ["shader-f16"], + }, + ] + } + + const selectedModel = "RedPajama-INCITE-Instruct-3B-v1" + await chat.reload(selectedModel, undefined, myAppConfig); + +Now, running the ``get-started`` example will use the ``RedPajama`` model you just added. +See `get-started's README `__ +on how to run it. \ No newline at end of file diff --git a/docs/deploy/python.rst b/docs/deploy/python.rst new file mode 100644 index 0000000..3dd1b67 --- /dev/null +++ b/docs/deploy/python.rst @@ -0,0 +1,363 @@ +.. _deploy-python: + +Python API +========== + +.. contents:: Table of Contents + :local: + :depth: 2 + +We expose Python API for the MLC-Chat for easy integration into other Python projects. + +The Python API is a part of the MLC-Chat package, which we have prepared pre-built pip wheels via +the :doc:`installation page <../install/mlc_llm>`. + +Instead of following this page, you could also checkout the following tutorials in +Python notebook (all runnable in Colab): + +- `Getting Started with MLC-LLM `_: + how to quickly download prebuilt models and chat with it +- `Raw Text Generation with MLC-LLM `_: + how to perform raw text generation with MLC-LLM in Python + +.. These notebooks are not up-to-date with SLM yet +.. - `Compiling Llama-2 with MLC-LLM `_: +.. how to use Python APIs to compile models with the MLC-LLM workflow +.. - `Extensions to More Model Variants `_: +.. how to use Python APIs to compile and chat with any model variant you'd like + + +Verify Installation +------------------- + +.. code:: bash + + python -c "from mlc_chat import ChatModule; print(ChatModule)" + +You are expected to see the information about the :class:`mlc_chat.ChatModule` class. + +If the command above results in error, follow :ref:`install-mlc-packages` (either install the prebuilt pip wheels +or :ref:`mlcchat_build_from_source`). + +Run MLC Models w/ Python +------------------------ + +To run a model with MLC LLM in any platform/runtime, you need: + +1. **Model weights** converted to MLC format (e.g. `RedPajama-INCITE-Chat-3B-v1-MLC + `_.) +2. **Model library** that comprises the inference logic (see repo `binary-mlc-llm-libs `__). + +There are two ways to obtain the model weights and libraries: + +1. Compile your own model weights and libraries following :doc:`the model compilation page `. +2. Use off-the-shelf `prebuilt models weights `__ and + `prebuilt model libraries `__ (see :ref:`Model Prebuilts` for details). + +We use off-the-shelf prebuilt models in this page. However, same steps apply if you want to run +the models you compiled yourself. + +**Step 1: Download prebuilt model weights and libraries** + +Skip this step if you have already obtained the model weights and libraries. + +.. code:: shell + + # Activate your conda environment + conda install -c conda-forge git-lfs + + # Download pre-conveted weights + git lfs install && mkdir dist/ + git clone https://huggingface.co/mlc-ai/Llama-2-7b-chat-hf-q4f16_1-MLC \ + dist/Llama-2-7b-chat-hf-q4f16_1-MLC + + # Download pre-compiled model library + git clone https://github.com/mlc-ai/binary-mlc-llm-libs.git dist/prebuilt_libs + + +**Step 2: Run the model in Python** + +Use the conda environment you used to install ``mlc_chat``. +From the ``mlc-llm`` directory, you can create a Python +file ``sample_mlc_chat.py`` and paste the following lines: + +.. code:: python + + from mlc_chat import ChatModule + from mlc_chat.callback import StreamToStdout + + # Create a ChatModule instance + cm = ChatModule( + model="dist/Llama-2-7b-chat-hf-q4f16_1-MLC", + model_lib_path="dist/prebuilt_libs/Llama-2-7b-chat-hf/Llama-2-7b-chat-hf-q4f16_1-cuda.so" + # Vulkan on Linux: Llama-2-7b-chat-hf-q4f16_1-vulkan.so + # Metal on macOS: Llama-2-7b-chat-hf-q4f16_1-metal.so + # Other platforms: Llama-2-7b-chat-hf-q4f16_1-{backend}.{suffix} + ) + + # You can change to other models that you downloaded + # Model variants of the same architecture can reuse the same model library + # Here WizardMath reuses Mistral's model library + # cm = ChatModule( + # model="dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC", # or "dist/WizardMath-7B-V1.1-q4f16_1-MLC" + # model_lib_path="dist/prebuilt_libs/Mistral-7B-Instruct-v0.2/Mistral-7B-Instruct-v0.2-q4f16_1-cuda.so" + # ) + + # Generate a response for a given prompt + output = cm.generate( + prompt="What is the meaning of life?", + progress_callback=StreamToStdout(callback_interval=2), + ) + + # Print prefill and decode performance statistics + print(f"Statistics: {cm.stats()}\n") + + output = cm.generate( + prompt="How many points did you list out?", + progress_callback=StreamToStdout(callback_interval=2), + ) + + # Reset the chat module by + # cm.reset_chat() + + +Now run the Python file to start the chat + +.. code:: bash + + python sample_mlc_chat.py + + +.. collapse:: See output + + .. code:: + + Using model folder: ./dist/prebuilt/mlc-chat-Llama-2-7b-chat-hf-q4f16_1 + Using mlc chat config: ./dist/prebuilt/mlc-chat-Llama-2-7b-chat-hf-q4f16_1/mlc-chat-config.json + Using library model: ./dist/prebuilt/lib/Llama-2-7b-chat-hf-q4f16_1-cuda.so + + Thank you for your question! The meaning of life is a complex and subjective topic that has been debated by philosophers, theologians, scientists, and many others for centuries. There is no one definitive answer to this question, as it can vary depending on a person's beliefs, values, experiences, and perspectives. + + However, here are some possible ways to approach the question: + + 1. Religious or spiritual beliefs: Many people believe that the meaning of life is to fulfill a divine or spiritual purpose, whether that be to follow a set of moral guidelines, to achieve spiritual enlightenment, or to fulfill a particular destiny. + 2. Personal growth and development: Some people believe that the meaning of life is to learn, grow, and evolve as individuals, to develop one's talents and abilities, and to become the best version of oneself. + 3. Relationships and connections: Others believe that the meaning of life is to form meaningful connections and relationships with others, to love and be loved, and to build a supportive and fulfilling social network. + 4. Contribution and impact: Some people believe that the meaning of life is to make a positive impact on the world, to contribute to society in a meaningful way, and to leave a lasting legacy. + 5. Simple pleasures and enjoyment: Finally, some people believe that the meaning of life is to simply enjoy the present moment, to find pleasure and happiness in the simple things in life, and to appreciate the beauty and wonder of the world around us. + + Ultimately, the meaning of life is a deeply personal and subjective question, and each person must find their own answer based on their own beliefs, values, and experiences. + + Statistics: prefill: 3477.5 tok/s, decode: 153.6 tok/s + + I listed out 5 possible ways to approach the question of the meaning of life. + +| + +**Running other models** + +Checkout the :doc:`/prebuilt_models` page to run other pre-compiled models. + +For models other than the prebuilt ones we provided: + +1. If the model is a variant to an existing model library (e.g. ``WizardMathV1.1`` and ``OpenHermes`` are variants of ``Mistral`` as + shown in the code snippet), follow :ref:`convert-weights-via-MLC` to convert the weights and reuse existing model libraries. +2. Otherwise, follow :ref:`compile-model-libraries` to compile both the model library and weights. + + +Configure MLCChat in Python +--------------------------- +If you have checked out :ref:`Configure MLCChat in JSON`, you would know +that you could configure MLCChat through various fields such as ``temperature``. We provide the +option of overriding any field you'd like in Python, so that you do not need to manually edit +``mlc-chat-config.json``. + +Since there are two concepts -- `MLCChat Configuration` and `Conversation Configuration` -- we correspondingly +provide two dataclasses :class:`mlc_chat.ChatConfig` and :class:`mlc_chat.ConvConfig`. + +We provide an example below. + +.. code:: python + + from mlc_chat import ChatModule, ChatConfig, ConvConfig + from mlc_chat.callback import StreamToStdout + + # Using a `ConvConfig`, we modify `system`, a field in the conversation template + # `system` refers to the prompt encoded before starting the chat + conv_config = ConvConfig(system='Please show as much happiness as you can when talking to me.') + + # We then include the `ConvConfig` instance in `ChatConfig` while overriding `max_gen_len` + # Note that `conv_config` is an optional subfield of `chat_config` + chat_config = ChatConfig(max_gen_len=256, conv_config=conv_config) + + # Using the `chat_config` we created, instantiate a `ChatModule` + cm = ChatModule( + chat_config=chat_config, + model="dist/Llama-2-7b-chat-hf-q4f16_1-MLC", + model_lib_path="dist/prebuilt_libs/Llama-2-7b-chat-hf/Llama-2-7b-chat-hf-q4f16_1-cuda.so" + # Vulkan on Linux: Llama-2-7b-chat-hf-q4f16_1-vulkan.so + # Metal on macOS: Llama-2-7b-chat-hf-q4f16_1-metal.so + # Other platforms: Llama-2-7b-chat-hf-q4f16_1-{backend}.{suffix} + ) + + output = cm.generate( + prompt="What is one plus one?", + progress_callback=StreamToStdout(callback_interval=2), + ) + + # You could also pass in a `ConvConfig` instance to `reset_chat()` + conv_config = ConvConfig(system='Please show as much sadness as you can when talking to me.') + chat_config = ChatConfig(max_gen_len=128, conv_config=conv_config) + cm.reset_chat(chat_config) + + output = cm.generate( + prompt="What is one plus one?", + progress_callback=StreamToStdout(callback_interval=2), + ) + + +.. collapse:: See output + + .. code:: + + Using model folder: ./dist/prebuilt/mlc-chat-Llama-2-7b-chat-hf-q4f16_1 + Using mlc chat config: ./dist/prebuilt/mlc-chat-Llama-2-7b-chat-hf-q4f16_1/mlc-chat-config.json + Using library model: ./dist/prebuilt/lib/Llama-2-7b-chat-hf-q4f16_1-cuda.so + + Oh, wow, *excitedly* one plus one? *grinning* Well, let me see... *counting on fingers* One plus one is... *eureka* Two! + ... + + *Sobs* Oh, the tragedy of it all... *sobs* One plus one... *chokes back tears* It's... *gulps* it's... *breaks down in tears* TWO! + ... + +| + +.. note:: + You do not need to specify the entire ``ChatConfig`` or ``ConvConfig``. Instead, we will first + load all the fields defined in ``mlc-chat-config.json``, a file required when instantiating + a :class:`mlc_chat.ChatModule`. Then, we will load in the optional ``ChatConfig`` you provide, overriding the + fields specified. + + It is also worth noting that ``ConvConfig`` itself is overriding the original conversation template + specified by the field ``conv_template`` in the chat configuration. Learn more about it in + :ref:`Configure MLCChat in JSON`. + +Raw Text Generation in Python +----------------------------- + +Raw text generation allows the user to have more flexibility over his prompts, +without being forced to create a new conversational template, making prompt customization easier. +This serves other demands for APIs to handle LLM generation without the usual system prompts and other items. + +We provide an example below. + +.. code:: python + + from mlc_chat import ChatModule, ChatConfig, ConvConfig + from mlc_chat.callback import StreamToStdout + + # Use a `ConvConfig` to define the generation settings + # Since the "LM" template only supports raw text generation, + # System prompts will not be executed even if provided + conv_config = ConvConfig(stop_tokens=[2,], add_bos=True, stop_str="[INST]") + + # Note that `conv_config` is an optional subfield of `chat_config` + # The "LM" template serves the basic purposes of raw text generation + chat_config = ChatConfig(conv_config=conv_config, conv_template="LM") + + # Using the `chat_config` we created, instantiate a `ChatModule` + cm = ChatModule( + chat_config=chat_config, + model="dist/Llama-2-7b-chat-hf-q4f16_1-MLC", + model_lib_path="dist/prebuilt_libs/Llama-2-7b-chat-hf/Llama-2-7b-chat-hf-q4f16_1-cuda.so" + # Vulkan on Linux: Llama-2-7b-chat-hf-q4f16_1-vulkan.so + # Metal on macOS: Llama-2-7b-chat-hf-q4f16_1-metal.so + # Other platforms: Llama-2-7b-chat-hf-q4f16_1-{backend}.{suffix} + ) + # To make the model follow conversations a chat structure should be provided + # This allows users to build their own prompts without building a new template + system_prompt = "<>\nYou are a helpful, respectful and honest assistant.\n<>\n\n" + inst_prompt = "What is mother nature?" + + # Concatenate system and instruction prompts, and add instruction tags + output = cm.generate( + prompt=f"[INST] {system_prompt+inst_prompt} [/INST]", + progress_callback=StreamToStdout(callback_interval=2), + ) + + # The LM template has no memory, so it will be reset every single generation + # In this case the model will just follow normal text completion + # because there isn't a chat structure + output = cm.generate( + prompt="Life is a quality that distinguishes", + progress_callback=StreamToStdout(callback_interval=2), + ) + +.. note:: + The ``LM`` is a template without memory, which means that every execution will be cleared. + Additionally, system prompts will not be run when instantiating a `mlc_chat.ChatModule`, + unless explicitly given inside the prompt. + +Stream Iterator in Python +------------------------- + +Stream Iterator gives users an option to stream generated text to the function that the API is called from, +instead of streaming to stdout, which could be a necessity when building services on top of MLC Chat. + +We provide an example below. + +.. code:: python + + from mlc_chat import ChatModule + from mlc_chat.callback import StreamIterator + + # Create a ChatModule instance + cm = ChatModule( + model="dist/Llama-2-7b-chat-hf-q4f16_1-MLC", + model_lib_path="dist/prebuilt_libs/Llama-2-7b-chat-hf/Llama-2-7b-chat-hf-q4f16_1-cuda.so" + # Vulkan on Linux: Llama-2-7b-chat-hf-q4f16_1-vulkan.so + # Metal on macOS: Llama-2-7b-chat-hf-q4f16_1-metal.so + # Other platforms: Llama-2-7b-chat-hf-q4f16_1-{backend}.{suffix} + ) + + # Stream to an Iterator + from threading import Thread + + stream = StreamIterator(callback_interval=2) + generation_thread = Thread( + target=cm.generate, + kwargs={"prompt": "What is the meaning of life?", "progress_callback": stream}, + ) + generation_thread.start() + + output = "" + for delta_message in stream: + output += delta_message + + generation_thread.join() + + +API Reference +------------- + +User can initiate a chat module by creating :class:`mlc_chat.ChatModule` class, which is a wrapper of the MLC-Chat model. +The :class:`mlc_chat.ChatModule` class provides the following methods: + +.. currentmodule:: mlc_chat + +.. autoclass:: ChatModule + :members: + :exclude-members: evaluate + :undoc-members: + :show-inheritance: + + .. automethod:: __init__ + +.. autoclass:: ChatConfig + :members: + +.. autoclass:: ConvConfig + :members: + +.. autoclass:: GenerationConfig + :members: diff --git a/docs/deploy/rest.rst b/docs/deploy/rest.rst new file mode 100644 index 0000000..d12029a --- /dev/null +++ b/docs/deploy/rest.rst @@ -0,0 +1,394 @@ +Rest API +======== + +.. contents:: Table of Contents + :local: + :depth: 2 + +We provide `REST API `_ +for a user to interact with MLC-Chat in their own programs. + +Install MLC-Chat Package +------------------------ + +The REST API is a part of the MLC-Chat package, which we have prepared pre-built :doc:`pip wheels <../install/mlc_llm>`. + +Verify Installation +^^^^^^^^^^^^^^^^^^^ + +.. code:: bash + + python -m mlc_chat.rest --help + +You are expected to see the help information of the REST API. + +.. _mlcchat_package_build_from_source: + +Optional: Build from Source +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If the prebuilt is unavailable on your platform, or you would like to build a runtime +that supports other GPU runtime than the prebuilt version. We can build a customized version +of mlc chat runtime. You only need to do this if you choose not to use the prebuilt. + +First, make sure you install TVM unity (following the instruction in :ref:`install-tvm-unity`). +You can choose to only pip install `mlc-ai-nightly` that comes with the tvm unity but skip `mlc-chat-nightly`. +Then please follow the instructions in :ref:`mlcchat_build_from_source` to build the necessary libraries. + +You can now use ``mlc_chat`` package by including the `python` directory to ``PYTHONPATH`` environment variable. + +.. code:: bash + + PYTHONPATH=python python -m mlc_chat.rest --help + +Launch the Server +----------------- + +To launch the REST server for MLC-Chat, run the following command in your terminal. + +.. code:: bash + + python -m mlc_chat.rest --model MODEL [--lib-path LIB_PATH] [--device DEVICE] [--host HOST] [--port PORT] + +--model The model folder after compiling with MLC-LLM build process. The parameter + can either be the model name with its quantization scheme + (e.g. ``Llama-2-7b-chat-hf-q4f16_1``), or a full path to the model + folder. In the former case, we will use the provided name to search + for the model folder over possible paths. +--lib-path An optional field to specify the full path to the model library file to use (e.g. a ``.so`` file). +--device The description of the device to run on. User should provide a string in the + form of 'device_name:device_id' or 'device_name', where 'device_name' is one of + 'cuda', 'metal', 'vulkan', 'rocm', 'opencl', 'auto' (automatically detect the + local device), and 'device_id' is the device id to run on. The default value is ``auto``, + with the device id set to 0 for default. +--host The host at which the server should be started, defaults to ``127.0.0.1``. +--port The port on which the server should be started, defaults to ``8000``. + +You can access ``http://127.0.0.1:PORT/docs`` (replace ``PORT`` with the port number you specified) to see the list of +supported endpoints. + +API Endpoints +------------- + +The REST API provides the following endpoints: + +.. http:get:: /v1/completions + +------------------------------------------------ + + Get a completion from MLC-Chat using a prompt. + +**Request body** + +**model**: *str* (required) + The model folder after compiling with MLC-LLM build process. The parameter + can either be the model name with its quantization scheme + (e.g. ``Llama-2-7b-chat-hf-q4f16_1``), or a full path to the model + folder. In the former case, we will use the provided name to search + for the model folder over possible paths. +**prompt**: *str* (required) + A list of chat messages. The last message should be from the user. +**stream**: *bool* (optional) + Whether to stream the response. If ``True``, the response will be streamed + as the model generates the response. If ``False``, the response will be + returned after the model finishes generating the response. +**temperature**: *float* (optional) + The temperature applied to logits before sampling. The default value is + ``0.7``. A higher temperature encourages more diverse outputs, while a + lower temperature produces more deterministic outputs. +**top_p**: *float* (optional) + This parameter determines the set of tokens from which we sample during + decoding. The default value is set to ``0.95``. At each step, we select + tokens from the minimal set that has a cumulative probability exceeding + the ``top_p`` parameter. + + For additional information on top-p sampling, please refer to this blog + post: https://huggingface.co/blog/how-to-generate#top-p-nucleus-sampling. +**repetition_penalty**: *float* (optional) + The repetition penalty controls the likelihood of the model generating + repeated texts. The default value is set to ``1.0``, indicating that no + repetition penalty is applied. Increasing the value reduces the + likelihood of repeat text generation. However, setting a high + ``repetition_penalty`` may result in the model generating meaningless + texts. The ideal choice of repetition penalty may vary among models. + + For more details on how repetition penalty controls text generation, please + check out the CTRL paper (https://arxiv.org/pdf/1909.05858.pdf). +**presence_penalty**: *float* (optional) + Positive values penalize new tokens if they are already present in the text so far, + decreasing the model's likelihood to repeat tokens. +**frequency_penalty**: *float* (optional) + Positive values penalize new tokens based on their existing frequency in the text so far, + decreasing the model's likelihood to repeat tokens. +**mean_gen_len**: *int* (optional) + The approximated average number of generated tokens in each round. Used + to determine whether the maximum window size would be exceeded. +**max_gen_len**: *int* (optional) + This parameter determines the maximum length of the generated text. If it is + not set, the model will generate text until it encounters a stop token. + +------------------------------------------------ + +**Returns** + If ``stream`` is set to ``False``, the response will be a ``CompletionResponse`` object. + If ``stream`` is set to ``True``, the response will be a stream of ``CompletionStreamResponse`` objects. + + +.. http:get:: /v1/chat/completions + +------------------------------------------------ + + Get a response from MLC-Chat using a prompt, either with or without streaming. + +**Request body** + +**model**: *str* (required) + The model folder after compiling with MLC-LLM build process. The parameter + can either be the model name with its quantization scheme + (e.g. ``Llama-2-7b-chat-hf-q4f16_1``), or a full path to the model + folder. In the former case, we will use the provided name to search + for the model folder over possible paths. +**messages**: *list[ChatMessage]* (required) + A list of chat messages. The last message should be from the user. +**stream**: *bool* (optional) + Whether to stream the response. If ``True``, the response will be streamed + as the model generates the response. If ``False``, the response will be + returned after the model finishes generating the response. +**temperature**: *float* (optional) + The temperature applied to logits before sampling. The default value is + ``0.7``. A higher temperature encourages more diverse outputs, while a + lower temperature produces more deterministic outputs. +**top_p**: *float* (optional) + This parameter determines the set of tokens from which we sample during + decoding. The default value is set to ``0.95``. At each step, we select + tokens from the minimal set that has a cumulative probability exceeding + the ``top_p`` parameter. + + For additional information on top-p sampling, please refer to this blog + post: https://huggingface.co/blog/how-to-generate#top-p-nucleus-sampling. +**repetition_penalty**: *float* (optional) + The repetition penalty controls the likelihood of the model generating + repeated texts. The default value is set to ``1.0``, indicating that no + repetition penalty is applied. Increasing the value reduces the + likelihood of repeat text generation. However, setting a high + ``repetition_penalty`` may result in the model generating meaningless + texts. The ideal choice of repetition penalty may vary among models. + + For more details on how repetition penalty controls text generation, please + check out the CTRL paper (https://arxiv.org/pdf/1909.05858.pdf). +**presence_penalty**: *float* (optional) + Positive values penalize new tokens if they are already present in the text so far, + decreasing the model's likelihood to repeat tokens. +**frequency_penalty**: *float* (optional) + Positive values penalize new tokens based on their existing frequency in the text so far, + decreasing the model's likelihood to repeat tokens. +**mean_gen_len**: *int* (optional) + The approximated average number of generated tokens in each round. Used + to determine whether the maximum window size would be exceeded. +**max_gen_len**: *int* (optional) + This parameter determines the maximum length of the generated text. If it is + not set, the model will generate text until it encounters a stop token. +**n**: *int* (optional) + This parameter determines the number of text samples to generate. The default + value is ``1``. Note that this parameter is only used when ``stream`` is set to + ``False``. +**stop**: *str* or *list[str]* (optional) + When ``stop`` is encountered, the model will stop generating output. + It can be a string or a list of strings. If it is a list of strings, the model + will stop generating output when any of the strings in the list is encountered. + Note that this parameter does not override the default stop string of the model. + +------------------------------------------------ + +**Returns** + If ``stream`` is set to ``False``, the response will be a ``ChatCompletionResponse`` object. + If ``stream`` is set to ``True``, the response will be a stream of ``ChatCompletionStreamResponse`` objects. + +.. http:get:: /chat/reset + + Reset the chat. + +.. http:get:: /stats + + Get the latest runtime stats (encode/decode speed). + +.. http:get:: /verbose_stats + + Get the verbose runtime stats (encode/decode speed, total runtime). + + +Request Objects +--------------- + +**ChatMessage** + +**role**: *str* (required) + The role(author) of the message. It can be either ``user`` or ``assistant``. +**content**: *str* (required) + The content of the message. +**name**: *str* (optional) + The name of the author of the message. + +Response Objects +---------------- + +**CompletionResponse** + +**id**: *str* + The id of the completion. +**object**: *str* + The object name ``text.completion``. +**created**: *int* + The time when the completion is created. +**choices**: *list[CompletionResponseChoice]* + A list of choices generated by the model. +**usage**: *UsageInfo* or *None* + The usage information of the model. + +------------------------------------------------ + +**CompletionResponseChoice** + +**index**: *int* + The index of the choice. +**text**: *str* + The message generated by the model. +**finish_reason**: *str* + The reason why the model finishes generating the message. It can be either + ``stop`` or ``length``. + + +------------------------------------------------ + +**CompletionStreamResponse** + +**id**: *str* + The id of the completion. +**object**: *str* + The object name ``text.completion.chunk``. +**created**: *int* + The time when the completion is created. +**choices**: *list[ChatCompletionResponseStreamhoice]* + A list of choices generated by the model. + +------------------------------------------------ + +**ChatCompletionResponseStreamChoice** + +**index**: *int* + The index of the choice. +**text**: *str* + The message generated by the model. +**finish_reason**: *str* + The reason why the model finishes generating the message. It can be either + ``stop`` or ``length``. + +------------------------------------------------ + +**ChatCompletionResponse** + +**id**: *str* + The id of the completion. +**object**: *str* + The object name ``chat.completion``. +**created**: *int* + The time when the completion is created. +**choices**: *list[ChatCompletionResponseChoice]* + A list of choices generated by the model. +**usage**: *UsageInfo* or *None* + The usage information of the model. + +------------------------------------------------ + +**ChatCompletionResponseChoice** + +**index**: *int* + The index of the choice. +**message**: *ChatMessage* + The message generated by the model. +**finish_reason**: *str* + The reason why the model finishes generating the message. It can be either + ``stop`` or ``length``. + +------------------------------------------------ + +**ChatCompletionStreamResponse** + +**id**: *str* + The id of the completion. +**object**: *str* + The object name ``chat.completion.chunk``. +**created**: *int* + The time when the completion is created. +**choices**: *list[ChatCompletionResponseStreamhoice]* + A list of choices generated by the model. + +------------------------------------------------ + +**ChatCompletionResponseStreamChoice** + +**index**: *int* + The index of the choice. +**delta**: *DeltaMessage* + The delta message generated by the model. +**finish_reason**: *str* + The reason why the model finishes generating the message. It can be either + ``stop`` or ``length``. + +------------------------------------------------ + + +**DeltaMessage** + +**role**: *str* + The role(author) of the message. It can be either ``user`` or ``assistant``. +**content**: *str* + The content of the message. + +------------------------------------------------ + + +Use REST API in your own program +-------------------------------- + +Once you have launched the REST server, you can use the REST API in your own program. Below is an example of using REST API to interact with MLC-Chat in Python (suppose the server is running on ``http://127.0.0.1:8000/``): + +.. code:: bash + + import requests + import json + + # Get a response using a prompt without streaming + payload = { + "model": "vicuna-v1-7b", + "messages": [{"role": "user", "content": "Write a haiku"}], + "stream": False + } + r = requests.post("http://127.0.0.1:8000/v1/chat/completions", json=payload) + print(f"Without streaming:\n{r.json()['choices'][0]['message']['content']}\n") + + # Reset the chat + r = requests.post("http://127.0.0.1:8000/chat/reset", json=payload) + print(f"Reset chat: {str(r)}\n") + + # Get a response using a prompt with streaming + payload = { + "model": "vicuna-v1-7b", + "messages": [{"role": "user", "content": "Write a haiku"}], + "stream": True + } + with requests.post("http://127.0.0.1:8000/v1/chat/completions", json=payload, stream=True) as r: + print(f"With streaming:") + for chunk in r: + content = json.loads(chunk[6:-2])["choices"][0]["delta"].get("content", "") + print(f"{content}", end="", flush=True) + print("\n") + + # Get the latest runtime stats + r = requests.get("http://127.0.0.1:8000/stats") + print(f"Runtime stats: {r.json()}\n") + +Please check `example folder `__ for more examples using REST API. + +.. note:: + The REST API is a uniform interface that supports multiple languages. You can also utilize the REST API in languages other than Python. diff --git a/docs/get_started/mlc_chat_config.rst b/docs/get_started/mlc_chat_config.rst new file mode 100644 index 0000000..c583c16 --- /dev/null +++ b/docs/get_started/mlc_chat_config.rst @@ -0,0 +1,254 @@ +.. _configure-mlc-chat-json: + +Configure MLCChat in JSON +========================= + +``mlc-chat-config.json`` is required for both compile-time and runtime, hence serving two purposes: + +1. Specify how we compile a model (shown in :ref:`compile-model-libraries`), and +2. Specify conversation behavior in runtime. + +**This page focuses on the second purpose.** We explain the components of a chat +configuration and how to customize them by modifying the file. Additionally, +the runtimes also provide APIs to optionally override some of the configurations. + +In runtime, this file is stored under the directory of each compiled model +(e.g. `RedPajama chat config `__). + + +.. _struct-mlc-chat-conv: + +Structure of MLCChat Configuration +---------------------------------- + +Below is the ``mlc-chat-config.json`` file corresponding to Llama2 model: + +.. code:: json + + // mlc-chat-config.json + { + // 1. Metadata used to specify how to compile a model + "model_type": "llama", + "quantization": "q4f16_1", + "version": "0.1.0", + "model_config": { + "hidden_size": 4096, + "intermediate_size": 11008, + // more fields here... + }, + "vocab_size": 32000, + "context_window_size": 4096, + "sliding_window_size": -1, + "prefill_chunk_size": 4096, + "tensor_parallel_shards": 1, + + // 2. Tokenizer-related fields + "pad_token_id": 0, + "bos_token_id": 1, + "eos_token_id": 2, + "tokenizer_files": [ + "tokenizer.model", + "tokenizer.json", + "tokenizer_config.json" + ] + + // 3. Chat related fields that affect runtime behavior + "mean_gen_len": 128, + "max_gen_len": 512, + "shift_fill_factor": 0.3, + "temperature": 0.6, + "repetition_penalty": 1.0, + "top_p": 0.9, + "conv_template": "llama-2", + } + +.. note:: + Fields in the first part of ``mlc-chat-config.json`` (e.g. ``context-window-size``) + is only for compile-time. Changing them during runtime may lead to unexpected behavior. + +**As shown above, the file is divided into three parts. We focus on the third part, which +can be customized to change the behavior of the model.** + +``conv_template`` + The name of the conversation template that this chat uses. For more information, please refer to :ref:`conversation structure `. + +``temperature`` + The temperature applied to logits before sampling. The default value is ``0.7``. A higher temperature encourages more diverse outputs, while a lower temperature produces more deterministic outputs. + +``repetition_penalty`` + The repetition penalty controls the likelihood of the model generating repeated texts. The default value is set to ``1.0``, indicating that no repetition penalty is applied. Increasing the value reduces the likelihood of repeat text generation. However, setting a high ``repetition_penalty`` may result in the model generating meaningless texts. The ideal choice of repetition penalty may vary among models. + + For more details on how repetition penalty controls text generation, please check out the `CTRL paper `_. + +``top_p`` + This parameter determines the set of tokens from which we sample during decoding. The default value is set to ``0.95``. At each step, we select tokens from the minimal set that has a cumulative probability exceeding the ``top_p`` parameter. + + For additional information on top-p sampling, please refer to this `blog post `_. + +``mean_gen_len`` + The approximated average number of generated tokens in each round. Used to determine whether the maximum window size would be exceeded. + +``max_gen_len`` + This parameter determines the maximum length of the generated text. If it is not set, the model will generate text until it encounters a stop token. + +``shift_fill_factor`` + The fraction of maximum window size to shift when it is exceeded. + +.. _struct-conv: + +Conversation Structure +^^^^^^^^^^^^^^^^^^^^^^ + +There are three options of loading conversation configurations: + +1. Load from pre-defined conversation templates. +2. Load from JSON format conversation configuration. +3. First load from pre-defined conversation templates, then override some fields with JSON format conversation configuration. + +.. _load-predefined-conv-template: + +Load from Pre-defined Conversation Templates +-------------------------------------------- + +MLC-LLM provided a set of pre-defined conversation templates, which you can directly use by specifying the template name in ``conv_template`` field in the ``mlc-chat-config.json``, below is a list (not complete) of supported conversation templates: + +- ``llama-2`` +- ``vicuna_v1.1`` +- ``redpajama_chat`` +- ``rwkv`` +- ``dolly`` +- ... + +Please refer to `conv_template.cc `_ for the full list of supported templates and their implementations. + +.. _load-json-conv-config: + +Load from JSON Conversation Configuration +----------------------------------------- + +Below is a generic structure of a JSON conversation configuration (we use vicuna as an example): + +.. code:: json + + // mlc-chat-config.json + { + // ... + "conv_config": { + "seps": [ + " ", + "<\/s>" + ], + "stop_tokens": [ + 2 + ], + "offset": 0, + "separator_style": 0, + "messages": [], + "stop_str": "<\/s>", + "roles": [ + "USER", + "ASSISTANT" + ], + "role_msg_sep": ": ", + "role_empty_sep": ": ", + "system": "A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions.", + "add_bos": true, + "name": "vicuna_v1.1" + } + } + +``roles`` + An array that describes the role names of the user and the model. These names are specific to the model being used. +``system`` + The prompt encoded before starting the chat. It can be customized to a user-defined prompt. +``add_bos`` + Determines whether a beginning-of-string (bos) token should be added before the input tokens. +``stop_str`` + When the ``stop_str`` is encountered, the model will stop generating output. +``stop_tokens`` + A list of token IDs that act as stop tokens. +``seps`` + An array of strings indicating the separators to be used after a user message and a model message respectively. +``messages`` + The chat history represented as an array of string pairs in the following format: ``[[role_0, msg_0], [role_1, msg_1], ...]`` +``offset`` + The offset used to begin the chat from the chat history. When ``offset`` is not ``0``, ``messages[0:offset-1]`` will be encoded. +``separator_style`` + Specifies whether we are in chat-bot mode (``0``) or pure LM prompt mode (``1``). +``role_msg_sep`` + A string indicating the separator between a role and a message. +``role_empty_sep`` + A string indicating the separator to append to a role when there is no message yet. + + +When the value of ``separator_style`` is set to 0 (or ``kSepRoleMsg``), each round of conversation follows the format: + +.. code:: text + + {role[0]}{separator_style}{user_input}{sep[0]} + {role[1]}{separator_style}{model_output}{sep[1]} + +Here, ``{user_input}`` represents the input provided by the user, and ``{model_output}`` represents the output generated by the model. + +On the other hand, if the value of ``separator_style`` is set to 1 (or ``kLM``), the model is not aware of the chat history and generates the response immediately after the user input prompt: + + +.. code:: text + + {user_prompt}{model_output} + + +.. _customize-conv-template: + +Customize Conversation Template +------------------------------- + +In the ``mlc-chat-config.json`` file, you have the option to specify both ``conv_template`` and ``conv_config``. MLC-LLM will first load the predefined template with the name specified in ``conv_template`` and then override some of the configurations specified in ``conv_config``. It's important to note that the configurations in ``conv_config`` don't need to be complete, allowing for partial updates. + +.. _example_replace_system_prompt: + +Example 1: Replace System Prompt +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you're tired of the default system prompt, here's an example of how you can replace it: + +.. code:: json + + // mlc-chat-config.json + { + // ... + "conv_template": "vicuna_v1.1", + "conv_config": { + "system": "You are not Vicuna, your name is Guanaco, now let's chat!" + } + } + + +The next time you run ``mlc_chat`` CLI, you will start a chat with Vicuna using a new system prompt. + +.. _example_resume_chat_history: + +Example 2: Resume from Chat History +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following example demonstrates how to chat with Vicuna and resume from a chat history: + +.. code:: json + + // mlc-chat-config.json + { + // ... + "conv_template": "vicuna_v1.1", + "conv_config": { + "messages": [ + ["USER", "Suppose we already have projects llama, alpaca and vicuna, what do you think would be a great name for the next project?"], + ["ASSISTANT", "Based on the previous projects, a possible name for the next project could be \"cervidae\" which is the scientific name for deer family. This name reflects the collaboration and teamwork involved in the development of the project, and also nods to the previous projects that have been developed by the team."], + ["USER", "I like cervidae, but the name is too long!"], + ["ASSISTANT", "In that case, a shorter and catchier name for the next project could be \"DeerRun\" which plays on the idea of the project being fast and efficient, just like a deer running through the woods. This name is memorable and easy to pronounce, making it a good choice for a project name."] + ], + "offset": 4 + } + } + + +The next time you start ``mlc_chat`` CLI, or use Python API, you will initiate a chat with Vicuna and resume from the provided chat history. diff --git a/docs/get_started/project_overview.rst b/docs/get_started/project_overview.rst new file mode 100644 index 0000000..2b6ff74 --- /dev/null +++ b/docs/get_started/project_overview.rst @@ -0,0 +1,88 @@ +.. _project-overview: + +Project Overview +================ + +This page introduces high-level project concepts to help us use and customize MLC LLM. +The MLC-LLM project consists of three distinct submodules: model definition, model compilation, and runtimes. + +.. figure:: /_static/img/project-structure.svg + :width: 600 + :align: center + :alt: Project Structure + + Three independent submodules in MLC LLM + +**➀ Model definition in Python.** MLC offers a variety of pre-defined architectures, such as Llama (e.g., Llama2, Vicuna, OpenLlama, Wizard), GPT-NeoX (e.g., RedPajama, Dolly), RNNs (e.g., RWKV), and GPT-J (e.g., MOSS). Model developers could solely define the model in pure Python, without having to touch code generation and runtime. + +**➁ Model compilation in Python.** Models are compiled by :doc:`TVM Unity ` compiler, where the compilation is configured in pure Python. MLC LLM quantizes and exports the Python-based model to a model library and quantized model weights. Quantization and optimization algorithms can be developed in pure Python to compress and accelerate LLMs for specific usecases. + +**➂ Platform-native runtimes.** Variants of MLCChat are provided on each platform: **C++** for command line, **Javascript** for web, **Swift** for iOS, and **Java** for Android, configurable with a JSON chat config. App developers only need to familiarize with the platform-naive runtimes to integrate MLC-compiled LLMs into their projects. + +.. _terminologies: + +Terminologies +------------- + +It is helpful for us to familiarize the basic terminologies used in the MLC chat applications. Below are the +three things you need to run a model with MLC. + +- **model lib**: The model library refers to the executable libraries that enable + the execution of a specific model architecture. On Linux and M-chip macOS, these libraries have the suffix + ``.so``; on intel macOS, the suffix is ``.dylib``; on Windows, the library file ends with ``.dll``; + on web browser, the library suffix is ``.wasm``. (see `binary-mlc-llm-libs `__). + +- **model weights**: The model weight is a folder that contains the quantized neural network weights + of the language models as well as the tokenizer configurations. (e.g. `Llama-2-7b-chat-hf-q4f16_1-MLC `__) + +- **chat config**: The chat configuration includes settings that allow customization of parameters such as temperature and system prompt. + The default chat config usually resides in the same directory as model weights. (e.g. see ``Llama-2-7b-chat-hf-q4f16_1``'s + `mlc-chat-config.json `__) + +Model Preparation +----------------- + + +There are several ways to prepare the model weights and model lib. + +- :ref:`Model Prebuilts` contains models that can be directly used. +- You can also :doc:`run model compilation ` for model weight variants for given supported architectures. +- Finally, you can incorporate a new model architecture/inference logic following :doc:`Define New Models `. + +A default chat config usually comes with the model weight directory. You can further customize +the system prompt, temperature, and other options by modifying the JSON file. +MLC chat runtimes also provide API to override these options during model reload. +Please refer to :doc:`/get_started/mlc_chat_config` for more details. + + +Runtime Flow Overview +--------------------- + +Once the model weights, model library, and chat configuration are prepared, an MLC chat runtime can consume them as an engine to drive a chat application. +The diagram below shows a typical workflow for a MLC chat application. + +.. image:: https://raw.githubusercontent.com/mlc-ai/web-data/a05d4598bae6eb5a3133652d5cc0323ced3b0e17/images/mlc-llm/tutorials/mlc-llm-flow-slm.svg + :width: 90% + :align: center + +On the right side of the figure, you can see pseudo-code illustrating the structure of an MLC chat API during the execution of a chat app. +Typically, there is a ``ChatModule`` that manages the model. We instantiate the chat app with two files: the model weights (which include an ``mlc-chat-config.json``) +and the model library. We also have an optional chat configuration, which allows for overriding settings such as the system prompt and temperature. + +All MLC runtimes, including iOS, Web, CLI, and others, use these three elements. +All the runtime can read the same model weight folder. The packaging of the model libraries may vary depending on the runtime. +For the CLI, the model libraries are stored in a DLL directory. +iOS and Android include pre-packaged model libraries within the app due to dynamic loading restrictions. +WebLLM utilizes URLs of local or Internet-hosted WebAssembly (Wasm) files. + +What to Do Next +--------------- + +Thank you for reading and learning the high-level concepts. +Moving next, feel free to check out documents on the left navigation panel and +learn about topics you are interested in. + +- :doc:`/get_started/mlc_chat_config` shows how to configure specific chat behavior. +- Build and Deploy App section contains guides to build apps + and platform-specific MLC chat runtimes. +- Compile models section provides guidelines to convert model weights and produce model libs. diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..15ad6ca --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,233 @@ +👋 Welcome to MLC LLM +===================== + +`Discord `_ | `GitHub `_ + +Machine Learning Compilation for Large Language Models (MLC LLM) is a high-performance universal deployment solution that allows native deployment of any large language models with native APIs with compiler acceleration. The mission of this project is to enable everyone to develop, optimize and deploy AI models natively on everyone's devices with ML compilation techniques. + +.. _get_started: + +Getting Started +--------------- + +To begin with, try out MLC LLM support for int4-quantized Llama2 7B. +It is recommended to have at least 6GB free VRAM to run it. + +.. tabs:: + + .. tab:: Python + + **Install MLC Chat Python**. :doc:`MLC LLM ` is available via pip. + It is always recommended to install it in an isolated conda virtual environment. + + **Download pre-quantized weights**. The commands below download the int4-quantized Llama2-7B from HuggingFace: + + .. code:: bash + + git lfs install && mkdir dist/ + git clone https://huggingface.co/mlc-ai/Llama-2-7b-chat-hf-q4f16_1-MLC \ + dist/Llama-2-7b-chat-hf-q4f16_1-MLC + + **Download pre-compiled model library**. The pre-compiled model library is available as below: + + .. code:: bash + + git clone https://github.com/mlc-ai/binary-mlc-llm-libs.git dist/prebuilt_libs + + **Run in Python.** The following Python script showcases the Python API of MLC LLM and its stream capability: + + .. code:: python + + from mlc_chat import ChatModule + from mlc_chat.callback import StreamToStdout + + cm = ChatModule( + model="dist/Llama-2-7b-chat-hf-q4f16_1-MLC", + model_lib_path="dist/prebuilt_libs/Llama-2-7b-chat-hf/Llama-2-7b-chat-hf-q4f16_1-cuda.so" + # Vulkan on Linux: Llama-2-7b-chat-hf-q4f16_1-vulkan.so + # Metal on macOS: Llama-2-7b-chat-hf-q4f16_1-metal.so + # Other platforms: Llama-2-7b-chat-hf-q4f16_1-{backend}.{suffix} + ) + cm.generate(prompt="What is the meaning of life?", progress_callback=StreamToStdout(callback_interval=2)) + + **Colab walkthrough.** A Jupyter notebook on `Colab `_ + is provided with detailed walkthrough of the Python API. + + **Documentation and tutorial.** Python API reference and its tutorials are `available online `_. + + .. figure:: https://raw.githubusercontent.com/mlc-ai/web-data/main/images/mlc-llm/tutorials/python-api.jpg + :width: 600 + :align: center + + MLC LLM Python API + + .. tab:: Command Line + + **Install MLC Chat CLI.** MLC Chat CLI is available via conda using the command below. + It is always recommended to install it in an isolated conda virtual environment. + For Windows/Linux users, make sure to have latest :ref:`Vulkan driver ` installed. + + .. code:: bash + + conda create -n mlc-chat-venv -c mlc-ai -c conda-forge mlc-chat-cli-nightly + conda activate mlc-chat-venv + + **Download pre-quantized weights**. The comamnds below download the int4-quantized Llama2-7B from HuggingFace: + + .. code:: bash + + git lfs install && mkdir dist/ + git clone https://huggingface.co/mlc-ai/Llama-2-7b-chat-hf-q4f16_1-MLC \ + dist/Llama-2-7b-chat-hf-q4f16_1-MLC + + **Download pre-compiled model library**. The pre-compiled model library is available as below: + + .. code:: bash + + git clone https://github.com/mlc-ai/binary-mlc-llm-libs.git dist/prebuilt_libs + + **Run in command line**. + + .. code:: bash + + mlc_chat chat HF://mlc-ai/Llama-2-7b-chat-hf-q4f16_1-MLC + + .. figure:: https://raw.githubusercontent.com/mlc-ai/web-data/main/images/mlc-llm/tutorials/Llama2-macOS.gif + :width: 500 + :align: center + + MLC LLM on CLI + + .. note:: + The MLC Chat CLI package is only built with Vulkan (Windows/Linux) and Metal (macOS). + To use other GPU backends such as CUDA and ROCm, please use the prebuilt Python package or build from source. + + .. tab:: Web Browser + + `WebLLM `__. MLC LLM generates performant code for WebGPU and WebAssembly, + so that LLMs can be run locally in a web browser without server resources. + + **Download pre-quantized weights**. This step is self-contained in WebLLM. + + **Download pre-compiled model library**. WebLLM automatically downloads WebGPU code to execute. + + **Check browser compatibility**. The latest Google Chrome provides WebGPU runtime and `WebGPU Report `__ as a useful tool to verify WebGPU capabilities of your browser. + + .. figure:: https://blog.mlc.ai/img/redpajama/web.gif + :width: 300 + :align: center + + MLC LLM on Web + + .. tab:: iOS + + **Install MLC Chat iOS**. It is available on AppStore: + + .. image:: https://developer.apple.com/assets/elements/badges/download-on-the-app-store.svg + :width: 135 + :target: https://apps.apple.com/us/app/mlc-chat/id6448482937 + + | + + **Requirement**. Llama2-7B model needs an iOS device with a minimum of 6GB RAM, whereas the RedPajama-3B model runs with at least 4GB RAM. + + **Tutorial and source code**. The source code of the iOS app is fully `open source `__, + and a :doc:`tutorial ` is included in documentation. + + .. figure:: https://blog.mlc.ai/img/redpajama/ios.gif + :width: 300 + :align: center + + MLC Chat on iOS + + .. tab:: Android + + **Install MLC Chat Android**. A prebuilt is available as an APK: + + .. image:: https://seeklogo.com/images/D/download-android-apk-badge-logo-D074C6882B-seeklogo.com.png + :width: 135 + :target: https://github.com/mlc-ai/binary-mlc-llm-libs/raw/main/mlc-chat.apk + + | + + **Requirement**. Llama2-7B model needs a device with a minimum of 6GB RAM, whereas the RedPajama-3B model runs with at least 4GB RAM. + The demo is tested on + + - Samsung S23 with Snapdragon 8 Gen 2 chip + - Redmi Note 12 Pro with Snapdragon 685 + - Google Pixel phones + + **Tutorial and source code**. The source code of the android app is fully `open source `__, + and a :doc:`tutorial ` is included in documentation. + + .. figure:: https://blog.mlc.ai/img/android/android-recording.gif + :width: 300 + :align: center + + MLC LLM on Android + + +.. toctree:: + :maxdepth: 1 + :caption: Get Started + :hidden: + + get_started/project_overview.rst + get_started/mlc_chat_config.rst + +.. toctree:: + :maxdepth: 1 + :caption: Build and Deploy Apps + :hidden: + + deploy/javascript.rst + deploy/rest.rst + deploy/cli.rst + deploy/python.rst + deploy/ios.rst + deploy/android.rst + +.. toctree:: + :maxdepth: 1 + :caption: Compile Models + :hidden: + + compilation/convert_weights.rst + compilation/compile_models.rst + compilation/define_new_models.rst + compilation/configure_quantization.rst + +.. toctree:: + :maxdepth: 1 + :caption: Model Prebuilts + :hidden: + + prebuilt_models.rst + prebuilt_models_deprecated.rst + +.. toctree:: + :maxdepth: 1 + :caption: Dependency Installation + :hidden: + + install/tvm.rst + install/mlc_llm.rst + install/conda.rst + install/gpu.rst + install/emcc.rst + +.. toctree:: + :maxdepth: 1 + :caption: Community + :hidden: + + community/guideline.rst + community/faq.rst + + +.. toctree:: + :maxdepth: 1 + :caption: Privacy + :hidden: + + privacy.rst diff --git a/docs/install/conda.rst b/docs/install/conda.rst new file mode 100644 index 0000000..305c0f6 --- /dev/null +++ b/docs/install/conda.rst @@ -0,0 +1,66 @@ +Install Conda +============= + +MLC LLM does not depend on, but generally recommends conda as a generic dependency manager, primarily because it creates unified cross-platform experience to make windows/Linux/macOS development equally easy. Moreover, conda is python-friendly and provides all the python packages needed for MLC LLM, such as numpy. + +.. contents:: Table of Contents + :depth: 2 + + +Install Miniconda +----------------- + +**Use installer.** Miniconda, a minimal distribution of conda, comes with out-of-box installer across Windows/macOS/Linux. Please refer to its `official website `_ link for detailed instructions. + +**Set libmamba as the dependency solver.** The default dependency solver in conda could be slow in certain scenarios, and it is always recommended to upgrade it to libmamba, a faster solver. + +.. code-block:: bash + :caption: Set libmamba as the default solver + + # update conda + conda update --yes -n base -c defaults conda + # install `conda-libmamba-solver` + conda install --yes -n base conda-libmamba-solver + # set it as the default solver + conda config --set solver libmamba + +.. note:: + Conda is a generic dependency manager, which is not necessarily related to any Python distributions. + In fact, some of our tutorials recommends to use conda to install cmake, git and rust for its unified experience across OS platforms. + + +Validate installation +--------------------- + +**Step 1. Check conda-arch mismatch.** Nowadays macOS runs on two different architectures: arm64 and x86_64, which could particularly lead to many misuses in MLC LLM, where the error message hints about "architecture mismatch". Use the following command to make sure particular conda architecture is installed accordingly: + +.. code-block:: bash + :caption: Check conda architecture + + >>> conda info | grep platform + # for arm mac + platform : osx-arm64 + # for x86 mac + platform : osx-64 + +**Step 2. Check conda virtual environment.** If you have installed python in your conda virtual environment, make sure conda, Python and pip are all from this environment: + +.. code-block:: bash + :caption: Check conda virtual environment (macOS, Linux) + + >>> echo $CONDA_PREFIX + /.../miniconda3/envs/mlc-doc-venv + >>> which python + /.../miniconda3/envs/mlc-doc-venv/bin/python + >>> which pip + /.../miniconda3/envs/mlc-doc-venv/bin/pip + +.. code-block:: bat + :caption: Check conda virtual environment (Windows) + + >>> echo $Env:CONDA_PREFIX + \...\miniconda3\envs\mlc-doc-venv + >>> Get-Command python.exe + \...\miniconda3\envs\mlc-doc-venv\bin\python.exe + >>> Get-Command pip.exe + \...\miniconda3\envs\mlc-doc-venv\bin\pip.exe diff --git a/docs/install/emcc.rst b/docs/install/emcc.rst new file mode 100644 index 0000000..9320be4 --- /dev/null +++ b/docs/install/emcc.rst @@ -0,0 +1,64 @@ +.. _install-web-build: + +Install Wasm Build Environment +============================== + +This page describes the steps to setup build environment for WebAssembly and WebGPU builds. + +Step 1: Install EMSDK +--------------------- + +Emscripten is an LLVM-based compiler that compiles C/C++ source code to WebAssembly. +We need to install emscripten for webgpu build. + +- Please follow the installation instruction `here `__ + to install the latest emsdk. +- Source path/to/emsdk_env.sh so emcc is reachable from PATH and the command emcc works. + +Validate that emcc is accessible in shell + +.. code:: bash + + emcc --version + +Step 2: Set TVM_HOME +-------------------- + +We need to set a path to a tvm source in order to build tvm runtime. +Note that you do not need to build tvm unity from the source. The source here is only used to build the web runtime component. +Set environment variable in your shell startup profile in to point to ``3rdparty/tvm`` + +.. code:: bash + + export TVM_HOME=/path/to/3rdparty/tvm + + +Step 3: Prepare Wasm Runtime +---------------------------- + +First, we need to obtain a copy of the mlc-llm source code for the setup script + +.. code:: bash + + git clone https://github.com/mlc-ai/mlc-llm.git --recursive + cd mlc-llm + +Now we can prepare wasm runtime using the script in mlc-llm repo + +.. code:: bash + + ./scripts/prep_emcc_deps.sh + +We can then validate the outcome + +.. code:: bash + + >>> echo ${TVM_HOME} + + /path/set/in/step2 + + >>> ls -l ${TVM_HOME}/web/dist/wasm/*.bc + + tvmjs_support.bc + wasm_runtime.bc + webgpu_runtime.bc diff --git a/docs/install/gpu.rst b/docs/install/gpu.rst new file mode 100644 index 0000000..608c238 --- /dev/null +++ b/docs/install/gpu.rst @@ -0,0 +1,201 @@ +GPU Drivers and SDKs +==================== + +.. contents:: Table of Contents + :depth: 2 + +MLC LLM is a universal deployment solution that allows efficient CPU/GPU code generation without AutoTVM-based performance tuning. This section focuses on generic GPU environment setup and troubleshooting. + +CUDA +---- + +CUDA is required to compile and run models with CUDA backend. + +Installation +^^^^^^^^^^^^ + +If you have a NVIDIA GPU and you want to use models compiled with CUDA +backend, you should install CUDA, which can be downloaded from +`here `__. + +Validate Installation +^^^^^^^^^^^^^^^^^^^^^ + +To verify you have correctly installed CUDA runtime and NVIDIA driver, run ``nvidia-smi`` in command line and see if you can get the GPU information. + +ROCm +---- + +ROCm is required to compile and run models with ROCm backend. + +Installation +^^^^^^^^^^^^ + +Right now MLC LLM only supports ROCm 5.6. +If you have AMD GPU and you want to use models compiled with ROCm +backend, you should install ROCm 5.6 from `here `__. + +Validate Installation +^^^^^^^^^^^^^^^^^^^^^ + +To verify you have correctly installed ROCm 5.6, run ``rocm-smi`` in command line. +If you see the list of AMD devices printed out in a table, it means the ROCm is correctly installed. + +.. _vulkan_driver: + +Vulkan Driver +------------- + +Installation +^^^^^^^^^^^^ + +To run pre-trained models (e.g. pulled from MLC-AI's Hugging Face repository) compiled with Vulkan backend, you are expected to install Vulkan driver on your machine. + +Please check `this +page `__ and find the +Vulkan driver according to your GPU vendor. + +AMD Radeon and Radeon PRO +######################### + +For AMD Radeon and Radeon PRO users, please download AMD's drivers from official website (`Linux `__ / `Windows `__). +For Linux users, after you installed the ``amdgpu-install`` package, you can follow the instructions in its `documentation `__ to install +the driver. We recommend you installing ROCr OpenCL and PRO Vulkan (proprietary) for best performance, which can be done by running the following command: + +.. code:: bash + + amdgpu-install --usecase=graphics,opencl --opencl=rocr --vulkan=pro --no-32 + +Validate Installation +^^^^^^^^^^^^^^^^^^^^^ + +To verify whether Vulkan installation is successful or not, you are encouraged to install ``vulkaninfo``, below are the instructions to install ``vulkaninfo`` on different platforms: + +.. tabs :: + + .. code-tab :: bash Ubuntu/Debian + + sudo apt-get update + sudo apt-get install vulkan-tools + + .. code-tab :: bash Windows + + # It comes with your GPU driver + + .. code-tab :: bash Fedora + + sudo dnf install vulkan-tools + + .. code-tab :: bash Arch Linux + + sudo pacman -S vulkan-tools + # Arch Linux has maintained an awesome wiki page for Vulkan which you can refer to for troubleshooting: https://wiki.archlinux.org/title/Vulkan + + .. code-tab :: bash Other Distributions + + # Please install Vulkan SDK for your platform + # https://vulkan.lunarg.com/sdk/home + + +After installation, you can run ``vulkaninfo`` in command line and see if you can get the GPU information. + +.. note:: + WSL support for Windows is work-in-progress at the moment. Please do not use WSL on Windows to run Vulkan. + +Vulkan SDK +---------- + +Vulkan SDK is required for compiling models to Vulkan backend. To build TVM Unity compiler from source, you will need to install Vulkan SDK as a dependency, but our :doc:`pre-built wheels <../install/mlc_llm>` already ships with Vulkan SDK. + +Check Vulkan SDK installation guide according to your platform: + +.. tabs :: + + .. tab :: Windows + + `Getting Started with the Windows Tarball Vulkan SDK `__ + + .. tab :: Linux + + For Ubuntu user, please check + `Getting Started with the Ubuntu Vulkan SDK `__ + + For other Linux distributions, please check + `Getting Started with the Linux Tarball Vulkan SDK `__ + + .. tab :: Mac + + `Getting Started with the macOS Vulkan SDK `__ + +Please refer to installation and setup page for next steps to build TVM-Unity from source. + +OpenCL SDK +---------- + +OpenCL SDK is only required when you want to build your own models for OpenCL backend. Please refer to `OpenCL's Github Repository `__ for installation guide of OpenCL-SDK. + +Orange Pi 5 (RK3588 based SBC) +------------------------------ + +OpenCL SDK and Mali GPU driver is required to compile and run models for OpenCL backend. + +Installation +^^^^^^^^^^^^ + +* Download and install the Ubuntu 22.04 for your board from `here `__ + +* Download and install ``libmali-g610.so`` + +.. code-block:: bash + + cd /usr/lib && sudo wget https://github.com/JeffyCN/mirrors/raw/libmali/lib/aarch64-linux-gnu/libmali-valhall-g610-g6p0-x11-wayland-gbm.so + +* Check if file ``mali_csffw.bin`` exist under path ``/lib/firmware``, if not download it with command: + +.. code-block:: bash + + cd /lib/firmware && sudo wget https://github.com/JeffyCN/mirrors/raw/libmali/firmware/g610/mali_csffw.bin + +* Download OpenCL ICD loader and manually add libmali to ICD + +.. code-block:: bash + + sudo apt update + sudo apt install mesa-opencl-icd + sudo mkdir -p /etc/OpenCL/vendors + echo "/usr/lib/libmali-valhall-g610-g6p0-x11-wayland-gbm.so" | sudo tee /etc/OpenCL/vendors/mali.icd + +* Download and install ``libOpenCL`` + +.. code-block:: bash + + sudo apt install ocl-icd-opencl-dev + +* Download and install dependencies for Mali OpenCL + +.. code-block:: bash + + sudo apt install libxcb-dri2-0 libxcb-dri3-0 libwayland-client0 libwayland-server0 libx11-xcb1 + +* Download and install clinfo to check if OpenCL successfully installed + +.. code-block:: bash + + sudo apt install clinfo + +Validate Installation +^^^^^^^^^^^^^^^^^^^^^ + +To verify you have correctly installed OpenCL runtime and Mali GPU driver, run ``clinfo`` in command line and see if you can get the GPU information. +You are expect to see the following information: + +.. code-block:: bash + + $ clinfo + arm_release_ver: g13p0-01eac0, rk_so_ver: 3 + Number of platforms 2 + Platform Name ARM Platform + Platform Vendor ARM + Platform Version OpenCL 2.1 v1.g6p0-01eac0.2819f9d4dbe0b5a2f89c835d8484f9cd + Platform Profile FULL_PROFILE + ... diff --git a/docs/install/mlc_llm.rst b/docs/install/mlc_llm.rst new file mode 100644 index 0000000..004ee15 --- /dev/null +++ b/docs/install/mlc_llm.rst @@ -0,0 +1,240 @@ +.. _install-mlc-packages: + +Install MLC LLM Python Package +============================== + +.. contents:: Table of Contents + :local: + :depth: 2 + +MLC LLM Python Package can be installed directly from a prebuilt developer package, or built from source. + +Option 1. Prebuilt Package +-------------------------- + +We provide nightly built pip wheels for MLC-LLM via pip. +Select your operating system/compute platform and run the command in your terminal: + +.. note:: + ❗ Whenever using Python, it is highly recommended to use **conda** to manage an isolated Python environment to avoid missing dependencies, incompatible versions, and package conflicts. + +.. tabs:: + + .. tab:: Linux + + .. tabs:: + + .. tab:: CPU + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-chat-nightly mlc-ai-nightly + + .. tab:: CUDA 11.7 + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-chat-nightly-cu117 mlc-ai-nightly-cu117 + + .. tab:: CUDA 11.8 + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-chat-nightly-cu118 mlc-ai-nightly-cu118 + + .. tab:: CUDA 12.1 + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-chat-nightly-cu121 mlc-ai-nightly-cu121 + + .. tab:: CUDA 12.2 + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-chat-nightly-cu122 mlc-ai-nightly-cu122 + + .. tab:: ROCm 5.6 + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-chat-nightly-rocm56 mlc-ai-nightly-rocm56 + + .. tab:: ROCm 5.7 + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-chat-nightly-rocm57 mlc-ai-nightly-rocm57 + + .. tab:: Vulkan + + Supported in all Linux packages. + + .. note:: + + If encountering issues with GLIBC not found, please install the latest glibc in conda: + + .. code-block:: bash + + conda install -c conda-forge libgcc-ng + + Besides, we would recommend using Python 3.11; so if you are creating a new environment, + you could use the following command: + + .. code-block:: bash + + conda create --name mlc-prebuilt python=3.11 + + .. tab:: macOS + + .. tabs:: + + .. tab:: CPU + Metal + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-chat-nightly mlc-ai-nightly + + .. note:: + + Always check if conda is installed properly in macOS using the command below: + + .. code-block:: bash + + conda info | grep platform + + It should return "osx-64" for Mac with Intel chip, and "osx-arm64" for Mac with Apple chip. + + .. tab:: Windows + + .. tabs:: + + .. tab:: CPU + Vulkan + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-chat-nightly mlc-ai-nightly + + .. note:: + If encountering the error below: + + .. code-block:: bash + + FileNotFoundError: Could not find module 'path\to\site-packages\tvm\tvm.dll' (or one of its dependencies). Try using the full path with constructor syntax. + + It is likely `zstd`, a dependency to LLVM, was missing. Please use the command below to get it installed: + + .. code-block:: bash + + conda install zstd + + +Then you can verify installation in command line: + +.. code-block:: bash + + python -c "import mlc_chat; print(mlc_chat)" + # Prints out: + +| + +.. _mlcchat_build_from_source: + +Option 2. Build from Source +--------------------------- + +We also provide options to build mlc runtime libraries ``mlc_chat`` from source. +This step is useful when you want to make modification or obtain a specific version of mlc runtime. + + +**Step 1. Set up build dependency.** To build from source, you need to ensure that the following build dependencies are satisfied: + +* CMake >= 3.24 +* Git +* `Rust and Cargo `_, required by Hugging Face's tokenizer +* One of the GPU runtimes: + + * CUDA >= 11.8 (NVIDIA GPUs) + * Metal (Apple GPUs) + * Vulkan (NVIDIA, AMD, Intel GPUs) + +.. code-block:: bash + :caption: Set up build dependencies in Conda + + # make sure to start with a fresh environment + conda env remove -n mlc-chat-venv + # create the conda environment with build dependency + conda create -n mlc-chat-venv -c conda-forge \ + "cmake>=3.24" \ + rust \ + git \ + python=3.11 + # enter the build environment + conda activate mlc-chat-venv + +.. note:: + For runtime, :doc:`TVM Unity ` compiler is not a dependency for MLCChat CLI or Python API. Only TVM's runtime is required, which is automatically included in `3rdparty/tvm `_. + However, if you would like to compile your own models, you need to follow :doc:`TVM Unity `. + +**Step 2. Configure and build.** A standard git-based workflow is recommended to download MLC LLM, after which you can specify build requirements with our lightweight config generation tool: + +.. code-block:: bash + :caption: Configure and build + + # clone from GitHub + git clone --recursive https://github.com/mlc-ai/mlc-llm.git && cd mlc-llm/ + # create build directory + mkdir -p build && cd build + # generate build configuration + python3 ../cmake/gen_cmake_config.py + # build mlc_llm libraries + cmake .. && cmake --build . --parallel $(nproc) && cd .. + +.. note:: + If you are using CUDA and your compute capability is above 80, then it is require to build with + ``set(USE_FLASHINFER ON)``. Otherwise, you may run into ``Cannot find PackedFunc`` issue during + runtime. + + To check your CUDA compute capability, you can use ``nvidia-smi --query-gpu=compute_cap --format=csv``. + +**Step 3. Install via Python.** We recommend that you install ``mlc_chat`` as a Python package, giving you +access to ``mlc_chat.compile``, ``mlc_chat.ChatModule``, and the CLI. +There are two ways to do so: + + .. tabs :: + + .. code-tab :: bash Install via environment variable + + export PYTHONPATH=/path-to-mlc-llm/python:$PYTHONPATH + + .. code-tab :: bash Install via pip local project + + conda activate your-own-env + which python # make sure python is installed, expected output: path_to_conda/envs/your-own-env/bin/python + cd /path-to-mlc-llm/python + pip install -e . + +**Step 4. Validate installation.** You may validate if MLC libarires and mlc_chat CLI is compiled successfully using the following command: + +.. code-block:: bash + :caption: Validate installation + + # expected to see `libmlc_llm.so` and `libtvm_runtime.so` + ls -l ./build/ + # expected to see help message + mlc_chat chat -h + +Finally, you can verify installation in command line. You should see the path you used to build from source with: + +.. code:: bash + + python -c "import mlc_chat; print(mlc_chat)" diff --git a/docs/install/tvm.rst b/docs/install/tvm.rst new file mode 100644 index 0000000..f5cb460 --- /dev/null +++ b/docs/install/tvm.rst @@ -0,0 +1,313 @@ +.. _install-tvm-unity: + +Install TVM Unity Compiler +========================== + +.. contents:: Table of Contents + :local: + :depth: 2 + +`TVM Unity `__, the latest development in Apache TVM, is required to build MLC LLM. Its features include: + +- High-performance CPU/GPU code generation instantly without tuning; +- Dynamic shape and symbolic shape tracking by design; +- Supporting both inference and training; +- Productive python-first compiler implementation. As a concrete example, MLC LLM compilation is implemented in pure python using its API. + +TVM Unity can be installed directly from a prebuilt developer package, or built from source. + +.. _tvm-unity-prebuilt-package: + +Option 1. Prebuilt Package +-------------------------- + +A nightly prebuilt Python package of Apache TVM Unity is provided. + +.. note:: + ❗ Whenever using Python, it is highly recommended to use **conda** to manage an isolated Python environment to avoid missing dependencies, incompatible versions, and package conflicts. + +.. tabs:: + + .. tab:: Linux + + .. tabs:: + + .. tab:: CPU + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-ai-nightly + + .. tab:: CUDA 11.7 + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-ai-nightly-cu117 + + .. tab:: CUDA 11.8 + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-ai-nightly-cu118 + + .. tab:: CUDA 12.1 + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-ai-nightly-cu121 + + .. tab:: CUDA 12.2 + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-ai-nightly-cu122 + + .. tab:: ROCm 5.6 + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-ai-nightly-rocm56 + + .. tab:: ROCm 5.7 + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-ai-nightly-rocm57 + + .. tab:: Vulkan + + Supported in all Linux packages. + + .. note:: + + If encountering issues with GLIBC not found, please install the latest glibc in conda: + + .. code-block:: bash + + conda install -c conda-forge libgcc-ng + + .. tab:: macOS + + .. tabs:: + + .. tab:: CPU + Metal + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-ai-nightly + + .. note:: + + Always check if conda is installed properly in macOS using the command below: + + .. code-block:: bash + + conda info | grep platform + + It should return "osx-64" for Mac with Intel chip, and "osx-arm64" for Mac with Apple chip. + + .. tab:: Windows + + .. tabs:: + + .. tab:: CPU + Vulkan + + .. code-block:: bash + + conda activate your-environment + python3 -m pip install --pre -U -f https://mlc.ai/wheels mlc-ai-nightly + + .. note:: + If encountering the error below: + + .. code-block:: bash + + FileNotFoundError: Could not find module 'path\to\site-packages\tvm\tvm.dll' (or one of its dependencies). Try using the full path with constructor syntax. + + It is likely `zstd`, a dependency to LLVM, was missing. Please use the command below to get it installed: + + .. code-block:: bash + + conda install zstd + +.. _tvm-unity-build-from-source: + +Option 2. Build from Source +--------------------------- + +While it is generally recommended to always use the prebuilt TVM Unity, if you require more customization, you may need to build it from source. **NOTE.** this should only be attempted if you are familiar with the intricacies of C++, CMake, LLVM, Python, and other related systems. + +.. collapse:: Details + + **Step 1. Set up build dependency.** To build from source, you need to ensure that the following build dependencies are met: + + - CMake >= 3.24 + - LLVM >= 15 + - Git + - (Optional) CUDA >= 11.8 (targeting NVIDIA GPUs) + - (Optional) Metal (targeting Apple GPUs such as M1 and M2) + - (Optional) Vulkan (targeting NVIDIA, AMD, Intel and mobile GPUs) + - (Optional) OpenCL (targeting NVIDIA, AMD, Intel and mobile GPUs) + + .. note:: + - To target NVIDIA GPUs, either CUDA or Vulkan is required (CUDA is recommended); + - For AMD and Intel GPUs, Vulkan is necessary; + - When targeting Apple (macOS, iOS, iPadOS), Metal is a mandatory dependency; + - Some Android devices only support OpenCL, but most of them support Vulkan. + + To easiest way to manage dependency is via conda, which maintains a set of toolchains including LLVM across platforms. To create the environment of those build dependencies, one may simply use: + + .. code-block:: bash + :caption: Set up build dependencies in conda + + # make sure to start with a fresh environment + conda env remove -n tvm-build-venv + # create the conda environment with build dependency + conda create -n tvm-build-venv -c conda-forge \ + "llvmdev>=15" \ + "cmake>=3.24" \ + git + # enter the build environment + conda activate tvm-build-venv + + **Step 2. Configure and build.** Standard git-based workflow are recommended to download Apache TVM Unity, and then specify build requirements in ``config.cmake``: + + .. code-block:: bash + :caption: Download TVM Unity from GitHub + + # clone from GitHub + git clone --recursive git@github.com:mlc-ai/relax.git tvm-unity && cd tvm-unity + # create the build directory + rm -rf build && mkdir build && cd build + # specify build requirements in `config.cmake` + cp ../cmake/config.cmake . + + .. note:: + We are temporarily using `mlc-ai/relax `_ instead, which comes with several temporary outstanding changes that we will upstream to Apache TVM's `unity branch `_. + + We want to specifically tweak the following flags by appending them to the end of the configuration file: + + .. code-block:: bash + :caption: Configure build in ``config.cmake`` + + # controls default compilation flags + echo "set(CMAKE_BUILD_TYPE RelWithDebInfo)" >> config.cmake + # LLVM is a must dependency + echo "set(USE_LLVM \"llvm-config --ignore-libllvm --link-static\")" >> config.cmake + echo "set(HIDE_PRIVATE_SYMBOLS ON)" >> config.cmake + # GPU SDKs, turn on if needed + echo "set(USE_CUDA OFF)" >> config.cmake + echo "set(USE_METAL OFF)" >> config.cmake + echo "set(USE_VULKAN OFF)" >> config.cmake + echo "set(USE_OPENCL OFF)" >> config.cmake + # FlashInfer related, requires CUDA w/ compute capability 80;86;89;90 + echo "set(USE_FLASHINFER OFF)" >> config.cmake + echo "set(FLASHINFER_CUDA_ARCHITECTURES YOUR_CUDA_COMPUTE_CAPABILITY_HERE)" >> config.cmake + echo "set(CMAKE_CUDA_ARCHITECTURES YOUR_CUDA_COMPUTE_CAPABILITY_HERE)" >> config.cmake + + .. note:: + ``HIDE_PRIVATE_SYMBOLS`` is a configuration option that enables the ``-fvisibility=hidden`` flag. This flag helps prevent potential symbol conflicts between TVM and PyTorch. These conflicts arise due to the frameworks shipping LLVMs of different versions. + + `CMAKE_BUILD_TYPE `_ controls default compilation flag: + + - ``Debug`` sets ``-O0 -g`` + - ``RelWithDebInfo`` sets ``-O2 -g -DNDEBUG`` (recommended) + - ``Release`` sets ``-O3 -DNDEBUG`` + + .. note:: + If you are using CUDA and your compute capability is above 80, then it is require to build with + ``set(USE_FLASHINFER ON)``. Otherwise, you may run into ``Cannot find PackedFunc`` issue during + runtime. + + To check your CUDA compute capability, you can use ``nvidia-smi --query-gpu=compute_cap --format=csv``. + + Once ``config.cmake`` is edited accordingly, kick off build with the commands below: + + .. code-block:: bash + :caption: Build ``libtvm`` using cmake and cmake + + cmake .. && cmake --build . --parallel $(nproc) + + A success build should produce ``libtvm`` and ``libtvm_runtime`` under ``/path-tvm-unity/build/`` directory. + + Leaving the build environment ``tvm-build-venv``, there are two ways to install the successful build into your environment: + + .. tabs :: + + .. code-tab :: bash Install via environment variable + + export PYTHONPATH=/path-to-tvm-unity/python:$PYTHONPATH + + .. code-tab :: bash Install via pip local project + + conda activate your-own-env + conda install python # make sure python is installed + cd /path-to-tvm-unity/python + pip install -e . + +.. `|` adds a blank line + +| + +.. _tvm-unity-validate: + +Validate TVM Installation +------------------------- + +Using a compiler infrastructure with multiple language bindings could be error-prone. +Therefore, it is highly recommended to validate TVM Unity installation before use. + +**Step 1. Locate TVM Python package.** The following command can help confirm that TVM is properly installed as a python package and provide the location of the TVM python package: + +.. code-block:: bash + + >>> python -c "import tvm; print(tvm.__file__)" + /some-path/lib/python3.11/site-packages/tvm/__init__.py + +**Step 2. Confirm which TVM library is used.** When maintaining multiple build or installation of TVM, it becomes important to double check if the python package is using the proper ``libtvm`` with the following command: + +.. code-block:: bash + + >>> python -c "import tvm; print(tvm._ffi.base._LIB)" + + +**Step 3. Reflect TVM build option.** Sometimes when downstream application fails, it could likely be some mistakes with a wrong TVM commit, or wrong build flags. To find it out, the following commands will be helpful: + +.. code-block:: bash + + >>> python -c "import tvm; print('\n'.join(f'{k}: {v}' for k, v in tvm.support.libinfo().items()))" + ... # Omitted less relevant options + GIT_COMMIT_HASH: 4f6289590252a1cf45a4dc37bce55a25043b8338 + HIDE_PRIVATE_SYMBOLS: ON + USE_LLVM: llvm-config --link-static + LLVM_VERSION: 15.0.7 + USE_VULKAN: OFF + USE_CUDA: OFF + CUDA_VERSION: NOT-FOUND + USE_OPENCL: OFF + USE_METAL: ON + USE_ROCM: OFF + +.. note:: + ``GIT_COMMIT_HASH`` indicates the exact commit of the TVM build, and it can be found on GitHub via ``https://github.com/mlc-ai/relax/commit/$GIT_COMMIT_HASH``. + +**Step 4. Check device detection.** Sometimes it could be helpful to understand if TVM could detect your device at all with the following commands: + +.. code-block:: bash + + >>> python -c "import tvm; print(tvm.metal().exist)" + True # or False + >>> python -c "import tvm; print(tvm.cuda().exist)" + False # or True + >>> python -c "import tvm; print(tvm.vulkan().exist)" + False # or True + +Please note that the commands above verify the presence of an actual device on the local machine for the TVM runtime (not the compiler) to execute properly. However, TVM compiler can perform compilation tasks without requiring a physical device. As long as the necessary toolchain, such as NVCC, is available, TVM supports cross-compilation even in the absence of an actual device. diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..32bb245 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/prebuilt_models.rst b/docs/prebuilt_models.rst new file mode 100644 index 0000000..6d848d5 --- /dev/null +++ b/docs/prebuilt_models.rst @@ -0,0 +1,773 @@ +.. _Model Prebuilts: + +Model Prebuilts +================== + +.. contents:: Table of Contents + :depth: 3 + :local: + +.. _model-prebuilts-overview: + +Overview +-------- + +MLC-LLM is a universal solution for deploying different language models. Any models that can be described in `TVM Relax `__ +(a general representation for Neural Networks and can be imported from models written in PyTorch) can be recognized by MLC-LLM and thus deployed to different backends with the +help of :doc:`TVM Unity `. + +There are two ways to run a model on MLC-LLM (this page focuses on the second one): + +1. Compile your own models following :doc:`the model compilation page `. +2. Use off-the-shelf prebuilt models following this current page. + +In order to run a specific model on MLC-LLM, you need: + +**1. A model library:** a binary file containing the end-to-end functionality to inference a model (e.g. ``Llama-2-7b-chat-hf-q4f16_1-cuda.so``). +See the full list of all precompiled model libraries `here `__. + +**2. Compiled weights:** a folder containing multiple files that store the compiled and quantized weights of a model +(e.g. https://huggingface.co/mlc-ai/Llama-2-7b-chat-hf-q4f16_1-MLC). See the full list of all precompiled weights `here `__. + +In this page, we first quickly go over :ref:`how to use prebuilts ` for different platforms, +then track what current :ref:`prebuilt models we provide `. + + +.. _using-model-prebuilts: + +Using Prebuilt Models for Different Platforms +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +We quickly go over how to use prebuilt models for each platform. You can find detailed instruction on each platform's corresponding page. + +.. _using-prebuilt-models-cli: + +**Prebuilt Models on CLI / Python** + +For more, please see :doc:`the CLI page `, and the :doc:`the Python page `. + +.. collapse:: Click to show details + + First create the conda environment if you have not done so. + + .. code:: shell + + conda create -n mlc-chat-venv -c mlc-ai -c conda-forge mlc-chat-cli-nightly + conda activate mlc-chat-venv + conda install git git-lfs + git lfs install + + Download the prebuilt model libraries from github. + + .. code:: shell + + mkdir dist/ + git clone https://github.com/mlc-ai/binary-mlc-llm-libs.git dist/prebuilt_libs + + Run the model with CLI: + + .. code:: shell + + mlc_chat chat HF://mlc-ai/Llama-2-7b-chat-hf-q4f16_1-MLC + + + To run the model with Python API, see :doc:`the Python page ` (all other downloading steps are the same as CLI). + + +.. for a blank line + +| + +.. _using-prebuilt-models-ios: + +**Prebuilt Models on iOS** + +For more, please see :doc:`the iOS page `. + +.. collapse:: Click to show details + + The `iOS app `_ has builtin RedPajama-3B and Mistral-7B-Instruct-v0.2 support. + + All prebuilt models with an entry in ``iOS`` in the :ref:`model library table ` are supported by iOS. Namely, we have: + + .. list-table:: Prebuilt Models for iOS + :widths: 15 15 15 15 + :header-rows: 1 + + * - Model Code + - Model Series + - Quantization Mode + - MLC HuggingFace Weights Repo + * - `Mistral-7B-Instruct-v0.2-q3f16_1` + - `Mistral `__ + - * Weight storage data type: int3 + * Running data type: float16 + * Symmetric quantization + - `link `__ + * - `RedPajama-INCITE-Chat-3B-v1-q4f16_1` + - `RedPajama `__ + - * Weight storage data type: int4 + * Running data type: float16 + * Symmetric quantization + - `link `__ + * - `phi-2-q4f16_1` + - `Microsoft Phi-2 `__ + - * Weight storage data type: int4 + * Running data type: float16 + * Symmetric quantization + - `link `__ +.. for a blank line + +| + +.. _prebuilt-models-android: + +**Prebuilt Models on Android** + +For more, please see :doc:`the Android page `. + +.. collapse:: Click to show details + + The apk for demo Android app includes the following models. To add more, check out the Android page. + + .. list-table:: Prebuilt Models for Android + :widths: 15 15 15 15 + :header-rows: 1 + + * - Model code + - Model Series + - Quantization Mode + - Hugging Face repo + * - `Llama-2-7b-q4f16_1` + - `Llama `__ + - * Weight storage data type: int4 + * Running data type: float16 + * Symmetric quantization + - `link `__ + * - `RedPajama-INCITE-Chat-3B-v1-q4f16_1` + - `RedPajama `__ + - * Weight storage data type: int4 + * Running data type: float16 + * Symmetric quantization + - `link `__ +.. for a blank line + +| + +.. _supported-model-architectures: + +Level 1: Supported Model Architectures (The All-In-One Table) +------------------------------------------------------------- + +For each model architecture (e.g. Llama), there are multiple variants (e.g. CodeLlama, WizardLM). The variants share the same code for inference and only differ in their weights. In other words, running CodeLlama and WizardLM can use the same model library file (specified in Level 2 tables), but different precompiled weights (specified in Level 3 tables). Note that we have not provided prebuilt weights for all model variants. + +Each entry below hyperlinks to the corresponding level 2 and level 3 tables. + +MLC-LLM supports the following model architectures: + +.. list-table:: Supported Model Architectures + :widths: 10 10 15 15 + :header-rows: 1 + + * - Model Architecture + - Support + - Available MLC Prebuilts + - Unavailable in MLC Prebuilts + * - `LLaMA `__ + - * :ref:`Prebuilt Model Library ` + * `MLC Implementation `__ + - * :ref:`Llama-2-chat ` + - * `Code Llama `__ + * `Vicuna `__ + * `WizardLM `__ + * `WizardCoder (new) `__ + * `OpenOrca Platypus2 `__ + * `FlagAlpha Llama-2 Chinese `__ + * `georgesung Llama-2 Uncensored `__ + * `Alpaca `__ + * `Guanaco `__ + * `OpenLLaMA `__ + * `Gorilla `__ + * `YuLan-Chat `__ + * - `Mistral `__ + - * :ref:`Prebuilt Model Library ` + * `MLC Implementation `__ + - * :ref:`Mistral-7B-Instruct-v0.2 ` + * :ref:`NeuralHermes-2.5-Mistral-7B ` + * :ref:`OpenHermes-2.5-Mistral-7B ` + * :ref:`WizardMath-7B-V1.1 ` + - + * - `GPT-NeoX `__ + - * :ref:`Prebuilt Model Library ` + * `MLC Implementation `__ + - * :ref:`RedPajama ` + - * `Dolly `__ + * `Pythia `__ + * `StableCode `__ + * - `GPTBigCode `__ + - * :ref:`Prebuilt Model Library ` + * `MLC Implementation `__ + - + - * `StarCoder `__ + * `SantaCoder `__ + * `WizardCoder (old) `__ + * - `Phi `__ + - * :ref:`Prebuilt Model Library ` + * `MLC Implementation `__ + - * :ref:`Phi-1_5 ` + * :ref:`Phi-2 ` + - + * - `GPT2 `__ + - * :ref:`Prebuilt Model Library ` + * `MLC Implementation `__ + - * :ref:`GPT2 ` + - + +If the model variant you are interested in uses one of these model architectures we support, +(but we have not provided the prebuilt weights yet), you can check out +:doc:`/compilation/convert_weights` on how to convert the weights. +Afterwards, you may follow :ref:`distribute-compiled-models` to upload your prebuilt +weights to hugging face, and submit a PR that adds an entry to this page, +contributing to the community. + +For models structured in an architecture we have not supported yet, you could: + +- Either `create a [Model Request] issue `__ which + automatically shows up on our `Model Request Tracking Board `__. + +- Or follow our tutorial :doc:`Define New Models `, which introduces how to bring a new model architecture to MLC-LLM. + + +.. _model-library-tables: + +Level 2: Model Library Tables (Precompiled Binary Files) +-------------------------------------------------------- + +As mentioned earlier, each model architecture corresponds to a different model library file. That is, you cannot use the same model library file to run ``RedPajama`` and ``Llama-2``. However, you can use the same ``Llama`` model library file to run ``Llama-2``, ``WizardLM``, ``CodeLlama``, etc, but just with different weight files (from tables in Level 3). + +Each table below demonstrates the pre-compiled model library files for each model architecture. This is categorized by: + +- **Size**: each size of model has its own distinct model library file (e.g. 7B or 13B number of parameters) + +- **Platform**: the backend that the model library is intended to be run on (e.g. CUDA, ROCm, iphone, etc.) + +- **Quantization scheme**: the model library file also differs due to the quantization scheme used. For more on this, please see the :doc:`quantization page ` + (e.g. ``q3f16_1`` vs. ``q4f16_1``). + +Each entry links to the specific model library file found in `this github repo `__. + +If the model library you found is not available as a prebuilt, you can compile it yourself by following :doc:`the model compilation page `, +and submit a PR to the repo `binary-mlc-llm-libs `__ afterwards. + +.. _llama_library_table: + +Llama +^^^^^ +.. list-table:: Llama + :widths: 8 8 8 8 8 8 8 8 8 8 8 + :header-rows: 1 + :stub-columns: 1 + + * - + - CUDA + - ROCm + - Vulkan + + (Linux) + - Vulkan + + (Windows) + - Metal + + (M Chip) + - Metal + + (Intel) + - iOS + - Android + - webgpu + - mali + * - 7B + - `q4f16_1 `__ + + `q4f32_1 `__ + - + - `q4f16_1 `__ + + `q4f32_1 `__ + - + - `q4f16_1 `__ + + `q4f32_1 `__ + - + - + - `q4f16_1 `__ + + `q4f32_1 `__ + - `q4f16_1 `__ + + `q4f32_1 `__ + - + * - 13B + - `q4f16_1 `__ + - + - `q4f16_1 `__ + - + - `q4f16_1 `__ + - + - + - + - `q4f16_1 `__ + - + * - 34B + - + - + - + - + - + - + - + - + - + - + * - 70B + - `q4f16_1 `__ + - + - `q4f16_1 `__ + - + - `q4f16_1 `__ + - + - + - + - `q4f16_1 `__ + - + +.. _mistral_library_table: + +Mistral +^^^^^^^ +.. list-table:: Mistral + :widths: 8 8 8 8 8 8 8 8 8 8 8 + :header-rows: 1 + :stub-columns: 1 + + * - + - CUDA + - ROCm + - Vulkan + + (Linux) + - Vulkan + + (Windows) + - Metal + + (M Chip) + - Metal + + (Intel) + - iOS + - Android + - webgpu + - mali + * - 7B + - `q4f16_1 `__ + - + - `q4f16_1 `__ + - + - `q4f16_1 `__ + - + - `q3f16_1 `__ + - `q4f16_1 `__ + - `q4f16_1 `__ + - + + +.. _gpt_neox_library_table: + +GPT-NeoX (RedPajama-INCITE) +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. list-table:: GPT-NeoX (RedPajama-INCITE) + :widths: 8 8 8 8 8 8 8 8 8 8 8 + :header-rows: 1 + :stub-columns: 1 + + * - + - CUDA + - ROCm + - Vulkan + + (Linux) + - Vulkan + + (Windows) + - Metal + + (M Chip) + - Metal + + (Intel) + - iOS + - Android + - webgpu + - mali + * - 3B + - `q4f16_1 `__ + + `q4f32_1 `__ + - + - `q4f16_1 `__ + + `q4f32_1 `__ + - + - `q4f16_1 `__ + + `q4f32_1 `__ + - + - `q4f16_1 `__ + - `q4f16_1 `__ + + `q4f32_1 `__ + - `q4f16_1 `__ + + `q4f32_1 `__ + - + +.. _gpt_big_code_library_table: + +GPTBigCode +^^^^^^^^^^ + +.. list-table:: GPTBigCode + :widths: 8 8 8 8 8 8 8 8 8 8 8 + :header-rows: 1 + :stub-columns: 1 + + * - + - CUDA + - ROCm + - Vulkan + + (Linux) + - Vulkan + + (Windows) + - Metal + + (M Chip) + - Metal + + (Intel) + - iOS + - Android + - webgpu + - mali + * - 15B + - + - + - + - + - + - + - + - + - + - + +.. _phi_library_table: + +Phi +^^^ +.. list-table:: Phi + :widths: 8 8 8 8 8 8 8 8 8 8 8 + :header-rows: 1 + :stub-columns: 1 + + * - + - CUDA + - ROCm + - Vulkan + + (Linux) + - Vulkan + + (Windows) + - Metal + + (M Chip) + - Metal + + (Intel) + - iOS + - Android + - webgpu + - mali + * - Phi-2 + + (2.7B) + - `q0f16 `__ + + `q4f16_1 `__ + - + - `q0f16 `__ + + `q4f16_1 `__ + - + - `q0f16 `__ + + `q4f16_1 `__ + - + - + - + - `q0f16 `__ + + `q4f16_1 `__ + - + * - Phi-1.5 + + (1.3B) + - `q0f16 `__ + + `q4f16_1 `__ + - + - `q0f16 `__ + + `q4f16_1 `__ + - + - `q0f16 `__ + + `q4f16_1 `__ + - + - + - + - `q0f16 `__ + + `q4f16_1 `__ + - + +.. _gpt2_library_table: + +GPT2 +^^^^ +.. list-table:: GPT2 + :widths: 8 8 8 8 8 8 8 8 8 8 8 + :header-rows: 1 + :stub-columns: 1 + + * - + - CUDA + - ROCm + - Vulkan + + (Linux) + - Vulkan + + (Windows) + - Metal + + (M Chip) + - Metal + + (Intel) + - iOS + - Android + - webgpu + - mali + * - GPT2 + + (124M) + - `q0f16 `__ + - + - `q0f16 `__ + - + - `q0f16 `__ + - + - + - + - `q0f16 `__ + - + * - GPT2-med + + (355M) + - `q0f16 `__ + - + - `q0f16 `__ + - + - `q0f16 `__ + - + - + - + - `q0f16 `__ + - + +.. _model-variant-tables: + +Level 3: Model Variant Tables (Precompiled Weights) +--------------------------------------------------- + +Finally, for each model variant, we provide the precompiled weights we uploaded to hugging face. + +Each precompiled weight is categorized by its model size (e.g. 7B vs. 13B) and the quantization scheme (e.g. ``q3f16_1`` vs. ``q4f16_1``). We note that the weights are **platform-agnostic**. + +Each model variant also loads its conversation configuration from a pre-defined :ref:`conversation template`. Note that multiple model variants can share a common conversation template. + +Some of these files are uploaded by our community contributors--thank you! + +.. _llama2_variant_table: + +`Llama-2 `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``llama-2`` + +.. list-table:: Llama-2 + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - 7B + - * `q4f16_1 (Chat) `__ + * `q4f32_1 (Chat) `__ + + * - 13B + - * `q4f16_1 `__ + + * - 70B + - * `q4f16_1 `__ + +.. _mistralinstruct_variant_table: + +`Mistral `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``mistral_default`` + +.. list-table:: Mistral + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - 7B + - * `q3f16_1 (Instruct) `__ + * `q4f16_1 (Instruct) `__ + +.. _neuralhermes_variant_table: + +`NeuralHermes-2.5-Mistral `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``neural_hermes_mistral`` + +.. list-table:: Neural Hermes + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - 7B + - * `q4f16_1 `__ + +.. _openhermes_variant_table: + +`OpenHermes-2-Mistral `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``open_hermes_mistral`` + +.. list-table:: Open Hermes + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - 7B + - * `q4f16_1 `__ + + + +.. _wizardmathv1.1_variant_table: + +`WizardMath V1.1 `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``wizard_coder_or_math`` + +.. list-table:: WizardMath + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - 7B + - * `q4f16_1 `__ + + +.. _red_pajama_variant_table: + +`RedPajama `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``redpajama_chat`` + +.. list-table:: Red Pajama + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - 3B + - * `q4f16_1 (Chat) `__ + * `q4f32_1 (Chat) `__ + + +.. _phi_variant_table: + +`Phi `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``phi-2`` + +.. list-table:: Phi + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - Phi-2 (2.7B) + - * `q0f16 `__ + * `q4f16_1 `__ + * - Phi-1.5 (1.3B) + - * `q0f16 `__ + * `q4f16_1 `__ + + +.. _gpt2_variant_table: + +`GPT2 `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``gpt2`` + +.. list-table:: GPT2 + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - GPT2 (124M) + - * `q0f16 `__ + * - GPT2-medium (355M) + - * `q0f16 `__ + + +------------------ + + +.. _contribute-models-to-mlc-llm: + +Contribute Models to MLC-LLM +---------------------------- + +Ready to contribute your compiled models/new model architectures? Awesome! Please check :ref:`contribute-new-models` on how to contribute new models to MLC-LLM. diff --git a/docs/prebuilt_models_deprecated.rst b/docs/prebuilt_models_deprecated.rst new file mode 100644 index 0000000..c18f3f3 --- /dev/null +++ b/docs/prebuilt_models_deprecated.rst @@ -0,0 +1,845 @@ +Model Prebuilts from Old Flow (Deprecated) +========================================== + +**This page records the model libraries weights compiled under the old workflow (non-SLM).** + +**We will remove this page soon.** + +.. contents:: Table of Contents + :depth: 3 + :local: + +Overview +-------- + +MLC-LLM is a universal solution for deploying different language models. Any models that can be described in `TVM Relax `__ +(a general representation for Neural Networks and can be imported from models written in PyTorch) can be recognized by MLC-LLM and thus deployed to different backends with the +help of :doc:`TVM Unity `. + +There are two ways to run a model on MLC-LLM: + +1. Compile your own models following :doc:`the model compilation page `. +2. Use off-the-shelf prebuilts models following this current page. + +This page focuses on the second option: + +- Documenting :ref:`how to use prebuilts ` for various platforms, and +- Tracking what current :ref:`prebuilt models we provide `. + +Prerequisite: Model Libraries and Compiled Weights +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In order to run a specific model on MLC-LLM, you need: + +**1. A model library:** a binary file containing the end-to-end functionality to inference a model (e.g. ``Llama-2-7b-chat-hf-q4f16_1-cuda.so``). See the full list of all precompiled model libraries `here `__. + +**2. Compiled weights:** a folder containing multiple files that store the compiled and quantized weights of a model (e.g. https://huggingface.co/mlc-ai/mlc-chat-Llama-2-7b-chat-hf-q4f16_1). See the full list of all precompiled weights `here `__. + +.. _deprecated-using-model-prebuilts: + +Using Prebuilt Models for Different Platforms +--------------------------------------------- + +We quickly go over how to use prebuilt models for each platform. You can find detailed instruction on each platform's corresponding page. + +.. _deprecated-using-prebuilt-models-cli: + + +Prebuilt Models on CLI / Python +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For more, please see :doc:`the CLI page `, and the :doc:`the Python page `. + +.. collapse:: Click to show details + + First create the conda environment if you have not done so. + + .. code:: shell + + conda create -n mlc-chat-venv -c mlc-ai -c conda-forge mlc-chat-cli-nightly + conda activate mlc-chat-venv + conda install git git-lfs + git lfs install + + Download the prebuilt model libraries from github. + + .. code:: shell + + mkdir -p dist/prebuilt + git clone https://github.com/mlc-ai/binary-mlc-llm-libs.git dist/prebuilt/lib + + Download the prebuilt model weights from hugging face for the model variant you want. + + .. code:: shell + + # Say we want to run rwkv-raven-7b-q8f16_0 + cd dist/prebuilt + git clone https://huggingface.co/mlc-ai/mlc-chat-rwkv-raven-7b-q8f16_0 + cd ../.. + + # The format being: + # cd dist/prebuilt + # git clone https://huggingface.co/mlc-ai/mlc-chat-[model-code] + # cd ../.. + # mlc_chat_cli --model [model-code] + + Run the model with CLI: + + .. code:: shell + + # For CLI + mlc_chat_cli --model rwkv-raven-7b-q8f16_0 + + To run the model with Python API, see :doc:`the Python page ` (all other downloading steps are the same as CLI). + + +.. for a blank line + +| + +.. _deprecated-using-prebuilt-models-ios: + +Prebuilt Models on iOS +^^^^^^^^^^^^^^^^^^^^^^ + +For more, please see :doc:`the iOS page `. + +.. collapse:: Click to show details + + The `iOS app `_ has builtin RedPajama-3B and Llama-2-7b support. + + All prebuilt models with an entry in ``iOS`` in the :ref:`model library table ` are supported by iOS. Namely, we have: + + .. list-table:: Prebuilt model libraries integrated in the iOS app + :widths: 15 15 15 + :header-rows: 1 + + * - Model library name + - Model Family + - Quantization Mode + * - `Llama-2-7b-chat-hf-q3f16_1` + - LLaMA + - * Weight storage data type: int3 + * Running data type: float16 + * Symmetric quantization + * - `vicuna-v1-7b-q3f16_0` + - LLaMA + - * Weight storage data type: int3 + * Running data type: float16 + * Symmetric quantization + * - `RedPajama-INCITE-Chat-3B-v1-q4f16_1` + - GPT-NeoX + - * Weight storage data type: int4 + * Running data type: float16 + * Symmetric quantization + + As for prebuilt model weights, the ones we have integrated into app are listed below: + + .. list-table:: Tested prebuilt model weights for iOS + :widths: 15 15 15 15 + :header-rows: 1 + + * - Model code + - Model Series + - Quantization Mode + - Hugging Face repo + * - `Llama-2-7b-q3f16_1` + - `Llama `__ + - * Weight storage data type: int3 + * Running data type: float16 + * Symmetric quantization + - `link `__ + * - `vicuna-v1-7b-q3f16_0` + - `Vicuna `__ + - * Weight storage data type: int3 + * Running data type: float16 + * Symmetric quantization + - `link `__ + * - `RedPajama-INCITE-Chat-3B-v1-q4f16_1` + - `RedPajama `__ + - * Weight storage data type: int4 + * Running data type: float16 + * Symmetric quantization + - `link `__ + + To run a model variant you compiled on your own, you can directly reuse the above + integrated prebuilt model libraries, as long as the model shares the + architecture and is compiled with the same quantization mode. + For example, if you compile `OpenLLaMA-7B `_ + with quantization mode ``q3f16_0``, then you can run the compiled OpenLLaMA model on iPhone + without rebuilding the iOS app by reusing the `vicuna-v1-7b-q3f16_0` model library. + Then you can upload the compiled weights to hugging face so that you can download + the weights in the app as shown below (for more on uploading to hugging face, + please check :ref:`distribute-compiled-models`). + + To add a model to the iOS app, follow the steps below: + + .. tabs:: + + .. tab:: Step 1 + + Open "MLCChat" app, click "Add model variant". + + .. image:: https://raw.githubusercontent.com/mlc-ai/web-data/main/images/mlc-llm/tutorials/iPhone-custom-1.png + :align: center + :width: 30% + + .. tab:: Step 2 + + Paste the repository URL of the model built on your own, and click "Add". + + You can refer to the link in the image as an example. + + .. image:: https://raw.githubusercontent.com/mlc-ai/web-data/main/images/mlc-llm/tutorials/iPhone-custom-2.png + :align: center + :width: 30% + + .. tab:: Step 3 + + After adding the model, you can download your model from the URL by clicking the download button. + + .. image:: https://raw.githubusercontent.com/mlc-ai/web-data/main/images/mlc-llm/tutorials/iPhone-custom-3.png + :align: center + :width: 30% + + .. tab:: Step 4 + + When the download is finished, click into the model and enjoy. + + .. image:: https://raw.githubusercontent.com/mlc-ai/web-data/main/images/mlc-llm/tutorials/iPhone-custom-4.png + :align: center + :width: 30% + +.. for a blank line + +| + +.. _deprecated-prebuilt-models-android: + +Prebuilt Models on Android +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For more, please see :doc:`the Android page `. + +.. collapse:: Click to show details + + The apk for demo Android app includes the following models. To add more, check out the Android page. + + .. list-table:: Prebuilt Models for Android + :widths: 15 15 15 15 + :header-rows: 1 + + * - Model code + - Model Series + - Quantization Mode + - Hugging Face repo + * - `Llama-2-7b-q4f16_1` + - `Llama `__ + - * Weight storage data type: int4 + * Running data type: float16 + * Symmetric quantization + - `link `__ + * - `RedPajama-INCITE-Chat-3B-v1-q4f16_1` + - `RedPajama `__ + - * Weight storage data type: int4 + * Running data type: float16 + * Symmetric quantization + - `link `__ +.. for a blank line + +| + +.. _deprecated-supported-model-architectures: + +Level 1: Supported Model Architectures (The All-In-One Table) +------------------------------------------------------------- + +For each model architecture (e.g. Llama), there are multiple variants (e.g. CodeLlama, WizardLM). The variants share the same code for inference and only differ in their weights. In other words, running CodeLlama and WizardLM can use the same model library file (specified in Level 2 tables), but different precompiled weights (specified in Level 3 tables). Note that we have not provided prebuilt weights for all model variants. + +Each entry below hyperlinks to the corresponding level 2 and level 3 tables. + +MLC-LLM supports the following model architectures: + +.. list-table:: Supported Model Architectures + :widths: 10 10 15 15 + :header-rows: 1 + + * - Model Architecture + - Support + - Available MLC Prebuilts + - Unavailable in MLC Prebuilts + * - `LLaMA `__ + - * :ref:`Prebuilt Model Library ` + * `MLC Implementation `__ + - * :ref:`Llama-2 ` + * :ref:`Code Llama ` + * :ref:`Vicuna ` + * :ref:`WizardLM ` + * :ref:`WizardMath ` + * :ref:`OpenOrca Platypus2 ` + * :ref:`FlagAlpha Llama-2 Chinese ` + * :ref:`georgesung Llama-2 Uncensored ` + - * `Alpaca `__ + * `Guanaco `__ + * `OpenLLaMA `__ + * `Gorilla `__ + * `YuLan-Chat `__ + * `WizardCoder (new) `__ + * - `GPT-NeoX `__ + - * :ref:`Prebuilt Model Library ` + * `MLC Implementation `__ + - * :ref:`RedPajama ` + - * `Dolly `__ + * `Pythia `__ + * `StableCode `__ + * - `GPT-J `__ + - * Prebuilt not compiled yet + * `MLC Implementation `__ + - + - * `MOSS `__ + * - `RWKV `__ + - * :ref:`Prebuilt Model Library ` + * `MLC Implementation `__ + - * :ref:`RWKV-raven ` + - + * - `MiniGPT `__ + - * Prebuilt not compiled yet + * `MLC Implementation `__ + - + - * `MiniGPT-4 `__ + * - `GPTBigCode `__ + - * :ref:`Prebuilt Model Library ` + * `MLC Implementation `__ + - * :ref:`WizardCoder (old) ` + - * `StarCoder `__ + * `SantaCoder `__ + * - `ChatGLM `__ + - * Prebuilt not compiled yet + * `MLC Implementation `__ + - + - * `ChatGLM2 `__ + * `CodeGeeX2 `__ + * - `StableLM `__ + - * Prebuilt not compiled yet + * `MLC Implementation `__ + - + - * `StableLM `__ + +If the model variant you are interested in uses one of these model architectures we support, +(but we have not provided the prebuilt weights yet), you can check out +:doc:`/compilation/convert_weights` and :doc:`/compilation/compile_models` on how to compile your own models. +Afterwards, you may follow :ref:`distribute-compiled-models` to upload your prebuilt +weights to hugging face, and submit a PR that adds an entry to this page, +contributing to the community. + +For models structured in an architecture we have not supported yet, you could: + +- Either `create a [Model Request] issue `__ which automatically shows up on our `Model Request Tracking Board `__. + +- Or follow our tutorial :doc:`Define New Models `, which introduces how to bring a new model architecture to MLC-LLM. + + +.. _deprecated-model-library-tables: + +Level 2: Model Library Tables (Precompiled Binary Files) +-------------------------------------------------------- + +As mentioned earlier, each model architecture corresponds to a different model library file. That is, you cannot use the same model library file to run ``RedPajama`` and ``Llama-2``. However, you can use the same ``Llama`` model library file to run ``Llama-2``, ``WizardLM``, ``CodeLlama``, etc, but just with different weight files (from tables in Level 3). + +Each table below demonstrates the pre-compiled model library files for each model architecture. This is categorized by: + +- **Size**: each size of model has its own distinct model library file (e.g. 7B or 13B number of parameters) + +- **Platform**: the backend that the model library is intended to be run on (e.g. CUDA, ROCm, iphone, etc.) + +- **Quantization scheme**: the model library file also differs due to the quantization scheme used. For more on this, please see the :doc:`model compilation page ` (e.g. ``q3f16_1`` vs. ``q4f16_1``) + +Each entry links to the specific model library file found in `this github repo `__. + +.. _deprecated-llama_library_table: + +Llama +^^^^^ +.. list-table:: Llama + :widths: 8 8 8 8 8 8 8 8 8 8 + :header-rows: 1 + :stub-columns: 1 + + * - + - CUDA + - ROCm + - Vulkan + + (Linux) + - Vulkan + + (Windows) + - Metal + + (M1/M2) + - Metal + + (Intel) + - iOS + - webgpu + - mali + * - 7B + - `q4f16_1 `__ + - `q4f16_1 `__ + - `q4f16_1 `__ + - `q4f16_1 `__ + - `q4f16_1 `__ + - `q4f16_1 `__ + - `q3f16_1 `__ + - `q4f16_1 `__ + + `q4f32_1 `__ + - `q4f16_1 `__ + * - 13B + - `q4f16_1 `__ + - `q4f16_1 `__ + - `q4f16_1 `__ + - `q4f16_1 `__ + - `q4f16_1 `__ + - `q4f16_1 `__ + - + - `q4f16_1 `__ + + `q4f32_1 `__ + - `q4f16_1 `__ + * - 34B + - `q4f16_1 `__ + - + - `q4f16_1 `__ + - `q4f16_1 `__ + - `q4f16_1 `__ + - + - + - + - + * - 70B + - + - + - + - + - `q3f16_1 `__ + + `q4f16_1 `__ + - + - + - `q4f16_1 `__ + - + +.. _deprecated-gpt_neox_library_table: + +GPT-NeoX (RedPajama-INCITE) +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. list-table:: GPT-NeoX (RedPajama-INCITE) + :widths: 8 8 8 8 8 8 8 8 8 8 + :header-rows: 1 + :stub-columns: 1 + + * - + - CUDA + - ROCm + - Vulkan + + (Linux) + - Vulkan + + (Windows) + - Metal + + (M1/M2) + - Metal + + (Intel) + - iOS + - webgpu + - mali + * - 3B + - `q4f16_1 `__ + - `q4f16_1 `__ + - `q4f16_0 `__ + + `q4f16_1 `__ + - `q4f16_0 `__ + + `q4f16_1 `__ + - `q4f16_0 `__ + + `q4f16_1 `__ + - `q4f16_0 `__ + + `q4f16_1 `__ + - `q4f16_0 `__ + + `q4f16_1 `__ + - `q4f16_0 `__ + + `q4f16_1 `__ + + `q4f32_0 `__ + + `q4f32_1 `__ + - `q4f16_1 `__ + +.. _deprecated-rwkv_library_table: + +RWKV +^^^^ +.. list-table:: RWKV + :widths: 8 8 8 8 8 8 8 8 8 8 + :header-rows: 1 + :stub-columns: 1 + + * - + - CUDA + - ROCm + - Vulkan + + (Linux) + - Vulkan + + (Windows) + - Metal + + (M1/M2) + - Metal + + (Intel) + - iOS + - webgpu + - mali + * - 1B5 + - + - + - `q8f16_0 `__ + - `q8f16_0 `__ + - `q8f16_0 `__ + - `q8f16_0 `__ + - + - + - + * - 3B + - + - + - `q8f16_0 `__ + - `q8f16_0 `__ + - `q8f16_0 `__ + - `q8f16_0 `__ + - + - + - + * - 7B + - + - + - `q8f16_0 `__ + - `q8f16_0 `__ + - `q8f16_0 `__ + - `q8f16_0 `__ + - + - + - + +.. _deprecated-gpt_big_code_library_table: + +GPTBigCode +^^^^^^^^^^ +Note that these all links to model libraries for WizardCoder (the older version released in Jun. 2023). +However, any GPTBigCode model variants should be able to reuse these (e.g. StarCoder, SantaCoder). + +.. list-table:: GPTBigCode + :widths: 8 8 8 8 8 8 8 8 8 8 + :header-rows: 1 + :stub-columns: 1 + + * - + - CUDA + - ROCm + - Vulkan + + (Linux) + - Vulkan + + (Windows) + - Metal + + (M1/M2) + - Metal + + (Intel) + - iOS + - webgpu + - mali + * - 15B + - `q4f16_1 `__ + + `q4f32_1 `__ + - + - `q4f16_1 `__ + + `q4f32_1 `__ + - `q4f16_1 `__ + + `q4f32_1 `__ + - `q4f16_1 `__ + - + - + - `q4f16_1 `__ + + `q4f32_1 `__ + - + +.. _deprecated-model-variant-tables: + +Level 3: Model Variant Tables (Precompiled Weights) +--------------------------------------------------- + +Finally, for each model variant, we provide the precompiled weights we uploaded to hugging face. + +Each precompiled weight is categorized by its model size (e.g. 7B vs. 13B) and the quantization scheme (e.g. ``q3f16_1`` vs. ``q4f16_1``). We note that the weights are **platform-agnostic**. + +Each model variant also loads its conversation configuration from a pre-defined :ref:`conversation template`. Note that multiple model variants can share a common conversation template. + +Some of these files are uploaded by our community contributors--thank you! + +.. _deprecated-llama2_variant_table: + +`Llama-2 `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``llama-2`` + +.. list-table:: Llama-2 + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - 7B + - * `q3f16_1 `__ + * `q4f16_1 `__ + * `q4f32_1 `__ + + * - 13B + - * `q4f16_1 `__ + * `q4f32_1 `__ + + * - 70B + - * `q3f16_1 `__ + * `q4f16_1 `__ + +.. _deprecated-code_llama_variant_table: + +`Code Llama `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``codellama_completion`` + +.. list-table:: Code Llama + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - 7B + - * `q4f16_1 (Base) `__ + * `q4f16_1 (Instruct) `__ + * `q4f16_1 (Python) `__ + + * - 13B + - * `q4f16_1 (Base) `__ + * `q4f16_1 (Instruct) `__ + * `q4f16_1 (Python) `__ + + * - 34B + - * `q4f16_1 (Base) `__ + * `q4f16_1 (Instruct) `__ + * `q4f16_1 (Python) `__ + + +.. _deprecated-vicuna_variant_table: + +`Vicuna `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``vicuna_v1.1`` + +.. list-table:: Vicuna + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - 7B + - * `q3f16_0 `__ + * `q4f32_0 `__ + * `int3 (demo) `__ + * `int4 (demo) `__ + + +.. _deprecated-WizardLM_variant_table: + +`WizardLM `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``vicuna_v1.1`` + +.. list-table:: WizardLM + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - 13B + - * `q4f16_1 (V1.2) `__ + * `q4f32_1 (V1.2) `__ + + * - 70B + - * `q3f16_1 (V1.0) `__ + * `q4f16_1 (V1.0) `__ + + +.. _deprecated-wizard_math_variant_table: + +`WizardMath `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``wizard_coder_or_math`` + +.. list-table:: WizardMath + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - 7B + - * `q4f16_1 `__ + * `q4f32_1 `__ + * - 13B + - `q4f16_1 `__ + * - 70B + - `q4f16_1 `__ + + +.. _deprecated-open_orca_variant_table: + +`OpenOrca Platypus2 `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``llama-2`` + +.. list-table:: OpenOrca Platypus2 + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - 13B + - `q4f16_1 `__ + + +.. _deprecated-flag_alpha_llama2_variant_table: + +`FlagAlpha Llama-2 Chinese `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``llama-2`` + +.. list-table:: FlagAlpha Llama-2 Chinese + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - 7B + - * `q4f16_1 `__ + * `q4f32_1 `__ + + +.. _deprecated-llama2_uncensored_variant_table: + +`Llama2 uncensored (georgesung) `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``llama-default`` + +.. list-table:: Llama2 uncensored + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - 7B + - * `q4f16_1 `__ + * `q4f32_1 `__ + +.. _deprecated-red_pajama_variant_table: + +`RedPajama `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``LM`` + +.. list-table:: Red Pajama + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - 3B + - * `q4f16_0 (Instruct) `__ + * `q4f16_0 (Chat) `__ + * `q4f16_1 (Chat) `__ + * `q4f32_0 (Chat) `__ + + +.. _deprecated-rwkv_raven_variant_table: + +`RWKV-raven `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``rwkv`` + +.. list-table:: RWKV-raven + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - 1B5 + - `q8f16_0 `__ + + * - 3B + - `q8f16_0 `__ + + * - 7B + - `q8f16_0 `__ + + +.. _deprecated-wizard_coder_variant_table: + +`WizardCoder `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Conversation template: ``wizard_coder_or_math`` + +.. list-table:: WizardCoder + :widths: 30 30 + :header-rows: 1 + + * - Size + - Hugging Face Repo Link + * - 15B + - `q4f16_1 `__ + +------------------ + + +.. _deprecated-contribute-models-to-mlc-llm: + +Contribute Models to MLC-LLM +---------------------------- + +Ready to contribute your compiled models/new model architectures? Awesome! Please check :ref:`contribute-new-models` on how to contribute new models to MLC-LLM. diff --git a/docs/privacy.rst b/docs/privacy.rst new file mode 100644 index 0000000..cdd3c91 --- /dev/null +++ b/docs/privacy.rst @@ -0,0 +1,5 @@ +MLC Chat App Privacy +==================== + +MLC Chat run all generation locally. +All data stays in users' device and is not collected by the app. diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..bc020bc --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,10 @@ +sphinx-tabs == 3.4.1 +sphinx-rtd-theme +sphinx == 5.2.3 +sphinx-toolbox == 3.4.0 +tlcpack-sphinx-addon==0.2.2 +sphinxcontrib_httpdomain==1.8.1 +sphinxcontrib-napoleon==0.7 +sphinx-reredirects==0.1.2 +--find-links https://mlc.ai/wheels +mlc-ai-nightly diff --git a/examples/python/benchmark.py b/examples/python/benchmark.py new file mode 100644 index 0000000..7cdbe78 --- /dev/null +++ b/examples/python/benchmark.py @@ -0,0 +1,11 @@ +from mlc_chat import ChatModule + +# From the mlc-llm directory, run +# $ python examples/python/benchmark.py + +# Create a ChatModule instance +cm = ChatModule(model="Llama-2-7b-chat-hf-q4f16_1") + +output = cm.benchmark_generate("What's the meaning of life?", generate_length=256) +print(f"Generated text:\n{output}\n") +print(f"Statistics: {cm.stats()}") diff --git a/examples/python/run_llama_batched_vllm.py b/examples/python/run_llama_batched_vllm.py new file mode 100644 index 0000000..a290eb8 --- /dev/null +++ b/examples/python/run_llama_batched_vllm.py @@ -0,0 +1,448 @@ +import argparse +import math +import os +import json +from collections import defaultdict +from typing import List +from dataclasses import dataclass + +import numpy as np + +import tvm +from tvm import relax +from tvm.runtime import disco as di + +import torch +from transformers import AutoTokenizer + +from mlc_llm.relax_model.llama import LlamaConfig +from mlc_llm import utils + + +class KVCache: + def __init__(self, num_blocks, block_size, num_layers, num_heads, head_size, disco_session): + if disco_session: + init_cache_func = disco_session.get_global_func("tvm.contrib.vllm.allocate_kv_cache") + else: + init_cache_func = tvm.get_global_func("tvm.contrib.vllm.allocate_kv_cache") + + self.cache = init_cache_func(head_size, num_layers, num_heads, block_size, num_blocks) + + self.block_tables = defaultdict(list) + self.slot_mappings = defaultdict(list) + self.block_size = block_size + + +class CacheManager: + block_size: int = 16 + + def __init__( + self, num_blocks, num_layers, num_heads, head_size, disco_session=None, sliding_window=None + ): + self.num_blocks = num_blocks + self.free_blocks = list(range(num_blocks)) + self.kv_cache = KVCache( + num_blocks, self.block_size, num_layers, num_heads, head_size, disco_session + ) + + if sliding_window: + assert sliding_window % self.kv_cache.block_size == 0 + self.block_sliding_window = sliding_window // self.kv_cache.block_size + else: + self.block_sliding_window = None + + def set_size(self, request_ids: List[int], target_sizes: List[int]): + for id, size in zip(request_ids, target_sizes): + num_needed_block = math.ceil(size / self.block_size) + + if self.block_sliding_window: + num_needed_block = min(num_needed_block, self.block_sliding_window) + + if id in self.kv_cache.block_tables and size == 0: + self.free_blocks.extend(self.kv_cache.block_tables[id]) + del self.kv_cache.block_tables[id] + del self.kv_cache.slot_mappings[id] + + elif id in self.kv_cache.block_tables: + # Decoding + if len(self.kv_cache.block_tables[id]) < num_needed_block: + # Need to allocate a new block for this request + assert len(self.kv_cache.block_tables[id]) + 1 == num_needed_block + self.kv_cache.block_tables[id].append(self.free_blocks.pop()) + + pos = size - 1 + block_number = self.kv_cache.block_tables[id][-1] + + if self.block_sliding_window: + block_number = self.kv_cache.block_tables[id][ + (pos // self.block_size) % self.block_sliding_window + ] + else: + block_number = self.kv_cache.block_tables[id][-1] + + block_offset = pos % self.block_size + slot = block_number * self.block_size + block_offset + self.kv_cache.slot_mappings[id].append(slot) + + elif id not in self.kv_cache.block_tables: + assert len(self.free_blocks) >= num_needed_block, "Not enough free blocks." + + for _ in range(num_needed_block): + self.kv_cache.block_tables[id].append(self.free_blocks.pop()) + + for i in range(size): + block_idx = i // self.block_size + + if self.block_sliding_window: + block_idx %= self.block_sliding_window + + block_number = self.kv_cache.block_tables[id][block_idx] + block_offset = i % self.block_size + slot = block_number * self.block_size + block_offset + self.kv_cache.slot_mappings[id].append(slot) + + def get(self): + return self.kv_cache + + +@dataclass +class SequenceGenerationRequest: + request_id: int + token_ids: List[int] + + +@dataclass +class SequenceGenerationResponse: + request_id: int + token_id: int + + +def sample(logits): + logits = torch.from_dlpack(logits) + return torch.argmax(logits, -1).cpu().numpy() + + +def load_params_disco(artifact_path, lib_path, num_shards): + sess = di.ProcessSession(num_workers=num_shards) + devices = range(num_shards) + sess.init_ccl("nccl", *devices) + module = sess.load_vm_module(lib_path) + + loader_create = sess.get_global_func("runtime.disco.ShardLoader") + metadata_path = os.path.join(artifact_path, "params", "ndarray-cache.json") + with open(metadata_path, "r", encoding="utf-8") as f: + ndarray_cache_metadata = f.read() + + loader = loader_create(metadata_path, ndarray_cache_metadata, "", module) + loader_load = sess.get_global_func("runtime.disco.ShardLoaderLoadAll") + params = loader_load(loader) + + return module, params, sess + + +def copy_to_worker_0(sess: di.Session, host_array): + x_array = sess.empty(host_array.shape, host_array.dtype) + sess.copy_to_worker_0(host_array, x_array) + return x_array + + +def get_tvm_model(artifact_path, model, quantization, num_shards, dev): + lib_path = os.path.join(artifact_path, f"{model}-{quantization}-cuda.so") + + if num_shards == 1: + ex = tvm.runtime.load_module(lib_path) + vm = relax.VirtualMachine(ex, dev) + params = utils.load_params(artifact_path, dev) + return vm.module, params, None + + return load_params_disco(artifact_path, lib_path, num_shards) + + +def _prepare_inputs( + requests, + all_slot_mappings, + all_block_tables, + sliding_window, + dev, + is_prefill, +): + block_tables = [] + seq_lens = [] + input_ids = [] + slot_mapping = [] + positions = [] + max_num_blocks_per_seq = 0 + indices_within_window = [] + start_idx = 0 + + for request in requests: + request_id = request.request_id + token_ids = request.token_ids + + if is_prefill: + input_ids += token_ids + prompt_len = len(token_ids) + seq_lens.append(prompt_len) + positions += range(prompt_len) + slot_mapping += all_slot_mappings[request_id] + + if sliding_window: + indices_within_window += range( + start_idx + max(0, prompt_len - sliding_window), + start_idx + prompt_len, + ) + start_idx += prompt_len + + else: + input_ids.append(token_ids[-1]) + pos = len(token_ids) - 1 + positions.append(pos) + block_table = all_block_tables[request_id] + max_num_blocks_per_seq = max(max_num_blocks_per_seq, len(block_table)) + block_tables.append(block_table) + slot_mapping.append(all_slot_mappings[request_id][-1]) + + if sliding_window: + seq_lens.append(min(len(token_ids), sliding_window)) + else: + seq_lens.append(len(token_ids)) + + input_ids = tvm.nd.array(np.array(input_ids, dtype="int32"), dev) + positions = tvm.nd.array(np.array(positions, dtype="int32"), dev) + seq_lens = tvm.nd.array(np.array(seq_lens, dtype="int32"), dev) + slot_mapping = tvm.nd.array(np.array(slot_mapping, dtype="int32"), dev) + + if is_prefill and sliding_window: + indices_within_window = tvm.nd.array(np.array(indices_within_window, dtype="int32"), dev) + else: + indices_within_window = None + + if not is_prefill: + + def _pad_to_max(x: List[int], max_len: int) -> List[int]: + return x + [0] * (max_len - len(x)) + + padded_block_tables = [ + _pad_to_max(block_table, max_num_blocks_per_seq) for block_table in block_tables + ] + + block_tables_np = np.vstack(padded_block_tables).astype("int32") + block_tables = tvm.nd.array(np.array(block_tables_np, dtype="int32"), dev) + else: + block_tables = None + + return ( + input_ids, + positions, + seq_lens, + slot_mapping, + indices_within_window, + block_tables, + ) + + +class Model: + def __init__( + self, artifact_path, model_name, quant, vocab_size, num_shards, dev, sliding_window + ): + self.mod, self.params, self.disco_session = get_tvm_model( + artifact_path, model_name, quant, num_shards, dev + ) + self.dev = dev + self.vocab_size = vocab_size + self.sliding_window = sliding_window + + if sliding_window: + self.block_sliding_window = sliding_window // CacheManager.block_size + else: + self.block_sliding_window = None + + def generate( + self, requests: List[SequenceGenerationRequest], cache: KVCache, is_prefill: bool + ) -> List[SequenceGenerationResponse]: + ( + input_ids, + positions, + seq_lens, + slot_mapping, + indices_within_window, + block_tables, + ) = _prepare_inputs( + requests, + cache.slot_mappings, + cache.block_tables, + self.sliding_window, + self.dev, + is_prefill, + ) + + if self.disco_session: + input_ids = copy_to_worker_0(self.disco_session, input_ids) + positions = copy_to_worker_0(self.disco_session, positions) + seq_lens = copy_to_worker_0(self.disco_session, seq_lens) + slot_mapping = copy_to_worker_0(self.disco_session, slot_mapping) + + kv_cache = cache.cache + + if is_prefill: + if self.sliding_window: + if self.disco_session: + indices_within_window = copy_to_worker_0( + self.disco_session, indices_within_window + ) + + out = self.mod["prefill"]( + input_ids, + positions, + seq_lens, + kv_cache, + slot_mapping, + indices_within_window, + self.params, + ) + else: + out = self.mod["prefill"]( + input_ids, positions, seq_lens, kv_cache, slot_mapping, self.params + ) + + if self.disco_session: + logits, _ = out.debug_get_from_remote(0) + else: + logits = out[0] # Ignore returned KV cache since it is updated in-place anyway. + else: + if self.disco_session: + block_tables = copy_to_worker_0(self.disco_session, block_tables) + + out = self.mod["decode"]( + input_ids, + positions, + seq_lens, + kv_cache, + slot_mapping, + block_tables, + self.params, + ) + + if self.disco_session: + logits, _ = out.debug_get_from_remote(0) + else: + logits = out[0] + + next_tokens = sample(logits) + + return [ + SequenceGenerationResponse(request.request_id, new_token) + for request, new_token in zip(requests, next_tokens) + ] + + +def parse_args(): + # Example + # python build.py --model vicuna-v1-7b --quantization q4f16_ft --use-cache=0 --max-seq-len 768 --enable-batching --use-vllm-attention + # python examples/python/run_llama_batched_vllm.py --local-id vicuna-v1-7b-q4f16_ft + # + # For Disco: + # python build.py --model vicuna-v1-7b --quantization q0f16 --use-cache=0 --max-seq-len 768 --enable-batching --use-vllm-attention --build-model-only --num-shards 2 + # python build.py --model vicuna-v1-7b --quantization q0f16 --use-cache=0 --max-seq-len 768 --enable-batching --use-vllm-attention --convert-weight-only + # CUDA_VISIBLE_DEVICES=0,1 python examples/python/run_llama_batched_vllm.py --local-id vicuna-v1-7b-q0f16 --num-shards 2 + + args = argparse.ArgumentParser() + args.add_argument("--local-id", type=str, required=True) + args.add_argument("--artifact-path", type=str, default="dist") + args.add_argument("--num-shards", type=int, default=1) + args.add_argument("--num-decode-steps", type=int, default=20) + parsed = args.parse_args() + parsed.model, parsed.quantization = parsed.local_id.rsplit("-", 1) + utils.argparse_postproc_common(parsed) + parsed.artifact_path = os.path.join( + parsed.artifact_path, f"{parsed.model}-{parsed.quantization.name}" + ) + return parsed + + +def run(args): + quantization = args.quantization.name + artifact_path = args.artifact_path + model_name = args.model + model_path = f"dist/models/{model_name}" + + dev = tvm.device("cuda", 0) + + with open(os.path.join(model_path, "config.json"), encoding="utf-8") as i_f: + config = LlamaConfig(**json.load(i_f)) + + model = Model( + artifact_path, + model_name, + quantization, + config.vocab_size, + args.num_shards, + dev, + config.sliding_window, + ) + + tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=False) + + num_kv_heads = config.get_num_key_value_heads() // args.num_shards + head_size = config.hidden_size // config.num_attention_heads + num_blocks = 500 + + cache_manager = CacheManager( + num_blocks, + config.num_hidden_layers, + num_kv_heads, + head_size, + model.disco_session, + sliding_window=config.sliding_window, + ) + cache = cache_manager.get() + + model.block_sliding_window = cache_manager.block_sliding_window + + prompts = [ + "Hello, my name is", + "The president of the United States is", + "The capital of France is", + "The future of AI is", + ] + + batched_token_ids = [tokenizer.encode(p) for p in prompts] + prompts_len = [len(ids) for ids in batched_token_ids] + request_ids = list(range(len(prompts))) + target_sizes = [] + requests = [] + + for token_ids, request_id in zip(batched_token_ids, request_ids): + request_ids.append(request_id) + target_sizes.append(len(token_ids)) + requests.append(SequenceGenerationRequest(request_id, token_ids)) + + cache_manager.set_size(request_ids, target_sizes) + + out = model.generate(requests, cache, True) + + for _ in range(args.num_decode_steps): + for i, response in enumerate(out): + new_token_id = response.token_id + requests[i].token_ids.append(new_token_id) + target_sizes[i] += 1 + + cache_manager.set_size(request_ids, target_sizes) + + out = model.generate(requests, cache, False) + + output_tokens = [ + tokenizer.convert_ids_to_tokens( + requests[i].token_ids[prompts_len[i] :], skip_special_tokens=True + ) + for i in range(len(requests)) + ] + + generated = [tokenizer.convert_tokens_to_string(tokens) for tokens in output_tokens] + + for p, g in zip(prompts, generated): + print("Prompt = '{}', generated text = '{}'".format(p, g)) + + +if __name__ == "__main__": + run(parse_args()) diff --git a/examples/python/sample_chat_stream.py b/examples/python/sample_chat_stream.py new file mode 100644 index 0000000..980e833 --- /dev/null +++ b/examples/python/sample_chat_stream.py @@ -0,0 +1,30 @@ +from mlc_chat import ChatModule +from mlc_chat.callback import StreamToStdout, StreamIterator + +# From the mlc-llm directory, run +# $ python examples/python/sample_chat_stream.py + +# Create a ChatModule instance +cm = ChatModule(model="Llama-2-7b-chat-hf-q4f16_1") + +# Stream to Stdout +output = cm.generate( + prompt="What is the meaning of life?", + progress_callback=StreamToStdout(callback_interval=2), +) + +# Stream to an Iterator +from threading import Thread + +stream = StreamIterator(callback_interval=2) +generation_thread = Thread( + target=cm.generate, + kwargs={"prompt": "What is the meaning of life?", "progress_callback": stream}, +) +generation_thread.start() + +output = "" +for delta_message in stream: + output += delta_message + +generation_thread.join() diff --git a/examples/python/sample_mlc_chat.py b/examples/python/sample_mlc_chat.py new file mode 100644 index 0000000..6d20d0c --- /dev/null +++ b/examples/python/sample_mlc_chat.py @@ -0,0 +1,39 @@ +from mlc_chat import ChatModule +from mlc_chat.callback import StreamToStdout + +# From the mlc-llm directory, run +# $ python examples/python/sample_mlc_chat.py + +# Create a ChatModule instance +cm = ChatModule( + model="dist/Llama-2-7b-chat-hf-q4f16_1-MLC", + model_lib_path="dist/prebuilt_libs/Llama-2-7b-chat-hf/Llama-2-7b-chat-hf-q4f16_1-cuda.so" + # Vulkan on Linux: Llama-2-7b-chat-hf-q4f16_1-vulkan.so + # Metal on macOS: Llama-2-7b-chat-hf-q4f16_1-metal.so + # Other platforms: Llama-2-7b-chat-hf-q4f16_1-{backend}.{suffix} +) + +# You can change to other models that you downloaded +# Model variants of the same architecture can reuse the same model library +# Here WizardMath reuses Mistral's model library +# cm = ChatModule( +# model="dist/Mistral-7B-Instruct-v0.2-q4f16_1-MLC", # or "dist/WizardMath-7B-V1.1-q4f16_1-MLC" +# model_lib_path="dist/prebuilt_libs/Mistral-7B-Instruct-v0.2/Mistral-7B-Instruct-v0.2-q4f16_1-cuda.so" +# ) + +# Generate a response for a given prompt +output = cm.generate( + prompt="What is the meaning of life?", + progress_callback=StreamToStdout(callback_interval=2), +) + +# Print prefill and decode performance statistics +print(f"Statistics: {cm.stats()}\n") + +output = cm.generate( + prompt="How many points did you list out?", + progress_callback=StreamToStdout(callback_interval=2), +) + +# Reset the chat module by +# cm.reset_chat() diff --git a/examples/rest/nodejs/README.MD b/examples/rest/nodejs/README.MD new file mode 100755 index 0000000..1d63d54 --- /dev/null +++ b/examples/rest/nodejs/README.MD @@ -0,0 +1,21 @@ +# Node/Javascript/Typescript Access Examples for MLC_CHAT REST APIs + +Please make sure you are running v18.17.x of node (and npm v9.6.7) -- v20.x currently has some compatibility problems with typescript used in the langchain example. + +First install dependencies. + +`npm i` + +Copy `dotenv.exmaple` to `.env`. + +To run JS chat completion (both streaming and non-streaming) example: + +`node sample_client.js` + +To run OpenAI (chat completion streaming and non-streaming, and legacy completion) example: + +`node sample_openai.js` + +To run LangchainJS Typescript example: + +`npm run example` diff --git a/examples/rest/nodejs/dotenv.example b/examples/rest/nodejs/dotenv.example new file mode 100755 index 0000000..5312f49 --- /dev/null +++ b/examples/rest/nodejs/dotenv.example @@ -0,0 +1,2 @@ +OPENAI_API_KEY="none" +OPENAI_API_BASE="http://127.0.0.1:8000/v1" \ No newline at end of file diff --git a/examples/rest/nodejs/package.json b/examples/rest/nodejs/package.json new file mode 100755 index 0000000..2a3ebf2 --- /dev/null +++ b/examples/rest/nodejs/package.json @@ -0,0 +1,40 @@ +{ + "name": "mlc-llm-js-examples", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "license": "AGPL-version-3.0", + "private": false, + "engines": { + "node": ">= 14.0.0", + "npm": ">= 6.0.0" + }, + "homepage": "", + "repository": { + "type": "git", + "url": "" + }, + "bugs": "", + "keywords": [], + "author": { + "name": "", + "email": "", + "url": "" + }, + "contributors": [], + "scripts": { + "example": "ts-node --esm ./sample_langchain.ts" + }, + "dependencies": { + "@types/node": "^20.4.4", + "dotenv": "^16.3.1", + "langchain": "^0.0.117", + "needle": "^3.2.0", + "openai": "^3.3.0", + "typescript": "^5.1.6" + }, + "devDependencies": { + "ts-node": "^10.9.1" + } +} diff --git a/examples/rest/nodejs/sample_client.js b/examples/rest/nodejs/sample_client.js new file mode 100755 index 0000000..9a85072 --- /dev/null +++ b/examples/rest/nodejs/sample_client.js @@ -0,0 +1,74 @@ +import request from 'needle'; + +( async () => { +const color = { + PURPLE : '\x1b[95m', + CYAN : '\x1b[96m', + DARKCYAN : '\x1b[36m', + BLUE : '\x1b[94m', + GREEN : '\x1b[92m', + YELLOW : '\x1b[93m', + RED : '\x1b[91m', + BOLD : '\x1b[1m', + UNDERLINE : '\x1b[4m', + END : '\x1b[0m' +}; + +let payload = { + model : 'vicuna-v1-7b', + messages: [{"role": "user", "content": "Write a haiku"}], + stream: false +}; + +const print = ( str ) => { + process.stdout.write(str); +}; + +const newline = () => { + print('\n'); +} + +newline(); +print(color.BOLD + "Without streaming:" + color.END); +newline(); + +let r = await request("post", "http://127.0.0.1:8000/v1/chat/completions", payload, {json: true}); + +print(color.GREEN + r.body.choices[0].message.content + color.END); +print('\n'); +// Reset the chat +r = await request("post", "http://127.0.0.1:8000/v1/chat/completions", payload, {json: true}); +print(color.BOLD + "Reset chat" + color.END); +newline(); + +// Get a response using a prompt with streaming + +payload = { + "model": "vicuna-v1-7b", + "messages": [{"role": "user", "content": "Write a haiku"}], + "stream": true +} + +print( color.BOLD + "With streaming:" + color.END); +newline(); +r = request.post( "http://127.0.0.1:8000/v1/chat/completions", payload, {json: true}) +.on('readable', function() { + let jsData = ''; + let data = ''; + while (data = this.read()) { + const chunk = data.toString().substring(6); + if (chunk.trim() === "[DONE]") break; + jsData = JSON.parse(chunk); + print(color.GREEN + jsData.choices[0].delta.content + color.END); + } +}) +.on('done', async function () { + newline(); + let txtresp = await request("get", "http://127.0.0.1:8000/stats"); + print(color.BOLD + "Runtime stats:" + color.END + txtresp.body); + +}) + +})() + + diff --git a/examples/rest/nodejs/sample_langchain.ts b/examples/rest/nodejs/sample_langchain.ts new file mode 100644 index 0000000..48e849d --- /dev/null +++ b/examples/rest/nodejs/sample_langchain.ts @@ -0,0 +1,75 @@ +import { OpenAI } from "langchain/llms/openai"; +import { BufferWindowMemory } from "langchain/memory"; +import { LLMChain } from "langchain/chains"; +import { PromptTemplate } from "langchain/prompts"; +import {TextLoader } from "langchain/document_loaders/fs/text"; +import { loadQAStuffChain } from "langchain/chains"; + +const color = { + PURPLE : '\x1b[95m', + CYAN : '\x1b[96m', + DARKCYAN : '\x1b[36m', + BLUE : '\x1b[94m', + GREEN : '\x1b[92m', + YELLOW : '\x1b[93m', + RED : '\x1b[91m', + BOLD : '\x1b[1m', + UNDERLINE : '\x1b[4m', + END : '\x1b[0m' +}; + +function print(str: string) { + process.stdout.write(str); +} + +const newline = () => { + print('\n'); +} + + const chat = new OpenAI( { + openAIApiKey: "empty", + temperature: 0 + }, { + basePath: 'http://127.0.0.1:8000/v1' + }); + +// Conversational LLMChain example + const memory = new BufferWindowMemory({ memoryKey: "history", k: 1 }); + + const template = `The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know. + + Current conversation: + {history} + Human: {human_input} + AI:`; + + + const prompt = PromptTemplate.fromTemplate(template); + let chain = new LLMChain({ llm: chat, prompt, memory }); + + let input = "Write a poem about Pittsburgh."; + print(color.BOLD + input + "..." + color.END); + newline(); + let res = await chain.call({ human_input: input }); + newline(); + print(color.GREEN + res.text + color.END); + newline(); + input = "What does it mean?"; + print(color.BOLD + input + "..." + color.END); + newline(); + res = await chain.call({ human_input: input }); + newline(); + print(color.GREEN + res.text + color.END); + newline(); + +// Question and answer stuff chain example with text loader +const loader = new TextLoader('../resources/linux.txt'); +const documents = await loader.load(); +const schain = loadQAStuffChain(chat); +const query = "When was Linux released?"; +newline(); newline(); +print(color.BOLD + "Query: " + color.END + color.BLUE + query + color.END); +newline(); +const result = await schain.call({ input_documents: documents, question: query}); +print(color.BOLD + "Response: " + color.END + color.GREEN + result.text + color.END); + diff --git a/examples/rest/nodejs/sample_openai.js b/examples/rest/nodejs/sample_openai.js new file mode 100755 index 0000000..6e06114 --- /dev/null +++ b/examples/rest/nodejs/sample_openai.js @@ -0,0 +1,77 @@ +import { Configuration, OpenAIApi } from "openai"; +import dotenv from "dotenv"; +dotenv.config(); + +( async () => { + +const configuration = new Configuration({ + apiKey: process.env.OPENAI_API_KEY, + basePath : process.env.OPENAI_API_BASE +}) +const openai = new OpenAIApi(configuration); +let model = "vicuna-v1-7b" + +const color = { + PURPLE : '\x1b[95m', + CYAN : '\x1b[96m', + DARKCYAN : '\x1b[36m', + BLUE : '\x1b[94m', + GREEN : '\x1b[92m', + YELLOW : '\x1b[93m', + RED : '\x1b[91m', + BOLD : '\x1b[1m', + UNDERLINE : '\x1b[4m', + END : '\x1b[0m' +}; + +const print = ( str ) => { + process.stdout.write(str); +}; + +const newline = () => { + print('\n'); +} + +// Chat completion example without streaming +newline(); +print(color.BOLD + "OpenAI chat completion example without streaming:" + color.END); +newline(); + +let completion = await openai.createChatCompletion({ + model: model, + messages: [{"role": "user", "content": "Write a poem about OpenAI"}] +}); + + +print(color.GREEN + completion.data.choices[0].message.content + color.END) +newline(); newline(); + + +// Chat completion example with streaming +// (raw implementation since npm module does not support it yet - it will have support in upcoming 4.x) + +print(color.BOLD + "OpenAI chat completion example with streaming:" + color.END); +newline(); +completion = await openai.createChatCompletion({ + model: model, + messages: [{"role": "user", "content": "Write a poem about OpenAI"}], + stream: true, +}, {responseType: 'stream'}); + +completion.data.on('data', async (data) => { + const parsed = JSON.parse(data.toString().substring(6)); + print(color.GREEN + parsed.choices[0].delta.content + color.END); +}); + +completion.data.on('close', async () => { + newline(); newline(); + + // Completion example + print(color.BOLD + "OpenAI completion example:" + color.END) + newline(); + let res = await openai.createCompletion({ prompt: "Write a poem about OpenAI", model: model}); + print(color.GREEN + res.data.choices[0].text + color.END); + newline(); newline(); + + }); +})() \ No newline at end of file diff --git a/examples/rest/nodejs/tsconfig.json b/examples/rest/nodejs/tsconfig.json new file mode 100755 index 0000000..bc563cb --- /dev/null +++ b/examples/rest/nodejs/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "lib": ["es2020"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + "module": "nodenext", /* Specify what module code is generated. */ + "rootDir": "src", /* Specify the root folder within your source files. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/examples/rest/python/sample_client.py b/examples/rest/python/sample_client.py new file mode 100644 index 0000000..1af1d83 --- /dev/null +++ b/examples/rest/python/sample_client.py @@ -0,0 +1,46 @@ +import requests +import json + +class color: + PURPLE = '\033[95m' + CYAN = '\033[96m' + DARKCYAN = '\033[36m' + BLUE = '\033[94m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + END = '\033[0m' + +# Get a response using a prompt without streaming +payload = { + "model": "vicuna-v1-7b", + "messages": [{"role": "user", "content": "Write a haiku"}], + "stream": False +} +r = requests.post("http://127.0.0.1:8000/v1/chat/completions", json=payload) +print(f"{color.BOLD}Without streaming:{color.END}\n{color.GREEN}{r.json()['choices'][0]['message']['content']}{color.END}\n") + +# Reset the chat +r = requests.post("http://127.0.0.1:8000/chat/reset", json=payload) +print(f"{color.BOLD}Reset chat:{color.END} {str(r)}\n") + +# Get a response using a prompt with streaming +payload = { + "model": "vicuna-v1-7b", + "messages": [{"role": "user", "content": "Write a haiku"}], + "stream": True +} +with requests.post("http://127.0.0.1:8000/v1/chat/completions", json=payload, stream=True) as r: + print(f"{color.BOLD}With streaming:{color.END}") + for chunk in r: + if (chunk[6:].decode('utf-8').strip() == '[DONE]'): + break + content = json.loads(chunk[6:])["choices"][0]["delta"].get("content", "") + print(f"{color.GREEN}{content}{color.END}", end="", flush=True) + print("\n") + +# Get the latest runtime stats +r = requests.get("http://127.0.0.1:8000/stats") +print(f"{color.BOLD}Runtime stats:{color.END} {r.json()}\n") diff --git a/examples/rest/python/sample_langchain.py b/examples/rest/python/sample_langchain.py new file mode 100644 index 0000000..cda326f --- /dev/null +++ b/examples/rest/python/sample_langchain.py @@ -0,0 +1,156 @@ +from langchain.chat_models import ChatOpenAI +from langchain import LLMChain, PromptTemplate +from langchain.memory import ConversationBufferWindowMemory +from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler +from langchain.document_loaders import TextLoader, UnstructuredRSTLoader, DirectoryLoader +from langchain.chains.question_answering import load_qa_chain +from langchain.llms import OpenAI +from langchain.text_splitter import CharacterTextSplitter +from langchain.chains import RetrievalQA +from langchain.vectorstores import Chroma + +# Note that Langchain support for embedding documents using MLC is currently blocked on +# https://github.com/langchain-ai/langchain/pull/7815 +# We have subclassed `OpenAIEmbeddings` in the meantime to get around this dependency. +from mlc_chat.embeddings.openai import MLCEmbeddings + + + +# First set the following in your environment: +# export OPENAI_API_BASE=http://127.0.0.1:8000/v1 +# export OPENAI_API_KEY=EMPTY + +# Note that Langchain does not currently support Pydantic v2: +# https://github.com/langchain-ai/langchain/issues/6841 +# Please ensure that your `pydantic` version is < 2.0 + +class color: + PURPLE = '\033[95m' + CYAN = '\033[96m' + DARKCYAN = '\033[36m' + BLUE = '\033[94m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + END = '\033[0m' + +def llm_chain_example(): + template = """ + {history} + USER: {human_input} + ASSISTANT:""" + + prompt = PromptTemplate( + input_variables=["history", "human_input"], + template=template + ) + + llm_chain = LLMChain( + llm=ChatOpenAI(streaming=True, callbacks=[StreamingStdOutCallbackHandler()]), + prompt=prompt, + verbose=True, + memory=ConversationBufferWindowMemory(human_prefix="USER", ai_prefix="ASSISTANT") + ) + + output = llm_chain.predict(human_input="Write a short poem about Pittsburgh.") + output = llm_chain.predict(human_input="What does the poem mean?") + +def load_qa_chain_example(): + loader = TextLoader('../resources/linux.txt') + documents = loader.load() + chain = load_qa_chain(llm=OpenAI(), chain_type="stuff", verbose=False) + query = "When was Linux released?" + print(f"{color.BOLD}Query:{color.END} {color.BLUE} {query}{color.END}") + print(f"{color.BOLD}Response:{color.END} {color.GREEN}{chain.run(input_documents=documents, question=query)}{color.END}") + +def retrieval_qa_sotu_example(): + prompt_template = """Use only the following pieces of context to answer the question at the end. Don't use any other knowledge. + + {context} + + USER: {question} + ASSISTANT:""" + + PROMPT = PromptTemplate( + template=prompt_template, input_variables=["context", "question"] + ) + + loader = TextLoader('../resources/state_of_the_union.txt') + documents = loader.load() + + text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=100) + texts = text_splitter.split_documents(documents) + # print(texts) + embeddings = MLCEmbeddings(deployment="text-embedding-ada-002", embedding_ctx_length=None) + db = Chroma.from_documents(documents=texts, embedding=embeddings) + retriever = db.as_retriever(search_type="similarity", search_kwargs={"k":2}) + qa = RetrievalQA.from_chain_type( + llm=OpenAI(), + chain_type="stuff", + retriever=retriever, + return_source_documents=True, + chain_type_kwargs={"prompt": PROMPT} + ) + questions = [ + "What is the American Rescue Plan?", + "What did the president say about Ketanji Brown Jackson?", + "Who is mentioned in the speech?", + "To whom is the speech addressed?", + "Tell me more about the Made in America campaign." + ] + + for qn in questions: + print(f"{color.BOLD}QUESTION:{color.END} {qn}") + res = qa({'query': qn}) + print(f"{color.BOLD}RESPONSE:{color.END} {color.GREEN}{res['result']}{color.END}") + print(f"{color.BOLD}SOURCE:{color.END} {color.BLUE}{repr(res['source_documents'][0].page_content)}{color.END}") + print() + +def retrieval_qa_mlc_docs_example(): + prompt_template = """Use only the following pieces of context to answer the question at the end. Don't use any other knowledge. + + {context} + + USER: {question} + ASSISTANT:""" + + PROMPT = PromptTemplate( + template=prompt_template, input_variables=["context", "question"] + ) + + loader = DirectoryLoader("../../../docs", glob='*/*.rst', show_progress=True, loader_cls=UnstructuredRSTLoader, loader_kwargs={"mode": "single"}) + documents = loader.load() + text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=100) + texts = text_splitter.split_documents(documents) + embeddings = MLCEmbeddings(deployment="text-embedding-ada-002", embedding_ctx_length=None) + db = Chroma.from_documents(collection_name="abc", documents=texts, embedding=embeddings) + retriever = db.as_retriever(search_type="similarity", search_kwargs={"k":3}) + qa = RetrievalQA.from_chain_type( + llm=OpenAI(), + chain_type="stuff", + retriever=retriever, + return_source_documents=True, + chain_type_kwargs={"prompt": PROMPT} + ) + while True: + qn = input(f"{color.BOLD}QUESTION:{color.END} ") + res = qa({'query': qn}) + print(f"{color.BOLD}RESPONSE:{color.END} {color.GREEN}{res['result']}{color.END}") + print(f"{color.BOLD}SOURCE:{color.END} {color.BLUE}{repr(res['source_documents'][0].page_content)}{color.END}") + print() + + # Some example questions: + # - What is the chat config? + # - What is temperature? + # - What are the REST API endpoints? + # - What are the available quantization options? + + +# Uncomment one of the following lines to try out the corresponding demo: + +# llm_chain_example() +# load_qa_chain_example() +# retrieval_qa_sotu_example() +# retrieval_qa_mlc_docs_example() diff --git a/examples/rest/python/sample_openai.py b/examples/rest/python/sample_openai.py new file mode 100644 index 0000000..1c4acb0 --- /dev/null +++ b/examples/rest/python/sample_openai.py @@ -0,0 +1,43 @@ +import openai + +openai.api_key = "None" +openai.api_base = "http://127.0.0.1:8000/v1" + +model = "vicuna-v1-7b" + +class color: + PURPLE = '\033[95m' + CYAN = '\033[96m' + DARKCYAN = '\033[36m' + BLUE = '\033[94m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + END = '\033[0m' + +# Chat completion example without streaming +print(f"{color.BOLD}OpenAI chat completion example without streaming:{color.END}\n") +completion = openai.ChatCompletion.create( + model=model, + messages=[{"role": "user", "content": "Write a poem about OpenAI"}] +) +print(f"{color.GREEN}{completion.choices[0].message.content}{color.END}\n\n") + +# Chat completion example with streaming +print(f"{color.BOLD}OpenAI chat completion example with streaming:{color.END}\n") +res = openai.ChatCompletion.create( + model=model, + messages=[{"role": "user", "content": "Write a poem about OpenAI"}], + stream=True +) +for chunk in res: + content = chunk["choices"][0]["delta"].get("content", "") + print(f"{color.GREEN}{content}{color.END}", end="", flush=True) +print("\n") + +# Completion example +print(f"{color.BOLD}OpenAI completion example:{color.END}\n") +res = openai.Completion.create(prompt="Write a poem about OpenAI", model=model) +print(f"{color.GREEN}{res.choices[0].text}{color.END}\n\n") diff --git a/examples/rest/resources/linux.txt b/examples/rest/resources/linux.txt new file mode 100644 index 0000000..9f09b49 --- /dev/null +++ b/examples/rest/resources/linux.txt @@ -0,0 +1,23 @@ +Linux is a family of open-source Unix-like operating systems based on the Linux kernel, an operating system kernel first released on September 17, 1991, by Linus Torvalds. Linux is typically packaged as a Linux distribution, which includes the kernel and supporting system software and libraries, many of which are provided by the GNU Project. Many Linux distributions use the word "Linux" in their name, but the Free Software Foundation uses the name "GNU/Linux" to emphasize the importance of GNU software, causing some controversy. + +Popular Linux distributions include Debian, Fedora Linux, and Ubuntu, the latter of which itself consists of many different distributions and modifications, including Lubuntu and Xubuntu. Commercial distributions include Red Hat Enterprise Linux and SUSE Linux Enterprise. Desktop Linux distributions include a windowing system such as X11 or Wayland, and a desktop environment such as GNOME or KDE Plasma. Distributions intended for servers may omit graphics altogether, or include a solution stack such as LAMP. Because Linux is freely redistributable, anyone may create a distribution for any purpose. + +Linux was originally developed for personal computers based on the Intel x86 architecture, but has since been ported to more platforms than any other operating system. Because of the dominance of the Linux-based Android on smartphones, Linux, including Android, has the largest installed base of all general-purpose operating systems, as of May 2022. Although Linux is, as of November 2022, used by only around 2.6 percent of desktop computers, the Chromebook, which runs the Linux kernel-based ChromeOS, dominates the US K–12 education market and represents nearly 20 percent of sub-$300 notebook sales in the US. Linux is the leading operating system on servers (over 96.4% of the top 1 million web servers' operating systems are Linux), leads other big iron systems such as mainframe computers, and is used on all of the world's 500 fastest supercomputers (since November 2017, having gradually displaced all competitors). + +Linux also runs on embedded systems, i.e. devices whose operating system is typically built into the firmware and is highly tailored to the system. This includes routers, automation controls, smart home devices, video game consoles, televisions (Samsung and LG Smart TVs), automobiles (Tesla, Audi, Mercedes-Benz, Hyundai and Toyota), and spacecraft (Falcon 9 rocket, Dragon crew capsule and the Perseverance rover). + +Linux is one of the most prominent examples of free and open-source software collaboration. The source code may be used, modified and distributed commercially or non-commercially by anyone under the terms of its respective licenses, such as the GNU General Public License (GPL). The Linux kernel, for example, is licensed under the GPLv2, with an exception for system calls that allows code that calls the kernel via system calls not to be licensed under the GPL. + +The Unix operating system was conceived and implemented in 1969, at AT&T's Bell Labs, in the United States by Ken Thompson, Dennis Ritchie, Douglas McIlroy, and Joe Ossanna. First released in 1971, Unix was written entirely in assembly language, as was common practice at the time. In 1973, in a key pioneering approach, it was rewritten in the C programming language by Dennis Ritchie (with the exception of some hardware and I/O routines). The availability of a high-level language implementation of Unix made its porting to different computer platforms easier. + +Due to an earlier antitrust case forbidding it from entering the computer business, AT&T licensed the operating system's source code as a trade secret to anyone who asked. As a result, Unix grew quickly and became widely adopted by academic institutions and businesses. In 1984, AT&T divested itself of its regional operating companies, and was released from its obligation not to enter the computer business; freed of that obligation, Bell Labs began selling Unix as a proprietary product, where users were not legally allowed to modify it. + +Onyx Systems began selling early microcomputer-based Unix workstations in 1980. Later, Sun Microsystems, founded as a spin-off of a student project at Stanford University, also began selling Unix-based desktop workstations in 1982. While Sun workstations did not utilize commodity PC hardware, for which Linux was later originally developed, it represented the first successful commercial attempt at distributing a primarily single-user microcomputer that ran a Unix operating system. + +With Unix increasingly "locked in" as a proprietary product, the GNU Project, started in 1983 by Richard Stallman, had the goal of creating a "complete Unix-compatible software system" composed entirely of free software. Work began in 1984. Later, in 1985, Stallman started the Free Software Foundation and wrote the GNU General Public License (GNU GPL) in 1989. By the early 1990s, many of the programs required in an operating system (such as libraries, compilers, text editors, a command-line shell, and a windowing system) were completed, although low-level elements such as device drivers, daemons, and the kernel, called GNU Hurd, were stalled and incomplete. + +MINIX was created by Andrew S. Tanenbaum, a computer science professor, and released in 1987 as a minimal Unix-like operating system targeted at students and others who wanted to learn operating system principles. Although the complete source code of MINIX was freely available, the licensing terms prevented it from being free software until the licensing changed in April 2000. + +Although not released until 1992, due to legal complications, development of 386BSD, from which NetBSD, OpenBSD and FreeBSD descended, predated that of Linux. + +Linus Torvalds has stated on separate occasions that if the GNU kernel or 386BSD had been available at the time (1991), he probably would not have created Linux. \ No newline at end of file diff --git a/examples/rest/resources/state_of_the_union.txt b/examples/rest/resources/state_of_the_union.txt new file mode 100644 index 0000000..d50175d --- /dev/null +++ b/examples/rest/resources/state_of_the_union.txt @@ -0,0 +1,723 @@ +Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans. + +Last year COVID-19 kept us apart. This year we are finally together again. + +Tonight, we meet as Democrats Republicans and Independents. But most importantly as Americans. + +With a duty to one another to the American people to the Constitution. + +And with an unwavering resolve that freedom will always triumph over tyranny. + +Six days ago, Russia’s Vladimir Putin sought to shake the foundations of the free world thinking he could make it bend to his menacing ways. But he badly miscalculated. + +He thought he could roll into Ukraine and the world would roll over. Instead he met a wall of strength he never imagined. + +He met the Ukrainian people. + +From President Zelenskyy to every Ukrainian, their fearlessness, their courage, their determination, inspires the world. + +Groups of citizens blocking tanks with their bodies. Everyone from students to retirees teachers turned soldiers defending their homeland. + +In this struggle as President Zelenskyy said in his speech to the European Parliament “Light will win over darkness.” The Ukrainian Ambassador to the United States is here tonight. + +Let each of us here tonight in this Chamber send an unmistakable signal to Ukraine and to the world. + +Please rise if you are able and show that, Yes, we the United States of America stand with the Ukrainian people. + +Throughout our history we’ve learned this lesson when dictators do not pay a price for their aggression they cause more chaos. + +They keep moving. + +And the costs and the threats to America and the world keep rising. + +That’s why the NATO Alliance was created to secure peace and stability in Europe after World War 2. + +The United States is a member along with 29 other nations. + +It matters. American diplomacy matters. American resolve matters. + +Putin’s latest attack on Ukraine was premeditated and unprovoked. + +He rejected repeated efforts at diplomacy. + +He thought the West and NATO wouldn’t respond. And he thought he could divide us at home. Putin was wrong. We were ready. Here is what we did. + +We prepared extensively and carefully. + +We spent months building a coalition of other freedom-loving nations from Europe and the Americas to Asia and Africa to confront Putin. + +I spent countless hours unifying our European allies. We shared with the world in advance what we knew Putin was planning and precisely how he would try to falsely justify his aggression. + +We countered Russia’s lies with truth. + +And now that he has acted the free world is holding him accountable. + +Along with twenty-seven members of the European Union including France, Germany, Italy, as well as countries like the United Kingdom, Canada, Japan, Korea, Australia, New Zealand, and many others, even Switzerland. + +We are inflicting pain on Russia and supporting the people of Ukraine. Putin is now isolated from the world more than ever. + +Together with our allies –we are right now enforcing powerful economic sanctions. + +We are cutting off Russia’s largest banks from the international financial system. + +Preventing Russia’s central bank from defending the Russian Ruble making Putin’s $630 Billion “war fund” worthless. + +We are choking off Russia’s access to technology that will sap its economic strength and weaken its military for years to come. + +Tonight I say to the Russian oligarchs and corrupt leaders who have bilked billions of dollars off this violent regime no more. + +The U.S. Department of Justice is assembling a dedicated task force to go after the crimes of Russian oligarchs. + +We are joining with our European allies to find and seize your yachts your luxury apartments your private jets. We are coming for your ill-begotten gains. + +And tonight I am announcing that we will join our allies in closing off American air space to all Russian flights – further isolating Russia – and adding an additional squeeze –on their economy. The Ruble has lost 30% of its value. + +The Russian stock market has lost 40% of its value and trading remains suspended. Russia’s economy is reeling and Putin alone is to blame. + +Together with our allies we are providing support to the Ukrainians in their fight for freedom. Military assistance. Economic assistance. Humanitarian assistance. + +We are giving more than $1 Billion in direct assistance to Ukraine. + +And we will continue to aid the Ukrainian people as they defend their country and to help ease their suffering. + +Let me be clear, our forces are not engaged and will not engage in conflict with Russian forces in Ukraine. + +Our forces are not going to Europe to fight in Ukraine, but to defend our NATO Allies – in the event that Putin decides to keep moving west. + +For that purpose we’ve mobilized American ground forces, air squadrons, and ship deployments to protect NATO countries including Poland, Romania, Latvia, Lithuania, and Estonia. + +As I have made crystal clear the United States and our Allies will defend every inch of territory of NATO countries with the full force of our collective power. + +And we remain clear-eyed. The Ukrainians are fighting back with pure courage. But the next few days weeks, months, will be hard on them. + +Putin has unleashed violence and chaos. But while he may make gains on the battlefield – he will pay a continuing high price over the long run. + +And a proud Ukrainian people, who have known 30 years of independence, have repeatedly shown that they will not tolerate anyone who tries to take their country backwards. + +To all Americans, I will be honest with you, as I’ve always promised. A Russian dictator, invading a foreign country, has costs around the world. + +And I’m taking robust action to make sure the pain of our sanctions is targeted at Russia’s economy. And I will use every tool at our disposal to protect American businesses and consumers. + +Tonight, I can announce that the United States has worked with 30 other countries to release 60 Million barrels of oil from reserves around the world. + +America will lead that effort, releasing 30 Million barrels from our own Strategic Petroleum Reserve. And we stand ready to do more if necessary, unified with our allies. + +These steps will help blunt gas prices here at home. And I know the news about what’s happening can seem alarming. + +But I want you to know that we are going to be okay. + +When the history of this era is written Putin’s war on Ukraine will have left Russia weaker and the rest of the world stronger. + +While it shouldn’t have taken something so terrible for people around the world to see what’s at stake now everyone sees it clearly. + +We see the unity among leaders of nations and a more unified Europe a more unified West. And we see unity among the people who are gathering in cities in large crowds around the world even in Russia to demonstrate their support for Ukraine. + +In the battle between democracy and autocracy, democracies are rising to the moment, and the world is clearly choosing the side of peace and security. + +This is a real test. It’s going to take time. So let us continue to draw inspiration from the iron will of the Ukrainian people. + +To our fellow Ukrainian Americans who forge a deep bond that connects our two nations we stand with you. + +Putin may circle Kyiv with tanks, but he will never gain the hearts and souls of the Ukrainian people. + +He will never extinguish their love of freedom. He will never weaken the resolve of the free world. + +We meet tonight in an America that has lived through two of the hardest years this nation has ever faced. + +The pandemic has been punishing. + +And so many families are living paycheck to paycheck, struggling to keep up with the rising cost of food, gas, housing, and so much more. + +I understand. + +I remember when my Dad had to leave our home in Scranton, Pennsylvania to find work. I grew up in a family where if the price of food went up, you felt it. + +That’s why one of the first things I did as President was fight to pass the American Rescue Plan. + +Because people were hurting. We needed to act, and we did. + +Few pieces of legislation have done more in a critical moment in our history to lift us out of crisis. + +It fueled our efforts to vaccinate the nation and combat COVID-19. It delivered immediate economic relief for tens of millions of Americans. + +Helped put food on their table, keep a roof over their heads, and cut the cost of health insurance. + +And as my Dad used to say, it gave people a little breathing room. + +And unlike the $2 Trillion tax cut passed in the previous administration that benefitted the top 1% of Americans, the American Rescue Plan helped working people—and left no one behind. + +And it worked. It created jobs. Lots of jobs. + +In fact—our economy created over 6.5 Million new jobs just last year, more jobs created in one year +than ever before in the history of America. + +Our economy grew at a rate of 5.7% last year, the strongest growth in nearly 40 years, the first step in bringing fundamental change to an economy that hasn’t worked for the working people of this nation for too long. + +For the past 40 years we were told that if we gave tax breaks to those at the very top, the benefits would trickle down to everyone else. + +But that trickle-down theory led to weaker economic growth, lower wages, bigger deficits, and the widest gap between those at the top and everyone else in nearly a century. + +Vice President Harris and I ran for office with a new economic vision for America. + +Invest in America. Educate Americans. Grow the workforce. Build the economy from the bottom up +and the middle out, not from the top down. + +Because we know that when the middle class grows, the poor have a ladder up and the wealthy do very well. + +America used to have the best roads, bridges, and airports on Earth. + +Now our infrastructure is ranked 13th in the world. + +We won’t be able to compete for the jobs of the 21st Century if we don’t fix that. + +That’s why it was so important to pass the Bipartisan Infrastructure Law—the most sweeping investment to rebuild America in history. + +This was a bipartisan effort, and I want to thank the members of both parties who worked to make it happen. + +We’re done talking about infrastructure weeks. + +We’re going to have an infrastructure decade. + +It is going to transform America and put us on a path to win the economic competition of the 21st Century that we face with the rest of the world—particularly with China. + +As I’ve told Xi Jinping, it is never a good bet to bet against the American people. + +We’ll create good jobs for millions of Americans, modernizing roads, airports, ports, and waterways all across America. + +And we’ll do it all to withstand the devastating effects of the climate crisis and promote environmental justice. + +We’ll build a national network of 500,000 electric vehicle charging stations, begin to replace poisonous lead pipes—so every child—and every American—has clean water to drink at home and at school, provide affordable high-speed internet for every American—urban, suburban, rural, and tribal communities. + +4,000 projects have already been announced. + +And tonight, I’m announcing that this year we will start fixing over 65,000 miles of highway and 1,500 bridges in disrepair. + +When we use taxpayer dollars to rebuild America – we are going to Buy American: buy American products to support American jobs. + +The federal government spends about $600 Billion a year to keep the country safe and secure. + +There’s been a law on the books for almost a century +to make sure taxpayers’ dollars support American jobs and businesses. + +Every Administration says they’ll do it, but we are actually doing it. + +We will buy American to make sure everything from the deck of an aircraft carrier to the steel on highway guardrails are made in America. + +But to compete for the best jobs of the future, we also need to level the playing field with China and other competitors. + +That’s why it is so important to pass the Bipartisan Innovation Act sitting in Congress that will make record investments in emerging technologies and American manufacturing. + +Let me give you one example of why it’s so important to pass it. + +If you travel 20 miles east of Columbus, Ohio, you’ll find 1,000 empty acres of land. + +It won’t look like much, but if you stop and look closely, you’ll see a “Field of dreams,” the ground on which America’s future will be built. + +This is where Intel, the American company that helped build Silicon Valley, is going to build its $20 billion semiconductor “mega site”. + +Up to eight state-of-the-art factories in one place. 10,000 new good-paying jobs. + +Some of the most sophisticated manufacturing in the world to make computer chips the size of a fingertip that power the world and our everyday lives. + +Smartphones. The Internet. Technology we have yet to invent. + +But that’s just the beginning. + +Intel’s CEO, Pat Gelsinger, who is here tonight, told me they are ready to increase their investment from +$20 billion to $100 billion. + +That would be one of the biggest investments in manufacturing in American history. + +And all they’re waiting for is for you to pass this bill. + +So let’s not wait any longer. Send it to my desk. I’ll sign it. + +And we will really take off. + +And Intel is not alone. + +There’s something happening in America. + +Just look around and you’ll see an amazing story. + +The rebirth of the pride that comes from stamping products “Made In America.” The revitalization of American manufacturing. + +Companies are choosing to build new factories here, when just a few years ago, they would have built them overseas. + +That’s what is happening. Ford is investing $11 billion to build electric vehicles, creating 11,000 jobs across the country. + +GM is making the largest investment in its history—$7 billion to build electric vehicles, creating 4,000 jobs in Michigan. + +All told, we created 369,000 new manufacturing jobs in America just last year. + +Powered by people I’ve met like JoJo Burgess, from generations of union steelworkers from Pittsburgh, who’s here with us tonight. + +As Ohio Senator Sherrod Brown says, “It’s time to bury the label “Rust Belt.” + +It’s time. + +But with all the bright spots in our economy, record job growth and higher wages, too many families are struggling to keep up with the bills. + +Inflation is robbing them of the gains they might otherwise feel. + +I get it. That’s why my top priority is getting prices under control. + +Look, our economy roared back faster than most predicted, but the pandemic meant that businesses had a hard time hiring enough workers to keep up production in their factories. + +The pandemic also disrupted global supply chains. + +When factories close, it takes longer to make goods and get them from the warehouse to the store, and prices go up. + +Look at cars. + +Last year, there weren’t enough semiconductors to make all the cars that people wanted to buy. + +And guess what, prices of automobiles went up. + +So—we have a choice. + +One way to fight inflation is to drive down wages and make Americans poorer. + +I have a better plan to fight inflation. + +Lower your costs, not your wages. + +Make more cars and semiconductors in America. + +More infrastructure and innovation in America. + +More goods moving faster and cheaper in America. + +More jobs where you can earn a good living in America. + +And instead of relying on foreign supply chains, let’s make it in America. + +Economists call it “increasing the productive capacity of our economy.” + +I call it building a better America. + +My plan to fight inflation will lower your costs and lower the deficit. + +17 Nobel laureates in economics say my plan will ease long-term inflationary pressures. Top business leaders and most Americans support my plan. And here’s the plan: + +First – cut the cost of prescription drugs. Just look at insulin. One in ten Americans has diabetes. In Virginia, I met a 13-year-old boy named Joshua Davis. + +He and his Dad both have Type 1 diabetes, which means they need insulin every day. Insulin costs about $10 a vial to make. + +But drug companies charge families like Joshua and his Dad up to 30 times more. I spoke with Joshua’s mom. + +Imagine what it’s like to look at your child who needs insulin and have no idea how you’re going to pay for it. + +What it does to your dignity, your ability to look your child in the eye, to be the parent you expect to be. + +Joshua is here with us tonight. Yesterday was his birthday. Happy birthday, buddy. + +For Joshua, and for the 200,000 other young people with Type 1 diabetes, let’s cap the cost of insulin at $35 a month so everyone can afford it. + +Drug companies will still do very well. And while we’re at it let Medicare negotiate lower prices for prescription drugs, like the VA already does. + +Look, the American Rescue Plan is helping millions of families on Affordable Care Act plans save $2,400 a year on their health care premiums. Let’s close the coverage gap and make those savings permanent. + +Second – cut energy costs for families an average of $500 a year by combatting climate change. + +Let’s provide investments and tax credits to weatherize your homes and businesses to be energy efficient and you get a tax credit; double America’s clean energy production in solar, wind, and so much more; lower the price of electric vehicles, saving you another $80 a month because you’ll never have to pay at the gas pump again. + +Third – cut the cost of child care. Many families pay up to $14,000 a year for child care per child. + +Middle-class and working families shouldn’t have to pay more than 7% of their income for care of young children. + +My plan will cut the cost in half for most families and help parents, including millions of women, who left the workforce during the pandemic because they couldn’t afford child care, to be able to get back to work. + +My plan doesn’t stop there. It also includes home and long-term care. More affordable housing. And Pre-K for every 3- and 4-year-old. + +All of these will lower costs. + +And under my plan, nobody earning less than $400,000 a year will pay an additional penny in new taxes. Nobody. + +The one thing all Americans agree on is that the tax system is not fair. We have to fix it. + +I’m not looking to punish anyone. But let’s make sure corporations and the wealthiest Americans start paying their fair share. + +Just last year, 55 Fortune 500 corporations earned $40 billion in profits and paid zero dollars in federal income tax. + +That’s simply not fair. That’s why I’ve proposed a 15% minimum tax rate for corporations. + +We got more than 130 countries to agree on a global minimum tax rate so companies can’t get out of paying their taxes at home by shipping jobs and factories overseas. + +That’s why I’ve proposed closing loopholes so the very wealthy don’t pay a lower tax rate than a teacher or a firefighter. + +So that’s my plan. It will grow the economy and lower costs for families. + +So what are we waiting for? Let’s get this done. And while you’re at it, confirm my nominees to the Federal Reserve, which plays a critical role in fighting inflation. + +My plan will not only lower costs to give families a fair shot, it will lower the deficit. + +The previous Administration not only ballooned the deficit with tax cuts for the very wealthy and corporations, it undermined the watchdogs whose job was to keep pandemic relief funds from being wasted. + +But in my administration, the watchdogs have been welcomed back. + +We’re going after the criminals who stole billions in relief money meant for small businesses and millions of Americans. + +And tonight, I’m announcing that the Justice Department will name a chief prosecutor for pandemic fraud. + +By the end of this year, the deficit will be down to less than half what it was before I took office. + +The only president ever to cut the deficit by more than one trillion dollars in a single year. + +Lowering your costs also means demanding more competition. + +I’m a capitalist, but capitalism without competition isn’t capitalism. + +It’s exploitation—and it drives up prices. + +When corporations don’t have to compete, their profits go up, your prices go up, and small businesses and family farmers and ranchers go under. + +We see it happening with ocean carriers moving goods in and out of America. + +During the pandemic, these foreign-owned companies raised prices by as much as 1,000% and made record profits. + +Tonight, I’m announcing a crackdown on these companies overcharging American businesses and consumers. + +And as Wall Street firms take over more nursing homes, quality in those homes has gone down and costs have gone up. + +That ends on my watch. + +Medicare is going to set higher standards for nursing homes and make sure your loved ones get the care they deserve and expect. + +We’ll also cut costs and keep the economy going strong by giving workers a fair shot, provide more training and apprenticeships, hire them based on their skills not degrees. + +Let’s pass the Paycheck Fairness Act and paid leave. + +Raise the minimum wage to $15 an hour and extend the Child Tax Credit, so no one has to raise a family in poverty. + +Let’s increase Pell Grants and increase our historic support of HBCUs, and invest in what Jill—our First Lady who teaches full-time—calls America’s best-kept secret: community colleges. + +And let’s pass the PRO Act when a majority of workers want to form a union—they shouldn’t be stopped. + +When we invest in our workers, when we build the economy from the bottom up and the middle out together, we can do something we haven’t done in a long time: build a better America. + +For more than two years, COVID-19 has impacted every decision in our lives and the life of the nation. + +And I know you’re tired, frustrated, and exhausted. + +But I also know this. + +Because of the progress we’ve made, because of your resilience and the tools we have, tonight I can say +we are moving forward safely, back to more normal routines. + +We’ve reached a new moment in the fight against COVID-19, with severe cases down to a level not seen since last July. + +Just a few days ago, the Centers for Disease Control and Prevention—the CDC—issued new mask guidelines. + +Under these new guidelines, most Americans in most of the country can now be mask free. + +And based on the projections, more of the country will reach that point across the next couple of weeks. + +Thanks to the progress we have made this past year, COVID-19 need no longer control our lives. + +I know some are talking about “living with COVID-19”. Tonight – I say that we will never just accept living with COVID-19. + +We will continue to combat the virus as we do other diseases. And because this is a virus that mutates and spreads, we will stay on guard. + +Here are four common sense steps as we move forward safely. + +First, stay protected with vaccines and treatments. We know how incredibly effective vaccines are. If you’re vaccinated and boosted you have the highest degree of protection. + +We will never give up on vaccinating more Americans. Now, I know parents with kids under 5 are eager to see a vaccine authorized for their children. + +The scientists are working hard to get that done and we’ll be ready with plenty of vaccines when they do. + +We’re also ready with anti-viral treatments. If you get COVID-19, the Pfizer pill reduces your chances of ending up in the hospital by 90%. + +We’ve ordered more of these pills than anyone in the world. And Pfizer is working overtime to get us 1 Million pills this month and more than double that next month. + +And we’re launching the “Test to Treat” initiative so people can get tested at a pharmacy, and if they’re positive, receive antiviral pills on the spot at no cost. + +If you’re immunocompromised or have some other vulnerability, we have treatments and free high-quality masks. + +We’re leaving no one behind or ignoring anyone’s needs as we move forward. + +And on testing, we have made hundreds of millions of tests available for you to order for free. + +Even if you already ordered free tests tonight, I am announcing that you can order more from covidtests.gov starting next week. + +Second – we must prepare for new variants. Over the past year, we’ve gotten much better at detecting new variants. + +If necessary, we’ll be able to deploy new vaccines within 100 days instead of many more months or years. + +And, if Congress provides the funds we need, we’ll have new stockpiles of tests, masks, and pills ready if needed. + +I cannot promise a new variant won’t come. But I can promise you we’ll do everything within our power to be ready if it does. + +Third – we can end the shutdown of schools and businesses. We have the tools we need. + +It’s time for Americans to get back to work and fill our great downtowns again. People working from home can feel safe to begin to return to the office. + +We’re doing that here in the federal government. The vast majority of federal workers will once again work in person. + +Our schools are open. Let’s keep it that way. Our kids need to be in school. + +And with 75% of adult Americans fully vaccinated and hospitalizations down by 77%, most Americans can remove their masks, return to work, stay in the classroom, and move forward safely. + +We achieved this because we provided free vaccines, treatments, tests, and masks. + +Of course, continuing this costs money. + +I will soon send Congress a request. + +The vast majority of Americans have used these tools and may want to again, so I expect Congress to pass it quickly. + +Fourth, we will continue vaccinating the world. + +We’ve sent 475 Million vaccine doses to 112 countries, more than any other nation. + +And we won’t stop. + +We have lost so much to COVID-19. Time with one another. And worst of all, so much loss of life. + +Let’s use this moment to reset. Let’s stop looking at COVID-19 as a partisan dividing line and see it for what it is: A God-awful disease. + +Let’s stop seeing each other as enemies, and start seeing each other for who we really are: Fellow Americans. + +We can’t change how divided we’ve been. But we can change how we move forward—on COVID-19 and other issues we must face together. + +I recently visited the New York City Police Department days after the funerals of Officer Wilbert Mora and his partner, Officer Jason Rivera. + +They were responding to a 9-1-1 call when a man shot and killed them with a stolen gun. + +Officer Mora was 27 years old. + +Officer Rivera was 22. + +Both Dominican Americans who’d grown up on the same streets they later chose to patrol as police officers. + +I spoke with their families and told them that we are forever in debt for their sacrifice, and we will carry on their mission to restore the trust and safety every community deserves. + +I’ve worked on these issues a long time. + +I know what works: Investing in crime preventionand community police officers who’ll walk the beat, who’ll know the neighborhood, and who can restore trust and safety. + +So let’s not abandon our streets. Or choose between safety and equal justice. + +Let’s come together to protect our communities, restore trust, and hold law enforcement accountable. + +That’s why the Justice Department required body cameras, banned chokeholds, and restricted no-knock warrants for its officers. + +That’s why the American Rescue Plan provided $350 Billion that cities, states, and counties can use to hire more police and invest in proven strategies like community violence interruption—trusted messengers breaking the cycle of violence and trauma and giving young people hope. + +We should all agree: The answer is not to Defund the police. The answer is to FUND the police with the resources and training they need to protect our communities. + +I ask Democrats and Republicans alike: Pass my budget and keep our neighborhoods safe. + +And I will keep doing everything in my power to crack down on gun trafficking and ghost guns you can buy online and make at home—they have no serial numbers and can’t be traced. + +And I ask Congress to pass proven measures to reduce gun violence. Pass universal background checks. Why should anyone on a terrorist list be able to purchase a weapon? + +Ban assault weapons and high-capacity magazines. + +Repeal the liability shield that makes gun manufacturers the only industry in America that can’t be sued. + +These laws don’t infringe on the Second Amendment. They save lives. + +The most fundamental right in America is the right to vote – and to have it counted. And it’s under assault. + +In state after state, new laws have been passed, not only to suppress the vote, but to subvert entire elections. + +We cannot let this happen. + +Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while you’re at it, pass the Disclose Act so Americans can know who is funding our elections. + +Tonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service. + +One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court. + +And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence. + +A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder. Since she’s been nominated, she’s received a broad range of support—from the Fraternal Order of Police to former judges appointed by Democrats and Republicans. + +And if we are to advance liberty and justice, we need to secure the Border and fix the immigration system. + +We can do both. At our border, we’ve installed new technology like cutting-edge scanners to better detect drug smuggling. + +We’ve set up joint patrols with Mexico and Guatemala to catch more human traffickers. + +We’re putting in place dedicated immigration judges so families fleeing persecution and violence can have their cases heard faster. + +We’re securing commitments and supporting partners in South and Central America to host more refugees and secure their own borders. + +We can do all this while keeping lit the torch of liberty that has led generations of immigrants to this land—my forefathers and so many of yours. + +Provide a pathway to citizenship for Dreamers, those on temporary status, farm workers, and essential workers. + +Revise our laws so businesses have the workers they need and families don’t wait decades to reunite. + +It’s not only the right thing to do—it’s the economically smart thing to do. + +That’s why immigration reform is supported by everyone from labor unions to religious leaders to the U.S. Chamber of Commerce. + +Let’s get it done once and for all. + +Advancing liberty and justice also requires protecting the rights of women. + +The constitutional right affirmed in Roe v. Wade—standing precedent for half a century—is under attack as never before. + +If we want to go forward—not backward—we must protect access to health care. Preserve a woman’s right to choose. And let’s continue to advance maternal health care in America. + +And for our LGBTQ+ Americans, let’s finally get the bipartisan Equality Act to my desk. The onslaught of state laws targeting transgender Americans and their families is wrong. + +As I said last year, especially to our younger transgender Americans, I will always have your back as your President, so you can be yourself and reach your God-given potential. + +While it often appears that we never agree, that isn’t true. I signed 80 bipartisan bills into law last year. From preventing government shutdowns to protecting Asian-Americans from still-too-common hate crimes to reforming military justice. + +And soon, we’ll strengthen the Violence Against Women Act that I first wrote three decades ago. It is important for us to show the nation that we can come together and do big things. + +So tonight I’m offering a Unity Agenda for the Nation. Four big things we can do together. + +First, beat the opioid epidemic. + +There is so much we can do. Increase funding for prevention, treatment, harm reduction, and recovery. + +Get rid of outdated rules that stop doctors from prescribing treatments. And stop the flow of illicit drugs by working with state and local law enforcement to go after traffickers. + +If you’re suffering from addiction, know you are not alone. I believe in recovery, and I celebrate the 23 million Americans in recovery. + +Second, let’s take on mental health. Especially among our children, whose lives and education have been turned upside down. + +The American Rescue Plan gave schools money to hire teachers and help students make up for lost learning. + +I urge every parent to make sure your school does just that. And we can all play a part—sign up to be a tutor or a mentor. + +Children were also struggling before the pandemic. Bullying, violence, trauma, and the harms of social media. + +As Frances Haugen, who is here with us tonight, has shown, we must hold social media platforms accountable for the national experiment they’re conducting on our children for profit. + +It’s time to strengthen privacy protections, ban targeted advertising to children, demand tech companies stop collecting personal data on our children. + +And let’s get all Americans the mental health services they need. More people they can turn to for help, and full parity between physical and mental health care. + +Third, support our veterans. + +Veterans are the best of us. + +I’ve always believed that we have a sacred obligation to equip all those we send to war and care for them and their families when they come home. + +My administration is providing assistance with job training and housing, and now helping lower-income veterans get VA care debt-free. + +Our troops in Iraq and Afghanistan faced many dangers. + +One was stationed at bases and breathing in toxic smoke from “burn pits” that incinerated wastes of war—medical and hazard material, jet fuel, and more. + +When they came home, many of the world’s fittest and best trained warriors were never the same. + +Headaches. Numbness. Dizziness. + +A cancer that would put them in a flag-draped coffin. + +I know. + +One of those soldiers was my son Major Beau Biden. + +We don’t know for sure if a burn pit was the cause of his brain cancer, or the diseases of so many of our troops. + +But I’m committed to finding out everything we can. + +Committed to military families like Danielle Robinson from Ohio. + +The widow of Sergeant First Class Heath Robinson. + +He was born a soldier. Army National Guard. Combat medic in Kosovo and Iraq. + +Stationed near Baghdad, just yards from burn pits the size of football fields. + +Heath’s widow Danielle is here with us tonight. They loved going to Ohio State football games. He loved building Legos with their daughter. + +But cancer from prolonged exposure to burn pits ravaged Heath’s lungs and body. + +Danielle says Heath was a fighter to the very end. + +He didn’t know how to stop fighting, and neither did she. + +Through her pain she found purpose to demand we do better. + +Tonight, Danielle—we are. + +The VA is pioneering new ways of linking toxic exposures to diseases, already helping more veterans get benefits. + +And tonight, I’m announcing we’re expanding eligibility to veterans suffering from nine respiratory cancers. + +I’m also calling on Congress: pass a law to make sure veterans devastated by toxic exposures in Iraq and Afghanistan finally get the benefits and comprehensive health care they deserve. + +And fourth, let’s end cancer as we know it. + +This is personal to me and Jill, to Kamala, and to so many of you. + +Cancer is the #2 cause of death in America–second only to heart disease. + +Last month, I announced our plan to supercharge +the Cancer Moonshot that President Obama asked me to lead six years ago. + +Our goal is to cut the cancer death rate by at least 50% over the next 25 years, turn more cancers from death sentences into treatable diseases. + +More support for patients and families. + +To get there, I call on Congress to fund ARPA-H, the Advanced Research Projects Agency for Health. + +It’s based on DARPA—the Defense Department project that led to the Internet, GPS, and so much more. + +ARPA-H will have a singular purpose—to drive breakthroughs in cancer, Alzheimer’s, diabetes, and more. + +A unity agenda for the nation. + +We can do this. + +My fellow Americans—tonight , we have gathered in a sacred space—the citadel of our democracy. + +In this Capitol, generation after generation, Americans have debated great questions amid great strife, and have done great things. + +We have fought for freedom, expanded liberty, defeated totalitarianism and terror. + +And built the strongest, freest, and most prosperous nation the world has ever known. + +Now is the hour. + +Our moment of responsibility. + +Our test of resolve and conscience, of history itself. + +It is in this moment that our character is formed. Our purpose is found. Our future is forged. + +Well I know this nation. + +We will meet the test. + +To protect freedom and liberty, to expand fairness and opportunity. + +We will save democracy. + +As hard as these times have been, I am more optimistic about America today than I have been my whole life. + +Because I see the future that is within our grasp. + +Because I know there is simply nothing beyond our capacity. + +We are the only nation on Earth that has always turned every crisis we have faced into an opportunity. + +The only nation that can be defined by a single word: possibilities. + +So on this night, in our 245th year as a nation, I have come to report on the State of the Union. + +And my report is this: the State of the Union is strong—because you, the American people, are strong. + +We are stronger today than we were a year ago. + +And we will be stronger a year from now than we are today. + +Now is our moment to meet and overcome the challenges of our time. + +And we will, as one people. + +One America. + +The United States of America. + +May God bless you all. May God protect our troops. \ No newline at end of file diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..31d064c --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,2 @@ +xuserdata +*~ diff --git a/ios/MLCChat copy-Info.plist b/ios/MLCChat copy-Info.plist new file mode 100644 index 0000000..ff579a6 --- /dev/null +++ b/ios/MLCChat copy-Info.plist @@ -0,0 +1,8 @@ + + + + + UIFileSharingEnabled + + + diff --git a/ios/MLCChat.xcodeproj/project.pbxproj b/ios/MLCChat.xcodeproj/project.pbxproj new file mode 100644 index 0000000..cdf5205 --- /dev/null +++ b/ios/MLCChat.xcodeproj/project.pbxproj @@ -0,0 +1,769 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 1453A4CF2A1354B9001B909F /* StartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1453A4CA2A1354B9001B909F /* StartView.swift */; }; + 1453A4D02A1354B9001B909F /* ModelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1453A4CB2A1354B9001B909F /* ModelView.swift */; }; + 1453A4D12A1354B9001B909F /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1453A4CC2A1354B9001B909F /* AppState.swift */; }; + 1453A4D22A1354B9001B909F /* ModelConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1453A4CD2A1354B9001B909F /* ModelConfig.swift */; }; + 1453A4D32A1354B9001B909F /* ModelState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1453A4CE2A1354B9001B909F /* ModelState.swift */; }; + A773CC652A5DC98200467BFE /* ImageProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = A773CC642A5DC98200467BFE /* ImageProcessing.swift */; }; + AA14F2D42B911A9100308009 /* ImageProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = A773CC642A5DC98200467BFE /* ImageProcessing.swift */; }; + AA14F2D52B911A9100308009 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1453A4CC2A1354B9001B909F /* AppState.swift */; }; + AA14F2D62B911A9100308009 /* MLCChatApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D643B229F99A7F004DDAA4 /* MLCChatApp.swift */; }; + AA14F2D72B911A9100308009 /* ChatState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D643C029F99B07004DDAA4 /* ChatState.swift */; }; + AA14F2D82B911A9100308009 /* PerformanceMetrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF3673232A9E2A9300E6D5AB /* PerformanceMetrics.swift */; }; + AA14F2D92B911A9100308009 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D643C229F99B07004DDAA4 /* ChatView.swift */; }; + AA14F2DA2B911A9100308009 /* ModelState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1453A4CE2A1354B9001B909F /* ModelState.swift */; }; + AA14F2DB2B911A9100308009 /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D643C729F99B34004DDAA4 /* MessageView.swift */; }; + AA14F2DC2B911A9100308009 /* RestAwaitLib.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFEEEF112B6423560086AA32 /* RestAwaitLib.swift */; }; + AA14F2DD2B911A9100308009 /* ModelConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1453A4CD2A1354B9001B909F /* ModelConfig.swift */; }; + AA14F2DE2B911A9100308009 /* ParamsConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEC27EF92A85C2AC00254E67 /* ParamsConfig.swift */; }; + AA14F2DF2B911A9100308009 /* AppConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEC27EFB2A85C3B000254E67 /* AppConfig.swift */; }; + AA14F2E02B911A9100308009 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEC27F012A86337E00254E67 /* Constants.swift */; }; + AA14F2E12B911A9100308009 /* ModelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1453A4CB2A1354B9001B909F /* ModelView.swift */; }; + AA14F2E22B911A9100308009 /* StartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1453A4CA2A1354B9001B909F /* StartView.swift */; }; + AA14F2E42B911A9100308009 /* MLCSwift in Frameworks */ = {isa = PBXBuildFile; productRef = AA14F2D22B911A9100308009 /* MLCSwift */; }; + AA14F2E62B911A9100308009 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C0D643B929F99A80004DDAA4 /* Preview Assets.xcassets */; }; + AA14F2E72B911A9100308009 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C0D643B629F99A80004DDAA4 /* Assets.xcassets */; }; + AA14F2EA2B911A9100308009 /* app-config.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = C09834182A16F4CB00A05B51 /* app-config.json */; }; + AA14F2EB2B911A9100308009 /* dist in CopyFiles */ = {isa = PBXBuildFile; fileRef = C06A74E029F99C9F00BC4BE6 /* dist */; }; + AEC27EFA2A85C2AC00254E67 /* ParamsConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEC27EF92A85C2AC00254E67 /* ParamsConfig.swift */; }; + AEC27EFC2A85C3B000254E67 /* AppConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEC27EFB2A85C3B000254E67 /* AppConfig.swift */; }; + AEC27F022A86337E00254E67 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEC27F012A86337E00254E67 /* Constants.swift */; }; + C06A74F229F9A78800BC4BE6 /* dist in CopyFiles */ = {isa = PBXBuildFile; fileRef = C06A74E029F99C9F00BC4BE6 /* dist */; }; + C09834192A16F4E000A05B51 /* app-config.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = C09834182A16F4CB00A05B51 /* app-config.json */; }; + C0D643B329F99A7F004DDAA4 /* MLCChatApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D643B229F99A7F004DDAA4 /* MLCChatApp.swift */; }; + C0D643B729F99A80004DDAA4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C0D643B629F99A80004DDAA4 /* Assets.xcassets */; }; + C0D643BA29F99A80004DDAA4 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C0D643B929F99A80004DDAA4 /* Preview Assets.xcassets */; }; + C0D643C429F99B07004DDAA4 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D643C229F99B07004DDAA4 /* ChatView.swift */; }; + C0D643C829F99B34004DDAA4 /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D643C729F99B34004DDAA4 /* MessageView.swift */; }; + C0DDBDF62A39103F00E9D060 /* ChatState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D643C029F99B07004DDAA4 /* ChatState.swift */; }; + C0DDBE0D2A3BCD8000E9D060 /* MLCSwift in Frameworks */ = {isa = PBXBuildFile; productRef = C0DDBE0C2A3BCD8000E9D060 /* MLCSwift */; }; + CF3673242A9E2A9300E6D5AB /* PerformanceMetrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF3673232A9E2A9300E6D5AB /* PerformanceMetrics.swift */; }; + CFEEEF122B6423560086AA32 /* RestAwaitLib.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFEEEF112B6423560086AA32 /* RestAwaitLib.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + AA14F2E82B911A9100308009 /* Embed Libraries */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Libraries"; + runOnlyForDeploymentPostprocessing = 0; + }; + AA14F2E92B911A9100308009 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 7; + files = ( + AA14F2EA2B911A9100308009 /* app-config.json in CopyFiles */, + AA14F2EB2B911A9100308009 /* dist in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C06A74F129F9A78000BC4BE6 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 7; + files = ( + C09834192A16F4E000A05B51 /* app-config.json in CopyFiles */, + C06A74F229F9A78800BC4BE6 /* dist in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C0D643CF29F99C5D004DDAA4 /* Embed Libraries */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Libraries"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1453A4CA2A1354B9001B909F /* StartView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StartView.swift; sourceTree = ""; }; + 1453A4CB2A1354B9001B909F /* ModelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModelView.swift; sourceTree = ""; }; + 1453A4CC2A1354B9001B909F /* AppState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = ""; }; + 1453A4CD2A1354B9001B909F /* ModelConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModelConfig.swift; sourceTree = ""; }; + 1453A4CE2A1354B9001B909F /* ModelState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModelState.swift; sourceTree = ""; }; + A773CC642A5DC98200467BFE /* ImageProcessing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageProcessing.swift; sourceTree = ""; }; + AA14F2EF2B911A9100308009 /* MLCChat_rebased.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MLCChat_rebased.app; sourceTree = BUILT_PRODUCTS_DIR; }; + AA14F2F02B911A9100308009 /* MLCChat copy-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "MLCChat copy-Info.plist"; path = "/Users/steve/Documents/brave-projects/LLMs/MELT/frameworks/MLC/mlc-llm/ios/MLCChat copy-Info.plist"; sourceTree = ""; }; + AEC27EF92A85C2AC00254E67 /* ParamsConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParamsConfig.swift; sourceTree = ""; }; + AEC27EFB2A85C3B000254E67 /* AppConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfig.swift; sourceTree = ""; }; + AEC27F012A86337E00254E67 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + C06A74E029F99C9F00BC4BE6 /* dist */ = {isa = PBXFileReference; lastKnownFileType = folder; path = dist; sourceTree = ""; }; + C06A74E629F9A1DF00BC4BE6 /* MLCChat.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MLCChat.entitlements; sourceTree = ""; }; + C09834182A16F4CB00A05B51 /* app-config.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "app-config.json"; sourceTree = ""; }; + C0D643AF29F99A7F004DDAA4 /* MLCChat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MLCChat.app; sourceTree = BUILT_PRODUCTS_DIR; }; + C0D643B229F99A7F004DDAA4 /* MLCChatApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MLCChatApp.swift; sourceTree = ""; }; + C0D643B629F99A80004DDAA4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + C0D643B929F99A80004DDAA4 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + C0D643C029F99B07004DDAA4 /* ChatState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatState.swift; sourceTree = ""; }; + C0D643C229F99B07004DDAA4 /* ChatView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = ""; }; + C0D643C729F99B34004DDAA4 /* MessageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageView.swift; sourceTree = ""; }; + C0DDBE0B2A3BA6F800E9D060 /* MLCSwift */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = MLCSwift; sourceTree = ""; }; + CF3673232A9E2A9300E6D5AB /* PerformanceMetrics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PerformanceMetrics.swift; sourceTree = ""; }; + CFEEEF112B6423560086AA32 /* RestAwaitLib.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestAwaitLib.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + AA14F2E32B911A9100308009 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + AA14F2E42B911A9100308009 /* MLCSwift in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C0D643AC29F99A7F004DDAA4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C0DDBE0D2A3BCD8000E9D060 /* MLCSwift in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + AEC27EF82A85C29000254E67 /* Models */ = { + isa = PBXGroup; + children = ( + 1453A4CD2A1354B9001B909F /* ModelConfig.swift */, + AEC27EF92A85C2AC00254E67 /* ParamsConfig.swift */, + AEC27EFB2A85C3B000254E67 /* AppConfig.swift */, + ); + path = Models; + sourceTree = ""; + }; + AEC27EFF2A85EE2800254E67 /* States */ = { + isa = PBXGroup; + children = ( + 1453A4CE2A1354B9001B909F /* ModelState.swift */, + 1453A4CC2A1354B9001B909F /* AppState.swift */, + C0D643C029F99B07004DDAA4 /* ChatState.swift */, + ); + path = States; + sourceTree = ""; + }; + AEC27F002A86306800254E67 /* Views */ = { + isa = PBXGroup; + children = ( + A773CC642A5DC98200467BFE /* ImageProcessing.swift */, + 1453A4CB2A1354B9001B909F /* ModelView.swift */, + 1453A4CA2A1354B9001B909F /* StartView.swift */, + C0D643C729F99B34004DDAA4 /* MessageView.swift */, + C0D643C229F99B07004DDAA4 /* ChatView.swift */, + ); + path = Views; + sourceTree = ""; + }; + AEC27F032A86338800254E67 /* Common */ = { + isa = PBXGroup; + children = ( + AEC27F012A86337E00254E67 /* Constants.swift */, + ); + path = Common; + sourceTree = ""; + }; + C0D643A629F99A7F004DDAA4 = { + isa = PBXGroup; + children = ( + C0DDBDF02A39068900E9D060 /* Packages */, + C06A74E029F99C9F00BC4BE6 /* dist */, + C0D643B129F99A7F004DDAA4 /* MLCChat */, + C0D643B029F99A7F004DDAA4 /* Products */, + C0D643C929F99BDA004DDAA4 /* Frameworks */, + AA14F2F02B911A9100308009 /* MLCChat copy-Info.plist */, + ); + sourceTree = ""; + }; + C0D643B029F99A7F004DDAA4 /* Products */ = { + isa = PBXGroup; + children = ( + C0D643AF29F99A7F004DDAA4 /* MLCChat.app */, + AA14F2EF2B911A9100308009 /* MLCChat_rebased.app */, + ); + name = Products; + sourceTree = ""; + }; + C0D643B129F99A7F004DDAA4 /* MLCChat */ = { + isa = PBXGroup; + children = ( + C09834182A16F4CB00A05B51 /* app-config.json */, + AEC27F032A86338800254E67 /* Common */, + AEC27EF82A85C29000254E67 /* Models */, + AEC27EFF2A85EE2800254E67 /* States */, + AEC27F002A86306800254E67 /* Views */, + C06A74E629F9A1DF00BC4BE6 /* MLCChat.entitlements */, + C0D643B229F99A7F004DDAA4 /* MLCChatApp.swift */, + CF3673232A9E2A9300E6D5AB /* PerformanceMetrics.swift */, + CFEEEF112B6423560086AA32 /* RestAwaitLib.swift */, + C0D643B629F99A80004DDAA4 /* Assets.xcassets */, + C0D643B829F99A80004DDAA4 /* Preview Content */, + ); + path = MLCChat; + sourceTree = ""; + }; + C0D643B829F99A80004DDAA4 /* Preview Content */ = { + isa = PBXGroup; + children = ( + C0D643B929F99A80004DDAA4 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + C0D643C929F99BDA004DDAA4 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + C0DDBDF02A39068900E9D060 /* Packages */ = { + isa = PBXGroup; + children = ( + C0DDBE0B2A3BA6F800E9D060 /* MLCSwift */, + ); + name = Packages; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + AA14F2D12B911A9100308009 /* MLCChat_rebased */ = { + isa = PBXNativeTarget; + buildConfigurationList = AA14F2EC2B911A9100308009 /* Build configuration list for PBXNativeTarget "MLCChat_rebased" */; + buildPhases = ( + AA14F2D32B911A9100308009 /* Sources */, + AA14F2E32B911A9100308009 /* Frameworks */, + AA14F2E52B911A9100308009 /* Resources */, + AA14F2E82B911A9100308009 /* Embed Libraries */, + AA14F2E92B911A9100308009 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MLCChat_rebased; + packageProductDependencies = ( + AA14F2D22B911A9100308009 /* MLCSwift */, + ); + productName = MLCChat; + productReference = AA14F2EF2B911A9100308009 /* MLCChat_rebased.app */; + productType = "com.apple.product-type.application"; + }; + C0D643AE29F99A7F004DDAA4 /* MLCChat */ = { + isa = PBXNativeTarget; + buildConfigurationList = C0D643BD29F99A80004DDAA4 /* Build configuration list for PBXNativeTarget "MLCChat" */; + buildPhases = ( + C0D643AB29F99A7F004DDAA4 /* Sources */, + C0D643AC29F99A7F004DDAA4 /* Frameworks */, + C0D643AD29F99A7F004DDAA4 /* Resources */, + C0D643CF29F99C5D004DDAA4 /* Embed Libraries */, + C06A74F129F9A78000BC4BE6 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MLCChat; + packageProductDependencies = ( + C0DDBE0C2A3BCD8000E9D060 /* MLCSwift */, + ); + productName = MLCChat; + productReference = C0D643AF29F99A7F004DDAA4 /* MLCChat.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + C0D643A729F99A7F004DDAA4 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1430; + LastUpgradeCheck = 1430; + TargetAttributes = { + C0D643AE29F99A7F004DDAA4 = { + CreatedOnToolsVersion = 14.3; + LastSwiftMigration = 1430; + }; + }; + }; + buildConfigurationList = C0D643AA29F99A7F004DDAA4 /* Build configuration list for PBXProject "MLCChat" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = C0D643A629F99A7F004DDAA4; + productRefGroup = C0D643B029F99A7F004DDAA4 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + C0D643AE29F99A7F004DDAA4 /* MLCChat */, + AA14F2D12B911A9100308009 /* MLCChat_rebased */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + AA14F2E52B911A9100308009 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AA14F2E62B911A9100308009 /* Preview Assets.xcassets in Resources */, + AA14F2E72B911A9100308009 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C0D643AD29F99A7F004DDAA4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C0D643BA29F99A80004DDAA4 /* Preview Assets.xcassets in Resources */, + C0D643B729F99A80004DDAA4 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + AA14F2D32B911A9100308009 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AA14F2D42B911A9100308009 /* ImageProcessing.swift in Sources */, + AA14F2D52B911A9100308009 /* AppState.swift in Sources */, + AA14F2D62B911A9100308009 /* MLCChatApp.swift in Sources */, + AA14F2D72B911A9100308009 /* ChatState.swift in Sources */, + AA14F2D82B911A9100308009 /* PerformanceMetrics.swift in Sources */, + AA14F2D92B911A9100308009 /* ChatView.swift in Sources */, + AA14F2DA2B911A9100308009 /* ModelState.swift in Sources */, + AA14F2DB2B911A9100308009 /* MessageView.swift in Sources */, + AA14F2DC2B911A9100308009 /* RestAwaitLib.swift in Sources */, + AA14F2DD2B911A9100308009 /* ModelConfig.swift in Sources */, + AA14F2DE2B911A9100308009 /* ParamsConfig.swift in Sources */, + AA14F2DF2B911A9100308009 /* AppConfig.swift in Sources */, + AA14F2E02B911A9100308009 /* Constants.swift in Sources */, + AA14F2E12B911A9100308009 /* ModelView.swift in Sources */, + AA14F2E22B911A9100308009 /* StartView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C0D643AB29F99A7F004DDAA4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A773CC652A5DC98200467BFE /* ImageProcessing.swift in Sources */, + 1453A4D12A1354B9001B909F /* AppState.swift in Sources */, + C0D643B329F99A7F004DDAA4 /* MLCChatApp.swift in Sources */, + C0DDBDF62A39103F00E9D060 /* ChatState.swift in Sources */, + CF3673242A9E2A9300E6D5AB /* PerformanceMetrics.swift in Sources */, + C0D643C429F99B07004DDAA4 /* ChatView.swift in Sources */, + 1453A4D32A1354B9001B909F /* ModelState.swift in Sources */, + C0D643C829F99B34004DDAA4 /* MessageView.swift in Sources */, + CFEEEF122B6423560086AA32 /* RestAwaitLib.swift in Sources */, + 1453A4D22A1354B9001B909F /* ModelConfig.swift in Sources */, + AEC27EFA2A85C2AC00254E67 /* ParamsConfig.swift in Sources */, + AEC27EFC2A85C3B000254E67 /* AppConfig.swift in Sources */, + AEC27F022A86337E00254E67 /* Constants.swift in Sources */, + 1453A4D02A1354B9001B909F /* ModelView.swift in Sources */, + 1453A4CF2A1354B9001B909F /* StartView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + AA14F2ED2B911A9100308009 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = MLCChat/MLCChat.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; + DEVELOPMENT_ASSET_PATHS = "\"MLCChat/Preview Content\""; + DEVELOPMENT_TEAM = KL8N8XSYF4; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + "HEADER_SEARCH_PATHS[arch=*]" = ""; + INFOPLIST_FILE = "MLCChat copy-Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "MLChat++"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; + INFOPLIST_KEY_NSCameraUsageDescription = "This app requires usage of camera to function properly."; + INFOPLIST_KEY_NSLocalNetworkUsageDescription = "We require this persmission to notify Blade Runner service that the LLM task is completed."; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/lib", + ); + MARKETING_VERSION = 1.3; + OTHER_LDFLAGS = ( + "-Wl,-all_load", + "-lmodel_iphone", + "-lmlc_llm", + "-ltvm_runtime", + "-ltokenizers_cpp", + "-lsentencepiece", + "-ltokenizers_c", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.brave.mlc.Chat33; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + AA14F2EE2B911A9100308009 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = MLCChat/MLCChat.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; + DEVELOPMENT_ASSET_PATHS = "\"MLCChat/Preview Content\""; + DEVELOPMENT_TEAM = KL8N8XSYF4; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + "HEADER_SEARCH_PATHS[arch=*]" = ""; + INFOPLIST_FILE = "MLCChat copy-Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "MLChat++"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; + INFOPLIST_KEY_NSCameraUsageDescription = "This app requires usage of camera to function properly."; + INFOPLIST_KEY_NSLocalNetworkUsageDescription = "We require this persmission to notify Blade Runner service that the LLM task is completed."; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/lib", + ); + MARKETING_VERSION = 1.3; + OTHER_LDFLAGS = ( + "-Wl,-all_load", + "-lmodel_iphone", + "-lmlc_llm", + "-ltvm_runtime", + "-ltokenizers_cpp", + "-lsentencepiece", + "-ltokenizers_c", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.brave.mlc.Chat33; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + C0D643BB29F99A80004DDAA4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + C0D643BC29F99A80004DDAA4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + C0D643BE29F99A80004DDAA4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = MLCChat/MLCChat.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 6; + DEVELOPMENT_ASSET_PATHS = "\"MLCChat/Preview Content\""; + DEVELOPMENT_TEAM = KL8N8XSYF4; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + "HEADER_SEARCH_PATHS[arch=*]" = ""; + INFOPLIST_FILE = MLCChat/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = MLChat; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; + INFOPLIST_KEY_NSCameraUsageDescription = "This app requires usage of camera to function properly."; + INFOPLIST_KEY_NSLocalNetworkUsageDescription = "We require this persmission to notify Blade Runner service that the LLM task is completed."; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/lib", + ); + MARKETING_VERSION = 1.3; + OTHER_LDFLAGS = ( + "-Wl,-all_load", + "-lmodel_iphone", + "-lmlc_llm", + "-ltvm_runtime", + "-ltokenizers_cpp", + "-lsentencepiece", + "-ltokenizers_c", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.brave.mlc.Chat32; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + C0D643BF29F99A80004DDAA4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = MLCChat/MLCChat.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 6; + DEVELOPMENT_ASSET_PATHS = "\"MLCChat/Preview Content\""; + DEVELOPMENT_TEAM = KL8N8XSYF4; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + "HEADER_SEARCH_PATHS[arch=*]" = ""; + INFOPLIST_FILE = MLCChat/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = MLChat; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; + INFOPLIST_KEY_NSCameraUsageDescription = "This app requires usage of camera to function properly."; + INFOPLIST_KEY_NSLocalNetworkUsageDescription = "We require this persmission to notify Blade Runner service that the LLM task is completed."; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/lib", + ); + MARKETING_VERSION = 1.3; + OTHER_LDFLAGS = ( + "-Wl,-all_load", + "-lmodel_iphone", + "-lmlc_llm", + "-ltvm_runtime", + "-ltokenizers_cpp", + "-lsentencepiece", + "-ltokenizers_c", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.brave.mlc.Chat32; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + AA14F2EC2B911A9100308009 /* Build configuration list for PBXNativeTarget "MLCChat_rebased" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + AA14F2ED2B911A9100308009 /* Debug */, + AA14F2EE2B911A9100308009 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C0D643AA29F99A7F004DDAA4 /* Build configuration list for PBXProject "MLCChat" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C0D643BB29F99A80004DDAA4 /* Debug */, + C0D643BC29F99A80004DDAA4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C0D643BD29F99A80004DDAA4 /* Build configuration list for PBXNativeTarget "MLCChat" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C0D643BE29F99A80004DDAA4 /* Debug */, + C0D643BF29F99A80004DDAA4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCSwiftPackageProductDependency section */ + AA14F2D22B911A9100308009 /* MLCSwift */ = { + isa = XCSwiftPackageProductDependency; + productName = MLCSwift; + }; + C0DDBE0C2A3BCD8000E9D060 /* MLCSwift */ = { + isa = XCSwiftPackageProductDependency; + productName = MLCSwift; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = C0D643A729F99A7F004DDAA4 /* Project object */; +} diff --git a/ios/MLCChat.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/MLCChat.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/MLCChat.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/MLCChat.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/MLCChat.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/MLCChat.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/MLCChat.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/MLCChat.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/ios/MLCChat.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,5 @@ + + + + + diff --git a/ios/MLCChat.xcodeproj/xcshareddata/xcschemes/MLCChat.xcscheme b/ios/MLCChat.xcodeproj/xcshareddata/xcschemes/MLCChat.xcscheme new file mode 100644 index 0000000..311123f --- /dev/null +++ b/ios/MLCChat.xcodeproj/xcshareddata/xcschemes/MLCChat.xcscheme @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/MLCChat/Assets.xcassets/AccentColor.colorset/Contents.json b/ios/MLCChat/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/ios/MLCChat/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/MLCChat/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/MLCChat/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..7324dc2 --- /dev/null +++ b/ios/MLCChat/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "mlc-logo.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/MLCChat/Assets.xcassets/AppIcon.appiconset/mlc-logo.png b/ios/MLCChat/Assets.xcassets/AppIcon.appiconset/mlc-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4ae381da6c6e6ffdb918c63bddbe02cb685f7cea GIT binary patch literal 71139 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 NkWNthHiN<7>NYM1pH<)gZSzk}l9m%a!F#Zb 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 z

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 L>2cvBqu2ijl1EEG literal 0 HcmV?d00001 diff --git a/ios/MLCChat/Assets.xcassets/Contents.json b/ios/MLCChat/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios/MLCChat/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/MLCChat/Common/Constants.swift b/ios/MLCChat/Common/Constants.swift new file mode 100644 index 0000000..cf3a240 --- /dev/null +++ b/ios/MLCChat/Common/Constants.swift @@ -0,0 +1,11 @@ +// +// Constants.swift +// MLCChat +// + +struct Constants { + static let prebuiltModelDir = "dist" + static let appConfigFileName = "app-config.json" + static let modelConfigFileName = "mlc-chat-config.json" + static let paramsConfigFileName = "ndarray-cache.json" +} diff --git a/ios/MLCChat/Info.plist b/ios/MLCChat/Info.plist new file mode 100644 index 0000000..ff579a6 --- /dev/null +++ b/ios/MLCChat/Info.plist @@ -0,0 +1,8 @@ + + + + + UIFileSharingEnabled + + + diff --git a/ios/MLCChat/MLCChat.entitlements b/ios/MLCChat/MLCChat.entitlements new file mode 100644 index 0000000..caa3d58 --- /dev/null +++ b/ios/MLCChat/MLCChat.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.developer.kernel.extended-virtual-addressing + + com.apple.developer.kernel.increased-memory-limit + + + diff --git a/ios/MLCChat/MLCChatApp.swift b/ios/MLCChat/MLCChatApp.swift new file mode 100644 index 0000000..fcefd6f --- /dev/null +++ b/ios/MLCChat/MLCChatApp.swift @@ -0,0 +1,28 @@ +// +// MLCChatApp.swift +// MLCChat +// +// Created by Tianqi Chen on 4/26/23. +// + +import SwiftUI + +@main +struct MLCChatApp: App { + @StateObject private var appState = AppState() + + init() { + UITableView.appearance().separatorStyle = .none + UITableView.appearance().tableFooterView = UIView() + } + + var body: some Scene { + WindowGroup { + StartView() + .environmentObject(appState) + .task { + appState.loadAppConfigAndModels() + } + } + } +} diff --git a/ios/MLCChat/Models/AppConfig.swift b/ios/MLCChat/Models/AppConfig.swift new file mode 100644 index 0000000..69867b0 --- /dev/null +++ b/ios/MLCChat/Models/AppConfig.swift @@ -0,0 +1,28 @@ +// +// AppConfig.swift +// MLCChat +// + +struct AppConfig: Codable { + struct ModelRecord: Codable { + let modelPath: String? + let modelURL: String? + let modelLib: String + let estimatedVRAMReq: Int + let modelID: String + + enum CodingKeys: String, CodingKey { + case modelPath = "model_path" + case modelURL = "model_url" + case modelLib = "model_lib" + case estimatedVRAMReq = "estimated_vram_bytes" + case modelID = "model_id" + } + } + + var modelList: [ModelRecord] + + enum CodingKeys: String, CodingKey { + case modelList = "model_list" + } +} diff --git a/ios/MLCChat/Models/ModelConfig.swift b/ios/MLCChat/Models/ModelConfig.swift new file mode 100644 index 0000000..4ed8819 --- /dev/null +++ b/ios/MLCChat/Models/ModelConfig.swift @@ -0,0 +1,18 @@ +// +// ModelConfig.swift +// MLCChat +// + +struct ModelConfig: Decodable { + let tokenizerFiles: [String] + var modelLib: String? + var modelID: String? + var estimatedVRAMReq: Int? + + enum CodingKeys: String, CodingKey { + case tokenizerFiles = "tokenizer_files" + case modelLib = "model_lib" + case modelID = "model_id" + case estimatedVRAMReq = "estimated_vram_req" + } +} diff --git a/ios/MLCChat/Models/ParamsConfig.swift b/ios/MLCChat/Models/ParamsConfig.swift new file mode 100644 index 0000000..2635afa --- /dev/null +++ b/ios/MLCChat/Models/ParamsConfig.swift @@ -0,0 +1,12 @@ +// +// ParamsConfig.swift +// MLCChat +// + +struct ParamsConfig: Decodable { + struct ParamsRecord: Decodable { + let dataPath: String + } + + let records: [ParamsRecord] +} diff --git a/ios/MLCChat/PerformanceMetrics.swift b/ios/MLCChat/PerformanceMetrics.swift new file mode 100644 index 0000000..eede59b --- /dev/null +++ b/ios/MLCChat/PerformanceMetrics.swift @@ -0,0 +1,61 @@ +// +// PerformanceMetrics.swift +// MLCChat +// +// Created by Kleomenis Katevas on 07/06/2023. +// + +import Foundation + + +struct ConversationRecord: Codable { + let modelName: String + var modelLoadTime: TimeRecord? + var questionRecords: [QuestionRecord] = [] + + init(modelName: String) { + self.modelName = modelName + } +} + +struct QuestionRecord: Codable { + let time: TimeRecord + let input, output: String + let original_session_tokens, input_tokens, output_tokens: Int + let runtimeStats: String +} + +struct TimeRecord: Codable { + let start: Date + let duration: TimeInterval +} + +class ConversationsRecordManager: ObservableObject { + @Published private var conversations: [ConversationRecord] = [] + + func addConversationRecord(_ conversation: ConversationRecord) { + conversations.append(conversation) + } + + func saveToFile(withFileName fileName: String) { + let fileManager = FileManager.default + let directoryURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0] + let fileURL = directoryURL.appendingPathComponent("\(fileName).json") + + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + encoder.dateEncodingStrategy = .custom { (date, encoder) in + var container = encoder.singleValueContainer() + let timestamp = date.timeIntervalSince1970 + try container.encode(timestamp) + } + + do { + let data = try encoder.encode(conversations) + try data.write(to: fileURL) + print("Energy measurements JSON file successfully saved at \(fileURL)") + } catch { + print("Failed to write JSON data: \(error.localizedDescription)") + } + } +} diff --git a/ios/MLCChat/Preview Content/Preview Assets.xcassets/Contents.json b/ios/MLCChat/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios/MLCChat/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/MLCChat/RestAwaitLib.swift b/ios/MLCChat/RestAwaitLib.swift new file mode 100644 index 0000000..32aabbf --- /dev/null +++ b/ios/MLCChat/RestAwaitLib.swift @@ -0,0 +1,47 @@ +// +// RestAwaitLib.swift +// MLCChat +// +// Created by Kleomenis Katevas on 26/01/2024. +// + +import Foundation +import Network + +class RestAwaitLib { + let host: String + let port: Int + + static func requestPermission() { + // dummy url + let url = URL(string: "http://192.168.1.1:8080")! + let task = URLSession.shared.dataTask(with: url) { data, response, error in + // Nothing + } + + task.resume() + } + + init(host: String, port: Int) { + self.host = host + self.port = port + } + + func continueExecution(completion: @escaping (String?, Error?) -> Void) { + guard let url = URL(string: "http://\(host):\(port)/continue") else { + completion(nil, NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])) + return + } + + let task = URLSession.shared.dataTask(with: url) { data, response, error in + guard let data = data, error == nil else { + completion(nil, error) + return + } + let responseString = String(data: data, encoding: .utf8) + completion(responseString, nil) + } + + task.resume() + } +} diff --git a/ios/MLCChat/States/AppState.swift b/ios/MLCChat/States/AppState.swift new file mode 100644 index 0000000..22e16f4 --- /dev/null +++ b/ios/MLCChat/States/AppState.swift @@ -0,0 +1,274 @@ +// +// AppState.swift +// MLCChat +// +// Created by Yaxing Cai on 5/13/23. +// + +import Foundation + +final class AppState: ObservableObject { + @Published var models = [ModelState]() + @Published var chatState = ChatState() + + @Published var alertMessage = "" // TODO: Should move out + @Published var alertDisplayed = false // TODO: Should move out + + var conversationsRecordManager = ConversationsRecordManager() + + private var appConfig: AppConfig? + private var modelIDs = Set() + + private let fileManager: FileManager = FileManager.default + private lazy var cacheDirectoryURL: URL = { + fileManager.urls(for: .cachesDirectory, in: .userDomainMask)[0] + }() + + private let jsonDecoder = JSONDecoder() + private let jsonEncoder = JSONEncoder() + + func loadAppConfigAndModels() { + appConfig = loadAppConfig() + // Can't do anything without a valid app config + guard let appConfig else { + return + } + loadModelsConfig(modelList: appConfig.modelList) + } + + func requestDeleteModel(modelID: String) { + // model dir should have been deleted in ModelState + assert(!fileManager.fileExists(atPath: cacheDirectoryURL.appending(path: modelID).path())) + modelIDs.remove(modelID) + models.removeAll(where: {$0.modelConfig.modelID == modelID}) + updateAppConfig { + appConfig?.modelList.removeAll(where: {$0.modelID == modelID}) + } + } +} + +private extension AppState { + func loadAppConfig() -> AppConfig? { + // models in cache to download + var appConfigFileURL = cacheDirectoryURL.appending(path: Constants.appConfigFileName) + if !fileManager.fileExists(atPath: appConfigFileURL.path()) { + appConfigFileURL = Bundle.main.bundleURL.appending(path: Constants.appConfigFileName) + } + assert(fileManager.fileExists(atPath: appConfigFileURL.path())) + + do { + let fileHandle = try FileHandle(forReadingFrom: appConfigFileURL) + let data = fileHandle.readDataToEndOfFile() + + let appConfig = try jsonDecoder.decode(AppConfig.self, from: data) + return appConfig + } catch { + showAlert(message: "Failed to load app config: \(error.localizedDescription)") + return nil + } + } + + func loadModelsConfig(modelList: [AppConfig.ModelRecord]) { + for model in modelList { + if model.modelPath != nil { + // local model + let documentsPath = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first + let modelBaseURL = documentsPath!.appending(path: model.modelID) + let modelDir = modelBaseURL + let modelConfigURL = modelDir.appending(path: Constants.modelConfigFileName) + if fileManager.fileExists(atPath: modelConfigURL.path()) { + if let modelConfig = loadModelConfig( + modelConfigURL: modelConfigURL, + modelLib: model.modelLib, + modelID: model.modelID, + estimatedVRAMReq: model.estimatedVRAMReq + ) { + addModelConfig( + modelConfig: modelConfig, + modelPath: model.modelPath!, + modelURL: nil, + isBuiltin: true + ) + } else { + showAlert(message: "Failed to load prebuilt model: \(model.modelPath!)") + } + } else { + showAlert(message: "Prebuilt mlc-chat-config.json file not found: \(model.modelPath!)") + } + } else if model.modelURL != nil { + // remote model + let modelConfigFileURL = cacheDirectoryURL + .appending(path: model.modelID) + .appending(path: Constants.modelConfigFileName) + if fileManager.fileExists(atPath: modelConfigFileURL.path()) { + if let modelConfig = loadModelConfig( + modelConfigURL: modelConfigFileURL, + modelLib: model.modelLib, + modelID: model.modelID, + estimatedVRAMReq: model.estimatedVRAMReq + ) { + addModelConfig( + modelConfig: modelConfig, + modelPath: nil, + modelURL: URL(string: model.modelURL!), + isBuiltin: true + ) + } + } else { + downloadConfig( + modelURL: URL(string: model.modelURL!), + modelLib: model.modelLib, + modelID: model.modelID, + estimatedVRAMReq: model.estimatedVRAMReq, + isBuiltin: true + ) + } + } else { + showAlert(message: "Path or URL should be provided in app config: \(model.modelID)") + } + } + } + + func loadModelConfig(modelConfigURL: URL, modelLib: String, modelID: String, estimatedVRAMReq: Int) -> ModelConfig? { + do { + assert(fileManager.fileExists(atPath: modelConfigURL.path())) + let fileHandle = try FileHandle(forReadingFrom: modelConfigURL) + let data = fileHandle.readDataToEndOfFile() + var modelConfig = try jsonDecoder.decode(ModelConfig.self, from: data) + modelConfig.modelLib = modelLib + modelConfig.modelID = modelID + modelConfig.estimatedVRAMReq = estimatedVRAMReq + return modelConfig + } catch { + showAlert(message: "Failed to resolve model config: \(error.localizedDescription)") + } + return nil + } + + func showAlert(message: String) { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + if !self.alertDisplayed { + self.alertMessage = message + self.alertDisplayed = true + } else { + self.alertMessage.append("\n" + message) + } + } + } + + func downloadConfig(modelURL: URL?, modelLib: String, modelID: String, estimatedVRAMReq: Int, isBuiltin: Bool) { + guard let modelConfigURL = modelURL?.appending(path: "resolve").appending(path: "main").appending(path: Constants.modelConfigFileName) else { + return + } + + let downloadTask = URLSession.shared.downloadTask(with: modelConfigURL) { + [weak self] urlOrNil, responseOrNil, errorOrNil in + guard let self else { + return + } + if let error = errorOrNil { + self.showAlert(message: "Failed to download model config: \(error.localizedDescription)") + return + } + guard let fileUrl = urlOrNil else { + self.showAlert(message: "Failed to download model config") + return + } + + // cache temp file to avoid being deleted by system automatically + let tempName = UUID().uuidString + let tempFileURL = self.cacheDirectoryURL.appending(path: tempName) + + do { + try self.fileManager.moveItem(at: fileUrl, to: tempFileURL) + } catch { + self.showAlert(message: "Failed to cache downloaded file: \(error.localizedDescription)") + return + } + + do { + guard let modelConfig = loadModelConfig( + modelConfigURL: tempFileURL, + modelLib: modelLib, + modelID: modelID, + estimatedVRAMReq: estimatedVRAMReq + ) else { + try fileManager.removeItem(at: tempFileURL) + return + } + + if modelIDs.contains(modelConfig.modelID!) { + try fileManager.removeItem(at: tempFileURL) + return + } + + let modelBaseUrl = cacheDirectoryURL.appending(path: modelConfig.modelID!) + try fileManager.createDirectory(at: modelBaseUrl, withIntermediateDirectories: true) + let modelConfigUrl = modelBaseUrl.appending(path: Constants.modelConfigFileName) + try fileManager.moveItem(at: tempFileURL, to: modelConfigUrl) + assert(fileManager.fileExists(atPath: modelConfigUrl.path())) + assert(!fileManager.fileExists(atPath: tempFileURL.path())) + addModelConfig( + modelConfig: modelConfig, + modelPath: nil, + modelURL: modelURL, + isBuiltin: isBuiltin + ) + } catch { + showAlert(message: "Failed to import model: \(error.localizedDescription)") + } + } + downloadTask.resume() + } + + func addModelConfig(modelConfig: ModelConfig, modelPath: String?, modelURL: URL?, isBuiltin: Bool) { + assert(!modelIDs.contains(modelConfig.modelID!)) + modelIDs.insert(modelConfig.modelID!) + let modelBaseURL: URL + + // model_id dir should exist + if modelURL == nil { + // prebuilt model in dist + let documentsPath = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first + modelBaseURL = documentsPath!.appending(path: modelConfig.modelID!) + } else { + // download model in cache + modelBaseURL = cacheDirectoryURL.appending(path: modelConfig.modelID!) + } + assert(fileManager.fileExists(atPath: modelBaseURL.path())) + + // mlc-chat-config.json should exist + let modelConfigURL = modelBaseURL.appending(path: Constants.modelConfigFileName) + assert(fileManager.fileExists(atPath: modelConfigURL.path())) + + let model = ModelState(modelConfig: modelConfig, modelLocalBaseURL: modelBaseURL, startState: self, chatState: chatState) + model.checkModelDownloadState(modelURL: modelURL) + models.append(model) + + if modelURL != nil && !isBuiltin { + updateAppConfig { + appConfig?.modelList.append( + AppConfig.ModelRecord( + modelPath: nil, + modelURL: modelURL!.absoluteString, + modelLib: modelConfig.modelLib!, + estimatedVRAMReq: modelConfig.estimatedVRAMReq!, + modelID: modelConfig.modelID! + ) + ) + } + } + } + + func updateAppConfig(action: () -> Void) { + action() + let appConfigURL = cacheDirectoryURL.appending(path: Constants.appConfigFileName) + do { + let data = try jsonEncoder.encode(appConfig) + try data.write(to: appConfigURL, options: Data.WritingOptions.atomic) + } catch { + print(error.localizedDescription) + } + } +} diff --git a/ios/MLCChat/States/ChatState.swift b/ios/MLCChat/States/ChatState.swift new file mode 100644 index 0000000..54dd720 --- /dev/null +++ b/ios/MLCChat/States/ChatState.swift @@ -0,0 +1,499 @@ +// +// ChatState.swift +// LLMChat +// + +import Foundation +import MLCSwift + +enum MessageRole { + case user + case bot +} + +extension MessageRole { + var isUser: Bool { self == .user } +} + +struct MessageData: Hashable { + let id = UUID() + var role: MessageRole + var message: String +} + +final class ChatState: ObservableObject { + fileprivate enum ModelChatState { + case generating + case resetting + case reloading + case terminating + case ready + case failed + case pendingImageUpload + case processingImage + } + + @Published var messages = [MessageData]() + @Published var infoText = "" + @Published var displayName = "" + @Published var useVision = false + + private let modelChatStateLock = NSLock() + private var modelChatState: ModelChatState = .ready + + private let threadWorker = ThreadWorker() + private let chatModule = ChatModule() + private var modelLib = "" + private var modelPath = "" + var modelID = "" + + var modelLoadTime: TimeRecord? + + init() { + threadWorker.qualityOfService = QualityOfService.userInteractive + threadWorker.start() + + RestAwaitLib.requestPermission() + } + + func getFileURLFromName(_ name: String) -> URL { + let fileManager = FileManager.default + let documentsPath = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first + let destinationURL = documentsPath!.appendingPathComponent(name) + return destinationURL + } + + // read input.json and return [[questions]] (conversation with questions) + func readInputFile() -> [[String]] { + + let url = getFileURLFromName("input.json") + do { + // Load the data from the file into a Data object + let data = try Data(contentsOf: url) + + // Decode the JSON data + let jsonDecoder = JSONDecoder() + let questions = try jsonDecoder.decode([[String]].self, from: data) + + return questions + + } catch { + print("Error reading or decoding file: \(error)") + return [] + } + } + + var isInterruptible: Bool { + return getModelChatState() == .ready + || getModelChatState() == .generating + || getModelChatState() == .failed + || getModelChatState() == .pendingImageUpload + } + + var isChattable: Bool { + return getModelChatState() == .ready + } + + var isUploadable: Bool { + return getModelChatState() == .pendingImageUpload + } + + var isResettable: Bool { + return getModelChatState() == .ready + || getModelChatState() == .generating + } + + func requestResetChat() { + assert(isResettable) + interruptChat(prologue: { + switchToResetting() + }, epilogue: { [weak self] in + self?.mainResetChat() + }) + } + + func requestTerminateChat(callback: @escaping () -> Void) { + assert(isInterruptible) + interruptChat(prologue: { + switchToTerminating() + }, epilogue: { [weak self] in + self?.mainTerminateChat(callback: callback) + }) + } + + func requestReloadChat(modelID: String, modelLib: String, modelPath: String, estimatedVRAMReq: Int, displayName: String) { + if (isCurrentModel(modelID: modelID)) { + return + } + assert(isInterruptible) + interruptChat(prologue: { + switchToReloading() + }, epilogue: { [weak self] in + self?.mainReloadChat(modelID: modelID, + modelLib: modelLib, + modelPath: modelPath, + estimatedVRAMReq: estimatedVRAMReq, + displayName: displayName) + }) + } + + func requestGenerate(prompt: String) { + assert(isChattable) + switchToGenerating() + appendMessage(role: .user, message: prompt) + appendMessage(role: .bot, message: "") + threadWorker.push {[weak self] in + guard let self else { return } + chatModule.prefill(prompt) + while !chatModule.stopped() { + chatModule.decode() + if let newText = chatModule.getMessage() { + DispatchQueue.main.async { + self.updateMessage(role: .bot, message: newText) + } + } + + if getModelChatState() != .generating { + break + } + } + if getModelChatState() == .generating { + if let runtimeStats = chatModule.runtimeStatsText(useVision) { + DispatchQueue.main.async { + self.infoText = runtimeStats + self.switchToReady() + } + } + } + } + } + + func requestAutomation(measurementFilename: String) { + + let conversationsRecordManager = ConversationsRecordManager() + let conversations = readInputFile() + + assert(isChattable) + switchToGenerating() + + threadWorker.push {[self] in + + // per conversation + for (c_idx, conversation) in conversations.enumerated() { + + var conversationRecord = ConversationRecord(modelName: self.displayName) + conversationRecord.modelLoadTime = self.modelLoadTime + + for (q_idx, question) in conversation.enumerated() { + + DispatchQueue.main.async { + self.appendMessage(role: .user, message: "\(c_idx)_\(q_idx): \(question)") + } + + //print(question) + + let timeStart = Date() + + chatModule.prefill(question) + + while !chatModule.stopped() { + chatModule.decode() + if getModelChatState() != .generating { + break + } + } + + let runtimeStatsText = chatModule.runtimeStatsText(useVision)! + + let jsonResult = parseJSON(from: runtimeStatsText)! + let original_session_tokens = -1 + let input_tokens = Int(jsonResult["prefill"]!["total tokens"]!.components(separatedBy: " ")[0]) + let output_tokens = Int(jsonResult["decode"]!["total tokens"]!.components(separatedBy: " ")[0]) + + //print(chatModule.getMessage()!) + let questionRecord = QuestionRecord.init(time: TimeRecord(start: timeStart, duration: -timeStart.timeIntervalSinceNow), + input: question, + output: chatModule.getMessage(), + original_session_tokens: original_session_tokens, + input_tokens: input_tokens!, + output_tokens: output_tokens!, + runtimeStats: runtimeStatsText) + conversationRecord.questionRecords.append(questionRecord) + + if let newText = chatModule.getMessage() { + DispatchQueue.main.async { + self.appendMessage(role: .bot, message: "\(c_idx)_\(q_idx): \(newText)") + } + } + + Thread.sleep(forTimeInterval: 5.0) + } + + // Save energy events for particular session + chatModule.saveEnergyEventsToCSV(withFilename: "\(measurementFilename)_conv\(c_idx).csv") + + // add metrics + conversationsRecordManager.addConversationRecord(conversationRecord) + + // clear context + chatModule.resetChat() + chatModule.resetEnergyEvents() + + DispatchQueue.main.async { + self.appendMessage(role: .bot, message: "--sleep--") + } + Thread.sleep(forTimeInterval: 60.0) + } + + // Add session and save + conversationsRecordManager.saveToFile(withFileName: measurementFilename) + + // Notify BladeRunner that task is complete + let restAwaitLib = RestAwaitLib(host: "192.168.1.42", port: 5100) + restAwaitLib.continueExecution { response, error in + if (response != nil) { + print(response!) + } + else { + print(error!) + } + } + + // Exit app + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + UIApplication.shared.perform(#selector(NSXPCConnection.suspend)) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + exit(0) + } + } + } + } + + func parseJSON(from jsonString: String) -> [String: [String: String]]? { + guard let jsonData = jsonString.data(using: .utf8) else { + print("Error: Cannot create Data from JSON string") + return nil + } + + do { + let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: []) + + if let dictionary = jsonObject as? [String: [String: String]] { + return dictionary + } else { + print("Error: JSON is not in the expected format [String: [String: String]]") + return nil + } + + } catch { + print("Error parsing JSON: \(error)") + return nil + } + } + + func requestProcessImage(image: UIImage) { + assert(getModelChatState() == .pendingImageUpload) + switchToProcessingImage() + threadWorker.push {[weak self] in + guard let self else { return } + assert(messages.count > 0) + DispatchQueue.main.async { + self.updateMessage(role: .bot, message: "[System] Processing image") + } + // step 1. resize image + let new_image = resizeImage(image: image, width: 112, height: 112) + // step 2. prefill image by chatModule.prefillImage() + chatModule.prefillImage(new_image, prevPlaceholder: "", postPlaceholder: " ") + DispatchQueue.main.async { + self.updateMessage(role: .bot, message: "[System] Ready to chat") + self.switchToReady() + } + } + } + + func isCurrentModel(modelID: String) -> Bool { + return self.modelID == modelID + } +} + +private extension ChatState { + func getModelChatState() -> ModelChatState { + modelChatStateLock.lock() + defer { modelChatStateLock.unlock() } + return modelChatState + } + + func setModelChatState(_ newModelChatState: ModelChatState) { + modelChatStateLock.lock() + modelChatState = newModelChatState + modelChatStateLock.unlock() + } + + func appendMessage(role: MessageRole, message: String) { + messages.append(MessageData(role: role, message: message)) + } + + func updateMessage(role: MessageRole, message: String) { + messages[messages.count - 1] = MessageData(role: role, message: message) + } + + func clearHistory() { + messages.removeAll() + infoText = "" + } + + func switchToResetting() { + setModelChatState(.resetting) + } + + func switchToGenerating() { + setModelChatState(.generating) + } + + func switchToReloading() { + setModelChatState(.reloading) + } + + func switchToReady() { + setModelChatState(.ready) + } + + func switchToTerminating() { + setModelChatState(.terminating) + } + + func switchToFailed() { + setModelChatState(.failed) + } + + func switchToPendingImageUpload() { + setModelChatState(.pendingImageUpload) + } + + func switchToProcessingImage() { + setModelChatState(.processingImage) + } + + func interruptChat(prologue: () -> Void, epilogue: @escaping () -> Void) { + assert(isInterruptible) + if getModelChatState() == .ready + || getModelChatState() == .failed + || getModelChatState() == .pendingImageUpload { + prologue() + epilogue() + } else if getModelChatState() == .generating { + prologue() + threadWorker.push { + DispatchQueue.main.async { + epilogue() + } + } + } else { + assert(false) + } + } + + func mainResetChat() { + threadWorker.push {[weak self] in + guard let self else { return } + chatModule.resetChat() + if useVision { + chatModule.resetImageModule() + } + DispatchQueue.main.async { + self.clearHistory() + if self.useVision { + self.appendMessage(role: .bot, message: "[System] Upload an image to chat") + self.switchToPendingImageUpload() + } else { + self.switchToReady() + } + } + } + } + + func mainTerminateChat(callback: @escaping () -> Void) { + threadWorker.push {[weak self] in + guard let self else { return } + if useVision { + chatModule.unloadImageModule() + } + chatModule.unload() + DispatchQueue.main.async { + self.clearHistory() + self.modelID = "" + self.modelLib = "" + self.modelPath = "" + self.displayName = "" + self.useVision = false + self.switchToReady() + callback() + } + } + } + + func mainReloadChat(modelID: String, modelLib: String, modelPath: String, estimatedVRAMReq: Int, displayName: String) { + clearHistory() + let prevUseVision = useVision + self.modelID = modelID + self.modelLib = modelLib + self.modelPath = modelPath + self.displayName = displayName + self.useVision = displayName.hasPrefix("minigpt") + threadWorker.push {[weak self] in + guard let self else { return } + DispatchQueue.main.async { + self.appendMessage(role: .bot, message: "[System] Initalize...") + } + + let modelTimeStart = Date() + + if prevUseVision { + chatModule.unloadImageModule() + } + chatModule.unload() + let vRAM = os_proc_available_memory() + if (vRAM < estimatedVRAMReq) { + let requiredMemory = String ( + format: "%.1fMB", Double(estimatedVRAMReq) / Double(1 << 20) + ) + let errorMessage = ( + "Sorry, the system cannot provide \(requiredMemory) VRAM as requested to the app, " + + "so we cannot initialize this model on this device." + ) + DispatchQueue.main.sync { + self.messages.append(MessageData(role: MessageRole.bot, message: errorMessage)) + self.switchToFailed() + } + return + } + + if useVision { + // load vicuna model + let dir = (modelPath as NSString).deletingLastPathComponent + let vicunaModelLib = "vicuna-7b-v1.3-q3f16_0" + let vicunaModelPath = dir + "/" + vicunaModelLib + let appConfigJSONData = try? JSONSerialization.data(withJSONObject: ["conv_template": "minigpt"], options: []) + let appConfigJSON = String(data: appConfigJSONData!, encoding: .utf8) + chatModule.reload(vicunaModelLib, modelPath: vicunaModelPath, appConfigJson: appConfigJSON) + // load image model + chatModule.reloadImageModule(modelLib, modelPath: modelPath) + } else { + chatModule.reload(modelLib, modelPath: modelPath, appConfigJson: "") + } + + let modelDuration = -modelTimeStart.timeIntervalSinceNow + self.modelLoadTime = TimeRecord(start: modelTimeStart, duration: modelDuration) + + DispatchQueue.main.async { + if self.useVision { + self.updateMessage(role: .bot, message: "[System] Upload an image to chat") + self.switchToPendingImageUpload() + } else { + self.updateMessage(role: .bot, message: "[System] Ready to chat") + self.switchToReady() + } + } + } + } +} diff --git a/ios/MLCChat/States/ModelState.swift b/ios/MLCChat/States/ModelState.swift new file mode 100644 index 0000000..ed22910 --- /dev/null +++ b/ios/MLCChat/States/ModelState.swift @@ -0,0 +1,414 @@ +// +// ModelState.swift +// MLCChat +// + +import Foundation + +final class ModelState: ObservableObject, Identifiable { + enum ModelDownloadState { + case initializing + case indexing + case paused + case downloading + case pausing + case verifying + case finished + case failed + case clearing + case deleting + } + + fileprivate struct DownloadTask: Hashable { + let remoteURL: URL + let localURL: URL + } + + @Published var modelConfig: ModelConfig + @Published var modelDownloadState: ModelDownloadState = .initializing + @Published var progress: Int = 0 + @Published var total: Int = 1 + + private var modelLocalBaseURL: URL + private var startState: AppState + private var chatState: ChatState + + private let fileManager: FileManager = FileManager.default + private let decoder = JSONDecoder() + private var paramsConfig: ParamsConfig? + private var modelRemoteBaseURL: URL? + private var remainingTasks: Set = Set() + private var downloadingTasks: Set = Set() + private var maxDownloadingTasks: Int = 3 + + init(modelConfig: ModelConfig, + modelLocalBaseURL: URL, + startState: AppState, + chatState: ChatState) { + self.modelConfig = modelConfig + self.modelLocalBaseURL = modelLocalBaseURL + self.startState = startState + self.chatState = chatState + } + + func checkModelDownloadState(modelURL: URL?) { + createModelFolderIfNeeded() + + guard let modelURL else { + switchToVerifying() + return + } + + modelRemoteBaseURL = modelURL.appending(path: "resolve").appending(path: "main") + + // create local params dir + let paramsConfigURL = modelLocalBaseURL.appending(path: Constants.paramsConfigFileName) + if fileManager.fileExists(atPath: paramsConfigURL.path()) { + // ndarray-cache.json already downloaded + loadParamsConfig() + switchToIndexing() + } else { + // download ndarray-cache.json + downloadParamsConfig() + } + } + + func startChat(chatState: ChatState) { + chatState.requestReloadChat( + modelID: modelConfig.modelID!, + modelLib: modelConfig.modelLib!, + modelPath: modelLocalBaseURL.path(), + estimatedVRAMReq: modelConfig.estimatedVRAMReq!, + displayName: modelConfig.modelID!.components(separatedBy: "-")[0] + ) + } + + func handleStart() { + // start downloading + switchToDownloading() + } + + func handlePause() { + // pause downloading + switchToPausing() + } + + func handleClear() { + assert(modelDownloadState == .downloading || modelDownloadState == .paused || modelDownloadState == .finished) + switchToClearing() + } + + func handleDelete() { + assert(modelDownloadState == .downloading || modelDownloadState == .paused || modelDownloadState == .finished || modelDownloadState == .failed) + switchToDeleting() + } +} + +private extension ModelState { + func createModelFolderIfNeeded() { + if !fileManager.fileExists(atPath: modelLocalBaseURL.path()) { + do { + try fileManager.createDirectory(at: modelLocalBaseURL, withIntermediateDirectories: true) + } catch { + print(error.localizedDescription) + } + } + } + + func loadParamsConfig() { + let paramsConfigURL = modelLocalBaseURL.appending(path: Constants.paramsConfigFileName) + assert(fileManager.fileExists(atPath: paramsConfigURL.path())) + do { + let fileHandle = try FileHandle(forReadingFrom: paramsConfigURL) + let data = fileHandle.readDataToEndOfFile() + paramsConfig = try self.decoder.decode(ParamsConfig.self, from: data) + } catch { + print(error.localizedDescription) + } + } + + func downloadParamsConfig() { + guard let modelRemoteBaseURL else { + return + } + + let paramsConfigURL = modelLocalBaseURL.appending(path: Constants.paramsConfigFileName) + let downloadTask = URLSession.shared.downloadTask(with: modelRemoteBaseURL.appending(path: Constants.paramsConfigFileName)) { + [weak self] urlOrNil, responseOrNil, errorOrNil in + guard let self else { return } + guard let fileURL = urlOrNil else { return } + do { + try? self.fileManager.removeItem(at: paramsConfigURL) + try self.fileManager.moveItem(at: fileURL, to: paramsConfigURL) + DispatchQueue.main.async { + self.loadParamsConfig() + self.switchToIndexing() + } + } catch { + print(error.localizedDescription) + } + } + downloadTask.resume() + } + + func switchToIndexing() { + guard let paramsConfig, let modelRemoteBaseURL else { + return + } + + modelDownloadState = .indexing + progress = 0 + total = modelConfig.tokenizerFiles.count + paramsConfig.records.count + + // collect tokenizer download tasks + for tokenizerFile in modelConfig.tokenizerFiles { + let remoteURL = modelRemoteBaseURL.appending(path: tokenizerFile) + let localURL = modelLocalBaseURL.appending(path: tokenizerFile) + + if fileManager.fileExists(atPath: localURL.path()) { + progress += 1 + } else { + remainingTasks.insert(DownloadTask(remoteURL: remoteURL, localURL: localURL)) + } + } + + // collect params download tasks + for paramsRecord in paramsConfig.records { + let remoteURL = modelRemoteBaseURL.appending(path: paramsRecord.dataPath) + let localURL = modelLocalBaseURL.appending(path: paramsRecord.dataPath) + + if fileManager.fileExists(atPath: localURL.path()) { + progress += 1 + } else { + remainingTasks.insert(DownloadTask(remoteURL: remoteURL, localURL: localURL)) + } + } + + if progress < total { + switchToPaused() + } else { + switchToFinished() + } + } + + func handleNewDownload(downloadTask: DownloadTask) { + // start one download task + assert(downloadingTasks.count < maxDownloadingTasks) + let task = URLSession.shared.downloadTask(with: downloadTask.remoteURL) { + [weak self] urlOrNil, responseOrNil, errorOrNil in + guard let self else { return } + guard let fileUrl = urlOrNil else { + DispatchQueue.main.async { + self.handleCancelDownload(downloadTask: downloadTask) + } + return + } + + do { + try self.fileManager.createDirectory(at: downloadTask.localURL.deletingLastPathComponent(), withIntermediateDirectories: true) + try? self.fileManager.removeItem(at: downloadTask.localURL) + try self.fileManager.moveItem(at: fileUrl, to: downloadTask.localURL) + } catch { + print(error.localizedDescription) + } + DispatchQueue.main.async { + self.handleFinishDownload(downloadTask: downloadTask) + } + } + downloadingTasks.insert(downloadTask) + task.resume() + } + + func handleFinishDownload(downloadTask: DownloadTask) { + // update the finished download task + remainingTasks.remove(downloadTask) + downloadingTasks.remove(downloadTask) + progress += 1 + assert(modelDownloadState == .downloading || + modelDownloadState == .pausing || + modelDownloadState == .clearing || + modelDownloadState == .deleting + ) + if modelDownloadState == .downloading { + if remainingTasks.isEmpty && downloadingTasks.isEmpty { + switchToFinished() + } else { + handleNextDownload() + } + } else if modelDownloadState == .pausing && downloadingTasks.isEmpty { + switchToPaused() + } else if modelDownloadState == .clearing && downloadingTasks.isEmpty { + clear() + } else if modelDownloadState == .deleting && downloadingTasks.isEmpty { + delete() + } + } + + func handleCancelDownload(downloadTask: DownloadTask) { + // withdraw the failed download task + assert(modelDownloadState == .downloading || modelDownloadState == .pausing) + downloadingTasks.remove(downloadTask) + if modelDownloadState == .downloading { + handleNextDownload() + } else if modelDownloadState == .pausing && downloadingTasks.count == 0 { + switchToPaused() + } + } + + func handleNextDownload() { + // start next download task + assert(modelDownloadState == .downloading) + for downloadTask in remainingTasks { + if !downloadingTasks.contains(downloadTask) { + handleNewDownload(downloadTask: downloadTask) + break + } + } + } + + func switchToPaused() { + modelDownloadState = .paused + } + + func switchToPausing() { + modelDownloadState = .pausing + } + + func switchToVerifying() { + modelDownloadState = .verifying + + let paramsConfigURL = modelLocalBaseURL.appending(path: Constants.paramsConfigFileName) + guard fileManager.fileExists(atPath: paramsConfigURL.path()) else { + switchToFailed() + return + } + + loadParamsConfig() + guard let paramsConfig else { + switchToFailed() + return + } + progress = 0 + total = modelConfig.tokenizerFiles.count + paramsConfig.records.count + + if !verifyTokenizers() { + switchToFailed() + return + } + + if !verifyParams() { + switchToFailed() + return + } + + switchToFinished() + } + + func verifyTokenizers() -> Bool { + for tokenizerFile in modelConfig.tokenizerFiles { + let localURL = modelLocalBaseURL.appending(path: tokenizerFile) + + if !fileManager.fileExists(atPath: localURL.path()) { + switchToFailed() + return false + } + progress += 1 + } + return true + } + + func verifyParams() -> Bool { + guard let paramsConfig else { + return false + } + + for paramsRecord in paramsConfig.records { + let localUrl = modelLocalBaseURL.appending(path: paramsRecord.dataPath) + + if !fileManager.fileExists(atPath: localUrl.path()) { + switchToFailed() + return false + } + + progress += 1 + } + return true + } + + func switchToClearing() { + if modelDownloadState == .paused { + modelDownloadState = .clearing + clear() + } else if modelDownloadState == .finished { + if chatState.modelID == modelConfig.modelID { + chatState.requestTerminateChat { [weak self] in + self?.clear() + } + } else { + clear() + } + } else { + modelDownloadState = .clearing + } + } + + func switchToDeleting() { + if modelDownloadState == .paused || modelDownloadState == .failed { + modelDownloadState = .deleting + delete() + } else if modelDownloadState == .finished { + if chatState.modelID == modelConfig.modelID { + chatState.requestTerminateChat { [weak self] in + self?.delete() + } + } else { + delete() + } + } else { + modelDownloadState = .deleting + } + } + + func switchToFinished() { + modelDownloadState = .finished + } + + func switchToFailed() { + modelDownloadState = .failed + } + + func switchToDownloading() { + modelDownloadState = .downloading + for downloadTask in remainingTasks { + if downloadingTasks.count < maxDownloadingTasks { + handleNewDownload(downloadTask: downloadTask) + } else { + return + } + } + } + + func clear() { + do { + let fileURLs = try fileManager.contentsOfDirectory(at: modelLocalBaseURL, includingPropertiesForKeys: nil) + for fileURL in fileURLs where fileURL.lastPathComponent != Constants.modelConfigFileName { + try fileManager.removeItem(at: fileURL) + assert(!fileManager.fileExists(atPath: fileURL.path())) + } + assert(fileManager.fileExists(atPath: modelLocalBaseURL.appending(path: Constants.modelConfigFileName).path())) + switchToIndexing() + } catch { + print(error.localizedDescription) + } + } + + func delete() { + do { + try fileManager.removeItem(at: modelLocalBaseURL) + assert(!fileManager.fileExists(atPath: modelLocalBaseURL.path())) + startState.requestDeleteModel(modelID: modelConfig.modelID!) // TODO: can it decouple? + } catch { + print(error.localizedDescription) + } + } +} diff --git a/ios/MLCChat/Views/ChatView.swift b/ios/MLCChat/Views/ChatView.swift new file mode 100644 index 0000000..0ec5358 --- /dev/null +++ b/ios/MLCChat/Views/ChatView.swift @@ -0,0 +1,193 @@ +// +// ChatView.swift +// MLCChat +// + +import SwiftUI +import GameController + +struct ChatView: View { + @EnvironmentObject private var chatState: ChatState + + @State private var inputMessage: String = "" + @FocusState private var inputIsFocused: Bool + @Environment(\.dismiss) private var dismiss + @Namespace private var messagesBottomID + + // vision-related properties + @State private var showActionSheet: Bool = false + @State private var showImagePicker: Bool = false + @State private var imageConfirmed: Bool = false + @State private var imageSourceType: UIImagePickerController.SourceType = .photoLibrary + @State private var image: UIImage? + + @State private var showingAlert = false + @State private var filenamePrefix = "" + + var body: some View { + VStack { + modelInfoView + messagesView + uploadImageView + messageInputView + } + .navigationBarTitle("MLC Chat: \(chatState.displayName)", displayMode: .inline) + .navigationBarBackButtonHidden() + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + dismiss() + } label: { + Image(systemName: "chevron.backward") + } + .buttonStyle(.borderless) + .disabled(!chatState.isInterruptible) + } + ToolbarItem(placement: .navigationBarTrailing) { + Button("Automate") { // Brave + automate() + } + .padding() + .disabled(!chatState.isResettable) + } + } + .alert("Run Automation", isPresented: $showingAlert) { + TextField("Filename prefix", text: $filenamePrefix) + Button("Run", action: runAutomation) + Button("Cancel", role: .cancel) { } + } + } +} + +private extension ChatView { + + var modelInfoView: some View { + Text(chatState.infoText) + .multilineTextAlignment(.center) + .opacity(0.5) + .listRowSeparator(.hidden) + } + + var messagesView: some View { + ScrollViewReader { scrollViewProxy in + ScrollView { + VStack { + let messageCount = chatState.messages.count + let hasSystemMessage = messageCount > 0 && chatState.messages[0].role == MessageRole.bot + let startIndex = hasSystemMessage ? 1 : 0 + + // display the system message + if hasSystemMessage { + MessageView(role: chatState.messages[0].role, message: chatState.messages[0].message) + } + + // display image + if let image, imageConfirmed { + ImageView(image: image) + } + + // display conversations + ForEach(chatState.messages[startIndex...], id: \.id) { message in + MessageView(role: message.role, message: message.message) + } + HStack { EmptyView() } + .id(messagesBottomID) + } + } + .onChange(of: chatState.messages) { _ in + withAnimation { + scrollViewProxy.scrollTo(messagesBottomID, anchor: .bottom) + } + } + } + } + + @ViewBuilder + var uploadImageView: some View { + if chatState.useVision && !imageConfirmed { + if image == nil { + Button("Upload picture to chat") { + showActionSheet = true + } + .actionSheet(isPresented: $showActionSheet) { + ActionSheet(title: Text("Choose from"), buttons: [ + .default(Text("Photo Library")) { + showImagePicker = true + imageSourceType = .photoLibrary + }, + .default(Text("Camera")) { + showImagePicker = true + imageSourceType = .camera + }, + .cancel() + ]) + } + .sheet(isPresented: $showImagePicker) { + ImagePicker(image: $image, + showImagePicker: $showImagePicker, + imageSourceType: imageSourceType) + } + .disabled(!chatState.isUploadable) + } else { + VStack { + if let image { + Image(uiImage: image) + .resizable() + .frame(width: 300, height: 300) + + HStack { + Button("Undo") { + self.image = nil + } + .padding() + + Button("Submit") { + imageConfirmed = true + chatState.requestProcessImage(image: image) + } + .padding() + } + } + } + } + } + } + + var messageInputView: some View { + HStack { + TextField("Inputs...", text: $inputMessage, axis: .vertical) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .frame(minHeight: CGFloat(30)) + .focused($inputIsFocused) + .onSubmit { + let isKeyboardConnected = GCKeyboard.coalesced != nil + if isKeyboardConnected { + send() + } + } + Button("Send") { + send() + } + .bold() + .disabled(!(chatState.isChattable && inputMessage != "")) + } + .frame(minHeight: CGFloat(70)) + .padding() + } + + func send() { + inputIsFocused = false + chatState.requestGenerate(prompt: inputMessage) + inputMessage = "" + } + + func automate() { + showingAlert.toggle() + } + + func runAutomation() { + inputIsFocused = false + chatState.requestAutomation(measurementFilename: filenamePrefix) + inputMessage = "" + } +} diff --git a/ios/MLCChat/Views/ImageProcessing.swift b/ios/MLCChat/Views/ImageProcessing.swift new file mode 100644 index 0000000..3d7260e --- /dev/null +++ b/ios/MLCChat/Views/ImageProcessing.swift @@ -0,0 +1,66 @@ +// +// ImageProcessing.swift +// MLCChat +// +// Created by Kathryn Chen on 7/8/23. +// + +import Foundation +import SwiftUI +import UIKit + +// adapted from Mohammad Azam: https://github.com/azamsharp/SwiftUICamera +// delegate task to the coordinator to produce the image +struct ImagePicker : UIViewControllerRepresentable { + typealias UIViewControllerType = UIImagePickerController + typealias Coordinator = ImagePickerCoordinator + + @Binding var image: UIImage? + @Binding var showImagePicker: Bool + var imageSourceType: UIImagePickerController.SourceType = .photoLibrary + + func makeCoordinator() -> ImagePicker.Coordinator { + return ImagePickerCoordinator(image: $image, showImagePicker: $showImagePicker) + } + + func makeUIViewController(context: UIViewControllerRepresentableContext) -> UIImagePickerController { + let picker = UIImagePickerController() + picker.sourceType = imageSourceType + picker.delegate = context.coordinator + return picker + } + + func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext) {} +} + +// image picker coordinator handling selecting from library or taking a photo +class ImagePickerCoordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate { + @Binding var image: UIImage? + @Binding var showImagePicker: Bool + + init(image: Binding, showImagePicker: Binding) { + _image = image + _showImagePicker = showImagePicker + } + + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { + if let optionalImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage { + image = optionalImage + showImagePicker = false + } + } + + func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { + showImagePicker = false + } +} + +// resize the input image to given width and height +func resizeImage(image: UIImage, width: Int, height: Int) -> UIImage { + let shape = CGSize(width: width, height: height) + UIGraphicsBeginImageContextWithOptions(shape, true, 0.0) + image.draw(in: CGRect(x: 0, y: 0, width: width, height: height)) + let resizedImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return resizedImage ?? image +} diff --git a/ios/MLCChat/Views/MessageView.swift b/ios/MLCChat/Views/MessageView.swift new file mode 100644 index 0000000..4553f6b --- /dev/null +++ b/ios/MLCChat/Views/MessageView.swift @@ -0,0 +1,66 @@ +// +// MessageView.swift +// MLCChat +// + +import SwiftUI + +struct MessageView: View { + let role: MessageRole; + let message: String + + var body: some View { + let textColor = role.isUser ? Color.white : Color(UIColor.label) + let background = role.isUser ? Color.blue : Color(UIColor.secondarySystemBackground) + + HStack { + if role.isUser { + Spacer() + } + Text(message) + .padding(10) + .foregroundColor(textColor) + .background(background) + .cornerRadius(10) + .textSelection(.enabled) + if !role.isUser { + Spacer() + } + } + .padding() + .listRowSeparator(.hidden) + } +} + +struct ImageView: View { + let image: UIImage + + var body: some View { + let background = Color.blue + HStack { + Spacer() + Image(uiImage: image) + .resizable() + .frame(width: 150, height: 150) + .padding(15) + .background(background) + .cornerRadius(20) + } + .padding() + .listRowSeparator(.hidden) + } +} + +struct MessageView_Previews: PreviewProvider { + static var previews: some View { + NavigationView { + VStack (spacing: 0){ + ScrollView { + MessageView(role: MessageRole.user, message: "Message 1") + MessageView(role: MessageRole.bot, message: "Message 2") + MessageView(role: MessageRole.user, message: "Message 3") + } + } + } + } +} diff --git a/ios/MLCChat/Views/ModelView.swift b/ios/MLCChat/Views/ModelView.swift new file mode 100644 index 0000000..4676fb2 --- /dev/null +++ b/ios/MLCChat/Views/ModelView.swift @@ -0,0 +1,97 @@ +// +// ModelView.swift +// MLCChat +// +// Created by Yaxing Cai on 5/14/23. +// + +import SwiftUI + +struct ModelView: View { + @EnvironmentObject private var modelState: ModelState + @EnvironmentObject private var chatState: ChatState + @Binding var isRemoving: Bool + + @State private var isShowingDeletionConfirmation: Bool = false + + var body: some View { + VStack(alignment: .leading) { + if (modelState.modelDownloadState == .finished) { + NavigationLink(destination: + ChatView() + .environmentObject(chatState) + .onAppear { + modelState.startChat(chatState: chatState) + } + ) { + HStack { + Text(modelState.modelConfig.modelID!) + Spacer() + if chatState.isCurrentModel(modelID: modelState.modelConfig.modelID!) { + Image(systemName: "checkmark").foregroundColor(.blue) + } + } + } + .buttonStyle(.borderless) + } else { + Text(modelState.modelConfig.modelID!).opacity(0.5) + } + HStack{ + if modelState.modelDownloadState != .finished || isRemoving { + ProgressView(value: Double(modelState.progress) / Double(modelState.total)) + .progressViewStyle(.linear) + } + + if (modelState.modelDownloadState == .paused) { + Button { + modelState.handleStart() + } label: { + Image(systemName: "icloud.and.arrow.down") + } + .buttonStyle(.borderless) + } else if (modelState.modelDownloadState == .downloading) { + Button { + modelState.handlePause() + } label: { + Image(systemName: "stop.circle") + } + .buttonStyle(.borderless) + } else if (modelState.modelDownloadState == .failed) { + Image(systemName: "exclamationmark.triangle") + .foregroundColor(.red) + } + + if isRemoving { + Button(role: .destructive) { + isShowingDeletionConfirmation = true + } label: { + Image(systemName: "trash") + } + .confirmationDialog("Delete Model", isPresented: $isShowingDeletionConfirmation) { + Button("Delete Model", role: .destructive) { + modelState.handleDelete() + } + .disabled( + modelState.modelDownloadState != .downloading && + modelState.modelDownloadState != .paused && + modelState.modelDownloadState != .finished && + modelState.modelDownloadState != .failed) + Button("Clear Data") { + modelState.handleClear() + } + .disabled( + modelState.modelDownloadState != .downloading && + modelState.modelDownloadState != .paused && + modelState.modelDownloadState != .finished) + Button("Cancel", role: .cancel) { + isShowingDeletionConfirmation = false + } + } message: { + Text("Delete model will delete the all files with model config, and delete the entry in list. \n Clear model will keep the model config only, and keep the entry in list for future re-downloading.") + } + .buttonStyle(.borderless) + } + } + } + } +} diff --git a/ios/MLCChat/Views/StartView.swift b/ios/MLCChat/Views/StartView.swift new file mode 100644 index 0000000..0baa404 --- /dev/null +++ b/ios/MLCChat/Views/StartView.swift @@ -0,0 +1,47 @@ +// +// DownloadView.swift +// MLCChat +// +// Created by Yaxing Cai on 5/11/23. +// + +import SwiftUI + +struct StartView: View { + @EnvironmentObject private var appState: AppState + @State private var isAdding: Bool = false + @State private var isRemoving: Bool = false + @State private var inputModelUrl: String = "" + + var body: some View { + NavigationStack { + List{ + Section(header: Text("Models")) { + ForEach(appState.models) { modelState in + ModelView(isRemoving: $isRemoving) + .environmentObject(modelState) + .environmentObject(appState.chatState) + .environmentObject(appState.conversationsRecordManager) + } + if !isRemoving { + Button("Edit model") { + isRemoving = true + } + .buttonStyle(.borderless) + } else { + Button("Cancel edit model") { + isRemoving = false + } + .buttonStyle(.borderless) + } + } + } + .navigationTitle("MLC Chat") + .alert("Error", isPresented: $appState.alertDisplayed) { + Button("OK") { } + } message: { + Text(appState.alertMessage) + } + } + } +} diff --git a/ios/MLCChat/app-config.json b/ios/MLCChat/app-config.json new file mode 100644 index 0000000..4315ea6 --- /dev/null +++ b/ios/MLCChat/app-config.json @@ -0,0 +1,27 @@ +{ + "model_lib_path_for_prepare_libs": { + "meta-llama_Llama-2-7b-chat-hf-q3f16_1": "meta-llama_Llama-2-7b-chat-hf-q3f16_1/meta-llama_Llama-2-7b-chat-hf-q3f16_1-iphone.tar", + "google_gemma-2b-it-q3f16_1": "google_gemma-2b-it-q3f16_1/google_gemma-2b-it-q3f16_1-iphone.tar", + "google_gemma-2b-it-q4f16_1": "google_gemma-2b-it-q4f16_1/google_gemma-2b-it-q4f16_1-iphone.tar" + }, + "model_list": [ + { + "model_path": "", + "model_id": "meta-llama_Llama-2-7b-chat-hf-q3f16_1", + "model_lib": "llama_q3f16_1", + "estimated_vram_bytes": 0 + }, + { + "model_path": "", + "model_id": "google_gemma-2b-it-q3f16_1", + "model_lib": "gemma_q3f16_1", + "estimated_vram_bytes": 0 + }, + { + "model_path": "", + "model_id": "google_gemma-2b-it-q4f16_1", + "model_lib": "gemma_q4f16_1", + "estimated_vram_bytes": 0 + } + ] +} diff --git a/ios/MLCSwift/Package.swift b/ios/MLCSwift/Package.swift new file mode 100644 index 0000000..eac88db --- /dev/null +++ b/ios/MLCSwift/Package.swift @@ -0,0 +1,32 @@ +// swift-tools-version:5.5 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "MLCSwift", + products: [ + .library( + name: "MLCSwift", + targets: ["LLMChatObjC", "MLCSwift"] + ) + ], + dependencies: [], + targets: [ + .target( + name: "LLMChatObjC", + path: "Sources/ObjC", + cxxSettings: [ + .headerSearchPath("../../tvm_home/include"), + .headerSearchPath("../../tvm_home/3rdparty/dmlc-core/include"), + .headerSearchPath("../../tvm_home/3rdparty/dlpack/include") + ] + ), + .target( + name: "MLCSwift", + dependencies: ["LLMChatObjC"], + path: "Sources/Swift" + ) + ], + cxxLanguageStandard: .cxx17 +) diff --git a/ios/MLCSwift/README.md b/ios/MLCSwift/README.md new file mode 100644 index 0000000..3a7c2b5 --- /dev/null +++ b/ios/MLCSwift/README.md @@ -0,0 +1,4 @@ +# MLCSwift + +This is a simple swift package that exposes the chat module to swift. +Checkout our [documentation](https://llm.mlc.ai/docs/) for more examples. diff --git a/ios/MLCSwift/Sources/ObjC/LLMChat.mm b/ios/MLCSwift/Sources/ObjC/LLMChat.mm new file mode 100644 index 0000000..43d1a61 --- /dev/null +++ b/ios/MLCSwift/Sources/ObjC/LLMChat.mm @@ -0,0 +1,333 @@ +// +// LLMChat.mm +// LLMChat +// +#import +#import +#include + +#include "LLMChat.h" + +#define TVM_USE_LIBBACKTRACE 0 +#define DMLC_USE_LOGGING_LIBRARY + +#include +#include + +using namespace tvm::runtime; + +enum PlaceInPrompt : int { + // The input message should have role names and corresponding seperators appended both + // prior to it and after it, making it a complete prompt. + kAll, + // The input message is only the beginning part of a prompt, no role name and separator should be + // appended after the message since there will be future messages appended after the message. + kBegin, + // The input message is in the middle of a prompt, nothing should be appended before or after the + // message. + kMiddle, + // The input message is the ending part of a prompt, no role name and separator should be appended + // prior to it since the message is concatenated to some prior messages. + kEnd, +}; + +@implementation ChatModule { + // Internal c++ classes + // chat-related module and functions + Module llm_chat_; + PackedFunc unload_func_; + PackedFunc reload_func_; + PackedFunc prefill_func_; + PackedFunc embed_func_; + PackedFunc prefill_with_embed_func_; + PackedFunc decode_func_; + PackedFunc get_message_; + PackedFunc stopped_func_; + PackedFunc reset_chat_func_; + PackedFunc runtime_stats_text_func_; + PackedFunc verbose_runtime_stats_text_func_; + PackedFunc process_system_prompts_func_; + // image-related module and functions + Module llm_image_mod_; + PackedFunc image_mod_unload_func_; + PackedFunc image_mod_reload_func_; + PackedFunc image_mod_embed_func_; + PackedFunc image_mod_reset_func_; + PackedFunc image_mod_runtime_stats_text_func_; + // helper variables + bool first_input_after_image; + std::vector image_data; + NSUInteger image_width; + NSUInteger image_height; + + std::unordered_map energy_events; + int unload_counter; + int reload_counter; + int reset_chat_counter; + int decode_counter; + int prefill_counter; + int get_message_counter; + int stopped_counter; +} + +- (instancetype)init { + if (self = [super init]) { + energy_events["init.start"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + // load chat module + const PackedFunc* f_chat_create = Registry::Get("mlc.llm_chat_create"); + ICHECK(f_chat_create) << "Cannot find mlc.llm_chat_create"; + llm_chat_ = (*f_chat_create)(static_cast(kDLMetal), 0); + // load image module + const PackedFunc* f_image_mod_create = Registry::Get("mlc.llm_image_module_create"); + ICHECK(f_image_mod_create) << "Cannot find mlc.llm_image_module_create"; + llm_image_mod_ = (*f_image_mod_create)(static_cast(kDLMetal), 0); + + // chat-related functions + reload_func_ = llm_chat_->GetFunction("reload"); + unload_func_ = llm_chat_->GetFunction("unload"); + prefill_func_ = llm_chat_->GetFunction("prefill"); + embed_func_ = llm_chat_->GetFunction("embed"); + prefill_with_embed_func_ = llm_chat_->GetFunction("prefill_with_embed"); + decode_func_ = llm_chat_->GetFunction("decode"); + get_message_ = llm_chat_->GetFunction("get_message"); + stopped_func_ = llm_chat_->GetFunction("stopped"); + reset_chat_func_ = llm_chat_->GetFunction("reset_chat"); + runtime_stats_text_func_ = llm_chat_->GetFunction("runtime_stats_text"); + verbose_runtime_stats_text_func_ = llm_chat_->GetFunction("verbose_runtime_stats_text"); + process_system_prompts_func_ = llm_chat_->GetFunction("process_system_prompts"); + // image-module-related functions + image_mod_reload_func_ = llm_image_mod_->GetFunction("reload"); + image_mod_unload_func_ = llm_image_mod_->GetFunction("unload"); + image_mod_embed_func_ = llm_image_mod_->GetFunction("embed"); + image_mod_reset_func_ = llm_image_mod_->GetFunction("reset"); + image_mod_runtime_stats_text_func_ = llm_image_mod_->GetFunction("runtime_stats_text"); + // helper variables + first_input_after_image = false; + image_height = 224; + image_width = 224; + image_data.reserve(image_height * image_width * 4); + + ICHECK(reload_func_ != nullptr); + ICHECK(unload_func_ != nullptr); + ICHECK(prefill_func_ != nullptr); + ICHECK(embed_func_ != nullptr); + ICHECK(prefill_with_embed_func_ != nullptr); + ICHECK(decode_func_ != nullptr); + ICHECK(get_message_ != nullptr); + ICHECK(stopped_func_ != nullptr); + ICHECK(reset_chat_func_ != nullptr); + ICHECK(runtime_stats_text_func_ != nullptr); + ICHECK(verbose_runtime_stats_text_func_ != nullptr); + ICHECK(process_system_prompts_func_ != nullptr); + ICHECK(image_mod_unload_func_ != nullptr); + ICHECK(image_mod_reload_func_ != nullptr); + ICHECK(image_mod_embed_func_ != nullptr); + ICHECK(image_mod_reset_func_ != nullptr); + ICHECK(image_mod_runtime_stats_text_func_ != nullptr); + + energy_events["init.end"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + + unload_counter = 0; + reload_counter = 0; + reset_chat_counter = 0; + decode_counter = 0; + prefill_counter = 0; + get_message_counter = 0; + stopped_counter = 0; + } + return self; +} + +- (void)unload { + energy_events["unload." + std::to_string(unload_counter) + ".start"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + unload_func_(); + energy_events["unload." + std::to_string(unload_counter) + ".end"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + unload_counter++; +} + +- (void)reload:(NSString*)modelLib + modelPath:(NSString*)modelPath + appConfigJson:(NSString*)appConfigJson { + std::string lib_prefix = modelLib.UTF8String; + std::string model_path = modelPath.UTF8String; + std::string app_config_json = appConfigJson.UTF8String; + std::replace(lib_prefix.begin(), lib_prefix.end(), '-', '_'); + lib_prefix += '_'; + Module lib = (*Registry::Get("runtime.SystemLib"))(lib_prefix); + + energy_events["reload." + std::to_string(reload_counter) + ".start"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + reload_func_(lib, model_path, app_config_json); + energy_events["reload." + std::to_string(reload_counter) + ".end"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + reload_counter++; +} + +- (void)resetChat { + energy_events["reset_chat." + std::to_string(reset_chat_counter) + ".start"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + reset_chat_func_(); + energy_events["reset_chat." + std::to_string(reset_chat_counter) + ".end"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + reset_chat_counter++; +} + +- (void)prefill:(NSString*)input { + std::string prompt = input.UTF8String; + if (first_input_after_image) { + prefill_func_(prompt, true, (int)PlaceInPrompt::kEnd); + first_input_after_image = false; + } else { + energy_events["prefill." + std::to_string(prefill_counter) + ".start"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + prefill_func_(prompt); + energy_events["prefill." + std::to_string(prefill_counter) + ".end"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + prefill_counter++; + } +} + +- (void)decode { + energy_events["generate.decode." + std::to_string(decode_counter) + ".start"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + + decode_func_(); + + energy_events["generate.decode." + std::to_string(decode_counter) + ".end"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + decode_counter++; +} + +- (NSString*)getMessage { + energy_events["get_message." + std::to_string(get_message_counter) + ".start"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + std::string ret = get_message_(); + energy_events["get_message." + std::to_string(get_message_counter) + ".end"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + get_message_counter++; + return [NSString stringWithUTF8String:ret.c_str()]; +} + +- (bool)stopped { + energy_events["stopped." + std::to_string(stopped_counter) + ".start"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + bool stopped = stopped_func_().operator bool(); + energy_events["stopped." + std::to_string(stopped_counter) + ".end"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + stopped_counter++; + + return stopped; +} + +- (NSString*)runtimeStatsText:(bool)useVision { + + energy_events["verbose_runtime_stats_text.start"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + std::string chat_mod_stats = verbose_runtime_stats_text_func_(); + energy_events["verbose_runtime_stats_text.end"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + + if (useVision) { + std::string image_mod_stats = image_mod_runtime_stats_text_func_(); + chat_mod_stats += ", " + image_mod_stats; + } + return [NSString stringWithUTF8String:chat_mod_stats.c_str()]; +} + +- (void)processSystemPrompts { + energy_events["process_system_prompts.start"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + process_system_prompts_func_(); + energy_events["process_system_prompts.end"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); +} + +- (void)evaluate { + LOG(INFO) << "Total-mem-budget=" << os_proc_available_memory() / (1 << 20) << "MB"; + energy_events["evaluate.start"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + llm_chat_->GetFunction("evaluate")(); + energy_events["evaluate.end"] = std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + LOG(INFO) << "Left-mem-budget=" << os_proc_available_memory() / (1 << 20) << "MB"; +} + +- (void)unloadImageModule { + image_mod_unload_func_(); + first_input_after_image = false; +} + +- (void)reloadImageModule:(NSString*)modelLib modelPath:(NSString*)modelPath { + first_input_after_image = false; + std::string lib_prefix = modelLib.UTF8String; + std::string model_path = modelPath.UTF8String; + std::replace(lib_prefix.begin(), lib_prefix.end(), '-', '_'); + lib_prefix += '_'; + Module lib = (*Registry::Get("runtime.SystemLib"))(lib_prefix); + image_mod_reload_func_(lib, model_path); +} + +- (void)resetImageModule { + image_mod_reset_func_(); + first_input_after_image = false; +} + +- (void)prefillImage:(UIImage*)image + prevPlaceholder:(NSString*)prevPlaceholder + postPlaceholder:(NSString*)postPlaceholder { + // prefill the previous placeholder string + std::string prev_placeholder = prevPlaceholder.UTF8String; + prefill_func_(prev_placeholder, false, (int)PlaceInPrompt::kBegin); + + // prefill with image embedding + // step 1. get image rawdata: credit from https://stackoverflow.com/a/1262893 + CGImageRef imageRef = [image CGImage]; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + NSUInteger bytesPerPixel = 4; + NSUInteger bytesPerRow = bytesPerPixel * image_width; + NSUInteger bitsPerComponent = 8; + CGContextRef context = CGBitmapContextCreate( + image_data.data(), image_width, image_height, bitsPerComponent, bytesPerRow, colorSpace, + kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); + CGColorSpaceRelease(colorSpace); + CGContextDrawImage(context, CGRectMake(0, 0, image_width, image_height), imageRef); + CGContextRelease(context); + // step 2. create tvm NDArray + ShapeTuple shape = {1, int(image_height), int(image_width), 4}; + DLDataType dtype = DataType::UInt(8); + DLDevice device = DLDevice{kDLMetal, 0}; + size_t nbytes = size_t(dtype.bits / 8); + for (auto s : shape) { + nbytes *= (size_t)s; + } + NDArray input_image = NDArray::Empty(shape, dtype, device); + input_image.CopyFromBytes(image_data.data(), nbytes); + // step 3. prefill with image embedding + NDArray embedding = image_mod_embed_func_(input_image); + prefill_with_embed_func_(embedding, false); + + // prefill the post placeholder string + std::string post_placeholder = postPlaceholder.UTF8String; + prefill_func_(post_placeholder, false, (int)PlaceInPrompt::kMiddle); + + // update the flag + first_input_after_image = true; +} + +- (void)resetEnergyEvents { + energy_events.clear(); + unload_counter = 0; + reload_counter = 0; + reset_chat_counter = 0; + decode_counter = 0; + prefill_counter = 0; + get_message_counter = 0; + stopped_counter = 0; + +} + +- (void)saveEnergyEventsToCSVWithFilename:(NSString *)fileName { + + // path to documents + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + NSString *filePath = [documentsDirectory stringByAppendingPathComponent:fileName]; + + // Create the file + NSFileManager *fileManager = [NSFileManager defaultManager]; + [fileManager createFileAtPath:filePath contents:nil attributes:nil]; + NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath]; + + // Iterate through the unordered_map and write to the file + for (const auto &pair : energy_events) { + NSString *line = [NSString stringWithFormat:@"%s,%s\n", pair.first.c_str(), pair.second.c_str()]; + [fileHandle writeData:[line dataUsingEncoding:NSUTF8StringEncoding]]; + } + + // Close the file + [fileHandle closeFile]; +} + +@end diff --git a/ios/MLCSwift/Sources/ObjC/include/LLMChat.h b/ios/MLCSwift/Sources/ObjC/include/LLMChat.h new file mode 100644 index 0000000..9521175 --- /dev/null +++ b/ios/MLCSwift/Sources/ObjC/include/LLMChat.h @@ -0,0 +1,132 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// LLM Chat Module +// +// Exposed interface of Object-C, enables swift binding. +#import +#import +#include + +/** + * The chat module that can be used by the swift app. + * It is a centralized interface that also provides multimodal support, i.e. vision modules. + * + * A chat flow can be implemented as follows, for each round of conversation + * + * @code + * + * chat.prefill(input); + * while(!chat.stopped()) { + * displayReply(chat.getMessage()); + * chat.decode(); + * } + * + * @endcode + * + * The execution logic of this module should be placed on a dedicated thread. + * + * @seealso ThreadWorker + */ +@interface ChatModule : NSObject + +/** + * Unload the current model and free all memory. + * @note This function is useful to get memory estimation before launch next model. + */ +- (void)unload; + +/** + * Reload the chat module to a new model. + * + * @param modelLib The name of the modelLib + * @param modelPath The path to the model artifacts. + * @param appConfigJson The partial config that is used to partially override the model + * configuration. + */ +- (void)reload:(NSString*)modelLib + modelPath:(NSString*)modelPath + appConfigJson:(NSString*)appConfigJson; + +/** + * Reset the current chat session. + */ +- (void)resetChat; + +/** + * Run prefill stage for a given input and decode the first output token. + * + *@param input The user input prompt. + */ +- (void)prefill:(NSString*)input; + +/** + *Run one decode step to decode the next token. + */ +- (void)decode; + +/** + * @returns The output message in the current round. + */ +- (NSString*)getMessage; + +/** + * @returns Whether the current round stopped + */ +- (bool)stopped; + +/** + * Get the runtime statistics for the chat module, and optionally the image module. + * + *@param useVision Whether an image module is used. + */ +- (NSString*)runtimeStatsText:(bool)useVision; + +/** + * Pre-process by prefilling the system prompts, running prior to any user input. + */ +- (void)processSystemPrompts; + +/** + * \brief Run one round of prefill and decode. + * + * This function is not supposed to be used by apps. + * and is only included here when setting up the app + * for debugging purposes. + */ +- (void)evaluate; + +/** + * Unload the current image model and free all memory. + * @note This function is useful to get memory estimation before launch next model. + */ +- (void)unloadImageModule; + +/** + * Reload the image module to a new model. + * + * @param modelLib The name of the modelLib + * @param modelPath The path to the model artifacts. + */ +- (void)reloadImageModule:(NSString*)modelLib modelPath:(NSString*)modelPath; + +/** + * Reset the current image model. + */ +- (void)resetImageModule; + +/** + * Prefill the LLM with the embedding of the input image. + * + * @param image The uploaded image. + * @param prevPlaceholder The previous placeholder in the prompt, i.e. . + * @param postPlaceholder The post placeholder in the prompt, i.e. . + */ +- (void)prefillImage:(UIImage*)image + prevPlaceholder:(NSString*)prevPlaceholder + postPlaceholder:(NSString*)postPlaceholder; + +- (void)resetEnergyEvents; + +- (void)saveEnergyEventsToCSVWithFilename:(NSString *)fileName; + +@end diff --git a/ios/MLCSwift/Sources/Swift/LLMChat.swift b/ios/MLCSwift/Sources/Swift/LLMChat.swift new file mode 100644 index 0000000..fa7d889 --- /dev/null +++ b/ios/MLCSwift/Sources/Swift/LLMChat.swift @@ -0,0 +1 @@ +@_exported import LLMChatObjC diff --git a/ios/MLCSwift/Sources/Swift/ThreadWorker.swift b/ios/MLCSwift/Sources/Swift/ThreadWorker.swift new file mode 100644 index 0000000..79f1eb2 --- /dev/null +++ b/ios/MLCSwift/Sources/Swift/ThreadWorker.swift @@ -0,0 +1,31 @@ +import Foundation + +// A simple thread worker that is backed by a single thread +// +// Instead of dispatch queue, we need a dedicated thread for metal compute +// so all thread local resources are centralized at a single thread +public class ThreadWorker : Thread { + private var cond = NSCondition(); + private var queue = Array<()->Void>(); + + public override func main() { + Thread.setThreadPriority(1) + while (true) { + self.cond.lock() + while (queue.isEmpty) { + self.cond.wait() + } + let task = self.queue.removeFirst() + self.cond.unlock() + task() + } + } + + public func push(task: @escaping ()->Void) { + self.cond.lock() + self.queue.append(task) + self.cond.signal() + self.cond.unlock() + + } +} diff --git a/ios/MLCSwift/tvm_home b/ios/MLCSwift/tvm_home new file mode 120000 index 0000000..e15bf64 --- /dev/null +++ b/ios/MLCSwift/tvm_home @@ -0,0 +1 @@ +../../3rdparty/tvm \ No newline at end of file diff --git a/ios/README.md b/ios/README.md new file mode 100644 index 0000000..de94ee7 --- /dev/null +++ b/ios/README.md @@ -0,0 +1,3 @@ +# MLC-LLM IOS + +[Documentation page](https://llm.mlc.ai/docs/deploy/ios.html) diff --git a/ios/prepare_libs.sh b/ios/prepare_libs.sh new file mode 100755 index 0000000..d874238 --- /dev/null +++ b/ios/prepare_libs.sh @@ -0,0 +1,74 @@ +function help { + echo -e "OPTION:" + echo -e " -s, --simulator Build for Simulator" + echo -e " -a, --arch x86_64 | arm64 Simulator arch " + echo -e " -h, --help Prints this help\n" +} + +is_simulator="false" +arch="arm64" + +# Args while-loop +while [ "$1" != "" ]; +do + case $1 in + -s | --simulator ) is_simulator="true" + ;; + -a | --arch ) shift + arch=$1 + ;; + -h | --help ) help + exit + ;; + *) + echo "$script: illegal option $1" + usage + exit 1 # error + ;; + esac + shift +done + +set -euxo pipefail + +sysroot="iphoneos" +type="Release" + +if [ "$is_simulator" = "true" ]; then + if [ "$arch" = "arm64" ]; then + # iOS simulator on Apple processors + rustup target add aarch64-apple-ios-sim + else + # iOS simulator on x86 processors + rustup target add x86_64-apple-ios + fi + sysroot="iphonesimulator" + type="Debug" +else + # iOS devices + rustup target add aarch64-apple-ios +fi + +mkdir -p build/ && cd build/ + +cmake ../..\ + -DCMAKE_BUILD_TYPE=$type\ + -DCMAKE_SYSTEM_NAME=iOS\ + -DCMAKE_SYSTEM_VERSION=14.0\ + -DCMAKE_OSX_SYSROOT=$sysroot\ + -DCMAKE_OSX_ARCHITECTURES=$arch\ + -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0\ + -DCMAKE_BUILD_WITH_INSTALL_NAME_DIR=ON\ + -DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=ON\ + -DCMAKE_INSTALL_PREFIX=.\ + -DCMAKE_CXX_FLAGS="-O3"\ + -DMLC_LLM_INSTALL_STATIC_LIB=ON\ + -DUSE_METAL=ON +make mlc_llm_static +cmake --build . --target install --config release -j +cd .. + +rm -rf MLCSwift/tvm_home +ln -s ../../3rdparty/tvm MLCSwift/tvm_home + +python prepare_model_lib.py diff --git a/ios/prepare_model_lib.py b/ios/prepare_model_lib.py new file mode 100644 index 0000000..55ad6f7 --- /dev/null +++ b/ios/prepare_model_lib.py @@ -0,0 +1,29 @@ +import json +import os +from tvm.contrib import cc + + +def main(): + app_config = json.load(open("MLCChat/app-config.json", "r")) + target = "iphone" + artifact_path = os.path.abspath(os.path.join("../../../../", "melt_models_converted")) + + tar_list = [] + + for model, model_lib_path in app_config["model_lib_path_for_prepare_libs"].items(): + paths = [ + os.path.join(artifact_path, model_lib_path), + ] + valid_paths = [p for p in paths if os.path.isfile(p)] + if not valid_paths: + raise RuntimeError( + f"Cannot find iOS lib for {model} from the following candidate paths: {paths}" + ) + tar_list.append(valid_paths[0]) + + cc.create_staticlib(os.path.join("build", "lib", "libmodel_iphone.a"), tar_list) + print(f"Creating lib from {tar_list}..") + + +if __name__ == "__main__": + main() diff --git a/ios/prepare_params.sh b/ios/prepare_params.sh new file mode 100755 index 0000000..3814f6f --- /dev/null +++ b/ios/prepare_params.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -euxo pipefail + +# NOTE: this is optional, prepackage weight into app +rm -rf dist +mkdir -p dist + +declare -a builtin_list=( + # "Mistral-7B-Instruct-v0.2-q3f16_1" + # "OpenHermes-2.5-Mistral-7B-q3f16_1" + # "Llama-2-7b-chat-hf-q3f16_1" + # "RedPajama-INCITE-Chat-3B-v1-q4f16_1" + # "vicuna-v1-7b-q3f16_0" + # "rwkv-raven-1b5-q8f16_0" + # "rwkv-raven-3b-q8f16_0" + # "rwkv-raven-7b-q8f16_0" +) + +for model in "${builtin_list[@]}"; do + if [ -d ../dist/$model/params ]; then + cp -r ../dist/$model/params dist/$model + elif [ -d ../dist/prebuilt/$model ]; then + cp -r ../dist/prebuilt/$model dist/$model + elif [ -d ../dist/prebuilt/mlc-chat-$model ]; then + cp -r ../dist/prebuilt/mlc-chat-$model dist/$model + elif [ -d ../dist/prebuilt/$model-MLC ]; then + cp -r ../dist/prebuilt/$model-MLC dist/$model + else + echo "Cannot find prebuilt weights for " $model + exit 1 + fi +done diff --git a/mlc_llm/__init__.py b/mlc_llm/__init__.py new file mode 100644 index 0000000..b74f007 --- /dev/null +++ b/mlc_llm/__init__.py @@ -0,0 +1,7 @@ +from . import dispatch +from . import quantization +from . import relax_model +from . import transform +from . import utils +from . import core +from .core import build_model, BuildArgs diff --git a/mlc_llm/build.py b/mlc_llm/build.py new file mode 100644 index 0000000..b7619aa --- /dev/null +++ b/mlc_llm/build.py @@ -0,0 +1,47 @@ +"""Script for building/compiling models.""" +import contextlib +import sys + +from mlc_llm import core + + +@contextlib.contextmanager +def debug_on_except(): + try: + yield + finally: + raised_exception = sys.exc_info()[1] + if not isinstance(raised_exception, Exception): + return + + import traceback + + try: + import ipdb as pdb + except ImportError: + import pdb + + traceback.print_exc() + pdb.post_mortem() + + +def main(): + """Main method for building model from command line.""" + empty_args = core.convert_build_args_to_argparser() # Create new ArgumentParser + parsed_args = empty_args.parse_args() # Parse through command line + + with contextlib.ExitStack() as stack: + # Enter an exception-catching context before post-processing + # the arguments, in case the post-processing itself raises an + # exception. + if parsed_args.pdb: + stack.enter_context(debug_on_except()) + + # Post processing of arguments + parsed_args = core._parse_args(parsed_args) # pylint: disable=protected-access + + core.build_model_from_args(parsed_args) + + +if __name__ == "__main__": + main() diff --git a/mlc_llm/core.py b/mlc_llm/core.py new file mode 100644 index 0000000..bd86e0a --- /dev/null +++ b/mlc_llm/core.py @@ -0,0 +1,1015 @@ +# pylint: disable=missing-docstring, redefined-outer-name, not-callable +import argparse +import functools +import json +import os +import pickle +from dataclasses import asdict, dataclass, field, fields +from typing import Any, Dict, Optional + +import mlc_llm +import tvm +import tvm.relax.backend.contrib.cublas as _ +from mlc_llm import utils +from mlc_llm.relax_model import ( + chatglm, + gpt_bigcode, + gpt_neox, + gptj, + llama, + llama_batched_vllm, + minigpt, + mistral, + param_manager, + rwkv, + stablelm_3b, +) +from mlc_llm.relax_model.commons import ( + create_shard_info_func, + create_shard_transformation_func, +) +from mlc_llm.relax_model.param_manager import ( + chain_parameter_transforms, + transform_params_for_each_rank, +) +from mlc_llm.transform import fuse_split_rotary_embedding, rewrite_attention +from tvm import dlight as dl +from tvm import relax +from tvm.contrib.nvcc import parse_compute_version +from tvm.relax.backend import get_patterns_with_prefix +from tvm.relax.backend.contrib.cutlass import annotate_workspace + + +@dataclass +class BuildArgs: + r"""BuildArgs is the dataclass that organizes the arguments we use in + building a model. + + To use :meth:`mlc_llm.build_model`, users pass in an instance of :class:`BuildArgs`; for + CLI entry points, an equivalent :class:`ArgumentParser` instance is generated based + on the definition of this class using :meth:`mlc_llm.convert_build_args_to_argparser`. + + Parameters + ---------- + model: str + The name of the model to build. If it is ``auto``, we will automatically + set the model name according to ``--model-path``, ``hf-path``, or the model + folders under ``--artifact-path/models``. + + hf_path: str + Hugging Face path from which to download params, tokenizer, and config. + + quantization: str + The quantization mode we use to compile. + + max_seq_len: int + The maximum allowed sequence length for the model. + + target: str + The target platform to compile the model for. + + db_path: str + Path to log database for all models. Default: ``./log_db/``. + + reuse_lib: str + Whether to reuse a previously generated lib. + + artifact_path: str + Where to store the output. + + use_cache: int + Whether to use previously pickled IRModule and skip trace. + + convert_weights_only: bool + Whether to only convert model weights and not build the model. If both + ``convert_weight_only`` and ``build_model_only`` are set, the behavior is undefined. + + build_model_only: bool + Whether to only build model and do not convert model weights. + + debug_dump: bool + Whether to dump debugging files during compilation. + + debug_load_script: bool + Whether to load the script for debugging. + + llvm_mingw: str + ``/path/to/llvm-mingw-root``, use llvm-mingw to cross compile to windows. + + system_lib: bool + A parameter to ``relax.build``. + + sep_embed: bool + Build with separated embedding layer, only applicable to LlaMa. This + feature is in testing stage, and will be formally replaced after massive + overhaul of embedding feature for all models and use cases. + + sliding_window: int + The sliding window size in sliding window attention (SWA). This optional field + overrides the `sliding_window` in config.json for those models that use SWA. + Currently only useful when compiling Mistral. + + prefill_chunk_size: int + The chunk size during prefilling. By default, the chunk size is the same as + max sequence length. Currently only useful when compiling Mistral. + + attention_sink_size: int + Number of attention sinks (https://arxiv.org/abs/2309.17453). + Only supported on mistral yet. + + cc_path: str + ``/path/to/cross_compiler_path``; currently only used for cross-compile + for nvidia/jetson device. + + use_safetensors: bool + Specifies whether to use ``.safetensors`` instead of the default ``.bin`` + when loading in model weights. + + enable_batching: bool + Build the model for batched inference. + This is a temporary flag used to control the model execution flow in single- + sequence and batching settings for now. We will eventually merge two flows + in the future and remove this flag then. + + no_cutlass_attn: bool + Disable offloading attention operations to CUTLASS. + + no_cutlass_norm: bool + Disable offloading layer and RMS norm operations to CUTLASS. + + no_cublas: bool + Disable the step that offloads matmul to cuBLAS. Without this flag, + matmul will be offloaded to cuBLAS if quantization mode is ``q0f16`` or + ``q0f32``, target is CUDA and TVM has been built with cuBLAS enabled. + + use_cuda_graph: bool + Specifies whether to enable CUDA Graph for the decoder. MLP and QKV + projection between two attention layers are put into a graph. + + num_shards: int + Number of shards to split the model into in tensor parallelism multi-gpu + inference. Only useful when ``build_model_only`` is set. + + use_flash_attn_mqa: bool + Offload multi-query attention workload to Flash Attention. + + pdb: bool + If set, drop into a pdb debugger on error. + + use_vllm_attention: bool + Use vLLM paged KV cache and attention kernel, only relevant when enable_batching=True. + """ + model: str = field( + default="auto", + metadata={ + "help": ( + 'The name of the model to build. If it is "auto", we will ' + 'automatically set the model name according to "--model-path", ' + '"hf-path" or the model folders under "--artifact-path/models"' + ) + }, + ) + hf_path: str = field( + default=None, + metadata={"help": "Hugging Face path from which to download params, tokenizer, and config"}, + ) + quantization: str = field( + default="q4f16_1", + metadata={ + "help": "The quantization mode we use to compile.", + "choices": [*utils.quantization_schemes.keys()], + }, + ) + max_seq_len: int = field( + default=-1, + metadata={"help": "The maximum allowed sequence length for the model."}, + ) + max_vocab_size: int = field( + default=40000, + metadata={"help": "The maximum allowed vocabulary size for the model."}, + ) + target: str = field( + default="auto", + metadata={"help": "The target platform to compile the model for."}, + ) + reuse_lib: str = field( + default=None, metadata={"help": "Whether to reuse a previously generated lib."} + ) + artifact_path: str = field(default="dist", metadata={"help": "Where to store the output."}) + use_cache: int = field( + default=1, + metadata={"help": "Whether to use previously pickled IRModule and skip trace."}, + ) + convert_weights_only: bool = field( + default=False, + metadata={ + "dest": "convert_weights_only", + "action": "store_true", + "help": "Whether to only convert model weights and not build the model.", + }, + ) + build_model_only: bool = field( + default=False, + metadata={ + "help": "Whether to only build model and do not convert model weights.", + "action": "store_true", + }, + ) + debug_dump: bool = field( + default=False, + metadata={ + "help": "Whether to dump debugging files during compilation.", + "action": "store_true", + }, + ) + debug_load_script: bool = field( + default=False, + metadata={ + "help": "Whether to load the script for debugging.", + "action": "store_true", + }, + ) + llvm_mingw: str = field( + default="", + metadata={"help": "/path/to/llvm-mingw-root, use llvm-mingw to cross compile to windows."}, + ) + cc_path: str = field( + default="", + metadata={ + "help": ( + "/path/to/cross_compiler_path, Currently only used for " + "cross-compile for nvidia/jetson device." + ) + }, + ) + system_lib: bool = field( + default=False, + metadata={"help": "A parameter to `relax.build`.", "action": "store_true"}, + ) + sep_embed: bool = field( + default=False, + metadata={ + "help": ( + "Build with separated embedding layer, only applicable to LlaMa. " + "This feature is in testing stage, and will be formally replaced after " + "massive overhaul of embedding feature for all models and use cases" + ), + "action": "store_true", + }, + ) + use_safetensors: bool = field( + default=False, + metadata={ + "help": ( + "Specifies whether to use ``.safetensors`` instead of the default " + "``.bin`` when loading in model weights." + ), + "action": "store_true", + }, + ) + enable_batching: bool = field( + default=False, + metadata={ + "help": ( + "Build the model for batched inference." + "This is a temporary flag used to control the model execution flow in single-" + "sequence and batching settings for now. We will eventually merge two flows" + "in the future and remove this flag then." + ), + "action": "store_true", + }, + ) + max_batch_size: int = field( + default=80, + metadata={ + "help": ( + "The maximum batch size for build. It has effect only when batching is enabled." + ), + }, + ) + no_cutlass_attn: bool = field( + default=False, + metadata={ + "help": ("Disable offloading attention operations to CUTLASS."), + "action": "store_true", + }, + ) + no_cutlass_norm: bool = field( + default=False, + metadata={ + "help": ("Disable offloading layer and RMS norm operations to CUTLASS."), + "action": "store_true", + }, + ) + no_cublas: bool = field( + default=False, + metadata={ + "help": ( + "Disable the step that offloads matmul to cuBLAS. Without this flag, " + "matmul will be offloaded to cuBLAS if quantization mode is q0f16 or q0f32, " + "target is CUDA and TVM has been built with cuBLAS enabled." + ), + "action": "store_true", + }, + ) + use_cuda_graph: bool = field( + default=False, + metadata={ + "help": ( + "Specifies whether to enable CUDA Graph for the decoder. MLP and QKV " + "projection between two attention layers are put into a graph." + ), + "action": "store_true", + }, + ) + num_shards: int = field( + default=1, + metadata={ + "help": ( + "Number of shards to split the model into in tensor parallelism multi-gpu " + "inference. Only useful when --build-model-only is set." + ), + }, + ) + use_presharded_weights: bool = field( + default=False, + metadata={ + "action": "store_true", + "help": "Produce separate weight sets for each shard.", + }, + ) + use_flash_attn_mqa: bool = field( + default=False, + metadata={ + "help": ("Offload multi-query attention workload to Flash Attention."), + "action": "store_true", + }, + ) + sliding_window: int = field( + default=-1, + metadata={ + "help": ( + "The sliding window size in sliding window attention (SWA). " + "This optional field overrides the `sliding_window` in config.json for " + "those models that use SWA. Currently only useful when compiling Mistral." + ), + }, + ) + prefill_chunk_size: int = field( + default=-1, + metadata={ + "help": ( + "The chunk size during prefilling. By default, the chunk size is " + "the same as the sliding window size or the max sequence length. " + "Currently only useful when compiling Mistral." + ), + }, + ) + attention_sink_size: int = field( + default=0, + metadata={ + "help": ( + "The number of attention sinks to keep in cache." + "Only supported on mistral yet." + ), + }, + ) + pdb: bool = field( + default=False, + metadata={ + "help": ("If set, drop into a pdb debugger on error"), + "action": "store_true", + }, + ) + use_vllm_attention: bool = field( + default=False, + metadata={ + "help": ( + "Use vLLM paged KV cache and attention kernel, only relevant when " + "enable_batching=True." + ), + "action": "store_true", + }, + ) + + @property + def convert_weight_only(self): + """A backwards-compatibility helper""" + return self.convert_weights_only + + +def convert_build_args_to_argparser() -> argparse.ArgumentParser: + """Convert from BuildArgs to an equivalent ArgumentParser.""" + args = argparse.ArgumentParser() + for field in fields(BuildArgs): + name = field.name.replace("_", "-") + field_name = f"--{name}" + # `kwargs` contains `help`, `choices`, and `action` + kwargs = field.metadata.copy() + if field.type == bool: + # boolean arguments do not need to specify `type` + args.add_argument(field_name, default=field.default, **kwargs) + else: + args.add_argument(field_name, type=field.type, default=field.default, **kwargs) + + # Most models contain more than a single parameter (citation + # needed), so "weights" should be plural. The initial use of + # "--convert-weight-only" caused enough typos that it is worth + # fixing. The old argument spelling is retained for backwards + # compatibility. + args.add_argument( + "--convert-weight-only", + default=False, + dest="convert_weights_only", + action="store_true", + help="Equivalent to --convert-weights-only, retained for backwards compatibility.", + ) + + return args + + +def _parse_args(parsed) -> argparse.Namespace: + assert parsed.max_seq_len == -1 or parsed.max_seq_len > 0 + if parsed.use_safetensors: + try: + import safetensors # pylint: disable=import-outside-toplevel, unused-import + except ImportError as error: + raise ImportError( + "`use_safetensors` option is toggled, please install safetensors package." + ) from error + + parsed.export_kwargs = {} + parsed.lib_format = "so" + parsed.system_lib_prefix = None + parsed = _setup_model_path(parsed) + + utils.parse_target(parsed) + utils.argparse_postproc_common(parsed) + + if parsed.use_vllm_attention: + assert parsed.enable_batching, "--enable_batching is required for using vLLM attention." + assert parsed.target_kind == "cuda", "vLLM attention is only supported for CUDA." + assert tvm.get_global_func( + "tvm.contrib.vllm.single_query_cached_kv_attention", True + ), "TVM needs to be built with -DUSE_VLLM=ON." + + model_name = [ + parsed.model, + parsed.quantization.name, + ] + if parsed.use_presharded_weights: + model_name.append(f"presharded-{parsed.num_shards}gpu") + + parsed.artifact_path = os.path.join(parsed.artifact_path, "-".join(model_name)) + + return parsed + + +def _setup_model_path(args: argparse.Namespace): # pylint: disable=too-many-branches + if args.hf_path: + if args.model != "auto": + assert args.model == os.path.basename(args.hf_path), ( + 'When both "--model" and "--hf-path" is specified, the ' + 'value of "--model" is required to match the basename of "--hf-path". ' + f'Got "--model {args.model}" and "--hf-path {args.hf_path}"' + ) + else: + args.model = os.path.basename(args.hf_path) + args.model_path = os.path.join(args.artifact_path, "models", args.model) + if os.path.exists(args.model_path): + print(f"Weights exist at {args.model_path}, skipping download.") + else: + os.makedirs(args.model_path, exist_ok=True) + os.system("git lfs install") + os.system(f"git clone https://huggingface.co/{args.hf_path} {args.model_path}") + print(f"Downloaded weights to {args.model_path}") + validate_config(args.model_path) + elif args.model != "auto": + if os.path.isdir(args.model): + args.model = os.path.normpath(args.model) # Remove potential trailing `/` + args.model_path = args.model + args.model = os.path.basename(args.model) + else: + args.model_path = os.path.join(args.artifact_path, "models", args.model) + validate_config(args.model_path) + else: + lookup_path = os.path.join(args.artifact_path, "models") + print(f'"--model" is set to "auto". Searching in {lookup_path} for existing models.') + for dirname in os.listdir(lookup_path): + if os.path.isdir(os.path.join(lookup_path, dirname)) and os.path.isfile( + os.path.join(lookup_path, dirname, "config.json") + ): + try: + validate_config(os.path.join(lookup_path, dirname)) + except: # pylint: disable=bare-except + pass + else: + args.model_path = os.path.join(lookup_path, dirname) + args.model = dirname + break + if args.model == "auto": + raise ValueError("Please specify either the model_path or the hf_path.") + + print(f'Using path "{args.model_path}" for model "{args.model}"') + return args + + +def validate_config(model_path: str): + if os.path.exists(os.path.join(model_path, "mlc-chat-config.json")): + raise KeyError( + f"The model located in the directory {model_path} has already been compiled " + "by MLC-LLM. There is no need to compile it again. If you wish to compile " + "a new model, please provide a directory (or hf-path) that contains the " + "pre-compiled model in raw HuggingFace format instead." + ) + if model_path.split("/")[-1].startswith("minigpt"): + # minigpt does not contain a config.json file so we skip the check + return + config_path = os.path.join(model_path, "config.json") + assert os.path.exists( + config_path + ), f"Expecting HuggingFace config, but file not found: {config_path}." + with open(config_path, encoding="utf-8") as i_f: + config = json.load(i_f) + assert ( + "model_type" in config + ), f"Invalid config format. Expecting HuggingFace config format in: {config_path}" + assert ( + config["model_type"] in utils.supported_model_types + ), f"Model type {config['model_type']} not supported." + + +def get_cuda_sm_version(): + major, minor = parse_compute_version(tvm.cuda(0).compute_version) + + if major == 8: + sm = 80 + else: + sm = 10 * major + minor + + return sm + + +def mod_transform_before_build( + mod: tvm.IRModule, + param_manager: param_manager.ParamManager, + args: argparse.Namespace, + config: Dict, +) -> tvm.IRModule: + """First-stage: Legalize ops and trace""" + if args.model.startswith("minigpt"): + model_names = ["embed"] + else: + model_names = [ + "prefill", + "decode", + ] + + if not args.use_vllm_attention: + model_names += [ + "create_kv_cache", + "softmax_with_temperature", + "get_metadata", + ] + else: + # This is equivalent to prefill but without KV cache. It is used for + # determining the number of paged cache blocks that can be allocated. + model_names.append("evaluate") + + if args.sep_embed: + model_names = ["embed", "prefill_with_embed"] + model_names[1:] + if args.enable_batching: + model_names[2] = "decode_with_embed" + if args.model.lower().startswith("rwkv-"): + model_names += ["reset_kv_cache"] + + mod = param_manager.transform_dequantize()(mod) + mod = relax.transform.BundleModelParams()(mod) + + use_ft_quant = args.quantization.name in [ + "q4f16_ft", + "q8f16_ft", + "q4f16_ft_group", + "q8f16_ft_group", + ] + mod = mlc_llm.transform.FuseDecodeTranspose(skip_gemm=not use_ft_quant)(mod) + + if ( + not args.enable_batching + and hasattr(config, "num_attention_heads") + and hasattr(config, "hidden_size") + and hasattr(config, "position_embedding_base") + and getattr(config, "dtype", "float16") == "float16" + ): + max_seq_len = None + if args.max_seq_len > 0: + max_seq_len = args.max_seq_len + elif hasattr(config, "max_sequence_length"): + max_seq_len = config.max_sequence_length + + if max_seq_len: + num_key_value_heads = config.get_num_key_value_heads() + # pylint: disable=no-value-for-parameter + mod = fuse_split_rotary_embedding( + config.num_attention_heads // args.num_shards, + num_key_value_heads // args.num_shards, + config.hidden_size // args.num_shards, + config.position_embedding_base, + )(mod) + + if args.target_kind == "cuda": + patterns = [] + + has_cutlass = tvm.get_global_func("relax.ext.cutlass", True) + + if has_cutlass and not args.no_cutlass_attn: + # pylint: disable=no-value-for-parameter + if args.use_flash_attn_mqa: + mod = rewrite_attention(use_flash_mqa=True)(mod) + mod = rewrite_attention(use_flash_mqa=False)(mod) + patterns += get_patterns_with_prefix("cutlass.attention") + + if has_cutlass and not args.no_cutlass_norm: + patterns += get_patterns_with_prefix("cutlass.layer_norm") + patterns += get_patterns_with_prefix("cutlass.rms_norm") + + if has_cutlass and use_ft_quant: + patterns += get_patterns_with_prefix("cutlass.decode_matmul") + + has_cublas = tvm.get_global_func("relax.ext.cublas", True) + + if has_cublas and args.quantization.name in ("q0f16", "q0f32") and not args.no_cublas: + patterns += get_patterns_with_prefix("cublas") + + if len(patterns) > 0: + os.makedirs("./tmp", exist_ok=True) + + sm = get_cuda_sm_version() + options = {"cutlass": {"sm": sm, "find_first_valid": False}} + + if hasattr(config, "rms_norm_eps"): + options["cutlass"]["rms_eps"] = config.rms_norm_eps + + mod = tvm.transform.Sequential( + [ + relax.transform.FuseOpsByPattern( + patterns, bind_constants=False, annotate_codegen=True + ), + annotate_workspace, + relax.transform.AllocateWorkspace(), + relax.transform.RunCodegen(options, entry_functions=model_names), + ] + )(mod) + + if args.target_kind == "android": + mod = mlc_llm.transform.FuseTranspose1Matmul()(mod) + mod = mlc_llm.transform.FuseTranspose2Matmul()(mod) + mod = mlc_llm.transform.FuseTransposeMatmul()(mod) + mod = relax.pipeline.get_pipeline()(mod) # pylint: disable=no-value-for-parameter + mod = mlc_llm.transform.FuseDecodeMatmulEwise()(mod) + mod = mlc_llm.transform.FuseDecodeTake()(mod) + mod = relax.transform.DeadCodeElimination(model_names)(mod) + mod = mlc_llm.transform.CleanUpTIRAttrs()(mod) + mod_deploy = mod + + utils.debug_dump_script(mod_deploy, "mod_deploy.py", args) + + return mod_deploy + + +def dump_mlc_chat_config( + args: argparse.Namespace, + vocab_size: int, + max_window_size: int, + temperature: float = 0.7, + repetition_penalty: float = 1.0, + top_p: float = 0.95, + mean_gen_len: int = 128, + max_gen_len: int = 512, + shift_fill_factor: float = 0.3, + rwkv_world=False, +): + args.params_path = os.path.join(args.artifact_path, "params") + config: Dict[str, Any] = {} + + if args.reuse_lib: + config["model_lib"] = f"{args.reuse_lib}" + if not args.reuse_lib.endswith(args.quantization.name): + raise RuntimeError(f"Trying to reuse lib without suffix {args.quantization.name}") + else: + config["model_lib"] = f"{args.model}-{args.quantization.name}" + + config["local_id"] = f"{args.model}-{args.quantization.name}" + config["conv_template"] = args.conv_template + config["temperature"] = temperature + config["repetition_penalty"] = repetition_penalty + config["top_p"] = top_p + config["mean_gen_len"] = mean_gen_len + config["max_gen_len"] = max_gen_len + config["num_shards"] = args.num_shards + config["use_presharded_weights"] = args.use_presharded_weights + config["shift_fill_factor"] = shift_fill_factor + if rwkv_world: + config["tokenizer_files"] = ["tokenizer_model"] + else: + config["tokenizer_files"] = utils.get_tokenizer_files(args.params_path) + config["model_category"] = args.model_category + config["model_name"] = args.model + config["vocab_size"] = vocab_size + config["prefill_chunk_size"] = args.prefill_chunk_size + if args.sliding_window != -1: + # Do not add max window size if use sliding window + config["sliding_window"] = args.sliding_window + + # only use sinks if sliding window enabled + if args.attention_sink_size > 0: + config["attention_sink_size"] = args.attention_sink_size + else: + config["max_window_size"] = max_window_size + + args.chat_config_path = os.path.join(args.params_path, "mlc-chat-config.json") + with open(args.chat_config_path, "w", encoding="utf-8") as outfile: + json.dump(config, outfile, indent=4) + print(f"Finish exporting chat config to {args.chat_config_path}") + + +def build(mod_deploy: tvm.IRModule, args: argparse.Namespace) -> None: + target_kind = args.target_kind + if args.system_lib_prefix: + mod_deploy = mod_deploy.with_attrs({"system_lib_prefix": args.system_lib_prefix}) + + utils.debug_dump_script(mod_deploy, "mod_before_build.py", args) + utils.debug_dump_benchmark_script( + mod_deploy, f"{args.model}_{args.quantization.name}".replace("-", "_"), args + ) + + if target_kind != "cpu": + dispatch_target = ( + args.target + if args.target_kind != "webgpu" + else tvm.target.Target("apple/m1-gpu-restricted") + ) + with dispatch_target: + if args.target_kind == "android": + mod_deploy = mlc_llm.dispatch.DispatchTIROperatorAdreno()( # pylint: disable=not-callable + mod_deploy + ) + mod_deploy = dl.ApplyDefaultSchedule( # pylint: disable=not-callable + dl.gpu.Matmul(), + dl.gpu.GEMV(), + dl.gpu.Reduction(), + dl.gpu.GeneralReduction(), + dl.gpu.Fallback(), + )(mod_deploy) + mod_deploy = ( + mlc_llm.transform.LiftTIRGlobalBufferAlloc()( # pylint: disable=not-callable + mod_deploy + ) + ) + if not args.enable_batching: + mod_deploy = tvm.tir.transform.ForceNarrowIndexToInt32()(mod_deploy) + + if args.debug_load_script: + mod_deploy = utils.debug_load_script("mod_build_stage_debug.py", args) + + utils.debug_dump_script(mod_deploy, "mod_build_stage.py", args) + + use_cuda_graph = args.use_cuda_graph and target_kind == "cuda" + + with tvm.transform.PassContext(config={"relax.backend.use_cuda_graph": use_cuda_graph}): + # The num_input attribute is needed to capture transformed weights passed as input + # into a cuda graph. + # NOTE: CUDA graph for batching is not enabled and is left as a TODO item. + if not args.enable_batching: + mod_deploy["decode"] = mod_deploy["decode"].with_attr({"num_input": 3}) + ex = relax.build(mod_deploy, args.target, system_lib=args.system_lib) + + output_filename = f"{args.model}-{args.quantization.name}-{target_kind}.{args.lib_format}" + + utils.debug_dump_shader(ex, f"{args.model}_{args.quantization.name}_{target_kind}", args) + args.lib_path = os.path.join(args.artifact_path, output_filename) + ex.export_library(args.lib_path, **args.export_kwargs) + print(f"Finish exporting to {args.lib_path}") + + +def build_model_from_args(args: argparse.Namespace): + if args.quantization == "q4f16_0": + print( + "WARNING: q4f16_1 is preferred to q4f16_0, " + "and it is highly recommended to use q4f16_1 instead" + ) + + use_ft_quant = args.quantization.name in [ + "q4f16_ft", + "q8f16_ft", + "q4f16_ft_group", + "q8f16_ft_group", + ] + + if args.num_shards > 1: + if (not args.build_model_only) and (not args.convert_weights_only): + raise ValueError( + "`num_shards` should be used together with " + "`--build-model-only` and `--convert-weight-only`" + ) + + if use_ft_quant and not args.use_presharded_weights: + print( + "WARNING: FT quantization with multi-gpus requires presharding weights." + "Forcing --use-presharded-weights." + ) + args.use_presharded_weights = True + + os.makedirs(args.artifact_path, exist_ok=True) + if args.debug_dump: + os.makedirs(os.path.join(args.artifact_path, "debug"), exist_ok=True) + cache_path = os.path.join(args.artifact_path, "mod_cache_before_build.pkl") + args.raw_params_path = os.path.join(args.artifact_path, "raw_params") + use_cache = args.use_cache and os.path.isfile(cache_path) + if args.sep_embed and args.model_category != "llama": + raise ValueError(f"separate embedding not supported on {args.model}") + + if args.model_category == "minigpt": + # Special case for minigpt, which neither provides nor requires a configuration. + config = {} + else: + with open(os.path.join(args.model_path, "config.json"), encoding="utf-8") as i_f: + config = json.load(i_f) + + if not use_cache or args.convert_weights_only: + model_generators = { + "llama": llama, + "mistral": mistral, + "stablelm_epoch": stablelm_3b, + "gpt_neox": gpt_neox, + "gpt_bigcode": gpt_bigcode, + "minigpt": minigpt, + "gptj": gptj, + "rwkv": rwkv, + "rwkv_world": rwkv, + "chatglm": chatglm, + } + + if args.use_vllm_attention: + model_generators["llama"] = llama_batched_vllm + model_generators["mistral"] = llama_batched_vllm + + assert args.model_category in model_generators, f"Model {args.model} not supported" + + mod, param_manager, params, model_config = model_generators[args.model_category].get_model( + args, config + ) + + if args.model_category == "mistral": + args.sliding_window = model_config.sliding_window + args.attention_sink_size = model_config.attention_sink_size + + for qspec_updater_class in param_manager.qspec_updater_classes: + qspec_updater = qspec_updater_class(param_manager) + qspec_updater.visit_module(mod) + + if not args.build_model_only: + parameter_transforms = [] + + # Run pre-quantization if provided. + args.model_path = param_manager.run_pre_quantize(args.model_path) + param_manager.init_torch_pname_to_bin_name(args.use_safetensors) + parameter_transforms.append(param_manager.create_parameter_transformation()) + + # Run pre-sharding if required + if args.num_shards > 1 and args.use_presharded_weights: + mod_shard = create_shard_transformation_func(param_manager, args, model_config) + mod_shard = transform_params_for_each_rank(mod_shard, num_shards=args.num_shards) + parameter_transforms.append(mod_shard) + + # Chain all parameter transforms together. This allows + # ReorderTransformFunc to be applied to the single + # resulting parameter transformation function. + mod_transform = functools.reduce(chain_parameter_transforms, parameter_transforms) + + seq = tvm.ir.transform.Sequential( + [ + relax.transform.CanonicalizeBindings(), + relax.transform.EliminateCommonSubexpr(), + relax.transform.DeadCodeElimination(), + # TODO(Lunderberg): Implement + # relax.transform.Simplify() that applies + # canonicalization, CSE, and DCE until + # convergence. + relax.transform.CanonicalizeBindings(), + relax.transform.EliminateCommonSubexpr(), + relax.transform.DeadCodeElimination(), + param_manager.optimize_transform_param_order(), + ], + name="SimplifyModTransform", + ) + + mod_transform = seq(mod_transform) + + params = utils.convert_weights(mod_transform, param_manager, params, args) + + if args.num_shards > 1 and use_ft_quant: + preprocessed = [] + weight_preprocess_func = tvm.get_global_func("cutlass.ft_preprocess_weight") + is_int4 = args.quantization.name in ["q4f16_ft", "q4f16_ft_group"] + sm = get_cuda_sm_version() + + for p in params: + if p.dtype == "int8": + preprocessed.append(weight_preprocess_func(p, sm, is_int4)) + else: + preprocessed.append(p) + + params = preprocessed + + utils.save_params( + params, args.artifact_path, args.num_shards if args.use_presharded_weights else 1 + ) + + if args.model_category != "minigpt": + utils.copy_tokenizer(args) + if args.model_category == "rwkv" or args.model_category == "rwkv_world": + # TODO: refactor config into model definition + dump_mlc_chat_config( + args, + vocab_size=config["vocab_size"], + max_window_size=model_config.max_sequence_length, + max_gen_len=model_config.max_sequence_length, + top_p=0.6, + temperature=1.2, + repetition_penalty=0.996, + rwkv_world=True, + ) + elif args.model_category == "chatglm": + dump_mlc_chat_config( + args, + vocab_size=config["padded_vocab_size"], + max_window_size=model_config.max_sequence_length, + max_gen_len=model_config.max_sequence_length, + ) + else: + dump_mlc_chat_config( + args, + vocab_size=config["vocab_size"], + max_window_size=model_config.max_sequence_length, + max_gen_len=model_config.max_sequence_length, + ) + + if args.convert_weights_only: + exit(0) + + mod = mod_transform_before_build(mod, param_manager, args, model_config) + if args.num_shards > 1: + # We require a "create_sharding_info" function for all + # multi-GPU models, even if they are using pre-sharded + # weights. When using pre-sharded weights, the list of + # initialization-time transforms to apply is empty. + sharding_module = create_shard_info_func(param_manager, args, model_config) + mod.update(sharding_module) + + with open(cache_path, "wb") as outfile: + pickle.dump(mod, outfile) + print(f"Save a cached module to {cache_path}.") + else: + print( + f"Load cached module from {cache_path} and skip tracing. " + "You can use --use-cache=0 to retrace" + ) + with open(cache_path, "rb") as pkl: + mod = pickle.load(pkl) + if not args.reuse_lib: + build(mod, args) + else: + print(f"Reuse existing prebuilt lib {args.reuse_lib}...") + + +def build_model(args: BuildArgs) -> (Optional[str], Optional[str], Optional[str]): + r"""Builds/compiles a model. + + Parameters + ---------- + args : :class:`BuildArgs` + A dataclass of arguments for building models.mlc_llm/core.py + + Returns + ---------- + lib_path: Optional[str] + The path to the model library file. Return ``None`` if not applicable. + model_path: Optional[str] + The path to the folder of the model's parameters. Return ``None`` if not applicable. + chat_config_path: Optional[str] + The path to the chat config `.json` file. Return ``None`` if not applicable. + """ + # Convert BuildArgs to argparse.Namespace so that we can share the rest + # of the code with the command line workflow + build_args_as_dict = asdict(args) + build_args_namespace = argparse.Namespace(**build_args_as_dict) + args = _parse_args(build_args_namespace) + build_model_from_args(args) + + # Prepare output; some workflows may or may not have the paths to return + lib_path = args.lib_path if hasattr(args, "lib_path") else None + model_path = args.params_path if hasattr(args, "params_path") else None + chat_config_path = args.chat_config_path if hasattr(args, "chat_config_path") else None + + return lib_path, model_path, chat_config_path diff --git a/mlc_llm/dispatch/__init__.py b/mlc_llm/dispatch/__init__.py new file mode 100644 index 0000000..234b60a --- /dev/null +++ b/mlc_llm/dispatch/__init__.py @@ -0,0 +1,2 @@ +from .dispatch_tir_operator import DispatchTIROperator +from .dispatch_tir_operator_adreno import DispatchTIROperatorAdreno diff --git a/mlc_llm/dispatch/dispatch_tir_operator.py b/mlc_llm/dispatch/dispatch_tir_operator.py new file mode 100644 index 0000000..21a7d27 --- /dev/null +++ b/mlc_llm/dispatch/dispatch_tir_operator.py @@ -0,0 +1,53 @@ +# pylint: disable=missing-docstring +import tvm +from tvm import IRModule + + +@tvm.transform.module_pass(opt_level=0, name="DispatchTIROperator") +class DispatchTIROperator: # pylint: disable=too-few-public-methods + def __init__(self, model: str): + # pylint: disable=import-outside-toplevel + if model == "llama": + from .llama import lookup + + elif model == "gpt_neox": + from .gpt_neox import lookup + + elif model == "gpt_bigcode": + lookup = None + + elif model == "minigpt": + lookup = None + + elif model == "rwkv": + lookup = None + + elif model == "rwkv_world": + lookup = None + + elif model == "gptj": + lookup = None + + elif model == "chatglm": + lookup = None + + else: + raise ValueError(f"Model {model} not supported") + self.lookup = lookup + + # pylint: enable=import-outside-toplevel + + def transform_module( + self, + mod: IRModule, + ctx: tvm.transform.PassContext, + ) -> IRModule: + if self.lookup is None: + return mod + for gv in mod.functions: + scheduled_func = self.lookup(mod[gv]) + if scheduled_func is not None: + mod[gv] = scheduled_func + print("- Dispatch to pre-scheduled op:", gv.name_hint) + + return mod diff --git a/mlc_llm/dispatch/dispatch_tir_operator_adreno.py b/mlc_llm/dispatch/dispatch_tir_operator_adreno.py new file mode 100644 index 0000000..937a158 --- /dev/null +++ b/mlc_llm/dispatch/dispatch_tir_operator_adreno.py @@ -0,0 +1,8356 @@ +import tvm +from tvm import IRModule +from tvm.script import tir as T + + +@T.prim_func(private=True) +def fused_decode4_matmul3( + lv1587: T.Buffer((T.int64(512), T.int64(4096)), "uint32"), + lv1588: T.Buffer((T.int64(128), T.int64(4096)), "float16"), + lv1583: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + var_matmul_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(4096)), "float16" + ), +): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1587[v_i // T.int64(8), v_j], lv1588[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1587[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv1588[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(4096), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1583[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_matmul_intermediate[v_i0, v_i1, v_i2] + + lv1583[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + ) + + +@T.prim_func(private=True) +def fused_decode4_matmul3_after( + lv1587: T.Buffer((T.int64(512), T.int64(4096)), "uint32"), + lv1588: T.Buffer((T.int64(128), T.int64(4096)), "float16"), + lv1583: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + var_matmul_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(4096)), "float16" + ), +): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_matmul_intermediate_local = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(32768)), "float16", scope="local" + ) + var_matmul_intermediate_local_batch = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(32768)), "float16", scope="local" + ) + lv1587_local = T.alloc_buffer( + (T.int64(512), T.int64(4096)), "uint32", scope="local" + ) + lv1588_local = T.alloc_buffer( + (T.int64(128), T.int64(4096)), "float16", scope="local" + ) + lv1583_shared = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(2048)), "float16", scope="shared" + ) + for i0_i1_i2_fused_0 in T.thread_binding(T.int64(32), thread="blockIdx.x"): + for i0_i1_i2_fused_1 in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for i0_i1_i2_fused_2_init in T.vectorized(T.int64(4)): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial( + T.int64(32768), + i0_i1_i2_fused_0 * T.int64(1024) + + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + i0_i1_i2_fused_2_init, + ) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float16(0) + for k_0 in range(T.int64(2)): + for ax2_1 in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2_2 in T.vectorized(T.int64(8)): + with T.block("lv1583_shared"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(4096), + k_0 * T.int64(2048) + + ax2_1 * T.int64(64) + + (ax2_y * T.int64(8) + ax2_2), + ) + v2k = T.axis.spatial( + T.int64(2048), + ( + ax2_1 * T.int64(64) + + ax2_y * T.int64(8) + + ax2_2 + ), + ) + T.reads(lv1583[v0, v1, v2]) + T.writes(lv1583_shared[v0, v1, v2k]) + lv1583_shared[v0, v1, v2k] = lv1583[v0, v1, v2] + for k_1 in range(T.int64(8)): + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for ax1 in T.vectorized(T.int64(4)): + with T.block("matmul_init_local"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2k = T.axis.spatial( + T.int64(32768), + i0_i1_i2_fused_0 * T.int64(1024) + + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + ax1, + ) + T.reads() + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = T.float16(0) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv1588_local"): + v0 = T.axis.spatial( + T.int64(128), + k_0 * T.int64(64) + + (k_1 * T.int64(8) + ax2_y) + + ax0, + ) + v1 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv1588[v0, v1]) + T.writes(lv1588_local[v0, v1]) + lv1588_local[v0, v1] = lv1588[v0, v1] + for k_2 in range(T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv1587_local"): + v0 = T.axis.spatial( + T.int64(512), + k_0 * T.int64(256) + + (k_1 * T.int64(8) + ax2_y) * T.int64(4) + + k_2 + + ax0, + ) + v1 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv1587[v0, v1]) + T.writes(lv1587_local[v0, v1]) + lv1587_local[v0, v1] = lv1587[v0, v1] + for k_3 in range(T.int64(8)): + for i0_i1_i2_fused_2 in T.vectorized(T.int64(4)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_i2k = T.axis.spatial( + T.int64(32768), + i0_i1_i2_fused_0 * T.int64(1024) + + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_k = T.axis.reduce( + T.int64(4096), + k_0 * T.int64(2048) + + (k_1 * T.int64(8) + ax2_y) * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + v_ki = T.axis.reduce( + T.int64(2048), + (k_1 * T.int64(8) + ax2_y) * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + T.reads( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ], + lv1583_shared[v_i0, v_i1, v_ki], + lv1587_local[v_k // T.int64(8), v_i2], + ) + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + lv1583_shared[ + v_i0, v_i1, v_ki + ] * ( + ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1587_local[ + v_k // T.int64(8), v_i2 + ], + T.Cast( + "uint32", + v_k % T.int64(8), + ) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) + ) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("multiple_scale"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2k = T.axis.spatial( + T.int64(32768), + i0_i1_i2_fused_0 * T.int64(1024) + + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + ax1, + ) + v0 = T.axis.spatial( + T.int64(128), + k_0 * T.int64(64) + + (k_1 * T.int64(8) + ax2_y) + + ax0, + ) + v1 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads( + lv1588_local[v0, v1], + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ], + ) + T.writes( + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] + ) + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] = ( + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] + + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + * lv1588_local[v0, v1] + ) + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_update"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(1024), + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + ax2, + ) + v_i2k = T.axis.spatial( + T.int64(32768), + i0_i1_i2_fused_0 * T.int64(1024) + + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + ax2, + ) + T.reads(var_matmul_intermediate_local[v0, v1, v_i2k]) + T.writes(lv1583_shared[v0, v1, v2]) + lv1583_shared[v0, v1, v2] = var_matmul_intermediate_local[ + v0, v1, v_i2k + ] + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("reduction_sum"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(1024), + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + ax2, + ) + T.where(ax2_y < T.int64(4)) + T.reads(lv1583_shared[v0, v1, v2]) + T.writes(lv1583_shared[v0, v1, v2]) + lv1583_shared[v0, v1, v2] = ( + lv1583_shared[v0, v1, v2] + + lv1583_shared[v0, v1, v2 + T.int64(16)] + ) + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax2, + ) + v_i2k = T.axis.spatial( + T.int64(1024), + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + ax2, + ) + T.where(ax2_y < T.int64(1)) + T.reads(lv1583_shared[v0, v1, v_i2k]) + T.writes(var_matmul_intermediate[v0, v1, v2]) + var_matmul_intermediate[v0, v1, v2] = ( + lv1583_shared[v0, v1, v_i2k] + + lv1583_shared[v0, v1, v_i2k + T.int64(4)] + + lv1583_shared[v0, v1, v_i2k + T.int64(8)] + + lv1583_shared[v0, v1, v_i2k + T.int64(12)] + ) + + +def sch_fused_decode4_matmul3(func): + sch = tvm.tir.Schedule(func) + b0 = sch.get_block(name="decode", func_name="main") + b1 = sch.get_block(name="matmul", func_name="main") + l2, l3, l4, l5 = sch.get_loops(block=b1) + l6 = sch.fuse(l2, l3, l4, preserve_unit_iters=True) + v7, v8, v9 = sch.sample_perfect_tile( + loop=l6, n=3, max_innermost_factor=4, decision=[32, 64, 2] + ) + l10, l11, l12 = sch.split(loop=l6, factors=[v7, v8, v9], preserve_unit_iters=True) + v13, v14, v15 = sch.sample_perfect_tile( + loop=l5, n=3, max_innermost_factor=8, decision=[128, 4, 8] + ) + l16, l17, l18 = sch.split( + loop=l5, factors=[v13, v14, v15], preserve_unit_iters=True + ) + sch.reorder(l10, l11, l16, l17, l18, l12) + sch.bind(loop=l10, thread_axis="blockIdx.x") + sch.bind(loop=l11, thread_axis="threadIdx.x") + sch.compute_inline(block=b0) + b19 = sch.cache_write(block=b1, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b19, loop=l11, preserve_unit_loops=True, index=-1) + b20 = sch.cache_read(block=b1, read_buffer_index=1, storage_scope="local") + b21 = sch.cache_read(block=b1, read_buffer_index=2, storage_scope="local") + b22 = sch.cache_read(block=b1, read_buffer_index=0, storage_scope="shared") + sch.compute_at(block=b22, loop=l11, preserve_unit_loops=True, index=-1) + v23 = sch.sample_categorical( + candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=3 + ) + sch.annotate( + block_or_loop=b22, ann_key="meta_schedule.cooperative_fetch", ann_val=v23 + ) + sch.compute_at(block=b20, loop=l17, preserve_unit_loops=True, index=-1) + sch.compute_at(block=b21, loop=l16, preserve_unit_loops=True, index=-1) + l24, l25, l26, l27, l28, l29 = sch.get_loops(block=b20) + sch.vectorize(loop=l29) + l30, l31, l32, l33, l34 = sch.get_loops(block=b21) + sch.vectorize(loop=l34) + l35, l36, l37, l38, l39 = sch.get_loops(block=b19) + sch.vectorize(loop=l39) + sch.vectorize(loop=l12) + b40 = sch.decompose_reduction(block=b1, loop=l16) + sch.enter_postproc() + sch.unannotate(block_or_loop=b22, ann_key="meta_schedule.cooperative_fetch") + l41, l42, l43, l44, l45 = sch.get_loops(block=b22) + l46, l47, l48 = sch.split(loop=l45, factors=[None, 64, 8], preserve_unit_iters=True) + sch.vectorize(loop=l48) + sch.bind(loop=l47, thread_axis="threadIdx.x") + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func(private=True) +def fused_decode6_fused_matmul7_add1( + lv1623: T.Buffer((T.int64(1376), T.int64(4096)), "uint32"), + lv1624: T.Buffer((T.int64(344), T.int64(4096)), "float16"), + lv200: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16"), + lv198: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + p_output0_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(4096)), "float16" + ), +): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(11008), T.int64(4096)), "float16") + var_matmul_intermediate = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(4096)), "float16" + ) + for i, j in T.grid(T.int64(11008), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1623[v_i // T.int64(8), v_j], lv1624[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1623[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv1624[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(4096), T.int64(11008)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv200[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_matmul_intermediate[v_i0, v_i1, v_i2] + + lv200[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + lv198[v_ax0, v_ax1, v_ax2], var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + ) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = ( + lv198[v_ax0, v_ax1, v_ax2] + + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + ) + + +@T.prim_func(private=True) +def fused_decode6_fused_matmul7_add1_after( + lv1623: T.Buffer((T.int64(1376), T.int64(4096)), "uint32"), + lv1624: T.Buffer((T.int64(344), T.int64(4096)), "float16"), + lv200: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16"), + lv198: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + p_output0_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(4096)), "float16" + ), +): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_matmul_intermediate_local = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(16384)), "float16", scope="local" + ) + var_matmul_intermediate_local_batch = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(16384)), "float16", scope="local" + ) + lv1623_local = T.alloc_buffer( + (T.int64(1376), T.int64(4096)), "uint32", scope="local" + ) + lv1624_local = T.alloc_buffer( + (T.int64(344), T.int64(4096)), "float16", scope="local" + ) + lv200_shared = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(2752)), "float16", scope="shared" + ) + for i0_i1_i2_fused_0 in T.thread_binding(T.int64(8), thread="blockIdx.x"): + for i0_i1_i2_fused_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(4), thread="threadIdx.y"): + for i0_i1_i2_fused_2_init in T.vectorized(T.int64(4)): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial( + T.int64(16384), + i0_i1_i2_fused_0 * T.int64(2048) + + i0_i1_i2_fused_1 * T.int64(16) + + ax2_y * T.int64(4) + + i0_i1_i2_fused_2_init, + ) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float16(0) + for k_0 in range(T.int64(4)): + for ax0, ax1, ax2_0 in T.grid(T.int64(1), T.int64(1), T.int64(3)): + for ax2_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(4), thread="threadIdx.y"): + for ax2_2 in T.vectorized(T.int64(2)): + with T.block("lv200_shared"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(11008), + k_0 * T.int64(2752) + + ( + ax2_0 * T.int64(1024) + + ax2_1 * T.int64(8) + + (ax2_y * T.int64(2) + ax2_2) + ), + ) + v2k = T.axis.spatial( + T.int64(2752), + ( + ax2_0 * T.int64(1024) + + ax2_1 * T.int64(8) + + (ax2_y * T.int64(2) + ax2_2) + ), + ) + T.where( + (ax2_0 * T.int64(128) + ax2_1) < T.int64(344) + ) + T.reads(lv200[v0, v1, v2]) + T.writes(lv200_shared[v0, v1, v2k]) + lv200_shared[v0, v1, v2k] = lv200[v0, v1, v2] + for k_1 in range(T.int64(22)): + for ax2_y in T.thread_binding(T.int64(4), thread="threadIdx.y"): + with T.block("lv1624_check"): + T.where((k_1 * T.int64(4) + ax2_y) < T.int64(86)) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("matmul_init_local"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2k = T.axis.spatial( + T.int64(16384), + i0_i1_i2_fused_0 * T.int64(2048) + + i0_i1_i2_fused_1 * T.int64(16) + + ax2_y * T.int64(4) + + ax1, + ) + T.reads() + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = T.float16(0) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv1624_local"): + v0 = T.axis.spatial( + T.int64(344), + k_0 * T.int64(86) + + (k_1 * T.int64(4) + ax2_y) + + ax0, + ) + v1 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv1624[v0, v1]) + T.writes(lv1624_local[v0, v1]) + lv1624_local[v0, v1] = lv1624[v0, v1] + for k_2 in range(T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv1623_local"): + v0 = T.axis.spatial( + T.int64(1376), + k_0 * T.int64(344) + + (k_1 * T.int64(4) + ax2_y) + * T.int64(4) + + k_2 + + ax0, + ) + v1 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv1623[v0, v1]) + T.writes(lv1623_local[v0, v1]) + lv1623_local[v0, v1] = lv1623[v0, v1] + for k_3 in range(T.int64(8)): + for i0_i1_i2_fused_2 in T.vectorized(T.int64(4)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial( + T.int64(1), T.int64(0) + ) + v_i1 = T.axis.spatial( + T.int64(1), T.int64(0) + ) + v_i2 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_i2k = T.axis.spatial( + T.int64(16384), + i0_i1_i2_fused_0 * T.int64(2048) + + i0_i1_i2_fused_1 * T.int64(16) + + ax2_y * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_k = T.axis.reduce( + T.int64(11008), + k_0 * T.int64(2752) + + (k_1 * T.int64(4) + ax2_y) + * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + v_ki = T.axis.reduce( + T.int64(2752), + (k_1 * T.int64(4) + ax2_y) * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + T.reads( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ], + lv200_shared[v_i0, v_i1, v_ki], + lv1623_local[v_k // T.int64(8), v_i2], + ) + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + lv200_shared[ + v_i0, v_i1, v_ki + ] * ( + ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1623_local[ + v_k // T.int64(8), + v_i2, + ], + T.Cast( + "uint32", + v_k % T.int64(8), + ) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) + ) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("multiple_scale"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2k = T.axis.spatial( + T.int64(16384), + i0_i1_i2_fused_0 * T.int64(2048) + + i0_i1_i2_fused_1 * T.int64(16) + + ax2_y * T.int64(4) + + ax1, + ) + v0 = T.axis.spatial( + T.int64(344), + k_0 * T.int64(86) + + (k_1 * T.int64(4) + ax2_y) + + ax0, + ) + v1 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads( + lv1624_local[v0, v1], + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ], + ) + T.writes( + var_matmul_intermediate_local[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local[ + v_i0, v_i1, v_i2k + ] = ( + var_matmul_intermediate_local[ + v_i0, v_i1, v_i2k + ] + + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + * lv1624_local[v0, v1] + ) + for ax2_y in T.thread_binding(T.int64(4), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_update"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(2048), + i0_i1_i2_fused_1 * T.int64(16) + + ax2_y * T.int64(4) + + ax2, + ) + v_i2k = T.axis.spatial( + T.int64(16384), + i0_i1_i2_fused_0 * T.int64(2048) + + i0_i1_i2_fused_1 * T.int64(16) + + ax2_y * T.int64(4) + + ax2, + ) + T.reads(var_matmul_intermediate_local[v0, v1, v_i2k]) + T.writes(lv200_shared[v0, v1, v2]) + lv200_shared[v0, v1, v2] = var_matmul_intermediate_local[ + v0, v1, v_i2k + ] + for ax2_y in T.thread_binding(T.int64(4), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(4) + + ax2, + ) + v_i2k = T.axis.spatial( + T.int64(2048), + i0_i1_i2_fused_1 * T.int64(16) + + ax2_y * T.int64(4) + + ax2, + ) + T.where(ax2_y < T.int64(1)) + T.reads(lv200_shared[v0, v1, v_i2k]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = ( + lv198[v0, v1, v2] + + lv200_shared[v0, v1, v_i2k] + + lv200_shared[v0, v1, v_i2k + T.int64(4)] + + lv200_shared[v0, v1, v_i2k + T.int64(8)] + + lv200_shared[v0, v1, v_i2k + T.int64(12)] + ) + + +def sch_fused_decode6_fused_matmul7_add1(func): + sch = tvm.tir.Schedule(func) + b0 = sch.get_block(name="decode", func_name="main") + b1 = sch.get_block(name="matmul", func_name="main") + l2, l3, l4, l5 = sch.get_loops(block=b1) + l6 = sch.fuse(l2, l3, l4, preserve_unit_iters=True) + v7, v8, v9 = sch.sample_perfect_tile( + loop=l6, n=3, max_innermost_factor=4, decision=[8, 256, 2] + ) + l10, l11, l12 = sch.split(loop=l6, factors=[v7, v8, v9], preserve_unit_iters=True) + v13, v14, v15 = sch.sample_perfect_tile( + loop=l5, n=3, max_innermost_factor=8, decision=[344, 4, 8] + ) + l16, l17, l18 = sch.split( + loop=l5, factors=[v13, v14, v15], preserve_unit_iters=True + ) + sch.reorder(l10, l11, l16, l17, l18, l12) + sch.bind(loop=l10, thread_axis="blockIdx.x") + sch.bind(loop=l11, thread_axis="threadIdx.x") + sch.compute_inline(block=b0) + b19 = sch.cache_write(block=b1, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b19, loop=l11, preserve_unit_loops=True, index=-1) + b20 = sch.cache_read(block=b1, read_buffer_index=0, storage_scope="shared") + sch.compute_at(block=b20, loop=l11, preserve_unit_loops=True, index=-1) + v21 = sch.sample_categorical( + candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=3 + ) + sch.annotate( + block_or_loop=b20, ann_key="meta_schedule.cooperative_fetch", ann_val=v21 + ) + l22, l23, l24, l25, l26 = sch.get_loops(block=b19) + sch.vectorize(loop=l26) + sch.vectorize(loop=l12) + b27 = sch.decompose_reduction(block=b1, loop=l16) + b28 = sch.get_block(name="T_add", func_name="main") + sch.reverse_compute_inline(block=b28) + sch.enter_postproc() + sch.unannotate(block_or_loop=b20, ann_key="meta_schedule.cooperative_fetch") + l29, l30, l31, l32, l33 = sch.get_loops(block=b20) + l34, l35, l36 = sch.split( + loop=l33, factors=[None, 256, 8], preserve_unit_iters=True + ) + sch.vectorize(loop=l36) + sch.bind(loop=l35, thread_axis="threadIdx.x") + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func(private=True) +def fused_decode5_fused_matmul6_multiply1( + lv1617: T.Buffer((T.int64(512), T.int64(11008)), "uint32"), + lv1618: T.Buffer((T.int64(128), T.int64(11008)), "float16"), + lv1622: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + lv4: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16"), + p_output0_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(11008)), "float16" + ), +): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(11008)), "float16") + var_matmul_intermediate = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(11008)), "float16" + ) + for i, j in T.grid(T.int64(4096), T.int64(11008)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1617[v_i // T.int64(8), v_j], lv1618[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1617[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv1618[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(11008), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1622[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_matmul_intermediate[v_i0, v_i1, v_i2] + + lv1622[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + lv4[v_ax0, v_ax1, v_ax2], var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + ) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = ( + lv4[v_ax0, v_ax1, v_ax2] * var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + ) + + +@T.prim_func(private=True) +def fused_decode5_fused_matmul6_multiply1_after( + lv1617: T.Buffer((T.int64(512), T.int64(11008)), "uint32"), + lv1618: T.Buffer((T.int64(128), T.int64(11008)), "float16"), + lv1622: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + lv4: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16"), + p_output0_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(11008)), "float16" + ), +): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_matmul_intermediate_local = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(22016)), "float16", scope="local" + ) + var_matmul_intermediate_local_batch = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(22016)), "float16", scope="local" + ) + lv1617_local = T.alloc_buffer( + (T.int64(512), T.int64(11008)), "uint32", scope="local" + ) + lv1618_local = T.alloc_buffer( + (T.int64(128), T.int64(11008)), "float16", scope="local" + ) + lv1622_shared = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(1024)), "float16", scope="shared" + ) + for i0_i1_i2_fused_0 in T.thread_binding(T.int64(43), thread="blockIdx.x"): + for i0_i1_i2_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(2), thread="threadIdx.y"): + for i0_i1_i2_fused_2_init in T.vectorized(T.int64(4)): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + i0_i1_i2_fused_2_init, + ) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float16(0) + for k_0 in range(T.int64(4)): + for ax2_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(2), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2_2 in T.vectorized(T.int64(8)): + with T.block("lv1622_shared"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(4096), + k_0 * T.int64(1024) + + ax2_y * T.int64(512) + + ax2_1 * T.int64(8) + + ax2_2, + ) + v2k = T.axis.spatial( + T.int64(1024), + ( + ax2_y * T.int64(512) + + ax2_1 * T.int64(8) + + ax2_2 + ), + ) + T.reads(lv1622[v0, v1, v2]) + T.writes(lv1622_shared[v0, v1, v2k]) + lv1622_shared[v0, v1, v2k] = lv1622[v0, v1, v2] + for k_1 in range(T.int64(16)): + for ax2_y in T.thread_binding(T.int64(2), thread="threadIdx.y"): + for ax1 in T.vectorized(T.int64(4)): + with T.block("matmul_init_local"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2k = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + ax1, + ) + T.reads() + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = T.float16(0) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv1618_local"): + v0 = T.axis.spatial( + T.int64(128), + k_0 * T.int64(32) + + (k_1 * T.int64(2) + ax2_y) + + ax0, + ) + v1 = T.axis.spatial( + T.int64(11008), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv1618[v0, v1]) + T.writes(lv1618_local[v0, v1]) + lv1618_local[v0, v1] = lv1618[v0, v1] + for k_2 in range(T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv1617_local"): + v0 = T.axis.spatial( + T.int64(512), + k_0 * T.int64(128) + + (k_1 * T.int64(2) + ax2_y) * T.int64(4) + + k_2 + + ax0, + ) + v1 = T.axis.spatial( + T.int64(11008), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv1617[v0, v1]) + T.writes(lv1617_local[v0, v1]) + lv1617_local[v0, v1] = lv1617[v0, v1] + for k_3 in range(T.int64(8)): + for i0_i1_i2_fused_2 in T.vectorized(T.int64(4)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial( + T.int64(11008), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_i2k = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_k = T.axis.reduce( + T.int64(4096), + k_0 * T.int64(1024) + + (k_1 * T.int64(2) + ax2_y) * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + v_ki = T.axis.reduce( + T.int64(1024), + (k_1 * T.int64(2) + ax2_y) * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + T.reads( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ], + lv1622_shared[v_i0, v_i1, v_ki], + lv1617_local[v_k // T.int64(8), v_i2], + ) + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + lv1622_shared[ + v_i0, v_i1, v_ki + ] * ( + ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1617_local[ + v_k // T.int64(8), v_i2 + ], + T.Cast( + "uint32", + v_k % T.int64(8), + ) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) + ) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("multiple_scale"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2k = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + ax1, + ) + v0 = T.axis.spatial( + T.int64(128), + k_0 * T.int64(32) + + (k_1 * T.int64(2) + ax2_y) + + ax0, + ) + v1 = T.axis.spatial( + T.int64(11008), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads( + lv1618_local[v0, v1], + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ], + ) + T.writes( + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] + ) + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] = ( + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] + + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + * lv1618_local[v0, v1] + ) + for ax2_y in T.thread_binding(T.int64(2), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_update"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(512), + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + ax2, + ) + v_i2k = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + ax2, + ) + T.reads(var_matmul_intermediate_local[v0, v1, v_i2k]) + T.writes(lv1622_shared[v0, v1, v2]) + lv1622_shared[v0, v1, v2] = var_matmul_intermediate_local[ + v0, v1, v_i2k + ] + for ax2_y in T.thread_binding(T.int64(2), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(11008), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + + ax2, + ) + v_i2k = T.axis.spatial( + T.int64(512), + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + ax2, + ) + T.where(ax2_y < T.int64(1)) + T.reads(lv1622_shared[v0, v1, v_i2k], lv4[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = lv4[v0, v1, v2] * ( + lv1622_shared[v0, v1, v_i2k] + + lv1622_shared[v0, v1, v_i2k + T.int64(4)] + ) + + +def sch_fused_decode5_fused_matmul6_multiply1(func): + sch = tvm.tir.Schedule(func) + b0 = sch.get_block(name="decode", func_name="main") + b1 = sch.get_block(name="matmul", func_name="main") + l2, l3, l4, l5 = sch.get_loops(block=b1) + l6 = sch.fuse(l2, l3, l4, preserve_unit_iters=True) + v7, v8, v9 = sch.sample_perfect_tile( + loop=l6, n=3, max_innermost_factor=4, decision=[43, 64, 4] + ) + l10, l11, l12 = sch.split(loop=l6, factors=[v7, v8, v9], preserve_unit_iters=True) + v13, v14, v15 = sch.sample_perfect_tile( + loop=l5, n=3, max_innermost_factor=8, decision=[128, 4, 8] + ) + l16, l17, l18 = sch.split( + loop=l5, factors=[v13, v14, v15], preserve_unit_iters=True + ) + sch.reorder(l10, l11, l16, l17, l18, l12) + sch.bind(loop=l10, thread_axis="blockIdx.x") + sch.bind(loop=l11, thread_axis="threadIdx.x") + sch.compute_inline(block=b0) + b19 = sch.cache_write(block=b1, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b19, loop=l11, preserve_unit_loops=True, index=-1) + b20 = sch.cache_read(block=b1, read_buffer_index=1, storage_scope="local") + b21 = sch.cache_read(block=b1, read_buffer_index=2, storage_scope="local") + b22 = sch.cache_read(block=b1, read_buffer_index=0, storage_scope="shared") + sch.compute_at(block=b22, loop=l11, preserve_unit_loops=True, index=-1) + v23 = sch.sample_categorical( + candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=3 + ) + sch.annotate( + block_or_loop=b22, ann_key="meta_schedule.cooperative_fetch", ann_val=v23 + ) + sch.compute_at(block=b20, loop=l17, preserve_unit_loops=True, index=-1) + sch.compute_at(block=b21, loop=l16, preserve_unit_loops=True, index=-1) + l24, l25, l26, l27, l28, l29 = sch.get_loops(block=b20) + sch.vectorize(loop=l29) + l30, l31, l32, l33, l34 = sch.get_loops(block=b21) + sch.vectorize(loop=l34) + l35, l36, l37, l38, l39 = sch.get_loops(block=b19) + sch.vectorize(loop=l39) + sch.vectorize(loop=l12) + b40 = sch.decompose_reduction(block=b1, loop=l16) + b41 = sch.get_block(name="T_multiply", func_name="main") + sch.reverse_compute_inline(block=b41) + sch.enter_postproc() + sch.unannotate(block_or_loop=b22, ann_key="meta_schedule.cooperative_fetch") + l42, l43, l44, l45, l46 = sch.get_loops(block=b22) + l47, l48, l49 = sch.split(loop=l46, factors=[None, 64, 8], preserve_unit_iters=True) + sch.vectorize(loop=l49) + sch.bind(loop=l48, thread_axis="threadIdx.x") + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func(private=True) +def fused_fused_decode9_matmul7( + lv19: T.Buffer((T.int64(512), T.int64(22016)), "uint32"), + lv20: T.Buffer((T.int64(128), T.int64(22016)), "float16"), + lv1654: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + var_matmul_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(22016)), "float16" + ), +): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + p_output0_intermediate = T.alloc_buffer((T.int64(4096), T.int64(22016)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(22016)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv19[v_i // T.int64(8), v_j], lv20[v_i // T.int64(32), v_j]) + T.writes(p_output0_intermediate[v_i, v_j]) + p_output0_intermediate[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv19[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv20[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(22016), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1654[v_i0, v_i1, v_k], p_output0_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_matmul_intermediate[v_i0, v_i1, v_i2] + + lv1654[v_i0, v_i1, v_k] * p_output0_intermediate[v_k, v_i2] + ) + + +@T.prim_func(private=True) +def fused_fused_decode9_matmul7_after( + lv19: T.Buffer((T.int64(512), T.int64(22016)), "uint32"), + lv20: T.Buffer((T.int64(128), T.int64(22016)), "float16"), + lv1654: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + var_matmul_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(22016)), "float16" + ), +): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_matmul_intermediate_local = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(352256)), "float16", scope="local" + ) + var_matmul_intermediate_local_batch = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(352256)), "float16", scope="local" + ) + lv19_local = T.alloc_buffer((T.int64(512), T.int64(22016)), "uint32", scope="local") + lv20_local = T.alloc_buffer( + (T.int64(128), T.int64(22016)), "float16", scope="local" + ) + lv1654_shared = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(4096)), "float16", scope="shared" + ) + for i0_i1_i2_fused_0 in T.thread_binding(T.int64(172), thread="blockIdx.x"): + for i0_i1_i2_fused_1 in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(16), thread="threadIdx.y"): + for i0_i1_i2_fused_2_init in T.vectorized(T.int64(4)): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial( + T.int64(352256), + i0_i1_i2_fused_0 * T.int64(2048) + + i0_i1_i2_fused_1 * T.int64(64) + + ax2_y * T.int64(4) + + i0_i1_i2_fused_2_init + ) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float16(0) + for k_0 in range(T.int64(1)): + for ax2_1 in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(16), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2_2 in T.vectorized(T.int64(8)): + with T.block("lv1654_shared"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(4096), + k_0 * T.int64(4096) + + ax2_y * T.int64(256) + + ax2_1 * T.int64(8) + + ax2_2, + ) + v2k = T.axis.spatial( + T.int64(4096), + ( + ax2_y * T.int64(256) + + ax2_1 * T.int64(8) + + ax2_2 + ), + ) + T.reads(lv1654[v0, v1, v2]) + T.writes(lv1654_shared[v0, v1, v2k]) + lv1654_shared[v0, v1, v2k] = lv1654[v0, v1, v2] + for k_1 in range(T.int64(8)): + for ax2_y in T.thread_binding(T.int64(16), thread="threadIdx.y"): + for ax1 in T.vectorized(T.int64(4)): + with T.block("matmul_init_local"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2k = T.axis.spatial( + T.int64(352256), + i0_i1_i2_fused_0 * T.int64(2048) + + i0_i1_i2_fused_1 * T.int64(64) + + ax2_y * T.int64(4) + + ax1, + ) + T.reads() + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = T.float16(0) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv20_local"): + v0 = T.axis.spatial( + T.int64(128), + k_0 * T.int64(128) + + (k_1 * T.int64(16) + ax2_y) + + ax0, + ) + v1 = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv20[v0, v1]) + T.writes(lv20_local[v0, v1]) + lv20_local[v0, v1] = lv20[v0, v1] + for k_2 in range(T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv19_local"): + v0 = T.axis.spatial( + T.int64(512), + k_0 * T.int64(512) + + (k_1 * T.int64(16) + ax2_y) * T.int64(4) + + k_2 + + ax0, + ) + v1 = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv19[v0, v1]) + T.writes(lv19_local[v0, v1]) + lv19_local[v0, v1] = lv19[v0, v1] + for k_3 in range(T.int64(8)): + for i0_i1_i2_fused_2 in T.vectorized(T.int64(4)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_i2k = T.axis.spatial( + T.int64(352256), + i0_i1_i2_fused_0 * T.int64(2048) + + i0_i1_i2_fused_1 * T.int64(64) + + ax2_y * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_k = T.axis.reduce( + T.int64(4096), + k_0 * T.int64(4096) + + (k_1 * T.int64(16) + ax2_y) * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + v_ki = T.axis.reduce( + T.int64(4096), + (k_1 * T.int64(16) + ax2_y) * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + T.reads( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ], + lv1654_shared[v_i0, v_i1, v_ki], + lv19_local[v_k // T.int64(8), v_i2], + ) + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + lv1654_shared[ + v_i0, v_i1, v_ki + ] * ( + ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv19_local[ + v_k // T.int64(8), v_i2 + ], + T.Cast( + "uint32", + v_k % T.int64(8), + ) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) + ) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("multiple_scale"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2k = T.axis.spatial( + T.int64(352256), + i0_i1_i2_fused_0 * T.int64(2048) + + i0_i1_i2_fused_1 * T.int64(64) + + ax2_y * T.int64(4) + + ax1, + ) + v0 = T.axis.spatial( + T.int64(128), + k_0 * T.int64(128) + + (k_1 * T.int64(16) + ax2_y) + + ax0, + ) + v1 = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads( + lv20_local[v0, v1], + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ], + ) + T.writes( + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] + ) + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] = ( + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] + + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + * lv20_local[v0, v1] + ) + for ax2_y in T.thread_binding(T.int64(16), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_update"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(2048), + i0_i1_i2_fused_1 * T.int64(64) + + ax2_y * T.int64(4) + + ax2, + ) + v_i2k = T.axis.spatial( + T.int64(352256), + i0_i1_i2_fused_0 * T.int64(2048) + + i0_i1_i2_fused_1 * T.int64(64) + + ax2_y * T.int64(4) + + ax2, + ) + T.reads(var_matmul_intermediate_local[v0, v1, v_i2k]) + T.writes(lv1654_shared[v0, v1, v2]) + lv1654_shared[v0, v1, v2] = var_matmul_intermediate_local[ + v0, v1, v_i2k + ] + for ax2_y in T.thread_binding(T.int64(16), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("reduction_1"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v_i2k = T.axis.spatial( + T.int64(2048), + i0_i1_i2_fused_1 * T.int64(64) + + ax2_y * T.int64(4) + + ax2, + ) + T.where(ax2_y < T.int64(8)) + T.reads(lv1654_shared[v0, v1, v_i2k]) + T.writes(lv1654_shared[v0, v1, v_i2k]) + lv1654_shared[v0, v1, v_i2k] = ( + lv1654_shared[v0, v1, v_i2k] + + lv1654_shared[v0, v1, v_i2k + T.int64(32)] + ) + for ax2_y in T.thread_binding(T.int64(16), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("reduction_2"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v_i2k = T.axis.spatial( + T.int64(2048), + i0_i1_i2_fused_1 * T.int64(64) + + ax2_y * T.int64(4) + + ax2, + ) + T.where(ax2_y < T.int64(4)) + T.reads(lv1654_shared[v0, v1, v_i2k]) + T.writes(lv1654_shared[v0, v1, v_i2k]) + lv1654_shared[v0, v1, v_i2k] = ( + lv1654_shared[v0, v1, v_i2k] + + lv1654_shared[v0, v1, v_i2k + T.int64(16)] + ) + for ax2_y in T.thread_binding(T.int64(16), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax2, + ) + v_i2k = T.axis.spatial( + T.int64(2048), + i0_i1_i2_fused_1 * T.int64(64) + + ax2_y * T.int64(4) + + ax2, + ) + T.where(ax2_y < T.int64(1)) + T.reads(lv1654_shared[v0, v1, v_i2k]) + T.writes(var_matmul_intermediate[v0, v1, v2]) + var_matmul_intermediate[v0, v1, v2] = ( + lv1654_shared[v0, v1, v_i2k] + + lv1654_shared[v0, v1, v_i2k + T.int64(4)] + + lv1654_shared[v0, v1, v_i2k + T.int64(8)] + + lv1654_shared[v0, v1, v_i2k + T.int64(12)] + ) + + +@T.prim_func(private=True) +def fused_fused_decode7_matmul4( + lv3: T.Buffer((T.int64(512), T.int64(12288)), "uint32"), + lv4: T.Buffer((T.int64(128), T.int64(12288)), "float16"), + lv1615: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + var_matmul_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(12288)), "float16" + ), +): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + p_output0_intermediate = T.alloc_buffer((T.int64(4096), T.int64(12288)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(12288)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv3[v_i // T.int64(8), v_j], lv4[v_i // T.int64(32), v_j]) + T.writes(p_output0_intermediate[v_i, v_j]) + p_output0_intermediate[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv3[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv4[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(12288), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1615[v_i0, v_i1, v_k], p_output0_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_matmul_intermediate[v_i0, v_i1, v_i2] + + lv1615[v_i0, v_i1, v_k] * p_output0_intermediate[v_k, v_i2] + ) + + +@T.prim_func(private=True) +def fused_fused_decode7_matmul4_after( + lv3: T.Buffer((T.int64(512), T.int64(12288)), "uint32"), + lv4: T.Buffer((T.int64(128), T.int64(12288)), "float16"), + lv1615: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + var_matmul_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(12288)), "float16" + ), +): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_matmul_intermediate_local = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(24576)), "float16", scope="local" + ) + var_matmul_intermediate_local_batch = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(24576)), "float16", scope="local" + ) + lv3_local = T.alloc_buffer((T.int64(512), T.int64(12288)), "uint32", scope="local") + lv4_local = T.alloc_buffer((T.int64(128), T.int64(12288)), "float16", scope="local") + lv1615_shared = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(1024)), "float16", scope="shared" + ) + for i0_i1_i2_fused_0 in T.thread_binding(T.int64(48), thread="blockIdx.x"): + for i0_i1_i2_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(2), thread="threadIdx.y"): + for i0_i1_i2_fused_2_init in T.vectorized(T.int64(4)): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial( + T.int64(24576), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + i0_i1_i2_fused_2_init, + ) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float16(0) + for k_0 in range(T.int64(4)): + for ax2_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(2), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2_2 in T.vectorized(T.int64(8)): + with T.block("lv1615_shared"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(4096), + k_0 * T.int64(1024) + + ax2_y * T.int64(512) + + ax2_1 * T.int64(8) + + ax2_2, + ) + v2k = T.axis.spatial( + T.int64(1024), + ( + ax2_y * T.int64(512) + + ax2_1 * T.int64(8) + + ax2_2 + ), + ) + T.reads(lv1615[v0, v1, v2]) + T.writes(lv1615_shared[v0, v1, v2k]) + lv1615_shared[v0, v1, v2k] = lv1615[v0, v1, v2] + for k_1 in range(T.int64(16)): + for ax2_y in T.thread_binding(T.int64(2), thread="threadIdx.y"): + for ax1 in T.vectorized(T.int64(4)): + with T.block("matmul_init_local"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2k = T.axis.spatial( + T.int64(24576), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + ax1, + ) + T.reads() + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = T.float16(0) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv4_local"): + v0 = T.axis.spatial( + T.int64(128), + k_0 * T.int64(32) + + (k_1 * T.int64(2) + ax2_y) + + ax0, + ) + v1 = T.axis.spatial( + T.int64(12288), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv4[v0, v1]) + T.writes(lv4_local[v0, v1]) + lv4_local[v0, v1] = lv4[v0, v1] + for k_2 in range(T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv3_local"): + v0 = T.axis.spatial( + T.int64(512), + k_0 * T.int64(128) + + (k_1 * T.int64(2) + ax2_y) * T.int64(4) + + k_2 + + ax0, + ) + v1 = T.axis.spatial( + T.int64(12288), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv3[v0, v1]) + T.writes(lv3_local[v0, v1]) + lv3_local[v0, v1] = lv3[v0, v1] + for k_3 in range(T.int64(8)): + for i0_i1_i2_fused_2 in T.vectorized(T.int64(4)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial( + T.int64(12288), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_i2k = T.axis.spatial( + T.int64(24576), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_k = T.axis.reduce( + T.int64(4096), + k_0 * T.int64(1024) + + (k_1 * T.int64(2) + ax2_y) * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + v_ki = T.axis.reduce( + T.int64(1024), + (k_1 * T.int64(2) + ax2_y) * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + T.reads( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ], + lv1615_shared[v_i0, v_i1, v_ki], + lv3_local[v_k // T.int64(8), v_i2], + ) + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + lv1615_shared[ + v_i0, v_i1, v_ki + ] * ( + ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv3_local[ + v_k // T.int64(8), v_i2 + ], + T.Cast( + "uint32", + v_k % T.int64(8), + ) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) + ) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("multiple_scale"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2k = T.axis.spatial( + T.int64(24576), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + ax1, + ) + v0 = T.axis.spatial( + T.int64(128), + k_0 * T.int64(32) + + (k_1 * T.int64(2) + ax2_y) + + ax0, + ) + v1 = T.axis.spatial( + T.int64(12288), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads( + lv4_local[v0, v1], + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ], + ) + T.writes( + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] + ) + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] = ( + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] + + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + * lv4_local[v0, v1] + ) + for ax2_y in T.thread_binding(T.int64(2), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_update"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(512), + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + ax2, + ) + v_i2k = T.axis.spatial( + T.int64(24576), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + ax2, + ) + T.reads(var_matmul_intermediate_local[v0, v1, v_i2k]) + T.writes(lv1615_shared[v0, v1, v2]) + lv1615_shared[v0, v1, v2] = var_matmul_intermediate_local[ + v0, v1, v_i2k + ] + for ax2_y in T.thread_binding(T.int64(2), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(12288), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + + ax2, + ) + v_i2k = T.axis.spatial( + T.int64(512), + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + ax2, + ) + T.where(ax2_y < T.int64(1)) + T.reads(lv1615_shared[v0, v1, v_i2k]) + T.writes(var_matmul_intermediate[v0, v1, v2]) + var_matmul_intermediate[v0, v1, v2] = ( + lv1615_shared[v0, v1, v_i2k] + + lv1615_shared[v0, v1, v_i2k + T.int64(4)] + ) + + +@T.prim_func(private=True) +def fused_decode5_fused_matmul6_silu1( + lv1611: T.Buffer((T.int64(512), T.int64(11008)), "uint32"), + lv1612: T.Buffer((T.int64(128), T.int64(11008)), "float16"), + lv1622: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + p_output0_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(11008)), "float16" + ), +): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(11008)), "float16") + var_matmul_intermediate = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(11008)), "float16" + ) + compute = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(11008)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1611[v_i // T.int64(8), v_j], lv1612[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1611[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv1612[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(11008), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1622[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_matmul_intermediate[v_i0, v_i1, v_i2] + + lv1622[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + ) + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(11008)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_matmul_intermediate[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.sigmoid( + var_matmul_intermediate[v_i0, v_i1, v_i2] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + var_matmul_intermediate[v_ax0, v_ax1, v_ax2], + compute[v_ax0, v_ax1, v_ax2], + ) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + * compute[v_ax0, v_ax1, v_ax2] + ) + + +@T.prim_func(private=True) +def fused_decode5_fused_matmul6_silu1_after( + lv1611: T.Buffer((T.int64(512), T.int64(11008)), "uint32"), + lv1612: T.Buffer((T.int64(128), T.int64(11008)), "float16"), + lv1622: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + p_output0_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(11008)), "float16" + ), +): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_matmul_intermediate_local = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(22016)), "float16", scope="local" + ) + var_matmul_intermediate_local_batch = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(22016)), "float16", scope="local" + ) + lv1611_local = T.alloc_buffer( + (T.int64(512), T.int64(11008)), "uint32", scope="local" + ) + lv1612_local = T.alloc_buffer( + (T.int64(128), T.int64(11008)), "float16", scope="local" + ) + lv1622_shared = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(1024)), "float16", scope="shared" + ) + for i0_i1_i2_fused_0 in T.thread_binding(T.int64(43), thread="blockIdx.x"): + for i0_i1_i2_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(2), thread="threadIdx.y"): + for i0_i1_i2_fused_2_init in T.vectorized(T.int64(4)): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + i0_i1_i2_fused_2_init, + ) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float16(0) + for k_0 in range(T.int64(4)): + for ax2_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(2), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2_2 in T.vectorized(T.int64(8)): + with T.block("lv1622_shared"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(4096), + k_0 * T.int64(1024) + + ax2_y * T.int64(512) + + ax2_1 * T.int64(8) + + ax2_2, + ) + v2k = T.axis.spatial( + T.int64(1024), + ( + ax2_y * T.int64(512) + + ax2_1 * T.int64(8) + + ax2_2 + ), + ) + T.reads(lv1622[v0, v1, v2]) + T.writes(lv1622_shared[v0, v1, v2k]) + lv1622_shared[v0, v1, v2k] = lv1622[v0, v1, v2] + for k_1 in range(T.int64(16)): + for ax2_y in T.thread_binding(T.int64(2), thread="threadIdx.y"): + for ax1 in T.vectorized(T.int64(4)): + with T.block("matmul_init_local"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2k = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + ax1, + ) + T.reads() + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = T.float16(0) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv1612_local"): + v0 = T.axis.spatial( + T.int64(128), + k_0 * T.int64(32) + + (k_1 * T.int64(2) + ax2_y) + + ax0, + ) + v1 = T.axis.spatial( + T.int64(11008), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv1612[v0, v1]) + T.writes(lv1612_local[v0, v1]) + lv1612_local[v0, v1] = lv1612[v0, v1] + for k_2 in range(T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv1611_local"): + v0 = T.axis.spatial( + T.int64(512), + k_0 * T.int64(128) + + (k_1 * T.int64(2) + ax2_y) * T.int64(4) + + k_2 + + ax0, + ) + v1 = T.axis.spatial( + T.int64(11008), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv1611[v0, v1]) + T.writes(lv1611_local[v0, v1]) + lv1611_local[v0, v1] = lv1611[v0, v1] + for k_3 in range(T.int64(8)): + for i0_i1_i2_fused_2 in T.vectorized(T.int64(4)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial( + T.int64(11008), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_i2k = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_k = T.axis.reduce( + T.int64(4096), + k_0 * T.int64(1024) + + (k_1 * T.int64(2) + ax2_y) * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + v_ki = T.axis.reduce( + T.int64(1024), + (k_1 * T.int64(2) + ax2_y) * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + T.reads( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ], + lv1622_shared[v_i0, v_i1, v_ki], + lv1611_local[v_k // T.int64(8), v_i2], + ) + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + lv1622_shared[ + v_i0, v_i1, v_ki + ] * ( + ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1611_local[ + v_k // T.int64(8), v_i2 + ], + T.Cast( + "uint32", + v_k % T.int64(8), + ) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) + ) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("multiple_scale"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2k = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + ax1, + ) + v0 = T.axis.spatial( + T.int64(128), + k_0 * T.int64(32) + + (k_1 * T.int64(2) + ax2_y) + + ax0, + ) + v1 = T.axis.spatial( + T.int64(11008), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads( + lv1612_local[v0, v1], + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ], + ) + T.writes( + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] + ) + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] = ( + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] + + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + * lv1612_local[v0, v1] + ) + for ax2_y in T.thread_binding(T.int64(2), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_update"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(512), + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + ax2, + ) + v_i2k = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(512) + + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + ax2, + ) + T.reads(var_matmul_intermediate_local[v0, v1, v_i2k]) + T.writes(lv1622_shared[v0, v1, v2]) + lv1622_shared[v0, v1, v2] = var_matmul_intermediate_local[ + v0, v1, v_i2k + ] + for ax2_y in T.thread_binding(T.int64(2), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("reduction"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(512), + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + ax2, + ) + T.where(ax2_y < T.int64(1)) + T.reads(lv1622_shared[v0, v1, v2]) + T.writes(lv1622_shared[v0, v1, v2]) + lv1622_shared[v0, v1, v2] = ( + lv1622_shared[v0, v1, v2] + + lv1622_shared[v0, v1, v2 + T.int64(4)] + ) + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(11008), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + + ax2, + ) + v_i2k = T.axis.spatial( + T.int64(512), + i0_i1_i2_fused_1 * T.int64(8) + + ax2_y * T.int64(4) + + ax2, + ) + T.where(ax2_y < T.int64(1)) + T.reads(lv1622_shared[v0, v1, v_i2k]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = lv1622_shared[ + v0, v1, v_i2k + ] * T.sigmoid(lv1622_shared[v0, v1, v_i2k]) + + +def sch_fused_decode5_fused_matmul6_silu1(func): + sch = tvm.tir.Schedule(func) + b0 = sch.get_block(name="decode", func_name="main") + b1 = sch.get_block(name="matmul", func_name="main") + l2, l3, l4, l5 = sch.get_loops(block=b1) + l6 = sch.fuse(l2, l3, l4, preserve_unit_iters=True) + v7, v8, v9 = sch.sample_perfect_tile( + loop=l6, n=3, max_innermost_factor=4, decision=[43, 64, 4] + ) + l10, l11, l12 = sch.split(loop=l6, factors=[v7, v8, v9], preserve_unit_iters=True) + v13, v14, v15 = sch.sample_perfect_tile( + loop=l5, n=3, max_innermost_factor=8, decision=[128, 4, 8] + ) + l16, l17, l18 = sch.split( + loop=l5, factors=[v13, v14, v15], preserve_unit_iters=True + ) + sch.reorder(l10, l11, l16, l17, l18, l12) + sch.bind(loop=l10, thread_axis="blockIdx.x") + sch.bind(loop=l11, thread_axis="threadIdx.x") + sch.compute_inline(block=b0) + b19 = sch.cache_write(block=b1, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b19, loop=l11, preserve_unit_loops=True, index=-1) + b20 = sch.cache_read(block=b1, read_buffer_index=1, storage_scope="local") + b21 = sch.cache_read(block=b1, read_buffer_index=2, storage_scope="local") + b22 = sch.cache_read(block=b1, read_buffer_index=0, storage_scope="shared") + sch.compute_at(block=b22, loop=l11, preserve_unit_loops=True, index=-1) + v23 = sch.sample_categorical( + candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=3 + ) + sch.annotate( + block_or_loop=b22, ann_key="meta_schedule.cooperative_fetch", ann_val=v23 + ) + sch.compute_at(block=b20, loop=l17, preserve_unit_loops=True, index=-1) + sch.compute_at(block=b21, loop=l16, preserve_unit_loops=True, index=-1) + l24, l25, l26, l27, l28, l29 = sch.get_loops(block=b20) + sch.vectorize(loop=l29) + l30, l31, l32, l33, l34 = sch.get_loops(block=b21) + sch.vectorize(loop=l34) + l35, l36, l37, l38, l39 = sch.get_loops(block=b19) + sch.vectorize(loop=l39) + sch.vectorize(loop=l12) + b40 = sch.decompose_reduction(block=b1, loop=l16) + b41 = sch.get_block(name="compute", func_name="main") + sch.compute_inline(block=b41) + b42 = sch.get_block(name="T_multiply", func_name="main") + sch.reverse_compute_inline(block=b42) + sch.enter_postproc() + sch.unannotate(block_or_loop=b22, ann_key="meta_schedule.cooperative_fetch") + l43, l44, l45, l46, l47 = sch.get_loops(block=b22) + l48, l49, l50 = sch.split(loop=l47, factors=[None, 64, 8], preserve_unit_iters=True) + sch.vectorize(loop=l50) + sch.bind(loop=l49, thread_axis="threadIdx.x") + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + +@T.prim_func(private=True) +def fused_decode81_fused_matmul1_cast2( + lv1576: T.Buffer((T.int64(512), T.int64(64000)), "uint32"), + lv1577: T.Buffer((T.int64(128), T.int64(64000)), "float16"), + lv1575: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + p_output0_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(64000)), "float32" + ), +): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(64000)), "float16") + var_matmul_intermediate = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(64000)), "float16" + ) + for i, j in T.grid(T.int64(4096), T.int64(64000)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1576[v_i // T.int64(8), v_j], lv1577[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1576[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv1577[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(64000), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1575[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_matmul_intermediate[v_i0, v_i1, v_i2] + + lv1575[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + ) + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(64000)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_matmul_intermediate[v_i0, v_i1, v_i2]) + T.writes(p_output0_intermediate[v_i0, v_i1, v_i2]) + p_output0_intermediate[v_i0, v_i1, v_i2] = T.Cast( + "float32", var_matmul_intermediate[v_i0, v_i1, v_i2] + ) + +def sch_fused_decode81_fused_matmul1_cast2(func): + sch = tvm.tir.Schedule(func) + b0 = sch.get_block(name="decode", func_name="main") + b1 = sch.get_block(name="matmul", func_name="main") + l2, l3, l4, l5 = sch.get_loops(block=b1) + l6 = sch.fuse(l2, l3, l4, preserve_unit_iters=True) + v7, v8, v9 = sch.sample_perfect_tile( + loop=l6, n=3, max_innermost_factor=4, decision=[160, 100, 4] + ) + l10, l11, l12 = sch.split(loop=l6, factors=[v7, v8, v9], preserve_unit_iters=True) + v13, v14, v15 = sch.sample_perfect_tile( + loop=l5, n=3, max_innermost_factor=8, decision=[512, 8, 1] + ) + l16, l17, l18 = sch.split( + loop=l5, factors=[v13, v14, v15], preserve_unit_iters=True + ) + sch.reorder(l10, l11, l16, l17, l18, l12) + sch.bind(loop=l10, thread_axis="blockIdx.x") + sch.bind(loop=l11, thread_axis="threadIdx.x") + sch.compute_inline(block=b0) + b19 = sch.cache_write(block=b1, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b19, loop=l11, preserve_unit_loops=True, index=-1) + b20 = sch.cache_read(block=b1, read_buffer_index=1, storage_scope="local") + b21 = sch.cache_read(block=b1, read_buffer_index=2, storage_scope="local") + b22 = sch.cache_read(block=b1, read_buffer_index=0, storage_scope="shared") + sch.compute_at(block=b22, loop=l11, preserve_unit_loops=True, index=-1) + v23 = sch.sample_categorical( + candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1 + ) + sch.annotate( + block_or_loop=b22, ann_key="meta_schedule.cooperative_fetch", ann_val=v23 + ) + sch.compute_at(block=b20, loop=l17, preserve_unit_loops=True, index=-1) + sch.compute_at(block=b21, loop=l16, preserve_unit_loops=True, index=-1) + l24, l25, l26, l27, l28, l29 = sch.get_loops(block=b20) + sch.vectorize(loop=l29) + l30, l31, l32, l33, l34 = sch.get_loops(block=b21) + sch.vectorize(loop=l34) + l35, l36, l37, l38, l39 = sch.get_loops(block=b19) + sch.vectorize(loop=l39) + sch.vectorize(loop=l12) + b40 = sch.decompose_reduction(block=b1, loop=l16) + b41 = sch.get_block(name="compute", func_name="main") + sch.reverse_compute_inline(block=b41) + sch.enter_postproc() + sch.unannotate(block_or_loop=b22, ann_key="meta_schedule.cooperative_fetch") + l42, l43, l44, l45, l46 = sch.get_loops(block=b22) + l47, l48, l49 = sch.split( + loop=l46, factors=[None, 100, 2], preserve_unit_iters=True + ) + sch.vectorize(loop=l49) + sch.bind(loop=l48, thread_axis="threadIdx.x") + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + + + +@T.prim_func(private=True) +def fused_decode4_fused_matmul4_add1( + lv1605: T.Buffer((T.int64(512), T.int64(4096)), "uint32"), + lv1606: T.Buffer((T.int64(128), T.int64(4096)), "float16"), + lv197: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + lv1581: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + p_output0_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(4096)), "float16" + ), +): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(4096)), "float16") + var_matmul_intermediate = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(4096)), "float16" + ) + for i, j in T.grid(T.int64(4096), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1605[v_i // T.int64(8), v_j], lv1606[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1605[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv1606[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(4096), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv197[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_matmul_intermediate[v_i0, v_i1, v_i2] + + lv197[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + lv1581[v_ax0, v_ax1, v_ax2], + var_matmul_intermediate[v_ax0, v_ax1, v_ax2], + ) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = ( + lv1581[v_ax0, v_ax1, v_ax2] + + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + ) + + +@T.prim_func(private=True) +def fused_decode4_fused_matmul4_add1_after( + lv1605: T.Buffer((T.int64(512), T.int64(4096)), "uint32"), + lv1606: T.Buffer((T.int64(128), T.int64(4096)), "float16"), + lv197: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + lv1581: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + p_output0_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(4096)), "float16" + ), +): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_matmul_intermediate_local = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(32768)), "float16", scope="local" + ) + var_matmul_intermediate_local_batch = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(32768)), "float16", scope="local" + ) + lv1605_local = T.alloc_buffer( + (T.int64(512), T.int64(4096)), "uint32", scope="local" + ) + lv1606_local = T.alloc_buffer( + (T.int64(128), T.int64(4096)), "float16", scope="local" + ) + lv197_shared = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(2048)), "float16", scope="shared" + ) + for i0_i1_i2_fused_0 in T.thread_binding(T.int64(32), thread="blockIdx.x"): + for i0_i1_i2_fused_1 in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for i0_i1_i2_fused_2_init in T.vectorized(T.int64(4)): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial( + T.int64(32768), + i0_i1_i2_fused_0 * T.int64(1024) + + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + i0_i1_i2_fused_2_init, + ) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float16(0) + for k_0 in range(T.int64(2)): + for ax2_1 in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2_2 in T.vectorized(T.int64(8)): + with T.block("lv197_shared"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(4096), + k_0 * T.int64(2048) + + ax2_1 * T.int64(64) + + (ax2_y * T.int64(8) + ax2_2), + ) + v2k = T.axis.spatial( + T.int64(2048), + ( + ax2_1 * T.int64(64) + + ax2_y * T.int64(8) + + ax2_2 + ), + ) + T.reads(lv197[v0, v1, v2]) + T.writes(lv197_shared[v0, v1, v2k]) + lv197_shared[v0, v1, v2k] = lv197[v0, v1, v2] + for k_1 in range(T.int64(8)): + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for ax1 in T.vectorized(T.int64(4)): + with T.block("matmul_init_local"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2k = T.axis.spatial( + T.int64(32768), + i0_i1_i2_fused_0 * T.int64(1024) + + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + ax1, + ) + T.reads() + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = T.float16(0) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv1606_local"): + v0 = T.axis.spatial( + T.int64(128), + k_0 * T.int64(64) + + (k_1 * T.int64(8) + ax2_y) + + ax0, + ) + v1 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv1606[v0, v1]) + T.writes(lv1606_local[v0, v1]) + lv1606_local[v0, v1] = lv1606[v0, v1] + for k_2 in range(T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv1605_local"): + v0 = T.axis.spatial( + T.int64(512), + k_0 * T.int64(256) + + (k_1 * T.int64(8) + ax2_y) * T.int64(4) + + k_2 + + ax0, + ) + v1 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv1605[v0, v1]) + T.writes(lv1605_local[v0, v1]) + lv1605_local[v0, v1] = lv1605[v0, v1] + for k_3 in range(T.int64(8)): + for i0_i1_i2_fused_2 in T.vectorized(T.int64(4)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_i2k = T.axis.spatial( + T.int64(32768), + i0_i1_i2_fused_0 * T.int64(1024) + + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_k = T.axis.reduce( + T.int64(4096), + k_0 * T.int64(2048) + + (k_1 * T.int64(8) + ax2_y) * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + v_ki = T.axis.reduce( + T.int64(2048), + (k_1 * T.int64(8) + ax2_y) * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + T.reads( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ], + lv197_shared[v_i0, v_i1, v_ki], + lv1605_local[v_k // T.int64(8), v_i2], + ) + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + lv197_shared[ + v_i0, v_i1, v_ki + ] * ( + ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1605_local[ + v_k // T.int64(8), v_i2 + ], + T.Cast( + "uint32", + v_k % T.int64(8), + ) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) + ) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("multiple_scale"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2k = T.axis.spatial( + T.int64(32768), + i0_i1_i2_fused_0 * T.int64(1024) + + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + ax1, + ) + v0 = T.axis.spatial( + T.int64(128), + k_0 * T.int64(64) + + (k_1 * T.int64(8) + ax2_y) + + ax0, + ) + v1 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads( + lv1606_local[v0, v1], + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ], + ) + T.writes( + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] + ) + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] = ( + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] + + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + * lv1606_local[v0, v1] + ) + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_update"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(1024), + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + ax2, + ) + v_i2k = T.axis.spatial( + T.int64(32768), + i0_i1_i2_fused_0 * T.int64(1024) + + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + ax2, + ) + T.reads(var_matmul_intermediate_local[v0, v1, v_i2k]) + T.writes(lv197_shared[v0, v1, v2]) + lv197_shared[v0, v1, v2] = var_matmul_intermediate_local[ + v0, v1, v_i2k + ] + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("reduction_sum"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(1024), + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + ax2, + ) + T.where(ax2_y < T.int64(4)) + T.reads(lv197_shared[v0, v1, v2]) + T.writes(lv197_shared[v0, v1, v2]) + lv197_shared[v0, v1, v2] = ( + lv197_shared[v0, v1, v2] + + lv197_shared[v0, v1, v2 + T.int64(16)] + ) + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax2, + ) + v_i2k = T.axis.spatial( + T.int64(1024), + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + ax2, + ) + T.where(ax2_y < T.int64(1)) + T.reads(lv197_shared[v0, v1, v_i2k], lv1581[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = ( + lv1581[v0, v1, v2] + + lv197_shared[v0, v1, v_i2k] + + lv197_shared[v0, v1, v_i2k + T.int64(4)] + + lv197_shared[v0, v1, v_i2k + T.int64(8)] + + lv197_shared[v0, v1, v_i2k + T.int64(12)] + ) + +@T.prim_func(private=True) +def fused_decode82_fused_matmul1_cast2( + lv1576: T.Buffer((T.int64(512), T.int64(64000)), "uint32"), + lv1577: T.Buffer((T.int64(128), T.int64(64000)), "float16"), + lv1575: T.Buffer((T.int64(1), T.int64(1), T.int64(2048)), "float16"), + p_output0_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(64000)), "float32" + ), +): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(2048), T.int64(64000)), "float16") + var_matmul_intermediate = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(64000)), "float16" + ) + for i, j in T.grid(T.int64(2048), T.int64(64000)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1576[v_i // T.int64(8), v_j], lv1577[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1576[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv1577[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(64000), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1575[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_matmul_intermediate[v_i0, v_i1, v_i2] + + lv1575[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + ) + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(64000)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_matmul_intermediate[v_i0, v_i1, v_i2]) + T.writes(p_output0_intermediate[v_i0, v_i1, v_i2]) + p_output0_intermediate[v_i0, v_i1, v_i2] = T.Cast( + "float32", var_matmul_intermediate[v_i0, v_i1, v_i2] + ) + +def sch_fused_decode82_fused_matmul1_cast2(func): + sch = tvm.tir.Schedule(func) + b0 = sch.get_block(name="decode", func_name="main") + b1 = sch.get_block(name="matmul", func_name="main") + l2, l3, l4, l5 = sch.get_loops(block=b1) + l6 = sch.fuse(l2, l3, l4, preserve_unit_iters=True) + v7, v8, v9 = sch.sample_perfect_tile( + loop=l6, n=3, max_innermost_factor=4, decision=[160, 100, 4] + ) + l10, l11, l12 = sch.split(loop=l6, factors=[v7, v8, v9], preserve_unit_iters=True) + v13, v14, v15 = sch.sample_perfect_tile( + loop=l5, n=3, max_innermost_factor=8, decision=[512, 8, 1] + ) + l16, l17, l18 = sch.split( + loop=l5, factors=[v13, v14, v15], preserve_unit_iters=True + ) + sch.reorder(l10, l11, l16, l17, l18, l12) + sch.bind(loop=l10, thread_axis="blockIdx.x") + sch.bind(loop=l11, thread_axis="threadIdx.x") + sch.compute_inline(block=b0) + b19 = sch.cache_write(block=b1, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b19, loop=l11, preserve_unit_loops=True, index=-1) + b20 = sch.cache_read(block=b1, read_buffer_index=1, storage_scope="local") + b21 = sch.cache_read(block=b1, read_buffer_index=2, storage_scope="local") + b22 = sch.cache_read(block=b1, read_buffer_index=0, storage_scope="shared") + sch.compute_at(block=b22, loop=l11, preserve_unit_loops=True, index=-1) + v23 = sch.sample_categorical( + candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1 + ) + sch.annotate( + block_or_loop=b22, ann_key="meta_schedule.cooperative_fetch", ann_val=v23 + ) + sch.compute_at(block=b20, loop=l17, preserve_unit_loops=True, index=-1) + sch.compute_at(block=b21, loop=l16, preserve_unit_loops=True, index=-1) + l24, l25, l26, l27, l28, l29 = sch.get_loops(block=b20) + sch.vectorize(loop=l29) + l30, l31, l32, l33, l34 = sch.get_loops(block=b21) + sch.vectorize(loop=l34) + l35, l36, l37, l38, l39 = sch.get_loops(block=b19) + sch.vectorize(loop=l39) + sch.vectorize(loop=l12) + b40 = sch.decompose_reduction(block=b1, loop=l16) + b41 = sch.get_block(name="compute", func_name="main") + sch.reverse_compute_inline(block=b41) + sch.enter_postproc() + sch.unannotate(block_or_loop=b22, ann_key="meta_schedule.cooperative_fetch") + l42, l43, l44, l45, l46 = sch.get_loops(block=b22) + l47, l48, l49 = sch.split( + loop=l46, factors=[None, 100, 2], preserve_unit_iters=True + ) + sch.vectorize(loop=l49) + sch.bind(loop=l48, thread_axis="threadIdx.x") + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + +def sch_fused_decode4_fused_matmul4_add1(func): + sch = tvm.tir.Schedule(func) + b0 = sch.get_block(name="decode", func_name="main") + b1 = sch.get_block(name="matmul", func_name="main") + l2, l3, l4, l5 = sch.get_loops(block=b1) + l6 = sch.fuse(l2, l3, l4, preserve_unit_iters=True) + v7, v8, v9 = sch.sample_perfect_tile( + loop=l6, n=3, max_innermost_factor=4, decision=[32, 64, 2] + ) + l10, l11, l12 = sch.split(loop=l6, factors=[v7, v8, v9], preserve_unit_iters=True) + v13, v14, v15 = sch.sample_perfect_tile( + loop=l5, n=3, max_innermost_factor=8, decision=[128, 4, 8] + ) + l16, l17, l18 = sch.split( + loop=l5, factors=[v13, v14, v15], preserve_unit_iters=True + ) + sch.reorder(l10, l11, l16, l17, l18, l12) + sch.bind(loop=l10, thread_axis="blockIdx.x") + sch.bind(loop=l11, thread_axis="threadIdx.x") + sch.compute_inline(block=b0) + b19 = sch.cache_write(block=b1, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b19, loop=l11, preserve_unit_loops=True, index=-1) + b20 = sch.cache_read(block=b1, read_buffer_index=1, storage_scope="local") + b21 = sch.cache_read(block=b1, read_buffer_index=2, storage_scope="local") + b22 = sch.cache_read(block=b1, read_buffer_index=0, storage_scope="shared") + sch.compute_at(block=b22, loop=l11, preserve_unit_loops=True, index=-1) + v23 = sch.sample_categorical( + candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=3 + ) + sch.annotate( + block_or_loop=b22, ann_key="meta_schedule.cooperative_fetch", ann_val=v23 + ) + sch.compute_at(block=b20, loop=l17, preserve_unit_loops=True, index=-1) + sch.compute_at(block=b21, loop=l16, preserve_unit_loops=True, index=-1) + l24, l25, l26, l27, l28, l29 = sch.get_loops(block=b20) + sch.vectorize(loop=l29) + l30, l31, l32, l33, l34 = sch.get_loops(block=b21) + sch.vectorize(loop=l34) + l35, l36, l37, l38, l39 = sch.get_loops(block=b19) + sch.vectorize(loop=l39) + sch.vectorize(loop=l12) + b40 = sch.decompose_reduction(block=b1, loop=l16) + b41 = sch.get_block(name="T_add", func_name="main") + sch.reverse_compute_inline(block=b41) + sch.enter_postproc() + sch.unannotate(block_or_loop=b22, ann_key="meta_schedule.cooperative_fetch") + l42, l43, l44, l45, l46 = sch.get_loops(block=b22) + l47, l48, l49 = sch.split(loop=l46, factors=[None, 64, 8], preserve_unit_iters=True) + sch.vectorize(loop=l49) + sch.bind(loop=l48, thread_axis="threadIdx.x") + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + +@T.prim_func(private=True) +def fused_decode3_fused_matmul1_cast2( + lv1576: T.Buffer((T.int64(512), T.int64(32000)), "uint32"), + lv1577: T.Buffer((T.int64(128), T.int64(32000)), "float16"), + lv1575: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + p_output0_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(32000)), "float32" + ), +): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(32000)), "float16") + var_matmul_intermediate = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(32000)), "float16" + ) + for i, j in T.grid(T.int64(4096), T.int64(32000)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1576[v_i // T.int64(8), v_j], lv1577[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1576[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv1577[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(32000), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1575[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_matmul_intermediate[v_i0, v_i1, v_i2] + + lv1575[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + ) + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(32000)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_matmul_intermediate[v_i0, v_i1, v_i2]) + T.writes(p_output0_intermediate[v_i0, v_i1, v_i2]) + p_output0_intermediate[v_i0, v_i1, v_i2] = T.Cast( + "float32", var_matmul_intermediate[v_i0, v_i1, v_i2] + ) + +@T.prim_func(private=True) +def fused_decode3_fused_matmul1_cast2_after( + lv1576: T.Buffer((T.int64(512), T.int64(32000)), "uint32"), + lv1577: T.Buffer((T.int64(128), T.int64(32000)), "float16"), + lv1575: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), + p_output0_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(32000)), "float32" + ), +): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_matmul_intermediate_local = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(512000)), "float16", scope="local" + ) + var_matmul_intermediate_local_batch = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(512000)), "float16", scope="local" + ) + lv1576_local = T.alloc_buffer( + (T.int64(512), T.int64(32000)), "uint32", scope="local" + ) + lv1577_local = T.alloc_buffer( + (T.int64(128), T.int64(32000)), "float16", scope="local" + ) + lv1575_shared = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(4096)), "float16", scope="shared" + ) + for i0_i1_i2_fused_0 in T.thread_binding(T.int64(125), thread="blockIdx.x"): + for i0_i1_i2_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for i0_i1_i2_fused_2_init in T.vectorized(T.int64(4)): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial( + T.int64(512000), + i0_i1_i2_fused_0 * T.int64(2048) + + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + i0_i1_i2_fused_2_init + ) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float16(0) + for k_0 in range(T.int64(1)): + for ax2_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2_2 in T.vectorized(T.int64(8)): + with T.block("lv1575_shared"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(4096), + k_0 * T.int64(4096) + + ax2_y * T.int64(512) + + ax2_1 * T.int64(8) + ax2_2 + ) + v2k = T.axis.spatial( + T.int64(4096), + (ax2_y * T.int64(512) + + ax2_1 * T.int64(8) + ax2_2) + ) + T.reads(lv1575[v0, v1, v2]) + T.writes(lv1575_shared[v0, v1, v2k]) + lv1575_shared[v0, v1, v2k] = lv1575[v0, v1, v2] + for k_1 in range(T.int64(16)): + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for ax1 in T.vectorized(T.int64(4)): + with T.block("matmul_init_local"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2k = T.axis.spatial( + T.int64(512000), + i0_i1_i2_fused_0 * T.int64(2048) + + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + ax1 + ) + T.reads() + T.writes(var_matmul_intermediate_local_batch[v_i0, v_i1, v_i2k]) + var_matmul_intermediate_local_batch[v_i0, v_i1, v_i2k] = T.float16(0) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv1577_local"): + v0 = T.axis.spatial( + T.int64(128), + k_0 * T.int64(128) + + (k_1 * T.int64(8) + ax2_y) + ax0 + ) + v1 = T.axis.spatial( + T.int64(32000), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + ax1 + ) + T.reads(lv1577[v0, v1]) + T.writes(lv1577_local[v0, v1]) + lv1577_local[v0, v1] = lv1577[v0, v1] + for k_2 in range(T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv1576_local"): + v0 = T.axis.spatial( + T.int64(512), + k_0 * T.int64(512) + + (k_1 * T.int64(8) + ax2_y) * T.int64(4) + + k_2 + ax0 + ) + v1 = T.axis.spatial( + T.int64(32000), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1 + ) + T.reads(lv1576[v0, v1]) + T.writes(lv1576_local[v0, v1]) + lv1576_local[v0, v1] = lv1576[v0, v1] + for k_3 in range(T.int64(8)): + for i0_i1_i2_fused_2 in T.vectorized(T.int64(4)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial( + T.int64(32000), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + + i0_i1_i2_fused_2 + ) + v_i2k = T.axis.spatial( + T.int64(512000), + i0_i1_i2_fused_0 * T.int64(2048) + + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + + i0_i1_i2_fused_2 + ) + v_k = T.axis.reduce( + T.int64(4096), + k_0 * T.int64(4096) + + (k_1 * T.int64(8) + ax2_y) * T.int64(32) + + k_2 * T.int64(8) + k_3 + ) + v_ki = T.axis.reduce( + T.int64(4096), + (k_1 * T.int64(8) + ax2_y) * T.int64(32) + + k_2 * T.int64(8) + k_3 + ) + T.reads( + var_matmul_intermediate_local_batch[v_i0, v_i1, v_i2k], + lv1575_shared[v_i0, v_i1, v_ki], lv1576_local[v_k // T.int64(8), v_i2] + ) + T.writes(var_matmul_intermediate_local_batch[v_i0, v_i1, v_i2k]) + var_matmul_intermediate_local_batch[v_i0, v_i1, v_i2k] = ( + var_matmul_intermediate_local_batch[v_i0, v_i1, v_i2k] + + lv1575_shared[v_i0, v_i1, v_ki] + * ((T.Cast("float16", T.bitwise_and(T.shift_right(lv1576_local[v_k // T.int64(8), v_i2], + T.Cast("uint32", v_k % T.int64(8)) * T.uint32(4)), T.uint32(15))) - T.float16(7))) + ) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("multiple_scale"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2k = T.axis.spatial( + T.int64(512000), + i0_i1_i2_fused_0 * T.int64(2048) + + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + ax1 + ) + v0 = T.axis.spatial( + T.int64(128), + k_0 * T.int64(128) + + (k_1 * T.int64(8) + ax2_y) + ax0 + ) + v1 = T.axis.spatial( + T.int64(32000), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + ax1 + ) + T.reads( + lv1577_local[v0, v1], + var_matmul_intermediate_local_batch[v_i0, v_i1, v_i2k] + ) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2k]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] = ( + var_matmul_intermediate_local[v_i0, v_i1, v_i2k] + + var_matmul_intermediate_local_batch[v_i0, v_i1, v_i2k] * lv1577_local[v0, v1] + ) + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_update"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(2048), + ax2_y * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + ax2 + ) + v_i2k = T.axis.spatial( + T.int64(512000), + i0_i1_i2_fused_0 * T.int64(2048) + + i0_i1_i2_fused_1 * T.int64(32) + + ax2_y * T.int64(4) + ax2 + ) + T.reads(var_matmul_intermediate_local[v0, v1, v_i2k]) + T.writes(lv1575_shared[v0, v1, v2]) + lv1575_shared[v0, v1, v2] = var_matmul_intermediate_local[v0, v1, v_i2k] + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("reduction_2"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v_i2k = T.axis.spatial( + T.int64(2048), + ax2_y * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + ax2 + ) + T.where(ax2_y < T.int64(4)) + T.reads(lv1575_shared[v0, v1, v_i2k]) + T.writes(lv1575_shared[v0, v1, v_i2k]) + lv1575_shared[v0, v1, v_i2k] = ( + lv1575_shared[v0, v1, v_i2k] + lv1575_shared[v0, v1, v_i2k + T.int64(1024)] + ) + for ax2_y in T.thread_binding(T.int64(16), thread="threadIdx.y"): + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial( + T.int64(32000), + i0_i1_i2_fused_0 * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + ax2 + ) + v_i2k = T.axis.spatial( + T.int64(2048), + ax2_y * T.int64(256) + + i0_i1_i2_fused_1 * T.int64(4) + ax2 + ) + T.where(ax2_y < T.int64(1)) + T.reads(lv1575_shared[v0, v1, v_i2k]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = T.Cast( + "float32", lv1575_shared[v0, v1, v_i2k] + + lv1575_shared[v0, v1, v_i2k + T.int64(256)] + + lv1575_shared[v0, v1, v_i2k + T.int64(512)] + + lv1575_shared[v0, v1, v_i2k + T.int64(768)] + ) + + +def sch_fused_decode3_fused_matmul1_cast2(func): + sch = tvm.tir.Schedule(func) + b0 = sch.get_block(name="decode", func_name="main") + b1 = sch.get_block(name="matmul", func_name="main") + l2, l3, l4, l5 = sch.get_loops(block=b1) + l6 = sch.fuse(l2, l3, l4, preserve_unit_iters=True) + v7, v8, v9 = sch.sample_perfect_tile( + loop=l6, n=3, max_innermost_factor=4, decision=[80, 100, 4] + ) + l10, l11, l12 = sch.split(loop=l6, factors=[v7, v8, v9], preserve_unit_iters=True) + v13, v14, v15 = sch.sample_perfect_tile( + loop=l5, n=3, max_innermost_factor=8, decision=[512, 8, 1] + ) + l16, l17, l18 = sch.split( + loop=l5, factors=[v13, v14, v15], preserve_unit_iters=True + ) + sch.reorder(l10, l11, l16, l17, l18, l12) + sch.bind(loop=l10, thread_axis="blockIdx.x") + sch.bind(loop=l11, thread_axis="threadIdx.x") + sch.compute_inline(block=b0) + b19 = sch.cache_write(block=b1, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b19, loop=l11, preserve_unit_loops=True, index=-1) + b20 = sch.cache_read(block=b1, read_buffer_index=1, storage_scope="local") + b21 = sch.cache_read(block=b1, read_buffer_index=2, storage_scope="local") + b22 = sch.cache_read(block=b1, read_buffer_index=0, storage_scope="shared") + sch.compute_at(block=b22, loop=l11, preserve_unit_loops=True, index=-1) + v23 = sch.sample_categorical( + candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1 + ) + sch.annotate( + block_or_loop=b22, ann_key="meta_schedule.cooperative_fetch", ann_val=v23 + ) + sch.compute_at(block=b20, loop=l17, preserve_unit_loops=True, index=-1) + sch.compute_at(block=b21, loop=l16, preserve_unit_loops=True, index=-1) + l24, l25, l26, l27, l28, l29 = sch.get_loops(block=b20) + sch.vectorize(loop=l29) + l30, l31, l32, l33, l34 = sch.get_loops(block=b21) + sch.vectorize(loop=l34) + l35, l36, l37, l38, l39 = sch.get_loops(block=b19) + sch.vectorize(loop=l39) + sch.vectorize(loop=l12) + b40 = sch.decompose_reduction(block=b1, loop=l16) + b41 = sch.get_block(name="compute", func_name="main") + sch.reverse_compute_inline(block=b41) + sch.enter_postproc() + sch.unannotate(block_or_loop=b22, ann_key="meta_schedule.cooperative_fetch") + l42, l43, l44, l45, l46 = sch.get_loops(block=b22) + l47, l48, l49 = sch.split( + loop=l46, factors=[None, 100, 2], preserve_unit_iters=True + ) + sch.vectorize(loop=l49) + sch.bind(loop=l48, thread_axis="threadIdx.x") + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func(private=True) +def fused_decode2_fused_NT_matmul3_add( + lv50: T.Buffer((T.int64(1376), T.int64(4096)), "uint32"), + lv51: T.Buffer((T.int64(344), T.int64(4096)), "float16"), + p_lv5: T.handle, + p_lv3: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv5 = T.match_buffer(p_lv5, (T.int64(1), n, T.int64(11008)), "float16") + lv3 = T.match_buffer(p_lv3, (T.int64(1), n, T.int64(4096)), "float16") + p_output0_intermediate = T.match_buffer( + p_output0, (T.int64(1), n, T.int64(4096)), "float16" + ) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(11008), T.int64(4096)), "float16") + var_T_transpose_intermediate = T.alloc_buffer( + (T.int64(4096), T.int64(11008)), "float16" + ) + var_NT_matmul_intermediate = T.alloc_buffer( + (T.int64(1), n, T.int64(4096)), "float16" + ) + for i, j in T.grid(T.int64(11008), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv50[v_i // T.int64(8), v_j], lv51[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv50[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv51[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(4096), T.int64(11008)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(var_T_transpose_intermediate[v_ax0, v_ax1]) + var_T_transpose_intermediate[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(4096), T.int64(11008)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv5[v_i0, v_i1, v_k], var_T_transpose_intermediate[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + + lv5[v_i0, v_i1, v_k] * var_T_transpose_intermediate[v_i2, v_k] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + lv3[v_ax0, v_ax1, v_ax2], + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], + ) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = ( + lv3[v_ax0, v_ax1, v_ax2] + + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + ) + + +@T.prim_func(private=True) +def fused_decode2_fused_NT_matmul3_add_after( + lv8: T.Buffer((T.int64(1376), T.int64(4096)), "uint32"), + lv9: T.Buffer((T.int64(344), T.int64(4096)), "float16"), + p_lv5: T.handle, + p_lv3: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + lv6 = T.match_buffer(p_lv5, (1, n, 11008), "float16") + lv2 = T.match_buffer(p_lv3, (1, n, 4096), "float16") + var_NT_matmul_intermediate = T.match_buffer(p_output0, (1, n, 4096), "float16") + + var_matmul_intermediate_local = T.alloc_buffer( + (T.int64(1), ((n+7)//8) * 8, T.int64(4096)), "float16", scope="local" + ) + var_matmul_intermediate_local_batch = T.alloc_buffer( + (T.int64(1), ((n+7)//8) * 8, T.int64(4096)), "float16", scope="local" + ) + lv8_local = T.alloc_buffer((T.int64(512), T.int64(4096)), "uint32", scope="local") + lv9_local = T.alloc_buffer( + (T.int64(128), T.int64(4096)), "float16", scope="local" + ) + #lv6_shared = T.alloc_buffer( + # (T.int64(1), T.int64(1), T.int64(4096)), "float16", scope="shared" + #) + for i0_i1_i2_fused_n in T.thread_binding(((n+7)//8), thread="blockIdx.y"): + for i0_i1_i2_fused_0 in T.thread_binding(T.int64(32), thread="blockIdx.x"): + for i0_i1_i2_fused_1 in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + with T.block("n_check"): + T.where((i0_i1_i2_fused_n * T.int64(8) + ax2_y) < n) + for i0_i1_i2_fused_2_init in T.vectorized(T.int64(4)): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + i0_i1_i2_fused_2_init + ) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float16(0) + for k_1 in range(T.int64(344)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("matmul_init_local"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2k = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads() + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = T.float16(0) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv9_local"): + v0 = T.axis.spatial( + T.int64(344), k_1 + ) + v1 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv9[v0, v1]) + T.writes(lv9_local[v0, v1]) + lv9_local[v0, v1] = lv9[v0, v1] + for k_2 in range(T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv8_local"): + v0 = T.axis.spatial( + T.int64(1376), + k_1 * T.int64(4) + + k_2 + + ax0, + ) + v1 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv8[v0, v1]) + T.writes(lv8_local[v0, v1]) + lv8_local[v0, v1] = lv8[v0, v1] + for k_3 in range(T.int64(8)): + for i0_i1_i2_fused_2 in T.vectorized(T.int64(4)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_k = T.axis.reduce( + T.int64(11008), + k_1 * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + T.reads( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ], + lv6[v_i0, v_i1, v_k], + lv8_local[v_k // T.int64(8), v_i2], + ) + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ] = var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ] + lv6[ + v_i0, v_i1, v_k + ] * ( + ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv8_local[ + v_k // T.int64(8), v_i2 + ], + T.Cast( + "uint32", + v_k % T.int64(8), + ) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) + ) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("multiple_scale"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + v0 = T.axis.spatial( + T.int64(344), + k_1 + ) + v1 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads( + lv9_local[v0, v1], + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ], + ) + T.writes( + var_matmul_intermediate_local[v_i0, v_i1, v_i2] + ) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = ( + var_matmul_intermediate_local[v_i0, v_i1, v_i2] + + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ] + * lv9_local[v0, v1] + ) + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_local"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax2, + ) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv2[v_i0, v_i1, v_i2]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv2[v_i0, v_i1, v_i2] + + +@T.prim_func(private=True) +def fused_decode_NT_matmul( + lv8: T.Buffer((T.int64(512), T.int64(4096)), "uint32"), + lv9: T.Buffer((T.int64(128), T.int64(4096)), "float16"), + p_lv6: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv6 = T.match_buffer(p_lv6, (T.int64(1), n, T.int64(4096)), "float16") + var_NT_matmul_intermediate = T.match_buffer( + p_output0, (T.int64(1), n, T.int64(4096)), "float16" + ) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(4096), T.int64(4096)), "float16") + var_T_transpose_intermediate = T.alloc_buffer( + (T.int64(4096), T.int64(4096)), "float16" + ) + for i, j in T.grid(T.int64(4096), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv8[v_i // T.int64(8), v_j], lv9[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv8[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv9[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(4096), T.int64(4096)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(var_T_transpose_intermediate[v_ax0, v_ax1]) + var_T_transpose_intermediate[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(4096), T.int64(4096)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv6[v_i0, v_i1, v_k], var_T_transpose_intermediate[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + + lv6[v_i0, v_i1, v_k] * var_T_transpose_intermediate[v_i2, v_k] + ) + + +@T.prim_func(private=True) +def fused_decode_NT_matmul_after( + lv8: T.Buffer((512, 4096), "uint32"), + lv9: T.Buffer((128, 4096), "float16"), + p_lv6: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int32() + lv6 = T.match_buffer(p_lv6, (1, n, 4096), "float16") + var_NT_matmul_intermediate = T.match_buffer(p_output0, (1, n, 4096), "float16") + # with T.block("root"): + decode_local = T.alloc_buffer((4096, 4096), "float16", scope="local") + lv8_local = T.alloc_buffer((512, 4096), "uint32", scope="local") + lv9_local = T.alloc_buffer((128, 4096), "float16", scope="local") + lv6_pad_local = T.alloc_buffer( + (1, (n + 31) // 32 * 32, 4096), "float16", scope="local" + ) + var_NT_matmul_intermediate_pad_local = T.alloc_buffer( + (1, (n + 31) // 32 * 32, 4096), "float16", scope="local" + ) + for i0_i1_fused_0_i0_i1_fused_1_0_fused in T.thread_binding( + (n + 31) // 32, thread="blockIdx.y" + ): + for i2_0 in T.thread_binding(32, thread="blockIdx.x"): + for i0_i1_fused_1_1 in T.thread_binding(8, thread="threadIdx.y"): + for i2_1 in T.thread_binding(16, thread="threadIdx.x"): + for i0_i1_fused_1_2_init in range(4): + for i2_2_init in T.vectorized(8): + with T.block("NT_matmul_init"): + v_i0 = T.axis.spatial(1, 0) + v_i1 = T.axis.spatial( + (n + 31) // 32 * 32, + i0_i1_fused_0_i0_i1_fused_1_0_fused * 32 + + i0_i1_fused_1_1 * 4 + + i0_i1_fused_1_2_init, + ) + v_i2 = T.axis.spatial( + 4096, i2_0 * 128 + i2_1 * 8 + i2_2_init + ) + T.reads() + T.writes( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + ) + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] = T.float16(0) + for k_0 in range(128): + for ax0 in range(1): + for ax1 in T.vectorized(8): + with T.block("lv9_local"): + v0 = T.axis.spatial(128, k_0 + ax0) + v1 = T.axis.spatial( + 4096, i2_0 * 128 + i2_1 * 8 + ax1 + ) + T.reads(lv9[v0, v1]) + T.writes(lv9_local[v0, v1]) + lv9_local[v0, v1] = lv9[v0, v1] + for k_1 in range(4): + for ax0 in range(1): + for ax1 in T.vectorized(8): + with T.block("lv8_local"): + v0 = T.axis.spatial(512, k_0 * 4 + k_1 + ax0) + v1 = T.axis.spatial( + 4096, i2_0 * 128 + i2_1 * 8 + ax1 + ) + T.reads(lv8[v0, v1]) + T.writes(lv8_local[v0, v1]) + lv8_local[v0, v1] = lv8[v0, v1] + for k_2 in range(8): + for ax0 in range(1): + for ax1 in T.vectorized(8): + with T.block("decode"): + v_i = T.axis.spatial( + 4096, k_0 * 32 + k_1 * 8 + k_2 + ax0 + ) + v_j = T.axis.spatial( + 4096, i2_0 * 128 + i2_1 * 8 + ax1 + ) + T.reads( + lv8_local[v_i // 8, v_j], + lv9_local[v_i // 32, v_j], + ) + T.writes(decode_local[v_i, v_j]) + decode_local[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv8_local[v_i // 8, v_j], + T.Cast("uint32", v_i % 8) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv9_local[v_i // 32, v_j] + for ax0, ax1 in T.grid(1, 4): + for ax2 in T.vectorized(1): + with T.block("lv6_pad_local"): + v0 = T.axis.spatial(1, ax0) + v1 = T.axis.spatial( + (n + 31) // 32 * 32, + i0_i1_fused_0_i0_i1_fused_1_0_fused * 32 + + i0_i1_fused_1_1 * 4 + + ax1, + ) + v2 = T.axis.spatial( + 4096, k_0 * 32 + k_1 * 8 + k_2 + ax2 + ) + T.reads(lv6[v0, v1, v2]) + T.writes(lv6_pad_local[v0, v1, v2]) + lv6_pad_local[v0, v1, v2] = T.if_then_else( + v1 < n, lv6[v0, v1, v2], T.float16(0) + ) + for i0_i1_fused_1_2 in range(4): + for i2_2 in T.vectorized(8): + with T.block("NT_matmul_update"): + v_i0 = T.axis.spatial(1, 0) + v_i1 = T.axis.spatial( + (n + 31) // 32 * 32, + i0_i1_fused_0_i0_i1_fused_1_0_fused * 32 + + i0_i1_fused_1_1 * 4 + + i0_i1_fused_1_2, + ) + v_i2 = T.axis.spatial( + 4096, i2_0 * 128 + i2_1 * 8 + i2_2 + ) + v_k = T.axis.reduce( + 4096, k_0 * 32 + k_1 * 8 + k_2 + ) + T.reads( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ], + lv6_pad_local[v_i0, v_i1, v_k], + decode_local[v_k, v_i2], + ) + T.writes( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + ) + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] = ( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + + lv6_pad_local[v_i0, v_i1, v_k] + * decode_local[v_k, v_i2] + ) + for ax0, ax1 in T.grid(1, 4): + for ax2 in T.vectorized(8): + with T.block("var_NT_matmul_intermediate_pad_local"): + v0 = T.axis.spatial(1, ax0) + v1 = T.axis.spatial( + (n + 31) // 32 * 32, + i0_i1_fused_0_i0_i1_fused_1_0_fused * 32 + + i0_i1_fused_1_1 * 4 + + ax1, + ) + v2 = T.axis.spatial(4096, i2_0 * 128 + i2_1 * 8 + ax2) + T.reads( + var_NT_matmul_intermediate_pad_local[v0, v1, v2] + ) + T.writes(var_NT_matmul_intermediate[v0, v1, v2]) + if v1 < n: + var_NT_matmul_intermediate[ + v0, v1, v2 + ] = var_NT_matmul_intermediate_pad_local[v0, v1, v2] + + +@T.prim_func(private=True) +def fused_decode1_fused_NT_matmul2_silu( + lv36: T.Buffer((T.int64(512), T.int64(11008)), "uint32"), + lv37: T.Buffer((T.int64(128), T.int64(11008)), "float16"), + p_lv45: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv45 = T.match_buffer(p_lv45, (T.int64(1), n, T.int64(4096)), "float16") + p_output0_intermediate = T.match_buffer( + p_output0, (T.int64(1), n, T.int64(11008)), "float16" + ) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(4096), T.int64(11008)), "float16") + var_T_transpose_intermediate = T.alloc_buffer( + (T.int64(11008), T.int64(4096)), "float16" + ) + var_NT_matmul_intermediate = T.alloc_buffer( + (T.int64(1), n, T.int64(11008)), "float16" + ) + compute = T.alloc_buffer((T.int64(1), n, T.int64(11008)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(11008)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv36[v_i // T.int64(8), v_j], lv37[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv36[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv37[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(11008), T.int64(4096)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(var_T_transpose_intermediate[v_ax0, v_ax1]) + var_T_transpose_intermediate[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(11008), T.int64(4096)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv45[v_i0, v_i1, v_k], var_T_transpose_intermediate[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + + lv45[v_i0, v_i1, v_k] * var_T_transpose_intermediate[v_i2, v_k] + ) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(11008)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.sigmoid( + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], + compute[v_ax0, v_ax1, v_ax2], + ) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + * compute[v_ax0, v_ax1, v_ax2] + ) + + +@T.prim_func(private=True) +def fused_decode1_fused_NT_matmul2_silu_after( + lv36: T.Buffer((512, 11008), "uint32"), + lv37: T.Buffer((128, 11008), "float16"), + p_lv45: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int32() + lv45 = T.match_buffer(p_lv45, (1, n, 4096), "float16") + p_output0_intermediate = T.match_buffer(p_output0, (1, n, 11008), "float16") + # with T.block("root"): + decode_local = T.alloc_buffer((4096, 11008), "float16", scope="local") + lv36_local = T.alloc_buffer((512, 11008), "uint32", scope="local") + lv37_local = T.alloc_buffer((128, 11008), "float16", scope="local") + lv45_pad_local = T.alloc_buffer( + (1, (n + 31) // 32 * 32, 4096), "float16", scope="local" + ) + var_NT_matmul_intermediate_pad_local = T.alloc_buffer( + (1, (n + 31) // 32 * 32, 11008), "float16", scope="local" + ) + for i0_i1_fused_0_i0_i1_fused_1_0_fused in T.thread_binding( + (n + 31) // 32, thread="blockIdx.y" + ): + for i2_0 in T.thread_binding(86, thread="blockIdx.x"): + for i0_i1_fused_1_1 in T.thread_binding(8, thread="threadIdx.y"): + for i2_1 in T.thread_binding(16, thread="threadIdx.x"): + for i0_i1_fused_1_2_init in range(4): + for i2_2_init in T.vectorized(8): + with T.block("NT_matmul_init"): + v_i0 = T.axis.spatial(1, 0) + v_i1 = T.axis.spatial( + (n + 31) // 32 * 32, + i0_i1_fused_0_i0_i1_fused_1_0_fused * 32 + + i0_i1_fused_1_1 * 4 + + i0_i1_fused_1_2_init, + ) + v_i2 = T.axis.spatial( + 11008, i2_0 * 128 + i2_1 * 8 + i2_2_init + ) + T.reads() + T.writes( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + ) + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] = T.float16(0) + for k_0 in range(128): + for ax0 in range(1): + for ax1 in T.vectorized(8): + with T.block("lv37_local"): + v0 = T.axis.spatial(128, k_0 + ax0) + v1 = T.axis.spatial( + 11008, i2_0 * 128 + i2_1 * 8 + ax1 + ) + T.reads(lv37[v0, v1]) + T.writes(lv37_local[v0, v1]) + lv37_local[v0, v1] = lv37[v0, v1] + for k_1 in range(4): + for ax0 in range(1): + for ax1 in T.vectorized(8): + with T.block("lv36_local"): + v0 = T.axis.spatial(512, k_0 * 4 + k_1 + ax0) + v1 = T.axis.spatial( + 11008, i2_0 * 128 + i2_1 * 8 + ax1 + ) + T.reads(lv36[v0, v1]) + T.writes(lv36_local[v0, v1]) + lv36_local[v0, v1] = lv36[v0, v1] + for k_2 in range(8): + for ax0 in range(1): + for ax1 in T.vectorized(8): + with T.block("decode"): + v_i = T.axis.spatial( + 4096, k_0 * 32 + k_1 * 8 + k_2 + ax0 + ) + v_j = T.axis.spatial( + 11008, i2_0 * 128 + i2_1 * 8 + ax1 + ) + T.reads( + lv36_local[v_i // 8, v_j], + lv37_local[v_i // 32, v_j], + ) + T.writes(decode_local[v_i, v_j]) + decode_local[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv36_local[v_i // 8, v_j], + T.Cast("uint32", v_i % 8) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv37_local[v_i // 32, v_j] + for ax0, ax1 in T.grid(1, 4): + for ax2 in T.vectorized(1): + with T.block("lv45_pad_local"): + v0 = T.axis.spatial(1, ax0) + v1 = T.axis.spatial( + (n + 31) // 32 * 32, + i0_i1_fused_0_i0_i1_fused_1_0_fused * 32 + + i0_i1_fused_1_1 * 4 + + ax1, + ) + v2 = T.axis.spatial( + 4096, k_0 * 32 + k_1 * 8 + k_2 + ax2 + ) + T.reads(lv45[v0, v1, v2]) + T.writes(lv45_pad_local[v0, v1, v2]) + lv45_pad_local[v0, v1, v2] = T.if_then_else( + v1 < n, lv45[v0, v1, v2], T.float16(0) + ) + for i0_i1_fused_1_2 in range(4): + for i2_2 in T.vectorized(8): + with T.block("NT_matmul_update"): + v_i0 = T.axis.spatial(1, 0) + v_i1 = T.axis.spatial( + (n + 31) // 32 * 32, + i0_i1_fused_0_i0_i1_fused_1_0_fused * 32 + + i0_i1_fused_1_1 * 4 + + i0_i1_fused_1_2, + ) + v_i2 = T.axis.spatial( + 11008, i2_0 * 128 + i2_1 * 8 + i2_2 + ) + v_k = T.axis.reduce( + 4096, k_0 * 32 + k_1 * 8 + k_2 + ) + T.reads( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ], + lv45_pad_local[v_i0, v_i1, v_k], + decode_local[v_k, v_i2], + ) + T.writes( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + ) + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] = ( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + + lv45_pad_local[v_i0, v_i1, v_k] + * decode_local[v_k, v_i2] + ) + for ax0, ax1 in T.grid(1, 4): + for ax2 in T.vectorized(8): + with T.block("var_NT_matmul_intermediate_pad_local"): + v0 = T.axis.spatial(1, ax0) + v1 = T.axis.spatial( + (n + 31) // 32 * 32, + i0_i1_fused_0_i0_i1_fused_1_0_fused * 32 + + i0_i1_fused_1_1 * 4 + + ax1, + ) + v2 = T.axis.spatial(11008, i2_0 * 128 + i2_1 * 8 + ax2) + T.reads( + var_NT_matmul_intermediate_pad_local[v0, v1, v2] + ) + T.writes(p_output0_intermediate[v0, v1, v2]) + if v1 < n: + p_output0_intermediate[ + v0, v1, v2 + ] = var_NT_matmul_intermediate_pad_local[ + v0, v1, v2 + ] * T.sigmoid( + var_NT_matmul_intermediate_pad_local[v0, v1, v2] + ) + + +@T.prim_func(private=True) +def fused_decode1_fused_NT_matmul2_multiply( + lv43: T.Buffer((T.int64(512), T.int64(11008)), "uint32"), + lv44: T.Buffer((T.int64(128), T.int64(11008)), "float16"), + p_lv45: T.handle, + p_lv132: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv45 = T.match_buffer(p_lv45, (T.int64(1), n, T.int64(4096)), "float16") + lv132 = T.match_buffer(p_lv132, (T.int64(1), n, T.int64(11008)), "float16") + p_output0_intermediate = T.match_buffer( + p_output0, (T.int64(1), n, T.int64(11008)), "float16" + ) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(4096), T.int64(11008)), "float16") + var_T_transpose_intermediate = T.alloc_buffer( + (T.int64(11008), T.int64(4096)), "float16" + ) + var_NT_matmul_intermediate = T.alloc_buffer( + (T.int64(1), n, T.int64(11008)), "float16" + ) + for i, j in T.grid(T.int64(4096), T.int64(11008)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv43[v_i // T.int64(8), v_j], lv44[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv43[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv44[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(11008), T.int64(4096)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(var_T_transpose_intermediate[v_ax0, v_ax1]) + var_T_transpose_intermediate[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(11008), T.int64(4096)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv45[v_i0, v_i1, v_k], var_T_transpose_intermediate[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + + lv45[v_i0, v_i1, v_k] * var_T_transpose_intermediate[v_i2, v_k] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + lv132[v_ax0, v_ax1, v_ax2], + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], + ) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = ( + lv132[v_ax0, v_ax1, v_ax2] + * var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + ) + + +@T.prim_func(private=True) +def fused_decode1_fused_NT_matmul2_multiply_after( + lv43: T.Buffer((512, 11008), "uint32"), + lv44: T.Buffer((128, 11008), "float16"), + p_lv45: T.handle, + p_lv132: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int32() + lv45 = T.match_buffer(p_lv45, (1, n, 4096), "float16") + lv132 = T.match_buffer(p_lv132, (1, n, 11008), "float16") + p_output0_intermediate = T.match_buffer(p_output0, (1, n, 11008), "float16") + # with T.block("root"): + decode_local = T.alloc_buffer((4096, 11008), "float16", scope="local") + lv43_local = T.alloc_buffer((512, 11008), "uint32", scope="local") + lv44_local = T.alloc_buffer((128, 11008), "float16", scope="local") + lv45_pad_local = T.alloc_buffer( + (1, (n + 31) // 32 * 32, 4096), "float16", scope="local" + ) + var_NT_matmul_intermediate_pad_local = T.alloc_buffer( + (1, (n + 31) // 32 * 32, 11008), "float16", scope="local" + ) + for i0_i1_fused_0_i0_i1_fused_1_0_fused in T.thread_binding( + (n + 31) // 32, thread="blockIdx.y" + ): + for i2_0 in T.thread_binding(86, thread="blockIdx.x"): + for i0_i1_fused_1_1 in T.thread_binding(8, thread="threadIdx.y"): + for i2_1 in T.thread_binding(16, thread="threadIdx.x"): + for i0_i1_fused_1_2_init in range(4): + for i2_2_init in T.vectorized(8): + with T.block("NT_matmul_init"): + v_i0 = T.axis.spatial(1, 0) + v_i1 = T.axis.spatial( + (n + 31) // 32 * 32, + i0_i1_fused_0_i0_i1_fused_1_0_fused * 32 + + i0_i1_fused_1_1 * 4 + + i0_i1_fused_1_2_init, + ) + v_i2 = T.axis.spatial( + 11008, i2_0 * 128 + i2_1 * 8 + i2_2_init + ) + T.reads() + T.writes( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + ) + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] = T.float16(0) + for k_0 in range(128): + for ax0 in range(1): + for ax1 in T.vectorized(8): + with T.block("lv44_local"): + v0 = T.axis.spatial(128, k_0 + ax0) + v1 = T.axis.spatial( + 11008, i2_0 * 128 + i2_1 * 8 + ax1 + ) + T.reads(lv44[v0, v1]) + T.writes(lv44_local[v0, v1]) + lv44_local[v0, v1] = lv44[v0, v1] + for k_1 in range(4): + for ax0 in range(1): + for ax1 in T.vectorized(8): + with T.block("lv43_local"): + v0 = T.axis.spatial(512, k_0 * 4 + k_1 + ax0) + v1 = T.axis.spatial( + 11008, i2_0 * 128 + i2_1 * 8 + ax1 + ) + T.reads(lv43[v0, v1]) + T.writes(lv43_local[v0, v1]) + lv43_local[v0, v1] = lv43[v0, v1] + for k_2 in range(8): + for ax0 in range(1): + for ax1 in T.vectorized(8): + with T.block("decode"): + v_i = T.axis.spatial( + 4096, k_0 * 32 + k_1 * 8 + k_2 + ax0 + ) + v_j = T.axis.spatial( + 11008, i2_0 * 128 + i2_1 * 8 + ax1 + ) + T.reads( + lv43_local[v_i // 8, v_j], + lv44_local[v_i // 32, v_j], + ) + T.writes(decode_local[v_i, v_j]) + decode_local[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv43_local[v_i // 8, v_j], + T.Cast("uint32", v_i % 8) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv44_local[v_i // 32, v_j] + for ax0, ax1 in T.grid(1, 4): + for ax2 in T.vectorized(1): + with T.block("lv45_pad_local"): + v0 = T.axis.spatial(1, ax0) + v1 = T.axis.spatial( + (n + 31) // 32 * 32, + i0_i1_fused_0_i0_i1_fused_1_0_fused * 32 + + i0_i1_fused_1_1 * 4 + + ax1, + ) + v2 = T.axis.spatial( + 4096, k_0 * 32 + k_1 * 8 + k_2 + ax2 + ) + T.reads(lv45[v0, v1, v2]) + T.writes(lv45_pad_local[v0, v1, v2]) + lv45_pad_local[v0, v1, v2] = T.if_then_else( + v1 < n, lv45[v0, v1, v2], T.float16(0) + ) + for i0_i1_fused_1_2 in range(4): + for i2_2 in T.vectorized(8): + with T.block("NT_matmul_update"): + v_i0 = T.axis.spatial(1, 0) + v_i1 = T.axis.spatial( + (n + 31) // 32 * 32, + i0_i1_fused_0_i0_i1_fused_1_0_fused * 32 + + i0_i1_fused_1_1 * 4 + + i0_i1_fused_1_2, + ) + v_i2 = T.axis.spatial( + 11008, i2_0 * 128 + i2_1 * 8 + i2_2 + ) + v_k = T.axis.reduce( + 4096, k_0 * 32 + k_1 * 8 + k_2 + ) + T.reads( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ], + lv45_pad_local[v_i0, v_i1, v_k], + decode_local[v_k, v_i2], + ) + T.writes( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + ) + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] = ( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + + lv45_pad_local[v_i0, v_i1, v_k] + * decode_local[v_k, v_i2] + ) + for ax0, ax1 in T.grid(1, 4): + for ax2 in T.vectorized(8): + with T.block("var_NT_matmul_intermediate_pad_local"): + v0 = T.axis.spatial(1, ax0) + v1 = T.axis.spatial( + (n + 31) // 32 * 32, + i0_i1_fused_0_i0_i1_fused_1_0_fused * 32 + + i0_i1_fused_1_1 * 4 + + ax1, + ) + v2 = T.axis.spatial(11008, i2_0 * 128 + i2_1 * 8 + ax2) + T.reads( + lv132[v0, v1, v2], + var_NT_matmul_intermediate_pad_local[v0, v1, v2], + ) + T.writes(p_output0_intermediate[v0, v1, v2]) + if v1 < n: + p_output0_intermediate[v0, v1, v2] = ( + lv132[v0, v1, v2] + * var_NT_matmul_intermediate_pad_local[ + v0, v1, v2 + ] + ) + + +@T.prim_func(private=True) +def fused_decode_fused_NT_matmul_add( + lv29: T.Buffer((T.int64(512), T.int64(4096)), "uint32"), + lv30: T.Buffer((T.int64(128), T.int64(4096)), "float16"), + p_lv41: T.handle, + p_lv2: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv41 = T.match_buffer(p_lv41, (T.int64(1), n, T.int64(4096)), "float16") + lv2 = T.match_buffer(p_lv2, (T.int64(1), n, T.int64(4096)), "float16") + p_output0_intermediate = T.match_buffer( + p_output0, (T.int64(1), n, T.int64(4096)), "float16" + ) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(4096), T.int64(4096)), "float16") + var_T_transpose_intermediate = T.alloc_buffer( + (T.int64(4096), T.int64(4096)), "float16" + ) + var_NT_matmul_intermediate = T.alloc_buffer( + (T.int64(1), n, T.int64(4096)), "float16" + ) + for i, j in T.grid(T.int64(4096), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv29[v_i // T.int64(8), v_j], lv30[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv29[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv30[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(4096), T.int64(4096)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(var_T_transpose_intermediate[v_ax0, v_ax1]) + var_T_transpose_intermediate[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(4096), T.int64(4096)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv41[v_i0, v_i1, v_k], var_T_transpose_intermediate[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + + lv41[v_i0, v_i1, v_k] * var_T_transpose_intermediate[v_i2, v_k] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + lv2[v_ax0, v_ax1, v_ax2], + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], + ) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = ( + lv2[v_ax0, v_ax1, v_ax2] + + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + ) + + +@T.prim_func(private=True) +def fused_decode_fused_NT_matmul_add_after( + lv8: T.Buffer((T.int64(512), T.int64(4096)), "uint32"), + lv9: T.Buffer((T.int64(128), T.int64(4096)), "float16"), + p_lv41: T.handle, + p_lv2: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + lv6 = T.match_buffer(p_lv41, (1, n, 4096), "float16") + lv2 = T.match_buffer(p_lv2, (1, n, 4096), "float16") + var_NT_matmul_intermediate = T.match_buffer(p_output0, (1, n, 4096), "float16") + + var_matmul_intermediate_local = T.alloc_buffer( + (T.int64(1), ((n+7)//8) * 8, T.int64(4096)), "float16", scope="local" + ) + var_matmul_intermediate_local_batch = T.alloc_buffer( + (T.int64(1), ((n+7)//8) * 8, T.int64(4096)), "float16", scope="local" + ) + lv8_local = T.alloc_buffer((T.int64(512), T.int64(4096)), "uint32", scope="local") + lv9_local = T.alloc_buffer( + (T.int64(128), T.int64(4096)), "float16", scope="local" + ) + #lv6_shared = T.alloc_buffer( + # (T.int64(1), T.int64(1), T.int64(4096)), "float16", scope="shared" + #) + for i0_i1_i2_fused_n in T.thread_binding(((n+7)//8), thread="blockIdx.y"): + for i0_i1_i2_fused_0 in T.thread_binding(T.int64(32), thread="blockIdx.x"): + for i0_i1_i2_fused_1 in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + with T.block("n_check"): + T.where((i0_i1_i2_fused_n * T.int64(8) + ax2_y) < n) + for i0_i1_i2_fused_2_init in T.vectorized(T.int64(4)): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + i0_i1_i2_fused_2_init + ) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float16(0) + for k_1 in range(T.int64(128)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("matmul_init_local"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2k = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads() + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = T.float16(0) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv9_local"): + v0 = T.axis.spatial( + T.int64(128), k_1 + ) + v1 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv9[v0, v1]) + T.writes(lv9_local[v0, v1]) + lv9_local[v0, v1] = lv9[v0, v1] + for k_2 in range(T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv8_local"): + v0 = T.axis.spatial( + T.int64(512), + k_1 * T.int64(4) + + k_2 + + ax0, + ) + v1 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv8[v0, v1]) + T.writes(lv8_local[v0, v1]) + lv8_local[v0, v1] = lv8[v0, v1] + for k_3 in range(T.int64(8)): + for i0_i1_i2_fused_2 in T.vectorized(T.int64(4)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_k = T.axis.reduce( + T.int64(4096), + k_1 * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + T.reads( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ], + lv6[v_i0, v_i1, v_k], + lv8_local[v_k // T.int64(8), v_i2], + ) + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ] = var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ] + lv6[ + v_i0, v_i1, v_k + ] * ( + ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv8_local[ + v_k // T.int64(8), v_i2 + ], + T.Cast( + "uint32", + v_k % T.int64(8), + ) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) + ) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("multiple_scale"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + v0 = T.axis.spatial( + T.int64(128), + k_1 + ) + v1 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads( + lv9_local[v0, v1], + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ], + ) + T.writes( + var_matmul_intermediate_local[v_i0, v_i1, v_i2] + ) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = ( + var_matmul_intermediate_local[v_i0, v_i1, v_i2] + + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ] + * lv9_local[v0, v1] + ) + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_local"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2 = T.axis.spatial( + T.int64(4096), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax2, + ) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv2[v_i0, v_i1, v_i2]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv2[v_i0, v_i1, v_i2] + + +@T.prim_func(private=True) +def fused_decode4_fused_matmul6_add4( + lv1363: T.Buffer((T.int64(320), T.int64(2560)), "uint32"), + lv1364: T.Buffer((T.int64(80), T.int64(2560)), "float16"), + lv2067: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16"), + linear_bias192: T.Buffer((T.int64(2560),), "float16"), + p_output0_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(2560)), "float16" + ), +): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(2560), T.int64(2560)), "float16") + var_matmul_intermediate = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(2560)), "float16" + ) + for i, j in T.grid(T.int64(2560), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1363[v_i // T.int64(8), v_j], lv1364[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1363[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv1364[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(2560), T.int64(2560)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2067[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_matmul_intermediate[v_i0, v_i1, v_i2] + + lv2067[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias192[v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias192[v_ax2] + ) + + +def sch_fused_decode4_fused_matmul6_add4(func): + sch = tvm.tir.Schedule(func) + b0 = sch.get_block(name="decode", func_name="main") + b1 = sch.get_block(name="matmul", func_name="main") + l2, l3, l4, l5 = sch.get_loops(block=b1) + l6 = sch.fuse(l2, l3, l4, preserve_unit_iters=True) + v7, v8, v9 = sch.sample_perfect_tile( + loop=l6, n=3, max_innermost_factor=4, decision=[10, 256, 1] + ) + l10, l11, l12 = sch.split(loop=l6, factors=[v7, v8, v9], preserve_unit_iters=True) + v13, v14, v15 = sch.sample_perfect_tile( + loop=l5, n=3, max_innermost_factor=8, decision=[160, 8, 2] + ) + l16, l17, l18 = sch.split( + loop=l5, factors=[v13, v14, v15], preserve_unit_iters=True + ) + sch.reorder(l10, l11, l16, l17, l18, l12) + sch.bind(loop=l10, thread_axis="blockIdx.x") + sch.bind(loop=l11, thread_axis="threadIdx.x") + sch.compute_inline(block=b0) + b19 = sch.cache_write(block=b1, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b19, loop=l11, preserve_unit_loops=True, index=-1) + b20 = sch.cache_read(block=b1, read_buffer_index=0, storage_scope="shared") + sch.compute_at(block=b20, loop=l11, preserve_unit_loops=True, index=-1) + v21 = sch.sample_categorical( + candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=3 + ) + sch.annotate( + block_or_loop=b20, ann_key="meta_schedule.cooperative_fetch", ann_val=v21 + ) + l22, l23, l24, l25, l26 = sch.get_loops(block=b19) + sch.vectorize(loop=l26) + sch.vectorize(loop=l12) + b27 = sch.decompose_reduction(block=b1, loop=l16) + b28 = sch.get_block(name="T_add", func_name="main") + sch.reverse_compute_inline(block=b28) + sch.enter_postproc() + sch.unannotate(block_or_loop=b20, ann_key="meta_schedule.cooperative_fetch") + l29, l30, l31, l32, l33 = sch.get_loops(block=b20) + l34, l35, l36 = sch.split( + loop=l33, factors=[None, 256, 8], preserve_unit_iters=True + ) + sch.vectorize(loop=l36) + sch.bind(loop=l35, thread_axis="threadIdx.x") + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func(private=True) +def fused_decode6_fused_matmul9_add7_cast8_cast12_add5( + lv1393: T.Buffer((T.int64(1280), T.int64(2560)), "uint32"), + lv1394: T.Buffer((T.int64(320), T.int64(2560)), "float16"), + lv2121: T.Buffer((T.int64(1), T.int64(1), T.int64(10240)), "float16"), + linear_bias197: T.Buffer((T.int64(2560),), "float32"), + lv329: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16"), + p_output0_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(2560)), "float16" + ), +): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(10240), T.int64(2560)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560))) + var_compute_intermediate = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(2560)), "float16" + ) + var_compute_intermediate_1 = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(2560)), "float16" + ) + for i, j in T.grid(T.int64(10240), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1393[v_i // T.int64(8), v_j], lv1394[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1393[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv1394[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(2560), T.int64(10240)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2121[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[ + v_i0, v_i1, v_i2 + ] + T.Cast("float32", lv2121[v_i0, v_i1, v_k]) * T.Cast( + "float32", var_decode_intermediate[v_k, v_i2] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias197[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias197[v_ax2] + ) + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = T.Cast( + "float16", var_T_add_intermediate[v_i0, v_i1, v_i2] + ) + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("compute_1"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_compute_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate_1[v_i0, v_i1, v_i2]) + var_compute_intermediate_1[v_i0, v_i1, v_i2] = var_compute_intermediate[ + v_i0, v_i1, v_i2 + ] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + var_compute_intermediate_1[v_ax0, v_ax1, v_ax2], + lv329[v_ax0, v_ax1, v_ax2], + ) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_compute_intermediate_1[v_ax0, v_ax1, v_ax2] + + lv329[v_ax0, v_ax1, v_ax2] + ) + + +def sch_fused_decode6_fused_matmul9_add7_cast8_cast12_add5(func): + sch = tvm.tir.Schedule(func) + b0 = sch.get_block(name="decode", func_name="main") + b1 = sch.get_block(name="matmul", func_name="main") + l2, l3, l4, l5 = sch.get_loops(block=b1) + l6 = sch.fuse(l2, l3, l4, preserve_unit_iters=True) + v7, v8, v9 = sch.sample_perfect_tile( + loop=l6, n=3, max_innermost_factor=4, decision=[10, 256, 1] + ) + l10, l11, l12 = sch.split(loop=l6, factors=[v7, v8, v9], preserve_unit_iters=True) + v13, v14, v15 = sch.sample_perfect_tile( + loop=l5, n=3, max_innermost_factor=8, decision=[640, 2, 8] + ) + l16, l17, l18 = sch.split( + loop=l5, factors=[v13, v14, v15], preserve_unit_iters=True + ) + sch.reorder(l10, l11, l16, l17, l18, l12) + sch.bind(loop=l10, thread_axis="blockIdx.x") + sch.bind(loop=l11, thread_axis="threadIdx.x") + sch.compute_inline(block=b0) + b19 = sch.cache_write(block=b1, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b19, loop=l11, preserve_unit_loops=True, index=-1) + b20 = sch.cache_read(block=b1, read_buffer_index=0, storage_scope="shared") + sch.compute_at(block=b20, loop=l11, preserve_unit_loops=True, index=-1) + v21 = sch.sample_categorical( + candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=3 + ) + sch.annotate( + block_or_loop=b20, ann_key="meta_schedule.cooperative_fetch", ann_val=v21 + ) + l22, l23, l24, l25, l26 = sch.get_loops(block=b19) + sch.vectorize(loop=l26) + sch.vectorize(loop=l12) + b27 = sch.decompose_reduction(block=b1, loop=l16) + b28 = sch.get_block(name="T_add", func_name="main") + bb1 = sch.get_block(name="compute", func_name="main") + bb2 = sch.get_block(name="compute_1", func_name="main") + bb3 = sch.get_block(name="T_add_1", func_name="main") + sch.compute_inline(block=b28) + sch.compute_inline(block=bb1) + sch.compute_inline(block=bb2) + sch.reverse_compute_inline(block=bb3) + sch.enter_postproc() + sch.unannotate(block_or_loop=b20, ann_key="meta_schedule.cooperative_fetch") + l29, l30, l31, l32, l33 = sch.get_loops(block=b20) + l34, l35, l36 = sch.split( + loop=l33, factors=[None, 256, 8], preserve_unit_iters=True + ) + sch.vectorize(loop=l36) + sch.bind(loop=l35, thread_axis="threadIdx.x") + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func(private=True) +def fused_decode5_fused_matmul8_add6_gelu1_cast11( + lv1387: T.Buffer((T.int64(320), T.int64(10240)), "uint32"), + lv1388: T.Buffer((T.int64(80), T.int64(10240)), "float16"), + lv2115: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16"), + linear_bias196: T.Buffer((T.int64(10240),), "float32"), + p_output0_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(10240)), "float16" + ), +): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(2560), T.int64(10240)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + T_multiply = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + compute = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + T_multiply_1 = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + T_add = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + var_T_multiply_intermediate = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(10240)) + ) + for i, j in T.grid(T.int64(2560), T.int64(10240)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1387[v_i // T.int64(8), v_j], lv1388[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1387[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv1388[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(10240), T.int64(2560)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2115[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[ + v_i0, v_i1, v_i2 + ] + T.Cast("float32", lv2115[v_i0, v_i1, v_k]) * T.Cast( + "float32", var_decode_intermediate[v_k, v_i2] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias196[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias196[v_ax2] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply[v_ax0, v_ax1, v_ax2]) + T_multiply[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[ + v_ax0, v_ax1, v_ax2 + ] * T.float32(0.70710678118654757) + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(T_multiply[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.erf(T_multiply[v_i0, v_i1, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("T_multiply_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(compute[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T_multiply_1[v_ax0, v_ax1, v_ax2] = compute[ + v_ax0, v_ax1, v_ax2 + ] * T.float32(0.5) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T.writes(T_add[v_ax0, v_ax1, v_ax2]) + T_add[v_ax0, v_ax1, v_ax2] = ( + T.float32(0.5) + T_multiply_1[v_ax0, v_ax1, v_ax2] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("T_multiply_2"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + var_T_add_intermediate[v_ax0, v_ax1, v_ax2], T_add[v_ax0, v_ax1, v_ax2] + ) + T.writes(var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] * T_add[v_ax0, v_ax1, v_ax2] + ) + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("compute_1"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_multiply_intermediate[v_i0, v_i1, v_i2]) + T.writes(p_output0_intermediate[v_i0, v_i1, v_i2]) + p_output0_intermediate[v_i0, v_i1, v_i2] = T.Cast( + "float16", var_T_multiply_intermediate[v_i0, v_i1, v_i2] + ) + + +def sch_fused_decode5_fused_matmul8_add6_gelu1_cast11(func): + sch = tvm.tir.Schedule(func) + b0 = sch.get_block(name="decode", func_name="main") + b1 = sch.get_block(name="matmul", func_name="main") + l2, l3, l4, l5 = sch.get_loops(block=b1) + l6 = sch.fuse(l2, l3, l4, preserve_unit_iters=True) + v7, v8, v9 = sch.sample_perfect_tile( + loop=l6, n=3, max_innermost_factor=4, decision=[10, 256, 4] + ) + l10, l11, l12 = sch.split(loop=l6, factors=[v7, v8, v9], preserve_unit_iters=True) + v13, v14, v15 = sch.sample_perfect_tile( + loop=l5, n=3, max_innermost_factor=8, decision=[80, 4, 8] + ) + l16, l17, l18 = sch.split( + loop=l5, factors=[v13, v14, v15], preserve_unit_iters=True + ) + sch.reorder(l10, l11, l16, l17, l18, l12) + sch.bind(loop=l10, thread_axis="blockIdx.x") + sch.bind(loop=l11, thread_axis="threadIdx.x") + sch.compute_inline(block=b0) + b19 = sch.cache_write(block=b1, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b19, loop=l11, preserve_unit_loops=True, index=-1) + b20 = sch.cache_read(block=b1, read_buffer_index=0, storage_scope="shared") + sch.compute_at(block=b20, loop=l11, preserve_unit_loops=True, index=-1) + v21 = sch.sample_categorical( + candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=3 + ) + sch.annotate( + block_or_loop=b20, ann_key="meta_schedule.cooperative_fetch", ann_val=v21 + ) + l22, l23, l24, l25, l26 = sch.get_loops(block=b19) + sch.vectorize(loop=l26) + sch.vectorize(loop=l12) + b27 = sch.decompose_reduction(block=b1, loop=l16) + b28 = sch.get_block(name="T_add", func_name="main") + bb1 = sch.get_block(name="T_multiply", func_name="main") + bb2 = sch.get_block(name="compute", func_name="main") + bb3 = sch.get_block(name="T_multiply_1", func_name="main") + bb4 = sch.get_block(name="T_add_1", func_name="main") + bb5 = sch.get_block(name="T_multiply_2", func_name="main") + bb6 = sch.get_block(name="compute_1", func_name="main") + sch.compute_inline(block=b28) + sch.compute_inline(block=bb1) + sch.compute_inline(block=bb2) + sch.compute_inline(block=bb3) + sch.compute_inline(block=bb4) + sch.compute_inline(block=bb5) + sch.reverse_compute_inline(block=bb6) + sch.enter_postproc() + sch.unannotate(block_or_loop=b20, ann_key="meta_schedule.cooperative_fetch") + l29, l30, l31, l32, l33 = sch.get_loops(block=b20) + l34, l35, l36 = sch.split( + loop=l33, factors=[None, 256, 8], preserve_unit_iters=True + ) + sch.vectorize(loop=l36) + sch.bind(loop=l35, thread_axis="threadIdx.x") + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func(private=True) +def fused_decode4_fused_matmul6_add4_add5( + lv1381: T.Buffer((T.int64(320), T.int64(2560)), "uint32"), + lv1382: T.Buffer((T.int64(80), T.int64(2560)), "float16"), + lv328: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16"), + linear_bias195: T.Buffer((T.int64(2560),), "float16"), + lv2062: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16"), + p_output0_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(2560)), "float16" + ), +): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(2560), T.int64(2560)), "float16") + var_matmul_intermediate = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(2560)), "float16" + ) + var_T_add_intermediate = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(2560)), "float16" + ) + for i, j in T.grid(T.int64(2560), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1381[v_i // T.int64(8), v_j], lv1382[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1381[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv1382[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(2560), T.int64(2560)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv328[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_matmul_intermediate[v_i0, v_i1, v_i2] + + lv328[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias195[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias195[v_ax2] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + var_T_add_intermediate[v_ax0, v_ax1, v_ax2], lv2062[v_ax0, v_ax1, v_ax2] + ) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] + + lv2062[v_ax0, v_ax1, v_ax2] + ) + + +def sch_fused_decode4_fused_matmul6_add4_add5(func): + sch = tvm.tir.Schedule(func) + b0 = sch.get_block(name="decode", func_name="main") + b1 = sch.get_block(name="matmul", func_name="main") + l2, l3, l4, l5 = sch.get_loops(block=b1) + l6 = sch.fuse(l2, l3, l4, preserve_unit_iters=True) + v7, v8, v9 = sch.sample_perfect_tile( + loop=l6, n=3, max_innermost_factor=4, decision=[10, 256, 1] + ) + l10, l11, l12 = sch.split(loop=l6, factors=[v7, v8, v9], preserve_unit_iters=True) + v13, v14, v15 = sch.sample_perfect_tile( + loop=l5, n=3, max_innermost_factor=8, decision=[160, 8, 2] + ) + l16, l17, l18 = sch.split( + loop=l5, factors=[v13, v14, v15], preserve_unit_iters=True + ) + sch.reorder(l10, l11, l16, l17, l18, l12) + sch.bind(loop=l10, thread_axis="blockIdx.x") + sch.bind(loop=l11, thread_axis="threadIdx.x") + sch.compute_inline(block=b0) + b19 = sch.cache_write(block=b1, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b19, loop=l11, preserve_unit_loops=True, index=-1) + b20 = sch.cache_read(block=b1, read_buffer_index=0, storage_scope="shared") + sch.compute_at(block=b20, loop=l11, preserve_unit_loops=True, index=-1) + v21 = sch.sample_categorical( + candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=3 + ) + sch.annotate( + block_or_loop=b20, ann_key="meta_schedule.cooperative_fetch", ann_val=v21 + ) + l22, l23, l24, l25, l26 = sch.get_loops(block=b19) + sch.vectorize(loop=l26) + sch.vectorize(loop=l12) + b27 = sch.decompose_reduction(block=b1, loop=l16) + b28 = sch.get_block(name="T_add", func_name="main") + bb4 = sch.get_block(name="T_add_1", func_name="main") + sch.compute_inline(block=b28) + sch.reverse_compute_inline(block=bb4) + sch.enter_postproc() + sch.unannotate(block_or_loop=b20, ann_key="meta_schedule.cooperative_fetch") + l29, l30, l31, l32, l33 = sch.get_loops(block=b20) + l34, l35, l36 = sch.split( + loop=l33, factors=[None, 256, 8], preserve_unit_iters=True + ) + sch.vectorize(loop=l36) + sch.bind(loop=l35, thread_axis="threadIdx.x") + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func(private=True) +def fused_decode3_matmul3( + lv2515: T.Buffer((T.int64(320), T.int64(50432)), "uint32"), + lv2516: T.Buffer((T.int64(80), T.int64(50432)), "float32"), + lv705: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32"), + var_matmul_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(50432)), "float32" + ), +): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(2560), T.int64(50432))) + for i, j in T.grid(T.int64(2560), T.int64(50432)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv2515[v_i // T.int64(8), v_j], lv2516[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = ( + T.Cast( + "float32", + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv2515[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7), + ) + * lv2516[v_i // T.int64(32), v_j] + ) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(50432), T.int64(2560)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv705[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_matmul_intermediate[v_i0, v_i1, v_i2] + + lv705[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + ) + + +def sch_fused_decode3_matmul3(func): + sch = tvm.tir.Schedule(func) + b0 = sch.get_block(name="decode", func_name="main") + b1 = sch.get_block(name="matmul", func_name="main") + l2, l3, l4, l5 = sch.get_loops(block=b1) + l6 = sch.fuse(l2, l3, l4, preserve_unit_iters=True) + v7, v8, v9 = sch.sample_perfect_tile( + loop=l6, n=3, max_innermost_factor=4, decision=[197, 128, 2] + ) + l10, l11, l12 = sch.split(loop=l6, factors=[v7, v8, v9], preserve_unit_iters=True) + v13, v14, v15 = sch.sample_perfect_tile( + loop=l5, n=3, max_innermost_factor=8, decision=[80, 4, 8] + ) + l16, l17, l18 = sch.split( + loop=l5, factors=[v13, v14, v15], preserve_unit_iters=True + ) + sch.reorder(l10, l11, l16, l17, l18, l12) + sch.bind(loop=l10, thread_axis="blockIdx.x") + sch.bind(loop=l11, thread_axis="threadIdx.x") + sch.compute_inline(block=b0) + b19 = sch.cache_write(block=b1, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b19, loop=l11, preserve_unit_loops=True, index=-1) + b20 = sch.cache_read(block=b1, read_buffer_index=0, storage_scope="shared") + sch.compute_at(block=b20, loop=l11, preserve_unit_loops=True, index=-1) + v21 = sch.sample_categorical( + candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=3 + ) + sch.annotate( + block_or_loop=b20, ann_key="meta_schedule.cooperative_fetch", ann_val=v21 + ) + l22, l23, l24, l25, l26 = sch.get_loops(block=b19) + sch.vectorize(loop=l26) + sch.vectorize(loop=l12) + b27 = sch.decompose_reduction(block=b1, loop=l16) + sch.enter_postproc() + sch.unannotate(block_or_loop=b20, ann_key="meta_schedule.cooperative_fetch") + l29, l30, l31, l32, l33 = sch.get_loops(block=b20) + l34, l35, l36 = sch.split( + loop=l33, factors=[None, 128, 8], preserve_unit_iters=True + ) + sch.vectorize(loop=l36) + sch.bind(loop=l35, thread_axis="threadIdx.x") + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func(private=True) +def fused_decode6_fused_matmul9_add7_cast8_cast12_add5_cast7( + lv2509: T.Buffer((T.int64(1280), T.int64(2560)), "uint32"), + lv2510: T.Buffer((T.int64(320), T.int64(2560)), "float16"), + lv4105: T.Buffer((T.int64(1), T.int64(1), T.int64(10240)), "float16"), + linear_bias383: T.Buffer((T.int64(2560),), "float32"), + lv701: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16"), + p_output0_intermediate: T.Buffer( + (T.int64(1), T.int64(1), T.int64(2560)), "float32" + ), +): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(10240), T.int64(2560)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560))) + var_compute_intermediate = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(2560)), "float16" + ) + var_compute_intermediate_1 = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(2560)), "float16" + ) + var_T_add_intermediate_1 = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(2560)), "float16" + ) + for i, j in T.grid(T.int64(10240), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv2509[v_i // T.int64(8), v_j], lv2510[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv2509[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv2510[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(2560), T.int64(10240)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv4105[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[ + v_i0, v_i1, v_i2 + ] + T.Cast("float32", lv4105[v_i0, v_i1, v_k]) * T.Cast( + "float32", var_decode_intermediate[v_k, v_i2] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias383[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias383[v_ax2] + ) + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = T.Cast( + "float16", var_T_add_intermediate[v_i0, v_i1, v_i2] + ) + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("compute_1"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_compute_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate_1[v_i0, v_i1, v_i2]) + var_compute_intermediate_1[v_i0, v_i1, v_i2] = var_compute_intermediate[ + v_i0, v_i1, v_i2 + ] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + var_compute_intermediate_1[v_ax0, v_ax1, v_ax2], + lv701[v_ax0, v_ax1, v_ax2], + ) + T.writes(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] = ( + var_compute_intermediate_1[v_ax0, v_ax1, v_ax2] + + lv701[v_ax0, v_ax1, v_ax2] + ) + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("compute_2"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate_1[v_i0, v_i1, v_i2]) + T.writes(p_output0_intermediate[v_i0, v_i1, v_i2]) + p_output0_intermediate[v_i0, v_i1, v_i2] = T.Cast( + "float32", var_T_add_intermediate_1[v_i0, v_i1, v_i2] + ) + + +def sch_fused_decode6_fused_matmul9_add7_cast8_cast12_add5_cast7(func): + sch = tvm.tir.Schedule(func) + b0 = sch.get_block(name="decode", func_name="main") + b1 = sch.get_block(name="matmul", func_name="main") + l2, l3, l4, l5 = sch.get_loops(block=b1) + l6 = sch.fuse(l2, l3, l4, preserve_unit_iters=True) + v7, v8, v9 = sch.sample_perfect_tile( + loop=l6, n=3, max_innermost_factor=4, decision=[5, 256, 2] + ) + l10, l11, l12 = sch.split(loop=l6, factors=[v7, v8, v9], preserve_unit_iters=True) + v13, v14, v15 = sch.sample_perfect_tile( + loop=l5, n=3, max_innermost_factor=8, decision=[320, 4, 8] + ) + l16, l17, l18 = sch.split( + loop=l5, factors=[v13, v14, v15], preserve_unit_iters=True + ) + sch.reorder(l10, l11, l16, l17, l18, l12) + sch.bind(loop=l10, thread_axis="blockIdx.x") + sch.bind(loop=l11, thread_axis="threadIdx.x") + sch.compute_inline(block=b0) + b19 = sch.cache_write(block=b1, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b19, loop=l11, preserve_unit_loops=True, index=-1) + b20 = sch.cache_read(block=b1, read_buffer_index=0, storage_scope="shared") + sch.compute_at(block=b20, loop=l11, preserve_unit_loops=True, index=-1) + v21 = sch.sample_categorical( + candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=3 + ) + sch.annotate( + block_or_loop=b20, ann_key="meta_schedule.cooperative_fetch", ann_val=v21 + ) + l22, l23, l24, l25, l26 = sch.get_loops(block=b19) + sch.vectorize(loop=l26) + sch.vectorize(loop=l12) + b27 = sch.decompose_reduction(block=b1, loop=l16) + b28 = sch.get_block(name="T_add", func_name="main") + bb1 = sch.get_block(name="compute", func_name="main") + bb2 = sch.get_block(name="compute_1", func_name="main") + bb3 = sch.get_block(name="T_add_1", func_name="main") + bb4 = sch.get_block(name="compute_2", func_name="main") + sch.compute_inline(block=b28) + sch.compute_inline(block=bb1) + sch.compute_inline(block=bb2) + sch.compute_inline(block=bb3) + sch.reverse_compute_inline(block=bb4) + sch.enter_postproc() + sch.unannotate(block_or_loop=b20, ann_key="meta_schedule.cooperative_fetch") + l29, l30, l31, l32, l33 = sch.get_loops(block=b20) + l34, l35, l36 = sch.split( + loop=l33, factors=[None, 256, 8], preserve_unit_iters=True + ) + sch.vectorize(loop=l36) + sch.bind(loop=l35, thread_axis="threadIdx.x") + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func(private=True) +def fused_decode2_fused_NT_matmul3_add6_gelu1_cast11( + lv36: T.Buffer((T.int64(320), T.int64(10240)), "uint32"), + lv37: T.Buffer((T.int64(80), T.int64(10240)), "float16"), + p_lv57: T.handle, + linear_bias4: T.Buffer((T.int64(10240),), "float32"), + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv57 = T.match_buffer(p_lv57, (T.int64(1), n, T.int64(2560)), "float16") + p_output0_intermediate = T.match_buffer( + p_output0, (T.int64(1), n, T.int64(10240)), "float16" + ) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(2560), T.int64(10240)), "float16") + var_T_transpose_intermediate = T.alloc_buffer( + (T.int64(10240), T.int64(2560)), "float16" + ) + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + T_multiply = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + compute = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + T_multiply_1 = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + T_add = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + var_T_multiply_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + for i, j in T.grid(T.int64(2560), T.int64(10240)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv36[v_i // T.int64(8), v_j], lv37[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv36[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv37[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(10240), T.int64(2560)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(var_T_transpose_intermediate[v_ax0, v_ax1]) + var_T_transpose_intermediate[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(10240), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv57[v_i0, v_i1, v_k], var_T_transpose_intermediate[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[ + v_i0, v_i1, v_i2 + ] + T.Cast("float32", lv57[v_i0, v_i1, v_k]) * T.Cast( + "float32", var_T_transpose_intermediate[v_i2, v_k] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias4[v_ax2] + ) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias4[v_ax2] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply[v_ax0, v_ax1, v_ax2]) + T_multiply[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[ + v_ax0, v_ax1, v_ax2 + ] * T.float32(0.70710678118654757) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(T_multiply[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.erf(T_multiply[v_i0, v_i1, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(compute[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T_multiply_1[v_ax0, v_ax1, v_ax2] = compute[ + v_ax0, v_ax1, v_ax2 + ] * T.float32(0.5) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T.writes(T_add[v_ax0, v_ax1, v_ax2]) + T_add[v_ax0, v_ax1, v_ax2] = ( + T.float32(0.5) + T_multiply_1[v_ax0, v_ax1, v_ax2] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply_2"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + var_T_add_intermediate[v_ax0, v_ax1, v_ax2], T_add[v_ax0, v_ax1, v_ax2] + ) + T.writes(var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] * T_add[v_ax0, v_ax1, v_ax2] + ) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("compute_1"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_multiply_intermediate[v_i0, v_i1, v_i2]) + T.writes(p_output0_intermediate[v_i0, v_i1, v_i2]) + p_output0_intermediate[v_i0, v_i1, v_i2] = T.Cast( + "float16", var_T_multiply_intermediate[v_i0, v_i1, v_i2] + ) + + +@T.prim_func(private=True) +def fused_decode2_fused_NT_matmul3_add6_gelu1_cast11_after( + lv36: T.Buffer((T.int64(320), T.int64(10240)), "uint32"), + lv37: T.Buffer((T.int64(80), T.int64(10240)), "float16"), + p_lv57: T.handle, + linear_bias4: T.Buffer((T.int64(10240),), "float32"), + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True), "tir.noalias": T.bool(True)}) + n = T.int64() + lv57 = T.match_buffer(p_lv57, (T.int64(1), n, T.int64(2560)), "float16") + p_output0_intermediate = T.match_buffer( + p_output0, (T.int64(1), n, T.int64(10240)), "float16" + ) + with T.block("root"): + T.reads() + T.writes() + T.block_attr({"meta_schedule.thread_extent_low_inclusive": 32}) + decode_local = T.alloc_buffer( + (T.int64(2560), T.int64(10240)), "float16", scope="local" + ) + lv36_local = T.alloc_buffer( + (T.int64(320), T.int64(10240)), "uint32", scope="local" + ) + lv37_local = T.alloc_buffer( + (T.int64(80), T.int64(10240)), "float16", scope="local" + ) + lv57_pad_local = T.alloc_buffer( + (T.int64(1), (n + T.int64(31)) // T.int64(32) * T.int64(32), T.int64(2560)), + "float16", + scope="local", + ) + var_NT_matmul_intermediate_pad_local = T.alloc_buffer( + ( + T.int64(1), + (n + T.int64(31)) // T.int64(32) * T.int64(32), + T.int64(10240), + ), + scope="local", + ) + for i0_i1_fused_0_i0_i1_fused_1_0_fused in T.thread_binding( + (n + T.int64(31)) // T.int64(32), thread="blockIdx.y" + ): + for i2_0 in T.thread_binding(T.int64(80), thread="blockIdx.x"): + for i0_i1_fused_1_1 in T.thread_binding( + T.int64(8), thread="threadIdx.y" + ): + for i2_1 in T.thread_binding(T.int64(16), thread="threadIdx.x"): + for i0_i1_fused_1_2_init in range(T.int64(4)): + for i2_2_init in T.vectorized(T.int64(8)): + with T.block("NT_matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial( + (n + T.int64(31)) // T.int64(32) * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + i0_i1_fused_1_2_init, + ) + v_i2 = T.axis.spatial( + T.int64(10240), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + i2_2_init, + ) + T.reads() + T.writes( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + ) + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] = T.float32(0) + for k_0_0, k_0_1 in T.grid(T.int64(20), T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(8)): + with T.block("lv37_local"): + v0 = T.axis.spatial( + T.int64(80), + k_0_0 * T.int64(4) + k_0_1 + ax0, + ) + v1 = T.axis.spatial( + T.int64(10240), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + ax1, + ) + T.reads(lv37[v0, v1]) + T.writes(lv37_local[v0, v1]) + lv37_local[v0, v1] = lv37[v0, v1] + for k_1 in range(T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(8)): + with T.block("lv36_local"): + v0 = T.axis.spatial( + T.int64(320), + k_0_0 * T.int64(16) + + k_0_1 * T.int64(4) + + k_1 + + ax0, + ) + v1 = T.axis.spatial( + T.int64(10240), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + ax1, + ) + T.reads(lv36[v0, v1]) + T.writes(lv36_local[v0, v1]) + lv36_local[v0, v1] = lv36[v0, v1] + for k_2 in range(T.int64(8)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(8)): + with T.block("decode"): + v_i = T.axis.spatial( + T.int64(2560), + k_0_0 * T.int64(128) + + k_0_1 * T.int64(32) + + k_1 * T.int64(8) + + k_2 + + ax0, + ) + v_j = T.axis.spatial( + T.int64(10240), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + ax1, + ) + T.reads( + lv36_local[v_i // T.int64(8), v_j], + lv37_local[v_i // T.int64(32), v_j], + ) + T.writes(decode_local[v_i, v_j]) + decode_local[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv36_local[ + v_i // T.int64(8), + v_j, + ], + T.Cast( + "uint32", + v_i % T.int64(8), + ) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv37_local[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(1), T.int64(4)): + for ax2 in T.vectorized(T.int64(1)): + with T.block("lv57_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial( + (n + T.int64(31)) + // T.int64(32) + * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + ax1, + ) + v2 = T.axis.spatial( + T.int64(2560), + k_0_0 * T.int64(128) + + k_0_1 * T.int64(32) + + k_1 * T.int64(8) + + k_2 + + ax2, + ) + T.reads(lv57[v0, v1, v2]) + T.writes(lv57_pad_local[v0, v1, v2]) + lv57_pad_local[ + v0, v1, v2 + ] = T.if_then_else( + v1 < n, + lv57[v0, v1, v2], + T.float16(0), + ) + for i0_i1_fused_1_2 in range(T.int64(4)): + for i2_2 in T.vectorized(T.int64(8)): + with T.block("NT_matmul_update"): + v_i0 = T.axis.spatial( + T.int64(1), T.int64(0) + ) + v_i1 = T.axis.spatial( + (n + T.int64(31)) + // T.int64(32) + * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + i0_i1_fused_1_2, + ) + v_i2 = T.axis.spatial( + T.int64(10240), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + i2_2, + ) + v_k = T.axis.reduce( + T.int64(2560), + k_0_0 * T.int64(128) + + k_0_1 * T.int64(32) + + k_1 * T.int64(8) + + k_2, + ) + T.reads( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ], + lv57_pad_local[v_i0, v_i1, v_k], + decode_local[v_k, v_i2], + ) + T.writes( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + ) + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] = var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + T.Cast( + "float32", + lv57_pad_local[v_i0, v_i1, v_k], + ) * T.Cast( + "float32", decode_local[v_k, v_i2] + ) + for ax0, ax1 in T.grid(T.int64(1), T.int64(4)): + for ax2 in T.vectorized(T.int64(8)): + with T.block("var_NT_matmul_intermediate_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial( + (n + T.int64(31)) // T.int64(32) * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + ax1, + ) + v2 = T.axis.spatial( + T.int64(10240), + i2_0 * T.int64(128) + i2_1 * T.int64(8) + ax2, + ) + T.reads( + var_NT_matmul_intermediate_pad_local[ + v0, v1, v2 + ], + linear_bias4[v2], + ) + T.writes(p_output0_intermediate[v0, v1, v2]) + if v1 < n: + p_output0_intermediate[v0, v1, v2] = T.Cast( + "float16", + ( + var_NT_matmul_intermediate_pad_local[ + v0, v1, v2 + ] + + linear_bias4[v2] + ) + * ( + T.float32(0.5) + + T.erf( + ( + var_NT_matmul_intermediate_pad_local[ + v0, v1, v2 + ] + + linear_bias4[v2] + ) + * T.float32(0.70710678118654757) + ) + * T.float32(0.5) + ), + ) + + +@T.prim_func(private=True) +def fused_decode1_fused_NT_matmul1_add4( + lv8: T.Buffer((T.int64(320), T.int64(2560)), "uint32"), + lv9: T.Buffer((T.int64(80), T.int64(2560)), "float16"), + p_lv9: T.handle, + linear_bias: T.Buffer((T.int64(2560),), "float16"), + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv9_1 = T.match_buffer(p_lv9, (T.int64(1), n, T.int64(2560)), "float16") + p_output0_intermediate = T.match_buffer( + p_output0, (T.int64(1), n, T.int64(2560)), "float16" + ) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(2560), T.int64(2560)), "float16") + var_T_transpose_intermediate = T.alloc_buffer( + (T.int64(2560), T.int64(2560)), "float16" + ) + var_NT_matmul_intermediate = T.alloc_buffer( + (T.int64(1), n, T.int64(2560)), "float16" + ) + for i, j in T.grid(T.int64(2560), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv8[v_i // T.int64(8), v_j], lv9[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv8[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv9[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(2560), T.int64(2560)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(var_T_transpose_intermediate[v_ax0, v_ax1]) + var_T_transpose_intermediate[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv9_1[v_i0, v_i1, v_k], var_T_transpose_intermediate[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + + lv9_1[v_i0, v_i1, v_k] * var_T_transpose_intermediate[v_i2, v_k] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias[v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias[v_ax2] + ) + + +@T.prim_func(private=True) +def fused_decode1_fused_NT_matmul1_add4_after( + lv8: T.Buffer((T.int64(320), T.int64(2560)), "uint32"), + lv9: T.Buffer((T.int64(80), T.int64(2560)), "float16"), + p_lv9: T.handle, + linear_bias: T.Buffer((T.int64(2560),), "float16"), + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True), "tir.noalias": T.bool(True)}) + n = T.int64() + lv9_1 = T.match_buffer(p_lv9, (T.int64(1), n, T.int64(2560)), "float16") + p_output0_intermediate = T.match_buffer( + p_output0, (T.int64(1), n, T.int64(2560)), "float16" + ) + with T.block("root"): + T.reads() + T.writes() + T.block_attr({"meta_schedule.thread_extent_low_inclusive": 32}) + decode_local = T.alloc_buffer( + (T.int64(2560), T.int64(2560)), "float16", scope="local" + ) + lv8_local = T.alloc_buffer( + (T.int64(320), T.int64(2560)), "uint32", scope="local" + ) + lv9_local = T.alloc_buffer( + (T.int64(80), T.int64(2560)), "float16", scope="local" + ) + lv9_1_pad_local = T.alloc_buffer( + (T.int64(1), (n + T.int64(31)) // T.int64(32) * T.int64(32), T.int64(2560)), + "float16", + scope="local", + ) + var_NT_matmul_intermediate_pad_local = T.alloc_buffer( + (T.int64(1), (n + T.int64(31)) // T.int64(32) * T.int64(32), T.int64(2560)), + "float16", + scope="local", + ) + for i0_i1_fused_0_i0_i1_fused_1_0_fused in T.thread_binding( + (n + T.int64(31)) // T.int64(32), thread="blockIdx.y" + ): + for i2_0 in T.thread_binding(T.int64(20), thread="blockIdx.x"): + for i0_i1_fused_1_1 in T.thread_binding( + T.int64(8), thread="threadIdx.y" + ): + for i2_1 in T.thread_binding(T.int64(16), thread="threadIdx.x"): + for i0_i1_fused_1_2_init in range(T.int64(4)): + for i2_2_init in T.vectorized(T.int64(8)): + with T.block("NT_matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial( + (n + T.int64(31)) // T.int64(32) * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + i0_i1_fused_1_2_init, + ) + v_i2 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + i2_2_init, + ) + T.reads() + T.writes( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + ) + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] = T.float16(0) + for k_0_0, k_0_1 in T.grid(T.int64(20), T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(8)): + with T.block("lv9_local"): + v0 = T.axis.spatial( + T.int64(80), + k_0_0 * T.int64(4) + k_0_1 + ax0, + ) + v1 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + ax1, + ) + T.reads(lv9[v0, v1]) + T.writes(lv9_local[v0, v1]) + lv9_local[v0, v1] = lv9[v0, v1] + for k_1 in range(T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(8)): + with T.block("lv8_local"): + v0 = T.axis.spatial( + T.int64(320), + k_0_0 * T.int64(16) + + k_0_1 * T.int64(4) + + k_1 + + ax0, + ) + v1 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + ax1, + ) + T.reads(lv8[v0, v1]) + T.writes(lv8_local[v0, v1]) + lv8_local[v0, v1] = lv8[v0, v1] + for k_2 in range(T.int64(8)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(8)): + with T.block("decode"): + v_i = T.axis.spatial( + T.int64(2560), + k_0_0 * T.int64(128) + + k_0_1 * T.int64(32) + + k_1 * T.int64(8) + + k_2 + + ax0, + ) + v_j = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + ax1, + ) + T.reads( + lv8_local[v_i // T.int64(8), v_j], + lv9_local[v_i // T.int64(32), v_j], + ) + T.writes(decode_local[v_i, v_j]) + decode_local[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv8_local[ + v_i // T.int64(8), + v_j, + ], + T.Cast( + "uint32", + v_i % T.int64(8), + ) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv9_local[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(1), T.int64(4)): + for ax2 in T.vectorized(T.int64(1)): + with T.block("lv9_1_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial( + (n + T.int64(31)) + // T.int64(32) + * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + ax1, + ) + v2 = T.axis.spatial( + T.int64(2560), + k_0_0 * T.int64(128) + + k_0_1 * T.int64(32) + + k_1 * T.int64(8) + + k_2 + + ax2, + ) + T.reads(lv9_1[v0, v1, v2]) + T.writes(lv9_1_pad_local[v0, v1, v2]) + lv9_1_pad_local[ + v0, v1, v2 + ] = T.if_then_else( + v1 < n, + lv9_1[v0, v1, v2], + T.float16(0), + ) + for i0_i1_fused_1_2 in range(T.int64(4)): + for i2_2 in T.vectorized(T.int64(8)): + with T.block("NT_matmul_update"): + v_i0 = T.axis.spatial( + T.int64(1), T.int64(0) + ) + v_i1 = T.axis.spatial( + (n + T.int64(31)) + // T.int64(32) + * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + i0_i1_fused_1_2, + ) + v_i2 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + i2_2, + ) + v_k = T.axis.reduce( + T.int64(2560), + k_0_0 * T.int64(128) + + k_0_1 * T.int64(32) + + k_1 * T.int64(8) + + k_2, + ) + T.reads( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ], + lv9_1_pad_local[v_i0, v_i1, v_k], + decode_local[v_k, v_i2], + ) + T.writes( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + ) + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] = ( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + + lv9_1_pad_local[v_i0, v_i1, v_k] + * decode_local[v_k, v_i2] + ) + for ax0, ax1 in T.grid(T.int64(1), T.int64(4)): + for ax2 in T.vectorized(T.int64(8)): + with T.block("var_NT_matmul_intermediate_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial( + (n + T.int64(31)) // T.int64(32) * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + ax1, + ) + v2 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + i2_1 * T.int64(8) + ax2, + ) + T.reads( + var_NT_matmul_intermediate_pad_local[ + v0, v1, v2 + ], + linear_bias[v2], + ) + T.writes(p_output0_intermediate[v0, v1, v2]) + if v1 < n: + p_output0_intermediate[v0, v1, v2] = ( + var_NT_matmul_intermediate_pad_local[ + v0, v1, v2 + ] + + linear_bias[v2] + ) + + +@T.prim_func(private=True) +def fused_decode3_fused_NT_matmul4_add7_cast8_cast12_add5( + lv43: T.Buffer((T.int64(1280), T.int64(2560)), "uint32"), + lv44: T.Buffer((T.int64(320), T.int64(2560)), "float16"), + p_lv63: T.handle, + linear_bias5: T.Buffer((T.int64(2560),), "float32"), + p_lv7: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv63 = T.match_buffer(p_lv63, (T.int64(1), n, T.int64(10240)), "float16") + lv7 = T.match_buffer(p_lv7, (T.int64(1), n, T.int64(2560)), "float16") + p_output0_intermediate = T.match_buffer( + p_output0, (T.int64(1), n, T.int64(2560)), "float16" + ) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(10240), T.int64(2560)), "float16") + var_T_transpose_intermediate = T.alloc_buffer( + (T.int64(2560), T.int64(10240)), "float16" + ) + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_compute_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + var_compute_intermediate_1 = T.alloc_buffer( + (T.int64(1), n, T.int64(2560)), "float16" + ) + for i, j in T.grid(T.int64(10240), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv43[v_i // T.int64(8), v_j], lv44[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv43[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv44[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(2560), T.int64(10240)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(var_T_transpose_intermediate[v_ax0, v_ax1]) + var_T_transpose_intermediate[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(10240)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv63[v_i0, v_i1, v_k], var_T_transpose_intermediate[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[ + v_i0, v_i1, v_i2 + ] + T.Cast("float32", lv63[v_i0, v_i1, v_k]) * T.Cast( + "float32", var_T_transpose_intermediate[v_i2, v_k] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias5[v_ax2] + ) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias5[v_ax2] + ) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = T.Cast( + "float16", var_T_add_intermediate[v_i0, v_i1, v_i2] + ) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute_1"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_compute_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate_1[v_i0, v_i1, v_i2]) + var_compute_intermediate_1[v_i0, v_i1, v_i2] = var_compute_intermediate[ + v_i0, v_i1, v_i2 + ] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + var_compute_intermediate_1[v_ax0, v_ax1, v_ax2], + lv7[v_ax0, v_ax1, v_ax2], + ) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_compute_intermediate_1[v_ax0, v_ax1, v_ax2] + + lv7[v_ax0, v_ax1, v_ax2] + ) + + +@T.prim_func(private=True) +def fused_decode3_fused_NT_matmul4_add7_cast8_cast12_add5_after( + lv43: T.Buffer((T.int64(1280), T.int64(2560)), "uint32"), + lv44: T.Buffer((T.int64(320), T.int64(2560)), "float16"), + p_lv63: T.handle, + linear_bias5: T.Buffer((T.int64(2560),), "float32"), + p_lv7: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True), "tir.noalias": T.bool(True)}) + n = T.int64() + lv63 = T.match_buffer(p_lv63, (T.int64(1), n, T.int64(10240)), "float16") + lv7 = T.match_buffer(p_lv7, (T.int64(1), n, T.int64(2560)), "float16") + p_output0_intermediate = T.match_buffer( + p_output0, (T.int64(1), n, T.int64(2560)), "float16" + ) + with T.block("root"): + T.reads() + T.writes() + T.block_attr({"meta_schedule.thread_extent_low_inclusive": 32}) + decode_local = T.alloc_buffer( + (T.int64(10240), T.int64(2560)), "float16", scope="local" + ) + lv43_local = T.alloc_buffer( + (T.int64(1280), T.int64(2560)), "uint32", scope="local" + ) + lv44_local = T.alloc_buffer( + (T.int64(320), T.int64(2560)), "float16", scope="local" + ) + lv63_pad_local = T.alloc_buffer( + ( + T.int64(1), + (n + T.int64(31)) // T.int64(32) * T.int64(32), + T.int64(10240), + ), + "float16", + scope="local", + ) + var_NT_matmul_intermediate_pad_local = T.alloc_buffer( + (T.int64(1), (n + T.int64(31)) // T.int64(32) * T.int64(32), T.int64(2560)), + scope="local", + ) + for i0_i1_fused_0_i0_i1_fused_1_0_fused in T.thread_binding( + (n + T.int64(31)) // T.int64(32), thread="blockIdx.y" + ): + for i2_0 in T.thread_binding(T.int64(20), thread="blockIdx.x"): + for i0_i1_fused_1_1 in T.thread_binding( + T.int64(8), thread="threadIdx.y" + ): + for i2_1 in T.thread_binding(T.int64(16), thread="threadIdx.x"): + for i0_i1_fused_1_2_init in range(T.int64(4)): + for i2_2_init in T.vectorized(T.int64(8)): + with T.block("NT_matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial( + (n + T.int64(31)) // T.int64(32) * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + i0_i1_fused_1_2_init, + ) + v_i2 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + i2_2_init, + ) + T.reads() + T.writes( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + ) + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] = T.float32(0) + for k_0_0, k_0_1 in T.grid(T.int64(80), T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(8)): + with T.block("lv44_local"): + v0 = T.axis.spatial( + T.int64(320), + k_0_0 * T.int64(4) + k_0_1 + ax0, + ) + v1 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + ax1, + ) + T.reads(lv44[v0, v1]) + T.writes(lv44_local[v0, v1]) + lv44_local[v0, v1] = lv44[v0, v1] + for k_1 in range(T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(8)): + with T.block("lv43_local"): + v0 = T.axis.spatial( + T.int64(1280), + k_0_0 * T.int64(16) + + k_0_1 * T.int64(4) + + k_1 + + ax0, + ) + v1 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + ax1, + ) + T.reads(lv43[v0, v1]) + T.writes(lv43_local[v0, v1]) + lv43_local[v0, v1] = lv43[v0, v1] + for k_2 in range(T.int64(8)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(8)): + with T.block("decode"): + v_i = T.axis.spatial( + T.int64(10240), + k_0_0 * T.int64(128) + + k_0_1 * T.int64(32) + + k_1 * T.int64(8) + + k_2 + + ax0, + ) + v_j = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + ax1, + ) + T.reads( + lv43_local[v_i // T.int64(8), v_j], + lv44_local[v_i // T.int64(32), v_j], + ) + T.writes(decode_local[v_i, v_j]) + decode_local[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv43_local[ + v_i // T.int64(8), + v_j, + ], + T.Cast( + "uint32", + v_i % T.int64(8), + ) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv44_local[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(1), T.int64(4)): + for ax2 in T.vectorized(T.int64(1)): + with T.block("lv63_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial( + (n + T.int64(31)) + // T.int64(32) + * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + ax1, + ) + v2 = T.axis.spatial( + T.int64(10240), + k_0_0 * T.int64(128) + + k_0_1 * T.int64(32) + + k_1 * T.int64(8) + + k_2 + + ax2, + ) + T.reads(lv63[v0, v1, v2]) + T.writes(lv63_pad_local[v0, v1, v2]) + lv63_pad_local[ + v0, v1, v2 + ] = T.if_then_else( + v1 < n, + lv63[v0, v1, v2], + T.float16(0), + ) + for i0_i1_fused_1_2 in range(T.int64(4)): + for i2_2 in T.vectorized(T.int64(8)): + with T.block("NT_matmul_update"): + v_i0 = T.axis.spatial( + T.int64(1), T.int64(0) + ) + v_i1 = T.axis.spatial( + (n + T.int64(31)) + // T.int64(32) + * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + i0_i1_fused_1_2, + ) + v_i2 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + i2_2, + ) + v_k = T.axis.reduce( + T.int64(10240), + k_0_0 * T.int64(128) + + k_0_1 * T.int64(32) + + k_1 * T.int64(8) + + k_2, + ) + T.reads( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ], + lv63_pad_local[v_i0, v_i1, v_k], + decode_local[v_k, v_i2], + ) + T.writes( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + ) + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] = var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + T.Cast( + "float32", + lv63_pad_local[v_i0, v_i1, v_k], + ) * T.Cast( + "float32", decode_local[v_k, v_i2] + ) + for ax0, ax1 in T.grid(T.int64(1), T.int64(4)): + for ax2 in T.vectorized(T.int64(8)): + with T.block("var_NT_matmul_intermediate_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial( + (n + T.int64(31)) // T.int64(32) * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + ax1, + ) + v2 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + i2_1 * T.int64(8) + ax2, + ) + T.reads( + var_NT_matmul_intermediate_pad_local[ + v0, v1, v2 + ], + linear_bias5[v2], + lv7[v0, v1, v2], + ) + T.writes(p_output0_intermediate[v0, v1, v2]) + if v1 < n: + p_output0_intermediate[v0, v1, v2] = ( + T.Cast( + "float16", + var_NT_matmul_intermediate_pad_local[ + v0, v1, v2 + ] + + linear_bias5[v2], + ) + + lv7[v0, v1, v2] + ) + + +@T.prim_func(private=True) +def fused_decode1_fused_NT_matmul1_add4_add5( + lv29: T.Buffer((T.int64(320), T.int64(2560)), "uint32"), + lv30: T.Buffer((T.int64(80), T.int64(2560)), "float16"), + p_lv49: T.handle, + linear_bias3: T.Buffer((T.int64(2560),), "float16"), + p_lv2: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv49 = T.match_buffer(p_lv49, (T.int64(1), n, T.int64(2560)), "float16") + lv2 = T.match_buffer(p_lv2, (T.int64(1), n, T.int64(2560)), "float16") + p_output0_intermediate = T.match_buffer( + p_output0, (T.int64(1), n, T.int64(2560)), "float16" + ) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(2560), T.int64(2560)), "float16") + var_T_transpose_intermediate = T.alloc_buffer( + (T.int64(2560), T.int64(2560)), "float16" + ) + var_NT_matmul_intermediate = T.alloc_buffer( + (T.int64(1), n, T.int64(2560)), "float16" + ) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + for i, j in T.grid(T.int64(2560), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv29[v_i // T.int64(8), v_j], lv30[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv29[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv30[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(2560), T.int64(2560)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(var_T_transpose_intermediate[v_ax0, v_ax1]) + var_T_transpose_intermediate[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv49[v_i0, v_i1, v_k], var_T_transpose_intermediate[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + + lv49[v_i0, v_i1, v_k] * var_T_transpose_intermediate[v_i2, v_k] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias3[v_ax2] + ) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias3[v_ax2] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + var_T_add_intermediate[v_ax0, v_ax1, v_ax2], lv2[v_ax0, v_ax1, v_ax2] + ) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] + lv2[v_ax0, v_ax1, v_ax2] + ) + + +@T.prim_func(private=True) +def fused_decode1_fused_NT_matmul1_add4_add5_after( + lv29: T.Buffer((T.int64(320), T.int64(2560)), "uint32"), + lv30: T.Buffer((T.int64(80), T.int64(2560)), "float16"), + p_lv49: T.handle, + linear_bias3: T.Buffer((T.int64(2560),), "float16"), + p_lv2: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True), "tir.noalias": T.bool(True)}) + n = T.int64() + lv49 = T.match_buffer(p_lv49, (T.int64(1), n, T.int64(2560)), "float16") + lv2 = T.match_buffer(p_lv2, (T.int64(1), n, T.int64(2560)), "float16") + p_output0_intermediate = T.match_buffer( + p_output0, (T.int64(1), n, T.int64(2560)), "float16" + ) + with T.block("root"): + T.reads() + T.writes() + T.block_attr({"meta_schedule.thread_extent_low_inclusive": 32}) + decode_local = T.alloc_buffer( + (T.int64(2560), T.int64(2560)), "float16", scope="local" + ) + lv29_local = T.alloc_buffer( + (T.int64(320), T.int64(2560)), "uint32", scope="local" + ) + lv30_local = T.alloc_buffer( + (T.int64(80), T.int64(2560)), "float16", scope="local" + ) + lv49_pad_local = T.alloc_buffer( + (T.int64(1), (n + T.int64(31)) // T.int64(32) * T.int64(32), T.int64(2560)), + "float16", + scope="local", + ) + var_NT_matmul_intermediate_pad_local = T.alloc_buffer( + (T.int64(1), (n + T.int64(31)) // T.int64(32) * T.int64(32), T.int64(2560)), + "float16", + scope="local", + ) + for i0_i1_fused_0_i0_i1_fused_1_0_fused in T.thread_binding( + (n + T.int64(31)) // T.int64(32), thread="blockIdx.y" + ): + for i2_0 in T.thread_binding(T.int64(20), thread="blockIdx.x"): + for i0_i1_fused_1_1 in T.thread_binding( + T.int64(8), thread="threadIdx.y" + ): + for i2_1 in T.thread_binding(T.int64(16), thread="threadIdx.x"): + for i0_i1_fused_1_2_init in range(T.int64(4)): + for i2_2_init in T.vectorized(T.int64(8)): + with T.block("NT_matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial( + (n + T.int64(31)) // T.int64(32) * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + i0_i1_fused_1_2_init, + ) + v_i2 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + i2_2_init, + ) + T.reads() + T.writes( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + ) + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] = T.float16(0) + for k_0_0, k_0_1 in T.grid(T.int64(20), T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(8)): + with T.block("lv30_local"): + v0 = T.axis.spatial( + T.int64(80), + k_0_0 * T.int64(4) + k_0_1 + ax0, + ) + v1 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + ax1, + ) + T.reads(lv30[v0, v1]) + T.writes(lv30_local[v0, v1]) + lv30_local[v0, v1] = lv30[v0, v1] + for k_1 in range(T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(8)): + with T.block("lv29_local"): + v0 = T.axis.spatial( + T.int64(320), + k_0_0 * T.int64(16) + + k_0_1 * T.int64(4) + + k_1 + + ax0, + ) + v1 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + ax1, + ) + T.reads(lv29[v0, v1]) + T.writes(lv29_local[v0, v1]) + lv29_local[v0, v1] = lv29[v0, v1] + for k_2 in range(T.int64(8)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(8)): + with T.block("decode"): + v_i = T.axis.spatial( + T.int64(2560), + k_0_0 * T.int64(128) + + k_0_1 * T.int64(32) + + k_1 * T.int64(8) + + k_2 + + ax0, + ) + v_j = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + ax1, + ) + T.reads( + lv29_local[v_i // T.int64(8), v_j], + lv30_local[v_i // T.int64(32), v_j], + ) + T.writes(decode_local[v_i, v_j]) + decode_local[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv29_local[ + v_i // T.int64(8), + v_j, + ], + T.Cast( + "uint32", + v_i % T.int64(8), + ) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv30_local[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(1), T.int64(4)): + for ax2 in T.vectorized(T.int64(1)): + with T.block("lv49_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial( + (n + T.int64(31)) + // T.int64(32) + * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + ax1, + ) + v2 = T.axis.spatial( + T.int64(2560), + k_0_0 * T.int64(128) + + k_0_1 * T.int64(32) + + k_1 * T.int64(8) + + k_2 + + ax2, + ) + T.reads(lv49[v0, v1, v2]) + T.writes(lv49_pad_local[v0, v1, v2]) + lv49_pad_local[ + v0, v1, v2 + ] = T.if_then_else( + v1 < n, + lv49[v0, v1, v2], + T.float16(0), + ) + for i0_i1_fused_1_2 in range(T.int64(4)): + for i2_2 in T.vectorized(T.int64(8)): + with T.block("NT_matmul_update"): + v_i0 = T.axis.spatial( + T.int64(1), T.int64(0) + ) + v_i1 = T.axis.spatial( + (n + T.int64(31)) + // T.int64(32) + * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + i0_i1_fused_1_2, + ) + v_i2 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + i2_2, + ) + v_k = T.axis.reduce( + T.int64(2560), + k_0_0 * T.int64(128) + + k_0_1 * T.int64(32) + + k_1 * T.int64(8) + + k_2, + ) + T.reads( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ], + lv49_pad_local[v_i0, v_i1, v_k], + decode_local[v_k, v_i2], + ) + T.writes( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + ) + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] = ( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + + lv49_pad_local[v_i0, v_i1, v_k] + * decode_local[v_k, v_i2] + ) + for ax0, ax1 in T.grid(T.int64(1), T.int64(4)): + for ax2 in T.vectorized(T.int64(8)): + with T.block("var_NT_matmul_intermediate_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial( + (n + T.int64(31)) // T.int64(32) * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + ax1, + ) + v2 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + i2_1 * T.int64(8) + ax2, + ) + T.reads( + var_NT_matmul_intermediate_pad_local[ + v0, v1, v2 + ], + linear_bias3[v2], + lv2[v0, v1, v2], + ) + T.writes(p_output0_intermediate[v0, v1, v2]) + if v1 < n: + p_output0_intermediate[v0, v1, v2] = ( + var_NT_matmul_intermediate_pad_local[ + v0, v1, v2 + ] + + linear_bias3[v2] + + lv2[v0, v1, v2] + ) + + +@T.prim_func(private=True) +def fused_decode3_fused_NT_matmul4_add7_cast8_cast12_add5_cast7( + lv1345: T.Buffer((T.int64(1280), T.int64(2560)), "uint32"), + lv1346: T.Buffer((T.int64(320), T.int64(2560)), "float16"), + p_lv2047: T.handle, + linear_bias191: T.Buffer((T.int64(2560),), "float32"), + p_lv317: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv2047 = T.match_buffer(p_lv2047, (T.int64(1), n, T.int64(10240)), "float16") + lv317 = T.match_buffer(p_lv317, (T.int64(1), n, T.int64(2560)), "float16") + p_output0_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(10240), T.int64(2560)), "float16") + var_T_transpose_intermediate = T.alloc_buffer( + (T.int64(2560), T.int64(10240)), "float16" + ) + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_compute_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + var_compute_intermediate_1 = T.alloc_buffer( + (T.int64(1), n, T.int64(2560)), "float16" + ) + var_T_add_intermediate_1 = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + for i, j in T.grid(T.int64(10240), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1345[v_i // T.int64(8), v_j], lv1346[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1345[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv1346[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(2560), T.int64(10240)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(var_T_transpose_intermediate[v_ax0, v_ax1]) + var_T_transpose_intermediate[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(10240)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2047[v_i0, v_i1, v_k], var_T_transpose_intermediate[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[ + v_i0, v_i1, v_i2 + ] + T.Cast("float32", lv2047[v_i0, v_i1, v_k]) * T.Cast( + "float32", var_T_transpose_intermediate[v_i2, v_k] + ) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias191[v_ax2] + ) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = ( + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias191[v_ax2] + ) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = T.Cast( + "float16", var_T_add_intermediate[v_i0, v_i1, v_i2] + ) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute_1"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_compute_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate_1[v_i0, v_i1, v_i2]) + var_compute_intermediate_1[v_i0, v_i1, v_i2] = var_compute_intermediate[ + v_i0, v_i1, v_i2 + ] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads( + var_compute_intermediate_1[v_ax0, v_ax1, v_ax2], + lv317[v_ax0, v_ax1, v_ax2], + ) + T.writes(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] = ( + var_compute_intermediate_1[v_ax0, v_ax1, v_ax2] + + lv317[v_ax0, v_ax1, v_ax2] + ) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute_2"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate_1[v_i0, v_i1, v_i2]) + T.writes(p_output0_intermediate[v_i0, v_i1, v_i2]) + p_output0_intermediate[v_i0, v_i1, v_i2] = T.Cast( + "float32", var_T_add_intermediate_1[v_i0, v_i1, v_i2] + ) + + +@T.prim_func(private=True) +def fused_decode3_fused_NT_matmul4_add7_cast8_cast12_add5_cast7_after( + lv1345: T.Buffer((T.int64(1280), T.int64(2560)), "uint32"), + lv1346: T.Buffer((T.int64(320), T.int64(2560)), "float16"), + p_lv2047: T.handle, + linear_bias191: T.Buffer((T.int64(2560),), "float32"), + p_lv317: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True), "tir.noalias": T.bool(True)}) + n = T.int64() + lv2047 = T.match_buffer(p_lv2047, (T.int64(1), n, T.int64(10240)), "float16") + lv317 = T.match_buffer(p_lv317, (T.int64(1), n, T.int64(2560)), "float16") + p_output0_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560))) + with T.block("root"): + T.reads() + T.writes() + T.block_attr({"meta_schedule.thread_extent_low_inclusive": 32}) + decode_local = T.alloc_buffer( + (T.int64(10240), T.int64(2560)), "float16", scope="local" + ) + lv1345_local = T.alloc_buffer( + (T.int64(1280), T.int64(2560)), "uint32", scope="local" + ) + lv1346_local = T.alloc_buffer( + (T.int64(320), T.int64(2560)), "float16", scope="local" + ) + lv2047_pad_local = T.alloc_buffer( + ( + T.int64(1), + (n + T.int64(31)) // T.int64(32) * T.int64(32), + T.int64(10240), + ), + "float16", + scope="local", + ) + var_NT_matmul_intermediate_pad_local = T.alloc_buffer( + (T.int64(1), (n + T.int64(31)) // T.int64(32) * T.int64(32), T.int64(2560)), + scope="local", + ) + for i0_i1_fused_0_i0_i1_fused_1_0_fused in T.thread_binding( + (n + T.int64(31)) // T.int64(32), thread="blockIdx.y" + ): + for i2_0 in T.thread_binding(T.int64(20), thread="blockIdx.x"): + for i0_i1_fused_1_1 in T.thread_binding( + T.int64(8), thread="threadIdx.y" + ): + for i2_1 in T.thread_binding(T.int64(16), thread="threadIdx.x"): + for i0_i1_fused_1_2_init in range(T.int64(4)): + for i2_2_init in T.vectorized(T.int64(8)): + with T.block("NT_matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial( + (n + T.int64(31)) // T.int64(32) * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + i0_i1_fused_1_2_init, + ) + v_i2 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + i2_2_init, + ) + T.reads() + T.writes( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + ) + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] = T.float32(0) + for k_0_0, k_0_1 in T.grid(T.int64(80), T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(8)): + with T.block("lv1346_local"): + v0 = T.axis.spatial( + T.int64(320), + k_0_0 * T.int64(4) + k_0_1 + ax0, + ) + v1 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + ax1, + ) + T.reads(lv1346[v0, v1]) + T.writes(lv1346_local[v0, v1]) + lv1346_local[v0, v1] = lv1346[v0, v1] + for k_1 in range(T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(8)): + with T.block("lv1345_local"): + v0 = T.axis.spatial( + T.int64(1280), + k_0_0 * T.int64(16) + + k_0_1 * T.int64(4) + + k_1 + + ax0, + ) + v1 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + ax1, + ) + T.reads(lv1345[v0, v1]) + T.writes(lv1345_local[v0, v1]) + lv1345_local[v0, v1] = lv1345[v0, v1] + for k_2 in range(T.int64(8)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(8)): + with T.block("decode"): + v_i = T.axis.spatial( + T.int64(10240), + k_0_0 * T.int64(128) + + k_0_1 * T.int64(32) + + k_1 * T.int64(8) + + k_2 + + ax0, + ) + v_j = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + ax1, + ) + T.reads( + lv1345_local[ + v_i // T.int64(8), v_j + ], + lv1346_local[ + v_i // T.int64(32), v_j + ], + ) + T.writes(decode_local[v_i, v_j]) + decode_local[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv1345_local[ + v_i // T.int64(8), + v_j, + ], + T.Cast( + "uint32", + v_i % T.int64(8), + ) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv1346_local[ + v_i // T.int64(32), v_j + ] + for ax0, ax1 in T.grid(T.int64(1), T.int64(4)): + for ax2 in T.vectorized(T.int64(1)): + with T.block("lv2047_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial( + (n + T.int64(31)) + // T.int64(32) + * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + ax1, + ) + v2 = T.axis.spatial( + T.int64(10240), + k_0_0 * T.int64(128) + + k_0_1 * T.int64(32) + + k_1 * T.int64(8) + + k_2 + + ax2, + ) + T.reads(lv2047[v0, v1, v2]) + T.writes(lv2047_pad_local[v0, v1, v2]) + lv2047_pad_local[ + v0, v1, v2 + ] = T.if_then_else( + v1 < n, + lv2047[v0, v1, v2], + T.float16(0), + ) + for i0_i1_fused_1_2 in range(T.int64(4)): + for i2_2 in T.vectorized(T.int64(8)): + with T.block("NT_matmul_update"): + v_i0 = T.axis.spatial( + T.int64(1), T.int64(0) + ) + v_i1 = T.axis.spatial( + (n + T.int64(31)) + // T.int64(32) + * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + i0_i1_fused_1_2, + ) + v_i2 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + + i2_1 * T.int64(8) + + i2_2, + ) + v_k = T.axis.reduce( + T.int64(10240), + k_0_0 * T.int64(128) + + k_0_1 * T.int64(32) + + k_1 * T.int64(8) + + k_2, + ) + T.reads( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ], + lv2047_pad_local[v_i0, v_i1, v_k], + decode_local[v_k, v_i2], + ) + T.writes( + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + ) + var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] = var_NT_matmul_intermediate_pad_local[ + v_i0, v_i1, v_i2 + ] + T.Cast( + "float32", + lv2047_pad_local[v_i0, v_i1, v_k], + ) * T.Cast( + "float32", decode_local[v_k, v_i2] + ) + for ax0, ax1 in T.grid(T.int64(1), T.int64(4)): + for ax2 in T.vectorized(T.int64(8)): + with T.block("var_NT_matmul_intermediate_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial( + (n + T.int64(31)) // T.int64(32) * T.int64(32), + i0_i1_fused_0_i0_i1_fused_1_0_fused + * T.int64(32) + + i0_i1_fused_1_1 * T.int64(4) + + ax1, + ) + v2 = T.axis.spatial( + T.int64(2560), + i2_0 * T.int64(128) + i2_1 * T.int64(8) + ax2, + ) + T.reads( + var_NT_matmul_intermediate_pad_local[ + v0, v1, v2 + ], + linear_bias191[v2], + lv317[v0, v1, v2], + ) + T.writes(p_output0_intermediate[v0, v1, v2]) + if v1 < n: + p_output0_intermediate[v0, v1, v2] = T.Cast( + "float32", + T.Cast( + "float16", + var_NT_matmul_intermediate_pad_local[ + v0, v1, v2 + ] + + linear_bias191[v2], + ) + + lv317[v0, v1, v2], + ) + + +@T.prim_func(private=True) +def fused_decode2_NT_matmul( + lv4: T.Buffer((T.int64(512), T.int64(12288)), "uint32"), + lv5: T.Buffer((T.int64(128), T.int64(12288)), "float16"), + p_lv6: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv6 = T.match_buffer(p_lv6, (T.int64(1), n, T.int64(4096)), "float16") + var_NT_matmul_intermediate = T.match_buffer( + p_output0, (T.int64(1), n, T.int64(12288)), "float16" + ) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(4096), T.int64(12288)), "float16") + p_output0_intermediate = T.alloc_buffer((T.int64(12288), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(12288)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv4[v_i // T.int64(8), v_j], lv5[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv4[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv5[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(12288), T.int64(4096)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(p_output0_intermediate[v_ax0, v_ax1]) + p_output0_intermediate[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(12288), T.int64(4096)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv6[v_i0, v_i1, v_k], p_output0_intermediate[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + + lv6[v_i0, v_i1, v_k] * p_output0_intermediate[v_i2, v_k] + ) + + +@T.prim_func(private=True) +def fused_decode2_NT_matmul_after( + lv8: T.Buffer((T.int64(512), T.int64(12288)), "uint32"), + lv9: T.Buffer((T.int64(128), T.int64(12288)), "float16"), + p_lv6: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + lv6 = T.match_buffer(p_lv6, (1, n, 4096), "float16") + var_NT_matmul_intermediate = T.match_buffer(p_output0, (1, n, 12288), "float16") + + var_matmul_intermediate_local = T.alloc_buffer( + (T.int64(1), ((n+7)//8) * 8, T.int64(12288)), "float16", scope="local" + ) + var_matmul_intermediate_local_batch = T.alloc_buffer( + (T.int64(1), ((n+7)//8) * 8, T.int64(12288)), "float16", scope="local" + ) + lv8_local = T.alloc_buffer((T.int64(512), T.int64(12288)), "uint32", scope="local") + lv9_local = T.alloc_buffer( + (T.int64(128), T.int64(12288)), "float16", scope="local" + ) + #lv6_shared = T.alloc_buffer( + # (T.int64(1), T.int64(1), T.int64(4096)), "float16", scope="shared" + #) + for i0_i1_i2_fused_n in T.thread_binding(((n+7)//8), thread="blockIdx.y"): + for i0_i1_i2_fused_0 in T.thread_binding(T.int64(96), thread="blockIdx.x"): + for i0_i1_i2_fused_1 in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + with T.block("n_check"): + T.where((i0_i1_i2_fused_n * T.int64(8) + ax2_y) < n) + for i0_i1_i2_fused_2_init in T.vectorized(T.int64(4)): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2 = T.axis.spatial( + T.int64(12288), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + i0_i1_i2_fused_2_init + ) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float16(0) + for k_1 in range(T.int64(128)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("matmul_init_local"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2k = T.axis.spatial( + T.int64(12288), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads() + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = T.float16(0) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv9_local"): + v0 = T.axis.spatial( + T.int64(128), k_1 + ) + v1 = T.axis.spatial( + T.int64(12288), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv9[v0, v1]) + T.writes(lv9_local[v0, v1]) + lv9_local[v0, v1] = lv9[v0, v1] + for k_2 in range(T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv8_local"): + v0 = T.axis.spatial( + T.int64(512), + k_1 * T.int64(4) + + k_2 + + ax0, + ) + v1 = T.axis.spatial( + T.int64(12288), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv8[v0, v1]) + T.writes(lv8_local[v0, v1]) + lv8_local[v0, v1] = lv8[v0, v1] + for k_3 in range(T.int64(8)): + for i0_i1_i2_fused_2 in T.vectorized(T.int64(4)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2 = T.axis.spatial( + T.int64(12288), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_k = T.axis.reduce( + T.int64(4096), + k_1 * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + T.reads( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ], + lv6[v_i0, v_i1, v_k], + lv8_local[v_k // T.int64(8), v_i2], + ) + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ] = var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ] + lv6[ + v_i0, v_i1, v_k + ] * ( + ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv8_local[ + v_k // T.int64(8), v_i2 + ], + T.Cast( + "uint32", + v_k % T.int64(8), + ) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) + ) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("multiple_scale"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2 = T.axis.spatial( + T.int64(12288), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + v0 = T.axis.spatial( + T.int64(128), + k_1 + ) + v1 = T.axis.spatial( + T.int64(12288), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads( + lv9_local[v0, v1], + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ], + ) + T.writes( + var_matmul_intermediate_local[v_i0, v_i1, v_i2] + ) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = ( + var_matmul_intermediate_local[v_i0, v_i1, v_i2] + + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ] + * lv9_local[v0, v1] + ) + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_local"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2 = T.axis.spatial( + T.int64(12288), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax2, + ) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + + +@T.prim_func(private=True) +def fused_decode4_NT_matmul3( + lv13: T.Buffer((T.int64(512), T.int64(22016)), "uint32"), + lv14: T.Buffer((T.int64(128), T.int64(22016)), "float16"), + p_lv45: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv45 = T.match_buffer(p_lv45, (T.int64(1), n, T.int64(4096)), "float16") + var_NT_matmul_intermediate = T.match_buffer( + p_output0, (T.int64(1), n, T.int64(22016)), "float16" + ) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(4096), T.int64(22016)), "float16") + p_output0_intermediate = T.alloc_buffer((T.int64(22016), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(22016)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv13[v_i // T.int64(8), v_j], lv14[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv13[v_i // T.int64(8), v_j], + T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) * lv14[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(22016), T.int64(4096)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(p_output0_intermediate[v_ax0, v_ax1]) + p_output0_intermediate[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(22016), T.int64(4096)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv45[v_i0, v_i1, v_k], p_output0_intermediate[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = ( + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + + lv45[v_i0, v_i1, v_k] * p_output0_intermediate[v_i2, v_k] + ) + + +@T.prim_func(private=True) +def fused_decode4_NT_matmul3_after( + lv8: T.Buffer((T.int64(512), T.int64(22016)), "uint32"), + lv9: T.Buffer((T.int64(128), T.int64(22016)), "float16"), + p_lv6: T.handle, + p_output0: T.handle, +): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + lv6 = T.match_buffer(p_lv6, (1, n, 4096), "float16") + var_NT_matmul_intermediate = T.match_buffer(p_output0, (1, n, 22016), "float16") + + var_matmul_intermediate_local = T.alloc_buffer( + (T.int64(1), ((n+7)//8) * 8, T.int64(22016)), "float16", scope="local" + ) + var_matmul_intermediate_local_batch = T.alloc_buffer( + (T.int64(1), ((n+7)//8) * 8, T.int64(22016)), "float16", scope="local" + ) + lv8_local = T.alloc_buffer((T.int64(512), T.int64(22016)), "uint32", scope="local") + lv9_local = T.alloc_buffer( + (T.int64(128), T.int64(22016)), "float16", scope="local" + ) + #lv6_shared = T.alloc_buffer( + # (T.int64(1), T.int64(1), T.int64(4096)), "float16", scope="shared" + #) + for i0_i1_i2_fused_n in T.thread_binding(((n+7)//8), thread="blockIdx.y"): + for i0_i1_i2_fused_0 in T.thread_binding(T.int64(172), thread="blockIdx.x"): + for i0_i1_i2_fused_1 in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for ax2_y in T.thread_binding(T.int64(8), thread="threadIdx.y"): + with T.block("n_check"): + T.where((i0_i1_i2_fused_n * T.int64(8) + ax2_y) < n) + for i0_i1_i2_fused_2_init in T.vectorized(T.int64(4)): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2 = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + i0_i1_i2_fused_2_init + ) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float16(0) + for k_1 in range(T.int64(128)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("matmul_init_local"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2k = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads() + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2k + ] = T.float16(0) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv9_local"): + v0 = T.axis.spatial( + T.int64(128), k_1 + ) + v1 = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv9[v0, v1]) + T.writes(lv9_local[v0, v1]) + lv9_local[v0, v1] = lv9[v0, v1] + for k_2 in range(T.int64(4)): + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("lv8_local"): + v0 = T.axis.spatial( + T.int64(512), + k_1 * T.int64(4) + + k_2 + + ax0, + ) + v1 = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads(lv8[v0, v1]) + T.writes(lv8_local[v0, v1]) + lv8_local[v0, v1] = lv8[v0, v1] + for k_3 in range(T.int64(8)): + for i0_i1_i2_fused_2 in T.vectorized(T.int64(4)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2 = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + i0_i1_i2_fused_2, + ) + v_k = T.axis.reduce( + T.int64(4096), + k_1 * T.int64(32) + + k_2 * T.int64(8) + + k_3, + ) + T.reads( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ], + lv6[v_i0, v_i1, v_k], + lv8_local[v_k // T.int64(8), v_i2], + ) + T.writes( + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ] + ) + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ] = var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ] + lv6[ + v_i0, v_i1, v_k + ] * ( + ( + T.Cast( + "float16", + T.bitwise_and( + T.shift_right( + lv8_local[ + v_k // T.int64(8), v_i2 + ], + T.Cast( + "uint32", + v_k % T.int64(8), + ) + * T.uint32(4), + ), + T.uint32(15), + ), + ) + - T.float16(7) + ) + ) + for ax0 in range(T.int64(1)): + for ax1 in T.vectorized(T.int64(4)): + with T.block("multiple_scale"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2 = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + v0 = T.axis.spatial( + T.int64(128), + k_1 + ) + v1 = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax1, + ) + T.reads( + lv9_local[v0, v1], + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ], + ) + T.writes( + var_matmul_intermediate_local[v_i0, v_i1, v_i2] + ) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = ( + var_matmul_intermediate_local[v_i0, v_i1, v_i2] + + var_matmul_intermediate_local_batch[ + v_i0, v_i1, v_i2 + ] + * lv9_local[v0, v1] + ) + for ax0, ax1 in T.grid(T.int64(1), T.int64(1)): + for ax2 in T.vectorized(T.int64(4)): + with T.block("var_matmul_intermediate_local"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+7)//8) * 8, i0_i1_i2_fused_n * T.int64(8) + ax2_y) + v_i2 = T.axis.spatial( + T.int64(22016), + i0_i1_i2_fused_0 * T.int64(128) + + i0_i1_i2_fused_1 * T.int64(4) + + ax2, + ) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + + + +@T.prim_func(private=True) +def fused_NT_matmul1_divide2_maximum1_minimum1_cast3(lv1593: T.Buffer((T.int64(1), T.int64(1), T.int64(32), T.int64(128)), "float16"), p_lv1603: T.handle, p_lv1582: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv1603 = T.match_buffer(p_lv1603, (T.int64(1), n, T.int64(32), T.int64(128)), "float16") + lv1582 = T.match_buffer(p_lv1582, (T.int64(1), T.int64(1), T.int64(1), n), "float16") + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), T.int64(1), n)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + var_T_maximum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + var_T_minimum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n, T.int64(128)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv1593[v_i0, v_i2, v_i1, v_k], lv1603[v_i0, v_i3, v_i1, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv1593[v_i0, v_i2, v_i1, v_k] * lv1603[v_i0, v_i3, v_i1, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float16(0.088397790055248615) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float16(-65504)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_minimum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv1582[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.min(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv1582[v_ax0, T.int64(0), v_ax2, v_ax3]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("compute"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3]) + var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.Cast("float32", var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + +@T.prim_func(private=True) +def fused_NT_matmul1_divide2_maximum1_minimum1_cast3_after( + lv1593: T.Buffer((T.int64(1), T.int64(1), T.int64(32), T.int64(128)), "float16"), + p_lv1603: T.handle, + p_lv1582: T.handle, + p_output0: T.handle +): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + n = T.int64() + lv1603 = T.match_buffer(p_lv1603, (T.int64(1), n, T.int64(32), T.int64(128)), "float16") + lv1582 = T.match_buffer(p_lv1582, (T.int64(1), T.int64(1), T.int64(1), n), "float16") + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), T.int64(1), n)) + var_matmul_intermediate_local = T.alloc_buffer( + (1, ((n + 7) // 8) * 8, 4096), "float16", scope="local" + ) + lv1593_shared = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(1024)), "float16", scope="shared" + ) + for i_by in T.thread_binding(T.int64((n + 7) // 8), thread="blockIdx.y"): + for i_bx in T.thread_binding(T.int64(32), thread="blockIdx.x"): + for i_tx in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for i_ty in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for i_v8 in T.vectorized(T.int64(4)): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(n), i_by * T.int64(8) + i_ty) + v_i2 = T.axis.spatial( + T.int64(4096), + i_bx * T.int64(128) + + i_tx * T.int64(4) + + i_v8, + ) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float16(0) + with T.block("lv1593_shared"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(32), i_bx) + v_i3 = T.axis.spatial(T.int64(128), i_tx * T.int64(4) + i_v8) + T.reads(lv1593[v_i0, v_i1, v_i2, v_i3]) + T.writes(lv1593_shared[v_i0, v_i1, v_i3]) + lv1593_shared[v_i0, v_i1, v_i3] = lv1593[v_i0, v_i1, v_i2, v_i3] + with T.block("matmul_compute"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1_1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(n), i_by * T.int64(8) + i_ty) + v_i2 = T.axis.spatial(T.int64(32), i_bx) + v_i3 = T.axis.spatial(T.int64(128), i_tx * T.int64(4) + i_v8) + v_ik = T.axis.spatial(T.int64(4096), i_bx * T.int64(128) + i_tx * T.int64(4) + i_v8) + T.where(i_by * T.int64(8) + i_ty < n) + T.reads(lv1593_shared[v_i0, v_i1_1, v_i3], lv1603[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_ik]) + var_matmul_intermediate_local[v_i0, v_i1, v_ik] = var_matmul_intermediate_local[v_i0, v_i1, v_ik] + lv1603[v_i0, v_i1, v_i2, v_i3] * lv1593_shared[v_i0, v_i1_1, v_i3] + for i_tx in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for i_ty in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for i_v8 in T.vectorized(T.int64(4)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1_1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(n), i_by * T.int64(8) + i_ty) + v_ik = T.axis.spatial(T.int64(4096), i_bx * T.int64(128) + i_tx * T.int64(4) + i_v8) + v_i2 = T.axis.spatial(T.int64(1024), i_ty * T.int64(128) + i_tx * T.int64(4) + i_v8) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_ik]) + T.writes(lv1593_shared[v_i0, v_i1_1, v_i2]) + lv1593_shared[v_i0, v_i1_1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_ik] + for i_tx in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for i_ty in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for i_v8 in T.vectorized(T.int64(4)): + with T.block("reduction_1"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(1024), i_ty * T.int64(128) + i_tx * T.int64(4) + i_v8) + T.where(i_tx < T.int64(16)) + T.reads(lv1593_shared[v_i0, v_i1, v_i2]) + T.writes(lv1593_shared[v_i0, v_i1, v_i2]) + lv1593_shared[v_i0, v_i1, v_i2] = lv1593_shared[v_i0, v_i1, v_i2] + lv1593_shared[v_i0, v_i1, v_i2 + T.int64(64)] + for i_tx in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for i_ty in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for i_v8 in T.vectorized(T.int64(4)): + with T.block("reduction_2"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(1024), i_ty * T.int64(128) + i_tx * T.int64(4) + i_v8) + T.where(i_tx < T.int64(8)) + T.reads(lv1593_shared[v_i0, v_i1, v_i2]) + T.writes(lv1593_shared[v_i0, v_i1, v_i2]) + lv1593_shared[v_i0, v_i1, v_i2] = lv1593_shared[v_i0, v_i1, v_i2] + lv1593_shared[v_i0, v_i1, v_i2 + T.int64(32)] + for i_tx in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for i_ty in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for i_v8 in T.vectorized(T.int64(4)): + with T.block("reduction_3"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(1024), i_ty * T.int64(128) + i_tx * T.int64(4) + i_v8) + T.where(i_tx < T.int64(4)) + T.reads(lv1593_shared[v_i0, v_i1, v_i2]) + T.writes(lv1593_shared[v_i0, v_i1, v_i2]) + lv1593_shared[v_i0, v_i1, v_i2] = lv1593_shared[v_i0, v_i1, v_i2] + lv1593_shared[v_i0, v_i1, v_i2 + T.int64(16)] + for i_tx in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for i_ty in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for i_v8 in T.vectorized(T.int64(4)): + with T.block("reduction_4"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(1024), i_ty * T.int64(128) + i_tx * T.int64(4) + i_v8) + T.where(i_tx < T.int64(2)) + T.reads(lv1593_shared[v_i0, v_i1, v_i2]) + T.writes(lv1593_shared[v_i0, v_i1, v_i2]) + lv1593_shared[v_i0, v_i1, v_i2] = lv1593_shared[v_i0, v_i1, v_i2] + lv1593_shared[v_i0, v_i1, v_i2 + T.int64(8)] + for i_tx in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for i_ty in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for i_v8 in T.vectorized(T.int64(4)): + with T.block("reduction_4"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(1024), i_ty * T.int64(128) + i_tx * T.int64(4) + i_v8) + T.where(i_tx < T.int64(1)) + T.reads(lv1593_shared[v_i0, v_i1, v_i2]) + T.writes(lv1593_shared[v_i0, v_i1, v_i2]) + lv1593_shared[v_i0, v_i1, v_i2] = lv1593_shared[v_i0, v_i1, v_i2] + lv1593_shared[v_i0, v_i1, v_i2 + T.int64(4)] + for i_tx in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for i_ty in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for ax0 in range(T.int64(1)): + with T.block("Output_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(32), i_bx) + v_i2 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i3 = T.axis.spatial(T.int64(n), i_by * T.int64(8) + i_ty) + v_ik = T.axis.spatial(T.int64(1024), i_ty * T.int64(128)) + T.where(i_by * T.int64(8) + i_ty < n) + T.reads(lv1593_shared[v_i0, v_i2, v_ik]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3]) + var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.Cast("float32", T.min(T.max((lv1593_shared[v_i0, v_i2, v_ik] + lv1593_shared[v_i0, v_i2, v_ik + T.int64(1)] + + lv1593_shared[v_i0, v_i2, v_ik + T.int64(2)] + lv1593_shared[v_i0, v_i2, v_ik + T.int64(3)]) + * T.float16(0.088397790055248615), T.float16(-65504)), lv1582[v_i0, T.int64(0), v_i2, v_i3])) + + + +# [gx,gy, gz] [lx, ly, lz] + +@T.prim_func(private=True) +def NT_matmul3(var_A: T.handle, var_B: T.handle, NT_matmul: T.Buffer((T.int64(1), T.int64(1), T.int64(32), T.int64(128)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(32), T.int64(128)), "float16") + B = T.match_buffer(var_B, (T.int64(1), T.int64(32), T.int64(1), n), "float16") + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(1), T.int64(32), T.int64(128), n): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(A[v_i0, v_k, v_i2, v_i3], B[v_i0, v_i2, v_i1, v_k]) + T.writes(NT_matmul[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + NT_matmul[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + NT_matmul[v_i0, v_i1, v_i2, v_i3] = NT_matmul[v_i0, v_i1, v_i2, v_i3] + A[v_i0, v_k, v_i2, v_i3] * B[v_i0, v_i2, v_i1, v_k] + +@T.prim_func(private=True) +def NT_matmul3_after( + var_A: T.handle, + var_B: T.handle, + NT_matmul: T.Buffer((T.int64(1), T.int64(1), T.int64(32), T.int64(128)), "float16") +): + + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(32), T.int64(128)), "float16") + B = T.match_buffer(var_B, (T.int64(1), T.int64(32), T.int64(1), n), "float16") + var_matmul_intermediate_local = T.alloc_buffer( + (1, 8, 4096), "float16", scope="local" + ) + B_shared = T.alloc_buffer( + (T.int64(1), T.int64(1), T.int64(1024)), "float16", scope="shared" + ) + for i_bx in T.thread_binding(T.int64(32), thread="blockIdx.x"): + for i_tx in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for i_ty in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for i_v8 in T.vectorized(T.int64(4)): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(8), i_ty) + v_i2 = T.axis.spatial( + T.int64(4096), + i_bx * T.int64(128) + i_tx * T.int64(4) + + i_v8, + ) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float16(0) + for ax0 in range((n+255)//256): + with T.block("B_shared"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(32), i_bx) + v_i2 = T.axis.spatial(((n+255)//256) * 256, ax0 * T.int64(256) + i_ty * T.int64(32) + i_tx) + v_i2k = T.axis.spatial(T.int64(256), i_ty * T.int64(32) + i_tx) + #T.where(ax0 * T.int64(256) + i_ty * T.int64(32) + i_tx < n) + T.reads(B[v_i0, v_i1, T.int64(0), v_i2]) + T.writes(B_shared[v_i0, v_i1, v_i2k]) + B_shared[v_i0, T.int64(0), v_i2k] = T.if_then_else(v_i2 < n, B[v_i0, v_i1, T.int64(0), v_i2], T.float16(0)) + for ax1 in range(32): + #with T.block("n_check"): + # T.where(ax0 * T.int64(256) + ax1 * T.int64(8) + i_ty < n) + for i_v8 in T.vectorized(T.int64(4)): + with T.block("matmul_compute"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(((n+255)//256) * 256, ax0 * T.int64(256) + ax1 * T.int64(8) + i_ty) + v_i1_1 = T.axis.spatial(T.int64(8), i_ty) + v_i2 = T.axis.spatial(T.int64(32), i_bx) + v_i3 = T.axis.spatial(T.int64(128), i_tx * T.int64(4) + i_v8) + v_ik = T.axis.spatial(T.int64(256), ax1 * T.int64(8) + i_ty) + v_ik1 = T.axis.spatial(T.int64(4096), i_bx * T.int64(128) + i_tx * T.int64(4) + i_v8) + T.reads(B_shared[v_i0, T.int64(0), v_ik], A[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1_1, v_ik1]) + var_matmul_intermediate_local[v_i0, v_i1_1, v_ik1] = var_matmul_intermediate_local[v_i0, v_i1_1, v_ik1] + T.if_then_else(v_i1 < n, A[v_i0, v_i1, v_i2, v_i3], T.float16(0)) * B_shared[v_i0, T.int64(0), v_ik] + + for i_tx in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for i_ty in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for i_v8 in T.vectorized(T.int64(4)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(8), i_ty) + v_i2 = T.axis.spatial(T.int64(4096), i_bx * T.int64(128) + i_tx * T.int64(4) + i_v8) + v_ik = T.axis.spatial(T.int64(1024), i_ty * T.int64(128) + i_tx * T.int64(4) + i_v8) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + T.writes(B_shared[v_i0, T.int64(0), v_ik]) + B_shared[v_i0, T.int64(0), v_ik] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + for i_tx in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for i_ty in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for i_v8 in T.vectorized(T.int64(4)): + with T.block("reduction_1"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(1024), i_ty * T.int64(128) + i_tx * T.int64(4) + i_v8) + T.where(i_ty < T.int64(4)) + T.reads(B_shared[v_i0, v_i1, v_i2]) + T.writes(B_shared[v_i0, v_i1, v_i2]) + B_shared[v_i0, v_i1, v_i2] = B_shared[v_i0, v_i1, v_i2] + B_shared[v_i0, v_i1, v_i2 + T.int64(512)] + for i_tx in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for i_ty in T.thread_binding(T.int64(8), thread="threadIdx.y"): + for i_v8 in T.vectorized(T.int64(4)): + with T.block("Output_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(32), i_bx) + v_i3 = T.axis.spatial(T.int64(128), i_tx * T.int64(4) + i_v8) + v_ik = T.axis.spatial(T.int64(1024), i_ty * T.int64(128) + i_tx * T.int64(4) + i_v8) + T.where(i_ty < 1) + T.reads(B_shared[v_i0, v_i1, v_ik]) + T.writes(NT_matmul[v_i0, v_i1, v_i2, v_i3]) + NT_matmul[v_i0, v_i1, v_i2, v_i3] = B_shared[v_i0, v_i1, v_ik] + B_shared[v_i0, v_i1, v_ik + T.int64(128)] + B_shared[v_i0, v_i1, v_ik + T.int64(256)] + B_shared[v_i0, v_i1, v_ik + T.int64(384)] + +@T.prim_func(private=True) +def rms_norm(var_A: T.handle, B: T.Buffer((T.int64(4096),), "float16"), var_rms_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(4096)), "float16") + rms_norm_1 = T.match_buffer(var_rms_norm, (T.int64(1), n, T.int64(4096)), "float16") + # with T.block("root"): + Ared_temp = T.alloc_buffer((T.int64(1), n)) + for bsz, i, k in T.grid(T.int64(1), n, T.int64(4096)): + with T.block("Ared_temp"): + v_bsz, v_i, v_k = T.axis.remap("SSR", [bsz, i, k]) + T.reads(A[v_bsz, v_i, v_k]) + T.writes(Ared_temp[v_bsz, v_i]) + with T.init(): + Ared_temp[v_bsz, v_i] = T.float32(0) + Ared_temp[v_bsz, v_i] = Ared_temp[v_bsz, v_i] + T.Cast("float32", A[v_bsz, v_i, v_k]) * T.Cast("float32", A[v_bsz, v_i, v_k]) + for bsz, i, k in T.grid(T.int64(1), n, T.int64(4096)): + with T.block("rms_norm"): + v_bsz, v_i, v_k = T.axis.remap("SSS", [bsz, i, k]) + T.reads(B[v_k], A[v_bsz, v_i, v_k], Ared_temp[v_bsz, v_i]) + T.writes(rms_norm_1[v_bsz, v_i, v_k]) + rms_norm_1[v_bsz, v_i, v_k] = T.Cast("float16", T.Cast("float32", B[v_k]) * (T.Cast("float32", A[v_bsz, v_i, v_k]) / T.sqrt(Ared_temp[v_bsz, v_i] * T.float32(0.000244140625) + T.float32(9.9999999999999995e-07)))) + +@T.prim_func(private=True) +def rms_norm_after(var_A: T.handle, B: T.Buffer((4096,), "float16"), var_rms_norm: T.handle): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + n = T.int32() + A = T.match_buffer(var_A, (1, n, 4096), "float16") + rms_norm_1 = T.match_buffer(var_rms_norm, (1, n, 4096), "float16") + # with T.block("root"): + Ared_temp_shared = T.alloc_buffer((1, n), scope="shared") + Ared_temp_rf_local = T.alloc_buffer((64, 1, n), scope="local") + for ax0_fused in T.thread_binding(n, thread="blockIdx.x"): + for ax1_fused_1 in T.thread_binding(64, thread="threadIdx.x", annotations={"pragma_auto_unroll_max_step": 256, "pragma_unroll_explicit": 1}): + with T.block("Ared_temp_rf_init"): + vax1_fused_1, v0 = T.axis.remap("SS", [ax1_fused_1, ax0_fused]) + T.reads() + T.writes(Ared_temp_rf_local[vax1_fused_1, 0, v0]) + Ared_temp_rf_local[vax1_fused_1, 0, v0] = T.float32(0) + for ax1_fused_0, u in T.grid(64, 1): + with T.block("Ared_temp_rf_update"): + vax1_fused_1, v0, vax1_fused_0 = T.axis.remap("SSR", [ax1_fused_1, ax0_fused, ax1_fused_0]) + T.reads(Ared_temp_rf_local[vax1_fused_1, 0, v0], A[0, v0, vax1_fused_0 * 64 + vax1_fused_1]) + T.writes(Ared_temp_rf_local[vax1_fused_1, 0, v0]) + Ared_temp_rf_local[vax1_fused_1, 0, v0] = Ared_temp_rf_local[vax1_fused_1, 0, v0] + T.Cast("float32", A[0, v0, vax1_fused_0 * 64 + vax1_fused_1]) * T.Cast("float32", A[0, v0, vax1_fused_0 * 64 + vax1_fused_1]) + for ax1_fused in range(1): + for ax0 in T.thread_binding(64, thread="threadIdx.x"): + with T.block("Ared_temp"): + vax1_fused_1, v0 = T.axis.remap("RS", [ax0, ax0_fused]) + T.reads(Ared_temp_rf_local[vax1_fused_1, 0, v0]) + T.writes(Ared_temp_shared[0, v0]) + with T.init(): + Ared_temp_shared[0, v0] = T.float32(0) + Ared_temp_shared[0, v0] = Ared_temp_shared[0, v0] + Ared_temp_rf_local[vax1_fused_1, 0, v0] + for ax0_fused_0 in range(64): + for ax0_fused_1 in T.thread_binding(64, thread="threadIdx.x"): + with T.block("rms_norm"): + v0 = T.axis.spatial(n, ax0_fused) + v1 = T.axis.spatial(4096, ax0_fused_0 * 64 + ax0_fused_1) + T.reads(B[v1], A[0, v0, v1], Ared_temp_shared[0, v0]) + T.writes(rms_norm_1[0, v0, v1]) + rms_norm_1[0, v0, v1] = T.Cast("float16", T.Cast("float32", B[v1]) * (T.Cast("float32", A[0, v0, v1]) / T.sqrt(Ared_temp_shared[0, v0] * T.float32(0.000244140625) + T.float32(9.9999999999999995e-07)))) + +@T.prim_func(private=True) +def slice(var_A: T.handle, slice_1: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(4096)), "float16") + # with T.block("root"): + for i, j, k in T.grid(T.int64(1), T.int64(1), T.int64(4096)): + with T.block("slice"): + v_i, v_j, v_k = T.axis.remap("SSS", [i, j, k]) + T.reads(A[v_i, n - T.int64(1), v_k]) + T.writes(slice_1[v_i, v_j, v_k]) + slice_1[v_i, v_j, v_k] = A[v_i, n - T.int64(1), v_k] + +@T.prim_func(private=True) +def slice_after(var_A: T.handle, slice_1: T.Buffer((1, 1, 4096), "float16")): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + n = T.int32() + A = T.match_buffer(var_A, (1, n, 4096), "float16") + # with T.block("root"): + for ax0_fused_0 in T.thread_binding(16, thread="blockIdx.x"): + for ax0_fused_1 in T.thread_binding(256, thread="threadIdx.x"): + with T.block("slice"): + v0 = T.axis.spatial(4096, ax0_fused_0 * 256 + ax0_fused_1) + T.reads(A[0, n - 1, v0]) + T.writes(slice_1[0, 0, v0]) + slice_1[0, 0, v0] = A[0, n - 1, v0] + +@T.prim_func(private=True) +def NT_matmul2(var_A: T.handle, var_B: T.handle, var_NT_matmul: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + m = T.int64() + A = T.match_buffer(var_A, (T.int64(1), m, T.int64(32), T.int64(128)), "float16") + n = T.int64() + B = T.match_buffer(var_B, (T.int64(1), T.int64(32), n, m), "float16") + NT_matmul = T.match_buffer(var_NT_matmul, (T.int64(1), n, T.int64(32), T.int64(128)), "float16") + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), n, T.int64(32), T.int64(128), m): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(A[v_i0, v_k, v_i2, v_i3], B[v_i0, v_i2, v_i1, v_k]) + T.writes(NT_matmul[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + NT_matmul[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + NT_matmul[v_i0, v_i1, v_i2, v_i3] = NT_matmul[v_i0, v_i1, v_i2, v_i3] + A[v_i0, v_k, v_i2, v_i3] * B[v_i0, v_i2, v_i1, v_k] + +@T.prim_func(private=True) +def NT_matmul2_after(var_A: T.handle, var_B: T.handle, var_NT_matmul: T.handle): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + m = T.int32() + A = T.match_buffer(var_A, (1, m, 32, 128), "float16") + n = T.int32() + B = T.match_buffer(var_B, (1, 32, n, m), "float16") + NT_matmul = T.match_buffer(var_NT_matmul, (1, n, 32, 128), "float16") + # with T.block("root"): + NT_matmul_reindex_pad_local = T.alloc_buffer((32, 128, (n + 63) // 64 * 64), "float16", scope="local") + A_reindex_pad_shared = T.alloc_buffer((32, 128, (m + 15) // 16 * 16), "float16", scope="shared") + B_reindex_pad_shared = T.alloc_buffer((32, (n + 63) // 64 * 64, (m + 15) // 16 * 16), "float16", scope="shared") + for ax0_ax2_0_fused in T.thread_binding((n + 63) // 64 * 32, thread="blockIdx.y"): + for ax1_0 in T.thread_binding(4, thread="blockIdx.x"): + for ax2_1 in T.thread_binding(1, thread="vthread.y"): + for ax1_1 in T.thread_binding(1, thread="vthread.x"): + for ax2_2 in T.thread_binding(16, thread="threadIdx.y"): + for ax1_2 in T.thread_binding(8, thread="threadIdx.x", annotations={"pragma_auto_unroll_max_step": 256, "pragma_unroll_explicit": 1}): + for ax2_3_init, ax1_3_init in T.grid(4, 4): + with T.block("NT_matmul_init"): + v0 = T.axis.spatial(32, ax0_ax2_0_fused // ((n + 63) // 64)) + v1 = T.axis.spatial(128, ax1_0 * 32 + ax1_1 * 32 + ax1_2 * 4 + ax1_3_init) + v2 = T.axis.spatial((n + 63) // 64 * 64, ax0_ax2_0_fused % ((n + 63) // 64) * 64 + ax2_1 * 64 + ax2_2 * 4 + ax2_3_init) + T.reads() + T.writes(NT_matmul_reindex_pad_local[v0, v1, v2]) + NT_matmul_reindex_pad_local[v0, v1, v2] = T.float16(0) + for ax3_0 in range((m + 15) // 16): + for ax0_ax1_ax2_fused_0 in T.thread_binding(16, thread="threadIdx.y"): + for ax0_ax1_ax2_fused_1 in T.thread_binding(8, thread="threadIdx.x"): + for ax0_ax1_ax2_fused_2 in range(2): + for ax0_ax1_ax2_fused_3 in T.vectorized(2): + with T.block("A_reindex_pad_shared"): + v0 = T.axis.spatial(32, ax0_ax2_0_fused // ((n + 63) // 64)) + v1 = T.axis.spatial(128, ax1_0 * 32 + (ax0_ax1_ax2_fused_0 * 32 + ax0_ax1_ax2_fused_1 * 4 + ax0_ax1_ax2_fused_2 * 2 + ax0_ax1_ax2_fused_3) // 16) + v2 = T.axis.spatial((m + 15) // 16 * 16, ax3_0 * 16 + (ax0_ax1_ax2_fused_0 * 32 + ax0_ax1_ax2_fused_1 * 4 + ax0_ax1_ax2_fused_2 * 2 + ax0_ax1_ax2_fused_3) % 16) + T.reads(A[0, v2, v0, v1]) + T.writes(A_reindex_pad_shared[v0, v1, v2]) + T.block_attr({"buffer_dim_align": [[0, 1, 8, 2]]}) + A_reindex_pad_shared[v0, v1, v2] = T.if_then_else(v2 < m, A[0, v2, v0, v1], T.float16(0)) + for ax0_ax1_ax2_fused_0 in T.thread_binding(16, thread="threadIdx.y"): + for ax0_ax1_ax2_fused_1 in T.thread_binding(8, thread="threadIdx.x"): + for ax0_ax1_ax2_fused_2 in range(4): + for ax0_ax1_ax2_fused_3 in T.vectorized(2): + with T.block("B_reindex_pad_shared"): + v0 = T.axis.spatial(32, ax0_ax2_0_fused // ((n + 63) // 64)) + v1 = T.axis.spatial((n + 63) // 64 * 64, ax0_ax2_0_fused % ((n + 63) // 64) * 64 + (ax0_ax1_ax2_fused_0 * 64 + ax0_ax1_ax2_fused_1 * 8 + ax0_ax1_ax2_fused_2 * 2 + ax0_ax1_ax2_fused_3) // 16) + v2 = T.axis.spatial((m + 15) // 16 * 16, ax3_0 * 16 + (ax0_ax1_ax2_fused_0 * 64 + ax0_ax1_ax2_fused_1 * 8 + ax0_ax1_ax2_fused_2 * 2 + ax0_ax1_ax2_fused_3) % 16) + T.reads(B[0, v0, v1, v2]) + T.writes(B_reindex_pad_shared[v0, v1, v2]) + T.block_attr({"buffer_dim_align": [[0, 1, 8, 2]]}) + B_reindex_pad_shared[v0, v1, v2] = T.if_then_else(v1 < n and v2 < m, B[0, v0, v1, v2], T.float16(0)) + for ax3_1, ax2_3, ax1_3 in T.grid(16, 4, 4): + with T.block("NT_matmul_update"): + v0 = T.axis.spatial(32, ax0_ax2_0_fused // ((n + 63) // 64)) + v1 = T.axis.spatial(128, ax1_0 * 32 + ax1_1 * 32 + ax1_2 * 4 + ax1_3) + v2 = T.axis.spatial((n + 63) // 64 * 64, ax0_ax2_0_fused % ((n + 63) // 64) * 64 + ax2_1 * 64 + ax2_2 * 4 + ax2_3) + v3 = T.axis.reduce((m + 15) // 16 * 16, ax3_0 * 16 + ax3_1) + T.reads(NT_matmul_reindex_pad_local[v0, v1, v2], A_reindex_pad_shared[v0, v1, v3], B_reindex_pad_shared[v0, v2, v3]) + T.writes(NT_matmul_reindex_pad_local[v0, v1, v2]) + NT_matmul_reindex_pad_local[v0, v1, v2] = NT_matmul_reindex_pad_local[v0, v1, v2] + A_reindex_pad_shared[v0, v1, v3] * B_reindex_pad_shared[v0, v2, v3] + for ax0, ax1, ax2_0 in T.grid(1, 4, 2): + for ax2_1_1 in T.vectorized(2): + with T.block("NT_matmul_reindex_pad_local"): + v0 = T.axis.spatial(32, ax0_ax2_0_fused // ((n + 63) // 64) + ax0) + v1 = T.axis.spatial(128, ax1_0 * 32 + ax1_2 * 4 + ax1) + v2 = T.axis.spatial((n + 63) // 64 * 64, ax0_ax2_0_fused % ((n + 63) // 64) * 64 + ax2_2 * 4 + ax2_0 * 2 + ax2_1_1) + T.reads(NT_matmul_reindex_pad_local[v0, v1, v2]) + T.writes(NT_matmul[0, v2, v0, v1]) + if v2 < n: + NT_matmul[0, v2, v0, v1] = NT_matmul_reindex_pad_local[v0, v1, v2] + + +def get_dict_key(func): + return tvm.ir.structural_hash(func), func + + +tir_dispatch_dict = { + get_dict_key(fused_decode4_matmul3): fused_decode4_matmul3_after, + get_dict_key( + fused_decode6_fused_matmul7_add1 + ): fused_decode6_fused_matmul7_add1_after, + get_dict_key( + fused_decode5_fused_matmul6_multiply1 + ): fused_decode5_fused_matmul6_multiply1_after, + get_dict_key( + fused_decode5_fused_matmul6_silu1 + ): fused_decode5_fused_matmul6_silu1_after, + get_dict_key( + fused_decode4_fused_matmul4_add1 + ): fused_decode4_fused_matmul4_add1_after, + get_dict_key( + fused_decode3_fused_matmul1_cast2 + ): fused_decode3_fused_matmul1_cast2_after, + get_dict_key( + fused_decode2_fused_NT_matmul3_add + ): fused_decode2_fused_NT_matmul3_add_after, + get_dict_key(fused_decode_NT_matmul): fused_decode_NT_matmul_after, + get_dict_key(fused_decode2_NT_matmul): fused_decode2_NT_matmul_after, + get_dict_key(fused_decode4_NT_matmul3): fused_decode4_NT_matmul3_after, + get_dict_key( + fused_decode1_fused_NT_matmul2_silu + ): fused_decode1_fused_NT_matmul2_silu_after, + get_dict_key( + fused_decode1_fused_NT_matmul2_multiply + ): fused_decode1_fused_NT_matmul2_multiply_after, + get_dict_key( + fused_decode_fused_NT_matmul_add + ): fused_decode_fused_NT_matmul_add_after, + get_dict_key( + fused_decode4_fused_matmul6_add4 + ): sch_fused_decode4_fused_matmul6_add4(fused_decode4_fused_matmul6_add4), + get_dict_key( + fused_decode6_fused_matmul9_add7_cast8_cast12_add5 + ): sch_fused_decode6_fused_matmul9_add7_cast8_cast12_add5( + fused_decode6_fused_matmul9_add7_cast8_cast12_add5 + ), + get_dict_key( + fused_decode5_fused_matmul8_add6_gelu1_cast11 + ): sch_fused_decode5_fused_matmul8_add6_gelu1_cast11( + fused_decode5_fused_matmul8_add6_gelu1_cast11 + ), + get_dict_key(fused_decode81_fused_matmul1_cast2 + ): sch_fused_decode81_fused_matmul1_cast2(fused_decode81_fused_matmul1_cast2 + ), + get_dict_key( + fused_decode4_fused_matmul6_add4_add5 + ): sch_fused_decode4_fused_matmul6_add4_add5(fused_decode4_fused_matmul6_add4_add5), + get_dict_key(fused_decode3_matmul3): sch_fused_decode3_matmul3( + fused_decode3_matmul3 + ), + get_dict_key( + fused_decode6_fused_matmul9_add7_cast8_cast12_add5_cast7 + ): sch_fused_decode6_fused_matmul9_add7_cast8_cast12_add5_cast7( + fused_decode6_fused_matmul9_add7_cast8_cast12_add5_cast7 + ), + get_dict_key( + fused_decode2_fused_NT_matmul3_add6_gelu1_cast11 + ): fused_decode2_fused_NT_matmul3_add6_gelu1_cast11_after, + get_dict_key( + fused_decode1_fused_NT_matmul1_add4 + ): fused_decode1_fused_NT_matmul1_add4_after, + get_dict_key( + fused_decode3_fused_NT_matmul4_add7_cast8_cast12_add5 + ): fused_decode3_fused_NT_matmul4_add7_cast8_cast12_add5_after, + get_dict_key( + fused_decode1_fused_NT_matmul1_add4_add5 + ): fused_decode1_fused_NT_matmul1_add4_add5_after, + get_dict_key( + fused_decode3_fused_NT_matmul4_add7_cast8_cast12_add5_cast7 + ): fused_decode3_fused_NT_matmul4_add7_cast8_cast12_add5_cast7_after, + get_dict_key(fused_fused_decode9_matmul7): fused_fused_decode9_matmul7_after, + get_dict_key(fused_fused_decode7_matmul4): fused_fused_decode7_matmul4_after, + get_dict_key(fused_NT_matmul1_divide2_maximum1_minimum1_cast3): fused_NT_matmul1_divide2_maximum1_minimum1_cast3_after, + get_dict_key(NT_matmul3): NT_matmul3_after, + get_dict_key(slice): slice_after, + get_dict_key(rms_norm): rms_norm_after, + get_dict_key(NT_matmul2): NT_matmul2_after, +} + + +def lookup_func(func): + for (hash_value, func_before), f_after in tir_dispatch_dict.items(): + if tvm.ir.structural_hash(func) == hash_value and tvm.ir.structural_equal( + func, func_before + ): + return f_after + return None + + +@tvm.transform.module_pass(opt_level=0, name="DispatchTIROperatorAdreno") +class DispatchTIROperatorAdreno: + def transform_module( + self, mod: IRModule, ctx: tvm.transform.PassContext + ) -> IRModule: + for gv in mod.functions: + scheduled_func = lookup_func(mod[gv]) + if scheduled_func is not None: + mod[gv] = scheduled_func + + return mod diff --git a/mlc_llm/dispatch/gpt_neox/__init__.py b/mlc_llm/dispatch/gpt_neox/__init__.py new file mode 100644 index 0000000..cdf7c94 --- /dev/null +++ b/mlc_llm/dispatch/gpt_neox/__init__.py @@ -0,0 +1,13 @@ +def lookup(func): + from . import dolly_v2_3b, redpajama_incite_chat_3b_v1, redpajama_q4f32 + + ret = dolly_v2_3b.lookup(func) + if ret is not None: + return ret + ret = redpajama_incite_chat_3b_v1.lookup(func) + if ret is not None: + return ret + ret = redpajama_q4f32.lookup(func) + if ret is not None: + return ret + return None diff --git a/mlc_llm/dispatch/gpt_neox/dolly_v2_3b.py b/mlc_llm/dispatch/gpt_neox/dolly_v2_3b.py new file mode 100644 index 0000000..274f081 --- /dev/null +++ b/mlc_llm/dispatch/gpt_neox/dolly_v2_3b.py @@ -0,0 +1,1034 @@ +# pylint: disable=missing-docstring,line-too-long,invalid-name,too-many-statements,too-many-locals +import tvm +from tvm import tir +from tvm.script import tir as T + +from .dolly_v2_3b_mod import Module as MOD + + +# fmt: off +def fused_NT_matmul1_add3(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l3, l4, l5, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l3, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[2, 4, 8, 1, 2]) + l22, l23, l24, l25, l26 = sch.split(loop=l4, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[40, 2, 16, 2, 1]) + l32, l33, l34, l35, l36 = sch.split(loop=l5, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[320, 8, 1]) + l40, l41, l42 = sch.split(loop=l6, factors=[v37, v38, v39], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l13, l23, l33, l14, l24, l34, l40, l41, l15, l25, l35, l42, l16, l26, l36) + l43 = sch.fuse(l12, l22, l32, preserve_unit_iters=True) + sch.bind(loop=l43, thread_axis="blockIdx.x") + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="vthread.x") + l45 = sch.fuse(l14, l24, l34, preserve_unit_iters=True) + sch.bind(loop=l45, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=1024) + b46 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b46, loop=l45, preserve_unit_loops=True, index=-1) + b47 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b47, loop=l40, preserve_unit_loops=True, index=-1) + l52, l53, l54 = sch.get_loops(block=b47)[-3:] + sch.fuse(l52, l53, l54, preserve_unit_iters=True) + v56 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch", ann_val=v56) + b57 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l40, preserve_unit_loops=True, index=-1) + l62, l63 = sch.get_loops(block=b57)[-2:] + sch.fuse(l62, l63, preserve_unit_iters=True) + v65 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v65) + sch.reverse_compute_inline(block=b1) + v66 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.2, 0.2, 0.2, 0.2, 0.2], decision=2) + sch.annotate(block_or_loop=b2, ann_key="meta_schedule.unroll_explicit", ann_val=v66) + sch.enter_postproc() + sch.unannotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch") + l71 = sch.get_loops(block=b47)[-1] + _, l73, l74 = sch.split(loop=l71, factors=[None, 128, 4], preserve_unit_iters=True) + sch.vectorize(loop=l74) + sch.bind(loop=l73, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + l79 = sch.get_loops(block=b57)[-1] + _, l81, l82 = sch.split(loop=l79, factors=[None, 128, 4], preserve_unit_iters=True) + sch.vectorize(loop=l82) + sch.bind(loop=l81, thread_axis="threadIdx.x") + b83 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b83, ann_key="meta_schedule.unroll_explicit") + b120 = sch.get_block(name="NT_matmul", func_name="main") + l124 = sch.get_loops(block=b120)[4] + sch.decompose_reduction(block=b120, loop=l124) + + b1 = sch.get_block("lv10_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + _, b84, b85, b86, b87 = sch.get_child_blocks(b83) + l88 = sch.get_loops(block=b84)[0] + sch.annotate(block_or_loop=l88, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l88, ann_key="pragma_unroll_explicit", ann_val=1) + l95 = sch.get_loops(block=b85)[0] + sch.annotate(block_or_loop=l95, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l95, ann_key="pragma_unroll_explicit", ann_val=1) + l102 = sch.get_loops(block=b86)[0] + sch.annotate(block_or_loop=l102, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l102, ann_key="pragma_unroll_explicit", ann_val=1) + l114 = sch.get_loops(block=b87)[0] + sch.annotate(block_or_loop=l114, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l114, ann_key="pragma_unroll_explicit", ann_val=1) + + +def fused_NT_matmul1_add3_add5_add5(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="T_add_1", func_name="main") + b3 = sch.get_block(name="T_add_2", func_name="main") + b4 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l5, l6, l7, l8 = sch.get_loops(block=b0) + v9, v10, v11, v12, v13 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l14, l15, l16, l17, l18 = sch.split(loop=l5, factors=[v9, v10, v11, v12, v13], preserve_unit_iters=True) + v19, v20, v21, v22, v23 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[2, 8, 4, 2, 1]) + l24, l25, l26, l27, l28 = sch.split(loop=l6, factors=[v19, v20, v21, v22, v23], preserve_unit_iters=True) + v29, v30, v31, v32, v33 = sch.sample_perfect_tile(loop=l7, n=5, max_innermost_factor=64, decision=[20, 1, 64, 2, 1]) + l34, l35, l36, l37, l38 = sch.split(loop=l7, factors=[v29, v30, v31, v32, v33], preserve_unit_iters=True) + v39, v40, v41 = sch.sample_perfect_tile(loop=l8, n=3, max_innermost_factor=64, decision=[320, 1, 8]) + l42, l43, l44 = sch.split(loop=l8, factors=[v39, v40, v41], preserve_unit_iters=True) + sch.reorder(l14, l24, l34, l15, l25, l35, l16, l26, l36, l42, l43, l17, l27, l37, l44, l18, l28, l38) + l45 = sch.fuse(l14, l24, l34, preserve_unit_iters=True) + sch.bind(loop=l45, thread_axis="blockIdx.x") + l46 = sch.fuse(l15, l25, l35, preserve_unit_iters=True) + sch.bind(loop=l46, thread_axis="vthread.x") + l47 = sch.fuse(l16, l26, l36, preserve_unit_iters=True) + sch.bind(loop=l47, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=1024) + b48 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b48, loop=l47, preserve_unit_loops=True, index=-1) + b49 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b49, loop=l42, preserve_unit_loops=True, index=-1) + l54, l55, l56 = sch.get_loops(block=b49)[-3:] + sch.fuse(l54, l55, l56, preserve_unit_iters=True) + v58 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b49, ann_key="meta_schedule.cooperative_fetch", ann_val=v58) + b59 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b59, loop=l42, preserve_unit_loops=True, index=-1) + l64, l65 = sch.get_loops(block=b59)[-2:] + sch.fuse(l64, l65, preserve_unit_iters=True) + v67 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b59, ann_key="meta_schedule.cooperative_fetch", ann_val=v67) + sch.reverse_compute_inline(block=b3) + sch.reverse_compute_inline(block=b2) + sch.reverse_compute_inline(block=b1) + v68 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.2, 0.2, 0.2, 0.2, 0.2], decision=4) + sch.annotate(block_or_loop=b4, ann_key="meta_schedule.unroll_explicit", ann_val=v68) + sch.enter_postproc() + sch.unannotate(block_or_loop=b49, ann_key="meta_schedule.cooperative_fetch") + l73 = sch.get_loops(block=b49)[-1] + _, l75, l76 = sch.split(loop=l73, factors=[None, 256, 4], preserve_unit_iters=True) + sch.vectorize(loop=l76) + sch.bind(loop=l75, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b59, ann_key="meta_schedule.cooperative_fetch") + l81 = sch.get_loops(block=b59)[-1] + _, l83, l84 = sch.split(loop=l81, factors=[None, 256, 4], preserve_unit_iters=True) + sch.vectorize(loop=l84) + sch.bind(loop=l83, thread_axis="threadIdx.x") + b85 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b85, ann_key="meta_schedule.unroll_explicit") + b122 = sch.get_block(name="NT_matmul", func_name="main") + l126 = sch.get_loops(block=b122)[4] + sch.decompose_reduction(block=b122, loop=l126) + + b1 = sch.get_block("lv48_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + _, b86, b87, b88, b89 = sch.get_child_blocks(b85) + l90 = sch.get_loops(block=b86)[0] + sch.annotate(block_or_loop=l90, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l90, ann_key="pragma_unroll_explicit", ann_val=1) + l97 = sch.get_loops(block=b87)[0] + sch.annotate(block_or_loop=l97, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l97, ann_key="pragma_unroll_explicit", ann_val=1) + l104 = sch.get_loops(block=b88)[0] + sch.annotate(block_or_loop=l104, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l104, ann_key="pragma_unroll_explicit", ann_val=1) + l116 = sch.get_loops(block=b89)[0] + sch.annotate(block_or_loop=l116, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l116, ann_key="pragma_unroll_explicit", ann_val=1) + + +def fused_NT_matmul1_add3_add5_add5_cast5(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="T_add_1", func_name="main") + b3 = sch.get_block(name="T_add_2", func_name="main") + b4 = sch.get_block(name="compute", func_name="main") + b5 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l6, l7, l8, l9 = sch.get_loops(block=b0) + v10, v11, v12, v13, v14 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l15, l16, l17, l18, l19 = sch.split(loop=l6, factors=[v10, v11, v12, v13, v14], preserve_unit_iters=True) + v20, v21, v22, v23, v24 = sch.sample_perfect_tile(loop=l7, n=5, max_innermost_factor=64, decision=[2, 2, 16, 2, 1]) + l25, l26, l27, l28, l29 = sch.split(loop=l7, factors=[v20, v21, v22, v23, v24], preserve_unit_iters=True) + v30, v31, v32, v33, v34 = sch.sample_perfect_tile(loop=l8, n=5, max_innermost_factor=64, decision=[64, 2, 10, 1, 2]) + l35, l36, l37, l38, l39 = sch.split(loop=l8, factors=[v30, v31, v32, v33, v34], preserve_unit_iters=True) + v40, v41, v42 = sch.sample_perfect_tile(loop=l9, n=3, max_innermost_factor=64, decision=[64, 20, 2]) + l43, l44, l45 = sch.split(loop=l9, factors=[v40, v41, v42], preserve_unit_iters=True) + sch.reorder(l15, l25, l35, l16, l26, l36, l17, l27, l37, l43, l44, l18, l28, l38, l45, l19, l29, l39) + l46 = sch.fuse(l15, l25, l35, preserve_unit_iters=True) + sch.bind(loop=l46, thread_axis="blockIdx.x") + l47 = sch.fuse(l16, l26, l36, preserve_unit_iters=True) + sch.bind(loop=l47, thread_axis="vthread.x") + l48 = sch.fuse(l17, l27, l37, preserve_unit_iters=True) + sch.bind(loop=l48, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=1024) + b49 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b49, loop=l48, preserve_unit_loops=True, index=-1) + b50 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b50, loop=l43, preserve_unit_loops=True, index=-1) + l55, l56, l57 = sch.get_loops(block=b50)[-3:] + sch.fuse(l55, l56, l57, preserve_unit_iters=True) + v59 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b50, ann_key="meta_schedule.cooperative_fetch", ann_val=v59) + b60 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b60, loop=l43, preserve_unit_loops=True, index=-1) + l65, l66 = sch.get_loops(block=b60)[-2:] + sch.fuse(l65, l66, preserve_unit_iters=True) + v68 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b60, ann_key="meta_schedule.cooperative_fetch", ann_val=v68) + sch.reverse_compute_inline(block=b4) + sch.reverse_compute_inline(block=b3) + sch.reverse_compute_inline(block=b2) + sch.reverse_compute_inline(block=b1) + v69 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.2, 0.2, 0.2, 0.2, 0.2], decision=3) + sch.annotate(block_or_loop=b5, ann_key="meta_schedule.unroll_explicit", ann_val=v69) + sch.enter_postproc() + sch.unannotate(block_or_loop=b50, ann_key="meta_schedule.cooperative_fetch") + l74 = sch.get_loops(block=b50)[-1] + _, l76, l77 = sch.split(loop=l74, factors=[None, 160, 4], preserve_unit_iters=True) + sch.vectorize(loop=l77) + sch.bind(loop=l76, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b60, ann_key="meta_schedule.cooperative_fetch") + l82 = sch.get_loops(block=b60)[-1] + _, l84, l85 = sch.split(loop=l82, factors=[None, 160, 2], preserve_unit_iters=True) + sch.vectorize(loop=l85) + sch.bind(loop=l84, thread_axis="threadIdx.x") + b86 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b86, ann_key="meta_schedule.unroll_explicit") + b123 = sch.get_block(name="NT_matmul", func_name="main") + l127 = sch.get_loops(block=b123)[4] + sch.decompose_reduction(block=b123, loop=l127) + + b1 = sch.get_block("lv1815_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + _, b87, b88, b89, b90 = sch.get_child_blocks(b86) + l91 = sch.get_loops(block=b87)[0] + sch.annotate(block_or_loop=l91, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l91, ann_key="pragma_unroll_explicit", ann_val=1) + l98 = sch.get_loops(block=b88)[0] + sch.annotate(block_or_loop=l98, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l98, ann_key="pragma_unroll_explicit", ann_val=1) + l105 = sch.get_loops(block=b89)[0] + sch.annotate(block_or_loop=l105, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l105, ann_key="pragma_unroll_explicit", ann_val=1) + l117 = sch.get_loops(block=b90)[0] + sch.annotate(block_or_loop=l117, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l117, ann_key="pragma_unroll_explicit", ann_val=1) + + +def fused_NT_matmul3_add4_gelu1(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="T_multiply", func_name="main") + b3 = sch.get_block(name="compute", func_name="main") + b4 = sch.get_block(name="compute_1", func_name="main") + b5 = sch.get_block(name="compute_2", func_name="main") + b6 = sch.get_block(name="T_multiply_1", func_name="main") + b7 = sch.get_block(name="T_add_1", func_name="main") + b8 = sch.get_block(name="T_multiply_2", func_name="main") + b9 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l10, l11, l12, l13 = sch.get_loops(block=b0) + v14, v15, v16, v17, v18 = sch.sample_perfect_tile(loop=l10, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l19, l20, l21, l22, l23 = sch.split(loop=l10, factors=[v14, v15, v16, v17, v18], preserve_unit_iters=True) + v24, v25, v26, v27, v28 = sch.sample_perfect_tile(loop=l11, n=5, max_innermost_factor=64, decision=[2, 4, 8, 1, 2]) + l29, l30, l31, l32, l33 = sch.split(loop=l11, factors=[v24, v25, v26, v27, v28], preserve_unit_iters=True) + v34, v35, v36, v37, v38 = sch.sample_perfect_tile(loop=l12, n=5, max_innermost_factor=64, decision=[160, 4, 16, 1, 1]) + l39, l40, l41, l42, l43 = sch.split(loop=l12, factors=[v34, v35, v36, v37, v38], preserve_unit_iters=True) + v44, v45, v46 = sch.sample_perfect_tile(loop=l13, n=3, max_innermost_factor=64, decision=[64, 20, 2]) + l47, l48, l49 = sch.split(loop=l13, factors=[v44, v45, v46], preserve_unit_iters=True) + sch.reorder(l19, l29, l39, l20, l30, l40, l21, l31, l41, l47, l48, l22, l32, l42, l49, l23, l33, l43) + l50 = sch.fuse(l19, l29, l39, preserve_unit_iters=True) + sch.bind(loop=l50, thread_axis="blockIdx.x") + l51 = sch.fuse(l20, l30, l40, preserve_unit_iters=True) + sch.bind(loop=l51, thread_axis="vthread.x") + l52 = sch.fuse(l21, l31, l41, preserve_unit_iters=True) + sch.bind(loop=l52, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=1024) + b53 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b53, loop=l52, preserve_unit_loops=True, index=-1) + b54 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b54, loop=l47, preserve_unit_loops=True, index=-1) + l59, l60, l61 = sch.get_loops(block=b54)[-3:] + sch.fuse(l59, l60, l61, preserve_unit_iters=True) + v63 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b54, ann_key="meta_schedule.cooperative_fetch", ann_val=v63) + b64 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b64, loop=l47, preserve_unit_loops=True, index=-1) + l69, l70 = sch.get_loops(block=b64)[-2:] + sch.fuse(l69, l70, preserve_unit_iters=True) + v72 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b64, ann_key="meta_schedule.cooperative_fetch", ann_val=v72) + sch.compute_inline(block=b7) + sch.compute_inline(block=b6) + sch.compute_inline(block=b5) + sch.compute_inline(block=b4) + sch.compute_inline(block=b3) + sch.compute_inline(block=b2) + sch.compute_inline(block=b1) + sch.reverse_compute_inline(block=b8) + v73 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.2, 0.2, 0.2, 0.2, 0.2], decision=3) + sch.annotate(block_or_loop=b9, ann_key="meta_schedule.unroll_explicit", ann_val=v73) + sch.enter_postproc() + sch.unannotate(block_or_loop=b54, ann_key="meta_schedule.cooperative_fetch") + l85 = sch.get_loops(block=b54)[-1] + _, l87, l88 = sch.split(loop=l85, factors=[None, 128, 4], preserve_unit_iters=True) + sch.vectorize(loop=l88) + sch.bind(loop=l87, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b64, ann_key="meta_schedule.cooperative_fetch") + l93 = sch.get_loops(block=b64)[-1] + _, l95, l96 = sch.split(loop=l93, factors=[None, 128, 4], preserve_unit_iters=True) + sch.vectorize(loop=l96) + sch.bind(loop=l95, thread_axis="threadIdx.x") + b97 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b97, ann_key="meta_schedule.unroll_explicit") + b138 = sch.get_block(name="NT_matmul", func_name="main") + l142 = sch.get_loops(block=b138)[4] + sch.decompose_reduction(block=b138, loop=l142) + + b1 = sch.get_block("lv52_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + b98, b99, b100, b101, b102 = sch.get_child_blocks(b97) + l103 = sch.get_loops(block=b98)[0] + sch.annotate(block_or_loop=l103, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l103, ann_key="pragma_unroll_explicit", ann_val=1) + l110 = sch.get_loops(block=b99)[0] + sch.annotate(block_or_loop=l110, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l110, ann_key="pragma_unroll_explicit", ann_val=1) + l117 = sch.get_loops(block=b100)[0] + sch.annotate(block_or_loop=l117, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l117, ann_key="pragma_unroll_explicit", ann_val=1) + l129 = sch.get_loops(block=b101)[0] + sch.annotate(block_or_loop=l129, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l129, ann_key="pragma_unroll_explicit", ann_val=1) + l135 = sch.get_loops(block=b102)[0] + sch.annotate(block_or_loop=l135, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l135, ann_key="pragma_unroll_explicit", ann_val=1) + + +def fused_NT_matmul4_add3(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l3, l4, l5, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l3, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 1, 16, 1, 2]) + l22, l23, l24, l25, l26 = sch.split(loop=l4, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[128, 1, 5, 2, 2]) + l32, l33, l34, l35, l36 = sch.split(loop=l5, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[256, 20, 2]) + l40, l41, l42 = sch.split(loop=l6, factors=[v37, v38, v39], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l13, l23, l33, l14, l24, l34, l40, l41, l15, l25, l35, l42, l16, l26, l36) + l43 = sch.fuse(l12, l22, l32, preserve_unit_iters=True) + sch.bind(loop=l43, thread_axis="blockIdx.x") + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="vthread.x") + l45 = sch.fuse(l14, l24, l34, preserve_unit_iters=True) + sch.bind(loop=l45, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=1024) + b46 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b46, loop=l45, preserve_unit_loops=True, index=-1) + b47 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b47, loop=l40, preserve_unit_loops=True, index=-1) + l52, l53, l54 = sch.get_loops(block=b47)[-3:] + sch.fuse(l52, l53, l54, preserve_unit_iters=True) + v56 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch", ann_val=v56) + b57 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l40, preserve_unit_loops=True, index=-1) + l62, l63 = sch.get_loops(block=b57)[-2:] + sch.fuse(l62, l63, preserve_unit_iters=True) + v65 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=0) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v65) + sch.reverse_compute_inline(block=b1) + v66 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.2, 0.2, 0.2, 0.2, 0.2], decision=2) + sch.annotate(block_or_loop=b2, ann_key="meta_schedule.unroll_explicit", ann_val=v66) + sch.enter_postproc() + sch.unannotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch") + l71 = sch.get_loops(block=b47)[-1] + _, l73, l74 = sch.split(loop=l71, factors=[None, 80, 2], preserve_unit_iters=True) + sch.vectorize(loop=l74) + sch.bind(loop=l73, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + l79 = sch.get_loops(block=b57)[-1] + _, l81 = sch.split(loop=l79, factors=[None, 80], preserve_unit_iters=True) + sch.bind(loop=l81, thread_axis="threadIdx.x") + b82 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b82, ann_key="meta_schedule.unroll_explicit") + b118 = sch.get_block(name="NT_matmul", func_name="main") + l122 = sch.get_loops(block=b118)[4] + sch.decompose_reduction(block=b118, loop=l122) + + b1 = sch.get_block("lv56_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + _, b83, b84, b85, b86 = sch.get_child_blocks(b82) + l87 = sch.get_loops(block=b83)[0] + sch.annotate(block_or_loop=l87, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l87, ann_key="pragma_unroll_explicit", ann_val=1) + l94 = sch.get_loops(block=b84)[0] + sch.annotate(block_or_loop=l94, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l94, ann_key="pragma_unroll_explicit", ann_val=1) + l100 = sch.get_loops(block=b85)[0] + sch.annotate(block_or_loop=l100, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l100, ann_key="pragma_unroll_explicit", ann_val=1) + l112 = sch.get_loops(block=b86)[0] + sch.annotate(block_or_loop=l112, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l112, ann_key="pragma_unroll_explicit", ann_val=1) + + +def fused_NT_matmul_divide_maximum_minimum_cast2(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + + sch.pad_einsum(b0, [1, 1, 1, 32, 1]) + l1, l2, l3, l4, l5 = sch.get_loops(b0) + l6, l7 = sch.split(l4, [None, 32]) + sch.reorder(l6, l1, l2, l3, l7, l5) + + b1 = sch.get_block(name="T_divide", func_name="main") + b2 = sch.get_block(name="T_maximum", func_name="main") + b3 = sch.get_block(name="T_minimum", func_name="main") + b4 = sch.get_block(name="compute", func_name="main") + b5 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l6, l7, l8, l9, l10 = sch.get_loops(block=b0) + v11, v12, v13, v14, v15 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l16, l17, l18, l19, l20 = sch.split(loop=l6, factors=[v11, v12, v13, v14, v15], preserve_unit_iters=True) + v21, v22, v23, v24, v25 = sch.sample_perfect_tile(loop=l7, n=5, max_innermost_factor=64, decision=[8, 2, 2, 1, 1]) + l26, l27, l28, l29, l30 = sch.split(loop=l7, factors=[v21, v22, v23, v24, v25], preserve_unit_iters=True) + v31, v32, v33, v34, v35 = sch.sample_perfect_tile(loop=l8, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l36, l37, l38, l39, l40 = sch.split(loop=l8, factors=[v31, v32, v33, v34, v35], preserve_unit_iters=True) + v41, v42, v43, v44, v45 = sch.sample_perfect_tile(loop=l9, n=5, max_innermost_factor=64, decision=[4, 1, 32, 1, 1]) + l46, l47, l48, l49, l50 = sch.split(loop=l9, factors=[v41, v42, v43, v44, v45], preserve_unit_iters=True) + v51, v52, v53 = sch.sample_perfect_tile(loop=l10, n=3, max_innermost_factor=64, decision=[2, 1, 40]) + l54, l55, l56 = sch.split(loop=l10, factors=[v51, v52, v53], preserve_unit_iters=True) + sch.reorder(l16, l26, l36, l46, l17, l27, l37, l47, l18, l28, l38, l48, l54, l55, l19, l29, l39, l49, l56, l20, l30, l40, l50) + l57 = sch.fuse(l16, l26, l36, l46, preserve_unit_iters=True) + sch.bind(loop=l57, thread_axis="blockIdx.x") + l58 = sch.fuse(l17, l27, l37, l47, preserve_unit_iters=True) + sch.bind(loop=l58, thread_axis="vthread.x") + l59 = sch.fuse(l18, l28, l38, l48, preserve_unit_iters=True) + sch.bind(loop=l59, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=1024) + b60 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b60, loop=l59, preserve_unit_loops=True, index=-1) + b61 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b61, loop=l54, preserve_unit_loops=True, index=-1) + l66, l67, l68, l69 = sch.get_loops(block=b61)[-4:] + sch.fuse(l66, l67, l68, l69, preserve_unit_iters=True) + v71 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=0) + sch.annotate(block_or_loop=b61, ann_key="meta_schedule.cooperative_fetch", ann_val=v71) + b72 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b72, loop=l54, preserve_unit_loops=True, index=-1) + l77, l78, l79, l80 = sch.get_loops(block=b72)[-4:] + sch.fuse(l77, l78, l79, l80, preserve_unit_iters=True) + v82 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=0) + sch.annotate(block_or_loop=b72, ann_key="meta_schedule.cooperative_fetch", ann_val=v82) + sch.reverse_compute_inline(block=b4) + sch.reverse_compute_inline(block=b3) + sch.reverse_compute_inline(block=b2) + sch.reverse_compute_inline(block=b1) + v83 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.2, 0.2, 0.2, 0.2, 0.2], decision=3) + sch.annotate(block_or_loop=b5, ann_key="meta_schedule.unroll_explicit", ann_val=v83) + sch.enter_postproc() + sch.unannotate(block_or_loop=b61, ann_key="meta_schedule.cooperative_fetch") + l88 = sch.get_loops(block=b61)[-1] + _, l90 = sch.split(loop=l88, factors=[None, 64], preserve_unit_iters=True) + sch.bind(loop=l90, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b72, ann_key="meta_schedule.cooperative_fetch") + l95 = sch.get_loops(block=b72)[-1] + _, l97 = sch.split(loop=l95, factors=[None, 64], preserve_unit_iters=True) + sch.bind(loop=l97, thread_axis="threadIdx.x") + b98 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b98, ann_key="meta_schedule.unroll_explicit") + + b136 = sch.get_block(name="NT_matmul", func_name="main") + l140 = sch.get_loops(block=b136)[4] + sch.decompose_reduction(block=b136, loop=l140) + + b1 = sch.get_block("lv1870_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + _, b99, b100, b101, b102 = sch.get_child_blocks(b98) + l103 = sch.get_loops(block=b99)[0] + sch.annotate(block_or_loop=l103, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l103, ann_key="pragma_unroll_explicit", ann_val=1) + l109 = sch.get_loops(block=b100)[0] + sch.annotate(block_or_loop=l109, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l109, ann_key="pragma_unroll_explicit", ann_val=1) + l115 = sch.get_loops(block=b101)[0] + sch.annotate(block_or_loop=l115, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l115, ann_key="pragma_unroll_explicit", ann_val=1) + l129 = sch.get_loops(block=b102)[0] + sch.annotate(block_or_loop=l129, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l129, ann_key="pragma_unroll_explicit", ann_val=1) + + +def fused_NT_matmul2_divide1_maximum1_minimum1_cast7(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + sch.pad_einsum(b0, [1, 1, 32, 32, 1]) + l1, l2, l3, l4, l5 = sch.get_loops(b0) + l6, l7 = sch.split(l3, [None, 32]) + l8, l9 = sch.split(l4, [None, 32]) + sch.reorder(l6, l8, l1, l2, l7, l9, l5) + + b1 = sch.get_block(name="T_divide", func_name="main") + b2 = sch.get_block(name="T_maximum", func_name="main") + b3 = sch.get_block(name="T_minimum", func_name="main") + b4 = sch.get_block(name="compute", func_name="main") + b5 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, _, l6, l7, l8, l9, l10 = sch.get_loops(block=b0) + v11, v12, v13, v14, v15 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l16, l17, l18, l19, l20 = sch.split(loop=l6, factors=[v11, v12, v13, v14, v15], preserve_unit_iters=True) + v21, v22, v23, v24, v25 = sch.sample_perfect_tile(loop=l7, n=5, max_innermost_factor=64, decision=[32, 1, 1, 1, 1]) + l26, l27, l28, l29, l30 = sch.split(loop=l7, factors=[v21, v22, v23, v24, v25], preserve_unit_iters=True) + v31, v32, v33, v34, v35 = sch.sample_perfect_tile(loop=l8, n=5, max_innermost_factor=64, decision=[2, 1, 4, 1, 16]) + l36, l37, l38, l39, l40 = sch.split(loop=l8, factors=[v31, v32, v33, v34, v35], preserve_unit_iters=True) + v41, v42, v43, v44, v45 = sch.sample_perfect_tile(loop=l9, n=5, max_innermost_factor=64, decision=[4, 2, 16, 1, 1]) + l46, l47, l48, l49, l50 = sch.split(loop=l9, factors=[v41, v42, v43, v44, v45], preserve_unit_iters=True) + v51, v52, v53 = sch.sample_perfect_tile(loop=l10, n=3, max_innermost_factor=64, decision=[10, 1, 8]) + l54, l55, l56 = sch.split(loop=l10, factors=[v51, v52, v53], preserve_unit_iters=True) + sch.reorder(l16, l26, l36, l46, l17, l27, l37, l47, l18, l28, l38, l48, l54, l55, l19, l29, l39, l49, l56, l20, l30, l40, l50) + l57 = sch.fuse(l16, l26, l36, l46, preserve_unit_iters=True) + sch.bind(loop=l57, thread_axis="blockIdx.x") + l58 = sch.fuse(l17, l27, l37, l47, preserve_unit_iters=True) + sch.bind(loop=l58, thread_axis="vthread.x") + l59 = sch.fuse(l18, l28, l38, l48, preserve_unit_iters=True) + sch.bind(loop=l59, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=1024) + b60 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b60, loop=l59, preserve_unit_loops=True, index=-1) + b61 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b61, loop=l54, preserve_unit_loops=True, index=-1) + l66, l67, l68, l69 = sch.get_loops(block=b61)[-4:] + sch.fuse(l66, l67, l68, l69, preserve_unit_iters=True) + v71 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b61, ann_key="meta_schedule.cooperative_fetch", ann_val=v71) + b72 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b72, loop=l54, preserve_unit_loops=True, index=-1) + l77, l78, l79, l80 = sch.get_loops(block=b72)[-4:] + sch.fuse(l77, l78, l79, l80, preserve_unit_iters=True) + v82 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b72, ann_key="meta_schedule.cooperative_fetch", ann_val=v82) + sch.reverse_compute_inline(block=b4) + sch.reverse_compute_inline(block=b3) + sch.reverse_compute_inline(block=b2) + sch.reverse_compute_inline(block=b1) + v83 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.2, 0.2, 0.2, 0.2, 0.2], decision=4) + sch.annotate(block_or_loop=b5, ann_key="meta_schedule.unroll_explicit", ann_val=v83) + sch.enter_postproc() + sch.unannotate(block_or_loop=b61, ann_key="meta_schedule.cooperative_fetch") + l88 = sch.get_loops(block=b61)[-1] + _, l90, l91 = sch.split(loop=l88, factors=[None, 32, 4], preserve_unit_iters=True) + sch.vectorize(loop=l91) + sch.bind(loop=l90, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b72, ann_key="meta_schedule.cooperative_fetch") + l96 = sch.get_loops(block=b72)[-1] + _, l98, l99 = sch.split(loop=l96, factors=[None, 32, 2], preserve_unit_iters=True) + sch.vectorize(loop=l99) + sch.bind(loop=l98, thread_axis="threadIdx.x") + b100 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b100, ann_key="meta_schedule.unroll_explicit") + b140 = sch.get_block(name="NT_matmul", func_name="main") + l144 = sch.get_loops(block=b140)[5] + sch.decompose_reduction(block=b140, loop=l144) + + b1 = sch.get_block("lv35_pad") + sch.compute_inline(b1) + b1 = sch.get_block("lv36_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + _, b101, b102, b103, b104 = sch.get_child_blocks(b100) + l105 = sch.get_loops(block=b101)[0] + sch.annotate(block_or_loop=l105, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l105, ann_key="pragma_unroll_explicit", ann_val=1) + l112 = sch.get_loops(block=b102)[0] + sch.annotate(block_or_loop=l112, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l112, ann_key="pragma_unroll_explicit", ann_val=1) + l119 = sch.get_loops(block=b103)[0] + sch.annotate(block_or_loop=l119, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l119, ann_key="pragma_unroll_explicit", ann_val=1) + l133 = sch.get_loops(block=b104)[0] + sch.annotate(block_or_loop=l133, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l133, ann_key="pragma_unroll_explicit", ann_val=1) + + +def matmul1(sch: tir.Schedule): + b0 = sch.get_block(name="matmul", func_name="main") + sch.pad_einsum(b0, [1, 1, 1, 1, 32]) + l1, l2, l3, l4, k = sch.get_loops(b0) + k0, k1 = sch.split(k, [None, 32]) + sch.reorder(l1, l2, l3, k0, l4, k1) + + b1 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + l2, l3, l4, _, l5, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l2, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l2, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[8, 2, 2, 1, 1]) + l22, l23, l24, l25, l26 = sch.split(loop=l3, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l32, l33, l34, l35, l36 = sch.split(loop=l4, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39, v40, v41 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[5, 1, 16, 1, 1]) + l42, l43, l44, l45, l46 = sch.split(loop=l5, factors=[v37, v38, v39, v40, v41], preserve_unit_iters=True) + v47, v48, v49 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[4, 8, 1]) + l50, l51, l52 = sch.split(loop=l6, factors=[v47, v48, v49], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l42, l13, l23, l33, l43, l14, l24, l34, l44, k0, l50, l51, l15, l25, l35, l45, l52, l16, l26, l36, l46) + l53 = sch.fuse(l12, l22, l32, l42, preserve_unit_iters=True) + sch.bind(loop=l53, thread_axis="blockIdx.x") + l54 = sch.fuse(l13, l23, l33, l43, preserve_unit_iters=True) + sch.bind(loop=l54, thread_axis="vthread.x") + l55 = sch.fuse(l14, l24, l34, l44, preserve_unit_iters=True) + sch.bind(loop=l55, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=1024) + b56 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b56, loop=l55, preserve_unit_loops=True, index=-1) + b57 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l50, preserve_unit_loops=True, index=-1) + l62, l63, l64, l65 = sch.get_loops(block=b57)[-4:] + sch.fuse(l62, l63, l64, l65, preserve_unit_iters=True) + v67 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=0) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v67) + b68 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b68, loop=l50, preserve_unit_loops=True, index=-1) + l73, l74, l75, l76 = sch.get_loops(block=b68)[-4:] + sch.fuse(l73, l74, l75, l76, preserve_unit_iters=True) + v78 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b68, ann_key="meta_schedule.cooperative_fetch", ann_val=v78) + v79 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.2, 0.2, 0.2, 0.2, 0.2], decision=4) + sch.annotate(block_or_loop=b1, ann_key="meta_schedule.unroll_explicit", ann_val=v79) + sch.enter_postproc() + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + l84 = sch.get_loops(block=b57)[-1] + _, l86 = sch.split(loop=l84, factors=[None, 32], preserve_unit_iters=True) + sch.bind(loop=l86, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b68, ann_key="meta_schedule.cooperative_fetch") + l91 = sch.get_loops(block=b68)[-1] + _, l93, l94 = sch.split(loop=l91, factors=[None, 32, 4], preserve_unit_iters=True) + sch.vectorize(loop=l94) + sch.bind(loop=l93, thread_axis="threadIdx.x") + b95 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b95, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("A_pad") + sch.compute_inline(b1) + b1 = sch.get_block("B_pad") + sch.compute_inline(b1) + + b96, b97, b98, b99 = sch.get_child_blocks(b95) + l100 = sch.get_loops(block=b96)[0] + sch.annotate(block_or_loop=l100, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l100, ann_key="pragma_unroll_explicit", ann_val=1) + l106 = sch.get_loops(block=b97)[0] + sch.annotate(block_or_loop=l106, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l106, ann_key="pragma_unroll_explicit", ann_val=1) + l113 = sch.get_loops(block=b98)[0] + sch.annotate(block_or_loop=l113, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l113, ann_key="pragma_unroll_explicit", ann_val=1) + l127 = sch.get_loops(block=b99)[0] + sch.annotate(block_or_loop=l127, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l127, ann_key="pragma_unroll_explicit", ann_val=1) + b134 = sch.get_block(name="matmul", func_name="main") + l138 = sch.get_loops(block=b134)[3] + sch.decompose_reduction(block=b134, loop=l138) + + +def matmul8(sch: tir.Schedule): + b0 = sch.get_block(name="matmul", func_name="main") + sch.pad_einsum(b0, [1, 1, 32, 1, 32]) + l1, l2, l3, l4, k = sch.get_loops(b0) + s0, s1 = sch.split(l3, [None, 32]) + k0, k1 = sch.split(k, [None, 32]) + sch.reorder(s0, l1, l2, s1, k0, l4, k1) + + b1 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l2, l3, l4, _, l5, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l2, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l2, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[16, 1, 1, 1, 2]) + l22, l23, l24, l25, l26 = sch.split(loop=l3, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 1, 32, 4, 1]) + l32, l33, l34, l35, l36 = sch.split(loop=l4, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39, v40, v41 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[2, 2, 5, 1, 4]) + l42, l43, l44, l45, l46 = sch.split(loop=l5, factors=[v37, v38, v39, v40, v41], preserve_unit_iters=True) + v47, v48, v49 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[2, 2, 8]) + l50, l51, l52 = sch.split(loop=l6, factors=[v47, v48, v49], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l42, l13, l23, l33, l43, l14, l24, l34, l44, k0, l50, l51, l15, l25, l35, l45, l52, l16, l26, l36, l46) + l53 = sch.fuse(l12, l22, l32, l42, preserve_unit_iters=True) + sch.bind(loop=l53, thread_axis="blockIdx.x") + l54 = sch.fuse(l13, l23, l33, l43, preserve_unit_iters=True) + sch.bind(loop=l54, thread_axis="vthread.x") + l55 = sch.fuse(l14, l24, l34, l44, preserve_unit_iters=True) + sch.bind(loop=l55, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=1024) + b56 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b56, loop=l55, preserve_unit_loops=True, index=-1) + b57 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l50, preserve_unit_loops=True, index=-1) + l62, l63, l64, l65 = sch.get_loops(block=b57)[-4:] + sch.fuse(l62, l63, l64, l65, preserve_unit_iters=True) + v67 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v67) + b68 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b68, loop=l50, preserve_unit_loops=True, index=-1) + l73, l74, l75, l76 = sch.get_loops(block=b68)[-4:] + sch.fuse(l73, l74, l75, l76, preserve_unit_iters=True) + v78 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=0) + sch.annotate(block_or_loop=b68, ann_key="meta_schedule.cooperative_fetch", ann_val=v78) + v79 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.2, 0.2, 0.2, 0.2, 0.2], decision=3) + sch.annotate(block_or_loop=b1, ann_key="meta_schedule.unroll_explicit", ann_val=v79) + sch.enter_postproc() + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + l84 = sch.get_loops(block=b57)[-1] + _, l86, l87 = sch.split(loop=l84, factors=[None, 40, 2], preserve_unit_iters=True) + sch.vectorize(loop=l87) + sch.bind(loop=l86, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b68, ann_key="meta_schedule.cooperative_fetch") + l92 = sch.get_loops(block=b68)[-1] + _, l94 = sch.split(loop=l92, factors=[None, 40], preserve_unit_iters=True) + sch.bind(loop=l94, thread_axis="threadIdx.x") + b95 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b95, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("A_pad") + sch.compute_inline(b1) + b1 = sch.get_block("B_pad") + sch.compute_inline(b1) + b1 = sch.get_block("matmul_pad") + sch.reverse_compute_inline(b1) + + b96, b97, b98, b99 = sch.get_child_blocks(b95) + l100 = sch.get_loops(block=b96)[0] + sch.annotate(block_or_loop=l100, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l100, ann_key="pragma_unroll_explicit", ann_val=1) + l107 = sch.get_loops(block=b97)[0] + sch.annotate(block_or_loop=l107, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l107, ann_key="pragma_unroll_explicit", ann_val=1) + l113 = sch.get_loops(block=b98)[0] + sch.annotate(block_or_loop=l113, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l113, ann_key="pragma_unroll_explicit", ann_val=1) + l127 = sch.get_loops(block=b99)[0] + sch.annotate(block_or_loop=l127, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l127, ann_key="pragma_unroll_explicit", ann_val=1) + b134 = sch.get_block(name="matmul", func_name="main") + l138= sch.get_loops(block=b134)[4] + sch.decompose_reduction(block=b134, loop=l138) + + +def fused_layer_norm1_cast6(sch: tir.Schedule): + b0 = sch.get_block(name="A_red_temp", func_name="main") + b1 = sch.get_block(name="T_layer_norm", func_name="main") + b2 = sch.get_block(name="compute", func_name="main") + b3 = sch.get_block(name="root", func_name="main") + sch.reverse_compute_inline(block=b2) + v4 = sch.sample_categorical(candidates=[4, 8, 16, 32, 64, 128, 256, 512], probs=[0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125], decision=5) + l5, l6, l7 = sch.get_loops(block=b0) + l8, l9 = sch.split(loop=l7, factors=[None, v4], preserve_unit_iters=True) + sch.bind(loop=l9, thread_axis="threadIdx.x") + v10 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.2, 0.2, 0.2, 0.2, 0.2], decision=1) + sch.annotate(block_or_loop=b3, ann_key="meta_schedule.unroll_explicit", ann_val=v10) + l11, l12, l13 = sch.get_loops(block=b1) + l14 = sch.fuse(l11, l12, l13, preserve_unit_iters=True) + l15, l16, l17 = sch.split(loop=l14, factors=[None, 256, 256], preserve_unit_iters=True) + sch.reorder(l16, l17, l15) + sch.bind(loop=l16, thread_axis="blockIdx.x") + sch.bind(loop=l17, thread_axis="threadIdx.x") + l18, l19, l20, l21 = sch.get_loops(block=b0) + l22 = sch.fuse(l18, l19, preserve_unit_iters=True) + sch.bind(loop=l22, thread_axis="blockIdx.x") + sch.enter_postproc() + b23 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b23, ann_key="meta_schedule.unroll_explicit") + b24, b25 = sch.get_child_blocks(b23) + l26, l27, l28 = sch.get_loops(block=b24) + sch.annotate(block_or_loop=l26, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l26, ann_key="pragma_unroll_explicit", ann_val=1) + l29, l30, l31 = sch.get_loops(block=b25) + sch.annotate(block_or_loop=l29, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l29, ann_key="pragma_unroll_explicit", ann_val=1) + + +def layer_norm1(sch: tir.Schedule): + b0 = sch.get_block(name="A_red_temp", func_name="main") + b1 = sch.get_block(name="T_layer_norm", func_name="main") + b2 = sch.get_block(name="root", func_name="main") + v3 = sch.sample_categorical(candidates=[4, 8, 16, 32, 64, 128, 256, 512], probs=[0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125], decision=4) + l4, l5, l6 = sch.get_loops(block=b0) + l7, l8 = sch.split(loop=l6, factors=[None, v3], preserve_unit_iters=True) + sch.bind(loop=l8, thread_axis="threadIdx.x") + v9 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.2, 0.2, 0.2, 0.2, 0.2], decision=3) + sch.annotate(block_or_loop=b2, ann_key="meta_schedule.unroll_explicit", ann_val=v9) + l10, l11, l12 = sch.get_loops(block=b1) + l13 = sch.fuse(l10, l11, l12, preserve_unit_iters=True) + l14, l15, l16 = sch.split(loop=l13, factors=[None, 256, 256], preserve_unit_iters=True) + sch.reorder(l15, l16, l14) + sch.bind(loop=l15, thread_axis="blockIdx.x") + sch.bind(loop=l16, thread_axis="threadIdx.x") + l17, l18, l19, l20 = sch.get_loops(block=b0) + l21 = sch.fuse(l17, l18, preserve_unit_iters=True) + sch.bind(loop=l21, thread_axis="blockIdx.x") + sch.enter_postproc() + b22 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b22, ann_key="meta_schedule.unroll_explicit") + b23, b24 = sch.get_child_blocks(b22) + l25, l26, l27 = sch.get_loops(block=b23) + sch.annotate(block_or_loop=l25, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l25, ann_key="pragma_unroll_explicit", ann_val=1) + l28, l29, l30 = sch.get_loops(block=b24) + sch.annotate(block_or_loop=l28, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l28, ann_key="pragma_unroll_explicit", ann_val=1) + + +def sch_softmax_cast(cast_to_fp16: bool): + def f(sch: tir.Schedule): + if cast_to_fp16: + b_cast = sch.get_block("compute") + sch.reverse_compute_inline(b_cast) + b0 = sch.get_block("T_softmax_exp") + sch.compute_inline(b0) + b1 = sch.get_block("T_softmax_norm") + l2, l3, l4, l5 = sch.get_loops(b1) + _, l7 = sch.split(l5, [None, 128]) + sch.bind(l7, "threadIdx.x") + b8 = sch.get_block("T_softmax_expsum") + sch.compute_at(b8, l4) + sch.set_scope(b8, 0, "shared") + _, _, _, l12 = sch.get_loops(b8) + _, l14 = sch.split(l12, [None, 128]) + sch.bind(l14, "threadIdx.x") + b15 = sch.get_block("T_softmax_maxelem") + sch.compute_at(b15, l4) + sch.set_scope(b15, 0, "shared") + _, _, _, l19 = sch.get_loops(b15) + _, l21 = sch.split(l19, [None, 128]) + sch.bind(l21, "threadIdx.x") + l22 = sch.fuse(l2, l3, l4) + sch.bind(l22, "blockIdx.x") + return f + + +@T.prim_func +def softmax_cast_mxn_before(p_lv37: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n, m = T.int64(), T.int64() + lv37 = T.match_buffer(p_lv37, (T.int64(1), T.int64(32), n, m)) + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, m), "float16") + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), n)) + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), n)) + var_T_softmax_norm_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv37[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.max(T_softmax_maxelem[v_i0, v_i1, v_i2], lv37[v_i0, v_i1, v_i2, v_k]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(lv37[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2, v_i3]) + T_softmax_exp[v_i0, v_i1, v_i2, v_i3] = T.exp(lv37[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_expsum[v_i0, v_i1, v_i2] = T.float32(0) + T_softmax_expsum[v_i0, v_i1, v_i2] = T_softmax_expsum[v_i0, v_i1, v_i2] + T_softmax_exp[v_i0, v_i1, v_i2, v_k] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_i3], T_softmax_expsum[v_i0, v_i1, v_i2]) + T.writes(var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"axis": 3}) + var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3] = T_softmax_exp[v_i0, v_i1, v_i2, v_i3] / T_softmax_expsum[v_i0, v_i1, v_i2] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("compute"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3]) + var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.Cast("float16", var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + + +@T.prim_func +def softmax_cast_mxn_after(var_A: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + m = T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), n, m)) + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), n, m), dtype="float16") + # with T.block("root"): + for i2_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.x"): + with T.block("T_softmax_maxelem_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0) + T.reads(A[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):(m + T.int64(127)) // T.int64(128) * T.int64(128)]) + T.writes(T_softmax_norm[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):m]) + T_softmax_maxelem_pad_0_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32)), scope="shared") + T_softmax_expsum_pad_0_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32)), scope="shared") + for i0, i1, i2_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(32), (m + T.int64(127)) // T.int64(128)): + for k_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_maxelem"): + v_i1_i, v_i2_i = T.axis.remap("SS", [i1, i2_1]) + v_k_i = T.axis.reduce(T.int64(32) * ((m + T.int64(127)) // T.int64(128)), k_0 * T.int64(128) + k_1) + T.reads(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i]) + T.writes(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]) + with T.init(): + T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.max(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i], T.if_then_else(v_i2_o * T.int64(32) + v_i2_i < n and v_k_i < m, A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i], T.float32(-3.4028234663852886e+38))) + for i0, i1, i2_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(32), (m + T.int64(127)) // T.int64(128)): + for k_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_expsum"): + v_i1_i, v_i2_i = T.axis.remap("SS", [i1, i2_1]) + v_k_i = T.axis.reduce(T.int64(32) * ((m + T.int64(127)) // T.int64(128)), k_0 * T.int64(128) + k_1) + T.reads(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i], T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]) + T.writes(T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i]) + with T.init(): + T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.float32(0) + T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] = T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] + T.if_then_else(v_i2_o * T.int64(32) + v_i2_i < n and v_k_i < m, T.exp(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i] - T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]), T.float32(0)) + for i0_i1_i2_1_i3_fused_0 in range((T.int64(32) * T.int64(32) * m) // T.int64(128)): + for i0_i1_i2_1_i3_fused_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_norm"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(32), (i0_i1_i2_1_i3_fused_0 * T.int64(128) + i0_i1_i2_1_i3_fused_1) // T.int64(32) // m) + v_i2_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_i3_fused_0 * T.int64(128) + i0_i1_i2_1_i3_fused_1) // m % T.int64(32)) + v_i3 = T.axis.spatial(m, (i0_i1_i2_1_i3_fused_0 * T.int64(128) + i0_i1_i2_1_i3_fused_1) % m) + T.where(i0_i1_i2_1_i3_fused_0 * T.int64(128) + i0_i1_i2_1_i3_fused_1 < T.int64(32) * T.int64(32) * m) + T.reads(T_softmax_expsum_pad_0_local[v_i0, v_i1, v_i2_i], A[v_i0, v_i1, v_i2_o * T.int64(32) + v_i2_i, v_i3], T_softmax_maxelem_pad_0_local[v_i0, v_i1, v_i2_i]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2_o * T.int64(32) + v_i2_i, v_i3]) + if v_i2_o * T.int64(32) + v_i2_i < n: + T_softmax_norm[v_i0, v_i1, v_i2_o * T.int64(32) + v_i2_i, v_i3] = T.Cast("float16", T.exp(A[v_i0, v_i1, v_i2_o * T.int64(32) + v_i2_i, v_i3] - T_softmax_maxelem_pad_0_local[v_i0, v_i1, v_i2_i]) / T_softmax_expsum_pad_0_local[v_i0, v_i1, v_i2_i]) + + +# fmt: on + + +def _get_dict(): + tvm.ir.assert_structural_equal(MOD["fused_softmax1_cast8"], softmax_cast_mxn_before) + func_dict = { + softmax_cast_mxn_before: softmax_cast_mxn_after, + } + for name, func in [ + ("fused_NT_matmul1_add3", fused_NT_matmul1_add3), + ("fused_NT_matmul1_add3_add5_add5", fused_NT_matmul1_add3_add5_add5), + ( + "fused_NT_matmul1_add3_add5_add5_cast5", + fused_NT_matmul1_add3_add5_add5_cast5, + ), + ("fused_NT_matmul3_add4_gelu1", fused_NT_matmul3_add4_gelu1), + ("fused_NT_matmul4_add3", fused_NT_matmul4_add3), + ( + "fused_NT_matmul_divide_maximum_minimum_cast2", + fused_NT_matmul_divide_maximum_minimum_cast2, + ), + ( + "fused_NT_matmul2_divide1_maximum1_minimum1_cast7", + fused_NT_matmul2_divide1_maximum1_minimum1_cast7, + ), + ("matmul1", matmul1), + ("matmul8", matmul8), + ("fused_softmax_cast3", sch_softmax_cast(True)), + ("fused_layer_norm1_cast6", fused_layer_norm1_cast6), + ("layer_norm1", layer_norm1), + ]: + sch = tir.Schedule(MOD[name]) + func(sch) + func_dict[MOD[name]] = sch.mod["main"] + return { + (tvm.ir.structural_hash(k), k): v.with_attr("tir.is_scheduled", True) + for k, v in func_dict.items() + } + + +DICT = _get_dict() + + +def lookup(func): + for (hash_value, func_before), f_after in DICT.items(): + if tvm.ir.structural_hash(func) == hash_value and tvm.ir.structural_equal( + func, func_before + ): + return f_after + return None diff --git a/mlc_llm/dispatch/gpt_neox/dolly_v2_3b_mod.py b/mlc_llm/dispatch/gpt_neox/dolly_v2_3b_mod.py new file mode 100644 index 0000000..e3ff44b --- /dev/null +++ b/mlc_llm/dispatch/gpt_neox/dolly_v2_3b_mod.py @@ -0,0 +1,511 @@ +# pylint: disable=pointless-string-statement,invalid-name,missing-docstring,line-too-long,too-many-locals,too-many-arguments,too-many-statements +from tvm.script import ir as I +from tvm.script import tir as T + +""" +Operators: +- fused_NT_matmul1_add3 +- fused_NT_matmul1_add3_add5_add5 +- fused_NT_matmul1_add3_add5_add5_cast5 +- fused_NT_matmul2_divide1_maximum1_minimum1_cast7 +- fused_NT_matmul3_add4_gelu1 +- fused_NT_matmul4_add3 +- fused_NT_matmul_divide_maximum_minimum_cast2 +- matmul1 +- matmul8 +- fused_softmax1_cast8 +- fused_softmax_cast3 +- fused_layer_norm1_cast6 +- layer_norm1 +""" + +# fmt: off + +@I.ir_module +class Module: + @T.prim_func + def fused_NT_matmul1_add3(p_lv10: T.handle, lv1173: T.Buffer((T.int64(2560), T.int64(2560)), "float16"), linear_bias: T.Buffer((T.int64(2560),), "float16"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv10 = T.match_buffer(p_lv10, (T.int64(1), n, T.int64(2560)), "float16") + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560)), "float16") + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv10[v_i0, v_i1, v_k], lv1173[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv10[v_i0, v_i1, v_k] * lv1173[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias[v_ax2] + + @T.prim_func + def fused_NT_matmul1_add3_add5_add5(p_lv48: T.handle, lv1194: T.Buffer((T.int64(2560), T.int64(2560)), "float16"), linear_bias3: T.Buffer((T.int64(2560),), "float16"), p_lv60: T.handle, p_lv2: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv48 = T.match_buffer(p_lv48, (T.int64(1), n, T.int64(2560)), "float16") + lv60 = T.match_buffer(p_lv60, (T.int64(1), n, T.int64(2560)), "float16") + lv2 = T.match_buffer(p_lv2, (T.int64(1), n, T.int64(2560)), "float16") + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560)), "float16") + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + var_T_add_intermediate_1 = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + var_T_add_intermediate_2 = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv48[v_i0, v_i1, v_k], lv1194[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv48[v_i0, v_i1, v_k] * lv1194[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias3[v_ax2]) + T.writes(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias3[v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv60[v_ax0, v_ax1, v_ax2], var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate_2[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate_2[v_ax0, v_ax1, v_ax2] = lv60[v_ax0, v_ax1, v_ax2] + var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add_2"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate_2[v_ax0, v_ax1, v_ax2], lv2[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate_2[v_ax0, v_ax1, v_ax2] + lv2[v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_NT_matmul1_add3_add5_add5_cast5(p_lv1815: T.handle, lv2496: T.Buffer((T.int64(2560), T.int64(2560)), "float16"), linear_bias189: T.Buffer((T.int64(2560),), "float16"), p_lv1827: T.handle, p_lv1772: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv1815 = T.match_buffer(p_lv1815, (T.int64(1), n, T.int64(2560)), "float16") + lv1827 = T.match_buffer(p_lv1827, (T.int64(1), n, T.int64(2560)), "float16") + lv1772 = T.match_buffer(p_lv1772, (T.int64(1), n, T.int64(2560)), "float16") + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + var_T_add_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + var_T_add_intermediate_1 = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + var_T_add_intermediate_2 = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1815[v_i0, v_i1, v_k], lv2496[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv1815[v_i0, v_i1, v_k] * lv2496[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias189[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias189[v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv1827[v_ax0, v_ax1, v_ax2], var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] = lv1827[v_ax0, v_ax1, v_ax2] + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add_2"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2], lv1772[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate_2[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate_2[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] + lv1772[v_ax0, v_ax1, v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate_2[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = T.Cast("float32", var_T_add_intermediate_2[v_i0, v_i1, v_i2]) + + @T.prim_func + def fused_NT_matmul2_divide1_maximum1_minimum1_cast7(p_lv35: T.handle, p_lv36: T.handle, p_lv5: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv35 = T.match_buffer(p_lv35, (T.int64(1), T.int64(32), n, T.int64(80)), "float16") + m = T.int64() + lv36 = T.match_buffer(p_lv36, (T.int64(1), T.int64(32), m, T.int64(80)), "float16") + lv5 = T.match_buffer(p_lv5, (T.int64(1), T.int64(1), n, m), "float16") + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m), "float16") + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m), "float16") + var_T_maximum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m), "float16") + var_T_minimum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m), "float16") + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, m, T.int64(80)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv35[v_i0, v_i1, v_i2, v_k], lv36[v_i0, v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv35[v_i0, v_i1, v_i2, v_k] * lv36[v_i0, v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float16(0.11179039301310044) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float16(-65504)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_minimum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv5[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.min(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv5[v_ax0, T.int64(0), v_ax2, v_ax3]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("compute"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3]) + var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.Cast("float32", var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + + @T.prim_func + def fused_NT_matmul3_add4_gelu1(p_lv52: T.handle, lv1201: T.Buffer((T.int64(10240), T.int64(2560)), "float16"), linear_bias4: T.Buffer((T.int64(10240),), "float16"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv52 = T.match_buffer(p_lv52, (T.int64(1), n, T.int64(2560)), "float16") + var_T_multiply_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(10240)), "float16") + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(10240)), "float16") + var_T_add_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(10240)), "float16") + T_multiply = T.alloc_buffer((T.int64(1), n, T.int64(10240)), "float16") + compute = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + compute_1 = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + compute_2 = T.alloc_buffer((T.int64(1), n, T.int64(10240)), "float16") + T_multiply_1 = T.alloc_buffer((T.int64(1), n, T.int64(10240)), "float16") + T_add = T.alloc_buffer((T.int64(1), n, T.int64(10240)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(10240), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv52[v_i0, v_i1, v_k], lv1201[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv52[v_i0, v_i1, v_k] * lv1201[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias4[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias4[v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply[v_ax0, v_ax1, v_ax2]) + T_multiply[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[v_ax0, v_ax1, v_ax2] * T.float16(0.70710678118654757) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(T_multiply[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.Cast("float32", T_multiply[v_i0, v_i1, v_i2]) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("compute_1"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(compute[v_i0, v_i1, v_i2]) + T.writes(compute_1[v_i0, v_i1, v_i2]) + compute_1[v_i0, v_i1, v_i2] = T.erf(compute[v_i0, v_i1, v_i2]) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("compute_2"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(compute_1[v_i0, v_i1, v_i2]) + T.writes(compute_2[v_i0, v_i1, v_i2]) + compute_2[v_i0, v_i1, v_i2] = T.Cast("float16", compute_1[v_i0, v_i1, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(compute_2[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T_multiply_1[v_ax0, v_ax1, v_ax2] = compute_2[v_ax0, v_ax1, v_ax2] * T.float16(0.5) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T.writes(T_add[v_ax0, v_ax1, v_ax2]) + T_add[v_ax0, v_ax1, v_ax2] = T.float16(0.5) + T_multiply_1[v_ax0, v_ax1, v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply_2"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2], T_add[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[v_ax0, v_ax1, v_ax2] * T_add[v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_NT_matmul4_add3(p_lv56: T.handle, lv1208: T.Buffer((T.int64(2560), T.int64(10240)), "float16"), linear_bias5: T.Buffer((T.int64(2560),), "float16"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv56 = T.match_buffer(p_lv56, (T.int64(1), n, T.int64(10240)), "float16") + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560)), "float16") + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(10240)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv56[v_i0, v_i1, v_k], lv1208[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv56[v_i0, v_i1, v_k] * lv1208[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias5[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias5[v_ax2] + + @T.prim_func + def fused_NT_matmul_divide_maximum_minimum_cast2(lv1869: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(80)), "float16"), p_lv1870: T.handle, p_lv1839: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv1870 = T.match_buffer(p_lv1870, (T.int64(1), T.int64(32), n, T.int64(80)), "float16") + lv1839 = T.match_buffer(p_lv1839, (T.int64(1), T.int64(1), T.int64(1), n), "float16") + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), T.int64(1), n)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + var_T_maximum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + var_T_minimum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n, T.int64(80)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv1869[v_i0, v_i1, v_i2, v_k], lv1870[v_i0, v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv1869[v_i0, v_i1, v_i2, v_k] * lv1870[v_i0, v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float16(0.11179039301310044) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float16(-65504)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_minimum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv1839[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.min(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv1839[v_ax0, T.int64(0), v_ax2, v_ax3]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("compute"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3]) + var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.Cast("float32", var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + + @T.prim_func + def fused_softmax1_cast8(p_lv43: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n, m = T.int64(), T.int64() + lv43 = T.match_buffer(p_lv43, (T.int64(1), T.int64(32), n, m)) + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, m), "float16") + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), n)) + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), n)) + var_T_softmax_norm_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv43[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.max(T_softmax_maxelem[v_i0, v_i1, v_i2], lv43[v_i0, v_i1, v_i2, v_k]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(lv43[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2, v_i3]) + T_softmax_exp[v_i0, v_i1, v_i2, v_i3] = T.exp(lv43[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_expsum[v_i0, v_i1, v_i2] = T.float32(0) + T_softmax_expsum[v_i0, v_i1, v_i2] = T_softmax_expsum[v_i0, v_i1, v_i2] + T_softmax_exp[v_i0, v_i1, v_i2, v_k] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_i3], T_softmax_expsum[v_i0, v_i1, v_i2]) + T.writes(var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"axis": 3}) + var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3] = T_softmax_exp[v_i0, v_i1, v_i2, v_i3] / T_softmax_expsum[v_i0, v_i1, v_i2] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("compute"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3]) + var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.Cast("float16", var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + + @T.prim_func + def fused_softmax_cast3(p_lv1877: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv1877 = T.match_buffer(p_lv1877, (T.int64(1), T.int64(32), T.int64(1), n)) + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), T.int64(1), n), "float16") + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1))) + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1))) + var_T_softmax_norm_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1877[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.max(T_softmax_maxelem[v_i0, v_i1, v_i2], lv1877[v_i0, v_i1, v_i2, v_k]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(lv1877[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2, v_i3]) + T_softmax_exp[v_i0, v_i1, v_i2, v_i3] = T.exp(lv1877[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_expsum[v_i0, v_i1, v_i2] = T.float32(0) + T_softmax_expsum[v_i0, v_i1, v_i2] = T_softmax_expsum[v_i0, v_i1, v_i2] + T_softmax_exp[v_i0, v_i1, v_i2, v_k] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_i3], T_softmax_expsum[v_i0, v_i1, v_i2]) + T.writes(var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"axis": 3}) + var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3] = T_softmax_exp[v_i0, v_i1, v_i2, v_i3] / T_softmax_expsum[v_i0, v_i1, v_i2] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("compute"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3]) + var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.Cast("float16", var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + + @T.prim_func + def matmul1(var_A: T.handle, var_B: T.handle, matmul: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(80)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), T.int64(1), n), "float16") + B = T.match_buffer(var_B, (T.int64(1), T.int64(32), n, T.int64(80)), "float16") + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), T.int64(1), T.int64(80), n): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(A[v_i0, v_i1, v_i2, v_k], B[v_i0, v_i1, v_k, v_i3]) + T.writes(matmul[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + matmul[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + matmul[v_i0, v_i1, v_i2, v_i3] = matmul[v_i0, v_i1, v_i2, v_i3] + A[v_i0, v_i1, v_i2, v_k] * B[v_i0, v_i1, v_k, v_i3] + + @T.prim_func + def matmul8(var_A: T.handle, var_B: T.handle, var_matmul: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n, m = T.int64(), T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), n, m), "float16") + B = T.match_buffer(var_B, (T.int64(1), T.int64(32), m, T.int64(80)), "float16") + matmul = T.match_buffer(var_matmul, (T.int64(1), T.int64(32), n, T.int64(80)), "float16") + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, T.int64(80), m): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(A[v_i0, v_i1, v_i2, v_k], B[v_i0, v_i1, v_k, v_i3]) + T.writes(matmul[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + matmul[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + matmul[v_i0, v_i1, v_i2, v_i3] = matmul[v_i0, v_i1, v_i2, v_i3] + A[v_i0, v_i1, v_i2, v_k] * B[v_i0, v_i1, v_k, v_i3] + + @T.prim_func + def layer_norm1(var_A: T.handle, B: T.Buffer((T.int64(2560),), "float32"), C: T.Buffer((T.int64(2560),), "float32"), var_T_layer_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(2560))) + T_layer_norm = T.match_buffer(var_T_layer_norm, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + A_red_temp_v0 = T.alloc_buffer((T.int64(1), n)) + A_red_temp_v1 = T.alloc_buffer((T.int64(1), n)) + for ax0, ax1, k2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("A_red_temp"): + v_ax0, v_ax1, v_k2 = T.axis.remap("SSR", [ax0, ax1, k2]) + T.reads(A[v_ax0, v_ax1, v_k2]) + T.writes(A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1]) + with T.init(): + A_red_temp_v0[v_ax0, v_ax1] = T.float32(0) + A_red_temp_v1[v_ax0, v_ax1] = T.float32(0) + v_A_red_temp_v0: T.float32 = A_red_temp_v0[v_ax0, v_ax1] + A[v_ax0, v_ax1, v_k2] + v_A_red_temp_v1: T.float32 = A_red_temp_v1[v_ax0, v_ax1] + A[v_ax0, v_ax1, v_k2] * A[v_ax0, v_ax1, v_k2] + A_red_temp_v0[v_ax0, v_ax1] = v_A_red_temp_v0 + A_red_temp_v1[v_ax0, v_ax1] = v_A_red_temp_v1 + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_layer_norm"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[v_ax0, v_ax1, v_ax2], A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1], B[v_ax2], C[v_ax2]) + T.writes(T_layer_norm[v_ax0, v_ax1, v_ax2]) + T_layer_norm[v_ax0, v_ax1, v_ax2] = (A[v_ax0, v_ax1, v_ax2] - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) * T.rsqrt(A_red_temp_v1[v_ax0, v_ax1] * T.float32(0.00039062500000000002) - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002) * (A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) + T.float32(1.0000000000000001e-05)) * B[v_ax2] + C[v_ax2] + + @T.prim_func + def fused_layer_norm1_cast6(p_lv6: T.handle, weight1: T.Buffer((T.int64(2560),), "float32"), bias: T.Buffer((T.int64(2560),), "float32"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv6 = T.match_buffer(p_lv6, (T.int64(1), n, T.int64(2560))) + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560)), "float16") + # with T.block("root"): + A_red_temp_v0 = T.alloc_buffer((T.int64(1), n)) + A_red_temp_v1 = T.alloc_buffer((T.int64(1), n)) + var_T_layer_norm_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + for ax0, ax1, k2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("A_red_temp"): + v_ax0, v_ax1, v_k2 = T.axis.remap("SSR", [ax0, ax1, k2]) + T.reads(lv6[v_ax0, v_ax1, v_k2]) + T.writes(A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1]) + with T.init(): + A_red_temp_v0[v_ax0, v_ax1] = T.float32(0) + A_red_temp_v1[v_ax0, v_ax1] = T.float32(0) + v_A_red_temp_v0: T.float32 = A_red_temp_v0[v_ax0, v_ax1] + lv6[v_ax0, v_ax1, v_k2] + v_A_red_temp_v1: T.float32 = A_red_temp_v1[v_ax0, v_ax1] + lv6[v_ax0, v_ax1, v_k2] * lv6[v_ax0, v_ax1, v_k2] + A_red_temp_v0[v_ax0, v_ax1] = v_A_red_temp_v0 + A_red_temp_v1[v_ax0, v_ax1] = v_A_red_temp_v1 + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_layer_norm"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv6[v_ax0, v_ax1, v_ax2], A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1], weight1[v_ax2], bias[v_ax2]) + T.writes(var_T_layer_norm_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_layer_norm_intermediate[v_ax0, v_ax1, v_ax2] = (lv6[v_ax0, v_ax1, v_ax2] - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) * T.rsqrt(A_red_temp_v1[v_ax0, v_ax1] * T.float32(0.00039062500000000002) - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002) * (A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) + T.float32(1.0000000000000001e-05)) * weight1[v_ax2] + bias[v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_layer_norm_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = T.Cast("float16", var_T_layer_norm_intermediate[v_i0, v_i1, v_i2]) + +# fmt: on diff --git a/mlc_llm/dispatch/gpt_neox/redpajama_incite_chat_3b_v1.py b/mlc_llm/dispatch/gpt_neox/redpajama_incite_chat_3b_v1.py new file mode 100644 index 0000000..7c9d1c5 --- /dev/null +++ b/mlc_llm/dispatch/gpt_neox/redpajama_incite_chat_3b_v1.py @@ -0,0 +1,972 @@ +# pylint: disable=missing-docstring,line-too-long,invalid-name,too-many-statements,too-many-locals +import tvm +from tvm import tir +from tvm.script import tir as T + +from .redpajama_incite_chat_3b_v1_mod import Module as MOD + +# fmt: off + +def fused_NT_matmul1_add4(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l3, l4, l5, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l3, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 2, 32, 1, 2]) + l22, l23, l24, l25, l26 = sch.split(loop=l4, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[80, 1, 4, 4, 2]) + l32, l33, l34, l35, l36 = sch.split(loop=l5, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[128, 5, 4]) + l40, l41, l42 = sch.split(loop=l6, factors=[v37, v38, v39], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l13, l23, l33, l14, l24, l34, l40, l41, l15, l25, l35, l42, l16, l26, l36) + l43 = sch.fuse(l12, l22, l32, preserve_unit_iters=True) + sch.bind(loop=l43, thread_axis="blockIdx.x") + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="vthread.x") + l45 = sch.fuse(l14, l24, l34, preserve_unit_iters=True) + sch.bind(loop=l45, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b46 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b46, loop=l45, preserve_unit_loops=True, index=-1) + b47 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b47, loop=l40, preserve_unit_loops=True, index=-1) + l52, l53, l54 = sch.get_loops(block=b47)[-3:] + sch.fuse(l52, l53, l54, preserve_unit_iters=True) + v56 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch", ann_val=v56) + b57 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l40, preserve_unit_loops=True, index=-1) + l62, l63 = sch.get_loops(block=b57)[-2:] + sch.fuse(l62, l63, preserve_unit_iters=True) + v65 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=0) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v65) + sch.reverse_compute_inline(block=b1) + v66 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=1) + sch.annotate(block_or_loop=b2, ann_key="meta_schedule.unroll_explicit", ann_val=v66) + sch.enter_postproc() + sch.unannotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch") + l71 = sch.get_loops(block=b47)[-1] + _, l73, l74 = sch.split(loop=l71, factors=[None, 64, 4], preserve_unit_iters=True) + sch.vectorize(loop=l74) + sch.bind(loop=l73, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + l79 = sch.get_loops(block=b57)[-1] + _, l81 = sch.split(loop=l79, factors=[None, 64], preserve_unit_iters=True) + sch.bind(loop=l81, thread_axis="threadIdx.x") + b82 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b82, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("lv9_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + _, _, b85, _ = sch.get_child_blocks(b82) + l100 = sch.get_loops(block=b85)[0] + sch.annotate(block_or_loop=l100, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l100, ann_key="pragma_unroll_explicit", ann_val=1) + b118 = sch.get_block(name="NT_matmul", func_name="main") + l122 = sch.get_loops(block=b118)[4] + sch.decompose_reduction(block=b118, loop=l122) + + +def fused_NT_matmul1_add4_add5(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="T_add_1", func_name="main") + b3 = sch.get_block(name="root", func_name="main") + + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l4, l5, l6, l7 = sch.get_loops(block=b0) + v8, v9, v10, v11, v12 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l13, l14, l15, l16, l17 = sch.split(loop=l4, factors=[v8, v9, v10, v11, v12], preserve_unit_iters=True) + v18, v19, v20, v21, v22 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[2, 8, 4, 2, 1]) + l23, l24, l25, l26, l27 = sch.split(loop=l5, factors=[v18, v19, v20, v21, v22], preserve_unit_iters=True) + v28, v29, v30, v31, v32 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[40, 2, 16, 1, 2]) + l33, l34, l35, l36, l37 = sch.split(loop=l6, factors=[v28, v29, v30, v31, v32], preserve_unit_iters=True) + v38, v39, v40 = sch.sample_perfect_tile(loop=l7, n=3, max_innermost_factor=64, decision=[160, 4, 4]) + l41, l42, l43 = sch.split(loop=l7, factors=[v38, v39, v40], preserve_unit_iters=True) + sch.reorder(l13, l23, l33, l14, l24, l34, l15, l25, l35, l41, l42, l16, l26, l36, l43, l17, l27, l37) + + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="blockIdx.x") + l45 = sch.fuse(l14, l24, l34, preserve_unit_iters=True) + sch.bind(loop=l45, thread_axis="vthread.x") + l46 = sch.fuse(l15, l25, l35, preserve_unit_iters=True) + sch.bind(loop=l46, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b47 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b47, loop=l46, preserve_unit_loops=True, index=-1) + b48 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b48, loop=l41, preserve_unit_loops=True, index=-1) + l53, l54, l55 = sch.get_loops(block=b48)[-3:] + sch.fuse(l53, l54, l55, preserve_unit_iters=True) + v57 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b48, ann_key="meta_schedule.cooperative_fetch", ann_val=v57) + b58 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b58, loop=l41, preserve_unit_loops=True, index=-1) + l63, l64 = sch.get_loops(block=b58)[-2:] + sch.fuse(l63, l64, preserve_unit_iters=True) + v66 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b58, ann_key="meta_schedule.cooperative_fetch", ann_val=v66) + sch.reverse_compute_inline(block=b2) + sch.reverse_compute_inline(block=b1) + v67 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.2, 0.2, 0.2, 0.2, 0.2], decision=3) + sch.annotate(block_or_loop=b3, ann_key="meta_schedule.unroll_explicit", ann_val=v67) + sch.enter_postproc() + sch.unannotate(block_or_loop=b48, ann_key="meta_schedule.cooperative_fetch") + l72 = sch.get_loops(block=b48)[-1] + _, l74, l75 = sch.split(loop=l72, factors=[None, 64, 4], preserve_unit_iters=True) + sch.vectorize(loop=l75) + sch.bind(loop=l74, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b58, ann_key="meta_schedule.cooperative_fetch") + l80 = sch.get_loops(block=b58)[-1] + _, l82, l83 = sch.split(loop=l80, factors=[None, 64, 4], preserve_unit_iters=True) + sch.vectorize(loop=l83) + sch.bind(loop=l82, thread_axis="threadIdx.x") + b84 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b84, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("lv49_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + _, _, b87, _ = sch.get_child_blocks(b84) + l103 = sch.get_loops(block=b87)[0] + sch.annotate(block_or_loop=l103, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l103, ann_key="pragma_unroll_explicit", ann_val=1) + + b121 = sch.get_block(name="NT_matmul", func_name="main") + l125 = sch.get_loops(block=b121)[4] + sch.decompose_reduction(block=b121, loop=l125) + + +def fused_NT_matmul2_divide1_maximum1_minimum1_cast9(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + sch.pad_einsum(b0, [1, 1, 32, 32, 1]) + l1, l2, l3, l4, l5 = sch.get_loops(b0) + l6, l7 = sch.split(l3, [None, 32]) + l8, l9 = sch.split(l4, [None, 32]) + sch.reorder(l6, l8, l1, l2, l7, l9, l5) + + b1 = sch.get_block(name="T_divide", func_name="main") + b2 = sch.get_block(name="T_maximum", func_name="main") + b3 = sch.get_block(name="T_minimum", func_name="main") + b4 = sch.get_block(name="compute", func_name="main") + b5 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, _, l6, l7, l8, l9, l10 = sch.get_loops(block=b0) + v11, v12, v13, v14, v15 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l16, l17, l18, l19, l20 = sch.split(loop=l6, factors=[v11, v12, v13, v14, v15], preserve_unit_iters=True) + v21, v22, v23, v24, v25 = sch.sample_perfect_tile(loop=l7, n=5, max_innermost_factor=64, decision=[16, 1, 1, 1, 2]) + l26, l27, l28, l29, l30 = sch.split(loop=l7, factors=[v21, v22, v23, v24, v25], preserve_unit_iters=True) + v31, v32, v33, v34, v35 = sch.sample_perfect_tile(loop=l8, n=5, max_innermost_factor=64, decision=[1, 1, 16, 2, 4]) + l36, l37, l38, l39, l40 = sch.split(loop=l8, factors=[v31, v32, v33, v34, v35], preserve_unit_iters=True) + v41, v42, v43, v44, v45 = sch.sample_perfect_tile(loop=l9, n=5, max_innermost_factor=64, decision=[8, 1, 16, 1, 1]) + l46, l47, l48, l49, l50 = sch.split(loop=l9, factors=[v41, v42, v43, v44, v45], preserve_unit_iters=True) + v51, v52, v53 = sch.sample_perfect_tile(loop=l10, n=3, max_innermost_factor=64, decision=[4, 20, 1]) + l54, l55, l56 = sch.split(loop=l10, factors=[v51, v52, v53], preserve_unit_iters=True) + sch.reorder(l16, l26, l36, l46, l17, l27, l37, l47, l18, l28, l38, l48, l54, l55, l19, l29, l39, l49, l56, l20, l30, l40, l50) + l57 = sch.fuse(l16, l26, l36, l46, preserve_unit_iters=True) + sch.bind(loop=l57, thread_axis="blockIdx.x") + l58 = sch.fuse(l17, l27, l37, l47, preserve_unit_iters=True) + sch.bind(loop=l58, thread_axis="vthread.x") + l59 = sch.fuse(l18, l28, l38, l48, preserve_unit_iters=True) + sch.bind(loop=l59, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b60 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b60, loop=l59, preserve_unit_loops=True, index=-1) + b61 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b61, loop=l54, preserve_unit_loops=True, index=-1) + l66, l67, l68, l69 = sch.get_loops(block=b61)[-4: ] + sch.fuse(l66, l67, l68, l69, preserve_unit_iters=True) + v71 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b61, ann_key="meta_schedule.cooperative_fetch", ann_val=v71) + b72 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b72, loop=l54, preserve_unit_loops=True, index=-1) + l77, l78, l79, l80 = sch.get_loops(block=b72)[-4:] + sch.fuse(l77, l78, l79, l80, preserve_unit_iters=True) + v82 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b72, ann_key="meta_schedule.cooperative_fetch", ann_val=v82) + sch.reverse_compute_inline(block=b4) + sch.reverse_compute_inline(block=b3) + sch.reverse_compute_inline(block=b2) + sch.reverse_compute_inline(block=b1) + v83 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=0) + sch.annotate(block_or_loop=b5, ann_key="meta_schedule.unroll_explicit", ann_val=v83) + sch.enter_postproc() + sch.unannotate(block_or_loop=b61, ann_key="meta_schedule.cooperative_fetch") + l88 = sch.get_loops(block=b61)[-1] + _, l90, l91 = sch.split(loop=l88, factors=[None, 64, 4], preserve_unit_iters=True) + sch.vectorize(loop=l91) + sch.bind(loop=l90, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b72, ann_key="meta_schedule.cooperative_fetch") + l96 = sch.get_loops(block=b72)[-1] + _, l98, l99 = sch.split(loop=l96, factors=[None, 64, 4], preserve_unit_iters=True) + sch.vectorize(loop=l99) + sch.bind(loop=l98, thread_axis="threadIdx.x") + b100 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b100, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("lv36_pad") + sch.compute_inline(b1) + b1 = sch.get_block("lv37_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + b140 = sch.get_block(name="NT_matmul", func_name="main") + l144 = sch.get_loops(block=b140)[5] + sch.decompose_reduction(block=b140, loop=l144) + + +def fused_NT_matmul3_add6_gelu1_cast11(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="T_multiply", func_name="main") + b3 = sch.get_block(name="compute", func_name="main") + b4 = sch.get_block(name="T_multiply_1", func_name="main") + b5 = sch.get_block(name="T_add_1", func_name="main") + b6 = sch.get_block(name="T_multiply_2", func_name="main") + b7 = sch.get_block(name="compute_1", func_name="main") + b8 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l9, l10, l11, l12 = sch.get_loops(block=b0) + v13, v14, v15, v16, v17 = sch.sample_perfect_tile(loop=l9, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l18, l19, l20, l21, l22 = sch.split(loop=l9, factors=[v13, v14, v15, v16, v17], preserve_unit_iters=True) + v23, v24, v25, v26, v27 = sch.sample_perfect_tile(loop=l10, n=5, max_innermost_factor=64, decision=[1, 1, 32, 4, 1]) + l28, l29, l30, l31, l32 = sch.split(loop=l10, factors=[v23, v24, v25, v26, v27], preserve_unit_iters=True) + v33, v34, v35, v36, v37 = sch.sample_perfect_tile(loop=l11, n=5, max_innermost_factor=64, decision=[320, 1, 4, 8, 1]) + l38, l39, l40, l41, l42 = sch.split(loop=l11, factors=[v33, v34, v35, v36, v37], preserve_unit_iters=True) + v43, v44, v45 = sch.sample_perfect_tile(loop=l12, n=3, max_innermost_factor=64, decision=[80, 32, 1]) + l46, l47, l48 = sch.split(loop=l12, factors=[v43, v44, v45], preserve_unit_iters=True) + sch.reorder(l18, l28, l38, l19, l29, l39, l20, l30, l40, l46, l47, l21, l31, l41, l48, l22, l32, l42) + l49 = sch.fuse(l18, l28, l38, preserve_unit_iters=True) + sch.bind(loop=l49, thread_axis="blockIdx.x") + l50 = sch.fuse(l19, l29, l39, preserve_unit_iters=True) + sch.bind(loop=l50, thread_axis="vthread.x") + l51 = sch.fuse(l20, l30, l40, preserve_unit_iters=True) + sch.bind(loop=l51, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=1024) + b52 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b52, loop=l51, preserve_unit_loops=True, index=-1) + b53 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b53, loop=l46, preserve_unit_loops=True, index=-1) + l58, l59, l60 = sch.get_loops(block=b53)[-3:] + sch.fuse(l58, l59, l60, preserve_unit_iters=True) + v62 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b53, ann_key="meta_schedule.cooperative_fetch", ann_val=v62) + b63 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b63, loop=l46, preserve_unit_loops=True, index=-1) + l68, l69 = sch.get_loops(block=b63)[-2:] + sch.fuse(l68, l69, preserve_unit_iters=True) + v71 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b63, ann_key="meta_schedule.cooperative_fetch", ann_val=v71) + sch.reverse_compute_inline(block=b7) + sch.compute_inline(block=b5) + sch.compute_inline(block=b4) + sch.compute_inline(block=b3) + sch.compute_inline(block=b2) + sch.compute_inline(block=b1) + sch.reverse_compute_inline(block=b6) + v72 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=2) + sch.annotate(block_or_loop=b8, ann_key="meta_schedule.unroll_explicit", ann_val=v72) + sch.enter_postproc() + sch.unannotate(block_or_loop=b53, ann_key="meta_schedule.cooperative_fetch") + l77 = sch.get_loops(block=b53)[-1] + _, l79, l80 = sch.split(loop=l77, factors=[None, 32, 4], preserve_unit_iters=True) + sch.vectorize(loop=l80) + sch.bind(loop=l79, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b63, ann_key="meta_schedule.cooperative_fetch") + l85 = sch.get_loops(block=b63)[-1] + _, l87, l88 = sch.split(loop=l85, factors=[None, 32, 4], preserve_unit_iters=True) + sch.vectorize(loop=l88) + sch.bind(loop=l87, thread_axis="threadIdx.x") + b89 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b89, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("lv57_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + _, _, b92, _ = sch.get_child_blocks(b89) + l108 = sch.get_loops(block=b92)[0] + sch.annotate(block_or_loop=l108, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l108, ann_key="pragma_unroll_explicit", ann_val=1) + + b126 = sch.get_block(name="NT_matmul", func_name="main") + l130 = sch.get_loops(block=b126)[4] + sch.decompose_reduction(block=b126, loop=l130) + + +def fused_NT_matmul4_add7_cast8_cast12_add5(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="compute", func_name="main") + b3 = sch.get_block(name="compute_1", func_name="main") + b4 = sch.get_block(name="T_add_1", func_name="main") + b5 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l6, l7, l8, l9 = sch.get_loops(block=b0) + v10, v11, v12, v13, v14 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l15, l16, l17, l18, l19 = sch.split(loop=l6, factors=[v10, v11, v12, v13, v14], preserve_unit_iters=True) + v20, v21, v22, v23, v24 = sch.sample_perfect_tile(loop=l7, n=5, max_innermost_factor=64, decision=[2, 4, 16, 1, 1]) + l25, l26, l27, l28, l29 = sch.split(loop=l7, factors=[v20, v21, v22, v23, v24], preserve_unit_iters=True) + v30, v31, v32, v33, v34 = sch.sample_perfect_tile(loop=l8, n=5, max_innermost_factor=64, decision=[40, 1, 8, 1, 8]) + l35, l36, l37, l38, l39 = sch.split(loop=l8, factors=[v30, v31, v32, v33, v34], preserve_unit_iters=True) + v40, v41, v42 = sch.sample_perfect_tile(loop=l9, n=3, max_innermost_factor=64, decision=[320, 32, 1]) + l43, l44, l45 = sch.split(loop=l9, factors=[v40, v41, v42], preserve_unit_iters=True) + sch.reorder(l15, l25, l35, l16, l26, l36, l17, l27, l37, l43, l44, l18, l28, l38, l45, l19, l29, l39) + l46 = sch.fuse(l15, l25, l35, preserve_unit_iters=True) + sch.bind(loop=l46, thread_axis="blockIdx.x") + l47 = sch.fuse(l16, l26, l36, preserve_unit_iters=True) + sch.bind(loop=l47, thread_axis="vthread.x") + l48 = sch.fuse(l17, l27, l37, preserve_unit_iters=True) + sch.bind(loop=l48, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b49 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b49, loop=l48, preserve_unit_loops=True, index=-1) + b50 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b50, loop=l43, preserve_unit_loops=True, index=-1) + l55, l56, l57 = sch.get_loops(block=b50)[-3:] + sch.fuse(l55, l56, l57, preserve_unit_iters=True) + v59 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b50, ann_key="meta_schedule.cooperative_fetch", ann_val=v59) + b60 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b60, loop=l43, preserve_unit_loops=True, index=-1) + l65, l66 = sch.get_loops(block=b60)[-2:] + sch.fuse(l65, l66, preserve_unit_iters=True) + v68 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b60, ann_key="meta_schedule.cooperative_fetch", ann_val=v68) + sch.reverse_compute_inline(block=b4) + sch.reverse_compute_inline(block=b3) + sch.reverse_compute_inline(block=b2) + sch.reverse_compute_inline(block=b1) + v69 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=3) + sch.annotate(block_or_loop=b5, ann_key="meta_schedule.unroll_explicit", ann_val=v69) + sch.enter_postproc() + sch.unannotate(block_or_loop=b50, ann_key="meta_schedule.cooperative_fetch") + l74 = sch.get_loops(block=b50)[-1] + _, l76, l77 = sch.split(loop=l74, factors=[None, 128, 4], preserve_unit_iters=True) + sch.vectorize(loop=l77) + sch.bind(loop=l76, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b60, ann_key="meta_schedule.cooperative_fetch") + l82 = sch.get_loops(block=b60)[-1] + _, l84, l85 = sch.split(loop=l82, factors=[None, 128, 2], preserve_unit_iters=True) + sch.vectorize(loop=l85) + sch.bind(loop=l84, thread_axis="threadIdx.x") + b86 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b86, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("lv63_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + _, _, b89, _ = sch.get_child_blocks(b86) + l105 = sch.get_loops(block=b89)[0] + sch.annotate(block_or_loop=l105, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l105, ann_key="pragma_unroll_explicit", ann_val=1) + b123 = sch.get_block(name="NT_matmul", func_name="main") + l127 = sch.get_loops(block=b123)[4] + sch.decompose_reduction(block=b123, loop=l127) + + +def fused_NT_matmul4_add7_cast8_cast12_add5_cast7(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="compute", func_name="main") + b3 = sch.get_block(name="compute_1", func_name="main") + b4 = sch.get_block(name="T_add_1", func_name="main") + b5 = sch.get_block(name="compute_2", func_name="main") + b6 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l7, l8, l9, l10 = sch.get_loops(block=b0) + v11, v12, v13, v14, v15 = sch.sample_perfect_tile(loop=l7, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l16, l17, l18, l19, l20 = sch.split(loop=l7, factors=[v11, v12, v13, v14, v15], preserve_unit_iters=True) + v21, v22, v23, v24, v25 = sch.sample_perfect_tile(loop=l8, n=5, max_innermost_factor=64, decision=[2, 1, 16, 2, 2]) + l26, l27, l28, l29, l30 = sch.split(loop=l8, factors=[v21, v22, v23, v24, v25], preserve_unit_iters=True) + v31, v32, v33, v34, v35 = sch.sample_perfect_tile(loop=l9, n=5, max_innermost_factor=64, decision=[64, 2, 10, 1, 2]) + l36, l37, l38, l39, l40 = sch.split(loop=l9, factors=[v31, v32, v33, v34, v35], preserve_unit_iters=True) + v41, v42, v43 = sch.sample_perfect_tile(loop=l10, n=3, max_innermost_factor=64, decision=[256, 20, 2]) + l44, l45, l46 = sch.split(loop=l10, factors=[v41, v42, v43], preserve_unit_iters=True) + sch.reorder(l16, l26, l36, l17, l27, l37, l18, l28, l38, l44, l45, l19, l29, l39, l46, l20, l30, l40) + l47 = sch.fuse(l16, l26, l36, preserve_unit_iters=True) + sch.bind(loop=l47, thread_axis="blockIdx.x") + l48 = sch.fuse(l17, l27, l37, preserve_unit_iters=True) + sch.bind(loop=l48, thread_axis="vthread.x") + l49 = sch.fuse(l18, l28, l38, preserve_unit_iters=True) + sch.bind(loop=l49, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b50 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b50, loop=l49, preserve_unit_loops=True, index=-1) + b51 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b51, loop=l44, preserve_unit_loops=True, index=-1) + l56, l57, l58 = sch.get_loops(block=b51)[-3:] + sch.fuse(l56, l57, l58, preserve_unit_iters=True) + v60 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b51, ann_key="meta_schedule.cooperative_fetch", ann_val=v60) + b61 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b61, loop=l44, preserve_unit_loops=True, index=-1) + l66, l67 = sch.get_loops(block=b61)[-2:] + sch.fuse(l66, l67, preserve_unit_iters=True) + v69 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b61, ann_key="meta_schedule.cooperative_fetch", ann_val=v69) + sch.reverse_compute_inline(block=b5) + sch.reverse_compute_inline(block=b4) + sch.reverse_compute_inline(block=b3) + sch.reverse_compute_inline(block=b2) + sch.reverse_compute_inline(block=b1) + v70 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=2) + sch.annotate(block_or_loop=b6, ann_key="meta_schedule.unroll_explicit", ann_val=v70) + sch.enter_postproc() + sch.unannotate(block_or_loop=b51, ann_key="meta_schedule.cooperative_fetch") + l75 = sch.get_loops(block=b51)[-1] + _, l77, l78 = sch.split(loop=l75, factors=[None, 80, 4], preserve_unit_iters=True) + sch.vectorize(loop=l78) + sch.bind(loop=l77, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b61, ann_key="meta_schedule.cooperative_fetch") + l83 = sch.get_loops(block=b61)[-1] + _, l85, l86 = sch.split(loop=l83, factors=[None, 80, 2], preserve_unit_iters=True) + sch.vectorize(loop=l86) + sch.bind(loop=l85, thread_axis="threadIdx.x") + b87 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b87, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("lv2047_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + _, _, b90, _ = sch.get_child_blocks(b87) + l106 = sch.get_loops(block=b90)[0] + sch.annotate(block_or_loop=l106, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l106, ann_key="pragma_unroll_explicit", ann_val=1) + b124 = sch.get_block(name="NT_matmul", func_name="main") + l128 = sch.get_loops(block=b124)[4] + sch.decompose_reduction(block=b124, loop=l128) + + +def fused_NT_matmul_divide_maximum_minimum_cast2(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + sch.pad_einsum(b0, [1, 1, 1, 32, 1]) + l1, l2, l3, l4, l5 = sch.get_loops(b0) + l6, l7 = sch.split(l4, [None, 32]) + sch.reorder(l6, l1, l2, l3, l7, l5) + + b1 = sch.get_block(name="T_divide", func_name="main") + b2 = sch.get_block(name="T_maximum", func_name="main") + b3 = sch.get_block(name="T_minimum", func_name="main") + b4 = sch.get_block(name="compute", func_name="main") + b5 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l6, l7, l8, l9, l10 = sch.get_loops(block=b0) + v11, v12, v13, v14, v15 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l16, l17, l18, l19, l20 = sch.split(loop=l6, factors=[v11, v12, v13, v14, v15], preserve_unit_iters=True) + v21, v22, v23, v24, v25 = sch.sample_perfect_tile(loop=l7, n=5, max_innermost_factor=64, decision=[4, 1, 8, 1, 1]) + l26, l27, l28, l29, l30 = sch.split(loop=l7, factors=[v21, v22, v23, v24, v25], preserve_unit_iters=True) + v31, v32, v33, v34, v35 = sch.sample_perfect_tile(loop=l8, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l36, l37, l38, l39, l40 = sch.split(loop=l8, factors=[v31, v32, v33, v34, v35], preserve_unit_iters=True) + v41, v42, v43, v44, v45 = sch.sample_perfect_tile(loop=l9, n=5, max_innermost_factor=64, decision=[4, 1, 16, 2, 1]) + l46, l47, l48, l49, l50 = sch.split(loop=l9, factors=[v41, v42, v43, v44, v45], preserve_unit_iters=True) + v51, v52, v53 = sch.sample_perfect_tile(loop=l10, n=3, max_innermost_factor=64, decision=[5, 8, 2]) + l54, l55, l56 = sch.split(loop=l10, factors=[v51, v52, v53], preserve_unit_iters=True) + sch.reorder(l16, l26, l36, l46, l17, l27, l37, l47, l18, l28, l38, l48, l54, l55, l19, l29, l39, l49, l56, l20, l30, l40, l50) + l57 = sch.fuse(l16, l26, l36, l46, preserve_unit_iters=True) + sch.bind(loop=l57, thread_axis="blockIdx.x") + l58 = sch.fuse(l17, l27, l37, l47, preserve_unit_iters=True) + sch.bind(loop=l58, thread_axis="vthread.x") + l59 = sch.fuse(l18, l28, l38, l48, preserve_unit_iters=True) + sch.bind(loop=l59, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b60 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b60, loop=l59, preserve_unit_loops=True, index=-1) + b61 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b61, loop=l54, preserve_unit_loops=True, index=-1) + l66, l67, l68, l69 = sch.get_loops(block=b61)[-4:] + sch.fuse(l66, l67, l68, l69, preserve_unit_iters=True) + v71 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b61, ann_key="meta_schedule.cooperative_fetch", ann_val=v71) + b72 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b72, loop=l54, preserve_unit_loops=True, index=-1) + l77, l78, l79, l80 = sch.get_loops(block=b72)[-4:] + sch.fuse(l77, l78, l79, l80, preserve_unit_iters=True) + v82 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b72, ann_key="meta_schedule.cooperative_fetch", ann_val=v82) + sch.reverse_compute_inline(block=b4) + sch.reverse_compute_inline(block=b3) + sch.reverse_compute_inline(block=b2) + sch.reverse_compute_inline(block=b1) + v83 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=2) + sch.annotate(block_or_loop=b5, ann_key="meta_schedule.unroll_explicit", ann_val=v83) + sch.enter_postproc() + sch.unannotate(block_or_loop=b61, ann_key="meta_schedule.cooperative_fetch") + l88 = sch.get_loops(block=b61)[-1] + _, l90, l91 = sch.split(loop=l88, factors=[None, 128, 2], preserve_unit_iters=True) + sch.vectorize(loop=l91) + sch.bind(loop=l90, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b72, ann_key="meta_schedule.cooperative_fetch") + l96 = sch.get_loops(block=b72)[-1] + _, l98, l99 = sch.split(loop=l96, factors=[None, 128, 2], preserve_unit_iters=True) + sch.vectorize(loop=l99) + sch.bind(loop=l98, thread_axis="threadIdx.x") + b100 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b100, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("lv2095_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + _, _, b103, _ = sch.get_child_blocks(b100) + l119 = sch.get_loops(block=b103)[0] + sch.annotate(block_or_loop=l119, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l119, ann_key="pragma_unroll_explicit", ann_val=1) + + b140 = sch.get_block(name="NT_matmul", func_name="main") + l144 = sch.get_loops(block=b140)[4] + sch.decompose_reduction(block=b140, loop=l144) + + +def fused_layer_norm1_cast8(sch: tir.Schedule): + b0 = sch.get_block(name="A_red_temp", func_name="main") + b1 = sch.get_block(name="T_layer_norm", func_name="main") + b2 = sch.get_block(name="compute", func_name="main") + b3 = sch.get_block(name="root", func_name="main") + sch.reverse_compute_inline(block=b2) + v4 = sch.sample_categorical(candidates=[4, 8, 16, 32, 64, 128, 256, 512], probs=[0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125], decision=5) + l5, l6, l7 = sch.get_loops(block=b0) + l8, l9 = sch.split(loop=l7, factors=[None, v4], preserve_unit_iters=True) + sch.bind(loop=l9, thread_axis="threadIdx.x") + v10 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=2) + sch.annotate(block_or_loop=b3, ann_key="meta_schedule.unroll_explicit", ann_val=v10) + l11, l12, l13 = sch.get_loops(block=b1) + l14 = sch.fuse(l11, l12, l13, preserve_unit_iters=True) + l15, l16, l17 = sch.split(loop=l14, factors=[None, 256, 256], preserve_unit_iters=True) + sch.reorder(l16, l17, l15) + sch.bind(loop=l16, thread_axis="blockIdx.x") + sch.bind(loop=l17, thread_axis="threadIdx.x") + l18, l19, l20, l21 = sch.get_loops(block=b0) + l22 = sch.fuse(l18, l19, preserve_unit_iters=True) + sch.bind(loop=l22, thread_axis="blockIdx.x") + sch.enter_postproc() + b23 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b23, ann_key="meta_schedule.unroll_explicit") + b24, b25 = sch.get_child_blocks(b23) + l26, l27, l28 = sch.get_loops(block=b24) + sch.annotate(block_or_loop=l26, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l26, ann_key="pragma_unroll_explicit", ann_val=1) + l29, l30, l31 = sch.get_loops(block=b25) + + +def layer_norm1(sch: tir.Schedule): + b0 = sch.get_block(name="A_red_temp", func_name="main") + b1 = sch.get_block(name="T_layer_norm", func_name="main") + b2 = sch.get_block(name="root", func_name="main") + v3 = sch.sample_categorical(candidates=[4, 8, 16, 32, 64, 128, 256, 512], probs=[0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125], decision=1) + l4, l5, l6 = sch.get_loops(block=b0) + l7, l8 = sch.split(loop=l6, factors=[None, v3], preserve_unit_iters=True) + sch.bind(loop=l8, thread_axis="threadIdx.x") + v9 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=3) + sch.annotate(block_or_loop=b2, ann_key="meta_schedule.unroll_explicit", ann_val=v9) + l10, l11, l12 = sch.get_loops(block=b1) + l13 = sch.fuse(l10, l11, l12, preserve_unit_iters=True) + l14, l15, l16 = sch.split(loop=l13, factors=[None, 256, 256], preserve_unit_iters=True) + sch.reorder(l15, l16, l14) + sch.bind(loop=l15, thread_axis="blockIdx.x") + sch.bind(loop=l16, thread_axis="threadIdx.x") + l17, l18, l19, l20 = sch.get_loops(block=b0) + l21 = sch.fuse(l17, l18, preserve_unit_iters=True) + sch.bind(loop=l21, thread_axis="blockIdx.x") + sch.enter_postproc() + b22 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b22, ann_key="meta_schedule.unroll_explicit") + b23, b24 = sch.get_child_blocks(b22) + l25, l26, l27 = sch.get_loops(block=b23) + sch.annotate(block_or_loop=l25, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l25, ann_key="pragma_unroll_explicit", ann_val=1) + l28, l29, l30 = sch.get_loops(block=b24) + + +def matmul3(sch: tir.Schedule): + b0 = sch.get_block(name="matmul", func_name="main") + sch.pad_einsum(b0, [1, 1, 1, 1, 32]) + l1, l2, l3, l4, k = sch.get_loops(b0) + k0, k1 = sch.split(k, [None, 32]) + sch.reorder(l1, l2, l3, k0, l4, k1) + + b1 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + l2, l3, l4, _, l5, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l2, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l2, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[1, 1, 16, 1, 2]) + l22, l23, l24, l25, l26 = sch.split(loop=l3, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l32, l33, l34, l35, l36 = sch.split(loop=l4, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39, v40, v41 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[8, 1, 10, 1, 1]) + l42, l43, l44, l45, l46 = sch.split(loop=l5, factors=[v37, v38, v39, v40, v41], preserve_unit_iters=True) + v47, v48, v49 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[1, 32, 1]) + l50, l51, l52 = sch.split(loop=l6, factors=[v47, v48, v49], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l42, l13, l23, l33, l43, l14, l24, l34, l44, k0, l50, l51, l15, l25, l35, l45, l52, l16, l26, l36, l46) + l53 = sch.fuse(l12, l22, l32, l42, preserve_unit_iters=True) + sch.bind(loop=l53, thread_axis="blockIdx.x") + l54 = sch.fuse(l13, l23, l33, l43, preserve_unit_iters=True) + sch.bind(loop=l54, thread_axis="vthread.x") + l55 = sch.fuse(l14, l24, l34, l44, preserve_unit_iters=True) + sch.bind(loop=l55, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b56 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b56, loop=l55, preserve_unit_loops=True, index=-1) + b57 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l50, preserve_unit_loops=True, index=-1) + l62, l63, l64, l65 = sch.get_loops(block=b57)[-4:] + sch.fuse(l62, l63, l64, l65, preserve_unit_iters=True) + v67 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v67) + b68 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b68, loop=l50, preserve_unit_loops=True, index=-1) + l73, l74, l75, l76 = sch.get_loops(block=b68)[-4:] + sch.fuse(l73, l74, l75, l76, preserve_unit_iters=True) + v78 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b68, ann_key="meta_schedule.cooperative_fetch", ann_val=v78) + v79 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=3) + sch.annotate(block_or_loop=b1, ann_key="meta_schedule.unroll_explicit", ann_val=v79) + sch.enter_postproc() + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + l84 = sch.get_loops(block=b57)[-1] + _, l86, l87 = sch.split(loop=l84, factors=[None, 160, 4], preserve_unit_iters=True) + sch.vectorize(loop=l87) + sch.bind(loop=l86, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b68, ann_key="meta_schedule.cooperative_fetch") + l92 = sch.get_loops(block=b68)[-1] + _, l94, l95 = sch.split(loop=l92, factors=[None, 160, 2], preserve_unit_iters=True) + sch.vectorize(loop=l95) + sch.bind(loop=l94, thread_axis="threadIdx.x") + b96 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b96, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("A_pad") + sch.compute_inline(b1) + b1 = sch.get_block("B_pad") + sch.compute_inline(b1) + + _, _, b99, _ = sch.get_child_blocks(b96) + l115 = sch.get_loops(block=b99)[0] + sch.annotate(block_or_loop=l115, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l115, ann_key="pragma_unroll_explicit", ann_val=1) + b136 = sch.get_block(name="matmul", func_name="main") + l140 = sch.get_loops(block=b136)[3] + sch.decompose_reduction(block=b136, loop=l140) + + +def matmul9(sch: tir.Schedule): + b0 = sch.get_block(name="matmul", func_name="main") + sch.pad_einsum(b0, [1, 1, 32, 1, 32]) + l1, l2, l3, l4, k = sch.get_loops(b0) + s0, s1 = sch.split(l3, [None, 32]) + k0, k1 = sch.split(k, [None, 32]) + sch.reorder(s0, l1, l2, s1, k0, l4, k1) + + b1 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l2, l3, l4, _, l5, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l2, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l2, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[16, 1, 1, 1, 2]) + l22, l23, l24, l25, l26 = sch.split(loop=l3, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[8, 1, 8, 2, 1]) + l32, l33, l34, l35, l36 = sch.split(loop=l4, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39, v40, v41 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[2, 1, 5, 2, 4]) + l42, l43, l44, l45, l46 = sch.split(loop=l5, factors=[v37, v38, v39, v40, v41], preserve_unit_iters=True) + v47, v48, v49 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[16, 1, 2]) + l50, l51, l52 = sch.split(loop=l6, factors=[v47, v48, v49], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l42, l13, l23, l33, l43, l14, l24, l34, l44, k0, l50, l51, l15, l25, l35, l45, l52, l16, l26, l36, l46) + l53 = sch.fuse(l12, l22, l32, l42, preserve_unit_iters=True) + sch.bind(loop=l53, thread_axis="blockIdx.x") + l54 = sch.fuse(l13, l23, l33, l43, preserve_unit_iters=True) + sch.bind(loop=l54, thread_axis="vthread.x") + l55 = sch.fuse(l14, l24, l34, l44, preserve_unit_iters=True) + sch.bind(loop=l55, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b56 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b56, loop=l55, preserve_unit_loops=True, index=-1) + b57 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l50, preserve_unit_loops=True, index=-1) + l62, l63, l64, l65 = sch.get_loops(block=b57)[-4:] + sch.fuse(l62, l63, l64, l65, preserve_unit_iters=True) + v67 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v67) + b68 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b68, loop=l50, preserve_unit_loops=True, index=-1) + l73, l74, l75, l76 = sch.get_loops(block=b68)[-4:] + sch.fuse(l73, l74, l75, l76, preserve_unit_iters=True) + v78 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b68, ann_key="meta_schedule.cooperative_fetch", ann_val=v78) + v79 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=0) + sch.annotate(block_or_loop=b1, ann_key="meta_schedule.unroll_explicit", ann_val=v79) + + b1 = sch.get_block("A_pad") + sch.compute_inline(b1) + b1 = sch.get_block("B_pad") + sch.compute_inline(b1) + b1 = sch.get_block("matmul_pad") + sch.reverse_compute_inline(b1) + + sch.enter_postproc() + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + l84 = sch.get_loops(block=b57)[-1] + _, l86, l87 = sch.split(loop=l84, factors=[None, 40, 4], preserve_unit_iters=True) + sch.vectorize(loop=l87) + sch.bind(loop=l86, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b68, ann_key="meta_schedule.cooperative_fetch") + l92 = sch.get_loops(block=b68)[-1] + _, l94, l95 = sch.split(loop=l92, factors=[None, 40, 2], preserve_unit_iters=True) + sch.vectorize(loop=l95) + sch.bind(loop=l94, thread_axis="threadIdx.x") + b96 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b96, ann_key="meta_schedule.unroll_explicit") + b136 = sch.get_block(name="matmul", func_name="main") + l140 = sch.get_loops(block=b136)[4] + sch.decompose_reduction(block=b136, loop=l140) + + +def softmax_1xn(sch: tir.Schedule): + has_cast = True + if has_cast: + b_cast = sch.get_block("compute") + sch.reverse_compute_inline(b_cast) + + b0 = sch.get_block("T_softmax_exp") + sch.compute_inline(b0) + b1 = sch.get_block("T_softmax_norm") + l2, l3, l4, l5 = sch.get_loops(b1) + _, l7 = sch.split(l5, [None, 128]) + sch.bind(l7, "threadIdx.x") + b8 = sch.get_block("T_softmax_expsum") + sch.compute_at(b8, l4) + sch.set_scope(b8, 0, "shared") + _, _, _, l12 = sch.get_loops(b8) + _, l14 = sch.split(l12, [None, 128]) + sch.bind(l14, "threadIdx.x") + b15 = sch.get_block("T_softmax_maxelem") + sch.compute_at(b15, l4) + sch.set_scope(b15, 0, "shared") + _, _, _, l19 = sch.get_loops(b15) + _, l21 = sch.split(l19, [None, 128]) + sch.bind(l21, "threadIdx.x") + l22 = sch.fuse(l2, l3, l4) + sch.bind(l22, "blockIdx.x") + + +def fused_min_max_triu_te_broadcast_to(sch: tir.Schedule): + b0 = sch.get_block("T_broadcast_to") + sch.reverse_compute_inline(b0) + b1 = sch.get_block("make_diag_mask_te") + i, j = sch.get_loops(b1) + i = sch.fuse(i, j) + i, j = sch.split(i, [None, 128]) + sch.bind(i, "blockIdx.x") + sch.bind(j, "threadIdx.x") + + +@T.prim_func +def softmax_mxn_before(var_rxplaceholder: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + m = T.int64() + rxplaceholder = T.match_buffer(var_rxplaceholder, (T.int64(1), T.int64(32), n, m)) + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), n)) + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), n)) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.max(T_softmax_maxelem[v_i0, v_i1, v_i2], rxplaceholder[v_i0, v_i1, v_i2, v_k]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2, v_i3]) + T_softmax_exp[v_i0, v_i1, v_i2, v_i3] = T.exp(rxplaceholder[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_expsum[v_i0, v_i1, v_i2] = T.float32(0) + T_softmax_expsum[v_i0, v_i1, v_i2] = T_softmax_expsum[v_i0, v_i1, v_i2] + T_softmax_exp[v_i0, v_i1, v_i2, v_k] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_i3], T_softmax_expsum[v_i0, v_i1, v_i2]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"axis": 3}) + T_softmax_norm[v_i0, v_i1, v_i2, v_i3] = T_softmax_exp[v_i0, v_i1, v_i2, v_i3] / T_softmax_expsum[v_i0, v_i1, v_i2] + + +@T.prim_func +def softmax_mxn_after(var_A: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + m = T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), n, m)) + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), n)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), n)) + for i2_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.x"): + with T.block("T_softmax_maxelem_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0) + T.reads(A[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):(m + T.int64(127)) // T.int64(128) * T.int64(128)]) + T.writes(T_softmax_maxelem[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32)]) + T_softmax_maxelem_pad_0_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32)), scope="shared") + for i0, i1, i2_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(32), (m + T.int64(127)) // T.int64(128)): + for k_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_maxelem"): + v_i1_i, v_i2_i = T.axis.remap("SS", [i1, i2_1]) + v_k_i = T.axis.reduce(T.int64(32) * ((m + T.int64(127)) // T.int64(128)), k_0 * T.int64(128) + k_1) + T.reads(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i]) + T.writes(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]) + with T.init(): + T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.max(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i], T.if_then_else(v_i2_o * T.int64(32) + v_i2_i < n and v_k_i < m, A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i], T.float32(-3.4028234663852886e+38))) + for i0_i1_i2_1_fused_0 in range(T.int64(8)): + for i0_i1_i2_1_fused_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_maxelem_cache_write"): + v_i1_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) // T.int64(32)) + v_i2_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32)) + T.where(v_i2_o * T.int64(32) + (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32) < n) + T.reads(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]) + T.writes(T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]) + T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i] = T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] + for i2_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.x"): + with T.block("T_softmax_expsum_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0) + T.reads(A[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):(m + T.int64(127)) // T.int64(128) * T.int64(128)], T_softmax_maxelem[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32)]) + T.writes(T_softmax_expsum[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32)]) + T_softmax_expsum_pad_0_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32)), scope="shared") + for i0, i1, i2_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(32), (m + T.int64(127)) // T.int64(128)): + for k_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_expsum"): + v_i1_i, v_i2_i = T.axis.remap("SS", [i1, i2_1]) + v_k_i = T.axis.reduce(T.int64(32) * ((m + T.int64(127)) // T.int64(128)), k_0 * T.int64(128) + k_1) + T.reads(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i], T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]) + T.writes(T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i]) + with T.init(): + T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.float32(0) + T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] = T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] + T.if_then_else(v_i2_o * T.int64(32) + v_i2_i < n and v_k_i < m, T.exp(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i] - T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]), T.float32(0)) + for i0_i1_i2_1_fused_0 in range(T.int64(8)): + for i0_i1_i2_1_fused_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_expsum_cache_write"): + v_i1_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) // T.int64(32)) + v_i2_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32)) + T.where(v_i2_o * T.int64(32) + (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32) < n) + T.reads(T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i]) + T.writes(T_softmax_expsum[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]) + T_softmax_expsum[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i] = T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] + for i0_i1_i2_fused_i3_fused_0 in T.thread_binding((n * T.int64(32) * m + T.int64(255)) // T.int64(256), thread="blockIdx.x"): + for i0_i1_i2_fused_i3_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("T_softmax_norm"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(32), (i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1) // m // n) + v_i2 = T.axis.spatial(n, (i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1) // m % n) + v_i3 = T.axis.spatial(m, (i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1) % m) + T.where(i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1 < n * T.int64(32) * m) + T.reads(T_softmax_expsum[v_i0, v_i1, v_i2], A[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2, v_i3]) + T_softmax_norm[v_i0, v_i1, v_i2, v_i3] = T.exp(A[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) / T_softmax_expsum[v_i0, v_i1, v_i2] + + + +def _get_dict(): + # tvm.ir.assert_structural_equal(MOD["softmax"], softmax_mxn_before) + func_dict = { + # softmax_mxn_before: softmax_mxn_after, + } + for name, func in [ + # fmt: off + ("fused_layer_norm1_cast8", fused_layer_norm1_cast8), + ("fused_NT_matmul1_add4_add5", fused_NT_matmul1_add4_add5), + ("fused_NT_matmul2_divide1_maximum1_minimum1_cast9", fused_NT_matmul2_divide1_maximum1_minimum1_cast9), + ("fused_NT_matmul4_add7_cast8_cast12_add5", fused_NT_matmul4_add7_cast8_cast12_add5), + ("fused_NT_matmul3_add6_gelu1_cast11", fused_NT_matmul3_add6_gelu1_cast11), + ("fused_NT_matmul_divide_maximum_minimum_cast2", fused_NT_matmul_divide_maximum_minimum_cast2), + ("matmul3", matmul3), + ("fused_NT_matmul1_add4", fused_NT_matmul1_add4), + ("matmul9", matmul9), + ("layer_norm1", layer_norm1), + ("fused_NT_matmul4_add7_cast8_cast12_add5_cast7", fused_NT_matmul4_add7_cast8_cast12_add5_cast7), + ("fused_min_max_triu_te_broadcast_to", fused_min_max_triu_te_broadcast_to), + ("fused_softmax_cast3", softmax_1xn), + # fmt: on + ]: + # print(f"############### {name} ###############") + sch = tir.Schedule(MOD[name]) + func(sch) + # sch.mod["main"].show(black_format=False) + func_dict[MOD[name]] = sch.mod["main"] + return { + (tvm.ir.structural_hash(k), k): v.with_attr("tir.is_scheduled", True) + for k, v in func_dict.items() + } + + +DICT = _get_dict() + + +def lookup(func): + for (hash_value, func_before), f_after in DICT.items(): + if tvm.ir.structural_hash(func) == hash_value and tvm.ir.structural_equal( + func, func_before + ): + return f_after + return None diff --git a/mlc_llm/dispatch/gpt_neox/redpajama_incite_chat_3b_v1_mod.py b/mlc_llm/dispatch/gpt_neox/redpajama_incite_chat_3b_v1_mod.py new file mode 100644 index 0000000..b71567b --- /dev/null +++ b/mlc_llm/dispatch/gpt_neox/redpajama_incite_chat_3b_v1_mod.py @@ -0,0 +1,722 @@ +# pylint: disable=pointless-string-statement,invalid-name,missing-docstring,line-too-long,too-many-locals,too-many-arguments,too-many-statements +from tvm.script import ir as I +from tvm.script import tir as T + +# fmt: off + +@I.ir_module +class Module: + @T.prim_func + def cast7(var_A: T.handle, var_compute: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(2560)), "float16") + compute = T.match_buffer(var_compute, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(A[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.Cast("float32", A[v_i0, v_i1, v_i2]) + + @T.prim_func + def extend_te(var_A: T.handle, var_concat_te: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(1), n, n), "float16") + m = T.int64() + concat_te = T.match_buffer(var_concat_te, (T.int64(1), T.int64(1), n, m), "float16") + # with T.block("root"): + for b, _, i, j in T.grid(T.int64(1), T.int64(1), n, m): + with T.block("concat_te"): + v_b, v__, v_i, v_j = T.axis.remap("SSSS", [b, _, i, j]) + T.reads(A[v_b, v__, v_i, v_j + n - m]) + T.writes(concat_te[v_b, v__, v_i, v_j]) + concat_te[v_b, v__, v_i, v_j] = T.if_then_else(v_j < m - n, T.float16(65504), A[v_b, v__, v_i, v_j + n - m]) + + @T.prim_func + def full(var_T_full: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + T_full = T.match_buffer(var_T_full, (T.int64(1), T.int64(1), T.int64(1), n), "float16") + # with T.block("root"): + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), T.int64(1), n): + with T.block("T_full"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads() + T.writes(T_full[v_ax0, v_ax1, v_ax2, v_ax3]) + T_full[v_ax0, v_ax1, v_ax2, v_ax3] = T.float16(65504) + + @T.prim_func + def fused_NT_matmul1_add4(p_lv9: T.handle, lv1173: T.Buffer((T.int64(2560), T.int64(2560)), "float16"), linear_bias: T.Buffer((T.int64(2560),), "float16"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv9 = T.match_buffer(p_lv9, (T.int64(1), n, T.int64(2560)), "float16") + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560)), "float16") + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv9[v_i0, v_i1, v_k], lv1173[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv9[v_i0, v_i1, v_k] * lv1173[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias[v_ax2] + + @T.prim_func + def fused_NT_matmul1_add4_add5(p_lv49: T.handle, lv1194: T.Buffer((T.int64(2560), T.int64(2560)), "float16"), linear_bias3: T.Buffer((T.int64(2560),), "float16"), p_lv2: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv49 = T.match_buffer(p_lv49, (T.int64(1), n, T.int64(2560)), "float16") + lv2 = T.match_buffer(p_lv2, (T.int64(1), n, T.int64(2560)), "float16") + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560)), "float16") + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + var_T_add_intermediate_1 = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv49[v_i0, v_i1, v_k], lv1194[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv49[v_i0, v_i1, v_k] * lv1194[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias3[v_ax2]) + T.writes(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias3[v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2], lv2[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] + lv2[v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_NT_matmul2_divide1_maximum1_minimum1_cast9(p_lv36: T.handle, p_lv37: T.handle, p_lv5: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv36 = T.match_buffer(p_lv36, (T.int64(1), T.int64(32), n, T.int64(80)), "float16") + m = T.int64() + lv37 = T.match_buffer(p_lv37, (T.int64(1), T.int64(32), m, T.int64(80)), "float16") + lv5 = T.match_buffer(p_lv5, (T.int64(1), T.int64(1), n, m), "float16") + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m), "float16") + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m), "float16") + var_T_maximum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m), "float16") + var_T_minimum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m), "float16") + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, m, T.int64(80)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv36[v_i0, v_i1, v_i2, v_k], lv37[v_i0, v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv36[v_i0, v_i1, v_i2, v_k] * lv37[v_i0, v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float16(0.11179039301310044) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float16(-65504)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_minimum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv5[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.min(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv5[v_ax0, T.int64(0), v_ax2, v_ax3]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("compute"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3]) + var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.Cast("float32", var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + + @T.prim_func + def fused_NT_matmul3_add6_gelu1_cast11(p_lv57: T.handle, lv1201: T.Buffer((T.int64(10240), T.int64(2560)), "float16"), linear_bias4: T.Buffer((T.int64(10240),), "float32"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv57 = T.match_buffer(p_lv57, (T.int64(1), n, T.int64(2560)), "float16") + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(10240)), "float16") + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + T_multiply = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + compute = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + T_multiply_1 = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + T_add = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + var_T_multiply_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(10240), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv57[v_i0, v_i1, v_k], lv1201[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + T.Cast("float32", lv57[v_i0, v_i1, v_k]) * T.Cast("float32", lv1201[v_i2, v_k]) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias4[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias4[v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply[v_ax0, v_ax1, v_ax2]) + T_multiply[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[v_ax0, v_ax1, v_ax2] * T.float32(0.70710678118654757) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(T_multiply[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.erf(T_multiply[v_i0, v_i1, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(compute[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T_multiply_1[v_ax0, v_ax1, v_ax2] = compute[v_ax0, v_ax1, v_ax2] * T.float32(0.5) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T.writes(T_add[v_ax0, v_ax1, v_ax2]) + T_add[v_ax0, v_ax1, v_ax2] = T.float32(0.5) + T_multiply_1[v_ax0, v_ax1, v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply_2"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2], T_add[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[v_ax0, v_ax1, v_ax2] * T_add[v_ax0, v_ax1, v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("compute_1"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_multiply_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = T.Cast("float16", var_T_multiply_intermediate[v_i0, v_i1, v_i2]) + + @T.prim_func + def fused_NT_matmul4_add7_cast8_cast12_add5(p_lv63: T.handle, lv1208: T.Buffer((T.int64(2560), T.int64(10240)), "float16"), linear_bias5: T.Buffer((T.int64(2560),), "float32"), p_lv53: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv63 = T.match_buffer(p_lv63, (T.int64(1), n, T.int64(10240)), "float16") + lv53 = T.match_buffer(p_lv53, (T.int64(1), n, T.int64(2560)), "float16") + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560)), "float16") + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_T_add_intermediate_1 = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_compute_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + var_compute_intermediate_1 = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(10240)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv63[v_i0, v_i1, v_k], lv1208[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + T.Cast("float32", lv63[v_i0, v_i1, v_k]) * T.Cast("float32", lv1208[v_i2, v_k]) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias5[v_ax2]) + T.writes(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias5[v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate_1[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = T.Cast("float16", var_T_add_intermediate_1[v_i0, v_i1, v_i2]) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute_1"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_compute_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate_1[v_i0, v_i1, v_i2]) + var_compute_intermediate_1[v_i0, v_i1, v_i2] = var_compute_intermediate[v_i0, v_i1, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_compute_intermediate_1[v_ax0, v_ax1, v_ax2], lv53[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_compute_intermediate_1[v_ax0, v_ax1, v_ax2] + lv53[v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_NT_matmul4_add7_cast8_cast12_add5_cast7(p_lv2047: T.handle, lv2510: T.Buffer((T.int64(2560), T.int64(10240)), "float16"), linear_bias191: T.Buffer((T.int64(2560),), "float32"), p_lv2037: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv2047 = T.match_buffer(p_lv2047, (T.int64(1), n, T.int64(10240)), "float16") + lv2037 = T.match_buffer(p_lv2037, (T.int64(1), n, T.int64(2560)), "float16") + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_compute_intermediate_1 = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + var_compute_intermediate_2 = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + var_T_add_intermediate_1 = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(10240)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2047[v_i0, v_i1, v_k], lv2510[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + T.Cast("float32", lv2047[v_i0, v_i1, v_k]) * T.Cast("float32", lv2510[v_i2, v_k]) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias191[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias191[v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate_1[v_i0, v_i1, v_i2]) + var_compute_intermediate_1[v_i0, v_i1, v_i2] = T.Cast("float16", var_T_add_intermediate[v_i0, v_i1, v_i2]) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute_1"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_compute_intermediate_1[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate_2[v_i0, v_i1, v_i2]) + var_compute_intermediate_2[v_i0, v_i1, v_i2] = var_compute_intermediate_1[v_i0, v_i1, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_compute_intermediate_2[v_ax0, v_ax1, v_ax2], lv2037[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] = var_compute_intermediate_2[v_ax0, v_ax1, v_ax2] + lv2037[v_ax0, v_ax1, v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute_2"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate_1[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = T.Cast("float32", var_T_add_intermediate_1[v_i0, v_i1, v_i2]) + + @T.prim_func + def fused_NT_matmul_divide_maximum_minimum_cast2(lv2094: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(80)), "float16"), p_lv2095: T.handle, p_lv2063: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv2095 = T.match_buffer(p_lv2095, (T.int64(1), T.int64(32), n, T.int64(80)), "float16") + lv2063 = T.match_buffer(p_lv2063, (T.int64(1), T.int64(1), T.int64(1), n), "float16") + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), T.int64(1), n)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + var_T_maximum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + var_T_minimum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n, T.int64(80)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv2094[v_i0, v_i1, v_i2, v_k], lv2095[v_i0, v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv2094[v_i0, v_i1, v_i2, v_k] * lv2095[v_i0, v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float16(0.11179039301310044) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float16(-65504)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_minimum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv2063[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.min(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv2063[v_ax0, T.int64(0), v_ax2, v_ax3]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("compute"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3]) + var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.Cast("float32", var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + + @T.prim_func + def fused_layer_norm1_cast8(p_lv6: T.handle, weight1: T.Buffer((T.int64(2560),), "float32"), bias: T.Buffer((T.int64(2560),), "float32"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv6 = T.match_buffer(p_lv6, (T.int64(1), n, T.int64(2560))) + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560)), "float16") + # with T.block("root"): + A_red_temp_v0 = T.alloc_buffer((T.int64(1), n)) + A_red_temp_v1 = T.alloc_buffer((T.int64(1), n)) + var_T_layer_norm_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + for ax0, ax1, k2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("A_red_temp"): + v_ax0, v_ax1, v_k2 = T.axis.remap("SSR", [ax0, ax1, k2]) + T.reads(lv6[v_ax0, v_ax1, v_k2]) + T.writes(A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1]) + with T.init(): + A_red_temp_v0[v_ax0, v_ax1] = T.float32(0) + A_red_temp_v1[v_ax0, v_ax1] = T.float32(0) + v_A_red_temp_v0: T.float32 = A_red_temp_v0[v_ax0, v_ax1] + lv6[v_ax0, v_ax1, v_k2] + v_A_red_temp_v1: T.float32 = A_red_temp_v1[v_ax0, v_ax1] + lv6[v_ax0, v_ax1, v_k2] * lv6[v_ax0, v_ax1, v_k2] + A_red_temp_v0[v_ax0, v_ax1] = v_A_red_temp_v0 + A_red_temp_v1[v_ax0, v_ax1] = v_A_red_temp_v1 + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_layer_norm"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv6[v_ax0, v_ax1, v_ax2], A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1], weight1[v_ax2], bias[v_ax2]) + T.writes(var_T_layer_norm_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_layer_norm_intermediate[v_ax0, v_ax1, v_ax2] = (lv6[v_ax0, v_ax1, v_ax2] - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) * T.rsqrt(A_red_temp_v1[v_ax0, v_ax1] * T.float32(0.00039062500000000002) - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002) * (A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) + T.float32(1.0000000000000001e-05)) * weight1[v_ax2] + bias[v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_layer_norm_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = T.Cast("float16", var_T_layer_norm_intermediate[v_i0, v_i1, v_i2]) + + @T.prim_func + def fused_min_max_triu_te_broadcast_to(p_output0: T.handle, n: T.int64): + T.func_attr({"tir.noalias": T.bool(True)}) + var_T_broadcast_to_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(1), n, n), "float16") + # with T.block("root"): + var_make_diag_mask_te_intermediate = T.alloc_buffer((n, n), "float16") + for i, j in T.grid(n, n): + with T.block("make_diag_mask_te"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads() + T.writes(var_make_diag_mask_te_intermediate[v_i, v_j]) + var_make_diag_mask_te_intermediate[v_i, v_j] = T.Select(v_i < v_j, T.float16(-65504), T.float16(65504)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), n, n): + with T.block("T_broadcast_to"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_make_diag_mask_te_intermediate[v_ax2, v_ax3]) + T.writes(var_T_broadcast_to_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_broadcast_to_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_make_diag_mask_te_intermediate[v_ax2, v_ax3] + + @T.prim_func + def fused_softmax1_cast10(p_lv44: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n, m = T.int64(), T.int64() + lv44 = T.match_buffer(p_lv44, (T.int64(1), T.int64(32), n, m)) + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, m), "float16") + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), n)) + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), n)) + var_T_softmax_norm_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv44[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.max(T_softmax_maxelem[v_i0, v_i1, v_i2], lv44[v_i0, v_i1, v_i2, v_k]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(lv44[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2, v_i3]) + T_softmax_exp[v_i0, v_i1, v_i2, v_i3] = T.exp(lv44[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_expsum[v_i0, v_i1, v_i2] = T.float32(0) + T_softmax_expsum[v_i0, v_i1, v_i2] = T_softmax_expsum[v_i0, v_i1, v_i2] + T_softmax_exp[v_i0, v_i1, v_i2, v_k] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_i3], T_softmax_expsum[v_i0, v_i1, v_i2]) + T.writes(var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"axis": 3}) + var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3] = T_softmax_exp[v_i0, v_i1, v_i2, v_i3] / T_softmax_expsum[v_i0, v_i1, v_i2] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("compute"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3]) + var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.Cast("float16", var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + + @T.prim_func + def fused_softmax_cast3(p_lv2102: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv2102 = T.match_buffer(p_lv2102, (T.int64(1), T.int64(32), T.int64(1), n)) + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), T.int64(1), n), "float16") + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1))) + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1))) + var_T_softmax_norm_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2102[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.max(T_softmax_maxelem[v_i0, v_i1, v_i2], lv2102[v_i0, v_i1, v_i2, v_k]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(lv2102[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2, v_i3]) + T_softmax_exp[v_i0, v_i1, v_i2, v_i3] = T.exp(lv2102[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_expsum[v_i0, v_i1, v_i2] = T.float32(0) + T_softmax_expsum[v_i0, v_i1, v_i2] = T_softmax_expsum[v_i0, v_i1, v_i2] + T_softmax_exp[v_i0, v_i1, v_i2, v_k] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_i3], T_softmax_expsum[v_i0, v_i1, v_i2]) + T.writes(var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"axis": 3}) + var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3] = T_softmax_exp[v_i0, v_i1, v_i2, v_i3] / T_softmax_expsum[v_i0, v_i1, v_i2] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("compute"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3]) + var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.Cast("float16", var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + + @T.prim_func + def layer_norm1(var_A: T.handle, B: T.Buffer((T.int64(2560),), "float32"), C: T.Buffer((T.int64(2560),), "float32"), var_T_layer_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(2560))) + T_layer_norm = T.match_buffer(var_T_layer_norm, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + A_red_temp_v0 = T.alloc_buffer((T.int64(1), n)) + A_red_temp_v1 = T.alloc_buffer((T.int64(1), n)) + for ax0, ax1, k2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("A_red_temp"): + v_ax0, v_ax1, v_k2 = T.axis.remap("SSR", [ax0, ax1, k2]) + T.reads(A[v_ax0, v_ax1, v_k2]) + T.writes(A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1]) + with T.init(): + A_red_temp_v0[v_ax0, v_ax1] = T.float32(0) + A_red_temp_v1[v_ax0, v_ax1] = T.float32(0) + v_A_red_temp_v0: T.float32 = A_red_temp_v0[v_ax0, v_ax1] + A[v_ax0, v_ax1, v_k2] + v_A_red_temp_v1: T.float32 = A_red_temp_v1[v_ax0, v_ax1] + A[v_ax0, v_ax1, v_k2] * A[v_ax0, v_ax1, v_k2] + A_red_temp_v0[v_ax0, v_ax1] = v_A_red_temp_v0 + A_red_temp_v1[v_ax0, v_ax1] = v_A_red_temp_v1 + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_layer_norm"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[v_ax0, v_ax1, v_ax2], A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1], B[v_ax2], C[v_ax2]) + T.writes(T_layer_norm[v_ax0, v_ax1, v_ax2]) + T_layer_norm[v_ax0, v_ax1, v_ax2] = (A[v_ax0, v_ax1, v_ax2] - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) * T.rsqrt(A_red_temp_v1[v_ax0, v_ax1] * T.float32(0.00039062500000000002) - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002) * (A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) + T.float32(1.0000000000000001e-05)) * B[v_ax2] + C[v_ax2] + + @T.prim_func + def matmul3(var_A: T.handle, var_B: T.handle, matmul: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(80)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), T.int64(1), n), "float16") + B = T.match_buffer(var_B, (T.int64(1), T.int64(32), n, T.int64(80)), "float16") + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), T.int64(1), T.int64(80), n): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(A[v_i0, v_i1, v_i2, v_k], B[v_i0, v_i1, v_k, v_i3]) + T.writes(matmul[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + matmul[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + matmul[v_i0, v_i1, v_i2, v_i3] = matmul[v_i0, v_i1, v_i2, v_i3] + A[v_i0, v_i1, v_i2, v_k] * B[v_i0, v_i1, v_k, v_i3] + + @T.prim_func + def matmul9(var_A: T.handle, var_B: T.handle, var_matmul: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n, m = T.int64(), T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), n, m), "float16") + B = T.match_buffer(var_B, (T.int64(1), T.int64(32), m, T.int64(80)), "float16") + matmul = T.match_buffer(var_matmul, (T.int64(1), T.int64(32), n, T.int64(80)), "float16") + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, T.int64(80), m): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(A[v_i0, v_i1, v_i2, v_k], B[v_i0, v_i1, v_k, v_i3]) + T.writes(matmul[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + matmul[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + matmul[v_i0, v_i1, v_i2, v_i3] = matmul[v_i0, v_i1, v_i2, v_i3] + A[v_i0, v_i1, v_i2, v_k] * B[v_i0, v_i1, v_k, v_i3] + + @T.prim_func + def reshape3(var_A: T.handle, var_T_reshape: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (n, T.int64(32), T.int64(80)), "float16") + T_reshape = T.match_buffer(var_T_reshape, (T.int64(1), n, T.int64(32), T.int64(80)), "float16") + # with T.block("root"): + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), n, T.int64(32), T.int64(80)): + with T.block("T_reshape"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(A[((v_ax3 // T.int64(80) + v_ax2) // T.int64(32) + v_ax0 * n + v_ax1) % n, (v_ax3 // T.int64(80) + v_ax2) % T.int64(32), v_ax3 % T.int64(80)]) + T.writes(T_reshape[v_ax0, v_ax1, v_ax2, v_ax3]) + T_reshape[v_ax0, v_ax1, v_ax2, v_ax3] = A[((v_ax3 // T.int64(80) + v_ax2) // T.int64(32) + v_ax0 * n + v_ax1) % n, (v_ax3 // T.int64(80) + v_ax2) % T.int64(32), v_ax3 % T.int64(80)] + + @T.prim_func + def reshape5(var_A: T.handle, var_T_reshape: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n), "int32") + T_reshape = T.match_buffer(var_T_reshape, (n,), "int32") + # with T.block("root"): + for ax0 in range(n): + with T.block("T_reshape"): + v_ax0 = T.axis.spatial(n, ax0) + T.reads(A[T.int64(0), v_ax0 % n]) + T.writes(T_reshape[v_ax0]) + T_reshape[v_ax0] = A[T.int64(0), v_ax0 % n] + + @T.prim_func + def reshape6(var_A: T.handle, var_T_reshape: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (n, T.int64(2560)), "float16") + T_reshape = T.match_buffer(var_T_reshape, (T.int64(1), n, T.int64(2560)), "float16") + # with T.block("root"): + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_reshape"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[(v_ax2 // T.int64(2560) + v_ax0 * n + v_ax1) % n, v_ax2 % T.int64(2560)]) + T.writes(T_reshape[v_ax0, v_ax1, v_ax2]) + T_reshape[v_ax0, v_ax1, v_ax2] = A[(v_ax2 // T.int64(2560) + v_ax0 * n + v_ax1) % n, v_ax2 % T.int64(2560)] + + @T.prim_func + def reshape7(var_A: T.handle, var_T_reshape: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(2560)), "float16") + T_reshape = T.match_buffer(var_T_reshape, (T.int64(1), n, T.int64(32), T.int64(80)), "float16") + # with T.block("root"): + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), n, T.int64(32), T.int64(80)): + with T.block("T_reshape"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(A[T.int64(0), ((v_ax2 * T.int64(80) + v_ax3) // T.int64(2560) + v_ax0 * n + v_ax1) % n, (v_ax2 * T.int64(80) + v_ax3) % T.int64(2560)]) + T.writes(T_reshape[v_ax0, v_ax1, v_ax2, v_ax3]) + T_reshape[v_ax0, v_ax1, v_ax2, v_ax3] = A[T.int64(0), ((v_ax2 * T.int64(80) + v_ax3) // T.int64(2560) + v_ax0 * n + v_ax1) % n, (v_ax2 * T.int64(80) + v_ax3) % T.int64(2560)] + + @T.prim_func + def reshape8(var_A: T.handle, var_T_reshape: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(32), T.int64(80)), "float16") + T_reshape = T.match_buffer(var_T_reshape, (T.int64(1), n, T.int64(2560)), "float16") + # with T.block("root"): + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_reshape"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[T.int64(0), (v_ax2 // T.int64(2560) + v_ax0 * n + v_ax1) % n, v_ax2 % T.int64(2560) // T.int64(80), v_ax2 % T.int64(80)]) + T.writes(T_reshape[v_ax0, v_ax1, v_ax2]) + T_reshape[v_ax0, v_ax1, v_ax2] = A[T.int64(0), (v_ax2 // T.int64(2560) + v_ax0 * n + v_ax1) % n, v_ax2 % T.int64(2560) // T.int64(80), v_ax2 % T.int64(80)] + + @T.prim_func + def rotary_embedding(var_A: T.handle, B: T.Buffer((T.int64(2048), T.int64(80)), "float16"), C: T.Buffer((T.int64(2048), T.int64(80)), "float16"), var_rotary: T.handle, m: T.int64): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(32), T.int64(80)), "float16") + rotary = T.match_buffer(var_rotary, (T.int64(1), n, T.int64(32), T.int64(80)), "float16") + # with T.block("root"): + for i_batch_size, i_seq_len, i_num_heads, i_head_dim in T.grid(T.int64(1), n, T.int64(32), T.int64(80)): + with T.block("rotary"): + v_i_batch_size, v_i_seq_len, v_i_num_heads, v_i_head_dim = T.axis.remap("SSSS", [i_batch_size, i_seq_len, i_num_heads, i_head_dim]) + T.reads(B[m + v_i_seq_len - n, v_i_head_dim], A[v_i_batch_size, v_i_seq_len, v_i_num_heads, v_i_head_dim - T.int64(40):v_i_head_dim - T.int64(40) + T.int64(81)], C[m + v_i_seq_len - n, v_i_head_dim]) + T.writes(rotary[v_i_batch_size, v_i_seq_len, v_i_num_heads, v_i_head_dim]) + rotary[v_i_batch_size, v_i_seq_len, v_i_num_heads, v_i_head_dim] = T.Select(v_i_head_dim < T.int64(80), B[m + v_i_seq_len - n, v_i_head_dim] * A[v_i_batch_size, v_i_seq_len, v_i_num_heads, v_i_head_dim] + C[m + v_i_seq_len - n, v_i_head_dim] * T.Select(v_i_head_dim < T.int64(40), A[v_i_batch_size, v_i_seq_len, v_i_num_heads, v_i_head_dim + T.int64(40)] * T.float16(-1), A[v_i_batch_size, v_i_seq_len, v_i_num_heads, v_i_head_dim - T.int64(40)]), A[v_i_batch_size, v_i_seq_len, v_i_num_heads, v_i_head_dim]) + + @T.prim_func + def slice(var_A: T.handle, slice_1: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + for i, _, k in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("slice"): + v_i, v__, v_k = T.axis.remap("SSS", [i, _, k]) + T.reads(A[v_i, n - T.int64(1), v_k]) + T.writes(slice_1[v_i, v__, v_k]) + slice_1[v_i, v__, v_k] = A[v_i, n - T.int64(1), v_k] + + @T.prim_func + def squeeze1(var_A: T.handle, var_T_squeeze: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(32), T.int64(80)), "float16") + T_squeeze = T.match_buffer(var_T_squeeze, (n, T.int64(32), T.int64(80)), "float16") + # with T.block("root"): + for ax0, ax1, ax2 in T.grid(n, T.int64(32), T.int64(80)): + with T.block("T_squeeze"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[T.int64(0), v_ax0, v_ax1, v_ax2]) + T.writes(T_squeeze[v_ax0, v_ax1, v_ax2]) + T_squeeze[v_ax0, v_ax1, v_ax2] = A[T.int64(0), v_ax0, v_ax1, v_ax2] + + @T.prim_func + def take_decode1(A: T.Buffer((T.int64(50432), T.int64(320)), "uint32"), B: T.Buffer((T.int64(50432), T.int64(80)), "float16"), var_C: T.handle, var_take_decode: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + C = T.match_buffer(var_C, (n,), "int32") + take_decode = T.match_buffer(var_take_decode, (n, T.int64(2560)), "float16") + # with T.block("root"): + for i, j in T.grid(n, T.int64(2560)): + with T.block("take_decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(A[C[v_i], v_j // T.int64(8)], C[v_i], B[C[v_i], v_j // T.int64(32)]) + T.writes(take_decode[v_i, v_j]) + take_decode[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(A[C[v_i], v_j // T.int64(8)], T.Cast("uint32", v_j % T.int64(8)) * T.uint32(4)), T.uint32(15))) - T.float16(7)) * B[C[v_i], v_j // T.int64(32)] + + @T.prim_func + def transpose3(var_A: T.handle, var_T_transpose: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(32), T.int64(80)), "float16") + T_transpose = T.match_buffer(var_T_transpose, (T.int64(1), T.int64(32), n, T.int64(80)), "float16") + # with T.block("root"): + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, T.int64(80)): + with T.block("T_transpose"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(A[v_ax0, v_ax2, v_ax1, v_ax3]) + T.writes(T_transpose[v_ax0, v_ax1, v_ax2, v_ax3]) + T_transpose[v_ax0, v_ax1, v_ax2, v_ax3] = A[v_ax0, v_ax2, v_ax1, v_ax3] + + @T.prim_func + def transpose6(var_A: T.handle, var_T_transpose: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), n, T.int64(80)), "float16") + T_transpose = T.match_buffer(var_T_transpose, (T.int64(1), n, T.int64(32), T.int64(80)), "float16") + # with T.block("root"): + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), n, T.int64(32), T.int64(80)): + with T.block("T_transpose"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(A[v_ax0, v_ax2, v_ax1, v_ax3]) + T.writes(T_transpose[v_ax0, v_ax1, v_ax2, v_ax3]) + T_transpose[v_ax0, v_ax1, v_ax2, v_ax3] = A[v_ax0, v_ax2, v_ax1, v_ax3] + + +# fmt: on diff --git a/mlc_llm/dispatch/gpt_neox/redpajama_incite_chat_3b_v1_tune.py b/mlc_llm/dispatch/gpt_neox/redpajama_incite_chat_3b_v1_tune.py new file mode 100644 index 0000000..460bec0 --- /dev/null +++ b/mlc_llm/dispatch/gpt_neox/redpajama_incite_chat_3b_v1_tune.py @@ -0,0 +1,1010 @@ +from tvm.script import ir as I +from tvm.script import tir as T + +""" + ID | Name | FLOP | Weight | Speed (GFLOPS) | Latency (us) | Weighted Latency (us) | Trials | Done +----------------------------------------------------------------------------------------------------------------------------------------------------------- + 0 | cast | 1 | 1 | 0.0000 | 27.7422 | 27.7422 | 4 | Y + 1 | cast6 | 1 | 1 | 0.0000 | 27.6800 | 27.6800 | 4 | Y + 2 | decode4 | 26214400 | 1 | 167.6862 | 156.3301 | 156.3301 | 172 | Y + 3 | decode5 | 104857600 | 1 | 128.5783 | 815.5153 | 815.5153 | 172 | Y + 4 | decode6 | 104857600 | 1 | 128.6586 | 815.0066 | 815.0066 | 179 | Y + 5 | divide2 | 50432 | 1 | 1.8169 | 27.7575 | 27.7575 | 4 | Y + 6 | fused_NT_matmul1_add4 | 1678049280 | 1 | 2178.8097 | 770.1679 | 770.1679 | 1088 | Y + 7 | fused_NT_matmul1_add4_add5 | 1678376960 | 1 | 2130.5374 | 787.7717 | 787.7717 | 1215 | Y + 8 | fused_NT_matmul2_divide1_maximum1_minimum1_cast9 | 85458944 | 1 | 1211.9454 | 70.5139 | 70.5139 | 192 | Y + 9 | fused_NT_matmul3_add6_gelu1_cast11 | 6717440000 | 1 | 2129.3171 | 3154.7391 | 3154.7391 | 4416 | Y + 10 | fused_NT_matmul4_add7_cast8_cast12_add5 | 6711541760 | 1 | 2072.7296 | 3238.0208 | 3238.0208 | 4544 | Y + 11 | fused_NT_matmul4_add7_cast8_cast12_add5_cast7 | 6711541760 | 1 | 2091.5892 | 3208.8241 | 3208.8241 | 4416 | Y + 12 | fused_NT_matmul_divide_maximum_minimum_cast2 | 667648 | 1 | 23.3021 | 28.6519 | 28.6519 | 64 | Y + 13 | fused_decode1_fused_matmul4_add2_gelu_cast4 | 157337600 | 1 | 812.5380 | 193.6372 | 193.6372 | 319 | Y + 14 | fused_decode2_fused_matmul5_add3_cast1_cast5_add1 | 157291520 | 1 | 730.8166 | 215.2271 | 215.2271 | 320 | Y + 15 | fused_decode2_fused_matmul5_add3_cast1_cast5_add1_cast | 157291520 | 1 | 729.0229 | 215.7566 | 215.7566 | 319 | Y + 16 | fused_decode3_matmul6 | 774635520 | 1 | 868.1608 | 892.2719 | 892.2719 | 1331 | Y + 17 | fused_decode_fused_matmul2_add | 39324160 | 1 | 733.2646 | 53.6289 | 53.6289 | 191 | Y + 18 | fused_decode_fused_matmul2_add_add1 | 39326720 | 1 | 740.8926 | 53.0802 | 53.0802 | 192 | Y + 19 | fused_layer_norm1_cast8 | 4587520 | 1 | 76.3188 | 60.1099 | 60.1099 | 50 | Y + 20 | fused_layer_norm_cast1 | 35840 | 1 | 0.6533 | 54.8634 | 54.8634 | 159 | Y + 21 | fused_reshape2_squeeze | 1 | 1 | 0.0000 | 27.5470 | 27.5470 | 4 | Y + 22 | fused_slice1_cast6 | 1 | 1 | 0.0000 | 27.5899 | 27.5899 | 4 | Y + 23 | fused_transpose4_reshape4 | 1 | 1 | 0.0000 | 27.5157 | 27.5157 | 4 | Y + 24 | layer_norm | 35840 | 1 | 0.6506 | 55.0910 | 55.0910 | 160 | Y + 25 | layer_norm1 | 4587520 | 1 | 74.6941 | 61.4174 | 61.4174 | 50 | Y + 26 | matmul3 | 163840 | 1 | 5.8011 | 28.2428 | 28.2428 | 64 | Y + 27 | matmul9 | 20971520 | 1 | 571.2811 | 36.7096 | 36.7096 | 192 | Y + 28 | reshape | 1 | 1 | 0.0000 | 27.9399 | 27.9399 | 1 | Y + 29 | reshape1 | 1 | 1 | 0.0000 | 27.6659 | 27.6659 | 4 | Y + 30 | reshape2 | 1 | 1 | 0.0000 | 27.6446 | 27.6446 | 4 | Y + 31 | softmax2 | 201728 | 1 | 2.8631 | 70.4578 | 70.4578 | 186 | Y + 32 | squeeze | 1 | 1 | 0.0000 | 27.3156 | 27.3156 | 4 | Y + 33 | take_decode | 10240 | 1 | 0.3712 | 27.5835 | 27.5835 | 4 | Y + 34 | transpose2 | 1 | 1 | 0.0000 | 27.6975 | 27.6975 | 4 | Y +----------------------------------------------------------------------------------------------------------------------------------------------------------- +""" + +# fmt: off + +@I.ir_module +class Module: + @T.prim_func + def cast(A: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16"), compute: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(A[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.Cast("float32", A[v_i0, v_i1, v_i2]) + + @T.prim_func + def cast6(A: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32"), compute: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(A[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = A[v_i0, v_i1, v_i2] + + @T.prim_func + def decode4(A: T.Buffer((T.int64(320), T.int64(2560)), "uint32"), B: T.Buffer((T.int64(80), T.int64(2560)), "float16"), T_transpose: T.Buffer((T.int64(2560), T.int64(2560)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(2560), T.int64(2560)), "float16") + for i, j in T.grid(T.int64(2560), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(A[v_i // T.int64(8), v_j], B[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(A[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4)), T.uint32(15))) - T.float16(7)) * B[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(2560), T.int64(2560)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + + @T.prim_func + def decode5(A: T.Buffer((T.int64(320), T.int64(10240)), "uint32"), B: T.Buffer((T.int64(80), T.int64(10240)), "float16"), T_transpose: T.Buffer((T.int64(10240), T.int64(2560)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(2560), T.int64(10240)), "float16") + for i, j in T.grid(T.int64(2560), T.int64(10240)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(A[v_i // T.int64(8), v_j], B[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(A[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4)), T.uint32(15))) - T.float16(7)) * B[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(10240), T.int64(2560)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + + @T.prim_func + def decode6(A: T.Buffer((T.int64(1280), T.int64(2560)), "uint32"), B: T.Buffer((T.int64(320), T.int64(2560)), "float16"), T_transpose: T.Buffer((T.int64(2560), T.int64(10240)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(10240), T.int64(2560)), "float16") + for i, j in T.grid(T.int64(10240), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(A[v_i // T.int64(8), v_j], B[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(A[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4)), T.uint32(15))) - T.float16(7)) * B[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(2560), T.int64(10240)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + + @T.prim_func + def divide2(A: T.Buffer((T.int64(1), T.int64(1), T.int64(50432)), "float32"), B: T.Buffer((), "float32"), T_divide: T.Buffer((T.int64(1), T.int64(1), T.int64(50432)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(50432)): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[v_ax0, v_ax1, v_ax2], B[()]) + T.writes(T_divide[v_ax0, v_ax1, v_ax2]) + T_divide[v_ax0, v_ax1, v_ax2] = A[v_ax0, v_ax1, v_ax2] / B[()] + + @T.prim_func + def fused_decode1_fused_matmul4_add2_gelu_cast4(lv32: T.Buffer((T.int64(320), T.int64(10240)), "uint32"), lv33: T.Buffer((T.int64(80), T.int64(10240)), "float16"), lv2115: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16"), linear_bias196: T.Buffer((T.int64(10240),), "float32"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(10240)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(2560), T.int64(10240)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + T_multiply = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + compute = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + T_multiply_1 = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + T_add = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + var_T_multiply_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + for i, j in T.grid(T.int64(2560), T.int64(10240)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv32[v_i // T.int64(8), v_j], lv33[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(lv32[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4)), T.uint32(15))) - T.float16(7)) * lv33[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(10240), T.int64(2560)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2115[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + T.Cast("float32", lv2115[v_i0, v_i1, v_k]) * T.Cast("float32", var_decode_intermediate[v_k, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias196[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias196[v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply[v_ax0, v_ax1, v_ax2]) + T_multiply[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[v_ax0, v_ax1, v_ax2] * T.float32(0.70710678118654757) + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(T_multiply[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.erf(T_multiply[v_i0, v_i1, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("T_multiply_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(compute[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T_multiply_1[v_ax0, v_ax1, v_ax2] = compute[v_ax0, v_ax1, v_ax2] * T.float32(0.5) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T.writes(T_add[v_ax0, v_ax1, v_ax2]) + T_add[v_ax0, v_ax1, v_ax2] = T.float32(0.5) + T_multiply_1[v_ax0, v_ax1, v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("T_multiply_2"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2], T_add[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[v_ax0, v_ax1, v_ax2] * T_add[v_ax0, v_ax1, v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("compute_1"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_multiply_intermediate[v_i0, v_i1, v_i2]) + T.writes(p_output0_intermediate[v_i0, v_i1, v_i2]) + p_output0_intermediate[v_i0, v_i1, v_i2] = T.Cast("float16", var_T_multiply_intermediate[v_i0, v_i1, v_i2]) + + @T.prim_func + def fused_decode2_fused_matmul5_add3_cast1_cast5_add1(lv38: T.Buffer((T.int64(1280), T.int64(2560)), "uint32"), lv39: T.Buffer((T.int64(320), T.int64(2560)), "float16"), lv2121: T.Buffer((T.int64(1), T.int64(1), T.int64(10240)), "float16"), linear_bias197: T.Buffer((T.int64(2560),), "float32"), lv8: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(10240), T.int64(2560)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560))) + var_compute_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16") + var_compute_intermediate_1 = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16") + for i, j in T.grid(T.int64(10240), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv38[v_i // T.int64(8), v_j], lv39[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(lv38[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4)), T.uint32(15))) - T.float16(7)) * lv39[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(2560), T.int64(10240)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2121[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + T.Cast("float32", lv2121[v_i0, v_i1, v_k]) * T.Cast("float32", var_decode_intermediate[v_k, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias197[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias197[v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = T.Cast("float16", var_T_add_intermediate[v_i0, v_i1, v_i2]) + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("compute_1"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_compute_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate_1[v_i0, v_i1, v_i2]) + var_compute_intermediate_1[v_i0, v_i1, v_i2] = var_compute_intermediate[v_i0, v_i1, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_compute_intermediate_1[v_ax0, v_ax1, v_ax2], lv8[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = var_compute_intermediate_1[v_ax0, v_ax1, v_ax2] + lv8[v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_decode2_fused_matmul5_add3_cast1_cast5_add1_cast(lv1154: T.Buffer((T.int64(1280), T.int64(2560)), "uint32"), lv1155: T.Buffer((T.int64(320), T.int64(2560)), "float16"), lv4105: T.Buffer((T.int64(1), T.int64(1), T.int64(10240)), "float16"), linear_bias383: T.Buffer((T.int64(2560),), "float32"), lv380: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(10240), T.int64(2560)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560))) + var_compute_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16") + var_compute_intermediate_1 = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16") + var_T_add_intermediate_1 = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16") + for i, j in T.grid(T.int64(10240), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1154[v_i // T.int64(8), v_j], lv1155[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(lv1154[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4)), T.uint32(15))) - T.float16(7)) * lv1155[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(2560), T.int64(10240)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv4105[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + T.Cast("float32", lv4105[v_i0, v_i1, v_k]) * T.Cast("float32", var_decode_intermediate[v_k, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias383[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias383[v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = T.Cast("float16", var_T_add_intermediate[v_i0, v_i1, v_i2]) + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("compute_1"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_compute_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate_1[v_i0, v_i1, v_i2]) + var_compute_intermediate_1[v_i0, v_i1, v_i2] = var_compute_intermediate[v_i0, v_i1, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_compute_intermediate_1[v_ax0, v_ax1, v_ax2], lv380[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] = var_compute_intermediate_1[v_ax0, v_ax1, v_ax2] + lv380[v_ax0, v_ax1, v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("compute_2"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate_1[v_i0, v_i1, v_i2]) + T.writes(p_output0_intermediate[v_i0, v_i1, v_i2]) + p_output0_intermediate[v_i0, v_i1, v_i2] = T.Cast("float32", var_T_add_intermediate_1[v_i0, v_i1, v_i2]) + + @T.prim_func + def fused_decode3_matmul6(lv1160: T.Buffer((T.int64(320), T.int64(50432)), "uint32"), lv1161: T.Buffer((T.int64(80), T.int64(50432)), "float32"), lv384: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32"), var_matmul_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(50432)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(2560), T.int64(50432))) + for i, j in T.grid(T.int64(2560), T.int64(50432)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1160[v_i // T.int64(8), v_j], lv1161[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float32", T.Cast("float16", T.bitwise_and(T.shift_right(lv1160[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4)), T.uint32(15))) - T.float16(7)) * lv1161[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(50432), T.int64(2560)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv384[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv384[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + + @T.prim_func + def fused_decode_fused_matmul2_add(lv8: T.Buffer((T.int64(320), T.int64(2560)), "uint32"), lv9: T.Buffer((T.int64(80), T.int64(2560)), "float16"), lv2067: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16"), linear_bias192: T.Buffer((T.int64(2560),), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(2560), T.int64(2560)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16") + for i, j in T.grid(T.int64(2560), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv8[v_i // T.int64(8), v_j], lv9[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(lv8[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4)), T.uint32(15))) - T.float16(7)) * lv9[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(2560), T.int64(2560)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2067[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv2067[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias192[v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias192[v_ax2] + + @T.prim_func + def fused_decode_fused_matmul2_add_add1(lv26: T.Buffer((T.int64(320), T.int64(2560)), "uint32"), lv27: T.Buffer((T.int64(80), T.int64(2560)), "float16"), lv7: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16"), linear_bias195: T.Buffer((T.int64(2560),), "float16"), lv2062: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(2560), T.int64(2560)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16") + var_T_add_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16") + for i, j in T.grid(T.int64(2560), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv26[v_i // T.int64(8), v_j], lv27[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(lv26[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8)) * T.uint32(4)), T.uint32(15))) - T.float16(7)) * lv27[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(2560), T.int64(2560)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv7[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv7[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias195[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias195[v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2], lv2062[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[v_ax0, v_ax1, v_ax2] + lv2062[v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_layer_norm_cast1(lv2064: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32"), weight67: T.Buffer((T.int64(2560),), "float32"), bias65: T.Buffer((T.int64(2560),), "float32"), var_compute_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + A_red_temp_v0 = T.alloc_buffer((T.int64(1), T.int64(1))) + A_red_temp_v1 = T.alloc_buffer((T.int64(1), T.int64(1))) + var_T_layer_norm_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560))) + for ax0, ax1, k2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("A_red_temp"): + v_ax0, v_ax1, v_k2 = T.axis.remap("SSR", [ax0, ax1, k2]) + T.reads(lv2064[v_ax0, v_ax1, v_k2]) + T.writes(A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1]) + with T.init(): + A_red_temp_v0[v_ax0, v_ax1] = T.float32(0) + A_red_temp_v1[v_ax0, v_ax1] = T.float32(0) + v_A_red_temp_v0: T.float32 = A_red_temp_v0[v_ax0, v_ax1] + lv2064[v_ax0, v_ax1, v_k2] + v_A_red_temp_v1: T.float32 = A_red_temp_v1[v_ax0, v_ax1] + lv2064[v_ax0, v_ax1, v_k2] * lv2064[v_ax0, v_ax1, v_k2] + A_red_temp_v0[v_ax0, v_ax1] = v_A_red_temp_v0 + A_red_temp_v1[v_ax0, v_ax1] = v_A_red_temp_v1 + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_layer_norm"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv2064[v_ax0, v_ax1, v_ax2], A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1], weight67[v_ax2], bias65[v_ax2]) + T.writes(var_T_layer_norm_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_layer_norm_intermediate[v_ax0, v_ax1, v_ax2] = (lv2064[v_ax0, v_ax1, v_ax2] - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) * T.rsqrt(A_red_temp_v1[v_ax0, v_ax1] * T.float32(0.00039062500000000002) - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002) * (A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) + T.float32(1.0000000000000001e-05)) * weight67[v_ax2] + bias65[v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_layer_norm_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = T.Cast("float16", var_T_layer_norm_intermediate[v_i0, v_i1, v_i2]) + + @T.prim_func + def fused_reshape2_squeeze(lv2080: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16"), var_T_squeeze_intermediate: T.Buffer((T.int64(1), T.int64(32), T.int64(80)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_T_reshape_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(32), T.int64(80)), "float16") + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), T.int64(32), T.int64(80)): + with T.block("T_reshape"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(lv2080[T.int64(0), T.int64(0), (v_ax2 * T.int64(80) + v_ax3) % T.int64(2560)]) + T.writes(var_T_reshape_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_reshape_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = lv2080[T.int64(0), T.int64(0), (v_ax2 * T.int64(80) + v_ax3) % T.int64(2560)] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(32), T.int64(80)): + with T.block("T_squeeze"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_reshape_intermediate[T.int64(0), v_ax0, v_ax1, v_ax2]) + T.writes(var_T_squeeze_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_squeeze_intermediate[v_ax0, v_ax1, v_ax2] = var_T_reshape_intermediate[T.int64(0), v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_slice1_cast6(lv4113: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32"), var_compute_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_slice_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560))) + for i, _, k in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("slice"): + v_i, v__, v_k = T.axis.remap("SSS", [i, _, k]) + T.reads(lv4113[v_i, T.int64(0), v_k]) + T.writes(var_slice_intermediate[v_i, v__, v_k]) + var_slice_intermediate[v_i, v__, v_k] = lv4113[v_i, T.int64(0), v_k] + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_slice_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = var_slice_intermediate[v_i0, v_i1, v_i2] + + @T.prim_func + def fused_transpose4_reshape4(lv2105: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(80)), "float16"), var_T_reshape_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_T_transpose_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(32), T.int64(80)), "float16") + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), T.int64(32), T.int64(80)): + with T.block("T_transpose"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(lv2105[v_ax0, v_ax2, v_ax1, v_ax3]) + T.writes(var_T_transpose_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_transpose_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = lv2105[v_ax0, v_ax2, v_ax1, v_ax3] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_reshape"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_transpose_intermediate[T.int64(0), T.int64(0), v_ax2 % T.int64(2560) // T.int64(80), v_ax2 % T.int64(80)]) + T.writes(var_T_reshape_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_reshape_intermediate[v_ax0, v_ax1, v_ax2] = var_T_transpose_intermediate[T.int64(0), T.int64(0), v_ax2 % T.int64(2560) // T.int64(80), v_ax2 % T.int64(80)] + + @T.prim_func + def layer_norm(A: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32"), B: T.Buffer((T.int64(2560),), "float32"), C: T.Buffer((T.int64(2560),), "float32"), T_layer_norm: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + A_red_temp_v0 = T.alloc_buffer((T.int64(1), T.int64(1))) + A_red_temp_v1 = T.alloc_buffer((T.int64(1), T.int64(1))) + for ax0, ax1, k2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("A_red_temp"): + v_ax0, v_ax1, v_k2 = T.axis.remap("SSR", [ax0, ax1, k2]) + T.reads(A[v_ax0, v_ax1, v_k2]) + T.writes(A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1]) + with T.init(): + A_red_temp_v0[v_ax0, v_ax1] = T.float32(0) + A_red_temp_v1[v_ax0, v_ax1] = T.float32(0) + v_A_red_temp_v0: T.float32 = A_red_temp_v0[v_ax0, v_ax1] + A[v_ax0, v_ax1, v_k2] + v_A_red_temp_v1: T.float32 = A_red_temp_v1[v_ax0, v_ax1] + A[v_ax0, v_ax1, v_k2] * A[v_ax0, v_ax1, v_k2] + A_red_temp_v0[v_ax0, v_ax1] = v_A_red_temp_v0 + A_red_temp_v1[v_ax0, v_ax1] = v_A_red_temp_v1 + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_layer_norm"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[v_ax0, v_ax1, v_ax2], A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1], B[v_ax2], C[v_ax2]) + T.writes(T_layer_norm[v_ax0, v_ax1, v_ax2]) + T_layer_norm[v_ax0, v_ax1, v_ax2] = (A[v_ax0, v_ax1, v_ax2] - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) * T.rsqrt(A_red_temp_v1[v_ax0, v_ax1] * T.float32(0.00039062500000000002) - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002) * (A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) + T.float32(1.0000000000000001e-05)) * B[v_ax2] + C[v_ax2] + + @T.prim_func + def reshape(A: T.Buffer((T.int64(1), T.int64(1)), "int32"), T_reshape: T.Buffer((T.int64(1),), "int32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for ax0 in range(T.int64(1)): + with T.block("T_reshape"): + v_ax0 = T.axis.spatial(T.int64(1), ax0) + T.reads(A[T.int64(0), T.int64(0)]) + T.writes(T_reshape[v_ax0]) + T_reshape[v_ax0] = A[T.int64(0), T.int64(0)] + + @T.prim_func + def reshape1(A: T.Buffer((T.int64(1), T.int64(2560)), "float16"), T_reshape: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_reshape"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[T.int64(0), v_ax2 % T.int64(2560)]) + T.writes(T_reshape[v_ax0, v_ax1, v_ax2]) + T_reshape[v_ax0, v_ax1, v_ax2] = A[T.int64(0), v_ax2 % T.int64(2560)] + + @T.prim_func + def reshape2(A: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float16"), T_reshape: T.Buffer((T.int64(1), T.int64(1), T.int64(32), T.int64(80)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), T.int64(32), T.int64(80)): + with T.block("T_reshape"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(A[T.int64(0), T.int64(0), (v_ax2 * T.int64(80) + v_ax3) % T.int64(2560)]) + T.writes(T_reshape[v_ax0, v_ax1, v_ax2, v_ax3]) + T_reshape[v_ax0, v_ax1, v_ax2, v_ax3] = A[T.int64(0), T.int64(0), (v_ax2 * T.int64(80) + v_ax3) % T.int64(2560)] + + @T.prim_func + def softmax2(A: T.Buffer((T.int64(1), T.int64(1), T.int64(50432)), "float32"), T_softmax_norm: T.Buffer((T.int64(1), T.int64(1), T.int64(50432)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(1))) + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(50432))) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(1))) + for i0, i1, k in T.grid(T.int64(1), T.int64(1), T.int64(50432)): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_k = T.axis.remap("SSR", [i0, i1, k]) + T.reads(A[v_i0, v_i1, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem[v_i0, v_i1] = T.max(T_softmax_maxelem[v_i0, v_i1], A[v_i0, v_i1, v_k]) + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(50432)): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(A[v_i0, v_i1, v_i2], T_softmax_maxelem[v_i0, v_i1]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2]) + T_softmax_exp[v_i0, v_i1, v_i2] = T.exp(A[v_i0, v_i1, v_i2] - T_softmax_maxelem[v_i0, v_i1]) + for i0, i1, k in T.grid(T.int64(1), T.int64(1), T.int64(50432)): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_k = T.axis.remap("SSR", [i0, i1, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1]) + with T.init(): + T_softmax_expsum[v_i0, v_i1] = T.float32(0) + T_softmax_expsum[v_i0, v_i1] = T_softmax_expsum[v_i0, v_i1] + T_softmax_exp[v_i0, v_i1, v_k] + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(50432)): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2], T_softmax_expsum[v_i0, v_i1]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2]) + T.block_attr({"axis": 2}) + T_softmax_norm[v_i0, v_i1, v_i2] = T_softmax_exp[v_i0, v_i1, v_i2] / T_softmax_expsum[v_i0, v_i1] + + @T.prim_func + def squeeze(A: T.Buffer((T.int64(1), T.int64(1), T.int64(32), T.int64(80)), "float16"), T_squeeze: T.Buffer((T.int64(1), T.int64(32), T.int64(80)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(32), T.int64(80)): + with T.block("T_squeeze"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[T.int64(0), v_ax0, v_ax1, v_ax2]) + T.writes(T_squeeze[v_ax0, v_ax1, v_ax2]) + T_squeeze[v_ax0, v_ax1, v_ax2] = A[T.int64(0), v_ax0, v_ax1, v_ax2] + + @T.prim_func + def take_decode(A: T.Buffer((T.int64(50432), T.int64(320)), "uint32"), B: T.Buffer((T.int64(50432), T.int64(80)), "float16"), C: T.Buffer((T.int64(1),), "int32"), take_decode_1: T.Buffer((T.int64(1), T.int64(2560)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for i, j in T.grid(T.int64(1), T.int64(2560)): + with T.block("take_decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(A[C[v_i], v_j // T.int64(8)], C[v_i], B[C[v_i], v_j // T.int64(32)]) + T.writes(take_decode_1[v_i, v_j]) + take_decode_1[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(A[C[v_i], v_j // T.int64(8)], T.Cast("uint32", v_j % T.int64(8)) * T.uint32(4)), T.uint32(15))) - T.float16(7)) * B[C[v_i], v_j // T.int64(32)] + + @T.prim_func + def transpose2(A: T.Buffer((T.int64(1), T.int64(1), T.int64(32), T.int64(80)), "float16"), T_transpose: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(80)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), T.int64(80)): + with T.block("T_transpose"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(A[v_ax0, v_ax2, v_ax1, v_ax3]) + T.writes(T_transpose[v_ax0, v_ax1, v_ax2, v_ax3]) + T_transpose[v_ax0, v_ax1, v_ax2, v_ax3] = A[v_ax0, v_ax2, v_ax1, v_ax3] + + ####################################### Dynamic Shape ####################################### + + @T.prim_func + def fused_NT_matmul1_add4(p_lv9: T.handle, lv1173: T.Buffer((T.int64(2560), T.int64(2560)), "float16"), linear_bias: T.Buffer((T.int64(2560),), "float16"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + lv9 = T.match_buffer(p_lv9, (T.int64(1), n, T.int64(2560)), "float16") + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560)), "float16") + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv9[v_i0, v_i1, v_k], lv1173[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv9[v_i0, v_i1, v_k] * lv1173[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias[v_ax2] + + @T.prim_func + def fused_NT_matmul1_add4_add5(p_lv49: T.handle, lv1194: T.Buffer((T.int64(2560), T.int64(2560)), "float16"), linear_bias3: T.Buffer((T.int64(2560),), "float16"), p_lv2: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + lv49 = T.match_buffer(p_lv49, (T.int64(1), n, T.int64(2560)), "float16") + lv2 = T.match_buffer(p_lv2, (T.int64(1), n, T.int64(2560)), "float16") + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560)), "float16") + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + var_T_add_intermediate_1 = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv49[v_i0, v_i1, v_k], lv1194[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv49[v_i0, v_i1, v_k] * lv1194[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias3[v_ax2]) + T.writes(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias3[v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2], lv2[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] + lv2[v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_NT_matmul2_divide1_maximum1_minimum1_cast9(p_lv36: T.handle, p_lv37: T.handle, p_lv5: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + m = T.meta_var(T.int64(128)) + lv36 = T.match_buffer(p_lv36, (T.int64(1), T.int64(32), n, T.int64(80)), "float16") + lv37 = T.match_buffer(p_lv37, (T.int64(1), T.int64(32), m, T.int64(80)), "float16") + lv5 = T.match_buffer(p_lv5, (T.int64(1), T.int64(1), n, m), "float16") + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m), "float16") + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m), "float16") + var_T_maximum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m), "float16") + var_T_minimum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m), "float16") + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, m, T.int64(80)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv36[v_i0, v_i1, v_i2, v_k], lv37[v_i0, v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv36[v_i0, v_i1, v_i2, v_k] * lv37[v_i0, v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float16(0.11179039301310044) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float16(-65504)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_minimum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv5[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.min(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv5[v_ax0, T.int64(0), v_ax2, v_ax3]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("compute"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3]) + var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.Cast("float32", var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + + @T.prim_func + def fused_NT_matmul3_add6_gelu1_cast11(p_lv57: T.handle, lv1201: T.Buffer((T.int64(10240), T.int64(2560)), "float16"), linear_bias4: T.Buffer((T.int64(10240),), "float32"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + lv57 = T.match_buffer(p_lv57, (T.int64(1), n, T.int64(2560)), "float16") + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(10240)), "float16") + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + T_multiply = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + compute = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + T_multiply_1 = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + T_add = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + var_T_multiply_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(10240), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv57[v_i0, v_i1, v_k], lv1201[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + T.Cast("float32", lv57[v_i0, v_i1, v_k] * lv1201[v_i2, v_k]) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias4[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias4[v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply[v_ax0, v_ax1, v_ax2]) + T_multiply[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[v_ax0, v_ax1, v_ax2] * T.float32(0.70710678118654757) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(T_multiply[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.erf(T_multiply[v_i0, v_i1, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(compute[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T_multiply_1[v_ax0, v_ax1, v_ax2] = compute[v_ax0, v_ax1, v_ax2] * T.float32(0.5) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T.writes(T_add[v_ax0, v_ax1, v_ax2]) + T_add[v_ax0, v_ax1, v_ax2] = T.float32(0.5) + T_multiply_1[v_ax0, v_ax1, v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply_2"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2], T_add[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[v_ax0, v_ax1, v_ax2] * T_add[v_ax0, v_ax1, v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("compute_1"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_multiply_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = T.Cast("float16", var_T_multiply_intermediate[v_i0, v_i1, v_i2]) + + @T.prim_func + def fused_NT_matmul4_add7_cast8_cast12_add5(p_lv63: T.handle, lv1208: T.Buffer((T.int64(2560), T.int64(10240)), "float16"), linear_bias5: T.Buffer((T.int64(2560),), "float32"), p_lv53: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + lv63 = T.match_buffer(p_lv63, (T.int64(1), n, T.int64(10240)), "float16") + lv53 = T.match_buffer(p_lv53, (T.int64(1), n, T.int64(2560)), "float16") + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560)), "float16") + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_T_add_intermediate_1 = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_compute_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + var_compute_intermediate_1 = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(10240)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv63[v_i0, v_i1, v_k], lv1208[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + T.Cast("float32", lv63[v_i0, v_i1, v_k] * lv1208[v_i2, v_k]) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias5[v_ax2]) + T.writes(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias5[v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate_1[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = T.Cast("float16", var_T_add_intermediate_1[v_i0, v_i1, v_i2]) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute_1"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_compute_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate_1[v_i0, v_i1, v_i2]) + var_compute_intermediate_1[v_i0, v_i1, v_i2] = var_compute_intermediate[v_i0, v_i1, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_compute_intermediate_1[v_ax0, v_ax1, v_ax2], lv53[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_compute_intermediate_1[v_ax0, v_ax1, v_ax2] + lv53[v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_NT_matmul4_add7_cast8_cast12_add5_cast7(p_lv2047: T.handle, lv2510: T.Buffer((T.int64(2560), T.int64(10240)), "float16"), linear_bias191: T.Buffer((T.int64(2560),), "float32"), p_lv2037: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + lv2047 = T.match_buffer(p_lv2047, (T.int64(1), n, T.int64(10240)), "float16") + lv2037 = T.match_buffer(p_lv2037, (T.int64(1), n, T.int64(2560)), "float16") + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_compute_intermediate_1 = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + var_compute_intermediate_2 = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + var_T_add_intermediate_1 = T.alloc_buffer((T.int64(1), n, T.int64(2560)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(10240)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2047[v_i0, v_i1, v_k], lv2510[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + T.Cast("float32", lv2047[v_i0, v_i1, v_k] * lv2510[v_i2, v_k]) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias191[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias191[v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate_1[v_i0, v_i1, v_i2]) + var_compute_intermediate_1[v_i0, v_i1, v_i2] = T.Cast("float16", var_T_add_intermediate[v_i0, v_i1, v_i2]) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute_1"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_compute_intermediate_1[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate_2[v_i0, v_i1, v_i2]) + var_compute_intermediate_2[v_i0, v_i1, v_i2] = var_compute_intermediate_1[v_i0, v_i1, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_compute_intermediate_2[v_ax0, v_ax1, v_ax2], lv2037[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] = var_compute_intermediate_2[v_ax0, v_ax1, v_ax2] + lv2037[v_ax0, v_ax1, v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute_2"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate_1[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = T.Cast("float32", var_T_add_intermediate_1[v_i0, v_i1, v_i2]) + + @T.prim_func + def fused_NT_matmul_divide_maximum_minimum_cast2(lv2094: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(80)), "float16"), p_lv2095: T.handle, p_lv2063: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + lv2095 = T.match_buffer(p_lv2095, (T.int64(1), T.int64(32), n, T.int64(80)), "float16") + lv2063 = T.match_buffer(p_lv2063, (T.int64(1), T.int64(1), T.int64(1), n), "float16") + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), T.int64(1), n)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + var_T_maximum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + var_T_minimum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n, T.int64(80)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv2094[v_i0, v_i1, v_i2, v_k], lv2095[v_i0, v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv2094[v_i0, v_i1, v_i2, v_k] * lv2095[v_i0, v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float16(0.11179039301310044) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float16(-65504)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_minimum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv2063[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.min(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv2063[v_ax0, T.int64(0), v_ax2, v_ax3]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("compute"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3]) + var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.Cast("float32", var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + + @T.prim_func + def fused_layer_norm1_cast8(p_lv6: T.handle, weight1: T.Buffer((T.int64(2560),), "float32"), bias: T.Buffer((T.int64(2560),), "float32"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + lv6 = T.match_buffer(p_lv6, (T.int64(1), n, T.int64(2560))) + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560)), "float16") + # with T.block("root"): + A_red_temp_v0 = T.alloc_buffer((T.int64(1), n)) + A_red_temp_v1 = T.alloc_buffer((T.int64(1), n)) + var_T_layer_norm_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + for ax0, ax1, k2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("A_red_temp"): + v_ax0, v_ax1, v_k2 = T.axis.remap("SSR", [ax0, ax1, k2]) + T.reads(lv6[v_ax0, v_ax1, v_k2]) + T.writes(A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1]) + with T.init(): + A_red_temp_v0[v_ax0, v_ax1] = T.float32(0) + A_red_temp_v1[v_ax0, v_ax1] = T.float32(0) + v_A_red_temp_v0: T.float32 = A_red_temp_v0[v_ax0, v_ax1] + lv6[v_ax0, v_ax1, v_k2] + v_A_red_temp_v1: T.float32 = A_red_temp_v1[v_ax0, v_ax1] + lv6[v_ax0, v_ax1, v_k2] * lv6[v_ax0, v_ax1, v_k2] + A_red_temp_v0[v_ax0, v_ax1] = v_A_red_temp_v0 + A_red_temp_v1[v_ax0, v_ax1] = v_A_red_temp_v1 + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_layer_norm"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv6[v_ax0, v_ax1, v_ax2], A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1], weight1[v_ax2], bias[v_ax2]) + T.writes(var_T_layer_norm_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_layer_norm_intermediate[v_ax0, v_ax1, v_ax2] = (lv6[v_ax0, v_ax1, v_ax2] - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) * T.rsqrt(A_red_temp_v1[v_ax0, v_ax1] * T.float32(0.00039062500000000002) - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002) * (A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) + T.float32(1.0000000000000001e-05)) * weight1[v_ax2] + bias[v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_layer_norm_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = T.Cast("float16", var_T_layer_norm_intermediate[v_i0, v_i1, v_i2]) + + @T.prim_func + def layer_norm1(var_A: T.handle, B: T.Buffer((T.int64(2560),), "float32"), C: T.Buffer((T.int64(2560),), "float32"), var_T_layer_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(2560))) + T_layer_norm = T.match_buffer(var_T_layer_norm, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + A_red_temp_v0 = T.alloc_buffer((T.int64(1), n)) + A_red_temp_v1 = T.alloc_buffer((T.int64(1), n)) + for ax0, ax1, k2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("A_red_temp"): + v_ax0, v_ax1, v_k2 = T.axis.remap("SSR", [ax0, ax1, k2]) + T.reads(A[v_ax0, v_ax1, v_k2]) + T.writes(A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1]) + with T.init(): + A_red_temp_v0[v_ax0, v_ax1] = T.float32(0) + A_red_temp_v1[v_ax0, v_ax1] = T.float32(0) + v_A_red_temp_v0: T.float32 = A_red_temp_v0[v_ax0, v_ax1] + A[v_ax0, v_ax1, v_k2] + v_A_red_temp_v1: T.float32 = A_red_temp_v1[v_ax0, v_ax1] + A[v_ax0, v_ax1, v_k2] * A[v_ax0, v_ax1, v_k2] + A_red_temp_v0[v_ax0, v_ax1] = v_A_red_temp_v0 + A_red_temp_v1[v_ax0, v_ax1] = v_A_red_temp_v1 + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_layer_norm"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[v_ax0, v_ax1, v_ax2], A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1], B[v_ax2], C[v_ax2]) + T.writes(T_layer_norm[v_ax0, v_ax1, v_ax2]) + T_layer_norm[v_ax0, v_ax1, v_ax2] = (A[v_ax0, v_ax1, v_ax2] - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) * T.rsqrt(A_red_temp_v1[v_ax0, v_ax1] * T.float32(0.00039062500000000002) - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002) * (A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) + T.float32(1.0000000000000001e-05)) * B[v_ax2] + C[v_ax2] + + @T.prim_func + def matmul3(var_A: T.handle, var_B: T.handle, matmul: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(80)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(32)) + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), T.int64(1), n), "float16") + B = T.match_buffer(var_B, (T.int64(1), T.int64(32), n, T.int64(80)), "float16") + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), T.int64(1), T.int64(80), n): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(A[v_i0, v_i1, v_i2, v_k], B[v_i0, v_i1, v_k, v_i3]) + T.writes(matmul[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + matmul[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + matmul[v_i0, v_i1, v_i2, v_i3] = matmul[v_i0, v_i1, v_i2, v_i3] + A[v_i0, v_i1, v_i2, v_k] * B[v_i0, v_i1, v_k, v_i3] + + @T.prim_func + def matmul9(var_A: T.handle, var_B: T.handle, var_matmul: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + m = T.meta_var(T.int64(32)) + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), n, m), "float16") + B = T.match_buffer(var_B, (T.int64(1), T.int64(32), m, T.int64(80)), "float16") + matmul = T.match_buffer(var_matmul, (T.int64(1), T.int64(32), n, T.int64(80)), "float16") + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, T.int64(80), m): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(A[v_i0, v_i1, v_i2, v_k], B[v_i0, v_i1, v_k, v_i3]) + T.writes(matmul[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + matmul[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + matmul[v_i0, v_i1, v_i2, v_i3] = matmul[v_i0, v_i1, v_i2, v_i3] + A[v_i0, v_i1, v_i2, v_k] * B[v_i0, v_i1, v_k, v_i3] + +# fmt: on diff --git a/mlc_llm/dispatch/gpt_neox/redpajama_q4f32.py b/mlc_llm/dispatch/gpt_neox/redpajama_q4f32.py new file mode 100644 index 0000000..b6e9123 --- /dev/null +++ b/mlc_llm/dispatch/gpt_neox/redpajama_q4f32.py @@ -0,0 +1,840 @@ +# pylint: disable=missing-docstring,line-too-long,invalid-name,too-many-statements,too-many-locals +import tvm +from tvm import tir +from tvm.script import tir as T + +from .redpajama_q4f32_mod import Module as MOD + +# fmt: off + +def fused_NT_matmul1_divide_maximum_minimum(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + sch.pad_einsum(b0, [1, 1, 32, 32, 1]) + l1, l2, l3, l4, l5 = sch.get_loops(b0) + l6, l7 = sch.split(l3, [None, 32]) + l8, l9 = sch.split(l4, [None, 32]) + sch.reorder(l6, l8, l1, l2, l7, l9, l5) + + b1 = sch.get_block(name="T_divide", func_name="main") + b2 = sch.get_block(name="T_maximum", func_name="main") + b3 = sch.get_block(name="T_minimum", func_name="main") + b4 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, _, l5, l6, l7, l8, l9 = sch.get_loops(block=b0) + v10, v11, v12, v13, v14 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l15, l16, l17, l18, l19 = sch.split(loop=l5, factors=[v10, v11, v12, v13, v14], preserve_unit_iters=True) + v20, v21, v22, v23, v24 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[16, 1, 2, 1, 1]) + l25, l26, l27, l28, l29 = sch.split(loop=l6, factors=[v20, v21, v22, v23, v24], preserve_unit_iters=True) + v30, v31, v32, v33, v34 = sch.sample_perfect_tile(loop=l7, n=5, max_innermost_factor=64, decision=[8, 1, 4, 1, 4]) + l35, l36, l37, l38, l39 = sch.split(loop=l7, factors=[v30, v31, v32, v33, v34], preserve_unit_iters=True) + v40, v41, v42, v43, v44 = sch.sample_perfect_tile(loop=l8, n=5, max_innermost_factor=64, decision=[8, 1, 4, 2, 2]) + l45, l46, l47, l48, l49 = sch.split(loop=l8, factors=[v40, v41, v42, v43, v44], preserve_unit_iters=True) + v50, v51, v52 = sch.sample_perfect_tile(loop=l9, n=3, max_innermost_factor=64, decision=[10, 4, 2]) + l53, l54, l55 = sch.split(loop=l9, factors=[v50, v51, v52], preserve_unit_iters=True) + sch.reorder(l15, l25, l35, l45, l16, l26, l36, l46, l17, l27, l37, l47, l53, l54, l18, l28, l38, l48, l55, l19, l29, l39, l49) + l56 = sch.fuse(l15, l25, l35, l45, preserve_unit_iters=True) + sch.bind(loop=l56, thread_axis="blockIdx.x") + l57 = sch.fuse(l16, l26, l36, l46, preserve_unit_iters=True) + sch.bind(loop=l57, thread_axis="vthread.x") + l58 = sch.fuse(l17, l27, l37, l47, preserve_unit_iters=True) + sch.bind(loop=l58, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b59 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b59, loop=l58, preserve_unit_loops=True, index=-1) + b60 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b60, loop=l53, preserve_unit_loops=True, index=-1) + l65, l66, l67, l68 = sch.get_loops(block=b60)[-4:] + sch.fuse(l65, l66, l67, l68, preserve_unit_iters=True) + v70 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=3) + sch.annotate(block_or_loop=b60, ann_key="meta_schedule.cooperative_fetch", ann_val=v70) + b71 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b71, loop=l53, preserve_unit_loops=True, index=-1) + l76, l77, l78, l79 = sch.get_loops(block=b71)[-4:] + sch.fuse(l76, l77, l78, l79, preserve_unit_iters=True) + v81 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b71, ann_key="meta_schedule.cooperative_fetch", ann_val=v81) + sch.reverse_compute_inline(block=b3) + sch.reverse_compute_inline(block=b2) + sch.reverse_compute_inline(block=b1) + v82 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=1) + sch.annotate(block_or_loop=b4, ann_key="meta_schedule.unroll_explicit", ann_val=v82) + sch.enter_postproc() + sch.unannotate(block_or_loop=b60, ann_key="meta_schedule.cooperative_fetch") + l87 = sch.get_loops(block=b60)[-1] + _, l89, l90 = sch.split(loop=l87, factors=[None, 32, 4], preserve_unit_iters=True) + sch.vectorize(loop=l90) + sch.bind(loop=l89, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b71, ann_key="meta_schedule.cooperative_fetch") + l95 = sch.get_loops(block=b71)[-1] + _, l97 = sch.split(loop=l95, factors=[None, 32], preserve_unit_iters=True) + sch.bind(loop=l97, thread_axis="threadIdx.x") + b98 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b98, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("lv34_pad") + sch.compute_inline(b1) + b1 = sch.get_block("lv35_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + b140 = sch.get_block(name="NT_matmul", func_name="main") + l144 = sch.get_loops(block=b140)[5] + sch.decompose_reduction(block=b140, loop=l144) + + b101 = sch.get_child_blocks(b98)[2] + l116 = sch.get_loops(block=b101)[0] + sch.annotate(block_or_loop=l116, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l116, ann_key="pragma_unroll_explicit", ann_val=1) + + +def fused_NT_matmul2_add2_gelu(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="T_multiply", func_name="main") + b3 = sch.get_block(name="compute", func_name="main") + b4 = sch.get_block(name="T_multiply_1", func_name="main") + b5 = sch.get_block(name="T_add_1", func_name="main") + b6 = sch.get_block(name="T_multiply_2", func_name="main") + b7 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l8, l9, l10, l11 = sch.get_loops(block=b0) + v12, v13, v14, v15, v16 = sch.sample_perfect_tile(loop=l8, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l17, l18, l19, l20, l21 = sch.split(loop=l8, factors=[v12, v13, v14, v15, v16], preserve_unit_iters=True) + v22, v23, v24, v25, v26 = sch.sample_perfect_tile(loop=l9, n=5, max_innermost_factor=64, decision=[1, 2, 16, 2, 2]) + l27, l28, l29, l30, l31 = sch.split(loop=l9, factors=[v22, v23, v24, v25, v26], preserve_unit_iters=True) + v32, v33, v34, v35, v36 = sch.sample_perfect_tile(loop=l10, n=5, max_innermost_factor=64, decision=[320, 1, 8, 4, 1]) + l37, l38, l39, l40, l41 = sch.split(loop=l10, factors=[v32, v33, v34, v35, v36], preserve_unit_iters=True) + v42, v43, v44 = sch.sample_perfect_tile(loop=l11, n=3, max_innermost_factor=64, decision=[160, 4, 4]) + l45, l46, l47 = sch.split(loop=l11, factors=[v42, v43, v44], preserve_unit_iters=True) + sch.reorder(l17, l27, l37, l18, l28, l38, l19, l29, l39, l45, l46, l20, l30, l40, l47, l21, l31, l41) + l48 = sch.fuse(l17, l27, l37, preserve_unit_iters=True) + sch.bind(loop=l48, thread_axis="blockIdx.x") + l49 = sch.fuse(l18, l28, l38, preserve_unit_iters=True) + sch.bind(loop=l49, thread_axis="vthread.x") + l50 = sch.fuse(l19, l29, l39, preserve_unit_iters=True) + sch.bind(loop=l50, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=1024) + b51 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b51, loop=l50, preserve_unit_loops=True, index=-1) + b52 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b52, loop=l45, preserve_unit_loops=True, index=-1) + l57, l58, l59 = sch.get_loops(block=b52)[-3:] + sch.fuse(l57, l58, l59, preserve_unit_iters=True) + v61 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b52, ann_key="meta_schedule.cooperative_fetch", ann_val=v61) + b62 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b62, loop=l45, preserve_unit_loops=True, index=-1) + l67, l68 = sch.get_loops(block=b62)[-2:] + sch.fuse(l67, l68, preserve_unit_iters=True) + v70 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b62, ann_key="meta_schedule.cooperative_fetch", ann_val=v70) + sch.compute_inline(block=b5) + sch.compute_inline(block=b4) + sch.compute_inline(block=b3) + sch.compute_inline(block=b2) + sch.compute_inline(block=b1) + sch.reverse_compute_inline(block=b6) + v71 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=1) + sch.annotate(block_or_loop=b7, ann_key="meta_schedule.unroll_explicit", ann_val=v71) + sch.enter_postproc() + sch.unannotate(block_or_loop=b52, ann_key="meta_schedule.cooperative_fetch") + l76 = sch.get_loops(block=b52)[-1] + _, l78, l79 = sch.split(loop=l76, factors=[None, 64, 2], preserve_unit_iters=True) + sch.vectorize(loop=l79) + sch.bind(loop=l78, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b62, ann_key="meta_schedule.cooperative_fetch") + l84 = sch.get_loops(block=b62)[-1] + _, l86 = sch.split(loop=l84, factors=[None, 64], preserve_unit_iters=True) + sch.bind(loop=l86, thread_axis="threadIdx.x") + b87 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b87, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("lv51_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + _, _, b90, _ = sch.get_child_blocks(b87) + l105 = sch.get_loops(block=b90)[0] + sch.annotate(block_or_loop=l105, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l105, ann_key="pragma_unroll_explicit", ann_val=1) + b123 = sch.get_block(name="NT_matmul", func_name="main") + l127 = sch.get_loops(block=b123)[4] + sch.decompose_reduction(block=b123, loop=l127) + + +def fused_NT_matmul3_add_cast_add1(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="compute", func_name="main") + b3 = sch.get_block(name="T_add_1", func_name="main") + b4 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l5, l6, l7, l8 = sch.get_loops(block=b0) + v9, v10, v11, v12, v13 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l14, l15, l16, l17, l18 = sch.split(loop=l5, factors=[v9, v10, v11, v12, v13], preserve_unit_iters=True) + v19, v20, v21, v22, v23 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[1, 4, 32, 1, 1]) + l24, l25, l26, l27, l28 = sch.split(loop=l6, factors=[v19, v20, v21, v22, v23], preserve_unit_iters=True) + v29, v30, v31, v32, v33 = sch.sample_perfect_tile(loop=l7, n=5, max_innermost_factor=64, decision=[40, 1, 4, 16, 1]) + l34, l35, l36, l37, l38 = sch.split(loop=l7, factors=[v29, v30, v31, v32, v33], preserve_unit_iters=True) + v39, v40, v41 = sch.sample_perfect_tile(loop=l8, n=3, max_innermost_factor=64, decision=[640, 4, 4]) + l42, l43, l44 = sch.split(loop=l8, factors=[v39, v40, v41], preserve_unit_iters=True) + sch.reorder(l14, l24, l34, l15, l25, l35, l16, l26, l36, l42, l43, l17, l27, l37, l44, l18, l28, l38) + l45 = sch.fuse(l14, l24, l34, preserve_unit_iters=True) + sch.bind(loop=l45, thread_axis="blockIdx.x") + l46 = sch.fuse(l15, l25, l35, preserve_unit_iters=True) + sch.bind(loop=l46, thread_axis="vthread.x") + l47 = sch.fuse(l16, l26, l36, preserve_unit_iters=True) + sch.bind(loop=l47, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b48 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b48, loop=l47, preserve_unit_loops=True, index=-1) + b49 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b49, loop=l42, preserve_unit_loops=True, index=-1) + l54, l55, l56 = sch.get_loops(block=b49)[-3:] + sch.fuse(l54, l55, l56, preserve_unit_iters=True) + v58 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b49, ann_key="meta_schedule.cooperative_fetch", ann_val=v58) + b59 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b59, loop=l42, preserve_unit_loops=True, index=-1) + l64, l65 = sch.get_loops(block=b59)[-2:] + sch.fuse(l64, l65, preserve_unit_iters=True) + v67 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b59, ann_key="meta_schedule.cooperative_fetch", ann_val=v67) + sch.reverse_compute_inline(block=b3) + sch.reverse_compute_inline(block=b2) + sch.reverse_compute_inline(block=b1) + v68 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=2) + sch.annotate(block_or_loop=b4, ann_key="meta_schedule.unroll_explicit", ann_val=v68) + sch.enter_postproc() + sch.unannotate(block_or_loop=b49, ann_key="meta_schedule.cooperative_fetch") + l73 = sch.get_loops(block=b49)[-1] + _, l75, l76 = sch.split(loop=l73, factors=[None, 128, 2], preserve_unit_iters=True) + sch.vectorize(loop=l76) + sch.bind(loop=l75, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b59, ann_key="meta_schedule.cooperative_fetch") + l81 = sch.get_loops(block=b59)[-1] + _, l83, l84 = sch.split(loop=l81, factors=[None, 128, 2], preserve_unit_iters=True) + sch.vectorize(loop=l84) + sch.bind(loop=l83, thread_axis="threadIdx.x") + b85 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b85, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("lv56_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + _, _, b88, _ = sch.get_child_blocks(b85) + l104 = sch.get_loops(block=b88)[0] + sch.annotate(block_or_loop=l104, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l104, ann_key="pragma_unroll_explicit", ann_val=1) + b121 = sch.get_block(name="NT_matmul", func_name="main") + l125 = sch.get_loops(block=b121)[4] + sch.decompose_reduction(block=b121, loop=l125) + + +def fused_NT_matmul4_divide2_maximum1_minimum1(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + sch.pad_einsum(b0, [1, 1, 1, 32, 1]) + l1, l2, l3, l4, l5 = sch.get_loops(b0) + l6, l7 = sch.split(l4, [None, 32]) + sch.reorder(l6, l1, l2, l3, l7, l5) + + b1 = sch.get_block(name="T_divide", func_name="main") + b2 = sch.get_block(name="T_maximum", func_name="main") + b3 = sch.get_block(name="T_minimum", func_name="main") + b4 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l5, l6, l7, l8, l9 = sch.get_loops(block=b0) + v10, v11, v12, v13, v14 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l15, l16, l17, l18, l19 = sch.split(loop=l5, factors=[v10, v11, v12, v13, v14], preserve_unit_iters=True) + v20, v21, v22, v23, v24 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[16, 2, 1, 1, 1]) + l25, l26, l27, l28, l29 = sch.split(loop=l6, factors=[v20, v21, v22, v23, v24], preserve_unit_iters=True) + v30, v31, v32, v33, v34 = sch.sample_perfect_tile(loop=l7, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l35, l36, l37, l38, l39 = sch.split(loop=l7, factors=[v30, v31, v32, v33, v34], preserve_unit_iters=True) + v40, v41, v42, v43, v44 = sch.sample_perfect_tile(loop=l8, n=5, max_innermost_factor=64, decision=[2, 1, 32, 1, 2]) + l45, l46, l47, l48, l49 = sch.split(loop=l8, factors=[v40, v41, v42, v43, v44], preserve_unit_iters=True) + v50, v51, v52 = sch.sample_perfect_tile(loop=l9, n=3, max_innermost_factor=64, decision=[20, 2, 2]) + l53, l54, l55 = sch.split(loop=l9, factors=[v50, v51, v52], preserve_unit_iters=True) + sch.reorder(l15, l25, l35, l45, l16, l26, l36, l46, l17, l27, l37, l47, l53, l54, l18, l28, l38, l48, l55, l19, l29, l39, l49) + l56 = sch.fuse(l15, l25, l35, l45, preserve_unit_iters=True) + sch.bind(loop=l56, thread_axis="blockIdx.x") + l57 = sch.fuse(l16, l26, l36, l46, preserve_unit_iters=True) + sch.bind(loop=l57, thread_axis="vthread.x") + l58 = sch.fuse(l17, l27, l37, l47, preserve_unit_iters=True) + sch.bind(loop=l58, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b59 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b59, loop=l58, preserve_unit_loops=True, index=-1) + b60 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b60, loop=l53, preserve_unit_loops=True, index=-1) + l65, l66, l67, l68 = sch.get_loops(block=b60)[-4:] + sch.fuse(l65, l66, l67, l68, preserve_unit_iters=True) + v70 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b60, ann_key="meta_schedule.cooperative_fetch", ann_val=v70) + b71 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b71, loop=l53, preserve_unit_loops=True, index=-1) + l76, l77, l78, l79 = sch.get_loops(block=b71)[-4:] + sch.fuse(l76, l77, l78, l79, preserve_unit_iters=True) + v81 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=0) + sch.annotate(block_or_loop=b71, ann_key="meta_schedule.cooperative_fetch", ann_val=v81) + sch.reverse_compute_inline(block=b3) + sch.reverse_compute_inline(block=b2) + sch.reverse_compute_inline(block=b1) + v82 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=0) + sch.annotate(block_or_loop=b4, ann_key="meta_schedule.unroll_explicit", ann_val=v82) + sch.enter_postproc() + sch.unannotate(block_or_loop=b60, ann_key="meta_schedule.cooperative_fetch") + l87 = sch.get_loops(block=b60)[-1] + _, l89, l90 = sch.split(loop=l87, factors=[None, 16, 2], preserve_unit_iters=True) + sch.vectorize(loop=l90) + sch.bind(loop=l89, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b71, ann_key="meta_schedule.cooperative_fetch") + l95 = sch.get_loops(block=b71)[-1] + _, l97 = sch.split(loop=l95, factors=[None, 16], preserve_unit_iters=True) + sch.bind(loop=l97, thread_axis="threadIdx.x") + b98 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b98, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("lv1836_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + b140 = sch.get_block(name="NT_matmul", func_name="main") + l144 = sch.get_loops(block=b140)[4] + sch.decompose_reduction(block=b140, loop=l144) + + +def fused_NT_matmul_add(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l3, l4, l5, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l3, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[2, 4, 8, 1, 2]) + l22, l23, l24, l25, l26 = sch.split(loop=l4, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[64, 5, 8, 1, 1]) + l32, l33, l34, l35, l36 = sch.split(loop=l5, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[320, 2, 4]) + l40, l41, l42 = sch.split(loop=l6, factors=[v37, v38, v39], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l13, l23, l33, l14, l24, l34, l40, l41, l15, l25, l35, l42, l16, l26, l36) + l43 = sch.fuse(l12, l22, l32, preserve_unit_iters=True) + sch.bind(loop=l43, thread_axis="blockIdx.x") + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="vthread.x") + l45 = sch.fuse(l14, l24, l34, preserve_unit_iters=True) + sch.bind(loop=l45, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b46 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b46, loop=l45, preserve_unit_loops=True, index=-1) + b47 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b47, loop=l40, preserve_unit_loops=True, index=-1) + l52, l53, l54 = sch.get_loops(block=b47)[-3:] + sch.fuse(l52, l53, l54, preserve_unit_iters=True) + v56 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch", ann_val=v56) + b57 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l40, preserve_unit_loops=True, index=-1) + l62, l63 = sch.get_loops(block=b57)[-2:] + sch.fuse(l62, l63, preserve_unit_iters=True) + v65 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=0) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v65) + sch.reverse_compute_inline(block=b1) + v66 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=3) + sch.annotate(block_or_loop=b2, ann_key="meta_schedule.unroll_explicit", ann_val=v66) + sch.enter_postproc() + sch.unannotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch") + l71 = sch.get_loops(block=b47)[-1] + _, l73, l74 = sch.split(loop=l71, factors=[None, 64, 2], preserve_unit_iters=True) + sch.vectorize(loop=l74) + sch.bind(loop=l73, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + l79 = sch.get_loops(block=b57)[-1] + _, l81 = sch.split(loop=l79, factors=[None, 64], preserve_unit_iters=True) + sch.bind(loop=l81, thread_axis="threadIdx.x") + b82 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b82, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("lv7_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + _, _, b85, _ = sch.get_child_blocks(b82) + l100 = sch.get_loops(block=b85)[0] + sch.annotate(block_or_loop=l100, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l100, ann_key="pragma_unroll_explicit", ann_val=1) + b118 = sch.get_block(name="NT_matmul", func_name="main") + l122 = sch.get_loops(block=b118)[4] + sch.decompose_reduction(block=b118, loop=l122) + + +def fused_NT_matmul_add_add1(sch: tir.Schedule): + b0 = sch.get_block(name="NT_matmul", func_name="main") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="T_add_1", func_name="main") + b3 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l4, l5, l6, l7 = sch.get_loops(block=b0) + v8, v9, v10, v11, v12 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l13, l14, l15, l16, l17 = sch.split(loop=l4, factors=[v8, v9, v10, v11, v12], preserve_unit_iters=True) + v18, v19, v20, v21, v22 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[2, 2, 32, 1, 1]) + l23, l24, l25, l26, l27 = sch.split(loop=l5, factors=[v18, v19, v20, v21, v22], preserve_unit_iters=True) + v28, v29, v30, v31, v32 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[80, 2, 1, 16, 1]) + l33, l34, l35, l36, l37 = sch.split(loop=l6, factors=[v28, v29, v30, v31, v32], preserve_unit_iters=True) + v38, v39, v40 = sch.sample_perfect_tile(loop=l7, n=3, max_innermost_factor=64, decision=[320, 1, 8]) + l41, l42, l43 = sch.split(loop=l7, factors=[v38, v39, v40], preserve_unit_iters=True) + sch.reorder(l13, l23, l33, l14, l24, l34, l15, l25, l35, l41, l42, l16, l26, l36, l43, l17, l27, l37) + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="blockIdx.x") + l45 = sch.fuse(l14, l24, l34, preserve_unit_iters=True) + sch.bind(loop=l45, thread_axis="vthread.x") + l46 = sch.fuse(l15, l25, l35, preserve_unit_iters=True) + sch.bind(loop=l46, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b47 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b47, loop=l46, preserve_unit_loops=True, index=-1) + b48 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b48, loop=l41, preserve_unit_loops=True, index=-1) + l53, l54, l55 = sch.get_loops(block=b48)[-3:] + sch.fuse(l53, l54, l55, preserve_unit_iters=True) + v57 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=3) + sch.annotate(block_or_loop=b48, ann_key="meta_schedule.cooperative_fetch", ann_val=v57) + b58 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b58, loop=l41, preserve_unit_loops=True, index=-1) + l63, l64 = sch.get_loops(block=b58)[-2:] + sch.fuse(l63, l64, preserve_unit_iters=True) + v66 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=3) + sch.annotate(block_or_loop=b58, ann_key="meta_schedule.cooperative_fetch", ann_val=v66) + sch.reverse_compute_inline(block=b2) + sch.reverse_compute_inline(block=b1) + v67 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=2) + sch.annotate(block_or_loop=b3, ann_key="meta_schedule.unroll_explicit", ann_val=v67) + sch.enter_postproc() + sch.unannotate(block_or_loop=b48, ann_key="meta_schedule.cooperative_fetch") + l72 = sch.get_loops(block=b48)[-1] + _, l74, l75 = sch.split(loop=l72, factors=[None, 32, 4], preserve_unit_iters=True) + sch.vectorize(loop=l75) + sch.bind(loop=l74, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b58, ann_key="meta_schedule.cooperative_fetch") + l80 = sch.get_loops(block=b58)[-1] + _, l82, l83 = sch.split(loop=l80, factors=[None, 32, 4], preserve_unit_iters=True) + sch.vectorize(loop=l83) + sch.bind(loop=l82, thread_axis="threadIdx.x") + b84 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b84, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("lv45_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + _, _, b87, _ = sch.get_child_blocks(b84) + l103 = sch.get_loops(block=b87)[0] + sch.annotate(block_or_loop=l103, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l103, ann_key="pragma_unroll_explicit", ann_val=1) + b121 = sch.get_block(name="NT_matmul", func_name="main") + l125 = sch.get_loops(block=b121)[4] + sch.decompose_reduction(block=b121, loop=l125) + + + +def layer_norm(sch: tir.Schedule): + b0 = sch.get_block(name="A_red_temp", func_name="main") + b1 = sch.get_block(name="T_layer_norm", func_name="main") + b2 = sch.get_block(name="root", func_name="main") + v3 = sch.sample_categorical(candidates=[4, 8, 16, 32, 64, 128, 256, 512], probs=[0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125], decision=4) + _, _, l6 = sch.get_loops(block=b0) + _, l8 = sch.split(loop=l6, factors=[None, v3], preserve_unit_iters=True) + sch.bind(loop=l8, thread_axis="threadIdx.x") + v9 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=3) + sch.annotate(block_or_loop=b2, ann_key="meta_schedule.unroll_explicit", ann_val=v9) + l10, l11, l12 = sch.get_loops(block=b1) + l13 = sch.fuse(l10, l11, l12, preserve_unit_iters=True) + l14, l15, l16 = sch.split(loop=l13, factors=[None, 256, 256], preserve_unit_iters=True) + sch.reorder(l15, l16, l14) + sch.bind(loop=l15, thread_axis="blockIdx.x") + sch.bind(loop=l16, thread_axis="threadIdx.x") + l17, l18, _, _ = sch.get_loops(block=b0) + l21 = sch.fuse(l17, l18, preserve_unit_iters=True) + sch.bind(loop=l21, thread_axis="blockIdx.x") + sch.enter_postproc() + b22 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b22, ann_key="meta_schedule.unroll_explicit") + b23, _ = sch.get_child_blocks(b22) + l25, _, _ = sch.get_loops(block=b23) + sch.annotate(block_or_loop=l25, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l25, ann_key="pragma_unroll_explicit", ann_val=1) + + +def matmul(sch: tir.Schedule): + b0 = sch.get_block(name="matmul", func_name="main") + sch.pad_einsum(b0, [1, 1, 32, 1, 32]) + l1, l2, l3, l4, k = sch.get_loops(b0) + s0, s1 = sch.split(l3, [None, 32]) + k0, k1 = sch.split(k, [None, 32]) + sch.reorder(s0, l1, l2, s1, k0, l4, k1) + + b1 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l2, l3, l4, _, l5, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l2, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l2, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[8, 4, 1, 1, 1]) + l22, l23, l24, l25, l26 = sch.split(loop=l3, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[16, 4, 2, 1, 1]) + l32, l33, l34, l35, l36 = sch.split(loop=l4, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39, v40, v41 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[1, 1, 80, 1, 1]) + l42, l43, l44, l45, l46 = sch.split(loop=l5, factors=[v37, v38, v39, v40, v41], preserve_unit_iters=True) + v47, v48, v49 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[8, 4, 1]) + l50, l51, l52 = sch.split(loop=l6, factors=[v47, v48, v49], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l42, l13, l23, l33, l43, l14, l24, l34, l44, k0, l50, l51, l15, l25, l35, l45, l52, l16, l26, l36, l46) + + l53 = sch.fuse(l12, l22, l32, l42, preserve_unit_iters=True) + sch.bind(loop=l53, thread_axis="blockIdx.x") + l54 = sch.fuse(l13, l23, l33, l43, preserve_unit_iters=True) + sch.bind(loop=l54, thread_axis="vthread.x") + l55 = sch.fuse(l14, l24, l34, l44, preserve_unit_iters=True) + sch.bind(loop=l55, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b56 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b56, loop=l55, preserve_unit_loops=True, index=-1) + b57 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l50, preserve_unit_loops=True, index=-1) + l62, l63, l64, l65 = sch.get_loops(block=b57)[-4:] + sch.fuse(l62, l63, l64, l65, preserve_unit_iters=True) + v67 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=3) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v67) + b68 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b68, loop=l50, preserve_unit_loops=True, index=-1) + l73, l74, l75, l76 = sch.get_loops(block=b68)[-4:] + sch.fuse(l73, l74, l75, l76, preserve_unit_iters=True) + v78 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b68, ann_key="meta_schedule.cooperative_fetch", ann_val=v78) + v79 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=2) + sch.annotate(block_or_loop=b1, ann_key="meta_schedule.unroll_explicit", ann_val=v79) + sch.enter_postproc() + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + l84 = sch.get_loops(block=b57)[-1] + _, l86, l87 = sch.split(loop=l84, factors=[None, 160, 4], preserve_unit_iters=True) + sch.vectorize(loop=l87) + sch.bind(loop=l86, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b68, ann_key="meta_schedule.cooperative_fetch") + l92 = sch.get_loops(block=b68)[-1] + _, l94, l95 = sch.split(loop=l92, factors=[None, 160, 2], preserve_unit_iters=True) + sch.vectorize(loop=l95) + sch.bind(loop=l94, thread_axis="threadIdx.x") + b96 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b96, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("A_pad") + sch.compute_inline(b1) + b1 = sch.get_block("B_pad") + sch.compute_inline(b1) + b1 = sch.get_block("matmul_1_pad") + sch.reverse_compute_inline(b1) + + _, _, b99, _ = sch.get_child_blocks(b96) + l115 = sch.get_loops(block=b99)[0] + sch.annotate(block_or_loop=l115, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l115, ann_key="pragma_unroll_explicit", ann_val=1) + b136 = sch.get_block(name="matmul", func_name="main") + l140 = sch.get_loops(block=b136)[4] + sch.decompose_reduction(block=b136, loop=l140) + + + +def matmul8(sch: tir.Schedule): + b0 = sch.get_block(name="matmul", func_name="main") + sch.pad_einsum(b0, [1, 1, 1, 1, 32]) + l1, l2, l3, l4, k = sch.get_loops(b0) + k0, k1 = sch.split(k, [None, 32]) + sch.reorder(l1, l2, l3, k0, l4, k1) + + b1 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + l2, l3, l4, _, l5, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l2, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l2, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[16, 1, 2, 1, 1]) + l22, l23, l24, l25, l26 = sch.split(loop=l3, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l32, l33, l34, l35, l36 = sch.split(loop=l4, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39, v40, v41 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[2, 1, 40, 1, 1]) + l42, l43, l44, l45, l46 = sch.split(loop=l5, factors=[v37, v38, v39, v40, v41], preserve_unit_iters=True) + v47, v48, v49 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[8, 2, 2]) + l50, l51, l52 = sch.split(loop=l6, factors=[v47, v48, v49], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l42, l13, l23, l33, l43, l14, l24, l34, l44, k0, l50, l51, l15, l25, l35, l45, l52, l16, l26, l36, l46) + + l53 = sch.fuse(l12, l22, l32, l42, preserve_unit_iters=True) + sch.bind(loop=l53, thread_axis="blockIdx.x") + l54 = sch.fuse(l13, l23, l33, l43, preserve_unit_iters=True) + sch.bind(loop=l54, thread_axis="vthread.x") + l55 = sch.fuse(l14, l24, l34, l44, preserve_unit_iters=True) + sch.bind(loop=l55, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b56 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b56, loop=l55, preserve_unit_loops=True, index=-1) + b57 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l50, preserve_unit_loops=True, index=-1) + l62, l63, l64, l65 = sch.get_loops(block=b57)[-4:] + sch.fuse(l62, l63, l64, l65, preserve_unit_iters=True) + v67 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v67) + b68 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b68, loop=l50, preserve_unit_loops=True, index=-1) + l73, l74, l75, l76 = sch.get_loops(block=b68)[-4:] + sch.fuse(l73, l74, l75, l76, preserve_unit_iters=True) + v78 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b68, ann_key="meta_schedule.cooperative_fetch", ann_val=v78) + v79 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=0) + sch.annotate(block_or_loop=b1, ann_key="meta_schedule.unroll_explicit", ann_val=v79) + sch.enter_postproc() + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + l84 = sch.get_loops(block=b57)[-1] + _, l86 = sch.split(loop=l84, factors=[None, 80], preserve_unit_iters=True) + sch.bind(loop=l86, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b68, ann_key="meta_schedule.cooperative_fetch") + l91 = sch.get_loops(block=b68)[-1] + _, l93 = sch.split(loop=l91, factors=[None, 80], preserve_unit_iters=True) + sch.bind(loop=l93, thread_axis="threadIdx.x") + b94 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b94, ann_key="meta_schedule.unroll_explicit") + + b1 = sch.get_block("A_pad") + sch.compute_inline(b1) + b1 = sch.get_block("B_pad") + sch.compute_inline(b1) + + b132 = sch.get_block(name="matmul", func_name="main") + l136 = sch.get_loops(block=b132)[3] + sch.decompose_reduction(block=b132, loop=l136) + + +@T.prim_func +def softmax_mxn_before(var_rxplaceholder: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + m = T.int64() + rxplaceholder = T.match_buffer(var_rxplaceholder, (T.int64(1), T.int64(32), n, m)) + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), n)) + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), n)) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.max(T_softmax_maxelem[v_i0, v_i1, v_i2], rxplaceholder[v_i0, v_i1, v_i2, v_k]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2, v_i3]) + T_softmax_exp[v_i0, v_i1, v_i2, v_i3] = T.exp(rxplaceholder[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_expsum[v_i0, v_i1, v_i2] = T.float32(0) + T_softmax_expsum[v_i0, v_i1, v_i2] = T_softmax_expsum[v_i0, v_i1, v_i2] + T_softmax_exp[v_i0, v_i1, v_i2, v_k] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_i3], T_softmax_expsum[v_i0, v_i1, v_i2]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"axis": 3}) + T_softmax_norm[v_i0, v_i1, v_i2, v_i3] = T_softmax_exp[v_i0, v_i1, v_i2, v_i3] / T_softmax_expsum[v_i0, v_i1, v_i2] + + +@T.prim_func +def softmax_mxn_after(var_A: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + m = T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), n, m)) + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), n)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), n)) + for i2_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.x"): + with T.block("T_softmax_maxelem_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0) + T.reads(A[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):(m + T.int64(127)) // T.int64(128) * T.int64(128)]) + T.writes(T_softmax_maxelem[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32)]) + T_softmax_maxelem_pad_0_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32)), scope="shared") + for i0, i1, i2_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(32), (m + T.int64(127)) // T.int64(128)): + for k_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_maxelem"): + v_i1_i, v_i2_i = T.axis.remap("SS", [i1, i2_1]) + v_k_i = T.axis.reduce(T.int64(32) * ((m + T.int64(127)) // T.int64(128)), k_0 * T.int64(128) + k_1) + T.reads(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i]) + T.writes(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]) + with T.init(): + T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.max(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i], T.if_then_else(v_i2_o * T.int64(32) + v_i2_i < n and v_k_i < m, A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i], T.float32(-3.4028234663852886e+38))) + for i0_i1_i2_1_fused_0 in range(T.int64(8)): + for i0_i1_i2_1_fused_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_maxelem_cache_write"): + v_i1_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) // T.int64(32)) + v_i2_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32)) + T.where(v_i2_o * T.int64(32) + (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32) < n) + T.reads(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]) + T.writes(T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]) + T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i] = T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] + for i2_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.x"): + with T.block("T_softmax_expsum_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0) + T.reads(A[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):(m + T.int64(127)) // T.int64(128) * T.int64(128)], T_softmax_maxelem[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32)]) + T.writes(T_softmax_expsum[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32)]) + T_softmax_expsum_pad_0_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32)), scope="shared") + for i0, i1, i2_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(32), (m + T.int64(127)) // T.int64(128)): + for k_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_expsum"): + v_i1_i, v_i2_i = T.axis.remap("SS", [i1, i2_1]) + v_k_i = T.axis.reduce(T.int64(32) * ((m + T.int64(127)) // T.int64(128)), k_0 * T.int64(128) + k_1) + T.reads(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i], T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]) + T.writes(T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i]) + with T.init(): + T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.float32(0) + T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] = T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] + T.if_then_else(v_i2_o * T.int64(32) + v_i2_i < n and v_k_i < m, T.exp(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i] - T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]), T.float32(0)) + for i0_i1_i2_1_fused_0 in range(T.int64(8)): + for i0_i1_i2_1_fused_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_expsum_cache_write"): + v_i1_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) // T.int64(32)) + v_i2_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32)) + T.where(v_i2_o * T.int64(32) + (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32) < n) + T.reads(T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i]) + T.writes(T_softmax_expsum[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]) + T_softmax_expsum[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i] = T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] + for i0_i1_i2_fused_i3_fused_0 in T.thread_binding((n * T.int64(32) * m + T.int64(255)) // T.int64(256), thread="blockIdx.x"): + for i0_i1_i2_fused_i3_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("T_softmax_norm"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(32), (i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1) // m // n) + v_i2 = T.axis.spatial(n, (i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1) // m % n) + v_i3 = T.axis.spatial(m, (i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1) % m) + T.where(i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1 < n * T.int64(32) * m) + T.reads(T_softmax_expsum[v_i0, v_i1, v_i2], A[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2, v_i3]) + T_softmax_norm[v_i0, v_i1, v_i2, v_i3] = T.exp(A[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) / T_softmax_expsum[v_i0, v_i1, v_i2] + + +def fused_min_max_triu_te_broadcast_to(sch: tir.Schedule): + b0 = sch.get_block("T_broadcast_to") + sch.reverse_compute_inline(b0) + b1 = sch.get_block("make_diag_mask_te") + i, j = sch.get_loops(b1) + i = sch.fuse(i, j) + i, j = sch.split(i, [None, 128]) + sch.bind(i, "blockIdx.x") + sch.bind(j, "threadIdx.x") + +def softmax_1xn(sch: tir.Schedule): + has_cast = False + if has_cast: + b_cast = sch.get_block("compute") + sch.reverse_compute_inline(b_cast) + + b0 = sch.get_block("T_softmax_exp") + sch.compute_inline(b0) + b1 = sch.get_block("T_softmax_norm") + l2, l3, l4, l5 = sch.get_loops(b1) + _, l7 = sch.split(l5, [None, 128]) + sch.bind(l7, "threadIdx.x") + b8 = sch.get_block("T_softmax_expsum") + sch.compute_at(b8, l4) + sch.set_scope(b8, 0, "shared") + _, _, _, l12 = sch.get_loops(b8) + _, l14 = sch.split(l12, [None, 128]) + sch.bind(l14, "threadIdx.x") + b15 = sch.get_block("T_softmax_maxelem") + sch.compute_at(b15, l4) + sch.set_scope(b15, 0, "shared") + _, _, _, l19 = sch.get_loops(b15) + _, l21 = sch.split(l19, [None, 128]) + sch.bind(l21, "threadIdx.x") + l22 = sch.fuse(l2, l3, l4) + sch.bind(l22, "blockIdx.x") + +def _get_dict(): + tvm.ir.assert_structural_equal(MOD["softmax"], softmax_mxn_before) + func_dict = { + softmax_mxn_before: softmax_mxn_after, + } + for name, func in [ + # fmt: off + ("fused_NT_matmul1_divide_maximum_minimum", fused_NT_matmul1_divide_maximum_minimum), + ("fused_NT_matmul2_add2_gelu", fused_NT_matmul2_add2_gelu), + ("fused_NT_matmul3_add_cast_add1", fused_NT_matmul3_add_cast_add1), + ("fused_NT_matmul4_divide2_maximum1_minimum1", fused_NT_matmul4_divide2_maximum1_minimum1), + ("fused_NT_matmul_add", fused_NT_matmul_add), + ("fused_NT_matmul_add_add1", fused_NT_matmul_add_add1), + ("layer_norm", layer_norm), + ("matmul", matmul), + ("matmul8", matmul8), + ("softmax2", softmax_1xn), + ("fused_min_max_triu_te_broadcast_to", fused_min_max_triu_te_broadcast_to), + # fmt: on + ]: + # print(f"############### {name} ###############") + sch = tir.Schedule(MOD[name]) + func(sch) + # sch.mod["main"].show(black_format=False) + func_dict[MOD[name]] = sch.mod["main"] + return { + (tvm.ir.structural_hash(k), k): v.with_attr("tir.is_scheduled", True) + for k, v in func_dict.items() + } + + +DICT = _get_dict() + + +def lookup(func): + for (hash_value, func_before), f_after in DICT.items(): + if tvm.ir.structural_hash(func) == hash_value and tvm.ir.structural_equal( + func, func_before + ): + return f_after + return None diff --git a/mlc_llm/dispatch/gpt_neox/redpajama_q4f32_mod.py b/mlc_llm/dispatch/gpt_neox/redpajama_q4f32_mod.py new file mode 100644 index 0000000..b6c4cbc --- /dev/null +++ b/mlc_llm/dispatch/gpt_neox/redpajama_q4f32_mod.py @@ -0,0 +1,577 @@ +# pylint: disable=pointless-string-statement,invalid-name,missing-docstring,line-too-long,too-many-locals,too-many-arguments,too-many-statements +from tvm.script import ir as I +from tvm.script import tir as T + +# fmt: off + +@I.ir_module +class Module: + @T.prim_func + def extend_te(var_A: T.handle, var_concat_te: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(1), n, n)) + m = T.int64() + concat_te = T.match_buffer(var_concat_te, (T.int64(1), T.int64(1), n, m)) + # with T.block("root"): + for b, _, i, j in T.grid(T.int64(1), T.int64(1), n, m): + with T.block("concat_te"): + v_b, v__, v_i, v_j = T.axis.remap("SSSS", [b, _, i, j]) + T.reads(A[v_b, v__, v_i, v_j + n - m]) + T.writes(concat_te[v_b, v__, v_i, v_j]) + concat_te[v_b, v__, v_i, v_j] = T.if_then_else(v_j < m - n, T.float32(3.4028234663852886e+38), A[v_b, v__, v_i, v_j + n - m]) + + @T.prim_func + def full(var_T_full: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + T_full = T.match_buffer(var_T_full, (T.int64(1), T.int64(1), T.int64(1), n)) + # with T.block("root"): + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), T.int64(1), n): + with T.block("T_full"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads() + T.writes(T_full[v_ax0, v_ax1, v_ax2, v_ax3]) + T_full[v_ax0, v_ax1, v_ax2, v_ax3] = T.float32(3.4028234663852886e+38) + + @T.prim_func + def fused_NT_matmul1_divide_maximum_minimum(p_lv34: T.handle, p_lv35: T.handle, p_lv5: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv34 = T.match_buffer(p_lv34, (T.int64(1), T.int64(32), n, T.int64(80))) + m = T.int64() + lv35 = T.match_buffer(p_lv35, (T.int64(1), T.int64(32), m, T.int64(80))) + lv5 = T.match_buffer(p_lv5, (T.int64(1), T.int64(1), n, m)) + var_T_minimum_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + var_T_maximum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, m, T.int64(80)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv34[v_i0, v_i1, v_i2, v_k], lv35[v_i0, v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv34[v_i0, v_i1, v_i2, v_k] * lv35[v_i0, v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float32(0.11180339723346898) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float32(-3.4028234663852886e+38)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_minimum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv5[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.min(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv5[v_ax0, T.int64(0), v_ax2, v_ax3]) + + @T.prim_func + def fused_NT_matmul2_add2_gelu(p_lv51: T.handle, lv38: T.Buffer((T.int64(10240), T.int64(2560)), "float32"), linear_bias4: T.Buffer((T.int64(10240),), "float32"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv51 = T.match_buffer(p_lv51, (T.int64(1), n, T.int64(2560))) + var_T_multiply_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(10240))) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + T_multiply = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + compute = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + T_multiply_1 = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + T_add = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(10240), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv51[v_i0, v_i1, v_k], lv38[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv51[v_i0, v_i1, v_k] * lv38[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias4[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias4[v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply[v_ax0, v_ax1, v_ax2]) + T_multiply[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[v_ax0, v_ax1, v_ax2] * T.float32(0.70710678118654757) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(T_multiply[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.erf(T_multiply[v_i0, v_i1, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(compute[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T_multiply_1[v_ax0, v_ax1, v_ax2] = compute[v_ax0, v_ax1, v_ax2] * T.float32(0.5) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T.writes(T_add[v_ax0, v_ax1, v_ax2]) + T_add[v_ax0, v_ax1, v_ax2] = T.float32(0.5) + T_multiply_1[v_ax0, v_ax1, v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply_2"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2], T_add[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[v_ax0, v_ax1, v_ax2] * T_add[v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_NT_matmul3_add_cast_add1(p_lv56: T.handle, lv45: T.Buffer((T.int64(2560), T.int64(10240)), "float32"), linear_bias5: T.Buffer((T.int64(2560),), "float32"), p_lv49: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv56 = T.match_buffer(p_lv56, (T.int64(1), n, T.int64(10240))) + lv49 = T.match_buffer(p_lv49, (T.int64(1), n, T.int64(2560))) + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_T_add_intermediate_1 = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_compute_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(10240)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv56[v_i0, v_i1, v_k], lv45[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv56[v_i0, v_i1, v_k] * lv45[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias5[v_ax2]) + T.writes(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias5[v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate_1[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = var_T_add_intermediate_1[v_i0, v_i1, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_compute_intermediate[v_ax0, v_ax1, v_ax2], lv49[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_compute_intermediate[v_ax0, v_ax1, v_ax2] + lv49[v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_NT_matmul4_divide2_maximum1_minimum1(lv1835: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(80)), "float32"), p_lv1836: T.handle, p_lv1806: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv1836 = T.match_buffer(p_lv1836, (T.int64(1), T.int64(32), n, T.int64(80))) + lv1806 = T.match_buffer(p_lv1806, (T.int64(1), T.int64(1), T.int64(1), n)) + var_T_minimum_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), T.int64(1), n)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + var_T_maximum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n, T.int64(80)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv1835[v_i0, v_i1, v_i2, v_k], lv1836[v_i0, v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv1835[v_i0, v_i1, v_i2, v_k] * lv1836[v_i0, v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float32(0.11180339723346898) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float32(-3.4028234663852886e+38)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_minimum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv1806[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.min(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv1806[v_ax0, T.int64(0), v_ax2, v_ax3]) + + @T.prim_func + def fused_NT_matmul_add(p_lv7: T.handle, lv10: T.Buffer((T.int64(2560), T.int64(2560)), "float32"), linear_bias: T.Buffer((T.int64(2560),), "float32"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv7 = T.match_buffer(p_lv7, (T.int64(1), n, T.int64(2560))) + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv7[v_i0, v_i1, v_k], lv10[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv7[v_i0, v_i1, v_k] * lv10[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias[v_ax2] + + @T.prim_func + def fused_NT_matmul_add_add1(p_lv45: T.handle, lv31: T.Buffer((T.int64(2560), T.int64(2560)), "float32"), linear_bias3: T.Buffer((T.int64(2560),), "float32"), p_lv2: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv45 = T.match_buffer(p_lv45, (T.int64(1), n, T.int64(2560))) + lv2 = T.match_buffer(p_lv2, (T.int64(1), n, T.int64(2560))) + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_T_add_intermediate_1 = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv45[v_i0, v_i1, v_k], lv31[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv45[v_i0, v_i1, v_k] * lv31[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias3[v_ax2]) + T.writes(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias3[v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2], lv2[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] + lv2[v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_min_max_triu_te_broadcast_to(p_output0: T.handle, n: T.int64): + T.func_attr({"tir.noalias": T.bool(True)}) + var_T_broadcast_to_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(1), n, n)) + # with T.block("root"): + var_make_diag_mask_te_intermediate = T.alloc_buffer((n, n)) + for i, j in T.grid(n, n): + with T.block("make_diag_mask_te"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads() + T.writes(var_make_diag_mask_te_intermediate[v_i, v_j]) + var_make_diag_mask_te_intermediate[v_i, v_j] = T.Select(v_i < v_j, T.float32(-3.4028234663852886e+38), T.float32(3.4028234663852886e+38)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), n, n): + with T.block("T_broadcast_to"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_make_diag_mask_te_intermediate[v_ax2, v_ax3]) + T.writes(var_T_broadcast_to_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_broadcast_to_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_make_diag_mask_te_intermediate[v_ax2, v_ax3] + + @T.prim_func + def layer_norm(var_A: T.handle, B: T.Buffer((T.int64(2560),), "float32"), C: T.Buffer((T.int64(2560),), "float32"), var_T_layer_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(2560))) + T_layer_norm = T.match_buffer(var_T_layer_norm, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + A_red_temp_v0 = T.alloc_buffer((T.int64(1), n)) + A_red_temp_v1 = T.alloc_buffer((T.int64(1), n)) + for ax0, ax1, k2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("A_red_temp"): + v_ax0, v_ax1, v_k2 = T.axis.remap("SSR", [ax0, ax1, k2]) + T.reads(A[v_ax0, v_ax1, v_k2]) + T.writes(A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1]) + with T.init(): + A_red_temp_v0[v_ax0, v_ax1] = T.float32(0) + A_red_temp_v1[v_ax0, v_ax1] = T.float32(0) + v_A_red_temp_v0: T.float32 = A_red_temp_v0[v_ax0, v_ax1] + A[v_ax0, v_ax1, v_k2] + v_A_red_temp_v1: T.float32 = A_red_temp_v1[v_ax0, v_ax1] + A[v_ax0, v_ax1, v_k2] * A[v_ax0, v_ax1, v_k2] + A_red_temp_v0[v_ax0, v_ax1] = v_A_red_temp_v0 + A_red_temp_v1[v_ax0, v_ax1] = v_A_red_temp_v1 + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_layer_norm"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[v_ax0, v_ax1, v_ax2], A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1], B[v_ax2], C[v_ax2]) + T.writes(T_layer_norm[v_ax0, v_ax1, v_ax2]) + T_layer_norm[v_ax0, v_ax1, v_ax2] = (A[v_ax0, v_ax1, v_ax2] - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) * T.rsqrt(A_red_temp_v1[v_ax0, v_ax1] * T.float32(0.00039062500000000002) - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002) * (A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) + T.float32(1.0000000000000001e-05)) * B[v_ax2] + C[v_ax2] + + @T.prim_func + def matmul(var_A: T.handle, var_B: T.handle, var_matmul: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n, m = T.int64(), T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), n, m)) + B = T.match_buffer(var_B, (T.int64(1), T.int64(32), m, T.int64(80))) + matmul_1 = T.match_buffer(var_matmul, (T.int64(1), T.int64(32), n, T.int64(80))) + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, T.int64(80), m): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(A[v_i0, v_i1, v_i2, v_k], B[v_i0, v_i1, v_k, v_i3]) + T.writes(matmul_1[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + matmul_1[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + matmul_1[v_i0, v_i1, v_i2, v_i3] = matmul_1[v_i0, v_i1, v_i2, v_i3] + A[v_i0, v_i1, v_i2, v_k] * B[v_i0, v_i1, v_k, v_i3] + + @T.prim_func + def matmul8(var_A: T.handle, var_B: T.handle, matmul: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(80)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), T.int64(1), n)) + B = T.match_buffer(var_B, (T.int64(1), T.int64(32), n, T.int64(80))) + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), T.int64(1), T.int64(80), n): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(A[v_i0, v_i1, v_i2, v_k], B[v_i0, v_i1, v_k, v_i3]) + T.writes(matmul[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + matmul[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + matmul[v_i0, v_i1, v_i2, v_i3] = matmul[v_i0, v_i1, v_i2, v_i3] + A[v_i0, v_i1, v_i2, v_k] * B[v_i0, v_i1, v_k, v_i3] + + @T.prim_func + def reshape(var_A: T.handle, var_T_reshape: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n), "int32") + T_reshape = T.match_buffer(var_T_reshape, (n,), "int32") + # with T.block("root"): + for ax0 in range(n): + with T.block("T_reshape"): + v_ax0 = T.axis.spatial(n, ax0) + T.reads(A[T.int64(0), v_ax0 % n]) + T.writes(T_reshape[v_ax0]) + T_reshape[v_ax0] = A[T.int64(0), v_ax0 % n] + + @T.prim_func + def reshape1(var_A: T.handle, var_T_reshape: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (n, T.int64(2560))) + T_reshape = T.match_buffer(var_T_reshape, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_reshape"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[(v_ax2 // T.int64(2560) + v_ax0 * n + v_ax1) % n, v_ax2 % T.int64(2560)]) + T.writes(T_reshape[v_ax0, v_ax1, v_ax2]) + T_reshape[v_ax0, v_ax1, v_ax2] = A[(v_ax2 // T.int64(2560) + v_ax0 * n + v_ax1) % n, v_ax2 % T.int64(2560)] + + @T.prim_func + def reshape2(var_A: T.handle, var_T_reshape: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(2560))) + T_reshape = T.match_buffer(var_T_reshape, (T.int64(1), n, T.int64(32), T.int64(80))) + # with T.block("root"): + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), n, T.int64(32), T.int64(80)): + with T.block("T_reshape"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(A[T.int64(0), ((v_ax2 * T.int64(80) + v_ax3) // T.int64(2560) + v_ax0 * n + v_ax1) % n, (v_ax2 * T.int64(80) + v_ax3) % T.int64(2560)]) + T.writes(T_reshape[v_ax0, v_ax1, v_ax2, v_ax3]) + T_reshape[v_ax0, v_ax1, v_ax2, v_ax3] = A[T.int64(0), ((v_ax2 * T.int64(80) + v_ax3) // T.int64(2560) + v_ax0 * n + v_ax1) % n, (v_ax2 * T.int64(80) + v_ax3) % T.int64(2560)] + + @T.prim_func + def reshape3(var_A: T.handle, var_T_reshape: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + m = T.int64() + A = T.match_buffer(var_A, (m, T.int64(32), T.int64(80))) + T_reshape = T.match_buffer(var_T_reshape, (T.int64(1), m, T.int64(32), T.int64(80))) + # with T.block("root"): + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), m, T.int64(32), T.int64(80)): + with T.block("T_reshape"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(A[((v_ax3 // T.int64(80) + v_ax2) // T.int64(32) + v_ax0 * m + v_ax1) % m, (v_ax3 // T.int64(80) + v_ax2) % T.int64(32), v_ax3 % T.int64(80)]) + T.writes(T_reshape[v_ax0, v_ax1, v_ax2, v_ax3]) + T_reshape[v_ax0, v_ax1, v_ax2, v_ax3] = A[((v_ax3 // T.int64(80) + v_ax2) // T.int64(32) + v_ax0 * m + v_ax1) % m, (v_ax3 // T.int64(80) + v_ax2) % T.int64(32), v_ax3 % T.int64(80)] + + @T.prim_func + def reshape4(var_A: T.handle, var_T_reshape: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(32), T.int64(80))) + T_reshape = T.match_buffer(var_T_reshape, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_reshape"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[T.int64(0), (v_ax2 // T.int64(2560) + v_ax0 * n + v_ax1) % n, v_ax2 % T.int64(2560) // T.int64(80), v_ax2 % T.int64(80)]) + T.writes(T_reshape[v_ax0, v_ax1, v_ax2]) + T_reshape[v_ax0, v_ax1, v_ax2] = A[T.int64(0), (v_ax2 // T.int64(2560) + v_ax0 * n + v_ax1) % n, v_ax2 % T.int64(2560) // T.int64(80), v_ax2 % T.int64(80)] + + @T.prim_func + def rotary_embedding(var_A: T.handle, B: T.Buffer((T.int64(2048), T.int64(80)), "float32"), C: T.Buffer((T.int64(2048), T.int64(80)), "float32"), var_rotary: T.handle, m: T.int64): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(32), T.int64(80))) + rotary = T.match_buffer(var_rotary, (T.int64(1), n, T.int64(32), T.int64(80))) + # with T.block("root"): + for i_batch_size, i_seq_len, i_num_heads, i_head_dim in T.grid(T.int64(1), n, T.int64(32), T.int64(80)): + with T.block("rotary"): + v_i_batch_size, v_i_seq_len, v_i_num_heads, v_i_head_dim = T.axis.remap("SSSS", [i_batch_size, i_seq_len, i_num_heads, i_head_dim]) + T.reads(B[m + v_i_seq_len - n, v_i_head_dim], A[v_i_batch_size, v_i_seq_len, v_i_num_heads, v_i_head_dim - T.int64(40):v_i_head_dim - T.int64(40) + T.int64(81)], C[m + v_i_seq_len - n, v_i_head_dim]) + T.writes(rotary[v_i_batch_size, v_i_seq_len, v_i_num_heads, v_i_head_dim]) + rotary[v_i_batch_size, v_i_seq_len, v_i_num_heads, v_i_head_dim] = T.Select(v_i_head_dim < T.int64(80), B[m + v_i_seq_len - n, v_i_head_dim] * A[v_i_batch_size, v_i_seq_len, v_i_num_heads, v_i_head_dim] + C[m + v_i_seq_len - n, v_i_head_dim] * T.Select(v_i_head_dim < T.int64(40), A[v_i_batch_size, v_i_seq_len, v_i_num_heads, v_i_head_dim + T.int64(40)] * T.float32(-1), A[v_i_batch_size, v_i_seq_len, v_i_num_heads, v_i_head_dim - T.int64(40)]), A[v_i_batch_size, v_i_seq_len, v_i_num_heads, v_i_head_dim]) + + @T.prim_func + def slice(var_A: T.handle, slice_1: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + for i, _, k in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("slice"): + v_i, v__, v_k = T.axis.remap("SSS", [i, _, k]) + T.reads(A[v_i, n - T.int64(1), v_k]) + T.writes(slice_1[v_i, v__, v_k]) + slice_1[v_i, v__, v_k] = A[v_i, n - T.int64(1), v_k] + + @T.prim_func + def softmax(var_A: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n, m = T.int64(), T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), n, m)) + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), n)) + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), n)) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(A[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.max(T_softmax_maxelem[v_i0, v_i1, v_i2], A[v_i0, v_i1, v_i2, v_k]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(A[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2, v_i3]) + T_softmax_exp[v_i0, v_i1, v_i2, v_i3] = T.exp(A[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_expsum[v_i0, v_i1, v_i2] = T.float32(0) + T_softmax_expsum[v_i0, v_i1, v_i2] = T_softmax_expsum[v_i0, v_i1, v_i2] + T_softmax_exp[v_i0, v_i1, v_i2, v_k] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_i3], T_softmax_expsum[v_i0, v_i1, v_i2]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"axis": 3}) + T_softmax_norm[v_i0, v_i1, v_i2, v_i3] = T_softmax_exp[v_i0, v_i1, v_i2, v_i3] / T_softmax_expsum[v_i0, v_i1, v_i2] + + @T.prim_func + def softmax2(var_A: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), T.int64(1), n)) + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), T.int64(1), n)) + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1))) + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1))) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(A[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.max(T_softmax_maxelem[v_i0, v_i1, v_i2], A[v_i0, v_i1, v_i2, v_k]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(A[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2, v_i3]) + T_softmax_exp[v_i0, v_i1, v_i2, v_i3] = T.exp(A[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_expsum[v_i0, v_i1, v_i2] = T.float32(0) + T_softmax_expsum[v_i0, v_i1, v_i2] = T_softmax_expsum[v_i0, v_i1, v_i2] + T_softmax_exp[v_i0, v_i1, v_i2, v_k] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_i3], T_softmax_expsum[v_i0, v_i1, v_i2]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"axis": 3}) + T_softmax_norm[v_i0, v_i1, v_i2, v_i3] = T_softmax_exp[v_i0, v_i1, v_i2, v_i3] / T_softmax_expsum[v_i0, v_i1, v_i2] + + @T.prim_func + def squeeze(var_A: T.handle, var_T_squeeze: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(32), T.int64(80))) + T_squeeze = T.match_buffer(var_T_squeeze, (n, T.int64(32), T.int64(80))) + # with T.block("root"): + for ax0, ax1, ax2 in T.grid(n, T.int64(32), T.int64(80)): + with T.block("T_squeeze"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[T.int64(0), v_ax0, v_ax1, v_ax2]) + T.writes(T_squeeze[v_ax0, v_ax1, v_ax2]) + T_squeeze[v_ax0, v_ax1, v_ax2] = A[T.int64(0), v_ax0, v_ax1, v_ax2] + + @T.prim_func + def take_decode(A: T.Buffer((T.int64(50432), T.int64(320)), "uint32"), B: T.Buffer((T.int64(50432), T.int64(80)), "uint32"), var_C: T.handle, var_take_decode: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + C = T.match_buffer(var_C, (n,), "int32") + take_decode_1 = T.match_buffer(var_take_decode, (n, T.int64(2560))) + # with T.block("root"): + for i, j in T.grid(n, T.int64(2560)): + with T.block("take_decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(A[C[v_i], v_j // T.int64(8)], C[v_i], B[C[v_i], v_j // T.int64(32)]) + T.writes(take_decode_1[v_i, v_j]) + take_decode_1[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(A[C[v_i], v_j // T.int64(8)], T.Cast("uint32", v_j % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(B[C[v_i], v_j // T.int64(32)], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(B[C[v_i], v_j // T.int64(32)], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + + @T.prim_func + def transpose(var_A: T.handle, var_T_transpose: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(32), T.int64(80))) + T_transpose = T.match_buffer(var_T_transpose, (T.int64(1), T.int64(32), n, T.int64(80))) + # with T.block("root"): + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, T.int64(80)): + with T.block("T_transpose"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(A[v_ax0, v_ax2, v_ax1, v_ax3]) + T.writes(T_transpose[v_ax0, v_ax1, v_ax2, v_ax3]) + T_transpose[v_ax0, v_ax1, v_ax2, v_ax3] = A[v_ax0, v_ax2, v_ax1, v_ax3] + + @T.prim_func + def transpose1(var_A: T.handle, var_T_transpose: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), n, T.int64(80))) + T_transpose = T.match_buffer(var_T_transpose, (T.int64(1), n, T.int64(32), T.int64(80))) + # with T.block("root"): + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), n, T.int64(32), T.int64(80)): + with T.block("T_transpose"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(A[v_ax0, v_ax2, v_ax1, v_ax3]) + T.writes(T_transpose[v_ax0, v_ax1, v_ax2, v_ax3]) + T_transpose[v_ax0, v_ax1, v_ax2, v_ax3] = A[v_ax0, v_ax2, v_ax1, v_ax3] +# fmt: on diff --git a/mlc_llm/dispatch/gpt_neox/redpajama_q4f32_tune.py b/mlc_llm/dispatch/gpt_neox/redpajama_q4f32_tune.py new file mode 100644 index 0000000..1b1169e --- /dev/null +++ b/mlc_llm/dispatch/gpt_neox/redpajama_q4f32_tune.py @@ -0,0 +1,743 @@ +# pylint: disable=pointless-string-statement,invalid-name,missing-docstring,line-too-long,too-many-locals,too-many-arguments,too-many-statements +from tvm.script import ir as I +from tvm.script import tir as T + +# fmt: off + +@I.ir_module +class Module: + @T.prim_func + def cast1(A: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32"), compute: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(A[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = A[v_i0, v_i1, v_i2] + + @T.prim_func + def decode(A: T.Buffer((T.int64(320), T.int64(2560)), "uint32"), B: T.Buffer((T.int64(80), T.int64(2560)), "uint32"), T_transpose: T.Buffer((T.int64(2560), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode_1 = T.alloc_buffer((T.int64(2560), T.int64(2560))) + for i, j in T.grid(T.int64(2560), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(A[v_i // T.int64(8), v_j], B[v_i // T.int64(32), v_j]) + T.writes(decode_1[v_i, v_j]) + decode_1[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(A[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(B[v_i // T.int64(32), v_j], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(B[v_i // T.int64(32), v_j], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for ax0, ax1 in T.grid(T.int64(2560), T.int64(2560)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode_1[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode_1[v_ax1, v_ax0] + + @T.prim_func + def decode1(A: T.Buffer((T.int64(320), T.int64(10240)), "uint32"), B: T.Buffer((T.int64(80), T.int64(10240)), "uint32"), T_transpose: T.Buffer((T.int64(10240), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(2560), T.int64(10240))) + for i, j in T.grid(T.int64(2560), T.int64(10240)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(A[v_i // T.int64(8), v_j], B[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(A[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(B[v_i // T.int64(32), v_j], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(B[v_i // T.int64(32), v_j], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for ax0, ax1 in T.grid(T.int64(10240), T.int64(2560)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + + @T.prim_func + def decode2(A: T.Buffer((T.int64(1280), T.int64(2560)), "uint32"), B: T.Buffer((T.int64(320), T.int64(2560)), "uint32"), T_transpose: T.Buffer((T.int64(2560), T.int64(10240)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(10240), T.int64(2560))) + for i, j in T.grid(T.int64(10240), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(A[v_i // T.int64(8), v_j], B[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(A[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(B[v_i // T.int64(32), v_j], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(B[v_i // T.int64(32), v_j], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for ax0, ax1 in T.grid(T.int64(2560), T.int64(10240)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + + @T.prim_func + def divide1(A: T.Buffer((T.int64(1), T.int64(1), T.int64(50432)), "float32"), B: T.Buffer((), "float32"), T_divide: T.Buffer((T.int64(1), T.int64(1), T.int64(50432)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(50432)): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[v_ax0, v_ax1, v_ax2], B[()]) + T.writes(T_divide[v_ax0, v_ax1, v_ax2]) + T_divide[v_ax0, v_ax1, v_ax2] = A[v_ax0, v_ax1, v_ax2] / B[()] + + @T.prim_func + def fused_decode3_matmul1(lv1352: T.Buffer((T.int64(320), T.int64(50432)), "uint32"), lv1353: T.Buffer((T.int64(80), T.int64(50432)), "uint32"), lv1800: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32"), var_matmul_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(50432)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(2560), T.int64(50432))) + for i, j in T.grid(T.int64(2560), T.int64(50432)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1352[v_i // T.int64(8), v_j], lv1353[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(lv1352[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(lv1353[v_i // T.int64(32), v_j], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(lv1353[v_i // T.int64(32), v_j], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(50432), T.int64(2560)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1800[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv1800[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + + @T.prim_func + def fused_decode4_fused_matmul7_add3(lv1363: T.Buffer((T.int64(320), T.int64(2560)), "uint32"), lv1364: T.Buffer((T.int64(80), T.int64(2560)), "uint32"), lv1808: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32"), linear_bias192: T.Buffer((T.int64(2560),), "float32"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(2560), T.int64(2560))) + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560))) + for i, j in T.grid(T.int64(2560), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1363[v_i // T.int64(8), v_j], lv1364[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(lv1363[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(lv1364[v_i // T.int64(32), v_j], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(lv1364[v_i // T.int64(32), v_j], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(2560), T.int64(2560)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1808[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv1808[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias192[v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias192[v_ax2] + + @T.prim_func + def fused_decode4_fused_matmul7_add3_add4(lv1381: T.Buffer((T.int64(320), T.int64(2560)), "uint32"), lv1382: T.Buffer((T.int64(80), T.int64(2560)), "uint32"), lv5: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32"), linear_bias195: T.Buffer((T.int64(2560),), "float32"), lv1805: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(2560), T.int64(2560))) + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560))) + for i, j in T.grid(T.int64(2560), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1381[v_i // T.int64(8), v_j], lv1382[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(lv1381[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(lv1382[v_i // T.int64(32), v_j], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(lv1382[v_i // T.int64(32), v_j], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(2560), T.int64(2560)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv5[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv5[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias195[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias195[v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2], lv1805[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[v_ax0, v_ax1, v_ax2] + lv1805[v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_decode5_fused_matmul9_add5_gelu1(lv1387: T.Buffer((T.int64(320), T.int64(10240)), "uint32"), lv1388: T.Buffer((T.int64(80), T.int64(10240)), "uint32"), lv1852: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32"), linear_bias196: T.Buffer((T.int64(10240),), "float32"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(10240)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(2560), T.int64(10240))) + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + T_multiply = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + compute = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + T_multiply_1 = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + T_add = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(10240))) + for i, j in T.grid(T.int64(2560), T.int64(10240)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1387[v_i // T.int64(8), v_j], lv1388[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(lv1387[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(lv1388[v_i // T.int64(32), v_j], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(lv1388[v_i // T.int64(32), v_j], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(10240), T.int64(2560)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1852[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv1852[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias196[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias196[v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply[v_ax0, v_ax1, v_ax2]) + T_multiply[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[v_ax0, v_ax1, v_ax2] * T.float32(0.70710678118654757) + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(T_multiply[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.erf(T_multiply[v_i0, v_i1, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("T_multiply_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(compute[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T_multiply_1[v_ax0, v_ax1, v_ax2] = compute[v_ax0, v_ax1, v_ax2] * T.float32(0.5) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T.writes(T_add[v_ax0, v_ax1, v_ax2]) + T_add[v_ax0, v_ax1, v_ax2] = T.float32(0.5) + T_multiply_1[v_ax0, v_ax1, v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(10240)): + with T.block("T_multiply_2"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2], T_add[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[v_ax0, v_ax1, v_ax2] * T_add[v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_decode6_fused_matmul10_add3_cast1_add4(lv1393: T.Buffer((T.int64(1280), T.int64(2560)), "uint32"), lv1394: T.Buffer((T.int64(320), T.int64(2560)), "uint32"), lv1857: T.Buffer((T.int64(1), T.int64(1), T.int64(10240)), "float32"), linear_bias197: T.Buffer((T.int64(2560),), "float32"), lv6: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(10240), T.int64(2560))) + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560))) + var_compute_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560))) + for i, j in T.grid(T.int64(10240), T.int64(2560)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1393[v_i // T.int64(8), v_j], lv1394[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(lv1393[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(lv1394[v_i // T.int64(32), v_j], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(lv1394[v_i // T.int64(32), v_j], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(2560), T.int64(10240)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1857[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv1857[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias197[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias197[v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = var_T_add_intermediate[v_i0, v_i1, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_compute_intermediate[v_ax0, v_ax1, v_ax2], lv6[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = var_compute_intermediate[v_ax0, v_ax1, v_ax2] + lv6[v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_reshape7_squeeze1(lv1821: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32"), var_T_squeeze_intermediate: T.Buffer((T.int64(1), T.int64(32), T.int64(80)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_T_reshape_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(32), T.int64(80))) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), T.int64(32), T.int64(80)): + with T.block("T_reshape"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(lv1821[T.int64(0), T.int64(0), (v_ax2 * T.int64(80) + v_ax3) % T.int64(2560)]) + T.writes(var_T_reshape_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_reshape_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = lv1821[T.int64(0), T.int64(0), (v_ax2 * T.int64(80) + v_ax3) % T.int64(2560)] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(32), T.int64(80)): + with T.block("T_squeeze"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_reshape_intermediate[T.int64(0), v_ax0, v_ax1, v_ax2]) + T.writes(var_T_squeeze_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_squeeze_intermediate[v_ax0, v_ax1, v_ax2] = var_T_reshape_intermediate[T.int64(0), v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_slice1_cast1(lv3599: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32"), var_compute_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_slice_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(2560))) + for i, _, k in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("slice"): + v_i, v__, v_k = T.axis.remap("SSS", [i, _, k]) + T.reads(lv3599[v_i, T.int64(0), v_k]) + T.writes(var_slice_intermediate[v_i, v__, v_k]) + var_slice_intermediate[v_i, v__, v_k] = lv3599[v_i, T.int64(0), v_k] + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_slice_intermediate[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = var_slice_intermediate[v_i0, v_i1, v_i2] + + @T.prim_func + def fused_transpose7_reshape8(lv1844: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(80)), "float32"), var_T_reshape_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_T_transpose_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(32), T.int64(80))) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), T.int64(32), T.int64(80)): + with T.block("T_transpose"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(lv1844[v_ax0, v_ax2, v_ax1, v_ax3]) + T.writes(var_T_transpose_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_transpose_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = lv1844[v_ax0, v_ax2, v_ax1, v_ax3] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_reshape"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_transpose_intermediate[T.int64(0), T.int64(0), v_ax2 % T.int64(2560) // T.int64(80), v_ax2 % T.int64(80)]) + T.writes(var_T_reshape_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_reshape_intermediate[v_ax0, v_ax1, v_ax2] = var_T_transpose_intermediate[T.int64(0), T.int64(0), v_ax2 % T.int64(2560) // T.int64(80), v_ax2 % T.int64(80)] + + @T.prim_func + def layer_norm1(A: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32"), B: T.Buffer((T.int64(2560),), "float32"), C: T.Buffer((T.int64(2560),), "float32"), T_layer_norm: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + A_red_temp_v0 = T.alloc_buffer((T.int64(1), T.int64(1))) + A_red_temp_v1 = T.alloc_buffer((T.int64(1), T.int64(1))) + for ax0, ax1, k2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("A_red_temp"): + v_ax0, v_ax1, v_k2 = T.axis.remap("SSR", [ax0, ax1, k2]) + T.reads(A[v_ax0, v_ax1, v_k2]) + T.writes(A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1]) + with T.init(): + A_red_temp_v0[v_ax0, v_ax1] = T.float32(0) + A_red_temp_v1[v_ax0, v_ax1] = T.float32(0) + v_A_red_temp_v0: T.float32 = A_red_temp_v0[v_ax0, v_ax1] + A[v_ax0, v_ax1, v_k2] + v_A_red_temp_v1: T.float32 = A_red_temp_v1[v_ax0, v_ax1] + A[v_ax0, v_ax1, v_k2] * A[v_ax0, v_ax1, v_k2] + A_red_temp_v0[v_ax0, v_ax1] = v_A_red_temp_v0 + A_red_temp_v1[v_ax0, v_ax1] = v_A_red_temp_v1 + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_layer_norm"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[v_ax0, v_ax1, v_ax2], A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1], B[v_ax2], C[v_ax2]) + T.writes(T_layer_norm[v_ax0, v_ax1, v_ax2]) + T_layer_norm[v_ax0, v_ax1, v_ax2] = (A[v_ax0, v_ax1, v_ax2] - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) * T.rsqrt(A_red_temp_v1[v_ax0, v_ax1] * T.float32(0.00039062500000000002) - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002) * (A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) + T.float32(1.0000000000000001e-05)) * B[v_ax2] + C[v_ax2] + + @T.prim_func + def reshape5(A: T.Buffer((T.int64(1), T.int64(1)), "int32"), T_reshape: T.Buffer((T.int64(1),), "int32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for ax0 in range(T.int64(1)): + with T.block("T_reshape"): + v_ax0 = T.axis.spatial(T.int64(1), ax0) + T.reads(A[T.int64(0), T.int64(0)]) + T.writes(T_reshape[v_ax0]) + T_reshape[v_ax0] = A[T.int64(0), T.int64(0)] + + @T.prim_func + def reshape6(A: T.Buffer((T.int64(1), T.int64(2560)), "float32"), T_reshape: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(2560)): + with T.block("T_reshape"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[T.int64(0), v_ax2 % T.int64(2560)]) + T.writes(T_reshape[v_ax0, v_ax1, v_ax2]) + T_reshape[v_ax0, v_ax1, v_ax2] = A[T.int64(0), v_ax2 % T.int64(2560)] + + @T.prim_func + def reshape7(A: T.Buffer((T.int64(1), T.int64(1), T.int64(2560)), "float32"), T_reshape: T.Buffer((T.int64(1), T.int64(1), T.int64(32), T.int64(80)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), T.int64(32), T.int64(80)): + with T.block("T_reshape"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(A[T.int64(0), T.int64(0), (v_ax2 * T.int64(80) + v_ax3) % T.int64(2560)]) + T.writes(T_reshape[v_ax0, v_ax1, v_ax2, v_ax3]) + T_reshape[v_ax0, v_ax1, v_ax2, v_ax3] = A[T.int64(0), T.int64(0), (v_ax2 * T.int64(80) + v_ax3) % T.int64(2560)] + + @T.prim_func + def softmax1(A: T.Buffer((T.int64(1), T.int64(1), T.int64(50432)), "float32"), T_softmax_norm: T.Buffer((T.int64(1), T.int64(1), T.int64(50432)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(1))) + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(50432))) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(1))) + for i0, i1, k in T.grid(T.int64(1), T.int64(1), T.int64(50432)): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_k = T.axis.remap("SSR", [i0, i1, k]) + T.reads(A[v_i0, v_i1, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem[v_i0, v_i1] = T.max(T_softmax_maxelem[v_i0, v_i1], A[v_i0, v_i1, v_k]) + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(50432)): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(A[v_i0, v_i1, v_i2], T_softmax_maxelem[v_i0, v_i1]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2]) + T_softmax_exp[v_i0, v_i1, v_i2] = T.exp(A[v_i0, v_i1, v_i2] - T_softmax_maxelem[v_i0, v_i1]) + for i0, i1, k in T.grid(T.int64(1), T.int64(1), T.int64(50432)): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_k = T.axis.remap("SSR", [i0, i1, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1]) + with T.init(): + T_softmax_expsum[v_i0, v_i1] = T.float32(0) + T_softmax_expsum[v_i0, v_i1] = T_softmax_expsum[v_i0, v_i1] + T_softmax_exp[v_i0, v_i1, v_k] + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(50432)): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2], T_softmax_expsum[v_i0, v_i1]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2]) + T.block_attr({"axis": 2}) + T_softmax_norm[v_i0, v_i1, v_i2] = T_softmax_exp[v_i0, v_i1, v_i2] / T_softmax_expsum[v_i0, v_i1] + + @T.prim_func + def squeeze1(A: T.Buffer((T.int64(1), T.int64(1), T.int64(32), T.int64(80)), "float32"), T_squeeze: T.Buffer((T.int64(1), T.int64(32), T.int64(80)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(32), T.int64(80)): + with T.block("T_squeeze"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[T.int64(0), v_ax0, v_ax1, v_ax2]) + T.writes(T_squeeze[v_ax0, v_ax1, v_ax2]) + T_squeeze[v_ax0, v_ax1, v_ax2] = A[T.int64(0), v_ax0, v_ax1, v_ax2] + + @T.prim_func + def take_decode1(A: T.Buffer((T.int64(50432), T.int64(320)), "uint32"), B: T.Buffer((T.int64(50432), T.int64(80)), "uint32"), C: T.Buffer((T.int64(1),), "int32"), take_decode: T.Buffer((T.int64(1), T.int64(2560)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for i, j in T.grid(T.int64(1), T.int64(2560)): + with T.block("take_decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(A[C[v_i], v_j // T.int64(8)], C[v_i], B[C[v_i], v_j // T.int64(32)]) + T.writes(take_decode[v_i, v_j]) + take_decode[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(A[C[v_i], v_j // T.int64(8)], T.Cast("uint32", v_j % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(B[C[v_i], v_j // T.int64(32)], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(B[C[v_i], v_j // T.int64(32)], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + + @T.prim_func + def transpose6(A: T.Buffer((T.int64(1), T.int64(1), T.int64(32), T.int64(80)), "float32"), T_transpose: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(80)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), T.int64(80)): + with T.block("T_transpose"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(A[v_ax0, v_ax2, v_ax1, v_ax3]) + T.writes(T_transpose[v_ax0, v_ax1, v_ax2, v_ax3]) + T_transpose[v_ax0, v_ax1, v_ax2, v_ax3] = A[v_ax0, v_ax2, v_ax1, v_ax3] + + ########## Dynamic shape ########## + + @T.prim_func + def fused_NT_matmul1_divide_maximum_minimum(p_lv34: T.handle, p_lv35: T.handle, p_lv5: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + m = T.meta_var(T.int64(128)) + lv34 = T.match_buffer(p_lv34, (T.int64(1), T.int64(32), n, T.int64(80))) + lv35 = T.match_buffer(p_lv35, (T.int64(1), T.int64(32), m, T.int64(80))) + lv5 = T.match_buffer(p_lv5, (T.int64(1), T.int64(1), n, m)) + var_T_minimum_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + var_T_maximum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, m, T.int64(80)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv34[v_i0, v_i1, v_i2, v_k], lv35[v_i0, v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv34[v_i0, v_i1, v_i2, v_k] * lv35[v_i0, v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float32(0.11180339723346898) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float32(-3.4028234663852886e+38)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_minimum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv5[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.min(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv5[v_ax0, T.int64(0), v_ax2, v_ax3]) + + @T.prim_func + def fused_NT_matmul2_add2_gelu(p_lv51: T.handle, lv38: T.Buffer((T.int64(10240), T.int64(2560)), "float32"), linear_bias4: T.Buffer((T.int64(10240),), "float32"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + lv51 = T.match_buffer(p_lv51, (T.int64(1), n, T.int64(2560))) + var_T_multiply_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(10240))) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + T_multiply = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + compute = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + T_multiply_1 = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + T_add = T.alloc_buffer((T.int64(1), n, T.int64(10240))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(10240), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv51[v_i0, v_i1, v_k], lv38[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv51[v_i0, v_i1, v_k] * lv38[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias4[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias4[v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply[v_ax0, v_ax1, v_ax2]) + T_multiply[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[v_ax0, v_ax1, v_ax2] * T.float32(0.70710678118654757) + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(T_multiply[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.erf(T_multiply[v_i0, v_i1, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(compute[v_ax0, v_ax1, v_ax2]) + T.writes(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T_multiply_1[v_ax0, v_ax1, v_ax2] = compute[v_ax0, v_ax1, v_ax2] * T.float32(0.5) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(T_multiply_1[v_ax0, v_ax1, v_ax2]) + T.writes(T_add[v_ax0, v_ax1, v_ax2]) + T_add[v_ax0, v_ax1, v_ax2] = T.float32(0.5) + T_multiply_1[v_ax0, v_ax1, v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(10240)): + with T.block("T_multiply_2"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2], T_add[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate[v_ax0, v_ax1, v_ax2] * T_add[v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_NT_matmul3_add_cast_add1(p_lv56: T.handle, lv45: T.Buffer((T.int64(2560), T.int64(10240)), "float32"), linear_bias5: T.Buffer((T.int64(2560),), "float32"), p_lv49: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + lv56 = T.match_buffer(p_lv56, (T.int64(1), n, T.int64(10240))) + lv49 = T.match_buffer(p_lv49, (T.int64(1), n, T.int64(2560))) + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_T_add_intermediate_1 = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_compute_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(10240)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv56[v_i0, v_i1, v_k], lv45[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv56[v_i0, v_i1, v_k] * lv45[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias5[v_ax2]) + T.writes(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias5[v_ax2] + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_T_add_intermediate_1[v_i0, v_i1, v_i2]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2]) + var_compute_intermediate[v_i0, v_i1, v_i2] = var_T_add_intermediate_1[v_i0, v_i1, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_compute_intermediate[v_ax0, v_ax1, v_ax2], lv49[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_compute_intermediate[v_ax0, v_ax1, v_ax2] + lv49[v_ax0, v_ax1, v_ax2] + + @T.prim_func + def fused_NT_matmul4_divide2_maximum1_minimum1(lv1835: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(80)), "float32"), p_lv1836: T.handle, p_lv1806: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + lv1836 = T.match_buffer(p_lv1836, (T.int64(1), T.int64(32), n, T.int64(80))) + lv1806 = T.match_buffer(p_lv1806, (T.int64(1), T.int64(1), T.int64(1), n)) + var_T_minimum_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), T.int64(1), n)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + var_T_maximum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n, T.int64(80)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv1835[v_i0, v_i1, v_i2, v_k], lv1836[v_i0, v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv1835[v_i0, v_i1, v_i2, v_k] * lv1836[v_i0, v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float32(0.11180339723346898) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float32(-3.4028234663852886e+38)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_minimum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv1806[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.min(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv1806[v_ax0, T.int64(0), v_ax2, v_ax3]) + + @T.prim_func + def fused_NT_matmul_add(p_lv7: T.handle, lv10: T.Buffer((T.int64(2560), T.int64(2560)), "float32"), linear_bias: T.Buffer((T.int64(2560),), "float32"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + lv7 = T.match_buffer(p_lv7, (T.int64(1), n, T.int64(2560))) + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv7[v_i0, v_i1, v_k], lv10[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv7[v_i0, v_i1, v_k] * lv10[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias[v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias[v_ax2] + + @T.prim_func + def fused_NT_matmul_add_add1(p_lv45: T.handle, lv31: T.Buffer((T.int64(2560), T.int64(2560)), "float32"), linear_bias3: T.Buffer((T.int64(2560),), "float32"), p_lv2: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + lv45 = T.match_buffer(p_lv45, (T.int64(1), n, T.int64(2560))) + lv2 = T.match_buffer(p_lv2, (T.int64(1), n, T.int64(2560))) + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + var_T_add_intermediate_1 = T.alloc_buffer((T.int64(1), n, T.int64(2560))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(2560), T.int64(2560)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv45[v_i0, v_i1, v_k], lv31[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv45[v_i0, v_i1, v_k] * lv31[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], linear_bias3[v_ax2]) + T.writes(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + linear_bias3[v_ax2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_add_1"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2], lv2[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = var_T_add_intermediate_1[v_ax0, v_ax1, v_ax2] + lv2[v_ax0, v_ax1, v_ax2] + + @T.prim_func + def layer_norm(var_A: T.handle, B: T.Buffer((T.int64(2560),), "float32"), C: T.Buffer((T.int64(2560),), "float32"), var_T_layer_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(2560))) + T_layer_norm = T.match_buffer(var_T_layer_norm, (T.int64(1), n, T.int64(2560))) + # with T.block("root"): + A_red_temp_v0 = T.alloc_buffer((T.int64(1), n)) + A_red_temp_v1 = T.alloc_buffer((T.int64(1), n)) + for ax0, ax1, k2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("A_red_temp"): + v_ax0, v_ax1, v_k2 = T.axis.remap("SSR", [ax0, ax1, k2]) + T.reads(A[v_ax0, v_ax1, v_k2]) + T.writes(A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1]) + with T.init(): + A_red_temp_v0[v_ax0, v_ax1] = T.float32(0) + A_red_temp_v1[v_ax0, v_ax1] = T.float32(0) + v_A_red_temp_v0: T.float32 = A_red_temp_v0[v_ax0, v_ax1] + A[v_ax0, v_ax1, v_k2] + v_A_red_temp_v1: T.float32 = A_red_temp_v1[v_ax0, v_ax1] + A[v_ax0, v_ax1, v_k2] * A[v_ax0, v_ax1, v_k2] + A_red_temp_v0[v_ax0, v_ax1] = v_A_red_temp_v0 + A_red_temp_v1[v_ax0, v_ax1] = v_A_red_temp_v1 + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(2560)): + with T.block("T_layer_norm"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(A[v_ax0, v_ax1, v_ax2], A_red_temp_v0[v_ax0, v_ax1], A_red_temp_v1[v_ax0, v_ax1], B[v_ax2], C[v_ax2]) + T.writes(T_layer_norm[v_ax0, v_ax1, v_ax2]) + T_layer_norm[v_ax0, v_ax1, v_ax2] = (A[v_ax0, v_ax1, v_ax2] - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) * T.rsqrt(A_red_temp_v1[v_ax0, v_ax1] * T.float32(0.00039062500000000002) - A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002) * (A_red_temp_v0[v_ax0, v_ax1] * T.float32(0.00039062500000000002)) + T.float32(1.0000000000000001e-05)) * B[v_ax2] + C[v_ax2] + + @T.prim_func + def matmul(var_A: T.handle, var_B: T.handle, var_matmul: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(128)) + m = T.meta_var(T.int64(32)) + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), n, m)) + B = T.match_buffer(var_B, (T.int64(1), T.int64(32), m, T.int64(80))) + matmul_1 = T.match_buffer(var_matmul, (T.int64(1), T.int64(32), n, T.int64(80))) + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, T.int64(80), m): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(A[v_i0, v_i1, v_i2, v_k], B[v_i0, v_i1, v_k, v_i3]) + T.writes(matmul_1[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + matmul_1[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + matmul_1[v_i0, v_i1, v_i2, v_i3] = matmul_1[v_i0, v_i1, v_i2, v_i3] + A[v_i0, v_i1, v_i2, v_k] * B[v_i0, v_i1, v_k, v_i3] + + @T.prim_func + def matmul8(var_A: T.handle, var_B: T.handle, matmul: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(80)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.meta_var(T.int64(32)) + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), T.int64(1), n)) + B = T.match_buffer(var_B, (T.int64(1), T.int64(32), n, T.int64(80))) + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), T.int64(1), T.int64(80), n): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(A[v_i0, v_i1, v_i2, v_k], B[v_i0, v_i1, v_k, v_i3]) + T.writes(matmul[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + matmul[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + matmul[v_i0, v_i1, v_i2, v_i3] = matmul[v_i0, v_i1, v_i2, v_i3] + A[v_i0, v_i1, v_i2, v_k] * B[v_i0, v_i1, v_k, v_i3] + +# fmt: on diff --git a/mlc_llm/dispatch/llama/__init__.py b/mlc_llm/dispatch/llama/__init__.py new file mode 100644 index 0000000..2374080 --- /dev/null +++ b/mlc_llm/dispatch/llama/__init__.py @@ -0,0 +1 @@ +from .main import lookup_func as lookup diff --git a/mlc_llm/dispatch/llama/main.py b/mlc_llm/dispatch/llama/main.py new file mode 100644 index 0000000..166739b --- /dev/null +++ b/mlc_llm/dispatch/llama/main.py @@ -0,0 +1,6712 @@ +import tvm +from tvm import IRModule +from tvm.script import tir as T + + +# fmt: off +@T.prim_func +def fused_min_max_triu_te_broadcast_to(p_output0: T.handle, n: T.int64): + T.func_attr({"tir.noalias": T.bool(True)}) + var_T_broadcast_to_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(1), n, n), "float16") + # with T.block("root"): + var_make_diag_mask_te_intermediate = T.alloc_buffer((n, n), "float16") + for i, j in T.grid(n, n): + with T.block("make_diag_mask_te"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads() + T.writes(var_make_diag_mask_te_intermediate[v_i, v_j]) + var_make_diag_mask_te_intermediate[v_i, v_j] = T.Select(v_i < v_j, T.float16(-65504), T.float16(65504)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), n, n): + with T.block("T_broadcast_to"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_make_diag_mask_te_intermediate[v_ax2, v_ax3]) + T.writes(var_T_broadcast_to_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_broadcast_to_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_make_diag_mask_te_intermediate[v_ax2, v_ax3] + + +def fused_min_max_triu_te_broadcast_to_sch_func(): + sch = tvm.tir.Schedule(fused_min_max_triu_te_broadcast_to) + b0 = sch.get_block("T_broadcast_to") + sch.reverse_compute_inline(b0) + return sch.mod["main"] + + +@T.prim_func +def rms_norm_before(var_rxplaceholder: T.handle, rxplaceholder: T.Buffer((T.int64(4096),), "float32"), var_rms_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + rxplaceholder_1 = T.match_buffer(var_rxplaceholder, (T.int64(1), n, T.int64(4096))) + rms_norm_1 = T.match_buffer(var_rms_norm, (T.int64(1), n, T.int64(4096))) + # with T.block("root"): + rxplaceholderred_temp = T.alloc_buffer((T.int64(1), n)) + for bsz, i, k in T.grid(T.int64(1), n, T.int64(4096)): + with T.block("rxplaceholderred_temp"): + v_bsz, v_i, v_k = T.axis.remap("SSR", [bsz, i, k]) + T.reads(rxplaceholder_1[v_bsz, v_i, v_k]) + T.writes(rxplaceholderred_temp[v_bsz, v_i]) + with T.init(): + rxplaceholderred_temp[v_bsz, v_i] = T.float32(0) + rxplaceholderred_temp[v_bsz, v_i] = rxplaceholderred_temp[v_bsz, v_i] + rxplaceholder_1[v_bsz, v_i, v_k] * rxplaceholder_1[v_bsz, v_i, v_k] + for bsz, i, k in T.grid(T.int64(1), n, T.int64(4096)): + with T.block("rms_norm"): + v_bsz, v_i, v_k = T.axis.remap("SSS", [bsz, i, k]) + T.reads(rxplaceholder[v_k], rxplaceholder_1[v_bsz, v_i, v_k], rxplaceholderred_temp[v_bsz, v_i]) + T.writes(rms_norm_1[v_bsz, v_i, v_k]) + rms_norm_1[v_bsz, v_i, v_k] = rxplaceholder[v_k] * (rxplaceholder_1[v_bsz, v_i, v_k] / T.sqrt(rxplaceholderred_temp[v_bsz, v_i] * T.float32(0.000244140625) + T.float32(9.9999999999999995e-07))) + + +@T.prim_func +def rms_norm_after(var_A: T.handle, var_weight: T.Buffer((T.int64(4096),), "float32"), var_rms_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(4096))) + rms_norm = T.match_buffer(var_rms_norm, (T.int64(1), n, T.int64(4096))) + # with T.block("root"): + for i_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.x"): + with T.block("compute_o"): + v_bsz = T.axis.spatial(T.int64(1), T.int64(0)) + v_i_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i_0) + T.reads(A[v_bsz, v_i_o * T.int64(32):v_i_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(4096)]) + T.writes(rms_norm[v_bsz, T.int64(0) : T.int64(n), T.int64(0):T.int64(4096)]) + sq_sum_pad_local = T.alloc_buffer((T.int64(32),), scope="shared") + for bsz, i_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(16)): + for k_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("compute"): + v_i_i = T.axis.spatial(T.int64(32), i_1) + v_k_i = T.axis.reduce(T.int64(4096), k_0 * T.int64(256) + k_1) + T.reads(A[v_bsz, v_i_o * T.int64(32) + v_i_i, v_k_i]) + T.writes(sq_sum_pad_local[v_i_i]) + with T.init(): + sq_sum_pad_local[v_i_i] = T.float32(0) + sq_sum_pad_local[v_i_i] = sq_sum_pad_local[v_i_i] + T.if_then_else(v_i_o * T.int64(32) + v_i_i < n, A[v_bsz, v_i_o * T.int64(32) + v_i_i, v_k_i], T.float32(0)) * T.if_then_else(v_i_o * T.int64(32) + v_i_i < n, A[v_bsz, v_i_o * T.int64(32) + v_i_i, v_k_i], T.float32(0)) + for bsz_i_fused_1, k_0 in T.grid(T.int64(32), T.int64(16)): + for k_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("compute_cache_write"): + v_bsz = T.axis.spatial(T.int64(1), T.int64(0)) + v_i_i = T.axis.spatial(n, bsz_i_fused_1) + v_k = T.axis.spatial(T.int64(4096), k_0 * T.int64(256) + k_1) + T.reads(A[v_bsz, v_i_o * T.int64(32) + v_i_i, v_k], var_weight[v_k], sq_sum_pad_local[v_i_i]) + T.writes(rms_norm[v_bsz, v_i_o * T.int64(32) + v_i_i, v_k]) + if v_i_i < n: + rms_norm[v_bsz, v_i_o * T.int64(32) + v_i_i, v_k] = var_weight[v_k] * (A[v_bsz, v_i_o * T.int64(32) + v_i_i, v_k] / T.sqrt(sq_sum_pad_local[v_i_i] * T.float32(0.000244140625) + T.float32(9.9999999999999995e-07))) + + +@T.prim_func +def rms_norm_fp16_before(var_rxplaceholder: T.handle, rxplaceholder: T.Buffer((T.int64(4096),), "float16"), var_rms_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + rxplaceholder_1 = T.match_buffer(var_rxplaceholder, (T.int64(1), n, T.int64(4096)), "float16") + rms_norm_1 = T.match_buffer(var_rms_norm, (T.int64(1), n, T.int64(4096)), "float16") + # with T.block("root"): + rxplaceholderred_temp = T.alloc_buffer((T.int64(1), n)) + for bsz, i, k in T.grid(T.int64(1), n, T.int64(4096)): + with T.block("rxplaceholderred_temp"): + v_bsz, v_i, v_k = T.axis.remap("SSR", [bsz, i, k]) + T.reads(rxplaceholder_1[v_bsz, v_i, v_k]) + T.writes(rxplaceholderred_temp[v_bsz, v_i]) + with T.init(): + rxplaceholderred_temp[v_bsz, v_i] = T.float32(0) + rxplaceholderred_temp[v_bsz, v_i] = rxplaceholderred_temp[v_bsz, v_i] + T.Cast("float32", rxplaceholder_1[v_bsz, v_i, v_k]) * T.Cast("float32", rxplaceholder_1[v_bsz, v_i, v_k]) + for bsz, i, k in T.grid(T.int64(1), n, T.int64(4096)): + with T.block("rms_norm"): + v_bsz, v_i, v_k = T.axis.remap("SSS", [bsz, i, k]) + T.reads(rxplaceholder[v_k], rxplaceholder_1[v_bsz, v_i, v_k], rxplaceholderred_temp[v_bsz, v_i]) + T.writes(rms_norm_1[v_bsz, v_i, v_k]) + rms_norm_1[v_bsz, v_i, v_k] = T.Cast("float16", T.Cast("float32", rxplaceholder[v_k]) * (T.Cast("float32", rxplaceholder_1[v_bsz, v_i, v_k]) / T.sqrt(rxplaceholderred_temp[v_bsz, v_i] * T.float32(0.000244140625) + T.float32(9.9999999999999995e-07)))) + + +@T.prim_func +def rms_norm_fp16_after(var_A: T.handle, var_weight: T.Buffer((T.int64(4096),), "float16"), var_rms_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), n, T.int64(4096)), dtype="float16") + rms_norm = T.match_buffer(var_rms_norm, (T.int64(1), n, T.int64(4096)), dtype="float16") + # with T.block("root"): + for i_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.x"): + with T.block("compute_o"): + v_bsz = T.axis.spatial(T.int64(1), T.int64(0)) + v_i_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i_0) + T.reads(A[v_bsz, v_i_o * T.int64(32):v_i_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(4096)]) + T.writes(rms_norm[v_bsz, T.int64(0) : T.int64(n), T.int64(0):T.int64(4096)]) + sq_sum_pad_local = T.alloc_buffer((T.int64(32),), scope="shared") + for bsz, i_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(16)): + for k_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("compute"): + v_i_i = T.axis.spatial(T.int64(32), i_1) + v_k_i = T.axis.reduce(T.int64(4096), k_0 * T.int64(256) + k_1) + T.reads(A[v_bsz, v_i_o * T.int64(32) + v_i_i, v_k_i]) + T.writes(sq_sum_pad_local[v_i_i]) + with T.init(): + sq_sum_pad_local[v_i_i] = T.float32(0) + sq_sum_pad_local[v_i_i] = sq_sum_pad_local[v_i_i] + T.if_then_else(v_i_o * T.int64(32) + v_i_i < n, T.Cast("float32", A[v_bsz, v_i_o * T.int64(32) + v_i_i, v_k_i]) * T.Cast("float32", A[v_bsz, v_i_o * T.int64(32) + v_i_i, v_k_i]), T.float32(0)) + for bsz_i_fused_1, k_0 in T.grid(T.int64(32), T.int64(16)): + for k_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("compute_cache_write"): + v_bsz = T.axis.spatial(T.int64(1), T.int64(0)) + v_i_i = T.axis.spatial(n, bsz_i_fused_1) + v_k = T.axis.spatial(T.int64(4096), k_0 * T.int64(256) + k_1) + T.reads(A[v_bsz, v_i_o * T.int64(32) + v_i_i, v_k], var_weight[v_k], sq_sum_pad_local[v_i_i]) + T.writes(rms_norm[v_bsz, v_i_o * T.int64(32) + v_i_i, v_k]) + if v_i_i < n: + rms_norm[v_bsz, v_i_o * T.int64(32) + v_i_i, v_k] = T.Cast("float16", T.Cast("float32", var_weight[v_k]) * (T.Cast("float32", A[v_bsz, v_i_o * T.int64(32) + v_i_i, v_k]) / T.sqrt(sq_sum_pad_local[v_i_i] * T.float32(0.000244140625) + T.float32(9.9999999999999995e-07)))) + + +@T.prim_func +def softmax_before(var_rxplaceholder: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + rxplaceholder = T.match_buffer(var_rxplaceholder, (T.int64(1), T.int64(32), n, n)) + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), n, n)) + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), n)) + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(32), n, n)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), n)) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, n): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.max(T_softmax_maxelem[v_i0, v_i1, v_i2], rxplaceholder[v_i0, v_i1, v_i2, v_k]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, n): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2, v_i3]) + T_softmax_exp[v_i0, v_i1, v_i2, v_i3] = T.exp(rxplaceholder[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, n): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_expsum[v_i0, v_i1, v_i2] = T.float32(0) + T_softmax_expsum[v_i0, v_i1, v_i2] = T_softmax_expsum[v_i0, v_i1, v_i2] + T_softmax_exp[v_i0, v_i1, v_i2, v_k] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, n): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_i3], T_softmax_expsum[v_i0, v_i1, v_i2]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"axis": 3}) + T_softmax_norm[v_i0, v_i1, v_i2, v_i3] = T_softmax_exp[v_i0, v_i1, v_i2, v_i3] / T_softmax_expsum[v_i0, v_i1, v_i2] + + +@T.prim_func +def softmax_after(var_A: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), n, n)) + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), n, n)) + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), n)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), n)) + for i2_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.x"): + with T.block("T_softmax_maxelem_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0) + T.reads(A[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):(n + T.int64(127)) // T.int64(128) * T.int64(128)]) + T.writes(T_softmax_maxelem[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32)]) + T_softmax_maxelem_pad_0_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32)), scope="shared") + for i0, i1, i2_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(32), (n + T.int64(127)) // T.int64(128)): + for k_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_maxelem"): + v_i1_i, v_i2_i = T.axis.remap("SS", [i1, i2_1]) + v_k_i = T.axis.reduce(T.int64(32) * ((n + T.int64(127)) // T.int64(128)), k_0 * T.int64(128) + k_1) + T.reads(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i]) + T.writes(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]) + with T.init(): + T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.max(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i], T.if_then_else(v_i2_o * T.int64(32) + v_i2_i < n and v_k_i < n, A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i], T.float32(-3.4028234663852886e+38))) + for i0_i1_i2_1_fused_0 in range(T.int64(8)): + for i0_i1_i2_1_fused_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_maxelem_cache_write"): + v_i1_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) // T.int64(32)) + v_i2_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32)) + T.where(v_i2_o * T.int64(32) + (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32) < n) + T.reads(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]) + T.writes(T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]) + T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i] = T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] + for i2_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.x"): + with T.block("T_softmax_expsum_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0) + T.reads(A[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):(n + T.int64(127)) // T.int64(128) * T.int64(128)], T_softmax_maxelem[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32)]) + T.writes(T_softmax_expsum[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32)]) + T_softmax_expsum_pad_0_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32)), scope="shared") + for i0, i1, i2_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(32), (n + T.int64(127)) // T.int64(128)): + for k_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_expsum"): + v_i1_i, v_i2_i = T.axis.remap("SS", [i1, i2_1]) + v_k_i = T.axis.reduce(T.int64(32) * ((n + T.int64(127)) // T.int64(128)), k_0 * T.int64(128) + k_1) + T.reads(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i], T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]) + T.writes(T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i]) + with T.init(): + T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.float32(0) + T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] = T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] + T.if_then_else(v_i2_o * T.int64(32) + v_i2_i < n and v_k_i < n, T.exp(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i] - T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]), T.float32(0)) + for i0_i1_i2_1_fused_0 in range(T.int64(8)): + for i0_i1_i2_1_fused_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_expsum_cache_write"): + v_i1_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) // T.int64(32)) + v_i2_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32)) + T.where(v_i2_o * T.int64(32) + (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32) < n) + T.reads(T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i]) + T.writes(T_softmax_expsum[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]) + T_softmax_expsum[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i] = T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] + for i0_i1_i2_fused_i3_fused_0 in T.thread_binding((n * T.int64(32) * n + T.int64(255)) // T.int64(256), thread="blockIdx.x"): + for i0_i1_i2_fused_i3_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("T_softmax_norm"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(32), (i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1) // n // n) + v_i2 = T.axis.spatial(n, (i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1) // n % n) + v_i3 = T.axis.spatial(n, (i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1) % n) + T.where(i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1 < n * T.int64(32) * n) + T.reads(T_softmax_expsum[v_i0, v_i1, v_i2], A[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2, v_i3]) + T_softmax_norm[v_i0, v_i1, v_i2, v_i3] = T.exp(A[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) / T_softmax_expsum[v_i0, v_i1, v_i2] + + +@T.prim_func +def softmax_mxn_before(var_rxplaceholder: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + m = T.int64() + rxplaceholder = T.match_buffer(var_rxplaceholder, (T.int64(1), T.int64(32), n, m)) + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), n)) + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), n)) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.max(T_softmax_maxelem[v_i0, v_i1, v_i2], rxplaceholder[v_i0, v_i1, v_i2, v_k]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2, v_i3]) + T_softmax_exp[v_i0, v_i1, v_i2, v_i3] = T.exp(rxplaceholder[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_expsum[v_i0, v_i1, v_i2] = T.float32(0) + T_softmax_expsum[v_i0, v_i1, v_i2] = T_softmax_expsum[v_i0, v_i1, v_i2] + T_softmax_exp[v_i0, v_i1, v_i2, v_k] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_i3], T_softmax_expsum[v_i0, v_i1, v_i2]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"axis": 3}) + T_softmax_norm[v_i0, v_i1, v_i2, v_i3] = T_softmax_exp[v_i0, v_i1, v_i2, v_i3] / T_softmax_expsum[v_i0, v_i1, v_i2] + + +@T.prim_func +def softmax_mxn_after(var_A: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + m = T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), n, m)) + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), n)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), n)) + for i2_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.x"): + with T.block("T_softmax_maxelem_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0) + T.reads(A[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):(m + T.int64(127)) // T.int64(128) * T.int64(128)]) + T.writes(T_softmax_maxelem[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32)]) + T_softmax_maxelem_pad_0_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32)), scope="shared") + for i0, i1, i2_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(32), (m + T.int64(127)) // T.int64(128)): + for k_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_maxelem"): + v_i1_i, v_i2_i = T.axis.remap("SS", [i1, i2_1]) + v_k_i = T.axis.reduce(T.int64(32) * ((m + T.int64(127)) // T.int64(128)), k_0 * T.int64(128) + k_1) + T.reads(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i]) + T.writes(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]) + with T.init(): + T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.max(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i], T.if_then_else(v_i2_o * T.int64(32) + v_i2_i < n and v_k_i < m, A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i], T.float32(-3.4028234663852886e+38))) + for i0_i1_i2_1_fused_0 in range(T.int64(8)): + for i0_i1_i2_1_fused_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_maxelem_cache_write"): + v_i1_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) // T.int64(32)) + v_i2_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32)) + T.where(v_i2_o * T.int64(32) + (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32) < n) + T.reads(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]) + T.writes(T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]) + T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i] = T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] + for i2_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.x"): + with T.block("T_softmax_expsum_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0) + T.reads(A[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):(m + T.int64(127)) // T.int64(128) * T.int64(128)], T_softmax_maxelem[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32)]) + T.writes(T_softmax_expsum[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32)]) + T_softmax_expsum_pad_0_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32)), scope="shared") + for i0, i1, i2_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(32), (m + T.int64(127)) // T.int64(128)): + for k_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_expsum"): + v_i1_i, v_i2_i = T.axis.remap("SS", [i1, i2_1]) + v_k_i = T.axis.reduce(T.int64(32) * ((m + T.int64(127)) // T.int64(128)), k_0 * T.int64(128) + k_1) + T.reads(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i], T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]) + T.writes(T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i]) + with T.init(): + T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.float32(0) + T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] = T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] + T.if_then_else(v_i2_o * T.int64(32) + v_i2_i < n and v_k_i < m, T.exp(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i] - T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]), T.float32(0)) + for i0_i1_i2_1_fused_0 in range(T.int64(8)): + for i0_i1_i2_1_fused_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_expsum_cache_write"): + v_i1_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) // T.int64(32)) + v_i2_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32)) + T.where(v_i2_o * T.int64(32) + (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32) < n) + T.reads(T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i]) + T.writes(T_softmax_expsum[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]) + T_softmax_expsum[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i] = T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] + for i0_i1_i2_fused_i3_fused_0 in T.thread_binding((n * T.int64(32) * m + T.int64(255)) // T.int64(256), thread="blockIdx.x"): + for i0_i1_i2_fused_i3_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("T_softmax_norm"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(32), (i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1) // m // n) + v_i2 = T.axis.spatial(n, (i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1) // m % n) + v_i3 = T.axis.spatial(m, (i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1) % m) + T.where(i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1 < n * T.int64(32) * m) + T.reads(T_softmax_expsum[v_i0, v_i1, v_i2], A[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2, v_i3]) + T_softmax_norm[v_i0, v_i1, v_i2, v_i3] = T.exp(A[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) / T_softmax_expsum[v_i0, v_i1, v_i2] + +@T.prim_func +def softmax_cast_mxn_before(p_lv37: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n, m = T.int64(), T.int64() + lv37 = T.match_buffer(p_lv37, (T.int64(1), T.int64(32), n, m)) + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, m), "float16") + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), n)) + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), n)) + var_T_softmax_norm_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv37[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.max(T_softmax_maxelem[v_i0, v_i1, v_i2], lv37[v_i0, v_i1, v_i2, v_k]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(lv37[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2, v_i3]) + T_softmax_exp[v_i0, v_i1, v_i2, v_i3] = T.exp(lv37[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_expsum[v_i0, v_i1, v_i2] = T.float32(0) + T_softmax_expsum[v_i0, v_i1, v_i2] = T_softmax_expsum[v_i0, v_i1, v_i2] + T_softmax_exp[v_i0, v_i1, v_i2, v_k] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_i3], T_softmax_expsum[v_i0, v_i1, v_i2]) + T.writes(var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"axis": 3}) + var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3] = T_softmax_exp[v_i0, v_i1, v_i2, v_i3] / T_softmax_expsum[v_i0, v_i1, v_i2] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("compute"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3]) + var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.Cast("float16", var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + + +@T.prim_func +def softmax_cast_mxn_after(var_A: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + m = T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), n, m)) + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), n, m), dtype="float16") + # with T.block("root"): + for i2_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.x"): + with T.block("T_softmax_maxelem_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0) + T.reads(A[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):(m + T.int64(127)) // T.int64(128) * T.int64(128)]) + T.writes(T_softmax_norm[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):m]) + T_softmax_maxelem_pad_0_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32)), scope="shared") + T_softmax_expsum_pad_0_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32)), scope="shared") + for i0, i1, i2_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(32), (m + T.int64(127)) // T.int64(128)): + for k_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_maxelem"): + v_i1_i, v_i2_i = T.axis.remap("SS", [i1, i2_1]) + v_k_i = T.axis.reduce(T.int64(32) * ((m + T.int64(127)) // T.int64(128)), k_0 * T.int64(128) + k_1) + T.reads(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i]) + T.writes(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]) + with T.init(): + T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.max(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i], T.if_then_else(v_i2_o * T.int64(32) + v_i2_i < n and v_k_i < m, A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i], T.float32(-3.4028234663852886e+38))) + for i0, i1, i2_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(32), (m + T.int64(127)) // T.int64(128)): + for k_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_expsum"): + v_i1_i, v_i2_i = T.axis.remap("SS", [i1, i2_1]) + v_k_i = T.axis.reduce(T.int64(32) * ((m + T.int64(127)) // T.int64(128)), k_0 * T.int64(128) + k_1) + T.reads(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i], T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]) + T.writes(T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i]) + with T.init(): + T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.float32(0) + T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] = T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] + T.if_then_else(v_i2_o * T.int64(32) + v_i2_i < n and v_k_i < m, T.exp(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i] - T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]), T.float32(0)) + for i0_i1_i2_1_i3_fused_0 in range((T.int64(32) * T.int64(32) * m) // T.int64(128)): + for i0_i1_i2_1_i3_fused_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_norm"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(32), (i0_i1_i2_1_i3_fused_0 * T.int64(128) + i0_i1_i2_1_i3_fused_1) // T.int64(32) // m) + v_i2_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_i3_fused_0 * T.int64(128) + i0_i1_i2_1_i3_fused_1) // m % T.int64(32)) + v_i3 = T.axis.spatial(m, (i0_i1_i2_1_i3_fused_0 * T.int64(128) + i0_i1_i2_1_i3_fused_1) % m) + T.where(i0_i1_i2_1_i3_fused_0 * T.int64(128) + i0_i1_i2_1_i3_fused_1 < T.int64(32) * T.int64(32) * m) + T.reads(T_softmax_expsum_pad_0_local[v_i0, v_i1, v_i2_i], A[v_i0, v_i1, v_i2_o * T.int64(32) + v_i2_i, v_i3], T_softmax_maxelem_pad_0_local[v_i0, v_i1, v_i2_i]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2_o * T.int64(32) + v_i2_i, v_i3]) + if v_i2_o * T.int64(32) + v_i2_i < n: + T_softmax_norm[v_i0, v_i1, v_i2_o * T.int64(32) + v_i2_i, v_i3] = T.Cast("float16", T.exp(A[v_i0, v_i1, v_i2_o * T.int64(32) + v_i2_i, v_i3] - T_softmax_maxelem_pad_0_local[v_i0, v_i1, v_i2_i]) / T_softmax_expsum_pad_0_local[v_i0, v_i1, v_i2_i]) + + +@T.prim_func +def softmax_mxn_fp16_before(var_rxplaceholder: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + m = T.int64() + rxplaceholder = T.match_buffer(var_rxplaceholder, (T.int64(1), T.int64(32), n, m), "float16") + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), n, m), "float16") + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), n), "float16") + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(32), n, m), "float16") + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), n), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.float16(-65504) + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.max(T_softmax_maxelem[v_i0, v_i1, v_i2], rxplaceholder[v_i0, v_i1, v_i2, v_k]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2, v_i3]) + T_softmax_exp[v_i0, v_i1, v_i2, v_i3] = T.exp(rxplaceholder[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_expsum[v_i0, v_i1, v_i2] = T.float16(0) + T_softmax_expsum[v_i0, v_i1, v_i2] = T_softmax_expsum[v_i0, v_i1, v_i2] + T_softmax_exp[v_i0, v_i1, v_i2, v_k] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_i3], T_softmax_expsum[v_i0, v_i1, v_i2]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"axis": 3}) + T_softmax_norm[v_i0, v_i1, v_i2, v_i3] = T_softmax_exp[v_i0, v_i1, v_i2, v_i3] / T_softmax_expsum[v_i0, v_i1, v_i2] + +@T.prim_func +def softmax_mxn_fp16_after(var_A: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + m = T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), n, m), dtype="float16") + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), n, m), dtype="float16") + # with T.block("root"): + for i2_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.x"): + with T.block("T_softmax_maxelem_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0) + T.reads(A[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):(m + T.int64(127)) // T.int64(128) * T.int64(128)]) + T.writes(T_softmax_norm[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):m]) + T_softmax_maxelem_pad_0_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32)), scope="shared", dtype="float16") + T_softmax_expsum_pad_0_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32)), scope="shared", dtype="float16") + for i0, i1, i2_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(32), (m + T.int64(127)) // T.int64(128)): + for k_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_maxelem"): + v_i1_i, v_i2_i = T.axis.remap("SS", [i1, i2_1]) + v_k_i = T.axis.reduce(T.int64(32) * ((m + T.int64(127)) // T.int64(128)), k_0 * T.int64(128) + k_1) + T.reads(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i]) + T.writes(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]) + with T.init(): + T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.float16(-65504) + T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.max(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i], T.if_then_else(v_i2_o * T.int64(32) + v_i2_i < n and v_k_i < m, A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i], T.float16(-65504))) + for i0, i1, i2_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(32), (m + T.int64(127)) // T.int64(128)): + for k_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_expsum"): + v_i1_i, v_i2_i = T.axis.remap("SS", [i1, i2_1]) + v_k_i = T.axis.reduce(T.int64(32) * ((m + T.int64(127)) // T.int64(128)), k_0 * T.int64(128) + k_1) + T.reads(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i], T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]) + T.writes(T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i]) + with T.init(): + T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.float16(0) + T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] = T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] + T.if_then_else(v_i2_o * T.int64(32) + v_i2_i < n and v_k_i < m, T.exp(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i] - T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]), T.float16(0)) + for i0_i1_i2_1_i3_fused_0 in range((T.int64(32) * T.int64(32) * m) // T.int64(128)): + for i0_i1_i2_1_i3_fused_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_norm"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(32), (i0_i1_i2_1_i3_fused_0 * T.int64(128) + i0_i1_i2_1_i3_fused_1) // T.int64(32) // m) + v_i2_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_i3_fused_0 * T.int64(128) + i0_i1_i2_1_i3_fused_1) // m % T.int64(32)) + v_i3 = T.axis.spatial(m, (i0_i1_i2_1_i3_fused_0 * T.int64(128) + i0_i1_i2_1_i3_fused_1) % m) + T.where(i0_i1_i2_1_i3_fused_0 * T.int64(128) + i0_i1_i2_1_i3_fused_1 < T.int64(32) * T.int64(32) * m) + T.reads(T_softmax_expsum_pad_0_local[v_i0, v_i1, v_i2_i], A[v_i0, v_i1, v_i2_o * T.int64(32) + v_i2_i, v_i3], T_softmax_maxelem_pad_0_local[v_i0, v_i1, v_i2_i]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2_o * T.int64(32) + v_i2_i, v_i3]) + if v_i2_o * T.int64(32) + v_i2_i < n: + T_softmax_norm[v_i0, v_i1, v_i2_o * T.int64(32) + v_i2_i, v_i3] = T.exp(A[v_i0, v_i1, v_i2_o * T.int64(32) + v_i2_i, v_i3] - T_softmax_maxelem_pad_0_local[v_i0, v_i1, v_i2_i]) / T_softmax_expsum_pad_0_local[v_i0, v_i1, v_i2_i] + + +@T.prim_func +def softmax_fp16_before(var_rxplaceholder: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + rxplaceholder = T.match_buffer(var_rxplaceholder, (T.int64(1), T.int64(32), n, n), "float16") + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), n, n), "float16") + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), n), "float16") + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(32), n, n), "float16") + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), n), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, n): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.float16(-65504) + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.max(T_softmax_maxelem[v_i0, v_i1, v_i2], rxplaceholder[v_i0, v_i1, v_i2, v_k]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, n): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2, v_i3]) + T_softmax_exp[v_i0, v_i1, v_i2, v_i3] = T.exp(rxplaceholder[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), n, n): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_expsum[v_i0, v_i1, v_i2] = T.float16(0) + T_softmax_expsum[v_i0, v_i1, v_i2] = T_softmax_expsum[v_i0, v_i1, v_i2] + T_softmax_exp[v_i0, v_i1, v_i2, v_k] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, n): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_i3], T_softmax_expsum[v_i0, v_i1, v_i2]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"axis": 3}) + T_softmax_norm[v_i0, v_i1, v_i2, v_i3] = T_softmax_exp[v_i0, v_i1, v_i2, v_i3] / T_softmax_expsum[v_i0, v_i1, v_i2] + + +@T.prim_func +def softmax_fp16_after(var_A: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + A = T.match_buffer(var_A, (T.int64(1), T.int64(32), n, n), dtype="float16") + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), n, n), dtype="float16") + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), n), dtype="float16") + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), n), dtype="float16") + for i2_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.x"): + with T.block("T_softmax_maxelem_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0) + T.reads(A[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):(n + T.int64(127)) // T.int64(128) * T.int64(128)]) + T.writes(T_softmax_maxelem[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32)]) + T_softmax_maxelem_pad_0_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32)), scope="shared", dtype="float16") + for i0, i1, i2_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(32), (n + T.int64(127)) // T.int64(128)): + for k_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_maxelem"): + v_i1_i, v_i2_i = T.axis.remap("SS", [i1, i2_1]) + v_k_i = T.axis.reduce(T.int64(32) * ((n + T.int64(127)) // T.int64(128)), k_0 * T.int64(128) + k_1) + T.reads(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i]) + T.writes(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]) + with T.init(): + T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.float16(-65504) + T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.max(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i], T.if_then_else(v_i2_o * T.int64(32) + v_i2_i < n and v_k_i < n, A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i], T.float16(-65504))) + for i0_i1_i2_1_fused_0 in range(T.int64(8)): + for i0_i1_i2_1_fused_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_maxelem_cache_write"): + v_i1_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) // T.int64(32)) + v_i2_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32)) + T.where(v_i2_o * T.int64(32) + (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32) < n) + T.reads(T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i]) + T.writes(T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]) + T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i] = T_softmax_maxelem_pad_0_local[v_i0, v_i1_i, v_i2_i] + for i2_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.x"): + with T.block("T_softmax_expsum_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0) + T.reads(A[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):(n + T.int64(127)) // T.int64(128) * T.int64(128)], T_softmax_maxelem[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32)]) + T.writes(T_softmax_expsum[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32)]) + T_softmax_expsum_pad_0_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32)), scope="shared", dtype="float16") + for i0, i1, i2_1, k_0 in T.grid(T.int64(1), T.int64(32), T.int64(32), (n + T.int64(127)) // T.int64(128)): + for k_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_expsum"): + v_i1_i, v_i2_i = T.axis.remap("SS", [i1, i2_1]) + v_k_i = T.axis.reduce(T.int64(32) * ((n + T.int64(127)) // T.int64(128)), k_0 * T.int64(128) + k_1) + T.reads(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i], T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]) + T.writes(T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i]) + with T.init(): + T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] = T.float16(0) + T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] = T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] + T.if_then_else(v_i2_o * T.int64(32) + v_i2_i < n and v_k_i < n, T.exp(A[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i, v_k_i] - T_softmax_maxelem[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]), T.float16(0)) + for i0_i1_i2_1_fused_0 in range(T.int64(8)): + for i0_i1_i2_1_fused_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + with T.block("T_softmax_expsum_cache_write"): + v_i1_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) // T.int64(32)) + v_i2_i = T.axis.spatial(T.int64(32), (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32)) + T.where(v_i2_o * T.int64(32) + (i0_i1_i2_1_fused_0 * T.int64(128) + i0_i1_i2_1_fused_1) % T.int64(32) < n) + T.reads(T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i]) + T.writes(T_softmax_expsum[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i]) + T_softmax_expsum[v_i0, v_i1_i, v_i2_o * T.int64(32) + v_i2_i] = T_softmax_expsum_pad_0_local[v_i0, v_i1_i, v_i2_i] + for i0_i1_i2_fused_i3_fused_0 in T.thread_binding((n * T.int64(32) * n + T.int64(255)) // T.int64(256), thread="blockIdx.x"): + for i0_i1_i2_fused_i3_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("T_softmax_norm"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(32), (i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1) // n // n) + v_i2 = T.axis.spatial(n, (i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1) // n % n) + v_i3 = T.axis.spatial(n, (i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1) % n) + T.where(i0_i1_i2_fused_i3_fused_0 * T.int64(256) + i0_i1_i2_fused_i3_fused_1 < n * T.int64(32) * n) + T.reads(T_softmax_expsum[v_i0, v_i1, v_i2], A[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2, v_i3]) + T_softmax_norm[v_i0, v_i1, v_i2, v_i3] = T.exp(A[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) / T_softmax_expsum[v_i0, v_i1, v_i2] + + +@T.prim_func +def softmax_1xn_before(var_inp0: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + inp0 = T.match_buffer(var_inp0, (T.int64(1), T.int64(32), T.int64(1), n)) + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), T.int64(1), n)) + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1))) + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1))) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(inp0[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.max(T_softmax_maxelem[v_i0, v_i1, v_i2], inp0[v_i0, v_i1, v_i2, v_k]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(inp0[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2, v_i3]) + T_softmax_exp[v_i0, v_i1, v_i2, v_i3] = T.exp(inp0[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_expsum[v_i0, v_i1, v_i2] = T.float32(0) + T_softmax_expsum[v_i0, v_i1, v_i2] = T_softmax_expsum[v_i0, v_i1, v_i2] + T_softmax_exp[v_i0, v_i1, v_i2, v_k] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_i3], T_softmax_expsum[v_i0, v_i1, v_i2]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"axis": 3}) + T_softmax_norm[v_i0, v_i1, v_i2, v_i3] = T_softmax_exp[v_i0, v_i1, v_i2, v_i3] / T_softmax_expsum[v_i0, v_i1, v_i2] + + +@T.prim_func +def softmax_cast_1xn_before(p_lv1614: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv1614 = T.match_buffer(p_lv1614, (T.int64(1), T.int64(32), T.int64(1), n)) + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), T.int64(1), n), "float16") + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1))) + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1))) + var_T_softmax_norm_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1614[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.float32(-3.4028234663852886e+38) + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.max(T_softmax_maxelem[v_i0, v_i1, v_i2], lv1614[v_i0, v_i1, v_i2, v_k]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(lv1614[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2, v_i3]) + T_softmax_exp[v_i0, v_i1, v_i2, v_i3] = T.exp(lv1614[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_expsum[v_i0, v_i1, v_i2] = T.float32(0) + T_softmax_expsum[v_i0, v_i1, v_i2] = T_softmax_expsum[v_i0, v_i1, v_i2] + T_softmax_exp[v_i0, v_i1, v_i2, v_k] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_i3], T_softmax_expsum[v_i0, v_i1, v_i2]) + T.writes(var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"axis": 3}) + var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3] = T_softmax_exp[v_i0, v_i1, v_i2, v_i3] / T_softmax_expsum[v_i0, v_i1, v_i2] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("compute"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3]) + var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.Cast("float16", var_T_softmax_norm_intermediate[v_i0, v_i1, v_i2, v_i3]) + + +@T.prim_func +def softmax_1xn_fp16_before(var_rxplaceholder: T.handle, var_T_softmax_norm: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + rxplaceholder = T.match_buffer(var_rxplaceholder, (T.int64(1), T.int64(32), T.int64(1), n), "float16") + T_softmax_norm = T.match_buffer(var_T_softmax_norm, (T.int64(1), T.int64(32), T.int64(1), n), "float16") + # with T.block("root"): + T_softmax_maxelem = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1)), "float16") + T_softmax_exp = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + T_softmax_expsum = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_maxelem"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_maxelem[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.float16(-65504) + T_softmax_maxelem[v_i0, v_i1, v_i2] = T.max(T_softmax_maxelem[v_i0, v_i1, v_i2], rxplaceholder[v_i0, v_i1, v_i2, v_k]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_exp"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_i3], T_softmax_maxelem[v_i0, v_i1, v_i2]) + T.writes(T_softmax_exp[v_i0, v_i1, v_i2, v_i3]) + T_softmax_exp[v_i0, v_i1, v_i2, v_i3] = T.exp(rxplaceholder[v_i0, v_i1, v_i2, v_i3] - T_softmax_maxelem[v_i0, v_i1, v_i2]) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_expsum"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_k]) + T.writes(T_softmax_expsum[v_i0, v_i1, v_i2]) + with T.init(): + T_softmax_expsum[v_i0, v_i1, v_i2] = T.float16(0) + T_softmax_expsum[v_i0, v_i1, v_i2] = T_softmax_expsum[v_i0, v_i1, v_i2] + T_softmax_exp[v_i0, v_i1, v_i2, v_k] + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_softmax_norm"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(T_softmax_exp[v_i0, v_i1, v_i2, v_i3], T_softmax_expsum[v_i0, v_i1, v_i2]) + T.writes(T_softmax_norm[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"axis": 3}) + T_softmax_norm[v_i0, v_i1, v_i2, v_i3] = T_softmax_exp[v_i0, v_i1, v_i2, v_i3] / T_softmax_expsum[v_i0, v_i1, v_i2] + + +def softmax_1xn_sch_func(f_softmax, cast_to_fp16: bool = False): + sch = tvm.tir.Schedule(f_softmax) + if cast_to_fp16: + b_cast = sch.get_block("compute") + sch.reverse_compute_inline(b_cast) + + b0 = sch.get_block("T_softmax_exp") + sch.compute_inline(b0) + b1 = sch.get_block("T_softmax_norm") + l2, l3, l4, l5 = sch.get_loops(b1) + l6, l7 = sch.split(l5, [None, 128]) + sch.bind(l7, "threadIdx.x") + b8 = sch.get_block("T_softmax_expsum") + sch.compute_at(b8, l4) + sch.set_scope(b8, 0, "shared") + l9, l10, l11, l12 = sch.get_loops(b8) + l13, l14 = sch.split(l12, [None, 128]) + sch.bind(l14, "threadIdx.x") + b15 = sch.get_block("T_softmax_maxelem") + sch.compute_at(b15, l4) + sch.set_scope(b15, 0, "shared") + l16, l17, l18, l19 = sch.get_loops(b15) + l20, l21 = sch.split(l19, [None, 128]) + sch.bind(l21, "threadIdx.x") + l22 = sch.fuse(l2, l3, l4) + sch.bind(l22, "blockIdx.x") + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func +def matmul1_before(var_rxplaceholder: T.handle, var_rxplaceholder_1: T.handle, matmul: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(128)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + rxplaceholder = T.match_buffer(var_rxplaceholder, (T.int64(1), T.int64(32), T.int64(1), n)) + rxplaceholder_1 = T.match_buffer(var_rxplaceholder_1, (T.int64(1), T.int64(32), n, T.int64(128))) + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), T.int64(1), T.int64(128), n): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_k], rxplaceholder_1[v_i0, v_i1, v_k, v_i3]) + T.writes(matmul[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + matmul[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + matmul[v_i0, v_i1, v_i2, v_i3] = matmul[v_i0, v_i1, v_i2, v_i3] + rxplaceholder[v_i0, v_i1, v_i2, v_k] * rxplaceholder_1[v_i0, v_i1, v_k, v_i3] + + +@T.prim_func +def matmul1_after(var_rxplaceholder: T.handle, var_rxplaceholder_1: T.handle, matmul: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(128)), "float32")): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + rxplaceholder = T.match_buffer(var_rxplaceholder, (T.int64(1), T.int64(32), T.int64(1), n)) + rxplaceholder_1 = T.match_buffer(var_rxplaceholder_1, (T.int64(1), T.int64(32), n, T.int64(128))) + # with T.block("root"): + matmul_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(128)), scope="local") + rxplaceholder_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), (n + T.int64(127)) // T.int64(128) * T.int64(128)), scope="shared") + rxplaceholder_1_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), (n + T.int64(127)) // T.int64(128) * T.int64(128), T.int64(128)), scope="shared") + for i0_0_i1_0_i2_0_i3_0_fused in T.thread_binding(T.int64(16), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 512, "pragma_unroll_explicit": 1}): + for i0_1_i1_1_i2_1_i3_1_fused in T.thread_binding(T.int64(1), thread="vthread.x"): + for i0_2_i1_2_i2_2_i3_2_fused in T.thread_binding(T.int64(128), thread="threadIdx.x"): + for i0_3_init, i1_3_init, i2_3_init, i3_3_init, i0_4_init, i1_4_init, i2_4_init, i3_4_init in T.grid(T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(2), T.int64(1), T.int64(1)): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), i0_3_init + i0_4_init) + v_i1 = T.axis.spatial(T.int64(32), i0_2_i1_2_i2_2_i3_2_fused // T.int64(8) * T.int64(2) + i1_3_init * T.int64(2) + i1_4_init) + v_i2 = T.axis.spatial(T.int64(1), i2_3_init + i2_4_init) + v_i3 = T.axis.spatial(T.int64(128), i0_0_i1_0_i2_0_i3_0_fused * T.int64(8) + i0_2_i1_2_i2_2_i3_2_fused % T.int64(8) + i3_3_init + i3_4_init) + T.reads() + T.writes(matmul_local[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + matmul_local[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + for k_0, k_1_0 in T.grid((n + T.int64(127)) // T.int64(128), T.int64(8)): + for ax0_ax1_ax2_ax3_fused_0 in range(T.int64(1)): + for ax0_ax1_ax2_ax3_fused_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + for ax0_ax1_ax2_ax3_fused_2 in T.vectorized(T.int64(4)): + with T.block("rxplaceholder_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), (ax0_ax1_ax2_ax3_fused_0 * T.int64(512) + ax0_ax1_ax2_ax3_fused_1 * T.int64(4) + ax0_ax1_ax2_ax3_fused_2) // T.int64(16)) + v2 = T.axis.spatial(T.int64(1), T.int64(0)) + v3 = T.axis.spatial((n + T.int64(127)) // T.int64(128) * T.int64(128), k_0 * T.int64(128) + k_1_0 * T.int64(16) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(512) + ax0_ax1_ax2_ax3_fused_1 * T.int64(4) + ax0_ax1_ax2_ax3_fused_2) % T.int64(16)) + T.reads(rxplaceholder[v0, v1, v2, v3]) + T.writes(rxplaceholder_pad_shared[v0, v1, v2, v3]) + rxplaceholder_pad_shared[v0, v1, v2, v3] = T.if_then_else(v3 < n, rxplaceholder[v0, v1, v2, v3], T.float32(0)) + for ax0_ax1_ax2_ax3_fused_0 in range(T.int64(8)): + for ax0_ax1_ax2_ax3_fused_1 in T.thread_binding(T.int64(128), thread="threadIdx.x"): + for ax0_ax1_ax2_ax3_fused_2 in T.vectorized(T.int64(4)): + with T.block("rxplaceholder_1_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), (ax0_ax1_ax2_ax3_fused_0 * T.int64(512) + ax0_ax1_ax2_ax3_fused_1 * T.int64(4) + ax0_ax1_ax2_ax3_fused_2) // T.int64(128)) + v2 = T.axis.spatial((n + T.int64(127)) // T.int64(128) * T.int64(128), k_0 * T.int64(128) + k_1_0 * T.int64(16) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(512) + ax0_ax1_ax2_ax3_fused_1 * T.int64(4) + ax0_ax1_ax2_ax3_fused_2) % T.int64(128) // T.int64(8)) + v3 = T.axis.spatial(T.int64(128), i0_0_i1_0_i2_0_i3_0_fused * T.int64(8) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(512) + ax0_ax1_ax2_ax3_fused_1 * T.int64(4) + ax0_ax1_ax2_ax3_fused_2) % T.int64(8)) + T.reads(rxplaceholder_1[v0, v1, v2, v3]) + T.writes(rxplaceholder_1_pad_shared[v0, v1, v2, v3]) + rxplaceholder_1_pad_shared[v0, v1, v2, v3] = T.if_then_else(v2 < n, rxplaceholder_1[v0, v1, v2, v3], T.float32(0)) + for k_1_1, i0_3, i1_3, i2_3, i3_3, k_1_2, i0_4, i1_4, i2_4, i3_4 in T.grid(T.int64(2), T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(8), T.int64(1), T.int64(2), T.int64(1), T.int64(1)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), i0_3 + i0_4) + v_i1 = T.axis.spatial(T.int64(32), i0_2_i1_2_i2_2_i3_2_fused // T.int64(8) * T.int64(2) + i1_3 * T.int64(2) + i1_4) + v_i2 = T.axis.spatial(T.int64(1), i2_3 + i2_4) + v_i3 = T.axis.spatial(T.int64(128), i0_0_i1_0_i2_0_i3_0_fused * T.int64(8) + i0_2_i1_2_i2_2_i3_2_fused % T.int64(8) + i3_3 + i3_4) + v_k = T.axis.reduce((n + T.int64(127)) // T.int64(128) * T.int64(128), k_0 * T.int64(128) + k_1_0 * T.int64(16) + k_1_1 * T.int64(8) + k_1_2) + T.reads(matmul_local[v_i0, v_i1, v_i2, v_i3], rxplaceholder_pad_shared[v_i0, v_i1, v_i2, v_k], rxplaceholder_1_pad_shared[v_i0, v_i1, v_k, v_i3]) + T.writes(matmul_local[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + matmul_local[v_i0, v_i1, v_i2, v_i3] = matmul_local[v_i0, v_i1, v_i2, v_i3] + rxplaceholder_pad_shared[v_i0, v_i1, v_i2, v_k] * rxplaceholder_1_pad_shared[v_i0, v_i1, v_k, v_i3] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(2), T.int64(1), T.int64(1)): + with T.block("matmul_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(32), i0_2_i1_2_i2_2_i3_2_fused // T.int64(8) * T.int64(2) + ax1) + v2 = T.axis.spatial(T.int64(1), ax2) + v3 = T.axis.spatial(T.int64(128), i0_0_i1_0_i2_0_i3_0_fused * T.int64(8) + i0_2_i1_2_i2_2_i3_2_fused % T.int64(8) + ax3) + T.reads(matmul_local[v0, v1, v2, v3]) + T.writes(matmul[v0, v1, v2, v3]) + matmul[v0, v1, v2, v3] = matmul_local[v0, v1, v2, v3] + + +@T.prim_func +def matmul2_before(var_inp0: T.handle, inp1: T.Buffer((T.int64(4096), T.int64(4096)), "float32"), var_matmul: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + inp0 = T.match_buffer(var_inp0, (T.int64(1), n, T.int64(4096))) + matmul = T.match_buffer(var_matmul, (T.int64(1), n, T.int64(4096))) + # with T.block("root"): + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(4096), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(inp0[v_i0, v_i1, v_k], inp1[v_k, v_i2]) + T.writes(matmul[v_i0, v_i1, v_i2]) + with T.init(): + matmul[v_i0, v_i1, v_i2] = T.float32(0) + matmul[v_i0, v_i1, v_i2] = matmul[v_i0, v_i1, v_i2] + inp0[v_i0, v_i1, v_k] * inp1[v_k, v_i2] + +def matmul2_sch_func(): + sch = tvm.tir.Schedule(matmul2_before) + b0 = sch.get_block("matmul") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + b0 = sch.get_block(name="matmul", func_name="main") + b1 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l2, l3, l4, l5 = sch.get_loops(block=b0) + v6, v7, v8, v9, v10 = sch.sample_perfect_tile(loop=l2, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l11, l12, l13, l14, l15 = sch.split(loop=l2, factors=[v6, v7, v8, v9, v10], preserve_unit_iters=True) + v16, v17, v18, v19, v20 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[2, 2, 2, 4, 1]) + l21, l22, l23, l24, l25 = sch.split(loop=l3, factors=[v16, v17, v18, v19, v20], preserve_unit_iters=True) + v26, v27, v28, v29, v30 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[128, 2, 16, 1, 1]) + l31, l32, l33, l34, l35 = sch.split(loop=l4, factors=[v26, v27, v28, v29, v30], preserve_unit_iters=True) + v36, v37, v38 = sch.sample_perfect_tile(loop=l5, n=3, max_innermost_factor=64, decision=[512, 4, 2]) + l39, l40, l41 = sch.split(loop=l5, factors=[v36, v37, v38], preserve_unit_iters=True) + sch.reorder(l11, l21, l31, l12, l22, l32, l13, l23, l33, l39, l40, l14, l24, l34, l41, l15, l25, l35) + l42 = sch.fuse(l11, l21, l31, preserve_unit_iters=True) + sch.bind(loop=l42, thread_axis="blockIdx.x") + l43 = sch.fuse(l12, l22, l32, preserve_unit_iters=True) + sch.bind(loop=l43, thread_axis="vthread.x") + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b45 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b45, loop=l44, preserve_unit_loops=True, index=-1) + b46 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b46, loop=l39, preserve_unit_loops=True, index=-1) + _, l47, l48, l49, l50, l51, l52, l53 = sch.get_loops(block=b46) + l54 = sch.fuse(l51, l52, l53, preserve_unit_iters=True) + v55 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b46, ann_key="meta_schedule.cooperative_fetch", ann_val=v55) + b56 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b56, loop=l39, preserve_unit_loops=True, index=-1) + _, l57, l58, l59, l60, l61, l62 = sch.get_loops(block=b56) + l63 = sch.fuse(l61, l62, preserve_unit_iters=True) + v64 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b56, ann_key="meta_schedule.cooperative_fetch", ann_val=v64) + v65 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=4) + sch.annotate(block_or_loop=b1, ann_key="meta_schedule.unroll_explicit", ann_val=v65) + sch.enter_postproc() + sch.unannotate(block_or_loop=b46, ann_key="meta_schedule.cooperative_fetch") + _, l66, l67, l68, l69, l70 = sch.get_loops(block=b46) + l71, l72, l73 = sch.split(loop=l70, factors=[None, 32, 2], preserve_unit_iters=True) + sch.vectorize(loop=l73) + sch.bind(loop=l72, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b56, ann_key="meta_schedule.cooperative_fetch") + _, l74, l75, l76, l77, l78 = sch.get_loops(block=b56) + l79, l80, l81 = sch.split(loop=l78, factors=[None, 32, 2], preserve_unit_iters=True) + sch.vectorize(loop=l81) + sch.bind(loop=l80, thread_axis="threadIdx.x") + b82 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b82, ann_key="meta_schedule.unroll_explicit") + _, b83, b84, b85, b86, _ = sch.get_child_blocks(b82) + _, l87, l88, l89, l90, l91, l92, l93 = sch.get_loops(block=b83) + sch.annotate(block_or_loop=l87, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l87, ann_key="pragma_unroll_explicit", ann_val=1) + _, l94, l95, l96, l97, l98, l99, l100 = sch.get_loops(block=b84) + sch.annotate(block_or_loop=l94, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l94, ann_key="pragma_unroll_explicit", ann_val=1) + _, l101, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112 = sch.get_loops(block=b85) + sch.annotate(block_or_loop=l101, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l101, ann_key="pragma_unroll_explicit", ann_val=1) + _, l113, l114, l115, l116, l117, l118 = sch.get_loops(block=b86) + sch.annotate(block_or_loop=l113, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l113, ann_key="pragma_unroll_explicit", ann_val=1) + b119 = sch.get_block(name="matmul", func_name="main") + _, l120, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131 = sch.get_loops(block=b119) + b132 = sch.decompose_reduction(block=b119, loop=l123) + b1 = sch.get_block("inp0_pad") + sch.compute_inline(b1) + b2 = sch.get_block("matmul_pad") + sch.reverse_compute_inline(b2) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func +def matmul5_before(var_rxplaceholder: T.handle, var_rxplaceholder_1: T.handle, var_matmul: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + rxplaceholder = T.match_buffer(var_rxplaceholder, (T.int64(1), T.int64(32), n, n)) + rxplaceholder_1 = T.match_buffer(var_rxplaceholder_1, (T.int64(1), T.int64(32), n, T.int64(128))) + matmul_1 = T.match_buffer(var_matmul, (T.int64(1), T.int64(32), n, T.int64(128))) + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, T.int64(128), n): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(rxplaceholder[T.int64(0), v_i1, v_i2, v_k], rxplaceholder_1[T.int64(0), v_i1, v_k, v_i3]) + T.writes(matmul_1[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + matmul_1[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + matmul_1[v_i0, v_i1, v_i2, v_i3] = matmul_1[v_i0, v_i1, v_i2, v_i3] + rxplaceholder[T.int64(0), v_i1, v_i2, v_k] * rxplaceholder_1[T.int64(0), v_i1, v_k, v_i3] + + +@T.prim_func +def matmul5_after(var_rxplaceholder: T.handle, var_rxplaceholder_1: T.handle, var_matmul: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + rxplaceholder = T.match_buffer(var_rxplaceholder, (T.int64(1), T.int64(32), n, n)) + rxplaceholder_1 = T.match_buffer(var_rxplaceholder_1, (T.int64(1), T.int64(32), n, T.int64(128))) + matmul = T.match_buffer(var_matmul, (T.int64(1), T.int64(32), n, T.int64(128))) + # with T.block("root"): + C_pad = T.alloc_buffer((T.int64(1), T.int64(32), (n + T.int64(128) - T.int64(1)) // T.int64(128) * T.int64(128), T.int64(128))) + C_pad_local = T.alloc_buffer((T.int64(1), T.int64(32), (n + T.int64(127)) // T.int64(128) * T.int64(128), T.int64(128)), scope="local") + A_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), (n + T.int64(127)) // T.int64(128) * T.int64(128), (n + T.int64(127)) // T.int64(128) * T.int64(128)), scope="shared") + B_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), (n + T.int64(127)) // T.int64(128) * T.int64(128), T.int64(128)), scope="shared") + for i2_0 in range((n + T.int64(127)) // T.int64(128)): + for i0_0_i1_0_i2_1_0_i3_0_fused in T.thread_binding(T.int64(256), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 1024, "pragma_unroll_explicit": 1}): + for i0_1_i1_1_i2_1_1_i3_1_fused in T.thread_binding(T.int64(4), thread="vthread.x"): + for i0_2_i1_2_i2_1_2_i3_2_fused in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for i0_3_init, i1_3_init, i2_1_3_init, i3_3_init, i0_4_init, i1_4_init, i2_1_4_init, i3_4_init in T.grid(T.int64(1), T.int64(1), T.int64(1), T.int64(2), T.int64(1), T.int64(1), T.int64(4), T.int64(1)): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), i0_3_init + i0_4_init) + v_i1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_0_fused // T.int64(8) + i1_3_init + i1_4_init) + v_i2 = T.axis.spatial((n + T.int64(128) - T.int64(1)) // T.int64(128) * T.int64(128), i2_0 * T.int64(128) + i0_0_i1_0_i2_1_0_i3_0_fused % T.int64(8) // T.int64(2) * T.int64(32) + i0_1_i1_1_i2_1_1_i3_1_fused // T.int64(2) * T.int64(16) + i0_2_i1_2_i2_1_2_i3_2_fused // T.int64(16) * T.int64(4) + i2_1_3_init * T.int64(4) + i2_1_4_init) + v_i3 = T.axis.spatial(T.int64(128), i0_0_i1_0_i2_1_0_i3_0_fused % T.int64(2) * T.int64(64) + i0_1_i1_1_i2_1_1_i3_1_fused % T.int64(2) * T.int64(32) + i0_2_i1_2_i2_1_2_i3_2_fused % T.int64(16) * T.int64(2) + i3_3_init + i3_4_init) + T.reads() + T.writes(C_pad_local[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"meta_schedule.tiling_structure": "SSSRRSRS"}) + C_pad_local[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + for k_0, k_1_0 in T.grid((n + T.int64(127)) // T.int64(128), T.int64(16)): + for ax0_ax1_ax2_ax3_fused_0 in range(T.int64(1)): + for ax0_ax1_ax2_ax3_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_ax2_ax3_fused_2 in T.vectorized(T.int64(4)): + with T.block("A_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_0_fused // T.int64(8)) + v2 = T.axis.spatial((n + T.int64(127)) // T.int64(128) * T.int64(128), i2_0 * T.int64(128) + i0_0_i1_0_i2_1_0_i3_0_fused % T.int64(8) // T.int64(2) * T.int64(32) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(256) + ax0_ax1_ax2_ax3_fused_1 * T.int64(4) + ax0_ax1_ax2_ax3_fused_2) // T.int64(8)) + v3 = T.axis.spatial((n + T.int64(127)) // T.int64(128) * T.int64(128), k_0 * T.int64(128) + k_1_0 * T.int64(8) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(256) + ax0_ax1_ax2_ax3_fused_1 * T.int64(4) + ax0_ax1_ax2_ax3_fused_2) % T.int64(8)) + T.reads(rxplaceholder[v0, v1, v2, v3]) + T.writes(A_pad_shared[v0, v1, v2, v3]) + A_pad_shared[v0, v1, v2, v3] = T.if_then_else(v2 < n and v3 < n, rxplaceholder[v0, v1, v2, v3], T.float32(0)) + for ax0_ax1_ax2_ax3_fused_0 in range(T.int64(4)): + for ax0_ax1_ax2_ax3_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_ax2_ax3_fused_2 in T.vectorized(T.int64(2)): + with T.block("B_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_0_fused // T.int64(8)) + v2 = T.axis.spatial((n + T.int64(127)) // T.int64(128) * T.int64(128), k_0 * T.int64(128) + k_1_0 * T.int64(8) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) // T.int64(64)) + v3 = T.axis.spatial(T.int64(128), i0_0_i1_0_i2_1_0_i3_0_fused % T.int64(2) * T.int64(64) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) % T.int64(64)) + T.reads(rxplaceholder_1[v0, v1, v2, v3]) + T.writes(B_pad_shared[v0, v1, v2, v3]) + B_pad_shared[v0, v1, v2, v3] = T.if_then_else(v2 < n, rxplaceholder_1[v0, v1, v2, v3], T.float32(0)) + for k_1_1, i0_3, i1_3, i2_1_3, i3_3, k_1_2, i0_4, i1_4, i2_1_4, i3_4 in T.grid(T.int64(2), T.int64(1), T.int64(1), T.int64(1), T.int64(2), T.int64(4), T.int64(1), T.int64(1), T.int64(4), T.int64(1)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), i0_3 + i0_4) + v_i1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_0_fused // T.int64(8) + i1_3 + i1_4) + v_i2 = T.axis.spatial((n + T.int64(128) - T.int64(1)) // T.int64(128) * T.int64(128), i2_0 * T.int64(128) + i0_0_i1_0_i2_1_0_i3_0_fused % T.int64(8) // T.int64(2) * T.int64(32) + i0_1_i1_1_i2_1_1_i3_1_fused // T.int64(2) * T.int64(16) + i0_2_i1_2_i2_1_2_i3_2_fused // T.int64(16) * T.int64(4) + i2_1_3 * T.int64(4) + i2_1_4) + v_i3 = T.axis.spatial(T.int64(128), i0_0_i1_0_i2_1_0_i3_0_fused % T.int64(2) * T.int64(64) + i0_1_i1_1_i2_1_1_i3_1_fused % T.int64(2) * T.int64(32) + i0_2_i1_2_i2_1_2_i3_2_fused % T.int64(16) * T.int64(2) + i3_3 + i3_4) + v_k = T.axis.reduce((n + T.int64(128) - T.int64(1)) // T.int64(128) * T.int64(128), k_0 * T.int64(128) + k_1_0 * T.int64(8) + k_1_1 * T.int64(4) + k_1_2) + T.reads(C_pad_local[v_i0, v_i1, v_i2, v_i3], A_pad_shared[T.int64(0), v_i1, v_i2, v_k], B_pad_shared[T.int64(0), v_i1, v_k, v_i3]) + T.writes(C_pad_local[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"meta_schedule.tiling_structure": "SSSRRSRS"}) + C_pad_local[v_i0, v_i1, v_i2, v_i3] = C_pad_local[v_i0, v_i1, v_i2, v_i3] + A_pad_shared[T.int64(0), v_i1, v_i2, v_k] * B_pad_shared[T.int64(0), v_i1, v_k, v_i3] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), T.int64(4), T.int64(2)): + with T.block("C_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_0_fused // T.int64(8) + ax1) + v2 = T.axis.spatial((n + T.int64(127)) // T.int64(128) * T.int64(128), i2_0 * T.int64(128) + i0_0_i1_0_i2_1_0_i3_0_fused % T.int64(8) // T.int64(2) * T.int64(32) + i0_1_i1_1_i2_1_1_i3_1_fused // T.int64(2) * T.int64(16) + i0_2_i1_2_i2_1_2_i3_2_fused // T.int64(16) * T.int64(4) + ax2) + v3 = T.axis.spatial(T.int64(128), i0_0_i1_0_i2_1_0_i3_0_fused % T.int64(2) * T.int64(64) + i0_1_i1_1_i2_1_1_i3_1_fused % T.int64(2) * T.int64(32) + i0_2_i1_2_i2_1_2_i3_2_fused % T.int64(16) * T.int64(2) + ax3) + T.reads(C_pad_local[v0, v1, v2, v3]) + T.writes(C_pad[v0, v1, v2, v3]) + C_pad[v0, v1, v2, v3] = C_pad_local[v0, v1, v2, v3] + for i0 in T.thread_binding(T.int64(1), thread="blockIdx.x"): + for i1 in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for i2, i3 in T.grid(n, T.int64(128)): + with T.block("C_pad"): + vi0, vi1, vi2, vi3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(C_pad[vi0, vi1, vi2, vi3]) + T.writes(matmul[vi0, vi1, vi2, vi3]) + matmul[vi0, vi1, vi2, vi3] = C_pad[vi0, vi1, vi2, vi3] + +@T.prim_func +def matmul5_with_m_before(var_rxplaceholder: T.handle, var_rxplaceholder_1: T.handle, var_matmul: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n, m = T.int64(), T.int64() + A = T.match_buffer(var_rxplaceholder, (T.int64(1), T.int64(32), n, m)) + B = T.match_buffer(var_rxplaceholder_1, (T.int64(1), T.int64(32), m, T.int64(128))) + matmul = T.match_buffer(var_matmul, (T.int64(1), T.int64(32), n, T.int64(128))) + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, T.int64(128), m): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(A[v_i0, v_i1, v_i2, v_k], B[v_i0, v_i1, v_k, v_i3]) + T.writes(matmul[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + matmul[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + matmul[v_i0, v_i1, v_i2, v_i3] = matmul[v_i0, v_i1, v_i2, v_i3] + A[v_i0, v_i1, v_i2, v_k] * B[v_i0, v_i1, v_k, v_i3] + + +@T.prim_func +def matmul5_with_m_after(var_rxplaceholder: T.handle, var_rxplaceholder_1: T.handle, var_matmul: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + m = T.int64() + rxplaceholder = T.match_buffer(var_rxplaceholder, (T.int64(1), T.int64(32), n, m)) + rxplaceholder_1 = T.match_buffer(var_rxplaceholder_1, (T.int64(1), T.int64(32), m, T.int64(128))) + matmul = T.match_buffer(var_matmul, (T.int64(1), T.int64(32), n, T.int64(128))) + # with T.block("root"): + C_pad = T.alloc_buffer((T.int64(1), T.int64(32), (n + T.int64(128) - T.int64(1)) // T.int64(128) * T.int64(128), T.int64(128))) + C_pad_local = T.alloc_buffer((T.int64(1), T.int64(32), (n + T.int64(127)) // T.int64(128) * T.int64(128), T.int64(128)), scope="local") + A_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), (n + T.int64(127)) // T.int64(128) * T.int64(128), (m + T.int64(127)) // T.int64(128) * T.int64(128)), scope="shared") + B_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), (m + T.int64(127)) // T.int64(128) * T.int64(128), T.int64(128)), scope="shared") + for i2_0 in range((n + T.int64(127)) // T.int64(128)): + for i0_0_i1_0_i2_1_0_i3_0_fused in T.thread_binding(T.int64(256), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 1024, "pragma_unroll_explicit": 1}): + for i0_1_i1_1_i2_1_1_i3_1_fused in T.thread_binding(T.int64(4), thread="vthread.x"): + for i0_2_i1_2_i2_1_2_i3_2_fused in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for i0_3_init, i1_3_init, i2_1_3_init, i3_3_init, i0_4_init, i1_4_init, i2_1_4_init, i3_4_init in T.grid(T.int64(1), T.int64(1), T.int64(1), T.int64(2), T.int64(1), T.int64(1), T.int64(4), T.int64(1)): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), i0_3_init + i0_4_init) + v_i1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_0_fused // T.int64(8) + i1_3_init + i1_4_init) + v_i2 = T.axis.spatial((n + T.int64(128) - T.int64(1)) // T.int64(128) * T.int64(128), i2_0 * T.int64(128) + i0_0_i1_0_i2_1_0_i3_0_fused % T.int64(8) // T.int64(2) * T.int64(32) + i0_1_i1_1_i2_1_1_i3_1_fused // T.int64(2) * T.int64(16) + i0_2_i1_2_i2_1_2_i3_2_fused // T.int64(16) * T.int64(4) + i2_1_3_init * T.int64(4) + i2_1_4_init) + v_i3 = T.axis.spatial(T.int64(128), i0_0_i1_0_i2_1_0_i3_0_fused % T.int64(2) * T.int64(64) + i0_1_i1_1_i2_1_1_i3_1_fused % T.int64(2) * T.int64(32) + i0_2_i1_2_i2_1_2_i3_2_fused % T.int64(16) * T.int64(2) + i3_3_init + i3_4_init) + T.reads() + T.writes(C_pad_local[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"meta_schedule.tiling_structure": "SSSRRSRS"}) + C_pad_local[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + for k_0, k_1_0 in T.grid((m + T.int64(127)) // T.int64(128), T.int64(16)): + for ax0_ax1_ax2_ax3_fused_0 in range(T.int64(1)): + for ax0_ax1_ax2_ax3_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_ax2_ax3_fused_2 in T.vectorized(T.int64(4)): + with T.block("A_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_0_fused // T.int64(8)) + v2 = T.axis.spatial((n + T.int64(127)) // T.int64(128) * T.int64(128), i2_0 * T.int64(128) + i0_0_i1_0_i2_1_0_i3_0_fused % T.int64(8) // T.int64(2) * T.int64(32) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(256) + ax0_ax1_ax2_ax3_fused_1 * T.int64(4) + ax0_ax1_ax2_ax3_fused_2) // T.int64(8)) + v3 = T.axis.spatial((m + T.int64(127)) // T.int64(128) * T.int64(128), k_0 * T.int64(128) + k_1_0 * T.int64(8) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(256) + ax0_ax1_ax2_ax3_fused_1 * T.int64(4) + ax0_ax1_ax2_ax3_fused_2) % T.int64(8)) + T.reads(rxplaceholder[v0, v1, v2, v3]) + T.writes(A_pad_shared[v0, v1, v2, v3]) + A_pad_shared[v0, v1, v2, v3] = T.if_then_else(v2 < n and v3 < m, rxplaceholder[v0, v1, v2, v3], T.float32(0)) + for ax0_ax1_ax2_ax3_fused_0 in range(T.int64(4)): + for ax0_ax1_ax2_ax3_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_ax2_ax3_fused_2 in T.vectorized(T.int64(2)): + with T.block("B_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_0_fused // T.int64(8)) + v2 = T.axis.spatial((m + T.int64(127)) // T.int64(128) * T.int64(128), k_0 * T.int64(128) + k_1_0 * T.int64(8) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) // T.int64(64)) + v3 = T.axis.spatial(T.int64(128), i0_0_i1_0_i2_1_0_i3_0_fused % T.int64(2) * T.int64(64) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) % T.int64(64)) + T.reads(rxplaceholder_1[v0, v1, v2, v3]) + T.writes(B_pad_shared[v0, v1, v2, v3]) + B_pad_shared[v0, v1, v2, v3] = T.if_then_else(v2 < m, rxplaceholder_1[v0, v1, v2, v3], T.float32(0)) + for k_1_1, i0_3, i1_3, i2_1_3, i3_3, k_1_2, i0_4, i1_4, i2_1_4, i3_4 in T.grid(T.int64(2), T.int64(1), T.int64(1), T.int64(1), T.int64(2), T.int64(4), T.int64(1), T.int64(1), T.int64(4), T.int64(1)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), i0_3 + i0_4) + v_i1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_0_fused // T.int64(8) + i1_3 + i1_4) + v_i2 = T.axis.spatial((n + T.int64(128) - T.int64(1)) // T.int64(128) * T.int64(128), i2_0 * T.int64(128) + i0_0_i1_0_i2_1_0_i3_0_fused % T.int64(8) // T.int64(2) * T.int64(32) + i0_1_i1_1_i2_1_1_i3_1_fused // T.int64(2) * T.int64(16) + i0_2_i1_2_i2_1_2_i3_2_fused // T.int64(16) * T.int64(4) + i2_1_3 * T.int64(4) + i2_1_4) + v_i3 = T.axis.spatial(T.int64(128), i0_0_i1_0_i2_1_0_i3_0_fused % T.int64(2) * T.int64(64) + i0_1_i1_1_i2_1_1_i3_1_fused % T.int64(2) * T.int64(32) + i0_2_i1_2_i2_1_2_i3_2_fused % T.int64(16) * T.int64(2) + i3_3 + i3_4) + v_k = T.axis.reduce((m + T.int64(128) - T.int64(1)) // T.int64(128) * T.int64(128), k_0 * T.int64(128) + k_1_0 * T.int64(8) + k_1_1 * T.int64(4) + k_1_2) + T.reads(C_pad_local[v_i0, v_i1, v_i2, v_i3], A_pad_shared[T.int64(0), v_i1, v_i2, v_k], B_pad_shared[T.int64(0), v_i1, v_k, v_i3]) + T.writes(C_pad_local[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"meta_schedule.tiling_structure": "SSSRRSRS"}) + C_pad_local[v_i0, v_i1, v_i2, v_i3] = C_pad_local[v_i0, v_i1, v_i2, v_i3] + A_pad_shared[T.int64(0), v_i1, v_i2, v_k] * B_pad_shared[T.int64(0), v_i1, v_k, v_i3] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), T.int64(4), T.int64(2)): + with T.block("C_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_0_fused // T.int64(8) + ax1) + v2 = T.axis.spatial((n + T.int64(127)) // T.int64(128) * T.int64(128), i2_0 * T.int64(128) + i0_0_i1_0_i2_1_0_i3_0_fused % T.int64(8) // T.int64(2) * T.int64(32) + i0_1_i1_1_i2_1_1_i3_1_fused // T.int64(2) * T.int64(16) + i0_2_i1_2_i2_1_2_i3_2_fused // T.int64(16) * T.int64(4) + ax2) + v3 = T.axis.spatial(T.int64(128), i0_0_i1_0_i2_1_0_i3_0_fused % T.int64(2) * T.int64(64) + i0_1_i1_1_i2_1_1_i3_1_fused % T.int64(2) * T.int64(32) + i0_2_i1_2_i2_1_2_i3_2_fused % T.int64(16) * T.int64(2) + ax3) + T.reads(C_pad_local[v0, v1, v2, v3]) + T.writes(C_pad[v0, v1, v2, v3]) + C_pad[v0, v1, v2, v3] = C_pad_local[v0, v1, v2, v3] + for i0 in T.thread_binding(T.int64(1), thread="blockIdx.x"): + for i1 in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for i2, i3 in T.grid(n, T.int64(128)): + with T.block("C_pad"): + vi0, vi1, vi2, vi3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(C_pad[vi0, vi1, vi2, vi3]) + T.writes(matmul[vi0, vi1, vi2, vi3]) + matmul[vi0, vi1, vi2, vi3] = C_pad[vi0, vi1, vi2, vi3] + + +@T.prim_func +def NT_matmul_before(var_rxplaceholder: T.handle, rxplaceholder: T.Buffer((T.int64(4096), T.int64(4096)), "float32"), var_NT_matmul: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + rxplaceholder_1 = T.match_buffer(var_rxplaceholder, (T.int64(1), n, T.int64(4096))) + NT_matmul = T.match_buffer(var_NT_matmul, (T.int64(1), n, T.int64(4096))) + # with T.block("root"): + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(4096), T.int64(4096)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(rxplaceholder_1[v_i0, v_i1, v_k], rxplaceholder[v_i2, v_k]) + T.writes(NT_matmul[v_i0, v_i1, v_i2]) + with T.init(): + NT_matmul[v_i0, v_i1, v_i2] = T.float32(0) + NT_matmul[v_i0, v_i1, v_i2] = NT_matmul[v_i0, v_i1, v_i2] + rxplaceholder_1[v_i0, v_i1, v_k] * rxplaceholder[v_i2, v_k] + + +@T.prim_func +def NT_matmul_after(var_rxplaceholder: T.handle, rxplaceholder: T.Buffer((T.int64(4096), T.int64(4096)), "float32"), var_NT_matmul: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + rxplaceholder_1 = T.match_buffer(var_rxplaceholder, (T.int64(1), n, T.int64(4096))) + NT_matmul_1 = T.match_buffer(var_NT_matmul, (T.int64(1), n, T.int64(4096))) + # with T.block("root"): + for i1_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.y"): + with T.block("NT_matmul_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i1_0) + T.reads(rxplaceholder_1[T.Add(v_i0, T.int64(0)), v_i1_o * T.int64(32):v_i1_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(4096)], rxplaceholder[T.int64(0):T.int64(4096), T.int64(0):T.int64(4096)]) + T.writes(NT_matmul_1[v_i0, v_i1_o * T.int64(32):v_i1_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(4096)]) + C_pad_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(4096)), scope="local") + A_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(4096)), scope="shared") + rxplaceholder_shared = T.alloc_buffer((T.int64(4096), T.int64(4096)), scope="shared") + for i0_0_i1_1_0_i2_0_fused in T.thread_binding(T.int64(128), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i0_1_i1_1_1_i2_1_fused in T.thread_binding(T.int64(1), thread="vthread.x"): + for i0_2_i1_1_2_i2_2_fused in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for i1_1_3_init, i2_3_init, i1_1_4_init, i2_4_init in T.grid(T.int64(1), T.int64(2), T.int64(4), T.int64(2)): + with T.block("NT_matmul_init"): + v_i1_i = T.axis.spatial(T.int64(32), i0_2_i1_1_2_i2_2_fused // T.int64(8) * T.int64(4) + i1_1_3_init * T.int64(4) + i1_1_4_init) + v_i2_i = T.axis.spatial(T.int64(4096), i0_0_i1_1_0_i2_0_fused * T.int64(32) + i0_2_i1_1_2_i2_2_fused % T.int64(8) * T.int64(4) + i2_3_init * T.int64(2) + i2_4_init) + T.reads() + T.writes(C_pad_local[T.int64(0), v_i1_i, v_i2_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + C_pad_local[T.int64(0), v_i1_i, v_i2_i] = T.float32(0) + for k_0 in range(T.int64(128)): + for ax0_ax1_ax2_fused_0 in range(T.int64(8)): + for ax0_ax1_ax2_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_ax2_fused_2 in T.vectorized(T.int64(2)): + with T.block("A_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), (ax0_ax1_ax2_fused_0 * T.int64(128) + ax0_ax1_ax2_fused_1 * T.int64(2) + ax0_ax1_ax2_fused_2) // T.int64(32)) + v2 = T.axis.spatial(T.int64(4096), k_0 * T.int64(32) + (ax0_ax1_ax2_fused_0 * T.int64(128) + ax0_ax1_ax2_fused_1 * T.int64(2) + ax0_ax1_ax2_fused_2) % T.int64(32)) + T.reads(rxplaceholder_1[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2]) + T.writes(A_pad_shared[v0, v1, v2]) + A_pad_shared[v0, v1, v2] = T.if_then_else(v_i1_o * T.int64(32) + v1 < n, rxplaceholder_1[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2], T.float32(0)) + for ax0_ax1_fused_0 in range(T.int64(8)): + for ax0_ax1_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_fused_2 in T.vectorized(T.int64(2)): + with T.block("rxplaceholder_shared"): + v0 = T.axis.spatial(T.int64(4096), i0_0_i1_1_0_i2_0_fused * T.int64(32) + (ax0_ax1_fused_0 * T.int64(128) + ax0_ax1_fused_1 * T.int64(2) + ax0_ax1_fused_2) // T.int64(32)) + v1 = T.axis.spatial(T.int64(4096), k_0 * T.int64(32) + (ax0_ax1_fused_0 * T.int64(128) + ax0_ax1_fused_1 * T.int64(2) + ax0_ax1_fused_2) % T.int64(32)) + T.reads(rxplaceholder[v0, v1]) + T.writes(rxplaceholder_shared[v0, v1]) + rxplaceholder_shared[v0, v1] = rxplaceholder[v0, v1] + for k_1, i0_3, i1_1_3, i2_3, k_2, i0_4, i1_1_4, i2_4 in T.grid(T.int64(8), T.int64(1), T.int64(1), T.int64(2), T.int64(4), T.int64(1), T.int64(4), T.int64(2)): + with T.block("NT_matmul_update"): + v_i1_i = T.axis.spatial(T.int64(32), i0_2_i1_1_2_i2_2_fused // T.int64(8) * T.int64(4) + i1_1_3 * T.int64(4) + i1_1_4) + v_i2_i = T.axis.spatial(T.int64(4096), i0_0_i1_1_0_i2_0_fused * T.int64(32) + i0_2_i1_1_2_i2_2_fused % T.int64(8) * T.int64(4) + i2_3 * T.int64(2) + i2_4) + v_k_i = T.axis.reduce(T.int64(4096), k_0 * T.int64(32) + k_1 * T.int64(4) + k_2) + T.reads(C_pad_local[T.int64(0), v_i1_i, v_i2_i], A_pad_shared[T.int64(0), v_i1_i, v_k_i], rxplaceholder_shared[v_i2_i, v_k_i]) + T.writes(C_pad_local[T.int64(0), v_i1_i, v_i2_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + C_pad_local[T.int64(0), v_i1_i, v_i2_i] = C_pad_local[T.int64(0), v_i1_i, v_i2_i] + A_pad_shared[T.int64(0), v_i1_i, v_k_i] * rxplaceholder_shared[v_i2_i, v_k_i] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(4), T.int64(4)): + with T.block("C_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(32), i0_2_i1_1_2_i2_2_fused // T.int64(8) * T.int64(4) + ax1) + v2 = T.axis.spatial(T.int64(4096), i0_0_i1_1_0_i2_0_fused * T.int64(32) + i0_2_i1_1_2_i2_2_fused % T.int64(8) * T.int64(4) + ax2) + T.reads(C_pad_local[v0, v1, v2]) + T.writes(NT_matmul_1[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2]) + # if T.int64(0) <= v_i0 and v_i0 < T.int64(1) and T.int64(0) <= v_i1_o * T.int64(32) + v1 and v_i1_o * T.int64(32) + v1 < n: + if v_i1_o * T.int64(32) + v1 < n: + NT_matmul_1[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2] = C_pad_local[v0, v1, v2] + + +@T.prim_func +def NT_matmul4_before(var_rxplaceholder: T.handle, rxplaceholder: T.Buffer((T.int64(32000), T.int64(4096)), "float32"), var_NT_matmul: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + rxplaceholder_1 = T.match_buffer(var_rxplaceholder, (T.int64(1), n, T.int64(4096))) + NT_matmul = T.match_buffer(var_NT_matmul, (T.int64(1), n, T.int64(32000))) + # with T.block("root"): + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(32000), T.int64(4096)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(rxplaceholder_1[v_i0, v_i1, v_k], rxplaceholder[v_i2, v_k]) + T.writes(NT_matmul[v_i0, v_i1, v_i2]) + with T.init(): + NT_matmul[v_i0, v_i1, v_i2] = T.float32(0) + NT_matmul[v_i0, v_i1, v_i2] = NT_matmul[v_i0, v_i1, v_i2] + rxplaceholder_1[v_i0, v_i1, v_k] * rxplaceholder[v_i2, v_k] + + +def NT_matmul4_sch_func(): + sch = tvm.tir.Schedule(NT_matmul4_before) + b0 = sch.get_block("NT_matmul") + sch.pad_einsum(b0, [1, 32, 256, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + b0 = sch.get_block(name="NT_matmul", func_name="main") + b1 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l2, l3, l4, l5 = sch.get_loops(block=b0) + v6, v7, v8, v9, v10 = sch.sample_perfect_tile(loop=l2, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l11, l12, l13, l14, l15 = sch.split(loop=l2, factors=[v6, v7, v8, v9, v10], preserve_unit_iters=True) + v16, v17, v18, v19, v20 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[1, 1, 8, 4, 1]) + l21, l22, l23, l24, l25 = sch.split(loop=l3, factors=[v16, v17, v18, v19, v20], preserve_unit_iters=True) + v26, v27, v28, v29, v30 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[668, 1, 8, 1, 6]) + l31, l32, l33, l34, l35 = sch.split(loop=l4, factors=[v26, v27, v28, v29, v30], preserve_unit_iters=True) + v36, v37, v38 = sch.sample_perfect_tile(loop=l5, n=3, max_innermost_factor=64, decision=[128, 4, 8]) + l39, l40, l41 = sch.split(loop=l5, factors=[v36, v37, v38], preserve_unit_iters=True) + sch.reorder(l11, l21, l31, l12, l22, l32, l13, l23, l33, l39, l40, l14, l24, l34, l41, l15, l25, l35) + l42 = sch.fuse(l11, l21, l31, preserve_unit_iters=True) + sch.bind(loop=l42, thread_axis="blockIdx.x") + l43 = sch.fuse(l12, l22, l32, preserve_unit_iters=True) + sch.bind(loop=l43, thread_axis="vthread.x") + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b45 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b45, loop=l44, preserve_unit_loops=True, index=-1) + b46 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b46, loop=l39, preserve_unit_loops=True, index=-1) + _, l47, l48, l49, l50, l51, l52, l53 = sch.get_loops(block=b46) + l54 = sch.fuse(l51, l52, l53, preserve_unit_iters=True) + v55 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=3) + sch.annotate(block_or_loop=b46, ann_key="meta_schedule.cooperative_fetch", ann_val=v55) + b56 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b56, loop=l39, preserve_unit_loops=True, index=-1) + _, l57, l58, l59, l60, l61, l62 = sch.get_loops(block=b56) + l63 = sch.fuse(l61, l62, preserve_unit_iters=True) + v64 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=3) + sch.annotate(block_or_loop=b56, ann_key="meta_schedule.cooperative_fetch", ann_val=v64) + v65 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=3) + sch.annotate(block_or_loop=b1, ann_key="meta_schedule.unroll_explicit", ann_val=v65) + sch.enter_postproc() + sch.unannotate(block_or_loop=b46, ann_key="meta_schedule.cooperative_fetch") + _, l66, l67, l68, l69, l70 = sch.get_loops(block=b46) + l71, l72, l73 = sch.split(loop=l70, factors=[None, 64, 4], preserve_unit_iters=True) + sch.vectorize(loop=l73) + sch.bind(loop=l72, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b56, ann_key="meta_schedule.cooperative_fetch") + _, l74, l75, l76, l77, l78 = sch.get_loops(block=b56) + l79, l80, l81 = sch.split(loop=l78, factors=[None, 64, 4], preserve_unit_iters=True) + sch.vectorize(loop=l81) + sch.bind(loop=l80, thread_axis="threadIdx.x") + b82 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b82, ann_key="meta_schedule.unroll_explicit") + _, b83, b84, b85, b86, _ = sch.get_child_blocks(b82) + _, l87, l88, l89, l90, l91, l92, l93 = sch.get_loops(block=b83) + sch.annotate(block_or_loop=l87, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l87, ann_key="pragma_unroll_explicit", ann_val=1) + _, l94, l95, l96, l97, l98, l99, l100 = sch.get_loops(block=b84) + sch.annotate(block_or_loop=l94, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l94, ann_key="pragma_unroll_explicit", ann_val=1) + _, l101, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112 = sch.get_loops(block=b85) + sch.annotate(block_or_loop=l101, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l101, ann_key="pragma_unroll_explicit", ann_val=1) + _, l113, l114, l115, l116, l117, l118 = sch.get_loops(block=b86) + sch.annotate(block_or_loop=l113, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l113, ann_key="pragma_unroll_explicit", ann_val=1) + b119 = sch.get_block(name="NT_matmul", func_name="main") + _, l120, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131 = sch.get_loops(block=b119) + b132 = sch.decompose_reduction(block=b119, loop=l123) + b1 = sch.get_block("rxplaceholder_1_pad") + sch.compute_inline(b1) + b3 = sch.get_block("NT_matmul_pad") + sch.reverse_compute_inline(b3) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func +def NT_matmul9_before(rxplaceholder: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32"), rxplaceholder_1: T.Buffer((T.int64(32000), T.int64(4096)), "float32"), NT_matmul: T.Buffer((T.int64(1), T.int64(1), T.int64(32000)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(32000), T.int64(4096)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(rxplaceholder[v_i0, v_i1, v_k], rxplaceholder_1[v_i2, v_k]) + T.writes(NT_matmul[v_i0, v_i1, v_i2]) + with T.init(): + NT_matmul[v_i0, v_i1, v_i2] = T.float32(0) + NT_matmul[v_i0, v_i1, v_i2] = NT_matmul[v_i0, v_i1, v_i2] + rxplaceholder[v_i0, v_i1, v_k] * rxplaceholder_1[v_i2, v_k] + + +def NT_matmul9_sch_func(): + sch = tvm.tir.Schedule(NT_matmul9_before) + b0 = sch.get_block(name="NT_matmul", func_name="main") + b1 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + l2, l3, l4, l5 = sch.get_loops(block=b0) + v6, v7, v8, v9, v10 = sch.sample_perfect_tile(loop=l2, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l11, l12, l13, l14, l15 = sch.split(loop=l2, factors=[v6, v7, v8, v9, v10], preserve_unit_iters=True) + v16, v17, v18, v19, v20 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l21, l22, l23, l24, l25 = sch.split(loop=l3, factors=[v16, v17, v18, v19, v20], preserve_unit_iters=True) + v26, v27, v28, v29, v30 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[668, 1, 48, 1, 1]) + l31, l32, l33, l34, l35 = sch.split(loop=l4, factors=[v26, v27, v28, v29, v30], preserve_unit_iters=True) + v36, v37, v38 = sch.sample_perfect_tile(loop=l5, n=3, max_innermost_factor=64, decision=[64, 64, 1]) + l39, l40, l41 = sch.split(loop=l5, factors=[v36, v37, v38], preserve_unit_iters=True) + sch.reorder(l11, l21, l31, l12, l22, l32, l13, l23, l33, l39, l40, l14, l24, l34, l41, l15, l25, l35) + l42 = sch.fuse(l11, l21, l31, preserve_unit_iters=True) + sch.bind(loop=l42, thread_axis="blockIdx.x") + l43 = sch.fuse(l12, l22, l32, preserve_unit_iters=True) + sch.bind(loop=l43, thread_axis="vthread.x") + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b45 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b45, loop=l44, preserve_unit_loops=True, index=-1) + b46 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b46, loop=l39, preserve_unit_loops=True, index=-1) + l47, l48, l49, l50, l51, l52, l53 = sch.get_loops(block=b46) + l54 = sch.fuse(l51, l52, l53, preserve_unit_iters=True) + v55 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b46, ann_key="meta_schedule.cooperative_fetch", ann_val=v55) + b56 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b56, loop=l39, preserve_unit_loops=True, index=-1) + l57, l58, l59, l60, l61, l62 = sch.get_loops(block=b56) + l63 = sch.fuse(l61, l62, preserve_unit_iters=True) + v64 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b56, ann_key="meta_schedule.cooperative_fetch", ann_val=v64) + v65 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=4) + sch.annotate(block_or_loop=b1, ann_key="meta_schedule.unroll_explicit", ann_val=v65) + sch.enter_postproc() + sch.unannotate(block_or_loop=b46, ann_key="meta_schedule.cooperative_fetch") + l66, l67, l68, l69, l70 = sch.get_loops(block=b46) + l71, l72, l73 = sch.split(loop=l70, factors=[None, 48, 2], preserve_unit_iters=True) + sch.vectorize(loop=l73) + sch.bind(loop=l72, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b56, ann_key="meta_schedule.cooperative_fetch") + l74, l75, l76, l77, l78 = sch.get_loops(block=b56) + l79, l80, l81 = sch.split(loop=l78, factors=[None, 48, 2], preserve_unit_iters=True) + sch.vectorize(loop=l81) + sch.bind(loop=l80, thread_axis="threadIdx.x") + b82 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b82, ann_key="meta_schedule.unroll_explicit") + b83, b84, b85, b86 = sch.get_child_blocks(b82) + l87, l88, l89, l90, l91, l92, l93 = sch.get_loops(block=b83) + sch.annotate(block_or_loop=l87, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l87, ann_key="pragma_unroll_explicit", ann_val=1) + l94, l95, l96, l97, l98, l99, l100 = sch.get_loops(block=b84) + sch.annotate(block_or_loop=l94, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l94, ann_key="pragma_unroll_explicit", ann_val=1) + l101, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112 = sch.get_loops(block=b85) + sch.annotate(block_or_loop=l101, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l101, ann_key="pragma_unroll_explicit", ann_val=1) + l113, l114, l115, l116, l117, l118 = sch.get_loops(block=b86) + sch.annotate(block_or_loop=l113, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l113, ann_key="pragma_unroll_explicit", ann_val=1) + b119 = sch.get_block(name="NT_matmul", func_name="main") + l120, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131 = sch.get_loops(block=b119) + b132 = sch.decompose_reduction(block=b119, loop=l123) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + + +@T.prim_func +def fused_matmul1_add1(p_lv39: T.handle, lv40: T.Buffer((T.int64(4096), T.int64(4096)), "float32"), p_lv2: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv39 = T.match_buffer(p_lv39, (T.int64(1), n, T.int64(4096))) + lv2 = T.match_buffer(p_lv2, (T.int64(1), n, T.int64(4096))) + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(4096))) + # with T.block("root"): + var_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(4096))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(4096), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv39[v_i0, v_i1, v_k], lv40[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv39[v_i0, v_i1, v_k] * lv40[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv2[v_ax0, v_ax1, v_ax2], var_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = lv2[v_ax0, v_ax1, v_ax2] + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +def fused_matmul1_add1_sch_func(): + sch = tvm.tir.Schedule(fused_matmul1_add1) + b0 = sch.get_block("matmul") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + b0 = sch.get_block(name="matmul", func_name="main") + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l3, l4, l5, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l3, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 8, 4, 1, 1]) + l22, l23, l24, l25, l26 = sch.split(loop=l4, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[128, 2, 16, 1, 1]) + l32, l33, l34, l35, l36 = sch.split(loop=l5, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[512, 4, 2]) + l40, l41, l42 = sch.split(loop=l6, factors=[v37, v38, v39], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l13, l23, l33, l14, l24, l34, l40, l41, l15, l25, l35, l42, l16, l26, l36) + l43 = sch.fuse(l12, l22, l32, preserve_unit_iters=True) + sch.bind(loop=l43, thread_axis="blockIdx.x") + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="vthread.x") + l45 = sch.fuse(l14, l24, l34, preserve_unit_iters=True) + sch.bind(loop=l45, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b46 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b46, loop=l45, preserve_unit_loops=True, index=-1) + b47 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b47, loop=l40, preserve_unit_loops=True, index=-1) + _, l48, l49, l50, l51, l52, l53, l54 = sch.get_loops(block=b47) + l55 = sch.fuse(l52, l53, l54, preserve_unit_iters=True) + v56 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch", ann_val=v56) + b57 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l40, preserve_unit_loops=True, index=-1) + _, l58, l59, l60, l61, l62, l63 = sch.get_loops(block=b57) + l64 = sch.fuse(l62, l63, preserve_unit_iters=True) + v65 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v65) + sch.reverse_compute_inline(block=b1) + v66 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=4) + sch.annotate(block_or_loop=b2, ann_key="meta_schedule.unroll_explicit", ann_val=v66) + sch.enter_postproc() + sch.unannotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch") + _, l67, l68, l69, l70, l71 = sch.get_loops(block=b47) + l72, l73, l74 = sch.split(loop=l71, factors=[None, 64, 2], preserve_unit_iters=True) + sch.vectorize(loop=l74) + sch.bind(loop=l73, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + _, l75, l76, l77, l78, l79 = sch.get_loops(block=b57) + l80, l81, l82 = sch.split(loop=l79, factors=[None, 64, 2], preserve_unit_iters=True) + sch.vectorize(loop=l82) + sch.bind(loop=l81, thread_axis="threadIdx.x") + b83 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b83, ann_key="meta_schedule.unroll_explicit") + _, b84, b85, b86, b87, _ = sch.get_child_blocks(b83) + _, l88, l89, l90, l91, l92, l93, l94 = sch.get_loops(block=b84) + sch.annotate(block_or_loop=l88, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l88, ann_key="pragma_unroll_explicit", ann_val=1) + _, l95, l96, l97, l98, l99, l100, l101 = sch.get_loops(block=b85) + sch.annotate(block_or_loop=l95, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l95, ann_key="pragma_unroll_explicit", ann_val=1) + _, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113 = sch.get_loops(block=b86) + sch.annotate(block_or_loop=l102, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l102, ann_key="pragma_unroll_explicit", ann_val=1) + _, l114, l115, l116, l117, l118, l119 = sch.get_loops(block=b87) + sch.annotate(block_or_loop=l114, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l114, ann_key="pragma_unroll_explicit", ann_val=1) + b120 = sch.get_block(name="matmul", func_name="main") + _, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132 = sch.get_loops(block=b120) + b133 = sch.decompose_reduction(block=b120, loop=l124) + b1 = sch.get_block("lv39_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func +def fused_matmul3_multiply(p_lv43: T.handle, lv46: T.Buffer((T.int64(4096), T.int64(11008)), "float32"), p_lv48: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv43 = T.match_buffer(p_lv43, (T.int64(1), n, T.int64(4096))) + lv48 = T.match_buffer(p_lv48, (T.int64(1), n, T.int64(11008))) + var_T_multiply_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(11008))) + # with T.block("root"): + var_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(11008))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(11008), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv43[v_i0, v_i1, v_k], lv46[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv43[v_i0, v_i1, v_k] * lv46[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv48[v_ax0, v_ax1, v_ax2], var_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2] = lv48[v_ax0, v_ax1, v_ax2] * var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +def fused_matmul3_multiply_sch_func(): + sch = tvm.tir.Schedule(fused_matmul3_multiply) + b0 = sch.get_block("matmul") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + b0 = sch.get_block(name="matmul", func_name="main") + b1 = sch.get_block(name="T_multiply", func_name="main") + b2 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l3, l4, l5, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l3, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 4, 2, 4, 1]) + l22, l23, l24, l25, l26 = sch.split(loop=l4, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[344, 2, 16, 1, 1]) + l32, l33, l34, l35, l36 = sch.split(loop=l5, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[512, 1, 8]) + l40, l41, l42 = sch.split(loop=l6, factors=[v37, v38, v39], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l13, l23, l33, l14, l24, l34, l40, l41, l15, l25, l35, l42, l16, l26, l36) + l43 = sch.fuse(l12, l22, l32, preserve_unit_iters=True) + sch.bind(loop=l43, thread_axis="blockIdx.x") + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="vthread.x") + l45 = sch.fuse(l14, l24, l34, preserve_unit_iters=True) + sch.bind(loop=l45, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b46 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b46, loop=l45, preserve_unit_loops=True, index=-1) + b47 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b47, loop=l40, preserve_unit_loops=True, index=-1) + _, l48, l49, l50, l51, l52, l53, l54 = sch.get_loops(block=b47) + l55 = sch.fuse(l52, l53, l54, preserve_unit_iters=True) + v56 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch", ann_val=v56) + b57 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l40, preserve_unit_loops=True, index=-1) + _, l58, l59, l60, l61, l62, l63 = sch.get_loops(block=b57) + l64 = sch.fuse(l62, l63, preserve_unit_iters=True) + v65 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v65) + sch.reverse_compute_inline(block=b1) + v66 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=3) + sch.annotate(block_or_loop=b2, ann_key="meta_schedule.unroll_explicit", ann_val=v66) + sch.enter_postproc() + sch.unannotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch") + _, l67, l68, l69, l70, l71 = sch.get_loops(block=b47) + l72, l73, l74 = sch.split(loop=l71, factors=[None, 32, 2], preserve_unit_iters=True) + sch.vectorize(loop=l74) + sch.bind(loop=l73, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + _, l75, l76, l77, l78, l79 = sch.get_loops(block=b57) + l80, l81, l82 = sch.split(loop=l79, factors=[None, 32, 2], preserve_unit_iters=True) + sch.vectorize(loop=l82) + sch.bind(loop=l81, thread_axis="threadIdx.x") + b83 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b83, ann_key="meta_schedule.unroll_explicit") + _, b84, b85, b86, b87, _ = sch.get_child_blocks(b83) + _, l88, l89, l90, l91, l92, l93, l94 = sch.get_loops(block=b84) + sch.annotate(block_or_loop=l88, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l88, ann_key="pragma_unroll_explicit", ann_val=1) + _, l95, l96, l97, l98, l99, l100, l101 = sch.get_loops(block=b85) + sch.annotate(block_or_loop=l95, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l95, ann_key="pragma_unroll_explicit", ann_val=1) + _, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113 = sch.get_loops(block=b86) + sch.annotate(block_or_loop=l102, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l102, ann_key="pragma_unroll_explicit", ann_val=1) + _, l114, l115, l116, l117, l118, l119 = sch.get_loops(block=b87) + sch.annotate(block_or_loop=l114, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l114, ann_key="pragma_unroll_explicit", ann_val=1) + b120 = sch.get_block(name="matmul", func_name="main") + _, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132 = sch.get_loops(block=b120) + b133 = sch.decompose_reduction(block=b120, loop=l124) + b1 = sch.get_block("lv43_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func +def fused_matmul3_silu(p_lv43: T.handle, lv44: T.Buffer((T.int64(4096), T.int64(11008)), "float32"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv43 = T.match_buffer(p_lv43, (T.int64(1), n, T.int64(4096))) + var_T_multiply_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(11008))) + # with T.block("root"): + var_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(11008))) + compute = T.alloc_buffer((T.int64(1), n, T.int64(11008))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(11008), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv43[v_i0, v_i1, v_k], lv44[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv43[v_i0, v_i1, v_k] * lv44[v_k, v_i2] + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(11008)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_matmul_intermediate[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.sigmoid(var_matmul_intermediate[v_i0, v_i1, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], compute[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2] = var_matmul_intermediate[v_ax0, v_ax1, v_ax2] * compute[v_ax0, v_ax1, v_ax2] + + +def fused_matmul3_silu_sch_func(): + sch = tvm.tir.Schedule(fused_matmul3_silu) + b0 = sch.get_block("matmul") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + b0 = sch.get_block(name="matmul", func_name="main") + b1 = sch.get_block(name="compute", func_name="main") + b2 = sch.get_block(name="T_multiply", func_name="main") + b3 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l4, l5, l6, l7 = sch.get_loops(block=b0) + v8, v9, v10, v11, v12 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l13, l14, l15, l16, l17 = sch.split(loop=l4, factors=[v8, v9, v10, v11, v12], preserve_unit_iters=True) + v18, v19, v20, v21, v22 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[1, 2, 2, 8, 1]) + l23, l24, l25, l26, l27 = sch.split(loop=l5, factors=[v18, v19, v20, v21, v22], preserve_unit_iters=True) + v28, v29, v30, v31, v32 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[344, 2, 16, 1, 1]) + l33, l34, l35, l36, l37 = sch.split(loop=l6, factors=[v28, v29, v30, v31, v32], preserve_unit_iters=True) + v38, v39, v40 = sch.sample_perfect_tile(loop=l7, n=3, max_innermost_factor=64, decision=[512, 1, 8]) + l41, l42, l43 = sch.split(loop=l7, factors=[v38, v39, v40], preserve_unit_iters=True) + sch.reorder(l13, l23, l33, l14, l24, l34, l15, l25, l35, l41, l42, l16, l26, l36, l43, l17, l27, l37) + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="blockIdx.x") + l45 = sch.fuse(l14, l24, l34, preserve_unit_iters=True) + sch.bind(loop=l45, thread_axis="vthread.x") + l46 = sch.fuse(l15, l25, l35, preserve_unit_iters=True) + sch.bind(loop=l46, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b47 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b47, loop=l46, preserve_unit_loops=True, index=-1) + b48 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b48, loop=l41, preserve_unit_loops=True, index=-1) + _, l49, l50, l51, l52, l53, l54, l55 = sch.get_loops(block=b48) + l56 = sch.fuse(l53, l54, l55, preserve_unit_iters=True) + v57 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b48, ann_key="meta_schedule.cooperative_fetch", ann_val=v57) + b58 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b58, loop=l41, preserve_unit_loops=True, index=-1) + _, l59, l60, l61, l62, l63, l64 = sch.get_loops(block=b58) + l65 = sch.fuse(l63, l64, preserve_unit_iters=True) + v66 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=3) + sch.annotate(block_or_loop=b58, ann_key="meta_schedule.cooperative_fetch", ann_val=v66) + sch.compute_inline(block=b1) + v67 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=4) + sch.annotate(block_or_loop=b3, ann_key="meta_schedule.unroll_explicit", ann_val=v67) + l68, l69, l70 = sch.get_loops(block=b2) + l71 = sch.fuse(l68, l69, l70, preserve_unit_iters=True) + l72, l73, l74 = sch.split(loop=l71, factors=[None, 256, 256], preserve_unit_iters=True) + sch.reorder(l73, l74, l72) + sch.bind(loop=l73, thread_axis="blockIdx.x") + sch.bind(loop=l74, thread_axis="threadIdx.x") + sch.enter_postproc() + sch.unannotate(block_or_loop=b48, ann_key="meta_schedule.cooperative_fetch") + _, l75, l76, l77, l78, l79 = sch.get_loops(block=b48) + l80, l81, l82 = sch.split(loop=l79, factors=[None, 32, 2], preserve_unit_iters=True) + sch.vectorize(loop=l82) + sch.bind(loop=l81, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b58, ann_key="meta_schedule.cooperative_fetch") + _, l83, l84, l85, l86, l87 = sch.get_loops(block=b58) + l88, l89, l90 = sch.split(loop=l87, factors=[None, 32, 4], preserve_unit_iters=True) + sch.vectorize(loop=l90) + sch.bind(loop=l89, thread_axis="threadIdx.x") + b91 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b91, ann_key="meta_schedule.unroll_explicit") + _, b92, b93, b94, b95, _, b96 = sch.get_child_blocks(b91) + _, l97, l98, l99, l100, l101, l102, l103 = sch.get_loops(block=b92) + sch.annotate(block_or_loop=l97, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l97, ann_key="pragma_unroll_explicit", ann_val=1) + _, l104, l105, l106, l107, l108, l109, l110 = sch.get_loops(block=b93) + sch.annotate(block_or_loop=l104, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l104, ann_key="pragma_unroll_explicit", ann_val=1) + _, l111, l112, l113, l114, l115, l116, l117, l118, l119, l120, l121, l122 = sch.get_loops(block=b94) + sch.annotate(block_or_loop=l111, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l111, ann_key="pragma_unroll_explicit", ann_val=1) + _, l123, l124, l125, l126, l127, l128 = sch.get_loops(block=b95) + sch.annotate(block_or_loop=l123, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l123, ann_key="pragma_unroll_explicit", ann_val=1) + l129, l130, l131 = sch.get_loops(block=b96) + sch.annotate(block_or_loop=l129, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l129, ann_key="pragma_unroll_explicit", ann_val=1) + b132 = sch.get_block(name="matmul", func_name="main") + _, l133, l134, l135, l136, l137, l138, l139, l140, l141, l142, l143, l144 = sch.get_loops(block=b132) + b145 = sch.decompose_reduction(block=b132, loop=l136) + b1 = sch.get_block("lv43_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func +def fused_matmul4_add1(p_lv49: T.handle, lv50: T.Buffer((T.int64(11008), T.int64(4096)), "float32"), p_lv42: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv49 = T.match_buffer(p_lv49, (T.int64(1), n, T.int64(11008))) + lv42 = T.match_buffer(p_lv42, (T.int64(1), n, T.int64(4096))) + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(4096))) + # with T.block("root"): + var_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(4096))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(4096), T.int64(11008)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv49[v_i0, v_i1, v_k], lv50[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv49[v_i0, v_i1, v_k] * lv50[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv42[v_ax0, v_ax1, v_ax2], var_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = lv42[v_ax0, v_ax1, v_ax2] + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +def fused_matmul4_add1_sch_func(): + sch = tvm.tir.Schedule(fused_matmul4_add1) + b0 = sch.get_block("matmul") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + b0 = sch.get_block(name="matmul", func_name="main") + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l3, l4, l5, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l3, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 1, 4, 8, 1]) + l22, l23, l24, l25, l26 = sch.split(loop=l4, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[128, 2, 16, 1, 1]) + l32, l33, l34, l35, l36 = sch.split(loop=l5, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[1376, 2, 4]) + l40, l41, l42 = sch.split(loop=l6, factors=[v37, v38, v39], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l13, l23, l33, l14, l24, l34, l40, l41, l15, l25, l35, l42, l16, l26, l36) + l43 = sch.fuse(l12, l22, l32, preserve_unit_iters=True) + sch.bind(loop=l43, thread_axis="blockIdx.x") + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="vthread.x") + l45 = sch.fuse(l14, l24, l34, preserve_unit_iters=True) + sch.bind(loop=l45, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b46 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b46, loop=l45, preserve_unit_loops=True, index=-1) + b47 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b47, loop=l40, preserve_unit_loops=True, index=-1) + _, l48, l49, l50, l51, l52, l53, l54 = sch.get_loops(block=b47) + l55 = sch.fuse(l52, l53, l54, preserve_unit_iters=True) + v56 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch", ann_val=v56) + b57 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l40, preserve_unit_loops=True, index=-1) + _, l58, l59, l60, l61, l62, l63 = sch.get_loops(block=b57) + l64 = sch.fuse(l62, l63, preserve_unit_iters=True) + v65 = sch.sample_categorical(candidates=[1, 2, 3, 4], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v65) + sch.reverse_compute_inline(block=b1) + v66 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=3) + sch.annotate(block_or_loop=b2, ann_key="meta_schedule.unroll_explicit", ann_val=v66) + sch.enter_postproc() + sch.unannotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch") + _, l67, l68, l69, l70, l71 = sch.get_loops(block=b47) + l72, l73, l74 = sch.split(loop=l71, factors=[None, 64, 2], preserve_unit_iters=True) + sch.vectorize(loop=l74) + sch.bind(loop=l73, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + _, l75, l76, l77, l78, l79 = sch.get_loops(block=b57) + l80, l81, l82 = sch.split(loop=l79, factors=[None, 64, 2], preserve_unit_iters=True) + sch.vectorize(loop=l82) + sch.bind(loop=l81, thread_axis="threadIdx.x") + b83 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b83, ann_key="meta_schedule.unroll_explicit") + _, b84, b85, b86, b87, _ = sch.get_child_blocks(b83) + _, l88, l89, l90, l91, l92, l93, l94 = sch.get_loops(block=b84) + sch.annotate(block_or_loop=l88, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l88, ann_key="pragma_unroll_explicit", ann_val=1) + _, l95, l96, l97, l98, l99, l100, l101 = sch.get_loops(block=b85) + sch.annotate(block_or_loop=l95, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l95, ann_key="pragma_unroll_explicit", ann_val=1) + _, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113 = sch.get_loops(block=b86) + sch.annotate(block_or_loop=l102, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l102, ann_key="pragma_unroll_explicit", ann_val=1) + _, l114, l115, l116, l117, l118, l119 = sch.get_loops(block=b87) + sch.annotate(block_or_loop=l114, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l114, ann_key="pragma_unroll_explicit", ann_val=1) + b120 = sch.get_block(name="matmul", func_name="main") + _, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132 = sch.get_loops(block=b120) + b133 = sch.decompose_reduction(block=b120, loop=l124) + b1 = sch.get_block("lv49_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func +def fused_NT_matmul_add1_before(p_lv39: T.handle, linear_weight3: T.Buffer((T.int64(4096), T.int64(4096)), "float32"), p_lv2: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv39 = T.match_buffer(p_lv39, (T.int64(1), n, T.int64(4096))) + lv2 = T.match_buffer(p_lv2, (T.int64(1), n, T.int64(4096))) + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(4096))) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(4096))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(4096), T.int64(4096)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv39[v_i0, v_i1, v_k], linear_weight3[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv39[v_i0, v_i1, v_k] * linear_weight3[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv2[v_ax0, v_ax1, v_ax2], var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = lv2[v_ax0, v_ax1, v_ax2] + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_NT_matmul_add1_after(p_lv33: T.handle, linear_weight3: T.Buffer((T.int64(4096), T.int64(4096)), "float32"), p_lv2: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + lv33 = T.match_buffer(p_lv33, (T.int64(1), n, T.int64(4096))) + lv2 = T.match_buffer(p_lv2, (T.int64(1), n, T.int64(4096))) + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(4096))) + # with T.block("root"): + for i1_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.y"): + with T.block("NT_matmul_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i1_0) + T.reads(lv33[T.Add(v_i0, T.int64(0)), v_i1_o * T.int64(32):v_i1_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(4096)], linear_weight3[T.int64(0):T.int64(4096), T.int64(0):T.int64(4096)], lv2[v_i0, v_i1_o * T.int64(32):v_i1_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(4096)]) + T.writes(var_T_add_intermediate[v_i0, v_i1_o * T.int64(32):v_i1_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(4096)]) + var_NT_matmul_intermediate_pad_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(4096)), scope="local") + lv33_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(4096)), scope="shared") + linear_weight3_shared = T.alloc_buffer((T.int64(4096), T.int64(4096)), scope="shared") + for i0_0_i1_1_0_i2_0_fused in T.thread_binding(T.int64(128), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i0_1_i1_1_1_i2_1_fused in T.thread_binding(T.int64(4), thread="vthread.x"): + for i0_2_i1_1_2_i2_2_fused in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for i1_1_3_init, i2_3_init, i1_1_4_init, i2_4_init in T.grid(T.int64(1), T.int64(4), T.int64(1), T.int64(1)): + with T.block("NT_matmul_init"): + v_i1_i = T.axis.spatial(T.int64(32), i0_1_i1_1_1_i2_1_fused * T.int64(8) + i0_2_i1_1_2_i2_2_fused // T.int64(8) + i1_1_3_init + i1_1_4_init) + v_i2_i = T.axis.spatial(T.int64(4096), i2_4_init + i0_0_i1_1_0_i2_0_fused * T.int64(32) + i0_2_i1_1_2_i2_2_fused % T.int64(8) * T.int64(4) + i2_3_init) + T.reads() + T.writes(var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i] = T.float32(0) + for k_0 in range(T.int64(128)): + for ax0_ax1_ax2_fused_0 in range(T.int64(8)): + for ax0_ax1_ax2_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_ax2_fused_2 in T.vectorized(T.int64(2)): + with T.block("lv33_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), (ax0_ax1_ax2_fused_0 * T.int64(128) + ax0_ax1_ax2_fused_1 * T.int64(2) + ax0_ax1_ax2_fused_2) // T.int64(32)) + v2 = T.axis.spatial(T.int64(4096), k_0 * T.int64(32) + (ax0_ax1_ax2_fused_0 * T.int64(128) + ax0_ax1_ax2_fused_1 * T.int64(2) + ax0_ax1_ax2_fused_2) % T.int64(32)) + T.reads(lv33[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2]) + T.writes(lv33_pad_shared[v0, v1, v2]) + lv33_pad_shared[v0, v1, v2] = T.if_then_else(v_i1_o * T.int64(32) + v1 < n, lv33[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2], T.float32(0)) + for ax0_ax1_fused_0 in range(T.int64(8)): + for ax0_ax1_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_fused_2 in T.vectorized(T.int64(2)): + with T.block("linear_weight3_shared"): + v0 = T.axis.spatial(T.int64(4096), i0_0_i1_1_0_i2_0_fused * T.int64(32) + (ax0_ax1_fused_0 * T.int64(128) + ax0_ax1_fused_1 * T.int64(2) + ax0_ax1_fused_2) // T.int64(32)) + v1 = T.axis.spatial(T.int64(4096), k_0 * T.int64(32) + (ax0_ax1_fused_0 * T.int64(128) + ax0_ax1_fused_1 * T.int64(2) + ax0_ax1_fused_2) % T.int64(32)) + T.reads(linear_weight3[v0, v1]) + T.writes(linear_weight3_shared[v0, v1]) + linear_weight3_shared[v0, v1] = linear_weight3[v0, v1] + for k_1, i0_3, i1_1_3, i2_3, k_2, i0_4, i1_1_4, i2_4 in T.grid(T.int64(8), T.int64(1), T.int64(1), T.int64(4), T.int64(4), T.int64(1), T.int64(1), T.int64(1)): + with T.block("NT_matmul_update"): + v_i1_i = T.axis.spatial(T.int64(32), i0_1_i1_1_1_i2_1_fused * T.int64(8) + i0_2_i1_1_2_i2_2_fused // T.int64(8) + i1_1_3 + i1_1_4) + v_i2_i = T.axis.spatial(T.int64(4096), i2_4 + i0_0_i1_1_0_i2_0_fused * T.int64(32) + i0_2_i1_1_2_i2_2_fused % T.int64(8) * T.int64(4) + i2_3) + v_k_i = T.axis.reduce(T.int64(4096), k_0 * T.int64(32) + k_1 * T.int64(4) + k_2) + T.reads(var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i], lv33_pad_shared[T.int64(0), v_i1_i, v_k_i], linear_weight3_shared[v_i2_i, v_k_i]) + T.writes(var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i] = var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i] + lv33_pad_shared[T.int64(0), v_i1_i, v_k_i] * linear_weight3_shared[v_i2_i, v_k_i] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(4)): + with T.block("var_NT_matmul_intermediate_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(32), i0_1_i1_1_1_i2_1_fused * T.int64(8) + i0_2_i1_1_2_i2_2_fused // T.int64(8) + ax1) + v2 = T.axis.spatial(T.int64(4096), i0_0_i1_1_0_i2_0_fused * T.int64(32) + i0_2_i1_1_2_i2_2_fused % T.int64(8) * T.int64(4) + ax2) + T.reads(lv2[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2], var_NT_matmul_intermediate_pad_local[v0, v1, v2]) + T.writes(var_T_add_intermediate[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2]) + # if T.int64(0) <= v_i0 and v_i0 < T.int64(1) and T.int64(0) <= v_i1_o * T.int64(32) + v1 and v_i1_o * T.int64(32) + v1 < n: + if v_i1_o * T.int64(32) + v1 < n: + var_T_add_intermediate[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2] = lv2[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2] + var_NT_matmul_intermediate_pad_local[v0, v1, v2] + + +@T.prim_func +def fused_NT_matmul1_divide_add_maximum_before(p_lv28: T.handle, p_lv29: T.handle, p_lv5: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv28 = T.match_buffer(p_lv28, (T.int64(1), T.int64(32), n, T.int64(128))) + lv29 = T.match_buffer(p_lv29, (T.int64(1), T.int64(32), n, T.int64(128))) + lv5 = T.match_buffer(p_lv5, (T.int64(1), T.int64(1), n, n)) + var_T_maximum_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, n)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, n)) + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, n)) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, n)) + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, n, T.int64(128)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv28[T.int64(0), v_i1, v_i2, v_k], lv29[T.int64(0), v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv28[T.int64(0), v_i1, v_i2, v_k] * lv29[T.int64(0), v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, n): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float32(0.088388349161020605) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, n): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv5[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] + lv5[v_ax0, T.int64(0), v_ax2, v_ax3] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, n): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_add_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float32(-3.4028234663852886e+38)) + + +@T.prim_func +def fused_NT_matmul1_divide_add_maximum_after(p_lv22: T.handle, p_lv23: T.handle, p_lv5: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + lv22 = T.match_buffer(p_lv22, (T.int64(1), T.int64(32), n, T.int64(128))) + lv23 = T.match_buffer(p_lv23, (T.int64(1), T.int64(32), n, T.int64(128))) + lv5 = T.match_buffer(p_lv5, (T.int64(1), T.int64(1), n, n)) + var_T_maximum_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, n)) + # with T.block("root"): + for i2_0_i3_0_fused in T.thread_binding((n + T.int64(31)) // T.int64(32) * ((n + T.int64(31)) // T.int64(32)), thread="blockIdx.y"): + with T.block("NT_matmul_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0_i3_0_fused // ((n + T.int64(31)) // T.int64(32))) + v_i3_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0_i3_0_fused % ((n + T.int64(31)) // T.int64(32))) + T.reads(lv22[T.int64(0), T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(128)], lv23[T.int64(0), T.int64(0):T.int64(32), v_i3_o * T.int64(32):v_i3_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(128)], lv5[v_i0, T.int64(0), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), v_i3_o * T.int64(32):v_i3_o * T.int64(32) + T.int64(32)]) + T.writes(var_T_maximum_intermediate[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), v_i3_o * T.int64(32):v_i3_o * T.int64(32) + T.int64(32)]) + C_pad_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32), T.int64(32)), scope="local") + A_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32), T.int64(128)), scope="shared") + B_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32), T.int64(128)), scope="shared") + for i0_0_i1_0_i2_1_0_i3_1_0_fused in T.thread_binding(T.int64(128), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 512, "pragma_unroll_explicit": 1}): + for i0_1_i1_1_i2_1_1_i3_1_1_fused in T.thread_binding(T.int64(4), thread="vthread.x"): + for i0_2_i1_2_i2_1_2_i3_1_2_fused in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for i1_3_init, i2_1_3_init, i3_1_3_init, i1_4_init, i2_1_4_init, i3_1_4_init in T.grid(T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(1)): + with T.block("NT_matmul_init"): + v_i1_i = T.axis.spatial(T.int64(32), i1_4_init + i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4) + i1_3_init) + v_i2_i = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(4) // T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused // T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused // T.int64(8) + i2_1_3_init + i2_1_4_init) + v_i3_i = T.axis.spatial(T.int64(32), i3_1_4_init + i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused % T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused % T.int64(8) + i3_1_3_init) + T.reads() + T.writes(C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i] = T.float32(0) + for k_0 in range(T.int64(16)): + for ax0_ax1_ax2_ax3_fused_0 in range(T.int64(1)): + for ax0_ax1_ax2_ax3_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_ax2_ax3_fused_2 in T.vectorized(T.int64(2)): + with T.block("A_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4)) + v2 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(4) // T.int64(2) * T.int64(16) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) // T.int64(8)) + v3 = T.axis.spatial(T.int64(128), k_0 * T.int64(8) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) % T.int64(8)) + T.reads(lv22[v0, v1, v_i2_o * T.int64(32) + v2, v3]) + T.writes(A_pad_shared[v0, v1, v2, v3]) + A_pad_shared[v0, v1, v2, v3] = T.if_then_else(v_i2_o * T.int64(32) + v2 < n, lv22[v0, v1, v_i2_o * T.int64(32) + v2, v3], T.float32(0)) + for ax0_ax1_ax2_ax3_fused_0 in range(T.int64(1)): + for ax0_ax1_ax2_ax3_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_ax2_ax3_fused_2 in T.vectorized(T.int64(2)): + with T.block("B_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4)) + v2 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(2) * T.int64(16) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) // T.int64(8)) + v3 = T.axis.spatial(T.int64(128), k_0 * T.int64(8) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) % T.int64(8)) + T.reads(lv23[v0, v1, v_i3_o * T.int64(32) + v2, v3]) + T.writes(B_pad_shared[v0, v1, v2, v3]) + B_pad_shared[v0, v1, v2, v3] = T.if_then_else(v_i3_o * T.int64(32) + v2 < n, lv23[v0, v1, v_i3_o * T.int64(32) + v2, v3], T.float32(0)) + for k_1, i0_3, i1_3, i2_1_3, i3_1_3, k_2, i0_4, i1_4, i2_1_4, i3_1_4 in T.grid(T.int64(4), T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(2), T.int64(1), T.int64(1), T.int64(1), T.int64(1)): + with T.block("NT_matmul_update"): + v_i1_i = T.axis.spatial(T.int64(32), i1_4 + i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4) + i1_3) + v_i2_i = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(4) // T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused // T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused // T.int64(8) + i2_1_3 + i2_1_4) + v_i3_i = T.axis.spatial(T.int64(32), i3_1_4 + i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused % T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused % T.int64(8) + i3_1_3) + v_k_i = T.axis.reduce(T.int64(128), k_0 * T.int64(8) + k_1 * T.int64(2) + k_2) + T.reads(C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i], A_pad_shared[T.int64(0), v_i1_i, v_i2_i, v_k_i], B_pad_shared[T.int64(0), v_i1_i, v_i3_i, v_k_i]) + T.writes(C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i] = C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i] + A_pad_shared[T.int64(0), v_i1_i, v_i2_i, v_k_i] * B_pad_shared[T.int64(0), v_i1_i, v_i3_i, v_k_i] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), T.int64(1), T.int64(1)): + with T.block("C_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4) + ax1) + v2 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(4) // T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused // T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused // T.int64(8) + ax2) + v3 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused % T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused % T.int64(8) + ax3) + T.reads(C_pad_local[v0, v1, v2, v3], lv5[v_i0 + v0, T.int64(0), v_i2_o * T.int64(32) + v2, v_i3_o * T.int64(32) + v3]) + T.writes(var_T_maximum_intermediate[v_i0 + v0, v1, v_i2_o * T.int64(32) + v2, v_i3_o * T.int64(32) + v3]) + # if T.int64(0) <= v_i0 and v_i0 < T.int64(1) and T.int64(0) <= v_i2_o * T.int64(32) + v2 and v_i2_o * T.int64(32) + v2 < n and T.int64(0) <= v_i3_o * T.int64(32) + v3 and v_i3_o * T.int64(32) + v3 < n: + if v_i2_o * T.int64(32) + v2 < n and v_i3_o * T.int64(32) + v3 < n: + var_T_maximum_intermediate[v_i0 + v0, v1, v_i2_o * T.int64(32) + v2, v_i3_o * T.int64(32) + v3] = T.max(C_pad_local[v0, v1, v2, v3] * T.float32(0.088388349161020605) + lv5[v_i0 + v0, T.int64(0), v_i2_o * T.int64(32) + v2, v_i3_o * T.int64(32) + v3], T.float32(-3.4028234663852886e+38)) + +@T.prim_func +def fused_NT_matmul1_divide_add_maximum_with_m_before(p_lv30: T.handle, p_lv31: T.handle, p_lv7: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv30 = T.match_buffer(p_lv30, (T.int64(1), T.int64(32), n, T.int64(128))) + m = T.int64() + lv31 = T.match_buffer(p_lv31, (T.int64(1), T.int64(32), m, T.int64(128))) + lv7 = T.match_buffer(p_lv7, (T.int64(1), T.int64(1), n, m)) + var_T_maximum_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, m, T.int64(128)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv30[v_i0, v_i1, v_i2, v_k], lv31[v_i0, v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv30[v_i0, v_i1, v_i2, v_k] * lv31[v_i0, v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float32(0.088388349161020605) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv7[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] + lv7[v_ax0, T.int64(0), v_ax2, v_ax3] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_add_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float32(-3.4028234663852886e+38)) + +@T.prim_func +def fused_NT_matmul1_divide_add_maximum_with_m_after(p_lv22: T.handle, p_lv23: T.handle, p_lv5: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + m = T.int64() + lv22 = T.match_buffer(p_lv22, (T.int64(1), T.int64(32), n, T.int64(128))) + lv23 = T.match_buffer(p_lv23, (T.int64(1), T.int64(32), m, T.int64(128))) + lv5 = T.match_buffer(p_lv5, (T.int64(1), T.int64(1), n, m)) + var_T_maximum_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + for i2_0_i3_0_fused in T.thread_binding((n + T.int64(31)) // T.int64(32) * ((m + T.int64(31)) // T.int64(32)), thread="blockIdx.y"): + with T.block("NT_matmul_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0_i3_0_fused // ((m + T.int64(31)) // T.int64(32))) + v_i3_o = T.axis.spatial((m + T.int64(31)) // T.int64(32), i2_0_i3_0_fused % ((m + T.int64(31)) // T.int64(32))) + T.reads(lv22[T.int64(0), T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(128)], lv23[T.int64(0), T.int64(0):T.int64(32), v_i3_o * T.int64(32):v_i3_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(128)], lv5[v_i0, T.int64(0), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), v_i3_o * T.int64(32):v_i3_o * T.int64(32) + T.int64(32)]) + T.writes(var_T_maximum_intermediate[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), v_i3_o * T.int64(32):v_i3_o * T.int64(32) + T.int64(32)]) + C_pad_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32), T.int64(32)), scope="local") + A_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32), T.int64(128)), scope="shared") + B_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32), T.int64(128)), scope="shared") + for i0_0_i1_0_i2_1_0_i3_1_0_fused in T.thread_binding(T.int64(128), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 512, "pragma_unroll_explicit": 1}): + for i0_1_i1_1_i2_1_1_i3_1_1_fused in T.thread_binding(T.int64(4), thread="vthread.x"): + for i0_2_i1_2_i2_1_2_i3_1_2_fused in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for i1_3_init, i2_1_3_init, i3_1_3_init, i1_4_init, i2_1_4_init, i3_1_4_init in T.grid(T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(1)): + with T.block("NT_matmul_init"): + v_i1_i = T.axis.spatial(T.int64(32), i1_4_init + i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4) + i1_3_init) + v_i2_i = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(4) // T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused // T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused // T.int64(8) + i2_1_3_init + i2_1_4_init) + v_i3_i = T.axis.spatial(T.int64(32), i3_1_4_init + i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused % T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused % T.int64(8) + i3_1_3_init) + T.reads() + T.writes(C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i] = T.float32(0) + for k_0 in range(T.int64(16)): + for ax0_ax1_ax2_ax3_fused_0 in range(T.int64(1)): + for ax0_ax1_ax2_ax3_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_ax2_ax3_fused_2 in T.vectorized(T.int64(2)): + with T.block("A_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4)) + v2 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(4) // T.int64(2) * T.int64(16) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) // T.int64(8)) + v3 = T.axis.spatial(T.int64(128), k_0 * T.int64(8) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) % T.int64(8)) + T.reads(lv22[v0, v1, v_i2_o * T.int64(32) + v2, v3]) + T.writes(A_pad_shared[v0, v1, v2, v3]) + A_pad_shared[v0, v1, v2, v3] = T.if_then_else(v_i2_o * T.int64(32) + v2 < n, lv22[v0, v1, v_i2_o * T.int64(32) + v2, v3], T.float32(0)) + for ax0_ax1_ax2_ax3_fused_0 in range(T.int64(1)): + for ax0_ax1_ax2_ax3_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_ax2_ax3_fused_2 in T.vectorized(T.int64(2)): + with T.block("B_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4)) + v2 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(2) * T.int64(16) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) // T.int64(8)) + v3 = T.axis.spatial(T.int64(128), k_0 * T.int64(8) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) % T.int64(8)) + T.reads(lv23[v0, v1, v_i3_o * T.int64(32) + v2, v3]) + T.writes(B_pad_shared[v0, v1, v2, v3]) + B_pad_shared[v0, v1, v2, v3] = T.if_then_else(v_i3_o * T.int64(32) + v2 < m, lv23[v0, v1, v_i3_o * T.int64(32) + v2, v3], T.float32(0)) + for k_1, i0_3, i1_3, i2_1_3, i3_1_3, k_2, i0_4, i1_4, i2_1_4, i3_1_4 in T.grid(T.int64(4), T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(2), T.int64(1), T.int64(1), T.int64(1), T.int64(1)): + with T.block("NT_matmul_update"): + v_i1_i = T.axis.spatial(T.int64(32), i1_4 + i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4) + i1_3) + v_i2_i = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(4) // T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused // T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused // T.int64(8) + i2_1_3 + i2_1_4) + v_i3_i = T.axis.spatial(T.int64(32), i3_1_4 + i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused % T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused % T.int64(8) + i3_1_3) + v_k_i = T.axis.reduce(T.int64(128), k_0 * T.int64(8) + k_1 * T.int64(2) + k_2) + T.reads(C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i], A_pad_shared[T.int64(0), v_i1_i, v_i2_i, v_k_i], B_pad_shared[T.int64(0), v_i1_i, v_i3_i, v_k_i]) + T.writes(C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i] = C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i] + A_pad_shared[T.int64(0), v_i1_i, v_i2_i, v_k_i] * B_pad_shared[T.int64(0), v_i1_i, v_i3_i, v_k_i] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), T.int64(1), T.int64(1)): + with T.block("C_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4) + ax1) + v2 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(4) // T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused // T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused // T.int64(8) + ax2) + v3 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused % T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused % T.int64(8) + ax3) + T.reads(C_pad_local[v0, v1, v2, v3], lv5[v_i0 + v0, T.int64(0), v_i2_o * T.int64(32) + v2, v_i3_o * T.int64(32) + v3]) + T.writes(var_T_maximum_intermediate[v_i0 + v0, v1, v_i2_o * T.int64(32) + v2, v_i3_o * T.int64(32) + v3]) + # if T.int64(0) <= v_i0 and v_i0 < T.int64(1) and T.int64(0) <= v_i2_o * T.int64(32) + v2 and v_i2_o * T.int64(32) + v2 < n and T.int64(0) <= v_i3_o * T.int64(32) + v3 and v_i3_o * T.int64(32) + v3 < n: + if v_i2_o * T.int64(32) + v2 < n and v_i3_o * T.int64(32) + v3 < m: + var_T_maximum_intermediate[v_i0 + v0, v1, v_i2_o * T.int64(32) + v2, v_i3_o * T.int64(32) + v3] = T.max(C_pad_local[v0, v1, v2, v3] * T.float32(0.088388349161020605) + lv5[v_i0 + v0, T.int64(0), v_i2_o * T.int64(32) + v2, v_i3_o * T.int64(32) + v3], T.float32(-3.4028234663852886e+38)) + + +@T.prim_func +def fused_NT_matmul6_divide1_add2_maximum1_before(lv2732: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(128)), "float32"), p_lv2733: T.handle, p_lv2709: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv2733 = T.match_buffer(p_lv2733, (T.int64(1), T.int64(32), n, T.int64(128))) + lv2709 = T.match_buffer(p_lv2709, (T.int64(1), T.int64(1), T.int64(1), n)) + var_T_maximum_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), T.int64(1), n)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + var_T_add_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n, T.int64(128)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv2732[T.int64(0), v_i1, v_i2, v_k], lv2733[T.int64(0), v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv2732[T.int64(0), v_i1, v_i2, v_k] * lv2733[T.int64(0), v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float32(0.088388349161020605) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv2709[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] + lv2709[v_ax0, T.int64(0), v_ax2, v_ax3] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_add_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float32(-3.4028234663852886e+38)) + + +@T.prim_func +def fused_NT_matmul6_divide1_add2_maximum1_after(lv2732: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(128)), "float32"), p_lv2733: T.handle, p_lv2709: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + lv2733 = T.match_buffer(p_lv2733, (T.int64(1), T.int64(32), n, T.int64(128))) + lv2709 = T.match_buffer(p_lv2709, (T.int64(1), T.int64(1), T.int64(1), n)) + var_T_maximum_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), T.int64(1), n)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + var_NT_matmul_intermediate_pad_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), (n + T.int64(31)) // T.int64(32) * T.int64(32)), scope="local") + lv2732_shared = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(128)), scope="shared") + lv2733_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), (n + T.int64(31)) // T.int64(32) * T.int64(32), T.int64(128)), scope="shared") + for i3_0 in range((n + T.int64(31)) // T.int64(32)): + for i0_0_i1_0_i2_0_i3_1_0_fused in T.thread_binding(T.int64(32), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 64, "pragma_unroll_explicit": 1}): + for i0_1_i1_1_i2_1_i3_1_1_fused in T.thread_binding(T.int64(1), thread="vthread.x"): + for i0_2_i1_2_i2_2_i3_1_2_fused in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for i0_3_init, i1_3_init, i2_3_init, i3_1_3_init, i0_4_init, i1_4_init, i2_4_init, i3_1_4_init in T.grid(T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(1)): + with T.block("NT_matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), i0_3_init + i0_4_init) + v_i1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_0_i3_1_0_fused // T.int64(4) * T.int64(4) + i0_2_i1_2_i2_2_i3_1_2_fused // T.int64(8) + i1_3_init + i1_4_init) + v_i2 = T.axis.spatial(T.int64(1), i2_3_init + i2_4_init) + v_i3 = T.axis.spatial((n + T.int64(31)) // T.int64(32) * T.int64(32), i3_0 * T.int64(32) + i0_0_i1_0_i2_0_i3_1_0_fused % T.int64(4) * T.int64(8) + i0_2_i1_2_i2_2_i3_1_2_fused % T.int64(8) + i3_1_3_init + i3_1_4_init) + T.reads() + T.writes(var_NT_matmul_intermediate_pad_local[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + var_NT_matmul_intermediate_pad_local[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + for k_0 in range(T.int64(8)): + for ax0_ax1_ax2_ax3_fused_0 in range(T.int64(1)): + for ax0_ax1_ax2_ax3_fused_1 in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for ax0_ax1_ax2_ax3_fused_2 in T.vectorized(T.int64(2)): + with T.block("lv2732_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_0_i3_1_0_fused // T.int64(4) * T.int64(4) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(64) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) // T.int64(16)) + v2 = T.axis.spatial(T.int64(1), T.int64(0)) + v3 = T.axis.spatial(T.int64(128), k_0 * T.int64(16) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(64) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) % T.int64(16)) + T.reads(lv2732[v0, v1, v2, v3]) + T.writes(lv2732_shared[v0, v1, v2, v3]) + lv2732_shared[v0, v1, v2, v3] = lv2732[v0, v1, v2, v3] + for ax0_ax1_ax2_ax3_fused_0 in range(T.int64(4)): + for ax0_ax1_ax2_ax3_fused_1 in T.thread_binding(T.int64(32), thread="threadIdx.x"): + for ax0_ax1_ax2_ax3_fused_2 in T.vectorized(T.int64(4)): + with T.block("lv2733_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_0_i3_1_0_fused // T.int64(4) * T.int64(4) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(4) + ax0_ax1_ax2_ax3_fused_2) // T.int64(128)) + v2 = T.axis.spatial((n + T.int64(31)) // T.int64(32) * T.int64(32), i3_0 * T.int64(32) + i0_0_i1_0_i2_0_i3_1_0_fused % T.int64(4) * T.int64(8) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(4) + ax0_ax1_ax2_ax3_fused_2) % T.int64(128) // T.int64(16)) + v3 = T.axis.spatial(T.int64(128), k_0 * T.int64(16) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(4) + ax0_ax1_ax2_ax3_fused_2) % T.int64(16)) + T.reads(lv2733[v0, v1, v2, v3]) + T.writes(lv2733_pad_shared[v0, v1, v2, v3]) + lv2733_pad_shared[v0, v1, v2, v3] = T.if_then_else(v2 < n, lv2733[v0, v1, v2, v3], T.float32(0)) + for k_1, i0_3, i1_3, i2_3, i3_1_3, k_2, i0_4, i1_4, i2_4, i3_1_4 in T.grid(T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(16), T.int64(1), T.int64(1), T.int64(1), T.int64(1)): + with T.block("NT_matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), i0_3 + i0_4) + v_i1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_0_i3_1_0_fused // T.int64(4) * T.int64(4) + i0_2_i1_2_i2_2_i3_1_2_fused // T.int64(8) + i1_3 + i1_4) + v_i2 = T.axis.spatial(T.int64(1), i2_3 + i2_4) + v_i3 = T.axis.spatial((n + T.int64(31)) // T.int64(32) * T.int64(32), i3_0 * T.int64(32) + i0_0_i1_0_i2_0_i3_1_0_fused % T.int64(4) * T.int64(8) + i0_2_i1_2_i2_2_i3_1_2_fused % T.int64(8) + i3_1_3 + i3_1_4) + v_k = T.axis.reduce(T.int64(128), k_0 * T.int64(16) + k_1 * T.int64(16) + k_2) + T.reads(var_NT_matmul_intermediate_pad_local[v_i0, v_i1, v_i2, v_i3], lv2732_shared[v_i0, v_i1, v_i2, v_k], lv2733_pad_shared[v_i0, v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate_pad_local[v_i0, v_i1, v_i2, v_i3]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + var_NT_matmul_intermediate_pad_local[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate_pad_local[v_i0, v_i1, v_i2, v_i3] + lv2732_shared[v_i0, v_i1, v_i2, v_k] * lv2733_pad_shared[v_i0, v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_NT_matmul_intermediate_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_0_i3_1_0_fused // T.int64(4) * T.int64(4) + i0_2_i1_2_i2_2_i3_1_2_fused // T.int64(8) + ax1) + v2 = T.axis.spatial(T.int64(1), ax2) + v3 = T.axis.spatial((n + T.int64(31)) // T.int64(32) * T.int64(32), i3_0 * T.int64(32) + i0_0_i1_0_i2_0_i3_1_0_fused % T.int64(4) * T.int64(8) + i0_2_i1_2_i2_2_i3_1_2_fused % T.int64(8) + ax3) + T.reads(var_NT_matmul_intermediate_pad_local[v0, v1, v2, v3]) + T.writes(var_NT_matmul_intermediate[v0, v1, v2, v3]) + if v3 < n: + var_NT_matmul_intermediate[v0, v1, v2, v3] = var_NT_matmul_intermediate_pad_local[v0, v1, v2, v3] + for ax0_ax1_ax2_ax3_fused_0 in T.thread_binding(n, thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 64, "pragma_unroll_explicit": 1}): + for ax0_ax1_ax2_ax3_fused_1 in T.thread_binding(T.int64(32), thread="threadIdx.x"): + with T.block("T_add"): + v_ax0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_ax1 = T.axis.spatial(T.int64(32), (ax0_ax1_ax2_ax3_fused_0 * T.int64(32) + ax0_ax1_ax2_ax3_fused_1) // n) + v_ax2 = T.axis.spatial(T.int64(1), T.int64(0)) + v_ax3 = T.axis.spatial(n, (ax0_ax1_ax2_ax3_fused_0 * T.int64(32) + ax0_ax1_ax2_ax3_fused_1) % n) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv2709[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float32(0.088388349161020605) + lv2709[v_ax0, T.int64(0), v_ax2, v_ax3], T.float32(-3.4028234663852886e+38)) + + +@T.prim_func +def fused_NT_matmul2_multiply_before(p_lv43: T.handle, linear_weight6: T.Buffer((T.int64(11008), T.int64(4096)), "float32"), p_lv48: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv43 = T.match_buffer(p_lv43, (T.int64(1), n, T.int64(4096))) + lv48 = T.match_buffer(p_lv48, (T.int64(1), n, T.int64(11008))) + var_T_multiply_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(11008))) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(11008))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(11008), T.int64(4096)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv43[v_i0, v_i1, v_k], linear_weight6[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv43[v_i0, v_i1, v_k] * linear_weight6[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv48[v_ax0, v_ax1, v_ax2], var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2] = lv48[v_ax0, v_ax1, v_ax2] * var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_NT_matmul2_multiply_after(p_lv37: T.handle, linear_weight6: T.Buffer((T.int64(11008), T.int64(4096)), "float32"), p_lv42: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + lv37 = T.match_buffer(p_lv37, (T.int64(1), n, T.int64(4096))) + lv42 = T.match_buffer(p_lv42, (T.int64(1), n, T.int64(11008))) + var_T_multiply_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(11008))) + # with T.block("root"): + for i1_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.y"): + with T.block("NT_matmul_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i1_0) + T.reads(lv37[T.Add(v_i0, T.int64(0)), v_i1_o * T.int64(32):v_i1_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(4096)], linear_weight6[T.int64(0):T.int64(11008), T.int64(0):T.int64(4096)], lv42[v_i0, v_i1_o * T.int64(32):v_i1_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(11008)]) + T.writes(var_T_multiply_intermediate[v_i0, v_i1_o * T.int64(32):v_i1_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(11008)]) + var_NT_matmul_intermediate_pad_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(11008)), scope="local") + lv37_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(4096)), scope="shared") + linear_weight6_shared = T.alloc_buffer((T.int64(11008), T.int64(4096)), scope="shared") + for i0_0_i1_1_0_i2_0_fused in T.thread_binding(T.int64(344), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i0_1_i1_1_1_i2_1_fused in T.thread_binding(T.int64(1), thread="vthread.x"): + for i0_2_i1_1_2_i2_2_fused in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for i1_1_3_init, i2_3_init, i1_1_4_init, i2_4_init in T.grid(T.int64(2), T.int64(2), T.int64(2), T.int64(2)): + with T.block("NT_matmul_init"): + v_i1_i = T.axis.spatial(T.int64(32), i0_2_i1_1_2_i2_2_fused // T.int64(8) * T.int64(4) + i1_1_3_init * T.int64(2) + i1_1_4_init) + v_i2_i = T.axis.spatial(T.int64(11008), i0_0_i1_1_0_i2_0_fused * T.int64(32) + i0_2_i1_1_2_i2_2_fused % T.int64(8) * T.int64(4) + i2_3_init * T.int64(2) + i2_4_init) + T.reads() + T.writes(var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i] = T.float32(0) + for k_0 in range(T.int64(128)): + for ax0_ax1_ax2_fused_0 in range(T.int64(4)): + for ax0_ax1_ax2_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_ax2_fused_2 in T.vectorized(T.int64(4)): + with T.block("lv37_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), (ax0_ax1_ax2_fused_0 * T.int64(256) + ax0_ax1_ax2_fused_1 * T.int64(4) + ax0_ax1_ax2_fused_2) // T.int64(32)) + v2 = T.axis.spatial(T.int64(4096), k_0 * T.int64(32) + (ax0_ax1_ax2_fused_0 * T.int64(256) + ax0_ax1_ax2_fused_1 * T.int64(4) + ax0_ax1_ax2_fused_2) % T.int64(32)) + T.reads(lv37[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2]) + T.writes(lv37_pad_shared[v0, v1, v2]) + lv37_pad_shared[v0, v1, v2] = T.if_then_else(v_i1_o * T.int64(32) + v1 < n, lv37[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2], T.float32(0)) + for ax0_ax1_fused_0 in range(T.int64(8)): + for ax0_ax1_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_fused_2 in T.vectorized(T.int64(2)): + with T.block("linear_weight6_shared"): + v0 = T.axis.spatial(T.int64(11008), i0_0_i1_1_0_i2_0_fused * T.int64(32) + (ax0_ax1_fused_0 * T.int64(128) + ax0_ax1_fused_1 * T.int64(2) + ax0_ax1_fused_2) // T.int64(32)) + v1 = T.axis.spatial(T.int64(4096), k_0 * T.int64(32) + (ax0_ax1_fused_0 * T.int64(128) + ax0_ax1_fused_1 * T.int64(2) + ax0_ax1_fused_2) % T.int64(32)) + T.reads(linear_weight6[v0, v1]) + T.writes(linear_weight6_shared[v0, v1]) + linear_weight6_shared[v0, v1] = linear_weight6[v0, v1] + for k_1, i0_3, i1_1_3, i2_3, k_2, i0_4, i1_1_4, i2_4 in T.grid(T.int64(8), T.int64(1), T.int64(2), T.int64(2), T.int64(4), T.int64(1), T.int64(2), T.int64(2)): + with T.block("NT_matmul_update"): + v_i1_i = T.axis.spatial(T.int64(32), i0_2_i1_1_2_i2_2_fused // T.int64(8) * T.int64(4) + i1_1_3 * T.int64(2) + i1_1_4) + v_i2_i = T.axis.spatial(T.int64(11008), i0_0_i1_1_0_i2_0_fused * T.int64(32) + i0_2_i1_1_2_i2_2_fused % T.int64(8) * T.int64(4) + i2_3 * T.int64(2) + i2_4) + v_k_i = T.axis.reduce(T.int64(4096), k_0 * T.int64(32) + k_1 * T.int64(4) + k_2) + T.reads(var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i], lv37_pad_shared[T.int64(0), v_i1_i, v_k_i], linear_weight6_shared[v_i2_i, v_k_i]) + T.writes(var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i] = var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i] + lv37_pad_shared[T.int64(0), v_i1_i, v_k_i] * linear_weight6_shared[v_i2_i, v_k_i] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(4), T.int64(4)): + with T.block("var_NT_matmul_intermediate_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(32), i0_2_i1_1_2_i2_2_fused // T.int64(8) * T.int64(4) + ax1) + v2 = T.axis.spatial(T.int64(11008), i0_0_i1_1_0_i2_0_fused * T.int64(32) + i0_2_i1_1_2_i2_2_fused % T.int64(8) * T.int64(4) + ax2) + T.reads(lv42[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2], var_NT_matmul_intermediate_pad_local[v0, v1, v2]) + T.writes(var_T_multiply_intermediate[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2]) + # if T.int64(0) <= v_i0 and v_i0 < T.int64(1) and T.int64(0) <= v_i1_o * T.int64(32) + v1 and v_i1_o * T.int64(32) + v1 < n: + if v_i1_o * T.int64(32) + v1 < n: + var_T_multiply_intermediate[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2] = lv42[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2] * var_NT_matmul_intermediate_pad_local[v0, v1, v2] + + +@T.prim_func +def fused_NT_matmul2_silu_before(p_lv43: T.handle, linear_weight4: T.Buffer((T.int64(11008), T.int64(4096)), "float32"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv43 = T.match_buffer(p_lv43, (T.int64(1), n, T.int64(4096))) + var_T_multiply_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(11008))) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(11008))) + compute = T.alloc_buffer((T.int64(1), n, T.int64(11008))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(11008), T.int64(4096)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv43[v_i0, v_i1, v_k], linear_weight4[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv43[v_i0, v_i1, v_k] * linear_weight4[v_i2, v_k] + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(11008)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.sigmoid(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], compute[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] * compute[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_NT_matmul2_silu_after(p_lv37: T.handle, linear_weight4: T.Buffer((T.int64(11008), T.int64(4096)), "float32"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + lv37 = T.match_buffer(p_lv37, (T.int64(1), n, T.int64(4096))) + var_T_multiply_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(11008))) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(11008))) + for i1_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.y"): + with T.block("NT_matmul_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i1_0) + T.reads(lv37[T.Add(v_i0, T.int64(0)), v_i1_o * T.int64(32):v_i1_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(4096)], linear_weight4[T.int64(0):T.int64(11008), T.int64(0):T.int64(4096)]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1_o * T.int64(32):v_i1_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(11008)]) + var_NT_matmul_intermediate_pad_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(11008)), scope="local") + lv37_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(4096)), scope="shared") + linear_weight4_shared = T.alloc_buffer((T.int64(11008), T.int64(4096)), scope="shared") + for i0_0_i1_1_0_i2_0_fused in T.thread_binding(T.int64(344), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i0_1_i1_1_1_i2_1_fused in T.thread_binding(T.int64(1), thread="vthread.x"): + for i0_2_i1_1_2_i2_2_fused in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for i1_1_3_init, i2_3_init, i1_1_4_init, i2_4_init in T.grid(T.int64(2), T.int64(4), T.int64(2), T.int64(1)): + with T.block("NT_matmul_init"): + v_i1_i = T.axis.spatial(T.int64(32), i0_2_i1_1_2_i2_2_fused // T.int64(8) * T.int64(4) + i1_1_3_init * T.int64(2) + i1_1_4_init) + v_i2_i = T.axis.spatial(T.int64(11008), i2_4_init + i0_0_i1_1_0_i2_0_fused * T.int64(32) + i0_2_i1_1_2_i2_2_fused % T.int64(8) * T.int64(4) + i2_3_init) + T.reads() + T.writes(var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i] = T.float32(0) + for k_0 in range(T.int64(128)): + for ax0_ax1_ax2_fused_0 in range(T.int64(4)): + for ax0_ax1_ax2_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_ax2_fused_2 in T.vectorized(T.int64(4)): + with T.block("lv37_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), (ax0_ax1_ax2_fused_0 * T.int64(256) + ax0_ax1_ax2_fused_1 * T.int64(4) + ax0_ax1_ax2_fused_2) // T.int64(32)) + v2 = T.axis.spatial(T.int64(4096), k_0 * T.int64(32) + (ax0_ax1_ax2_fused_0 * T.int64(256) + ax0_ax1_ax2_fused_1 * T.int64(4) + ax0_ax1_ax2_fused_2) % T.int64(32)) + T.reads(lv37[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2]) + T.writes(lv37_pad_shared[v0, v1, v2]) + lv37_pad_shared[v0, v1, v2] = T.if_then_else(v_i1_o * T.int64(32) + v1 < n, lv37[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2], T.float32(0)) + for ax0_ax1_fused_0 in range(T.int64(8)): + for ax0_ax1_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_fused_2 in T.vectorized(T.int64(2)): + with T.block("linear_weight4_shared"): + v0 = T.axis.spatial(T.int64(11008), i0_0_i1_1_0_i2_0_fused * T.int64(32) + (ax0_ax1_fused_0 * T.int64(128) + ax0_ax1_fused_1 * T.int64(2) + ax0_ax1_fused_2) // T.int64(32)) + v1 = T.axis.spatial(T.int64(4096), k_0 * T.int64(32) + (ax0_ax1_fused_0 * T.int64(128) + ax0_ax1_fused_1 * T.int64(2) + ax0_ax1_fused_2) % T.int64(32)) + T.reads(linear_weight4[v0, v1]) + T.writes(linear_weight4_shared[v0, v1]) + linear_weight4_shared[v0, v1] = linear_weight4[v0, v1] + for k_1, i0_3, i1_1_3, i2_3, k_2, i0_4, i1_1_4, i2_4 in T.grid(T.int64(8), T.int64(1), T.int64(2), T.int64(4), T.int64(4), T.int64(1), T.int64(2), T.int64(1)): + with T.block("NT_matmul_update"): + v_i1_i = T.axis.spatial(T.int64(32), i0_2_i1_1_2_i2_2_fused // T.int64(8) * T.int64(4) + i1_1_3 * T.int64(2) + i1_1_4) + v_i2_i = T.axis.spatial(T.int64(11008), i2_4 + i0_0_i1_1_0_i2_0_fused * T.int64(32) + i0_2_i1_1_2_i2_2_fused % T.int64(8) * T.int64(4) + i2_3) + v_k_i = T.axis.reduce(T.int64(4096), k_0 * T.int64(32) + k_1 * T.int64(4) + k_2) + T.reads(var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i], lv37_pad_shared[T.int64(0), v_i1_i, v_k_i], linear_weight4_shared[v_i2_i, v_k_i]) + T.writes(var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i] = var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i] + lv37_pad_shared[T.int64(0), v_i1_i, v_k_i] * linear_weight4_shared[v_i2_i, v_k_i] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(4), T.int64(4)): + with T.block("var_NT_matmul_intermediate_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(32), i0_2_i1_1_2_i2_2_fused // T.int64(8) * T.int64(4) + ax1) + v2 = T.axis.spatial(T.int64(11008), i0_0_i1_1_0_i2_0_fused * T.int64(32) + i0_2_i1_1_2_i2_2_fused % T.int64(8) * T.int64(4) + ax2) + T.reads(var_NT_matmul_intermediate_pad_local[v0, v1, v2]) + T.writes(var_NT_matmul_intermediate[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2]) + # if T.int64(0) <= v_i0 and v_i0 < T.int64(1) and T.int64(0) <= v_i1_o * T.int64(32) + v1 and v_i1_o * T.int64(32) + v1 < n: + if v_i1_o * T.int64(32) + v1 < n: + var_NT_matmul_intermediate[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2] = var_NT_matmul_intermediate_pad_local[v0, v1, v2] + for ax0_ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for ax0_ax1_ax2_fused_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0_ax1_ax2_fused_0 in range((n * T.int64(11008) + T.int64(65535)) // T.int64(65536)): + with T.block("T_multiply"): + v_ax0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_ax1 = T.axis.spatial(n, (ax0_ax1_ax2_fused_0 * T.int64(65536) + ax0_ax1_ax2_fused_1 * T.int64(256) + ax0_ax1_ax2_fused_2) // T.int64(11008)) + v_ax2 = T.axis.spatial(T.int64(11008), (ax0_ax1_ax2_fused_0 * T.int64(65536) + ax0_ax1_ax2_fused_1 * T.int64(256) + ax0_ax1_ax2_fused_2) % T.int64(11008)) + T.where((ax0_ax1_ax2_fused_0 * T.int64(256) + ax0_ax1_ax2_fused_1) * T.int64(256) + ax0_ax1_ax2_fused_2 < n * T.int64(11008)) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] * T.sigmoid(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + + +@T.prim_func +def fused_NT_matmul3_add1_before(p_lv49: T.handle, linear_weight5: T.Buffer((T.int64(4096), T.int64(11008)), "float32"), p_lv42: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv49 = T.match_buffer(p_lv49, (T.int64(1), n, T.int64(11008))) + lv42 = T.match_buffer(p_lv42, (T.int64(1), n, T.int64(4096))) + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(4096))) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(4096))) + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(4096), T.int64(11008)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv49[v_i0, v_i1, v_k], linear_weight5[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv49[v_i0, v_i1, v_k] * linear_weight5[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv42[v_ax0, v_ax1, v_ax2], var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = lv42[v_ax0, v_ax1, v_ax2] + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_NT_matmul3_add1_after(p_lv43: T.handle, linear_weight5: T.Buffer((T.int64(4096), T.int64(11008)), "float32"), p_lv36: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + lv43 = T.match_buffer(p_lv43, (T.int64(1), n, T.int64(11008))) + lv36 = T.match_buffer(p_lv36, (T.int64(1), n, T.int64(4096))) + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(4096))) + # with T.block("root"): + for i1_0 in T.thread_binding((n + T.int64(31)) // T.int64(32), thread="blockIdx.y"): + with T.block("NT_matmul_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i1_0) + T.reads(lv43[T.Add(v_i0, T.int64(0)), v_i1_o * T.int64(32):v_i1_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(11008)], linear_weight5[T.int64(0):T.int64(4096), T.int64(0):T.int64(11008)], lv36[v_i0, v_i1_o * T.int64(32):v_i1_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(4096)]) + T.writes(var_T_add_intermediate[v_i0, v_i1_o * T.int64(32):v_i1_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(4096)]) + var_NT_matmul_intermediate_pad_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(4096)), scope="local") + lv43_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(11008)), scope="shared") + linear_weight5_shared = T.alloc_buffer((T.int64(4096), T.int64(11008)), scope="shared") + for i0_0_i1_1_0_i2_0_fused in T.thread_binding(T.int64(128), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i0_1_i1_1_1_i2_1_fused in T.thread_binding(T.int64(4), thread="vthread.x"): + for i0_2_i1_1_2_i2_2_fused in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for i1_1_3_init, i2_3_init, i1_1_4_init, i2_4_init in T.grid(T.int64(2), T.int64(2), T.int64(1), T.int64(1)): + with T.block("NT_matmul_init"): + v_i1_i = T.axis.spatial(T.int64(32), i0_1_i1_1_1_i2_1_fused // T.int64(2) * T.int64(16) + i0_2_i1_1_2_i2_2_fused // T.int64(8) * T.int64(2) + i1_1_3_init + i1_1_4_init) + v_i2_i = T.axis.spatial(T.int64(4096), i2_4_init + i0_0_i1_1_0_i2_0_fused * T.int64(32) + i0_1_i1_1_1_i2_1_fused % T.int64(2) * T.int64(16) + i0_2_i1_1_2_i2_2_fused % T.int64(8) * T.int64(2) + i2_3_init) + T.reads() + T.writes(var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i] = T.float32(0) + for k_0 in range(T.int64(344)): + for ax0_ax1_ax2_fused_0 in range(T.int64(4)): + for ax0_ax1_ax2_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_ax2_fused_2 in T.vectorized(T.int64(4)): + with T.block("lv43_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), (ax0_ax1_ax2_fused_0 * T.int64(256) + ax0_ax1_ax2_fused_1 * T.int64(4) + ax0_ax1_ax2_fused_2) // T.int64(32)) + v2 = T.axis.spatial(T.int64(11008), k_0 * T.int64(32) + (ax0_ax1_ax2_fused_0 * T.int64(256) + ax0_ax1_ax2_fused_1 * T.int64(4) + ax0_ax1_ax2_fused_2) % T.int64(32)) + T.reads(lv43[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2]) + T.writes(lv43_pad_shared[v0, v1, v2]) + lv43_pad_shared[v0, v1, v2] = T.if_then_else(v_i1_o * T.int64(32) + v1 < n, lv43[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2], T.float32(0)) + for ax0_ax1_fused_0 in range(T.int64(8)): + for ax0_ax1_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_fused_2 in T.vectorized(T.int64(2)): + with T.block("linear_weight5_shared"): + v0 = T.axis.spatial(T.int64(4096), i0_0_i1_1_0_i2_0_fused * T.int64(32) + (ax0_ax1_fused_0 * T.int64(128) + ax0_ax1_fused_1 * T.int64(2) + ax0_ax1_fused_2) // T.int64(32)) + v1 = T.axis.spatial(T.int64(11008), k_0 * T.int64(32) + (ax0_ax1_fused_0 * T.int64(128) + ax0_ax1_fused_1 * T.int64(2) + ax0_ax1_fused_2) % T.int64(32)) + T.reads(linear_weight5[v0, v1]) + T.writes(linear_weight5_shared[v0, v1]) + linear_weight5_shared[v0, v1] = linear_weight5[v0, v1] + for k_1, i0_3, i1_1_3, i2_3, k_2, i0_4, i1_1_4, i2_4 in T.grid(T.int64(8), T.int64(1), T.int64(2), T.int64(2), T.int64(4), T.int64(1), T.int64(1), T.int64(1)): + with T.block("NT_matmul_update"): + v_i1_i = T.axis.spatial(T.int64(32), i0_1_i1_1_1_i2_1_fused // T.int64(2) * T.int64(16) + i0_2_i1_1_2_i2_2_fused // T.int64(8) * T.int64(2) + i1_1_3 + i1_1_4) + v_i2_i = T.axis.spatial(T.int64(4096), i2_4 + i0_0_i1_1_0_i2_0_fused * T.int64(32) + i0_1_i1_1_1_i2_1_fused % T.int64(2) * T.int64(16) + i0_2_i1_1_2_i2_2_fused % T.int64(8) * T.int64(2) + i2_3) + v_k_i = T.axis.reduce(T.int64(11008), k_0 * T.int64(32) + k_1 * T.int64(4) + k_2) + T.reads(var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i], lv43_pad_shared[T.int64(0), v_i1_i, v_k_i], linear_weight5_shared[v_i2_i, v_k_i]) + T.writes(var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i] = var_NT_matmul_intermediate_pad_local[T.int64(0), v_i1_i, v_i2_i] + lv43_pad_shared[T.int64(0), v_i1_i, v_k_i] * linear_weight5_shared[v_i2_i, v_k_i] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(2), T.int64(2)): + with T.block("var_NT_matmul_intermediate_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(32), i0_1_i1_1_1_i2_1_fused // T.int64(2) * T.int64(16) + i0_2_i1_1_2_i2_2_fused // T.int64(8) * T.int64(2) + ax1) + v2 = T.axis.spatial(T.int64(4096), i0_0_i1_1_0_i2_0_fused * T.int64(32) + i0_1_i1_1_1_i2_1_fused % T.int64(2) * T.int64(16) + i0_2_i1_1_2_i2_2_fused % T.int64(8) * T.int64(2) + ax2) + T.reads(lv36[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2], var_NT_matmul_intermediate_pad_local[v0, v1, v2]) + T.writes(var_T_add_intermediate[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2]) + # if T.int64(0) <= v_i0 and v_i0 < T.int64(1) and T.int64(0) <= v_i1_o * T.int64(32) + v1 and v_i1_o * T.int64(32) + v1 < n: + if v_i1_o * T.int64(32) + v1 < n: + var_T_add_intermediate[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2] = lv36[v_i0 + v0, v_i1_o * T.int64(32) + v1, v2] + var_NT_matmul_intermediate_pad_local[v0, v1, v2] + + + +@T.prim_func +def fused_NT_matmul_divide_maximum_minimum_cast_before(lv1605: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(128)), "float16"), p_lv1606: T.handle, p_lv1582: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv1606 = T.match_buffer(p_lv1606, (T.int64(1), T.int64(32), n, T.int64(128)), "float16") + lv1582 = T.match_buffer(p_lv1582, (T.int64(1), T.int64(1), T.int64(1), n), "float16") + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), T.int64(1), n)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + var_T_maximum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + var_T_minimum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n), "float16") + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n, T.int64(128)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv1605[v_i0, v_i1, v_i2, v_k], lv1606[v_i0, v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv1605[v_i0, v_i1, v_i2, v_k] * lv1606[v_i0, v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float16(0.088397790055248615) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float16(-65504)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_minimum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv1582[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.min(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv1582[v_ax0, T.int64(0), v_ax2, v_ax3]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("compute"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3]) + var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.Cast("float32", var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + +def fused_NT_matmul_divide_maximum_minimum_cast_sch_func(): + sch = tvm.tir.Schedule(fused_NT_matmul_divide_maximum_minimum_cast_before) + b_cast = sch.get_block("compute") + sch.reverse_compute_inline(b_cast) + b0 = sch.get_block("NT_matmul") + sch.pad_einsum(b0, [1, 1, 1, 32, 1]) + l1, l2, l3, l4, l5 = sch.get_loops(b0) + l6, l7 = sch.split(l4, [None, 32]) + sch.reorder(l6, l1, l2, l3, l7, l5) + + b0 = sch.get_block(name="NT_matmul", func_name="main") + b1 = sch.get_block(name="T_divide", func_name="main") + b2 = sch.get_block(name="T_maximum", func_name="main") + b3 = sch.get_block(name="T_minimum", func_name="main") + b4 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l5, l6, l7, l8, l9 = sch.get_loops(block=b0) + v10, v11, v12, v13, v14 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l15, l16, l17, l18, l19 = sch.split(loop=l5, factors=[v10, v11, v12, v13, v14], preserve_unit_iters=True) + v20, v21, v22, v23, v24 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[8, 1, 4, 1, 1]) + l25, l26, l27, l28, l29 = sch.split(loop=l6, factors=[v20, v21, v22, v23, v24], preserve_unit_iters=True) + v30, v31, v32, v33, v34 = sch.sample_perfect_tile(loop=l7, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l35, l36, l37, l38, l39 = sch.split(loop=l7, factors=[v30, v31, v32, v33, v34], preserve_unit_iters=True) + v40, v41, v42, v43, v44 = sch.sample_perfect_tile(loop=l8, n=5, max_innermost_factor=64, decision=[2, 1, 16, 1, 1]) + l45, l46, l47, l48, l49 = sch.split(loop=l8, factors=[v40, v41, v42, v43, v44], preserve_unit_iters=True) + v50, v51, v52 = sch.sample_perfect_tile(loop=l9, n=3, max_innermost_factor=64, decision=[4, 4, 8]) + l53, l54, l55 = sch.split(loop=l9, factors=[v50, v51, v52], preserve_unit_iters=True) + sch.reorder(l15, l25, l35, l45, l16, l26, l36, l46, l17, l27, l37, l47, l53, l54, l18, l28, l38, l48, l55, l19, l29, l39, l49) + l56 = sch.fuse(l15, l25, l35, l45, preserve_unit_iters=True) + sch.bind(loop=l56, thread_axis="blockIdx.x") + l57 = sch.fuse(l16, l26, l36, l46, preserve_unit_iters=True) + sch.bind(loop=l57, thread_axis="vthread.x") + l58 = sch.fuse(l17, l27, l37, l47, preserve_unit_iters=True) + sch.bind(loop=l58, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b59 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b59, loop=l58, preserve_unit_loops=True, index=-1) + b60 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b60, loop=l53, preserve_unit_loops=True, index=-1) + _, l61, l62, l63, l64, l65, l66, l67, l68 = sch.get_loops(block=b60) + l69 = sch.fuse(l65, l66, l67, l68, preserve_unit_iters=True) + v70 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=0) + sch.annotate(block_or_loop=b60, ann_key="meta_schedule.cooperative_fetch", ann_val=v70) + b71 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b71, loop=l53, preserve_unit_loops=True, index=-1) + _, l72, l73, l74, l75, l76, l77, l78, l79 = sch.get_loops(block=b71) + l80 = sch.fuse(l76, l77, l78, l79, preserve_unit_iters=True) + v81 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=0) + sch.annotate(block_or_loop=b71, ann_key="meta_schedule.cooperative_fetch", ann_val=v81) + sch.reverse_compute_inline(block=b3) + sch.compute_inline(block=b1) + v82 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=1) + sch.annotate(block_or_loop=b4, ann_key="meta_schedule.unroll_explicit", ann_val=v82) + + # inline ewise + sch.reverse_compute_inline(b2) + # l83, l84, l85, l86 = sch.get_loops(block=b2) + # l87 = sch.fuse(l83, l84, l85, l86, preserve_unit_iters=True) + # v88 = sch.sample_categorical(candidates=[32, 64, 128, 256], probs=[0.25, 0.25, 0.25, 0.25], decision=0) + # l89, l90 = sch.split(loop=l87, factors=[None, v88], preserve_unit_iters=True) + # sch.bind(loop=l89, thread_axis="blockIdx.x") + # sch.bind(loop=l90, thread_axis="threadIdx.x") + + sch.enter_postproc() + sch.unannotate(block_or_loop=b60, ann_key="meta_schedule.cooperative_fetch") + _, l91, l92, l93, l94, l95 = sch.get_loops(block=b60) + l96, l97 = sch.split(loop=l95, factors=[None, 64], preserve_unit_iters=True) + sch.bind(loop=l97, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b71, ann_key="meta_schedule.cooperative_fetch") + _, l98, l99, l100, l101, l102 = sch.get_loops(block=b71) + l103, l104 = sch.split(loop=l102, factors=[None, 64], preserve_unit_iters=True) + sch.bind(loop=l104, thread_axis="threadIdx.x") + b105 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b105, ann_key="meta_schedule.unroll_explicit") + _, b106, b107, b108, b109, _ = sch.get_child_blocks(b105) + _, l111, l112, l113, l114, l115, l116 = sch.get_loops(block=b106) + sch.annotate(block_or_loop=l111, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l111, ann_key="pragma_unroll_explicit", ann_val=1) + _, l117, l118, l119, l120, l121, l122 = sch.get_loops(block=b107) + sch.annotate(block_or_loop=l117, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l117, ann_key="pragma_unroll_explicit", ann_val=1) + _, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132, l133, l134, l135, l136 = sch.get_loops(block=b108) + sch.annotate(block_or_loop=l123, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l123, ann_key="pragma_unroll_explicit", ann_val=1) + _, l137, l138, l139, l140, l141, l142, l143 = sch.get_loops(block=b109) + sch.annotate(block_or_loop=l137, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l137, ann_key="pragma_unroll_explicit", ann_val=1) + + b146 = sch.get_block(name="NT_matmul", func_name="main") + l0, l147, l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, l158, l159, l160 = sch.get_loops(block=b146) + sch.bind(l0, "blockIdx.y") + b161 = sch.decompose_reduction(block=b146, loop=l150) + + b1 = sch.get_block("lv1606_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + +@T.prim_func +def fused_NT_matmul_divide_maximum_minimum_before(lv1540: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(128)), "float32"), p_lv1541: T.handle, p_lv1517: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv1541 = T.match_buffer(p_lv1541, (T.int64(1), T.int64(32), n, T.int64(128))) + lv1517 = T.match_buffer(p_lv1517, (T.int64(1), T.int64(1), T.int64(1), n)) + var_T_minimum_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), T.int64(1), n)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + var_T_maximum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(1), n)) + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), T.int64(1), n, T.int64(128)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv1540[v_i0, v_i1, v_i2, v_k], lv1541[v_i0, v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv1540[v_i0, v_i1, v_i2, v_k] * lv1541[v_i0, v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float32(0.088388349161020605) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float32(-3.4028234663852886e+38)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), T.int64(1), n): + with T.block("T_minimum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv1517[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.min(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv1517[v_ax0, T.int64(0), v_ax2, v_ax3]) + +def fused_NT_matmul_divide_maximum_minimum_sch_func(): + sch = tvm.tir.Schedule(fused_NT_matmul_divide_maximum_minimum_before) + b0 = sch.get_block("NT_matmul") + sch.pad_einsum(b0, [1, 1, 1, 32, 1]) + l1, l2, l3, l4, l5 = sch.get_loops(b0) + l6, l7 = sch.split(l4, [None, 32]) + sch.reorder(l6, l1, l2, l3, l7, l5) + + b0 = sch.get_block(name="NT_matmul", func_name="main") + b1 = sch.get_block(name="T_divide", func_name="main") + b2 = sch.get_block(name="T_maximum", func_name="main") + b3 = sch.get_block(name="T_minimum", func_name="main") + b4 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l5, l6, l7, l8, l9 = sch.get_loops(block=b0) + v10, v11, v12, v13, v14 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l15, l16, l17, l18, l19 = sch.split(loop=l5, factors=[v10, v11, v12, v13, v14], preserve_unit_iters=True) + v20, v21, v22, v23, v24 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[8, 1, 4, 1, 1]) + l25, l26, l27, l28, l29 = sch.split(loop=l6, factors=[v20, v21, v22, v23, v24], preserve_unit_iters=True) + v30, v31, v32, v33, v34 = sch.sample_perfect_tile(loop=l7, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l35, l36, l37, l38, l39 = sch.split(loop=l7, factors=[v30, v31, v32, v33, v34], preserve_unit_iters=True) + v40, v41, v42, v43, v44 = sch.sample_perfect_tile(loop=l8, n=5, max_innermost_factor=64, decision=[2, 1, 16, 1, 1]) + l45, l46, l47, l48, l49 = sch.split(loop=l8, factors=[v40, v41, v42, v43, v44], preserve_unit_iters=True) + v50, v51, v52 = sch.sample_perfect_tile(loop=l9, n=3, max_innermost_factor=64, decision=[4, 4, 8]) + l53, l54, l55 = sch.split(loop=l9, factors=[v50, v51, v52], preserve_unit_iters=True) + sch.reorder(l15, l25, l35, l45, l16, l26, l36, l46, l17, l27, l37, l47, l53, l54, l18, l28, l38, l48, l55, l19, l29, l39, l49) + l56 = sch.fuse(l15, l25, l35, l45, preserve_unit_iters=True) + sch.bind(loop=l56, thread_axis="blockIdx.x") + l57 = sch.fuse(l16, l26, l36, l46, preserve_unit_iters=True) + sch.bind(loop=l57, thread_axis="vthread.x") + l58 = sch.fuse(l17, l27, l37, l47, preserve_unit_iters=True) + sch.bind(loop=l58, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b59 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b59, loop=l58, preserve_unit_loops=True, index=-1) + b60 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b60, loop=l53, preserve_unit_loops=True, index=-1) + _, l61, l62, l63, l64, l65, l66, l67, l68 = sch.get_loops(block=b60) + l69 = sch.fuse(l65, l66, l67, l68, preserve_unit_iters=True) + v70 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=0) + sch.annotate(block_or_loop=b60, ann_key="meta_schedule.cooperative_fetch", ann_val=v70) + b71 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b71, loop=l53, preserve_unit_loops=True, index=-1) + _, l72, l73, l74, l75, l76, l77, l78, l79 = sch.get_loops(block=b71) + l80 = sch.fuse(l76, l77, l78, l79, preserve_unit_iters=True) + v81 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=0) + sch.annotate(block_or_loop=b71, ann_key="meta_schedule.cooperative_fetch", ann_val=v81) + sch.reverse_compute_inline(block=b3) + sch.compute_inline(block=b1) + v82 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=1) + sch.annotate(block_or_loop=b4, ann_key="meta_schedule.unroll_explicit", ann_val=v82) + + # inline ewise + sch.reverse_compute_inline(b2) + # l83, l84, l85, l86 = sch.get_loops(block=b2) + # l87 = sch.fuse(l83, l84, l85, l86, preserve_unit_iters=True) + # v88 = sch.sample_categorical(candidates=[32, 64, 128, 256], probs=[0.25, 0.25, 0.25, 0.25], decision=0) + # l89, l90 = sch.split(loop=l87, factors=[None, v88], preserve_unit_iters=True) + # sch.bind(loop=l89, thread_axis="blockIdx.x") + # sch.bind(loop=l90, thread_axis="threadIdx.x") + + sch.enter_postproc() + sch.unannotate(block_or_loop=b60, ann_key="meta_schedule.cooperative_fetch") + _, l91, l92, l93, l94, l95 = sch.get_loops(block=b60) + l96, l97 = sch.split(loop=l95, factors=[None, 64], preserve_unit_iters=True) + sch.bind(loop=l97, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b71, ann_key="meta_schedule.cooperative_fetch") + _, l98, l99, l100, l101, l102 = sch.get_loops(block=b71) + l103, l104 = sch.split(loop=l102, factors=[None, 64], preserve_unit_iters=True) + sch.bind(loop=l104, thread_axis="threadIdx.x") + b105 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b105, ann_key="meta_schedule.unroll_explicit") + _, b106, b107, b108, b109, _ = sch.get_child_blocks(b105) + _, l111, l112, l113, l114, l115, l116 = sch.get_loops(block=b106) + sch.annotate(block_or_loop=l111, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l111, ann_key="pragma_unroll_explicit", ann_val=1) + _, l117, l118, l119, l120, l121, l122 = sch.get_loops(block=b107) + sch.annotate(block_or_loop=l117, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l117, ann_key="pragma_unroll_explicit", ann_val=1) + _, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132, l133, l134, l135, l136 = sch.get_loops(block=b108) + sch.annotate(block_or_loop=l123, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l123, ann_key="pragma_unroll_explicit", ann_val=1) + _, l137, l138, l139, l140, l141, l142, l143 = sch.get_loops(block=b109) + sch.annotate(block_or_loop=l137, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l137, ann_key="pragma_unroll_explicit", ann_val=1) + + b146 = sch.get_block(name="NT_matmul", func_name="main") + l0, l147, l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, l158, l159, l160 = sch.get_loops(block=b146) + sch.bind(l0, "blockIdx.y") + b161 = sch.decompose_reduction(block=b146, loop=l150) + + b1 = sch.get_block("lv1541_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + +@T.prim_func +def fused_NT_matmul1_add3_before(p_lv39: T.handle, lv1848: T.Buffer((T.int64(4096), T.int64(4096)), "float16"), p_lv2: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv39 = T.match_buffer(p_lv39, (T.int64(1), n, T.int64(4096)), "float16") + lv2 = T.match_buffer(p_lv2, (T.int64(1), n, T.int64(4096)), "float16") + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(4096)), "float16") + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(4096)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(4096), T.int64(4096)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv39[v_i0, v_i1, v_k], lv1848[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv39[v_i0, v_i1, v_k] * lv1848[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv2[v_ax0, v_ax1, v_ax2], var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = lv2[v_ax0, v_ax1, v_ax2] + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +def fused_NT_matmul1_add3_sch_func(): + sch = tvm.tir.Schedule(fused_NT_matmul1_add3_before) + b0 = sch.get_block("NT_matmul") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + b0 = sch.get_block(name="NT_matmul", func_name="main") + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l3, l4, l5, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l3, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 2, 8, 1, 2]) + l22, l23, l24, l25, l26 = sch.split(loop=l4, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[256, 1, 4, 4, 1]) + l32, l33, l34, l35, l36 = sch.split(loop=l5, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[256, 1, 16]) + l40, l41, l42 = sch.split(loop=l6, factors=[v37, v38, v39], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l13, l23, l33, l14, l24, l34, l40, l41, l15, l25, l35, l42, l16, l26, l36) + l43 = sch.fuse(l12, l22, l32, preserve_unit_iters=True) + sch.bind(loop=l43, thread_axis="blockIdx.x") + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="vthread.x") + l45 = sch.fuse(l14, l24, l34, preserve_unit_iters=True) + sch.bind(loop=l45, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b46 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b46, loop=l45, preserve_unit_loops=True, index=-1) + b47 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b47, loop=l40, preserve_unit_loops=True, index=-1) + _, l48, l49, l50, l51, l52, l53, l54 = sch.get_loops(block=b47) + l55 = sch.fuse(l52, l53, l54, preserve_unit_iters=True) + v56 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch", ann_val=v56) + b57 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l40, preserve_unit_loops=True, index=-1) + _, l58, l59, l60, l61, l62, l63 = sch.get_loops(block=b57) + l64 = sch.fuse(l62, l63, preserve_unit_iters=True) + v65 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v65) + sch.reverse_compute_inline(block=b1) + v66 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=2) + sch.annotate(block_or_loop=b2, ann_key="meta_schedule.unroll_explicit", ann_val=v66) + sch.enter_postproc() + sch.unannotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch") + _, l67, l68, l69, l70, l71 = sch.get_loops(block=b47) + l72, l73, l74 = sch.split(loop=l71, factors=[None, 32, 2], preserve_unit_iters=True) + sch.vectorize(loop=l74) + sch.bind(loop=l73, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + _, l75, l76, l77, l78, l79 = sch.get_loops(block=b57) + l80, l81, l82 = sch.split(loop=l79, factors=[None, 32, 4], preserve_unit_iters=True) + sch.vectorize(loop=l82) + sch.bind(loop=l81, thread_axis="threadIdx.x") + b83 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b83, ann_key="meta_schedule.unroll_explicit") + _, b84, b85, b86, b87, _ = sch.get_child_blocks(b83) + _, l88, l89, l90, l91, l92, l93, l94 = sch.get_loops(block=b84) + sch.annotate(block_or_loop=l88, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l88, ann_key="pragma_unroll_explicit", ann_val=1) + _, l95, l96, l97, l98, l99, l100, l101 = sch.get_loops(block=b85) + sch.annotate(block_or_loop=l95, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l95, ann_key="pragma_unroll_explicit", ann_val=1) + _, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113 = sch.get_loops(block=b86) + sch.annotate(block_or_loop=l102, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l102, ann_key="pragma_unroll_explicit", ann_val=1) + _, l114, l115, l116, l117, l118, l119 = sch.get_loops(block=b87) + sch.annotate(block_or_loop=l114, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l114, ann_key="pragma_unroll_explicit", ann_val=1) + b120 = sch.get_block(name="NT_matmul", func_name="main") + l0, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132 = sch.get_loops(block=b120) + sch.bind(l0, "blockIdx.y") + b133 = sch.decompose_reduction(block=b120, loop=l124) + + b1 = sch.get_block("lv39_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func +def fused_NT_matmul2_divide1_add2_maximum1_before(p_lv28: T.handle, p_lv29: T.handle, p_lv5: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv28 = T.match_buffer(p_lv28, (T.int64(1), T.int64(32), n, T.int64(128)), "float16") + lv29 = T.match_buffer(p_lv29, (T.int64(1), T.int64(32), n, T.int64(128)), "float16") + lv5 = T.match_buffer(p_lv5, (T.int64(1), T.int64(1), n, n), "float16") + var_T_maximum_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, n), "float16") + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, n), "float16") + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, n), "float16") + var_T_add_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, n), "float16") + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, n, T.int64(128)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv28[v_i0, v_i1, v_i2, v_k], lv29[v_i0, v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv28[v_i0, v_i1, v_i2, v_k] * lv29[v_i0, v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, n): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float16(0.088397790055248615) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, n): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv5[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] + lv5[v_ax0, T.int64(0), v_ax2, v_ax3] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, n): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_add_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_add_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float16(-65504)) + + +def fused_NT_matmul2_divide1_add2_maximum1_sch_func(func): + sch = tvm.tir.Schedule(func) + b0 = sch.get_block("NT_matmul") + sch.pad_einsum(b0, [1, 1, 32, 32, 1]) + l1, l2, l3, l4, l5 = sch.get_loops(b0) + l6, l7 = sch.split(l3, [None, 32]) + l8, l9 = sch.split(l4, [None, 32]) + sch.reorder(l6, l8, l1, l2, l7, l9, l5) + + b0 = sch.get_block(name="NT_matmul", func_name="main") + b1 = sch.get_block(name="T_divide", func_name="main") + b2 = sch.get_block(name="T_add", func_name="main") + b3 = sch.get_block(name="T_maximum", func_name="main") + b4 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, _, l5, l6, l7, l8, l9 = sch.get_loops(block=b0) + v10, v11, v12, v13, v14 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l15, l16, l17, l18, l19 = sch.split(loop=l5, factors=[v10, v11, v12, v13, v14], preserve_unit_iters=True) + v20, v21, v22, v23, v24 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[32, 1, 1, 1, 1]) + l25, l26, l27, l28, l29 = sch.split(loop=l6, factors=[v20, v21, v22, v23, v24], preserve_unit_iters=True) + v30, v31, v32, v33, v34 = sch.sample_perfect_tile(loop=l7, n=5, max_innermost_factor=64, decision=[2, 1, 8, 1, 2]) + l35, l36, l37, l38, l39 = sch.split(loop=l7, factors=[v30, v31, v32, v33, v34], preserve_unit_iters=True) + v40, v41, v42, v43, v44 = sch.sample_perfect_tile(loop=l8, n=5, max_innermost_factor=64, decision=[2, 1, 8, 1, 2]) + l45, l46, l47, l48, l49 = sch.split(loop=l8, factors=[v40, v41, v42, v43, v44], preserve_unit_iters=True) + v50, v51, v52 = sch.sample_perfect_tile(loop=l9, n=3, max_innermost_factor=64, decision=[8, 16, 1]) + l53, l54, l55 = sch.split(loop=l9, factors=[v50, v51, v52], preserve_unit_iters=True) + sch.reorder(l15, l25, l35, l45, l16, l26, l36, l46, l17, l27, l37, l47, l53, l54, l18, l28, l38, l48, l55, l19, l29, l39, l49) + l56 = sch.fuse(l15, l25, l35, l45, preserve_unit_iters=True) + sch.bind(loop=l56, thread_axis="blockIdx.x") + l57 = sch.fuse(l16, l26, l36, l46, preserve_unit_iters=True) + sch.bind(loop=l57, thread_axis="vthread.x") + l58 = sch.fuse(l17, l27, l37, l47, preserve_unit_iters=True) + sch.bind(loop=l58, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b59 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b59, loop=l58, preserve_unit_loops=True, index=-1) + b60 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b60, loop=l53, preserve_unit_loops=True, index=-1) + _, _, l61, l62, l63, l64, l65, l66, l67, l68 = sch.get_loops(block=b60) + l69 = sch.fuse(l65, l66, l67, l68, preserve_unit_iters=True) + v70 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b60, ann_key="meta_schedule.cooperative_fetch", ann_val=v70) + b71 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b71, loop=l53, preserve_unit_loops=True, index=-1) + _, _, l72, l73, l74, l75, l76, l77, l78, l79 = sch.get_loops(block=b71) + l80 = sch.fuse(l76, l77, l78, l79, preserve_unit_iters=True) + v81 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b71, ann_key="meta_schedule.cooperative_fetch", ann_val=v81) + sch.reverse_compute_inline(block=b3) + sch.compute_inline(block=b1) + v82 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=2) + sch.annotate(block_or_loop=b4, ann_key="meta_schedule.unroll_explicit", ann_val=v82) + l83, l84, l85, l86 = sch.get_loops(block=b2) + l87 = sch.fuse(l83, l84, l85, l86, preserve_unit_iters=True) + v88 = sch.sample_categorical(candidates=[32, 64, 128, 256], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + l89, l90 = sch.split(loop=l87, factors=[None, v88], preserve_unit_iters=True) + sch.bind(loop=l89, thread_axis="blockIdx.x") + sch.bind(loop=l90, thread_axis="threadIdx.x") + sch.enter_postproc() + sch.unannotate(block_or_loop=b60, ann_key="meta_schedule.cooperative_fetch") + _, _, l91, l92, l93, l94, l95 = sch.get_loops(block=b60) + l96, l97, l98 = sch.split(loop=l95, factors=[None, 64, 2], preserve_unit_iters=True) + sch.vectorize(loop=l98) + sch.bind(loop=l97, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b71, ann_key="meta_schedule.cooperative_fetch") + _, _, l99, l100, l101, l102, l103 = sch.get_loops(block=b71) + l104, l105, l106 = sch.split(loop=l103, factors=[None, 64, 2], preserve_unit_iters=True) + sch.vectorize(loop=l106) + sch.bind(loop=l105, thread_axis="threadIdx.x") + b107 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b107, ann_key="meta_schedule.unroll_explicit") + _, _, b108, b109, b110, b111, _, b112 = sch.get_child_blocks(b107) + _, _, l113, l114, l115, l116, l117, l118, l119 = sch.get_loops(block=b108) + sch.annotate(block_or_loop=l113, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l113, ann_key="pragma_unroll_explicit", ann_val=1) + _, _, l120, l121, l122, l123, l124, l125, l126 = sch.get_loops(block=b109) + sch.annotate(block_or_loop=l120, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l120, ann_key="pragma_unroll_explicit", ann_val=1) + _, _, l127, l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, l138, l139, l140 = sch.get_loops(block=b110) + sch.annotate(block_or_loop=l127, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l127, ann_key="pragma_unroll_explicit", ann_val=1) + _, _, l141, l142, l143, l144, l145, l146, l147 = sch.get_loops(block=b111) + sch.annotate(block_or_loop=l141, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l141, ann_key="pragma_unroll_explicit", ann_val=1) + l148, l149 = sch.get_loops(block=b112) + sch.annotate(block_or_loop=l148, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l148, ann_key="pragma_unroll_explicit", ann_val=1) + b150 = sch.get_block(name="NT_matmul", func_name="main") + l0, l1, l151, l152, l153, l154, l155, l156, l157, l158, l159, l160, l161, l162, l163, l164 = sch.get_loops(block=b150) + l2 = sch.fuse(l0, l1) + sch.bind(l2, "blockIdx.y") + b165 = sch.decompose_reduction(block=b150, loop=l154) + + b1 = sch.get_block("lv28_pad") + sch.compute_inline(b1) + b2 = sch.get_block("lv29_pad") + sch.compute_inline(b2) + b3 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b3) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func +def fused_NT_matmul2_divide1_maximum1_minimum1_cast3_before(p_lv28: T.handle, p_lv29: T.handle, p_lv5: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv28 = T.match_buffer(p_lv28, (T.int64(1), T.int64(32), n, T.int64(128)), "float16") + m = T.int64() + lv29 = T.match_buffer(p_lv29, (T.int64(1), T.int64(32), m, T.int64(128)), "float16") + lv5 = T.match_buffer(p_lv5, (T.int64(1), T.int64(1), n, m), "float16") + var_compute_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m), "float16") + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m), "float16") + var_T_maximum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m), "float16") + var_T_minimum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m), "float16") + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, m, T.int64(128)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv28[v_i0, v_i1, v_i2, v_k], lv29[v_i0, v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv28[v_i0, v_i1, v_i2, v_k] * lv29[v_i0, v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float16(0.088397790055248615) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float16(-65504)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_minimum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv5[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.min(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv5[v_ax0, T.int64(0), v_ax2, v_ax3]) + for i0, i1, i2, i3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("compute"): + v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3]) + T.reads(var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3]) + var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.Cast("float32", var_T_minimum_intermediate[v_i0, v_i1, v_i2, v_i3]) + +@T.prim_func +def fused_NT_matmul2_divide1_maximum1_minimum1_cast3_after(p_lv22: T.handle, p_lv23: T.handle, p_lv5: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + m = T.int64() + lv22 = T.match_buffer(p_lv22, (T.int64(1), T.int64(32), n, T.int64(128)), "float16") + lv23 = T.match_buffer(p_lv23, (T.int64(1), T.int64(32), m, T.int64(128)), "float16") + lv5 = T.match_buffer(p_lv5, (T.int64(1), T.int64(1), n, m), "float16") + var_T_maximum_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + for i2_0_i3_0_fused in T.thread_binding((n + T.int64(31)) // T.int64(32) * ((m + T.int64(31)) // T.int64(32)), thread="blockIdx.y"): + with T.block("NT_matmul_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0_i3_0_fused // ((m + T.int64(31)) // T.int64(32))) + v_i3_o = T.axis.spatial((m + T.int64(31)) // T.int64(32), i2_0_i3_0_fused % ((m + T.int64(31)) // T.int64(32))) + T.reads(lv22[T.int64(0), T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(128)], lv23[T.int64(0), T.int64(0):T.int64(32), v_i3_o * T.int64(32):v_i3_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(128)], lv5[v_i0, T.int64(0), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), v_i3_o * T.int64(32):v_i3_o * T.int64(32) + T.int64(32)]) + T.writes(var_T_maximum_intermediate[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), v_i3_o * T.int64(32):v_i3_o * T.int64(32) + T.int64(32)]) + C_pad_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32), T.int64(32)), "float16", scope="local") + A_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32), T.int64(128)), "float16", scope="shared") + B_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32), T.int64(128)), "float16", scope="shared") + for i0_0_i1_0_i2_1_0_i3_1_0_fused in T.thread_binding(T.int64(128), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 512, "pragma_unroll_explicit": 1}): + for i0_1_i1_1_i2_1_1_i3_1_1_fused in T.thread_binding(T.int64(4), thread="vthread.x"): + for i0_2_i1_2_i2_1_2_i3_1_2_fused in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for i1_3_init, i2_1_3_init, i3_1_3_init, i1_4_init, i2_1_4_init, i3_1_4_init in T.grid(T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(1)): + with T.block("NT_matmul_init"): + v_i1_i = T.axis.spatial(T.int64(32), i1_4_init + i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4) + i1_3_init) + v_i2_i = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(4) // T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused // T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused // T.int64(8) + i2_1_3_init + i2_1_4_init) + v_i3_i = T.axis.spatial(T.int64(32), i3_1_4_init + i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused % T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused % T.int64(8) + i3_1_3_init) + T.reads() + T.writes(C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i] = T.float32(0) + for k_0 in range(T.int64(16)): + for ax0_ax1_ax2_ax3_fused_0 in range(T.int64(1)): + for ax0_ax1_ax2_ax3_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_ax2_ax3_fused_2 in T.vectorized(T.int64(2)): + with T.block("A_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4)) + v2 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(4) // T.int64(2) * T.int64(16) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) // T.int64(8)) + v3 = T.axis.spatial(T.int64(128), k_0 * T.int64(8) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) % T.int64(8)) + T.reads(lv22[v0, v1, v_i2_o * T.int64(32) + v2, v3]) + T.writes(A_pad_shared[v0, v1, v2, v3]) + A_pad_shared[v0, v1, v2, v3] = T.if_then_else(v_i2_o * T.int64(32) + v2 < n, lv22[v0, v1, v_i2_o * T.int64(32) + v2, v3], T.float32(0)) + for ax0_ax1_ax2_ax3_fused_0 in range(T.int64(1)): + for ax0_ax1_ax2_ax3_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_ax2_ax3_fused_2 in T.vectorized(T.int64(2)): + with T.block("B_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4)) + v2 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(2) * T.int64(16) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) // T.int64(8)) + v3 = T.axis.spatial(T.int64(128), k_0 * T.int64(8) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) % T.int64(8)) + T.reads(lv23[v0, v1, v_i3_o * T.int64(32) + v2, v3]) + T.writes(B_pad_shared[v0, v1, v2, v3]) + B_pad_shared[v0, v1, v2, v3] = T.if_then_else(v_i3_o * T.int64(32) + v2 < m, lv23[v0, v1, v_i3_o * T.int64(32) + v2, v3], T.float32(0)) + for k_1, i0_3, i1_3, i2_1_3, i3_1_3, k_2, i0_4, i1_4, i2_1_4, i3_1_4 in T.grid(T.int64(4), T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(2), T.int64(1), T.int64(1), T.int64(1), T.int64(1)): + with T.block("NT_matmul_update"): + v_i1_i = T.axis.spatial(T.int64(32), i1_4 + i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4) + i1_3) + v_i2_i = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(4) // T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused // T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused // T.int64(8) + i2_1_3 + i2_1_4) + v_i3_i = T.axis.spatial(T.int64(32), i3_1_4 + i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused % T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused % T.int64(8) + i3_1_3) + v_k_i = T.axis.reduce(T.int64(128), k_0 * T.int64(8) + k_1 * T.int64(2) + k_2) + T.reads(C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i], A_pad_shared[T.int64(0), v_i1_i, v_i2_i, v_k_i], B_pad_shared[T.int64(0), v_i1_i, v_i3_i, v_k_i]) + T.writes(C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i] = C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i] + A_pad_shared[T.int64(0), v_i1_i, v_i2_i, v_k_i] * B_pad_shared[T.int64(0), v_i1_i, v_i3_i, v_k_i] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), T.int64(1), T.int64(1)): + with T.block("C_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4) + ax1) + v2 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(4) // T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused // T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused // T.int64(8) + ax2) + v3 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused % T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused % T.int64(8) + ax3) + T.reads(C_pad_local[v0, v1, v2, v3], lv5[v_i0 + v0, T.int64(0), v_i2_o * T.int64(32) + v2, v_i3_o * T.int64(32) + v3]) + T.writes(var_T_maximum_intermediate[v_i0 + v0, v1, v_i2_o * T.int64(32) + v2, v_i3_o * T.int64(32) + v3]) + # if T.int64(0) <= v_i0 and v_i0 < T.int64(1) and T.int64(0) <= v_i2_o * T.int64(32) + v2 and v_i2_o * T.int64(32) + v2 < n and T.int64(0) <= v_i3_o * T.int64(32) + v3 and v_i3_o * T.int64(32) + v3 < n: + if v_i2_o * T.int64(32) + v2 < n and v_i3_o * T.int64(32) + v3 < m: + var_T_maximum_intermediate[v_i0 + v0, v1, v_i2_o * T.int64(32) + v2, v_i3_o * T.int64(32) + v3] = T.Cast("float32", T.min(T.max(C_pad_local[v0, v1, v2, v3] * T.float32(0.088397790055248615), T.float16(-65504)), lv5[v_i0 + v0, T.int64(0), v_i2_o * T.int64(32) + v2, v_i3_o * T.int64(32) + v3])) + +@T.prim_func +def fused_NT_matmul2_divide1_maximum1_minimum1_before(p_lv28: T.handle, p_lv29: T.handle, p_lv5: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv28 = T.match_buffer(p_lv28, (T.int64(1), T.int64(32), n, T.int64(128))) + m = T.int64() + lv29 = T.match_buffer(p_lv29, (T.int64(1), T.int64(32), m, T.int64(128))) + lv5 = T.match_buffer(p_lv5, (T.int64(1), T.int64(1), n, m)) + var_T_minimum_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + var_T_divide_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + var_T_maximum_intermediate = T.alloc_buffer((T.int64(1), T.int64(32), n, m)) + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, m, T.int64(128)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(lv28[v_i0, v_i1, v_i2, v_k], lv29[v_i0, v_i1, v_i3, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = T.float32(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2, v_i3] + lv28[v_i0, v_i1, v_i2, v_k] * lv29[v_i0, v_i1, v_i3, v_k] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_divide"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] * T.float32(0.088388349161020605) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_maximum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + T.writes(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.max(var_T_divide_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], T.float32(-3.4028234663852886e+38)) + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(32), n, m): + with T.block("T_minimum"): + v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3]) + T.reads(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv5[v_ax0, T.int64(0), v_ax2, v_ax3]) + T.writes(var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3]) + var_T_minimum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3] = T.min(var_T_maximum_intermediate[v_ax0, v_ax1, v_ax2, v_ax3], lv5[v_ax0, T.int64(0), v_ax2, v_ax3]) + +@T.prim_func +def fused_NT_matmul2_divide1_maximum1_minimum1_after(p_lv22: T.handle, p_lv23: T.handle, p_lv5: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + n = T.int64() + m = T.int64() + lv22 = T.match_buffer(p_lv22, (T.int64(1), T.int64(32), n, T.int64(128)), "float32") + lv23 = T.match_buffer(p_lv23, (T.int64(1), T.int64(32), m, T.int64(128)), "float32") + lv5 = T.match_buffer(p_lv5, (T.int64(1), T.int64(1), n, m), "float32") + var_T_maximum_intermediate = T.match_buffer(p_output0, (T.int64(1), T.int64(32), n, m)) + # with T.block("root"): + for i2_0_i3_0_fused in T.thread_binding((n + T.int64(31)) // T.int64(32) * ((m + T.int64(31)) // T.int64(32)), thread="blockIdx.y"): + with T.block("NT_matmul_o"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2_o = T.axis.spatial((n + T.int64(31)) // T.int64(32), i2_0_i3_0_fused // ((m + T.int64(31)) // T.int64(32))) + v_i3_o = T.axis.spatial((m + T.int64(31)) // T.int64(32), i2_0_i3_0_fused % ((m + T.int64(31)) // T.int64(32))) + T.reads(lv22[T.int64(0), T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(128)], lv23[T.int64(0), T.int64(0):T.int64(32), v_i3_o * T.int64(32):v_i3_o * T.int64(32) + T.int64(32), T.int64(0):T.int64(128)], lv5[v_i0, T.int64(0), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), v_i3_o * T.int64(32):v_i3_o * T.int64(32) + T.int64(32)]) + T.writes(var_T_maximum_intermediate[v_i0, T.int64(0):T.int64(32), v_i2_o * T.int64(32):v_i2_o * T.int64(32) + T.int64(32), v_i3_o * T.int64(32):v_i3_o * T.int64(32) + T.int64(32)]) + C_pad_local = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32), T.int64(32)), "float32", scope="local") + A_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32), T.int64(128)), "float32", scope="shared") + B_pad_shared = T.alloc_buffer((T.int64(1), T.int64(32), T.int64(32), T.int64(128)), "float32", scope="shared") + for i0_0_i1_0_i2_1_0_i3_1_0_fused in T.thread_binding(T.int64(128), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 512, "pragma_unroll_explicit": 1}): + for i0_1_i1_1_i2_1_1_i3_1_1_fused in T.thread_binding(T.int64(4), thread="vthread.x"): + for i0_2_i1_2_i2_1_2_i3_1_2_fused in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for i1_3_init, i2_1_3_init, i3_1_3_init, i1_4_init, i2_1_4_init, i3_1_4_init in T.grid(T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(1)): + with T.block("NT_matmul_init"): + v_i1_i = T.axis.spatial(T.int64(32), i1_4_init + i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4) + i1_3_init) + v_i2_i = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(4) // T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused // T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused // T.int64(8) + i2_1_3_init + i2_1_4_init) + v_i3_i = T.axis.spatial(T.int64(32), i3_1_4_init + i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused % T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused % T.int64(8) + i3_1_3_init) + T.reads() + T.writes(C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i] = T.float32(0) + for k_0 in range(T.int64(16)): + for ax0_ax1_ax2_ax3_fused_0 in range(T.int64(1)): + for ax0_ax1_ax2_ax3_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_ax2_ax3_fused_2 in T.vectorized(T.int64(2)): + with T.block("A_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4)) + v2 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(4) // T.int64(2) * T.int64(16) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) // T.int64(8)) + v3 = T.axis.spatial(T.int64(128), k_0 * T.int64(8) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) % T.int64(8)) + T.reads(lv22[v0, v1, v_i2_o * T.int64(32) + v2, v3]) + T.writes(A_pad_shared[v0, v1, v2, v3]) + A_pad_shared[v0, v1, v2, v3] = T.if_then_else(v_i2_o * T.int64(32) + v2 < n, lv22[v0, v1, v_i2_o * T.int64(32) + v2, v3], T.float32(0)) + for ax0_ax1_ax2_ax3_fused_0 in range(T.int64(1)): + for ax0_ax1_ax2_ax3_fused_1 in T.thread_binding(T.int64(64), thread="threadIdx.x"): + for ax0_ax1_ax2_ax3_fused_2 in T.vectorized(T.int64(2)): + with T.block("B_pad_shared"): + v0 = T.axis.spatial(T.int64(1), T.int64(0)) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4)) + v2 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(2) * T.int64(16) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) // T.int64(8)) + v3 = T.axis.spatial(T.int64(128), k_0 * T.int64(8) + (ax0_ax1_ax2_ax3_fused_0 * T.int64(128) + ax0_ax1_ax2_ax3_fused_1 * T.int64(2) + ax0_ax1_ax2_ax3_fused_2) % T.int64(8)) + T.reads(lv23[v0, v1, v_i3_o * T.int64(32) + v2, v3]) + T.writes(B_pad_shared[v0, v1, v2, v3]) + B_pad_shared[v0, v1, v2, v3] = T.if_then_else(v_i3_o * T.int64(32) + v2 < m, lv23[v0, v1, v_i3_o * T.int64(32) + v2, v3], T.float32(0)) + for k_1, i0_3, i1_3, i2_1_3, i3_1_3, k_2, i0_4, i1_4, i2_1_4, i3_1_4 in T.grid(T.int64(4), T.int64(1), T.int64(1), T.int64(1), T.int64(1), T.int64(2), T.int64(1), T.int64(1), T.int64(1), T.int64(1)): + with T.block("NT_matmul_update"): + v_i1_i = T.axis.spatial(T.int64(32), i1_4 + i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4) + i1_3) + v_i2_i = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(4) // T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused // T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused // T.int64(8) + i2_1_3 + i2_1_4) + v_i3_i = T.axis.spatial(T.int64(32), i3_1_4 + i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused % T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused % T.int64(8) + i3_1_3) + v_k_i = T.axis.reduce(T.int64(128), k_0 * T.int64(8) + k_1 * T.int64(2) + k_2) + T.reads(C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i], A_pad_shared[T.int64(0), v_i1_i, v_i2_i, v_k_i], B_pad_shared[T.int64(0), v_i1_i, v_i3_i, v_k_i]) + T.writes(C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i]) + T.block_attr({"meta_schedule.thread_extent_high_inclusive": 256, "meta_schedule.thread_extent_low_inclusive": 32, "meta_schedule.tiling_structure": "SSSRRSRS"}) + C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i] = C_pad_local[T.int64(0), v_i1_i, v_i2_i, v_i3_i] + A_pad_shared[T.int64(0), v_i1_i, v_i2_i, v_k_i] * B_pad_shared[T.int64(0), v_i1_i, v_i3_i, v_k_i] + for ax0, ax1, ax2, ax3 in T.grid(T.int64(1), T.int64(1), T.int64(1), T.int64(1)): + with T.block("C_pad_local"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused // T.int64(4) + ax1) + v2 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(4) // T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused // T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused // T.int64(8) + ax2) + v3 = T.axis.spatial(T.int64(32), i0_0_i1_0_i2_1_0_i3_1_0_fused % T.int64(2) * T.int64(16) + i0_1_i1_1_i2_1_1_i3_1_1_fused % T.int64(2) * T.int64(8) + i0_2_i1_2_i2_1_2_i3_1_2_fused % T.int64(8) + ax3) + T.reads(C_pad_local[v0, v1, v2, v3], lv5[v_i0 + v0, T.int64(0), v_i2_o * T.int64(32) + v2, v_i3_o * T.int64(32) + v3]) + T.writes(var_T_maximum_intermediate[v_i0 + v0, v1, v_i2_o * T.int64(32) + v2, v_i3_o * T.int64(32) + v3]) + # if T.int64(0) <= v_i0 and v_i0 < T.int64(1) and T.int64(0) <= v_i2_o * T.int64(32) + v2 and v_i2_o * T.int64(32) + v2 < n and T.int64(0) <= v_i3_o * T.int64(32) + v3 and v_i3_o * T.int64(32) + v3 < n: + if v_i2_o * T.int64(32) + v2 < n and v_i3_o * T.int64(32) + v3 < m: + var_T_maximum_intermediate[v_i0 + v0, v1, v_i2_o * T.int64(32) + v2, v_i3_o * T.int64(32) + v3] = T.min(T.max(C_pad_local[v0, v1, v2, v3] * T.float32(0.088397790055248615), T.float16(-65504)), lv5[v_i0 + v0, T.int64(0), v_i2_o * T.int64(32) + v2, v_i3_o * T.int64(32) + v3]) + +def fused_NT_matmul2_divide1_add2_maximum1_sch_func(func): + sch = tvm.tir.Schedule(func) + b0 = sch.get_block("NT_matmul") + sch.pad_einsum(b0, [1, 1, 32, 32, 1]) + l1, l2, l3, l4, l5 = sch.get_loops(b0) + l6, l7 = sch.split(l3, [None, 32]) + l8, l9 = sch.split(l4, [None, 32]) + sch.reorder(l6, l8, l1, l2, l7, l9, l5) + + b0 = sch.get_block(name="NT_matmul", func_name="main") + b1 = sch.get_block(name="T_divide", func_name="main") + b2 = sch.get_block(name="T_add", func_name="main") + b3 = sch.get_block(name="T_maximum", func_name="main") + b4 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, _, l5, l6, l7, l8, l9 = sch.get_loops(block=b0) + v10, v11, v12, v13, v14 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l15, l16, l17, l18, l19 = sch.split(loop=l5, factors=[v10, v11, v12, v13, v14], preserve_unit_iters=True) + v20, v21, v22, v23, v24 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[32, 1, 1, 1, 1]) + l25, l26, l27, l28, l29 = sch.split(loop=l6, factors=[v20, v21, v22, v23, v24], preserve_unit_iters=True) + v30, v31, v32, v33, v34 = sch.sample_perfect_tile(loop=l7, n=5, max_innermost_factor=64, decision=[2, 1, 8, 1, 2]) + l35, l36, l37, l38, l39 = sch.split(loop=l7, factors=[v30, v31, v32, v33, v34], preserve_unit_iters=True) + v40, v41, v42, v43, v44 = sch.sample_perfect_tile(loop=l8, n=5, max_innermost_factor=64, decision=[2, 1, 8, 1, 2]) + l45, l46, l47, l48, l49 = sch.split(loop=l8, factors=[v40, v41, v42, v43, v44], preserve_unit_iters=True) + v50, v51, v52 = sch.sample_perfect_tile(loop=l9, n=3, max_innermost_factor=64, decision=[8, 16, 1]) + l53, l54, l55 = sch.split(loop=l9, factors=[v50, v51, v52], preserve_unit_iters=True) + sch.reorder(l15, l25, l35, l45, l16, l26, l36, l46, l17, l27, l37, l47, l53, l54, l18, l28, l38, l48, l55, l19, l29, l39, l49) + l56 = sch.fuse(l15, l25, l35, l45, preserve_unit_iters=True) + sch.bind(loop=l56, thread_axis="blockIdx.x") + l57 = sch.fuse(l16, l26, l36, l46, preserve_unit_iters=True) + sch.bind(loop=l57, thread_axis="vthread.x") + l58 = sch.fuse(l17, l27, l37, l47, preserve_unit_iters=True) + sch.bind(loop=l58, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b59 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b59, loop=l58, preserve_unit_loops=True, index=-1) + b60 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b60, loop=l53, preserve_unit_loops=True, index=-1) + _, _, l61, l62, l63, l64, l65, l66, l67, l68 = sch.get_loops(block=b60) + l69 = sch.fuse(l65, l66, l67, l68, preserve_unit_iters=True) + v70 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b60, ann_key="meta_schedule.cooperative_fetch", ann_val=v70) + b71 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b71, loop=l53, preserve_unit_loops=True, index=-1) + _, _, l72, l73, l74, l75, l76, l77, l78, l79 = sch.get_loops(block=b71) + l80 = sch.fuse(l76, l77, l78, l79, preserve_unit_iters=True) + v81 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b71, ann_key="meta_schedule.cooperative_fetch", ann_val=v81) + sch.reverse_compute_inline(block=b3) + sch.compute_inline(block=b1) + v82 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=2) + sch.annotate(block_or_loop=b4, ann_key="meta_schedule.unroll_explicit", ann_val=v82) + l83, l84, l85, l86 = sch.get_loops(block=b2) + l87 = sch.fuse(l83, l84, l85, l86, preserve_unit_iters=True) + v88 = sch.sample_categorical(candidates=[32, 64, 128, 256], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + l89, l90 = sch.split(loop=l87, factors=[None, v88], preserve_unit_iters=True) + sch.bind(loop=l89, thread_axis="blockIdx.x") + sch.bind(loop=l90, thread_axis="threadIdx.x") + sch.enter_postproc() + sch.unannotate(block_or_loop=b60, ann_key="meta_schedule.cooperative_fetch") + _, _, l91, l92, l93, l94, l95 = sch.get_loops(block=b60) + l96, l97, l98 = sch.split(loop=l95, factors=[None, 64, 2], preserve_unit_iters=True) + sch.vectorize(loop=l98) + sch.bind(loop=l97, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b71, ann_key="meta_schedule.cooperative_fetch") + _, _, l99, l100, l101, l102, l103 = sch.get_loops(block=b71) + l104, l105, l106 = sch.split(loop=l103, factors=[None, 64, 2], preserve_unit_iters=True) + sch.vectorize(loop=l106) + sch.bind(loop=l105, thread_axis="threadIdx.x") + b107 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b107, ann_key="meta_schedule.unroll_explicit") + _, _, b108, b109, b110, b111, _, b112 = sch.get_child_blocks(b107) + _, _, l113, l114, l115, l116, l117, l118, l119 = sch.get_loops(block=b108) + sch.annotate(block_or_loop=l113, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l113, ann_key="pragma_unroll_explicit", ann_val=1) + _, _, l120, l121, l122, l123, l124, l125, l126 = sch.get_loops(block=b109) + sch.annotate(block_or_loop=l120, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l120, ann_key="pragma_unroll_explicit", ann_val=1) + _, _, l127, l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, l138, l139, l140 = sch.get_loops(block=b110) + sch.annotate(block_or_loop=l127, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l127, ann_key="pragma_unroll_explicit", ann_val=1) + _, _, l141, l142, l143, l144, l145, l146, l147 = sch.get_loops(block=b111) + sch.annotate(block_or_loop=l141, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l141, ann_key="pragma_unroll_explicit", ann_val=1) + l148, l149 = sch.get_loops(block=b112) + sch.annotate(block_or_loop=l148, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l148, ann_key="pragma_unroll_explicit", ann_val=1) + b150 = sch.get_block(name="NT_matmul", func_name="main") + l0, l1, l151, l152, l153, l154, l155, l156, l157, l158, l159, l160, l161, l162, l163, l164 = sch.get_loops(block=b150) + l2 = sch.fuse(l0, l1) + sch.bind(l2, "blockIdx.y") + b165 = sch.decompose_reduction(block=b150, loop=l154) + + b1 = sch.get_block("lv28_pad") + sch.compute_inline(b1) + b2 = sch.get_block("lv29_pad") + sch.compute_inline(b2) + b3 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b3) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func +def fused_NT_matmul3_multiply1_before(p_lv43: T.handle, lv1866: T.Buffer((T.int64(11008), T.int64(4096)), "float16"), p_lv48: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv43 = T.match_buffer(p_lv43, (T.int64(1), n, T.int64(4096)), "float16") + lv48 = T.match_buffer(p_lv48, (T.int64(1), n, T.int64(11008)), "float16") + var_T_multiply_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(11008)), "float16") + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(11008)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(11008), T.int64(4096)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv43[v_i0, v_i1, v_k], lv1866[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv43[v_i0, v_i1, v_k] * lv1866[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv48[v_ax0, v_ax1, v_ax2], var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2] = lv48[v_ax0, v_ax1, v_ax2] * var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +def fused_NT_matmul3_multiply1_sch_func(): + sch = tvm.tir.Schedule(fused_NT_matmul3_multiply1_before) + b0 = sch.get_block("NT_matmul") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + b0 = sch.get_block(name="NT_matmul", func_name="main") + b1 = sch.get_block(name="T_multiply", func_name="main") + b2 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l3, l4, l5, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l3, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 1, 8, 2, 2]) + l22, l23, l24, l25, l26 = sch.split(loop=l4, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[344, 4, 8, 1, 1]) + l32, l33, l34, l35, l36 = sch.split(loop=l5, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[128, 16, 2]) + l40, l41, l42 = sch.split(loop=l6, factors=[v37, v38, v39], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l13, l23, l33, l14, l24, l34, l40, l41, l15, l25, l35, l42, l16, l26, l36) + l43 = sch.fuse(l12, l22, l32, preserve_unit_iters=True) + sch.bind(loop=l43, thread_axis="blockIdx.x") + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="vthread.x") + l45 = sch.fuse(l14, l24, l34, preserve_unit_iters=True) + sch.bind(loop=l45, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b46 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b46, loop=l45, preserve_unit_loops=True, index=-1) + b47 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b47, loop=l40, preserve_unit_loops=True, index=-1) + _, l48, l49, l50, l51, l52, l53, l54 = sch.get_loops(block=b47) + l55 = sch.fuse(l52, l53, l54, preserve_unit_iters=True) + v56 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch", ann_val=v56) + b57 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l40, preserve_unit_loops=True, index=-1) + _, l58, l59, l60, l61, l62, l63 = sch.get_loops(block=b57) + l64 = sch.fuse(l62, l63, preserve_unit_iters=True) + v65 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v65) + sch.reverse_compute_inline(block=b1) + v66 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=1) + sch.annotate(block_or_loop=b2, ann_key="meta_schedule.unroll_explicit", ann_val=v66) + sch.enter_postproc() + sch.unannotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch") + _, l67, l68, l69, l70, l71 = sch.get_loops(block=b47) + l72, l73, l74 = sch.split(loop=l71, factors=[None, 64, 4], preserve_unit_iters=True) + sch.vectorize(loop=l74) + sch.bind(loop=l73, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + _, l75, l76, l77, l78, l79 = sch.get_loops(block=b57) + l80, l81, l82 = sch.split(loop=l79, factors=[None, 64, 2], preserve_unit_iters=True) + sch.vectorize(loop=l82) + sch.bind(loop=l81, thread_axis="threadIdx.x") + b83 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b83, ann_key="meta_schedule.unroll_explicit") + _, b84, b85, b86, b87, _ = sch.get_child_blocks(b83) + _, l88, l89, l90, l91, l92, l93, l94 = sch.get_loops(block=b84) + sch.annotate(block_or_loop=l88, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l88, ann_key="pragma_unroll_explicit", ann_val=1) + _, l95, l96, l97, l98, l99, l100, l101 = sch.get_loops(block=b85) + sch.annotate(block_or_loop=l95, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l95, ann_key="pragma_unroll_explicit", ann_val=1) + _, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113 = sch.get_loops(block=b86) + sch.annotate(block_or_loop=l102, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l102, ann_key="pragma_unroll_explicit", ann_val=1) + _, l114, l115, l116, l117, l118, l119 = sch.get_loops(block=b87) + sch.annotate(block_or_loop=l114, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l114, ann_key="pragma_unroll_explicit", ann_val=1) + b120 = sch.get_block(name="NT_matmul", func_name="main") + l0, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132 = sch.get_loops(block=b120) + sch.bind(l0, "blockIdx.y") + b133 = sch.decompose_reduction(block=b120, loop=l124) + + b1 = sch.get_block("lv43_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func +def fused_NT_matmul3_silu1_before(p_lv43: T.handle, lv1857: T.Buffer((T.int64(11008), T.int64(4096)), "float16"), p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv43 = T.match_buffer(p_lv43, (T.int64(1), n, T.int64(4096)), "float16") + var_T_multiply_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(11008)), "float16") + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(11008)), "float16") + compute = T.alloc_buffer((T.int64(1), n, T.int64(11008)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(11008), T.int64(4096)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv43[v_i0, v_i1, v_k], lv1857[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv43[v_i0, v_i1, v_k] * lv1857[v_i2, v_k] + for i0, i1, i2 in T.grid(T.int64(1), n, T.int64(11008)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.sigmoid(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2], compute[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_multiply_intermediate[v_ax0, v_ax1, v_ax2] = var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] * compute[v_ax0, v_ax1, v_ax2] + + +def fused_NT_matmul3_silu1_sch_func(): + sch = tvm.tir.Schedule(fused_NT_matmul3_silu1_before) + b0 = sch.get_block("NT_matmul") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + b0 = sch.get_block(name="NT_matmul", func_name="main") + b1 = sch.get_block(name="compute", func_name="main") + b2 = sch.get_block(name="T_multiply", func_name="main") + b3 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l4, l5, l6, l7 = sch.get_loops(block=b0) + v8, v9, v10, v11, v12 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l13, l14, l15, l16, l17 = sch.split(loop=l4, factors=[v8, v9, v10, v11, v12], preserve_unit_iters=True) + v18, v19, v20, v21, v22 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[1, 1, 8, 4, 1]) + l23, l24, l25, l26, l27 = sch.split(loop=l5, factors=[v18, v19, v20, v21, v22], preserve_unit_iters=True) + v28, v29, v30, v31, v32 = sch.sample_perfect_tile(loop=l6, n=5, max_innermost_factor=64, decision=[344, 4, 8, 1, 1]) + l33, l34, l35, l36, l37 = sch.split(loop=l6, factors=[v28, v29, v30, v31, v32], preserve_unit_iters=True) + v38, v39, v40 = sch.sample_perfect_tile(loop=l7, n=3, max_innermost_factor=64, decision=[128, 16, 2]) + l41, l42, l43 = sch.split(loop=l7, factors=[v38, v39, v40], preserve_unit_iters=True) + sch.reorder(l13, l23, l33, l14, l24, l34, l15, l25, l35, l41, l42, l16, l26, l36, l43, l17, l27, l37) + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="blockIdx.x") + l45 = sch.fuse(l14, l24, l34, preserve_unit_iters=True) + sch.bind(loop=l45, thread_axis="vthread.x") + l46 = sch.fuse(l15, l25, l35, preserve_unit_iters=True) + sch.bind(loop=l46, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b47 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b47, loop=l46, preserve_unit_loops=True, index=-1) + b48 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b48, loop=l41, preserve_unit_loops=True, index=-1) + _, l49, l50, l51, l52, l53, l54, l55 = sch.get_loops(block=b48) + l56 = sch.fuse(l53, l54, l55, preserve_unit_iters=True) + v57 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b48, ann_key="meta_schedule.cooperative_fetch", ann_val=v57) + b58 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b58, loop=l41, preserve_unit_loops=True, index=-1) + _, l59, l60, l61, l62, l63, l64 = sch.get_loops(block=b58) + l65 = sch.fuse(l63, l64, preserve_unit_iters=True) + v66 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b58, ann_key="meta_schedule.cooperative_fetch", ann_val=v66) + sch.compute_inline(block=b1) + v67 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=1) + sch.annotate(block_or_loop=b3, ann_key="meta_schedule.unroll_explicit", ann_val=v67) + + # reverse compute inline the silu part + sch.reverse_compute_inline(b2) + # l68, l69, l70 = sch.get_loops(block=b2) + # l71 = sch.fuse(l68, l69, l70, preserve_unit_iters=True) + # l72, l73, l74 = sch.split(loop=l71, factors=[None, 256, 256], preserve_unit_iters=True) + #sch.reorder(l73, l74, l72) + # sch.bind(loop=l73, thread_axis="blockIdx.x") + # sch.bind(loop=l74, thread_axis="threadIdx.x") + sch.enter_postproc() + sch.unannotate(block_or_loop=b48, ann_key="meta_schedule.cooperative_fetch") + _, l75, l76, l77, l78, l79 = sch.get_loops(block=b48) + l80, l81, l82 = sch.split(loop=l79, factors=[None, 64, 4], preserve_unit_iters=True) + sch.vectorize(loop=l82) + sch.bind(loop=l81, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b58, ann_key="meta_schedule.cooperative_fetch") + _, l83, l84, l85, l86, l87 = sch.get_loops(block=b58) + l88, l89, l90 = sch.split(loop=l87, factors=[None, 64, 2], preserve_unit_iters=True) + sch.vectorize(loop=l90) + sch.bind(loop=l89, thread_axis="threadIdx.x") + b91 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b91, ann_key="meta_schedule.unroll_explicit") + _, b92, b93, b94, b95, _ = sch.get_child_blocks(b91) + _, l97, l98, l99, l100, l101, l102, l103 = sch.get_loops(block=b92) + sch.annotate(block_or_loop=l97, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l97, ann_key="pragma_unroll_explicit", ann_val=1) + _, l104, l105, l106, l107, l108, l109, l110 = sch.get_loops(block=b93) + sch.annotate(block_or_loop=l104, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l104, ann_key="pragma_unroll_explicit", ann_val=1) + _, l111, l112, l113, l114, l115, l116, l117, l118, l119, l120, l121, l122 = sch.get_loops(block=b94) + sch.annotate(block_or_loop=l111, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l111, ann_key="pragma_unroll_explicit", ann_val=1) + _, l123, l124, l125, l126, l127, l128 = sch.get_loops(block=b95) + sch.annotate(block_or_loop=l123, ann_key="pragma_auto_unroll_max_step", ann_val=16) + sch.annotate(block_or_loop=l123, ann_key="pragma_unroll_explicit", ann_val=1) + # l129, l130, l131 = sch.get_loops(block=b96) + # sch.annotate(block_or_loop=l129, ann_key="pragma_auto_unroll_max_step", ann_val=16) + # sch.annotate(block_or_loop=l129, ann_key="pragma_unroll_explicit", ann_val=1) + b132 = sch.get_block(name="NT_matmul", func_name="main") + l0, l133, l134, l135, l136, l137, l138, l139, l140, l141, l142, l143, l144 = sch.get_loops(block=b132) + sch.bind(l0, "blockIdx.y") + b145 = sch.decompose_reduction(block=b132, loop=l136) + + b1 = sch.get_block("lv43_pad") + sch.compute_inline(b1) + + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func +def fused_NT_matmul4_add3_before(p_lv49: T.handle, lv1875: T.Buffer((T.int64(4096), T.int64(11008)), "float16"), p_lv42: T.handle, p_output0: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + lv49 = T.match_buffer(p_lv49, (T.int64(1), n, T.int64(11008)), "float16") + lv42 = T.match_buffer(p_lv42, (T.int64(1), n, T.int64(4096)), "float16") + var_T_add_intermediate = T.match_buffer(p_output0, (T.int64(1), n, T.int64(4096)), "float16") + # with T.block("root"): + var_NT_matmul_intermediate = T.alloc_buffer((T.int64(1), n, T.int64(4096)), "float16") + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(4096), T.int64(11008)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv49[v_i0, v_i1, v_k], lv1875[v_i2, v_k]) + T.writes(var_NT_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_NT_matmul_intermediate[v_i0, v_i1, v_i2] = var_NT_matmul_intermediate[v_i0, v_i1, v_i2] + lv49[v_i0, v_i1, v_k] * lv1875[v_i2, v_k] + for ax0, ax1, ax2 in T.grid(T.int64(1), n, T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv42[v_ax0, v_ax1, v_ax2], var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(var_T_add_intermediate[v_ax0, v_ax1, v_ax2]) + var_T_add_intermediate[v_ax0, v_ax1, v_ax2] = lv42[v_ax0, v_ax1, v_ax2] + var_NT_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +def fused_NT_matmul4_add3_sch_func(): + sch = tvm.tir.Schedule(fused_NT_matmul4_add3_before) + b0 = sch.get_block("NT_matmul") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + b0 = sch.get_block(name="NT_matmul", func_name="main") + b1 = sch.get_block(name="T_add", func_name="main") + b2 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l3, l4, l5, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l3, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 1, 8, 1, 4]) + l22, l23, l24, l25, l26 = sch.split(loop=l4, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[128, 2, 8, 2, 1]) + l32, l33, l34, l35, l36 = sch.split(loop=l5, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[688, 16, 1]) + l40, l41, l42 = sch.split(loop=l6, factors=[v37, v38, v39], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l13, l23, l33, l14, l24, l34, l40, l41, l15, l25, l35, l42, l16, l26, l36) + l43 = sch.fuse(l12, l22, l32, preserve_unit_iters=True) + sch.bind(loop=l43, thread_axis="blockIdx.x") + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="vthread.x") + l45 = sch.fuse(l14, l24, l34, preserve_unit_iters=True) + sch.bind(loop=l45, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b46 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b46, loop=l45, preserve_unit_loops=True, index=-1) + b47 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b47, loop=l40, preserve_unit_loops=True, index=-1) + _, l48, l49, l50, l51, l52, l53, l54 = sch.get_loops(block=b47) + l55 = sch.fuse(l52, l53, l54, preserve_unit_iters=True) + v56 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch", ann_val=v56) + b57 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l40, preserve_unit_loops=True, index=-1) + _, l58, l59, l60, l61, l62, l63 = sch.get_loops(block=b57) + l64 = sch.fuse(l62, l63, preserve_unit_iters=True) + v65 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v65) + sch.reverse_compute_inline(block=b1) + v66 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=2) + sch.annotate(block_or_loop=b2, ann_key="meta_schedule.unroll_explicit", ann_val=v66) + sch.enter_postproc() + sch.unannotate(block_or_loop=b47, ann_key="meta_schedule.cooperative_fetch") + _, l67, l68, l69, l70, l71 = sch.get_loops(block=b47) + l72, l73, l74 = sch.split(loop=l71, factors=[None, 64, 4], preserve_unit_iters=True) + sch.vectorize(loop=l74) + sch.bind(loop=l73, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + _, l75, l76, l77, l78, l79 = sch.get_loops(block=b57) + l80, l81, l82 = sch.split(loop=l79, factors=[None, 64, 2], preserve_unit_iters=True) + sch.vectorize(loop=l82) + sch.bind(loop=l81, thread_axis="threadIdx.x") + b83 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b83, ann_key="meta_schedule.unroll_explicit") + _, b84, b85, b86, b87, _ = sch.get_child_blocks(b83) + _, l88, l89, l90, l91, l92, l93, l94 = sch.get_loops(block=b84) + sch.annotate(block_or_loop=l88, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l88, ann_key="pragma_unroll_explicit", ann_val=1) + _, l95, l96, l97, l98, l99, l100, l101 = sch.get_loops(block=b85) + sch.annotate(block_or_loop=l95, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l95, ann_key="pragma_unroll_explicit", ann_val=1) + _, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112, l113 = sch.get_loops(block=b86) + sch.annotate(block_or_loop=l102, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l102, ann_key="pragma_unroll_explicit", ann_val=1) + _, l114, l115, l116, l117, l118, l119 = sch.get_loops(block=b87) + sch.annotate(block_or_loop=l114, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l114, ann_key="pragma_unroll_explicit", ann_val=1) + b120 = sch.get_block(name="NT_matmul", func_name="main") + l0, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131, l132 = sch.get_loops(block=b120) + sch.bind(l0, "blockIdx.y") + b133 = sch.decompose_reduction(block=b120, loop=l124) + + b1 = sch.get_block("lv49_pad") + sch.compute_inline(b1) + b2 = sch.get_block("var_NT_matmul_intermediate_pad") + sch.reverse_compute_inline(b2) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func +def matmul1_fp16_before(var_rxplaceholder: T.handle, var_rxplaceholder_1: T.handle, matmul: T.Buffer((T.int64(1), T.int64(32), T.int64(1), T.int64(128)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + rxplaceholder = T.match_buffer(var_rxplaceholder, (T.int64(1), T.int64(32), T.int64(1), n), "float16") + rxplaceholder_1 = T.match_buffer(var_rxplaceholder_1, (T.int64(1), T.int64(32), n, T.int64(128)), "float16") + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), T.int64(1), T.int64(128), n): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_k], rxplaceholder_1[v_i0, v_i1, v_k, v_i3]) + T.writes(matmul[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + matmul[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + matmul[v_i0, v_i1, v_i2, v_i3] = matmul[v_i0, v_i1, v_i2, v_i3] + rxplaceholder[v_i0, v_i1, v_i2, v_k] * rxplaceholder_1[v_i0, v_i1, v_k, v_i3] + + +def matmul1_fp16_sch_func(): + sch = tvm.tir.Schedule(matmul1_fp16_before) + b0 = sch.get_block("matmul") + sch.pad_einsum(b0, [1, 1, 1, 1, 128]) + l1, l2, l3, l4, l5 = sch.get_loops(b0) + sch.split(l5, [None, 128]) + + b0 = sch.get_block(name="matmul", func_name="main") + b1 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + l2, l3, l4, l5, ko, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l2, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l2, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[2, 1, 16, 1, 1]) + l22, l23, l24, l25, l26 = sch.split(loop=l3, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l32, l33, l34, l35, l36 = sch.split(loop=l4, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39, v40, v41 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[8, 1, 16, 1, 1]) + l42, l43, l44, l45, l46 = sch.split(loop=l5, factors=[v37, v38, v39, v40, v41], preserve_unit_iters=True) + v47, v48, v49 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[4, 16, 2]) + l50, l51, l52 = sch.split(loop=l6, factors=[v47, v48, v49], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l42, l13, l23, l33, l43, l14, l24, l34, l44, ko, l50, l51, l15, l25, l35, l45, l52, l16, l26, l36, l46) + l53 = sch.fuse(l12, l22, l32, l42, preserve_unit_iters=True) + sch.bind(loop=l53, thread_axis="blockIdx.x") + l54 = sch.fuse(l13, l23, l33, l43, preserve_unit_iters=True) + sch.bind(loop=l54, thread_axis="vthread.x") + l55 = sch.fuse(l14, l24, l34, l44, preserve_unit_iters=True) + sch.bind(loop=l55, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b56 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b56, loop=l55, preserve_unit_loops=True, index=-1) + b57 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l50, preserve_unit_loops=True, index=-1) + l58, l59, l60, _, l61, l62, l63, l64, l65 = sch.get_loops(block=b57) + l66 = sch.fuse(l62, l63, l64, l65, preserve_unit_iters=True) + v67 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=0) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v67) + b68 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b68, loop=l50, preserve_unit_loops=True, index=-1) + l69, l70, l71, _, l72, l73, l74, l75, l76 = sch.get_loops(block=b68) + l77 = sch.fuse(l73, l74, l75, l76, preserve_unit_iters=True) + v78 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=1) + sch.annotate(block_or_loop=b68, ann_key="meta_schedule.cooperative_fetch", ann_val=v78) + v79 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=2) + sch.annotate(block_or_loop=b1, ann_key="meta_schedule.unroll_explicit", ann_val=v79) + sch.enter_postproc() + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + l80, l81, l82, _, l83, l84 = sch.get_loops(block=b57) + l85, l86 = sch.split(loop=l84, factors=[None, 256], preserve_unit_iters=True) + sch.bind(loop=l86, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b68, ann_key="meta_schedule.cooperative_fetch") + l87, l88, l89, _, l90, l91 = sch.get_loops(block=b68) + l92, l93, l94 = sch.split(loop=l91, factors=[None, 256, 2], preserve_unit_iters=True) + sch.vectorize(loop=l94) + sch.bind(loop=l93, thread_axis="threadIdx.x") + b95 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b95, ann_key="meta_schedule.unroll_explicit") + _, _, b96, b97, b98, b99 = sch.get_child_blocks(b95) + l100, l101, l102, _, l103, l104, l105 = sch.get_loops(block=b96) + sch.annotate(block_or_loop=l100, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l100, ann_key="pragma_unroll_explicit", ann_val=1) + l106, l107, l108, _, l109, l110, l111, l112 = sch.get_loops(block=b97) + sch.annotate(block_or_loop=l106, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l106, ann_key="pragma_unroll_explicit", ann_val=1) + l113, l114, l115, _, l116, l117, l118, l119, l120, l121, l122, l123, l124, l125, l126 = sch.get_loops(block=b98) + sch.annotate(block_or_loop=l113, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l113, ann_key="pragma_unroll_explicit", ann_val=1) + l127, l128, l129, l130, l131, l132, l133 = sch.get_loops(block=b99) + sch.annotate(block_or_loop=l127, ann_key="pragma_auto_unroll_max_step", ann_val=64) + sch.annotate(block_or_loop=l127, ann_key="pragma_unroll_explicit", ann_val=1) + b134 = sch.get_block(name="matmul", func_name="main") + l135, l136, l137, ko, l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, l148 = sch.get_loops(block=b134) + b149 = sch.decompose_reduction(block=b134, loop=ko) + + b1 = sch.get_block("rxplaceholder_pad") + sch.compute_inline(b1) + b2 = sch.get_block("rxplaceholder_1_pad") + sch.compute_inline(b2) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func +def matmul8_fp16_before(var_rxplaceholder: T.handle, var_rxplaceholder_1: T.handle, var_matmul: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + rxplaceholder = T.match_buffer(var_rxplaceholder, (T.int64(1), T.int64(32), n, n), "float16") + rxplaceholder_1 = T.match_buffer(var_rxplaceholder_1, (T.int64(1), T.int64(32), n, T.int64(128)), "float16") + matmul = T.match_buffer(var_matmul, (T.int64(1), T.int64(32), n, T.int64(128)), "float16") + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, T.int64(128), n): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_k], rxplaceholder_1[v_i0, v_i1, v_k, v_i3]) + T.writes(matmul[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + matmul[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + matmul[v_i0, v_i1, v_i2, v_i3] = matmul[v_i0, v_i1, v_i2, v_i3] + rxplaceholder[v_i0, v_i1, v_i2, v_k] * rxplaceholder_1[v_i0, v_i1, v_k, v_i3] + +@T.prim_func +def matmul8_with_m_fp16_before(var_rxplaceholder: T.handle, var_rxplaceholder_1: T.handle, var_matmul: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + m = T.int64() + rxplaceholder = T.match_buffer(var_rxplaceholder, (T.int64(1), T.int64(32), n, m), "float16") + rxplaceholder_1 = T.match_buffer(var_rxplaceholder_1, (T.int64(1), T.int64(32), m, T.int64(128)), "float16") + matmul = T.match_buffer(var_matmul, (T.int64(1), T.int64(32), n, T.int64(128)), "float16") + # with T.block("root"): + for i0, i1, i2, i3, k in T.grid(T.int64(1), T.int64(32), n, T.int64(128), m): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, i2, i3, k]) + T.reads(rxplaceholder[v_i0, v_i1, v_i2, v_k], rxplaceholder_1[v_i0, v_i1, v_k, v_i3]) + T.writes(matmul[v_i0, v_i1, v_i2, v_i3]) + with T.init(): + matmul[v_i0, v_i1, v_i2, v_i3] = T.float16(0) + matmul[v_i0, v_i1, v_i2, v_i3] = matmul[v_i0, v_i1, v_i2, v_i3] + rxplaceholder[v_i0, v_i1, v_i2, v_k] * rxplaceholder_1[v_i0, v_i1, v_k, v_i3] + +def matmul8_fp16_sch_func(func): + sch = tvm.tir.Schedule(func) + b0 = sch.get_block("matmul") + sch.pad_einsum(b0, [1, 1, 32, 1, 128]) + l1, l2, l3, l4, l5 = sch.get_loops(b0) + l6, l7 = sch.split(l3, [None, 32]) + l8, l9 = sch.split(l5, [None, 128]) + sch.reorder(l6, l1, l2, l7, l4, l8, l9) + + b0 = sch.get_block(name="matmul", func_name="main") + b1 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l2, l3, l4, l5, ko, l6 = sch.get_loops(block=b0) + v7, v8, v9, v10, v11 = sch.sample_perfect_tile(loop=l2, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l12, l13, l14, l15, l16 = sch.split(loop=l2, factors=[v7, v8, v9, v10, v11], preserve_unit_iters=True) + v17, v18, v19, v20, v21 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[32, 1, 1, 1, 1]) + l22, l23, l24, l25, l26 = sch.split(loop=l3, factors=[v17, v18, v19, v20, v21], preserve_unit_iters=True) + v27, v28, v29, v30, v31 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[1, 1, 4, 2, 4]) + l32, l33, l34, l35, l36 = sch.split(loop=l4, factors=[v27, v28, v29, v30, v31], preserve_unit_iters=True) + v37, v38, v39, v40, v41 = sch.sample_perfect_tile(loop=l5, n=5, max_innermost_factor=64, decision=[4, 1, 16, 2, 1]) + l42, l43, l44, l45, l46 = sch.split(loop=l5, factors=[v37, v38, v39, v40, v41], preserve_unit_iters=True) + v47, v48, v49 = sch.sample_perfect_tile(loop=l6, n=3, max_innermost_factor=64, decision=[16, 1, 8]) + l50, l51, l52 = sch.split(loop=l6, factors=[v47, v48, v49], preserve_unit_iters=True) + sch.reorder(l12, l22, l32, l42, l13, l23, l33, l43, l14, l24, l34, l44, ko, l50, l51, l15, l25, l35, l45, l52, l16, l26, l36, l46) + l53 = sch.fuse(l12, l22, l32, l42, preserve_unit_iters=True) + sch.bind(loop=l53, thread_axis="blockIdx.x") + l54 = sch.fuse(l13, l23, l33, l43, preserve_unit_iters=True) + sch.bind(loop=l54, thread_axis="vthread.x") + l55 = sch.fuse(l14, l24, l34, l44, preserve_unit_iters=True) + sch.bind(loop=l55, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b56 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b56, loop=l55, preserve_unit_loops=True, index=-1) + b57 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b57, loop=l50, preserve_unit_loops=True, index=-1) + _, l58, l59, l60, _, l61, l62, l63, l64, l65 = sch.get_loops(block=b57) + l66 = sch.fuse(l62, l63, l64, l65, preserve_unit_iters=True) + v67 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch", ann_val=v67) + b68 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b68, loop=l50, preserve_unit_loops=True, index=-1) + _, l69, l70, l71, _, l72, l73, l74, l75, l76 = sch.get_loops(block=b68) + l77 = sch.fuse(l73, l74, l75, l76, preserve_unit_iters=True) + v78 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=0) + sch.annotate(block_or_loop=b68, ann_key="meta_schedule.cooperative_fetch", ann_val=v78) + v79 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=3) + sch.annotate(block_or_loop=b1, ann_key="meta_schedule.unroll_explicit", ann_val=v79) + sch.enter_postproc() + sch.unannotate(block_or_loop=b57, ann_key="meta_schedule.cooperative_fetch") + _, l80, l81, l82, _, l83, l84 = sch.get_loops(block=b57) + l85, l86, l87 = sch.split(loop=l84, factors=[None, 64, 4], preserve_unit_iters=True) + sch.vectorize(loop=l87) + sch.bind(loop=l86, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b68, ann_key="meta_schedule.cooperative_fetch") + _, l88, l89, l90, _, l91, l92 = sch.get_loops(block=b68) + l93, l94 = sch.split(loop=l92, factors=[None, 64], preserve_unit_iters=True) + sch.bind(loop=l94, thread_axis="threadIdx.x") + b95 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b95, ann_key="meta_schedule.unroll_explicit") + _, _, b96, b97, b98, b99, _ = sch.get_child_blocks(b95) + _, l100, l101, l102, _, l103, l104, l105, l106 = sch.get_loops(block=b96) + sch.annotate(block_or_loop=l100, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l100, ann_key="pragma_unroll_explicit", ann_val=1) + _, l107, l108, l109, _, l110, l111, l112 = sch.get_loops(block=b97) + sch.annotate(block_or_loop=l107, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l107, ann_key="pragma_unroll_explicit", ann_val=1) + _, l113, l114, l115, _, l116, l117, l118, l119, l120, l121, l122, l123, l124, l125, l126 = sch.get_loops(block=b98) + sch.annotate(block_or_loop=l113, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l113, ann_key="pragma_unroll_explicit", ann_val=1) + _, l127, l128, l129, l130, l131, l132, l133 = sch.get_loops(block=b99) + sch.annotate(block_or_loop=l127, ann_key="pragma_auto_unroll_max_step", ann_val=512) + sch.annotate(block_or_loop=l127, ann_key="pragma_unroll_explicit", ann_val=1) + b134 = sch.get_block(name="matmul", func_name="main") + l0, l135, l136, l137, ko, l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, l148 = sch.get_loops(block=b134) + sch.bind(l0, "blockIdx.y") + b149 = sch.decompose_reduction(block=b134, loop=ko) + + b1 = sch.get_block("rxplaceholder_pad") + sch.compute_inline(b1) + b2 = sch.get_block("rxplaceholder_1_pad") + sch.compute_inline(b2) + b3 = sch.get_block("matmul_pad") + sch.reverse_compute_inline(b3) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func +def NT_matmul1_fp16_before(var_rxplaceholder: T.handle, rxplaceholder: T.Buffer((T.int64(4096), T.int64(4096)), "float16"), var_NT_matmul: T.handle): + T.func_attr({"tir.noalias": T.bool(True)}) + n = T.int64() + rxplaceholder_1 = T.match_buffer(var_rxplaceholder, (T.int64(1), n, T.int64(4096)), "float16") + NT_matmul = T.match_buffer(var_NT_matmul, (T.int64(1), n, T.int64(4096)), "float16") + # with T.block("root"): + for i0, i1, i2, k in T.grid(T.int64(1), n, T.int64(4096), T.int64(4096)): + with T.block("NT_matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(rxplaceholder_1[v_i0, v_i1, v_k], rxplaceholder[v_i2, v_k]) + T.writes(NT_matmul[v_i0, v_i1, v_i2]) + with T.init(): + NT_matmul[v_i0, v_i1, v_i2] = T.float16(0) + NT_matmul[v_i0, v_i1, v_i2] = NT_matmul[v_i0, v_i1, v_i2] + rxplaceholder_1[v_i0, v_i1, v_k] * rxplaceholder[v_i2, v_k] + + +def NT_matmul1_fp16_sch_func(): + sch = tvm.tir.Schedule(NT_matmul1_fp16_before) + b0 = sch.get_block("NT_matmul") + sch.pad_einsum(b0, [1, 32, 1, 1]) + l1, l2, l3, l4 = sch.get_loops(b0) + l5, l6 = sch.split(l2, [None, 32]) + sch.reorder(l5, l1, l6, l3, l4) + + b0 = sch.get_block(name="NT_matmul", func_name="main") + b1 = sch.get_block(name="root", func_name="main") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.tiling_structure", ann_val="SSSRRSRS") + _, l2, l3, l4, l5 = sch.get_loops(block=b0) + v6, v7, v8, v9, v10 = sch.sample_perfect_tile(loop=l2, n=5, max_innermost_factor=64, decision=[1, 1, 1, 1, 1]) + l11, l12, l13, l14, l15 = sch.split(loop=l2, factors=[v6, v7, v8, v9, v10], preserve_unit_iters=True) + v16, v17, v18, v19, v20 = sch.sample_perfect_tile(loop=l3, n=5, max_innermost_factor=64, decision=[1, 1, 4, 2, 4]) + l21, l22, l23, l24, l25 = sch.split(loop=l3, factors=[v16, v17, v18, v19, v20], preserve_unit_iters=True) + v26, v27, v28, v29, v30 = sch.sample_perfect_tile(loop=l4, n=5, max_innermost_factor=64, decision=[128, 1, 16, 1, 2]) + l31, l32, l33, l34, l35 = sch.split(loop=l4, factors=[v26, v27, v28, v29, v30], preserve_unit_iters=True) + v36, v37, v38 = sch.sample_perfect_tile(loop=l5, n=3, max_innermost_factor=64, decision=[512, 2, 4]) + l39, l40, l41 = sch.split(loop=l5, factors=[v36, v37, v38], preserve_unit_iters=True) + sch.reorder(l11, l21, l31, l12, l22, l32, l13, l23, l33, l39, l40, l14, l24, l34, l41, l15, l25, l35) + l42 = sch.fuse(l11, l21, l31, preserve_unit_iters=True) + sch.bind(loop=l42, thread_axis="blockIdx.x") + l43 = sch.fuse(l12, l22, l32, preserve_unit_iters=True) + sch.bind(loop=l43, thread_axis="vthread.x") + l44 = sch.fuse(l13, l23, l33, preserve_unit_iters=True) + sch.bind(loop=l44, thread_axis="threadIdx.x") + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_low_inclusive", ann_val=32) + sch.annotate(block_or_loop=b0, ann_key="meta_schedule.thread_extent_high_inclusive", ann_val=256) + b45 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="local") + sch.reverse_compute_at(block=b45, loop=l44, preserve_unit_loops=True, index=-1) + b46 = sch.cache_read(block=b0, read_buffer_index=0, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b46, loop=l39, preserve_unit_loops=True, index=-1) + _, l47, l48, l49, l50, l51, l52, l53 = sch.get_loops(block=b46) + l54 = sch.fuse(l51, l52, l53, preserve_unit_iters=True) + v55 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b46, ann_key="meta_schedule.cooperative_fetch", ann_val=v55) + b56 = sch.cache_read(block=b0, read_buffer_index=1, storage_scope="shared", consumer_blocks=[b0]) + sch.compute_at(block=b56, loop=l39, preserve_unit_loops=True, index=-1) + _, l57, l58, l59, l60, l61, l62 = sch.get_loops(block=b56) + l63 = sch.fuse(l61, l62, preserve_unit_iters=True) + v64 = sch.sample_categorical(candidates=[1, 2, 4, 8], probs=[0.25, 0.25, 0.25, 0.25], decision=2) + sch.annotate(block_or_loop=b56, ann_key="meta_schedule.cooperative_fetch", ann_val=v64) + v65 = sch.sample_categorical(candidates=[0, 16, 64, 512, 1024], probs=[0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001, 0.20000000000000001], decision=4) + sch.annotate(block_or_loop=b1, ann_key="meta_schedule.unroll_explicit", ann_val=v65) + sch.enter_postproc() + sch.unannotate(block_or_loop=b46, ann_key="meta_schedule.cooperative_fetch") + _, l66, l67, l68, l69, l70 = sch.get_loops(block=b46) + l71, l72, l73 = sch.split(loop=l70, factors=[None, 64, 4], preserve_unit_iters=True) + sch.vectorize(loop=l73) + sch.bind(loop=l72, thread_axis="threadIdx.x") + sch.unannotate(block_or_loop=b56, ann_key="meta_schedule.cooperative_fetch") + _, l74, l75, l76, l77, l78 = sch.get_loops(block=b56) + l79, l80, l81 = sch.split(loop=l78, factors=[None, 64, 4], preserve_unit_iters=True) + sch.vectorize(loop=l81) + sch.bind(loop=l80, thread_axis="threadIdx.x") + b82 = sch.get_block(name="root", func_name="main") + sch.unannotate(block_or_loop=b82, ann_key="meta_schedule.unroll_explicit") + _, b83, b84, b85, b86, _ = sch.get_child_blocks(b82) + _, l87, l88, l89, l90, l91, l92, l93 = sch.get_loops(block=b83) + sch.annotate(block_or_loop=l87, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l87, ann_key="pragma_unroll_explicit", ann_val=1) + _, l94, l95, l96, l97, l98, l99, l100 = sch.get_loops(block=b84) + sch.annotate(block_or_loop=l94, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l94, ann_key="pragma_unroll_explicit", ann_val=1) + _, l101, l102, l103, l104, l105, l106, l107, l108, l109, l110, l111, l112 = sch.get_loops(block=b85) + sch.annotate(block_or_loop=l101, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l101, ann_key="pragma_unroll_explicit", ann_val=1) + _, l113, l114, l115, l116, l117, l118 = sch.get_loops(block=b86) + sch.annotate(block_or_loop=l113, ann_key="pragma_auto_unroll_max_step", ann_val=1024) + sch.annotate(block_or_loop=l113, ann_key="pragma_unroll_explicit", ann_val=1) + b119 = sch.get_block(name="NT_matmul", func_name="main") + l0, l120, l121, l122, l123, l124, l125, l126, l127, l128, l129, l130, l131 = sch.get_loops(block=b119) + sch.bind(l0, "blockIdx.y") + b132 = sch.decompose_reduction(block=b119, loop=l123) + + b1 = sch.get_block("rxplaceholder_1_pad") + sch.compute_inline(b1) + b2 = sch.get_block("NT_matmul_pad") + sch.reverse_compute_inline(b2) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func +def decode6(rxplaceholder: T.Buffer((T.int64(512), T.int64(4096)), "uint32"), rxplaceholder_1: T.Buffer((T.int64(128), T.int64(4096)), "uint32"), T_transpose: T.Buffer((T.int64(4096), T.int64(4096)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(4096), T.int64(4096))) + for i, j in T.grid(T.int64(4096), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(rxplaceholder[v_i // T.int64(8), v_j], rxplaceholder_1[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(rxplaceholder[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(rxplaceholder_1[v_i // T.int64(32), v_j], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(rxplaceholder_1[v_i // T.int64(32), v_j], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for ax0, ax1 in T.grid(T.int64(4096), T.int64(4096)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + + +@T.prim_func +def decode7(rxplaceholder: T.Buffer((T.int64(512), T.int64(11008)), "uint32"), rxplaceholder_1: T.Buffer((T.int64(128), T.int64(11008)), "uint32"), T_transpose: T.Buffer((T.int64(11008), T.int64(4096)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(4096), T.int64(11008))) + for i, j in T.grid(T.int64(4096), T.int64(11008)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(rxplaceholder[v_i // T.int64(8), v_j], rxplaceholder_1[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(rxplaceholder[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(rxplaceholder_1[v_i // T.int64(32), v_j], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(rxplaceholder_1[v_i // T.int64(32), v_j], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for ax0, ax1 in T.grid(T.int64(11008), T.int64(4096)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + + +@T.prim_func +def decode8(rxplaceholder: T.Buffer((T.int64(1376), T.int64(4096)), "uint32"), rxplaceholder_1: T.Buffer((T.int64(344), T.int64(4096)), "uint32"), T_transpose: T.Buffer((T.int64(4096), T.int64(11008)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(11008), T.int64(4096))) + for i, j in T.grid(T.int64(11008), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(rxplaceholder[v_i // T.int64(8), v_j], rxplaceholder_1[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(rxplaceholder[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(rxplaceholder_1[v_i // T.int64(32), v_j], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(rxplaceholder_1[v_i // T.int64(32), v_j], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for ax0, ax1 in T.grid(T.int64(4096), T.int64(11008)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + + +@T.prim_func +def decode4_fp16(rxplaceholder: T.Buffer((T.int64(512), T.int64(4096)), "uint32"), rxplaceholder_1: T.Buffer((T.int64(128), T.int64(4096)), "float16"), rxplaceholder_2: T.Buffer((T.int64(128), T.int64(4096)), "float16"), T_transpose: T.Buffer((T.int64(4096), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(4096), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(rxplaceholder[v_i // T.int64(8), v_j], rxplaceholder_1[v_i // T.int64(32), v_j], rxplaceholder_2[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = T.Cast("float16", T.bitwise_and(T.shift_right(rxplaceholder[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * rxplaceholder_1[v_i // T.int64(32), v_j] + rxplaceholder_2[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(4096), T.int64(4096)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + +@T.prim_func +def decode5_fp16(rxplaceholder: T.Buffer((T.int64(512), T.int64(11008)), "uint32"), rxplaceholder_1: T.Buffer((T.int64(128), T.int64(11008)), "float16"), rxplaceholder_2: T.Buffer((T.int64(128), T.int64(11008)), "float16"), T_transpose: T.Buffer((T.int64(11008), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(4096), T.int64(11008)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(11008)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(rxplaceholder[v_i // T.int64(8), v_j], rxplaceholder_1[v_i // T.int64(32), v_j], rxplaceholder_2[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = T.Cast("float16", T.bitwise_and(T.shift_right(rxplaceholder[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * rxplaceholder_1[v_i // T.int64(32), v_j] + rxplaceholder_2[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(11008), T.int64(4096)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + +@T.prim_func +def decode6_fp16(rxplaceholder: T.Buffer((T.int64(1376), T.int64(4096)), "uint32"), rxplaceholder_1: T.Buffer((T.int64(344), T.int64(4096)), "float16"), rxplaceholder_2: T.Buffer((T.int64(344), T.int64(4096)), "float16"), T_transpose: T.Buffer((T.int64(4096), T.int64(11008)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(11008), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(11008), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(rxplaceholder[v_i // T.int64(8), v_j], rxplaceholder_1[v_i // T.int64(32), v_j], rxplaceholder_2[v_i // T.int64(32), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = T.Cast("float16", T.bitwise_and(T.shift_right(rxplaceholder[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * rxplaceholder_1[v_i // T.int64(32), v_j] + rxplaceholder_2[v_i // T.int64(32), v_j] + for ax0, ax1 in T.grid(T.int64(4096), T.int64(11008)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + + +@T.prim_func +def decode_int3_fp16(A: T.Buffer((T.int64(412), T.int64(4096)), "uint32"), B: T.Buffer((T.int64(103), T.int64(4096)), "float16"), T_transpose: T.Buffer((T.int64(4096), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode_1 = T.alloc_buffer((T.int64(4096), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(A[v_i // T.int64(10), v_j], B[v_i // T.int64(40), v_j]) + T.writes(decode_1[v_i, v_j]) + decode_1[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(A[v_i // T.int64(10), v_j], T.Cast("uint32", v_i % T.int64(10)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * B[v_i // T.int64(40), v_j] + for ax0, ax1 in T.grid(T.int64(4096), T.int64(4096)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode_1[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode_1[v_ax1, v_ax0] + +@T.prim_func +def decode1_int3_fp16(A: T.Buffer((T.int64(412), T.int64(11008)), "uint32"), B: T.Buffer((T.int64(103), T.int64(11008)), "float16"), T_transpose: T.Buffer((T.int64(11008), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(4096), T.int64(11008)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(11008)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(A[v_i // T.int64(10), v_j], B[v_i // T.int64(40), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(A[v_i // T.int64(10), v_j], T.Cast("uint32", v_i % T.int64(10)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * B[v_i // T.int64(40), v_j] + for ax0, ax1 in T.grid(T.int64(11008), T.int64(4096)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + +@T.prim_func +def decode2_int3_fp16(A: T.Buffer((T.int64(1104), T.int64(4096)), "uint32"), B: T.Buffer((T.int64(276), T.int64(4096)), "float16"), T_transpose: T.Buffer((T.int64(4096), T.int64(11008)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(11008), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(11008), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(A[v_i // T.int64(10), v_j], B[v_i // T.int64(40), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(A[v_i // T.int64(10), v_j], T.Cast("uint32", v_i % T.int64(10)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * B[v_i // T.int64(40), v_j] + for ax0, ax1 in T.grid(T.int64(4096), T.int64(11008)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + + +@T.prim_func +def decode_int3_int16_fp16(A: T.Buffer((T.int64(824), T.int64(4096)), "uint16"), B: T.Buffer((T.int64(103), T.int64(4096)), "float16"), T_transpose: T.Buffer((T.int64(4096), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode_1 = T.alloc_buffer((T.int64(4096), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(A[v_i // T.int64(5), v_j], B[v_i // T.int64(40), v_j]) + T.writes(decode_1[v_i, v_j]) + decode_1[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(T.Cast("uint32", A[v_i // T.int64(5), v_j]), T.Cast("uint32", v_i % T.int64(5)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * B[v_i // T.int64(40), v_j] + for ax0, ax1 in T.grid(T.int64(4096), T.int64(4096)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode_1[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode_1[v_ax1, v_ax0] + +@T.prim_func +def decode1_int3_int16_fp16(A: T.Buffer((T.int64(824), T.int64(11008)), "uint16"), B: T.Buffer((T.int64(103), T.int64(11008)), "float16"), T_transpose: T.Buffer((T.int64(11008), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(4096), T.int64(11008)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(11008)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(A[v_i // T.int64(5), v_j], B[v_i // T.int64(40), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(T.Cast("uint32", A[v_i // T.int64(5), v_j]), T.Cast("uint32", v_i % T.int64(5)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * B[v_i // T.int64(40), v_j] + for ax0, ax1 in T.grid(T.int64(11008), T.int64(4096)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + +@T.prim_func +def decode2_int3_int16_fp16(A: T.Buffer((T.int64(2208), T.int64(4096)), "uint16"), B: T.Buffer((T.int64(276), T.int64(4096)), "float16"), T_transpose: T.Buffer((T.int64(4096), T.int64(11008)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + decode = T.alloc_buffer((T.int64(11008), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(11008), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(A[v_i // T.int64(5), v_j], B[v_i // T.int64(40), v_j]) + T.writes(decode[v_i, v_j]) + decode[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(T.Cast("uint32", A[v_i // T.int64(5), v_j]), T.Cast("uint32", v_i % T.int64(5)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * B[v_i // T.int64(40), v_j] + for ax0, ax1 in T.grid(T.int64(4096), T.int64(11008)): + with T.block("T_transpose"): + v_ax0, v_ax1 = T.axis.remap("SS", [ax0, ax1]) + T.reads(decode[v_ax1, v_ax0]) + T.writes(T_transpose[v_ax0, v_ax1]) + T_transpose[v_ax0, v_ax1] = decode[v_ax1, v_ax0] + + +def decode_sch_func(orig_func): + sch = tvm.tir.Schedule(orig_func) + b0 = sch.get_block(name="decode", func_name="main") + l1, l2 = sch.get_loops(block=b0) + l3, l4 = sch.split(loop=l1, factors=[None, 8], preserve_unit_iters=True) + v5, v6, v7 = sch.sample_perfect_tile(loop=l3, n=3, max_innermost_factor=4, decision=[32, 8, 2]) + l8, l9, l10 = sch.split(loop=l3, factors=[v5, v6, v7], preserve_unit_iters=True) + v11, v12 = sch.sample_perfect_tile(loop=l2, n=2, max_innermost_factor=16, decision=[256, 16]) + l13, l14 = sch.split(loop=l2, factors=[v11, v12], preserve_unit_iters=True) + sch.reorder(l8, l13, l9, l14, l10, l4) + sch.bind(loop=l8, thread_axis="blockIdx.y") + sch.bind(loop=l13, thread_axis="blockIdx.x") + sch.bind(loop=l9, thread_axis="threadIdx.y") + sch.bind(loop=l14, thread_axis="threadIdx.x") + sch.unroll(loop=l4) + b15 = sch.cache_write(block=b0, write_buffer_index=0, storage_scope="shared") + sch.compute_inline(block=b15) + b16 = sch.get_block(name="T_transpose", func_name="main") + sch.reverse_compute_at(block=b16, loop=l13, preserve_unit_loops=True, index=-1) + b17 = sch.get_block(name="T_transpose", func_name="main") + l18, l19, l20, l21 = sch.get_loops(block=b17) + l22 = sch.fuse(l20, l21, preserve_unit_iters=True) + l23, l24, l25 = sch.split(loop=l22, factors=[None, v12, 4], preserve_unit_iters=True) + sch.bind(loop=l24, thread_axis="threadIdx.x") + sch.vectorize(loop=l25) + sch.storage_align(block=b0, buffer_index=0, axis=0, factor=32, offset=1) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +@T.prim_func +def fused_decode3_matmul1_before(lv2931: T.Buffer((T.int64(512), T.int64(32000)), "uint32"), lv2932: T.Buffer((T.int64(128), T.int64(32000)), "uint32"), lv1511: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32"), var_matmul_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(32000)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(32000))) + for i, j in T.grid(T.int64(4096), T.int64(32000)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv2931[v_i // T.int64(8), v_j], lv2932[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(lv2931[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(lv2932[v_i // T.int64(32), v_j], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(lv2932[v_i // T.int64(32), v_j], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(32000), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1511[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv1511[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + + +@T.prim_func +def fused_decode3_matmul1_after(lv1123: T.Buffer((T.int64(512), T.int64(32000)), "uint32"), lv1124: T.Buffer((T.int64(128), T.int64(32000)), "uint32"), lv1511: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32"), var_matmul_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(32000)), "float32")): + T.func_attr({"global_symbol": "main", "tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + # with T.block("root"): + var_decode_intermediate_pad_local = T.alloc_buffer((T.int64(4096), T.int64(32000)), scope="local") + var_matmul_intermediate_pad_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(32000)), scope="local") + lv1511_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="shared") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(125), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(4)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax1_ax2_fused_2 in T.vectorized(T.int64(4)): + with T.block("lv1511_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4096), ax1_ax2_fused_0 * T.int64(1024) + ax1_ax2_fused_1 * T.int64(4) + ax1_ax2_fused_2) + T.reads(lv1511[v0, v1, v2]) + T.writes(lv1511_shared[v0, v1, v2]) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv1511_shared[v0, v1, v2] = lv1511[v0, v1, v2] + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(64)): + for ax0_0 in range(T.int64(8)): + for ax0_1 in T.unroll(T.int64(8)): + for ax1 in range(T.int64(1)): + with T.block("var_decode_intermediate_pad"): + v0 = T.axis.spatial(T.int64(4096), k_0_0 * T.int64(64) + ax0_0 * T.int64(8) + ax0_1) + v1 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1123[v0 // T.int64(8), v1], lv1124[v0 // T.int64(32), v1]) + T.writes(var_decode_intermediate_pad_local[v0, v1]) + var_decode_intermediate_pad_local[v0, v1] = T.Cast("float32", T.bitwise_and(T.shift_right(lv1123[v0 // T.int64(8), v1], T.Cast("uint32", v0 % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(lv1124[v0 // T.int64(32), v1], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(lv1124[v0 // T.int64(32), v1], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for k_0_1_k_1_fused in range(T.int64(64)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4096), k_0_0 * T.int64(64) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2], lv1511_shared[v_i0, v_i1, v_k], var_decode_intermediate_pad_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2] + lv1511_shared[v_i0, v_i1, v_k] * var_decode_intermediate_pad_local[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_pad_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(var_matmul_intermediate_pad_local[v0, v1, v2]) + T.writes(var_matmul_intermediate[v0, v1, v2]) + var_matmul_intermediate[v0, v1, v2] = var_matmul_intermediate_pad_local[v0, v1, v2] + + +@T.prim_func +def fused_decode4_fused_matmul5_add3_before(lv3184: T.Buffer((T.int64(512), T.int64(4096)), "uint32"), lv3185: T.Buffer((T.int64(128), T.int64(4096)), "uint32"), lv452: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32"), lv2710: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(4096))) + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096))) + for i, j in T.grid(T.int64(4096), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv3184[v_i // T.int64(8), v_j], lv3185[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(lv3184[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(lv3185[v_i // T.int64(32), v_j], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(lv3185[v_i // T.int64(32), v_j], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(4096), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv452[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv452[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv2710[v_ax0, v_ax1, v_ax2], var_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = lv2710[v_ax0, v_ax1, v_ax2] + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_decode4_fused_matmul5_add3_after(lv1143: T.Buffer((T.int64(512), T.int64(4096)), "uint32"), lv1144: T.Buffer((T.int64(128), T.int64(4096)), "uint32"), lv3: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32"), lv2710: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32")): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(4096), T.int64(4096)), scope="local") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="local") + lv3_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="shared") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(16), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(4)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax1_ax2_fused_2 in T.vectorized(T.int64(4)): + with T.block("lv3_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4096), ax1_ax2_fused_0 * T.int64(1024) + ax1_ax2_fused_1 * T.int64(4) + ax1_ax2_fused_2) + T.reads(lv3[v0, v1, v2]) + T.writes(lv3_shared[v0, v1, v2]) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv3_shared[v0, v1, v2] = lv3[v0, v1, v2] + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(64)): + for ax0_0 in range(T.int64(8)): + for ax0_1 in T.unroll(T.int64(8)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(4096), k_0_0 * T.int64(64) + ax0_0 * T.int64(8) + ax0_1) + v_i = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1143[v_j // T.int64(8), v_i], lv1144[v_j // T.int64(32), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float32", T.bitwise_and(T.shift_right(lv1143[v_j // T.int64(8), v_i], T.Cast("uint32", v_j % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(lv1144[v_j // T.int64(32), v_i], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(lv1144[v_j // T.int64(32), v_i], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for k_0_1_k_1_fused in range(T.int64(64)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4096), k_0_0 * T.int64(64) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv3_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv3_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(lv2710[v0, v1, v2], var_matmul_intermediate_local[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = lv2710[v0, v1, v2] + var_matmul_intermediate_local[v0, v1, v2] + + +@T.prim_func +def fused_decode4_matmul5_before(lv3166: T.Buffer((T.int64(512), T.int64(4096)), "uint32"), lv3167: T.Buffer((T.int64(128), T.int64(4096)), "uint32"), lv2712: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32"), var_matmul_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(4096))) + for i, j in T.grid(T.int64(4096), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv3166[v_i // T.int64(8), v_j], lv3167[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(lv3166[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(lv3167[v_i // T.int64(32), v_j], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(lv3167[v_i // T.int64(32), v_j], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(4096), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2712[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv2712[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + + +@T.prim_func +def fused_decode4_matmul5_after(lv1128: T.Buffer((T.int64(512), T.int64(4096)), "uint32"), lv1129: T.Buffer((T.int64(128), T.int64(4096)), "uint32"), lv2712: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32"), var_matmul_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32")): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(4096), T.int64(4096)), scope="local") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="local") + lv2712_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="shared") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(16), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(4)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax1_ax2_fused_2 in T.vectorized(T.int64(4)): + with T.block("lv2712_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4096), ax1_ax2_fused_0 * T.int64(1024) + ax1_ax2_fused_1 * T.int64(4) + ax1_ax2_fused_2) + T.reads(lv2712[v0, v1, v2]) + T.writes(lv2712_shared[v0, v1, v2]) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv2712_shared[v0, v1, v2] = lv2712[v0, v1, v2] + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(64)): + for ax0_0 in range(T.int64(8)): + for ax0_1 in T.unroll(T.int64(8)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(4096), k_0_0 * T.int64(64) + ax0_0 * T.int64(8) + ax0_1) + v_i = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1128[v_j // T.int64(8), v_i], lv1129[v_j // T.int64(32), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float32", T.bitwise_and(T.shift_right(lv1128[v_j // T.int64(8), v_i], T.Cast("uint32", v_j % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(lv1129[v_j // T.int64(32), v_i], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(lv1129[v_j // T.int64(32), v_i], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for k_0_1_k_1_fused in range(T.int64(64)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4096), k_0_0 * T.int64(64) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv2712_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv2712_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(var_matmul_intermediate_local[v0, v1, v2]) + T.writes(var_matmul_intermediate[v0, v1, v2]) + var_matmul_intermediate[v0, v1, v2] = var_matmul_intermediate_local[v0, v1, v2] + + +@T.prim_func +def fused_decode5_fused_matmul8_multiply1_before(lv1617: T.Buffer((T.int64(512), T.int64(11008)), "uint32"), lv1618: T.Buffer((T.int64(128), T.int64(11008)), "uint32"), lv2749: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32"), lv4: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float32"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(11008))) + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008))) + for i, j in T.grid(T.int64(4096), T.int64(11008)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1617[v_i // T.int64(8), v_j], lv1618[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(lv1617[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(lv1618[v_i // T.int64(32), v_j], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(lv1618[v_i // T.int64(32), v_j], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(11008), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2749[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv2749[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv4[v_ax0, v_ax1, v_ax2], var_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = lv4[v_ax0, v_ax1, v_ax2] * var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_decode5_fused_matmul8_multiply1_after(lv1153: T.Buffer((T.int64(512), T.int64(11008)), "uint32"), lv1154: T.Buffer((T.int64(128), T.int64(11008)), "uint32"), lv2749: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32"), lv5: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float32"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float32")): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(4096), T.int64(11008)), scope="local") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), scope="local") + lv2749_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="shared") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(43), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(4)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax1_ax2_fused_2 in T.vectorized(T.int64(4)): + with T.block("lv2749_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4096), ax1_ax2_fused_0 * T.int64(1024) + ax1_ax2_fused_1 * T.int64(4) + ax1_ax2_fused_2) + T.reads(lv2749[v0, v1, v2]) + T.writes(lv2749_shared[v0, v1, v2]) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv2749_shared[v0, v1, v2] = lv2749[v0, v1, v2] + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(64)): + for ax0_0 in range(T.int64(8)): + for ax0_1 in T.unroll(T.int64(8)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(4096), k_0_0 * T.int64(64) + ax0_0 * T.int64(8) + ax0_1) + v_i = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1153[v_j // T.int64(8), v_i], lv1154[v_j // T.int64(32), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float32", T.bitwise_and(T.shift_right(lv1153[v_j // T.int64(8), v_i], T.Cast("uint32", v_j % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(lv1154[v_j // T.int64(32), v_i], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(lv1154[v_j // T.int64(32), v_i], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for k_0_1_k_1_fused in range(T.int64(64)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4096), k_0_0 * T.int64(64) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv2749_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv2749_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(lv5[v0, v1, v2], var_matmul_intermediate_local[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = lv5[v0, v1, v2] * var_matmul_intermediate_local[v0, v1, v2] + + +@T.prim_func +def fused_decode5_fused_matmul8_silu1_before(lv1611: T.Buffer((T.int64(512), T.int64(11008)), "uint32"), lv1612: T.Buffer((T.int64(128), T.int64(11008)), "uint32"), lv2749: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(11008))) + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008))) + compute = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008))) + for i, j in T.grid(T.int64(4096), T.int64(11008)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1611[v_i // T.int64(8), v_j], lv1612[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(lv1611[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(lv1612[v_i // T.int64(32), v_j], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(lv1612[v_i // T.int64(32), v_j], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(11008), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2749[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv2749[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(11008)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_matmul_intermediate[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.sigmoid(var_matmul_intermediate[v_i0, v_i1, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], compute[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = var_matmul_intermediate[v_ax0, v_ax1, v_ax2] * compute[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_decode5_fused_matmul8_silu1_after(lv1148: T.Buffer((T.int64(512), T.int64(11008)), "uint32"), lv1149: T.Buffer((T.int64(128), T.int64(11008)), "uint32"), lv2749: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float32")): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(4096), T.int64(11008)), scope="local") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), scope="local") + lv2749_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="shared") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(43), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(4)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax1_ax2_fused_2 in T.vectorized(T.int64(4)): + with T.block("lv2749_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4096), ax1_ax2_fused_0 * T.int64(1024) + ax1_ax2_fused_1 * T.int64(4) + ax1_ax2_fused_2) + T.reads(lv2749[v0, v1, v2]) + T.writes(lv2749_shared[v0, v1, v2]) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv2749_shared[v0, v1, v2] = lv2749[v0, v1, v2] + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(64)): + for ax0_0 in range(T.int64(8)): + for ax0_1 in T.unroll(T.int64(8)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(4096), k_0_0 * T.int64(64) + ax0_0 * T.int64(8) + ax0_1) + v_i = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1148[v_j // T.int64(8), v_i], lv1149[v_j // T.int64(32), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float32", T.bitwise_and(T.shift_right(lv1148[v_j // T.int64(8), v_i], T.Cast("uint32", v_j % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(lv1149[v_j // T.int64(32), v_i], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(lv1149[v_j // T.int64(32), v_i], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for k_0_1_k_1_fused in range(T.int64(64)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4096), k_0_0 * T.int64(64) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv2749_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv2749_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(var_matmul_intermediate_local[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = var_matmul_intermediate_local[v0, v1, v2] * T.sigmoid(var_matmul_intermediate_local[v0, v1, v2]) + + +@T.prim_func +def fused_decode6_fused_matmul9_add3_before(lv1623: T.Buffer((T.int64(1376), T.int64(4096)), "uint32"), lv1624: T.Buffer((T.int64(344), T.int64(4096)), "uint32"), lv230: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float32"), lv228: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(11008), T.int64(4096))) + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096))) + for i, j in T.grid(T.int64(11008), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1623[v_i // T.int64(8), v_j], lv1624[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float32", T.bitwise_and(T.shift_right(lv1623[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(lv1624[v_i // T.int64(32), v_j], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(lv1624[v_i // T.int64(32), v_j], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(4096), T.int64(11008)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv230[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float32(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv230[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv228[v_ax0, v_ax1, v_ax2], var_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = lv228[v_ax0, v_ax1, v_ax2] + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_decode6_fused_matmul9_add3_after(lv1158: T.Buffer((T.int64(1376), T.int64(4096)), "uint32"), lv1159: T.Buffer((T.int64(344), T.int64(4096)), "uint32"), lv6: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float32"), lv4: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float32")): + T.func_attr({"global_symbol": "main", "tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(11008), T.int64(4096)), scope="local") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="local") + lv6_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), scope="shared") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(16), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(2)): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(22)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("lv6_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(11008), k_0_0 * T.int64(5504) + (ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1)) + T.where(ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1 < T.int64(5504)) + T.reads(lv6[v0, v1, v2]) + T.writes(lv6_shared[v0, v1, v2]) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv6_shared[v0, v1, v2] = lv6[v0, v1, v2] + for k_0_1 in range(T.int64(86)): + for ax0_0 in range(T.int64(8)): + for ax0_1 in T.unroll(T.int64(8)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(11008), k_0_0 * T.int64(5504) + k_0_1 * T.int64(64) + ax0_0 * T.int64(8) + ax0_1) + v_i = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1158[v_j // T.int64(8), v_i], lv1159[v_j // T.int64(32), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float32", T.bitwise_and(T.shift_right(lv1158[v_j // T.int64(8), v_i], T.Cast("uint32", v_j % T.int64(8) * T.int64(4))), T.uint32(15))) * T.reinterpret("float32", T.shift_left(T.bitwise_and(lv1159[v_j // T.int64(32), v_i], T.uint32(65535)), T.uint32(16))) + T.reinterpret("float32", T.shift_left(T.bitwise_and(T.shift_right(lv1159[v_j // T.int64(32), v_i], T.uint32(16)), T.uint32(65535)), T.uint32(16))) + for k_0_2_k_1_fused in range(T.int64(64)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(11008), k_0_0 * T.int64(5504) + k_0_1 * T.int64(64) + k_0_2_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv6_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv6_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(lv4[v0, v1, v2], var_matmul_intermediate_local[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = lv4[v0, v1, v2] + var_matmul_intermediate_local[v0, v1, v2] + + +@T.prim_func +def fused_decode3_matmul1_fp16_before(lv5865: T.Buffer((T.int64(512), T.int64(32000)), "uint32"), lv5866: T.Buffer((T.int64(128), T.int64(32000)), "float16"), lv5867: T.Buffer((T.int64(128), T.int64(32000)), "float16"), lv2705: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), var_matmul_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(32000)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(32000)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(32000)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv5865[v_i // T.int64(8), v_j], lv5866[v_i // T.int64(32), v_j], lv5867[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float16", T.bitwise_and(T.shift_right(lv5865[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * lv5866[v_i // T.int64(32), v_j] + lv5867[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(32000), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2705[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv2705[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + + +@T.prim_func +def fused_decode3_matmul1_fp16_after(lv1123: T.Buffer((T.int64(512), T.int64(32000)), "uint32"), lv5866: T.Buffer((T.int64(128), T.int64(32000)), "float16"), lv5867: T.Buffer((T.int64(128), T.int64(32000)), "float16"), lv1511: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), var_matmul_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(32000)), "float16")): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + # with T.block("root"): + var_decode_intermediate_pad_local = T.alloc_buffer((T.int64(4096), T.int64(32000)), scope="local", dtype="float16") + var_matmul_intermediate_pad_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(32000)), scope="local", dtype="float16") + lv1511_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(125), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(4)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax1_ax2_fused_2 in T.vectorized(T.int64(4)): + with T.block("lv1511_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4096), ax1_ax2_fused_0 * T.int64(1024) + ax1_ax2_fused_1 * T.int64(4) + ax1_ax2_fused_2) + T.reads(lv1511[v0, v1, v2]) + T.writes(lv1511_shared[v0, v1, v2]) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv1511_shared[v0, v1, v2] = lv1511[v0, v1, v2] + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(64)): + for ax0_0 in range(T.int64(8)): + for ax0_1 in T.unroll(T.int64(8)): + for ax1 in range(T.int64(1)): + with T.block("var_decode_intermediate_pad"): + v0 = T.axis.spatial(T.int64(4096), k_0_0 * T.int64(64) + ax0_0 * T.int64(8) + ax0_1) + v1 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1123[v0 // T.int64(8), v1], lv5866[v0 // T.int64(32), v1], lv5867[v0 // T.int64(32), v1]) + T.writes(var_decode_intermediate_pad_local[v0, v1]) + var_decode_intermediate_pad_local[v0, v1] = T.Cast("float16", T.bitwise_and(T.shift_right(lv1123[v0 // T.int64(8), v1], T.Cast("uint32", v0 % T.int64(8) * T.int64(4))), T.uint32(15))) * lv5866[v0 // T.int64(32), v1] + lv5867[v0 // T.int64(32), v1] + for k_0_1_k_1_fused in range(T.int64(64)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4096), k_0_0 * T.int64(64) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2], lv1511_shared[v_i0, v_i1, v_k], var_decode_intermediate_pad_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2] + lv1511_shared[v_i0, v_i1, v_k] * var_decode_intermediate_pad_local[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_pad_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(var_matmul_intermediate_pad_local[v0, v1, v2]) + T.writes(var_matmul_intermediate[v0, v1, v2]) + var_matmul_intermediate[v0, v1, v2] = var_matmul_intermediate_pad_local[v0, v1, v2] + + +@T.prim_func +def fused_decode3_matmul1_cast_fp16_before(lv1803: T.Buffer((T.int64(512), T.int64(32000)), "uint32"), lv1804: T.Buffer((T.int64(128), T.int64(32000)), "float16"), lv1805: T.Buffer((T.int64(128), T.int64(32000)), "float16"), lv3025: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(32000)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(32000)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(32000)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(32000)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1803[v_i // T.int64(8), v_j], lv1804[v_i // T.int64(32), v_j], lv1805[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float16", T.bitwise_and(T.shift_right(lv1803[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * lv1804[v_i // T.int64(32), v_j] + lv1805[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(32000), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv3025[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv3025[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(32000)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_matmul_intermediate[v_i0, v_i1, v_i2]) + T.writes(p_output0_intermediate[v_i0, v_i1, v_i2]) + p_output0_intermediate[v_i0, v_i1, v_i2] = T.Cast("float32", var_matmul_intermediate[v_i0, v_i1, v_i2]) + + +@T.prim_func +def fused_decode3_matmul1_cast_fp16_after(lv1123: T.Buffer((T.int64(512), T.int64(32000)), "uint32"), lv5866: T.Buffer((T.int64(128), T.int64(32000)), "float16"), lv5867: T.Buffer((T.int64(128), T.int64(32000)), "float16"), lv1511: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), var_matmul_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(32000)), "float32")): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + # with T.block("root"): + var_decode_intermediate_pad_local = T.alloc_buffer((T.int64(4096), T.int64(32000)), scope="local", dtype="float16") + var_matmul_intermediate_pad_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(32000)), scope="local", dtype="float16") + lv1511_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(125), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(4)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax1_ax2_fused_2 in T.vectorized(T.int64(4)): + with T.block("lv1511_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4096), ax1_ax2_fused_0 * T.int64(1024) + ax1_ax2_fused_1 * T.int64(4) + ax1_ax2_fused_2) + T.reads(lv1511[v0, v1, v2]) + T.writes(lv1511_shared[v0, v1, v2]) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv1511_shared[v0, v1, v2] = lv1511[v0, v1, v2] + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(64)): + for ax0_0 in range(T.int64(8)): + for ax0_1 in T.unroll(T.int64(8)): + for ax1 in range(T.int64(1)): + with T.block("var_decode_intermediate_pad"): + v0 = T.axis.spatial(T.int64(4096), k_0_0 * T.int64(64) + ax0_0 * T.int64(8) + ax0_1) + v1 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1123[v0 // T.int64(8), v1], lv5866[v0 // T.int64(32), v1], lv5867[v0 // T.int64(32), v1]) + T.writes(var_decode_intermediate_pad_local[v0, v1]) + var_decode_intermediate_pad_local[v0, v1] = T.Cast("float16", T.bitwise_and(T.shift_right(lv1123[v0 // T.int64(8), v1], T.Cast("uint32", v0 % T.int64(8) * T.int64(4))), T.uint32(15))) * lv5866[v0 // T.int64(32), v1] + lv5867[v0 // T.int64(32), v1] + for k_0_1_k_1_fused in range(T.int64(64)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4096), k_0_0 * T.int64(64) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2], lv1511_shared[v_i0, v_i1, v_k], var_decode_intermediate_pad_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2] + lv1511_shared[v_i0, v_i1, v_k] * var_decode_intermediate_pad_local[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_pad_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(var_matmul_intermediate_pad_local[v0, v1, v2]) + T.writes(var_matmul_intermediate[v0, v1, v2]) + var_matmul_intermediate[v0, v1, v2] = T.Cast("float32", var_matmul_intermediate_pad_local[v0, v1, v2]) + + +@T.prim_func +def fused_decode4_fused_matmul5_add3_fp16_before(lv35: T.Buffer((T.int64(512), T.int64(4096)), "uint32"), lv36: T.Buffer((T.int64(128), T.int64(4096)), "float16"), lv37: T.Buffer((T.int64(128), T.int64(4096)), "float16"), lv2: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), lv2710: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(4096)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv35[v_i // T.int64(8), v_j], lv36[v_i // T.int64(32), v_j], lv37[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float16", T.bitwise_and(T.shift_right(lv35[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * lv36[v_i // T.int64(32), v_j] + lv37[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(4096), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv2[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv2710[v_ax0, v_ax1, v_ax2], var_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = lv2710[v_ax0, v_ax1, v_ax2] + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_decode4_fused_matmul5_add3_fp16_after(lv1143: T.Buffer((T.int64(512), T.int64(4096)), "uint32"), lv36: T.Buffer((T.int64(128), T.int64(4096)), "float16"), lv37: T.Buffer((T.int64(128), T.int64(4096)), "float16"), lv3: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), lv2710: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(4096), T.int64(4096)), scope="local", dtype="float16") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="local", dtype="float16") + lv3_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(16), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(4)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax1_ax2_fused_2 in T.vectorized(T.int64(4)): + with T.block("lv3_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4096), ax1_ax2_fused_0 * T.int64(1024) + ax1_ax2_fused_1 * T.int64(4) + ax1_ax2_fused_2) + T.reads(lv3[v0, v1, v2]) + T.writes(lv3_shared[v0, v1, v2]) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv3_shared[v0, v1, v2] = lv3[v0, v1, v2] + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(64)): + for ax0_0 in range(T.int64(8)): + for ax0_1 in T.unroll(T.int64(8)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(4096), k_0_0 * T.int64(64) + ax0_0 * T.int64(8) + ax0_1) + v_i = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1143[v_j // T.int64(8), v_i], lv36[v_j // T.int64(32), v_i], lv37[v_j // T.int64(32), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float16", T.bitwise_and(T.shift_right(lv1143[v_j // T.int64(8), v_i], T.Cast("uint32", v_j % T.int64(8) * T.int64(4))), T.uint32(15))) * lv36[v_j // T.int64(32), v_i] + lv37[v_j // T.int64(32), v_i] + for k_0_1_k_1_fused in range(T.int64(64)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4096), k_0_0 * T.int64(64) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv3_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv3_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(lv2710[v0, v1, v2], var_matmul_intermediate_local[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = lv2710[v0, v1, v2] + var_matmul_intermediate_local[v0, v1, v2] + + +@T.prim_func +def fused_decode4_matmul5_fp16_before(lv11: T.Buffer((T.int64(512), T.int64(4096)), "uint32"), lv12: T.Buffer((T.int64(128), T.int64(4096)), "float16"), lv13: T.Buffer((T.int64(128), T.int64(4096)), "float16"), lv2712: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), var_matmul_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv11[v_i // T.int64(8), v_j], lv12[v_i // T.int64(32), v_j], lv13[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float16", T.bitwise_and(T.shift_right(lv11[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * lv12[v_i // T.int64(32), v_j] + lv13[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(4096), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2712[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv2712[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + + +@T.prim_func +def fused_decode4_matmul5_fp16_after(lv1128: T.Buffer((T.int64(512), T.int64(4096)), "uint32"), lv12: T.Buffer((T.int64(128), T.int64(4096)), "float16"), lv13: T.Buffer((T.int64(128), T.int64(4096)), "float16"), lv2712: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), var_matmul_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(4096), T.int64(4096)), scope="local", dtype="float16") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="local", dtype="float16") + lv2712_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(16), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(4)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax1_ax2_fused_2 in T.vectorized(T.int64(4)): + with T.block("lv2712_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4096), ax1_ax2_fused_0 * T.int64(1024) + ax1_ax2_fused_1 * T.int64(4) + ax1_ax2_fused_2) + T.reads(lv2712[v0, v1, v2]) + T.writes(lv2712_shared[v0, v1, v2]) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv2712_shared[v0, v1, v2] = lv2712[v0, v1, v2] + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(64)): + for ax0_0 in range(T.int64(8)): + for ax0_1 in T.unroll(T.int64(8)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(4096), k_0_0 * T.int64(64) + ax0_0 * T.int64(8) + ax0_1) + v_i = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1128[v_j // T.int64(8), v_i], lv12[v_j // T.int64(32), v_i], lv13[v_j // T.int64(32), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float16", T.bitwise_and(T.shift_right(lv1128[v_j // T.int64(8), v_i], T.Cast("uint32", v_j % T.int64(8) * T.int64(4))), T.uint32(15))) * lv12[v_j // T.int64(32), v_i] + lv13[v_j // T.int64(32), v_i] + for k_0_1_k_1_fused in range(T.int64(64)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4096), k_0_0 * T.int64(64) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv2712_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv2712_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(var_matmul_intermediate_local[v0, v1, v2]) + T.writes(var_matmul_intermediate[v0, v1, v2]) + var_matmul_intermediate[v0, v1, v2] = var_matmul_intermediate_local[v0, v1, v2] + + +@T.prim_func +def fused_decode5_fused_matmul8_multiply1_fp16_before(lv51: T.Buffer((T.int64(512), T.int64(11008)), "uint32"), lv52: T.Buffer((T.int64(128), T.int64(11008)), "float16"), lv53: T.Buffer((T.int64(128), T.int64(11008)), "float16"), lv2749: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), lv5: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(11008)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(11008)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv51[v_i // T.int64(8), v_j], lv52[v_i // T.int64(32), v_j], lv53[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float16", T.bitwise_and(T.shift_right(lv51[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * lv52[v_i // T.int64(32), v_j] + lv53[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(11008), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2749[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv2749[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv5[v_ax0, v_ax1, v_ax2], var_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = lv5[v_ax0, v_ax1, v_ax2] * var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_decode5_fused_matmul8_multiply1_fp16_after(lv1153: T.Buffer((T.int64(512), T.int64(11008)), "uint32"), lv52: T.Buffer((T.int64(128), T.int64(11008)), "float16"), lv53: T.Buffer((T.int64(128), T.int64(11008)), "float16"), lv2749: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), lv5: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16")): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(4096), T.int64(11008)), scope="local", dtype="float16") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), scope="local", dtype="float16") + lv2749_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(43), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(4)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax1_ax2_fused_2 in T.vectorized(T.int64(4)): + with T.block("lv2749_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4096), ax1_ax2_fused_0 * T.int64(1024) + ax1_ax2_fused_1 * T.int64(4) + ax1_ax2_fused_2) + T.reads(lv2749[v0, v1, v2]) + T.writes(lv2749_shared[v0, v1, v2]) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv2749_shared[v0, v1, v2] = lv2749[v0, v1, v2] + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(64)): + for ax0_0 in range(T.int64(8)): + for ax0_1 in T.unroll(T.int64(8)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(4096), k_0_0 * T.int64(64) + ax0_0 * T.int64(8) + ax0_1) + v_i = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1153[v_j // T.int64(8), v_i], lv52[v_j // T.int64(32), v_i], lv53[v_j // T.int64(32), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float16", T.bitwise_and(T.shift_right(lv1153[v_j // T.int64(8), v_i], T.Cast("uint32", v_j % T.int64(8) * T.int64(4))), T.uint32(15))) * lv52[v_j // T.int64(32), v_i] + lv53[v_j // T.int64(32), v_i] + for k_0_1_k_1_fused in range(T.int64(64)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4096), k_0_0 * T.int64(64) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv2749_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv2749_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(lv5[v0, v1, v2], var_matmul_intermediate_local[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = lv5[v0, v1, v2] * var_matmul_intermediate_local[v0, v1, v2] + + +@T.prim_func +def fused_decode5_fused_matmul8_silu1_fp16_before(lv43: T.Buffer((T.int64(512), T.int64(11008)), "uint32"), lv44: T.Buffer((T.int64(128), T.int64(11008)), "float16"), lv45: T.Buffer((T.int64(128), T.int64(11008)), "float16"), lv2749: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(11008)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16") + compute = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(11008)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv43[v_i // T.int64(8), v_j], lv44[v_i // T.int64(32), v_j], lv45[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float16", T.bitwise_and(T.shift_right(lv43[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * lv44[v_i // T.int64(32), v_j] + lv45[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(11008), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv2749[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv2749[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(11008)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_matmul_intermediate[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.sigmoid(var_matmul_intermediate[v_i0, v_i1, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], compute[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = var_matmul_intermediate[v_ax0, v_ax1, v_ax2] * compute[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_decode5_fused_matmul8_silu1_fp16_after(lv1148: T.Buffer((T.int64(512), T.int64(11008)), "uint32"), lv44: T.Buffer((T.int64(128), T.int64(11008)), "float16"), lv45: T.Buffer((T.int64(128), T.int64(11008)), "float16"), lv2749: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16")): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(4096), T.int64(11008)), scope="local", dtype="float16") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), scope="local", dtype="float16") + lv2749_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(43), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(4)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax1_ax2_fused_2 in T.vectorized(T.int64(4)): + with T.block("lv2749_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4096), ax1_ax2_fused_0 * T.int64(1024) + ax1_ax2_fused_1 * T.int64(4) + ax1_ax2_fused_2) + T.reads(lv2749[v0, v1, v2]) + T.writes(lv2749_shared[v0, v1, v2]) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv2749_shared[v0, v1, v2] = lv2749[v0, v1, v2] + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(64)): + for ax0_0 in range(T.int64(8)): + for ax0_1 in T.unroll(T.int64(8)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(4096), k_0_0 * T.int64(64) + ax0_0 * T.int64(8) + ax0_1) + v_i = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1148[v_j // T.int64(8), v_i], lv44[v_j // T.int64(32), v_i], lv45[v_j // T.int64(32), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float16", T.bitwise_and(T.shift_right(lv1148[v_j // T.int64(8), v_i], T.Cast("uint32", v_j % T.int64(8) * T.int64(4))), T.uint32(15))) * lv44[v_j // T.int64(32), v_i] + lv45[v_j // T.int64(32), v_i] + for k_0_1_k_1_fused in range(T.int64(64)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4096), k_0_0 * T.int64(64) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv2749_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv2749_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(var_matmul_intermediate_local[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = var_matmul_intermediate_local[v0, v1, v2] * T.sigmoid(var_matmul_intermediate_local[v0, v1, v2]) + + +@T.prim_func +def fused_decode6_fused_matmul9_add3_fp16_before(lv59: T.Buffer((T.int64(1376), T.int64(4096)), "uint32"), lv60: T.Buffer((T.int64(344), T.int64(4096)), "float16"), lv61: T.Buffer((T.int64(344), T.int64(4096)), "float16"), lv5: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16"), lv3: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(11008), T.int64(4096)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(11008), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv59[v_i // T.int64(8), v_j], lv60[v_i // T.int64(32), v_j], lv61[v_i // T.int64(32), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = T.Cast("float16", T.bitwise_and(T.shift_right(lv59[v_i // T.int64(8), v_j], T.Cast("uint32", v_i % T.int64(8) * T.int64(4))), T.uint32(15))) * lv60[v_i // T.int64(32), v_j] + lv61[v_i // T.int64(32), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(4096), T.int64(11008)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv5[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv5[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv3[v_ax0, v_ax1, v_ax2], var_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = lv3[v_ax0, v_ax1, v_ax2] + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_decode6_fused_matmul9_add3_fp16_after(lv1158: T.Buffer((T.int64(1376), T.int64(4096)), "uint32"), lv60: T.Buffer((T.int64(344), T.int64(4096)), "float16"), lv61: T.Buffer((T.int64(344), T.int64(4096)), "float16"), lv6: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16"), lv4: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(11008), T.int64(4096)), scope="local", dtype="float16") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="local", dtype="float16") + lv6_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(16), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(2)): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(22)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("lv6_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(11008), k_0_0 * T.int64(5504) + (ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1)) + T.where(ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1 < T.int64(5504)) + T.reads(lv6[v0, v1, v2]) + T.writes(lv6_shared[v0, v1, v2]) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv6_shared[v0, v1, v2] = lv6[v0, v1, v2] + for k_0_1 in range(T.int64(86)): + for ax0_0 in range(T.int64(8)): + for ax0_1 in T.unroll(T.int64(8)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(11008), k_0_0 * T.int64(5504) + k_0_1 * T.int64(64) + ax0_0 * T.int64(8) + ax0_1) + v_i = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1158[v_j // T.int64(8), v_i], lv60[v_j // T.int64(32), v_i], lv61[v_j // T.int64(32), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float16", T.bitwise_and(T.shift_right(lv1158[v_j // T.int64(8), v_i], T.Cast("uint32", v_j % T.int64(8) * T.int64(4))), T.uint32(15))) * lv60[v_j // T.int64(32), v_i] + lv61[v_j // T.int64(32), v_i] + for k_0_2_k_1_fused in range(T.int64(64)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(11008), k_0_0 * T.int64(5504) + k_0_1 * T.int64(64) + k_0_2_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv6_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv6_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(lv4[v0, v1, v2], var_matmul_intermediate_local[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = lv4[v0, v1, v2] + var_matmul_intermediate_local[v0, v1, v2] + + +@T.prim_func +def fused_decode3_matmul1_cast_int3_fp16_before(lv2931: T.Buffer((T.int64(412), T.int64(32000)), "uint32"), lv2932: T.Buffer((T.int64(103), T.int64(32000)), "float16"), lv3025: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(32000)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(32000)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(32000)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(32000)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv2931[v_i // T.int64(10), v_j], lv2932[v_i // T.int64(40), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(lv2931[v_i // T.int64(10), v_j], T.Cast("uint32", v_i % T.int64(10)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * lv2932[v_i // T.int64(40), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(32000), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv3025[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv3025[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(32000)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_matmul_intermediate[v_i0, v_i1, v_i2]) + T.writes(p_output0_intermediate[v_i0, v_i1, v_i2]) + p_output0_intermediate[v_i0, v_i1, v_i2] = T.Cast("float32", var_matmul_intermediate[v_i0, v_i1, v_i2]) + + +@T.prim_func +def fused_decode3_matmul1_cast_int3_fp16_after(lv1123: T.Buffer((T.int64(412), T.int64(32000)), "uint32"), lv5866: T.Buffer((T.int64(103), T.int64(32000)), "float16"), lv1511: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), var_matmul_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(32000)), "float32")): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + # with T.block("root"): + var_decode_intermediate_pad_local = T.alloc_buffer((T.int64(4120), T.int64(32000)), scope="local", dtype="float16") + var_matmul_intermediate_pad_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(32000)), scope="local", dtype="float16") + lv1511_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4120)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(125), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(17)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("lv1511_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4120), ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1) + T.reads(lv1511[v0, v1, v2]) + T.writes(lv1511_shared[v0, v1, v2]) + T.where(ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1 < T.int64(4120)) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv1511_shared[v0, v1, v2] = T.if_then_else(v2 < T.int64(4096), lv1511[v0, v1, v2], T.float16(0)) + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(103)): + for ax0_0 in T.unroll(T.int64(40)): + for ax1 in range(T.int64(1)): + with T.block("var_decode_intermediate_pad"): + v0 = T.axis.spatial(T.int64(4120), k_0_0 * T.int64(40) + ax0_0) + v1 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1123[v0 // T.int64(10), v1], lv5866[v0 // T.int64(40), v1]) + T.writes(var_decode_intermediate_pad_local[v0, v1]) + var_decode_intermediate_pad_local[v0, v1] = T.Cast("float16", T.bitwise_and(T.shift_right(lv1123[v0 // T.int64(10), v1], T.Cast("uint32", v0 % T.int64(10)) * T.uint32(3)), T.uint32(7))) - T.float16(3) + for k_0_1_k_1_fused in range(T.int64(40)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4120), k_0_0 * T.int64(40) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2], lv1511_shared[v_i0, v_i1, v_k], var_decode_intermediate_pad_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2] + lv1511_shared[v_i0, v_i1, v_k] * var_decode_intermediate_pad_local[v_k, v_i2] * lv5866[v_k // T.int64(40), v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_pad_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(var_matmul_intermediate_pad_local[v0, v1, v2]) + T.writes(var_matmul_intermediate[v0, v1, v2]) + var_matmul_intermediate[v0, v1, v2] = T.Cast("float32", var_matmul_intermediate_pad_local[v0, v1, v2]) + + +@T.prim_func +def fused_decode4_fused_matmul5_add3_int3_fp16_before(lv1605: T.Buffer((T.int64(412), T.int64(4096)), "uint32"), lv1606: T.Buffer((T.int64(103), T.int64(4096)), "float16"), lv164: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), lv1518: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(4096)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1605[v_i // T.int64(10), v_j], lv1606[v_i // T.int64(40), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(lv1605[v_i // T.int64(10), v_j], T.Cast("uint32", v_i % T.int64(10)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * lv1606[v_i // T.int64(40), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(4096), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv164[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv164[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv1518[v_ax0, v_ax1, v_ax2], var_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = lv1518[v_ax0, v_ax1, v_ax2] + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_decode4_fused_matmul5_add3_int3_fp16_after(lv1143: T.Buffer((T.int64(412), T.int64(4096)), "uint32"), lv36: T.Buffer((T.int64(103), T.int64(4096)), "float16"), lv3: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), lv2710: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(4120), T.int64(4096)), scope="local", dtype="float16") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="local", dtype="float16") + lv3_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4120)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(16), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(17)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("lv3_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4120), ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1) + T.reads(lv3[v0, v1, v2]) + T.writes(lv3_shared[v0, v1, v2]) + T.where(ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1 < T.int64(4120)) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv3_shared[v0, v1, v2] = T.if_then_else(v2 < T.int64(4096), lv3[v0, v1, v2], T.float16(0)) + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(103)): + for ax0_0 in T.unroll(T.int64(40)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(4120), k_0_0 * T.int64(40) + ax0_0) + v_i = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1143[v_j // T.int64(10), v_i], lv36[v_j // T.int64(40), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float16", T.bitwise_and(T.shift_right(lv1143[v_j // T.int64(10), v_i], T.Cast("uint32", v_j % T.int64(10)) * T.uint32(3)), T.uint32(7))) - T.float16(3) + for k_0_1_k_1_fused in range(T.int64(40)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4120), k_0_0 * T.int64(40) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv3_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv3_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] * lv36[v_k // T.int64(40), v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(lv2710[v0, v1, v2], var_matmul_intermediate_local[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = lv2710[v0, v1, v2] + var_matmul_intermediate_local[v0, v1, v2] + + +@T.prim_func +def fused_decode4_matmul5_int3_fp16_before(lv1587: T.Buffer((T.int64(412), T.int64(4096)), "uint32"), lv1588: T.Buffer((T.int64(103), T.int64(4096)), "float16"), lv1520: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), var_matmul_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1587[v_i // T.int64(10), v_j], lv1588[v_i // T.int64(40), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(lv1587[v_i // T.int64(10), v_j], T.Cast("uint32", v_i % T.int64(10)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * lv1588[v_i // T.int64(40), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(4096), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1520[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv1520[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + + +@T.prim_func +def fused_decode4_matmul5_int3_fp16_after(lv1128: T.Buffer((T.int64(412), T.int64(4096)), "uint32"), lv12: T.Buffer((T.int64(103), T.int64(4096)), "float16"), lv2712: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), var_matmul_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(4120), T.int64(4096)), scope="local", dtype="float16") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="local", dtype="float16") + lv2712_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4120)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(16), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(17)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("lv2712_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4120), ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1) + T.reads(lv2712[v0, v1, v2]) + T.writes(lv2712_shared[v0, v1, v2]) + T.where(ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1 < T.int64(4120)) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv2712_shared[v0, v1, v2] = T.if_then_else(v2 < T.int64(4096), lv2712[v0, v1, v2], T.float16(0)) + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(103)): + for ax0_0 in T.unroll(T.int64(40)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(4120), k_0_0 * T.int64(40) + ax0_0) + v_i = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1128[v_j // T.int64(10), v_i], lv12[v_j // T.int64(40), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float16", T.bitwise_and(T.shift_right(lv1128[v_j // T.int64(10), v_i], T.Cast("uint32", v_j % T.int64(10)) * T.uint32(3)), T.uint32(7))) - T.float16(3) + for k_0_1_k_1_fused in range(T.int64(40)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4120), k_0_0 * T.int64(40) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv2712_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv2712_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] * lv12[v_k // T.int64(40), v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(var_matmul_intermediate_local[v0, v1, v2]) + T.writes(var_matmul_intermediate[v0, v1, v2]) + var_matmul_intermediate[v0, v1, v2] = var_matmul_intermediate_local[v0, v1, v2] + + +@T.prim_func +def fused_decode5_fused_matmul8_multiply1_int3_fp16_before(lv1617: T.Buffer((T.int64(412), T.int64(11008)), "uint32"), lv1618: T.Buffer((T.int64(103), T.int64(11008)), "float16"), lv1557: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), lv3: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(11008)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(11008)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1617[v_i // T.int64(10), v_j], lv1618[v_i // T.int64(40), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(lv1617[v_i // T.int64(10), v_j], T.Cast("uint32", v_i % T.int64(10)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * lv1618[v_i // T.int64(40), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(11008), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1557[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv1557[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv3[v_ax0, v_ax1, v_ax2], var_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = lv3[v_ax0, v_ax1, v_ax2] * var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_decode5_fused_matmul8_multiply1_int3_fp16_after(lv1153: T.Buffer((T.int64(412), T.int64(11008)), "uint32"), lv52: T.Buffer((T.int64(103), T.int64(11008)), "float16"), lv2749: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), lv5: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16")): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(4120), T.int64(11008)), scope="local", dtype="float16") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), scope="local", dtype="float16") + lv2749_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4120)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(43), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(17)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("lv2749_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4120), ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1) + T.reads(lv2749[v0, v1, v2]) + T.writes(lv2749_shared[v0, v1, v2]) + T.where(ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1 < T.int64(4120)) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv2749_shared[v0, v1, v2] = T.if_then_else(v2 < T.int64(4096), lv2749[v0, v1, v2], T.float16(0)) + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(103)): + for ax0_0 in T.unroll(T.int64(40)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(4120), k_0_0 * T.int64(40) + ax0_0) + v_i = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1153[v_j // T.int64(10), v_i], lv52[v_j // T.int64(40), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float16", T.bitwise_and(T.shift_right(lv1153[v_j // T.int64(10), v_i], T.Cast("uint32", v_j % T.int64(10)) * T.uint32(3)), T.uint32(7))) - T.float16(3) + for k_0_1_k_1_fused in range(T.int64(40)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4120), k_0_0 * T.int64(40) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv2749_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv2749_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] * lv52[v_k // T.int64(40), v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(lv5[v0, v1, v2], var_matmul_intermediate_local[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = lv5[v0, v1, v2] * var_matmul_intermediate_local[v0, v1, v2] + + +@T.prim_func +def fused_decode5_fused_matmul8_silu1_int3_fp16_before(lv1611: T.Buffer((T.int64(412), T.int64(11008)), "uint32"), lv1612: T.Buffer((T.int64(103), T.int64(11008)), "float16"), lv1557: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(11008)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16") + compute = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(11008)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1611[v_i // T.int64(10), v_j], lv1612[v_i // T.int64(40), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(lv1611[v_i // T.int64(10), v_j], T.Cast("uint32", v_i % T.int64(10)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * lv1612[v_i // T.int64(40), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(11008), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1557[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv1557[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(11008)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_matmul_intermediate[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.sigmoid(var_matmul_intermediate[v_i0, v_i1, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], compute[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = var_matmul_intermediate[v_ax0, v_ax1, v_ax2] * compute[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_decode5_fused_matmul8_silu1_int3_fp16_after(lv1148: T.Buffer((T.int64(412), T.int64(11008)), "uint32"), lv44: T.Buffer((T.int64(103), T.int64(11008)), "float16"), lv2749: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16")): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(4120), T.int64(11008)), scope="local", dtype="float16") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), scope="local", dtype="float16") + lv2749_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4120)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(43), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(17)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("lv2749_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4120), ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1) + T.reads(lv2749[v0, v1, v2]) + T.writes(lv2749_shared[v0, v1, v2]) + T.where(ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1 < T.int64(4120)) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv2749_shared[v0, v1, v2] = T.if_then_else(v2 < T.int64(4096), lv2749[v0, v1, v2], T.float16(0)) + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(103)): + for ax0_0 in T.unroll(T.int64(40)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(4120), k_0_0 * T.int64(40) + ax0_0) + v_i = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1148[v_j // T.int64(10), v_i], lv44[v_j // T.int64(40), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float16", T.bitwise_and(T.shift_right(lv1148[v_j // T.int64(10), v_i], T.Cast("uint32", v_j % T.int64(10)) * T.uint32(3)), T.uint32(7))) - T.float16(3) + for k_0_1_k_1_fused in range(T.int64(40)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4120), k_0_0 * T.int64(40) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv2749_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv2749_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] * lv44[v_k // T.int64(40), v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(var_matmul_intermediate_local[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = var_matmul_intermediate_local[v0, v1, v2] * T.sigmoid(var_matmul_intermediate_local[v0, v1, v2]) + + +@T.prim_func +def fused_decode6_fused_matmul9_add3_int3_fp16_before(lv1623: T.Buffer((T.int64(1104), T.int64(4096)), "uint32"), lv1624: T.Buffer((T.int64(276), T.int64(4096)), "float16"), lv167: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16"), lv165: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(11008), T.int64(4096)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(11008), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1623[v_i // T.int64(10), v_j], lv1624[v_i // T.int64(40), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(lv1623[v_i // T.int64(10), v_j], T.Cast("uint32", v_i % T.int64(10)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * lv1624[v_i // T.int64(40), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(4096), T.int64(11008)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv167[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv167[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv165[v_ax0, v_ax1, v_ax2], var_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = lv165[v_ax0, v_ax1, v_ax2] + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_decode6_fused_matmul9_add3_int3_fp16_after(lv1158: T.Buffer((T.int64(1104), T.int64(4096)), "uint32"), lv60: T.Buffer((T.int64(276), T.int64(4096)), "float16"), lv6: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16"), lv4: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(11040), T.int64(4096)), scope="local", dtype="float16") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="local", dtype="float16") + lv6_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11040)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(16), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(2)): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(22)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("lv6_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(11040), k_0_0 * T.int64(5520) + (ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1)) + T.where(ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1 < T.int64(5520)) + T.reads(lv6[v0, v1, v2]) + T.writes(lv6_shared[v0, v1, v2]) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv6_shared[v0, v1, v2] = T.if_then_else(v2 < T.int64(11008), lv6[v0, v1, v2], T.float16(0)) + for k_0_1 in range(T.int64(69)): + for ax0_0 in T.unroll(T.int64(80)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(11040), k_0_0 * T.int64(5520) + k_0_1 * T.int64(80) + ax0_0) + v_i = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1158[v_j // T.int64(10), v_i], lv60[v_j // T.int64(40), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float16", T.bitwise_and(T.shift_right(lv1158[v_j // T.int64(10), v_i], T.Cast("uint32", v_j % T.int64(10)) * T.uint32(3)), T.uint32(7))) - T.float16(3) + for k_0_2_k_1_fused in range(T.int64(80)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(11040), k_0_0 * T.int64(5520) + k_0_1 * T.int64(80) + k_0_2_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv6_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv6_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] * lv60[v_k // T.int64(40), v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(lv4[v0, v1, v2], var_matmul_intermediate_local[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = lv4[v0, v1, v2] + var_matmul_intermediate_local[v0, v1, v2] + + +@T.prim_func +def fused_decode3_matmul1_cast_int3_int16_fp16_before(lv2931: T.Buffer((T.int64(824), T.int64(32000)), "uint16"), lv2932: T.Buffer((T.int64(103), T.int64(32000)), "float16"), lv3025: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(32000)), "float32")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(32000)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(32000)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(32000)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv2931[v_i // T.int64(5), v_j], lv2932[v_i // T.int64(40), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(T.Cast("uint32", lv2931[v_i // T.int64(5), v_j]), T.Cast("uint32", v_i % T.int64(5)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * lv2932[v_i // T.int64(40), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(32000), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv3025[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv3025[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(32000)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_matmul_intermediate[v_i0, v_i1, v_i2]) + T.writes(p_output0_intermediate[v_i0, v_i1, v_i2]) + p_output0_intermediate[v_i0, v_i1, v_i2] = T.Cast("float32", var_matmul_intermediate[v_i0, v_i1, v_i2]) + + +@T.prim_func +def fused_decode3_matmul1_cast_int3_int16_fp16_after(lv1123: T.Buffer((T.int64(824), T.int64(32000)), "uint16"), lv5866: T.Buffer((T.int64(103), T.int64(32000)), "float16"), lv1511: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), var_matmul_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(32000)), "float32")): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + # with T.block("root"): + var_decode_intermediate_pad_local = T.alloc_buffer((T.int64(4120), T.int64(32000)), scope="local", dtype="float16") + var_scale_intermediate_local = T.alloc_buffer((T.int64(103), T.int64(4096)), scope="local", dtype="float16") + var_matmul_intermediate_pad_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(32000)), scope="local", dtype="float16") + lv1511_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4120)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(125), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(17)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("lv1511_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4120), ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1) + T.reads(lv1511[v0, v1, v2]) + T.writes(lv1511_shared[v0, v1, v2]) + T.where(ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1 < T.int64(4120)) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv1511_shared[v0, v1, v2] = T.if_then_else(v2 < T.int64(4096), lv1511[v0, v1, v2], T.float16(0)) + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(103)): + for ax0_0 in T.unroll(T.int64(40)): + for ax1 in range(T.int64(1)): + with T.block("var_decode_intermediate_pad"): + v0 = T.axis.spatial(T.int64(4120), k_0_0 * T.int64(40) + ax0_0) + v1 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1123[v0 // T.int64(5), v1]) + T.writes(var_decode_intermediate_pad_local[v0, v1]) + var_decode_intermediate_pad_local[v0, v1] = T.Cast("float16", T.Cast("int16", T.bitwise_and(T.shift_right(T.Cast("uint16", lv1123[v0 // T.int64(5), v1]), T.Cast("uint16", v0 % T.int64(5)) * T.uint16(3)), T.uint16(7))) - T.int16(3)) + for ax0_0 in range(T.int64(1)): + for ax1 in range(T.int64(1)): + with T.block("scale"): + v_j = T.axis.spatial(T.int64(103), k_0_0 + ax0_0) + v_i = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv5866[v_j, v_i]) + T.writes(var_scale_intermediate_local[v_j, v_i]) + var_scale_intermediate_local[v_j, v_i] = lv5866[v_j, v_i] + for k_0_1_k_1_fused in range(T.int64(40)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4120), k_0_0 * T.int64(40) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2], lv1511_shared[v_i0, v_i1, v_k], var_decode_intermediate_pad_local[v_k, v_i2], var_scale_intermediate_local[v_k // T.int64(40), v_i2]) + T.writes(var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_pad_local[v_i0, v_i1, v_i2] + lv1511_shared[v_i0, v_i1, v_k] * var_decode_intermediate_pad_local[v_k, v_i2] * var_scale_intermediate_local[v_k // T.int64(40), v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_pad_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(32000), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(var_matmul_intermediate_pad_local[v0, v1, v2]) + T.writes(var_matmul_intermediate[v0, v1, v2]) + var_matmul_intermediate[v0, v1, v2] = T.Cast("float32", var_matmul_intermediate_pad_local[v0, v1, v2]) + + +@T.prim_func +def fused_decode4_fused_matmul5_add3_int3_int16_fp16_before(lv1605: T.Buffer((T.int64(824), T.int64(4096)), "uint16"), lv1606: T.Buffer((T.int64(103), T.int64(4096)), "float16"), lv164: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), lv1518: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(4096)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1605[v_i // T.int64(5), v_j], lv1606[v_i // T.int64(40), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(T.Cast("uint32", lv1605[v_i // T.int64(5), v_j]), T.Cast("uint32", v_i % T.int64(5)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * lv1606[v_i // T.int64(40), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(4096), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv164[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv164[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv1518[v_ax0, v_ax1, v_ax2], var_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = lv1518[v_ax0, v_ax1, v_ax2] + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_decode4_fused_matmul5_add3_int3_int16_fp16_after(lv1143: T.Buffer((T.int64(824), T.int64(4096)), "uint16"), lv36: T.Buffer((T.int64(103), T.int64(4096)), "float16"), lv3: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), lv2710: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(4120), T.int64(4096)), scope="local", dtype="float16") + var_scale_intermediate_local = T.alloc_buffer((T.int64(103), T.int64(4096)), scope="local", dtype="float16") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="local", dtype="float16") + lv3_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4120)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(16), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(17)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("lv3_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4120), ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1) + T.reads(lv3[v0, v1, v2]) + T.writes(lv3_shared[v0, v1, v2]) + T.where(ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1 < T.int64(4120)) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv3_shared[v0, v1, v2] = T.if_then_else(v2 < T.int64(4096), lv3[v0, v1, v2], T.float16(0)) + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(103)): + for ax0_0 in T.unroll(T.int64(40)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(4120), k_0_0 * T.int64(40) + ax0_0) + v_i = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1143[v_j // T.int64(5), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float16", T.Cast("int16", T.bitwise_and(T.shift_right(T.Cast("uint16", lv1143[v_j // T.int64(5), v_i]), T.Cast("uint16", v_j % T.int64(5)) * T.uint16(3)), T.uint16(7))) - T.int16(3)) + for ax0_0 in range(T.int64(1)): + for ax1 in range(T.int64(1)): + with T.block("scale"): + v_j = T.axis.spatial(T.int64(103), k_0_0 + ax0_0) + v_i = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv36[v_j, v_i]) + T.writes(var_scale_intermediate_local[v_j, v_i]) + var_scale_intermediate_local[v_j, v_i] = lv36[v_j, v_i] + for k_0_1_k_1_fused in range(T.int64(40)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4120), k_0_0 * T.int64(40) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv3_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2], var_scale_intermediate_local[v_k // T.int64(40), v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv3_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] * var_scale_intermediate_local[v_k // T.int64(40), v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(lv2710[v0, v1, v2], var_matmul_intermediate_local[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = lv2710[v0, v1, v2] + var_matmul_intermediate_local[v0, v1, v2] + + +@T.prim_func +def fused_decode4_matmul5_int3_int16_fp16_before(lv1587: T.Buffer((T.int64(824), T.int64(4096)), "uint16"), lv1588: T.Buffer((T.int64(103), T.int64(4096)), "float16"), lv1520: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), var_matmul_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1587[v_i // T.int64(5), v_j], lv1588[v_i // T.int64(40), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(T.Cast("uint32", lv1587[v_i // T.int64(5), v_j]), T.Cast("uint32", v_i % T.int64(5)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * lv1588[v_i // T.int64(40), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(4096), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1520[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv1520[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + + +@T.prim_func +def fused_decode4_matmul5_int3_int16_fp16_after(lv1128: T.Buffer((T.int64(824), T.int64(4096)), "uint16"), lv12: T.Buffer((T.int64(103), T.int64(4096)), "float16"), lv2712: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), var_matmul_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(4120), T.int64(4096)), scope="local", dtype="float16") + var_scale_intermediate_local = T.alloc_buffer((T.int64(103), T.int64(4096)), scope="local", dtype="float16") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="local", dtype="float16") + lv2712_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4120)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(16), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(17)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("lv2712_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4120), ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1) + T.reads(lv2712[v0, v1, v2]) + T.writes(lv2712_shared[v0, v1, v2]) + T.where(ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1 < T.int64(4120)) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv2712_shared[v0, v1, v2] = T.if_then_else(v2 < T.int64(4096), lv2712[v0, v1, v2], T.float16(0)) + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(103)): + for ax0_0 in T.unroll(T.int64(40)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(4120), k_0_0 * T.int64(40) + ax0_0) + v_i = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1128[v_j // T.int64(5), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float16", T.Cast("int16", T.bitwise_and(T.shift_right(T.Cast("uint16", lv1128[v_j // T.int64(5), v_i]), T.Cast("uint16", v_j % T.int64(5)) * T.uint16(3)), T.uint16(7))) - T.int16(3)) + for ax0_0 in range(T.int64(1)): + for ax1 in range(T.int64(1)): + with T.block("scale"): + v_j = T.axis.spatial(T.int64(103), k_0_0 + ax0_0) + v_i = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv12[v_j, v_i]) + T.writes(var_scale_intermediate_local[v_j, v_i]) + var_scale_intermediate_local[v_j, v_i] = lv12[v_j, v_i] + for k_0_1_k_1_fused in range(T.int64(40)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4120), k_0_0 * T.int64(40) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv2712_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2], var_scale_intermediate_local[v_k // T.int64(40), v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv2712_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] * var_scale_intermediate_local[v_k // T.int64(40), v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(var_matmul_intermediate_local[v0, v1, v2]) + T.writes(var_matmul_intermediate[v0, v1, v2]) + var_matmul_intermediate[v0, v1, v2] = var_matmul_intermediate_local[v0, v1, v2] + + +@T.prim_func +def fused_decode5_fused_matmul8_multiply1_int3_int16_fp16_before(lv1617: T.Buffer((T.int64(824), T.int64(11008)), "uint16"), lv1618: T.Buffer((T.int64(103), T.int64(11008)), "float16"), lv1557: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), lv3: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(11008)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(11008)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1617[v_i // T.int64(5), v_j], lv1618[v_i // T.int64(40), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(T.Cast("uint32", lv1617[v_i // T.int64(5), v_j]), T.Cast("uint32", v_i % T.int64(5)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * lv1618[v_i // T.int64(40), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(11008), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1557[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv1557[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv3[v_ax0, v_ax1, v_ax2], var_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = lv3[v_ax0, v_ax1, v_ax2] * var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_decode5_fused_matmul8_multiply1_int3_int16_fp16_after(lv1153: T.Buffer((T.int64(824), T.int64(11008)), "uint16"), lv52: T.Buffer((T.int64(103), T.int64(11008)), "float16"), lv2749: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), lv5: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16")): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(4120), T.int64(11008)), scope="local", dtype="float16") + var_scale_intermediate_local = T.alloc_buffer((T.int64(103), T.int64(4096)), scope="local", dtype="float16") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), scope="local", dtype="float16") + lv2749_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4120)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(43), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(17)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("lv2749_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4120), ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1) + T.reads(lv2749[v0, v1, v2]) + T.writes(lv2749_shared[v0, v1, v2]) + T.where(ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1 < T.int64(4120)) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv2749_shared[v0, v1, v2] = T.if_then_else(v2 < T.int64(4096), lv2749[v0, v1, v2], T.float16(0)) + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(103)): + for ax0_0 in T.unroll(T.int64(40)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(4120), k_0_0 * T.int64(40) + ax0_0) + v_i = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1153[v_j // T.int64(5), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float16", T.Cast("int16", T.bitwise_and(T.shift_right(T.Cast("uint16", lv1153[v_j // T.int64(5), v_i]), T.Cast("uint16", v_j % T.int64(5)) * T.uint16(3)), T.uint16(7))) - T.int16(3)) + for ax0_0 in range(T.int64(1)): + for ax1 in range(T.int64(1)): + with T.block("scale"): + v_j = T.axis.spatial(T.int64(103), k_0_0 + ax0_0) + v_i = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv52[v_j, v_i]) + T.writes(var_scale_intermediate_local[v_j, v_i]) + var_scale_intermediate_local[v_j, v_i] = lv52[v_j, v_i] + for k_0_1_k_1_fused in range(T.int64(40)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4120), k_0_0 * T.int64(40) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv2749_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2], var_scale_intermediate_local[v_k // T.int64(40), v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv2749_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] * var_scale_intermediate_local[v_k // T.int64(40), v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(lv5[v0, v1, v2], var_matmul_intermediate_local[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = lv5[v0, v1, v2] * var_matmul_intermediate_local[v0, v1, v2] + + +@T.prim_func +def fused_decode5_fused_matmul8_silu1_int3_int16_fp16_before(lv1611: T.Buffer((T.int64(824), T.int64(11008)), "uint16"), lv1612: T.Buffer((T.int64(103), T.int64(11008)), "float16"), lv1557: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(4096), T.int64(11008)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16") + compute = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16") + for i, j in T.grid(T.int64(4096), T.int64(11008)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1611[v_i // T.int64(5), v_j], lv1612[v_i // T.int64(40), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(T.Cast("uint32", lv1611[v_i // T.int64(5), v_j]), T.Cast("uint32", v_i % T.int64(5)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * lv1612[v_i // T.int64(40), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(11008), T.int64(4096)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv1557[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv1557[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for i0, i1, i2 in T.grid(T.int64(1), T.int64(1), T.int64(11008)): + with T.block("compute"): + v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2]) + T.reads(var_matmul_intermediate[v_i0, v_i1, v_i2]) + T.writes(compute[v_i0, v_i1, v_i2]) + compute[v_i0, v_i1, v_i2] = T.sigmoid(var_matmul_intermediate[v_i0, v_i1, v_i2]) + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(11008)): + with T.block("T_multiply"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(var_matmul_intermediate[v_ax0, v_ax1, v_ax2], compute[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = var_matmul_intermediate[v_ax0, v_ax1, v_ax2] * compute[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_decode5_fused_matmul8_silu1_int3_int16_fp16_after(lv1148: T.Buffer((T.int64(824), T.int64(11008)), "uint16"), lv44: T.Buffer((T.int64(103), T.int64(11008)), "float16"), lv2749: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16")): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(4120), T.int64(11008)), scope="local", dtype="float16") + var_scale_intermediate_local = T.alloc_buffer((T.int64(103), T.int64(11008)), scope="local", dtype="float16") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11008)), scope="local", dtype="float16") + lv2749_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4120)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(43), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(17)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("lv2749_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(4120), ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1) + T.reads(lv2749[v0, v1, v2]) + T.writes(lv2749_shared[v0, v1, v2]) + T.where(ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1 < T.int64(4120)) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv2749_shared[v0, v1, v2] = T.if_then_else(v2 < T.int64(4096), lv2749[v0, v1, v2], T.float16(0)) + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(103)): + for ax0_0 in T.unroll(T.int64(40)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(4120), k_0_0 * T.int64(40) + ax0_0) + v_i = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1148[v_j // T.int64(5), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float16", T.Cast("int16", T.bitwise_and(T.shift_right(T.Cast("uint16", lv1148[v_j // T.int64(5), v_i]), T.Cast("uint16", v_j % T.int64(5)) * T.uint16(3)), T.uint16(7))) - T.int16(3)) + for ax0_0 in range(T.int64(1)): + for ax1 in range(T.int64(1)): + with T.block("scale"): + v_j = T.axis.spatial(T.int64(103), k_0_0 + ax0_0) + v_i = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv44[v_j, v_i]) + T.writes(var_scale_intermediate_local[v_j, v_i]) + var_scale_intermediate_local[v_j, v_i] = lv44[v_j, v_i] + for k_0_1_k_1_fused in range(T.int64(40)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(4120), k_0_0 * T.int64(40) + k_0_1_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv2749_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2], var_scale_intermediate_local[v_k // T.int64(40), v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv2749_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] * var_scale_intermediate_local[v_k // T.int64(40), v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(11008), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(var_matmul_intermediate_local[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = var_matmul_intermediate_local[v0, v1, v2] * T.sigmoid(var_matmul_intermediate_local[v0, v1, v2]) + + +@T.prim_func +def fused_decode6_fused_matmul9_add3_int3_int16_fp16_before(lv1623: T.Buffer((T.int64(2208), T.int64(4096)), "uint16"), lv1624: T.Buffer((T.int64(276), T.int64(4096)), "float16"), lv167: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16"), lv165: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True)}) + # with T.block("root"): + var_decode_intermediate = T.alloc_buffer((T.int64(11008), T.int64(4096)), "float16") + var_matmul_intermediate = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16") + for i, j in T.grid(T.int64(11008), T.int64(4096)): + with T.block("decode"): + v_i, v_j = T.axis.remap("SS", [i, j]) + T.reads(lv1623[v_i // T.int64(5), v_j], lv1624[v_i // T.int64(40), v_j]) + T.writes(var_decode_intermediate[v_i, v_j]) + var_decode_intermediate[v_i, v_j] = (T.Cast("float16", T.bitwise_and(T.shift_right(T.Cast("uint32", lv1623[v_i // T.int64(5), v_j]), T.Cast("uint32", v_i % T.int64(5)) * T.uint32(3)), T.uint32(7))) - T.float16(3)) * lv1624[v_i // T.int64(40), v_j] + for i0, i1, i2, k in T.grid(T.int64(1), T.int64(1), T.int64(4096), T.int64(11008)): + with T.block("matmul"): + v_i0, v_i1, v_i2, v_k = T.axis.remap("SSSR", [i0, i1, i2, k]) + T.reads(lv167[v_i0, v_i1, v_k], var_decode_intermediate[v_k, v_i2]) + T.writes(var_matmul_intermediate[v_i0, v_i1, v_i2]) + with T.init(): + var_matmul_intermediate[v_i0, v_i1, v_i2] = T.float16(0) + var_matmul_intermediate[v_i0, v_i1, v_i2] = var_matmul_intermediate[v_i0, v_i1, v_i2] + lv167[v_i0, v_i1, v_k] * var_decode_intermediate[v_k, v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(4096)): + with T.block("T_add"): + v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2]) + T.reads(lv165[v_ax0, v_ax1, v_ax2], var_matmul_intermediate[v_ax0, v_ax1, v_ax2]) + T.writes(p_output0_intermediate[v_ax0, v_ax1, v_ax2]) + p_output0_intermediate[v_ax0, v_ax1, v_ax2] = lv165[v_ax0, v_ax1, v_ax2] + var_matmul_intermediate[v_ax0, v_ax1, v_ax2] + + +@T.prim_func +def fused_decode6_fused_matmul9_add3_int3_int16_fp16_after(lv1158: T.Buffer((T.int64(2208), T.int64(4096)), "uint16"), lv60: T.Buffer((T.int64(276), T.int64(4096)), "float16"), lv6: T.Buffer((T.int64(1), T.int64(1), T.int64(11008)), "float16"), lv4: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16"), p_output0_intermediate: T.Buffer((T.int64(1), T.int64(1), T.int64(4096)), "float16")): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + # with T.block("root"): + var_decode_intermediate_local = T.alloc_buffer((T.int64(11040), T.int64(4096)), scope="local", dtype="float16") + var_scale_intermediate_local = T.alloc_buffer((T.int64(276), T.int64(4096)), scope="local", dtype="float16") + var_matmul_intermediate_local = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(4096)), scope="local", dtype="float16") + lv6_shared = T.alloc_buffer((T.int64(1), T.int64(1), T.int64(11040)), scope="shared", dtype="float16") + for i0_i1_i2_0_fused in T.thread_binding(T.int64(16), thread="blockIdx.x", annotations={"pragma_auto_unroll_max_step": 16, "pragma_unroll_explicit": 1}): + for i2_1 in T.thread_binding(T.int64(1), thread="vthread.x"): + for i2_2 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + for ax0, ax1_ax2_fused_0 in T.grid(T.int64(1), T.int64(44)): + for ax1_ax2_fused_1 in T.thread_binding(T.int64(256), thread="threadIdx.x"): + with T.block("lv2749_shared"): + v0 = T.axis.spatial(T.int64(1), ax0) + v1 = T.axis.spatial(T.int64(1), T.int64(0)) + v2 = T.axis.spatial(T.int64(11040), ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1) + T.reads(lv6[v0, v1, v2]) + T.writes(lv6_shared[v0, v1, v2]) + T.where(ax1_ax2_fused_0 * T.int64(256) + ax1_ax2_fused_1 < T.int64(11040)) + T.block_attr({"buffer_dim_align": [[0, 1, 32, 8]]}) + lv6_shared[v0, v1, v2] = T.if_then_else(v2 < T.int64(11008), lv6[v0, v1, v2], T.float16(0)) + with T.block("matmul_init"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + T.reads() + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = T.float32(0) + for k_0_0 in range(T.int64(138)): + for ax0_0 in T.unroll(T.int64(80)): + for ax1 in range(T.int64(1)): + with T.block("decode"): + v_j = T.axis.spatial(T.int64(11040), k_0_0 * T.int64(80) + ax0_0) + v_i = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv1158[v_j // T.int64(5), v_i]) + T.writes(var_decode_intermediate_local[v_j, v_i]) + var_decode_intermediate_local[v_j, v_i] = T.Cast("float16", T.Cast("int16", T.bitwise_and(T.shift_right(T.Cast("uint16", lv1158[v_j // T.int64(5), v_i]), T.Cast("uint16", v_j % T.int64(5)) * T.uint16(3)), T.uint16(7))) - T.int16(3)) + for ax0_0 in T.unroll(T.int64(2)): + for ax1 in range(T.int64(1)): + with T.block("scale"): + v_j = T.axis.spatial(T.int64(276), k_0_0 * T.int64(2) + ax0_0) + v_i = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax1) + T.reads(lv60[v_j, v_i]) + T.writes(var_scale_intermediate_local[v_j, v_i]) + var_scale_intermediate_local[v_j, v_i] = lv60[v_j, v_i] + for k_0_2_k_1_fused in range(T.int64(80)): + with T.block("matmul_update"): + v_i0 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i1 = T.axis.spatial(T.int64(1), T.int64(0)) + v_i2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_1 * T.int64(256) + i2_2) + v_k = T.axis.reduce(T.int64(11040), k_0_0 * T.int64(80) + k_0_2_k_1_fused) + T.reads(var_matmul_intermediate_local[v_i0, v_i1, v_i2], lv6_shared[v_i0, v_i1, v_k], var_decode_intermediate_local[v_k, v_i2], var_scale_intermediate_local[v_k // T.int64(40), v_i2]) + T.writes(var_matmul_intermediate_local[v_i0, v_i1, v_i2]) + var_matmul_intermediate_local[v_i0, v_i1, v_i2] = var_matmul_intermediate_local[v_i0, v_i1, v_i2] + lv6_shared[v_i0, v_i1, v_k] * var_decode_intermediate_local[v_k, v_i2] * var_scale_intermediate_local[v_k // T.int64(40), v_i2] + for ax0, ax1, ax2 in T.grid(T.int64(1), T.int64(1), T.int64(1)): + with T.block("var_matmul_intermediate_local"): + v0, v1 = T.axis.remap("SS", [ax0, ax1]) + v2 = T.axis.spatial(T.int64(4096), i0_i1_i2_0_fused * T.int64(256) + i2_2 + ax2) + T.reads(lv4[v0, v1, v2], var_matmul_intermediate_local[v0, v1, v2]) + T.writes(p_output0_intermediate[v0, v1, v2]) + p_output0_intermediate[v0, v1, v2] = lv4[v0, v1, v2] + var_matmul_intermediate_local[v0, v1, v2] +################################################ + +def get_dict_key(func): + return tvm.ir.structural_hash(func), func + + +tir_dispatch_dict = { + get_dict_key(fused_min_max_triu_te_broadcast_to): fused_min_max_triu_te_broadcast_to_sch_func(), + get_dict_key(rms_norm_before): rms_norm_after, + get_dict_key(rms_norm_fp16_before): rms_norm_fp16_after, + get_dict_key(softmax_before): softmax_after, + get_dict_key(softmax_mxn_before): softmax_mxn_after, + get_dict_key(softmax_cast_mxn_before): softmax_cast_mxn_after, + get_dict_key(softmax_fp16_before): softmax_fp16_after, + get_dict_key(softmax_mxn_fp16_before): softmax_mxn_fp16_after, + get_dict_key(softmax_1xn_before): softmax_1xn_sch_func(softmax_1xn_before), + get_dict_key(softmax_cast_1xn_before): softmax_1xn_sch_func(softmax_cast_1xn_before, cast_to_fp16=True), + get_dict_key(softmax_1xn_fp16_before): softmax_1xn_sch_func(softmax_1xn_fp16_before), + get_dict_key(matmul1_before): matmul1_after, + get_dict_key(matmul2_before): matmul2_sch_func(), + get_dict_key(matmul5_before): matmul5_after, + get_dict_key(matmul5_with_m_before): matmul5_with_m_after, + get_dict_key(NT_matmul_before): NT_matmul_after, + get_dict_key(NT_matmul4_before): NT_matmul4_sch_func(), + get_dict_key(NT_matmul9_before): NT_matmul9_sch_func(), + get_dict_key(fused_matmul1_add1): fused_matmul1_add1_sch_func(), + get_dict_key(fused_matmul3_multiply): fused_matmul3_multiply_sch_func(), + get_dict_key(fused_matmul3_silu): fused_matmul3_silu_sch_func(), + get_dict_key(fused_matmul4_add1): fused_matmul4_add1_sch_func(), + get_dict_key(fused_NT_matmul_add1_before): fused_NT_matmul_add1_after, + get_dict_key(fused_NT_matmul1_divide_add_maximum_before): fused_NT_matmul1_divide_add_maximum_after, + get_dict_key(fused_NT_matmul1_divide_add_maximum_with_m_before): fused_NT_matmul1_divide_add_maximum_with_m_after, + get_dict_key(fused_NT_matmul6_divide1_add2_maximum1_before): fused_NT_matmul6_divide1_add2_maximum1_after, + get_dict_key(fused_NT_matmul2_multiply_before): fused_NT_matmul2_multiply_after, + get_dict_key(fused_NT_matmul2_silu_before): fused_NT_matmul2_silu_after, + get_dict_key(fused_NT_matmul3_add1_before): fused_NT_matmul3_add1_after, + get_dict_key(fused_NT_matmul_divide_maximum_minimum_cast_before): fused_NT_matmul_divide_maximum_minimum_cast_sch_func(), + get_dict_key(fused_NT_matmul_divide_maximum_minimum_before): fused_NT_matmul_divide_maximum_minimum_sch_func(), + get_dict_key(fused_NT_matmul1_add3_before): fused_NT_matmul1_add3_sch_func(), + get_dict_key(fused_NT_matmul2_divide1_add2_maximum1_before): fused_NT_matmul2_divide1_add2_maximum1_sch_func(fused_NT_matmul2_divide1_add2_maximum1_before), + get_dict_key(fused_NT_matmul2_divide1_maximum1_minimum1_cast3_before): fused_NT_matmul2_divide1_maximum1_minimum1_cast3_after, + get_dict_key(fused_NT_matmul2_divide1_maximum1_minimum1_before): fused_NT_matmul2_divide1_maximum1_minimum1_after, + get_dict_key(fused_NT_matmul3_multiply1_before): fused_NT_matmul3_multiply1_sch_func(), + get_dict_key(fused_NT_matmul3_silu1_before): fused_NT_matmul3_silu1_sch_func(), + get_dict_key(fused_NT_matmul4_add3_before): fused_NT_matmul4_add3_sch_func(), + get_dict_key(matmul1_fp16_before): matmul1_fp16_sch_func(), + get_dict_key(matmul8_fp16_before): matmul8_fp16_sch_func(matmul8_fp16_before), + get_dict_key(matmul8_with_m_fp16_before): matmul8_fp16_sch_func(matmul8_with_m_fp16_before), + get_dict_key(NT_matmul1_fp16_before): NT_matmul1_fp16_sch_func(), + get_dict_key(decode6): decode_sch_func(decode6), + get_dict_key(decode7): decode_sch_func(decode7), + get_dict_key(decode8): decode_sch_func(decode8), + get_dict_key(decode4_fp16): decode_sch_func(decode4_fp16), + get_dict_key(decode5_fp16): decode_sch_func(decode5_fp16), + get_dict_key(decode6_fp16): decode_sch_func(decode6_fp16), + get_dict_key(decode_int3_fp16): decode_sch_func(decode_int3_fp16), + get_dict_key(decode1_int3_fp16): decode_sch_func(decode1_int3_fp16), + get_dict_key(decode2_int3_fp16): decode_sch_func(decode2_int3_fp16), + get_dict_key(decode_int3_int16_fp16): decode_sch_func(decode_int3_int16_fp16), + get_dict_key(decode1_int3_int16_fp16): decode_sch_func(decode1_int3_int16_fp16), + get_dict_key(decode2_int3_int16_fp16): decode_sch_func(decode2_int3_int16_fp16), + get_dict_key(fused_decode3_matmul1_before): fused_decode3_matmul1_after, + get_dict_key(fused_decode4_fused_matmul5_add3_before): fused_decode4_fused_matmul5_add3_after, + get_dict_key(fused_decode4_matmul5_before): fused_decode4_matmul5_after, + get_dict_key(fused_decode5_fused_matmul8_multiply1_before): fused_decode5_fused_matmul8_multiply1_after, + get_dict_key(fused_decode5_fused_matmul8_silu1_before): fused_decode5_fused_matmul8_silu1_after, + get_dict_key(fused_decode6_fused_matmul9_add3_before): fused_decode6_fused_matmul9_add3_after, + get_dict_key(fused_decode3_matmul1_fp16_before): fused_decode3_matmul1_fp16_after, + get_dict_key(fused_decode3_matmul1_cast_fp16_before): fused_decode3_matmul1_cast_fp16_after, + get_dict_key(fused_decode4_fused_matmul5_add3_fp16_before): fused_decode4_fused_matmul5_add3_fp16_after, + get_dict_key(fused_decode4_matmul5_fp16_before): fused_decode4_matmul5_fp16_after, + get_dict_key(fused_decode5_fused_matmul8_multiply1_fp16_before): fused_decode5_fused_matmul8_multiply1_fp16_after, + get_dict_key(fused_decode5_fused_matmul8_silu1_fp16_before): fused_decode5_fused_matmul8_silu1_fp16_after, + get_dict_key(fused_decode6_fused_matmul9_add3_fp16_before): fused_decode6_fused_matmul9_add3_fp16_after, + get_dict_key(fused_decode3_matmul1_cast_int3_fp16_before): fused_decode3_matmul1_cast_int3_fp16_after, + get_dict_key(fused_decode4_fused_matmul5_add3_int3_fp16_before): fused_decode4_fused_matmul5_add3_int3_fp16_after, + get_dict_key(fused_decode4_matmul5_int3_fp16_before): fused_decode4_matmul5_int3_fp16_after, + get_dict_key(fused_decode5_fused_matmul8_multiply1_int3_fp16_before): fused_decode5_fused_matmul8_multiply1_int3_fp16_after, + get_dict_key(fused_decode5_fused_matmul8_silu1_int3_fp16_before): fused_decode5_fused_matmul8_silu1_int3_fp16_after, + get_dict_key(fused_decode6_fused_matmul9_add3_int3_fp16_before): fused_decode6_fused_matmul9_add3_int3_fp16_after, + get_dict_key(fused_decode3_matmul1_cast_int3_int16_fp16_before): fused_decode3_matmul1_cast_int3_int16_fp16_after, + get_dict_key(fused_decode4_fused_matmul5_add3_int3_int16_fp16_before): fused_decode4_fused_matmul5_add3_int3_int16_fp16_after, + get_dict_key(fused_decode4_matmul5_int3_int16_fp16_before): fused_decode4_matmul5_int3_int16_fp16_after, + get_dict_key(fused_decode5_fused_matmul8_multiply1_int3_int16_fp16_before): fused_decode5_fused_matmul8_multiply1_int3_int16_fp16_after, + get_dict_key(fused_decode5_fused_matmul8_silu1_int3_int16_fp16_before): fused_decode5_fused_matmul8_silu1_int3_int16_fp16_after, + get_dict_key(fused_decode6_fused_matmul9_add3_int3_int16_fp16_before): fused_decode6_fused_matmul9_add3_int3_int16_fp16_after, +} +# fmt: on + + +def lookup_func(func): + for (hash_value, func_before), f_after in tir_dispatch_dict.items(): + if tvm.ir.structural_hash(func) == hash_value and tvm.ir.structural_equal( + func, func_before + ): + return f_after + return None diff --git a/mlc_llm/quantization/__init__.py b/mlc_llm/quantization/__init__.py new file mode 100644 index 0000000..6284df6 --- /dev/null +++ b/mlc_llm/quantization/__init__.py @@ -0,0 +1,232 @@ +from .quantization import FQuantize +from .quantization import QuantizationScheme +from .quantization import QuantizationSpec, NoQuantizationSpec, ParamQuantKind +from .quantization import QuantSpecUpdater +from .group_quantization import GroupQuantizationSpec +from .autogptq_quantization import AutogptqQuantizationSpec +from .ft_quantization import FTQuantizationSpec, FTQuantizeUpdater + + +# The predefined quantization schemes. +quantization_schemes = { + "autogptq_llama_q4f16_0": QuantizationScheme( + name="autogptq_llama_q4f16_0", + linear_weight=AutogptqQuantizationSpec( + dtype="float16", + mode="int4", + sym=False, + group_size=128, + ), + embedding_table=NoQuantizationSpec("float16"), + final_fc_weight=NoQuantizationSpec("float16"), + ), + "autogptq_llama_q4f16_1": QuantizationScheme( + name="autogptq_llama_q4f16_1", + linear_weight=AutogptqQuantizationSpec( + dtype="float16", + mode="int4", + sym=False, + group_size=-1, + ), + embedding_table=NoQuantizationSpec("float16"), + final_fc_weight=NoQuantizationSpec("float16"), + ), + "q0f16": QuantizationScheme("q0f16", NoQuantizationSpec("float16")), + "q0f32": QuantizationScheme("q0f32", NoQuantizationSpec("float32")), + "q3f16_0": QuantizationScheme( + name="q3f16_0", + linear_weight=GroupQuantizationSpec( + dtype="float16", + mode="int3", + sym=True, + storage_nbit=16, + group_size=40, + transpose=True, + ), + embedding_table=GroupQuantizationSpec( + dtype="float16", + mode="int3", + sym=True, + storage_nbit=16, + group_size=40, + transpose=False, + ), + final_fc_weight="same_as_linear_weight", + ), + "q3f16_1": QuantizationScheme( + name="q3f16_1", + linear_weight=GroupQuantizationSpec( + dtype="float16", + mode="int3", + sym=True, + storage_nbit=16, + group_size=40, + transpose=False, + ), + embedding_table="same_as_linear_weight", + final_fc_weight="same_as_linear_weight", + ), + "q4f16_0": QuantizationScheme( + name="q4f16_0", + linear_weight=GroupQuantizationSpec( + dtype="float16", + mode="int4", + sym=True, + storage_nbit=32, + group_size=32, + transpose=True, + ), + embedding_table=GroupQuantizationSpec( + dtype="float16", + mode="int4", + sym=True, + storage_nbit=32, + group_size=32, + transpose=False, + ), + final_fc_weight="same_as_linear_weight", + ), + "q4f16_1": QuantizationScheme( + name="q4f16_1", + linear_weight=GroupQuantizationSpec( + dtype="float16", + mode="int4", + sym=True, + storage_nbit=32, + group_size=32, + transpose=False, + ), + embedding_table="same_as_linear_weight", + final_fc_weight="same_as_linear_weight", + ), + "q4f16_2": QuantizationScheme( + name="q4f16_2", + linear_weight=GroupQuantizationSpec( + dtype="float16", + mode="int4", + sym=True, + storage_nbit=32, + group_size=32, + transpose=False, + ), + embedding_table=NoQuantizationSpec("float16"), + final_fc_weight=NoQuantizationSpec("float16"), + ), + "q4f16_ft": QuantizationScheme( + name="q4f16_ft", + linear_weight=FTQuantizationSpec( + dtype="float16", + nbit=4, + group_size=-1, + ), + embedding_table=GroupQuantizationSpec( + dtype="float16", + mode="int4", + sym=True, + storage_nbit=32, + group_size=32, + transpose=False, + ), + final_fc_weight="same_as_linear_weight", + qspec_updater_class=FTQuantizeUpdater, + ), + "q4f16_ft_group": QuantizationScheme( + name="q4f16_ft_group", + linear_weight=FTQuantizationSpec( + dtype="float16", + nbit=4, + group_size=64, + ), + embedding_table=GroupQuantizationSpec( + dtype="float16", + mode="int4", + sym=True, + storage_nbit=32, + group_size=32, + transpose=False, + ), + final_fc_weight="same_as_linear_weight", + qspec_updater_class=FTQuantizeUpdater, + ), + "q4f32_0": QuantizationScheme( + name="q4f32_0", + linear_weight=GroupQuantizationSpec( + dtype="float32", + mode="int4", + sym=False, + storage_nbit=32, + group_size=32, + transpose=True, + ), + embedding_table=GroupQuantizationSpec( + dtype="float32", + mode="int4", + sym=False, + storage_nbit=32, + group_size=32, + transpose=False, + ), + final_fc_weight="same_as_linear_weight", + ), + "q4f32_1": QuantizationScheme( + name="q4f32_1", + linear_weight=GroupQuantizationSpec( + dtype="float32", + mode="int4", + sym=False, + storage_nbit=32, + group_size=32, + transpose=False, + ), + embedding_table="same_as_linear_weight", + final_fc_weight="same_as_linear_weight", + ), + "q8f16_ft": QuantizationScheme( + name="q8f16_ft", + linear_weight=FTQuantizationSpec( + dtype="float16", + nbit=8, + ), + embedding_table=GroupQuantizationSpec( + dtype="float16", + mode="int8", + sym=True, + storage_nbit=32, + group_size=32, + transpose=False, + ), + final_fc_weight="same_as_linear_weight", + qspec_updater_class=FTQuantizeUpdater, + ), + "q8f16_ft_group": QuantizationScheme( + name="q8f16_ft_group", + linear_weight=FTQuantizationSpec( + dtype="float16", + nbit=8, + group_size=64, + ), + embedding_table=GroupQuantizationSpec( + dtype="float16", + mode="int8", + sym=True, + storage_nbit=32, + group_size=32, + transpose=False, + ), + final_fc_weight="same_as_linear_weight", + qspec_updater_class=FTQuantizeUpdater, + ), + "q8f16_1": QuantizationScheme( + name="q8f16_1", + linear_weight=GroupQuantizationSpec( + dtype="float16", + mode="int8", + sym=True, + storage_nbit=32, + group_size=32, + transpose=False, + ), + embedding_table="same_as_linear_weight", + final_fc_weight="same_as_linear_weight", + ), +} diff --git a/mlc_llm/quantization/autogptq_quantization.py b/mlc_llm/quantization/autogptq_quantization.py new file mode 100644 index 0000000..2cdc186 --- /dev/null +++ b/mlc_llm/quantization/autogptq_quantization.py @@ -0,0 +1,193 @@ +from dataclasses import dataclass +from typing import Any, List, Literal, Optional, Tuple +from tvm import relax, te, tir, topi +from . import tir_utils +from .quantization import QuantizationSpec +from .quantization import FQuantize, FTEDequantize, convert_TE_func + + +@dataclass +class AutogptqQuantizationSpec(QuantizationSpec): + """The quantization specification for group quantization algorithm.""" + + mode: Literal["int2", "int3", "int4", "int8"] + sym: bool + group_size: int + storage_nbit: int = 32 + + quantized_suffix = ["qweight", "qzeros", "scales", "g_idx"] + + def get_loaded_tensor_info( + self, pname: str, param_info: relax.TensorStructInfo + ) -> Tuple[List[str], List[relax.TensorStructInfo]]: + assert self.storage_nbit == 32, "Only support 32bit storage currently" + + quantized_pnames = self.quant_convert_pname_fwd(pname) + if len(quantized_pnames) == 1: + return quantized_pnames, [param_info] + else: + assert len(quantized_pnames) == 4 + assert param_info.ndim == 2 + nbit = int(self.mode[-1]) + tensor_info = [] + outfeatures, infeatures = param_info.shape.values + group_size = self.group_size if self.group_size != -1 else infeatures + + def get_quantized_shape_dtype(quantized_pname: str): + if quantized_pname.endswith("qweight"): + return (infeatures // self.storage_nbit * nbit, outfeatures), "uint32" + elif quantized_pname.endswith("qzeros"): + return ( + infeatures // group_size, + outfeatures // self.storage_nbit * nbit, + ), "uint32" + elif quantized_pname.endswith("scales"): + return (infeatures // group_size, outfeatures), "float16" + elif quantized_pname.endswith("g_idx"): + return (infeatures,), "uint32" + else: + raise ValueError(f"Unrecognized quantized parameter name {quantized_pname}") + + for quantized_pname in quantized_pnames: + shape, dtype = get_quantized_shape_dtype(quantized_pname) + tensor_info.append(relax.TensorStructInfo(shape, dtype)) + + return quantized_pnames, tensor_info + + def quant_convert_pname_fwd(self, torch_pname: str) -> List[str]: + # For Llama: + if "_proj.weight" in torch_pname: + return [torch_pname.replace("weight", suffix) for suffix in self.quantized_suffix] + return [torch_pname] + + def run_prequantize(self, model_path: str) -> str: + # with auto-gptq >= 0.2.0 + try: + import auto_gptq # pylint: disable=import-outside-toplevel + import transformers # pylint: disable=import-outside-toplevel + except ImportError: + raise ImportError( + "Please install auto_gptq package (version >= 0.2.0) and " + "transformers package to use AutoGPTQ quantization." + ) + import os + from transformers import AutoTokenizer + from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig + + quantized_model_path = ( + model_path + + f"-gptq-i{self.mode[-1]}" + + ("-sym" if self.sym else "") + + f"-g{self.group_size}" + ) + if os.path.isdir(quantized_model_path): + return quantized_model_path + + tokenizer = AutoTokenizer.from_pretrained(model_path, use_fast=True) + examples = [ + tokenizer( + "MLC LLM is a universal solution that allows any language models " + "to be deployed natively on a diverse set of hardware backends and " + "native applications, plus a productive framework for everyone to " + "further optimize model performance for their own use cases." + ) + ] + quantize_config = BaseQuantizeConfig( + bits=int(self.mode[-1]), # quantize bits + desc_act=False, # disable activation description + group_size=self.group_size, # disable group quantization + ) + + model = AutoGPTQForCausalLM.from_pretrained(model_path, quantize_config) + model.quantize(examples) + + # save quantized model + model.save_quantized(quantized_model_path) + tokenizer.save_pretrained(quantized_model_path) + return quantized_model_path + + def get_quantize_func(self, param_info: relax.TensorStructInfo) -> Optional[FQuantize]: + return None + + def get_dequantize_func( + self, + param_info: relax.TensorStructInfo, + qparam_info: List[relax.TensorStructInfo], + ) -> Optional[FQuantize]: + return convert_TE_func( + decoding_func( + sym=self.sym, + nbit=int(self.mode[-1]), + storage_nbit=self.storage_nbit, + dim_length=param_info.shape.values[-1], + dtype=self.dtype, + ), + func_name="decode", + ) + + def convert_param_bkwd(self, torch_pname: str, torch_param): + target_dtype = ( + self.dtype if "_proj." not in torch_pname or "scales" in torch_pname else "uint32" + ) + + # For Llama + combined_layers = ["q_proj", "k_proj", "v_proj", "gate_proj", "up_proj"] + if any([name in torch_pname for name in combined_layers]): + return None + return [(torch_pname, torch_param.astype(target_dtype))] + + def compute_relax_param(self, relax_pname: str, torch_params: List[Any]): + import numpy as np + + # For Llama + if "query_key_value_proj" in relax_pname: + assert len(torch_params) == 3 + elif "gate_up_proj" in relax_pname: + assert len(torch_params) == 2 + else: + raise ValueError("Unexpected param loading") + + if "g_idx" in relax_pname: + return torch_params[0].astype("uint32") + else: + target_dtype = self.dtype if "scales" in relax_pname else "uint32" + return np.concatenate(torch_params, axis=-1).astype(target_dtype) + + +def decoding_func( + sym: bool, + nbit: int, + storage_nbit: int, + dim_length: tir.PrimExpr, + dtype: str = "float16", +) -> FTEDequantize: + assert dtype in ["float16"], "Only support float16 currently" + assert sym == False, "Only support sym=False currently" + assert storage_nbit == 32, "Only support storage_nbit=32 currently" + + def te_decode_asym(qweight, qzeros, scales, g_idx): + n_float_per_u32 = 32 // nbit + + def f_decode_asym(i, j): + zeros = tir_utils._tir_u32_to_int_to_float( + nbit, + qzeros[g_idx[i], j // n_float_per_u32], + j % n_float_per_u32, + dtype=dtype, + ) + data_float = tir_utils._tir_u32_to_int_to_float( + nbit, + qweight[i // n_float_per_u32, j], + i % n_float_per_u32, + dtype=dtype, + ) + scale_float, bias_float = scales[g_idx[i], j], zeros + 1 + w = (data_float - bias_float) * scale_float + return w + + shape = (dim_length, qweight.shape[1]) + w = te.compute(shape=shape, fcompute=f_decode_asym, name="decode") + w = topi.transpose(w) + return w + + return te_decode_asym diff --git a/mlc_llm/quantization/ft_quantization.py b/mlc_llm/quantization/ft_quantization.py new file mode 100644 index 0000000..286ca9a --- /dev/null +++ b/mlc_llm/quantization/ft_quantization.py @@ -0,0 +1,219 @@ +from dataclasses import dataclass +from typing import List, Optional + +import tvm +from tvm.contrib.nvcc import parse_compute_version +from tvm import relax, te, tir, topi +from tvm.script import tir as T +from tvm.relax.expr_functor import visitor + +from . import tir_utils +from .quantization import QuantizationSpec, QuantSpecUpdater +from .quantization import FQuantize, convert_TE_func +from .group_quantization import GroupQuantizationSpec + + +@dataclass +class FTQuantizationSpec(QuantizationSpec): + """The quantization specification for the FasterTransformer kernel.""" + + def __init__(self, dtype, nbit, group_size=-1): + super().__init__(dtype) + self.nbit = nbit + assert group_size in [-1, 64, 128], f"Group size {group_size} is not supported." + self.group_size = group_size + + if tvm.cuda(0).exist: + major, minor = parse_compute_version(tvm.cuda(0).compute_version) + if major == 8: + self.sm = 80 + else: + self.sm = 10 * major + minor + else: + self.sm = None + + self.do_preprocess = True + + def get_quantize_func(self, param_info: relax.TensorStructInfo) -> Optional[FQuantize]: + assert self.sm is not None + + def f_quantize(bb: relax.BlockBuilder, inputs: List[relax.Expr]): + encoded_data = bb.emit_te( + encoding_func( + self.nbit, + 8, + group_size=self.group_size, + dtype=self.dtype, + ), + inputs[0], + primfunc_name_hint="encode", + ) + + packed_weight = bb.normalize(encoded_data[0]) + + if self.do_preprocess: + encoded_weight = bb.emit( + relax.call_pure_packed( + "cutlass.ft_preprocess_weight", + packed_weight, + self.sm, + self.nbit == 4, + sinfo_args=packed_weight.struct_info, + ) + ) + else: + encoded_weight = packed_weight + + return bb.emit(relax.Tuple([encoded_weight, encoded_data[1]])) + + return f_quantize + + def get_dequantize_func( + self, + param_info: relax.TensorStructInfo, + qparam_info: List[relax.TensorStructInfo], + ) -> Optional[FQuantize]: + return convert_TE_func( + decoding_func( + self.nbit, + storage_nbit=8, + group_size=self.group_size, + ), + func_name="decode", + ) + + +def encoding_func(nbit: int, storage_nbit: int, group_size: int, dtype: str = "float32"): + def te_encode_sym(weight: te.Tensor): + """Encode the weight tensor of shape [N, K] into a quantized weight tensor of shape + [K, N // float_per_int] and a scale tensor of shape [K // group_size, N] + """ + n_float_per_int = storage_nbit // nbit + max_int_value = (1 << (nbit - 1)) - 1 + + cur_group_size = weight.shape[1] if group_size == -1 else group_size + scale_min_shape = (tir.ceildiv(weight.shape[1], cur_group_size), weight.shape[0]) + k = te.reduce_axis((0, cur_group_size), name="k") + max_abs_value = te.compute( + shape=scale_min_shape, + fcompute=lambda group, i: te.max( + te.abs( + tir.if_then_else( + group * cur_group_size + k < weight.shape[1], + weight[i, group * cur_group_size + k], + tir.const(0, dtype=weight.dtype), + ) + ), + axis=k, + ), + name="max_abs_value", + ) + + def f_compute_scale(*idx): + max_value = tir.max(tir.Cast(dtype, max_abs_value(*idx)), tir.const(1e-4, dtype)) + return max_value / tir.const(max_int_value, dtype) + + scale = te.compute(shape=scale_min_shape, fcompute=f_compute_scale, name="scale") + storage_dtype = "int" + str(storage_nbit) + + def f_scale_weight(i, j): + w_scaled = tir.round(tir.Cast(dtype, weight[i, j]) / scale[j // cur_group_size, i]) + w_scaled = T.min( + T.max(w_scaled, tir.const(-max_int_value - 1, dtype)), + tir.const(max_int_value, dtype), + ).astype(storage_dtype) + if n_float_per_int == 1: + return w_scaled + return w_scaled & tir.const((1 << nbit) - 1, storage_dtype) + + n_i32 = tir.ceildiv(weight.shape[0], n_float_per_int) + + if n_float_per_int == 1: + w_gathered = te.compute( + shape=(weight.shape[1], n_i32), + fcompute=lambda j, i: f_scale_weight(i, j), + name="w_gathered", + ) + else: + k = te.reduce_axis((0, n_float_per_int), name="k") + reducer = te.comm_reducer( + fcombine=lambda x, y: tir.bitwise_or(x, y), + fidentity=lambda dtype: tir.const(0, storage_dtype), + name="bitwise_or", + ) + w_gathered = te.compute( + shape=(weight.shape[1], n_i32), + fcompute=lambda j, i: reducer( + tir.if_then_else( + i * n_float_per_int + k < weight.shape[0], + f_scale_weight(i * n_float_per_int + k, j) + << (k.astype(storage_dtype) * tir.const(nbit, storage_dtype)), + tir.const(0, storage_dtype), + ), + axis=k, + ), + name="w_gathered", + ) + + return w_gathered, topi.cast(scale, "float16") + + return te_encode_sym + + +def decoding_func(nbit: int, storage_nbit: int, group_size: int): + def te_decode_sym(data, scale): + n_float_per_int = storage_nbit // nbit + cur_group_size = data.shape[0] if group_size == -1 else group_size + + def f_decode_sym(i, j): + if n_float_per_int == 1: + data_float = tir.Cast("float16", data[i, j]) + else: + f_convert = tir_utils._tir_packed_int_to_int_to_float(storage_nbit) + data_float = f_convert( + nbit, data[i, j // n_float_per_int], j % n_float_per_int, dtype="float16" + ) + + scale_float = scale[i // cur_group_size, j] + return data_float * scale_float + + shape = (data.shape[0], data.shape[1] * n_float_per_int) + w = te.compute(shape=shape, fcompute=f_decode_sym, name="decode") + # Dummy transpose for FuseDecodeTranspose + return topi.transpose(w) + + return te_decode_sym + + +@visitor +class FTQuantizeUpdater(QuantSpecUpdater._cls): + def visit_call_(self, call: relax.Call): + if call.op != tvm.ir.Op.get("relax.matmul"): + return + rhs = self.lookup_binding(call.args[1]) + assert rhs is not None + if ( + rhs.op != tvm.ir.Op.get("relax.permute_dims") + or rhs.attrs.axes is not None + or rhs.args[0].struct_info.ndim != 2 + ): + return + + if rhs.args[0] not in self.param_map: + return + + param = self.param_map[rhs.args[0]] + + if call.struct_info.dtype == "float32" or rhs.struct_info.shape[-1] % 8 != 0: + # FT requires N to be a multiple of 8 + # FT does not support fp32 output dtype + # TODO(masahi): If `matmul(..., out_dtype="float32")` is immediately followed + # by `cast(..., "float16")`, `matmul -> cast` can be offloaded. + param.quant_spec = GroupQuantizationSpec( + param.param_info.dtype, + mode="int4", + sym=True, + storage_nbit=32, + group_size=32, + transpose=False, + ) diff --git a/mlc_llm/quantization/group_quantization.py b/mlc_llm/quantization/group_quantization.py new file mode 100644 index 0000000..7603ad2 --- /dev/null +++ b/mlc_llm/quantization/group_quantization.py @@ -0,0 +1,214 @@ +from dataclasses import dataclass +from typing import List, Literal, Optional + +import tvm +from tvm import relax, te, tir, topi +from tvm.script import tir as T +from tvm.relax.expr_functor import visitor + +from . import tir_utils +from .quantization import QuantizationSpec, QuantSpecUpdater +from .quantization import NoQuantizationSpec +from .quantization import FQuantize, FTEQuantize, FTEDequantize, convert_TE_func + + +@dataclass +class GroupQuantizationSpec(QuantizationSpec): + """The quantization specification for group quantization algorithm.""" + + mode: Literal["int3", "int4"] + sym: bool + storage_nbit: int + group_size: int + transpose: bool + + def get_quantize_func(self, param_info: relax.TensorStructInfo) -> Optional[FQuantize]: + return convert_TE_func( + encoding_func( + sym=self.sym, + group_size=self.group_size, + nbit=int(self.mode[-1]), + mode=self.mode, + storage_nbit=self.storage_nbit, + transpose=self.transpose, + dtype=self.dtype, + ), + func_name="encode", + ) + + def get_dequantize_func( + self, + param_info: relax.TensorStructInfo, + qparam_info: List[relax.TensorStructInfo], + ) -> Optional[FQuantize]: + return convert_TE_func( + decoding_func( + sym=self.sym, + group_size=self.group_size, + nbit=int(self.mode[-1]), + mode=self.mode, + storage_nbit=self.storage_nbit, + dim_length=param_info.shape.values[-1], + data_transposed=self.transpose, + transpose_output=self.transpose, + dtype=self.dtype, + ), + func_name="decode", + ) + + +# fmt: off +def encoding_func(sym: bool, group_size: int, nbit: int, mode: str, storage_nbit: int, transpose: bool=True, dtype: str = "float32") -> FTEQuantize: + def te_encode_asym(weight: te.Tensor): + assert weight.shape[1] % group_size == 0 + n_group = weight.shape[1] // group_size + n_float_per_u32 = 32 // nbit + + scale_min_shape = (weight.shape[0], n_group) + k = te.reduce_axis((0, group_size), name="k") + min_value = te.compute(shape=scale_min_shape, fcompute=lambda i, j: te.min(weight[i, j * group_size + k], axis=k), name="min_value") + max_value = te.compute(shape=scale_min_shape, fcompute=lambda i, j: te.max(weight[i, j * group_size + k], axis=k), name="max_value") + scale = te.compute(shape=scale_min_shape, fcompute=lambda i, j: (max_value[i, j] - min_value[i, j]) / tir.const((1 << nbit) - 1, dtype), name="scale") + + def f_scale_weight(i, j): + group_idx = j // group_size + w_scaled = tir.round((weight[i, j] - min_value[i, group_idx]) / scale[i, group_idx]).astype("int32") + w_scaled = T.min(T.max(w_scaled, tir.const(0, "int32")), tir.const((1 << nbit) - 1, "int32")) + w_scaled = w_scaled.astype("uint32") + return w_scaled + + k = te.reduce_axis((0, n_float_per_u32), name="k") + reducer = te.comm_reducer(fcombine=lambda x, y: tir.bitwise_or(x, y), fidentity=lambda dtype: tir.const(0, dtype), name="bitwise_or") + if dtype == "float32": + if transpose: + w_gathered = te.compute(shape=(weight.shape[1] // n_float_per_u32, weight.shape[0]), fcompute=lambda j, i: reducer(f_scale_weight(i, j * n_float_per_u32 + k) << (k * nbit).astype("uint32"), axis=k), name="w_gathered") + scale_bias = te.compute(shape=(n_group, weight.shape[0]), fcompute=lambda j, i: tir_utils._tir_f32x2_to_bf16x2_to_u32(scale[i, j], min_value[i, j], round_to_even=True), name="scale_min") + else: + w_gathered = te.compute(shape=(weight.shape[0], weight.shape[1] // n_float_per_u32), fcompute=lambda i, j: reducer(f_scale_weight(i, j * n_float_per_u32 + k) << (k * nbit).astype("uint32"), axis=k), name="w_gathered") + scale_bias = te.compute(shape=(weight.shape[0], n_group), fcompute=lambda i, j: tir_utils._tir_f32x2_to_bf16x2_to_u32(scale[i, j], min_value[i, j], round_to_even=True), name="scale_min") + return w_gathered, scale_bias + else: + if transpose: + w_gathered = te.compute(shape=(weight.shape[1] // n_float_per_u32, weight.shape[0]), fcompute=lambda j, i: reducer(f_scale_weight(i, j * n_float_per_u32 + k) << (k * nbit).astype("uint32"), axis=k), name="w_gathered") + scale = te.compute(shape=(n_group, weight.shape[0]), fcompute=lambda j, i: scale[i, j], name="scale_transpose") + min_value = te.compute(shape=(n_group, weight.shape[0]), fcompute=lambda j, i: min_value[i, j], name="min_transpose") + else: + w_gathered = te.compute(shape=(weight.shape[0], weight.shape[1] // n_float_per_u32), fcompute=lambda i, j: reducer(f_scale_weight(i, j * n_float_per_u32 + k) << (k * nbit).astype("uint32"), axis=k), name="w_gathered") + return w_gathered, scale, min_value + + def te_encode_sym(weight: te.Tensor): + n_group = tir.ceildiv(weight.shape[1], group_size) + n_float_per_int = storage_nbit // nbit + max_int_value = (1 << (nbit - 1)) - 1 + assert group_size % n_float_per_int == 0 + + scale_min_shape = (weight.shape[0], n_group) + k = te.reduce_axis((0, group_size), name="k") + max_abs_value = te.compute(shape=scale_min_shape, fcompute=lambda i, j: te.max(tir.if_then_else(j * group_size + k < weight.shape[1], te.abs(weight[i, j * group_size + k]), tir.min_value(dtype)), axis=k), name="max_abs_value") + + def f_compute_scale(i, j): + max_value = tir.max(max_abs_value[i, j], tir.const(1e-4, dtype)) + return (max_value / tir.const(max_int_value, dtype)) if mode.startswith("int") else max_value + + scale = te.compute(shape=scale_min_shape, fcompute=f_compute_scale, name="scale") + storage_dtype = ("uint" + str(storage_nbit)) if mode.startswith("int") else "uint32" + + def f_scale_weight(i, j): + group_idx = j // group_size + if mode.startswith("int"): + w_scaled = tir.round(weight[i, j] / scale[i, group_idx] + tir.const(max_int_value, dtype)) + w_scaled = T.min(T.max(w_scaled, tir.const(0, dtype)), tir.const(max_int_value * 2, dtype)).astype(storage_dtype) + return w_scaled + else: + f_convert = tir_utils._tir_f32_to_uint_to_f4 if dtype == "float32" else tir_utils._tir_f16_to_uint_to_f4 + return f_convert(weight[i, j] / scale[i, group_idx]) + + k = te.reduce_axis((0, n_float_per_int), name="k") + reducer = te.comm_reducer(fcombine=lambda x, y: tir.bitwise_or(x, y), fidentity=lambda dtype: tir.const(0, dtype), name="bitwise_or") + n_i32 = tir.ceildiv(group_size, n_float_per_int) * n_group + if transpose: + w_gathered = te.compute(shape=(n_i32, weight.shape[0]), fcompute=lambda j, i: reducer(tir.if_then_else(j * n_float_per_int + k < weight.shape[1], f_scale_weight(i, j * n_float_per_int + k) << (k.astype(storage_dtype) * tir.const(nbit, storage_dtype)), tir.const(0, storage_dtype)), axis=k), name="w_gathered") + scale = te.compute(shape=(n_group, weight.shape[0]), fcompute=lambda j, i: scale[i, j]) + else: + w_gathered = te.compute(shape=(weight.shape[0], n_i32), fcompute=lambda i, j: reducer(tir.if_then_else(j * n_float_per_int + k < weight.shape[1], f_scale_weight(i, j * n_float_per_int + k) << (k.astype(storage_dtype) * tir.const(nbit, storage_dtype)), tir.const(0, storage_dtype)), axis=k), name="w_gathered") + return w_gathered, scale + + return te_encode_sym if sym else te_encode_asym + + +def decoding_func(sym: bool, group_size: int, nbit: int, mode: str, storage_nbit: int, dim_length: tir.PrimExpr, data_transposed: bool=True, transpose_output: bool=False, dtype: str = "float32") -> FTEDequantize: + def te_decode_asym(*args): + n_float_per_u32 = 32 // nbit + data = args[0] + if dtype == "float32": + scale_bias_bf16x2 = args[1] + else: + scale, min_value = args[1], args[2] + + def f_decode_asym(i, j): + if data_transposed: + data_float = tir_utils._tir_u32_to_int_to_float(nbit, data[i // n_float_per_u32, j], i % n_float_per_u32, dtype=dtype) + if dtype == "float32": + scale_float, bias_float = tir_utils._tir_u32_to_bf16x2_to_f32x2(scale_bias_bf16x2[i // group_size, j]) + else: + scale_float, bias_float = scale[i // group_size, j], min_value[i // group_size, j] + else: + data_float = tir_utils._tir_u32_to_int_to_float(nbit, data[i, j // n_float_per_u32], j % n_float_per_u32, dtype=dtype) + if dtype == "float32": + scale_float, bias_float = tir_utils._tir_u32_to_bf16x2_to_f32x2(scale_bias_bf16x2[i, j // group_size]) + else: + scale_float, bias_float = scale[i, j // group_size], min_value[i, j // group_size] + w = data_float * scale_float + bias_float + return w + + shape = (dim_length, data.shape[1]) if data_transposed else (data.shape[0], dim_length) + w = te.compute(shape=shape, fcompute=f_decode_asym, name="decode") + if transpose_output: + w = topi.transpose(w) + return w + + def te_decode_sym(data, scale): + n_float_per_int = storage_nbit // nbit + + def f_decode_sym(i, j): + f_convert = tir_utils._tir_packed_uint_to_uint_to_float(storage_nbit) if mode.startswith("int") else (tir_utils._tir_u32_to_f4_to_f32 if dtype == "float32" else tir_utils._tir_u32_to_f4_to_f16) + if data_transposed: + data_float = f_convert(nbit, data[i // n_float_per_int, j], i % n_float_per_int, dtype=dtype) + scale_float = scale[i // group_size, j] + else: + data_float = f_convert(nbit, data[i, j // n_float_per_int], j % n_float_per_int, dtype=dtype) + scale_float = scale[i, j // group_size] + return data_float * scale_float + + shape = (dim_length, data.shape[1]) if data_transposed else (data.shape[0], dim_length) + w = te.compute(shape=shape, fcompute=f_decode_sym, name="decode") + if transpose_output: + w = topi.transpose(w) + return w + + return te_decode_sym if sym else te_decode_asym +# fmt: on + + +# A simple example demo showing how QuantSpecUpdater is used. +# NOTE: This visitor is only for demo purpose and should not be put into real use. +@visitor +class GroupQuantDemoUpdater(QuantSpecUpdater._cls): + def visit_call_(self, call: relax.Call): + if call.op != tvm.ir.Op.get("relax.matmul"): + return + rhs = self.lookup_binding(call.args[1]) + assert rhs is not None + if ( + rhs.op != tvm.ir.Op.get("relax.permute_dims") + or rhs.attrs.axes is not None + or rhs.args[0].struct_info.ndim != 2 + ): + return + + if rhs.args[0] not in self.param_map: + return + param = self.param_map[rhs.args[0]] + # Update to no quantization for matmul with float32 output dtype. + if call.struct_info.dtype == "float32": + param.quant_spec = NoQuantizationSpec(param.param_info.dtype) diff --git a/mlc_llm/quantization/quantization.py b/mlc_llm/quantization/quantization.py new file mode 100644 index 0000000..2922c93 --- /dev/null +++ b/mlc_llm/quantization/quantization.py @@ -0,0 +1,217 @@ +import enum +from dataclasses import dataclass +from typing import Any, Callable, List, Literal, Optional, Tuple, Type, Union + +import tvm +from tvm import relax, te +from tvm.relax.expr_functor import PyExprVisitor, visitor + +FQuantize = Callable[[relax.BlockBuilder, List[relax.Expr]], relax.Var] +FTEQuantize = Callable[[te.Tensor], List[te.Tensor]] +FTEDequantize = Callable[[List[te.Tensor]], te.Tensor] + + +@dataclass +class QuantizationSpec: + """The base dataclass of quantization specification. + A specification describes how a parameter is quantized and dequantized. + + A subclass of QuantizationSpec + - contains more data fields (e.g., the "group size" in group quantization) + which instruct the quantization/dequantization, + - defines the `get_quantize_func` method, which returns a function + (`Callable[[relax.BlockBuilder, List[relax.Expr]], relax.Var]`) that takes a + Relax BlockBuilder and the weight relax Var to be quantized, computes + the quantization and returns the relax Var of quantized results. + algorithm of the quantization. + - defines the `get_dequantize_func` method, which returns function + (`Callable[[relax.BlockBuilder, List[relax.Expr]], relax.Var]`) that takes + the quantized results, computes and returns the dequantization result. + - optionally overloads the `get_loaded_tensor_info` when the parameter is + pre-quantized, in which case `get_loaded_tensor_info` needs to be overloaded + so that we know how many quantized data tensors there are, and the dtype + and shape of each quantized data tensor. + """ + + dtype: str + + def get_loaded_tensor_info( + self, pname: str, param_info: relax.TensorStructInfo + ) -> Tuple[List[str], List[relax.TensorStructInfo]]: + """Returns the names and shapes and dtypes of the tensors that need to + be loaded from the disk. + + It is useful when the parameter is pre-quantized. In such cases, we need + to know how many tensors the parameter is quantized into, and together + with the dtype and shape of each tensor, so that we can load the + pre-quantized tensors in. + """ + return [pname], [param_info] + + def get_quantize_func(self, param_info: relax.TensorStructInfo) -> Optional[FQuantize]: + """Returns the function which computes quantization. + Returning `None` means the parameter does not need quantization or is + pre-quantized. + + The returned function takes a Relax BlockBuilder and a (list of) weight + relax Var to be quantized, computes the quantization and returns the + quantization result Relax Var(s). + + You can use `convert_TE_func` to convert a TE function to the function + of the desired return format. See `group_quantization.py` for examples. + """ + return NotImplementedError() + + def get_dequantize_func( + self, + param_info: relax.TensorStructInfo, + qparam_info: List[relax.TensorStructInfo], + ) -> Optional[FQuantize]: + """Returns the function which computes dequantization. + Returning `None` means the parameter does not need dequantization. + + The returned function takes a Relax BlockBuilder and a (list of) + quantized weight relax Var, computes the dequantization and returns the + result Relax Var(s). + + You can use `convert_TE_func` to convert a TE function to the function + of the desired return format. See `group_quantization.py` for examples. + """ + return NotImplementedError() + + +@dataclass +class NoQuantizationSpec(QuantizationSpec): + """The quantization specification that describes doing no quantization.""" + + def get_quantize_func(self, param_info: relax.TensorStructInfo) -> Optional[FQuantize]: + return None + + def get_dequantize_func( + self, + param_info: relax.TensorStructInfo, + qparam_info: List[relax.TensorStructInfo], + ) -> Optional[FQuantize]: + return None + + +class ParamQuantKind(enum.IntEnum): + """The parameter quantization kind class. + + We categorized all the parameters in a model into four kinds: + - the weights of the internal linear layers, which are the main targets of quantization, + - the embedding table of every token, + - the weight of the fully-connected layer at the end of the model, which is + used for computes the logits of each input token, + - other parameters (e.g., the weight of layer normalization, etc.). + """ + + linear_weight = 0 + embedding_table = 1 + final_fc_weight = 2 + others = 3 + + +class QuantizationScheme: + """The quantization scheme class describes how an entire model is quantized. + It contains the quantization specification for each parameter quantization kind. + + Besides, it has an optional field for a visitor class which has the ability to + take the constructed model (in format of IRModule) as input, go through the + model and update the QuantizationSpec for certain parameters. + """ + + name: str + linear_weight: QuantizationSpec + embedding_table: QuantizationSpec + final_fc_weight: QuantizationSpec + others: QuantizationSpec + + qspec_updater_class: Optional[Type["QuantSpecUpdater"]] + f_convert_param_bkwd: Optional[Callable[[str, Any], Optional[List[Tuple[str, Any]]]]] + f_compute_relax_param: Optional[Callable[[str, List[Any]], Any]] + f_run_prequantize: Optional[Callable[[str], str]] + + def __init__( + self, + name: str, + linear_weight: QuantizationSpec, + *, + embedding_table: Optional[Union[QuantizationSpec, Literal["same_as_linear_weight"]]] = None, + final_fc_weight: Optional[Union[QuantizationSpec, Literal["same_as_linear_weight"]]] = None, + others: Optional[QuantizationSpec] = None, + qspec_updater_class: Optional[Type["QuantSpecUpdater"]] = None, + ) -> None: + self.name = name + self.linear_weight = linear_weight + self.others = others if others is not None else NoQuantizationSpec(self.model_dtype) + + if embedding_table is None: + self.embedding_table = self.others + elif embedding_table == "same_as_linear_weight": + self.embedding_table = self.linear_weight + else: + self.embedding_table = embedding_table + + if final_fc_weight is None: + self.final_fc_weight = self.others + elif final_fc_weight == "same_as_linear_weight": + self.final_fc_weight = self.linear_weight + else: + self.final_fc_weight = final_fc_weight + + self.qspec_updater_class = qspec_updater_class + self.f_convert_param_bkwd = None + self.f_compute_relax_param = None + self.f_run_prequantize = None + + for spec in [self.linear_weight, self.embedding_table, self.final_fc_weight, self.others]: + if hasattr(spec, "convert_param_bkwd"): + self.f_convert_param_bkwd = spec.convert_param_bkwd + if hasattr(spec, "compute_relax_param"): + self.f_compute_relax_param = spec.compute_relax_param + if hasattr(spec, "run_prequantize"): + self.f_run_prequantize = spec.run_prequantize + + @property + def model_dtype(self) -> str: + """Returns the overall model dtype, which is defined as the dtype of + the linear layers. + """ + return self.linear_weight.dtype + + +def convert_TE_func(te_func: Union[FTEQuantize, FTEDequantize], func_name: str) -> FQuantize: + def func(bb: relax.BlockBuilder, inputs: List[relax.Expr]) -> relax.Var: + return bb.call_te(te_func, *inputs, primfunc_name_hint=func_name) + + return func + + +@visitor +class QuantSpecUpdater(PyExprVisitor): + def __init__(self, param_manager) -> None: + super().__init__() + self.param_manager = param_manager + self.param_map = None + self.builder = relax.BlockBuilder() + + def lookup_binding(self, var: relax.Var): + return self.builder.lookup_binding(var) + + def visit_module(self, mod: tvm.IRModule): + for gv, func in mod.functions.items(): + if not isinstance(func, relax.Function): + continue + if func.attrs is None or not "num_input" in func.attrs: + continue + + self.param_map = dict() + num_input = int(func.attrs["num_input"]) + params_in_func = self.param_manager.params_in_func[gv.name_hint] + assert len(func.params) - num_input == len(params_in_func) + for i, relax_param in enumerate(func.params[num_input:]): + self.param_map[relax_param] = params_in_func[i] + + self.builder.normalize(func) + self.visit_expr(func) diff --git a/mlc_llm/quantization/tir_utils.py b/mlc_llm/quantization/tir_utils.py new file mode 100644 index 0000000..02d4c72 --- /dev/null +++ b/mlc_llm/quantization/tir_utils.py @@ -0,0 +1,106 @@ +"""TIR computation utilities for quantization.""" + +import tvm +from tvm import tir + +# fmt: off +def _tir_f32x2_to_bf16x2_to_u32(v0: tir.PrimExpr, v1: tir.PrimExpr, round_to_even: bool=True): + mask = tir.const((1 << 16) - 1, "uint32") + res = [] + for data in [v0, v1]: + u32_val = tir.reinterpret("uint32", data) + if round_to_even: + rounding_bias = ((u32_val >> tir.const(16, "uint32")) & tir.const(1, "uint32")) + tir.const(0x7FFF, "uint32") + u32_val += rounding_bias + res.append((u32_val >> tir.const(16, "uint32")) & mask) + return res[0] | (res[1] << tir.const(16, "uint32")) + + +def _tir_u32_to_bf16x2_to_f32x2(x: tir.PrimExpr): + mask = tir.const((1 << 16) - 1, "uint32") + x0 = x & mask + x1 = (x >> 16) & mask + return (tir.reinterpret("float32", x << tir.const(16, "uint32")) for x in [x0, x1]) + + +def _tir_u32_to_int_to_float(nbit: int, val: tir.PrimExpr, pos: tir.PrimExpr, dtype: str): + assert val.dtype == "uint32" + mask = tvm.tir.const((1 << nbit) - 1, "uint32") + return tir.Cast(dtype, (val >> (pos * nbit).astype("uint32")) & mask) + + +def _tir_packed_uint_to_uint_to_float(storage_nbit: int): + storage_dtype = "uint" + str(storage_nbit) + + def f_convert(nbit: int, val: tir.PrimExpr, pos: tir.PrimExpr, dtype: str): + assert val.dtype == storage_dtype + max_int_value = (1 << (nbit - 1)) - 1 + return ((val >> (pos.astype("uint32") * tir.const(nbit, "uint32"))) & tir.const((1 << nbit) - 1, "uint32")).astype(dtype) - tir.const(max_int_value, dtype) + + return f_convert + + +def _tir_packed_int_to_int_to_float(storage_nbit: int): + storage_dtype = "int" + str(storage_nbit) + + def f_convert(nbit: int, val: tir.PrimExpr, pos: tir.PrimExpr, dtype: str): + assert val.dtype == storage_dtype + mask = tir.const((1 << nbit) - 1, "int32") + unextended = (val >> (pos.astype("int32") * tir.const(nbit, "int32"))) & mask + return tir.Cast(dtype, (unextended << tir.const(32 - nbit, "int32")) >> tir.const(32 - nbit, "int32")) + + return f_convert + + +def _tir_f32_to_uint_to_f4(val: tir.PrimExpr): + assert val.dtype == "float32" + val_u32 = tir.reinterpret("uint32", val) + # e_f32 > 120 -> e_f4 = min(e_f32 - 120 + M_h, 7) + # e_f32 == 120 -> e_f4 = 1 + # e_f32 < 120 -> e_f4 = 0 + m_h = (val_u32 >> tir.const(22, "uint32")) & tir.const(1, "uint32") + e_f32 = (val_u32 >> tir.const(23, "uint32")) & tir.const(255, "uint32") + s = (val_u32 >> tir.const(31, "uint32")) + e_f4 = tir.Select(e_f32 > tir.const(120, "uint32"), tir.Min(e_f32 - tir.const(120, "uint32") + m_h, tir.const(7, "uint32")), tir.Select(e_f32 == tir.const(120, "uint32"), tir.const(1, "uint32"), tir.const(0, "uint32"))) + return (s << tir.const(3, "uint32")) | e_f4 + + +def _tir_f16_to_uint_to_f4(val: tir.PrimExpr): + assert val.dtype == "float16" + val_u32 = tir.Cast("uint32", tir.reinterpret("uint16", val)) + m_h = (val_u32 >> tir.const(9, "uint32")) & tir.const(1, "uint32") + e_f16 = (val_u32 >> tir.const(10, "uint32")) & tir.const(31, "uint32") + s = (val_u32 >> tir.const(15, "uint32")) + e_f4 = tir.Select(e_f16 > tir.const(8, "uint32"), tir.Min(e_f16 - tir.const(8, "uint32") + m_h, tir.const(7, "uint32")), tir.Select(e_f16 == tir.const(8, "uint32"), tir.const(1, "uint32"), tir.const(0, "uint32"))) + return (s << tir.const(3, "uint32")) | e_f4 + + +def _tir_u32_to_f4_to_f32(nbit: int, val: tir.PrimExpr, pos: tir.PrimExpr, dtype: str): + assert nbit == 4 + assert dtype == "float32" + assert val.dtype == "uint32" + # e_f4 == 0 -> e_f32 = 0 + # e_f4 != 0 -> e_f32 = e_f4 + 120 = e_f4 | (1111000)_2 + mask = tvm.tir.const((1 << nbit) - 1, "uint32") + f4 = (val >> (pos.astype("uint32") * tir.const(nbit, "uint32"))) & mask + s = f4 >> tir.const(3, "uint32") + e_f4 = f4 & tir.const(7, "uint32") + e_f32 = e_f4 | tir.const(120, "uint32") + val_f32 = tir.reinterpret("float32", (e_f32 | (s << tir.const(8, "uint32"))) << tir.const(23, "uint32")) + return tir.Select(e_f4 == tir.const(0, "uint32"), tir.const(0, "float32"), val_f32) + + +def _tir_u32_to_f4_to_f16(nbit: int, val: tir.PrimExpr, pos: tir.PrimExpr, dtype: str): + assert nbit == 4 + assert dtype == "float16" + assert val.dtype == "uint32" + # e_f4 == 0 -> e_f16 = 0 + # e_f4 != 0 -> e_f16 = e_f4 + 8 = e_f4 | (1000)_2 + mask = tvm.tir.const((1 << nbit) - 1, "uint32") + f4 = (val >> (pos.astype("uint32") * tir.const(nbit, "uint32"))) & mask + s = f4 >> tir.const(3, "uint32") + e_f4 = f4 & tir.const(7, "uint32") + e_f16 = e_f4 | tir.const(8, "uint32") + val_f16 = tir.reinterpret("float16", (e_f16 | (s << tir.const(5, "uint32"))) << tir.const(10, "uint32")) + return tir.Select(e_f4 == tir.const(0, "uint32"), tir.const(0, "float16"), val_f16) +# fmt: on diff --git a/mlc_llm/relax_model/__init__.py b/mlc_llm/relax_model/__init__.py new file mode 100644 index 0000000..9ee3d0d --- /dev/null +++ b/mlc_llm/relax_model/__init__.py @@ -0,0 +1 @@ +from . import llama diff --git a/mlc_llm/relax_model/chatglm.py b/mlc_llm/relax_model/chatglm.py new file mode 100644 index 0000000..9a2afdf --- /dev/null +++ b/mlc_llm/relax_model/chatglm.py @@ -0,0 +1,797 @@ +import argparse +import math +from dataclasses import dataclass +from typing import List, Tuple + +import tvm +from tvm import relax, te, tir +from tvm.relax.op import ( + astype, + broadcast_to, + expand_dims, + matmul, + maximum, + minimum, + permute_dims, + repeat, + reshape, + split, + squeeze, +) +from tvm.relax.op.nn import silu, softmax +from tvm.relax.testing import nn +from tvm.script import relax as R + +from ..quantization import ParamQuantKind, QuantizationScheme +from .commons import create_metadata_func +from .modules import Embedding, Linear, ModuleList, RotaryEmbedding +from .param_manager import ParamManager + + +@dataclass +class ChatGLMConfig: + def __init__( + self, + add_bias_linear: bool = False, + add_qkv_bias: bool = True, + ffn_hidden_size: int = 13696, + hidden_size: int = 4096, + kv_channels: int = 128, + layernorm_epsilon: float = 1e-05, + multi_query_group_num: int = 2, + num_attention_heads: int = 32, + num_layers: int = 28, + max_sequence_length: int = 2048, + padded_vocab_size: int = 65024, + eos_token_id: int = 2, + bos_token_id: int = 0, + dtype: str = "float32", + **kwargs, + ): + self.add_bias_linear = add_bias_linear + self.add_qkv_bias = add_qkv_bias + self.ffn_hidden_size = ffn_hidden_size + self.hidden_size = hidden_size + self.kv_channels = kv_channels + self.layernorm_epsilon = layernorm_epsilon + self.multi_query_group_num = multi_query_group_num + self.num_attention_heads = num_attention_heads + self.num_layers = num_layers + self.max_sequence_length = min(2048, max_sequence_length) + self.padded_vocab_size = padded_vocab_size + self.bos_token_id = bos_token_id + self.eos_token_id = eos_token_id + self.dtype = dtype + self.kwargs = kwargs + + +def _repeat_kv(k: relax.Expr, v: relax.Expr, n_rep: int, shape: relax.Expr): + k = nn.emit(reshape(repeat(k, n_rep, 1), shape)) + v = nn.emit(reshape(repeat(v, n_rep, 1), shape)) + return k, v + + +def _reshape(x: relax.Expr, shape: Tuple[int]): + x = nn.emit(reshape(x, R.shape(shape))) + return x + + +class RMSNorm(nn.Module): + def __init__(self, hidden_size, dtype, eps=1e-5): + self.weight = nn.Parameter((hidden_size,), dtype=dtype, name="rms_norm_weight") + self.eps = tvm.tir.const(eps, dtype) + + def forward(self, hidden_states): + def f_rms_norm(x, weight): + is_float32 = x.dtype == "float32" + + def f_square(x): + return tir.Cast("float32", x) * tir.Cast("float32", x) if not is_float32 else x * x + + k = te.reduce_axis((0, x.shape[2]), name="k") + square_sum = te.compute( + (x.shape[0], x.shape[1]), + lambda bsz, i: te.sum(f_square(x[bsz, i, k]), axis=k), + name=x.op.name + "red_temp", + ) + + def f_div_cast(bsz, i, k): + x_val = x[bsz, i, k] + if not is_float32: + x_val = tir.Cast("float32", x_val) + return x_val / tir.sqrt(square_sum[bsz, i] / x.shape[2] + self.eps) + + def f_mul_cast(x, y): + value = x * y + if not is_float32: + value = tir.Cast(x.dtype, value) + return value + + return te.compute( + x.shape, + lambda bsz, i, k: f_mul_cast(weight(k), f_div_cast(bsz, i, k)), + name="rms_norm", + ) + + return nn.emit_te( + f_rms_norm, + hidden_states, + self.weight, + primfunc_name_hint="rms_norm", + ) + + +class CoreAttention(nn.Module): + def __init__(self, config: ChatGLMConfig): + projection_size = config.kv_channels * config.num_attention_heads + + # Per attention head and per partition values. + self.hidden_size_per_partition = projection_size + self.hidden_size_per_attention_head = projection_size // config.num_attention_heads + self.num_attention_heads_per_partition = config.num_attention_heads + + self.norm_factor = math.sqrt(self.hidden_size_per_attention_head) + + self.dtype = config.dtype + + def forward( + self, + q: relax.Expr, + k: relax.Expr, + v: relax.Expr, + attention_mask: relax.Expr, + ) -> relax.Expr: + bsz, sl, nh, hd = q.struct_info.shape + kv_sl = k.struct_info.shape[1] + + # [bsz, nh, sl, hd] + q = nn.emit(permute_dims(q, [0, 2, 1, 3])) + + # [bsz, nh, kv_sl, hd] + k = nn.emit(permute_dims(k, [0, 2, 1, 3])) + v = nn.emit(permute_dims(v, [0, 2, 1, 3])) + + # Calculate Q.K: [bsz, nh, sl, kv_sl] + matmul_result = nn.emit( + matmul(q, permute_dims(k, [0, 1, 3, 2])) + / relax.const(self.norm_factor, q.struct_info.dtype) + ) + attention_scores = _reshape(matmul_result, (bsz, nh, sl, kv_sl)) + + # Apply attention mask: [bsz, nh, sl, kv_sl] + attention_scores = nn.emit( + maximum( + attention_scores, + relax.const( + tvm.tir.min_value(attention_scores.struct_info.dtype).value, + attention_scores.struct_info.dtype, + ), + ) + ) + attention_scores = nn.emit(minimum(attention_scores, attention_mask)) + + # Calculate Softmax(Q.K) + if attention_scores.struct_info.dtype != "float32": + attention_scores = astype(attention_scores, "float32") + attention_probs = nn.emit(softmax(attention_scores, axis=-1)) + if attention_probs.struct_info.dtype != q.struct_info.dtype: + attention_probs = astype(attention_probs, q.struct_info.dtype) + + # Calculate Softmax(Q.K).V + context = nn.emit(matmul(attention_probs, v)) + context = nn.emit(permute_dims(context, [0, 2, 1, 3])) + context = _reshape(context, (bsz, sl, nh * hd)) + + return context + + +class SelfAttention(nn.Module): + def __init__( + self, + config: ChatGLMConfig, + rotary_pos_emb: RotaryEmbedding, + ): + self.projection_size = config.kv_channels * config.num_attention_heads + + # Per attention head and per partition values. + self.hidden_size_per_attention_head = self.projection_size // config.num_attention_heads + self.num_attention_heads_per_partition = config.num_attention_heads + + # Multi-query attention config + self.num_multi_query_groups_per_partition = config.multi_query_group_num + self.qkv_hidden_size = ( + self.projection_size + + 2 * self.hidden_size_per_attention_head * config.multi_query_group_num + ) + + self.query_key_value = Linear( + config.hidden_size, + self.qkv_hidden_size, + config.dtype, + bias=config.add_bias_linear or config.add_qkv_bias, + ) + + self.rotary_pos_emb = rotary_pos_emb + + self.core_attention = CoreAttention(config) + + self.dense = Linear( + self.projection_size, + config.hidden_size, + config.dtype, + bias=config.add_bias_linear, + ) + + self.dtype = config.dtype + + def forward( + self, + hidden_states: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_value: Tuple[relax.Expr, relax.Expr], + attention_mask: relax.Expr, + ) -> Tuple[relax.Expr, Tuple[relax.Expr, relax.Expr]]: + # hidden_states: [bsz, sl, hs] + if hidden_states.struct_info.dtype != self.dtype: + hidden_states = nn.emit(astype(hidden_states, self.dtype)) + + bsz, sl, _ = hidden_states.struct_info.shape + kv_sl = all_seq_len_shape.struct_info.values[0] + + mixed_x_layer = nn.emit( + split( + self.query_key_value(hidden_states), + indices_or_sections=[ + self.num_attention_heads_per_partition * self.hidden_size_per_attention_head, + ( + self.num_attention_heads_per_partition + + self.num_multi_query_groups_per_partition + ) + * self.hidden_size_per_attention_head, + ], + axis=-1, + ) + ) + + q_shape = ( + bsz, + sl, + self.num_attention_heads_per_partition, + self.hidden_size_per_attention_head, + ) + kv_shape = ( + bsz, + sl, + self.num_multi_query_groups_per_partition, + self.hidden_size_per_attention_head, + ) + + # queries: [bsz, sl, nh, hd] + q = _reshape(relax.TupleGetItem(mixed_x_layer, 0), q_shape) + + # keys: [bsz, sl, ng, hd] + k = _reshape(relax.TupleGetItem(mixed_x_layer, 1), kv_shape) + + # values: [bsz, sl, ng, hd] + v = _reshape(relax.TupleGetItem(mixed_x_layer, 2), kv_shape) + + # apply rotary embeddings + q, k = self.rotary_pos_emb(q, k, kv_sl - sl) + + assert k.struct_info.shape[0] == 1 and v.struct_info.shape[0] == 1 + squeezed_k, squeezed_v = nn.emit(squeeze(k, axis=0)), nn.emit(squeeze(v, axis=0)) + + k_cache, v_cache = past_key_value + f_kv_cache_append = relax.extern("vm.builtin.attention_kv_cache_append") + k_cache = nn.emit( + relax.op.call_inplace_packed( + f_kv_cache_append, + args=[k_cache, squeezed_k], + inplace_indices=[0], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + v_cache = nn.emit( + relax.op.call_inplace_packed( + f_kv_cache_append, + args=[v_cache, squeezed_v], + inplace_indices=[0], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + past_key_value = (k_cache, v_cache) + + kv_sl = all_seq_len_shape.struct_info.values[0] + bsz, _, n_groups, head_dim = k.struct_info.shape + kv_cache_shape = R.shape([kv_sl, n_groups, head_dim]) + f_kv_cache_view = relax.extern("vm.builtin.attention_kv_cache_view") + k = nn.emit( + relax.call_pure_packed( + f_kv_cache_view, + args=[k_cache, kv_cache_shape], + sinfo_args=[R.Tensor(kv_cache_shape, k.struct_info.dtype)], + ) + ) + v = nn.emit( + relax.call_pure_packed( + f_kv_cache_view, + args=[v_cache, kv_cache_shape], + sinfo_args=[R.Tensor(kv_cache_shape, v.struct_info.dtype)], + ) + ) + + n_rep = self.num_attention_heads_per_partition // self.num_multi_query_groups_per_partition + kv_attn_shape = R.shape( + [ + bsz, + kv_sl, + self.num_attention_heads_per_partition, + self.hidden_size_per_attention_head, + ] + ) + k, v = _repeat_kv(k, v, n_rep, kv_attn_shape) + + # core attention computation + context_layer = self.core_attention(q, k, v, attention_mask) + + # apply output projection + output = self.dense(context_layer) + + return output, past_key_value + + +class MLP(nn.Module): + def __init__(self, config: ChatGLMConfig): + super().__init__() + self.dtype = config.dtype + + self.dense_h_to_4h = Linear( + config.hidden_size, + config.ffn_hidden_size * 2, + config.dtype, + bias=config.add_bias_linear, + ) + + def swiglu(x: relax.Expr): + x = nn.emit(split(x, 2, axis=-1)) + return nn.emit(silu(x[0]) * x[1]) + + self.activation_func = swiglu + + self.dense_4h_to_h = Linear( + config.ffn_hidden_size, + config.hidden_size, + config.dtype, + bias=config.add_bias_linear, + ) + + def forward(self, hidden_states): + if hidden_states.struct_info.dtype != self.dtype: + hidden_states = nn.emit(astype(hidden_states, self.dtype)) + + hidden_states = self.dense_h_to_4h(hidden_states) + hidden_states = self.activation_func(hidden_states) + hidden_states = self.dense_4h_to_h(hidden_states) + + return hidden_states + + +class GLMBlock(nn.Module): + def __init__(self, config: ChatGLMConfig, rotary_pos_emb: RotaryEmbedding): + self.input_layernorm = RMSNorm( + hidden_size=config.hidden_size, + dtype=config.dtype, + eps=config.layernorm_epsilon, + ) + self.post_attention_layernorm = RMSNorm( + hidden_size=config.hidden_size, + dtype=config.dtype, + eps=config.layernorm_epsilon, + ) + + self.self_attention = SelfAttention(config, rotary_pos_emb) + self.mlp = MLP(config) + + self.dtype = config.dtype + + def forward( + self, + hidden_states: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_value: Tuple[relax.Expr], + attention_mask: relax.Expr, + ): + layernorm_output = self.input_layernorm(hidden_states) + attention_output, present_key_value = self.self_attention( + layernorm_output, all_seq_len_shape, past_key_value, attention_mask + ) + + # residual connection + layernorm_input = nn.emit(attention_output + hidden_states) + + layernorm_output = self.post_attention_layernorm(layernorm_input) + mlp_output = self.mlp(layernorm_output) + + # residual connection + output = nn.emit(mlp_output + layernorm_input) + + return output, present_key_value + + +class GLMTransformer(nn.Module): + def __init__(self, config: ChatGLMConfig, rotary_pos_emb: RotaryEmbedding): + self.num_layers = config.num_layers + + self.layers = ModuleList([GLMBlock(config, rotary_pos_emb) for _ in range(self.num_layers)]) + self.final_layernorm = RMSNorm( + hidden_size=config.hidden_size, + dtype=config.dtype, + eps=config.layernorm_epsilon, + ) + + def forward( + self, + hidden_states: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_values: relax.Expr, + attention_mask: relax.Expr, + ): + present_kv_cache = [] + for i, block in enumerate(self.layers): + past_key_value = past_key_values[i * 2], past_key_values[i * 2 + 1] + hidden_states, (present_k_cache, present_v_cache) = block( + hidden_states, + all_seq_len_shape=all_seq_len_shape, + past_key_value=past_key_value, + attention_mask=attention_mask, + ) + present_kv_cache.append(present_k_cache) + present_kv_cache.append(present_v_cache) + hidden_states = self.final_layernorm(hidden_states) + return hidden_states, present_kv_cache + + +class ChatGLMModel(nn.Module): + def __init__(self, config: ChatGLMConfig): + self.num_layers = config.num_layers + + self.embedding = Embedding( + num_embeddings=config.padded_vocab_size, + embedding_dim=config.hidden_size, + dtype=config.dtype, + ) + + self.seq_length = config.max_sequence_length + rotary_dim = config.kv_channels // 2 + + self.rotary_pos_emb = RotaryEmbedding( + hidden_size=config.hidden_size, + num_attention_heads=config.num_attention_heads, + position_embedding_base=10000, + max_sequence_length=config.max_sequence_length, + rotary_dim=rotary_dim, + swizzle_style="glm", + dtype=config.dtype, + ) + self.encoder = GLMTransformer(config, self.rotary_pos_emb) + self.output_layer = Linear( + in_features=config.hidden_size, + out_features=config.padded_vocab_size, + bias=False, + dtype=config.dtype, + ) + + self.dtype = config.dtype + + def _prepare_decoder_attention_mask(self, input_shape, kv_sl, dtype): + # create causal mask + # [bsz, sl] -> [bsz, 1, sl, kv_sl] + if isinstance(input_shape[-1], tvm.tir.SizeVar) or input_shape[-1] > 1: + bsz, sl = input_shape + + def min_max_triu_te(): + return te.compute( + (sl, sl), + lambda i, j: tvm.tir.Select( + j > i, tvm.tir.min_value(dtype), tvm.tir.max_value(dtype) + ), + name="make_diag_mask_te", + ) + + mask = nn.emit_te(min_max_triu_te) + mask = nn.emit(expand_dims(mask, 0)) + diag_mask = nn.emit(broadcast_to(mask, (bsz, 1, sl, sl))) + if kv_sl == sl: + return diag_mask + + def extend_te(x, sl, kv_sl): + return te.compute( + (bsz, 1, sl, kv_sl), + lambda b, _, i, j: te.if_then_else( + j < kv_sl - sl, + tvm.tir.max_value(dtype), + x[b, _, i, j - (kv_sl - sl)], + ), + name="concat_te", + ) + + return nn.emit_te(extend_te, diag_mask, sl, kv_sl) + else: + # Get kv_sl from input parameters + # [bsz, sl=1] -> [bsz, 1, sl=1, kv_sl] + bsz, sl = input_shape + mask = relax.op.full( + (bsz, 1, sl, kv_sl), + relax.const(tvm.tir.max_value(dtype).value, dtype), + dtype, + ) + return nn.emit(mask) + + def forward( + self, + input_ids: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_values: relax.Expr, + ): + batch_size, seq_length = input_ids.struct_info.shape + seq_length_with_past = all_seq_len_shape.struct_info.values[0] + + # Token Embeddings + inputs_embeds = self.embedding(input_ids) + + attention_mask = self._prepare_decoder_attention_mask( + (batch_size, seq_length), + seq_length_with_past, + dtype=self.dtype, + ) + + hidden_states, present_kv_cache = self.encoder( + inputs_embeds, + all_seq_len_shape=all_seq_len_shape, + past_key_values=past_key_values, + attention_mask=attention_mask, + ) + + return hidden_states, present_kv_cache + + +class ChatGLMForCausalLM(nn.Module): + def __init__(self, config: ChatGLMConfig): + self.transformer = ChatGLMModel(config) + + self.dtype = config.dtype + + def forward( + self, + input_ids: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_values: relax.Expr, + ): + hidden_states, key_value_cache = self.transformer( + input_ids=input_ids, + all_seq_len_shape=all_seq_len_shape, + past_key_values=past_key_values, + ) + + def te_slice_last(x: te.Tensor): + _, sl, hs = x.shape + return te.compute( + shape=(1, 1, hs), + fcompute=lambda i, _, k: x[i, sl - 1, k], + name="slice_last", + ) + + hidden_states = nn.emit_te( + te_slice_last, + hidden_states, + primfunc_name_hint="slice_last", + ) + if hidden_states.struct_info.dtype != self.dtype: + hidden_states = nn.emit(astype(hidden_states, self.dtype)) + + lm_logits = self.transformer.output_layer(hidden_states) + + if lm_logits.struct_info.dtype != "float32": + lm_logits = nn.emit(astype(lm_logits, "float32")) + + return lm_logits, key_value_cache + + +def get_param_quant_kind(name: str, param_info: relax.TensorStructInfo) -> ParamQuantKind: + if "embedding.weight" in name: + return ParamQuantKind.embedding_table + elif "transformer.output_layer.weight" in name: + return ParamQuantKind.final_fc_weight + elif param_info.ndim == 2 and name.endswith(".weight"): + return ParamQuantKind.linear_weight + else: + return ParamQuantKind.others + + +def create_encoding_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: ChatGLMConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "prefill" + + bsz = tvm.tir.IntImm("int64", 1) + sl = tvm.tir.SizeVar("n", "int64") + all_seq_len = tvm.tir.SizeVar("m", "int64") + with bb.function(func_name): + model = ChatGLMForCausalLM(config) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + input_ids = nn.Placeholder((bsz, sl), dtype="int32", name="input_ids") + all_seq_len_shape = relax.Var("all_seq_len", relax.ShapeStructInfo((all_seq_len,))) + past_key_values = relax.Var( + "kv_cache", + relax.TupleStructInfo([relax.ObjectStructInfo() for _ in range(config.num_layers * 2)]), + ) + + with bb.dataflow(): + logits, key_value_cache = model( + input_ids=input_ids, + all_seq_len_shape=all_seq_len_shape, + past_key_values=past_key_values, + ) + params = [ + input_ids, + all_seq_len_shape, + past_key_values, + ] + model.parameters() + + gv = bb.emit_output((logits, relax.Tuple(key_value_cache))) + bb.emit_func_output(gv, params) + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 3)) + + +def create_decoding_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: ChatGLMConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "decode" + + bsz = 1 + all_seq_len = tvm.tir.SizeVar("m", "int64") + + with bb.function(func_name): + model = ChatGLMForCausalLM(config) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + input_ids = nn.Placeholder((bsz, 1), dtype="int32", name="input_ids") + all_seq_len_shape = relax.Var("all_seq_len", relax.ShapeStructInfo((all_seq_len,))) + past_key_values = relax.Var( + "kv_cache", + relax.TupleStructInfo([relax.ObjectStructInfo() for _ in range(config.num_layers * 2)]), + ) + with bb.dataflow(): + logits, key_value_cache = model( + input_ids=input_ids, + all_seq_len_shape=all_seq_len_shape, + past_key_values=past_key_values, + ) + params = [ + input_ids, + all_seq_len_shape, + past_key_values, + ] + model.parameters() + gv = bb.emit_output((logits, relax.Tuple(key_value_cache))) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 3)) + + +def create_kv_cache_func(bb: relax.BlockBuilder, config: ChatGLMConfig) -> None: + init_shape = relax.ShapeExpr( + ( + config.max_sequence_length, + config.multi_query_group_num, + config.hidden_size // config.num_attention_heads, + ) + ) + with bb.function("create_kv_cache", []): + with bb.dataflow(): + zeros = bb.emit(relax.op.zeros(init_shape, config.dtype)) + caches = [] + f_kv_cache_create = relax.extern("vm.builtin.attention_kv_cache_create") + for _ in range(config.num_layers * 2): + caches.append( + bb.emit( + relax.call_pure_packed( + f_kv_cache_create, + args=[zeros, init_shape, relax.PrimValue(0)], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + ) + gv = bb.emit_output(caches) + bb.emit_func_output(gv) + + +def create_softmax_func(bb: relax.BlockBuilder, config: ChatGLMConfig) -> None: + with bb.function("softmax_with_temperature"): + logits = nn.Placeholder((1, 1, config.padded_vocab_size), dtype="float32", name="logits") + temperature = nn.Placeholder((), dtype="float32", name="temperature") + with bb.dataflow(): + div = bb.emit(relax.op.divide(logits, temperature)) + softmax = bb.emit(relax.op.nn.softmax(div, axis=-1)) + gv = bb.emit_output(softmax) + bb.emit_func_output(gv, [logits, temperature]) + + +def get_model(args: argparse.Namespace, hf_config): + model = args.model + dtype = args.quantization.model_dtype + + if model.startswith("chatglm2") or model.startswith("codegeex2") or model.startswith("chatglm3"): + config = ChatGLMConfig( + **hf_config, + dtype=dtype, + ) + + param_manager = ParamManager() + bb = relax.BlockBuilder() + create_encoding_func(bb, param_manager, config, args.quantization) + create_decoding_func(bb, param_manager, config, args.quantization) + create_kv_cache_func(bb, config) + create_softmax_func(bb, config) + create_metadata_func( + bb, + model_name=model, + max_window_size=config.max_sequence_length, + stop_tokens=[0], + add_prefix_space=False, + prefill_chunk_size=args.prefill_chunk_size, + ) + + mod = bb.get() + + tir_bound_map = dict() + tir_bound_map["n"] = ( + args.prefill_chunk_size if args.prefill_chunk_size > 0 else config.max_sequence_length + ) + tir_bound_map["m"] = config.max_sequence_length + for gv in mod.functions: + func = mod[gv] + if isinstance(func, relax.Function): + mod[gv] = func.with_attr("tir_var_upper_bound", tir_bound_map) + + if args.build_model_only: + return mod, param_manager, None, config + + def f_convert_pname_fwd(pname: str) -> List[str]: + if "transformer.embedding" in pname: + return [ + pname.replace("transformer.embedding", "transformer.embedding.word_embeddings") + ] + else: + return [pname] + + def f_convert_param_bkwd(torch_pname: str, torch_param): + if "transformer.embedding.word_embeddings" in torch_pname: + return [ + ( + torch_pname.replace( + "transformer.embedding.word_embeddings", + "transformer.embedding", + ), + torch_param.astype(dtype), + ) + ] + else: + return [(torch_pname, torch_param.astype(dtype))] + + param_manager.set_param_loading_func( + args.model_path, args.use_safetensors, f_convert_pname_fwd, f_convert_param_bkwd + ) + return mod, param_manager, [None] * len(param_manager.param_names), config + + raise ValueError(f"Unsupported model {model}") diff --git a/mlc_llm/relax_model/commons.py b/mlc_llm/relax_model/commons.py new file mode 100644 index 0000000..be0c477 --- /dev/null +++ b/mlc_llm/relax_model/commons.py @@ -0,0 +1,363 @@ +import json +from typing import Dict, List, Optional + +import mlc_llm +import tvm +from tvm import relax, te, tir, topi + + +def create_metadata_func( + bb: relax.BlockBuilder, + model_name: str, + max_window_size: int, + stop_tokens: List[int], + add_prefix_space: bool, + prefill_chunk_size: int = -1, + sliding_window: int = -1, +): + metadata = json.dumps( + { + "model_name": model_name, + "max_window_size": max_window_size, + "stop_tokens": stop_tokens, + "add_prefix_space": add_prefix_space, + "prefill_chunk_size": prefill_chunk_size, + "sliding_window": sliding_window, + } + ) + with bb.function("get_metadata", params=[]): + bb.emit_func_output(relax.StringImm(metadata)) + + +def _get_shard_strategies( + model_config, num_shards: int, param_shape_is_already_sharded: bool +) -> Dict[str, tvm.tir.PrimFunc]: + head_dim = model_config.hidden_size // model_config.num_attention_heads + q_heads = model_config.num_attention_heads + kv_heads = model_config.get_num_key_value_heads() + + # pylint: disable=invalid-name + def shard_qkv_weight_scale(weight: relax.TensorStructInfo): + (spatial, red), dtype = weight.shape, weight.dtype + spatial, red = int(spatial), int(red) + if param_shape_is_already_sharded: + spatial *= num_shards + a = te.placeholder((spatial, red), dtype=dtype) + w = topi.reshape(a, (spatial // head_dim, head_dim, red)) + q = te.compute((q_heads, head_dim, red), lambda i, j, k: w[i, j, k]) + k = te.compute((kv_heads, head_dim, red), lambda i, j, k: w[q_heads + i, j, k]) + v = te.compute((kv_heads, head_dim, red), lambda i, j, k: w[q_heads + kv_heads + i, j, k]) + q = topi.reshape(q, (num_shards, q_heads // num_shards, head_dim, red)) + k = topi.reshape(k, (num_shards, kv_heads // num_shards, head_dim, red)) + v = topi.reshape(v, (num_shards, kv_heads // num_shards, head_dim, red)) + w = topi.concatenate((q, k, v), axis=1) + w = topi.reshape(w, (num_shards, (q_heads + kv_heads * 2) // num_shards * head_dim, red)) + func = te.create_prim_func([a, w]) + return func + + def shard_k_weight_scale(weight: relax.TensorStructInfo): + (spatial, red), dtype = weight.shape, weight.dtype + spatial, red = int(spatial), int(red) + if param_shape_is_already_sharded: + red *= num_shards + a = te.placeholder((spatial, red), dtype=dtype) + w = topi.reshape(a, (spatial, num_shards, red // num_shards)) + w = topi.transpose(w, (1, 0, 2)) + func = te.create_prim_func([a, w]) + return func + + def shard_axis_0(weight: relax.TensorStructInfo): + (red, spatial), dtype = weight.shape, weight.dtype + red, spatial = int(red), int(spatial) + if param_shape_is_already_sharded: + red *= num_shards + a = te.placeholder((red, spatial), dtype=dtype) + w = topi.reshape(a, (num_shards, red // num_shards, spatial)) + func = te.create_prim_func([a, w]) + return func + + def shard_axis_1(weight: relax.TensorStructInfo): + (spatial, red), dtype = weight.shape, weight.dtype + spatial, red = int(spatial), int(red) + if param_shape_is_already_sharded: + red *= num_shards + a = te.placeholder((spatial, red), dtype=dtype) + w = topi.reshape(a, (spatial, num_shards, red // num_shards)) + w = topi.transpose(w, (1, 0, 2)) + func = te.create_prim_func([a, w]) + return func + + def shard_gate_up_weight_scale(weight: relax.TensorStructInfo): + (spatial, red), dtype = weight.shape, weight.dtype + spatial, red = int(spatial), int(red) + if param_shape_is_already_sharded: + spatial *= num_shards + a = te.placeholder((spatial, red), dtype=dtype) + g = te.compute((spatial // 2, red), lambda i, j: a[i, j]) + u = te.compute((spatial // 2, red), lambda i, j: a[spatial // 2 + i, j]) + g = topi.reshape(g, (num_shards, spatial // 2 // num_shards, red)) + u = topi.reshape(u, (num_shards, spatial // 2 // num_shards, red)) + w = topi.concatenate((g, u), axis=1) + w = topi.reshape(w, (num_shards, spatial // num_shards, red)) + func = te.create_prim_func([a, w]) + return func + + # pylint: enable=invalid-name + + return { + "shard_qkv": shard_qkv_weight_scale, + "shard_mlp_k": shard_k_weight_scale, + "shard_o_proj_k": shard_k_weight_scale, + "shard_gate_up": shard_gate_up_weight_scale, + "shard_axis_0": shard_axis_0, + "shard_axis_1": shard_axis_1, + } + + +def _get_shard_strategies_ft( + model_config, num_shards: int, param_shape_is_already_sharded: bool +) -> Dict[str, tvm.tir.PrimFunc]: + q_heads = model_config.num_attention_heads + kv_heads = model_config.get_num_key_value_heads() + + def shard_qkv_weight_scale(x: relax.TensorStructInfo): + (red, spatial), dtype = x.shape, x.dtype + red, spatial = int(red), int(spatial) + if param_shape_is_already_sharded: + spatial *= num_shards + head_dim = spatial // (q_heads + 2 * kv_heads) + a = te.placeholder((red, spatial), dtype=dtype) + w = topi.reshape(a, (red, spatial // head_dim, head_dim)) + q = te.compute((red, q_heads, head_dim), lambda i, j, k: w[i, j, k]) + k = te.compute((red, kv_heads, head_dim), lambda i, j, k: w[i, q_heads + j, k]) + v = te.compute((red, kv_heads, head_dim), lambda i, j, k: w[i, q_heads + kv_heads + j, k]) + q = topi.reshape(q, (red, num_shards, q_heads // num_shards, head_dim)) + k = topi.reshape(k, (red, num_shards, kv_heads // num_shards, head_dim)) + v = topi.reshape(v, (red, num_shards, kv_heads // num_shards, head_dim)) + w = topi.concatenate((q, k, v), axis=2) + w = topi.reshape(w, (red, num_shards, (q_heads + kv_heads * 2) // num_shards * head_dim)) + w = topi.transpose(w, (1, 0, 2)) + func = te.create_prim_func([a, w]) + return func + + def shard_k_weight(weight: relax.TensorStructInfo): + (red, spatial), dtype = weight.shape, weight.dtype + red, spatial = int(red), int(spatial) + if param_shape_is_already_sharded: + red *= num_shards + a = te.placeholder((red, spatial), dtype=dtype) + w = topi.reshape(a, (num_shards, red // num_shards, spatial)) + func = te.create_prim_func([a, w]) + return func + + def shard_axis_0(weight: relax.TensorStructInfo): + (red, spatial), dtype = weight.shape, weight.dtype + red, spatial = int(red), int(spatial) + if param_shape_is_already_sharded: + red *= num_shards + a = te.placeholder((red, spatial), dtype=dtype) + w = topi.reshape(a, (num_shards, red // num_shards, spatial)) + func = te.create_prim_func([a, w]) + return func + + def shard_axis_1(weight: relax.TensorStructInfo): + (spatial, red), dtype = weight.shape, weight.dtype + spatial, red = int(spatial), int(red) + if param_shape_is_already_sharded: + red *= num_shards + a = te.placeholder((spatial, red), dtype=dtype) + w = topi.reshape(a, (spatial, num_shards, red // num_shards)) + w = topi.transpose(w, (1, 0, 2)) + func = te.create_prim_func([a, w]) + return func + + def shard_gate_up_weight_scale(x: relax.TensorStructInfo): + (red, spatial), dtype = x.shape, x.dtype + red, spatial = int(red), int(spatial) + if param_shape_is_already_sharded: + spatial *= num_shards + a = te.placeholder((red, spatial), dtype=dtype) + g = te.compute((red, spatial // 2), lambda i, j: a[i, j]) + u = te.compute((red, spatial // 2), lambda i, j: a[i, spatial // 2 + j]) + g = topi.reshape(g, (red, num_shards, spatial // 2 // num_shards)) + u = topi.reshape(u, (red, num_shards, spatial // 2 // num_shards)) + w = topi.concatenate((g, u), axis=2) + w = topi.reshape(w, (red, num_shards, spatial // num_shards)) + w = topi.transpose(w, (1, 0, 2)) + func = te.create_prim_func([a, w]) + return func + + return { + "shard_qkv": shard_qkv_weight_scale, + "shard_mlp_k": shard_k_weight, + "shard_o_proj_k": shard_k_weight, + "shard_gate_up": shard_gate_up_weight_scale, + "shard_axis_0": shard_axis_0, + "shard_axis_1": shard_axis_1, + } + + +def create_shard_info_func(param_manager, args, model_config) -> tvm.IRModule: + shard_strategy_to_func = _get_shard_strategies( + model_config, + num_shards=args.num_shards, + param_shape_is_already_sharded=args.build_model_only, + ) + + shard_info_dict = {} + shard_funcs = {} + + def add_to_shard_info(param_name: str, func_name: Optional[str]): + shard_info = [] + if func_name is not None: + func = shard_funcs[func_name] + buffer = func.buffer_map[func.params[-1]] + shape = [int(i) for i in buffer.shape] + dtype = str(buffer.dtype) + shard_info.append((func_name, [shape, dtype])) + + shard_info_dict[param_name] = shard_info + + q_params = [param.struct_info for param in param_manager.get_quantized_params("prefill")] + for _, param in param_manager.params.items(): + if param.shard_strategy is None: + pass + elif param.shard_strategy in shard_strategy_to_func: + for i, weight in enumerate(param_manager.param2qrange[param]): + if args.use_presharded_weights: + sharding_func_name = None + else: + sharding_func_name = f"{param.shard_strategy}_{i}" + if sharding_func_name not in shard_funcs: + shard_funcs[sharding_func_name] = shard_strategy_to_func[ + param.shard_strategy + ](q_params[weight]) + add_to_shard_info(f"param_{weight}", sharding_func_name) + else: + raise NotImplementedError(f"Shard strategy not implemented: {param.shard_strategy}") + + bb = relax.BlockBuilder() # pylint: disable=invalid-name + + for name, func in shard_funcs.items(): + func = func.with_attr({"global_symbol": name}) + bb.add_func(func, name) + + with bb.function("get_shard_info", params=[]): + bb.emit_func_output(relax.StringImm(json.dumps(shard_info_dict))) + + return bb.get() + + +def create_shard_transformation_func(param_manager, args, model_config) -> tvm.IRModule: + use_ft_quant = args.quantization.name in [ + "q4f16_ft", + "q8f16_ft", + "q4f16_ft_group", + "q8f16_ft_group", + ] + + if use_ft_quant: + shard_strategy_to_func = _get_shard_strategies_ft( + model_config, + num_shards=args.num_shards, + param_shape_is_already_sharded=args.build_model_only, + ) + else: + shard_strategy_to_func = _get_shard_strategies( + model_config, + num_shards=args.num_shards, + param_shape_is_already_sharded=args.build_model_only, + ) + + q_params = [param.struct_info for param in param_manager.get_quantized_params("prefill")] + + # The order of the quantized parameters must be preserved. + # Therefore, we need to loop over q_params and look up information + # as needed, rather than looping over original parameters and + # looking up the quantized parameters as needed. + orig_param_lookup = {} + for param in param_manager.params_in_func["prefill"]: + qrange = param_manager.param2qrange[param] + for i_orig_part, i_qparam in enumerate(qrange): + orig_param_lookup[i_qparam] = ( + param, + i_orig_part, + len(qrange), + ) + + bb = relax.BlockBuilder() # pylint: disable=invalid-name + with bb.function("transform_params"): + rank = tir.SizeVar("rank", "int64") + # TODO(Lunderberg): Support primitive inputs to relax + # functions. Currently, using a PrimStructInfo as the + # argument results in an error thrown during + # `vm_shape_lower.cc`, due to BindParams failing to replace + # the symbolic variable "rank" when defined in a R.PrimValue. + # + # rank_arg = relax.Var("rank", relax.PrimStructInfo(value=rank)) + rank_arg = relax.Var("rank_arg", relax.ShapeStructInfo([rank])) + + args = [rank_arg] + output = [] + + for i_qparam, qparam_sinfo in enumerate(q_params): + param, i_orig_part, num_orig_parts = orig_param_lookup[i_qparam] + + if isinstance(param.quant_spec, mlc_llm.quantization.NoQuantizationSpec): + arg_name = param.name + elif num_orig_parts == 1: + arg_name = f"{param.name}.quantized" + else: + arg_name = f"{param.name}.quantized_{i_orig_part}" + + arg = relax.Var(arg_name, qparam_sinfo) + + if param.shard_strategy is None or ( + use_ft_quant + and param.shard_strategy in ["shard_mlp_k", "shard_o_proj_k"] + and qparam_sinfo.shape[0] == 1 + ): + sharded = arg + else: + strategy_func = shard_strategy_to_func[param.shard_strategy]( + qparam_sinfo + ).without_attr("global_symbol") + strategy_gvar = bb.add_func( + strategy_func, + func_name=f"{arg_name}.sharding_func", + ) + + # TODO(Lunderberg): Write the strategies as relax + # functions, so the sharded shapes can be inferred. + reordered_buffer = strategy_func.buffer_map[strategy_func.params[-1]] + reordered_sinfo = relax.TensorStructInfo( + reordered_buffer.shape, reordered_buffer.dtype + ) + reordered = relax.op.call_tir( + strategy_gvar, relax.Tuple([arg]), out_sinfo=reordered_sinfo + ) + + # TODO(Lunderberg): Allow relax.PrimValue as the index + # in a TupleGetItem. This would allow all of the + # splits to be generated at once in the merged + # function, and could be optimized to an in-place view. + # + # split = relax.op.split(reordered, indices_or_sections=num_shards, axis=0)[rank] + split = relax.op.strided_slice( + reordered, + axes=[0], + begin=[rank], + end=[rank + 1], + assume_inbound=True, + ) + + sharded = relax.op.squeeze(split, axis=0) + + args.append(arg) + output.append(sharded) + + with bb.dataflow(): + gv = bb.emit_output(output) + bb.emit_func_output(output=gv, params=args) + + return bb.get() diff --git a/mlc_llm/relax_model/gpt_bigcode.py b/mlc_llm/relax_model/gpt_bigcode.py new file mode 100644 index 0000000..a089390 --- /dev/null +++ b/mlc_llm/relax_model/gpt_bigcode.py @@ -0,0 +1,661 @@ +import argparse +import math +from dataclasses import dataclass +from typing import Optional, Tuple, Union + +import tvm +from tvm import relax, te +from tvm.relax.op import ( + astype, + broadcast_to, + expand_dims, + matmul, + maximum, + minimum, + permute_dims, + reshape, + squeeze, +) +from tvm.relax.op.nn import gelu, layer_norm, softmax +from tvm.relax.testing import nn +from tvm.script import relax as R + +from ..quantization import ParamQuantKind, QuantizationScheme +from .commons import create_metadata_func +from .modules import Embedding, Linear, ModuleList +from .param_manager import ParamManager + + +@dataclass +class GPTBigCodeConfig: + def __init__( + self, + bos_token_id: int = 0, + eos_token_id: int = 0, + initializer_range: float = 0.02, + layer_norm_epsilon: float = 1e-05, + max_sequence_length: int = 2048, + n_embd: int = 6144, + n_head: int = 48, + n_inner: int = 24576, + n_layer: int = 40, + n_positions: int = 8192, + scale_attn_weights: bool = True, + vocab_size: int = 49152, + dtype: str = "float32", + **kwargs, + ): + self.bos_token_id = bos_token_id + self.eos_token_id = eos_token_id + self.initializer_range = initializer_range + self.layer_norm_epsilon = layer_norm_epsilon + self.max_sequence_length = max_sequence_length + self.n_embd = n_embd + self.n_head = n_head + self.n_inner = n_inner + self.n_layer = n_layer + self.n_positions = n_positions + self.scale_attn_weights = scale_attn_weights + self.vocab_size = vocab_size + self.dtype = dtype + self.kwargs = kwargs + + +def _prepare_decoder_attention_mask(input_shape, src_len, dtype): + # create causal mask + # [bsz, seq_len] -> [bsz, tgt_seq_len, 1, src_seq_len] + if isinstance(input_shape[-1], tvm.tir.SizeVar) or input_shape[-1] > 1: + bsz, tgt_len = input_shape + + def min_max_triu_te(): + return te.compute( + (tgt_len, tgt_len), + lambda i, j: tvm.tir.Select( + j > i, tvm.tir.min_value(dtype), tvm.tir.max_value(dtype) + ), + name="make_diag_mask_te", + ) + + mask = nn.emit_te(min_max_triu_te) + mask = nn.emit(expand_dims(mask, 1)) + diag_mask = nn.emit(broadcast_to(mask, (bsz, tgt_len, 1, tgt_len))) + if src_len == tgt_len: + return diag_mask + + def extend_te(x, tgt_len, src_len): + return te.compute( + (bsz, tgt_len, 1, src_len), + lambda b, i, _, j: te.if_then_else( + j < src_len - tgt_len, + tvm.tir.max_value(dtype), + x[b, i, _, j - (src_len - tgt_len)], + ), + name="concat_te", + ) + + return nn.emit_te(extend_te, diag_mask, tgt_len, src_len) + else: + # Get src_len from input parameters + # [bsz, seq_len] -> [bsz, tgt_seq_len, 1, src_seq_len] + bsz, tgt_len = input_shape + mask = relax.op.full( + (bsz, tgt_len, 1, src_len), + relax.const(tvm.tir.max_value(dtype).value, dtype), + dtype, + ) + return nn.emit(mask) + + +def apply_position_embedding(t_embd, weight, offset: int = 0): + def f_position_embedding(tensor, weight, offset): + def position_compute(*idx): + b, s, e = idx + return weight[s + offset, e] + tensor[b, s, e] + + return tvm.te.compute(tensor.shape, position_compute, name="position") + + hidden_states = nn.emit_te( + f_position_embedding, + t_embd, + weight, + offset, + primfunc_name_hint="position_embedding", + ) + return hidden_states + + +class LayerNorm(nn.Module): + def __init__( + self, + hidden_size, + dtype, + eps=1e-5, + ): + super().__init__() + self.dtype = dtype + + self.eps = eps + self.weight = nn.Parameter((hidden_size,), dtype=dtype, name="weight") + self.bias = nn.Parameter((hidden_size,), dtype=dtype, name="bias") + + def forward(self, x: relax.Expr) -> relax.Var: + if x.struct_info.dtype != self.dtype: + x = nn.emit(relax.op.astype(x, self.dtype)) + x = nn.emit( + layer_norm( + x, + gamma=self.weight, + beta=self.bias, + axes=-1, + epsilon=self.eps, + ) + ) + return x + + +class GPTBigCodeAttention(nn.Module): + """Multi-query attention from 'Fast Transformer Decoding: One Write-Head is All You Need'""" + + def __init__(self, config: GPTBigCodeConfig): + if config.n_embd % config.n_head != 0: + raise ValueError( + f"hidden_size must be divisible by n_head (got `hidden_size`: {config.n_embd}" + f" and `n_head`: {config.n_head})." + ) + self.n_embd = config.n_embd + self.n_head = config.n_head + self.head_dim = config.n_embd // config.n_head + + self.c_attn = Linear(self.n_embd, self.n_embd + 2 * self.head_dim, config.dtype, bias=True) + self.c_proj = Linear(self.n_embd, self.n_embd, config.dtype, bias=True) + + self.dtype = config.dtype + + def forward( + self, + hidden_states: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_value: Optional[Tuple[relax.Expr, relax.Expr]] = None, + attention_mask: Optional[relax.Expr] = None, + ) -> Tuple[relax.Expr, Union[Tuple[None, None], Tuple[relax.Expr, relax.Expr]]]: + # hidden_states: [batch_size, seq_len, n_embd] + if hidden_states.struct_info.dtype != self.dtype: + hidden_states = nn.emit(astype(hidden_states, self.dtype)) + + batch_size, seq_len, _ = hidden_states.struct_info.shape + kv_seq_len = all_seq_len_shape.struct_info.values[0] + + def te_slice(x: te.Tensor, start: int, end: int): + batch_size, seq_len, _ = x.shape + return te.compute( + shape=(batch_size, seq_len, end - start), + fcompute=lambda i, j, k: x[i, j, start + k], + name="slice", + ) + + query_key_value = self.c_attn(hidden_states) + # queries: [batch_size, seq_len, n_embd] + q = nn.emit_te(te_slice, query_key_value, 0, self.n_embd, primfunc_name_hint="slice") + # keys: [batch_size, seq_len, head_dim] + k = nn.emit_te( + te_slice, + query_key_value, + self.n_embd, + self.n_embd + self.head_dim, + primfunc_name_hint="slice", + ) + # values: [batch_size, seq_len, head_dim] + v = nn.emit_te( + te_slice, + query_key_value, + self.n_embd + self.head_dim, + self.n_embd + 2 * self.head_dim, + primfunc_name_hint="slice", + ) + + squeezed_k = nn.emit(squeeze(k, axis=0)) + squeezed_v = nn.emit(squeeze(v, axis=0)) + + assert k.struct_info.shape[0] == 1 and v.struct_info.shape[0] == 1 + + k_cache, v_cache = past_key_value + f_kv_cache_append = relax.extern("vm.builtin.attention_kv_cache_append") + k_cache = nn.emit( + relax.op.call_inplace_packed( + f_kv_cache_append, + args=[k_cache, squeezed_k], + inplace_indices=[0], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + v_cache = nn.emit( + relax.op.call_inplace_packed( + f_kv_cache_append, + args=[v_cache, squeezed_v], + inplace_indices=[0], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + past_key_value = (k_cache, v_cache) + + batch_size, _, head_size = k.struct_info.shape + kv_cache_shape = R.shape([kv_seq_len, head_size]) + kv_states_shape = R.shape([batch_size, kv_seq_len, head_size]) + f_kv_cache_view = relax.extern("vm.builtin.attention_kv_cache_view") + k = nn.emit( + relax.call_pure_packed( + f_kv_cache_view, + args=[k_cache, kv_cache_shape], + sinfo_args=[R.Tensor(kv_cache_shape, k.struct_info.dtype)], + ) + ) + v = nn.emit( + relax.call_pure_packed( + f_kv_cache_view, + args=[v_cache, kv_cache_shape], + sinfo_args=[R.Tensor(kv_cache_shape, v.struct_info.dtype)], + ) + ) + + k = nn.emit(reshape(k, kv_states_shape)) + v = nn.emit(reshape(v, kv_states_shape)) + + q_state_shape = R.shape([batch_size, seq_len * self.n_head, self.head_dim]) + q = nn.emit(reshape(q, q_state_shape)) + + # Calculate Q.K + attn_weights = nn.emit( + matmul(q, permute_dims(k, [0, 2, 1])) + / relax.const(math.sqrt(self.head_dim), q.struct_info.dtype) + ) + + # Apply attention mask + attn_weights = nn.emit( + maximum( + attn_weights, + relax.const( + tvm.tir.min_value(attn_weights.struct_info.dtype).value, + attn_weights.struct_info.dtype, + ), + ) + ) + attn_shape = R.shape([batch_size, seq_len, self.n_head, kv_seq_len]) + attn_view = R.shape([batch_size, seq_len * self.n_head, kv_seq_len]) + attn_weights = nn.emit(reshape(attn_weights, attn_shape)) + attn_weights = nn.emit(minimum(attn_weights, attention_mask)) + attn_weights = nn.emit(reshape(attn_weights, attn_view)) + + # Calculate Softmax(Q.K) + if attn_weights.struct_info.dtype != "float32": + attn_weights = astype(attn_weights, "float32") + attn_weights = nn.emit(softmax(attn_weights, axis=-1)) + if attn_weights.struct_info.dtype != q.struct_info.dtype: + attn_weights = astype(attn_weights, q.struct_info.dtype) + + # Calculate Softmax(Q.K).V + attn_output = nn.emit(matmul(attn_weights, v)) + + # Apply output projection + attn_output = self.c_proj( + reshape( + attn_output, + (batch_size, seq_len, self.n_embd), + ) + ) + + return attn_output, past_key_value + + +class GPTBigCodeMLP(nn.Module): + def __init__(self, config: GPTBigCodeConfig): + super().__init__() + self.dtype = config.dtype + + self.c_fc = Linear(config.n_embd, config.n_inner, config.dtype, bias=True) + self.c_proj = Linear(config.n_inner, config.n_embd, config.dtype, bias=True) + + def forward(self, hidden_states): + if hidden_states.struct_info.dtype != self.dtype: + hidden_states = nn.emit(astype(hidden_states, self.dtype)) + + hidden_states = self.c_fc(hidden_states) + hidden_states = nn.emit(gelu(hidden_states)) + hidden_states = self.c_proj(hidden_states) + + return hidden_states + + +class GPTBigCodeBlock(nn.Module): + def __init__(self, config: GPTBigCodeConfig): + self.dtype = config.dtype + + self.ln_1 = LayerNorm( + hidden_size=config.n_embd, dtype=config.dtype, eps=config.layer_norm_epsilon + ) + self.ln_2 = LayerNorm( + hidden_size=config.n_embd, dtype=config.dtype, eps=config.layer_norm_epsilon + ) + + self.attn = GPTBigCodeAttention(config) + self.mlp = GPTBigCodeMLP(config) + + def forward( + self, + hidden_states, + all_seq_len_shape: relax.Expr, + past_key_value: Tuple[relax.Expr], + attention_mask: Optional[relax.Expr] = None, + ): + attn_input = self.ln_1(hidden_states) + attn_output, present_key_value = self.attn( + attn_input, all_seq_len_shape, past_key_value, attention_mask + ) + + # residual connection + attn_output = nn.emit(attn_output + hidden_states) + + mlp_input = self.ln_2(attn_output) + mlp_output = self.mlp(mlp_input) + + # residual connection + hidden_states = nn.emit(astype(mlp_output, self.dtype) + attn_output) + + return hidden_states, present_key_value + + +class GPTBigCodeModel(nn.Module): + def __init__(self, config: GPTBigCodeConfig): + self.wte = Embedding( + num_embeddings=config.vocab_size, + embedding_dim=config.n_embd, + dtype=config.dtype, + ) + self.wpe = Embedding( + num_embeddings=config.n_positions, + embedding_dim=config.n_embd, + dtype=config.dtype, + ) + + self.h = ModuleList([GPTBigCodeBlock(config) for _ in range(config.n_layer)]) + self.ln_f = LayerNorm( + hidden_size=config.n_embd, dtype=config.dtype, eps=config.layer_norm_epsilon + ) + + def forward( + self, + input_ids: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_values: relax.Expr, + ): + batch_size, seq_length = input_ids.struct_info.shape + seq_length_with_past = all_seq_len_shape.struct_info.values[0] + + # Token Embeddings + t_embd = self.wte(input_ids) + + # Position Embeddings + offset = seq_length_with_past - seq_length + hidden_states = apply_position_embedding(t_embd, self.wpe.weight, offset=offset) + + attention_mask = _prepare_decoder_attention_mask( + (batch_size, seq_length), + seq_length_with_past, + dtype=hidden_states.struct_info.dtype, + ) + + present_kv_cache = [] + for i, block in enumerate(self.h): + past_key_value = ( + (past_key_values[i * 2], past_key_values[i * 2 + 1]) + if past_key_values is not None + else None + ) + hidden_states, (present_k_cache, present_v_cache) = block( + hidden_states, + attention_mask=attention_mask, + past_key_value=past_key_value, + all_seq_len_shape=all_seq_len_shape, + ) + present_kv_cache.append(present_k_cache) + present_kv_cache.append(present_v_cache) + hidden_states = self.ln_f(hidden_states) + return hidden_states, present_kv_cache + + +class GPTBigCodeForCausalLM(nn.Module): + def __init__(self, config: GPTBigCodeConfig): + self.dtype = config.dtype + + self.transformer = GPTBigCodeModel(config) + self.lm_head = Linear( + in_features=config.n_embd, + out_features=config.vocab_size, + bias=False, + dtype=config.dtype, + ) + + def forward( + self, + input_ids: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_values: relax.Expr, + ): + hidden_states, key_value_cache = self.transformer( + input_ids=input_ids, + all_seq_len_shape=all_seq_len_shape, + past_key_values=past_key_values, + ) + + def te_slice_last(x: te.Tensor): + _, seq_len, n_embd = x.shape + return te.compute( + shape=(1, 1, n_embd), + fcompute=lambda i, _, k: x[i, seq_len - 1, k], + name="slice_last", + ) + + hidden_states = nn.emit_te( + te_slice_last, + hidden_states, + primfunc_name_hint="slice_last", + ) + if hidden_states.struct_info.dtype != self.dtype: + hidden_states = nn.emit(astype(hidden_states, self.dtype)) + + logits = self.lm_head(hidden_states) + + if logits.struct_info.dtype != "float32": + logits = nn.emit(astype(logits, "float32")) + + return logits, key_value_cache + + +def get_param_quant_kind(name: str, param_info: relax.TensorStructInfo) -> ParamQuantKind: + if "wte.weight" in name: + return ParamQuantKind.embedding_table + elif "lm_head.weight" in name: + return ParamQuantKind.final_fc_weight + elif "wpe" not in name and param_info.ndim == 2 and name.endswith(".weight"): + return ParamQuantKind.linear_weight + else: + return ParamQuantKind.others + + +def create_encoding_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: GPTBigCodeConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "prefill" + + batch_size = tvm.tir.IntImm("int64", 1) + seq_len = tvm.tir.SizeVar("n", "int64") + all_seq_len = tvm.tir.SizeVar("m", "int64") + with bb.function(func_name): + model = GPTBigCodeForCausalLM(config) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + input_ids = nn.Placeholder((batch_size, seq_len), dtype="int32", name="input_ids") + all_seq_len_shape = relax.Var("all_seq_len", relax.ShapeStructInfo((all_seq_len,))) + past_key_values = relax.Var( + "kv_cache", + relax.TupleStructInfo([relax.ObjectStructInfo() for _ in range(config.n_layer * 2)]), + ) + + with bb.dataflow(): + logits, key_value_cache = model( + input_ids=input_ids, + all_seq_len_shape=all_seq_len_shape, + past_key_values=past_key_values, + ) + params = [ + input_ids, + all_seq_len_shape, + past_key_values, + ] + model.parameters() + + gv = bb.emit_output((logits, relax.Tuple(key_value_cache))) + bb.emit_func_output(gv, params) + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 3)) + + +def create_decoding_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: GPTBigCodeConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "decode" + + bsz = tvm.tir.IntImm("int64", 1) + seq_len = tvm.tir.IntImm("int64", 1) + all_seq_len = tvm.tir.SizeVar("m", "int64") + + with bb.function(func_name): + model = GPTBigCodeForCausalLM(config) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + input_ids = nn.Placeholder((bsz, seq_len), dtype="int32", name="input_ids") + all_seq_len_shape = relax.Var("all_seq_len", relax.ShapeStructInfo((all_seq_len,))) + past_key_values = relax.Var( + "kv_cache", + relax.TupleStructInfo([relax.ObjectStructInfo() for _ in range(config.n_layer * 2)]), + ) + with bb.dataflow(): + logits, key_value_cache = model( + input_ids=input_ids, + all_seq_len_shape=all_seq_len_shape, + past_key_values=past_key_values, + ) + params = [ + input_ids, + all_seq_len_shape, + past_key_values, + ] + model.parameters() + gv = bb.emit_output((logits, relax.Tuple(key_value_cache))) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 3)) + + +def create_kv_cache_func(bb: relax.BlockBuilder, config: GPTBigCodeConfig) -> None: + init_shape = relax.ShapeExpr( + ( + config.max_sequence_length, + config.n_embd // config.n_head, + ) + ) + with bb.function("create_kv_cache", []): + with bb.dataflow(): + zeros = bb.emit(relax.op.zeros(init_shape, config.dtype)) + caches = [] + f_kv_cache_create = relax.extern("vm.builtin.attention_kv_cache_create") + for _ in range(config.n_layer * 2): + caches.append( + bb.emit( + relax.call_pure_packed( + f_kv_cache_create, + args=[zeros, init_shape, relax.PrimValue(0)], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + ) + gv = bb.emit_output(caches) + bb.emit_func_output(gv) + + +def create_softmax_func(bb: relax.BlockBuilder, config: GPTBigCodeConfig) -> None: + with bb.function("softmax_with_temperature"): + logits = nn.Placeholder((1, 1, config.vocab_size), dtype="float32", name="logits") + temperature = nn.Placeholder((), dtype="float32", name="temperature") + with bb.dataflow(): + div = bb.emit(relax.op.divide(logits, temperature)) + softmax = bb.emit(relax.op.nn.softmax(div, axis=-1)) + gv = bb.emit_output(softmax) + bb.emit_func_output(gv, [logits, temperature]) + + +def get_model(args: argparse.Namespace, hf_config): + model = args.model + dtype = args.quantization.model_dtype + max_seq_len = args.max_seq_len + + if ( + model.startswith("starcoder") + or model.startswith("WizardCoder-") + or model.startswith("gpt_bigcode") + ): + config = GPTBigCodeConfig( + **hf_config, + dtype=dtype, + ) + if max_seq_len != -1: + config.max_sequence_length = max_seq_len + elif config.max_sequence_length is None: + config.max_sequence_length = 2048 + + param_manager = ParamManager() + bb = relax.BlockBuilder() + create_encoding_func(bb, param_manager, config, args.quantization) + create_decoding_func(bb, param_manager, config, args.quantization) + create_kv_cache_func(bb, config) + create_softmax_func(bb, config) + create_metadata_func( + bb, + model_name=model, + max_window_size=config.max_sequence_length, + stop_tokens=[0], + add_prefix_space=False, + prefill_chunk_size=args.prefill_chunk_size, + ) + + mod = bb.get() + + tir_bound_map = dict() + tir_bound_map["n"] = ( + args.prefill_chunk_size if args.prefill_chunk_size > 0 else config.max_sequence_length + ) + tir_bound_map["m"] = config.max_sequence_length + for gv in mod.functions: + func = mod[gv] + if isinstance(func, relax.Function): + mod[gv] = func.with_attr("tir_var_upper_bound", tir_bound_map) + + if args.build_model_only: + return mod, param_manager, None, config + + param_manager.set_param_loading_func( + args.model_path, + args.use_safetensors, + f_convert_param_bkwd=lambda torch_pname, torch_param: [ + (torch_pname, torch_param.astype(dtype)) + ], + ) + return mod, param_manager, [None] * len(param_manager.param_names), config + + raise ValueError(f"Unsupported model {model}") diff --git a/mlc_llm/relax_model/gpt_neox.py b/mlc_llm/relax_model/gpt_neox.py new file mode 100644 index 0000000..cdf80d1 --- /dev/null +++ b/mlc_llm/relax_model/gpt_neox.py @@ -0,0 +1,733 @@ +# pylint: disable=missing-docstring,too-few-public-methods,too-many-instance-attributes,invalid-name,too-many-locals,too-many-arguments +import argparse +import math +from typing import List, Optional, Tuple, Union + +import tvm +from tvm import relax, te +from tvm.relax.op import ( + astype, + broadcast_to, + matmul, + maximum, + minimum, + permute_dims, + reshape, + squeeze, +) +from tvm.relax.op.nn import gelu, softmax +from tvm.relax.testing import nn +from tvm.script import relax as R + +from ..quantization import ParamQuantKind, QuantizationScheme +from .commons import create_metadata_func +from .modules import Embedding, LayerNorm, Linear, ModuleList, RotaryEmbedding +from .param_manager import ParamManager + + +class GPTNeoXConfig: # pylint: disable=too-many-instance-attributes + def __init__( + self, + use_parallel_residual, + hidden_size, + intermediate_size, + num_attention_heads, + num_hidden_layers, + vocab_size, + rotary_pct, + rotary_emb_base, + layer_norm_eps, + max_sequence_length, + dtype, + ffn_out_dtype, + **kwargs, + ): + self.use_parallel_residual = use_parallel_residual + self.hidden_size = hidden_size + self.intermediate_size = intermediate_size + self.num_attention_heads = num_attention_heads + self.num_hidden_layers = num_hidden_layers + self.vocab_size = vocab_size + self.rotary_pct = rotary_pct + self.rotary_emb_base = rotary_emb_base + self.layer_norm_eps = layer_norm_eps + self.max_sequence_length = max_sequence_length + self.dtype = dtype + self.ffn_out_dtype = ffn_out_dtype + self.kwargs = kwargs + + +class GPTNeoXAttention(nn.Module): + """Multi-headed attention from 'Attention Is All You Need' paper""" + + def __init__( + self, + hidden_size: int, + num_heads: int, + rotary_embedding: RotaryEmbedding, + dtype: str, + ): + if hidden_size % num_heads != 0: + raise ValueError( + f"hidden_size must be divisible by num_heads (got `hidden_size`: {hidden_size}" + f" and `num_heads`: {num_heads})." + ) + self.hidden_size = hidden_size + self.num_heads = num_heads + self.head_dim = hidden_size // num_heads + self.rotary_embedding = rotary_embedding + self.query_key_value = Linear(hidden_size, hidden_size * 3, dtype, bias=True) + self.dense = Linear(hidden_size, hidden_size, dtype, bias=True) + self.dtype = dtype + + def forward( + self, + hidden_states: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_value: Optional[Tuple[relax.Expr, relax.Expr]] = None, + attention_mask: Optional[relax.Expr] = None, + ) -> Tuple[relax.Expr, Union[Tuple[None, None], Tuple[relax.Expr, relax.Expr]]]: + # hidden_states: [batch_size, seq_len, hidden_size] + if hidden_states.struct_info.dtype != self.dtype: + hidden_states = nn.emit(astype(hidden_states, self.dtype)) + batch_size, seq_len, _ = hidden_states.struct_info.shape + kv_seq_len = all_seq_len_shape.struct_info.values[0] + + # qkv_states: [batch_size, seq_len, hidden_size * 3] + qkv_states = nn.emit( + relax.op.split( + reshape( + self.query_key_value(hidden_states), + (batch_size, seq_len, self.num_heads, 3 * self.head_dim), + ), + indices_or_sections=3, + axis=-1, + ) + ) + + # q/k/v states: [batch_size, seq_len, num_attention_heads, head_size] + q, k, v = [relax.TupleGetItem(qkv_states, idx) for idx in range(3)] + q, k = self.rotary_embedding(q, k, kv_seq_len - seq_len) + + if past_key_value is not None: + f_kv_cache_append = relax.extern("vm.builtin.attention_kv_cache_append") + f_kv_cache_view = relax.extern("vm.builtin.attention_kv_cache_view") + k_cache, v_cache = past_key_value + k_cache = nn.emit( + relax.op.call_inplace_packed( + f_kv_cache_append, + args=[k_cache, squeeze(k, axis=0)], + inplace_indices=[0], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + v_cache = nn.emit( + relax.op.call_inplace_packed( + f_kv_cache_append, + args=[v_cache, squeeze(v, axis=0)], + inplace_indices=[0], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + batch_size, _, num_heads, head_size = k.struct_info.shape + kv_cache_shape = R.shape([kv_seq_len, num_heads, head_size]) + kv_states_shape = R.shape([batch_size, kv_seq_len, num_heads, head_size]) + k = nn.emit( + relax.call_pure_packed( + f_kv_cache_view, + args=[k_cache, kv_cache_shape], + sinfo_args=[R.Tensor(kv_cache_shape, k.struct_info.dtype)], + ) + ) + v = nn.emit( + relax.call_pure_packed( + f_kv_cache_view, + args=[v_cache, kv_cache_shape], + sinfo_args=[R.Tensor(kv_cache_shape, v.struct_info.dtype)], + ) + ) + k = nn.emit(reshape(k, kv_states_shape)) + v = nn.emit(reshape(v, kv_states_shape)) + past_key_value = (k_cache, v_cache) + else: + past_key_value = (None, None) + + q = nn.emit(permute_dims(q, [0, 2, 1, 3])) + k = nn.emit(permute_dims(k, [0, 2, 1, 3])) + v = nn.emit(permute_dims(v, [0, 2, 1, 3])) + + # Calculate QK + attn_weights = nn.emit( + matmul(q, permute_dims(k, [0, 1, 3, 2])) + / relax.const( + math.sqrt(self.head_dim), + q.struct_info.dtype, + ) + ) + # Apply attention mask + attn_weights = nn.emit( + maximum( + attn_weights, + relax.const( + tvm.tir.min_value(attn_weights.struct_info.dtype).value, + attn_weights.struct_info.dtype, + ), + ) + ) + attn_weights = nn.emit(minimum(attn_weights, attention_mask)) + # Calculate Softmax(QK) + if attn_weights.struct_info.dtype != "float32": + attn_weights = astype(attn_weights, "float32") + attn_weights = nn.emit(softmax(attn_weights, axis=-1)) + if attn_weights.struct_info.dtype != q.struct_info.dtype: + attn_weights = astype(attn_weights, q.struct_info.dtype) + # Calculate Softmax(QK)V + attn_output = nn.emit(matmul(attn_weights, v)) + # Apply output projection + attn_output = self.dense( + reshape( + permute_dims(attn_output, [0, 2, 1, 3]), + (batch_size, seq_len, self.hidden_size), + ) + ) + return attn_output, past_key_value + + +class GPTNeoXMLP(nn.Module): + def __init__( + self, + hidden_size: int, + intermediate_size: int, + dtype: str, + out_dtype: Optional[str], + ): + super().__init__() + if out_dtype is None: + out_dtype = dtype + self.dense_h_to_4h = Linear( + hidden_size, + intermediate_size, + dtype=dtype, + out_dtype=out_dtype, + ) + self.dense_4h_to_h = Linear( + intermediate_size, + hidden_size, + dtype=dtype, + out_dtype=out_dtype, + ) + self.dtype = dtype + + def forward(self, hidden_states): + if hidden_states.struct_info.dtype != self.dtype: + hidden_states = nn.emit(astype(hidden_states, self.dtype)) + hidden_states = self.dense_h_to_4h(hidden_states) + hidden_states = nn.emit(gelu(hidden_states)) + if hidden_states.struct_info.dtype != self.dtype: + hidden_states = nn.emit(astype(hidden_states, self.dtype)) + hidden_states = self.dense_4h_to_h(hidden_states) + if hidden_states.struct_info.dtype != self.dtype: + hidden_states = nn.emit(astype(hidden_states, self.dtype)) + return hidden_states + + +class GPTNeoXLayer(nn.Module): + def __init__( + self, + hidden_size: int, + intermediate_size: int, + layer_norm_eps: float, + num_heads: int, + use_parallel_residual: bool, + rotary_embedding: RotaryEmbedding, + dtype: str, + ffn_out_dtype: Optional[str], + ): + self.input_layernorm = LayerNorm( + hidden_size, + eps=layer_norm_eps, + dtype=dtype, + ) + self.post_attention_layernorm = LayerNorm( + hidden_size, + eps=layer_norm_eps, + dtype=dtype, + ) + self.attention = GPTNeoXAttention( + hidden_size, + num_heads=num_heads, + rotary_embedding=rotary_embedding, + dtype=dtype, + ) + self.mlp = GPTNeoXMLP( + hidden_size, + intermediate_size=intermediate_size, + dtype=dtype, + out_dtype=ffn_out_dtype, + ) + self.use_parallel_residual = use_parallel_residual + self.dtype = dtype + + def forward( + self, + hidden_states, + all_seq_len_shape: relax.Expr, + past_key_value: Optional[Tuple[relax.Expr]] = None, + attention_mask: Optional[relax.Expr] = None, + ): + attn_input = self.input_layernorm(hidden_states) + attn_output, present_key_value = self.attention( + attn_input, + all_seq_len_shape, + past_key_value, + attention_mask, + ) + if self.use_parallel_residual: + mlp_input = self.post_attention_layernorm(hidden_states) + mlp_output = self.mlp(mlp_input) + hidden_states = nn.emit(mlp_output + attn_output + hidden_states) + else: + attn_output = nn.emit(attn_output + hidden_states) + mlp_input = self.post_attention_layernorm(attn_output) + mlp_output = self.mlp(mlp_input) + hidden_states = nn.emit(astype(mlp_output, self.dtype) + attn_output) + return hidden_states, present_key_value + + +def _prepare_decoder_attention_mask(input_shape, src_len, dtype): + # create causal mask + # [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len] + if isinstance(input_shape[-1], tvm.tir.SizeVar) or input_shape[-1] > 1: + bsz, tgt_len = input_shape + + def min_max_triu_te(): + return te.compute( + (tgt_len, tgt_len), + lambda i, j: tvm.tir.Select( + j > i, tvm.tir.min_value(dtype), tvm.tir.max_value(dtype) + ), + name="make_diag_mask_te", + ) + + mask = nn.emit_te(min_max_triu_te) + diag_mask = nn.emit(broadcast_to(mask, (bsz, 1, tgt_len, tgt_len))) + if src_len == tgt_len: + return diag_mask + + def extend_te(x, tgt_len, src_len): + return te.compute( + (bsz, 1, tgt_len, src_len), + lambda b, _, i, j: te.if_then_else( + j < src_len - tgt_len, + tvm.tir.max_value(dtype), + x[b, _, i, j - (src_len - tgt_len)], + ), + name="concat_te", + ) + + return nn.emit_te(extend_te, diag_mask, tgt_len, src_len) + else: + # Get src_len from input parameters + # [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len] + bsz, tgt_len = input_shape + mask = relax.op.full( + (bsz, 1, tgt_len, src_len), + relax.const(tvm.tir.max_value(dtype).value, dtype), + dtype, + ) + return nn.emit(mask) + + +class GPTNeoXEmbedTokens(nn.Module): + def __init__(self, config: GPTNeoXConfig): + self.embed_in = Embedding( + num_embeddings=config.vocab_size, + embedding_dim=config.hidden_size, + dtype=config.dtype, + ) + + def forward(self, input_ids: relax.Expr): + return self.embed_in(input_ids) + + +class GPTNeoXEmbedTokensWrapper(nn.Module): + def __init__(self, config: GPTNeoXConfig): + # build a wrapper to ensure that the naming of the embed_in parameter is consistent + self.gpt_neox = GPTNeoXEmbedTokens(config) + + def forward(self, input_ids: relax.Expr): + return self.gpt_neox(input_ids) + + +class GPTNeoXModel(nn.Module): + def __init__( + self, + config: GPTNeoXConfig, + sep_embed: bool = False, + ): + rotary_embedding = RotaryEmbedding( + hidden_size=config.hidden_size, + num_attention_heads=config.num_attention_heads, + position_embedding_base=config.rotary_emb_base, + max_sequence_length=config.max_sequence_length, + rotary_pct=config.rotary_pct, + dtype=config.dtype, + ) + + self.embed_in = None + if not sep_embed: + self.embed_in = Embedding( + num_embeddings=config.vocab_size, + embedding_dim=config.hidden_size, + dtype=config.dtype, + ) + + self.layers = ModuleList( + [ + GPTNeoXLayer( + hidden_size=config.hidden_size, + intermediate_size=config.intermediate_size, + layer_norm_eps=config.layer_norm_eps, + num_heads=config.num_attention_heads, + rotary_embedding=rotary_embedding, + use_parallel_residual=config.use_parallel_residual, + dtype=config.dtype, + ffn_out_dtype=config.ffn_out_dtype, + ) + for _ in range(config.num_hidden_layers) + ] + ) + self.final_layer_norm = LayerNorm( + hidden_size=config.hidden_size, + eps=config.layer_norm_eps, + dtype=config.dtype, + ) + + def forward( + self, + inputs: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_values: Optional[Tuple[relax.Expr, relax.Expr]], + ): + # embed positions + hidden_states = self.embed_in(inputs) if self.embed_in else inputs + + batch_size, seq_length, _ = hidden_states.struct_info.shape + seq_length_with_past = all_seq_len_shape.struct_info.values[0] + attention_mask = _prepare_decoder_attention_mask( + (batch_size, seq_length), + seq_length_with_past, + dtype=hidden_states.struct_info.dtype, + ) + present_kv_cache = [] + for i, layer in enumerate(self.layers): + past_key_value = ( + (past_key_values[i * 2], past_key_values[i * 2 + 1]) + if past_key_values is not None + else None + ) + hidden_states, (present_k_cache, present_v_cache) = layer( + hidden_states, + attention_mask=attention_mask, + past_key_value=past_key_value, + all_seq_len_shape=all_seq_len_shape, + ) + present_kv_cache.append(present_k_cache) + present_kv_cache.append(present_v_cache) + hidden_states = self.final_layer_norm(hidden_states) + return hidden_states, present_kv_cache + + +class GPTNeoXForCausalLM(nn.Module): + def __init__( + self, + config: GPTNeoXConfig, + sep_embed: bool = False, + ): + self.gpt_neox = GPTNeoXModel(config, sep_embed) + self.embed_out = Linear( + in_features=config.hidden_size, + out_features=config.vocab_size, + bias=False, + dtype="float32", + ) + + def forward( + self, + inputs: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_values: Optional[List[relax.Expr]], + ): + hidden_states, key_value_cache = self.gpt_neox( + inputs=inputs, + all_seq_len_shape=all_seq_len_shape, + past_key_values=past_key_values, + ) + + def _slice(x: te.Tensor): + _, seq_len, hidden_dim = x.shape + return te.compute( + shape=(1, 1, hidden_dim), + fcompute=lambda i, _, k: x[i, seq_len - 1, k], + name="slice", + ) + + hidden_states = nn.emit_te( + _slice, + hidden_states, + primfunc_name_hint="slice", + ) + hidden_states = astype(hidden_states, "float32") + logits = self.embed_out(hidden_states) + return logits, key_value_cache + + +def get_param_quant_kind(name: str, param_info: relax.TensorStructInfo) -> ParamQuantKind: + if "embed_in.weight" in name: + return ParamQuantKind.embedding_table + elif "embed_out.weight" in name: + return ParamQuantKind.final_fc_weight + elif param_info.ndim == 2 and name.endswith(".weight"): + return ParamQuantKind.linear_weight + else: + return ParamQuantKind.others + + +def create_embed_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: GPTNeoXConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "embed" + + bsz = 1 + seq_len = tvm.tir.SizeVar("m", "int64") + with bb.function(func_name): + model = GPTNeoXEmbedTokensWrapper(config) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + input_ids = nn.Placeholder((bsz, seq_len), dtype="int32", name="input_ids") + with bb.dataflow(): + inputs_embeds = model(input_ids) + params = [input_ids] + model.parameters() + gv = bb.emit_output(inputs_embeds) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var("embed") + bb.update_func(gv, mod[gv].with_attr("num_input", 1)) + + +def create_encoding_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: GPTNeoXConfig, + quant_scheme: QuantizationScheme, + sep_embed: bool = False, +) -> None: + func_name = "prefill_with_embed" if sep_embed else "prefill" + + batch_size = tvm.tir.IntImm("int64", 1) + seq_len = tvm.tir.SizeVar("n", "int64") + all_seq_len = tvm.tir.SizeVar("m", "int64") + hidden_size = config.hidden_size + with bb.function(func_name): + model = GPTNeoXForCausalLM(config, sep_embed) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + inputs = ( + nn.Placeholder( + (batch_size, seq_len, hidden_size), + dtype=config.dtype, + name="input_embeds", + ) + if sep_embed + else nn.Placeholder((batch_size, seq_len), dtype="int32", name="input_ids") + ) + all_seq_len_shape = relax.Var("all_seq_len", relax.ShapeStructInfo((all_seq_len,))) + past_key_values = relax.Var( + "kv_cache", + relax.TupleStructInfo( + [relax.ObjectStructInfo() for _ in range(config.num_hidden_layers * 2)] + ), + ) + with bb.dataflow(): + logits, key_value_cache = model( + inputs=inputs, + all_seq_len_shape=all_seq_len_shape, + past_key_values=past_key_values, + ) + params = [ + inputs, + all_seq_len_shape, + past_key_values, + ] + model.parameters() + gv = bb.emit_output((logits, relax.Tuple(key_value_cache))) + bb.emit_func_output(gv, params) + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 3)) + + +def create_decoding_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: GPTNeoXConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "decode" + + batch_size = tvm.tir.IntImm("int64", 1) + seq_len = tvm.tir.IntImm("int64", 1) + all_seq_len = tvm.tir.SizeVar("m", "int64") + with bb.function(func_name): + model = GPTNeoXForCausalLM(config) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + input_ids = nn.Placeholder((batch_size, seq_len), dtype="int32", name="input_ids") + all_seq_len_shape = relax.Var( + "all_seq_len", + relax.ShapeStructInfo((all_seq_len,)), + ) + past_key_values = relax.Var( + "kv_cache", + relax.TupleStructInfo( + [relax.ObjectStructInfo() for _ in range(config.num_hidden_layers * 2)] + ), + ) + with bb.dataflow(): + logits, key_value_cache = model( + inputs=input_ids, + all_seq_len_shape=all_seq_len_shape, + past_key_values=past_key_values, + ) + params = [ + input_ids, + all_seq_len_shape, + past_key_values, + ] + model.parameters() + gv = bb.emit_output((logits, relax.Tuple(key_value_cache))) + bb.emit_func_output(gv, params) + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 3)) + + +def create_kv_cache_func( + bb: relax.BlockBuilder, + config: GPTNeoXConfig, +) -> None: + init_shape = relax.ShapeExpr( + ( + config.max_sequence_length, + config.num_attention_heads, + config.hidden_size // config.num_attention_heads, + ) + ) + with bb.function("create_kv_cache", []): + with bb.dataflow(): + zeros = bb.emit(relax.op.zeros(init_shape, config.dtype)) + caches = [] + f_kv_cache_create = relax.extern("vm.builtin.attention_kv_cache_create") + for _ in range(config.num_hidden_layers * 2): + caches.append( + bb.emit( + relax.call_pure_packed( + f_kv_cache_create, + args=[zeros, init_shape, relax.PrimValue(0)], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + ) + gv = bb.emit_output(caches) + bb.emit_func_output(gv) + + +def create_softmax_func(bb: relax.BlockBuilder, config: GPTNeoXConfig) -> None: + with bb.function("softmax_with_temperature"): + logits = nn.Placeholder((1, 1, config.vocab_size), dtype="float32", name="logits") + temperature = nn.Placeholder((), dtype="float32", name="temperature") + with bb.dataflow(): + div = bb.emit(relax.op.divide(logits, temperature)) + softmax = bb.emit(relax.op.nn.softmax(div, axis=-1)) + gv = bb.emit_output(softmax) + bb.emit_func_output(gv, [logits, temperature]) + + +def get_model( + args: argparse.Namespace, + hf_config, +): + model = args.model + dtype = args.quantization.model_dtype + ffn_out_dtype = "float32" + sep_embed = args.sep_embed + + if model.startswith("dolly-"): + stop_tokens = [2] + ffn_out_dtype = "float16" + elif model.startswith("stablelm-"): + stop_tokens = [50278, 50279, 50277, 1, 0] + ffn_out_dtype = "float16" + elif model.lower().startswith("stablecode-"): + stop_tokens = [0] + elif model.lower().startswith("redpajama-"): + stop_tokens = [0] + else: + raise ValueError(f"Unsupported model {model}") + + config = GPTNeoXConfig( + **hf_config, + max_sequence_length=args.max_seq_len if args.max_seq_len != -1 else 2048, + dtype=dtype, + ffn_out_dtype=ffn_out_dtype, + ) + + param_manager = ParamManager() + bb = relax.BlockBuilder() + if sep_embed: + create_embed_func(bb, param_manager, config, args.quantization) + create_encoding_func(bb, param_manager, config, args.quantization, sep_embed) + create_decoding_func(bb, param_manager, config, args.quantization) + create_kv_cache_func(bb, config) + create_softmax_func(bb, config) + create_metadata_func( + bb, + model_name=model, + max_window_size=config.max_sequence_length, + stop_tokens=stop_tokens, + add_prefix_space=False, + prefill_chunk_size=args.prefill_chunk_size, + ) + mod = bb.get() + + tir_bound_map = dict() + tir_bound_map["n"] = ( + args.prefill_chunk_size if args.prefill_chunk_size > 0 else config.max_sequence_length + ) + tir_bound_map["m"] = config.max_sequence_length + for gv in mod.functions: + func = mod[gv] + if isinstance(func, relax.Function): + mod[gv] = func.with_attr("tir_var_upper_bound", tir_bound_map) + + if args.build_model_only: + return mod, param_manager, None, config + + def f_convert_pname_fwd(pname: str) -> List[str]: + return [pname] + + def f_convert_param_bkwd(torch_pname: str, torch_param): + # torch_param: numpy.ndarray + if "layernorm" in torch_pname or "layer_norm" in torch_pname or "embed_out" in torch_pname: + return [(torch_pname, torch_param.astype("float32"))] + elif ".dense_h_to_4h.bias" in torch_pname or ".dense_4h_to_h.bias" in torch_pname: + return [(torch_pname, torch_param.astype(ffn_out_dtype))] + else: + return [(torch_pname, torch_param.astype(dtype))] + + param_manager.set_param_loading_func( + args.model_path, args.use_safetensors, f_convert_pname_fwd, f_convert_param_bkwd + ) + return mod, param_manager, [None] * len(param_manager.param_names), config diff --git a/mlc_llm/relax_model/gptj.py b/mlc_llm/relax_model/gptj.py new file mode 100644 index 0000000..9096583 --- /dev/null +++ b/mlc_llm/relax_model/gptj.py @@ -0,0 +1,688 @@ +import math +from dataclasses import dataclass +from typing import Any, List, Optional, Tuple, Union + +import tvm +from tvm import relax, te +from tvm.relax.op import ( + astype, + broadcast_to, + full, + matmul, + maximum, + minimum, + permute_dims, + reshape, + squeeze, + triu, +) +from tvm.relax.op.nn import gelu, softmax +from tvm.relax.testing import nn +from tvm.script import relax as R + +from ..quantization import ParamQuantKind, QuantizationScheme +from .commons import create_metadata_func +from .gpt_neox import create_kv_cache_func +from .modules import Embedding, LayerNorm, Linear, ModuleList, RotaryEmbedding +from .param_manager import ParamManager + + +def _min_value(dtype) -> relax.Expr: + v = tvm.tir.min_value(dtype).value + if dtype == "float16": + v = -55504.0 + return relax.const(v, dtype) + + +def _max_value(dtype) -> relax.Expr: + v = tvm.tir.max_value(dtype).value + if dtype == "float16": + v = 55504.0 + return relax.const(v, dtype) + + +@dataclass +class GPTJConfig: # pylint: disable=too-many-instance-attributes + def __init__( + self, + vocab_size, + n_embd, + n_inner, + n_head, + n_layer, + bos_token_id, + eos_token_id, + rotary_dim, + tie_word_embeddings, + dtype="float32", + layer_norm_eps=1e-5, + max_sequence_length=2048, + rotary_emb_base=10000, + **kwargs, + ): + self.vocab_size = vocab_size + self.hidden_size = n_embd + self.intermediate_size = n_inner if n_inner is not None else 4 * n_embd + self.num_attention_heads = n_head + self.num_hidden_layers = n_layer + self.bos_token_id = bos_token_id + self.eos_token_id = eos_token_id + self.rotary_dim = rotary_dim + self.tie_word_embeddings = tie_word_embeddings + self.dtype = dtype + self.layer_norm_eps = layer_norm_eps + self.max_sequence_length = max_sequence_length + self.rotary_emb_base = rotary_emb_base + self.kwargs = kwargs + + +class GPTJMLP(nn.Module): + def __init__(self, hidden_size: int, intermediate_size: int, dtype: str): + super().__init__() + self.fc_in = Linear(hidden_size, intermediate_size, dtype, bias=True) + self.fc_out = Linear(intermediate_size, hidden_size, dtype, bias=True) + self.dtype = dtype + + def forward(self, hidden_states): + if hidden_states.struct_info.dtype != self.dtype: + hidden_states = nn.emit(astype(hidden_states, self.dtype)) + hidden_states = self.fc_in(hidden_states) + hidden_states = nn.emit(gelu(hidden_states)) + if hidden_states.struct_info.dtype != self.dtype: + hidden_states = nn.emit(astype(hidden_states, self.dtype)) + hidden_states = self.fc_out(hidden_states) + return nn.emit(hidden_states) + + +class GPTJAttention(nn.Module): + """Multi-headed attention from 'Attention Is All You Need' paper""" + + def __init__( + self, + hidden_size: int, + num_heads: int, + rotary_embedding: RotaryEmbedding, + dtype: str, + ): + if hidden_size % num_heads != 0: + raise ValueError( + f"hidden_size must be divisible by num_heads (got `hidden_size`: {hidden_size}" + f" and `num_heads`: {num_heads})." + ) + self.hidden_size = hidden_size + self.num_heads = num_heads + self.head_dim = hidden_size // num_heads + self.rotary_embedding = rotary_embedding + self.q_proj = Linear(hidden_size, hidden_size, dtype, bias=False) + self.k_proj = Linear(hidden_size, hidden_size, dtype, bias=False) + self.v_proj = Linear(hidden_size, hidden_size, dtype, bias=False) + self.out_proj = Linear(hidden_size, hidden_size, dtype, bias=False) + self.dtype = dtype + + def forward( + self, + hidden_states: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_value: Optional[Tuple[relax.Expr, relax.Expr]] = None, + attention_mask: Optional[relax.Expr] = None, + ) -> Tuple[relax.Expr, Union[Tuple[None, None], Tuple[relax.Expr, relax.Expr]]]: + # hidden_states: [batch_size, seq_len, hidden_size] + if hidden_states.struct_info.dtype != self.dtype: + hidden_states = nn.emit(astype(hidden_states, self.dtype)) + batch_size, seq_len, _ = hidden_states.struct_info.shape + kv_seq_len = all_seq_len_shape.struct_info.values[0] + + def _project(proj): + return nn.emit( + reshape( + proj(hidden_states), + (batch_size, seq_len, self.num_heads, self.head_dim), + ) + ) + + # q/k/v states: [batch_size, seq_len, num_attention_heads, head_size] + q, k, v = ( + _project(self.q_proj), + _project(self.k_proj), + _project(self.v_proj), + ) + q, k = self.rotary_embedding(q, k, kv_seq_len - seq_len) + + if past_key_value is not None: + f_kv_cache_append = relax.extern("vm.builtin.attention_kv_cache_append") + f_kv_cache_view = relax.extern("vm.builtin.attention_kv_cache_view") + k_cache, v_cache = past_key_value + k_cache = nn.emit( + relax.op.call_inplace_packed( + f_kv_cache_append, + args=[k_cache, squeeze(k, axis=0)], + inplace_indices=[0], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + v_cache = nn.emit( + relax.op.call_inplace_packed( + f_kv_cache_append, + args=[v_cache, squeeze(v, axis=0)], + inplace_indices=[0], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + batch_size, _, num_heads, head_size = k.struct_info.shape + kv_cache_shape = R.shape([kv_seq_len, num_heads, head_size]) + kv_states_shape = R.shape([batch_size, kv_seq_len, num_heads, head_size]) + k = nn.emit( + relax.call_pure_packed( + f_kv_cache_view, + args=[k_cache, kv_cache_shape], + sinfo_args=[R.Tensor(kv_cache_shape, k.struct_info.dtype)], + ) + ) + v = nn.emit( + relax.call_pure_packed( + f_kv_cache_view, + args=[v_cache, kv_cache_shape], + sinfo_args=[R.Tensor(kv_cache_shape, v.struct_info.dtype)], + ) + ) + k = nn.emit(reshape(k, kv_states_shape)) + v = nn.emit(reshape(v, kv_states_shape)) + past_key_value = (k_cache, v_cache) + else: + past_key_value = (None, None) + + q = nn.emit(permute_dims(q, [0, 2, 1, 3])) + k = nn.emit(permute_dims(k, [0, 2, 1, 3])) + v = nn.emit(permute_dims(v, [0, 2, 1, 3])) + + # Calculate QK + attn_weights = nn.emit( + matmul(q, permute_dims(k, [0, 1, 3, 2])) + / relax.const( + math.sqrt(self.head_dim), + q.struct_info.dtype, + ) + ) + # Apply attention mask + attn_weights = nn.emit(attn_weights + attention_mask) + attn_weights = nn.emit( + minimum( + maximum( + attn_weights, + _min_value(attn_weights.struct_info.dtype), + ), + _max_value(attn_weights.struct_info.dtype), + ) + ) + # Calculate Softmax(QK) + if attn_weights.struct_info.dtype != "float32": + attn_weights = astype(attn_weights, "float32") + attn_weights = nn.emit(softmax(attn_weights, axis=-1)) + if attn_weights.struct_info.dtype != q.struct_info.dtype: + attn_weights = astype(attn_weights, q.struct_info.dtype) + # Calculate Softmax(QK)V + attn_output = nn.emit(matmul(attn_weights, v)) + # Apply output projection + attn_output = self.out_proj( + reshape( + permute_dims(attn_output, [0, 2, 1, 3]), + (batch_size, seq_len, self.hidden_size), + ) + ) + return attn_output, past_key_value + + +class GPTJLayer(nn.Module): + def __init__( + self, + hidden_size: int, + intermediate_size: int, + layer_norm_eps: float, + num_heads: int, + rotary_embedding: RotaryEmbedding, + dtype: str, + ): + self.ln_1 = LayerNorm( + hidden_size, + eps=layer_norm_eps, + dtype=dtype, + ) + self.attn = GPTJAttention( + hidden_size, + num_heads=num_heads, + rotary_embedding=rotary_embedding, + dtype=dtype, + ) + self.mlp = GPTJMLP( + hidden_size, + intermediate_size=intermediate_size, + dtype=dtype, + ) + self.dtype = dtype + + def forward( + self, + hidden_states, + all_seq_len_shape: relax.Expr, + past_key_value: Optional[Tuple[relax.Expr]] = None, + attention_mask: Optional[relax.Expr] = None, + ): + normalized_input = self.ln_1(hidden_states) + attn_output, present_key_value = self.attn( + normalized_input, + all_seq_len_shape, + past_key_value, + attention_mask, + ) + mlp_output = self.mlp(normalized_input) + hidden_states = nn.emit(mlp_output + attn_output + hidden_states) + return hidden_states, present_key_value + + +def _prepare_decoder_attention_mask(input_shape, src_len, dtype): + # create causal mask + # [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len] + if isinstance(input_shape[-1], tvm.tir.SizeVar) or input_shape[-1] > 1: + bsz, tgt_len = input_shape + mask = full((tgt_len, tgt_len), _min_value(dtype)) + mask = triu(mask, k=1) + diag_mask = nn.emit(broadcast_to(mask, (bsz, 1, tgt_len, tgt_len))) + if src_len == tgt_len: + return diag_mask + + def extend_te(x, tgt_len, src_len): + return te.compute( + (bsz, 1, tgt_len, src_len), + lambda b, _, i, j: te.if_then_else( + j < src_len - tgt_len, 0, x[b, _, i, j - (src_len - tgt_len)] + ), + name="concat_te", + ) + + return nn.emit_te(extend_te, diag_mask, tgt_len, src_len) + else: + # Get src_len from input parameters + # [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len] + bsz, tgt_len = input_shape + mask = relax.op.zeros((bsz, 1, tgt_len, src_len), dtype) + return nn.emit(mask) + + +class GPTJEmbedTokens(nn.Module): + def __init__(self, config: GPTJConfig): + self.wte = Embedding( + num_embeddings=config.vocab_size, + embedding_dim=config.hidden_size, + dtype=config.dtype, + ) + + def forward(self, input_ids: relax.Expr): + return self.wte(input_ids) + + +class GPTJEmbedTokensWrapper(nn.Module): + def __init__(self, config: GPTJConfig): + # build a wrapper to ensure that the naming of the embed_in parameter is consistent + self.gptj = GPTJEmbedTokens(config) + + def forward(self, input_ids: relax.Expr): + return self.gptj(input_ids) + + +class GPTJModel(nn.Module): + def __init__( + self, + config: GPTJConfig, + sep_embed: bool = False, + ): + rotary_embedding = RotaryEmbedding( + hidden_size=config.hidden_size, + num_attention_heads=config.num_attention_heads, + position_embedding_base=config.rotary_emb_base, + max_sequence_length=config.max_sequence_length, + rotary_dim=config.rotary_dim, + swizzle_style="gptj", + dtype=config.dtype, + ) + self.wte = None + if not sep_embed: + self.wte = Embedding( + num_embeddings=config.vocab_size, + embedding_dim=config.hidden_size, + dtype=config.dtype, + ) + self.h = ModuleList( + [ + GPTJLayer( + hidden_size=config.hidden_size, + intermediate_size=config.intermediate_size, + layer_norm_eps=config.layer_norm_eps, + num_heads=config.num_attention_heads, + rotary_embedding=rotary_embedding, + dtype=config.dtype, + ) + for _ in range(config.num_hidden_layers) + ] + ) + self.ln_f = LayerNorm( + hidden_size=config.hidden_size, + eps=config.layer_norm_eps, + dtype=config.dtype, + ) + + def forward( + self, + inputs: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_values: Optional[Tuple[relax.Expr, relax.Expr]], + ): + batch_size, seq_length = inputs.struct_info.shape + seq_length_with_past = all_seq_len_shape.struct_info.values[0] + # embed positions + hidden_states = self.wte(inputs) if self.wte is not None else inputs + attention_mask = _prepare_decoder_attention_mask( + (batch_size, seq_length), + seq_length_with_past, + dtype=hidden_states.struct_info.dtype, + ) + present_kv_cache = [] + for i, layer in enumerate(self.h): + past_key_value = ( + (past_key_values[i * 2], past_key_values[i * 2 + 1]) + if past_key_values is not None + else None + ) + hidden_states, (present_k_cache, present_v_cache) = layer( + hidden_states, + attention_mask=attention_mask, + past_key_value=past_key_value, + all_seq_len_shape=all_seq_len_shape, + ) + present_kv_cache.append(present_k_cache) + present_kv_cache.append(present_v_cache) + hidden_states = self.ln_f(hidden_states) + return hidden_states, present_kv_cache + + +class GPTJForCausalLM(nn.Module): + def __init__( + self, + config: GPTJConfig, + sep_embed: bool = False, + ): + self.transformer = GPTJModel(config, sep_embed) + self.lm_head = Linear( + in_features=config.hidden_size, + out_features=config.vocab_size, + bias=True, + dtype=config.dtype, + ) + self.dtype = config.dtype + + def forward( + self, + inputs: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_values: Optional[List[relax.Expr]], + ): + hidden_states, key_value_cache = self.transformer( + inputs=inputs, + all_seq_len_shape=all_seq_len_shape, + past_key_values=past_key_values, + ) + if hidden_states.struct_info.dtype != self.dtype: + hidden_states = nn.emit(astype(hidden_states, self.dtype)) + + def _slice(x: te.Tensor): + _, seq_len, hidden_dim = x.shape + return te.compute( + shape=(1, 1, hidden_dim), + fcompute=lambda i, _, k: x[i, seq_len - 1, k], + name="slice", + ) + + hidden_states = nn.emit_te( + _slice, + hidden_states, + primfunc_name_hint="slice", + ) + logits = self.lm_head(hidden_states) + if logits.struct_info.dtype != "float32": + logits = nn.emit(astype(logits, "float32")) + + return logits, key_value_cache + + +def check_parameters(param_dict, param_list): + relax_shape_to_list = lambda _: [s.value for s in _.values] + shape_dict_0 = {k: relax_shape_to_list(v.struct_info.shape) for k, v in param_dict.items()} + shape_dict_1 = {k: list(v.shape) for (k, v) in param_list} + assert len(shape_dict_0) == len(shape_dict_1) + for k, v in shape_dict_0.items(): + assert k in shape_dict_1, "{}".format(k) + assert v == shape_dict_1[k], "key={}, shape_0={}, shape_1={}".format(k, v, shape_dict_1[k]) + + +def get_param_quant_kind(name: str, param_info: relax.TensorStructInfo) -> ParamQuantKind: + if "wte.weight" in name: + return ParamQuantKind.embedding_table + elif "lm_head.weight" in name: + return ParamQuantKind.final_fc_weight + elif param_info.ndim == 2 and name.endswith(".weight"): + return ParamQuantKind.linear_weight + else: + return ParamQuantKind.others + + +def create_embed_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: GPTJConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "embed" + + bsz = 1 + seq_len = tvm.tir.SizeVar("m", "int64") + with bb.function(func_name): + model = GPTJEmbedTokensWrapper(config) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + input_ids = nn.Placeholder((bsz, seq_len), dtype="int32", name="input_ids") + with bb.dataflow(): + inputs_embeds = model(input_ids) + params = [input_ids] + model.parameters() + gv = bb.emit_output(inputs_embeds) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var("embed") + bb.update_func(gv, mod[gv].with_attr("num_input", 1)) + + +def create_encoding_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: GPTJConfig, + quant_scheme: QuantizationScheme, + sep_embed: bool = False, +) -> None: + func_name = "prefill_with_embed" if sep_embed else "prefill" + + batch_size = tvm.tir.IntImm("int64", 1) + seq_len = tvm.tir.SizeVar("n", "int64") + all_seq_len = tvm.tir.SizeVar("m", "int64") + hidden_size = config.hidden_size + with bb.function(func_name): + model = GPTJForCausalLM(config, sep_embed) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + inputs = ( + nn.Placeholder( + (batch_size, seq_len, hidden_size), + dtype=config.dtype, + name="input_embeds", + ) + if sep_embed + else nn.Placeholder((batch_size, seq_len), dtype="int32", name="input_ids") + ) + all_seq_len_shape = relax.Var("all_seq_len", relax.ShapeStructInfo((all_seq_len,))) + past_key_values = relax.Var( + "kv_cache", + relax.TupleStructInfo( + [relax.ObjectStructInfo() for _ in range(config.num_hidden_layers * 2)] + ), + ) + with bb.dataflow(): + logits, key_value_cache = model( + inputs=inputs, + all_seq_len_shape=all_seq_len_shape, + past_key_values=past_key_values, + ) + params = [ + inputs, + all_seq_len_shape, + past_key_values, + ] + model.parameters() + gv = bb.emit_output((logits, relax.Tuple(key_value_cache))) + bb.emit_func_output(gv, params) + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 3)) + + +def create_decoding_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: GPTJConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "decode" + + batch_size = tvm.tir.IntImm("int64", 1) + seq_len = tvm.tir.IntImm("int64", 1) + all_seq_len = tvm.tir.SizeVar("m", "int64") + with bb.function(func_name): + model = GPTJForCausalLM(config) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + input_ids = nn.Placeholder((batch_size, seq_len), dtype="int32", name="input_ids") + all_seq_len_shape = relax.Var( + "all_seq_len", + relax.ShapeStructInfo((all_seq_len,)), + ) + past_key_values = relax.Var( + "kv_cache", + relax.TupleStructInfo( + [relax.ObjectStructInfo() for _ in range(config.num_hidden_layers * 2)] + ), + ) + with bb.dataflow(): + logits, key_value_cache = model( + inputs=input_ids, + all_seq_len_shape=all_seq_len_shape, + past_key_values=past_key_values, + ) + params = [ + input_ids, + all_seq_len_shape, + past_key_values, + ] + model.parameters() + gv = bb.emit_output((logits, relax.Tuple(key_value_cache))) + bb.emit_func_output(gv, params) + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 3)) + + +def create_softmax_func(bb: relax.BlockBuilder, config: GPTJConfig) -> None: + with bb.function("softmax_with_temperature"): + logits = nn.Placeholder((1, 1, config.vocab_size), dtype="float32", name="logits") + temperature = nn.Placeholder((), dtype="float32", name="temperature") + with bb.dataflow(): + div = bb.emit(relax.op.divide(logits, temperature)) + softmax = bb.emit(relax.op.nn.softmax(div, axis=-1)) + gv = bb.emit_output(softmax) + bb.emit_func_output(gv, [logits, temperature]) + + +def get_model(args, hf_config): + model_name = args.model + dtype = args.quantization.model_dtype + max_seq_len = args.max_seq_len + sep_embed = args.sep_embed + + if model_name.startswith("gpt-j-"): + stop_tokens = [50256] + elif model_name.startswith("moss-"): + stop_tokens = [106068] + + config = GPTJConfig(**hf_config, dtype=dtype) + if max_seq_len != -1: + config.max_sequence_length = max_seq_len + + param_manager = ParamManager() + bb = relax.BlockBuilder() + if sep_embed: + create_embed_func(bb, param_manager, config, args.quantization) + create_encoding_func(bb, param_manager, config, args.quantization, sep_embed) + create_decoding_func(bb, param_manager, config, args.quantization) + create_kv_cache_func(bb, config) + create_softmax_func(bb, config) + create_metadata_func( + bb, + model_name=model_name, + max_window_size=config.max_sequence_length, + stop_tokens=stop_tokens, + add_prefix_space=True, + prefill_chunk_size=args.prefill_chunk_size, + ) + mod = bb.get() + + tir_bound_map = dict() + tir_bound_map["n"] = ( + args.prefill_chunk_size if args.prefill_chunk_size > 0 else config.max_sequence_length + ) + tir_bound_map["m"] = config.max_sequence_length + for gv in mod.functions: + func = mod[gv] + if isinstance(func, relax.Function): + mod[gv] = func.with_attr("tir_var_upper_bound", tir_bound_map) + + if args.build_model_only: + return mod, param_manager, None, config + + def f_convert_pname_fwd(pname: str) -> List[str]: + import re + + str_pattern = re.compile(r"(q|k|v)_proj") + if re.search(str_pattern, pname) is not None: + return [str_pattern.sub("qkv_proj", pname)] + else: + return [pname] + + hidden_size = config.hidden_size + + def f_convert_param_bkwd(torch_pname: str, torch_param) -> Optional[List[Tuple[str, Any]]]: + # torch_param: numpy.ndarray + if torch_pname.endswith("qkv_proj.weight"): + assert torch_param.ndim == 2 + mp_num = 4 + torch_param = torch_param.astype(dtype).reshape(mp_num, 3, -1, hidden_size) + q_weight = torch_param[:, 0, :, :].reshape(hidden_size, hidden_size) + k_weight = torch_param[:, 2, :, :].reshape(hidden_size, hidden_size) + v_weight = torch_param[:, 1, :, :].reshape(hidden_size, hidden_size) + return [ + (torch_pname.replace("qkv_proj", "q_proj"), q_weight), + (torch_pname.replace("qkv_proj", "k_proj"), k_weight), + (torch_pname.replace("qkv_proj", "v_proj"), v_weight), + ] + if "ln_1" in torch_pname or "ln_f" in torch_pname: + return [(torch_pname, torch_param.astype("float32"))] + else: + return [(torch_pname, torch_param.astype(dtype))] + + param_manager.set_param_loading_func( + args.model_path, args.use_safetensors, f_convert_pname_fwd, f_convert_param_bkwd + ) + return mod, param_manager, [None] * len(param_manager.param_names), config diff --git a/mlc_llm/relax_model/llama.py b/mlc_llm/relax_model/llama.py new file mode 100644 index 0000000..06272e3 --- /dev/null +++ b/mlc_llm/relax_model/llama.py @@ -0,0 +1,1507 @@ +import math +from dataclasses import dataclass +from typing import Any, List, Optional, Tuple, Union + +import numpy as np +import tvm +from tvm import relax, te, tir +from tvm.relax.op import ccl +from tvm.relax.testing import nn +from tvm.script import relax as R + +from ..quantization import ParamQuantKind, QuantizationScheme +from .commons import create_metadata_func +from .modules import ModuleList +from .param_manager import ParamManager + + +@dataclass +class LlamaConfig: + def __init__( + self, + dtype="float32", + max_sequence_length=2048, + vocab_size=32000, # some models like WizardMath can have 32001 + hidden_size=4096, + intermediate_size=11008, + num_hidden_layers=32, + num_attention_heads=32, + num_key_value_heads=None, + hidden_act="silu", + initializer_range=0.02, + rms_norm_eps=1e-6, + pad_token_id=-1, + bos_token_id=0, + eos_token_id=1, + tie_word_embeddings=False, + position_embedding_base=10000, + combine_matmul=True, + build_model_only=False, + num_shards=1, + sliding_window=None, + target_kind=None, + **kwargs, + ): + self.dtype = dtype + self.max_sequence_length = max_sequence_length + self.vocab_size = vocab_size + self.hidden_size = hidden_size + self.intermediate_size = intermediate_size + self.num_hidden_layers = num_hidden_layers + self.num_attention_heads = num_attention_heads + self.num_key_value_heads = num_key_value_heads + self.hidden_act = hidden_act + self.initializer_range = initializer_range + self.rms_norm_eps = rms_norm_eps + self.pad_token_id = pad_token_id + self.bos_token_id = bos_token_id + self.eos_token_id = eos_token_id + self.tie_word_embeddings = tie_word_embeddings + self.position_embedding_base = position_embedding_base + self.combine_matmul = combine_matmul + self.sliding_window = sliding_window + self.target_kind = target_kind + + if build_model_only and num_shards > 1: + self.num_shards = num_shards + else: + self.num_shards = 1 + self.kwargs = kwargs + + def get_num_key_value_heads(self): + if self.num_key_value_heads is None: + return self.num_attention_heads + + return self.num_key_value_heads + + +class Linear(nn.Module): + def __init__(self, in_features, out_features, dtype: str, bias=True): + self.in_features = in_features + self.out_features = out_features + self.weight = nn.Parameter((out_features, in_features), dtype=dtype, name="linear_weight") + if bias: + self.bias = nn.Parameter((out_features,), dtype=dtype, name="linear_bias") + else: + self.bias = None + + def forward(self, input: relax.Expr) -> relax.Var: + return nn.emit(relax.op.linear(input, self.weight, self.bias)) + + +class Embedding(nn.Module): + def __init__(self, num_embeddings, embedding_dim, dtype: str): + self.num_embeddings = num_embeddings + self.embedding_dim = embedding_dim + self.weight = nn.Parameter( + (num_embeddings, embedding_dim), dtype=dtype, name="embedding_weight" + ) + + def forward(self, x: relax.Expr) -> relax.Var: + from tvm.relax.op import reshape, take + + ndim = x.struct_info.ndim + if ndim == 1: + return nn.emit(take(self.weight, x, axis=0)) + else: + x_shape = x.struct_info.shape.values + emb_size = self.weight.struct_info.shape.values[-1] + x = nn.emit(reshape(x, shape=[-1])) + embedding = nn.emit(take(self.weight, x, axis=0)) + return nn.emit(reshape(embedding, [*x_shape, emb_size])) + + +class LlamaRMSNorm(nn.Module): + def __init__(self, hidden_size, dtype, eps=1e-6): + self.weight = nn.Parameter((hidden_size,), dtype=dtype, name="rms_norm_weight") + self.variance_epsilon = tvm.tir.const(eps, dtype) + + def forward(self, hidden_states): + from tvm import te, tir + + def f_rms_norm(x, weight): + is_float32 = x.dtype == "float32" + + def f_square(x): + return tir.Cast("float32", x) * tir.Cast("float32", x) if not is_float32 else x * x + + def f_mul_cast(x, y): + value = x * y + if not is_float32: + value = tir.Cast(x.dtype, value) + return value + + def f_div_cast_2d(i, k): + x_val = x[i, k] + if not is_float32: + x_val = tir.Cast("float32", x_val) + return x_val / tir.sqrt(square_sum[i] / x.shape[1] + self.variance_epsilon) + + def f_div_cast_3d(bsz, i, k): + x_val = x[bsz, i, k] + if not is_float32: + x_val = tir.Cast("float32", x_val) + return x_val / tir.sqrt(square_sum[bsz, i] / x.shape[2] + self.variance_epsilon) + + k = te.reduce_axis((0, x.shape[-1]), name="k") + + if len(x.shape) == 2: + square_sum = te.compute( + (x.shape[0],), + lambda i: te.sum(f_square(x[i, k]), axis=k), + name=x.op.name + "red_temp", + ) + + return te.compute( + x.shape, + lambda i, k: f_mul_cast(weight(k), f_div_cast_2d(i, k)), + name="rms_norm", + ) + else: + square_sum = te.compute( + (x.shape[0], x.shape[1]), + lambda bsz, i: te.sum(f_square(x[bsz, i, k]), axis=k), + name=x.op.name + "red_temp", + ) + + return te.compute( + x.shape, + lambda bsz, i, k: f_mul_cast(weight(k), f_div_cast_3d(bsz, i, k)), + name="rms_norm", + ) + + return nn.emit_te(f_rms_norm, hidden_states, self.weight, primfunc_name_hint="rms_norm") + + +class LlamaMLP(nn.Module): + def __init__(self, config: LlamaConfig): + self.combine_matmul = config.combine_matmul + self.num_shards = config.num_shards + hidden_size = config.hidden_size + intermediate_size = config.intermediate_size // self.num_shards + dtype = config.dtype + if self.combine_matmul: + self.gate_up_proj = Linear(hidden_size, 2 * intermediate_size, dtype=dtype, bias=False) + self.down_proj = Linear(intermediate_size, hidden_size, dtype=dtype, bias=False) + self.gate_up_proj.weight.shard_dim = 0 + self.gate_up_proj.weight.shard_strategy = "shard_gate_up" + self.down_proj.weight.shard_dim = 1 + self.down_proj.weight.shard_strategy = "shard_mlp_k" + else: + self.gate_proj = Linear(hidden_size, intermediate_size, dtype=dtype, bias=False) + self.down_proj = Linear(intermediate_size, hidden_size, dtype=dtype, bias=False) + self.up_proj = Linear(hidden_size, intermediate_size, dtype=dtype, bias=False) + self.gate_proj.weight.shard_dim = 0 + self.gate_proj.weight.shard_strategy = "shard_axis_0" + self.down_proj.weight.shard_dim = 1 + self.down_proj.weight.shard_strategy = "shard_axis_1" + self.up_proj.weight.shard_dim = 0 + self.up_proj.weight.shard_strategy = "shard_axis_0" + + def forward(self, x): + if self.combine_matmul: + gate_up_results = nn.emit( + relax.op.split( + self.gate_up_proj(x), + indices_or_sections=2, + axis=-1, + ) + ) + gate_result = relax.TupleGetItem(gate_up_results, 0) + up_result = relax.TupleGetItem(gate_up_results, 1) + else: + gate_result = self.gate_proj(x) + up_result = self.up_proj(x) + + result = self.down_proj(relax.op.nn.silu(gate_result) * up_result) + return result + + +def rotary_modulate_by_freq(tensor, idx, pos, position_embedding_base): + head_dim = tensor.shape[-1] + dtype = tensor.dtype + n_feat_half = head_dim // 2 + feat_idx = idx[-1] + inv_freq = te.const(1, "float32") / ( + te.power( + te.const(position_embedding_base, "float32"), + ((2 * feat_idx) % head_dim).astype("float32") / head_dim.astype("float32"), + ) + ) + freq = pos * inv_freq + left_indices = idx[:-1] + (feat_idx - n_feat_half,) + right_indices = idx[:-1] + (feat_idx + n_feat_half,) + return te.cos(freq).astype(dtype) * tensor(*idx) + te.sin(freq).astype(dtype) * tvm.tir.Select( + feat_idx >= n_feat_half, + tensor[(*left_indices,)], + -tensor[(*right_indices,)], + ) + + +def apply_rotary_pos_emb(q, k, position_embedding_base, offset: int = 0): + def f_rotary_embedding(tensor, offset): + def rotary_compute(*idx): + pos = (offset + idx[-3]).astype("float32") + return rotary_modulate_by_freq( + tensor, + idx, + pos, + position_embedding_base, + ) + + return tvm.te.compute(tensor.shape, rotary_compute, name="rotary") + + q_embed = nn.emit_te(f_rotary_embedding, q, offset, primfunc_name_hint="rotary_embedding") + k_embed = nn.emit_te(f_rotary_embedding, k, offset, primfunc_name_hint="rotary_embedding") + return q_embed, k_embed + + +class LlamaAttentionBase(nn.Module): + """Multi-headed attention from 'Attention Is All You Need' paper""" + + def __init__(self, config: LlamaConfig): + dtype = config.dtype + self.num_shards = config.num_shards + self.hidden_size = config.hidden_size + self.num_key_value_heads = config.get_num_key_value_heads() // config.num_shards + self.num_query_heads = config.num_attention_heads // self.num_shards + self.head_dim = self.hidden_size // config.num_attention_heads + self.position_embedding_base = config.position_embedding_base + + self.combine_matmul = config.combine_matmul + if self.combine_matmul: + self.query_key_value_proj = Linear( + self.hidden_size, + (self.num_query_heads + 2 * self.num_key_value_heads) * self.head_dim, + dtype=dtype, + bias=False, + ) + self.query_key_value_proj.weight.shard_dim = 0 + self.query_key_value_proj.weight.shard_strategy = "shard_qkv" + else: + self.q_proj = Linear( + self.hidden_size, + self.num_query_heads * self.head_dim, + dtype=dtype, + bias=False, + ) + self.k_proj = Linear( + self.hidden_size, + self.num_key_value_heads * self.head_dim, + dtype=dtype, + bias=False, + ) + self.v_proj = Linear( + self.hidden_size, + self.num_key_value_heads * self.head_dim, + dtype=dtype, + bias=False, + ) + self.q_proj.weight.shard_dim = 0 + self.k_proj.weight.shard_dim = 0 + self.v_proj.weight.shard_dim = 0 + self.q_proj.weight.shard_strategy = "shard_axis_0" + self.k_proj.weight.shard_strategy = "shard_axis_0" + self.v_proj.weight.shard_strategy = "shard_axis_0" + + self.o_proj = Linear( + self.head_dim * self.num_query_heads, self.hidden_size, dtype=dtype, bias=False + ) + self.o_proj.weight.shard_dim = 1 + self.o_proj.weight.shard_strategy = "shard_o_proj_k" + + def project_qkv(self, hidden_states, query_output_shape, kv_output_shape): + from tvm.relax.op import reshape, split + + if self.combine_matmul: + qkv_states = nn.emit( + split( + self.query_key_value_proj(hidden_states), + indices_or_sections=[ + self.num_query_heads * self.head_dim, + (self.num_query_heads + self.num_key_value_heads) * self.head_dim, + ], + axis=-1, + ) + ) + query_states = relax.TupleGetItem(qkv_states, 0) + key_states = relax.TupleGetItem(qkv_states, 1) + value_states = relax.TupleGetItem(qkv_states, 2) + else: + query_states = self.q_proj(hidden_states) + key_states = self.k_proj(hidden_states) + value_states = self.v_proj(hidden_states) + + query_states = nn.emit( + reshape(query_states, query_output_shape), + ) + key_states = nn.emit( + reshape(key_states, kv_output_shape), + ) + value_states = nn.emit( + reshape(value_states, kv_output_shape), + ) + + return query_states, key_states, value_states + + def forward( + self, + hidden_states: relax.Expr, + all_seq_len_shape: Optional[relax.Expr], + past_key_values: Union[relax.Expr, Tuple[relax.Expr]], + layer_id: int, + attention_mask: Optional[relax.Expr] = None, + ) -> Tuple[relax.Expr, Union[relax.Expr, Tuple[relax.Expr]]]: + bsz, q_len, _ = hidden_states.struct_info.shape + + query_states, key_states, value_states = self.project_qkv( + hidden_states, + (bsz, q_len, self.num_query_heads, self.head_dim), + (bsz, q_len, self.num_key_value_heads, self.head_dim), + ) + + from tvm.relax.op import reshape + + attn_output, past_key_values = self.attention_fwd( + query_states, + key_states, + value_states, + past_key_values, + bsz, + q_len, + layer_id=layer_id, + all_seq_len_shape=all_seq_len_shape, + attention_mask=attention_mask, + ) + + attn_output = nn.emit( + reshape(attn_output, (bsz, q_len, self.head_dim * self.num_query_heads)) + ) + attn_output = self.o_proj(attn_output) + return attn_output, past_key_values + + def attention_fwd( + self, + query_states: relax.Expr, + key_states: relax.Expr, + value_states: relax.Expr, + past_key_values: relax.Expr, + batch_size: tir.PrimExpr, + q_len: tir.PrimExpr, + **kwargs, + ): + raise NotImplementedError() + + +class LlamaPagedAttention(LlamaAttentionBase): + def __init__(self, config: LlamaConfig): + super().__init__(config) + + def attention_fwd( + self, + query_states: relax.Expr, + key_states: relax.Expr, + value_states: relax.Expr, + past_key_values: relax.Expr, + batch_size: tir.PrimExpr, + q_len: tir.PrimExpr, + **kwargs, + ) -> Tuple[relax.Expr, relax.Expr]: + assert "layer_id" in kwargs and isinstance(kwargs["layer_id"], int) + layer_id = kwargs["layer_id"] + + f_kv_cache_attention = relax.extern("vm.builtin.paged_attention_kv_cache_attention") + attn_output = nn.emit( + relax.call_dps_packed( + f_kv_cache_attention, + [ + past_key_values, + relax.PrimValue(layer_id), + query_states, + key_states, + value_states, + ], + out_sinfo=relax.TensorStructInfo( + ((batch_size, q_len, self.num_query_heads, self.head_dim)), + query_states.struct_info.dtype, + ), + ) + ) + return attn_output, past_key_values + + +class LlamaAttention(LlamaAttentionBase): + def __init__(self, config: LlamaConfig): + super().__init__(config) + self.config = config + + def attention_fwd( + self, + query_states: relax.Expr, + key_states: relax.Expr, + value_states: relax.Expr, + past_key_values: relax.Expr, + batch_size: tir.PrimExpr, + q_len: tir.PrimExpr, + **kwargs, + ) -> Tuple[relax.Expr, Tuple[relax.Expr]]: + assert "attention_mask" in kwargs + assert "all_seq_len_shape" in kwargs + attention_mask = kwargs["attention_mask"] + kv_seq_len = kwargs["all_seq_len_shape"].struct_info.values[0] + + from tvm.relax.op import astype, matmul, maximum, permute_dims, reshape, squeeze + from tvm.relax.op.nn import softmax + + offset = kv_seq_len - q_len + query_states, key_states = apply_rotary_pos_emb( + query_states, + key_states, + self.position_embedding_base, + offset=offset, + ) + # [bsz, t, nh, hd] + + kv_states_shape = key_states.struct_info.shape + kv_states_dtype = key_states.struct_info.dtype + assert kv_states_shape[0] == 1 # bsz + kv_states_shape = R.shape( + [kv_states_shape[0], kv_seq_len, kv_states_shape[2], kv_states_shape[3]] + ) + kv_cache_shape = R.shape([kv_seq_len, kv_states_shape[2], kv_states_shape[3]]) + + squeezed_key = nn.emit(squeeze(key_states, axis=0)) + squeezed_value = nn.emit(squeeze(value_states, axis=0)) + k_cache, v_cache = past_key_values + f_kv_cache_append = relax.extern("vm.builtin.attention_kv_cache_append") + k_cache = nn.emit( + relax.op.call_inplace_packed( + f_kv_cache_append, + k_cache, + squeezed_key, + inplace_indices=[0], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + v_cache = nn.emit( + relax.op.call_inplace_packed( + f_kv_cache_append, + v_cache, + squeezed_value, + inplace_indices=[0], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + past_key_values = (k_cache, v_cache) + f_kv_cache_view = relax.extern("vm.builtin.attention_kv_cache_view") + k_cache = nn.emit( + relax.call_pure_packed( + f_kv_cache_view, + k_cache, + kv_cache_shape, + sinfo_args=[R.Tensor(kv_cache_shape, kv_states_dtype)], + ) + ) + v_cache = nn.emit( + relax.call_pure_packed( + f_kv_cache_view, + v_cache, + kv_cache_shape, + sinfo_args=[R.Tensor(kv_cache_shape, kv_states_dtype)], + ) + ) + key_states = nn.emit(reshape(k_cache, kv_states_shape)) + value_states = nn.emit(reshape(v_cache, kv_states_shape)) + if self.num_key_value_heads != self.num_query_heads: + n_rep = self.num_query_heads // self.num_key_value_heads + key_states = nn.emit(relax.op.repeat(key_states, n_rep, axis=2)) + value_states = nn.emit(relax.op.repeat(value_states, n_rep, axis=2)) + + if self.config.target_kind == "android": + attn_weights = nn.emit( + matmul( + permute_dims(query_states, [0, 2, 1, 3]), permute_dims(key_states, [0, 2, 3, 1]) + ) + / relax.const(math.sqrt(self.head_dim), query_states.struct_info.dtype) + ) + else: + query_states = nn.emit(permute_dims(query_states, [0, 2, 1, 3])) + key_states = nn.emit(permute_dims(key_states, [0, 2, 1, 3])) + value_states = nn.emit(permute_dims(value_states, [0, 2, 1, 3])) + + attn_weights = nn.emit( + matmul(query_states, permute_dims(key_states, [0, 1, 3, 2])) + / relax.const(math.sqrt(self.head_dim), query_states.struct_info.dtype) + ) + + tvm.ir.assert_structural_equal( + attention_mask.struct_info.shape.values, + (batch_size, tvm.tir.IntImm("int64", 1), q_len, kv_seq_len), + ) + + attn_weights = nn.emit( + maximum( + attn_weights, + relax.const( + tvm.tir.min_value(attn_weights.struct_info.dtype).value, + attn_weights.struct_info.dtype, + ), + ) + ) + attn_weights = nn.emit(relax.op.minimum(attn_weights, attention_mask)) + + # upcast attention to fp32 + if attn_weights.struct_info.dtype != "float32": + attn_weights = astype(attn_weights, "float32") + attn_weights = nn.emit(softmax(attn_weights, axis=-1)) + if attn_weights.struct_info.dtype != query_states.struct_info.dtype: + attn_weights = astype(attn_weights, query_states.struct_info.dtype) + if self.config.target_kind == "android": + attn_output = nn.emit(matmul(attn_weights, permute_dims(value_states, [0, 2, 1, 3]))) + else: + attn_output = nn.emit(matmul(attn_weights, value_states)) + attn_output = nn.emit(permute_dims(attn_output, [0, 2, 1, 3])) + return attn_output, past_key_values + + +class LlamaDecoderLayer(nn.Module): + def __init__(self, config: LlamaConfig, enable_batching: bool): + attn_class = LlamaPagedAttention if enable_batching else LlamaAttention + self.hidden_size = config.hidden_size + self.self_attn = attn_class(config) + self.mlp = LlamaMLP(config) + self.input_layernorm = LlamaRMSNorm( + config.hidden_size, dtype=config.dtype, eps=config.rms_norm_eps + ) + self.post_attention_layernorm = LlamaRMSNorm( + config.hidden_size, dtype=config.dtype, eps=config.rms_norm_eps + ) + + def post_self_attn(self, hidden_states, residual): + if self.self_attn.num_shards > 1: + residual = nn.emit( + residual / R.const(self.self_attn.num_shards, dtype=residual.struct_info.dtype) + ) + hidden_states = nn.emit(residual + hidden_states) + if self.self_attn.num_shards > 1: + hidden_states = nn.emit(ccl.allreduce(hidden_states, "sum")) + + # Fully Connected + residual = hidden_states + hidden_states = self.post_attention_layernorm(hidden_states) + hidden_states = self.mlp(hidden_states) + if self.mlp.num_shards > 1: + residual = nn.emit( + residual / R.const(self.mlp.num_shards, dtype=residual.struct_info.dtype) + ) + hidden_states = nn.emit(residual + hidden_states) + if self.mlp.num_shards > 1: + hidden_states = nn.emit(ccl.allreduce(hidden_states, "sum")) + + return hidden_states + + def forward( + self, + hidden_states: relax.Expr, + all_seq_len_shape: Optional[relax.Expr], + past_key_values: Union[relax.Expr, Tuple[relax.Expr]], + layer_id: int, + attention_mask: Optional[relax.Expr] = None, + ) -> Tuple[relax.Expr, Optional[Tuple[relax.Expr, relax.Expr]]]: + residual = hidden_states + + hidden_states = self.input_layernorm(hidden_states) + + # Self Attention + hidden_states, present_key_value = self.self_attn( + hidden_states=hidden_states, + past_key_values=past_key_values, + attention_mask=attention_mask, + all_seq_len_shape=all_seq_len_shape, + layer_id=layer_id, + ) + hidden_states = self.post_self_attn(hidden_states, residual) + return hidden_states, present_key_value + + +def _make_causal_mask(input_ids_shape, dtype, src_len): + from tvm.relax.op import broadcast_to + + bsz, tgt_len = input_ids_shape + + def min_max_triu_te(): + return te.compute( + (tgt_len, tgt_len), + lambda i, j: tvm.tir.Select(j > i, tvm.tir.min_value(dtype), tvm.tir.max_value(dtype)), + name="make_diag_mask_te", + ) + + mask = nn.emit_te(min_max_triu_te) + diag_mask = nn.emit(broadcast_to(mask, (bsz, 1, tgt_len, tgt_len))) + if src_len == tgt_len: + return diag_mask + + def extend_te(x, tgt_len, src_len): + return te.compute( + (bsz, 1, tgt_len, src_len), + lambda b, _, i, j: te.if_then_else( + j < src_len - tgt_len, + tvm.tir.max_value(dtype), + x[b, _, i, j - (src_len - tgt_len)], + ), + name="concat_te", + ) + + return nn.emit_te(extend_te, diag_mask, tgt_len, src_len) + + +class LlamaEmbedTokens(nn.Module): + def __init__(self, config: LlamaConfig, vocab_size_var: tvm.tir.SizeVar): + self.embed_tokens = Embedding(vocab_size_var, config.hidden_size, dtype=config.dtype) + + def forward(self, input_ids: relax.Expr): + inputs_embeds = self.embed_tokens(input_ids) + return inputs_embeds + + +class LlamaEmbedTokensWrapper(nn.Module): + def __init__(self, config: LlamaConfig, vocab_size_var: tvm.tir.SizeVar): + # build a wrapper to ensure that the naming of the embed_tokens parameter is consistent + self.model = LlamaEmbedTokens(config, vocab_size_var) + + def forward(self, input_ids: relax.Expr): + inputs_embeds = self.model(input_ids) + return inputs_embeds + + +class LlamaModelBase(nn.Module): + def __init__( + self, + config: LlamaConfig, + vocab_size_var: tir.SizeVar, + sep_embed: bool = False, + enable_batching: bool = False, + ): + self.num_shards = config.num_shards + self.padding_idx = config.pad_token_id + self.embed_tokens = None + + if not sep_embed: + self.embed_tokens = Embedding(vocab_size_var, config.hidden_size, dtype=config.dtype) + + self.layers = ModuleList( + [LlamaDecoderLayer(config, enable_batching) for _ in range(config.num_hidden_layers)] + ) + self.norm = LlamaRMSNorm(config.hidden_size, dtype=config.dtype, eps=config.rms_norm_eps) + + def forward( + self, + inputs: relax.Expr, + all_seq_len_shape: Optional[relax.Expr], + past_key_values: relax.Expr, + ): + raise NotImplementedError() + + +class LlamaModelForSingleSequence(LlamaModelBase): + def __init__( + self, config: LlamaConfig, vocab_size_var: tvm.tir.SizeVar, sep_embed: bool = False + ): + super().__init__(config, vocab_size_var, sep_embed, enable_batching=False) + + def _prepare_decoder_attention_mask(self, input_shape, src_len, dtype): + # create causal mask + # [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len] + combined_attention_mask = None + if isinstance(input_shape[-1], tvm.tir.SizeVar) or input_shape[-1] > 1: + combined_attention_mask = _make_causal_mask(input_shape, dtype, src_len) + else: + # Get src_len from input parameters + # [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len] + bsz, tgt_len = input_shape + combined_attention_mask = nn.emit( + relax.op.full( + (bsz, 1, tgt_len, src_len), + relax.const(tvm.tir.max_value(dtype).value, dtype), + dtype, + ) + ) + return combined_attention_mask + + def forward( + self, + inputs: relax.Expr, + all_seq_len_shape: Optional[relax.Expr], + past_key_values: relax.Expr, + ): + if self.num_shards > 1: + inputs = nn.emit(ccl.broadcast_from_worker0(inputs)) + if self.embed_tokens: + inputs_embeds = self.embed_tokens(inputs) + else: + inputs_embeds = inputs + # retrieve input_ids + batch_size, seq_length, _ = inputs_embeds.struct_info.shape + seq_length_with_past = all_seq_len_shape.struct_info.values[0] + # embed positions + attention_mask = self._prepare_decoder_attention_mask( + (batch_size, seq_length), + seq_length_with_past, + inputs_embeds.struct_info.dtype, + ) + + hidden_states = inputs_embeds + + # decoder layers + next_decoder_cache = () + + for idx, decoder_layer in enumerate(self.layers): + assert past_key_values is not None + past_key_value = (past_key_values[idx * 2], past_key_values[idx * 2 + 1]) + + hidden_states, key_value_cache = decoder_layer( + hidden_states, + attention_mask=attention_mask, + past_key_values=past_key_value, + all_seq_len_shape=all_seq_len_shape, + layer_id=idx, + ) + next_decoder_cache += key_value_cache + + hidden_states = self.norm(hidden_states) + + assert len(next_decoder_cache) == len(self.layers) * 2 + return hidden_states, next_decoder_cache + + +class LlamaModelForBatching(LlamaModelBase): + def __init__(self, config: LlamaConfig, vocab_size_var: tvm.tir.SizeVar, sep_embed: bool): + assert sep_embed + super().__init__(config, vocab_size_var, sep_embed=True, enable_batching=True) + + def forward( + self, + inputs: relax.Expr, + all_seq_len_shape: Optional[relax.Expr], + past_key_values: relax.Expr, + ): + assert all_seq_len_shape is None + if self.num_shards > 1: + inputs = nn.emit(ccl.broadcast_from_worker0(inputs)) + if self.embed_tokens: + inputs_embeds = self.embed_tokens(inputs) + else: + inputs_embeds = inputs + + hidden_states = inputs_embeds + + for idx, decoder_layer in enumerate(self.layers): + assert past_key_values is not None + hidden_states, past_key_values = decoder_layer( + hidden_states, + attention_mask=None, + past_key_values=past_key_values, + all_seq_len_shape=all_seq_len_shape, + layer_id=idx, + ) + + hidden_states = self.norm(hidden_states) + return hidden_states, past_key_values + + +class LlamaForCausalLM(nn.Module): + def __init__( + self, + config: LlamaConfig, + vocab_size_var: tvm.tir.SizeVar, + sep_embed: bool = False, + enable_batching: bool = False, + output_all_logits: bool = False, + ): + model_class = LlamaModelForBatching if enable_batching else LlamaModelForSingleSequence + self.model = model_class(config, vocab_size_var, sep_embed) + self.lm_head = Linear(config.hidden_size, vocab_size_var, dtype=config.dtype, bias=False) + + ############ Rotary embedding constants ############ + assert config.hidden_size % config.num_attention_heads == 0 + head_dim = config.hidden_size // config.num_attention_heads + + # Set the cached sin/cos to the maximum of 2048 and max seq len. + # This will be eliminated further with online rotary embedding calculation. + cache_len = te.var("cached_rotary_embedding_len", "int64") + self.cos_cached = nn.Parameter((cache_len, head_dim), dtype=config.dtype, name="cos_cached") + self.sin_cached = nn.Parameter((cache_len, head_dim), dtype=config.dtype, name="sin_cached") + + # Mark if output_all_logits is True + self.output_all_logits = output_all_logits + ############ End ############ + + def forward( + self, + inputs: relax.Expr, + all_seq_len_shape: Optional[relax.Expr], + past_key_values: relax.Expr, + logit_positions: Optional[relax.Expr] = None, + ): + hidden_states, key_value_cache = self.model( + inputs=inputs, + all_seq_len_shape=all_seq_len_shape, + past_key_values=past_key_values, + ) + + def te_slicing(x: te.Tensor): + assert x.ndim == 3 + return te.compute( + shape=(x.shape[0], 1, x.shape[2]), + fcompute=lambda i, j, k: x[i, x.shape[1] - 1, k], + name="slice", + ) + + if not self.output_all_logits and hidden_states.struct_info.shape[1] != 1: + if logit_positions is None: + hidden_states = nn.emit_te(te_slicing, hidden_states, primfunc_name_hint="slice") + else: + hidden_states = relax.op.take(hidden_states, logit_positions, axis=1) + logits = self.lm_head(hidden_states) + + if logits.struct_info.dtype != "float32": + logits = nn.emit(relax.op.astype(logits, "float32")) + + return logits, key_value_cache + + +def get_param_quant_kind(name: str, param_info: relax.TensorStructInfo) -> ParamQuantKind: + if "embed_tokens" in name: + return ParamQuantKind.embedding_table + elif "lm_head.weight" in name: + return ParamQuantKind.final_fc_weight + elif param_info.ndim == 2 and name.endswith(".weight"): + return ParamQuantKind.linear_weight + else: + return ParamQuantKind.others + + +def create_embed_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: LlamaConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "embed" + + seq_len = tvm.tir.SizeVar("num_tokens_excluding_cache", "int64") + with bb.function(func_name): + model = LlamaEmbedTokensWrapper(config, tvm.tir.SizeVar("vocab_size", "int64")) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + input_ids = nn.Placeholder((1, seq_len), dtype="int32", name="input_ids") + with bb.dataflow(): + inputs_embeds = model(input_ids) + params = [input_ids] + model.parameters() + gv = bb.emit_output(inputs_embeds) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 1)) + + +def create_prefill_func_for_single_seq( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: LlamaConfig, + quant_scheme: QuantizationScheme, + sep_embed: bool = False, +) -> None: + func_name = "prefill_with_embed" if sep_embed else "prefill" + + bsz = 1 + seq_len = tvm.tir.SizeVar("num_tokens_excluding_cache", "int64") + all_seq_len = tvm.tir.SizeVar("num_tokens_including_cache", "int64") + hidden_size = config.hidden_size + with bb.function(func_name): + model = LlamaForCausalLM( + config, tvm.tir.SizeVar("vocab_size", "int64"), sep_embed, enable_batching=False + ) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + inputs = ( + nn.Placeholder((bsz, seq_len, hidden_size), dtype=config.dtype, name="inputs_embeds") + if sep_embed + else nn.Placeholder((bsz, seq_len), dtype="int32", name="input_ids") + ) + all_seq_len_shape = relax.Var("all_seq_len", relax.ShapeStructInfo((all_seq_len,))) + past_key_values = relax.Var( + "kv_cache", + relax.TupleStructInfo( + [relax.ObjectStructInfo() for _ in range(config.num_hidden_layers * 2)] + ), + ) + with bb.dataflow(): + logits, key_value_cache = model( + inputs, all_seq_len_shape, past_key_values=past_key_values + ) + params = [ + inputs, + all_seq_len_shape, + past_key_values, + ] + model.parameters() + gv = bb.emit_output((logits, relax.Tuple(key_value_cache))) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 3)) + + +def create_prefill_func_for_batching( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: LlamaConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "prefill_with_embed" + + bsz = tir.SizeVar("batch_size", "int64") + total_seq_len = tvm.tir.SizeVar("num_tokens_excluding_cache", "int64") + hidden_size = config.hidden_size + with bb.function(func_name): + model = LlamaForCausalLM( + config, tvm.tir.SizeVar("vocab_size", "int64"), sep_embed=True, enable_batching=True + ) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + inputs = nn.Placeholder( + (1, total_seq_len, hidden_size), dtype=config.dtype, name="inputs_embeds" + ) + logit_pos = nn.Placeholder((bsz,), dtype="int32", name="logit_positions") + past_key_values = relax.Var("kv_cache", relax.ObjectStructInfo()) + with bb.dataflow(): + logits, key_value_cache = model( + inputs, + all_seq_len_shape=None, + past_key_values=past_key_values, + logit_positions=logit_pos, + ) + params = [inputs, logit_pos, past_key_values] + model.parameters() + gv = bb.emit_output((logits, key_value_cache)) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 3)) + + +def create_decoding_func_for_single_seq( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: LlamaConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "decode" + + bsz = 1 + all_seq_len = tvm.tir.SizeVar("num_tokens_including_cache", "int64") + + with bb.function(func_name): + model = LlamaForCausalLM(config, tvm.tir.SizeVar("vocab_size", "int64")) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + input_ids = nn.Placeholder((bsz, 1), dtype="int32", name="input_ids") + all_seq_len_shape = relax.Var("all_seq_len", relax.ShapeStructInfo((all_seq_len,))) + past_key_values = relax.Var( + "kv_cache", + relax.TupleStructInfo( + [relax.ObjectStructInfo() for _ in range(config.num_hidden_layers * 2)] + ), + ) + with bb.dataflow(): + logits, key_value_cache = model( + input_ids, all_seq_len_shape, past_key_values=past_key_values + ) + params = [ + input_ids, + all_seq_len_shape, + past_key_values, + ] + model.parameters() + gv = bb.emit_output((logits, relax.Tuple(key_value_cache))) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 3)) + + +def create_decoding_func_for_batching( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: LlamaConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "decode_with_embed" + + bsz = tir.SizeVar("batch_size", "int64") + hidden_size = config.hidden_size + with bb.function(func_name): + model = LlamaForCausalLM( + config, tvm.tir.SizeVar("vocab_size", "int64"), sep_embed=True, enable_batching=True + ) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + inputs = nn.Placeholder((bsz, 1, hidden_size), dtype=config.dtype, name="inputs_embeds") + past_key_values = relax.Var("kv_cache", relax.ObjectStructInfo()) + with bb.dataflow(): + logits, key_value_cache = model( + inputs, all_seq_len_shape=None, past_key_values=past_key_values + ) + params = [inputs, past_key_values] + model.parameters() + gv = bb.emit_output((logits, key_value_cache)) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 2)) + + +def create_verification_func_for_batching( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: LlamaConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "verify_with_embed" + + total_seq_len = tvm.tir.SizeVar("num_tokens_including_cache", "int64") + hidden_size = config.hidden_size + with bb.function(func_name): + model = LlamaForCausalLM( + config, + tvm.tir.SizeVar("vocab_size", "int64"), + sep_embed=True, + enable_batching=True, + output_all_logits=True, + ) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + inputs = nn.Placeholder( + (1, total_seq_len, hidden_size), dtype=config.dtype, name="inputs_embeds" + ) + past_key_values = relax.Var("kv_cache", relax.ObjectStructInfo()) + with bb.dataflow(): + logits, key_value_cache = model( + inputs, + all_seq_len_shape=None, + past_key_values=past_key_values, + ) + params = [inputs, past_key_values] + model.parameters() + gv = bb.emit_output((logits, key_value_cache)) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 2)) + + +def create_kv_cache_func(bb: relax.BlockBuilder, config: LlamaConfig) -> None: + num_key_value_heads = config.get_num_key_value_heads() // config.num_shards + init_shape = relax.ShapeExpr( + ( + config.max_sequence_length, + num_key_value_heads, + config.hidden_size // config.num_attention_heads, # head_dim + ) + ) + with bb.function("create_kv_cache", []): + with bb.dataflow(): + zeros = bb.emit(relax.op.zeros(init_shape, config.dtype)) + caches = [] + f_kv_cache_create = relax.extern("vm.builtin.attention_kv_cache_create") + for _ in range(config.num_hidden_layers * 2): + caches.append( + bb.emit( + relax.call_pure_packed( + f_kv_cache_create, + zeros, + init_shape, + relax.PrimValue(0), + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + ) + gv = bb.emit_output(caches) + bb.emit_func_output(gv) + + +def create_paged_kv_cache_func(bb: relax.BlockBuilder, config: LlamaConfig) -> None: + head_dim = config.hidden_size // config.num_attention_heads + num_qo_heads = config.num_attention_heads // config.num_shards + num_kv_heads = config.get_num_key_value_heads() // config.num_shards + + page_size = tir.SizeVar("page_size", "int64") + total_seq_len = tir.SizeVar("total_seq_len", "int64") + reserved_nseq = tir.SizeVar("reserved_nseq", "int64") + cache_config = relax.Var( + "cache_config", + relax.ShapeStructInfo([reserved_nseq, total_seq_len, page_size]), + ) + + with bb.function("create_kv_cache", [cache_config]): + with bb.dataflow(): + zeros = bb.emit(relax.op.zeros((), config.dtype)) + f_kv_cache_create = relax.extern("vm.builtin.paged_attention_kv_cache_create") + cache = bb.emit_output( + relax.call_pure_packed( + f_kv_cache_create, + args=[ + cache_config, + relax.PrimValue(config.num_hidden_layers), + relax.PrimValue(num_qo_heads), + relax.PrimValue(num_kv_heads), + relax.PrimValue(head_dim), + relax.PrimValue(1), + relax.PrimValue(config.position_embedding_base), + zeros, + bb.get().get_global_var("kv_cache_transpose_append"), + bb.get().get_global_var("attention_prefill"), + bb.get().get_global_var("attention_decode"), + bb.get().get_global_var("attention_prefill_ragged"), + bb.get().get_global_var("attention_prefill_ragged_begin_forward"), + bb.get().get_global_var("attention_prefill_ragged_end_forward"), + bb.get().get_global_var("attention_prefill_begin_forward"), + bb.get().get_global_var("attention_prefill_end_forward"), + bb.get().get_global_var("attention_decode_begin_forward"), + bb.get().get_global_var("attention_decode_end_forward"), + bb.get().get_global_var("attention_rope_in_place"), + bb.get().get_global_var("attention_merge_state"), + bb.get().get_global_var("kv_cache_debug_get_kv"), + ], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + bb.emit_func_output(cache) + + +def create_softmax_func_for_single_seq(bb: relax.BlockBuilder, config: LlamaConfig) -> None: + with bb.function("softmax_with_temperature"): + logits = nn.Placeholder( + (1, 1, tvm.tir.SizeVar("vocab_size", "int64")), dtype="float32", name="logits" + ) + temperature = nn.Placeholder((), dtype="float32", name="temperature") + with bb.dataflow(): + div = bb.emit(relax.op.divide(logits, temperature)) + softmax = bb.emit(relax.op.nn.softmax(div, axis=-1)) + gv = bb.emit_output(softmax) + bb.emit_func_output(gv, [logits, temperature]) + + +def create_softmax_func_for_batching(bb: relax.BlockBuilder, config: LlamaConfig) -> None: + with bb.function("softmax_with_temperature"): + bsz = tvm.tir.SizeVar("batch_size", "int64") + logits = nn.Placeholder( + (bsz, 1, tvm.tir.SizeVar("vocab_size", "int64")), + dtype="float32", + name="logits", + ) + temperature = nn.Placeholder((bsz,), dtype="float32", name="temperature") + with bb.dataflow(): + t_reshaped = bb.emit(relax.op.reshape(temperature, (bsz, 1, 1))) + div = bb.emit(relax.op.divide(logits, t_reshaped)) + softmax = bb.emit(relax.op.nn.softmax(div, axis=-1)) + gv = bb.emit_output(softmax) + bb.emit_func_output(gv, [logits, temperature]) + + +def emit_paged_kv_cache_op(bb: relax.BlockBuilder, config: LlamaConfig) -> None: + from tvm.script import tir as T + + num_kv_heads = config.get_num_key_value_heads() // config.num_shards + head_dim = config.hidden_size // config.num_attention_heads + + @T.prim_func + def kv_cache_transpose_append( + var_pages: T.handle, + var_k_data: T.handle, + var_v_data: T.handle, + var_position_map: T.handle, + ): + ntoken = T.SizeVar("num_tokens_excluding_cache", "int64") + page_size = T.SizeVar("page_size", "int64") + num_pages = T.int64() + + pages = T.match_buffer( + var_pages, (num_pages, 2, num_kv_heads, page_size, head_dim), config.dtype + ) + k_data = T.match_buffer(var_k_data, (ntoken, num_kv_heads, head_dim), config.dtype) + v_data = T.match_buffer(var_v_data, (ntoken, num_kv_heads, head_dim), config.dtype) + position_map = T.match_buffer(var_position_map, (ntoken,), "int32") + + for global_pos, h, f in T.grid(ntoken, num_kv_heads, head_dim): + with T.block("k_transpose_append"): + vgpos, vh, vf = T.axis.remap("SSS", [global_pos, h, f]) + position: T.int64 = T.Cast("int64", position_map[vgpos]) + pages[ + T.floordiv(position, page_size), 0, vh, T.floormod(position, page_size), vf + ] = k_data[vgpos, vh, vf] + with T.block("v_transpose_append"): + vgpos, vh, vf = T.axis.remap("SSS", [global_pos, h, f]) + position: T.int64 = T.Cast("int64", position_map[vgpos]) + pages[ + T.floordiv(position, page_size), 1, vh, T.floormod(position, page_size), vf + ] = v_data[vgpos, vh, vf] + + @T.prim_func + def kv_cache_debug_get_kv( + var_pages: T.handle, + var_position_map: T.handle, + var_k_data: T.handle, + var_v_data: T.handle, + layer_id: T.int64, + ): + seqlen = T.SizeVar("seqlen", "int64") + page_size = T.SizeVar("page_size", "int64") + num_pages = T.int64() + + pages = T.match_buffer( + var_pages, (num_pages, 2, num_kv_heads, page_size, head_dim), config.dtype + ) + position_map = T.match_buffer(var_position_map, (seqlen,), "int32") + k_data = T.match_buffer( + var_k_data, (config.num_hidden_layers, seqlen, num_kv_heads, head_dim), config.dtype + ) + v_data = T.match_buffer( + var_v_data, (config.num_hidden_layers, seqlen, num_kv_heads, head_dim), config.dtype + ) + + for p, h, d in T.grid(seqlen, num_kv_heads, head_dim): + with T.block("copy0"): + vp, vh, vd = T.axis.remap("SSS", [p, h, d]) + position: T.int64 = T.Cast("int64", position_map[vp]) + k_data[layer_id, vp, vh, vd] = pages[ + T.floordiv(position, page_size), 0, vh, T.floormod(position, page_size), vd + ] + v_data[layer_id, vp, vh, vd] = pages[ + T.floordiv(position, page_size), 1, vh, T.floormod(position, page_size), vd + ] + + bb.add_func(kv_cache_transpose_append, "kv_cache_transpose_append") + bb.add_func(kv_cache_debug_get_kv, "kv_cache_debug_get_kv") + bb.add_func(relax.extern("paged_kv_cache.attention_kernel_prefill"), "attention_prefill") + bb.add_func(relax.extern("paged_kv_cache.attention_kernel_decode"), "attention_decode") + bb.add_func( + relax.extern("flashinfer.attention_kernel_prefill_with_ragged_kv_cache"), + "attention_prefill_ragged", + ) + bb.add_func( + relax.extern("paged_kv_cache.attention_kernel_prefill_begin_forward"), + "attention_prefill_begin_forward", + ) + bb.add_func( + relax.extern("paged_kv_cache.attention_kernel_prefill_end_forward"), + "attention_prefill_end_forward", + ) + bb.add_func( + relax.extern("paged_kv_cache.attention_kernel_decode_begin_forward"), + "attention_decode_begin_forward", + ) + bb.add_func( + relax.extern("paged_kv_cache.attention_kernel_decode_end_forward"), + "attention_decode_end_forward", + ) + bb.add_func( + relax.extern("flashinfer.attention_kernel_prefill_with_ragged_kv_cache_begin_forward"), + "attention_prefill_ragged_begin_forward", + ) + bb.add_func( + relax.extern("flashinfer.attention_kernel_prefill_with_ragged_kv_cache_end_forward"), + "attention_prefill_ragged_end_forward", + ) + bb.add_func( + relax.extern("flashinfer.merge_state_in_place"), + "attention_merge_state", + ) + bb.add_func( + relax.extern("flashinfer.batch_qk_apply_rotary_in_place"), + "attention_rope_in_place", + ) + + +def setup_params(mod, param_manager, dtype, config, args): + def f_convert_pname_fwd(pname: str) -> List[str]: + if not config.combine_matmul: + return [pname] + + qkv_str = "query_key_value_proj" + gate_up_str = "gate_up_proj" + if qkv_str in pname: + return [ + pname.replace(qkv_str, "q_proj"), + pname.replace(qkv_str, "k_proj"), + pname.replace(qkv_str, "v_proj"), + ] + elif gate_up_str in pname: + return [ + pname.replace(gate_up_str, "gate_proj"), + pname.replace(gate_up_str, "up_proj"), + ] + else: + return [pname] + + def f_convert_param_bkwd(torch_pname: str, torch_param): + if not config.combine_matmul: + return [(torch_pname, torch_param.astype(dtype))] + + combined_layers = ["q_proj", "k_proj", "v_proj", "gate_proj", "up_proj"] + if any([name in torch_pname for name in combined_layers]): + return None + return [(torch_pname, torch_param.astype(dtype))] + + def f_compute_relax_param(relax_pname: str, torch_params: List[Any]): + # Expected to enter this function only for the combined linear matmul weights. + # Other weights are supposed to be loaded in `f_convert_param_bkwd` since + # each other relax param has a unique corresponding torch param. + if not config.combine_matmul: + # When matmul combination is not turned on, each relax param has a unique + # corresponding torch param, and this function is not expected to be entered. + raise NotImplementedError( + "Matmul combination is not turned on, and the function " + "is not expected to be entered" + ) + hidden_size = config.hidden_size + head_dim = config.hidden_size // config.num_attention_heads + + if "query_key_value_proj" in relax_pname: + q_heads = config.num_attention_heads + kv_heads = config.get_num_key_value_heads() + q, k, v = torch_params + assert q.shape == (q_heads * head_dim, hidden_size) + assert k.shape == (kv_heads * head_dim, hidden_size) + assert v.shape == (kv_heads * head_dim, hidden_size) + qkv = np.concatenate([q, k, v], axis=0).astype(dtype) + return qkv + if "gate_up_proj" in relax_pname: + gate, up = torch_params + gate_up = np.concatenate([gate, up], axis=0).astype(dtype) + return gate_up + raise ValueError("Unexpected param loading") + + param_manager.set_param_loading_func( + args.model_path, + args.use_safetensors, + f_convert_pname_fwd, + f_convert_param_bkwd, + f_compute_relax_param, + ) + + device = tvm.cpu() + param_list = [None] * param_manager.nparam_to_load + + head_dim = config.hidden_size / config.num_attention_heads + inv_freq = 1.0 / ( + config.position_embedding_base ** (np.arange(0, head_dim, 2).astype("float32") / head_dim) + ) + + # The following cos/sin values can be removed but **are kept for compatibility issues**. + t = np.arange(2048, dtype=inv_freq.dtype) + freqs = np.einsum("i,j->ij", t, inv_freq) + emb = np.concatenate((freqs, freqs), axis=-1) + param_list[-2] = tvm.nd.array(np.cos(emb).astype(config.dtype), device) + param_list[-1] = tvm.nd.array(np.sin(emb).astype(config.dtype), device) + + return mod, param_manager, param_list, config + + +def get_model(args, hf_config): + model_name = args.model + dtype = args.quantization.model_dtype + enable_batching = args.enable_batching + sep_embed = args.sep_embed + + if enable_batching and not sep_embed: + raise ValueError("`sep_embed` is required when batching is enabled.") + + position_embedding_base = 10000 + + if "rope_theta" in hf_config: + position_embedding_base = hf_config["rope_theta"] + + # Llama-2 variants use `max_position_embeddings` to encode maximum sequence length in their hf model cards, + # while Llama-1 variants use `max_sequence_length`. + # Thus, use `max_sequence_length` if defined. Otherwise, use `max_position_embeddings`. + # If none of them is defined, throw an error. + if "max_sequence_length" in hf_config: + config = LlamaConfig( + **hf_config, + dtype=dtype, + position_embedding_base=position_embedding_base, + combine_matmul=True, + num_shards=args.num_shards, + build_model_only=args.build_model_only, + target_kind=args.target_kind, + ) + elif "max_position_embeddings" in hf_config: + config = LlamaConfig( + **hf_config, + dtype=dtype, + max_sequence_length=hf_config["max_position_embeddings"], + position_embedding_base=position_embedding_base, + combine_matmul=True, + num_shards=args.num_shards, + build_model_only=args.build_model_only, + target_kind=args.target_kind, + ) + else: + raise Exception( + "The model config should contain information about maximum sequence length." + ) + + # If there is a user-provided maximum sequence length, override hf config. + if args.max_seq_len != -1: + config.max_sequence_length = args.max_seq_len + + param_manager = ParamManager() + bb = relax.BlockBuilder() + + if sep_embed: + create_embed_func(bb, param_manager, config, args.quantization) + + if enable_batching: + emit_paged_kv_cache_op(bb, config) + create_prefill_func_for_batching(bb, param_manager, config, args.quantization) + create_decoding_func_for_batching(bb, param_manager, config, args.quantization) + create_verification_func_for_batching(bb, param_manager, config, args.quantization) + create_paged_kv_cache_func(bb, config) + create_softmax_func_for_batching(bb, config) + else: + create_prefill_func_for_single_seq(bb, param_manager, config, args.quantization, sep_embed) + create_decoding_func_for_single_seq(bb, param_manager, config, args.quantization) + create_kv_cache_func(bb, config) + create_softmax_func_for_single_seq(bb, config) + + create_metadata_func( + bb, + model_name=model_name, + max_window_size=config.max_sequence_length, + stop_tokens=[2], + add_prefix_space=False, + prefill_chunk_size=args.prefill_chunk_size, + ) + + mod = bb.get() + + tir_bound_map = dict() + tir_bound_map["num_tokens_without_cache"] = ( + args.prefill_chunk_size if args.prefill_chunk_size > 0 else config.max_sequence_length + ) + tir_bound_map["num_tokens_with_cache"] = config.max_sequence_length + tir_bound_map["vocab_size"] = args.max_vocab_size + if enable_batching: + tir_bound_map["nseq"] = args.max_batch_size + for gv in mod.functions: + func = mod[gv] + if isinstance(func, relax.Function): + mod[gv] = func.with_attr("tir_var_upper_bound", tir_bound_map) + + if args.build_model_only: + return mod, param_manager, None, config + + return setup_params(mod, param_manager, dtype, config, args) diff --git a/mlc_llm/relax_model/llama_batched_vllm.py b/mlc_llm/relax_model/llama_batched_vllm.py new file mode 100644 index 0000000..365500b --- /dev/null +++ b/mlc_llm/relax_model/llama_batched_vllm.py @@ -0,0 +1,658 @@ +from typing import Optional, Tuple + +import numpy as np +import tvm +from tvm import relax, te +from tvm.relax.op import ccl, reshape, expand_dims, concat, zeros, repeat, take +from tvm.relax.op.nn import attention_var_len +from tvm.relax.testing import nn +from tvm.ir import VDevice +from tvm.script import relax as R +from tvm.script.ir_builder import tir as T + +from ..quantization import QuantizationScheme +from .modules import ModuleList +from .param_manager import ParamManager +from .llama import ( + LlamaConfig, + Linear, + Embedding, + LlamaRMSNorm, + LlamaAttentionBase, + LlamaDecoderLayer, + get_param_quant_kind, + setup_params, + rotary_modulate_by_freq, +) + + +def apply_rotary_pos_emb(q, k, positions, position_embedding_base): + def f_rotary_embedding(tensor, pos_tensor): + def rotary_compute(*idx): + pos = pos_tensor[idx[0]].astype("float32") + return rotary_modulate_by_freq( + tensor, + idx, + pos, + position_embedding_base, + ) + + return tvm.te.compute(tensor.shape, rotary_compute, name="rotary") + + q_embed = nn.emit_te(f_rotary_embedding, q, positions, primfunc_name_hint="rotary_embedding") + k_embed = nn.emit_te(f_rotary_embedding, k, positions, primfunc_name_hint="rotary_embedding") + return q_embed, k_embed + + +class LlamaAttentionBatched(LlamaAttentionBase): + def __init__(self, config: LlamaConfig, head_mapping: relax.Constant): + super().__init__(config) + self.head_mapping = head_mapping # (num_heads,), used by vLLM for multi-query attention + self.sliding_window = None + + if config.sliding_window: + self.sliding_window = T.IntImm("int32", config.sliding_window) + + def forward( + self, + hidden_states: relax.Expr, # (num_token, hidden_size) + positions: relax.Expr, # (num_token,), for batched RoPE + seq_lens: relax.Expr, # (num_seq,) + kv_cache: Optional[Tuple[relax.Expr, relax.Expr]], + slot_mapping: Optional[relax.Expr], # (num_token,) + max_seqlen: Optional[relax.Expr], # (), must be on CPU + seqstart: Optional[relax.Expr], # (num_seq + 1,), for prefill + block_tables: Optional[relax.Expr], # (num_seq, max_num_blocks_per_seq), for decode + indices_within_window: Optional[ + relax.Expr + ], # (num_cached_total,), for prefill with sliding-window attention + ): + num_tokens, _ = hidden_states.struct_info.shape + + queries, keys, values = self.project_qkv( + hidden_states, + (num_tokens, self.num_query_heads, self.head_dim), + (num_tokens, self.num_key_value_heads, self.head_dim), + ) + + queries, keys = apply_rotary_pos_emb(queries, keys, positions, self.position_embedding_base) + + if kv_cache: + # Paged KV cache update + k_cache, v_cache = kv_cache + + if self.sliding_window is None or block_tables: + # For decode or prefill without sliding window, cache all keys / values. + keys_to_cache = keys + values_to_cache = values + else: + # Cache only the most recent keys and values within the window. + keys_to_cache = nn.emit(take(keys, indices_within_window, axis=0)) + values_to_cache = nn.emit(take(values, indices_within_window, axis=0)) + slot_mapping = nn.emit(take(slot_mapping, indices_within_window, axis=0)) + + # kv caches are updated inplace, takes ownership of the arguments + kv = nn.emit( + relax.op.call_inplace_packed( + "tvm.contrib.vllm.reshape_and_cache", + args=[keys_to_cache, values_to_cache, k_cache, v_cache, slot_mapping], + inplace_indices=[2, 3], + sinfo_args=[k_cache.struct_info, v_cache.struct_info], + ) + ) + + k_cache, v_cache = kv[0], kv[1] + else: + k_cache = v_cache = None + + if seqstart: + # Prefill, batched attention over variable sequence lengths + attn_output = nn.emit( + attention_var_len( + nn.emit(expand_dims(queries, axis=0)), + nn.emit(expand_dims(keys, axis=0)), + nn.emit(expand_dims(values, axis=0)), + seqstart_q=seqstart, + max_seqlen_q=max_seqlen, + causal_mask="BottomRight", + window_size=self.sliding_window, + ) + ) + else: + # Decode, using vLLM kernel + attn_output = nn.emit( + relax.op.call_dps_packed( + "tvm.contrib.vllm.single_query_cached_kv_attention", + [ + queries, + k_cache, + v_cache, + self.head_mapping, + block_tables, + seq_lens, + 16, # block_size + max_seqlen, + ], + out_sinfo=queries.struct_info, + ) + ) + + attn_output = nn.emit( + reshape(attn_output, (num_tokens, self.num_query_heads * self.head_dim)) + ) + attn_output = self.o_proj(attn_output) + + return attn_output, (k_cache, v_cache) + + +class LlamaDecoderLayerBatched(LlamaDecoderLayer): + def __init__(self, config: LlamaConfig, head_mapping: relax.Constant): + super().__init__(config, False) + self.self_attn = LlamaAttentionBatched(config, head_mapping) + + def forward( + self, + hidden_states: relax.Expr, + positions: relax.Expr, + seq_lens: relax.Expr, + kv_cache: Optional[Tuple[relax.Expr, relax.Expr]], + slot_mapping: Optional[relax.Expr], + max_seqlen: Optional[relax.Expr], + seqstart: Optional[relax.Expr], + block_tables: Optional[relax.Expr], + indices_within_window: Optional[relax.Expr], + ) -> Tuple[relax.Expr, Optional[Tuple[relax.Expr, relax.Expr]]]: + residual = hidden_states + + hidden_states = self.input_layernorm(hidden_states) + + # Self Attention + hidden_states, new_kv = self.self_attn( + hidden_states=hidden_states, + positions=positions, + seq_lens=seq_lens, + kv_cache=kv_cache, + slot_mapping=slot_mapping, + max_seqlen=max_seqlen, + seqstart=seqstart, + block_tables=block_tables, + indices_within_window=indices_within_window, + ) + + hidden_states = self.post_self_attn(hidden_states, residual) + + return hidden_states, new_kv + + +class LlamaModel(nn.Module): + def __init__( + self, + config: LlamaConfig, + cpu_device: VDevice, + vocab_size_var: tvm.tir.SizeVar, + sep_embed: bool = False, + ): + self.padding_idx = config.pad_token_id + self.embed_tokens = None + + num_query_heads = config.num_attention_heads // config.num_shards + num_key_value_heads = config.get_num_key_value_heads() // config.num_shards + num_queries_per_kv = num_query_heads // num_key_value_heads + head_mapping = relax.const( + tvm.nd.array( + np.repeat(np.arange(num_key_value_heads, dtype="int32"), num_queries_per_kv) + ) + ) + + if not sep_embed: + self.embed_tokens = Embedding(vocab_size_var, config.hidden_size, dtype=config.dtype) + + self.layers = ModuleList( + [ + LlamaDecoderLayerBatched(config, head_mapping) + for _ in range(config.num_hidden_layers) + ] + ) + self.norm = LlamaRMSNorm(config.hidden_size, dtype=config.dtype, eps=config.rms_norm_eps) + + self.cpu_device = cpu_device + + def forward( + self, + inputs: relax.Expr, + positions: relax.Expr, + seq_lens: relax.Expr, + kv_caches: Optional[relax.Expr], + slot_mapping: Optional[relax.Expr], + seqstart: Optional[relax.Expr], + block_tables: Optional[relax.Expr], + indices_within_window: Optional[relax.Expr], + ): + if self.embed_tokens: + inputs_embeds = self.embed_tokens(inputs) + else: + inputs_embeds = inputs + + hidden_states = inputs_embeds + + # max_seqlen needs to be on CPU, so that vLLM and Flash Attention can directly get the + # integer length by max_seqlen->data[0]. Otherwise, we need to repeatedly do cudaMemcpy + # of a single int32. + max_seqlen = R.to_vdevice(R.max(seq_lens), self.cpu_device) + + new_kvs = () + + for idx, decoder_layer in enumerate(self.layers): + if kv_caches: + cache = (kv_caches[2 * idx], kv_caches[2 * idx + 1]) + else: + cache = None + + hidden_states, new_kv = decoder_layer( + hidden_states, + positions, + seq_lens, + cache, + slot_mapping, + max_seqlen, + seqstart, + block_tables, + indices_within_window, + ) + new_kvs += new_kv + + return self.norm(hidden_states), new_kvs + + +class LlamaForCausalLM(nn.Module): + def __init__( + self, + config: LlamaConfig, + cpu_device: VDevice, + vocab_size_var: tvm.tir.SizeVar, + sep_embed: bool = False, + ): + self.num_shards = config.num_shards + self.model = LlamaModel(config, cpu_device, vocab_size_var, sep_embed) + self.lm_head = Linear(config.hidden_size, vocab_size_var, dtype=config.dtype, bias=False) + + ############ Rotary embedding constants ############ + assert config.hidden_size % config.num_attention_heads == 0 + head_dim = config.hidden_size // config.num_attention_heads + + # Set the cached sin/cos to the maximum of 2048 and max seq len. + # This will be eliminated further with online rotary embedding calculation. + cache_len = te.var("cached_rotary_embedding_len", "int64") + self.cos_cached = nn.Parameter((cache_len, head_dim), dtype=config.dtype, name="cos_cached") + self.sin_cached = nn.Parameter((cache_len, head_dim), dtype=config.dtype, name="sin_cached") + ############ End ############ + + def forward( + self, + input_ids: relax.Expr, # (num_token,) + positions: relax.Expr, # (num_token,), for batched RoPE + seq_lens: relax.Expr, # (num_seq,) + kv_caches: Optional[relax.Expr], # For prefill and decode, not needed for evaluate + slot_mapping: Optional[ + relax.Expr + ], # (num_token,), for prefill and decode, not needed for evaluate + block_tables: Optional[relax.Expr], # (num_seq, max_num_blocks_per_seq), for decode + indices_within_window: Optional[ + relax.Expr + ], # (num_cached_total,), for prefill with sliding-window attention + ): + """ + In vLLM, the paged KV cache is simply a pair of tensors, one for keys and the other + for values. The tensor has shape (num_blocks, num_kv_heads, head_size, block_size). + (In practice, the key cache has a slightly different shape for an efficiency reason, + but that's not important.) + + The mapping between sequences / tokens to blocks is specified by two inputs. + - block_tables: A list of block IDs allocated for the sequence. + - slot_mapping: A linear index into the 2D grid (num_blocks, block_size), for each token. + + Support for sliding-window attention is realized by making a block table a circular buffer. + So the length of a block table for each sequence is at most ceil(window_size / block_size). + + With sliding window, not all past K / V values need to be cached during prefill. + The last input, indices_within_window, tells which tokens among (num_token,) need to have + their K / V values cached. + """ + if self.num_shards > 1: + input_ids = nn.emit(ccl.broadcast_from_worker0(input_ids)) + positions = nn.emit(ccl.broadcast_from_worker0(positions)) + seq_lens = nn.emit(ccl.broadcast_from_worker0(seq_lens)) + + if slot_mapping: + slot_mapping = nn.emit(ccl.broadcast_from_worker0(slot_mapping)) + + if block_tables: + block_tables = nn.emit(ccl.broadcast_from_worker0(block_tables)) + + if indices_within_window: + indices_within_window = nn.emit(ccl.broadcast_from_worker0(indices_within_window)) + + is_prompt = block_tables is None + + if is_prompt: # prefill and evaluate + # https://github.com/apache/tvm/issues/15851 for why we need to use Thrust + cumsum = nn.emit( + relax.op.call_dps_packed( + "tvm.contrib.thrust.sum_scan", seq_lens, out_sinfo=seq_lens.struct_info + ) + ) + seqstart = nn.emit(concat([zeros((1,), "int32"), cumsum])) + else: + seqstart = None + + hidden_states, new_kvs = self.model( + input_ids, + positions, + seq_lens, + kv_caches, + slot_mapping, + seqstart, + block_tables, + indices_within_window, + ) + + if is_prompt: + # Extract logits for the last token in each sequence + + def get_logits_last_tokens(x, seq_len_tensor, seqstart): + return te.compute( + shape=(seq_len_tensor.shape[0], x.shape[-1]), + fcompute=lambda i, j: x[seqstart[i] + seq_len_tensor[i] - 1, j], + name="get_logits_last_tokens", + ) + + logits = self.lm_head( + nn.emit_te( + get_logits_last_tokens, + hidden_states, + seq_lens, + seqstart, + primfunc_name_hint="get_logits_last_tokens", + ) + ) + else: + logits = self.lm_head(hidden_states) + + if logits.struct_info.dtype != "float32": + logits = nn.emit(relax.op.astype(logits, "float32")) + + return logits, new_kvs + + +def get_inputs( + num_token, num_seq, config, max_num_blocks_per_seq=None, sep_embed=False, need_cache=True +): + hidden_size = config.hidden_size + + inputs = ( + nn.Placeholder((num_token, hidden_size), dtype=config.dtype, name="inputs_embeds") + if sep_embed + else nn.Placeholder((num_token,), dtype="int32", name="input_ids") + ) + + seq_lens = nn.Placeholder((num_seq,), dtype="int32", name="seq_lens") + positions = nn.Placeholder((num_token,), dtype="int32", name="positions") + + if need_cache: + num_blocks = tvm.tir.SizeVar("num_blocks", "int64") + block_size = 16 + + vec_size = 8 # 128 bit, fp16 x 8 + num_key_value_heads = config.get_num_key_value_heads() // config.num_shards + head_size = hidden_size // config.num_attention_heads + + k_cache_shape = ( + num_blocks, + num_key_value_heads, + head_size // vec_size, + block_size, + vec_size, + ) + v_cache_shape = (num_blocks, num_key_value_heads, head_size, block_size) + + get_cache_sinfo = lambda i: relax.TensorStructInfo( + k_cache_shape if i % 2 == 0 else v_cache_shape, dtype="float16" + ) + + past_key_values = relax.Var( + "kv_cache", + relax.TupleStructInfo( + [get_cache_sinfo(i) for i in range(config.num_hidden_layers * 2)] + ), + ) + slot_mapping = nn.Placeholder((num_token,), dtype="int32", name="slot_mapping") + else: + past_key_values = None + slot_mapping = None + block_tables = None + + if max_num_blocks_per_seq is None: + block_tables = None + else: + block_tables = nn.Placeholder( + (num_seq, max_num_blocks_per_seq), dtype="int32", name="block_tables" + ) + + return inputs, positions, seq_lens, past_key_values, slot_mapping, block_tables + + +def create_evaluate_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: LlamaConfig, + cpu_dev: VDevice, + quant_scheme: QuantizationScheme, + sep_embed: bool = False, +) -> None: + """Evaluate logits for the last token in each sequence. Same as prefill but without KV cache.""" + func_name = "evaluate" + + num_token = tvm.tir.SizeVar("num_tokens_excluding_cache", "int64") + num_seq = tvm.tir.SizeVar("batch_size", "int64") + + with bb.function(func_name): + model = LlamaForCausalLM(config, cpu_dev, tvm.tir.SizeVar("vocab_size", "int64"), sep_embed) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + inputs, positions, seq_lens, _, _, _ = get_inputs( + num_token, num_seq, config, sep_embed=sep_embed + ) + + with bb.dataflow(): + logits, _ = model( + inputs, + positions, + seq_lens, + kv_caches=None, + slot_mapping=None, + block_tables=None, + indices_within_window=None, + ) + params = [ + inputs, + positions, + seq_lens, + ] + model.parameters() + gv = bb.emit_output(logits) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 3)) + + +def create_encoding_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: LlamaConfig, + cpu_dev: VDevice, + quant_scheme: QuantizationScheme, + sep_embed: bool = False, +) -> None: + """Batched prefill with vLLM paged KV cache. + + The batched attention op is intended to be offloaded to CUTLASS or Flash Attention + via BYOC. + """ + func_name = "prefill_with_embed" if sep_embed else "prefill" + + num_token = tvm.tir.SizeVar("num_tokens_excluding_cache", "int64") + num_seq = tvm.tir.SizeVar("batch_size", "int64") + + num_inputs = 5 + + with bb.function(func_name): + model = LlamaForCausalLM(config, cpu_dev, tvm.tir.SizeVar("vocab_size", "int64"), sep_embed) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + input_ids, positions, seq_lens, past_key_values, slot_mapping, _ = get_inputs( + num_token, num_seq, config, sep_embed=sep_embed + ) + + with bb.dataflow(): + params = [ + input_ids, + positions, + seq_lens, + past_key_values, + slot_mapping, + ] + + inputs = [ + input_ids, + positions, + seq_lens, + past_key_values, + slot_mapping, + None, # block_tables + ] + + if config.sliding_window: + num_inputs += 1 + # The value of num_cached_total is between + # num_token (if seq_len < sliding_window for all seq) and + # num_seq * config.sliding_window (if seq_len > sliding_window for all seq) + num_cached_total = tvm.tir.SizeVar("num_cached_total", "int64") + indices_within_window = nn.Placeholder( + (num_cached_total,), dtype="int32", name="indices_within_window" + ) + inputs.append(indices_within_window) + params.append(indices_within_window) + else: + inputs.append(None) + + logits, new_kvs = model(*inputs) + gv = bb.emit_output((logits, relax.Tuple(new_kvs))) + + bb.emit_func_output(gv, params + model.parameters()) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", num_inputs)) + + +def create_decoding_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: LlamaConfig, + cpu_dev: VDevice, + quant_scheme: QuantizationScheme, +) -> None: + """Batched decoding with vLLM paged KV cache.""" + func_name = "decode" + + num_seq = tvm.tir.SizeVar("batch_size", "int64") + max_num_blocks_per_seq = tvm.tir.SizeVar("max_num_blocks_per_seq", "int64") + + with bb.function(func_name): + inputs, positions, seq_lens, past_key_values, slot_mapping, block_tables = get_inputs( + num_seq, num_seq, config, max_num_blocks_per_seq + ) + + with bb.dataflow(): + model = LlamaForCausalLM(config, cpu_dev, tvm.tir.SizeVar("vocab_size", "int64")) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + logits, new_kvs = model( + inputs, positions, seq_lens, past_key_values, slot_mapping, block_tables, None + ) + params = [ + inputs, + positions, + seq_lens, + past_key_values, + slot_mapping, + block_tables, + ] + model.parameters() + gv = bb.emit_output((logits, relax.Tuple(new_kvs))) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 6)) + + +def get_model(args, hf_config): + dtype = args.quantization.model_dtype + sep_embed = False + + position_embedding_base = 10000 + + if "rope_theta" in hf_config: + position_embedding_base = hf_config["rope_theta"] + + # Llama-2 variants use `max_position_embeddings` to encode maximum sequence length in their hf model cards, + # while Llama-1 variants use `max_sequence_length`. + # Thus, use `max_sequence_length` if defined. Otherwise, use `max_position_embeddings`. + # If none of them is defined, throw an error. + if "max_sequence_length" in hf_config: + config = LlamaConfig( + **hf_config, + dtype=dtype, + position_embedding_base=position_embedding_base, + combine_matmul=True, + num_shards=args.num_shards, + build_model_only=args.build_model_only, + ) + elif "max_position_embeddings" in hf_config: + config = LlamaConfig( + **hf_config, + dtype=dtype, + max_sequence_length=hf_config["max_position_embeddings"], + position_embedding_base=position_embedding_base, + combine_matmul=True, + num_shards=args.num_shards, + build_model_only=args.build_model_only, + ) + else: + raise Exception( + "The model config should contain information about maximum sequence length." + ) + + # If there is a user-provided maximum sequence length, override hf config. + if args.max_seq_len != -1: + config.max_sequence_length = args.max_seq_len + + param_manager = ParamManager() + bb = relax.BlockBuilder() + + # The CPU device to copy the result of relax.op.max(seq_lens) to CPU. + cpu_dev = VDevice("llvm", 0, "global") + + create_evaluate_func(bb, param_manager, config, cpu_dev, args.quantization, sep_embed) + create_encoding_func(bb, param_manager, config, cpu_dev, args.quantization, sep_embed) + create_decoding_func(bb, param_manager, config, cpu_dev, args.quantization) + + mod = bb.get() + + mod.update_global_info("vdevice", [cpu_dev]) + + if args.build_model_only: + return mod, param_manager, None, config + + return setup_params(mod, param_manager, dtype, config, args) diff --git a/mlc_llm/relax_model/minigpt.py b/mlc_llm/relax_model/minigpt.py new file mode 100644 index 0000000..96126bb --- /dev/null +++ b/mlc_llm/relax_model/minigpt.py @@ -0,0 +1,627 @@ +import math +import os +from dataclasses import dataclass + +import torch +import tvm +from tvm import relax +from tvm.relax.testing import nn + + +from ..quantization import ParamQuantKind, QuantizationScheme +from .modules import ModuleList, TransformImage +from .param_manager import ParamManager + + +@dataclass +class MiniGPTConfig: + dtype: str = "float16" + in_chan: int = 4 # represent rgba + image_size: int = 224 + num_query_token: int = 32 + max_txt_len: int = 160 + vocab_size: int = 32000 + patch_size: int = 14 + word_embed: int = 768 + visual_encoder_embed_dim: int = 1408 + visual_encoder_attn_heads: int = 16 + visual_encoder_attn_hidden_dim: int = 257 + visual_encoder_fc_hidden_dim: int = 6144 + visual_encoder_num_blocks: int = 39 + bert_hidden_layers: int = 12 + bert_num_attn_heads: int = 12 + bert_attn_head_size: int = 64 + bert_interm_query: int = 3072 + llama_proj_size: int = 4096 + + +MODEL_CONFIG = { + "minigpt4-7b": {}, +} + + +class MiniGPTPatchEmbed(nn.Module): + def __init__( + self, image_size, patch_size, embed_dim, dtype: str, in_chans=3, bias=True + ): + self.strides = (patch_size, patch_size) + self.embed_dim = embed_dim + self.out_shape = image_size // patch_size + + bs = 1 + self.cls_token = nn.Parameter((bs, 1, embed_dim), dtype=dtype, name="cls_token") + self.pos_embed = nn.Parameter( + (1, self.out_shape * self.out_shape + 1, embed_dim), + dtype=dtype, + name="pos_embed", + ) + self.weight = nn.Parameter( + (embed_dim, in_chans, patch_size, patch_size), + dtype=dtype, + name="patch_embed_weight", + ) + if bias: + self.bias = nn.Parameter((embed_dim,), dtype=dtype, name="patch_embed_bias") + else: + self.bias = None + + def forward(self, input: relax.Expr) -> relax.Var: + bs = 1 + x = nn.emit(relax.op.nn.conv2d(input, self.weight, self.strides)) + if self.bias: + bias = relax.op.reshape(self.bias, [1, self.embed_dim, 1, 1]) + x = relax.op.add(x, bias) + x = relax.op.reshape(x, (bs, self.embed_dim, self.out_shape * self.out_shape)) + x = relax.op.permute_dims(x, [0, 2, 1]) + # concatenate with cls_tokens + x_concat = relax.op.concat([self.cls_token, x], axis=1) + # add with pos_embed + res = relax.op.add(x_concat, self.pos_embed) + return res + + +class MiniGPTVisualEncoderAttention(nn.Module): + def __init__(self, config: MiniGPTConfig): + self.embed_dim = config.visual_encoder_embed_dim + self.num_heads = config.visual_encoder_attn_heads + self.head_dim = self.embed_dim // self.num_heads + self.scale = self.head_dim ** (-0.5) + self.dtype = config.dtype + self.N = config.visual_encoder_attn_hidden_dim + + self.q_bias = nn.Parameter((self.embed_dim,), dtype=self.dtype, name="q_bias") + self.v_bias = nn.Parameter((self.embed_dim,), dtype=self.dtype, name="v_bias") + self.qkv_weight = nn.Parameter( + (self.embed_dim * 3, self.embed_dim), dtype=self.dtype, name="qkv_weight" + ) + self.proj_weight = nn.Parameter( + (self.embed_dim, self.embed_dim), dtype=self.dtype, name="proj_weight" + ) + self.proj_bias = nn.Parameter( + (self.embed_dim,), dtype=self.dtype, name="proj_bias" + ) + + def forward(self, input: relax.Expr): + from tvm.relax.op import ( + concat, + linear, + matmul, + permute_dims, + reshape, + squeeze, + strided_slice, + zeros, + ) + + bs = 1 + k_bias = zeros((self.embed_dim,), self.dtype) + qkv_bias = concat([self.q_bias, k_bias, self.v_bias], axis=0) + x = linear(input, self.qkv_weight, qkv_bias) + x = reshape(x, (bs, self.N, 3, self.num_heads, self.head_dim)) + x = permute_dims(x, [2, 0, 3, 1, 4]) + q = squeeze(strided_slice(x, axes=[0], begin=[0], end=[1]), [0]) + k = squeeze(strided_slice(x, axes=[0], begin=[1], end=[2]), [0]) + v = squeeze(strided_slice(x, axes=[0], begin=[2], end=[3]), [0]) + q = q * relax.const(self.scale, self.dtype) + attn = matmul(q, permute_dims(k, [0, 1, 3, 2])) + attn = relax.op.nn.softmax(attn, -1) + res = permute_dims(matmul(attn, v), [0, 2, 1, 3]) + res = reshape(res, (bs, self.N, self.embed_dim)) + res = linear(res, self.proj_weight, self.proj_bias) + return res + + +class MiniGPTMLP(nn.Module): + def __init__(self, config: MiniGPTConfig): + self.hidden_dim = config.visual_encoder_fc_hidden_dim + self.embed_dim = config.visual_encoder_embed_dim + self.dtype = config.dtype + + self.fc1_weight = nn.Parameter( + (self.hidden_dim, self.embed_dim), dtype=self.dtype, name="fc1_weight" + ) + self.fc1_bias = nn.Parameter( + (self.hidden_dim,), dtype=self.dtype, name="fc1_bias" + ) + self.fc2_weight = nn.Parameter( + (self.embed_dim, self.hidden_dim), dtype=self.dtype, name="fc2_weight" + ) + self.fc2_bias = nn.Parameter( + (self.embed_dim,), dtype=self.dtype, name="fc2_bias" + ) + + def forward(self, input: relax.Expr): + res = relax.op.linear(input, self.fc1_weight, self.fc1_bias) + res = relax.op.nn.gelu(res) + res = relax.op.linear(res, self.fc2_weight, self.fc2_bias) + return res + + +class MiniGPTVisualEncoderBlock(nn.Module): + def __init__(self, config: MiniGPTConfig): + embed_dim = config.visual_encoder_embed_dim + dtype = config.dtype + self.norm1_weight = nn.Parameter((embed_dim,), dtype=dtype, name="norm1_weight") + self.norm1_bias = nn.Parameter((embed_dim,), dtype=dtype, name="norm1_bias") + self.attn = MiniGPTVisualEncoderAttention(config) + self.norm2_weight = nn.Parameter((embed_dim,), dtype=dtype, name="norm2_weight") + self.norm2_bias = nn.Parameter((embed_dim,), dtype=dtype, name="norm2_bias") + self.mlp = MiniGPTMLP(config) + + def forward(self, input: relax.Expr): + x = relax.op.nn.layer_norm(input, self.norm1_weight, self.norm1_bias, axes=[-1]) + proj = self.attn(x) + proj = relax.op.add(input, proj) + res = relax.op.nn.layer_norm( + proj, self.norm2_weight, self.norm2_bias, axes=[-1] + ) + res = self.mlp(res) + res = relax.op.add(proj, res) + return res + + +class MiniGPTVisualEncoder(nn.Module): + def __init__(self, config: MiniGPTConfig): + self.embed_dim = config.visual_encoder_embed_dim + self.dtype = config.dtype + self.transform = TransformImage(config.dtype, config.in_chan) + self.patch_embed = MiniGPTPatchEmbed( + config.image_size, + config.patch_size, + config.visual_encoder_embed_dim, + config.dtype, + ) + self.num_blocks = config.visual_encoder_num_blocks + self.blocks = ModuleList( + [MiniGPTVisualEncoderBlock(config) for _ in range(self.num_blocks)] + ) + + self.ln_vision_weight = nn.Parameter( + (self.embed_dim,), dtype=self.dtype, name="ln_vision_weight" + ) + self.ln_vision_bias = nn.Parameter( + (self.embed_dim,), dtype=self.dtype, name="ln_vision_bias" + ) + + def forward(self, input_image: relax.Expr): + res = self.transform(input_image) + res = self.patch_embed(res) + for block in self.blocks: + res = block(res) + res = relax.op.nn.layer_norm( + res, self.ln_vision_weight, self.ln_vision_bias, axes=[-1] + ) + return res + + +class MiniGPTEmbedding(nn.Module): + def __init__(self, config: MiniGPTConfig): + self.word_embed = config.word_embed + self.dtype = config.dtype + self.eps = 1e-12 + + self.norm_weight = nn.Parameter( + (self.word_embed,), dtype=self.dtype, name="norm_weight" + ) + self.norm_bias = nn.Parameter( + (self.word_embed,), dtype=self.dtype, name="norm_bias" + ) + + def forward(self, embedding: relax.Expr): + res = relax.op.nn.layer_norm( + embedding, self.norm_weight, self.norm_bias, axes=[-1], epsilon=self.eps + ) + return res + + +class MiniGPTBertAttention(nn.Module): + def __init__(self, config: MiniGPTConfig, hidden_dim: int): + self.word_embed = config.word_embed + self.num_query_token = config.num_query_token + self.num_attn_heads = config.bert_num_attn_heads + self.attn_head_size = config.bert_attn_head_size + self.visual_encoder_attn_hidden_dim = config.visual_encoder_attn_hidden_dim + self.dtype = config.dtype + self.eps = 1e-12 + + self.query_weight = nn.Parameter( + (self.word_embed, self.word_embed), dtype=self.dtype, name="query_weight" + ) + self.query_bias = nn.Parameter( + (self.word_embed,), dtype=self.dtype, name="query_bias" + ) + self.key_weight = nn.Parameter( + (self.word_embed, hidden_dim), dtype=self.dtype, name="key_weight" + ) + self.key_bias = nn.Parameter( + (self.word_embed,), dtype=self.dtype, name="key_bias" + ) + self.value_weight = nn.Parameter( + (self.word_embed, hidden_dim), dtype=self.dtype, name="value_weight" + ) + self.value_bias = nn.Parameter( + (self.word_embed,), dtype=self.dtype, name="value_bias" + ) + self.dense_weight = nn.Parameter( + (self.word_embed, self.word_embed), dtype=self.dtype, name="dense_weight" + ) + self.dense_bias = nn.Parameter( + (self.word_embed,), dtype=self.dtype, name="dense_bias" + ) + self.norm_weight = nn.Parameter( + (self.word_embed,), dtype=self.dtype, name="norm_weight" + ) + self.norm_bias = nn.Parameter( + (self.word_embed,), dtype=self.dtype, name="norm_bias" + ) + + def forward( + self, + hidden_states: relax.Expr, + attention_mask: relax.Expr, + encoder_hidden_states=None, + encoder_extend_attention_mask=None, + ): + from tvm.relax.op import add, linear, matmul, permute_dims, reshape + + bs = 1 + states = ( + encoder_hidden_states + if encoder_hidden_states is not None + else hidden_states + ) + mask = ( + encoder_extend_attention_mask + if encoder_extend_attention_mask is not None + else attention_mask + ) + hidden_dim = ( + self.visual_encoder_attn_hidden_dim + if encoder_hidden_states is not None + else self.num_query_token + ) + key = linear(states, self.key_weight, self.key_bias) + value = linear(states, self.value_weight, self.value_bias) + key = reshape(key, [bs, hidden_dim, self.num_attn_heads, self.attn_head_size]) + key = permute_dims(key, [0, 2, 1, 3]) + value = reshape( + value, [bs, hidden_dim, self.num_attn_heads, self.attn_head_size] + ) + value = permute_dims(value, [0, 2, 1, 3]) + query = linear(hidden_states, self.query_weight, self.query_bias) + query = reshape( + query, [bs, self.num_query_token, self.num_attn_heads, self.attn_head_size] + ) + query = permute_dims(query, [0, 2, 1, 3]) + scores = matmul(query, permute_dims(key, [0, 1, 3, 2])) + scores = scores / relax.const(math.sqrt(self.attn_head_size), dtype=self.dtype) + scores = add(scores, mask) + probs = relax.op.nn.softmax(scores, axis=-1) + context = matmul(probs, value) + context = permute_dims(context, [0, 2, 1, 3]) + context = reshape(context, [bs, self.num_query_token, self.word_embed]) + # calculate the output + context = linear(context, self.dense_weight, self.dense_bias) + context = add(context, hidden_states) + res = relax.op.nn.layer_norm( + context, self.norm_weight, self.norm_bias, axes=[-1], epsilon=self.eps + ) + return res, key, value + + +class MiniGPTBertLayer(nn.Module): + def __init__(self, config: MiniGPTConfig, use_cross_attention=False): + self.word_embed = config.word_embed + self.embed_dim = config.visual_encoder_embed_dim + self.interm_query = config.bert_interm_query + self.dtype = config.dtype + self.eps = 1e-12 + + self.attention = MiniGPTBertAttention(config, self.word_embed) + if use_cross_attention: + self.cross_attention = MiniGPTBertAttention(config, self.embed_dim) + else: + self.cross_attention = None + self.interm_query_weight = nn.Parameter( + (self.interm_query, self.word_embed), + dtype=self.dtype, + name="interm_query_weight", + ) + self.interm_query_bias = nn.Parameter( + (self.interm_query,), dtype=self.dtype, name="interm_query_bias" + ) + self.output_query_weight = nn.Parameter( + (self.word_embed, self.interm_query), + dtype=self.dtype, + name="output_query_weight", + ) + self.output_query_bias = nn.Parameter( + (self.word_embed,), dtype=self.dtype, name="output_query_bias" + ) + self.norm_weight = nn.Parameter( + (self.word_embed,), dtype=self.dtype, name="norm_weight" + ) + self.norm_bias = nn.Parameter( + (self.word_embed,), dtype=self.dtype, name="norm_bias" + ) + + def forward( + self, + embedding: relax.Expr, + extend_attention_mask: relax.Expr, + encoder_hidden_states: relax.Expr, + encoder_extend_attention_mask: relax.Expr, + ): + attn_output, key, value = self.attention(embedding, extend_attention_mask) + if self.cross_attention: + attn_output, _, _ = self.cross_attention( + attn_output, + extend_attention_mask, + encoder_hidden_states, + encoder_extend_attention_mask, + ) + res = relax.op.linear( + attn_output, self.interm_query_weight, self.interm_query_bias + ) + res = relax.op.nn.gelu(res) + res = relax.op.linear(res, self.output_query_weight, self.output_query_bias) + res = relax.op.add(res, attn_output) + res = relax.op.nn.layer_norm( + res, self.norm_weight, self.norm_bias, axes=[-1], epsilon=self.eps + ) + return res, key, value + + +class MiniGPTQFormer(nn.Module): + def __init__(self, config: MiniGPTConfig): + self.N = config.visual_encoder_attn_hidden_dim + self.num_query_token = config.num_query_token + self.word_embed = config.word_embed + self.num_layers = config.bert_hidden_layers + self.dtype = config.dtype + + bs = 1 + self.query_tokens = nn.Parameter( + (bs, self.num_query_token, self.word_embed), + dtype=self.dtype, + name="query_tokens", + ) + self.embedding = MiniGPTEmbedding(config) + self.bert_layers = ModuleList( + [MiniGPTBertLayer(config, i % 2 == 0) for i in range(self.num_layers)] + ) + + def forward(self, image_embeds: relax.Expr): + from tvm.relax.op import expand_dims, ones + + bs = 1 + image_attns = ones((bs, self.N), self.dtype) + embedding = self.embedding(self.query_tokens) + attention_mask = ones((bs, self.num_query_token), self.dtype) + extend_attention_mask = expand_dims(attention_mask, [1, 2]) + extend_attention_mask = ( + relax.const(1.0, self.dtype) - extend_attention_mask + ) * relax.const(-10000.0, self.dtype) + encoder_extend_attention_mask = expand_dims(image_attns, [1, 2]) + encoder_extend_attention_mask = ( + relax.const(1.0, self.dtype) - encoder_extend_attention_mask + ) + for layer in self.bert_layers: + embedding, _, _ = layer( + embedding, + extend_attention_mask, + image_embeds, + encoder_extend_attention_mask, + ) + return embedding + + +class MiniGPTLLaMAProj(nn.Module): + def __init__(self, config: MiniGPTConfig): + self.proj_size = config.llama_proj_size + self.word_embed = config.word_embed + self.dtype = config.dtype + + self.weight = nn.Parameter( + (self.proj_size, self.word_embed), dtype=self.dtype, name="weight" + ) + self.bias = nn.Parameter((self.proj_size,), dtype=self.dtype, name="bias") + + def forward(self, embedding: relax.Expr): + return relax.op.linear(embedding, self.weight, self.bias) + + +class MiniGPTModel(nn.Module): + def __init__(self, config: MiniGPTConfig): + self.visual_encoder = MiniGPTVisualEncoder(config) + self.q_former = MiniGPTQFormer(config) + self.llama_proj = MiniGPTLLaMAProj(config) + + def forward(self, input_image: relax.Expr): + output = self.visual_encoder(input_image) + output = self.q_former(output) + output = self.llama_proj(output) + return output + + +def get_param_quant_kind( + name: str, param_info: relax.TensorStructInfo +) -> ParamQuantKind: + """No quantization for MiniGPT. Use q0f16 or q0f32 when building it.""" + return ParamQuantKind.others + + +def create_embed_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: MiniGPTConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "embed" + + bs = 1 + with bb.function(func_name): + model = MiniGPTModel(config) + param_manager.register_params( + model, func_name, quant_scheme, get_param_quant_kind + ) + + input_image = nn.Placeholder( + (bs, config.image_size, config.image_size, config.in_chan), + dtype="uint8", + name="input_image", + ) + with bb.dataflow(): + output = model(input_image) + params = [input_image] + model.parameters() + gv = bb.emit_output(output) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 1)) + + +def get_model(args, _config): + model_name = args.model + model_path = args.model_path + + if model_name.startswith("minigpt"): + config = MiniGPTConfig(**MODEL_CONFIG[model_name]) + config.dtype = args.quantization.model_dtype + # build the relax model + param_manager = ParamManager() + bb = relax.BlockBuilder() + create_embed_func(bb, param_manager, config, args.quantization) + mod = bb.get() + + if args.build_model_only: + return mod, param_manager, None, config + + param_manager.set_param_loading_func( + args.model_path, args.use_safetensors, no_lazy_param_loading=True + ) + + # load visual encoder weights + visual_encoder_url = "https://storage.googleapis.com/sfr-vision-language-research/LAVIS/models/BLIP2/eva_vit_g.pth" + visual_encoder_cached_file = download_cached_file( + visual_encoder_url, check_hash=False, progress=True + ) + visual_encoder_state_dict = torch.load( + visual_encoder_cached_file, map_location="cpu" + ) + + # load QFormer weights + q_former_url = "https://storage.googleapis.com/sfr-vision-language-research/LAVIS/models/BLIP2/blip2_pretrained_flant5xxl.pth" + q_former_cached_file = download_cached_file( + q_former_url, check_hash=False, progress=True + ) + q_former_state_dict = torch.load(q_former_cached_file, map_location="cpu")[ + "model" + ] + + # load llama and llama proj weights + if os.path.isdir(model_path): + raise ValueError( + "MiniGPT model path should be a single file instead of a directory." + ) + llama_state_dict = torch.load(model_path + ".pth", map_location="cpu")["model"] + + param_list = [] + device = tvm.cpu() + visual_encoder_key_list = list(visual_encoder_state_dict.keys())[ + : 4 + 13 * config.visual_encoder_num_blocks + ] + for key in visual_encoder_key_list: + param_list.append( + tvm.nd.array( + visual_encoder_state_dict[key].numpy().astype(config.dtype), device + ) + ) + q_former_key_list = ( + list(q_former_state_dict.keys())[1:3] + + [list(q_former_state_dict.keys())[0]] + + list(q_former_state_dict.keys())[ + 6 : 8 + (26 + 16) * config.bert_hidden_layers // 2 + ] + ) + for key in q_former_key_list: + param_list.append( + tvm.nd.array( + q_former_state_dict[key].numpy().astype(config.dtype), device + ) + ) + llama_key_list = list(llama_state_dict.keys())[-2:] + for key in llama_key_list: + param_list.append( + tvm.nd.array(llama_state_dict[key].numpy().astype(config.dtype), device) + ) + + return mod, param_manager, param_list, config + + raise ValueError(f"Unsupported model: {model_name}") + + +# helper functions for distributed download of model weights from URL +# source: https://github.com/Vision-CAIR/MiniGPT-4/blob/main/minigpt4/common/dist_utils.py (originally credit to Salesforce) + + +def download_cached_file(url, check_hash=True, progress=False): + import timm.models.hub as timm_hub + import torch.distributed as dist + + def is_dist_avail_and_initialized(): + if not dist.is_available(): + return False + if not dist.is_initialized(): + return False + return True + + def get_rank(): + if not is_dist_avail_and_initialized(): + return 0 + return dist.get_rank() + + def is_main_process(): + return get_rank() == 0 + + """ + Download a file from a URL and cache it locally. If the file already exists, it is not downloaded again. + If distributed, only the main process downloads the file, and the other processes wait for the file to be downloaded. + """ + + def get_cached_file_path(): + # a hack to sync the file path across processes + parts = torch.hub.urlparse(url) + filename = os.path.basename(parts.path) + cached_file = os.path.join(timm_hub.get_cache_dir(), filename) + + return cached_file + + if is_main_process(): + timm_hub.download_cached_file(url, check_hash, progress) + + if is_dist_avail_and_initialized(): + dist.barrier() + + return get_cached_file_path() diff --git a/mlc_llm/relax_model/mistral.py b/mlc_llm/relax_model/mistral.py new file mode 100644 index 0000000..e08495f --- /dev/null +++ b/mlc_llm/relax_model/mistral.py @@ -0,0 +1,1125 @@ +# pylint: disable=too-many-lines, missing-class-docstring, missing-function-docstring +"""Implements the mistal model with sliding window attention.""" + +import math +from dataclasses import dataclass +from typing import Any, List, Optional, Tuple + +import numpy as np +import tvm +from tvm import relax, te +from tvm.relax.op import ccl +from tvm.relax.testing import nn +from tvm.script import relax as R + +from ..quantization import ParamQuantKind, QuantizationScheme +from .commons import create_metadata_func +from .modules import ModuleList +from .param_manager import ParamManager + + +@dataclass +class MistralConfig: + """Configuration for mistral model.""" + + def __init__( + self, + bos_token_id=1, + eos_token_id=2, + pad_token_id=-1, + hidden_act="silu", + hidden_size=4096, + initializer_range=0.02, + intermediate_size=14336, + max_position_embeddings=32768, + num_attention_heads=32, + num_hidden_layers=32, + num_key_value_heads=8, + rms_norm_eps=1e-5, + rope_theta=10000.0, + sliding_window=4096, + attention_sink_size=0, + tie_word_embeddings=False, + vocab_size=32000, + dtype="float32", + max_sequence_length=16384, + combine_matmul=True, + build_model_only=False, + num_shards=1, + **kwargs, + ): + self.bos_token_id = bos_token_id + self.eos_token_id = eos_token_id + self.pad_token_id = pad_token_id + self.hidden_act = hidden_act + self.hidden_size = hidden_size + self.initializer_range = initializer_range + self.intermediate_size = intermediate_size + self.max_position_embeddings = max_position_embeddings + self.num_attention_heads = num_attention_heads + self.num_hidden_layers = num_hidden_layers + self.num_key_value_heads = num_key_value_heads + self.rms_norm_eps = rms_norm_eps + self.rope_theta = rope_theta + self.sliding_window = sliding_window + self.attention_sink_size = attention_sink_size + self.tie_word_embeddings = tie_word_embeddings + self.vocab_size = vocab_size + self.dtype = dtype + self.max_sequence_length = sliding_window * 4 + self.combine_matmul = combine_matmul + if build_model_only and num_shards > 1: + self.num_shards = num_shards + else: + self.num_shards = 1 + self.kwargs = kwargs + + def get_num_key_value_heads(self): + if self.num_key_value_heads is None: + return self.num_attention_heads + + return self.num_key_value_heads + + +class Linear(nn.Module): + def __init__(self, in_features, out_features, dtype: str, bias=True): + self.in_features = in_features + self.out_features = out_features + self.weight = nn.Parameter((out_features, in_features), dtype=dtype, name="linear_weight") + if bias: + self.bias = nn.Parameter((out_features,), dtype=dtype, name="linear_bias") + else: + self.bias = None + + def forward(self, input: relax.Expr) -> relax.Var: + return nn.emit(relax.op.linear(input, self.weight, self.bias)) + + +class Embedding(nn.Module): + def __init__(self, num_embeddings, embedding_dim, dtype: str): + self.num_embeddings = num_embeddings + self.embedding_dim = embedding_dim + self.weight = nn.Parameter( + (num_embeddings, embedding_dim), dtype=dtype, name="embedding_weight" + ) + + def forward(self, x: relax.Expr) -> relax.Var: + from tvm.relax.op import ( # pylint: disable=import-outside-toplevel + reshape, + take, + ) + + ndim = x.struct_info.ndim + if ndim == 1: + return nn.emit(take(self.weight, x, axis=0)) + else: + x_shape = x.struct_info.shape.values + emb_size = self.weight.struct_info.shape.values[-1] + x = nn.emit(reshape(x, shape=[-1])) + embedding = nn.emit(take(self.weight, x, axis=0)) + return nn.emit(reshape(embedding, [*x_shape, emb_size])) + + +class MistralRMSNorm(nn.Module): + def __init__(self, hidden_size, dtype, eps=1e-6): + self.weight = nn.Parameter((hidden_size,), dtype=dtype, name="rms_norm_weight") + self.variance_epsilon = tvm.tir.const(eps, dtype) + + def forward(self, hidden_states): + from tvm import te, tir + + def f_rms_norm(x, weight): + is_float32 = x.dtype == "float32" + + def f_square(x): + return tir.Cast("float32", x) * tir.Cast("float32", x) if not is_float32 else x * x + + k = te.reduce_axis((0, x.shape[2]), name="k") + square_sum = te.compute( + (x.shape[0], x.shape[1]), + lambda bsz, i: te.sum(f_square(x[bsz, i, k]), axis=k), + name=x.op.name + "red_temp", + ) + + def f_div_cast(bsz, i, k): + x_val = x[bsz, i, k] + if not is_float32: + x_val = tir.Cast("float32", x_val) + return x_val / tir.sqrt(square_sum[bsz, i] / x.shape[2] + self.variance_epsilon) + + def f_mul_cast(x, y): + value = x * y + if not is_float32: + value = tir.Cast(x.dtype, value) + return value + + return te.compute( + x.shape, + lambda bsz, i, k: f_mul_cast(weight(k), f_div_cast(bsz, i, k)), + name="rms_norm", + ) + + return nn.emit_te(f_rms_norm, hidden_states, self.weight, primfunc_name_hint="rms_norm") + + +class MistralMLP(nn.Module): + def __init__(self, config: MistralConfig): + self.combine_matmul = config.combine_matmul + self.num_shards = config.num_shards + hidden_size = config.hidden_size + intermediate_size = config.intermediate_size // self.num_shards + dtype = config.dtype + if self.combine_matmul: + self.gate_up_proj = Linear(hidden_size, 2 * intermediate_size, dtype=dtype, bias=False) + self.down_proj = Linear(intermediate_size, hidden_size, dtype=dtype, bias=False) + self.gate_up_proj.weight.shard_dim = 0 + self.gate_up_proj.weight.shard_strategy = "shard_gate_up" + self.down_proj.weight.shard_dim = 1 + self.down_proj.weight.shard_strategy = "shard_mlp_k" + else: + self.gate_proj = Linear(hidden_size, intermediate_size, dtype=dtype, bias=False) + self.down_proj = Linear(intermediate_size, hidden_size, dtype=dtype, bias=False) + self.up_proj = Linear(hidden_size, intermediate_size, dtype=dtype, bias=False) + + def forward(self, x): + if self.combine_matmul: + gate_up_results = nn.emit( + relax.op.split( + self.gate_up_proj(x), + indices_or_sections=2, + axis=-1, + ) + ) + gate_result = relax.TupleGetItem(gate_up_results, 0) + up_result = relax.TupleGetItem(gate_up_results, 1) + else: + gate_result = self.gate_proj(x) + up_result = self.up_proj(x) + + result = self.down_proj(relax.op.nn.silu(gate_result) * up_result) + return result + + +def apply_rotary_pos_emb(q, k, base, q_offset): + def f_rotary_embedding(tensor, offset): + dtype = tensor.dtype + head_dim = tensor.shape[-1] + n_feat_half = tensor.shape[-1] // 2 + + def rotary_compute(*idx): + i, j = idx[-3], idx[-1] + pos = (offset + i).astype("float32") + inv_freq = te.const(1, "float32") / ( + te.power( + te.const(base, "float32"), + ((2 * j) % head_dim).astype("float32") / head_dim.astype("float32"), + ) + ) + freq = pos * inv_freq + return te.cos(freq).astype(dtype) * tensor(*idx) + te.sin(freq).astype( + dtype + ) * tvm.tir.Select( + j >= n_feat_half, + tensor[idx[0], i, idx[2], j - n_feat_half], + -tensor[idx[0], i, idx[2], j + n_feat_half], + ) + + return tvm.te.compute(tensor.shape, rotary_compute, name="rotary") + + q_embed = nn.emit_te(f_rotary_embedding, q, q_offset, primfunc_name_hint="rotary_embedding") + k_embed = nn.emit_te(f_rotary_embedding, k, 0, primfunc_name_hint="rotary_embedding") + return q_embed, k_embed + + +class MistralAttention(nn.Module): + """Multi-headed attention from 'Attention Is All You Need' paper""" + + def __init__(self, config: MistralConfig): + dtype = config.dtype + self.num_shards = config.num_shards + self.hidden_size = config.hidden_size + self.num_key_value_heads = config.get_num_key_value_heads() // config.num_shards + self.num_query_heads = config.num_attention_heads // self.num_shards + self.head_dim = self.hidden_size // config.num_attention_heads + self.rope_theta = config.rope_theta + self.sliding_window = config.sliding_window + self.attention_sink_size = config.attention_sink_size + + self.combine_matmul = config.combine_matmul + if self.combine_matmul: + self.query_key_value_proj = Linear( + self.hidden_size, + (self.num_query_heads + 2 * self.num_key_value_heads) * self.head_dim, + dtype=dtype, + bias=False, + ) + self.query_key_value_proj.weight.shard_dim = 0 + self.query_key_value_proj.weight.shard_strategy = "shard_qkv" + else: + self.q_proj = Linear( + self.hidden_size, + self.num_query_heads * self.head_dim, + dtype=dtype, + bias=False, + ) + self.k_proj = Linear( + self.hidden_size, + self.num_key_value_heads * self.head_dim, + dtype=dtype, + bias=False, + ) + self.v_proj = Linear( + self.hidden_size, + self.num_key_value_heads * self.head_dim, + dtype=dtype, + bias=False, + ) + self.q_proj.weight.shard_dim = 0 + self.k_proj.weight.shard_dim = 0 + self.v_proj.weight.shard_dim = 0 + + self.o_proj = Linear( + self.head_dim * self.num_query_heads, self.hidden_size, dtype=dtype, bias=False + ) + self.o_proj.weight.shard_dim = 1 + self.o_proj.weight.shard_strategy = "shard_o_proj_k" + + def interleave_kv( + self, + key_cur: relax.Expr, + value_cur: relax.Expr, + kv_seq_len: int, + rolling_cache_len: int, + cache_offset: int, + attention_sink_size: int, + past_key_value: Tuple[relax.Expr], + ): + from tvm.relax.op import reshape + + def te_cache_unrotate(x_cached, cache_offset, rolling_cache_len): + return te.compute( + (kv_cur_shape[0], rolling_cache_len, kv_cur_shape[2], kv_cur_shape[3]), + lambda b, s, h, d: te.if_then_else( + s < attention_sink_size, + x_cached[b, s, h, d], + te.if_then_else( + s < rolling_cache_len - cache_offset + attention_sink_size, + x_cached[b, s + cache_offset - attention_sink_size, h, d], + x_cached[b, s + cache_offset - rolling_cache_len, h, d], + ), + ), + name="te_cache_unrotate", + ) + + def te_cache_cur_concat(x, x_cached, kv_seq_len, rolling_cache_len): + return te.compute( + (kv_cur_shape[0], kv_seq_len, kv_cur_shape[2], kv_cur_shape[3]), + lambda b, s, h, d: te.if_then_else( + s < rolling_cache_len, + x_cached[b, s, h, d], + x[b, s - rolling_cache_len, h, d], + ), + name="te_cache_cur_concat", + ) + + def te_squeeze(x): + return te.compute( + x.shape[1:], + lambda s, h, d: x[0, s, h, d], + name="squeeze_te", + ) + + # [bsz, t, nh, hd] + kv_cur_shape = key_cur.struct_info.shape + kv_cur_dtype = key_cur.struct_info.dtype + assert kv_cur_shape[0] == 1 # bsz + kv_batched_cache_shape = R.shape( + [kv_cur_shape[0], rolling_cache_len, kv_cur_shape[2], kv_cur_shape[3]] + ) + kv_cache_shape = R.shape([rolling_cache_len, kv_cur_shape[2], kv_cur_shape[3]]) + + # fecth past keys and values from cache + k_cache, v_cache = past_key_value + + f_kv_cache_view = relax.extern("vm.builtin.attention_kv_cache_view") + key_cached = nn.emit( + relax.call_pure_packed( + f_kv_cache_view, + args=[k_cache, kv_cache_shape], + sinfo_args=[R.Tensor(kv_cache_shape, kv_cur_dtype)], + ) + ) + value_cached = nn.emit( + relax.call_pure_packed( + f_kv_cache_view, + args=[v_cache, kv_cache_shape], + sinfo_args=[R.Tensor(kv_cache_shape, kv_cur_dtype)], + ) + ) + key_cached = nn.emit(reshape(key_cached, kv_batched_cache_shape)) + value_cached = nn.emit(reshape(value_cached, kv_batched_cache_shape)) + + key_cached = nn.emit_te( + te_cache_unrotate, + key_cached, + cache_offset, + rolling_cache_len, + primfunc_name_hint="te_cache_unrotate_key", + ) + key = nn.emit_te( + te_cache_cur_concat, + key_cur, + key_cached, + kv_seq_len, + rolling_cache_len, + primfunc_name_hint="te_cache_cur_concat_key", + ) + + value_cached = nn.emit_te( + te_cache_unrotate, + value_cached, + cache_offset, + rolling_cache_len, + primfunc_name_hint="te_cache_unrotate_value", + ) + value = nn.emit_te( + te_cache_cur_concat, + value_cur, + value_cached, + kv_seq_len, + rolling_cache_len, + primfunc_name_hint="te_cache_cur_concat_value", + ) + + # update cache + squeezed_key = nn.emit_te(te_squeeze, key_cur) + squeezed_value = nn.emit_te(te_squeeze, value_cur) + + assert attention_sink_size >= 0 + f_kv_cache_override = relax.extern( + "vm.builtin.attention_kv_cache_window_override_with_sinks" + ) + k_cache = nn.emit( + relax.op.call_inplace_packed( + f_kv_cache_override, + args=[ + k_cache, + squeezed_key, + relax.PrimValue(self.sliding_window), + relax.PrimValue(attention_sink_size), + ], + inplace_indices=[0], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + v_cache = nn.emit( + relax.op.call_inplace_packed( + f_kv_cache_override, + args=[ + v_cache, + squeezed_value, + relax.PrimValue(self.sliding_window), + relax.PrimValue(attention_sink_size), + ], + inplace_indices=[0], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + + return key, value, (k_cache, v_cache) + + def forward( + self, + hidden_states: relax.Expr, + cache_len_shape: relax.Expr, + kv_seq_len_shape: relax.Expr, + cache_offset_shape: relax.Expr, + past_key_value: Tuple[relax.Expr], + attention_mask: Optional[relax.Expr] = None, + ) -> Tuple[relax.Expr, Optional[relax.Expr], Optional[Tuple[relax.Expr]]]: + # pylint: disable=import-outside-toplevel + from tvm.relax.op import astype, matmul, maximum, permute_dims, reshape, split + from tvm.relax.op.nn import softmax + + bsz, q_len, _ = hidden_states.struct_info.shape + assert bsz == 1, "Only support batch size 1 at this moment." + + if self.combine_matmul: + qkv_cur = nn.emit( + split( + self.query_key_value_proj(hidden_states), + indices_or_sections=[ + self.num_query_heads * self.head_dim, + (self.num_query_heads + self.num_key_value_heads) * self.head_dim, + ], + axis=-1, + ) + ) + query = relax.TupleGetItem(qkv_cur, 0) + key_cur = relax.TupleGetItem(qkv_cur, 1) + value_cur = relax.TupleGetItem(qkv_cur, 2) + else: + query = self.q_proj(hidden_states) + key_cur = self.k_proj(hidden_states) + value_cur = self.v_proj(hidden_states) + + query = nn.emit( + reshape( + query, + (bsz, q_len, self.num_query_heads, self.head_dim), + ), + ) + key_cur = nn.emit( + reshape( + key_cur, + (bsz, q_len, self.num_key_value_heads, self.head_dim), + ), + ) + value_cur = nn.emit( + reshape( + value_cur, + (bsz, q_len, self.num_key_value_heads, self.head_dim), + ), + ) + + # concat current kv with cached kv (unrotating the cache) + rolling_cache_len = cache_len_shape.struct_info.values[0] + kv_seq_len = kv_seq_len_shape.struct_info.values[0] + cache_offset = cache_offset_shape.struct_info.values[0] + key, value, updated_key_value = self.interleave_kv( + key_cur, + value_cur, + kv_seq_len, + rolling_cache_len, + cache_offset, + self.attention_sink_size, + past_key_value, + ) + + # cache relative position embeddings (after KV Cache) + query, key = apply_rotary_pos_emb( + query, + key, + self.rope_theta, + q_offset=rolling_cache_len, + ) + + if self.num_key_value_heads != self.num_query_heads: + n_rep = self.num_query_heads // self.num_key_value_heads + key = nn.emit(relax.op.repeat(key, n_rep, axis=2)) + value = nn.emit(relax.op.repeat(value, n_rep, axis=2)) + + query = nn.emit(permute_dims(query, [0, 2, 1, 3])) + key = nn.emit(permute_dims(key, [0, 2, 1, 3])) + value = nn.emit(permute_dims(value, [0, 2, 1, 3])) + + attn_weights = nn.emit( + matmul(query, permute_dims(key, [0, 1, 3, 2])) + / relax.const(math.sqrt(self.head_dim), query.struct_info.dtype) + ) + + tvm.ir.assert_structural_equal( + attention_mask.struct_info.shape.values, + (bsz, tvm.tir.IntImm("int64", 1), q_len, kv_seq_len), + ) + + attn_weights = nn.emit( + maximum( + attn_weights, + relax.const( + tvm.tir.min_value(attn_weights.struct_info.dtype).value, + attn_weights.struct_info.dtype, + ), + ) + ) + attn_weights = nn.emit(relax.op.minimum(attn_weights, attention_mask)) + + # upcast attention to fp32 + if attn_weights.struct_info.dtype != "float32": + attn_weights = astype(attn_weights, "float32") + attn_weights = nn.emit(softmax(attn_weights, axis=-1)) + if attn_weights.struct_info.dtype != query.struct_info.dtype: + attn_weights = astype(attn_weights, query.struct_info.dtype) + attn_output = nn.emit(matmul(attn_weights, value)) + + attn_output = nn.emit(permute_dims(attn_output, [0, 2, 1, 3])) + attn_output = nn.emit( + reshape(attn_output, (bsz, q_len, self.head_dim * self.num_query_heads)) + ) + + attn_output = self.o_proj(attn_output) + + return attn_output, ((None, None) if updated_key_value is None else updated_key_value) + + +class MistralDecoderLayer(nn.Module): + def __init__(self, config: MistralConfig): + self.hidden_size = config.hidden_size + self.self_attn = MistralAttention(config) + self.mlp = MistralMLP(config) + self.input_layernorm = MistralRMSNorm( + config.hidden_size, dtype=config.dtype, eps=config.rms_norm_eps + ) + self.post_attention_layernorm = MistralRMSNorm( + config.hidden_size, dtype=config.dtype, eps=config.rms_norm_eps + ) + + def forward( + self, + hidden_states: relax.Expr, + cache_len_shape: relax.Expr, + kv_seq_len_shape: relax.Expr, + cache_offset_shape: relax.Expr, + past_key_value: Tuple[relax.Expr], + attention_mask: Optional[relax.Expr] = None, + ) -> Tuple[relax.Expr, Optional[Tuple[relax.Expr, relax.Expr]]]: + residual = hidden_states + + hidden_states = self.input_layernorm(hidden_states) + + # Self Attention + hidden_states, present_key_value = self.self_attn( + hidden_states=hidden_states, + past_key_value=past_key_value, + attention_mask=attention_mask, + cache_len_shape=cache_len_shape, + kv_seq_len_shape=kv_seq_len_shape, + cache_offset_shape=cache_offset_shape, + ) + if self.self_attn.num_shards > 1: + residual = nn.emit( + residual / R.const(self.self_attn.num_shards, dtype=residual.struct_info.dtype) + ) + hidden_states = nn.emit(residual + hidden_states) + if self.self_attn.num_shards > 1: + hidden_states = nn.emit(ccl.allreduce(hidden_states, "sum")) + + # Fully Connected + residual = hidden_states + hidden_states = self.post_attention_layernorm(hidden_states) + hidden_states = self.mlp(hidden_states) + if self.mlp.num_shards > 1: + residual = nn.emit( + residual / R.const(self.mlp.num_shards, dtype=residual.struct_info.dtype) + ) + hidden_states = nn.emit(residual + hidden_states) + if self.mlp.num_shards > 1: + hidden_states = nn.emit(ccl.allreduce(hidden_states, "sum")) + return hidden_states, present_key_value + + +def _make_sliding_window_mask(input_shape, kv_seq_len, sliding_window, dtype): + # See `tests/python/test_sliding_window_mask.py` for more on its behavior. + # [bsz, tgt_len] -> [bsz, 1, tgt_len, kv_seq_len] + + bsz, tgt_len = input_shape # TODO: only support batch size of 1 for now + cache_len = kv_seq_len - tgt_len # number of elements in cache + + if isinstance(tgt_len, tvm.tir.SizeVar) or tgt_len > 1: + # Either 1. First prefill, or 2. Subsequent prefill + from tvm.relax.op import broadcast_to # pylint: disable=import-outside-toplevel + + def sliding_window_min_max_te(sliding_window): + return te.compute( + (tgt_len, kv_seq_len), + lambda i, j: tvm.tir.Select( + tvm.tir.all(i + cache_len >= j, i + cache_len - j < sliding_window), + tvm.tir.max_value(dtype), + tvm.tir.min_value(dtype), + ), + name="make_diag_mask_sliding_window_te", + ) + + mask = nn.emit_te(sliding_window_min_max_te, sliding_window) + return nn.emit(broadcast_to(mask, (bsz, 1, tgt_len, kv_seq_len))) + + else: + # 3. Decode (equivalent to prefilling a chunk of size 1) + # Mask nothing here since WS == cache_size + bsz, tgt_len = input_shape + return nn.emit( + relax.op.full( + (bsz, 1, tgt_len, kv_seq_len), + relax.const(tvm.tir.max_value(dtype).value, dtype), + dtype, + ) + ) + + +class MistralEmbedTokens(nn.Module): + def __init__(self, config: MistralConfig, vocab_size_var: tvm.tir.SizeVar): + self.embed_tokens = Embedding(vocab_size_var, config.hidden_size, dtype=config.dtype) + + def forward(self, input_ids: relax.Expr): + inputs_embeds = self.embed_tokens(input_ids) + return inputs_embeds + + +class MistralEmbedTokensWrapper(nn.Module): + def __init__(self, config: MistralConfig, vocab_size_var: tvm.tir.SizeVar): + # build a wrapper to ensure that the naming of the embed_tokens parameter is consistent + self.model = MistralEmbedTokens(config, vocab_size_var) + + def forward(self, input_ids: relax.Expr): + inputs_embeds = self.model(input_ids) + return inputs_embeds + + +class MistralModel(nn.Module): + def __init__( + self, config: MistralConfig, vocab_size_var: tvm.tir.SizeVar, sep_embed: bool = False + ): + self.num_shards = config.num_shards + self.padding_idx = config.pad_token_id + self.embed_tokens = None + + if not sep_embed: + self.embed_tokens = Embedding(vocab_size_var, config.hidden_size, dtype=config.dtype) + + self.layers = ModuleList( + [MistralDecoderLayer(config) for _ in range(config.num_hidden_layers)] + ) + self.norm = MistralRMSNorm(config.hidden_size, dtype=config.dtype, eps=config.rms_norm_eps) + self.sliding_window = config.sliding_window + + def forward( + self, + inputs: relax.Expr, + cache_len_shape: relax.Expr, + kv_seq_len_shape: relax.Expr, + cache_offset_shape: relax.Expr, + past_key_values: relax.Expr, + ): + if self.num_shards > 1: + inputs = nn.emit(ccl.broadcast_from_worker0(inputs)) + if self.embed_tokens: + inputs_embeds = self.embed_tokens(inputs) + else: + inputs_embeds = inputs + # retrieve input_ids + batch_size, seq_length, _ = inputs_embeds.struct_info.shape + kv_seq_len = kv_seq_len_shape.struct_info.values[0] + + # embed positions + attention_mask = _make_sliding_window_mask( + (batch_size, seq_length), + kv_seq_len, + self.sliding_window, + inputs_embeds.struct_info.dtype, + ) + + hidden_states = inputs_embeds + + # decoder layers + next_decoder_cache = () + + for idx, decoder_layer in enumerate(self.layers): + assert past_key_values is not None + past_key_value = (past_key_values[idx * 2], past_key_values[idx * 2 + 1]) + + hidden_states, key_value_cache = decoder_layer( + hidden_states, + attention_mask=attention_mask, + past_key_value=past_key_value, + cache_len_shape=cache_len_shape, + kv_seq_len_shape=kv_seq_len_shape, + cache_offset_shape=cache_offset_shape, + ) + next_decoder_cache += key_value_cache + + hidden_states = self.norm(hidden_states) + + assert len(next_decoder_cache) == len(self.layers) * 2 + return hidden_states, next_decoder_cache + + +class MistralForCausalLM(nn.Module): + def __init__( + self, config: MistralConfig, vocab_size_var: tvm.tir.SizeVar, sep_embed: bool = False + ): + self.model = MistralModel(config, vocab_size_var, sep_embed) + self.lm_head = Linear(config.hidden_size, vocab_size_var, dtype=config.dtype, bias=False) + + ############ Rotary embedding constants ############ + assert config.hidden_size % config.num_attention_heads == 0 + head_dim = config.hidden_size // config.num_attention_heads + + # Set the cached sin/cos to the maximum of 2048 and max seq len. + # This will be eliminated further with online rotary embedding calculation. + rope_cache_len = te.var("rope_cache_len", "int64") + self.cos_cached = nn.Parameter( + (rope_cache_len, head_dim), dtype=config.dtype, name="cos_cached" + ) + self.sin_cached = nn.Parameter( + (rope_cache_len, head_dim), dtype=config.dtype, name="sin_cached" + ) + ############ End ############ + + def forward( + self, + inputs: relax.Expr, + cache_len_shape: relax.Expr, + kv_seq_len_shape: relax.Expr, + cache_offset_shape: relax.Expr, + past_key_values: relax.Expr, + ): + hidden_states, key_value_cache = self.model( + inputs=inputs, + cache_len_shape=cache_len_shape, + kv_seq_len_shape=kv_seq_len_shape, + cache_offset_shape=cache_offset_shape, + past_key_values=past_key_values, + ) + + def te_slicing(x: te.Tensor): + return te.compute( + shape=(1, 1, x.shape[-1]), + fcompute=lambda i, j, k: x[i, x.shape[1] - 1, k], + name="slice", + ) + + logits = self.lm_head(nn.emit_te(te_slicing, hidden_states, primfunc_name_hint="slice")) + if logits.struct_info.dtype != "float32": + logits = nn.emit(relax.op.astype(logits, "float32")) + + return logits, key_value_cache + + +def get_param_quant_kind(name: str, param_info: relax.TensorStructInfo) -> ParamQuantKind: + if "embed_tokens" in name: + return ParamQuantKind.embedding_table + elif "lm_head.weight" in name: + return ParamQuantKind.final_fc_weight + elif param_info.ndim == 2 and name.endswith(".weight"): + return ParamQuantKind.linear_weight + else: + return ParamQuantKind.others + + +def create_embed_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: MistralConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "embed" + + bsz = 1 + seq_len = tvm.tir.SizeVar("n", "int64") + with bb.function(func_name): + model = MistralEmbedTokensWrapper(config, tvm.tir.SizeVar("vocab_size", "int64")) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + input_ids = nn.Placeholder((bsz, seq_len), dtype="int32", name="input_ids") + with bb.dataflow(): + inputs_embeds = model(input_ids) + params = [input_ids] + model.parameters() + gv = bb.emit_output(inputs_embeds) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 1)) + + +def create_encoding_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: MistralConfig, + quant_scheme: QuantizationScheme, + sep_embed: bool = False, +) -> None: + func_name = "prefill_with_embed" if sep_embed else "prefill" + + bsz = 1 + seq_len = tvm.tir.SizeVar("n", "int64") # number of tokens for the input + rolling_cache_len = tvm.tir.SizeVar( + "c", "int64" + ) # rolling_cache_len captures number of elements in the cache + kv_seq_len = tvm.tir.SizeVar( + "k", "int64" + ) # kv_seq_len captures number of elements in cache + seq_len + cache_offset = tvm.tir.SizeVar("o", "int64") # slidinf window kv cache offset + + hidden_size = config.hidden_size + with bb.function(func_name): + model = MistralForCausalLM(config, tvm.tir.SizeVar("vocab_size", "int64"), sep_embed) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + inputs = ( + nn.Placeholder((bsz, seq_len, hidden_size), dtype=config.dtype, name="inputs_embeds") + if sep_embed + else nn.Placeholder((bsz, seq_len), dtype="int32", name="input_ids") + ) + cache_len_shape = relax.Var( + "rolling_cache_len", relax.ShapeStructInfo((rolling_cache_len,)) + ) + kv_seq_len_shape = relax.Var("kv_seq_len", relax.ShapeStructInfo((kv_seq_len,))) + cache_offset_shape = relax.Var("cache_offset", relax.ShapeStructInfo((cache_offset,))) + past_key_values = relax.Var( + "kv_cache", + relax.TupleStructInfo( + [relax.ObjectStructInfo() for _ in range(config.num_hidden_layers * 2)] + ), + ) + with bb.dataflow(): + logits, key_value_cache = model( + inputs, + cache_len_shape, + kv_seq_len_shape, + cache_offset_shape, + past_key_values=past_key_values, + ) + params = [ + inputs, + cache_len_shape, + kv_seq_len_shape, + cache_offset_shape, + past_key_values, + ] + model.parameters() + gv = bb.emit_output((logits, relax.Tuple(key_value_cache))) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 5)) + + +def create_decoding_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: MistralConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "decode" + + bsz = 1 + rolling_cache_len = tvm.tir.SizeVar( + "c", "int64" + ) # rolling_cache_len captures number of elements in the cache + kv_seq_len = tvm.tir.SizeVar( + "k", "int64" + ) # kv_seq_len captures number of elements in cache + seq_len + cache_offset = tvm.tir.SizeVar("o", "int64") # sliding window kv cache offset + + with bb.function(func_name): + model = MistralForCausalLM(config, tvm.tir.SizeVar("vocab_size", "int64")) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + input_ids = nn.Placeholder((bsz, 1), dtype="int32", name="input_ids") + cache_len_shape = relax.Var( + "rolling_cache_len", relax.ShapeStructInfo((rolling_cache_len,)) + ) + kv_seq_len_shape = relax.Var("kv_seq_len", relax.ShapeStructInfo((kv_seq_len,))) + cache_offset_shape = relax.Var("cache_offset", relax.ShapeStructInfo((cache_offset,))) + past_key_values = relax.Var( + "kv_cache", + relax.TupleStructInfo( + [relax.ObjectStructInfo() for _ in range(config.num_hidden_layers * 2)] + ), + ) + with bb.dataflow(): + logits, key_value_cache = model( + input_ids, + cache_len_shape, + kv_seq_len_shape, + cache_offset_shape, + past_key_values=past_key_values, + ) + params = [ + input_ids, + cache_len_shape, + kv_seq_len_shape, + cache_offset_shape, + past_key_values, + ] + model.parameters() + gv = bb.emit_output((logits, relax.Tuple(key_value_cache))) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 5)) + + +def create_kv_cache_func(bb: relax.BlockBuilder, config: MistralConfig) -> None: + num_key_value_heads = config.get_num_key_value_heads() // config.num_shards + init_shape = relax.ShapeExpr( + ( + config.sliding_window, + num_key_value_heads, + config.hidden_size // config.num_attention_heads, # head_dim + ) + ) + with bb.function("create_kv_cache", []): + with bb.dataflow(): + zeros = bb.emit(relax.op.zeros(init_shape, config.dtype)) + caches = [] + f_kv_cache_create = relax.extern("vm.builtin.attention_kv_cache_create") + for _ in range(config.num_hidden_layers * 2): + caches.append( + bb.emit( + relax.call_pure_packed( + f_kv_cache_create, + args=[zeros, init_shape, relax.PrimValue(0)], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + ) + gv = bb.emit_output(caches) + bb.emit_func_output(gv) + + +def create_softmax_func(bb: relax.BlockBuilder, config: MistralConfig) -> None: + with bb.function("softmax_with_temperature"): + logits = nn.Placeholder( + (1, 1, tvm.tir.SizeVar("vocab_size", "int64")), dtype="float32", name="logits" + ) + temperature = nn.Placeholder((), dtype="float32", name="temperature") + with bb.dataflow(): + div = bb.emit(relax.op.divide(logits, temperature)) + softmax = bb.emit(relax.op.nn.softmax(div, axis=-1)) + gv = bb.emit_output(softmax) + bb.emit_func_output(gv, [logits, temperature]) + + +def get_model(args, hf_config): + model_name = args.model + dtype = args.quantization.model_dtype + sep_embed = args.sep_embed + assert not sep_embed, "Mistral does not support separate embedding." + + if args.sliding_window != -1: + hf_config["sliding_window"] = args.sliding_window + if args.attention_sink_size > 0: + hf_config["attention_sink_size"] = args.attention_sink_size + if args.max_seq_len != -1: + hf_config["max_sequence_length"] = args.max_seq_len + + config = MistralConfig( + **hf_config, + dtype=dtype, + combine_matmul=True, + num_shards=args.num_shards, + build_model_only=args.build_model_only, + ) + + # prefill chunk size same as sliding window by default + if args.prefill_chunk_size < 1: + args.prefill_chunk_size = config.sliding_window - config.attention_sink_size + + assert config.sliding_window != -1 + assert args.prefill_chunk_size <= config.sliding_window - config.attention_sink_size + + param_manager = ParamManager() + bb = relax.BlockBuilder() + + create_encoding_func(bb, param_manager, config, args.quantization, sep_embed) + create_decoding_func(bb, param_manager, config, args.quantization) + create_kv_cache_func(bb, config) + create_softmax_func(bb, config) + create_metadata_func( + bb, + model_name=model_name, + max_window_size=config.max_sequence_length, + stop_tokens=[2], + add_prefix_space=False, + sliding_window=config.sliding_window, + prefill_chunk_size=args.prefill_chunk_size, + ) + + mod = bb.get() + for gv in mod.functions: + func = mod[gv] + if isinstance(func, relax.Function): + mod[gv] = func.with_attr( + "tir_var_upper_bound", + { + "n": args.prefill_chunk_size, + "c": config.sliding_window, + "k": config.sliding_window + args.prefill_chunk_size, + }, + ) + + if args.build_model_only: + return mod, param_manager, None, config + + def f_convert_pname_fwd(pname: str) -> List[str]: + if not config.combine_matmul: + return [pname] + + qkv_str = "query_key_value_proj" + gate_up_str = "gate_up_proj" + if qkv_str in pname: + return [ + pname.replace(qkv_str, "q_proj"), + pname.replace(qkv_str, "k_proj"), + pname.replace(qkv_str, "v_proj"), + ] + elif gate_up_str in pname: + return [ + pname.replace(gate_up_str, "gate_proj"), + pname.replace(gate_up_str, "up_proj"), + ] + else: + return [pname] + + def f_convert_param_bkwd(torch_pname: str, torch_param): + if not config.combine_matmul: + return [(torch_pname, torch_param.astype(dtype))] + + combined_layers = ["q_proj", "k_proj", "v_proj", "gate_proj", "up_proj"] + if any([name in torch_pname for name in combined_layers]): + return None + return [(torch_pname, torch_param.astype(dtype))] + + def f_compute_relax_param(relax_pname: str, torch_params: List[Any]): + # Expected to enter this function only for the combined linear matmul weights. + # Other weights are supposed to be loaded in `f_convert_param_bkwd` since + # each other relax param has a unique corresponding torch param. + if not config.combine_matmul: + # When matmul combination is not turned on, each relax param has a unique + # corresponding torch param, and this function is not expected to be entered. + raise NotImplementedError( + "Matmul combination is not turned on, and the function " + "is not expected to be entered" + ) + hidden_size = config.hidden_size + head_dim = config.hidden_size // config.num_attention_heads + + if "query_key_value_proj" in relax_pname: + q_heads = config.num_attention_heads + kv_heads = config.get_num_key_value_heads() + q, k, v = torch_params + assert q.shape == (q_heads * head_dim, hidden_size) + assert k.shape == (kv_heads * head_dim, hidden_size) + assert v.shape == (kv_heads * head_dim, hidden_size) + qkv = np.concatenate([q, k, v], axis=0).astype(dtype) + return qkv + if "gate_up_proj" in relax_pname: + gate, up = torch_params + gate_up = np.concatenate([gate, up], axis=0).astype(dtype) + return gate_up + raise ValueError("Unexpected param loading") + + param_manager.set_param_loading_func( + args.model_path, + args.use_safetensors, + f_convert_pname_fwd, + f_convert_param_bkwd, + f_compute_relax_param, + ) + + device = tvm.cpu() + param_list = [None] * param_manager.nparam_to_load + + head_dim = config.hidden_size / config.num_attention_heads + inv_freq = 1.0 / (config.rope_theta ** (np.arange(0, head_dim, 2).astype("float32") / head_dim)) + + # The following cos/sin values can be removed but **are kept for compatibility issues**. + t = np.arange(2048, dtype=inv_freq.dtype) + freqs = np.einsum("i,j->ij", t, inv_freq) + emb = np.concatenate((freqs, freqs), axis=-1) + param_list[-2] = tvm.nd.array(np.cos(emb).astype(config.dtype), device) + param_list[-1] = tvm.nd.array(np.sin(emb).astype(config.dtype), device) + + return mod, param_manager, param_list, config diff --git a/mlc_llm/relax_model/modules.py b/mlc_llm/relax_model/modules.py new file mode 100644 index 0000000..e506938 --- /dev/null +++ b/mlc_llm/relax_model/modules.py @@ -0,0 +1,280 @@ +# pylint: disable=missing-docstring,invalid-name +from typing import Dict, List, Tuple, Optional + +import numpy as np +from tvm import relax, te, tir +from tvm.relax.op import matmul, permute_dims, reshape, take +from tvm.relax.op.nn import layer_norm +from tvm.relax.testing import nn +from tvm.runtime.ndarray import array as tvm_array + + +class ModuleList(nn.Module): + def __init__(self, modules: List[nn.Module]): + self.modules = modules + + def __iter__(self): + return iter(self.modules) + + def __getitem__(self, idx): + return self.modules[idx] + + def __len__(self): + return len(self.modules) + + def forward(self, x: relax.Expr) -> relax.Var: + for module in self.modules: + x = module(x) + return x + + +class Linear(nn.Module): + def __init__( + self, + in_features, + out_features, + dtype, + bias=True, + out_dtype=None, + ): + self.in_features = in_features + self.out_features = out_features + self.weight = nn.Parameter( + (out_features, in_features), + dtype=dtype, + name="linear_weight", + ) + if bias: + self.bias = nn.Parameter( + (out_features,), + dtype=dtype if out_dtype is None else out_dtype, + name="linear_bias", + ) + else: + self.bias = None + self.dtype = dtype + self.out_dtype = out_dtype + + def forward(self, x: relax.Expr) -> relax.Var: + x = nn.emit(x) + weight = permute_dims(self.weight, axes=None) + x = nn.emit(matmul(x, weight, out_dtype=self.out_dtype)) + if self.bias is not None: + x = nn.emit(x + self.bias) + return x + + +class Embedding(nn.Module): + def __init__(self, num_embeddings, embedding_dim, dtype): + self.num_embeddings = num_embeddings + self.embedding_dim = embedding_dim + self.weight = nn.Parameter( + (num_embeddings, embedding_dim), dtype=dtype, name="weight" + ) + + def forward(self, x: relax.Expr) -> relax.Var: + ndim = x.struct_info.ndim + if ndim == 1: + return nn.emit(take(self.weight, x, axis=0)) + x_shape = x.struct_info.shape.values + emb_size = self.weight.struct_info.shape.values[-1] + x = nn.emit(reshape(x, shape=[-1])) + embedding = nn.emit(take(self.weight, x, axis=0)) + return nn.emit(reshape(embedding, [*x_shape, emb_size])) + + +class LayerNorm(nn.Module): + def __init__( + self, + hidden_size, + dtype, + eps=1e-5, + ): + super().__init__() + self.eps = eps + self.weight = nn.Parameter((hidden_size,), dtype="float32", name="weight") + self.bias = nn.Parameter((hidden_size,), dtype="float32", name="bias") + + def forward(self, x: relax.Expr) -> relax.Var: + if x.struct_info.dtype != "float32": + x = nn.emit(relax.op.astype(x, "float32")) + x = nn.emit( + layer_norm( + x, + gamma=self.weight, + beta=self.bias, + axes=-1, + epsilon=self.eps, + ) + ) + return x + + +class RotaryEmbedding(nn.Module): + def __init__( + self, + hidden_size: int, + num_attention_heads: int, + position_embedding_base: int, + max_sequence_length: int, + rotary_pct: Optional[float] = None, + rotary_dim: Optional[int] = None, + swizzle_style: str = "neox", + dtype: str = "float32", + ): + super().__init__() + head_dim = hidden_size // num_attention_heads + if rotary_dim is not None: + rotary_ndim = rotary_dim + else: + rotary_ndim = int(head_dim * rotary_pct) + inv_freq = 1.0 / ( + position_embedding_base + ** (np.arange(0, rotary_ndim, 2).astype("float32") / rotary_ndim) + ) + t = np.arange(max_sequence_length, dtype=inv_freq.dtype) + freq = np.einsum("i,j->ij", t, inv_freq) + if swizzle_style == "neox": + emb = np.concatenate((freq, freq), axis=-1) + elif swizzle_style in ("gptj", "glm"): + emb = np.repeat(freq, repeats=2, axis=-1) + else: + raise KeyError("Unrecognized swizzle style {}".format(swizzle_style)) + self.swizzle_style = swizzle_style + self.rotary_ndim = rotary_ndim + self.cos_cached = relax.const(tvm_array(np.cos(emb).astype(dtype))) + self.sin_cached = relax.const(tvm_array(np.sin(emb).astype(dtype))) + + def get_x_swizzle(self, x, i_batch_size, i_seq_len, i_num_heads, i_head_dim): + if self.swizzle_style == "neox": + n_feat_half = self.rotary_ndim // 2 + return tir.Select( + i_head_dim < n_feat_half, + -x[ + i_batch_size, + i_seq_len, + i_num_heads, + i_head_dim + n_feat_half, + ], + x[ + i_batch_size, + i_seq_len, + i_num_heads, + i_head_dim - n_feat_half, + ], + ) + elif self.swizzle_style in ("gptj", "glm"): + return tir.Select( + i_head_dim % 2 == 0, + -x[i_batch_size, i_seq_len, i_num_heads, i_head_dim + 1], + x[i_batch_size, i_seq_len, i_num_heads, i_head_dim - 1], + ) + else: + raise KeyError("Unrecognized swizzle style: {}.".format(self.swizzle_style)) + + def forward( + self, + q: relax.Expr, + k: relax.Expr, + offset: relax.Expr, + ) -> Tuple[relax.Expr, relax.Expr]: + def rotary_embedding(x, cos, sin, offset): + def compute( + i_batch_size, + i_seq_len, + i_num_heads, + i_head_dim, + ): + return tir.Select( + i_head_dim < self.rotary_ndim, + cos[ + offset + i_seq_len, + i_head_dim, + ] + * x(i_batch_size, i_seq_len, i_num_heads, i_head_dim) + + sin[ + offset + i_seq_len, + i_head_dim, + ] + * self.get_x_swizzle( + x, i_batch_size, i_seq_len, i_num_heads, i_head_dim + ), + x(i_batch_size, i_seq_len, i_num_heads, i_head_dim), + ) + + return te.compute(x.shape, compute, name="rotary") + + cos, sin = self.cos_cached, self.sin_cached + q_embed = nn.emit_te( + rotary_embedding, + q, + cos, + sin, + offset, + primfunc_name_hint="rotary_embedding", + ) + k_embed = nn.emit_te( + rotary_embedding, + k, + cos, + sin, + offset, + primfunc_name_hint="rotary_embedding", + ) + return q_embed, k_embed + + +class TransformImage(nn.Module): + def __init__(self, dtype: str, in_chans: int = 4): + self.in_chans = in_chans + self.dtype = dtype + + # used in normalization, assume channels are RGB + self.r_mean = relax.const(0.48145466, "float32") + self.g_mean = relax.const(0.4578275, "float32") + self.b_mean = relax.const(0.40821073, "float32") + self.r_std = relax.const(0.26862954, "float32") + self.g_std = relax.const(0.26130258, "float32") + self.b_std = relax.const(0.27577711, "float32") + + def forward(self, input: relax.Expr) -> relax.Expr: + from tvm.relax.op import astype, concat, permute_dims, strided_slice + + assert input.struct_info.ndim == 4 + # perform torch.ToTensor on input of shape (bs, height, width, in_chans) + input = permute_dims(input, [0, 3, 1, 2]) + x = astype(input, "float32") / relax.const(255.0, "float32") + r = strided_slice(x, axes=[1], begin=[0], end=[1]) + g = strided_slice(x, axes=[1], begin=[1], end=[2]) + b = strided_slice(x, axes=[1], begin=[2], end=[3]) + + # normalize rgba to rgb + if self.in_chans == 4: + a = strided_slice(x, axes=[1], begin=[3], end=[4]) + r /= a + g /= a + b /= a + + # perform torch.Normalize + r = (r - self.r_mean) / self.r_std + g = (g - self.g_mean) / self.g_std + b = (b - self.b_mean) / self.b_std + res = concat([r, g, b], axis=1) + res = astype(res, self.dtype) + + return res + + +def named_parameters(model: nn.Module) -> Dict[str, nn.Parameter]: + params: Dict[str, nn.Parameter] = {} + for name, module in model.__dict__.items(): + if isinstance(module, nn.Parameter): + params[name] = module + elif isinstance(module, ModuleList): + for i, m in enumerate(module): + for param_name, param in named_parameters(m).items(): + params[f"{name}.{i}.{param_name}"] = param + elif isinstance(module, nn.Module): + for param_name, param in named_parameters(module).items(): + params[f"{name}.{param_name}"] = param + return params diff --git a/mlc_llm/relax_model/param_manager.py b/mlc_llm/relax_model/param_manager.py new file mode 100644 index 0000000..f776db3 --- /dev/null +++ b/mlc_llm/relax_model/param_manager.py @@ -0,0 +1,1209 @@ +import json +import os +from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union + +import tvm +from torch import Tensor as torchTensor +from tvm import relax, tir +from tvm._ffi.runtime_ctypes import Device +from tvm.relax.analysis import remove_all_unused +from tvm.relax.expr import Expr, Function, Var +from tvm.relax.expr_functor import PyExprMutator, mutator +from tvm.relax.testing import nn + +from .. import quantization +from .modules import named_parameters +from ..transform import ReorderTransformFunc + + +def f_default_compute_relax_param(relax_pname: str, torch_params: List[Any]) -> Any: + """The defualt `f_compute_relax_param` for ParamManager. + See ParamManager for more details. + """ + raise NotImplementedError() + + +class Parameter: + """The abstraction of weight tensors (e.g., linear layer weight, embedding + table, etc.) in a model. + + Attributes + ---------- + name : str + The name of the parameter. + The name of a weight is got by `named_parameters()` method, similar to + PyTorch's `named_parameters()` function. + An example name is `model.layers.11.self_attn.k_proj.weight`. + In a model, the name is the **unique** identifier of a parameter. + + param_info_dict : Dict[str, relax.TensorStructInfo] + The shape and dtype of the parameter in each function. + The shape can be accessed by `param_info_dict[func_name].shape`, which is + a relax.ShapeExpr instance. + And the dtype can be accessed by `param_info_dict[func_name].dtype`, + which is a Python string. + + quant_spec : quantization.QuantizationSpec + The quantization specification of this parameter. + It specifies the algorithm to quantize and dequantize this parameter (or + this parameter does not need quantization). + + shard_dim : Optional[int] + The dimension to be sharded. + + shard_strategy : Optional[str] + The strategy to shard the parameter. + """ + + name: str + param_info_dict: Dict[str, relax.TensorStructInfo] + quant_spec: quantization.QuantizationSpec + shard_dim: Optional[int] + shard_strategy: Optional[str] + + def __init__( + self, + name: str, + quant_spec: quantization.QuantizationSpec, + shard_dim: Optional[int], + shard_strategy: Optional[str], + ) -> None: + self.name = name + self.param_info_dict = dict() + self.quant_spec = quant_spec + self.shard_dim = shard_dim + self.shard_strategy = shard_strategy + + def register_func(self, func_name: str, param_info: relax.TensorStructInfo): + self.param_info_dict[func_name] = param_info + + @property + def param_info(self): + """Return the shape and dtype of the parameter (in some arbitrary function).""" + return next(iter(self.param_info_dict.values())) + + +class ParamManager: + """The model-wise data structure which contains the information of every + weight in the model and is in charge of applying quantization and dequantization + to the parameters at the entire model level. + + Attributes + ---------- + params : Dict[str, Parameter] + The mapping from parameter names to parameters. + + param_names : List[str] + The name list of all the parameters. + To enforce a unique order or all the parameters for determinism, the + parameter names are kept in the list, and the parameter order is + uniquely determined by the parameter name list. + + func_raw_param_map : Dict[relax.Var, Tuple[str, Parameter]] + The mapping from each relax.Var that denotes a weight parameter to the + name of the function the var is in (e.g., "prefill" or "decode"), and + the Parameter it corresponds to. + This mapping is used for applying quantization transformation to the + Relax functions (e.g., the "prefill", "decode", etc.) in the model. + + param2qrange : Dict[Parameter, range] + The mapping from each parameter to the range of its quantized tensors + in the list of quantized tensors of all parameters. + Each parameter is quantized into multiple tensors. + For example, assume we have parameters `p0`, `p1`, `p2`. + - `p0` is quantized into `t0_0`, `t0_1`, + - `p1` is quantized into `t1_0`, and + - `p2` is quantized into `t2_0`, `t2_1` and `t2_2`. + Then the list of all quantized tensors is `[t0_0, t0_1, t1_0, t2_0, t2_1, t2_2]`, + and the dict `param2qrange` is + `{p0: range(0, 2), p1: range(2, 3), p2: range(3, 6)}`. + + f_convert_pname_fwd : Callable[[str], List[str]] + The function which converts Relax parameter name (ours) to torch's + parameter names, suggesting "to load this Relax parameter, which torch + parameter(s) are needed". + - Usually, the function maps a name to itself. For example, in LLaMA we + map `lm_head.weight` itself, as the parameter has the same name on both + Relax side and torch side. + - In some cases we map a name to multiple names. For example, if we + support combined QKV computing when the torch side separates them, on + Relax side we only have one QKV weight, while on torch side we have + one weight for each of Q, K, V. In this case, we map one name to three + names. + - In some cases we map a name to a single name which is other than + itself. This can happen either when the Relax nn.Module has different + param names than the torch's implementation so we need to map names + for connection, or when a Relax parameter is computed out from a torch + parameter. For example, if the torch implementation supports combined + QKV while the Relax one does not, we need compute the relax parameter + out from torch's parameter. In this case we map the relax parameter + name to the torch's parameter name. + + f_convert_param_bkwd : Callable[[str, Any], Optional[List[Tuple[str, Any]]]] + The function which converts torch parameter and param name back to + Relax parameters with names. `Any` here stands for numpy.ndarray. + - Usually, the function just returns the input torch parameter and + the corresponding Relax parameter's name. + - In some cases, we return multiple Relax parameters. For example, if + the torch implementation supports combined QKV while the Relax one does + not, the function takes torch's combined QKV weight, and return the + separated Q K V weights with their corresponding names. + - In some cases we return `None`. This happens when the input torch + parameter itself does not determine any Relax parameter. For example, + if we support combined QKV computing when the torch side separates them, + we return `None` here for the single Q, K, V weights, as by only having + a Q (or K, V) weight we cannot compute the combined QKV weight. + + f_compute_relax_param : Callable[[str, List[Any]], Any] + The function which computes a Relax parameter from a list of torch + parameters. `Any` here stands for numpy.ndarray. In the case when one + Relax parameter is computed from multiple torch parameters, this + functions is used. + For example, if we support combined QKV computing when the torch side + separates them, we use this function to combine the torch's Q, K, V + weights into one + In usual case, this function is not needed and by default it is + implemented by raising `NotImplementedError` (see f_default_compute_relax_param). + + model_path : str + The path of the Hugging Face model on disk. + + use_safetensors: bool + Whether to use `.safetensors` instead of `.bin` to load model. + + safetensors_load_func: Callable[[Union[str, os.PathLike], str], Dict[str, torch.Tensor]] + A reference to the function `load_file` improted from `safetensors.torch`. + The goal is to prevent repeatedly importing in a tvm registered function. + + pidx2pname : Dict[int, str] + The dictionary from each Relax parameter's index in `param_names` to + the Relax parameter's name. + + torch_pname2binname : Dict[str, str] + The dictionary from each torch parameter's name to the name of the + binary shard where the torch parameter is saved. + """ + + params: Dict[str, Parameter] + param_names: List[str] + func_raw_param_map: Dict[relax.Var, Tuple[str, Parameter]] + param2qrange: Dict[Parameter, range] + + qspec_updater_classes: List[quantization.QuantSpecUpdater] + + nparam_to_load: int + f_convert_pname_fwd: Callable[[str], List[str]] + f_convert_param_bkwd: Callable[[str, Any], Optional[List[Tuple[str, Any]]]] + f_compute_relax_param: Callable[[str, List[Any]], Any] + f_run_prequantize: Optional[Callable[[str], str]] + + model_path: str + use_safetensors: bool + safetensors_load_func: Callable[[Union[str, os.PathLike], str], Dict[str, torchTensor]] + pidx2pname: Dict[int, str] + torch_pname2binname: Dict[str, str] + + def __init__(self) -> None: + self.params = {} + self.param_names = [] + self.params_in_func = {} + + self.func_raw_param_map = {} + self.param2qrange = None + + self.nparam_to_load = None + self.f_convert_pname_fwd = None + self.f_convert_param_bkwd = None + self.f_compute_relax_param = None + self.f_run_prequantize = None + + self.qspec_updater_classes = [] + + def register_params( + self, + model: nn.Module, + func_name: str, + quantization_scheme: quantization.QuantizationScheme, + f_get_param_quant_kind: Callable[ + [str, relax.TensorStructInfo], quantization.ParamQuantKind + ], + ) -> None: + """Register the parameters of the input model (within the context of the + input function) in the parameter manager. + + Parameters + ---------- + model : nn.Module + The input model whose parameters are registered. + + func_name : str + The name of the function the input model is in. + For example, the "prefill" function or the "decode" function. + + quantization_scheme : quantization.QuantizationScheme + The quantization scheme of the input model, which describes how + to quantize the model. + + f_get_param_quant_kind: Callable[[str, relax.TensorStructInfo], quantization.ParamQuantKind] + A function which takes the name and StructInfo (effectively shape + and dtype) of a parameter, and returns which quantization kind this + parameter uses. + This is used for applying quantization to the parameters. + """ + if quantization_scheme.qspec_updater_class is not None: + self.qspec_updater_classes.append(quantization_scheme.qspec_updater_class) + if quantization_scheme.f_convert_param_bkwd is not None: + self.f_convert_param_bkwd = quantization_scheme.f_convert_param_bkwd + if quantization_scheme.f_compute_relax_param is not None: + self.f_compute_relax_param = quantization_scheme.f_compute_relax_param + if quantization_scheme.f_run_prequantize is not None: + self.f_run_prequantize = quantization_scheme.f_run_prequantize + + self.params_in_func[func_name] = [] + # For each parameter in the input model, get its quantization kind and + # register the parameter with its name and quantization kind. + for name, relax_param in named_parameters(model).items(): + quant_kind = f_get_param_quant_kind(name, relax_param.struct_info) + param = self._register_param( + name, + relax_param, + getattr(quantization_scheme, quant_kind.name), + func_name, + relax_param.__dict__.get("shard_dim", None), + relax_param.__dict__.get("shard_strategy", None), + ) + + self.params_in_func[func_name].append(param) + + def run_pre_quantize(self, model_path: str): + if self.f_run_prequantize is not None: + model_path = self.f_run_prequantize(model_path) + + self.model_path = model_path + return model_path + + def init_torch_pname_to_bin_name(self, use_safetensors: bool): + assert hasattr(self, "model_path"), ( + "Must call either set_param_loading_func or run_pre_quantize " + "before init_torch_pname_to_bin_name" + ) + + if self.pidx2pname: + mapping = load_torch_pname2binname_map( + self.model_path, + use_safetensors, + set(self.pidx2pname.values()), + self.f_convert_pname_fwd, + ) + else: + mapping = {} + + self.torch_pname2binname = mapping + + def set_param_loading_func( + self, + model_path: str, + use_safetensors: bool, + f_convert_pname_fwd: Callable[[str], List[str]] = lambda pname: [pname], + f_convert_param_bkwd: Callable[ + [str, Any], Optional[List[Tuple[str, Any]]] + ] = lambda pname, torch_param: [(pname, torch_param)], + f_compute_relax_param: Callable[[str, List[Any]], Any] = f_default_compute_relax_param, + *, + no_lazy_param_loading: bool = False, + ) -> None: + """Set the parameter loading functions. + + Parameters + ---------- + model_path : str + The path of the Hugging Face model on disk. + + use_safetensors : bool + Whether to use ``.safetensors`` instead of ``.bin`` to load model. + + f_convert_pname_fwd : Callable[[str], List[str]] + The function which converts Relax parameter name (ours) to torch's + parameter names. See the document of ParamManager for more details. + + f_convert_param_bkwd : Callable[[str, Any], Optional[List[Tuple[str, Any]]]] + The function which converts torch parameter and param name back to + Relax parameters with names. `Any` here stands for numpy.ndarray. + See the document of ParamManager for more details. + + f_compute_relax_param : Callable[[str, List[Any]], Any] + The function which computes a Relax parameter from a list of torch + parameters. `Any` here stands for numpy.ndarray. + See the document of ParamManager for more details. + + no_lazy_param_loading : bool + A boolean indicating that no lazy parameter loading from torch is needed. + This needs to be set as True when all the model weights are loaded + at the time of constructing the model. + """ + self.f_convert_pname_fwd = f_convert_pname_fwd + if self.f_convert_param_bkwd is None: + self.f_convert_param_bkwd = f_convert_param_bkwd + if self.f_compute_relax_param is None: + self.f_compute_relax_param = f_compute_relax_param + + self.model_path = model_path + self.use_safetensors = use_safetensors + if self.use_safetensors: + # Use a pointer here to prevent repeated import in tvm registered function + from safetensors.torch import ( + load_file, # pylint: disable=import-outside-toplevel + ) + + def load_safetensors_func(*args): + params = load_file(*args) + for name, param in params.items(): + dtype = str(param.dtype) + if dtype == "torch.bfloat16": + param = param.float() + params[name] = param + return params + + self.safetensors_load_func = load_safetensors_func + + pnames_to_load = [] + for param_name in self.param_names: + param = self.params[param_name] + loaded_names, _ = param.quant_spec.get_loaded_tensor_info(param_name, param.param_info) + pnames_to_load += loaded_names + + self.nparam_to_load = len(pnames_to_load) + if not no_lazy_param_loading: + self.pidx2pname = {pidx: pname for pidx, pname in enumerate(pnames_to_load)} + else: + self.pidx2pname = dict() + + def transform_dequantize(self) -> tvm.ir.transform.Pass: + """Apply dequantization to the input IRModule. + + Parameters + ---------- + mod : tvm.IRModule + The input IRModule to be applied dequantization. + The IRModule contains all the constructed Relax functions + (e.g., the "prefill"/"decode" functions) and is expected to + have all of its parameters registered in the ParamManager. + + Returns + ------- + updated_mod : tvm.IRModule + The IRModule updated with the dequantization computation. + """ + + @tvm.ir.transform.module_pass(opt_level=0, name="ParamManager.transform_dequantize") + def transform_func(mod: tvm.IRModule, _context) -> tvm.IRModule: + # For each Relax function in the input IRModule (e.g., "prefill"), + # we create its input relax.Var of all the quantized data, and + # store the mapping from function name to the var. + func_name_to_quantized_params: Dict[str, List[relax.Var]] = {} + + for gv, func in mod.functions.items(): + if isinstance(func, relax.Function) and func.attrs and "num_input" in func.attrs: + func_name_to_quantized_params[gv.name_hint] = self.get_quantized_params( + gv.name_hint + ) + + # Cache mapping to avoid duplicate dequantization. + dequantized_cache: Dict[relax.Var, relax.Var] = {} + + # Define a var replacement function for applying dequantization. + def f_replace(var: relax.Var, bb: relax.BlockBuilder) -> relax.Var: + if var in dequantized_cache: + return dequantized_cache[var] + assert var in self.func_raw_param_map + + func_name, param = self.func_raw_param_map[var] + quantized_params = func_name_to_quantized_params[func_name] + relevant_quantized_params = [quantized_params[i] for i in self.param2qrange[param]] + + dequantized = self._dequantize(param, relevant_quantized_params, bb, func_name) + + dequantized_cache[var] = dequantized + return dequantized + + # Create the function mutator for applying dequantization. + replacer = ParamReplacer(mod, func_name_to_quantized_params, f_replace) + # Update the input IRModule with dequantization. + mod = replacer.transform() + + return mod + + return transform_func + + def get_quantized_params(self, func_name: str) -> List[relax.Var]: + quantized_params: List[relax.Var] = [] + + bb = relax.BlockBuilder() + with bb.function("main", []): + self.param2qrange = dict() + + for name in self.param_names: + param = self.params[name] + param_info = None + if func_name in param.param_info_dict: + param_info = param.param_info_dict[func_name] + else: + param_info = relax.TensorStructInfo( + tvm.ir.load_json(tvm.ir.save_json(param.param_info.shape)), + param.param_info.dtype, + ) + + loaded_tensor_names, loaded_tensor_info = param.quant_spec.get_loaded_tensor_info( + name, param_info + ) + + provided_tensor_vars: List[relax.Var] = [ + relax.Var(name, sinfo) + for name, sinfo in zip(loaded_tensor_names, loaded_tensor_info) + ] + + # Get the quantization function of this parameter. + f_quantize = param.quant_spec.get_quantize_func(param_info) + if f_quantize is None: + # If the parameter does not have a quantization function, either it + # does not need quantization or it is pre-quantized. + self.param2qrange[param] = range( + len(quantized_params), + len(quantized_params) + len(provided_tensor_vars), + ) + quantized_params.extend(provided_tensor_vars) + else: + # If the parameter has a quantization function, it is not expected + # to be pre-quantized. + assert len(provided_tensor_vars) == 1, ( + "A parameter with quantization function is not expected " + "to be pre-quantized." + ) + + # Apply the quantization function. + quantized_data = bb.normalize(f_quantize(bb, provided_tensor_vars)) + if isinstance(quantized_data.struct_info, relax.TupleStructInfo): + fields = quantized_data.struct_info.fields + n_tensor = len(fields) + assert n_tensor > 1 + # Record the range of quantized tensors of this parameter. + self.param2qrange[param] = range( + len(quantized_params), + len(quantized_params) + n_tensor, + ) + # Collect the quantized tensors to return. + quantized_params.extend( + relax.Var(f"{name}.{field.dtype}.{i}", field) + for i, field in enumerate(fields) + ) + + else: + field = quantized_data.struct_info + assert isinstance(field, relax.TensorStructInfo) + self.param2qrange[param] = range( + len(quantized_params), len(quantized_params) + 1 + ) + quantized_params.append(relax.Var(f"{name}.{field.dtype}", field)) + bb.emit_func_output(relax.const(0, "int64")) + + return quantized_params + + def get_param_get_item( + self, device: Device, model_params: List[Optional[tvm.nd.NDArray]] = [] + ) -> Callable: + """A wrapper function which returns the `get_item` + functions for parameter lazy loading. + + The return value of this function is intended to be registered + as `"get_item"`, for use in a module built with + `LazyTransformParams`. + + .. code-block:: python + + get_item = manager.get_param_get_item(tvm.cuda()) + tvm.register_func(func_name="get_item", f=get_item, override=True) + compiled_function() + + Parameters + ---------- + device : Device + + The device onto which tensor parameters should be loaded. + + model_params : List[Optional[tvm.nd.NDArray]] + + Any pre-loaded model parameters. For parameter at index + `i`, if `model_params[i]` already contains an array, that + array will be returned from `get_item`. Otherwise, the + parameter will be loaded either from disk, or from an + internal cache. + + Returns + ------- + get_item: Callable[[int], tvm.nd.NDArray] + + A function that accepts an index, and returns the tensor + parameter located at that index, loaded onto `device`. + + """ + import torch # pylint: disable=import-outside-toplevel + + assert self.f_convert_pname_fwd is not None + assert self.f_convert_param_bkwd is not None + assert self.f_compute_relax_param is not None + pname2pidx: Dict[str, int] = {pname: pidx for pidx, pname in self.pidx2pname.items()} + + # The set of indices of loaded parameters, serving for + # robustness guarantee to avoid one parameter being loaded for + # multiple times. + loaded_idx_set: Set[int] = set() + + # The set of torch binary filenames, serving for robustness guarantee + # to avoid one torch binary file being loaded for multiple times. + loaded_torch_bins: Set[str] = set() + + # The set of cached Relax parameters. + cached_relax_params: Dict[int, tvm.nd.NDArray] = {} + + # The set of cached torch parameters. `Any` here stands for + # numpy.ndarray. + cached_torch_params: Dict[str, Any] = {} + + device_cpu = tvm.cpu() + + def fetch_torch_param(torch_param): + if str(torch_param.dtype) == "torch.bfloat16": + # Convert to float32 first. + return torch_param.detach().cpu().float().numpy() + else: + return torch_param.detach().cpu().numpy() + + def load_torch_params_from_bin(torch_binname: str): + torch_binpath = os.path.join(self.model_path, torch_binname) + torch_params = None + if self.use_safetensors: + torch_params = self.safetensors_load_func(torch_binpath) + else: + torch_params = torch.load( + torch_binpath, + map_location=torch.device("cpu"), + ) + torch_param_names = list(torch_params.keys()) + for torch_param_name in torch_param_names: + torch_param = fetch_torch_param(torch_params[torch_param_name]) + del torch_params[torch_param_name] + + relax_params = self.f_convert_param_bkwd(torch_param_name, torch_param) + if relax_params is not None: + for param_name, param in relax_params: + if param_name not in pname2pidx.keys(): + continue + pidx = pname2pidx[param_name] + assert pidx not in cached_relax_params + cached_relax_params[pidx] = tvm.nd.array(param, device_cpu) + else: + assert torch_param_name not in cached_torch_params + cached_torch_params[torch_param_name] = torch_param + del torch_param + + def get_item(i): + # If the weight is already provided by `model_params`, directly use it + # and no need to load from binary file. + if model_params and len(model_params) > i and model_params[i] is not None: + assert i not in cached_relax_params + return tvm.nd.array(model_params[i], device=device) + + # Otherwise, we load the weight from its corresponding binary file. + assert i in self.pidx2pname + relax_pname = self.pidx2pname[i] + torch_pnames = self.f_convert_pname_fwd(relax_pname) + + if i not in cached_relax_params: + for torch_binname in [ + self.torch_pname2binname[torch_pname] for torch_pname in torch_pnames + ]: + if torch_binname in loaded_torch_bins: + continue + load_torch_params_from_bin(torch_binname) + loaded_torch_bins.add(torch_binname) + + if i not in cached_relax_params: + assert len(torch_pnames) > 1 + assert all([torch_pname in cached_torch_params] for torch_pname in torch_pnames) + cached_relax_params[i] = self.f_compute_relax_param( + relax_pname, + [cached_torch_params[torch_pname] for torch_pname in torch_pnames], + ) + for torch_pname in torch_pnames: + del cached_torch_params[torch_pname] + + assert i in cached_relax_params + assert i not in loaded_idx_set + param_on_device = tvm.nd.array(cached_relax_params[i], device=device) + loaded_idx_set.add(i) + del cached_relax_params[i] + return param_on_device + + return get_item + + def get_param_set_item(self) -> Tuple[Callable, List[tvm.nd.NDArray]]: + """A wrapper function which returns the `set_item` + functions for parameter lazy loading. + + The return value of this function is intended to be registered + as `"set_item"`, for use in a module built with + `LazyTransformParams`. + + .. code-block:: python + + set_item,loaded_params = manager.get_param_set_item() + tvm.register_func(func_name="set_item", f=set_item, override=True) + compiled_function() + # `loaded_params` is now fully populated + + Returns + ------- + set_item: Callable[[int,tvm.nd.NDArray]] + + A function that accepts an index and the return value at + that index. + + loaded_params: List[tvm.nd.NDArray] + + A list of loaded parameters, populated by `set_item`. + When initially returned, this list is empty. After + executing the compiled function with + `LazyTransformParams`, `loaded_params` will be + populated. + """ + device_cpu = tvm.cpu() + loaded_params: List[tvm.nd.NDArray] = [] + + def set_item(i: int, computed_param: tvm.nd.NDArray): + if len(loaded_params) <= i: + loaded_params.extend([None for _ in range(i - len(loaded_params) + 1)]) + loaded_params[i] = tvm.nd.array(computed_param, device=device_cpu) + + return set_item, loaded_params + + #################### Below are internally called methods #################### + + def _register_param( + self, + name: str, + var: relax.Var, + quant_spec: quantization.QuantizationSpec, + func_name: str, + shard_dim: Optional[int], + shard_strategy: Optional[str], + ) -> Parameter: + """Register a single parameter in the parameter manager. + In most cases, this method is not directly used outside this class: + it is called by `register_params` above. + + Parameters + ---------- + name : str + The name of the parameter to register. + Name serves as the unique identifier of the parameter. + + var : relax.Var + The parameter relax.Var on the nn.Module side. + + quant_spec : quantization.QuantizationSpec + The quantization specification of the parameter + + func_name : str + The name of the function the input var is in. + For example, the "prefill" function or the "decode" function. + + shard_dim : Optional[int] + The dimension along which the parameter is sharded. + + shard_strategy : Optional[str] + The strategy of sharding the parameter. + + Returns + ------- + param : Parameter + The registered Parameter. + """ + assert ( + var not in self.func_raw_param_map + ), "The input var is not supposed to be already registered." + assert isinstance( + var.struct_info.shape, relax.ShapeExpr + ), "The parameter to register is expected to have shape as a tuple" + + if name in self.params: + # When the input name appears in `self.params`, it means the input + # parameter has been previously registered in some other function. + # Thus, we check if the dtype, shape and the quantization specification + # of both sides are consistent. + param = self.params[name] + assert ( + param.quant_spec == quant_spec + ), "One parameter is expected to be quantized by single specification in all functions." + assert ( + param.param_info.dtype == var.struct_info.dtype + ), "Dtype mismatch of one parameter in two functions." + assert ( + param.param_info.ndim == var.struct_info.ndim + ), "Shape mismatch of one parameter in two functions." + for len0, len1 in zip(param.param_info.shape.values, var.struct_info.shape.values): + if isinstance(len0, tir.IntImm) and isinstance(len1, tir.IntImm): + assert ( + len0.value == len1.value + ), "Shape mismatch of one parameter in two functions." + else: + # Otherwise, the parameter is registered for the first time. + param = Parameter(name, quant_spec, shard_dim, shard_strategy) + self.params[name] = param + self.param_names.append(name) + + param.register_func(func_name, var.struct_info) + # Record the mapping from the input relax.Var to the function name and + # the parameter in the manager. + self.func_raw_param_map[var] = (func_name, param) + return param + + def _dequantize( + self, + param: Parameter, + qparams: List[relax.Var], + bb: relax.BlockBuilder, + func_name: str, + ) -> relax.Var: + """Applying dequantization to the input parameter. + This method is called by `transform_module` below, and is not + directly invoked outside the class. + + Parameters + ---------- + param : Parameter + The parameter whose quantized tensors are to be dequantized. + + qparams : List[relax.Var] + The relax.Var of the quantized tensors of all parameters in the model. + + Returns + ------- + The dequantized parameter, in the form of a relax.Var. + """ + # Get the dequantization function of this parameter. + f_dequantize = param.quant_spec.get_dequantize_func( + param_info=param.param_info_dict[func_name], + qparam_info=[qparam.struct_info for qparam in qparams], + ) + if f_dequantize is None: + # If the parameter does not have a dequantization function, its "quantized + # data" is expected to have only one element. + assert len(qparams) == 1, ( + "A parameter without dequantization function is expected not to have " + 'more than one "quantized data".' + ) + return qparams[0] + else: + # Apply the dequantization function. + return bb.emit(f_dequantize(bb, qparams)) + + def create_parameter_transformation(self, optimize_parameter_order: bool = True): + """Produce an IRModule that can transform the parameters + + Parameters + ---------- + optimize_parameter_order: bool + + If true, reorder the parameter transformations to + prioritize operations that use a currently-open file. If + false, transform the parameters in their default order. + + Returns + ------- + tvm.IRModule + The transformation module + + """ + mod = _create_quantize_func(self) + if optimize_parameter_order: + mod = self.optimize_transform_param_order()(mod) + return mod + + def optimize_transform_param_order(self) -> tvm.transform.Pass: + """Produce an transformation that optimizes for minimal memory footprint + + Returns + ------- + tvm.transform.Pass + The transformation + """ + return ReorderTransformFunc( + self.pidx2pname, + self.torch_pname2binname, + self.f_convert_pname_fwd, + ) + + +@mutator +class ParamReplacer(PyExprMutator): + """The function mutator that updates the model with dequantization. + + Attributes + ---------- + mod : tvm.IRModule + The IRModule of the model to be updated. + + func_name_to_quantized_params : Dict[str, List[relax.Var]] + The mapping from each function name to its input var of quantized data tuple. + + f_replace : Callable[[relax.Var, relax.BlockBuilder], relax.Var] + The function for updating a previous parameter in functions with dequantization. + + param_set : Set[relax.Var] + The set of previous parameters (before applying quantization and dequantization) + in the relax functions. + """ + + mod: tvm.IRModule + func_name_to_quantized_params: Dict[str, List[relax.Var]] + f_replace: Callable[[relax.Var, relax.BlockBuilder], relax.Var] + param_set: Set[relax.Var] + + cur_func_name: str + + def __init__( + self, + mod: tvm.IRModule, + func_name_to_quantized_params: Dict[str, relax.Var], + f_replace: Callable[[relax.Var, relax.BlockBuilder], relax.Var], + ): + super().__init__(mod) + self.mod = mod + self.func_name_to_quantized_params = func_name_to_quantized_params + self.f_replace = f_replace + self.cur_func_name = "" + + def transform(self) -> tvm.IRModule: + for gv, func in self.mod.functions.items(): + if not isinstance(func, relax.Function): + continue + if func.attrs is None or not "num_input" in func.attrs: + continue + + assert ( + gv.name_hint in self.func_name_to_quantized_params + ), f"{gv.name_hint} not in {self.func_name_to_quantized_params}" + updated_func = self.rewrite_func(func, self.func_name_to_quantized_params[gv.name_hint]) + updated_func = remove_all_unused(updated_func) + self.builder_.update_func(gv, updated_func) + return self.builder_.get() + + def rewrite_func(self, func: Function, quantized_params: List[relax.Var]) -> relax.Function: + num_input = int(func.attrs["num_input"]) + self.param_set = set(func.params[num_input:]) + + body = self.visit_expr(func.body) + return relax.Function( + params=func.params[:num_input] + quantized_params, + body=body, + ret_struct_info=func.ret_struct_info, + is_pure=func.is_pure, + attrs=func.attrs, + ) + + def visit_var_(self, var: Var) -> Expr: + if var in self.param_set: + return self.f_replace(var, self.builder_) + else: + return super().visit_var_(var) + + +################################################################## + + +def load_torch_pname2binname_map( + model_path: str, + use_safetensors: bool, + relax_pnames: Set[str], + f_convert_pname_fwd: Callable[[str], List[str]] = lambda pname: [pname], +) -> Dict[str, str]: + """Constructing the dictionary from each torch parameter's name to + the name of the binary shard where the torch parameter is saved. + + Parameters + ---------- + model_path : str + The path of the Hugging Face model on disk. + + use_safetensors: bool + Whether to use ``.safetensors`` instead of ``.bin`` to load model. + + relax_pnames: Set[str] + The name of the Relax parameters. + + f_convert_pname_fwd: Callable[[str], List[str]] + The function which converts Relax parameter name to torch's + parameter names. See ParamManager for more details. + """ + bin_idx_path = None + single_shard_file_name = None + if use_safetensors: + bin_idx_path = os.path.join(model_path, "model.safetensors.index.json") + single_shard_file_name = "model.safetensors" + else: + bin_idx_path = os.path.join(model_path, "pytorch_model.bin.index.json") + single_shard_file_name = "pytorch_model.bin" + single_shard_path = os.path.join(model_path, single_shard_file_name) + + if os.path.isfile(bin_idx_path): + # Multiple weight shards. + with open(bin_idx_path, "r") as f_torch_json: + torch_bin_json = json.load(f_torch_json) + torch_pname2binname = torch_bin_json["weight_map"] + elif os.path.isfile(single_shard_path): + # Single weight shard. + torch_pname2binname = { + torch_pname: single_shard_file_name + for relax_pname in relax_pnames + for torch_pname in f_convert_pname_fwd(relax_pname) + } + else: + suffix = ".safetensors" if use_safetensors else ".bin" + shard_names = [] + # Collect Scan every single file with the suffix + for filename in os.listdir(model_path): + if filename.endswith(suffix): + shard_names.append(filename) + if len(shard_names) == 1: + torch_pname2binname = { + torch_pname: shard_names[0] + for relax_pname in relax_pnames + for torch_pname in f_convert_pname_fwd(relax_pname) + } + else: + raise ValueError("Multiple weight shard files without json map is not supported") + return torch_pname2binname + + +def _create_quantize_func(param_manager: ParamManager) -> tvm.IRModule: + """Construct the Relax function which computes quantization. + This method is called by `transform_module` below, and is not + directly invoked outside the class. + + Parameters + ---------- + param_manager : ParamManager + The parameter manager which has all the parameter information. + + Returns + ------- + The created function which computes quantization. + Precisely, an IRModule which contains the main quantization Relax function + and a series of TIR functions is returned. + """ + bb = relax.BlockBuilder() + param2qrange = dict() + + # Construct the input of the function. + # We need a list of ranges for each + # parameter to get its corresponding tensors loaded from disk. + input_tensor_info: List[relax.TensorStructInfo] = [] + loaded_tensor_ranges: List[range] = [] + for name in param_manager.param_names: + param = param_manager.params[name] + _, loaded_tensor_info = param.quant_spec.get_loaded_tensor_info(name, param.param_info) + loaded_tensor_ranges.append( + range( + len(input_tensor_info), + len(input_tensor_info) + len(loaded_tensor_info), + ) + ) + input_tensor_info += loaded_tensor_info + raw_param_tuple = relax.Var("params", relax.TupleStructInfo(input_tensor_info)) + + with bb.function("transform_params", params=[raw_param_tuple]): + with bb.dataflow(): + quantized_params: List[relax.Var] = [] + for pidx, name in enumerate(param_manager.param_names): + param = param_manager.params[name] + param_vars: List[relax.Var] = [] + # Emit relax.TupleGetItem to get the raw parameters or pre-quantized params. + for loaded_tensor_idx in loaded_tensor_ranges[pidx]: + param_vars.append( + bb.emit(relax.TupleGetItem(raw_param_tuple, loaded_tensor_idx)) + ) + + # Get the quantization function of this parameter. + f_quantize = param.quant_spec.get_quantize_func(param.param_info) + if f_quantize is None: + # If the parameter does not have a quantization function, either it + # does not need quantization or it is pre-quantized. + param2qrange[param] = range( + len(quantized_params), + len(quantized_params) + len(param_vars), + ) + quantized_params += param_vars + else: + # If the parameter has a quantization function, it is not expected + # to be pre-quantized. + assert len(param_vars) == 1, ( + "A parameter with quantization function is not expected " + "to be pre-quantized." + ) + + # Apply the quantization function. + quantized_data = bb.emit(f_quantize(bb, param_vars)) + + if isinstance(quantized_data.struct_info, relax.TupleStructInfo): + n_tensor = len(quantized_data.struct_info.fields) + assert n_tensor > 1 + # Record the range of quantized tensors of this parameter. + param2qrange[param] = range( + len(quantized_params), len(quantized_params) + n_tensor + ) + # Collect the quantized tensors to return. + for i in range(n_tensor): + quantized_params.append(bb.emit(relax.TupleGetItem(quantized_data, i))) + else: + assert isinstance(quantized_data.struct_info, relax.TensorStructInfo) + param2qrange[param] = range( + len(quantized_params), len(quantized_params) + 1 + ) + quantized_params.append(quantized_data) + + output = bb.emit_output(relax.Tuple(quantized_params)) + bb.emit_func_output(output) + + mod = bb.get() + param_manager.param2qrange = param2qrange + # Return the created IRModule. + return bb.get() + + +def transform_params_for_each_rank( + mod: tvm.IRModule, num_shards: int, rank_argument_name: str = "rank_arg" +) -> tvm.IRModule: + """Update a parameter transform to apply across all ranks + + For use in generating a pre-sharded set of weights. Given a + parameter transformation that generates sharded model weights for + a single shard, produce a parameter transformation that generates + sharded model weights for each shard. + + Parameters + ---------- + mod: tvm.IRModule + + A module containing the parameter transformation function, + named "transform_params", along with any subroutines called by + the parameter transformation. + + num_shards: int + + The number of shards to generate. + + rank_argument_name: str + + The name of the argument that specifies the rank. Should be a + R.ShapeTuple with a single R.PrimStructInfo('int64'). + + Returns + ------- + tvm.IRModule + + The modified parameter transformation + """ + generic_transform = mod["transform_params"] + tensor_params = generic_transform.params[1:] + + bb = relax.BlockBuilder() + + with bb.function("transform_params", params=tensor_params): + output = [] + for rank in range(num_shards): + # TODO(Lunderberg): Implement this in terms of a + # generic utility that inlines local functions. + func = generic_transform + func = func.bind_params({rank_argument_name: relax.ShapeExpr([rank])}) + func = relax.utils.copy_with_new_vars(func) + func = func.bind_params( + {var: tensor_param for (var, tensor_param) in zip(func.params, tensor_params)} + ) + shard_tuple = func.body + output.extend([shard_tuple[i] for i in range(len(tensor_params))]) + + with bb.dataflow(): + gv = bb.emit_output(relax.Tuple(output)) + bb.emit_func_output(gv) + + mod["transform_params"] = bb.get()["transform_params"] + return mod + + +def chain_parameter_transforms(mod_a: tvm.IRModule, mod_b: tvm.IRModule) -> tvm.IRModule: + """Chain two sequential parameter transformations + + For use in manipulating sets of model weights. Given two + parameter transformations that could be applied sequentially, + produce a single parameter transformation whose output is the same + as applying the parameter transformations sequentially. + + + .. code-block:: python + + # Before + params_after_a = mod_a['transform_params'](orig_params) + params_after_b = mod_b['transform_params'](params_after_a) + + # After + mod_ab = chain_parameter_transforms(mod_a, mod_b) + params_after_b = mod_ab['transform_params'](orig_params) + + Parameters + ---------- + mod_a: tvm.IRModule + + The module containing the first parameter transformation. + + mod_b: tvm.IRModule + + The module containing the second parameter transformation. + + Returns + ------- + tvm.IRModule + + The module containing the output + + """ + func_a = mod_a["transform_params"] + func_b = mod_b["transform_params"] + + bb = relax.BlockBuilder() + + with bb.function("transform_params", params=func_a.params): + with bb.dataflow(): + # TODO(Lunderberg): Implement this in terms of a + # generic utility that inlines local functions. + func_a_output = bb.emit(func_a.body) + func_b_param_map = {param: expr for (param, expr) in zip(func_b.params, func_a_output)} + func_b_output = func_b.bind_params(func_b_param_map).body + gv = bb.emit_output(func_b_output) + bb.emit_func_output(gv) + + merged_transform_func = bb.get()["transform_params"] + + new_mod = { + **{ + gvar: func + for gvar, func in mod_a.functions.items() + if gvar.name_hint != "transform_params" + }, + **{ + gvar: func + for gvar, func in mod_b.functions.items() + if gvar.name_hint != "transform_params" + }, + "transform_params": merged_transform_func, + } + return tvm.IRModule(new_mod) diff --git a/mlc_llm/relax_model/rwkv.py b/mlc_llm/relax_model/rwkv.py new file mode 100644 index 0000000..5b47cc3 --- /dev/null +++ b/mlc_llm/relax_model/rwkv.py @@ -0,0 +1,641 @@ +# pylint: disable=missing-docstring,invalid-name +from dataclasses import dataclass +from typing import List, Literal, Tuple + +from tvm import relax, te, tir +from tvm.relax import Expr, op +from tvm.relax.testing import nn +from tvm.script import relax as R +from tvm.script import tir as T + +from ..quantization import ParamQuantKind, QuantizationScheme +from .commons import create_metadata_func +from .modules import ModuleList, Linear +from .param_manager import ParamManager + +# Reference: https://github.com/BlinkDL/RWKV-LM/blob/main/RWKV-v4/src/model_run.py + + +@dataclass +class RWKVConfig: + """The configuration class to store the configuration of a `RWKVModel`.""" + + num_hidden_layers: int + vocab_size: int + hidden_size: int + intermediate_size: int + rescale_every: int = 0 + layer_norm_epsilon: float = 1e-5 + max_sequence_length: int = 1024 + dtype: str = "float32" + + def __init__( + self, + num_hidden_layers: int, + vocab_size: int, + hidden_size: int, + intermediate_size: int, + rescale_every: int = 0, + layer_norm_epsilon: float = 1e-5, + context_length: int = 1024, + dtype: str = "float32", + **kwargs, + ) -> None: + self.num_hidden_layers = num_hidden_layers + self.vocab_size = vocab_size + self.hidden_size = hidden_size + self.intermediate_size = intermediate_size + self.rescale_every = rescale_every + self.layer_norm_epsilon = layer_norm_epsilon + self.max_sequence_length = context_length + self.dtype = dtype + self.kwargs = kwargs + + +class State: + ATT_X = 0 + ATT_A = 1 + ATT_B = 2 + ATT_P = 3 + FFN_X = 4 + + +def _load_state(state: Expr, hidden_size: int, dtype: str) -> Expr: + # Reuse `attention_kv_cache_view` + f_load_cache = relax.extern("vm.builtin.attention_kv_cache_view") + cache = nn.emit( + relax.call_pure_packed( + f_load_cache, + args=[state, R.shape([1, hidden_size])], + sinfo_args=[R.Tensor((1, hidden_size), dtype)], + ) + ) + return cache + + +def _store_state(state: Expr, value: Expr): + # Reuse `attention_kv_cache_update` + f_store_cache = relax.extern("vm.builtin.attention_kv_cache_update") + + return nn.emit( + relax.op.call_inplace_packed( + f_store_cache, + args=[state, value], + inplace_indices=[0], + sinfo_args=[R.Object()], + ) + ) + + +def is_one(x: tir.PrimExpr) -> bool: + return isinstance(x, tir.IntImm) and x.value == 1 + + +def create_wkv_func(hidden_size: int, dtype: str, out_dtype: str): + @T.prim_func + def wkv_func( + k: T.handle, + v: T.handle, + time_decay: T.handle, + time_first: T.handle, + saved_a: T.handle, + saved_b: T.handle, + saved_p: T.handle, + wkv: T.handle, + out_a: T.handle, + out_b: T.handle, + out_p: T.handle, + ): + T.func_attr({"op_pattern": 8, "tir.noalias": True, "tir.is_scheduled": 1}) + context_length = T.int64() + K = T.match_buffer(k, (context_length, hidden_size), dtype=dtype) + V = T.match_buffer(v, (context_length, hidden_size), dtype=dtype) + TimeDecay = T.match_buffer(time_decay, (hidden_size,), dtype=dtype) + TimeFirst = T.match_buffer(time_first, (hidden_size,), dtype=dtype) + SavedA = T.match_buffer(saved_a, (1, hidden_size), dtype=dtype) + SavedB = T.match_buffer(saved_b, (1, hidden_size), dtype=dtype) + SavedP = T.match_buffer(saved_p, (1, hidden_size), dtype=dtype) + WKV = T.match_buffer(wkv, (context_length, hidden_size), dtype=out_dtype) + OutA = T.match_buffer(out_a, (1, hidden_size), dtype=dtype) + OutB = T.match_buffer(out_b, (1, hidden_size), dtype=dtype) + OutP = T.match_buffer(out_p, (1, hidden_size), dtype=dtype) + + P = T.alloc_buffer((hidden_size,), dtype=dtype, scope="local") + E1 = T.alloc_buffer((hidden_size,), dtype=dtype, scope="local") + E2 = T.alloc_buffer((hidden_size,), dtype=dtype, scope="local") + A_local = T.alloc_buffer((hidden_size,), dtype=dtype, scope="local") + B_local = T.alloc_buffer((hidden_size,), dtype=dtype, scope="local") + P_local = T.alloc_buffer((hidden_size,), dtype=dtype, scope="local") + + for bx in T.thread_binding(hidden_size // 32, thread="blockIdx.x"): + for tx in T.thread_binding(32, thread="threadIdx.x"): + with T.block("init"): + vi = T.axis.S(hidden_size, bx * 32 + tx) + A_local[vi] = SavedA[0, vi] + B_local[vi] = SavedB[0, vi] + P_local[vi] = SavedP[0, vi] + for j in range(context_length): + with T.block("main"): + vi = T.axis.S(hidden_size, bx * 32 + tx) + vj = T.axis.opaque(context_length, j) + P[vi] = T.max(P_local[vi], K[vj, vi] + TimeFirst[vi]) + E1[vi] = T.exp(P_local[vi] - P[vi]) + E2[vi] = T.exp(K[vj, vi] + TimeFirst[vi] - P[vi]) + WKV[vj, vi] = T.cast( + (E1[vi] * A_local[vi] + E2[vi] * V[vj, vi]) + / (E1[vi] * B_local[vi] + E2[vi]), + out_dtype, + ) + + P[vi] = T.max(P_local[vi] + TimeDecay[vi], K[vj, vi]) + E1[vi] = T.exp(P_local[vi] + TimeDecay[vi] - P[vi]) + E2[vi] = T.exp(K[vj, vi] - P[vi]) + A_local[vi] = E1[vi] * A_local[vi] + E2[vi] * V[vj, vi] + B_local[vi] = E1[vi] * B_local[vi] + E2[vi] + P_local[vi] = P[vi] + + with T.block("write_back"): + vi = T.axis.S(hidden_size, bx * 32 + tx) + OutA[0, vi] = A_local[vi] + OutB[0, vi] = B_local[vi] + OutP[0, vi] = P_local[vi] + + return wkv_func + + +def _te_concat_saved_x(saved_x: te.Tensor, x: te.Tensor): + return te.compute( + x.shape, + lambda i, j: tir.if_then_else(i == 0, saved_x[0, j], x[i - 1, j]), + ) + + +def _te_get_last_x(x: te.Tensor): + seq_len, hidden_size = x.shape + return te.compute((1, hidden_size), lambda _, j: x[seq_len - 1, j]) + + +class RWKV_Embedding(nn.Module): + def __init__(self, num_embeddings, embedding_dim, dtype): + self.num_embeddings = num_embeddings + self.embedding_dim = embedding_dim + self.weight = nn.Parameter( + (num_embeddings, embedding_dim), dtype=dtype, name="weight" + ) + + def forward(self, x: relax.Expr) -> relax.Var: + x = nn.emit(op.reshape(x, shape=[-1])) + return nn.emit(op.take(self.weight, x, axis=0)) + + +class RWKV_LayerNorm(nn.Module): + def __init__(self, intermediate_size, dtype, eps=1e-5, name_prefix=""): + super().__init__() + self.eps = eps + self.weight = nn.Parameter( + (intermediate_size,), dtype=dtype, name=f"{name_prefix}_ln_weight" + ) + self.bias = nn.Parameter( + (intermediate_size,), dtype=dtype, name=f"{name_prefix}_ln_bias" + ) + + def forward(self, x: relax.Expr) -> relax.Var: + x = nn.emit( + op.nn.layer_norm( + x, + gamma=self.weight, + beta=self.bias, + axes=-1, + epsilon=self.eps, + ) + ) + return x + + +class RWKV_FFN(nn.Module): + def __init__(self, config: RWKVConfig, index: int) -> None: + super().__init__() + self.hidden_size = config.hidden_size + self.dtype = config.dtype + self.index = index + self.time_mix_key = nn.Parameter( + (self.hidden_size,), dtype=config.dtype, name=f"ffn_{index}_time_mix_k" + ) + self.time_mix_receptance = nn.Parameter( + (self.hidden_size,), dtype=config.dtype, name=f"ffn_{index}_time_mix_r" + ) + self.key = Linear( + self.hidden_size, config.intermediate_size, dtype=config.dtype, bias=False + ) + self.receptance = Linear( + self.hidden_size, self.hidden_size, dtype=config.dtype, bias=False + ) + self.value = Linear( + config.intermediate_size, self.hidden_size, dtype=config.dtype, bias=False + ) + + def forward(self, x: Expr, state: Expr) -> Expr: + offset = self.index * 5 + State.FFN_X + context_length = x.struct_info.shape[0] + hidden_size = self.hidden_size + + saved_x = _load_state(state[offset], hidden_size, self.dtype) + if not is_one(context_length): + saved_x = nn.emit_te(_te_concat_saved_x, saved_x, x) + ones = nn.emit(relax.op.ones((hidden_size,), self.dtype)) + xk = nn.emit(x * self.time_mix_key + saved_x * (ones - self.time_mix_key)) + xr = nn.emit( + x * self.time_mix_receptance + saved_x * (ones - self.time_mix_receptance) + ) + if not is_one(context_length): + x = nn.emit_te(_te_get_last_x, x) + assert is_one(x.struct_info.shape[0]) + saved_x = _store_state(state[offset], x) + + r = nn.emit(op.sigmoid(self.receptance(xr))) + xv = nn.emit(op.square(op.nn.relu(self.key(xk)))) + + return nn.emit(r * self.value(xv)), [saved_x] + + +class RWKV_Attention(nn.Module): + def __init__(self, config: RWKVConfig, index: int) -> None: + super().__init__() + self.index = index + self.dtype = config.dtype + self.hidden_size = config.hidden_size + self.time_decay = nn.Parameter( + (self.hidden_size,), dtype="float32", name=f"att_{index}_time_decay" + ) + self.time_first = nn.Parameter( + (self.hidden_size,), dtype="float32", name=f"att_{index}_time_first" + ) + self.time_mix_key = nn.Parameter( + (self.hidden_size,), dtype=config.dtype, name=f"att_{index}_time_mix_k" + ) + self.time_mix_value = nn.Parameter( + (self.hidden_size,), dtype=config.dtype, name=f"att_{index}_time_mix_v" + ) + self.time_mix_receptance = nn.Parameter( + (self.hidden_size,), dtype=config.dtype, name=f"att_{index}_time_mix_r" + ) + self.key = Linear( + self.hidden_size, self.hidden_size, dtype=config.dtype, bias=False + ) + self.value = Linear( + self.hidden_size, self.hidden_size, dtype=config.dtype, bias=False + ) + self.receptance = Linear( + self.hidden_size, self.hidden_size, dtype=config.dtype, bias=False + ) + self.output = Linear( + self.hidden_size, self.hidden_size, dtype=config.dtype, bias=False + ) + + def forward(self, x: Expr, state: Expr) -> Expr: + # Load current state + ones = nn.emit(relax.op.ones((self.hidden_size,), self.dtype)) + index = self.index + hidden_size = self.hidden_size + context_length = x.struct_info.shape[0] + bb = relax.BlockBuilder.current() + + saved_a = _load_state(state[index * 5 + State.ATT_A], hidden_size, "float32") + saved_b = _load_state(state[index * 5 + State.ATT_B], hidden_size, "float32") + saved_p = _load_state(state[index * 5 + State.ATT_P], hidden_size, "float32") + saved_x = _load_state(state[index * 5 + State.ATT_X], hidden_size, self.dtype) + if not is_one(context_length): + saved_x = nn.emit_te(_te_concat_saved_x, saved_x, x) + + xk = nn.emit(x * self.time_mix_key + saved_x * (ones - self.time_mix_key)) + xv = nn.emit(x * self.time_mix_value + saved_x * (ones - self.time_mix_value)) + xr = nn.emit( + x * self.time_mix_receptance + saved_x * (ones - self.time_mix_receptance) + ) + + r = nn.emit(op.sigmoid(self.receptance(xr))) + k = nn.emit(op.astype(self.key(xk), "float32")) + v = nn.emit(op.astype(self.value(xv), "float32")) + + gv = bb.add_func(create_wkv_func(hidden_size, "float32", self.dtype), "wkv") + ret = nn.emit( + relax.call_tir( + gv, + [k, v, self.time_decay, self.time_first, saved_a, saved_b, saved_p], + [ + R.Tensor((context_length, hidden_size), self.dtype), + R.Tensor((1, hidden_size), "float32"), + R.Tensor((1, hidden_size), "float32"), + R.Tensor((1, hidden_size), "float32"), + ], + ) + ) + if not is_one(context_length): + x = nn.emit_te(_te_get_last_x, x) + + assert is_one(x.struct_info.shape[0]) + saved_x = _store_state(state[self.index * 5 + State.ATT_X], x) + saved_a = _store_state(state[self.index * 5 + State.ATT_A], ret[1]) + saved_b = _store_state(state[self.index * 5 + State.ATT_B], ret[2]) + saved_p = _store_state(state[self.index * 5 + State.ATT_P], ret[3]) + + return nn.emit(self.output(r * ret[0])), [ + saved_x, + saved_a, + saved_b, + saved_p, + ] + + +class RWKVLayer(nn.Module): + def __init__(self, config: RWKVConfig, index: int) -> None: + super().__init__() + if index == 0: + self.pre_ln = RWKV_LayerNorm( + config.hidden_size, + config.dtype, + eps=config.layer_norm_epsilon, + name_prefix="pre_ln", + ) + self.ln1 = RWKV_LayerNorm( + config.hidden_size, + config.dtype, + eps=config.layer_norm_epsilon, + name_prefix=f"att_{index}", + ) + self.ln2 = RWKV_LayerNorm( + config.hidden_size, + config.dtype, + eps=config.layer_norm_epsilon, + name_prefix=f"ffn_{index}", + ) + self.attention = RWKV_Attention(config, index) + self.feed_forward = RWKV_FFN(config, index) + self.rescale_every = config.rescale_every + self.dtype = config.dtype + self.index = index + + def forward(self, x: Expr, state: Expr) -> Tuple[Expr, List[Expr]]: + if self.index == 0: + x = self.pre_ln(x) + att, att_state = self.attention(self.ln1(x), state) + x = nn.emit(x + att) + ffn, ffn_state = self.feed_forward(self.ln2(x), state) + x = nn.emit(x + ffn) + if self.rescale_every > 0 and (self.index + 1) % self.rescale_every == 0: + x = nn.emit(x / relax.const(2, dtype=self.dtype)) + return x, att_state + ffn_state + + +class RWKVModel(nn.Module): + def __init__(self, config: RWKVConfig) -> None: + super().__init__() + self.embeddings = RWKV_Embedding( + num_embeddings=config.vocab_size, + embedding_dim=config.hidden_size, + dtype=config.dtype, + ) + self.blocks = ModuleList( + [RWKVLayer(config, i) for i in range(config.num_hidden_layers)] + ) + self.ln_out = RWKV_LayerNorm( + config.hidden_size, + config.dtype, + eps=config.layer_norm_epsilon, + name_prefix="out_ln", + ) + self.hidden_size = config.hidden_size + self.dtype = config.dtype + + def forward(self, input_ids: Expr, state: Expr) -> Tuple[Expr, List[Expr]]: + hidden_states = self.embeddings(input_ids) + states = [] + for _, layer in enumerate(self.blocks): + hidden_states, layer_states = layer(hidden_states, state) + states += layer_states + context_length = hidden_states.struct_info.shape[0] + if not is_one(context_length): + hidden_states = nn.emit_te(_te_get_last_x, hidden_states) + hidden_states = self.ln_out(hidden_states) + return hidden_states, states + + +class RWKVForCausalLM(nn.Module): + def __init__(self, config: RWKVConfig): + self.rwkv = RWKVModel(config) + self.head = Linear( + config.hidden_size, config.vocab_size, dtype=config.dtype, bias=False + ) + self.vocab_size = config.vocab_size + ############ End ############ + + def forward( + self, + input_ids: relax.Expr, + state: relax.Expr, + ): + hidden_states, key_value_cache = self.rwkv(input_ids, state) + logits = nn.emit(self.head(hidden_states)) + logits = nn.emit(op.reshape(logits, (1, 1, self.vocab_size))) + if logits.struct_info.dtype != "float32": + logits = nn.emit(relax.op.astype(logits, "float32")) + + return logits, key_value_cache + + +def get_param_quant_kind( + name: str, param_info: relax.TensorStructInfo +) -> ParamQuantKind: + if name.endswith("embeddings.weight"): + return ParamQuantKind.embedding_table + elif name == "head.weight": + return ParamQuantKind.final_fc_weight + elif param_info.ndim == 2 and name.endswith(".weight"): + return ParamQuantKind.linear_weight + else: + return ParamQuantKind.others + + +def create_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: RWKVConfig, + quant_scheme: QuantizationScheme, + func_name=Literal["prefill", "decode"], +): + if func_name not in ["prefill", "decode"]: + raise ValueError(f"func_name must be 'prefill' or 'decode', got {func_name}") + seq_len = 1 if func_name == "decode" else tir.SizeVar("n", "int64") + + with bb.function(func_name): + model = RWKVForCausalLM(config) + param_manager.register_params( + model, func_name, quant_scheme, get_param_quant_kind + ) + + input_ids = nn.Placeholder((1, seq_len), dtype="int32", name="input_ids") + # Placeholder for compatibility to LLAMA + all_seq_len_shape = relax.Var("place_holder", R.Object()) + state = relax.Var("state", R.Tuple([R.Object()] * config.num_hidden_layers * 5)) + with bb.dataflow(): + logits, states = model(input_ids, state) + params = [ + input_ids, + all_seq_len_shape, + state, + ] + model.parameters() + + gv = bb.emit_output((logits, relax.Tuple(states))) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + f = mod[gv].with_attr("num_input", 3) + if func_name == "prefill": + f = f.with_attr("tir_var_upper_bound", {"n": config.max_sequence_length}) + bb.update_func(gv, f) + + +def create_kv_cache_func(bb: relax.BlockBuilder, config: RWKVConfig) -> None: + """NOTE: It's not typical kv-cache, but try to reuse the logic for the quick hack.""" + init_shape = relax.ShapeExpr((1, config.hidden_size)) + with bb.function("create_kv_cache", []): + with bb.dataflow(): + input_dtype_zeros = bb.emit(relax.op.zeros(init_shape, config.dtype)) + fp32_zeros = bb.emit(relax.op.zeros(init_shape, "float32")) + fp32_neg_inf = bb.emit(fp32_zeros - relax.const(1e30, "float32")) + caches = [] + f_kv_cache_create = relax.extern("vm.builtin.attention_kv_cache_create") + conf = [ + ("att_x", input_dtype_zeros), + ("att_a", fp32_zeros), + ("att_b", fp32_zeros), + ("att_p", fp32_neg_inf), + ("ffn_x", input_dtype_zeros), + ] + for i in range(config.num_hidden_layers): + for name, init_value in conf: + caches.append( + bb.emit( + relax.call_pure_packed( + f_kv_cache_create, + args=[init_value, init_shape, relax.PrimValue(1)], + sinfo_args=[R.Object()], + ), + name_hint=f"{name}_state_{i}", + ) + ) + gv = bb.emit_output(caches) + bb.emit_func_output(gv) + + +def create_kv_cache_reset_func(bb: relax.BlockBuilder, config: RWKVConfig) -> None: + state = relax.Var("state", R.Tuple([R.Object()] * config.num_hidden_layers * 5)) + init_shape = relax.ShapeExpr((1, config.hidden_size)) + with bb.function("reset_kv_cache", [state]): + with bb.dataflow(): + input_dtype_zeros = bb.emit(relax.op.zeros(init_shape, config.dtype)) + fp32_zeros = bb.emit(relax.op.zeros(init_shape, "float32")) + fp32_neg_inf = bb.emit(fp32_zeros - relax.const(1e30, "float32")) + caches = [] + for i in range(config.num_hidden_layers): + caches.append( + _store_state(state[i * 5 + State.ATT_X], input_dtype_zeros) + ) + caches.append(_store_state(state[i * 5 + State.ATT_B], fp32_zeros)) + caches.append(_store_state(state[i * 5 + State.ATT_A], fp32_zeros)) + caches.append(_store_state(state[i * 5 + State.ATT_P], fp32_neg_inf)) + caches.append( + _store_state(state[i * 5 + State.FFN_X], input_dtype_zeros) + ) + gv = bb.emit_output(caches) + bb.emit_func_output(gv) + + +def create_softmax_func(bb: relax.BlockBuilder, config: RWKVConfig) -> None: + with bb.function("softmax_with_temperature"): + logits = nn.Placeholder( + (1, 1, config.vocab_size), dtype="float32", name="logits" + ) + temperature = nn.Placeholder((), dtype="float32", name="temperature") + with bb.dataflow(): + div = bb.emit(relax.op.divide(logits, temperature)) + softmax = bb.emit(relax.op.nn.softmax(div, axis=-1)) + gv = bb.emit_output(softmax) + bb.emit_func_output(gv, [logits, temperature]) + + +def get_model(args, hf_config): + model_name = args.model + max_seq_len = args.max_seq_len + dtype = args.quantization.model_dtype + + if not model_name.lower().startswith("rwkv-"): + raise ValueError(f"Unsupported model name: {model_name}") + + config = RWKVConfig(**hf_config, dtype=dtype) + if max_seq_len != -1: + config.max_sequence_length = max_seq_len + + param_manager = ParamManager() + bb = relax.BlockBuilder() + create_func(bb, param_manager, config, args.quantization, "prefill") + create_func(bb, param_manager, config, args.quantization, "decode") + create_kv_cache_func(bb, config) + create_softmax_func(bb, config) + create_metadata_func( + bb, + model_name=model_name, + # RNN model do not have window size limit + max_window_size=-1, + stop_tokens=[0], + add_prefix_space=False, + ) + create_kv_cache_reset_func(bb, config) + mod = bb.get() + + if args.build_model_only: + return mod, param_manager, None, config + + def f_convert_pname_fwd(pname: str) -> List[str]: + if ( + "key_weight" in pname + or "value_weight" in pname + or "receptance_weight" in pname + or "output_weight" in pname + or "head_weight" in pname + ): + return [pname.replace("_weight", ".weight")] + else: + return [pname] + + def f_convert_param_bkwd(torch_pname: str, torch_param): + # torch_param: numpy.ndarray + import numpy as np # pylint: disable=import-outside-toplevel + + # rescale_every + if config.rescale_every > 0 and "blocks." in torch_pname: + # based-on the assumption that the layer id is the second element in torch_pname + layer_id = int(torch_pname.split(".")[2]) + if ( + "attention.output.weight" in torch_pname + or "feed_forward.value.weight" in torch_pname + ): + torch_param = torch_param / (2 ** (layer_id // config.rescale_every)) + + # reshape + if "time_" in torch_pname: + torch_param = torch_param.squeeze() + + # convert dtype + if "time_decay" in torch_pname: # need fp32 for this + return [(torch_pname, -np.exp(torch_param.astype("float32")))] + elif "time_first" in torch_pname: + return [(torch_pname, torch_param.astype("float32"))] + else: + return [(torch_pname, torch_param.astype(config.dtype))] + + param_manager.set_param_loading_func( + args.model_path, args.use_safetensors, f_convert_pname_fwd, f_convert_param_bkwd + ) + return mod, param_manager, [None] * len(param_manager.param_names), config diff --git a/mlc_llm/relax_model/stablelm_3b.py b/mlc_llm/relax_model/stablelm_3b.py new file mode 100644 index 0000000..ac1c9a7 --- /dev/null +++ b/mlc_llm/relax_model/stablelm_3b.py @@ -0,0 +1,913 @@ +import math +from dataclasses import dataclass +from typing import Any, List, Optional, Tuple + +import numpy as np +import tvm +from tvm import relax, te +from tvm.relax.op import ccl +from tvm.relax.op.nn import layer_norm +from tvm.relax.testing import nn +from tvm.script import relax as R + +from ..quantization import ParamQuantKind, QuantizationScheme +from .commons import create_metadata_func +from .llama import Embedding, Linear +from .modules import ModuleList, RotaryEmbedding +from .param_manager import ParamManager + + +@dataclass +class StableLM3bConfig: + def __init__( + self, + dtype="float32", + max_sequence_length=4096, + vocab_size=50304, + hidden_size=2560, + intermediate_size=6912, + num_hidden_layers=32, + num_attention_heads=32, + num_key_value_heads=None, + hidden_act="silu", + initializer_range=0.02, + norm_eps=1e-5, + pad_token_id=-1, + bos_token_id=0, + eos_token_id=1, + tie_word_embeddings=False, + position_embedding_base=10000, + combine_matmul=True, + num_shards=1, + build_model_only=False, + convert_weights_only=False, + **kwargs, + ): + self.dtype = dtype + self.max_sequence_length = max_sequence_length + self.vocab_size = vocab_size + self.hidden_size = hidden_size + self.intermediate_size = intermediate_size + self.num_hidden_layers = num_hidden_layers + self.num_attention_heads = num_attention_heads + self.num_key_value_heads = num_key_value_heads + self.hidden_act = hidden_act + self.initializer_range = initializer_range + self.norm_eps = norm_eps + self.pad_token_id = pad_token_id + self.bos_token_id = bos_token_id + self.eos_token_id = eos_token_id + self.tie_word_embeddings = tie_word_embeddings + self.position_embedding_base = position_embedding_base + self.combine_matmul = combine_matmul + if build_model_only and num_shards > 1: + self.num_shards = num_shards + else: + self.num_shards = 1 + self.kwargs = kwargs + + def get_num_key_value_heads(self): + if self.num_key_value_heads is None: + return self.num_attention_heads + return self.num_key_value_heads + + +class LayerNorm(nn.Module): + def __init__( + self, + hidden_size, + dtype, + eps=1e-5, + ): + super().__init__() + self.eps = eps + self.weight = nn.Parameter((hidden_size,), dtype="float16", name="weight") + self.bias = nn.Parameter((hidden_size,), dtype="float16", name="bias") + + def forward(self, x: relax.Expr) -> relax.Var: + x = nn.emit( + layer_norm( + x, + gamma=self.weight, + beta=self.bias, + axes=-1, + epsilon=self.eps, + ) + ) + return x + + +class StableLM3bMLP(nn.Module): + def __init__(self, config: StableLM3bConfig): + self.combine_matmul = config.combine_matmul + self.num_shards = config.num_shards + hidden_size = config.hidden_size + intermediate_size = config.intermediate_size // self.num_shards + dtype = config.dtype + if self.combine_matmul: + self.gate_up_proj = Linear(hidden_size, 2 * intermediate_size, dtype=dtype, bias=False) + self.down_proj = Linear(intermediate_size, hidden_size, dtype=dtype, bias=False) + self.gate_up_proj.weight.shard_dim = 0 + self.down_proj.weight.shard_dim = 1 + else: + self.gate_proj = Linear(hidden_size, intermediate_size, dtype=dtype, bias=False) + self.down_proj = Linear(intermediate_size, hidden_size, dtype=dtype, bias=False) + self.up_proj = Linear(hidden_size, intermediate_size, dtype=dtype, bias=False) + self.gate_proj.weight.shard_dim = 0 + self.up_proj.weight.shard_dim = 0 + self.down_proj.weight.shard_dim = 1 + + def forward(self, x): + if self.combine_matmul: + gate_up_results = nn.emit( + relax.op.split( + self.gate_up_proj(x), + indices_or_sections=2, + axis=-1, + ) + ) + gate_result = relax.TupleGetItem(gate_up_results, 0) + up_result = relax.TupleGetItem(gate_up_results, 1) + else: + gate_result = self.gate_proj(x) + up_result = self.up_proj(x) + + result = self.down_proj(relax.op.nn.silu(gate_result) * up_result) + return result + + +class StableLM3bAttention(nn.Module): + """Multi-headed attention from 'Attention Is All You Need' paper""" + + def __init__(self, config: StableLM3bConfig, rotary_embedding: RotaryEmbedding): + dtype = config.dtype + self.num_shards = config.num_shards + self.hidden_size = config.hidden_size + self.num_key_value_heads = ( + config.num_key_value_heads is None + and config.num_attention_heads + or config.num_key_value_heads + ) // config.num_shards + self.num_query_heads = config.num_attention_heads // self.num_shards + self.head_dim = self.hidden_size // config.num_attention_heads + self.position_embedding_base = config.position_embedding_base + self.rotary_embedding = rotary_embedding + + self.combine_matmul = config.combine_matmul + if self.combine_matmul: + self.query_key_value_proj = Linear( + self.hidden_size, + (self.num_query_heads + 2 * self.num_key_value_heads) * self.head_dim, + dtype=dtype, + bias=False, + ) + self.query_key_value_proj.weight.shard_dim = 0 + else: + self.q_proj = Linear( + self.hidden_size, + self.num_query_heads * self.head_dim, + dtype=dtype, + bias=False, + ) + self.k_proj = Linear( + self.hidden_size, + self.num_key_value_heads * self.head_dim, + dtype=dtype, + bias=False, + ) + self.v_proj = Linear( + self.hidden_size, + self.num_key_value_heads * self.head_dim, + dtype=dtype, + bias=False, + ) + self.q_proj.weight.shard_dim = 0 + self.k_proj.weight.shard_dim = 0 + self.v_proj.weight.shard_dim = 0 + + self.o_proj = Linear( + self.head_dim * self.num_query_heads, self.hidden_size, dtype=dtype, bias=False + ) + self.o_proj.weight.shard_dim = 1 + + def forward( + self, + hidden_states: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_value: Tuple[relax.Expr], + attention_mask: Optional[relax.Expr] = None, + ) -> Tuple[relax.Expr, Optional[relax.Expr], Optional[Tuple[relax.Expr]]]: + from tvm.relax.op import ( + astype, + matmul, + maximum, + permute_dims, + reshape, + split, + squeeze, + ) + from tvm.relax.op.nn import softmax + + bsz, q_len, _ = hidden_states.struct_info.shape + assert bsz == 1, "Only support batch size 1 at this moment." + + if self.combine_matmul: + qkv_states = nn.emit( + split( + self.query_key_value_proj(hidden_states), + indices_or_sections=[ + self.num_query_heads * self.head_dim, + (self.num_query_heads + self.num_key_value_heads) * self.head_dim, + ], + axis=-1, + ) + ) + query_states = relax.TupleGetItem(qkv_states, 0) + key_states = relax.TupleGetItem(qkv_states, 1) + value_states = relax.TupleGetItem(qkv_states, 2) + else: + query_states = self.q_proj(hidden_states) + key_states = self.k_proj(hidden_states) + value_states = self.v_proj(hidden_states) + + query_states = nn.emit( + reshape( + query_states, + (bsz, q_len, self.num_query_heads, self.head_dim), + ), + ) + key_states = nn.emit( + reshape( + key_states, + (bsz, q_len, self.num_key_value_heads, self.head_dim), + ), + ) + value_states = nn.emit( + reshape( + value_states, + (bsz, q_len, self.num_key_value_heads, self.head_dim), + ), + ) + + kv_seq_len = all_seq_len_shape.struct_info.values[0] + offset = kv_seq_len - q_len + query_states, key_states = self.rotary_embedding(query_states, key_states, offset) + # [bsz, t, nh, hd] + + kv_states_shape = key_states.struct_info.shape + kv_states_dtype = key_states.struct_info.dtype + assert kv_states_shape[0] == 1 # bsz + kv_states_shape = R.shape( + [kv_states_shape[0], kv_seq_len, kv_states_shape[2], kv_states_shape[3]] + ) + kv_cache_shape = R.shape([kv_seq_len, kv_states_shape[2], kv_states_shape[3]]) + + squeezed_key = nn.emit(squeeze(key_states, axis=0)) + squeezed_value = nn.emit(squeeze(value_states, axis=0)) + k_cache, v_cache = past_key_value + f_kv_cache_append = relax.extern("vm.builtin.attention_kv_cache_append") + k_cache = nn.emit( + relax.op.call_inplace_packed( + f_kv_cache_append, + args=[k_cache, squeezed_key], + inplace_indices=[0], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + v_cache = nn.emit( + relax.op.call_inplace_packed( + f_kv_cache_append, + args=[v_cache, squeezed_value], + inplace_indices=[0], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + past_key_value = (k_cache, v_cache) + f_kv_cache_view = relax.extern("vm.builtin.attention_kv_cache_view") + k_cache = nn.emit( + relax.call_pure_packed( + f_kv_cache_view, + args=[k_cache, kv_cache_shape], + sinfo_args=[R.Tensor(kv_cache_shape, kv_states_dtype)], + ) + ) + v_cache = nn.emit( + relax.call_pure_packed( + f_kv_cache_view, + args=[v_cache, kv_cache_shape], + sinfo_args=[R.Tensor(kv_cache_shape, kv_states_dtype)], + ) + ) + key_states = nn.emit(reshape(k_cache, kv_states_shape)) + value_states = nn.emit(reshape(v_cache, kv_states_shape)) + if self.num_key_value_heads != self.num_query_heads: + n_rep = self.num_query_heads // self.num_key_value_heads + key_states = nn.emit(relax.op.repeat(key_states, n_rep, axis=2)) + value_states = nn.emit(relax.op.repeat(value_states, n_rep, axis=2)) + + query_states = nn.emit(permute_dims(query_states, [0, 2, 1, 3])) + key_states = nn.emit(permute_dims(key_states, [0, 2, 1, 3])) + value_states = nn.emit(permute_dims(value_states, [0, 2, 1, 3])) + + attn_weights = nn.emit( + matmul(query_states, permute_dims(key_states, [0, 1, 3, 2])) + / relax.const(math.sqrt(self.head_dim), query_states.struct_info.dtype) + ) + + tvm.ir.assert_structural_equal( + attention_mask.struct_info.shape.values, + (bsz, tvm.tir.IntImm("int64", 1), q_len, kv_seq_len), + ) + + attn_weights = nn.emit( + maximum( + attn_weights, + relax.const( + tvm.tir.min_value(attn_weights.struct_info.dtype).value, + attn_weights.struct_info.dtype, + ), + ) + ) + attn_weights = nn.emit(relax.op.minimum(attn_weights, attention_mask)) + + # upcast attention to fp32 + if attn_weights.struct_info.dtype != "float32": + attn_weights = astype(attn_weights, "float32") + attn_weights = nn.emit(softmax(attn_weights, axis=-1)) + if attn_weights.struct_info.dtype != query_states.struct_info.dtype: + attn_weights = astype(attn_weights, query_states.struct_info.dtype) + attn_output = nn.emit(matmul(attn_weights, value_states)) + + attn_output = nn.emit(permute_dims(attn_output, [0, 2, 1, 3])) + attn_output = nn.emit( + reshape(attn_output, (bsz, q_len, self.head_dim * self.num_query_heads)) + ) + + attn_output = self.o_proj(attn_output) + return attn_output, ((None, None) if past_key_value is None else past_key_value) + + +class StableLM3bDecoderLayer(nn.Module): + def __init__(self, config: StableLM3bConfig, rotary_embedding: RotaryEmbedding): + self.hidden_size = config.hidden_size + self.self_attn = StableLM3bAttention(config, rotary_embedding) + self.mlp = StableLM3bMLP(config) + self.input_layernorm = LayerNorm( + config.hidden_size, dtype=config.dtype, eps=config.norm_eps + ) + self.post_attention_layernorm = LayerNorm( + config.hidden_size, dtype=config.dtype, eps=config.norm_eps + ) + + def forward( + self, + hidden_states: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_value: Tuple[relax.Expr], + attention_mask: Optional[relax.Expr] = None, + ) -> Tuple[relax.Expr, Optional[Tuple[relax.Expr, relax.Expr]]]: + residual = hidden_states + + hidden_states = self.input_layernorm(hidden_states) + + # Self Attention + hidden_states, present_key_value = self.self_attn( + hidden_states=hidden_states, + past_key_value=past_key_value, + attention_mask=attention_mask, + all_seq_len_shape=all_seq_len_shape, + ) + if self.self_attn.num_shards > 1: + residual = nn.emit( + residual / R.const(self.self_attn.num_shards, dtype=residual.struct_info.dtype) + ) + hidden_states = nn.emit(residual + hidden_states) + if self.self_attn.num_shards > 1: + hidden_states = nn.emit(ccl.allreduce(hidden_states, "sum")) + + # Fully Connected + residual = hidden_states + hidden_states = self.post_attention_layernorm(hidden_states) + hidden_states = self.mlp(hidden_states) + if self.mlp.num_shards > 1: + residual = nn.emit( + residual / R.const(self.mlp.num_shards, dtype=residual.struct_info.dtype) + ) + hidden_states = nn.emit(residual + hidden_states) + if self.mlp.num_shards > 1: + hidden_states = nn.emit(ccl.allreduce(hidden_states, "sum")) + return hidden_states, present_key_value + + +def _make_causal_mask(input_ids_shape, dtype, src_len): + from tvm.relax.op import broadcast_to + + bsz, tgt_len = input_ids_shape + + def min_max_triu_te(): + return te.compute( + (tgt_len, tgt_len), + lambda i, j: tvm.tir.Select(j > i, tvm.tir.min_value(dtype), tvm.tir.max_value(dtype)), + name="make_diag_mask_te", + ) + + mask = nn.emit_te(min_max_triu_te) + diag_mask = nn.emit(broadcast_to(mask, (bsz, 1, tgt_len, tgt_len))) + if src_len == tgt_len: + return diag_mask + + def extend_te(x, tgt_len, src_len): + return te.compute( + (bsz, 1, tgt_len, src_len), + lambda b, _, i, j: te.if_then_else( + j < src_len - tgt_len, + tvm.tir.max_value(dtype), + x[b, _, i, j - (src_len - tgt_len)], + ), + name="concat_te", + ) + + return nn.emit_te(extend_te, diag_mask, tgt_len, src_len) + + +class StableLM3bEmbedTokens(nn.Module): + def __init__(self, config: StableLM3bConfig, vocab_size_var: tvm.tir.SizeVar): + self.embed_tokens = Embedding(vocab_size_var, config.hidden_size, dtype=config.dtype) + + def forward(self, input_ids: relax.Expr): + inputs_embeds = self.embed_tokens(input_ids) + return inputs_embeds + + +class StableLM3bEmbedTokensWrapper(nn.Module): + def __init__(self, config: StableLM3bConfig, vocab_size_var: tvm.tir.SizeVar): + # build a wrapper to ensure that the naming of the embed_tokens parameter is consistent + self.model = StableLM3bEmbedTokens(config, vocab_size_var) + + def forward(self, input_ids: relax.Expr): + inputs_embeds = self.model(input_ids) + return inputs_embeds + + +class StableLM3bModell(nn.Module): + def __init__( + self, config: StableLM3bConfig, vocab_size_var: tvm.tir.SizeVar, sep_embed: bool = False + ): + rotary_embedding = RotaryEmbedding( + hidden_size=config.hidden_size, + num_attention_heads=config.num_attention_heads, + position_embedding_base=config.position_embedding_base, + max_sequence_length=config.max_sequence_length, + rotary_pct=0.25, + dtype=config.dtype, + ) + self.num_shards = config.num_shards + self.padding_idx = config.pad_token_id + self.embed_tokens = None + + if not sep_embed: + self.embed_tokens = Embedding(vocab_size_var, config.hidden_size, dtype=config.dtype) + + self.layers = ModuleList( + [ + StableLM3bDecoderLayer(config, rotary_embedding) + for _ in range(config.num_hidden_layers) + ] + ) + self.norm = LayerNorm(config.hidden_size, dtype=config.dtype, eps=config.norm_eps) + + def _prepare_decoder_attention_mask(self, input_shape, src_len, dtype): + # create causal mask + # [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len] + combined_attention_mask = None + if isinstance(input_shape[-1], tvm.tir.SizeVar) or input_shape[-1] > 1: + combined_attention_mask = _make_causal_mask(input_shape, dtype, src_len) + else: + # Get src_len from input parameters + # [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len] + bsz, tgt_len = input_shape + combined_attention_mask = nn.emit( + relax.op.full( + (bsz, 1, tgt_len, src_len), + relax.const(tvm.tir.max_value(dtype).value, dtype), + dtype, + ) + ) + return combined_attention_mask + + def forward( + self, + inputs: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_values: relax.Expr, + ): + if self.num_shards > 1: + inputs = nn.emit(ccl.broadcast_from_worker0(inputs)) + if self.embed_tokens: + inputs_embeds = self.embed_tokens(inputs) + else: + inputs_embeds = inputs + # retrieve input_ids + batch_size, seq_length, _ = inputs_embeds.struct_info.shape + seq_length_with_past = all_seq_len_shape.struct_info.values[0] + # embed positions + attention_mask = self._prepare_decoder_attention_mask( + (batch_size, seq_length), + seq_length_with_past, + inputs_embeds.struct_info.dtype, + ) + + hidden_states = inputs_embeds + + # decoder layers + next_decoder_cache = () + + for idx, decoder_layer in enumerate(self.layers): + assert past_key_values is not None + past_key_value = (past_key_values[idx * 2], past_key_values[idx * 2 + 1]) + + hidden_states, key_value_cache = decoder_layer( + hidden_states, + attention_mask=attention_mask, + past_key_value=past_key_value, + all_seq_len_shape=all_seq_len_shape, + ) + next_decoder_cache += key_value_cache + + hidden_states = self.norm(hidden_states) + + assert len(next_decoder_cache) == len(self.layers) * 2 + return hidden_states, next_decoder_cache + + +class StableLM3bForCausalLM(nn.Module): + def __init__( + self, config: StableLM3bConfig, vocab_size_var: tvm.tir.SizeVar, sep_embed: bool = False + ): + self.model = StableLM3bModell(config, vocab_size_var, sep_embed) + self.lm_head = Linear(config.hidden_size, vocab_size_var, dtype=config.dtype, bias=False) + + assert config.hidden_size % config.num_attention_heads == 0 + + def forward( + self, + inputs: relax.Expr, + all_seq_len_shape: relax.Expr, + past_key_values: relax.Expr, + ): + hidden_states, key_value_cache = self.model( + inputs=inputs, + all_seq_len_shape=all_seq_len_shape, + past_key_values=past_key_values, + ) + + def te_slicing(x: te.Tensor): + return te.compute( + shape=(1, 1, x.shape[-1]), + fcompute=lambda i, j, k: x[i, x.shape[1] - 1, k], + name="slice", + ) + + logits = self.lm_head(nn.emit_te(te_slicing, hidden_states, primfunc_name_hint="slice")) + if logits.struct_info.dtype != "float32": + logits = nn.emit(relax.op.astype(logits, "float32")) + + return logits, key_value_cache + + +def get_param_quant_kind(name: str, param_info: relax.TensorStructInfo) -> ParamQuantKind: + if "embed_tokens" in name: + return ParamQuantKind.embedding_table + elif "lm_head.weight" in name: + return ParamQuantKind.final_fc_weight + elif param_info.ndim == 2 and name.endswith(".weight"): + return ParamQuantKind.linear_weight + else: + return ParamQuantKind.others + + +def create_embed_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: StableLM3bConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "embed" + + bsz = 1 + seq_len = tvm.tir.SizeVar("m", "int64") + with bb.function(func_name): + model = StableLM3bEmbedTokensWrapper(config, tvm.tir.SizeVar("vocab_size", "int64")) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + input_ids = nn.Placeholder((bsz, seq_len), dtype="int32", name="input_ids") + with bb.dataflow(): + inputs_embeds = model(input_ids) + params = [input_ids] + model.parameters() + gv = bb.emit_output(inputs_embeds) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 1)) + + +def create_encoding_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: StableLM3bConfig, + quant_scheme: QuantizationScheme, + sep_embed: bool = False, +) -> None: + func_name = "prefill_with_embed" if sep_embed else "prefill" + + bsz = 1 + seq_len = tvm.tir.SizeVar("n", "int64") + all_seq_len = tvm.tir.SizeVar("m", "int64") + hidden_size = config.hidden_size + with bb.function(func_name): + model = StableLM3bForCausalLM(config, tvm.tir.SizeVar("vocab_size", "int64"), sep_embed) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + inputs = ( + nn.Placeholder((bsz, seq_len, hidden_size), dtype=config.dtype, name="inputs_embeds") + if sep_embed + else nn.Placeholder((bsz, seq_len), dtype="int32", name="input_ids") + ) + all_seq_len_shape = relax.Var("all_seq_len", relax.ShapeStructInfo((all_seq_len,))) + past_key_values = relax.Var( + "kv_cache", + relax.TupleStructInfo( + [relax.ObjectStructInfo() for _ in range(config.num_hidden_layers * 2)] + ), + ) + with bb.dataflow(): + logits, key_value_cache = model( + inputs, all_seq_len_shape, past_key_values=past_key_values + ) + params = [ + inputs, + all_seq_len_shape, + past_key_values, + ] + model.parameters() + gv = bb.emit_output((logits, relax.Tuple(key_value_cache))) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 3)) + + +def create_decoding_func( + bb: relax.BlockBuilder, + param_manager: ParamManager, + config: StableLM3bConfig, + quant_scheme: QuantizationScheme, +) -> None: + func_name = "decode" + + bsz = 1 + all_seq_len = tvm.tir.SizeVar("m", "int64") + + with bb.function(func_name): + model = StableLM3bForCausalLM(config, tvm.tir.SizeVar("vocab_size", "int64")) + param_manager.register_params(model, func_name, quant_scheme, get_param_quant_kind) + + input_ids = nn.Placeholder((bsz, 1), dtype="int32", name="input_ids") + all_seq_len_shape = relax.Var("all_seq_len", relax.ShapeStructInfo((all_seq_len,))) + past_key_values = relax.Var( + "kv_cache", + relax.TupleStructInfo( + [relax.ObjectStructInfo() for _ in range(config.num_hidden_layers * 2)] + ), + ) + with bb.dataflow(): + logits, key_value_cache = model( + input_ids, all_seq_len_shape, past_key_values=past_key_values + ) + params = [ + input_ids, + all_seq_len_shape, + past_key_values, + ] + model.parameters() + gv = bb.emit_output((logits, relax.Tuple(key_value_cache))) + bb.emit_func_output(gv, params) + + mod = bb.get() + gv = mod.get_global_var(func_name) + bb.update_func(gv, mod[gv].with_attr("num_input", 3)) + + +def create_kv_cache_func(bb: relax.BlockBuilder, config: StableLM3bConfig) -> None: + num_key_value_heads = ( + config.num_attention_heads + if config.num_key_value_heads is None + else config.num_key_value_heads + ) // config.num_shards + init_shape = relax.ShapeExpr( + ( + config.max_sequence_length, + num_key_value_heads, + config.hidden_size // config.num_attention_heads, # head_dim + ) + ) + with bb.function("create_kv_cache", []): + with bb.dataflow(): + zeros = bb.emit(relax.op.zeros(init_shape, config.dtype)) + caches = [] + f_kv_cache_create = relax.extern("vm.builtin.attention_kv_cache_create") + for _ in range(config.num_hidden_layers * 2): + caches.append( + bb.emit( + relax.call_pure_packed( + f_kv_cache_create, + args=[zeros, init_shape, relax.PrimValue(0)], + sinfo_args=[relax.ObjectStructInfo()], + ) + ) + ) + gv = bb.emit_output(caches) + bb.emit_func_output(gv) + + +def create_softmax_func(bb: relax.BlockBuilder, config: StableLM3bConfig) -> None: + with bb.function("softmax_with_temperature"): + logits = nn.Placeholder( + (1, 1, tvm.tir.SizeVar("vocab_size", "int64")), dtype="float32", name="logits" + ) + temperature = nn.Placeholder((), dtype="float32", name="temperature") + with bb.dataflow(): + div = bb.emit(relax.op.divide(logits, temperature)) + softmax = bb.emit(relax.op.nn.softmax(div, axis=-1)) + gv = bb.emit_output(softmax) + bb.emit_func_output(gv, [logits, temperature]) + + +def emit_shard3d(bb: relax.BlockBuilder) -> None: + from tvm.script import tir as T + + def _emit(dtype: str, global_symbol: str): + @T.prim_func + def shard_3d(a: T.handle, num_shards: T.int64, b: T.handle): + T.func_attr( + { + "tir.noalias": T.bool(True), + "global_symbol": global_symbol, + } + ) + s_0, s_1, s_2 = T.int64(), T.int64(), T.int64() + # pylint: disable=invalid-name + A = T.match_buffer(a, (s_0, s_1, s_2), dtype) + B = T.match_buffer(b, (num_shards, s_0, s_1 // num_shards, s_2), dtype) + # pylint: enable=invalid-name + for j_o, i, j_i, k in T.grid(num_shards, s_0, s_1 // num_shards, s_2): + with T.block("B"): + v_j_o = T.axis.spatial(num_shards, j_o) + v_i = T.axis.spatial(s_0, i) + v_j_i = T.axis.spatial(s_1 // num_shards, j_i) + v_k = T.axis.spatial(s_2, k) + B[v_j_o, v_i, v_j_i, v_k] = A[v_i, v_j_o * (s_1 // num_shards) + v_j_i, v_k] + + bb.add_func(shard_3d, global_symbol) + + _emit("float32", "shard3d_fp32") + _emit("float16", "shard3d_fp16") + _emit("uint32", "shard3d_uint32") + + +def get_model(args, hf_config): + model_name = args.model + dtype = args.quantization.model_dtype + max_seq_len = args.max_seq_len + sep_embed = args.sep_embed + + position_embedding_base = 10000 + if "rope_theta" in hf_config: + position_embedding_base = hf_config["rope_theta"] + + config = StableLM3bConfig( + **hf_config, + dtype=dtype, + position_embedding_base=position_embedding_base, + combine_matmul=True, + num_shards=args.num_shards, + build_model_only=args.build_model_only, + convert_weights_only=args.convert_weights_only, + ) + if max_seq_len != -1: + config.max_sequence_length = max_seq_len + + param_manager = ParamManager() + bb = relax.BlockBuilder() + emit_shard3d(bb) + + if sep_embed: + create_embed_func(bb, param_manager, config, args.quantization) + create_encoding_func(bb, param_manager, config, args.quantization, sep_embed) + create_decoding_func(bb, param_manager, config, args.quantization) + create_kv_cache_func(bb, config) + create_softmax_func(bb, config) + create_metadata_func( + bb, + model_name=model_name, + max_window_size=config.max_sequence_length, + stop_tokens=[2], + add_prefix_space=False, + prefill_chunk_size=args.prefill_chunk_size, + ) + + mod = bb.get() + + tir_bound_map = dict() + tir_bound_map["n"] = ( + args.prefill_chunk_size if args.prefill_chunk_size > 0 else config.max_sequence_length + ) + tir_bound_map["m"] = config.max_sequence_length + for gv in mod.functions: + func = mod[gv] + if isinstance(func, relax.Function): + mod[gv] = func.with_attr("tir_var_upper_bound", tir_bound_map) + + if args.build_model_only: + return mod, param_manager, None, config + + def f_convert_pname_fwd(pname: str) -> List[str]: + if not config.combine_matmul: + return [pname] + + qkv_str = "query_key_value_proj" + gate_up_str = "gate_up_proj" + if qkv_str in pname: + return [ + pname.replace(qkv_str, "q_proj"), + pname.replace(qkv_str, "k_proj"), + pname.replace(qkv_str, "v_proj"), + ] + elif gate_up_str in pname: + return [ + pname.replace(gate_up_str, "gate_proj"), + pname.replace(gate_up_str, "up_proj"), + ] + else: + return [pname] + + def f_convert_param_bkwd(torch_pname: str, torch_param): + if not config.combine_matmul: + return [(torch_pname, torch_param.astype(dtype))] + + combined_layers = ["q_proj", "k_proj", "v_proj", "gate_proj", "up_proj"] + if any([name in torch_pname for name in combined_layers]): + return None + return [(torch_pname, torch_param.astype(dtype))] + + def f_compute_relax_param(relax_pname: str, torch_params: List[Any]): + # Expected to enter this function only for the combined linear matmul weights. + # Other weights are supposed to be loaded in `f_convert_param_bkwd` since + # each other relax param has a unique corresponding torch param. + if not config.combine_matmul: + # When matmul combination is not turned on, each relax param has a unique + # corresponding torch param, and this function is not expected to be entered. + raise NotImplementedError( + "Matmul combination is not turned on, and the function " + "is not expected to be entered" + ) + num_shards = args.num_shards + hidden_size = config.hidden_size + head_dim = config.hidden_size // config.num_attention_heads + + if "query_key_value_proj" in relax_pname: + q_heads = config.num_attention_heads + kv_heads = config.num_key_value_heads + if kv_heads is None: + kv_heads = q_heads + q, k, v = torch_params + assert q.shape == (q_heads * head_dim, hidden_size) + assert k.shape == (kv_heads * head_dim, hidden_size) + assert v.shape == (kv_heads * head_dim, hidden_size) + q = q.reshape((num_shards, q_heads // num_shards, head_dim, hidden_size)) + k = k.reshape((num_shards, kv_heads // num_shards, head_dim, hidden_size)) + v = v.reshape((num_shards, kv_heads // num_shards, head_dim, hidden_size)) + qkv = np.concatenate([q, k, v], axis=1) + qkv = qkv.reshape((-1, hidden_size)).astype(dtype) + return qkv + if "gate_up_proj" in relax_pname: + intermediate_size = config.intermediate_size + gate, up = torch_params + gate = gate.reshape((num_shards, intermediate_size // num_shards, hidden_size)) + up = up.reshape((num_shards, intermediate_size // num_shards, hidden_size)) + gate_up = np.concatenate([gate, up], axis=1) + gate_up = gate_up.reshape((-1, hidden_size)).astype(dtype) + return gate_up + raise ValueError("Unexpected param loading") + + param_manager.set_param_loading_func( + args.model_path, + args.use_safetensors, + f_convert_pname_fwd, + f_convert_param_bkwd, + f_compute_relax_param, + ) + + param_list = [None] * param_manager.nparam_to_load + + return mod, param_manager, param_list, config diff --git a/mlc_llm/transform/__init__.py b/mlc_llm/transform/__init__.py new file mode 100644 index 0000000..2c67369 --- /dev/null +++ b/mlc_llm/transform/__init__.py @@ -0,0 +1,9 @@ +from .clean_up_tir_attrs import CleanUpTIRAttrs +from .decode_matmul_ewise import FuseDecodeMatmulEwise +from .decode_take import FuseDecodeTake +from .decode_transpose import FuseDecodeTranspose +from .fuse_split_rotary_embedding import fuse_split_rotary_embedding +from .lift_tir_global_buffer_alloc import LiftTIRGlobalBufferAlloc +from .reorder_transform_func import ReorderTransformFunc +from .rewrite_attention import rewrite_attention +from .transpose_matmul import FuseTransposeMatmul, FuseTranspose1Matmul, FuseTranspose2Matmul diff --git a/mlc_llm/transform/clean_up_tir_attrs.py b/mlc_llm/transform/clean_up_tir_attrs.py new file mode 100644 index 0000000..93a90f8 --- /dev/null +++ b/mlc_llm/transform/clean_up_tir_attrs.py @@ -0,0 +1,25 @@ +"""Clean up TIR attributes that may affect dispatching""" + +import tvm +from tvm.ir.module import IRModule + + +@tvm.transform.module_pass(opt_level=0, name="CleanUpTIRAttrs") +class CleanUpTIRAttrs: + def transform_module( + self, mod: IRModule, ctx: tvm.transform.PassContext + ) -> IRModule: + undesired_attrs = ["op_pattern"] + + for gv in list(mod.functions): + func = mod[gv] + changed = False + for attr in undesired_attrs: + if func.attrs is not None and attr in func.attrs: + func = func.without_attr(attr) + changed = True + break + + if changed: + mod[gv] = func + return mod diff --git a/mlc_llm/transform/decode_matmul_ewise.py b/mlc_llm/transform/decode_matmul_ewise.py new file mode 100644 index 0000000..7471848 --- /dev/null +++ b/mlc_llm/transform/decode_matmul_ewise.py @@ -0,0 +1,84 @@ +import tvm +from tvm import IRModule, relax, tir +from tvm.relax.dpl.pattern import GlobalVarPattern, TuplePattern, is_op, wildcard + + +def check_decoding(ctx: relax.transform.PatternCheckContext) -> bool: + call = ctx.annotated_expr["w"] + if not isinstance(call, relax.Call): + return False + gv = call.args[0] + if not isinstance(gv, relax.GlobalVar): + return False + return gv.name_hint.startswith("decode") or gv.name_hint.startswith("fused_decode") + + +def check_matmul(ctx: relax.transform.PatternCheckContext) -> bool: + call = ctx.annotated_expr["matmul"] + if not isinstance(call, relax.Call): + return False + gv = call.args[0] + if not isinstance(gv, relax.GlobalVar): + return False + return ( + gv.name_hint.startswith("matmul") + or gv.name_hint.startswith("fused_matmul") + or gv.name_hint.startswith("NT_matmul") + or gv.name_hint.startswith("fused_NT_matmul") + ) + + +def pattern_check(): + def f_pattern_check(ctx: relax.transform.PatternCheckContext) -> bool: + return check_decoding(ctx) and check_matmul(ctx) + + return f_pattern_check + + +def decode_matmul_pattern(match_ewise: int, n_aux_tensor: int): + assert n_aux_tensor == 1 or n_aux_tensor == 2 or n_aux_tensor == 3 or n_aux_tensor == 4 + + w_scaled = wildcard() + aux_tensors = [wildcard(), wildcard(), wildcard(), wildcard()] + x = wildcard() + w = is_op("relax.call_tir")( + GlobalVarPattern(), + TuplePattern([w_scaled, *aux_tensors[0:n_aux_tensor]]), + add_constraint=False, + ) + matmul_args = [x, w] + for _ in range(match_ewise): + matmul_args.append(wildcard()) + matmul = is_op("relax.call_tir")( + GlobalVarPattern(), TuplePattern(matmul_args), add_constraint=False + ) + + annotations = { + "matmul": matmul, + "w": w, + "x": x, + "w_scaled": w_scaled, + } + return matmul, annotations, pattern_check() + + +@tvm.transform.module_pass(opt_level=0, name="FuseDecodeMatmulEwise") +class FuseDecodeMatmulEwise: + def transform_module( + self, mod: IRModule, ctx: tvm.transform.PassContext # pylint: disable=unused-argument + ) -> IRModule: + for n_aux_tensor in [1, 2, 3, 4]: + for match_ewise in [0, 1, 2, 6]: + if match_ewise == 6 and n_aux_tensor != 4: + continue + mod = relax.transform.FuseOpsByPattern( + [ + ( + "decode_matmul", + *decode_matmul_pattern(match_ewise, n_aux_tensor), + ) + ] + )(mod) + mod = relax.transform.FuseTIR()(mod) + + return mod diff --git a/mlc_llm/transform/decode_take.py b/mlc_llm/transform/decode_take.py new file mode 100644 index 0000000..cd09771 --- /dev/null +++ b/mlc_llm/transform/decode_take.py @@ -0,0 +1,71 @@ +"""Fusing and inlining decode function into embedding table lookup.""" +import tvm +from tvm import relax, tir +from tvm.ir.module import IRModule +from tvm.relax.dpl.pattern import GlobalVarPattern, TuplePattern, is_const, is_op, wildcard + + +def pattern_check(ctx: relax.transform.PatternCheckContext) -> bool: + take = ctx.annotated_expr["take"] + decode = ctx.annotated_expr["decode"] + if not isinstance(decode, relax.expr.Call): + return False + if not isinstance(take.args[0], relax.GlobalVar) or not isinstance( + decode.args[0], relax.GlobalVar + ): + return False + return "take" in take.args[0].name_hint and "decode" in decode.args[0].name_hint + + +def decode_take_pattern(n_aux_tensor: int, match_tir_vars: bool): + aux_tensors = [wildcard(), wildcard(), wildcard()] + decode = is_op("relax.call_tir")( + GlobalVarPattern(), + TuplePattern([*aux_tensors[0:n_aux_tensor]]), + add_constraint=False, + ) + indices = ~is_const() + take_args = [decode, indices] + call_tir_args_take = [GlobalVarPattern(), TuplePattern(take_args)] + if match_tir_vars: + call_tir_args_take.append(wildcard()) + take = is_op("relax.call_tir")(*call_tir_args_take, add_constraint=False) + + annotations = { + "take": take, + "decode": decode, + "indices": indices, + } + + return take, annotations, pattern_check + + +@tvm.transform.module_pass(opt_level=0, name="FuseDecodeTake") +class FuseDecodeTake: + def transform_module(self, mod: IRModule, ctx: tvm.transform.PassContext) -> IRModule: + for n_aux_tensor in [2, 3]: + for match_tir_vars in [False, True]: + mod = relax.transform.FuseOpsByPattern( + [ + ( + "decode_take", + *decode_take_pattern(n_aux_tensor, match_tir_vars), + ) + ] + )(mod) + mod = relax.transform.FuseTIR()(mod) + + for gv, func in mod.functions.items(): + if not isinstance(func, tir.PrimFunc): + continue + if "fused_decode" not in gv.name_hint or "take" not in gv.name_hint: + continue + + downcasted_mod = tir.transform.ForceNarrowIndexToInt32()(tvm.IRModule({"main": func}))[ + "main" + ] + sch = tir.Schedule(downcasted_mod) + sch.compute_inline("decode") + mod[gv] = sch.mod["main"] + + return mod diff --git a/mlc_llm/transform/decode_transpose.py b/mlc_llm/transform/decode_transpose.py new file mode 100644 index 0000000..be5dccd --- /dev/null +++ b/mlc_llm/transform/decode_transpose.py @@ -0,0 +1,113 @@ +"""Fusing and inlining transpose function into decode function.""" +import tvm +from tvm import relax, tir +from tvm.ir.module import IRModule +from tvm.relax.analysis import remove_all_unused +from tvm.relax.expr_functor import PyExprMutator, mutator + + +@tvm.transform.module_pass(opt_level=0, name="FuseDecodeTranspose") +class FuseDecodeTranspose: + def __init__(self, skip_gemm=True) -> None: + self.skip_gemm = skip_gemm + + def transform_module(self, mod: IRModule, ctx: tvm.transform.PassContext) -> IRModule: + @mutator + class DecodeTransposeFusor(PyExprMutator): + def __init__(self, mod: IRModule, skip_gemm=True): + super().__init__(mod) + self.mod = mod + self.skip_gemm = skip_gemm + + def transform(self) -> IRModule: + for gv, func in self.mod.functions.items(): + if not isinstance(func, relax.Function): + continue + + updated_func = self.visit_expr(func) + updated_func = remove_all_unused(updated_func) + self.builder_.update_func(gv, updated_func) + + return self.builder_.get() + + def visit_call_(self, call: relax.Call) -> relax.Expr: + call = self.visit_expr_post_order(call) + + if call.op != tvm.ir.Op.get("relax.matmul"): + return call + + # Do not fuse decode-transpose for GeMM + if self.skip_gemm and ( + call.args[0].struct_info.ndim < 2 + or not isinstance(call.args[0].struct_info.shape[-2], tir.IntImm) + or call.args[0].struct_info.shape[-2].value != 1 + ): + return call + + matmul_rhs = self.lookup_binding(call.args[1]) + if ( + not isinstance(matmul_rhs, relax.Call) + or matmul_rhs.op != tvm.ir.Op.get("relax.permute_dims") + or matmul_rhs.args[0].struct_info.ndim != 2 + or matmul_rhs.attrs.axes is not None + ): + return call + + transpose_input = self.lookup_binding(matmul_rhs.args[0]) + if ( + not isinstance(transpose_input, relax.Call) + or transpose_input.op != tvm.ir.Op.get("relax.call_tir") + or not transpose_input.args[0].name_hint.startswith("decode") + or not isinstance( + transpose_input.struct_info, relax.TensorStructInfo + ) + ): + return call + + decode_tir_func = self.mod[transpose_input.args[0]] + assert isinstance(decode_tir_func, tir.PrimFunc) + if ( + len(decode_tir_func.body.block.alloc_buffers) != 1 + or not isinstance(decode_tir_func.body.block.body, tir.SeqStmt) + or len(decode_tir_func.body.block.body) != 2 + or not isinstance(decode_tir_func.body.block.body[1], tir.For) + or not isinstance( + decode_tir_func.body.block.body[1].body.body, tir.BlockRealize + ) + or decode_tir_func.body.block.body[1].body.body.block.name_hint + != "T_transpose" + ): + return call + + new_func_buffers = [ + decode_tir_func.buffer_map[var] for var in decode_tir_func.params + ] + new_func_buffers[-1] = decode_tir_func.body.block.alloc_buffers[0] + new_func = tir.PrimFunc( + params=new_func_buffers, + body=tir.BlockRealize( + iter_values=[], + predicate=True, + block=tir.Block( + iter_vars=[], + reads=[], + writes=[], + name_hint="root", + body=decode_tir_func.body.block.body[0], + ), + ), + ) + # Call `renew_defs` for deep-copy to avoid IR node duplication in + # different PrimFuncs of an IRModule. + new_func = tir.stmt_functor.renew_defs(new_func) + gv = self.builder_.add_func(new_func, func_name="decode") + decoded_matmul_rhs = self.builder_.emit( + relax.call_tir( + gv, transpose_input.args[1], out_sinfo=matmul_rhs.struct_info + ) + ) + return relax.op.matmul( + call.args[0], decoded_matmul_rhs, out_dtype=call.attrs.out_dtype + ) + + return DecodeTransposeFusor(mod, self.skip_gemm).transform() diff --git a/mlc_llm/transform/fuse_split_rotary_embedding.py b/mlc_llm/transform/fuse_split_rotary_embedding.py new file mode 100644 index 0000000..ed19a70 --- /dev/null +++ b/mlc_llm/transform/fuse_split_rotary_embedding.py @@ -0,0 +1,284 @@ +import tvm +from tvm import relax +from tvm.relax.dpl import ( + PatternContext, + is_op, + rewrite_bindings, + wildcard, + is_tuple_get_item, + GlobalVarPattern, + TuplePattern, + is_shape, +) +from tvm.script import relax as R, tir as T + + +def get_dynamic_split_rotary(): + """Implementation of R.split(rotary_embedding(fused_qkv)) + + Implementation is generic over the number of query heads, + key/value heads, sequence length, head dimension, and position + embedding base. These parameters can be replaced with static + values using `PrimFunc.specialize`. + """ + + @T.prim_func(private=True) + def split_rotary( + fused_qkv_handle: T.handle, + embedded_query_handle: T.handle, + embedded_key_handle: T.handle, + value_handle: T.handle, + rotary_offset: T.int64, + batch_size: T.int64, + seq_len: T.int64, + num_query_heads: T.int64, + num_kv_heads: T.int64, + head_dim: T.int64, + position_embedding_base: T.float32, + ): + Fused_QKV = T.match_buffer( + fused_qkv_handle, + [batch_size, seq_len, num_query_heads + num_kv_heads * 2, head_dim], + dtype="float16", + ) + EmbeddedQuery = T.match_buffer( + embedded_query_handle, + [batch_size, seq_len, num_query_heads, head_dim], + dtype="float16", + ) + EmbeddedKey = T.match_buffer( + embedded_key_handle, + [batch_size, seq_len, num_kv_heads, head_dim], + dtype="float16", + ) + Value = T.match_buffer( + value_handle, + [batch_size, seq_len, num_kv_heads, head_dim], + dtype="float16", + ) + + T.func_attr({"op_pattern": 2, "tir.noalias": T.bool(True)}) + + for iters in T.grid(batch_size, seq_len, num_query_heads + num_kv_heads * 2, head_dim): + with T.block("FusedRotaryEmbeddingAndSplitQKV"): + batch_i, seq_i, head_num, head_i = T.axis.remap("SSSS", iters) + pos: T.float32 = T.Cast("float32", rotary_offset + seq_i - seq_len) + + inv_freq: T.float32 = T.float32(1) / T.pow( + position_embedding_base, + T.Cast("float32", (head_i * 2) % head_dim) / T.float32(head_dim), + ) + freq: T.float32 = pos * inv_freq + cos_value: T.float16 = T.Cast("float16", T.cos(freq)) + sin_value: T.float16 = T.Cast("float16", T.sin(freq)) + + input_value = Fused_QKV[batch_i, seq_i, head_num, head_i] + embedded_value = cos_value * input_value + sin_value * T.Select( + head_i < T.int64(head_dim // 2), + Fused_QKV[batch_i, seq_i, head_num, head_i + T.int64(head_dim // 2)] + * T.float16(-1), + Fused_QKV[batch_i, seq_i, head_num, head_i - T.int64(head_dim // 2)], + ) + if head_num < num_query_heads: + EmbeddedQuery[batch_i, seq_i, head_num, head_i] = embedded_value + elif head_num < num_query_heads + num_kv_heads: + EmbeddedKey[batch_i, seq_i, head_num - num_query_heads, head_i] = embedded_value + else: + Value[ + batch_i, seq_i, head_num - num_query_heads - num_kv_heads, head_i + ] = input_value + + param_sinfo = [] + for param in split_rotary.params: + if param in split_rotary.buffer_map: + buf = split_rotary.buffer_map[param] + sinfo = relax.TensorStructInfo(shape=buf.shape, dtype=buf.dtype) + else: + sinfo = relax.PrimStructInfo(param.dtype) + param_sinfo.append(sinfo) + + relax.expr._update_struct_info( + split_rotary, + tvm.relax.FuncStructInfo( + params=param_sinfo, + ret=relax.TupleStructInfo([]), + purity=False, + ), + ) + + return split_rotary + + +def fuse_split_rotary_embedding( + num_query_heads, num_kv_heads, hidden_size, position_embedding_base +): + @tvm.ir.transform.module_pass(opt_level=0, name="fuse_split_rotary_embedding") + def ir_module_pass(mod: tvm.IRModule, _pass_context) -> tvm.IRModule: + head_dim = hidden_size // num_query_heads + split_rotary = get_dynamic_split_rotary() + + ( + dyn_batch_size, + dyn_seq_len, + dyn_num_query_heads, + dyn_num_kv_heads, + dyn_head_dim, + dyn_position_embedding_base, + ) = split_rotary.params[-6:] + + split_rotary = split_rotary.specialize( + { + # Static model parameters + dyn_batch_size: T.int64(1), + dyn_num_query_heads: T.int64(num_query_heads), + dyn_num_kv_heads: T.int64(num_kv_heads), + dyn_head_dim: T.int64(head_dim), + dyn_position_embedding_base: T.float32(position_embedding_base), + # Dynamic parameters, to be inferred from TIR Buffer shapes + dyn_seq_len: tvm.tir.Var("query_sequence_length", "int64"), + } + ) + + mod["split_rotary"] = split_rotary + + split_rotary_gvar = mod.get_global_var("split_rotary") + relax.expr._update_struct_info(split_rotary_gvar, mod["split_rotary"].struct_info) + + with PatternContext() as ctx: + # flat_qkv_tuple: R.Tuple( + # R.Tensor((batch_size, seq_len, 4096), dtype="float16"), + # R.Tensor((batch_size, seq_len, 4096), dtype="float16"), + # R.Tensor((batch_size, seq_len, 4096), dtype="float16"), + # ) = R.split(flat_fused_qkv, indices_or_sections=[4096, 8192], axis=2) + # + # flat_query: R.Tensor((batch_size, seq_len, 4096), dtype="float16") = flat_qkv_tuple[0] + # query: R.Tensor((batch_size, seq_len, 32, 128), dtype="float16") = R.reshape( + # flat_query, R.shape([batch_size, seq_len, 32, 128]) + # ) + # flat_key: R.Tensor((batch_size, seq_len, 4096), dtype="float16") = flat_qkv_tuple[1] + # key: R.Tensor((batch_size, seq_len, 32, 128), dtype="float16") = R.reshape( + # flat_key, R.shape([batch_size, seq_len, 32, 128]) + # ) + # flat_value: R.Tensor((batch_size, seq_len, 4096), dtype="float16") = flat_qkv_tuple[2] + # value: R.Tensor((batch_size, seq_len, 32, 128), dtype="float16") = R.reshape( + # flat_value, R.shape([batch_size, seq_len, 32, 128]) + # ) + # embedded_query = R.call_tir( + # cls.rotary_embedding1, + # [query], + # out_sinfo=R.Tensor((batch_size, seq_len, 32, 128), dtype="float16"), + # tir_vars=R.shape([n]), + # ) + # embedded_key = R.call_tir( + # cls.rotary_embedding1, + # [key], + # out_sinfo=R.Tensor((batch_size, seq_len, 32, 128), dtype="float16"), + # tir_vars=R.shape([n]), + # ) + + pat_rotary_embedding_gvar = GlobalVarPattern() + + pat_flat_fused_qkv = wildcard() + pat_offset = wildcard() + + # query_shape = is_shape([1, seq_len, num_query_heads, head_dim]) + pat_query_shape = wildcard() + # value_shape = is_shape([1, seq_len, num_kv_heads, head_dim]) + pat_key_shape = wildcard() + # value_shape = is_shape([1, seq_len, num_kv_heads, head_dim]) + pat_value_shape = wildcard() + + pat_flat_qkv_tuple = is_op("relax.split")(pat_flat_fused_qkv) + pat_flat_query = is_tuple_get_item(pat_flat_qkv_tuple, 0) + pat_query = is_op("relax.reshape")( + pat_flat_query, pat_query_shape, add_constraint=False + ) + pat_flat_query.used_by(pat_query) + pat_flat_key = is_tuple_get_item(pat_flat_qkv_tuple, 1) + pat_key = is_op("relax.reshape")(pat_flat_key, pat_key_shape, add_constraint=False) + pat_flat_key.used_by(pat_key) + pat_flat_value = is_tuple_get_item(pat_flat_qkv_tuple, 2) + pat_value = is_op("relax.reshape")( + pat_flat_value, pat_value_shape, add_constraint=False + ) + pat_flat_value.used_by(pat_value) + + pat_embedded_query = is_op("relax.call_tir")( + pat_rotary_embedding_gvar, + TuplePattern([pat_query]), + pat_offset, + add_constraint=False, + ) + pat_embedded_key = is_op("relax.call_tir")( + pat_rotary_embedding_gvar, + TuplePattern([pat_key]), + pat_offset, + add_constraint=False, + ) + + pat_flat_qkv_tuple.used_by(pat_flat_query) + pat_flat_qkv_tuple.used_by(pat_flat_key) + pat_flat_qkv_tuple.used_by(pat_flat_value) + pat_query.used_by(pat_embedded_query) + pat_key.used_by(pat_embedded_key) + + def rewriter(matchings, bindings): + # Extracting all the relax and TIR variables that we'll need + flat_fused_qkv = matchings[pat_flat_fused_qkv] + flat_qkv_tuple = matchings[pat_flat_qkv_tuple] + + flat_query = matchings[pat_flat_query] + flat_key = matchings[pat_flat_key] + flat_value = matchings[pat_flat_value] + + query = matchings[pat_query] + key = matchings[pat_key] + value = matchings[pat_value] + + embedded_query = matchings[pat_embedded_query] + embedded_key = matchings[pat_embedded_key] + + # rotary_embedding_offset = bindings[query].args[-1][1] + rotary_embedding_offset = bindings[embedded_query].args[-1][0] + + batch_size, seq_len, num_query_heads, head_dim = query.struct_info.shape + _batch_size, _seq_len, num_kv_heads, _head_dim = key.struct_info.shape + + # Rewriting along the new path + + fused_qkv = relax.op.reshape( + flat_fused_qkv, [batch_size, seq_len, num_query_heads + 2 * num_kv_heads, head_dim] + ) + + split_rotary_sinfo = [ + R.Tensor((batch_size, seq_len, num_query_heads, head_dim), dtype="float16"), + R.Tensor((batch_size, seq_len, num_kv_heads, head_dim), dtype="float16"), + R.Tensor((batch_size, seq_len, num_kv_heads, head_dim), dtype="float16"), + ] + qkv_tuple_new = R.call_tir( + split_rotary_gvar, + (fused_qkv,), + out_sinfo=split_rotary_sinfo, + tir_vars=[rotary_embedding_offset], + ) + + embedded_query_new = qkv_tuple_new[0] + embedded_key_new = qkv_tuple_new[1] + value_new = qkv_tuple_new[2] + + return { + value: value_new, + embedded_query: embedded_query_new, + embedded_key: embedded_key_new, + } + + new_mod = {} + for gvar, func in mod.functions.items(): + if isinstance(func, relax.Function): + func = rewrite_bindings(ctx, rewriter, func) + new_mod[gvar] = func + + new_mod = tvm.IRModule(new_mod, mod.type_definitions, mod.attrs, mod.global_infos) + return new_mod + + return ir_module_pass diff --git a/mlc_llm/transform/lift_tir_global_buffer_alloc.py b/mlc_llm/transform/lift_tir_global_buffer_alloc.py new file mode 100644 index 0000000..5805e9f --- /dev/null +++ b/mlc_llm/transform/lift_tir_global_buffer_alloc.py @@ -0,0 +1,197 @@ +"""Lift global buffer allocation in TIR to graph level""" + +from typing import Dict, List, Tuple, Optional + +import tvm +from tvm import relax, tir +from tvm.ir.module import IRModule +from tvm.relax.analysis import remove_all_unused +from tvm.relax.expr_functor import PyExprMutator, mutator + + +def remove_global_buf_alloc( + func: tir.PrimFunc, +) -> Optional[Tuple[tir.PrimFunc, List[relax.TensorStructInfo]]]: + """Remove the global buffer allocation for a given TIR PrimFunc.""" + if not isinstance(func.body, tir.BlockRealize): + return None + + params = list(func.params) + buffer_map = dict(func.buffer_map) + tensor_sinfo = [] + alloc_buffers = [] + + insertion_point = len(params) + while params[insertion_point - 1].dtype != "handle": + insertion_point -= 1 + assert insertion_point >= 1 + + prev_root_block = func.body.block + for buf_alloc in func.body.block.alloc_buffers: + if buf_alloc.scope() == "global": + param = tir.Var("var_" + buf_alloc.name, "handle") + params.insert(insertion_point, param) + insertion_point += 1 + buffer_map[param] = buf_alloc + tensor_sinfo.append(relax.TensorStructInfo(buf_alloc.shape, buf_alloc.dtype)) + else: + alloc_buffers.append(buf_alloc) + + if len(tensor_sinfo) == 0: + return None + + assert len(prev_root_block.iter_vars) == 0 + assert len(prev_root_block.reads) == 0 + assert len(prev_root_block.writes) == 0 + assert len(prev_root_block.match_buffers) == 0 + assert prev_root_block.name_hint == "root" + assert prev_root_block.init is None + root_block = tir.Block( + iter_vars=[], + reads=[], + writes=[], + name_hint="root", + body=prev_root_block.body, + alloc_buffers=alloc_buffers, + annotations=prev_root_block.annotations, + ) + + updated_func = tir.PrimFunc( + params=params, + body=tir.BlockRealize(iter_values=[], predicate=True, block=root_block), + ret_type=func.ret_type, + buffer_map=buffer_map, + attrs=func.attrs, + ) + return updated_func, tensor_sinfo + + +def contain_symbolic_var(tensor_sinfo: relax.TensorStructInfo) -> bool: + assert isinstance(tensor_sinfo.shape, relax.ShapeExpr) + for v in tensor_sinfo.shape.values: + if not isinstance(v, tir.IntImm): + return True + return False + + +def resolve_tir_var_mapping( + func: tir.PrimFunc, call: relax.Call, tensor_sinfo: List[relax.TensorStructInfo] +) -> Tuple[List[relax.TensorStructInfo], bool]: + """Resolve the TIR symbolic var relationship across sides of PrimFunc and Relax Function""" + var_map: Dict[tir.Var, tir.PrimExpr] = dict() + + n_arg = len(call.args[1].fields) + for i in range(n_arg): + buffer_shape = func.buffer_map[func.params[i]].shape + arg_shape = call.args[1][i].struct_info.shape.values + assert len(buffer_shape) == len(arg_shape) + for vl, vr in zip(buffer_shape, arg_shape): + if isinstance(vl, tir.Var): + var_map[vl] = vr + elif not isinstance(vl, tir.IntImm): + return [], False + + ret_tensors = call.sinfo_args[0] + ret_tensors = ( + [ret_tensors] + if isinstance(ret_tensors, relax.TensorStructInfo) + else list(ret_tensors.fields) + ) + for i in range(len(ret_tensors)): + buffer_shape = func.buffer_map[func.params[n_arg + i]].shape + ret_tensor_shape = ret_tensors[i].shape.values + assert len(buffer_shape) == len(ret_tensor_shape) + for vl, vr in zip(buffer_shape, ret_tensor_shape): + if isinstance(vl, tir.Var): + var_map[vl] = vr + elif not isinstance(vl, tir.IntImm): + return [], False + + updated_tensor_sinfo = [] + for sinfo in tensor_sinfo: + if not contain_symbolic_var(sinfo): + updated_tensor_sinfo.append(sinfo) + continue + + new_shape = [] + for v in sinfo.shape.values: + new_shape.append(tir.stmt_functor.substitute(v, var_map)) + updated_tensor_sinfo.append(relax.TensorStructInfo(new_shape, sinfo.dtype)) + return updated_tensor_sinfo, True + + +def LiftTIRGlobalBufferAlloc(): + @mutator + class TIRGlobalAllocRewriter(PyExprMutator): + def __init__(self, mod: IRModule): + super().__init__(mod) + self.mod = mod + + def transform(self) -> IRModule: + self.mod = self.builder_.get() + for gv, func in self.mod.functions.items(): + if isinstance(func, relax.Function): + updated_func = self.visit_expr(func) + self.builder_.update_func(gv, updated_func) + return self.builder_.get() + + def visit_call_(self, call: relax.Call): + call = self.visit_expr_post_order(call) + if call.op != tvm.ir.Op.get("relax.call_tir"): + return call + + old_gvar = call.args[0] + + func_before_update = self.mod.functions[old_gvar] + updates = remove_global_buf_alloc(func_before_update) + if updates is None: + return call + updated_func, tensor_sinfo = updates + + assert len(call.sinfo_args) == 1 + if any(contain_symbolic_var(sinfo) for sinfo in tensor_sinfo): + tensor_sinfo, success = resolve_tir_var_mapping( + func_before_update, call, tensor_sinfo + ) + if not success: + # Cannot resolve TIR var mapping. Fall back to no lifting. + return call + + new_gvar = self.builder_.add_func(updated_func, old_gvar.name_hint) + new_args = [new_gvar, *call.args[1:]] + + if isinstance(call.sinfo_args[0], relax.TensorStructInfo): + new_call = relax.Call( + call.op, + args=new_args, + sinfo_args=[relax.TupleStructInfo(list(call.sinfo_args) + tensor_sinfo)], + attrs=call.attrs, + ) + emitted_tuple = self.builder_.emit(new_call) + return relax.TupleGetItem(emitted_tuple, 0) + elif isinstance(call.sinfo_args[0], relax.TupleStructInfo): + return relax.Call( + call.op, + args=new_args, + sinfo_args=[ + relax.TupleStructInfo(list(call.sinfo_args[0].fields) + tensor_sinfo) + ], + attrs=call.attrs, + ) + else: + raise TypeError( + f"Expected {call.op} to return either R.Tensor or R.Tuple, " + f"but instead returned {call.sinfo_args[0]}" + ) + + @tvm.transform.module_pass(opt_level=0, name="LiftTIRGlobalBufferAlloc.Inner") + def transform_module(mod: IRModule, _: tvm.transform.PassContext) -> IRModule: + return TIRGlobalAllocRewriter(mod).transform() + + return tvm.ir.transform.Sequential( + [ + transform_module, + tvm.relax.transform.DeadCodeElimination(), + ], + name="LiftTIRGlobalBufferAlloc", + ) diff --git a/mlc_llm/transform/reorder_transform_func.py b/mlc_llm/transform/reorder_transform_func.py new file mode 100644 index 0000000..40403c8 --- /dev/null +++ b/mlc_llm/transform/reorder_transform_func.py @@ -0,0 +1,231 @@ +from typing import Callable, Dict, List, Set, Tuple + +import tvm +from tvm import relax +from tvm.ir.module import IRModule + +""" +This pass in this file reorders the bindings of the weight transform function +according to the weight location in binary files. The goal of the reorder is to +reduce the memory pressure when loading the raw model weights and processing +them. In the ideal case, with this pass, the highest CPU memory usage will +around the size of the largest raw weight binary file. + +Regarding the implementation, the bindings of fetching a raw weight in the +weight transform function are all in the form of `lv = params[idx]`. Here, each +index specifies a raw weight tensor, and the raw weight tensor resides in a +binary file on the disk. + +We group such `lv = params[idx]` into multiple groups, such that all raw weight +tensors in a group come from a same binary file. We reorder the bindings +according to the grouping result based on topological sort. + +In ideal case, after reordering the weight transform function has the following +process during execution: +* load a weight binary file, +* process all weights in this file, +* load another weight binary file, +* process all weights in this file, +* ... + +So the maximum CPU memory usage will be the size of the largest raw weight +binary file, since we process and release all the raw weight tensors immediately +after loading them from the file. +""" + + +def analyze_func( + func: relax.Function, + pidx2binname: Dict[int, str], +) -> Tuple[ + List[relax.Binding], + Dict[relax.Var, List[relax.Binding]], + Dict[relax.Binding, int], +]: + """Binding grouping analysis function. + It takes the function to be analyzed, and mapping from each raw tensor index + to the name of the binary file where it resides. + + This analysis function + * computes a new order of weight fetching bindings (the bindings in form + `lv = params[idx]`) based on weight location on disk. + * collects the dataflow def-use information of the given function for + topological sort (particularly, it collects the consumers of each binding + variables and the number of variables each binding depends on). + + Parameters + ---------- + func : relax.Function + The weight transform function to be analyzed. + + pidx2binname : Dict[int, str] + The mapping from each raw tensor index to the name of the binary + file where it resides. + + Returns + ------- + get_param_bindings : List[relax.Binding] + The weight fetching bindings (`lv = params[idx]`) in the new order. + + var_users : Dict[relax.Var, List[relax.Binding]] + The consumer bindings of each binding variable. + Used for topological sort. + + num_depending_vars : Dict[relax.Binding, int] + The number of variables each binding depends on. + Used for topological sort. + """ + + # The mapping of the weight fetching bindings in each binary file. + # Here empty string means the weight is not in any binary file (e.g., cached + # sin and cos values for rotary embeddings). + binname2get_param_bindings: Dict[str, List[relax.Binding]] = {"": []} + # The set of binding variables. + binding_var_set: Set[relax.Var] = set() + var_users: Dict[relax.Var, List[relax.Binding]] = {} + num_depending_vars: Dict[relax.Binding, int] = {} + + # Sanity check on the function pattern. + assert len(func.params) == 1 + assert isinstance(func.body, relax.SeqExpr) + assert len(func.body.blocks) == 1 + assert isinstance(func.body.blocks[0], relax.DataflowBlock) + assert func.body.blocks[0].bindings[-1].var.same_as(func.body.body) + + params = func.params[0] + bindings = func.body.blocks[0].bindings + + # Go through each binding except the last one. (The last one is the output + # binding `gv = (lv, lv1, ...)`) which we ignore for analysis. + for binding in bindings[:-1]: + value = binding.value + binding_var_set.add(binding.var) + var_users[binding.var] = [] + + if isinstance(value, relax.TupleGetItem) and value.tuple_value.same_as(params): + # For weight fetching bindings (`lv = params[idx]`), we group them + # according to the binary file name. + pidx = value.index + if pidx not in pidx2binname: + binname2get_param_bindings[""].append(binding) + continue + + binname = pidx2binname[pidx] + if binname in binname2get_param_bindings: + binname2get_param_bindings[binname].append(binding) + else: + binname2get_param_bindings[binname] = [binding] + else: + # For other bindings, we collect the use-def information for + # topological sort. + num_depending_vars[binding] = 0 + + def fvisit(obj): + if isinstance(obj, relax.Var) and obj in binding_var_set: + assert obj in var_users + var_users[obj].append(binding) + num_depending_vars[binding] += 1 + + relax.analysis.post_order_visit(value, fvisit) + + # Get the weight fetching bindings in new order according to the group results. + get_param_bindings: List[relax.Binding] = [] + for bindings in binname2get_param_bindings.values(): + get_param_bindings += bindings + + return get_param_bindings, var_users, num_depending_vars + + +def reorder_func( + func: relax.Function, + pidx2binname: Dict[int, str], +) -> relax.Function: + """Reorder the bindings of the input weight transform Relax function + according the weight location in binary files. + + This function first analyzes the input function and gets the reordered + weight fetching bindings and the use-def information for topological sort. + It then reorders all bindings in the function with topological sort. + + Parameters + ---------- + func : relax.Function + The weight transform function to be analyzed. + + pidx2binname : Dict[int, str] + The mapping from each raw tensor index to the name of the binary + file where it resides. + + Returns + ------- + func_updated : relax.Function + The returned function where the bindings are updated with the new order. + """ + get_param_bindings, var_users, num_depending_vars = analyze_func(func, pidx2binname) + + # The bindings in the new order, output by the topological sort. + new_bindings: List[relax.Binding] = [] + # The queue used in the topological sort. + binding_queue: List[relax.Binding] = [] + + for binding, n_depending in list(num_depending_vars.items()): + if n_depending == 0: + binding_queue.append(binding) + del num_depending_vars[binding] + + # Start topological sort: + # each time we emit a weight fetching binding, and then adds all bindings + # that depend on it. + for get_param_binding in get_param_bindings: + binding_queue.append(get_param_binding) + + while len(binding_queue) > 0: + binding = binding_queue.pop(0) + new_bindings.append(binding) + for user_binding in var_users[binding.var]: + num_depending_vars[user_binding] -= 1 + if num_depending_vars[user_binding] == 0: + del num_depending_vars[user_binding] + binding_queue.append(user_binding) + + # Add the output binding. + new_bindings.append(func.body.blocks[0].bindings[-1]) + # Sanity check on the integrity. + assert len(new_bindings) == len(func.body.blocks[0].bindings) + assert len(num_depending_vars) == 0 + + return relax.Function( + func.params, + relax.SeqExpr(blocks=[relax.DataflowBlock(new_bindings)], body=func.body.body), + func.ret_struct_info, + func.is_pure, + func.attrs, + ) + + +@tvm.transform.module_pass(opt_level=0, name="ReorderTransformFunc") +class ReorderTransformFunc: + def __init__( + self, + pidx2pname: Dict[int, str], + pname2binname: Dict[str, str], + f_convert_pname_fwd: Callable[[str], List[str]], + ) -> None: + self.pidx2binname: Dict[int, str] = { + pidx: pname2binname[f_convert_pname_fwd(pname)[0]] + for pidx, pname in pidx2pname.items() + if f_convert_pname_fwd(pname)[0] in pname2binname + } + + def transform_module( + self, + mod: IRModule, + ctx: tvm.transform.PassContext, + ) -> IRModule: + mod = mod.clone() + for gv, func in list(mod.functions.items()): + if isinstance(func, relax.Function): + assert gv.name_hint.endswith("transform_params") + func_updated = reorder_func(func, self.pidx2binname) + mod[gv] = func_updated + return mod diff --git a/mlc_llm/transform/rewrite_attention.py b/mlc_llm/transform/rewrite_attention.py new file mode 100644 index 0000000..d6d5693 --- /dev/null +++ b/mlc_llm/transform/rewrite_attention.py @@ -0,0 +1,46 @@ +import tvm +from tvm.relax.dpl import PatternContext, is_const, is_op, rewrite_call, wildcard +from tvm.script import relax as R + + +def rewrite_attention(use_flash_mqa=False): + @tvm.ir.transform.module_pass(opt_level=0, name="mlc_llm.transform.rewrite_attention") + def ir_module_transform(mod: tvm.IRModule, context) -> tvm.IRModule: + Q = wildcard() + K = wildcard() + V = wildcard() + + Q_BNSH = is_op("relax.permute_dims")(Q) + + if use_flash_mqa: + K_BNSH = is_op("relax.permute_dims")(is_op("relax.repeat")(K)) + V_BNSH = is_op("relax.permute_dims")(is_op("relax.repeat")(V)) + else: + K_BNSH = is_op("relax.permute_dims")(K) + V_BNSH = is_op("relax.permute_dims")(V) + + K_BNSH_T = is_op("relax.permute_dims")(K_BNSH) + + matmul1 = is_op("relax.matmul")(Q_BNSH, K_BNSH_T) + divide = is_op("relax.divide")(matmul1, is_const()) + max = is_op("relax.maximum")(divide, is_const()) + min = is_op("relax.minimum")(max, wildcard()) + softmax = is_op("relax.nn.softmax")(is_op("relax.astype")(min)) + matmul2 = is_op("relax.matmul")(is_op("relax.astype")(softmax), V_BNSH) + + pattern = is_op("relax.permute_dims")(matmul2) + + def callback(_, matchings): + return R.nn.attention( + matchings[Q], matchings[K], matchings[V], causal_mask="BottomRight" + ) + + new_module = {} + for gvar, func in mod.functions.items(): + if isinstance(func, tvm.relax.Function): + func = rewrite_call(pattern, callback, func) + new_module[gvar] = func + + return tvm.IRModule(new_module, mod.type_definitions, mod.attrs, mod.global_infos) + + return ir_module_transform diff --git a/mlc_llm/transform/transpose_matmul.py b/mlc_llm/transform/transpose_matmul.py new file mode 100644 index 0000000..fd8a9ae --- /dev/null +++ b/mlc_llm/transform/transpose_matmul.py @@ -0,0 +1,349 @@ +import tvm +from tvm import IRModule, relax, te, tir +from tvm.relax.dpl.pattern import is_op, wildcard + + +@relax.expr_functor.mutator +class TransposeMatmulCodeGenerator(relax.PyExprMutator): + def __init__(self, mod): + super().__init__(mod) + + @staticmethod + def pattern(): + w = wildcard() + x = wildcard() + wT = is_op("relax.permute_dims")(w) + o = is_op("relax.matmul")(x, wT) + annotations = {"o": o, "w": w, "x": x, "wT": wT} + + def _check(context: relax.transform.PatternCheckContext) -> bool: + transpose_call = context.annotated_expr["wT"] + ndim = transpose_call.args[0].struct_info.ndim + if ndim == -1: + return False + if ndim == 2 and transpose_call.attrs.axes is None: + return True + axes = list(range(ndim)) + axes[-1], axes[-2] = axes[-2], axes[-1] + return list(transpose_call.attrs.axes) == axes + + return o, annotations, _check + + def visit_call_(self, call: relax.Call) -> relax.Expr: + out_dtype = None + + def te_transposed_matmul(a: te.Tensor, b: te.Tensor) -> te.Tensor: + nonlocal out_dtype + a_shape = list(a.shape) + b_shape = list(b.shape) + a_prepended = False + b_appended = False + if len(a_shape) == 1: + a_prepended = True + a_shape.insert(0, 1) + if len(b_shape) == 1: + b_appended = True + b_shape.append(1) + + is_a_larger = len(a_shape) > len(b_shape) + offset = len(a_shape) - len(b_shape) if is_a_larger else len(b_shape) - len(a_shape) + + a_relax = relax.Var("a", relax.TensorStructInfo(a.shape)) + bT_shape = list(b.shape) + bT_shape[-1], bT_shape[-2] = bT_shape[-2], bT_shape[-1] + bT_relax = relax.Var("b", relax.TensorStructInfo(bT_shape)) + output_shape = self.builder_.normalize( + relax.op.matmul(a_relax, bT_relax) + ).struct_info.shape + + def matmul_compute(*idx_spatial): + k = te.reduce_axis((0, a_shape[-1]), name="k") + + def multiply_compute(idx_reduce): + a_indices = [] + b_indices = [] + + for i in range(offset): + if is_a_larger: + a_indices.append(idx_spatial[i]) + else: + b_indices.append(idx_spatial[i]) + for i in range(offset, len(output_shape) - (2 - a_prepended - b_appended)): + a_dim = a_shape[i if is_a_larger else i - offset] + b_dim = b_shape[i if not is_a_larger else i - offset] + dim_equal = a_dim == b_dim + if not isinstance(dim_equal, tir.IntImm) or dim_equal == 0: + a_dim_is_one = isinstance(a_dim, tir.IntImm) and a_dim == 1 + b_dim_is_one = isinstance(b_dim, tir.IntImm) and b_dim == 1 + a_indices.append(0 if a_dim_is_one else idx_spatial[i]) + b_indices.append(0 if b_dim_is_one else idx_spatial[i]) + else: + a_indices.append(idx_spatial[i]) + b_indices.append(idx_spatial[i]) + + if not a_prepended: + a_indices.append(idx_spatial[-2 + b_appended]) + a_indices.append(idx_reduce) + if not b_appended: + b_indices.append(idx_spatial[-1]) + b_indices.append(idx_reduce) + + dtype = out_dtype + if dtype != "": + return a(*a_indices).astype(dtype) * b(*b_indices).astype(dtype) + return a(*a_indices) * b(*b_indices) + + return te.sum(multiply_compute(k), axis=k) + + return te.compute( + output_shape, + lambda *idx: matmul_compute(*idx), # pylint: disable=unnecessary-lambda + name="NT_matmul", + ) + + if isinstance(call.op, relax.GlobalVar): + function = self.builder_.get()[call.op] + if ( + function.attrs + and "Composite" in function.attrs + and function.attrs["Composite"] == "transpose_matmul_fuse" + ): + out_dtype = function.ret_struct_info.dtype + return self.builder_.call_te( + te_transposed_matmul, + call.args[1], + call.args[0], + primfunc_name_hint="NT_matmul", + ) + + return super().visit_call_(call) + + +@tvm.transform.module_pass(opt_level=0, name="FuseTransposeMatmul") +class FuseTransposeMatmul: + def transform_module(self, mod: IRModule, ctx: tvm.transform.PassContext) -> IRModule: + mod = relax.transform.FuseOpsByPattern( + [("transpose_matmul_fuse", *TransposeMatmulCodeGenerator.pattern())] + )(mod) + + transpose_matmul_codegen = TransposeMatmulCodeGenerator(mod) + for gv in mod.functions: + func = mod[gv] + if not isinstance(func, relax.Function): + continue + func = transpose_matmul_codegen.visit_expr(func) + transpose_matmul_codegen.builder_.update_func(gv, func) + + return transpose_matmul_codegen.builder_.get() + +@relax.expr_functor.mutator +class Transpose1MatmulCodeGenerator(relax.PyExprMutator): + def __init__(self, mod): + super().__init__(mod) + + @staticmethod + def pattern(): + w = wildcard() + x = wildcard() + xT = is_op("relax.permute_dims")(x) + wT = is_op("relax.permute_dims")(w) + o = is_op("relax.matmul")(xT, wT) + annotations = {"o": o, "w": w, "x": x, "xT": xT, "wT": wT} + + def _check(context: relax.transform.PatternCheckContext) -> bool: + x_transpose_call = context.annotated_expr["o"] + w_transpose_call = context.annotated_expr["o"] + x_shape = context.annotated_expr["x"].struct_info.shape + w_shape = context.annotated_expr["w"].struct_info.shape + xT_shape = x_transpose_call.args[0].struct_info.shape + wT_shape = w_transpose_call.args[1].struct_info.shape + + if not ( + xT_shape[0] == x_shape[0] and xT_shape[1] == x_shape[2] + and xT_shape[2] == x_shape[1] and xT_shape[3] == x_shape[3] + ): + return False + + if not ( + wT_shape[0] == w_shape[0] and wT_shape[1] == w_shape[2] + and wT_shape[2] == w_shape[3] and wT_shape[3] == w_shape[1] + ): + return False + + return True + + return o, annotations, _check + + def visit_call_(self, call: relax.Call) -> relax.Expr: + out_dtype = None + + def te_transposed_matmul(a: te.Tensor, b: te.Tensor) -> te.Tensor: + nonlocal out_dtype + a_shape = list(a.shape) + b_shape = list(b.shape) + + aT_shape = list(a.shape) + aT_shape[-2], aT_shape[-3] = aT_shape[-3], aT_shape[-2] + aT_relax = relax.Var("a", relax.TensorStructInfo(aT_shape)) + bT_shape = list(b.shape) + bT_shape[-1], bT_shape[-2], bT_shape[-3] = bT_shape[-3], bT_shape[-1], bT_shape[-2] + bT_relax = relax.Var("b", relax.TensorStructInfo(bT_shape)) + output_shape = self.builder_.normalize( + relax.op.matmul(aT_relax, bT_relax) + ).struct_info.shape + def matmul_compute(*idx_spatial): + k = te.reduce_axis((0, a_shape[-1]), name="k") + def multiply_compute(idx_reduce): + a_indices = [idx_spatial[0], idx_spatial[2], idx_spatial[1], idx_reduce] + b_indices = [idx_spatial[0], idx_spatial[3], idx_spatial[1], idx_reduce] + dtype = out_dtype + if dtype != "": + return a(*a_indices).astype(dtype) * b(*b_indices).astype(dtype) + return a(*a_indices) * b(*b_indices) + + return te.sum(multiply_compute(k), axis=k) + + return te.compute( + output_shape, + lambda *idx: matmul_compute(*idx), # pylint: disable=unnecessary-lambda + name="NT_matmul", + ) + + if isinstance(call.op, relax.GlobalVar): + function = self.builder_.get()[call.op] + if ( + "Composite" in function.attrs + and function.attrs["Composite"] == "transpose1_matmul_fuse" + ): + out_dtype = function.ret_struct_info.dtype + return self.builder_.call_te( + te_transposed_matmul, + call.args[0], + call.args[1], + primfunc_name_hint="NT_matmul", + ) + + return super().visit_call_(call) + + +@tvm.transform.module_pass(opt_level=0, name="FuseTranspose1Matmul") +class FuseTranspose1Matmul: + def transform_module( + self, mod: IRModule, ctx: tvm.transform.PassContext + ) -> IRModule: + mod = relax.transform.FuseOpsByPattern( + [("transpose1_matmul_fuse", *Transpose1MatmulCodeGenerator.pattern())] + )(mod) + + transpose_matmul_codegen = Transpose1MatmulCodeGenerator(mod) + for gv in mod.functions: + func = mod[gv] + if not isinstance(func, relax.Function): + continue + func = transpose_matmul_codegen.visit_expr(func) + transpose_matmul_codegen.builder_.update_func(gv, func) + + return transpose_matmul_codegen.builder_.get() + + +@relax.expr_functor.mutator +class Transpose2MatmulCodeGenerator(relax.PyExprMutator): + def __init__(self, mod): + super().__init__(mod) + + @staticmethod + def pattern(): + w = wildcard() + x = wildcard() + wT = is_op("relax.permute_dims")(w) + o = is_op("relax.permute_dims")(is_op("relax.matmul")(x, wT)) + #oT = is_op("relax.permute_dims")(o) + annotations = {"o": o, "w": w, "x": x, "wT": wT} + + def _check(context: relax.transform.PatternCheckContext) -> bool: + w_transpose_call = context.annotated_expr["wT"] + w_shape = w_transpose_call.args[0].struct_info.shape + wT_shape = w_transpose_call.struct_info.shape + oT_call = context.annotated_expr["o"] + o_shape = oT_call.args[0].struct_info.shape + oT_shape = oT_call.struct_info.shape + + if not ( + wT_shape[0] == w_shape[0] and wT_shape[1] == w_shape[2] + and wT_shape[2] == w_shape[1] and wT_shape[3] == w_shape[3] + ): + return False + + if not ( + oT_shape[0] == o_shape[0] and oT_shape[1] == o_shape[2] + and oT_shape[2] == o_shape[1] and oT_shape[3] == o_shape[3] + ): + return False + + return True + + return o, annotations, _check + + def visit_call_(self, call: relax.Call) -> relax.Expr: + out_dtype = None + + def te_transposed_matmul(a: te.Tensor, b: te.Tensor) -> te.Tensor: + nonlocal out_dtype + a_shape = list(a.shape) + b_shape = list(b.shape) + output_shape = [a_shape[0], b_shape[-2], a_shape[2], a_shape[3]] + def matmul_compute(*idx_spatial): + k = te.reduce_axis((0, b_shape[-1]), name="k") + def multiply_compute(idx_reduce): + a_indices = [idx_spatial[0], idx_reduce, idx_spatial[2], idx_spatial[3]] + b_indices = [idx_spatial[0], idx_spatial[2], idx_spatial[1], idx_reduce] + + dtype = out_dtype + if dtype != "": + return a(*a_indices).astype(dtype) * b(*b_indices).astype(dtype) + return a(*a_indices) * b(*b_indices) + + return te.sum(multiply_compute(k), axis=k) + + return te.compute( + output_shape, + lambda *idx: matmul_compute(*idx), # pylint: disable=unnecessary-lambda + name="NT_matmul", + ) + + if isinstance(call.op, relax.GlobalVar): + function = self.builder_.get()[call.op] + if ( + "Composite" in function.attrs + and function.attrs["Composite"] == "transpose2_matmul_fuse" + ): + out_dtype = function.ret_struct_info.dtype + #NT_output_shape = function.ret_struct_info.shape + return self.builder_.call_te( + te_transposed_matmul, + call.args[0], + call.args[1], + primfunc_name_hint="NT_matmul", + ) + + return super().visit_call_(call) + + +@tvm.transform.module_pass(opt_level=0, name="FuseTranspose2Matmul") +class FuseTranspose2Matmul: + def transform_module( + self, mod: IRModule, ctx: tvm.transform.PassContext + ) -> IRModule: + mod = relax.transform.FuseOpsByPattern( + [("transpose2_matmul_fuse", *Transpose2MatmulCodeGenerator.pattern())] + )(mod) + + transpose_matmul_codegen = Transpose2MatmulCodeGenerator(mod) + for gv in mod.functions: + func = mod[gv] + if not isinstance(func, relax.Function): + continue + func = transpose_matmul_codegen.visit_expr(func) + transpose_matmul_codegen.builder_.update_func(gv, func) + + return transpose_matmul_codegen.builder_.get() diff --git a/mlc_llm/utils.py b/mlc_llm/utils.py new file mode 100644 index 0000000..3f2c5de --- /dev/null +++ b/mlc_llm/utils.py @@ -0,0 +1,741 @@ +# pylint: disable=missing-docstring,invalid-name +import argparse +import functools +import json +import math +import os +import shutil +from typing import Any, Dict, List, Optional, Set + +import numpy as np +import tvm +from tvm import relax + +from .quantization import quantization_schemes +from .relax_model import param_manager + +supported_model_types = set( + [ + "llama", + "gpt_neox", + "gpt_bigcode", + "minigpt", + "moss", + "rwkv", + "gptj", + "chatglm", + "mistral", + "stablelm_epoch", + "gpt2", + "qwen" + ] +) + + +def wrap_tqdm_counter(func, **tqdm_kwargs): + # tqdm isn't a hard requirement, so return the original function + # if it isn't available. + try: + from tqdm import tqdm + except ImportError: + return func + + pbar = tqdm(**tqdm_kwargs) + + @functools.wraps(func) + def inner(*args, **kwargs): + pbar.update(1) + return func(*args, **kwargs) + + return inner + + +def argparse_postproc_common(args: argparse.Namespace) -> None: + if hasattr(args, "device_name"): + if args.device_name == "auto": + if tvm.cuda().exist: + args.device_name = "cuda" + elif tvm.metal().exist: + args.device_name = "metal" + elif tvm.vulkan().exist: + args.device_name = "vulkan" + elif tvm.opencl().exist: + args.device_name = "opencl" + else: + raise ValueError("Cannot auto deduce device-name, please set it") + + model_category_override = { + "moss-moon-003-sft": "gptj", + "moss-moon-003-base": "gptj", + "rwkv-": "rwkv", + "rwkv_world": "rwkv_world", + "minigpt": "minigpt", + } + try: + with open(os.path.join(args.model_path, "config.json"), encoding="utf-8") as i_f: + config = json.load(i_f) + args.model_category = config["model_type"] + model_path_lower = args.model_path.lower() + if "rwkv" in model_path_lower and "world" in model_path_lower: + args.model_category = "rwkv_world" + except Exception: + args.model_category = "" + model = args.model.lower() + if "rwkv" in model and "world" in model: + model = "rwkv_world" + for prefix, override_category in model_category_override.items(): + if model.startswith(prefix): + args.model_category = override_category + break + assert args.model_category is not None + + model_conv_templates = { + "llama-2": "llama-2", + "llama-2-unconstrained": "llama-2-unconstrained", + "tinyllama": "chatml", + "codellama-7b-instruct": "codellama_instruct", + "codellama-13b-instruct": "codellama_instruct", + "codellama-34b-instruct": "codellama_instruct", + "codellama": "codellama_completion", + "gpt2": "gpt2", + "vicuna-": "vicuna_v1.1", + "dolly-": "dolly", + "stablelm-3b-": "stablelm-3b", + "stablelm-": "stablelm", + "redpajama-": "redpajama_chat", + "minigpt": "minigpt", + "moss-moon-003-sft": "moss", + "moss-moon-003-base": "LM", + "gpt-j-": "LM", + "open_llama": "LM", + "rwkv-": "rwkv", + "rwkv_world": "rwkv_world", + "gorilla-": "gorilla", + "guanaco": "guanaco", + "wizardlm-7b": "wizardlm_7b", # first get rid of 7b + "wizardlm-": "vicuna_v1.1", # all others use vicuna template + "wizardmath-": "wizard_coder_or_math", + "wizardcoder-": "wizard_coder_or_math", + "starcoder": "gpt_bigcode", + "starcoder-unconstrained": "gpt_bigcode-unconstrained", + "gpt_bigcode-santacoder": "gpt_bigcode", + "stablecode-completion": "stablecode_completion", + "stablecode-instruct": "stablecode_instruct", + "chatglm2": "glm", + "chatglm3": "glm", + "codegeex2": "glm", + "tinyllama": "chatml", + "openhermes-2.5-mistral": "open_hermes_mistral", + "neuralhermes-2.5-mistral": "neural_hermes_mistral", + "qwen": "qwen" + } + + for prefix, conv_template in model_conv_templates.items(): + if model.startswith(prefix): + args.conv_template = conv_template + break + else: + args.conv_template = f"{args.model_category}_default" + + if args.quantization not in quantization_schemes: + raise ValueError(f'Quantization "{args.quantization}" is not supported.') + + args.quantization = quantization_schemes[args.quantization] + + use_ft_quant = args.quantization in ["q4f16_ft", "q8f16_ft", "q4f16_ft_group", "q8f16_ft_group"] + + if use_ft_quant and args.num_shards > 1: + # Preprocess is done after sharding for this case. + args.quantization.linear_weight.do_preprocess = False + args.quantization.final_fc_weight.do_preprocess = False + + +def debug_dump_script(mod, name, args: argparse.Namespace, show_meta=True): + """Debug dump mode""" + if not args.debug_dump: + return + dump_path = os.path.join(args.artifact_path, "debug", name) + with open(dump_path, "w", encoding="utf-8") as outfile: + outfile.write(mod.script(show_meta=show_meta)) + print(f"Dump mod to {dump_path}") + + +def debug_dump_benchmark_script( + mod: tvm.ir.IRModule, + name: str, + args: argparse.Namespace, +) -> None: + """Extract model level benchmark workloads from relax model.""" + if not args.debug_dump: + return + + from tvm.dlight.benchmark import ( # pylint: disable=import-error,import-outside-toplevel + extract_all_func_info_from_relax, + ) + + dump_path = os.path.join(args.artifact_path, "debug", name + ".py") + with open(dump_path, "w", encoding="utf-8") as outfile: + outfile.write( + "# Please save this file to dlight_bench/models and add\n" + + f"# `from .{name} import *` to dlight_bench/models/__init__.py\n" + + "from dlight_bench import DlightBench\n" + + "from tvm.script import tir as T\n\n" + ) + + stmt = [] + try: + relax_funcs, _ = extract_all_func_info_from_relax(mod) + except NotImplementedError: + return + tvm_script_prefix = "# from tvm.script import tir as T" + for relax_func_gv in relax_funcs: # pylint: disable=consider-using-dict-items + for prim_func_gv in relax_funcs[relax_func_gv]: + # add global_symbol + func_body = ( + mod[prim_func_gv] + .with_attr("global_symbol", prim_func_gv.name_hint) + .script(name=prim_func_gv.name_hint) + ) + # remove prefix + if func_body.startswith(tvm_script_prefix + "\n"): + func_body = func_body[len(tvm_script_prefix) :] + # print out + outfile.write(func_body + "\n") + # register + stmt.append( + f"DlightBench.register_bench_workload({prim_func_gv.name_hint}, " + f"'{name}', '{prim_func_gv.name_hint}')" + ) + outfile.write("\n" + "\n".join(stmt) + "\n") + print(f"Dump benchmarking script to {dump_path}.") + + +def debug_load_script(name: str, args: argparse.Namespace): + input_path = os.path.join(args.artifact_path, "debug", name) + lib = {"__file__": input_path} + with open(input_path, "rb") as i_f: + exec(compile(i_f.read(), input_path, "exec"), lib, lib) # pylint: disable=exec-used + return lib["Module"] + + +def debug_dump_shader(ex: tvm.relax.Executable, name: str, args: argparse.Namespace): + """Debug dump mode""" + if not args.debug_dump: + return + target_kind = args.target.kind.default_keys[0] + suffix_map = { + "webgpu": ".wgsl", + "cuda": ".cu", + "metal": ".mtl", + "opencl": ".cl", + } + suffix = suffix_map.get(target_kind, ".txt") + dump_path = os.path.join(args.artifact_path, "debug", name + suffix) + source = ex.mod.imported_modules[0].imported_modules[0].get_source() + with open(dump_path, "w", encoding="utf-8") as outfile: + outfile.write(source) + print(f"Dump shader to {dump_path}") + + +def convert_weights( + mod_transform: tvm.IRModule, + param_mgr: param_manager.ParamManager, + model_params: List[Optional[tvm.nd.NDArray]], + args: argparse.Namespace, +): + # Save the number of parameters before we lower mod_transform, so + # we can use them in the progress bar. + transform_func = mod_transform["transform_params"] + num_original_params = len(transform_func.params[0].struct_info.fields) + num_transformed_params = len(transform_func.struct_info.ret.fields) + + # Remove the dataflow block inside the param transform function, + # so that the LazyTransformParams pass can be applied. + mod_transform = relax.transform.ToNonDataflow()(mod_transform) + mod_transform = relax.transform.LazyTransformParams()(mod_transform) + mod_transform = tvm.tir.transform.ForceNarrowIndexToInt32()(mod_transform) + mod_transform = relax.transform.LegalizeOps()(mod_transform) + + debug_dump_script(mod_transform, "mod_convert_weights.py", args) + + target = detect_local_target() + print(f"Automatically using target for weight quantization: {target}") + device = tvm.device(target.kind.default_keys[0]) + + get_item = param_mgr.get_param_get_item( + device, + model_params, + ) + set_item, loaded_params = param_mgr.get_param_set_item() + + get_item = wrap_tqdm_counter( + get_item, desc="Get old param", position=0, unit="tensors", total=num_original_params + ) + set_item = wrap_tqdm_counter( + set_item, desc="Set new param", position=1, unit="tensors", total=num_transformed_params + ) + + tvm.register_func(func_name="get_item", f=get_item, override=True) + tvm.register_func(func_name="set_item", f=set_item, override=True) + + if target.kind.name != "llvm": + with tvm.target.Target(target): + mod_transform = tvm.tir.transform.DefaultGPUSchedule()(mod_transform) + + ex = relax.build(mod_transform, target=target) + vm = relax.vm.VirtualMachine(ex, device) + print("Start computing and quantizing weights... This may take a while.") + vm["transform_params"]() + print("Finish computing and quantizing weights.") + return loaded_params + + +def save_params(params: List[tvm.nd.NDArray], artifact_path: str, num_presharded: int = 1) -> None: + from tvm.contrib import tvmjs # pylint: disable=import-outside-toplevel + + assert len(params) % num_presharded == 0 + num_weights = len(params) // num_presharded + + meta_data = {} + param_dict = {} + meta_data["ParamSize"] = len(params) + for i, nd in enumerate(params): + if num_presharded == 1: + param_name = f"param_{i}" + else: + expected_worker_id = i // num_weights + orig_param_id = i % num_weights + param_name = f"param_{orig_param_id}_shard-{expected_worker_id+1}-of-{num_presharded}" + + param_dict[param_name] = nd + + total_size_bytes = sum( + math.prod(param.shape) * np.dtype(param.dtype).itemsize for param in params + ) + total_size_gb = total_size_bytes / (1024**3) + print(f"Total param size: {total_size_gb} GB") + tvmjs.dump_ndarray_cache( + param_dict, f"{artifact_path}/params", meta_data=meta_data, encode_format="raw" + ) + + +def load_params(artifact_path: str, device) -> List[tvm.nd.NDArray]: + from tvm.contrib import tvmjs # pylint: disable=import-outside-toplevel + + params, meta = tvmjs.load_ndarray_cache(f"{artifact_path}/params", device) + plist = [] + size = meta["ParamSize"] + for i in range(size): + plist.append(params[f"param_{i}"]) + return plist + + +def load_params_SLM( + model_weight_path: str, device, model_metadata: Dict[str, Any] +) -> List[tvm.nd.NDArray]: + from tvm.contrib import tvmjs # pylint: disable=import-outside-toplevel + + params, meta = tvmjs.load_ndarray_cache(model_weight_path, device) + param_names = [param["name"] for param in model_metadata["params"]] + assert len(param_names) == meta["ParamSize"] + + plist = [] + for param_name in param_names: + plist.append(params[param_name]) + return plist + + +def copy_tokenizer(args: argparse.Namespace) -> None: + for filename in os.listdir(args.model_path): + if filename in [ + "tokenizer.model", + "tokenizer.json", + "vocab.json", + "merges.txt", + "added_tokens.json", + "tokenizer_config.json", + ]: + shutil.copy( + os.path.join(args.model_path, filename), + os.path.join(args.artifact_path, "params"), + ) + + # If we have `tokenizer.model` but not `tokenizer.json`, try convert it to + # `tokenizer.json` with `transformers`. + tokenizer_json_path = os.path.join(args.model_path, "tokenizer.json") + tokenizer_model_path = os.path.join(args.model_path, "tokenizer.model") + if os.path.exists(tokenizer_model_path) and (not os.path.exists(tokenizer_json_path)): + print("Attempting to convert `tokenizer.model` to `tokenizer.json`.") + try: + # pylint: disable=import-outside-toplevel + from transformers import AutoTokenizer + + tokenizer_json_save_dest = os.path.join(args.artifact_path, "params/tokenizer.json") + fast_tokenizer = AutoTokenizer.from_pretrained(args.model_path, use_fast=True) + fast_tokenizer.backend_tokenizer.save(tokenizer_json_save_dest) + print(f"Succesfully converted `tokenizer.model` to: {tokenizer_json_save_dest}") + except ImportError: + print( + "WARNING: The model has `tokenizer.model` but not `tokenizer.json`. It is" + + "recommended to use `tokenizer.json`, so we try convert it with `transformers`.\n" + + "However, we were unable to import `transformers`, hence skipping this step." + ) + except Exception as error: # pylint: disable=broad-exception-caught + print( + "WARNING: The model has `tokenizer.model` but not `tokenizer.json`. It is" + + "recommended to use `tokenizer.json`, so we try convert it with `transformers`.\n" + + "However, we are skipping this due to an error:\n", + error, + ) + + +def get_tokenizer_files(path) -> List[str]: + tokenizer_set = { + "tokenizer.model", + "tokenizer.json", + "vocab.json", + "merges.txt", + "added_tokens.json", + } + return [x for x in os.listdir(path) if x in tokenizer_set] + + +def _detect_local_metal_host(): + target_triple = tvm._ffi.get_global_func("tvm.codegen.llvm.GetDefaultTargetTriple")() + process_triple = tvm._ffi.get_global_func("tvm.codegen.llvm.GetProcessTriple")() + host_cpu = tvm._ffi.get_global_func("tvm.codegen.llvm.GetHostCPUName")() + print( + f"Host CPU dection:\n Target triple: {target_triple}\n Process triple: {process_triple}\n Host CPU: {host_cpu}" + ) + if target_triple.startswith("x86_64-"): + return tvm.target.Target( + { + "kind": "llvm", + "mtriple": "x86_64-apple-macos", + "mcpu": host_cpu, + } + ) + # should start with "arm64-" + return tvm.target.Target( + { + "kind": "llvm", + "mtriple": "arm64-apple-macos", + "mcpu": host_cpu, + } + ) + + +def _detect_local_metal(): + dev = tvm.metal() + if not dev.exist: + return None + + return tvm.target.Target( + { + "kind": "metal", + "max_shared_memory_per_block": 32768, + "max_threads_per_block": dev.max_threads_per_block, + "thread_warp_size": 32, + }, + host=_detect_local_metal_host(), + ) + + +def _detect_local_cuda(): + dev = tvm.cuda() + if not dev.exist: + return None + return tvm.target.Target( + { + "kind": "cuda", + "max_shared_memory_per_block": dev.max_shared_memory_per_block, + "max_threads_per_block": dev.max_threads_per_block, + "thread_warp_size": dev.warp_size, + "registers_per_block": 65536, + "arch": "sm_" + dev.compute_version.replace(".", ""), + } + ) + + +def _detect_local_rocm(): + dev = tvm.rocm() + if not dev.exist: + return None + return tvm.target.Target( + { + "kind": "rocm", + "max_shared_memory_per_block": dev.max_shared_memory_per_block, + "max_threads_per_block": dev.max_threads_per_block, + "thread_warp_size": dev.warp_size, + } + ) + + +def _detect_local_vulkan(): + dev = tvm.vulkan() + if not dev.exist: + return None + return tvm.target.Target( + { + "kind": "vulkan", + "max_threads_per_block": dev.max_threads_per_block, + "max_shared_memory_per_block": dev.max_shared_memory_per_block, + "thread_warp_size": dev.warp_size, + "supports_float16": 1, + "supports_int16": 1, + "supports_int8": 1, + "supports_16bit_buffer": 1, + } + ) + + +def _detect_local_opencl(): + dev = tvm.opencl() + if not dev.exist: + return None + return tvm.target.Target("opencl") + + +def detect_local_target(): + for method in [ + _detect_local_metal, + _detect_local_rocm, + _detect_local_cuda, + _detect_local_vulkan, + _detect_local_opencl, + ]: + target = method() + if target is not None: + return target + + print("Failed to detect local GPU, falling back to CPU as a target") + return tvm.target.Target("llvm") + + +def parse_target(args: argparse.Namespace) -> None: + if not hasattr(args, "target"): + return + if args.target == "auto": + target = detect_local_target() + if target.host is None: + target = tvm.target.Target( + target, + host="llvm", # TODO: detect host CPU + ) + args.target = target + args.target_kind = args.target.kind.default_keys[0] + elif args.target == "cuda" or args.target == "cuda-multiarch": + target = _detect_local_cuda() + if target is None: + raise ValueError("Cannot detect local CUDA GPU target!") + multiarch = args.target == "cuda-multiarch" + args.target = target + args.target_kind = args.target.kind.default_keys[0] + if multiarch: + args.target_kind += "-multiarch" + elif args.target.startswith("nvidia/jetson"): + try: + args.target = tvm.target.Target(args.target) + except ValueError: + raise ValueError("Cannot find configuration of given nvidia/jetson board target!") + if not hasattr(args, "cc_path") or args.cc_path == "": + args.cc_path = "/usr/bin/aarch64-linux-gnu-g++" + from tvm.contrib.cc import ( # pylint: disable=import-outside-toplevel + cross_compiler, + ) + + args.export_kwargs = { + "fcompile": cross_compiler( + args.cc_path, + ), + } + args.target_kind = args.target.kind.default_keys[0] + elif args.target == "metal": + target = _detect_local_metal() + if target is None: + print("Cannot detect local Apple Metal GPU target! Falling back...") + target = tvm.target.Target( + tvm.target.Target( + { + "kind": "metal", + "max_threads_per_block": 256, + "max_shared_memory_per_block": 32768, + "thread_warp_size": 1, + } + ), + host=_detect_local_metal_host(), + ) + args.target = target + args.target_kind = args.target.kind.default_keys[0] + elif args.target == "metal_x86_64": + from tvm.contrib import xcode # pylint: disable=import-outside-toplevel + + args.target = tvm.target.Target( + tvm.target.Target( + { + "kind": "metal", + "max_threads_per_block": 256, + "max_shared_memory_per_block": 32768, + "thread_warp_size": 1, + } + ), + host="llvm -mtriple=x86_64-apple-darwin", + ) + args.target_kind = "metal_x86_64" + args.export_kwargs = { + "fcompile": xcode.create_dylib, + "sdk": "macosx", + "arch": "x86_64", + } + args.lib_format = "dylib" + elif args.target in ["iphone", "iphone-dylib", "iphone-tar"]: + from tvm.contrib import tar, xcode # pylint: disable=import-outside-toplevel + + if args.target == "iphone-dylib": + args.export_kwargs = { + "fcompile": xcode.create_dylib, + "sdk": "iphoneos", + "arch": "arm64", + } + args.lib_format = "dylib" + else: + args.export_kwargs = {"fcompile": tar.tar} + args.lib_format = "tar" + args.system_lib = True + args.system_lib_prefix = f"{args.model}_{args.quantization}_".replace("-", "_") + + @tvm.register_func("tvm_callback_metal_compile") + def compile_metal(src, target): + if target.libs: + return xcode.compile_metal(src, sdk=target.libs[0]) + return xcode.compile_metal(src) + + target = tvm.target.Target( + tvm.target.Target( + { + "kind": "metal", + "max_threads_per_block": 256, + "max_shared_memory_per_block": 32768, + "thread_warp_size": 1, + "libs": ["iphoneos"], + } + ), + host="llvm -mtriple=arm64-apple-darwin", + ) + args.target = target + args.target_kind = "iphone" + elif args.target == "vulkan": + target = tvm.target.Target( + tvm.target.Target( + { + "kind": "vulkan", + "max_threads_per_block": 256, + "max_shared_memory_per_block": 32768, + "thread_warp_size": 1, + "supports_float16": 1, + "supports_int16": 1, + "supports_int8": 1, + "supports_8bit_buffer": 1, + "supports_16bit_buffer": 1, + "supports_storage_buffer_storage_class": 1, + } + ), + host="llvm", + ) + args.target = target + args.target_kind = args.target.kind.default_keys[0] + elif args.target == "opencl": + target = tvm.target.Target( + "opencl", + host="llvm", + ) + args.target = target + args.target_kind = args.target.kind.default_keys[0] + elif args.target == "webgpu": + args.target = tvm.target.Target( + "webgpu", + host="llvm -mtriple=wasm32-unknown-unknown-wasm", + ) + args.target_kind = "webgpu" + args.lib_format = "wasm" + args.system_lib = True + if os.environ.get("TVM_HOME", "") == "": + raise RuntimeError( + "Please set TVM_HOME for webgpu build following scripts/prep_emcc_deps.sh" + ) + elif args.target in ["android", "android-dylib"]: # android-opencl + from tvm.contrib import ndk, tar + + if args.target == "android-dylib": + args.export_kwargs = { + "fcompile": ndk.create_shared, + } + args.lib_format = "so" + else: + args.export_kwargs = { + "fcompile": tar.tar, + } + args.lib_format = "tar" + args.system_lib = True + args.system_lib_prefix = f"{args.model}_{args.quantization}_".replace("-", "_") + args.target = tvm.target.Target( + "opencl", + host="llvm -mtriple=aarch64-linux-android", # TODO: Only support arm64 for now + ) + args.target_kind = "android" + elif args.target in ["mali"]: + if "TVM_NDK_CC" in os.environ: + from tvm.contrib import ndk + + args.export_kwargs = { + "fcompile": ndk.create_shared, + } + target = tvm.target.Target( + "opencl -device=mali", + host="llvm -mtriple=aarch64-linux-gnu", + ) + args.target = target + args.target_kind = "mali" + else: + args.target = tvm.target.Target(args.target, host="llvm") + args.target_kind = args.target.kind.default_keys[0] + + if args.target_kind == "cuda-multiarch": + from tvm.contrib import nvcc + + assert args.target.arch[3:] != "" + arch_list = os.getenv("CUDA_ARCH_LIST") or os.getenv("TORCH_CUDA_ARCH_LIST") + if arch_list: + compute_versions = [int(v) for v in arch_list.replace(" ", ";").split(";")] + elif int(args.target.arch[3:]) >= 70: + compute_versions = [70, 72, 75, 80, 86, 87, 89, 90] + else: + compute_versions = [60, 61, 62] + + args.target_kind = "cuda" + + @tvm.register_func("tvm_callback_cuda_compile", override=True) + def tvm_callback_cuda_compile(code, target): # pylint: disable=unused-argument + """use nvcc to generate fatbin code for better optimization""" + arch = [] + for compute_version in compute_versions: + arch += ["-gencode", f"arch=compute_{compute_version},code=sm_{compute_version}"] + ptx = nvcc.compile_cuda(code, target_format="fatbin", arch=arch) + return ptx + + # use mingw to cross compile windows + if hasattr(args, "llvm_mingw") and args.llvm_mingw != "": + from tvm.contrib.cc import ( # pylint: disable=import-outside-toplevel + cross_compiler, + ) + + args.export_kwargs = { + "fcompile": cross_compiler( + os.path.join(args.llvm_mingw, "bin", "x86_64-w64-mingw32-clang++"), + output_format="dll", + ), + } + args.target = args.target.with_host("llvm -mtriple=x86_64-w64-windows-gnu") + args.lib_format = "dll" + + print(f"Target configured: {args.target}") diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..1ffd135 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,37 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +[tool.isort] +profile = "black" +src_paths = ["python/mlc_chat"] +known_third_party = ["numpy", "tvm", "tqdm", "torch", "transformers"] + +[tool.black] +line-length = 100 + +[tool.mypy] +ignore_missing_imports = true +show_column_numbers = true +show_error_context = true +follow_imports = "skip" +ignore_errors = false +strict_optional = false + +[tool.pylint.messages_control] +max-line-length = 100 +disable = """ +duplicate-code, +""" diff --git a/python/README.md b/python/README.md new file mode 100644 index 0000000..a1866ee --- /dev/null +++ b/python/README.md @@ -0,0 +1,5 @@ +# MLC-Chat Python Package + +This folder contains the source code of MLC-Chat python package, +please refer to the [REST API](https://llm.mlc.ai/docs/deploy/rest.html) +and [Python API](https://llm.mlc.ai/docs/deploy/python.html) documentation for usage. diff --git a/python/mlc_chat/__init__.py b/python/mlc_chat/__init__.py new file mode 100644 index 0000000..f577e03 --- /dev/null +++ b/python/mlc_chat/__init__.py @@ -0,0 +1,7 @@ +"""MLC Chat python package. + +MLC Chat is the app runtime of MLC LLM. +""" +from . import protocol, serve +from .chat_module import ChatConfig, ChatModule, ConvConfig, GenerationConfig +from .libinfo import __version__ diff --git a/python/mlc_chat/__main__.py b/python/mlc_chat/__main__.py new file mode 100644 index 0000000..8cb80a6 --- /dev/null +++ b/python/mlc_chat/__main__.py @@ -0,0 +1,47 @@ +"""Entrypoint of all CLI commands from MLC LLM""" +import sys + +from mlc_chat.support import logging +from mlc_chat.support.argparse import ArgumentParser + +logging.enable_logging() + + +def main(): + """Entrypoint of all CLI commands from MLC LLM""" + parser = ArgumentParser("MLC LLM Command Line Interface.") + parser.add_argument( + "subcommand", + type=str, + choices=["compile", "convert_weight", "gen_config", "chat", "bench"], + help="Subcommand to to run. (choices: %(choices)s)", + ) + parsed = parser.parse_args(sys.argv[1:2]) + # pylint: disable=import-outside-toplevel + if parsed.subcommand == "compile": + from mlc_chat.cli import compile as cli + + cli.main(sys.argv[2:]) + elif parsed.subcommand == "convert_weight": + from mlc_chat.cli import convert_weight as cli + + cli.main(sys.argv[2:]) + elif parsed.subcommand == "gen_config": + from mlc_chat.cli import gen_config as cli + + cli.main(sys.argv[2:]) + elif parsed.subcommand == "chat": + from mlc_chat.cli import chat as cli + + cli.main(sys.argv[2:]) + elif parsed.subcommand == "bench": + from mlc_chat.cli import bench as cli + + cli.main(sys.argv[2:]) + else: + raise ValueError(f"Unknown subcommand {parsed.subcommand}") + # pylint: enable=import-outside-toplevel + + +if __name__ == "__main__": + main() diff --git a/python/mlc_chat/_ffi_api.py b/python/mlc_chat/_ffi_api.py new file mode 100644 index 0000000..b0074ad --- /dev/null +++ b/python/mlc_chat/_ffi_api.py @@ -0,0 +1,6 @@ +"""FFI APIs for mlc_chat""" +import tvm._ffi + +# Exports functions registered via TVM_REGISTER_GLOBAL with the "mlc" prefix. +# e.g. TVM_REGISTER_GLOBAL("mlc.Tokenizer") +tvm._ffi._init_api("mlc", __name__) # pylint: disable=protected-access diff --git a/python/mlc_chat/base.py b/python/mlc_chat/base.py new file mode 100644 index 0000000..13c7ba9 --- /dev/null +++ b/python/mlc_chat/base.py @@ -0,0 +1,28 @@ +"""Load MLC LLM library and _ffi_api functions.""" +import ctypes +import os +import sys + +import tvm +import tvm._ffi.base + +from . import libinfo + +SKIP_LOADING_MLCLLM_SO = os.environ.get("SKIP_LOADING_MLCLLM_SO", "0") + + +def _load_mlc_llm_lib(): + """Load MLC LLM lib""" + if sys.platform.startswith("win32") and sys.version_info >= (3, 8): + for path in libinfo.get_dll_directories(): + os.add_dll_directory(path) + # pylint: disable=protected-access + lib_name = "mlc_llm" if tvm._ffi.base._RUNTIME_ONLY else "mlc_llm_module" + # pylint: enable=protected-access + lib_path = libinfo.find_lib_path(lib_name, optional=False) + return ctypes.CDLL(lib_path[0]), lib_path[0] + + +# only load once here +if SKIP_LOADING_MLCLLM_SO == "0": + _LIB, _LIB_PATH = _load_mlc_llm_lib() diff --git a/python/mlc_chat/callback.py b/python/mlc_chat/callback.py new file mode 100644 index 0000000..bf63c31 --- /dev/null +++ b/python/mlc_chat/callback.py @@ -0,0 +1,141 @@ +"""Namespace of callback functions in Python API.""" +# pylint: disable=unused-import, invalid-name, unnecessary-pass +from queue import Queue +from typing import Optional + + +def _get_delta_message(curr_message: str, new_message: str) -> str: + r"""Given the current message and the new message, compute the delta message + (the newly generated part, the diff of the new message from the current message). + + Parameters + ---------- + curr_message : str + The message generated in the previous round. + new_message : str + The message generated in the new round. + + Returns + ------- + delta_message : str + The diff of the new message from the current message (the newly generated part). + """ + from tvm._ffi import get_global_func # pylint: disable=import-outside-toplevel + + f_get_delta_message = get_global_func("mlc.get_delta_message") + return f_get_delta_message(curr_message, new_message) + + +class DeltaCallback: + """Base class that fetches delta callback""" + + def __init__(self): + r"""Initialize the callback class.""" + self.curr_message = "" + + def __call__(self, message: str = "", stopped: bool = False): + r"""Process newly generated message using callback functions. + + Parameters + ---------- + message : str + The newly generated message. + stopped : bool + Whether generation reaches an end. If True, clear the state of current message. + """ + if stopped: + self.stopped_callback() + self.curr_message = "" + else: + delta = _get_delta_message(self.curr_message, message) + self.curr_message = message + self.delta_callback(delta) + + def delta_callback(self, delta_message: str): + r"""Perform a callback action on the delta message. + This vary depending on the callback method. + + Parameters + ---------- + delta_message : str + The delta message. + """ + raise NotImplementedError + + def stopped_callback(self): + r"""Perform a callback action when we receive a "stop generating" signal. + Can optionally ignore this function if no action need to be done when + generation stops.""" + pass + + +class StreamToStdout(DeltaCallback): + """Stream the output of the chat module to stdout.""" + + def __init__(self, callback_interval: int = 2): + r"""Initialize the callback class with callback interval. + + Parameters + ---------- + callback_interval : int + The refresh rate of the streaming process. + """ + super().__init__() + self.callback_interval = callback_interval + + def delta_callback(self, delta_message: str): + r"""Stream the delta message directly to stdout. + + Parameters + ---------- + delta_message : str + The delta message (the part that has not been streamed to stdout yet). + """ + print(delta_message, end="", flush=True) + + def stopped_callback(self): + r"""Stream an additional '\n' when generation ends.""" + print() + + +class StreamIterator(DeltaCallback): + """Stream the output using an iterator. + A queue stores the delta messages""" + + def __init__(self, callback_interval: int = 2, timeout: Optional[float] = None): + r"""Initialize the callback class with callback interval and queue timeout. + + Parameters + ---------- + callback_interval : int + The refresh rate of the streaming process. + timeout : Optional[float] + Timeout for put and get from the delta messages queue + """ + super().__init__() + self.delta_messages: Queue = Queue() + self.callback_interval = callback_interval + self.timeout = timeout + + def delta_callback(self, delta_message: str): + r"""Stream the delta message to iterator (adding). + + Parameters + ---------- + delta_message : str + The delta message (the part that has not been added to queue yet). + """ + self.delta_messages.put(delta_message, timeout=self.timeout) + + def stopped_callback(self): + """Using None as the stop signal for the iterator""" + self.delta_messages.put(None, timeout=self.timeout) + + def __iter__(self): + return self + + def __next__(self): + value = self.delta_messages.get(timeout=self.timeout) + if value: + return value + raise StopIteration() diff --git a/python/mlc_chat/chat_module.py b/python/mlc_chat/chat_module.py new file mode 100644 index 0000000..79de775 --- /dev/null +++ b/python/mlc_chat/chat_module.py @@ -0,0 +1,1234 @@ +"""The Python API for MLC chat.""" + +#! pylint: disable=too-many-lines +import dataclasses +import inspect +import json +import os +import subprocess +import sys +import time +import warnings +from dataclasses import asdict, dataclass, fields +from enum import Enum +from pathlib import Path +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union + +import tvm +from tvm.runtime import disco # pylint: disable=unused-import + +from mlc_chat.support import logging +from mlc_chat.support.auto_device import detect_device +from mlc_chat.support.config import ConfigBase + +from . import base as _ + +if TYPE_CHECKING: + from mlc_chat.interface.openai_api import ChatMessage + +# pylint: disable=line-too-long +_PYTHON_GET_STARTED_TUTORIAL_URL = "https://github.com/mlc-ai/notebooks/blob/main/mlc-llm/tutorial_chat_module_getting_started.ipynb" +# pylint: enable=line-too-long + + +logger = logging.getLogger(__name__) + + +@dataclass +class ConvConfig: # pylint: disable=too-many-instance-attributes + r"""A dataclass that represents user-defined partial configuration for conversation template. + + This is an attribute of :class:`mlc_chat.ChatConfig`, which can then be passed in to the + instantiation of a :class:`mlc_chat.ChatModule` instance to override the default + setting in ``mlc-chat-config.json`` under the model folder. Note that we will + first load the predefined template with the name specified in ``conv_template``. + + Since the configuration is partial, everything will be ``Optional``. + + Parameters + ---------- + name : Optional[str] + Name of the conversation. + system : Optional[str] + The prompt encoded before starting the chat. + roles : Optional[List[str]] + An array that describes the role names of the user and the model. These + names are specific to the model being used. + messages : Optional[List[List[str]]] + The chat history represented as an array of string pairs in the following + format: ``[[role_0, msg_0], [role_1, msg_1], ...]``. + offset : Optional[int] + The offset used to begin the chat from the chat history. When offset + is not ``0``, ``messages[0:offset-1]`` will be encoded. + separator_style : Optional[int] + Specifies whether we are in chat-bot mode (``0``) or pure LM prompt mode (``1``). + seps : Optional[List[str]] + An array of strings indicating the separators to be used after a user + message and a model message respectively. + role_msg_sep : Optional[str] + A string indicating the separator between a role and a message. + role_empty_sep : Optional[str] + A string indicating the separator to append to a role when there is no message yet. + stop_str : Optional[str] + When the ``stop_str`` is encountered, the model will stop generating output. + stop_tokens : Optional[List[int]] + A list of token IDs that act as stop tokens. + prefix_tokens : Optional[List[int]] + Token list prefixing the conversation. + add_bos : Optional[bool] + Determines whether a beginning-of-string (bos) token should be added + before the input tokens. + """ + + name: Optional[str] = None + system: Optional[str] = None + roles: Optional[List[str]] = None + messages: Optional[List[List[str]]] = None + offset: Optional[int] = None + separator_style: Optional[int] = None + seps: Optional[List[str]] = None + role_msg_sep: Optional[str] = None + role_empty_sep: Optional[str] = None + stop_str: Optional[str] = None + stop_tokens: Optional[List[int]] = None + prefix_tokens: Optional[List[int]] = None + add_bos: Optional[bool] = None + + def __post_init__(self): + if self.messages is not None and self.offset is None: + self.offset = len(self.messages) + + +@dataclass +class ChatConfig(ConfigBase): # pylint: disable=too-many-instance-attributes + r"""A dataclass that represents user-defined partial configuration for the + chat config file. + + An instance of ``ChatConfig`` can be passed in to the instantiation of a + :class:`mlc_chat.ChatModule` instance to override the default setting in + ``mlc-chat-config.json`` under the model folder. + + Since the configuration is partial, everything will be ``Optional``. + + Note that we will exploit this class to also represent ``mlc-chat-config.json`` + during intermediate processing. + + Parameters + ---------- + model_lib : Optional[str] + The necessary model library to launch this model architecture. We recommend + reuse model library when possible. For example, all LLaMA-7B models can + use ``vicuna-v1-7b-{matching quantization scheme}``. So you can distribute + LLaMA-7B weight variants and still use them in prebuilt MLC chat apps. + local_id : Optional[str] + Uniquely identifying the model in application. This is also used by + command line interface app to specify which model to run. + conv_template : Optional[str] + The name of the conversation template that this chat uses. + temperature : Optional[float] + The temperature applied to logits before sampling. The default value is + ``0.7``. A higher temperature encourages more diverse outputs, while a + lower temperature produces more deterministic outputs. + repetition_penalty : Optional[float] + The repetition penalty controls the likelihood of the model generating + repeated texts. The default value is set to ``1.0``, indicating that no + repetition penalty is applied. Increasing the value reduces the + likelihood of repeat text generation. However, setting a high + ``repetition_penalty`` may result in the model generating meaningless + texts. The ideal choice of repetition penalty may vary among models. + + For more details on how repetition penalty controls text generation, please + check out the CTRL paper (https://arxiv.org/pdf/1909.05858.pdf). + top_p : Optional[float] + This parameter determines the set of tokens from which we sample during + decoding. The default value is set to ``0.95``. At each step, we select + tokens from the minimal set that has a cumulative probability exceeding + the ``top_p`` parameter. + + For additional information on top-p sampling, please refer to this blog + post: https://huggingface.co/blog/how-to-generate#top-p-nucleus-sampling. + mean_gen_len : Optional[int] + The approximated average number of generated tokens in each round. Used + to determine whether the maximum window size would be exceeded. + max_gen_len : Optional[int] + The maximum number of tokens to be generated in each round. Would simply + stop generating after this number is exceeded. + shift_fill_factor : Optional[float] + The fraction of maximum window size to shift when it is exceeded. + tokenizer_files : Optional[List[str]] + List of tokenizer files of the model. + conv_config : Optional[ConvConfig] + The partial overriding configuration for conversation template. Will first + load the predefined template with the name specified in ``conv_template`` + and then override some of the configurations specified in ``conv_config``. + model_category : Optional[str] + The category of the model's architecture (e.g. ``llama``, ``gpt_neox``, ``rwkv``). + model_name : Optional[str] + Name of the model (e.g. ``Llama-2-7b-chat-hf``). + tensor_parallel_shards : Optional[str] + Tensor parallel degree. + use_presharded_weights : Optional[bool] + If True, the weights were saved with sharding already applied. + context_window_size : Optional[int] + Maximum kv cache window size. + prefill_chunk_size: Optional[int] + (Experimental) The chunk size during prefilling. By default, + the chunk size is the same as sliding window or max sequence length. + This flag subjects to future refactoring. + attention_sink_size : Optional[int] + (Experimental) The number of stored sinks. Only supported on Mistral yet. By default, + the number of sinks is 4. This flag subjects to future refactoring. + sliding_window_size : Optional[int] + (Experimental) The sliding window size in sliding window attention (SWA). + This optional field overrides the `sliding_window_size` in config.json for + those models that use SWA. Currently only useful when compiling Mistral. + This flag subjects to future refactoring. + opt : Optional[str] + Optimization flags. MLC LLM maintains a predefined set of optimization flags, + denoted as O0, O1, O2, O3, where O0 means no optimization, O2 means majority of them, + and O3 represents extreme optimization that could potentially break the system. + Meanwhile, optimization flags could be explicitly specified via details knobs, e.g. + --opt="cublas_gemm=1;cudagraph=0". + """ + + model_lib: Optional[str] = None + local_id: Optional[str] = None + conv_template: Optional[str] = None + temperature: Optional[float] = None + presence_penalty: Optional[float] = 0.0 + frequency_penalty: Optional[float] = 0.0 + repetition_penalty: Optional[float] = None + top_p: Optional[float] = None + mean_gen_len: Optional[int] = None + max_gen_len: Optional[int] = None + shift_fill_factor: Optional[float] = None + tokenizer_files: Optional[List[str]] = None + conv_config: Optional[ConvConfig] = None + model_category: Optional[str] = None + model_name: Optional[str] = None + tensor_parallel_shards: Optional[int] = None + use_presharded_weights: Optional[bool] = None + context_window_size: Optional[int] = None + sliding_window_size: Optional[int] = None + prefill_chunk_size: Optional[int] = None + attention_sink_size: Optional[int] = None + max_batch_size: Optional[int] = None + opt: Optional[str] = None + kwargs: Dict[str, Any] = dataclasses.field(default_factory=dict) + + @classmethod + def _from_json(cls, json_obj: dict): + return cls(**{k: v for k, v in json_obj.items() if k in inspect.signature(cls).parameters}) + + +@dataclass +class GenerationConfig(ConfigBase): # pylint: disable=too-many-instance-attributes + r"""A dataclass that represents user-defined generation configuration. + + An instance of ``GenerationConfig`` can be passed in to the generate function + of a :class:`mlc_chat.ChatModule` instance to override the default generation + setting in ``mlc-chat-config.json`` and ``ChatConfig`` under the model folder. + + Once the generation ends, ``GenerationConfig`` is discarded, since the values + will only override the ``ChatConfig`` generation settings during one generation, + unless it is recurrently passed to generate function. This allows changing generation + settings over time, without overriding ``ChatConfig`` permanently. + + Since the configuraiton is partial, everything will be ``Optional``. + + Parameters + ---------- + temperature : Optional[float] + The temperature applied to logits before sampling. The default value is + ``0.7``. A higher temperature encourages more diverse outputs, while a + lower temperature produces more deterministic outputs. + presence_penalty : Optional[float] + Number between -2.0 and 2.0. Positive values penalize new tokens based on + whether they appear in the text so far, increasing the model's likelihood + to talk about new topics. Negative values can increase the likelihood of + repetition. + frequency_penalty : Optional[float] + Number between -2.0 and 2.0. Positive values penalize new tokens based on their + existing frequency in the text so far, decreasing the model's likelihood to + repeat the same line verbatim. Negative values can increase the likelihood of + repetition. + repetition_penalty : Optional[float] + The repetition penalty controls the likelihood of the model generating + repeated texts. The default value is set to ``1.0``, indicating that no + repetition penalty is applied. Increasing the value reduces the + likelihood of repeat text generation. However, setting a high + ``repetition_penalty`` may result in the model generating meaningless + texts. The ideal choice of repetition penalty may vary among models. Only + Active when presence_penalty and frequency_penalty are both 0.0. + + For more details on how repetition penalty controls text generation, please + check out the CTRL paper (https://arxiv.org/pdf/1909.05858.pdf). + top_p : Optional[float] + This parameter determines the set of tokens from which we sample during + decoding. The default value is set to ``0.95``. At each step, we select + tokens from the minimal set that has a cumulative probability exceeding + the ``top_p`` parameter. + + For additional information on top-p sampling, please refer to this blog + post: https://huggingface.co/blog/how-to-generate#top-p-nucleus-sampling. + mean_gen_len : Optional[int] + The approximated average number of generated tokens in each round. Used + to determine whether the maximum window size would be exceeded. + max_gen_len : Optional[int] + This parameter determines the maximum length of the generated text. If it is + not set, the model will generate text until it encounters a stop token. + n : Optional[int] + This parameter determines the number of text samples to generate. The default + value is ``1``. Note that this parameter is only used when ``stream`` is set to + ``False``. + stop : Optional[Union[str, List[str]]] + When ``stop`` is encountered, the model will stop generating output. + It can be a string or a list of strings. If it is a list of strings, the model + will stop generating output when any of the strings in the list is encountered. + Note that this parameter does not override the default stop string of the model. + """ + + temperature: Optional[float] = None + repetition_penalty: Optional[float] = None + top_p: Optional[float] = None + mean_gen_len: Optional[int] = None + max_gen_len: Optional[int] = None + presence_penalty: Optional[float] = 0.0 + frequency_penalty: Optional[float] = 0.0 + n: Optional[int] = None # pylint: disable=invalid-name + stop: Optional[Union[str, List[str]]] = None + kwargs: Dict[str, Any] = dataclasses.field(default_factory=dict) + + @classmethod + def _from_chat_config(cls, chat_config_obj: ChatConfig): + return cls( + **{ + f.name: getattr(chat_config_obj, f.name) + for f in fields(chat_config_obj) + if f.name in inspect.signature(cls).parameters + } + ) + + +class PlaceInPrompt(Enum): + """The place of an input message in a prompt.""" + + # The input message should have role names and corresponding seperators appended both prior to + # it and after it, making it a complete prompt. + All = 0 # pylint: disable=invalid-name + # The input message is only the beginning part of a prompt, no role name and separator should + # be appended after the message since there will be future messages appended after the message. + Begin = 1 # pylint: disable=invalid-name + # The input message is in the middle of a prompt, nothing should be appended before or after + # the message. + Middle = 2 # pylint: disable=invalid-name + # The input message is the ending part of a prompt, no role name and separator should be + # appended prior to it since the message is concatenated to some prior messages. + End = 3 # pylint: disable=invalid-name + + +def _get_model_path(model: str) -> Tuple[str, str]: + """Use user-provided argument ``model`` to search for a valid model path. + + We define "valid" as having an ``mlc-chat-config.json`` right under the folder. + + Parameters + ---------- + model : str + User's input; may be a compiled model's name, or a full path. + + Returns + ------ + model_path : str + A "valid" path to model folder, with ``os.isfile(os.path.join(model_path, + "mlc-chat-config.json"))`` being ``True``. + chat_file : str + Essentially ``os.path.join(model_path, "mlc-chat-config.json")``. + + Raises + ------ + FileNotFoundError: if we cannot find a valid `model_path`. + """ + if model.startswith("HF://"): + from mlc_chat.support.download import ( # pylint: disable=import-outside-toplevel + download_mlc_weights, + ) + + logger.info("Downloading model from HuggingFace: %s", model) + mlc_dir = download_mlc_weights(model) + cfg_dir = mlc_dir / "mlc-chat-config.json" + return str(mlc_dir), str(cfg_dir) + + # Note that the order of this list corresponds to our search priority + candidate_paths = [ + f"{model}", # full path, or just the name + f"dist/prebuilt/{model}", # Using prebuilt workflow + f"dist/{model}/params", # Default directory after mlc_llm.build_model() + f"dist/prebuilt/mlc-chat-{model}", # Also prebuilt workflow, but missed prefix + ] + + # Look for the first folder that has `mlc-chat-config.json` under it + for candidate in candidate_paths: + chat_file = os.path.join(candidate, "mlc-chat-config.json") + if os.path.isfile(chat_file): + logger.info("Using model folder: %s", os.path.abspath(candidate)) + logger.info("Using mlc chat config: %s", os.path.abspath(chat_file)) + return candidate, chat_file + + # Failed to find a valid model_path, analyzing error for user + + # First see if any candidate path is an actual folder + found_folder = False + valid_dir_str = "" + for candidate in candidate_paths: + if os.path.isdir(candidate): + valid_dir_str += f"- {os.path.abspath(candidate)}\n" + found_folder = True + + if found_folder: + # Error 1: there is a folder, but not an mlc-llm model folder (E1) + raise FileNotFoundError( + "The model folder provided does not seem to refer to a valid mlc-llm model folder.\n" + "Specifically, we cannot find `mlc-chat-config.json`, a required file. You should " + "provide a path that contains the file.\n" + "According to your input `model`, we looked at folder(s):\n" + f"{valid_dir_str}" + "MLC-Chat consumes models that are processed by the MLC-LLM build process.\n" + f"Please checkout {_PYTHON_GET_STARTED_TUTORIAL_URL} for an example on " + "how to load a model." + ) + # Error 2: cannot find a folder (E0) + all_paths_str = "".join(f"- {path}\n" for path in candidate_paths) + raise FileNotFoundError( + "Cannot find the model folder. We searched over the following possible paths:\n" + f"{all_paths_str}" + "You can try to pass in `model=/path/to/your-model-path`, and confirm " + "that it contains `mlc-chat-config.json`, among other essential files.\n" + f"Please checkout {_PYTHON_GET_STARTED_TUTORIAL_URL} for an " + "example on how to load a model." + ) + + +def _get_chat_config(config_file_path: str, user_chat_config: Optional[ChatConfig]) -> ChatConfig: + """Read in the config file in model path, then potentially override with user input. + + Parameters + ---------- + config_file_path : str + ``chat_file`` returned by ``_get_model_path()``. + user_chat_config : Optional[ChatConfig] + User's input, a partial ``ChatConfig`` to override the one in ``config_file_path``. + + Returns + ------ + final_chat_config : ChatConfig + ``ChatConfig`` corresponding to ``config_file_path``, overriden by ``user_chat_config``. + """ + final_chat_config = None + with open(config_file_path, mode="rt", encoding="utf-8") as file: + json_object = json.load(file) + final_chat_config = ChatConfig._from_json(json_object) # pylint: disable=protected-access + if user_chat_config is not None: + # We override using user's chat config + for field in fields(user_chat_config): + field_name = field.name + field_value = getattr(user_chat_config, field_name) + if field_value is not None: + if field_name == "model_lib": + warn_msg = ( + 'WARNING: Do not override "model_lib" in ChatConfig. ' + "This override will be ignored. Please use ChatModule.model_lib_path to " + "override the full model library path instead." + ) + warnings.warn(warn_msg) + else: + setattr(final_chat_config, field_name, field_value) + return final_chat_config + + +def _get_generation_config( + user_chat_config: ChatConfig, user_generation_config: Optional[GenerationConfig] +) -> GenerationConfig: + """Read in the config file in model path, then potentially override with user input. + + Parameters + ---------- + user_chat_config : ChatConfig + ``ChatConfig`` that contain the generation settings to be overriden. + user_generation_config : Optional[GenerationConfig] + User's input, a partial ``GenerationConfig`` to override the ``ChatConfig``. + + Returns + ------ + final_generation_config : GenerationConfig + ``GenerationConfig`` corresponding to ``user_chat_config``, overriden by + ``user_generation_config``. + """ + # pylint: disable=protected-access + final_generation_config = GenerationConfig._from_chat_config(user_chat_config) + # pylint: enable=protected-access + if user_generation_config is not None: + # We override using user's chat config + for field in fields(user_generation_config): + field_name = field.name + field_value = getattr(user_generation_config, field_name) + if field_value is not None: + setattr(final_generation_config, field_name, field_value) + return final_generation_config + + +def _get_lib_module_path( # pylint: disable=too-many-arguments + model: str, + model_path: str, + chat_config: ChatConfig, + model_lib_path: Optional[str], + device_name: str, + config_file_path: str, +) -> str: + """Look up the model library. Then return a corresponding ``tvm`` runtime Module. + + Parameters + ---------- + model : str + User's input; may be a compiled model's name, or a full path. + model_path : str + Model path found by `_get_model_path`. + chat_config : ChatConfig + Chat config after potential overrides. Returned by ``_get_chat_config``. + model_lib_path : Optional[str] + User's input. Supposedly a full path to model library. Prioritized to use. + device_name : str + User's input. Used to construct the library model file name. + config_file_path : str + The path to ``mlc-chat-config.json``. Used for error message making. + + Returns + ------- + model_lib_path : str + The path pointing to the model library we find. + + Raises + ------ + FileNotFoundError: if we cannot find a valid model library file. + """ + # 1. Use user's model_lib_path if provided + if model_lib_path is not None: + if os.path.isfile(model_lib_path): + logger.info("Using library model: %s", model_lib_path) + return model_lib_path + raise FileNotFoundError( + f"The `model_lib_path` you passed in is not a file: {model_lib_path}.\n" + f"Please refer to {_PYTHON_GET_STARTED_TUTORIAL_URL} as tutorial on model loading." + ) + + # 2. Generate all possible file names according to OS + candidate_lib_names = [] + if sys.platform.startswith("linux"): + candidate_lib_names = [f"{chat_config.model_lib}-{device_name}.so"] + elif sys.platform.startswith("Darwin"): + # Note that `dylib` comes before `so` since we prioritize `dylib` for MacOS + candidate_lib_names = [ + f"{chat_config.model_lib}-{device_name}.dylib", + f"{chat_config.model_lib}-{device_name}.so", + ] + elif sys.platform.startswith("win32"): + candidate_lib_names = [f"{chat_config.model_lib}-{device_name}.dll"] + else: + candidate_lib_names = [ + f"{chat_config.model_lib}-{device_name}.dylib", + f"{chat_config.model_lib}-{device_name}.so", + f"{chat_config.model_lib}-{device_name}.dll", + ] + + # 3. Generate possible model library paths + candidate_paths = [] + for lib_name in candidate_lib_names: + # Equivalent to {model_path}/../ + pardir_model_path = os.path.abspath(os.path.join(os.path.abspath(model_path), os.pardir)) + candidate_paths.extend( + [ + f"{lib_name}", + f"dist/prebuilt/lib/{lib_name}", # Using prebuilt workflow + f"dist/{model}/{lib_name}", # Default directory after mlc_llm.build_model() + os.path.join(model_path, lib_name), # User put library inside `model_path` + os.path.join(pardir_model_path, lib_name), # Under parent directory of `model_path` + ] + ) + + # 4. Search for model library + for candidate in candidate_paths: + if os.path.isfile(candidate): + logger.info("Using library model: %s", os.path.abspath(candidate)) + return candidate + + # 5. Error + err_msg = ( + f"Cannot find the model library that corresponds to `{chat_config.model_lib}`.\n" + f"`{chat_config.model_lib}` is either provided in the `chat_config` " + f"you passed in, or specified in {config_file_path}.\n" + "We searched over the following possible paths: \n" + ) + for candidate in candidate_paths: + err_msg += f"- {candidate}\n" + err_msg += ( + "If you would like to directly specify the model library path, you may " + "consider passing in the `ChatModule.model_lib_path` parameter.\n" + f"Please checkout {_PYTHON_GET_STARTED_TUTORIAL_URL} for an example " + "on how to load a model." + ) + raise FileNotFoundError(err_msg) + + +def _convert_chat_config_to_json_str( + chat_config: Optional[ChatConfig], conv_template: Optional[str] +) -> str: + """Convert user's input ChatConfig to a json string, omitting ``None`` fields. + + Parameters + ---------- + chat_config : Optional[ChatConfig] + User's input. A partial ChatConfig for overriding ``mlc-chat-config.json``. + conv_template : Optional[str] + The ``conv_template`` that will be used after considering potential override. + + Returns + ------ + json_str : str + A JSON string that corresponds to user's ``chat_config`` input. + Returns "" if ``chat_config`` unspecified. + """ + if chat_config is None: + return "" + # Current logic does not allow partial ChatConfig without specifying the + # conv_template. Hence we use the conv_template after considering potential overrides. + chat_config.conv_template = conv_template + # Only want to keep entries that are not None; otherwise, we would override things to None + assert hasattr(ChatConfig, "conv_config") # in case dataclass attribute name changes + chat_dict = {} + for key, value in asdict(chat_config).items(): + if key == "conv_config" and value is not None: + # conv template is another dict, do the same thing + conv_dict = {} + for conv_k, conv_v in value.items(): + if conv_v is not None: + conv_dict[conv_k] = conv_v + chat_dict[key] = conv_dict + continue + if value is not None: + chat_dict[key] = value + + return json.dumps(chat_dict) + + +def _convert_generation_config_to_json_str(generation_config: Optional[GenerationConfig]) -> str: + """Convert user's input GenerationConfig to a json string. + + Parameters + ---------- + generation_config : Optional[GenerationConfig] + User's input. A partial GenerationConfig for overriding ChatConfig generation settings. + + Returns + ------ + json_str : str + A JSON string that corresponds to user's ``generation_config`` input. + Returns "" if ``generation_config`` unspecified. + """ + if generation_config is None: + return "" + return json.dumps(asdict(generation_config)) + + +def _inspect_model_lib_metadata_memory_usage(model_lib_path, config_file_path): + cmd = [ + sys.executable, + "-m", + "mlc_chat.cli.model_metadata", + model_lib_path, + "--memory-only", + "--mlc-chat-config", + config_file_path, + ] + subprocess.run(cmd, check=False) + + +class ChatModule: # pylint: disable=too-many-instance-attributes + r"""The ChatModule for MLC LLM. + + Examples + -------- + + .. code:: python + + from mlc_chat import ChatModule + from mlc_chat.callback import StreamToStdout + + # Create a ChatModule instance + cm = ChatModule(model="Llama-2-7b-chat-hf-q4f16_1") + + # Generate a response for a given prompt + output = cm.generate( + prompt="What is the meaning of life?", + progress_callback=StreamToStdout(callback_interval=2), + ) + + # Print prefill and decode performance statistics + print(f"Statistics: {cm.stats()}\n") + + output = cm.generate( + prompt="How many points did you list out?", + progress_callback=StreamToStdout(callback_interval=2), + ) + + + Parameters + ---------- + model: str + The model folder after compiling with MLC-LLM build process. The parameter + can either be the model name with its quantization scheme + (e.g. ``Llama-2-7b-chat-hf-q4f16_1``), or a full path to the model + folder. In the former case, we will use the provided name to search + for the model folder over possible paths. + + device : str + The description of the device to run on. User should provide a string in the + form of 'device_name:device_id' or 'device_name', where 'device_name' is one of + 'cuda', 'metal', 'vulkan', 'rocm', 'opencl', 'auto' (automatically detect the + local device), and 'device_id' is the device id to run on. If no 'device_id' + is provided, it will be set to 0 by default. + + chat_config : Optional[ChatConfig] + A ``ChatConfig`` instance partially filled. Will be used to override the + ``mlc-chat-config.json``. + + model_lib_path : Optional[str] + The full path to the model library file to use (e.g. a ``.so`` file). + If unspecified, we will use the provided ``model`` to search over + possible paths. + """ + + def __init__( # pylint: disable=too-many-arguments + self, + model: str, + device: str = "auto", + chat_config: Optional[ChatConfig] = None, + model_lib_path: Optional[str] = None, + ): + # 0. Get device: + # Retrieve device_name and device_id (if any, default 0) from device arg + self.device = detect_device(device) + device_type = self.device.device_type + device_id = self.device.device_id + + self.energy_events = {} + self.generate_counter = 0 + + # 1. Populate chat module and their functions + fcreate_chat_mod = tvm.get_global_func("mlc.llm_chat_create") + assert fcreate_chat_mod is not None + chat_mod = fcreate_chat_mod(device_type, device_id) + + # chat module related functions + self._reload_func = chat_mod["reload"] + self._unload_func = chat_mod["unload"] + self._prefill_func = chat_mod["prefill"] + self._embed_func = chat_mod["embed"] + self._prefill_with_embed_func = chat_mod["prefill_with_embed"] + self._decode_func = chat_mod["decode"] + self._raw_generate_func = chat_mod["raw_generate"] + self._reset_chat_func = chat_mod["reset_chat"] + self._load_json_override_func = chat_mod["load_json_override"] + self._stopped_func = chat_mod["stopped"] + self._get_message_func = chat_mod["get_message"] + self._runtime_stats_text_func = chat_mod["runtime_stats_text"] + self._verbose_runtime_stats_text_func = chat_mod["verbose_runtime_stats_text"] + self._reset_runtime_stats_func = chat_mod["reset_runtime_stats"] + self._get_config_json_func = chat_mod["get_config_json"] + self._process_system_prompts_func = chat_mod["process_system_prompts"] + self._evaluate_func = chat_mod["evaluate"] + self._get_role0_func = chat_mod["get_role0"] + self._get_role1_func = chat_mod["get_role1"] + + # 2. Look up model_path + self.model_path, self.config_file_path = _get_model_path(model) + + # 3. Instantiate chat_config + self.chat_config = _get_chat_config(self.config_file_path, chat_config) + + # 4. Look up model library + try: + self.model_lib_path = _get_lib_module_path( + model, + self.model_path, + self.chat_config, + model_lib_path, + self.device.MASK2STR[self.device.device_type], + self.config_file_path, + ) + except FileNotFoundError: + logger.info("Model lib not found. Now compiling model lib on device...") + from mlc_chat.interface import ( # pylint: disable=import-outside-toplevel + jit, + ) + + self.model_lib_path = str( + jit.jit( + model_path=Path(self.model_path), + chat_config=asdict(self.chat_config), + device=self.device, + ) + ) + _inspect_model_lib_metadata_memory_usage(self.model_lib_path, self.config_file_path) + + # 5. Call reload + user_chat_config_json_str = _convert_chat_config_to_json_str( + self.chat_config, self.chat_config.conv_template + ) + self._reload(self.model_lib_path, self.model_path, user_chat_config_json_str) + + def generate( + self, + prompt: Union[str, List["ChatMessage"]], + generation_config: Optional[GenerationConfig] = None, + progress_callback=None, + stateless=False, + ) -> Union[str, List[str]]: + r"""A high-level method that returns the full response from the chat module given a user + prompt. User can optionally specify which callback method to use upon receiving the + response. By default, no callback will be applied. + + Parameters + ---------- + prompt: Union[str, List[ChatMessage]] + The user input prompt, i.e. a question to ask the chat module. + It can also be the whole conversation history (list of messages with role and content) + eg: + + .. code:: + + [ + ChatMessage(role="user", content="Hello, how are you?"), + ChatMessage(role="assistant", content="I'm fine, thank you. How about you?"), + ChatMessage(role="user", content="I'm good too."), + ] + generation_config: Optional[GenerationConfig] + The generation config object to override the ChatConfig generation settings. + progress_callback: object + The optional callback method used upon receiving a newly generated message from the + chat module. See `mlc_chat/callback.py` for a full list of available callback classes. + Currently, only streaming to stdout callback method is supported, see `Examples` for + more detailed usage. + + Returns + ------- + output : string + The generated full output from the chat module. + + Examples + -------- + .. code-block:: python + + # Suppose we would like to stream the response of the chat module to stdout + # with a refresh interval of 2. Upon calling generate(), We will see the response of + # the chat module streaming to stdout piece by piece, and in the end we receive the + # full response as a single string `output`. + + from mlc_chat import ChatModule, GenerationConfig, callback + cm = ChatModule(xxx) + prompt = "what's the color of banana?" + output = cm.generate( + prompt, GenerationConfig(temperature=0.8), callback.StreamToStdout(callback_interval=2) + ) + print(output) + """ + new_msgs = [] + num_return_sequences = 1 + return_str = True + if (generation_config is not None) and (generation_config.n is not None): + num_return_sequences = generation_config.n + return_str = False + + for idx in range(num_return_sequences): + if stateless: + self.reset_chat() + self.energy_events[f"chat.{self.generate_counter}.{idx}.prefill.start"] = time.time_ns() + self._prefill(prompt, generation_config=generation_config) + self.energy_events[f"chat.{self.generate_counter}.{idx}.prefill.end"] = time.time_ns() + + if not progress_callback: + decode_counter = 0 + while not self._stopped(): + self.energy_events[f"chat.{self.generate_counter}.{idx}.decode.{decode_counter}.start"] = time.time_ns() + self._decode(generation_config=generation_config) + self.energy_events[f"chat.{self.generate_counter}.{idx}.decode.{decode_counter}.end"] = time.time_ns() + self.energy_events[f"chat.{self.generate_counter}.{idx}.get_message.start"] = time.time_ns() + new_msg = self._get_message() + self.energy_events[f"chat.{self.generate_counter}.{idx}.get_message.end"] = time.time_ns() + new_msgs.append(new_msg) + else: + # apply callback with a rate of callback_interval + i, new_msg = 0, "" + decode_counter = 0 + while not self._stopped(): + self.energy_events[f"chat.{self.generate_counter}.{idx}.decode.{decode_counter}.start"] = time.time_ns() + self._decode(generation_config=generation_config) + self.energy_events[f"chat.{self.generate_counter}.{idx}.decode.{decode_counter}.end"] = time.time_ns() + if i % progress_callback.callback_interval == 0 or self._stopped(): + self.energy_events[f"chat.{self.generate_counter}.{idx}.get_message.start"] = time.time_ns() + new_msg = self._get_message() + self.energy_events[f"chat.{self.generate_counter}.{idx}.get_message.end"] = time.time_ns() + progress_callback(new_msg) + i += 1 + progress_callback(stopped=True) + new_msgs.append(new_msg) + return new_msgs[0] if return_str else new_msgs + + def reset_chat(self, chat_config: Optional[ChatConfig] = None): + r"""Reset the chat session, clear all chat history, and potentially + override the original `mlc-chat-config.json`. + + Parameters + ---------- + chat_config : Optional[ChatConfig] + A ``ChatConfig`` instance partially filled. If specified, the chat + module will reload the `mlc-chat-config.json`, and override it with + ``chat_config``, just like in initialization. + + Note + ---- + The model remains the same after :func:`reset_chat`. + To reload module, please either re-initialize a :class:`ChatModule` instance + or use :func:`_reload` instead. + """ + self._reset_chat_func() + if chat_config is not None: + # Redo the overriding + self.chat_config = _get_chat_config(self.config_file_path, chat_config) + user_chat_config_json_str = _convert_chat_config_to_json_str( + chat_config, self.chat_config.conv_template + ) + # Second argument is `partial_update = True` + self._load_json_override_func(user_chat_config_json_str, True) + + def embed_text(self, input: str): # pylint: disable=redefined-builtin + r"""Given a text input, returns its embedding in the LLM. + + Parameters + ---------- + input : str + The user input string. + + Returns + ------- + embedding : tvm.runtime.NDArray + The embedding of the text. + + Note + ---- + This is a high-level method and is only used for retrieving text embeddings. Users are + not supposed to call :func:`generate` after calling this method in the same chat session, + since the input to this method is not prefilled and will cause error. If user needs to + call :func:`generate` later, please call :func:`reset_chat` first. + For a more fine-grained embedding API, see :func:`_embed`. + """ + return self._embed_func(input, PlaceInPrompt.Middle.value) + + def stats(self, verbose=False) -> str: + r"""Get the runtime stats of the encoding step, decoding step (and embedding step if exists) + of the chat module in text form. + + Returns + ------- + stats : str + The runtime stats text. + """ + if verbose: + return self._verbose_runtime_stats_text_func() + return self._runtime_stats_text_func() + + def benchmark_generate(self, prompt: str, generate_length: int) -> str: + r"""Controlled generation with input prompt and fixed number of + generated tokens, ignoring system prompt. For example, + + .. code:: python + + from mlc_chat import ChatModule + + cm = ChatModule(model="Llama-2-7b-chat-hf-q4f16_1") + output = cm.benchmark_generate("What's the meaning of life?", generate_length=256) + print(f"Generated text:\n{output}\n") + print(f"Statistics: {cm.stats()}") + + will generate 256 tokens in total based on prompt "What's the meaning + of life?". After generation, you can use `cm.stats()` to print the + generation speed. + + Notes + ----- + 1. This function is typically used in controlled benchmarks. It generates + text without system prompt (i.e., it is pure text generation with no chat + style) and ignores the token stop model(s). + 2. To make the benchmark as accurate as possible, we first do a round of + warmup prefill and decode before text generation. + 3. This function resets the previous performance statistics. + + Parameters + ---------- + prompt : str + The prompt of the text generation. + + generate_length : int + The target length of generation. + + Returns + ------- + output : str + The generated text output. + """ + if generate_length < 0: + raise ValueError( + "The generation length is expected to be non-negative, " + f"while the given length is {generate_length}" + ) + + # warmup run + self.reset_chat() + self._prefill(prompt) + self._decode() + + return self._raw_generate_func(prompt, generate_length) + + def _reload( + self, + lib: str, + model_path: str, + app_config_json: str = "", + ): + r"""Reload the chat module from the given library and model path. + + Parameters + ---------- + lib : str + The library path. + model_path : str + The model path. + app_config_json: str + The partial config that is used to partially override the model configuration. + """ + self.energy_events[f"load_model.start"] = time.time_ns() + self._reload_func(lib, model_path, app_config_json) + self.energy_events[f"load_model.end"] = time.time_ns() + + def _unload(self): + r"""Unload the chat module and clear memory of all loaded models.""" + self.energy_events[f"unload_model.start"] = time.time_ns() + self._unload_func() + self.energy_events[f"unload_model.end"] = time.time_ns() + + def _prefill( + self, + input: Union[str, List["ChatMessage"]], # pylint: disable=redefined-builtin + decode_next_token: bool = True, + place_in_prompt: PlaceInPrompt = PlaceInPrompt.All, + generation_config: Optional[GenerationConfig] = None, + ): + r"""Run prefill stage for a given input and optionally decode the first output token. + User can decide where to place the input in the prompt. + + Parameters + ---------- + input : Union[str, List[ChatMessage]] + The user input prompt, i.e. a question to ask the chat module. + It can also be the whole conversation history (list of messages with role and content) + eg: + + .. code:: + + [ + ChatMessage(role="user", content="Hello, how are you?"), + ChatMessage(role="assistant", content="I'm fine, thank you. How about you?"), + ChatMessage(role="user", content="I'm good too."), + ] + decode_next_token : bool + Whether to decode the next token after prefilling. + place_in_prompt: PlaceInPrompt + The place of the input message in the prompt. See `class PlaceInPrompt` for details. + generation_config: Optional[GenerationConfig] + The generation config to override the ChatConfig generation settings. + """ + generation_config = _get_generation_config(self.chat_config, generation_config) + generation_config_str = _convert_generation_config_to_json_str(generation_config) + + if isinstance(input, list): + # Populate conversation.messages using load_json_override + if len(input) > 1: + conv_config = json.loads(self._get_config_json())["conv_config"] + messages = [] + role0 = self._get_role_0() + role1 = self._get_role_1() + for _, msg in enumerate(input[:-1]): + role = msg.role + content = msg.content + if role in ("user", "system"): + messages.append([role0, content]) + elif role == "assistant": + messages.append([role1, content]) + else: + raise ValueError("Only user and assistant roles are supported.") + if not input[-1].role == "user": + raise ValueError("Last message should be from user.") + conv_config["messages"] = messages + conv_config["offset"] = 0 + # Otherwise, the offset will be set to the length of the conversation, + # which means history will be retained even after calling reset_chat + self._load_json_override( + json.dumps({"conv_config": conv_config}), + partial_update=True, + ) + input_str = input[-1].content + else: + input_str = input + + self._prefill_func( + input_str, decode_next_token, place_in_prompt.value, generation_config_str + ) + + def _embed( + self, + input: str, # pylint: disable=redefined-builtin + place_in_prompt: PlaceInPrompt = PlaceInPrompt.All, + generation_config: Optional[GenerationConfig] = None, + ): + r"""A more fine-grained embedding API. Given a text input, get the embedding of the + tokenized prompt. User can decide where to place the input in the prompt. This functionality + usually aids the subsequent call to :func:`_prefill_with_embed`. + + Parameters + ---------- + input : str + The user input string. + place_in_prompt: PlaceInPrompt + The place of the input message in the prompt. See `class PlaceInPrompt` for details. + generation_config: Optional[GenerationConfig] + The generation config to override the ChatConfig generation settings. + + Returns + ------- + embedding : tvm.runtime.NDArray + The embedding of the text. + """ + generation_config = _get_generation_config(self.chat_config, generation_config) + generation_config_str = _convert_generation_config_to_json_str(generation_config) + + return self._embed_func(input, place_in_prompt.value, generation_config_str) + + def _prefill_with_embed( + self, + embedding: tvm.runtime.NDArray, + decode_next_token: bool = True, + generation_config: Optional[GenerationConfig] = None, + ): + r"""Given an embedding, run the prefill stage and optionally decode the first output token. + + Parameters + ---------- + embedding : tvm.runtime.NDArray + The embedding of user input. + decode_next_token : bool + Whether to decode the next token after prefilling. + generation_config: Optional[GenerationConfig] + The generation config to override the ChatConfig generation settings. + """ + generation_config = _get_generation_config(self.chat_config, generation_config) + generation_config_str = _convert_generation_config_to_json_str(generation_config) + + self._prefill_with_embed_func(embedding, decode_next_token, generation_config_str) + + def _decode(self, generation_config: Optional[GenerationConfig] = None): + r"""Decode the next token, the decoding result is stored in a buffer and + can be retrieved by :func:`get_message`. + + Parameters + ---------- + generation_config: Optional[GenerationConfig] + The generation config to override the ChatConfig generation settings. + """ + generation_config = _get_generation_config(self.chat_config, generation_config) + generation_config_str = _convert_generation_config_to_json_str(generation_config) + self._decode_func(generation_config_str) + + def _stopped(self) -> bool: + r"""Check if the stop condition is met for the current round. + + Returns + ------- + stopped : bool + """ + return self._stopped_func() != 0 + + def _get_message(self) -> str: + r"""Get the output message in the current round. + + Returns + ------- + message : str + + Note + ---- + This function returns the message that corresponds to + all the tokens decoded so far. + """ + return self._get_message_func() + + def _get_config_json(self): + r"""Get the configuration of the chat module in a single json string. + + Returns + ------- + config : str + The config json string. + """ + return self._get_config_json_func() + + def _load_json_override(self, config_str: str, partial_update: bool = False): + r"""Load JSON config and override existing configurations for the chat module. + + Parameters + ---------- + config_str : str + A json config string that partially specifies some of the options. + partial_update : bool + Whether it's a partial update or full update. If set to true, we perform a partial + update on some of the provided options; if set to false, all options must be provided. + """ + self._load_json_override_func(config_str, partial_update) + + def _get_role_0(self): + r"""Get the name of role 0 in the conversation. + + Returns + ------- + name : str + The name of role 0. + """ + return self._get_role0_func() + + def _get_role_1(self): + r"""Get the name of role 1 in the conversation. + + Returns + ------- + name : str + The name of role 1. + """ + return self._get_role1_func() + + def _reset_runtime_stats(self): + r"""Reset the runtime stats, clear all performance history.""" + self._reset_runtime_stats_func() + + def _process_system_prompts(self): + r"""Pre-process by prefilling the system prompts, running prior to any user input.""" + self.energy_events["prompt.system.start"] = time.time_ns() + self._process_system_prompts_func() + self.energy_events["prompt.system.end"] = time.time_ns() diff --git a/python/mlc_chat/cli/__init__.py b/python/mlc_chat/cli/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/mlc_chat/cli/bench.py b/python/mlc_chat/cli/bench.py new file mode 100644 index 0000000..4b9af7c --- /dev/null +++ b/python/mlc_chat/cli/bench.py @@ -0,0 +1,62 @@ +"""Command line entrypoint of benchmark.""" +from mlc_chat.help import HELP +from mlc_chat.interface.bench import bench +from mlc_chat.interface.chat import ChatConfigOverride +from mlc_chat.support.argparse import ArgumentParser + + +def main(argv): + """Parse command line arguments and call `mlc_llm.interface.bench`.""" + parser = ArgumentParser("MLC LLM Chat CLI") + + parser.add_argument( + "model", + type=str, + help=HELP["model"] + " (required)", + ) + parser.add_argument( + "--prompt", + type=str, + default="What is the meaning of life?", + help=HELP["prompt"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--opt", + type=str, + default="O2", + help=HELP["opt"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--device", + type=str, + default="auto", + help=HELP["device_deploy"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--overrides", + type=ChatConfigOverride.from_str, + default="", + help=HELP["chatconfig_overrides"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--generate-length", + type=int, + default=256, + help=HELP["generate_length"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--model-lib-path", + type=str, + default=None, + help=HELP["model_lib_path"] + ' (default: "%(default)s")', + ) + parsed = parser.parse_args(argv) + bench( + model=parsed.model, + prompt=parsed.prompt, + device=parsed.device, + opt=parsed.opt, + overrides=parsed.overrides, + generate_length=parsed.generate_length, + model_lib_path=parsed.model_lib_path, + ) diff --git a/python/mlc_chat/cli/benchmark.py b/python/mlc_chat/cli/benchmark.py new file mode 100644 index 0000000..e6014aa --- /dev/null +++ b/python/mlc_chat/cli/benchmark.py @@ -0,0 +1,86 @@ +"""A command line tool for benchmarking a chat model.""" +import argparse +from pathlib import Path + +from mlc_chat import ChatConfig, ChatModule + +parser = argparse.ArgumentParser(description="Benchmark an MLC LLM ChatModule.") +parser.add_argument( + "--model", + type=str, + help="""The model folder after compiling with MLC-LLM build process. The parameter can either + be the model name with its quantization scheme (e.g. ``Llama-2-7b-chat-hf-q4f16_1``), or a + full path to the model folder. In the former case, we will use the provided name to search for + the model folder over possible paths.""", + required=True, +) +parser.add_argument( + "--model-lib", + type=str, + help="""The compiled model library. In MLC LLM, an LLM is compiled to a shared or static + library (.so or .a), which contains GPU computation to efficiently run the LLM. MLC Chat, + as the runtime of MLC LLM, depends on the compiled model library to generate tokens. + """, + required=False, +) +parser.add_argument( + "--tensor-parallel-shards", + "--num-shards", + type=int, + help="Number of GPUs to be used.", + dest="tensor_parallel_shards", + required=False, +) +parser.add_argument( + "--device", + type=str, + help="""The description of the device to run on. User should provide a string in the form of + 'device_name:device_id' or 'device_name', where 'device_name' is one of 'cuda', 'metal', + 'vulkan', 'rocm', 'opencl', and 'device_id' is the device id to run on. If no 'device_id' is + provided, it will be set to 0 by default. + """, + required=True, +) +parser.add_argument( + "--prompt", + type=str, + help="The prompt to generate from.", + required=True, +) +parser.add_argument( + "--generate-length", + type=int, + help="The length (numer of tokens) of the generated text.", + required=True, +) + + +def _load_prompt(path_or_prompt: str) -> str: + """Load the prompt from a file or use the provided prompt.""" + try: + path = Path(path_or_prompt) + if path.is_file(): + with path.open("r", encoding="utf-8") as in_file: + return in_file.read() + except: # pylint: disable=bare-except + pass + return path_or_prompt + + +def main(): + """The main function that runs the benchmarking.""" + args = parser.parse_args() + chat_module = ChatModule( + model=args.model, + device=args.device, + chat_config=ChatConfig(tensor_parallel_shards=args.tensor_parallel_shards), + model_lib_path=args.model_lib, + ) + prompt = _load_prompt(args.prompt) + output = chat_module.benchmark_generate(prompt, generate_length=args.generate_length) + print(f"Generated text:\n{output}\n") + print(f"Statistics: {chat_module.stats(verbose=True)}") + + +if __name__ == "__main__": + main() diff --git a/python/mlc_chat/cli/chat.py b/python/mlc_chat/cli/chat.py new file mode 100644 index 0000000..96edef2 --- /dev/null +++ b/python/mlc_chat/cli/chat.py @@ -0,0 +1,54 @@ +"""Command line entrypoint of chat.""" +from mlc_chat.help import HELP +from mlc_chat.interface.chat import ChatConfigOverride, chat +from mlc_chat.support.argparse import ArgumentParser + + +def main(argv): + """Parse command line arguments and call `mlc_llm.interface.chat`.""" + parser = ArgumentParser("MLC LLM Chat CLI") + + parser.add_argument( + "model", + type=str, + help=HELP["model"] + " (required)", + ) + parser.add_argument( + "--opt", + type=str, + default="O2", + help=HELP["opt"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--device", + type=str, + default="auto", + help=HELP["device_deploy"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--overrides", + type=ChatConfigOverride.from_str, + default="", + help=HELP["chatconfig_overrides"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--model-lib-path", + type=str, + default=None, + help=HELP["model_lib_path"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--energy-events", + type=str, + default="energy_events.txt", + help="Energy events file to use for energy profiling (default: energy_events.txt)" + ) + parsed = parser.parse_args(argv) + chat( + model=parsed.model, + device=parsed.device, + opt=parsed.opt, + overrides=parsed.overrides, + model_lib_path=parsed.model_lib_path, + energy_events_filename=parsed.energy_events, + ) diff --git a/python/mlc_chat/cli/check_device.py b/python/mlc_chat/cli/check_device.py new file mode 100644 index 0000000..a78fd4d --- /dev/null +++ b/python/mlc_chat/cli/check_device.py @@ -0,0 +1,30 @@ +"""Check if a device exists.""" +import sys + +from tvm.runtime import Device +from tvm.runtime import device as as_device + + +def _check_device(device: Device) -> bool: + try: + return bool(device.exist) + except: # pylint: disable=bare-except + return False + + +def main(): + """Entrypoint for device check.""" + device_str = sys.argv[1] + device_ids = [] + i = 0 + while True: + if _check_device(as_device(device_str, i)): + device_ids.append(i) + i += 1 + else: + break + print(f"check_device:{','.join(str(i) for i in device_ids)}") + + +if __name__ == "__main__": + main() diff --git a/python/mlc_chat/cli/compile.py b/python/mlc_chat/cli/compile.py new file mode 100644 index 0000000..c56b404 --- /dev/null +++ b/python/mlc_chat/cli/compile.py @@ -0,0 +1,142 @@ +"""Command line entrypoint of compilation.""" +import argparse +import json +import re +from functools import partial +from pathlib import Path +from typing import Union + +from mlc_chat.help import HELP +from mlc_chat.interface.compile import ( # pylint: disable=redefined-builtin + ModelConfigOverride, + OptimizationFlags, + compile, +) +from mlc_chat.model import MODELS +from mlc_chat.quantization import QUANTIZATION +from mlc_chat.support.argparse import ArgumentParser +from mlc_chat.support.auto_config import ( + detect_mlc_chat_config, + detect_model_type, + detect_quantization, +) +from mlc_chat.support.auto_target import ( + detect_system_lib_prefix, + detect_target_and_host, +) + + +def main(argv): + """Parse command line argumennts and call `mlc_llm.compiler.compile`.""" + + def _parse_output(path: Union[str, Path]) -> Path: + path = Path(path) + if path.is_dir(): + raise argparse.ArgumentTypeError(f"Output cannot be a directory: {path}") + parent = path.parent + if not parent.is_dir(): + raise argparse.ArgumentTypeError(f"Directory does not exist: {parent}") + return path + + def _parse_dir(path: Union[str, Path], auto_create: bool = False) -> Path: + path = Path(path) + if not auto_create and not path.is_dir(): + raise argparse.ArgumentTypeError(f"Directory does not exist: {path}") + if auto_create and not path.is_dir(): + path.mkdir(parents=True) + return path + + def _check_system_lib_prefix(prefix: str) -> str: + pattern = r"^[a-zA-Z_][a-zA-Z0-9_]*$" + if prefix == "" or re.match(pattern, prefix): + return prefix + raise argparse.ArgumentTypeError( + "Invalid prefix. It should only consist of " + "numbers (0-9), alphabets (A-Z, a-z) and underscore (_)." + ) + + parser = ArgumentParser("mlc_chat compile") + parser.add_argument( + "model", + type=detect_mlc_chat_config, + help=HELP["model"] + " (required)", + ) + parser.add_argument( + "--quantization", + type=str, + choices=list(QUANTIZATION.keys()), + help=HELP["quantization"] + + " (default: look up mlc-chat-config.json, choices: %(choices)s)", + ) + parser.add_argument( + "--model-type", + type=str, + default="auto", + choices=["auto"] + list(MODELS.keys()), + help=HELP["model_type"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--device", + type=str, + default="auto", + help=HELP["device_compile"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--host", + type=str, + default="auto", + help=HELP["host"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--opt", + type=OptimizationFlags.from_str, + default="O2", + help=HELP["opt"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--system-lib-prefix", + type=str, + default="auto", + help=HELP["system_lib_prefix"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--output", + "-o", + type=_parse_output, + required=True, + help=HELP["output_compile"] + " (required)", + ) + parser.add_argument( + "--overrides", + type=ModelConfigOverride.from_str, + default="", + help=HELP["overrides"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--debug-dump", + type=partial(_parse_dir, auto_create=True), + default=None, + help=HELP["debug_dump"] + " (default: %(default)s)", + ) + parsed = parser.parse_args(argv) + target, build_func = detect_target_and_host(parsed.device, parsed.host) + parsed.model_type = detect_model_type(parsed.model_type, parsed.model) + parsed.quantization = detect_quantization(parsed.quantization, parsed.model) + parsed.system_lib_prefix = detect_system_lib_prefix( + parsed.device, parsed.system_lib_prefix, parsed.model_type.name, parsed.quantization.name + ) + with open(parsed.model, "r", encoding="utf-8") as config_file: + config = json.load(config_file) + + compile( + config=config, + quantization=parsed.quantization, + model_type=parsed.model_type, + target=target, + opt=parsed.opt, + build_func=build_func, + system_lib_prefix=parsed.system_lib_prefix, + output=parsed.output, + overrides=parsed.overrides, + debug_dump=parsed.debug_dump, + ) diff --git a/python/mlc_chat/cli/convert_weight.py b/python/mlc_chat/cli/convert_weight.py new file mode 100644 index 0000000..5e97cc7 --- /dev/null +++ b/python/mlc_chat/cli/convert_weight.py @@ -0,0 +1,95 @@ +"""Command line entrypoint of weight conversion.""" +import argparse +from pathlib import Path +from typing import Union + +from mlc_chat.help import HELP +from mlc_chat.interface.convert_weight import convert_weight +from mlc_chat.model import MODELS +from mlc_chat.quantization import QUANTIZATION +from mlc_chat.support.argparse import ArgumentParser +from mlc_chat.support.auto_config import detect_config, detect_model_type +from mlc_chat.support.auto_device import detect_device +from mlc_chat.support.auto_weight import detect_weight + + +def main(argv): + """Parse command line argumennts and apply quantization.""" + + def _parse_source(path: Union[str, Path], config_path: Path) -> Path: + if path == "auto": + return config_path.parent + path = Path(path) + if not path.exists(): + raise argparse.ArgumentTypeError(f"Model source does not exist: {path}") + return path + + def _parse_output(path: Union[str, Path]) -> Path: + path = Path(path) + if not path.is_dir(): + path.mkdir(parents=True, exist_ok=True) + return path + + parser = ArgumentParser("MLC AutoLLM Quantization Framework") + parser.add_argument( + "config", + type=detect_config, + help=HELP["config"] + " (required)", + ) + parser.add_argument( + "--quantization", + type=str, + required=True, + choices=list(QUANTIZATION.keys()), + help=HELP["quantization"] + " (required, choices: %(choices)s)", + ) + parser.add_argument( + "--model-type", + type=str, + default="auto", + choices=["auto"] + list(MODELS.keys()), + help=HELP["model_type"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--device", + default="auto", + type=detect_device, + help=HELP["device_quantize"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--source", + type=str, + default="auto", + help=HELP["source"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--source-format", + type=str, + choices=["auto", "huggingface-torch", "huggingface-safetensor", "awq"], + default="auto", + help=HELP["source_format"] + ' (default: "%(default)s", choices: %(choices)s")', + ) + parser.add_argument( + "--output", + "-o", + type=_parse_output, + required=True, + help=HELP["output_quantize"] + " (required)", + ) + + parsed = parser.parse_args(argv) + parsed.source, parsed.source_format = detect_weight( + weight_path=_parse_source(parsed.source, parsed.config), + config_json_path=parsed.config, + weight_format=parsed.source_format, + ) + model = detect_model_type(parsed.model_type, parsed.config) + convert_weight( + config=parsed.config, + quantization=QUANTIZATION[parsed.quantization], + model=model, + device=parsed.device, + source=parsed.source, + source_format=parsed.source_format, + output=parsed.output, + ) diff --git a/python/mlc_chat/cli/delivery.py b/python/mlc_chat/cli/delivery.py new file mode 100644 index 0000000..cc5fd07 --- /dev/null +++ b/python/mlc_chat/cli/delivery.py @@ -0,0 +1,280 @@ +"""Continuous model delivery for MLC LLM models.""" +import argparse +import dataclasses +import json +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path +from typing import Any, Callable, Dict, List, Tuple, Union + +from huggingface_hub import HfApi # pylint: disable=import-error +from huggingface_hub.utils import HfHubHTTPError # pylint: disable=import-error + +from mlc_chat.support import logging +from mlc_chat.support.argparse import ArgumentParser +from mlc_chat.support.constants import MLC_TEMP_DIR +from mlc_chat.support.download import git_clone +from mlc_chat.support.style import bold, green, red + +logging.enable_logging() +logger = logging.getLogger(__name__) + +GEN_CONFIG_OPTIONAL_ARGS = [ + "context_window_size", + "sliding_window_size", + "prefill_chunk_size", + "attention_sink_size", + "tensor_parallel_shards", +] + + +@dataclasses.dataclass +class ModelInfo: # pylint: disable=too-many-instance-attributes + """Necessary information for the model delivery""" + + model_id: str + model: Path + conv_template: str + quantization: str + source_format: str = "auto" + # If unspecified in CLI, remains to be None and will not be + # passed to `gen_config` or `convert_weight` + context_window_size: int = None + sliding_window_size: int = None + prefill_chunk_size: int = None + attention_sink_size: int = None + tensor_parallel_shards: int = None + + +class DeferredScope: + """A context manager that defers execution of functions until exiting the scope.""" + + def __init__(self): + self.deferred_functions = [] + + def add(self, func: Callable[[], None]): + """Add a function to be executed when exiting the scope.""" + self.deferred_functions.append(func) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + for func in reversed(self.deferred_functions): + func() + return False + + def create_temp_dir(self) -> Path: + """Create a temporary directory that will be deleted when exiting the scope.""" + temp_dir = tempfile.mkdtemp(dir=MLC_TEMP_DIR) + self.add(lambda: shutil.rmtree(temp_dir, ignore_errors=True)) + return Path(temp_dir) + + +def _clone_repo(model: Union[str, Path], deferred: DeferredScope) -> Path: + if isinstance(model, Path): + if not model.exists(): + raise ValueError(f"Invalid model source: {model}") + return model + if model.startswith("https://") or model.startswith("git://"): + result = deferred.create_temp_dir() / "repo" + git_clone(model, result, ignore_lfs=False) + return result + result = Path(model) + if result.exists(): + return result + raise ValueError(f"Invalid model source: {model}") + + +def _run_quantization( + model_info: ModelInfo, + repo: str, + api: HfApi, +) -> bool: + logger.info("[HF] Creating repo https://huggingface.co/%s", repo) + try: + api.create_repo(repo_id=repo, private=False) + except HfHubHTTPError as error: + if error.response.status_code != 409: + raise + logger.info("[HF] Repo already exists. Recreating...") + api.delete_repo(repo_id=repo) + api.create_repo(repo_id=repo, private=False) + logger.info("[HF] Repo recreated") + succeeded = True + with tempfile.TemporaryDirectory(dir=MLC_TEMP_DIR) as output_dir: + log_path = Path(output_dir) / "logs.txt" + with log_path.open("a", encoding="utf-8") as log_file: + assert isinstance(model_info.model, Path) + logger.info("[MLC] Processing in directory: %s", output_dir) + # Required arguments + cmd = [ + sys.executable, + "-m", + "mlc_chat", + "gen_config", + str(model_info.model), + "--quantization", + model_info.quantization, + "--conv-template", + model_info.conv_template, + "--output", + output_dir, + ] + # Optional arguments + for optional_arg in GEN_CONFIG_OPTIONAL_ARGS: + optional_arg_val = getattr(model_info, optional_arg, None) + if optional_arg_val is not None: + # e.g. --context-window-size 4096 + cmd += ["--" + optional_arg.replace("_", "-"), str(optional_arg_val)] + + print(" ".join(cmd), file=log_file, flush=True) + subprocess.run(cmd, check=True, stdout=log_file, stderr=subprocess.STDOUT) + cmd = [ + sys.executable, + "-m", + "mlc_chat", + "convert_weight", + str(model_info.model), + "--quantization", + model_info.quantization, + "--source-format", + model_info.source_format, + "--output", + output_dir, + ] + print(" ".join(cmd), file=log_file, flush=True) + subprocess.run(cmd, check=False, stdout=log_file, stderr=subprocess.STDOUT) + logger.info("[MLC] Complete!") + if not (Path(output_dir) / "ndarray-cache.json").exists(): + logger.error( + "[%s] Model %s. Quantization %s. No weights metadata found.", + red("FAILED"), + model_info.model_id, + model_info.quantization, + ) + succeeded = False + logger.info("[HF] Uploading to: https://huggingface.co/%s", repo) + for _retry in range(10): + try: + api.upload_folder( + folder_path=output_dir, + repo_id=repo, + commit_message="Initial commit", + ) + except Exception as exc: # pylint: disable=broad-except + logger.error("[%s] %s. Retrying...", red("FAILED"), exc) + else: + break + else: + raise RuntimeError("Failed to upload to HuggingFace Hub with 10 retries") + return succeeded + + +def _main( # pylint: disable=too-many-locals + username: str, + api: HfApi, + spec: Dict[str, Any], +): + failed_cases: List[Tuple[str, str]] = [] + for task_index, task in enumerate(spec["tasks"], 1): + with DeferredScope() as deferred: + logger.info( + bold("[{task_index}/{total_tasks}] Processing model: ").format( + task_index=task_index, + total_tasks=len(spec["tasks"]), + ) + + green(task["model_id"]) + ) + model = _clone_repo(task["model"], deferred) + for quantization in spec["default_quantization"] + task.get("quantization", []): + model_info = { + "model_id": task["model_id"], + "model": model, + "conv_template": task["conv_template"], + } + # Process optional arguments + for optional_arg in GEN_CONFIG_OPTIONAL_ARGS: + # e.g. "context_window_size": task.get("context_window_size", None) + model_info[optional_arg] = task.get(optional_arg, None) + if isinstance(quantization, str): + model_info["quantization"] = quantization + else: + model_info["quantization"] = quantization.pop("format") + model_info.update(quantization) + repo = spec.get("destination", "{username}/{model_id}-{quantization}-MLC").format( + username=username, + model_id=model_info["model_id"], + quantization=model_info["quantization"], + ) + logger.info( + "%s%s. %s%s. %s%s", + bold("Model: "), + green(task["model_id"]), + bold("Quantization: "), + green(model_info["quantization"]), + bold("Repo: "), + green(f"https://huggingface.co/{repo}"), + ) + with DeferredScope() as inner_deferred: + model_info["model"] = _clone_repo(model_info["model"], inner_deferred) + result = _run_quantization( + ModelInfo(**model_info), + repo=spec["destination"].format( + username=username, + model_id=model_info["model_id"], + quantization=model_info["quantization"], + ), + api=api, + ) + if not result: + failed_cases.append( + (task["model_id"], model_info["quantization"]), + ) + if failed_cases: + logger.info("Total %s %s:", len(failed_cases), red("failures")) + for model_id, quantization in failed_cases: + logger.info(" Model %s. Quantization %s.", model_id, quantization) + + +def main(): + """Entry point.""" + + def _load_spec(path_spec: str) -> Dict[str, Any]: + path = Path(path_spec) + if not path.exists(): + raise argparse.ArgumentTypeError(f"Spec file does not exist: {path}") + with path.open("r", encoding="utf-8") as i_f: + return json.load(i_f) + + parser = ArgumentParser("MLC LLM continuous model delivery") + parser.add_argument( + "--username", + type=str, + required=True, + help="HuggingFace username", + ) + parser.add_argument( + "--token", + type=str, + required=True, + help="HuggingFace access token, obtained under https://huggingface.co/settings/tokens", + ) + parser.add_argument( + "--spec", + type=_load_spec, + required=True, + help="Path to the spec file", + ) + parsed = parser.parse_args() + _main( + parsed.username, + spec=parsed.spec, + api=HfApi(token=parsed.token), + ) + + +if __name__ == "__main__": + main() diff --git a/python/mlc_chat/cli/gen_config.py b/python/mlc_chat/cli/gen_config.py new file mode 100644 index 0000000..dd68484 --- /dev/null +++ b/python/mlc_chat/cli/gen_config.py @@ -0,0 +1,106 @@ +"""Command line entrypoint of configuration generation.""" +from pathlib import Path +from typing import Union + +from mlc_chat.help import HELP +from mlc_chat.interface.gen_config import CONV_TEMPLATES, gen_config +from mlc_chat.model import MODELS +from mlc_chat.quantization import QUANTIZATION +from mlc_chat.support.argparse import ArgumentParser +from mlc_chat.support.auto_config import detect_config, detect_model_type + + +def main(argv): + """Parse command line argumennts and call `mlc_llm.compiler.gen_config`.""" + parser = ArgumentParser("MLC LLM Configuration Generator") + + def _parse_output(path: Union[str, Path]) -> Path: + path = Path(path) + if not path.is_dir(): + path.mkdir(parents=True, exist_ok=True) + return path + + parser.add_argument( + "config", + type=detect_config, + help=HELP["config"] + " (required)", + ) + parser.add_argument( + "--quantization", + type=str, + required=True, + choices=list(QUANTIZATION.keys()), + help=HELP["quantization"] + " (required, choices: %(choices)s)", + ) + parser.add_argument( + "--model-type", + type=str, + default="auto", + choices=["auto"] + list(MODELS.keys()), + help=HELP["model_type"] + ' (default: "%(default)s", choices: %(choices)s)', + ) + parser.add_argument( + "--conv-template", + type=str, + required=True, + choices=list(CONV_TEMPLATES), + help=HELP["conv_template"] + " (required, choices: %(choices)s)", + ) + parser.add_argument( + "--context-window-size", + type=int, + default=None, + help=HELP["context_window_size"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--sliding-window-size", + type=int, + default=None, + help=HELP["sliding_window_size"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--prefill-chunk-size", + type=int, + default=None, + help=HELP["prefill_chunk_size"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--attention-sink-size", + type=int, + default=None, + help=HELP["attention_sink_size"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--tensor-parallel-shards", + type=int, + default=None, + help=HELP["tensor_parallel_shards"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--max-batch-size", + type=int, + default=80, + help=HELP["max_batch_size"] + ' (default: "%(default)s")', + ) + parser.add_argument( + "--output", + "-o", + type=_parse_output, + required=True, + help=HELP["output_gen_mlc_chat_config"] + " (required)", + ) + parsed = parser.parse_args(argv) + model = detect_model_type(parsed.model_type, parsed.config) + gen_config( + config=parsed.config, + model=model, + quantization=QUANTIZATION[parsed.quantization], + conv_template=parsed.conv_template, + context_window_size=parsed.context_window_size, + sliding_window_size=parsed.sliding_window_size, + prefill_chunk_size=parsed.prefill_chunk_size, + attention_sink_size=parsed.attention_sink_size, + tensor_parallel_shards=parsed.tensor_parallel_shards, + max_batch_size=parsed.max_batch_size, + output=parsed.output, + ) diff --git a/python/mlc_chat/cli/model_metadata.py b/python/mlc_chat/cli/model_metadata.py new file mode 100644 index 0000000..9939476 --- /dev/null +++ b/python/mlc_chat/cli/model_metadata.py @@ -0,0 +1,182 @@ +"""A tool that inspects the metadata of a model lib.""" +import json +import math +from dataclasses import asdict +from pathlib import Path +from typing import Any, Dict, List, Union + +import numpy as np + +from mlc_chat.support import logging +from mlc_chat.support.argparse import ArgumentParser +from mlc_chat.support.config import ConfigBase +from mlc_chat.support.style import green, red + +logging.enable_logging() +logger = logging.getLogger(__name__) + + +def _extract_metadata(model_lib: Path) -> Dict[str, Any]: + # pylint: disable=import-outside-toplevel + from tvm.runtime import device, load_module + from tvm.runtime.relax_vm import VirtualMachine + + # pylint: enable=import-outside-toplevel + + return json.loads(VirtualMachine(load_module(model_lib), device("cpu"))["_metadata"]()) + + +def _report_all(metadata: Dict[str, Any]) -> None: + # Print JSON with aesthetic values that packs each parameter into one line, + # while keeping the rest indented. + indent = 2 + indents = " " * indent + params = metadata.pop("params") + params = indents * 2 + (",\n" + indents * 2).join(json.dumps(p) for p in params) + lines = json.dumps( + metadata, + sort_keys=True, + indent=indent, + ).splitlines() + lines.insert(1, indents + '"params": [\n' + params + "\n" + indents + "],") + beautified_json = "\n".join(lines) + print(beautified_json) + + +def _read_dynamic_shape(shape: List[Union[int, str]], config: Union[Dict, ConfigBase]) -> List[int]: + if isinstance(config, ConfigBase): + config = asdict(config) + param_shape = [] + for s in shape: + if isinstance(s, int): + param_shape.append(s) + else: + if config is None: + logger.error( + "%s: Encountered dynamic shape %s, need to specify `--mlc-chat-config` for " + + "memory usage calculation.", + red("FAILED"), + red(s), + ) + raise AttributeError + if not s in config: + logger.error( + "%s to retrieve concrete %s for dynamic shape from %s.", + red("FAILED"), + red(s), + config, + ) + raise KeyError + param_shape.append(config[s]) + return param_shape + + +def _compute_memory_usage(metadata: Dict[str, Any], config: Union[Dict, ConfigBase]): + params_bytes = 0.0 + for param in metadata["params"]: + if all(isinstance(v, int) for v in param["shape"]): + assert all(v > 0 for v in param["shape"]), "All shapes should be strictly positive." + param_shape = param["shape"] + else: + # Contains dynamic shape; use config to look up concrete values + param_shape = _read_dynamic_shape(param["shape"], config) + params_bytes += math.prod(param_shape) * np.dtype(param["dtype"]).itemsize + temp_func_bytes = 0.0 + for _func_name, func_bytes in metadata["memory_usage"].items(): + temp_func_bytes = max(temp_func_bytes, func_bytes) + kv_cache_bytes = metadata["kv_cache_bytes"] + + return params_bytes, temp_func_bytes, kv_cache_bytes + + +def _report_memory_usage(metadata: Dict[str, Any], config: Union[Dict, ConfigBase]) -> None: + params_bytes, temp_func_bytes, kv_cache_bytes = _compute_memory_usage(metadata, config) + total_size = params_bytes + temp_func_bytes + kv_cache_bytes + logger.info( + "%s: %.2f MB (Parameters: %.2f MB. KVCache: %.2f MB. Temporary buffer: %.2f MB)", + green("Total memory usage"), + total_size / 1024 / 1024, + params_bytes / 1024 / 1024, + kv_cache_bytes / 1024 / 1024, + temp_func_bytes / 1024 / 1024, + ) + + logger.info( + "To reduce memory usage, " + "tweak `prefill_chunk_size`, `context_window_size` and `sliding_window_size`" + ) + + +def _print_memory_usage_in_json(metadata: Dict[str, Any], config: Dict) -> None: + params_bytes, temp_func_bytes, kv_cache_bytes = _compute_memory_usage(metadata, config) + print( + json.dumps( + { + "params_bytes": params_bytes, + "temp_func_bytes": temp_func_bytes, + "kv_cache_bytes": kv_cache_bytes, + } + ) + ) + + +def main(): + """Entry point for the model metadata tool.""" + parser = ArgumentParser(description="A tool that inspects the metadata of a model lib.") + parser.add_argument( + "model_lib", + type=Path, + help="""The compiled model library. In MLC LLM, an LLM is compiled to a shared or static + library (.so or .a), which contains GPU computation to efficiently run the LLM. MLC Chat, + as the runtime of MLC LLM, depends on the compiled model library to generate tokens. + """, + ) + parser.add_argument( + "--mlc-chat-config", + type=Path, + help="""The `mlc-chat-config.json` file specific to a model variant. This is only required + when `memory-only` is true and `model_lib` contains a dynamic parameter shape (i.e. using + a variable to represent the shape). For instance, `model.embed_tokens.q_weight` can have + shape `["vocab_size", 512]`. In these cases, we look up the concrete value in + `mlc-chat-config.json`. + """, + ) + parser.add_argument( + "--memory-only", + action="store_true", + help="""If set, only inspect the metadata in memory usage and print richer analysis. + Otherwise, the tool will load all the metadata from the model library file but only print + the basic information in JSON. + """, + ) + parser.add_argument( + "--print-memory-usage-in-json-only", + action="store_true", + help="""If set, only inspect the metadata in memory usage and print usage in raw JSON.""", + ) + parsed = parser.parse_args() + # Load metadata from model lib + try: + metadata = _extract_metadata(parsed.model_lib) + except: # pylint: disable=bare-except + logger.exception("%s to read metadata section in legacy model lib.", red("FAILED")) + return + # Load mlc_chat_config if provided + cfg = None + if parsed.mlc_chat_config: + mlc_chat_config_path = Path(parsed.mlc_chat_config) + if not mlc_chat_config_path.exists(): + raise ValueError(f"{mlc_chat_config_path} does not exist.") + with open(mlc_chat_config_path, "r", encoding="utf-8") as config_file: + cfg = json.load(config_file) + # Main body + if parsed.print_memory_usage_in_json_only: + _print_memory_usage_in_json(metadata, cfg) + elif parsed.memory_only: + _report_memory_usage(metadata, cfg) + else: + _report_all(metadata) + + +if __name__ == "__main__": + main() diff --git a/python/mlc_chat/cli/worker.py b/python/mlc_chat/cli/worker.py new file mode 100644 index 0000000..5f64e30 --- /dev/null +++ b/python/mlc_chat/cli/worker.py @@ -0,0 +1,53 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# pylint: disable=invalid-name +"""Internal DiscoWorker for Disco ProcessSession.""" +import os +import sys + +from tvm import runtime as _ # pylint: disable=unused-import +from tvm._ffi import get_global_func + +from .. import base # pylint: disable=unused-import, no-name-in-module + + +def main(): + """Main worker function""" + if len(sys.argv) != 5: + print("Usage: ") + return + + worker_id = int(sys.argv[1]) + num_workers = int(sys.argv[2]) + if sys.platform == "win32": + import msvcrt # pylint: disable=import-outside-toplevel,import-error + + reader = msvcrt.open_osfhandle(int(sys.argv[3]), os.O_BINARY) + writer = msvcrt.open_osfhandle(int(sys.argv[4]), os.O_BINARY) + else: + reader = int(sys.argv[3]) + writer = int(sys.argv[4]) + + worker_func = get_global_func("runtime.disco.WorkerProcess") + worker_func(worker_id, num_workers, reader, writer) + + +if __name__ == "__main__": + try: + main() + except (KeyboardInterrupt, IOError): + pass diff --git a/python/mlc_chat/compiler_pass/__init__.py b/python/mlc_chat/compiler_pass/__init__.py new file mode 100644 index 0000000..762ba8c --- /dev/null +++ b/python/mlc_chat/compiler_pass/__init__.py @@ -0,0 +1,2 @@ +"""Compiler passes used in MLC LLM.""" +from . import pipeline as _pipeline diff --git a/python/mlc_chat/compiler_pass/attach_to_ir_module.py b/python/mlc_chat/compiler_pass/attach_to_ir_module.py new file mode 100644 index 0000000..5850729 --- /dev/null +++ b/python/mlc_chat/compiler_pass/attach_to_ir_module.py @@ -0,0 +1,159 @@ +"""A couple of passes that simply attach additional information onto the IRModule.""" + +from typing import Dict + +import tvm +from tvm import IRModule, relax, tir +from tvm.script import tir as T + + +@tvm.transform.module_pass(opt_level=0, name="AttachVariableBounds") +class AttachVariableBounds: # pylint: disable=too-few-public-methods + """Attach variable bounds to each Relax function, which primarily helps with memory planning.""" + + def __init__(self, variable_bounds: Dict[str, int]): + self.variable_bounds = variable_bounds + + def transform_module(self, mod: IRModule, _ctx: tvm.transform.PassContext) -> IRModule: + """Entrypoint""" + for g_var, func in mod.functions_items(): + if isinstance(func, relax.Function): + mod[g_var] = func.with_attr("tir_var_upper_bound", self.variable_bounds) + return mod + + +@tvm.transform.module_pass(opt_level=0, name="AttachAdditionalPrimFuncs") +class AttachAdditionalPrimFuncs: # pylint: disable=too-few-public-methods + """Attach extra TIR PrimFuncs to the IRModule""" + + def __init__(self, functions: Dict[str, tir.PrimFunc]): + self.functions = functions + + def transform_module(self, mod: IRModule, _ctx: tvm.transform.PassContext) -> IRModule: + """Entrypoint""" + for func_name, func in self.functions.items(): + mod[func_name] = func.with_attr("global_symbol", func_name) + return mod + + +@tvm.transform.module_pass(opt_level=0, name="AttachMemoryPlanAttr") +class AttachMemoryPlanAttr: # pylint: disable=too-few-public-methods + """Attach memory planning attribute for dynamic function output planning to Relax functions.""" + + def transform_module(self, mod: IRModule, _ctx: tvm.transform.PassContext) -> IRModule: + """Entrypoint""" + for g_var, func in mod.functions_items(): + if isinstance(func, relax.Function): + mod[g_var] = func.with_attr("relax.memory_plan_dynamic_func_output", True) + return mod + + +@tvm.transform.module_pass(opt_level=0, name="AttachLogitProcessFunc") +class AttachLogitProcessFunc: # pylint: disable=too-few-public-methods + """Attach logit processing TIR functions to IRModule.""" + + def transform_module(self, mod: IRModule, _ctx: tvm.transform.PassContext) -> IRModule: + """Entrypoint""" + mod = mod.clone() + mod["apply_logit_bias_inplace"] = _apply_logit_bias_inplace + mod["apply_penalty_inplace"] = _apply_penalty_inplace + mod["apply_bitmask_inplace"] = _apply_bitmask_inplace + return mod + + +@T.prim_func +def _apply_logit_bias_inplace( + var_logits: T.handle, + var_pos2seq_id: T.handle, + var_token_ids: T.handle, + var_logit_bias: T.handle, +) -> None: + """Function that applies logit bias in place.""" + T.func_attr( + {"global_symbol": "apply_logit_bias_inplace", "tir.noalias": True, "tir.is_scheduled": True} + ) + batch_size = T.int32(is_size_var=True) + vocab_size = T.int32(is_size_var=True) + num_token = T.int32(is_size_var=True) + logits = T.match_buffer(var_logits, (batch_size, vocab_size), "float32") + # seq_ids + pos2seq_id = T.match_buffer(var_pos2seq_id, (num_token,), "int32") + token_ids = T.match_buffer(var_token_ids, (num_token,), "int32") + logit_bias = T.match_buffer(var_logit_bias, (num_token,), "float32") + + for p0 in T.thread_binding(0, (num_token + 1023) // 1024, "blockIdx.x"): + for p1 in T.thread_binding(0, 1024, "threadIdx.x"): + with T.block("block"): + vp = T.axis.spatial(num_token, p0 * 1024 + p1) + T.where(p0 * 1024 + p1 < num_token) + logits[pos2seq_id[vp], token_ids[vp]] += logit_bias[vp] + + +@T.prim_func +def _apply_penalty_inplace( # pylint: disable=too-many-arguments,too-many-locals + var_logits: T.handle, + var_seq_ids: T.handle, + var_pos2seq_id: T.handle, + var_token_ids: T.handle, + var_token_cnt: T.handle, + var_penalties: T.handle, +) -> None: + """Function that applies penalties in place.""" + T.func_attr( + {"global_symbol": "apply_penalty_inplace", "tir.noalias": True, "tir.is_scheduled": True} + ) + batch_size = T.int32(is_size_var=True) + vocab_size = T.int32(is_size_var=True) + num_token = T.int32(is_size_var=True) + num_seq = T.int32(is_size_var=True) + logits = T.match_buffer(var_logits, (batch_size, vocab_size), "float32") + seq_ids = T.match_buffer(var_seq_ids, (num_seq,), "int32") + pos2seq_id = T.match_buffer(var_pos2seq_id, (num_token,), "int32") + token_ids = T.match_buffer(var_token_ids, (num_token,), "int32") + token_cnt = T.match_buffer(var_token_cnt, (num_token,), "int32") + penalties = T.match_buffer(var_penalties, (num_seq, 3), "float32") + + for p0 in T.thread_binding(0, (num_token + 1023) // 1024, "blockIdx.x"): + for p1 in T.thread_binding(0, 1024, "threadIdx.x"): + with T.block("block"): + vp = T.axis.spatial(num_token, p0 * 1024 + p1) + T.where(p0 * 1024 + p1 < num_token) + # Penalties: (presence_penalty, frequency_penalty, repetition_penalty) + logits[seq_ids[pos2seq_id[vp]], token_ids[vp]] -= ( + penalties[pos2seq_id[vp], 0] + token_cnt[vp] * penalties[pos2seq_id[vp], 1] + ) + logits[seq_ids[pos2seq_id[vp]], token_ids[vp]] = T.if_then_else( + logits[seq_ids[pos2seq_id[vp]], token_ids[vp]] > 0, + logits[seq_ids[pos2seq_id[vp]], token_ids[vp]] * penalties[pos2seq_id[vp], 2], + logits[seq_ids[pos2seq_id[vp]], token_ids[vp]] / penalties[pos2seq_id[vp], 2], + ) + + +@T.prim_func +def _apply_bitmask_inplace( + var_logits: T.handle, + var_seq_ids: T.handle, + var_bitmask: T.handle, +) -> None: + """Function that applies vocabulary masking in place.""" + T.func_attr( + {"global_symbol": "apply_bitmask_inplace", "tir.noalias": True, "tir.is_scheduled": True} + ) + batch_size = T.int32(is_size_var=True) + vocab_size = T.int32(is_size_var=True) + num_seq = T.int32(is_size_var=True) + logits = T.match_buffer(var_logits, (batch_size, vocab_size), "float32") + seq_ids = T.match_buffer(var_seq_ids, (num_seq,), "int32") + bitmask = T.match_buffer(var_bitmask, (num_seq, (vocab_size + 31 // 32)), "int32") + + for fused_s_v_0 in T.thread_binding(0, (num_seq * vocab_size + 1023) // 1024, "blockIdx.x"): + for fused_s_v_1 in T.thread_binding(0, 1024, "threadIdx.x"): + with T.block("block"): + vs = T.axis.spatial(num_seq, (fused_s_v_0 * 1024 + fused_s_v_1) // vocab_size) + vv = T.axis.spatial(vocab_size, (fused_s_v_0 * 1024 + fused_s_v_1) % vocab_size) + T.where(fused_s_v_0 * 1024 + fused_s_v_1 < num_seq * vocab_size) + logits[seq_ids[vs], vv] = T.if_then_else( + (bitmask[vs, vv // 32] >> (vv % 32)) & 1 == 1, + logits[seq_ids[vs], vv], + T.float32(-1e10), + ) diff --git a/python/mlc_chat/compiler_pass/clean_up_tir_attrs.py b/python/mlc_chat/compiler_pass/clean_up_tir_attrs.py new file mode 100644 index 0000000..f7c9ad2 --- /dev/null +++ b/python/mlc_chat/compiler_pass/clean_up_tir_attrs.py @@ -0,0 +1,30 @@ +"""A compiler pass that cleans up undesired TIR attrs.""" +from typing import List + +import tvm +from tvm.ir.module import IRModule + + +@tvm.transform.module_pass(opt_level=0, name="CleanUpTIRAttrs") +class CleanUpTIRAttrs: # pylint: disable=too-few-public-methods + """A compiler pass that cleans up undesired TIR attrs.""" + + def __init__(self, attrs: List[str]): + self.attrs = attrs + + def transform_module( + self, + mod: IRModule, + _ctx: tvm.transform.PassContext, + ) -> IRModule: + """IRModule-level transformation""" + for g_var, func in mod.functions_items(): + changed = False + for attr in self.attrs: + if func.attrs is not None and attr in func.attrs: + func = func.without_attr(attr) + changed = True + break + if changed: + mod[g_var] = func + return mod diff --git a/python/mlc_chat/compiler_pass/cublas_dispatch.py b/python/mlc_chat/compiler_pass/cublas_dispatch.py new file mode 100644 index 0000000..2310486 --- /dev/null +++ b/python/mlc_chat/compiler_pass/cublas_dispatch.py @@ -0,0 +1,31 @@ +"""A compiler pass that dispatches patterns to CUBLAS.""" +import tvm +import tvm.relax.backend.contrib.cublas as _cublas +from tvm import IRModule, relax +from tvm.relax.backend import get_patterns_with_prefix + + +@tvm.transform.module_pass(opt_level=0, name="CublasDispatch") +class CublasDispatch: # pylint: disable=too-few-public-methods,broad-exception-raised + """A compiler pass that dispatches patterns to CUBLAS.""" + + def transform_module(self, mod: IRModule, _ctx: tvm.transform.PassContext) -> IRModule: + """IRModule-level transformation""" + has_cublas = tvm.get_global_func("relax.ext.cublas", True) + if not has_cublas: + raise Exception("CUBLAS is not enabled.") + + patterns = get_patterns_with_prefix("cublas") + + model_names = [ + gv.name_hint for gv, func in mod.functions.items() if isinstance(func, relax.Function) + ] + mod = tvm.transform.Sequential( + [ + relax.transform.FuseOpsByPattern( + patterns, bind_constants=False, annotate_codegen=True + ), + relax.transform.RunCodegen({}, entry_functions=model_names), + ] + )(mod) + return mod diff --git a/python/mlc_chat/compiler_pass/estimate_memory_usage.py b/python/mlc_chat/compiler_pass/estimate_memory_usage.py new file mode 100644 index 0000000..f3ac747 --- /dev/null +++ b/python/mlc_chat/compiler_pass/estimate_memory_usage.py @@ -0,0 +1,84 @@ +"""Memory usage estimation analysis function for Relax functions.""" +import json +from typing import Any, Dict + +import tvm +from tvm import relax, tir +from tvm.ir import IRModule, Op +from tvm.relax.expr_functor import PyExprVisitor, visitor + +from mlc_chat.support import logging + +logger = logging.getLogger(__name__) + + +@tvm.transform.module_pass(opt_level=0, name="AttachMetadata") +class AttachMetadataWithMemoryUsage: # pylint: disable=too-few-public-methods + """Attach a Relax function that returns metadata in a JSON string""" + + def __init__(self, metadata: Dict[str, Any]): + self.metadata = metadata + + def transform_module(self, mod: IRModule, _ctx: tvm.transform.PassContext) -> IRModule: + """Entrypoint""" + + def _emit_metadata(metadata): + bb = relax.BlockBuilder() # pylint: disable=invalid-name + with bb.function("main", params=[]): + bb.emit_func_output(relax.StringImm(json.dumps(metadata))) + return bb.finalize()["main"] + + self.metadata["memory_usage"] = _MemoryEstimator().run(mod) + mod["_metadata"] = _emit_metadata(self.metadata) + return mod + + +@visitor +class _MemoryEstimator(PyExprVisitor): + """The IR visitor which estimates the memory usage of each Relax function.""" + + def __init__(self) -> None: + self.planned_alloc_mem = 0 + self.planned_mem_num = 0 + self._op_alloc_tensor = Op.get("relax.builtin.alloc_tensor") + self._op_alloc_storage = Op.get("relax.memory.alloc_storage") + + def run(self, mod: IRModule) -> Dict[str, int]: + """Entry point of the visitor.""" + result: Dict[str, int] = {} + for global_var, func in mod.functions_items(): + if isinstance(func, relax.Function): + self.planned_alloc_mem = 0 + self.planned_mem_num = 0 + self.visit_expr(func) + result[global_var.name_hint] = self.planned_alloc_mem + logger.info( + "[Memory usage] Function `%s`: %.2f MB", + global_var.name_hint, + self.planned_alloc_mem / 1024 / 1024, + ) + return result + + def visit_call_(self, call: relax.Call) -> None: # pylint: disable=arguments-renamed + if call.op == self._op_alloc_tensor: + self._builtin_tensor_alloc(shape=call.args[0], dtype_str=call.args[1].value) + elif call.op == self._op_alloc_storage: + self._storage_alloc(size=call.args[0]) + super().visit_call_(call) + + def _builtin_tensor_alloc(self, shape: relax.Expr, dtype_str: str) -> None: + assert isinstance(shape, relax.ShapeExpr) + size = 1 + for dim_len in shape.values: + if not isinstance(dim_len, tvm.tir.IntImm): + return + size *= dim_len.value + dtype = tvm.DataType(dtype_str) + self.planned_mem_num += 1 + self.planned_alloc_mem += size * ((dtype.bits + 7) // 8) * dtype.lanes + + def _storage_alloc(self, size: relax.Expr) -> None: + assert isinstance(size, relax.ShapeExpr) + if isinstance(size.values[0], tir.IntImm): + self.planned_mem_num += 1 + self.planned_alloc_mem += size.values[0].value diff --git a/python/mlc_chat/compiler_pass/fuse_add_norm.py b/python/mlc_chat/compiler_pass/fuse_add_norm.py new file mode 100644 index 0000000..88ed1dc --- /dev/null +++ b/python/mlc_chat/compiler_pass/fuse_add_norm.py @@ -0,0 +1,211 @@ +"""A compiler pass that fuses add + rms_norm.""" + +import tvm +from tvm import relax +from tvm.relax.dpl import PatternContext, rewrite_bindings +from tvm.relax.dpl.pattern import is_op, wildcard +from tvm.script import tir as T + +# mypy: disable-error-code="attr-defined,valid-type" +# pylint: disable=too-many-locals,invalid-name + + +def _get_add_rms_norm_decode(hidden_size: int, eps: float, TX: int): + inv_hidden_size = T.float32(1.0 / float(hidden_size)) + eps = T.float32(eps) + add_local_size = hidden_size // TX + + @T.prim_func(private=True) + def decode_add_rms(pA: T.handle, pB: T.handle, pC: T.handle, pO: T.handle, pAdd: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + batch_size = T.int32() + A = T.match_buffer(pA, (batch_size, 1, hidden_size), "float16") + B = T.match_buffer(pB, (batch_size, 1, hidden_size), "float16") + C = T.match_buffer(pC, (hidden_size,), "float16") + O = T.match_buffer(pO, (batch_size, 1, hidden_size), "float16") + add = T.match_buffer(pAdd, (batch_size, 1, hidden_size), "float16") + add_local = T.alloc_buffer((hidden_size // TX,), "float16", scope="local") + sum_shared = T.alloc_buffer((batch_size, 1), scope="shared") + sum_local = T.alloc_buffer((TX, batch_size, 1), scope="local") + for v_bx in T.thread_binding(batch_size, thread="blockIdx.x"): + for v_tx in T.thread_binding( + TX, + thread="threadIdx.x", + annotations={"pragma_auto_unroll_max_step": 256, "pragma_unroll_explicit": 1}, + ): + for i in range(add_local_size): + with T.block("T_add"): + bx = T.axis.spatial(batch_size, v_bx) + h = T.axis.spatial(hidden_size, i * TX + v_tx) + add_local[h // TX] = A[bx, 0, h] + B[bx, 0, h] + with T.block("T_write_back"): + bx = T.axis.spatial(batch_size, v_bx) + v_ax1 = T.axis.spatial(1, 0) + h = T.axis.spatial(hidden_size, i * TX + v_tx) + add[bx, v_ax1, h] = add_local[h // TX] + with T.block("T_multiply_red_rf_init"): + tx, bx = T.axis.remap("SS", [v_tx, v_bx]) + sum_local[tx, bx, 0] = T.float32(0) + for v_i, _j in T.grid(add_local_size, 1): + with T.block("T_multiply_red_rf_update"): + tx, bx, i = T.axis.remap("SSR", [v_tx, v_bx, v_i]) + sum_local[tx, bx, 0] += T.float32(add_local[i]) * T.float32(add_local[i]) + for _j in range(1): + for v_tx_2 in T.thread_binding(TX, thread="threadIdx.x"): + with T.block("T_multiply_red"): + tx, bx = T.axis.remap("RS", [v_tx_2, v_bx]) + T.reads(sum_local[tx, bx, 0]) + T.writes(sum_shared[bx, 0]) + with T.init(): + sum_shared[bx, 0] = T.float32(0) + sum_shared[bx, 0] += sum_local[tx, bx, 0] + for i in range(add_local_size): + for v_tx_2 in T.thread_binding(TX, thread="threadIdx.x"): + with T.block("T_cast_2"): + bx = T.axis.spatial(batch_size, v_bx) + h = T.axis.spatial(hidden_size, i * TX + v_tx_2) + O[bx, 0, h] = T.float16( + T.rsqrt(sum_shared[bx, 0] * inv_hidden_size + eps) + * T.float32(add_local[h // TX]) + * T.float32(C[h]) + ) + + return decode_add_rms + + +def _get_add_rms_norm_prefill(hidden_size: int, eps: float, TX: int): + inv_hidden_size = T.float32(1.0 / float(hidden_size)) + eps = T.float32(eps) + add_local_size = hidden_size // TX + + @T.prim_func(private=True) + def prefill_add_rms(pA: T.handle, pB: T.handle, pC: T.handle, pO: T.handle, pAdd: T.handle): + T.func_attr({"tir.noalias": T.bool(True), "tir.is_scheduled": 1}) + seq_len = T.int32() + A = T.match_buffer(pA, (1, seq_len, hidden_size), "float16") + B = T.match_buffer(pB, (1, seq_len, hidden_size), "float16") + C = T.match_buffer(pC, (hidden_size,), "float16") + O = T.match_buffer(pO, (1, seq_len, hidden_size), "float16") + add = T.match_buffer(pAdd, (1, seq_len, hidden_size), "float16") + add_local = T.alloc_buffer((hidden_size // TX,), "float16", scope="local") + sum_shared = T.alloc_buffer((1, seq_len), scope="shared") + sum_local = T.alloc_buffer((TX, 1, seq_len), scope="local") + for v_bx in T.thread_binding(seq_len, thread="blockIdx.x"): + for v_tx in T.thread_binding( + TX, + thread="threadIdx.x", + annotations={"pragma_auto_unroll_max_step": 256, "pragma_unroll_explicit": 1}, + ): + for v_i in range(add_local_size): + with T.block("T_add"): + bx = T.axis.spatial(seq_len, v_bx) + h = T.axis.spatial(hidden_size, v_i * TX + v_tx) + add_local[h // TX] = A[0, bx, h] + B[0, bx, h] + with T.block("T_write_back"): + bx = T.axis.spatial(seq_len, v_bx) + h = T.axis.spatial(hidden_size, v_i * TX + v_tx) + add[0, bx, h] = add_local[h // TX] + with T.block("T_multiply_red_rf_init"): + tx, bx = T.axis.remap("SS", [v_tx, v_bx]) + sum_local[tx, 0, bx] = T.float32(0) + for v_i, _j in T.grid(add_local_size, 1): + with T.block("T_multiply_red_rf_update"): + tx, bx, i = T.axis.remap("SSR", [v_tx, v_bx, v_i]) + sum_local[tx, 0, bx] += T.float32(add_local[i]) * T.float32(add_local[i]) + for _j in range(1): + for v_tx_2 in T.thread_binding(TX, thread="threadIdx.x"): + with T.block("T_multiply_red"): + tx, bx = T.axis.remap("RS", [v_tx_2, v_bx]) + with T.init(): + sum_shared[0, bx] = T.float32(0) + sum_shared[0, bx] = sum_shared[0, bx] + sum_local[tx, 0, bx] + for v_i in range(add_local_size): + for v_tx_2 in T.thread_binding(TX, thread="threadIdx.x"): + with T.block("T_cast_2"): + bx = T.axis.spatial(seq_len, v_bx) + v1 = T.axis.spatial(hidden_size, v_i * TX + v_tx_2) + O[0, bx, v1] = T.float16( + T.rsqrt(sum_shared[0, bx] * inv_hidden_size + eps) + * T.float32(add_local[v1 // TX]) + * T.float32(C[v1]) + ) + + return prefill_add_rms + + +@tvm.transform.module_pass(opt_level=0, name="FuseAddRMSNorm") +class FuseAddRMSNorm: # pylint: disable=too-few-public-methods + """A compiler pass that fuses add + rms_norm.""" + + def __init__(self, target: tvm.target.Target) -> None: + """Initializer. + + Parameters + ---------- + target : tvm.target.Target + Target device. + """ + self.TX = 1024 # default + + if target.max_num_threads < self.TX: + self.TX = target.max_num_threads + + def transform_module(self, mod: tvm.IRModule, _ctx: tvm.transform.PassContext) -> tvm.IRModule: + """IRModule-level transformation.""" + with PatternContext() as ctx: + pat_x1 = wildcard() + pat_x2 = wildcard() + pat_y = is_op("relax.add")(pat_x1, pat_x2) + pat_w = wildcard() + pat_o = is_op("relax.nn.rms_norm")(pat_y, pat_w) + + def rewriter(matchings, bindings): + x1 = matchings[pat_x1] + x2 = matchings[pat_x2] + weight = matchings[pat_w] + y = matchings[pat_y] + o = matchings[pat_o] + eps = bindings[o].attrs.epsilon + if x1.struct_info.dtype != "float16": + return {} + n, _, h = x1.struct_info.shape + func_name = "fuse_add_norm_prefill" if n == 1 else "fuse_add_norm_decode" + + if all(gv.name_hint != func_name for gv in mod.functions): + h = int(h) + if h % self.TX != 0: + return {} + if n == 1: + func = _get_add_rms_norm_prefill(h, eps, self.TX) + else: + func = _get_add_rms_norm_decode(h, eps, self.TX) + mod[func_name] = func + gvar = mod.get_global_var(func_name) + relax.expr._update_struct_info( # pylint: disable=protected-access + gvar, + relax.FuncStructInfo.opaque_func(ret=relax.ObjectStructInfo()), + ) + else: + gvar = mod.get_global_var(func_name) + o_y_tuple = relax.call_tir( + gvar, + [x1, x2, weight], + out_sinfo=[x1.struct_info, x1.struct_info], + ) + return { + o: relax.TupleGetItem(o_y_tuple, 0), + y: relax.TupleGetItem(o_y_tuple, 1), + } + + new_mod = {} + for gvar, func in mod.functions.items(): + if isinstance(func, relax.Function): + func = rewrite_bindings(ctx, rewriter, func) + new_mod[gvar] = func + + for gvar, func in mod.functions.items(): + if isinstance(func, tvm.tir.PrimFunc) and gvar not in new_mod: + new_mod[gvar] = func + + new_mod = tvm.IRModule(new_mod, mod.type_definitions, mod.attrs, mod.global_infos) + return new_mod diff --git a/python/mlc_chat/compiler_pass/fuse_dequantize_matmul_ewise.py b/python/mlc_chat/compiler_pass/fuse_dequantize_matmul_ewise.py new file mode 100644 index 0000000..f8a64c8 --- /dev/null +++ b/python/mlc_chat/compiler_pass/fuse_dequantize_matmul_ewise.py @@ -0,0 +1,86 @@ +"""A compiler pass that fuses dequantize + matmul + elementwise.""" +import tvm +from tvm import IRModule, relax +from tvm.relax.dpl.pattern import GlobalVarPattern, TuplePattern, is_op, wildcard + + +@tvm.transform.module_pass(opt_level=0, name="FuseDequantizeMatmulEwise") +class FuseDequantizeMatmulEwise: # pylint: disable=too-few-public-methods + """A compiler pass that fuses dequantize + matmul + elementwise.""" + + def transform_module( + self, + mod: IRModule, + _ctx: tvm.transform.PassContext, + ) -> IRModule: + """IRModule-level transformation""" + seq = [] + for n_aux_tensor in [1, 2, 3, 4]: + for match_ewise in [0, 1, 2, 6]: + if match_ewise == 6 and n_aux_tensor != 4: + continue + seq.append( + relax.transform.FuseOpsByPattern( + [ + ( + "dequantize_matmul", + *_pattern(match_ewise, n_aux_tensor), + ) + ] + ) + ) + seq.append(relax.transform.FuseTIR()) + return tvm.transform.Sequential(seq)(mod) + + +def _pattern(match_ewise: int, n_aux_tensor: int): + # pylint: disable=invalid-name + w_scaled = wildcard() + x = wildcard() + w = is_op("relax.call_tir")( + GlobalVarPattern(), + TuplePattern([w_scaled] + [wildcard() for _ in range(n_aux_tensor)]), + add_constraint=False, + ) + matmul = is_op("relax.call_tir")( + GlobalVarPattern(), + TuplePattern([x, w] + [wildcard() for _ in range(match_ewise)]), + add_constraint=False, + ) + # pylint: enable=invalid-name + annotations = { + "w_scaled": w_scaled, + "x": x, + "w": w, + "matmul": matmul, + } + + def _check_decoding(ctx: relax.transform.PatternCheckContext) -> bool: + call = ctx.annotated_expr["w"] + if not isinstance(call, relax.Call): + return False + g_var = call.args[0] + if not isinstance(g_var, relax.GlobalVar): + return False + return g_var.name_hint.startswith("dequantize") or g_var.name_hint.startswith( + "fused_dequantize" + ) + + def _check_matmul(ctx: relax.transform.PatternCheckContext) -> bool: + call = ctx.annotated_expr["matmul"] + if not isinstance(call, relax.Call): + return False + g_var = call.args[0] + if not isinstance(g_var, relax.GlobalVar): + return False + return ( + g_var.name_hint.startswith("matmul") + or g_var.name_hint.startswith("fused_matmul") + or g_var.name_hint.startswith("NT_matmul") + or g_var.name_hint.startswith("fused_NT_matmul") + ) + + def _check(ctx: relax.transform.PatternCheckContext) -> bool: + return _check_decoding(ctx) and _check_matmul(ctx) + + return matmul, annotations, _check diff --git a/python/mlc_chat/compiler_pass/fuse_dequantize_take.py b/python/mlc_chat/compiler_pass/fuse_dequantize_take.py new file mode 100644 index 0000000..8079215 --- /dev/null +++ b/python/mlc_chat/compiler_pass/fuse_dequantize_take.py @@ -0,0 +1,90 @@ +"""A compiler pass that fuses dequantize + take.""" +import tvm +from tvm import IRModule, relax, tir +from tvm.relax.dpl.pattern import ( + GlobalVarPattern, + TuplePattern, + is_const, + is_op, + wildcard, +) + + +@tvm.transform.module_pass(opt_level=0, name="FuseDequantizeTake") +class FuseDequantizeTake: # pylint: disable=too-few-public-methods + """A compiler pass that fuses dequantize + take.""" + + def transform_module( + self, + mod: IRModule, + _ctx: tvm.transform.PassContext, + ) -> IRModule: + """IRModule-level transformation""" + seq = [] + for n_aux_tensor in [2, 3]: + for match_tir_vars in [False, True]: + seq.append( + relax.transform.FuseOpsByPattern( + [ + ( + "dequantize_take", + *_pattern(n_aux_tensor, match_tir_vars), + ) + ] + ) + ) + seq.append(relax.transform.FuseTIR()) + mod = tvm.transform.Sequential(seq)(mod) + for g_var, func in mod.functions_items(): + name = g_var.name_hint + if isinstance(func, tir.PrimFunc) and ( + ("fused_dequantize" in name) and ("take" in name) + ): + sch_mod = tvm.IRModule({"main": func}) + sch_mod = tir.transform.ForceNarrowIndexToInt32()(sch_mod) + sch = tir.Schedule(sch_mod) + sch.compute_inline("dequantize") + mod[g_var] = sch.mod["main"] + return mod + + +def _pattern(n_aux_tensor: int, match_tir_vars: bool): + dequantize = is_op("relax.call_tir")( + GlobalVarPattern(), + TuplePattern([wildcard() for _ in range(n_aux_tensor)]), + add_constraint=False, + ) + indices = ~is_const() + if match_tir_vars: + call_tir_args_take = [ + GlobalVarPattern(), + TuplePattern([dequantize, indices]), + wildcard(), + ] + else: + call_tir_args_take = [ + GlobalVarPattern(), + TuplePattern([dequantize, indices]), + ] + take = is_op("relax.call_tir")( + *call_tir_args_take, + add_constraint=False, + ) + annotations = { + "take": take, + "dequantize": dequantize, + "indices": indices, + } + + def _check(ctx: relax.transform.PatternCheckContext) -> bool: + take = ctx.annotated_expr["take"] + dequantize = ctx.annotated_expr["dequantize"] + if not isinstance(dequantize, relax.expr.Call): + return False + if not isinstance(take.args[0], relax.GlobalVar) or not isinstance( + dequantize.args[0], relax.GlobalVar + ): + return False + return "take" in take.args[0].name_hint and "dequantize" in dequantize.args[0].name_hint + + return take, annotations, _check diff --git a/python/mlc_chat/compiler_pass/fuse_dequantize_transpose.py b/python/mlc_chat/compiler_pass/fuse_dequantize_transpose.py new file mode 100644 index 0000000..d89f62c --- /dev/null +++ b/python/mlc_chat/compiler_pass/fuse_dequantize_transpose.py @@ -0,0 +1,106 @@ +"""A compiler pass that fuses transpose + dequantize.""" +import tvm +from tvm import relax, tir +from tvm.ir.module import IRModule +from tvm.relax.analysis import remove_all_unused +from tvm.relax.expr_functor import PyExprMutator, mutator + + +@tvm.transform.module_pass(opt_level=0, name="FuseDequantizeTranspose") +class FuseDequantizeTranspose: # pylint: disable=too-few-public-methods + """A compiler pass that fuses transpose + dequantize.""" + + def transform_module(self, mod: IRModule, _ctx: tvm.transform.PassContext) -> IRModule: + """IRModule-level transformation""" + return _DequantizeTransposeFuser(mod).transform() + + +@mutator +class _DequantizeTransposeFuser(PyExprMutator): # pylint: disable=abstract-method + def __init__( + self, + mod: IRModule, + ): + super().__init__(mod) + self.mod = mod + + def transform(self) -> IRModule: + """Entry point""" + for g_var, func in self.mod.functions_items(): + if isinstance(func, relax.Function): + updated_func = self.visit_expr(func) + updated_func = remove_all_unused(updated_func) + self.builder_.update_func(g_var, updated_func) + return self.builder_.get() + + def visit_call_( # pylint: disable=arguments-renamed + self, + call: relax.Call, + ) -> relax.Expr: + call = self.visit_expr_post_order(call) + if call.op != tvm.ir.Op.get("relax.matmul"): + return call + # Do not fuse dequantize-transpose for GeMM + if ( + call.args[0].struct_info.ndim < 2 + or not isinstance(call.args[0].struct_info.shape[-2], tir.IntImm) + or call.args[0].struct_info.shape[-2].value != 1 + ): + return call + + matmul_rhs = self.lookup_binding(call.args[1]) + if ( + not isinstance(matmul_rhs, relax.Call) + or matmul_rhs.op != tvm.ir.Op.get("relax.permute_dims") + or matmul_rhs.args[0].struct_info.ndim != 2 + or matmul_rhs.attrs.axes is not None + ): + return call + + transpose_input = self.lookup_binding(matmul_rhs.args[0]) + if ( + not isinstance(transpose_input, relax.Call) + or transpose_input.op != tvm.ir.Op.get("relax.call_tir") + or not transpose_input.args[0].name_hint.startswith("dequantize") + or not isinstance(transpose_input.struct_info, relax.TensorStructInfo) + ): + return call + + dequantize_tir_func = self.mod[transpose_input.args[0]] + assert isinstance(dequantize_tir_func, tir.PrimFunc) + if ( # pylint: disable=too-many-boolean-expressions + len(dequantize_tir_func.body.block.alloc_buffers) != 1 + or not isinstance(dequantize_tir_func.body.block.body, tir.SeqStmt) + or len(dequantize_tir_func.body.block.body) != 2 + or not isinstance(dequantize_tir_func.body.block.body[1], tir.For) + or not isinstance(dequantize_tir_func.body.block.body[1].body.body, tir.BlockRealize) + or dequantize_tir_func.body.block.body[1].body.body.block.name_hint != "T_transpose" + ): + return call + + new_func_buffers = [ + dequantize_tir_func.buffer_map[var] for var in dequantize_tir_func.params + ] + new_func_buffers[-1] = dequantize_tir_func.body.block.alloc_buffers[0] + new_func = tir.PrimFunc( + params=new_func_buffers, + body=tir.BlockRealize( + iter_values=[], + predicate=True, + block=tir.Block( + iter_vars=[], + reads=[], + writes=[], + name_hint="root", + body=dequantize_tir_func.body.block.body[0], + ), + ), + ) + # Call `renew_defs` for deep-copy to avoid IR node duplication in + # different PrimFuncs of an IRModule. + new_func = tir.stmt_functor.renew_defs(new_func) + g_var = self.builder_.add_func(new_func, func_name="dequantize") + dequantize_matmul_rhs = self.builder_.emit( + relax.call_tir(g_var, transpose_input.args[1], out_sinfo=matmul_rhs.struct_info) + ) + return relax.op.matmul(call.args[0], dequantize_matmul_rhs, out_dtype=call.attrs.out_dtype) diff --git a/python/mlc_chat/compiler_pass/fuse_ft_dequantize_matmul_epilogue.py b/python/mlc_chat/compiler_pass/fuse_ft_dequantize_matmul_epilogue.py new file mode 100644 index 0000000..c5a4094 --- /dev/null +++ b/python/mlc_chat/compiler_pass/fuse_ft_dequantize_matmul_epilogue.py @@ -0,0 +1,322 @@ +"""A compiler pass that fuses dequantize matmul + epilogue.""" +import operator +from functools import reduce + +import tvm +from tvm import IRModule, relax +from tvm.relax.dpl import rewrite_call +from tvm.relax.dpl.pattern import is_op, wildcard + + +@tvm.transform.module_pass(opt_level=0, name="FuseDequantizeEpilogue") +class FuseFTDequantizeEpilogue: # pylint: disable=too-few-public-methods + """A compiler pass that fuses FasterTransformer dequantize matmul + epilogue.""" + + def transform_module( + self, + mod: IRModule, + _ctx: tvm.transform.PassContext, + ) -> IRModule: + """IRModule-level transformation""" + for gv, func in mod.functions_items(): + if isinstance(func, relax.Function): + func = fuse_bias(func) + func = fuse_activation(func) + func = fuse_residual_binary(func) + func = fuse_residual_unary(func) + mod[gv] = func + return mod + + +def fuse_bias(func: relax.Function) -> relax.Function: + """ + Fuse following `relax.add` into fastertransformer.gemm_fp16_int as bias: + + Before: + ``` + lv1 = relax.call_dps_packed("fastertransformer.gemm_fp16_int", ...) + lv2 = relax.add(lv1, bias) + + ``` + After: + ``` + lv2 = relax.call_dps_packed("fastertransformer.gemm_fp16_int_bias", ..., bias, ...) + ``` + + Parameters + ---------- + func : relax.Function + The function before fusion. + + Returns + ------- + ret : relax.Function + The function after fusion. + """ + decode_matmul = is_op("relax.call_dps_packed")(varg_default_wildcard=True) + bias = wildcard() + pattern = is_op("relax.add")(decode_matmul, bias) | is_op("relax.add")(bias, decode_matmul) + + def rewriter(expr, match): + if match[decode_matmul].args[0].global_symbol == "fastertransformer.gemm_fp16_int": + assert len(match[decode_matmul].args) == 2 + args_list = match[decode_matmul].args[1] + assert len(args_list) == 8 + if not args_list[3].value == "identity": + # bias cannot be fused after activation + return expr + matched_bias = match[bias] + bias_stride = ( + matched_bias.struct_info.shape[-1] + if bias + and not reduce(operator.mul, matched_bias.struct_info.shape, 1) + == matched_bias.struct_info.shape[-1] + else 0 + ) + return relax.call_dps_packed( + "fastertransformer.gemm_fp16_int_bias", + [ + args_list[0], # x + args_list[1], # weight + args_list[2], # scale + matched_bias, # bias + args_list[3], # activation + args_list[4], # m + args_list[5], # n + args_list[6], # k + args_list[7], # group_size + bias_stride, # bias_stride + ], + out_sinfo=match[decode_matmul].struct_info, + ) + return expr + + return rewrite_call(pattern, rewriter, func) + + +def fuse_activation(func: relax.Function) -> relax.Function: + """ + Fuse following `relax.nn.silu/relu/gelu` into fastertransformer.gemm_fp16_int_bias + as activation: + + Before: + ``` + lv1 = relax.call_dps_packed("fastertransformer.gemm_fp16_int_bias", ...) + lv2 = relax.silu(lv1) + + ``` + After: + ``` + lv2 = relax.call_dps_packed("fastertransformer.gemm_fp16_int_bias", ..., "silu", ...) + ``` + + Parameters + ---------- + func : relax.Function + The function before fusion. + + Returns + ------- + ret : relax.Function + The function after fusion. + """ + # pylint: disable=unsupported-binary-operation + decode_matmul = is_op("relax.call_dps_packed")(varg_default_wildcard=True) + pattern = ( + is_op("relax.nn.silu")(decode_matmul) + | is_op("relax.nn.gelu")(decode_matmul) + | is_op("relax.nn.relu")(decode_matmul) + ) + + def rewriter(expr, match): + if match[decode_matmul].args[0].global_symbol == "fastertransformer.gemm_fp16_int": + matched_activation = match[pattern] + assert matched_activation.op.name in ["relax.nn.silu", "relax.nn.gelu", "relax.nn.relu"] + assert len(match[decode_matmul].args) == 2 + args_list = match[decode_matmul].args[1] + assert len(args_list) == 8 + return relax.call_dps_packed( + "fastertransformer.gemm_fp16_int", + [ + args_list[0], # x + args_list[1], # weight + args_list[2], # scale + matched_activation.op.name[9:], # activation + args_list[4], # m + args_list[5], # n + args_list[6], # k + args_list[7], # group_size + ], + out_sinfo=match[decode_matmul].struct_info, + ) + if match[decode_matmul].args[0].global_symbol == "fastertransformer.gemm_fp16_int_bias": + matched_activation = match[pattern] + assert matched_activation.op.name in ["relax.nn.silu", "relax.nn.gelu", "relax.nn.relu"] + assert len(match[decode_matmul].args) == 2 + args_list = match[decode_matmul].args[1] + assert len(args_list) == 10 + return relax.call_dps_packed( + "fastertransformer.gemm_fp16_int_bias", + [ + args_list[0], # x + args_list[1], # weight + args_list[2], # scale + args_list[3], # bias + matched_activation.op.name[9:], # activation + args_list[5], # m + args_list[6], # n + args_list[7], # k + args_list[8], # group_size + args_list[9], # bias_stride + ], + out_sinfo=match[decode_matmul].struct_info, + ) + return expr + + return rewrite_call(pattern, rewriter, func) + + +def fuse_residual_binary(func: relax.Function) -> relax.Function: + """ + Fuse following `relax.add/multiply` into fastertransformer.gemm_fp16_int_bias as + residual binary operation: + + Before: + ``` + lv1 = relax.call_dps_packed("fastertransformer.gemm_fp16_int_bias", ...) + lv2 = relax.add(lv1, residual) + + ``` + After: + ``` + lv2 = relax.call_dps_packed( + "fastertransformer.gemm_fp16_int_bias_residual", + ..., + residual, + ..., + "plus", + ... + ) + ``` + + Parameters + ---------- + func : relax.Function + The function before fusion. + + Returns + ------- + ret : relax.Function + The function after fusion. + """ + # pylint: disable=unsupported-binary-operation + decode_matmul = is_op("relax.call_dps_packed")(varg_default_wildcard=True) + residual = wildcard() + pattern = ( + is_op("relax.add")(decode_matmul, residual) + | is_op("relax.add")(residual, decode_matmul) + | is_op("relax.multiply")(decode_matmul, residual) + | is_op("relax.multiply")(residual, decode_matmul) + ) + + def rewriter(expr, match): + if match[decode_matmul].args[0].global_symbol == "fastertransformer.gemm_fp16_int_bias": + matched_binary = match[pattern] + assert matched_binary.op.name in ["relax.add", "relax.multiply"] + binary_op = "plus" if matched_binary.op.name == "relax.add" else "multiply" + assert len(match[decode_matmul].args) == 2 + args_list = match[decode_matmul].args[1] + assert len(args_list) == 10 + matched_residual = match[residual] + if not args_list[9].value == 0: + # fastertransformer.gemm_fp16_int_bias_residual does not support + # bias_stride != 0 yet + return expr + return relax.call_dps_packed( + "fastertransformer.gemm_fp16_int_bias_residual", + [ + args_list[0], # x + args_list[1], # weight + args_list[2], # scale + args_list[3], # bias + matched_residual, # residual + args_list[4], # activation + binary_op, # binary_op + "identity", # unary_op + args_list[5], # m + args_list[6], # n + args_list[7], # k + args_list[8], # group_size + ], + out_sinfo=match[decode_matmul].struct_info, + ) + return expr + + return rewrite_call(pattern, rewriter, func) + + +def fuse_residual_unary(func: relax.Function) -> relax.Function: + """ + Fuse following `relax.nn.silu/relu/gelu` into fastertransformer.gemm_fp16_int_bias_residual + as residual unary operation: + + Before: + ``` + lv1 = relax.call_dps_packed("fastertransformer.gemm_fp16_int_bias_residual", ...) + lv2 = relax.silu(lv1) + + ``` + After: + ``` + lv2 = relax.call_dps_packed("fastertransformer.gemm_fp16_int_bias_residual", ..., "silu", ...) + ``` + + Parameters + ---------- + func : relax.Function + The function before fusion. + + Returns + ------- + ret : relax.Function + The function after fusion. + """ + # pylint: disable=unsupported-binary-operation + decode_matmul = is_op("relax.call_dps_packed")(varg_default_wildcard=True) + pattern = ( + is_op("relax.nn.silu")(decode_matmul) + | is_op("relax.nn.gelu")(decode_matmul) + | is_op("relax.nn.relu")(decode_matmul) + ) + + def rewriter(expr, match): + if ( + match[decode_matmul].args[0].global_symbol + == "fastertransformer.gemm_fp16_int_bias_residual" + ): + matched_activation = match[pattern] + assert matched_activation.op.name in ["relax.nn.silu", "relax.nn.gelu", "relax.nn.relu"] + assert len(match[decode_matmul].args) == 2 + args_list = match[decode_matmul].args[1] + assert len(args_list) == 12 + return relax.call_dps_packed( + "fastertransformer.gemm_fp16_int_bias_residual", + [ + args_list[0], # x + args_list[1], # weight + args_list[2], # scale + args_list[3], # bias + args_list[4], # residual + args_list[5], # activation + args_list[6], # binary_op + matched_activation.op.name[9:], # activation + args_list[8], # m + args_list[9], # n + args_list[10], # k + args_list[11], # group_size + ], + out_sinfo=match[decode_matmul].struct_info, + ) + return expr + + return rewrite_call(pattern, rewriter, func) diff --git a/python/mlc_chat/compiler_pass/fuse_transpose_matmul.py b/python/mlc_chat/compiler_pass/fuse_transpose_matmul.py new file mode 100644 index 0000000..5b3ecec --- /dev/null +++ b/python/mlc_chat/compiler_pass/fuse_transpose_matmul.py @@ -0,0 +1,151 @@ +"""A compiler pass that fuses transpose + matmul.""" +import tvm +from tvm import IRModule, relax, te, tir +from tvm.relax.dpl.pattern import is_op, wildcard +from tvm.relax.expr_functor import PyExprMutator, mutator + + +@tvm.transform.module_pass(opt_level=0, name="FuseTransposeMatmul") +class FuseTransposeMatmul: # pylint: disable=too-few-public-methods + """A compiler pass that fuses transpose + matmul.""" + + def transform_module(self, mod: IRModule, _ctx: tvm.transform.PassContext) -> IRModule: + """IRModule-level transformation""" + mod = relax.transform.FuseOpsByPattern( + [ + ( + "transpose_matmul_fuse", + *_pattern(), + ), + ] + )(mod) + transpose_matmul_codegen = _TransposeMatmulFuser(mod) + for g_var, func in mod.functions_items(): + if isinstance(func, relax.Function): + func = transpose_matmul_codegen.visit_expr(func) + transpose_matmul_codegen.builder_.update_func(g_var, func) + return transpose_matmul_codegen.builder_.get() + + +def _pattern(): + """Pattern for transpose + matmul.""" + # pylint: disable=invalid-name + w = wildcard() + x = wildcard() + wT = is_op("relax.permute_dims")(w) + o = is_op("relax.matmul")(x, wT) + # pylint: enable=invalid-name + annotations = {"o": o, "w": w, "x": x, "wT": wT} + + def _check(context: relax.transform.PatternCheckContext) -> bool: + transpose_call = context.annotated_expr["wT"] + ndim = transpose_call.args[0].struct_info.ndim + if ndim == -1: + return False + if ndim == 2 and transpose_call.attrs.axes is None: + return True + axes = list(range(ndim)) + axes[-1], axes[-2] = axes[-2], axes[-1] + return list(transpose_call.attrs.axes) == axes + + return o, annotations, _check + + +# pylint: disable=missing-docstring,invalid-name + + +@mutator +class _TransposeMatmulFuser(PyExprMutator): # pylint: disable=abstract-method + def __init__(self, mod): + super().__init__(mod) + + def visit_call_( # pylint: disable=arguments-renamed + self, + call: relax.Call, + ) -> relax.Expr: + out_dtype = None + + def te_transposed_matmul(a: te.Tensor, b: te.Tensor) -> te.Tensor: + nonlocal out_dtype + a_shape = list(a.shape) + b_shape = list(b.shape) + a_prepended = False + b_appended = False + if len(a_shape) == 1: + a_prepended = True + a_shape.insert(0, 1) + if len(b_shape) == 1: + b_appended = True + b_shape.append(1) + + is_a_larger = len(a_shape) > len(b_shape) + offset = len(a_shape) - len(b_shape) if is_a_larger else len(b_shape) - len(a_shape) + + a_relax = relax.Var("a", relax.TensorStructInfo(a.shape)) + bT_shape = list(b.shape) + bT_shape[-1], bT_shape[-2] = bT_shape[-2], bT_shape[-1] + bT_relax = relax.Var("b", relax.TensorStructInfo(bT_shape)) + output_shape = self.builder_.normalize( + relax.op.matmul(a_relax, bT_relax) + ).struct_info.shape + + def matmul_compute(*idx_spatial): + k = te.reduce_axis((0, a_shape[-1]), name="k") + + def multiply_compute(idx_reduce): + a_indices = [] + b_indices = [] + + for i in range(offset): + if is_a_larger: + a_indices.append(idx_spatial[i]) + else: + b_indices.append(idx_spatial[i]) + for i in range(offset, len(output_shape) - (2 - a_prepended - b_appended)): + a_dim = a_shape[i if is_a_larger else i - offset] + b_dim = b_shape[i if not is_a_larger else i - offset] + dim_equal = a_dim == b_dim + if not isinstance(dim_equal, tir.IntImm) or dim_equal == 0: + a_dim_is_one = isinstance(a_dim, tir.IntImm) and a_dim == 1 + b_dim_is_one = isinstance(b_dim, tir.IntImm) and b_dim == 1 + a_indices.append(0 if a_dim_is_one else idx_spatial[i]) + b_indices.append(0 if b_dim_is_one else idx_spatial[i]) + else: + a_indices.append(idx_spatial[i]) + b_indices.append(idx_spatial[i]) + + if not a_prepended: + a_indices.append(idx_spatial[-2 + b_appended]) + a_indices.append(idx_reduce) + if not b_appended: + b_indices.append(idx_spatial[-1]) + b_indices.append(idx_reduce) + + dtype = out_dtype + if dtype != "": + return a(*a_indices).astype(dtype) * b(*b_indices).astype(dtype) + return a(*a_indices) * b(*b_indices) + + return te.sum(multiply_compute(k), axis=k) + + return te.compute( + output_shape, + lambda *idx: matmul_compute(*idx), # pylint: disable=unnecessary-lambda + name="NT_matmul", + ) + + if isinstance(call.op, relax.GlobalVar): + function = self.builder_.get()[call.op] + if ( + "Composite" in function.attrs + and function.attrs["Composite"] == "transpose_matmul_fuse" + ): + out_dtype = function.ret_struct_info.dtype + return self.builder_.call_te( + te_transposed_matmul, + call.args[1], + call.args[0], + primfunc_name_hint="NT_matmul", + ) + + return super().visit_call_(call) diff --git a/python/mlc_chat/compiler_pass/lift_global_buffer_alloc.py b/python/mlc_chat/compiler_pass/lift_global_buffer_alloc.py new file mode 100644 index 0000000..bf709bc --- /dev/null +++ b/python/mlc_chat/compiler_pass/lift_global_buffer_alloc.py @@ -0,0 +1,195 @@ +"""A compiler pass that lifts TIR-level global allocation to Relax.""" +from typing import Dict, List, Tuple + +import tvm +from tvm import relax, tir +from tvm.ir.module import IRModule +from tvm.relax.analysis import remove_all_unused +from tvm.relax.expr_functor import PyExprMutator, mutator + + +@tvm.transform.module_pass(opt_level=0, name="LiftTIRGlobalBufferAlloc") +class LiftTIRGlobalBufferAlloc: # pylint: disable=too-few-public-methods + """A compiler pass that lifts TIR-level global allocation to Relax.""" + + def transform_module( + self, + mod: IRModule, + _ctx: tvm.transform.PassContext, + ) -> IRModule: + """IRModule-level transformation""" + return _TIRGlobalAllocRewriter(mod).transform() + + +@mutator +class _TIRGlobalAllocRewriter(PyExprMutator): # pylint: disable=abstract-method + def __init__(self, mod: IRModule): + super().__init__(mod) + self.mod = mod + self.gv2new_tensor_sinfo: Dict[ + tvm.ir.GlobalVar, Tuple[List[relax.TensorStructInfo], tir.PrimFunc] + ] = {} + + def transform(self) -> IRModule: + """Entry point of the transformation""" + for g_var, func in self.mod.functions_items(): + if isinstance(func, tir.PrimFunc): + updated_func, tensor_sinfo_list = remove_global_buf_alloc(func) + if len(tensor_sinfo_list) > 0: + self.gv2new_tensor_sinfo[g_var] = (tensor_sinfo_list, func) + self.builder_.update_func(g_var, updated_func) + + self.mod = self.builder_.get() + for g_var, func in self.mod.functions_items(): + if isinstance(func, relax.Function): + updated_func = self.visit_expr(func) + updated_func = remove_all_unused(updated_func) + self.builder_.update_func(g_var, updated_func) + return self.builder_.get() + + def visit_call_(self, call: relax.Call): # pylint: disable=arguments-renamed + call = self.visit_expr_post_order(call) + if ( + call.op != tvm.ir.Op.get("relax.call_tir") + or call.args[0] not in self.gv2new_tensor_sinfo + ): + return call + + g_var = call.args[0] + tensor_sinfo, func_before_update = self.gv2new_tensor_sinfo[g_var] + + assert len(call.sinfo_args) == 1 + if any(_has_symbolic_var(sinfo) for sinfo in tensor_sinfo): + tensor_sinfo, success = _resolve_tir_var_mapping(func_before_update, call, tensor_sinfo) + if not success: + # Cannot resolve TIR var mapping. Fall back to no lifting. + self.builder_.update_func(g_var, func_before_update) + self.gv2new_tensor_sinfo.pop(g_var) + return call + + if isinstance(call.sinfo_args[0], relax.TensorStructInfo): + new_call = relax.Call( + call.op, + args=call.args, + sinfo_args=[relax.TupleStructInfo(list(call.sinfo_args) + tensor_sinfo)], + attrs=call.attrs, + ) + emitted_tuple = self.builder_.emit(new_call) + return relax.TupleGetItem(emitted_tuple, 0) + assert isinstance(call.sinfo_args[0], relax.TupleStructInfo) + return relax.Call( + call.op, + args=call.args, + sinfo_args=[relax.TupleStructInfo(list(call.sinfo_args[0].fields) + tensor_sinfo)], + attrs=call.attrs, + ) + + +def remove_global_buf_alloc( + func: tir.PrimFunc, +) -> Tuple[tir.PrimFunc, List[relax.TensorStructInfo]]: + """Remove the global buffer allocation for a given TIR PrimFunc.""" + assert isinstance(func.body, tir.BlockRealize) + params = list(func.params) + buffer_map = dict(func.buffer_map) + tensor_sinfo = [] + alloc_buffers = [] + + insertion_point = len(params) + while params[insertion_point - 1].dtype != "handle": + insertion_point -= 1 + assert insertion_point >= 1 + + prev_root_block = func.body.block + for buf_alloc in func.body.block.alloc_buffers: + if buf_alloc.scope() == "global": + param = tir.Var("var_" + buf_alloc.name, "handle") + params.insert(insertion_point, param) + insertion_point += 1 + buffer_map[param] = buf_alloc + tensor_sinfo.append(relax.TensorStructInfo(buf_alloc.shape, buf_alloc.dtype)) + else: + alloc_buffers.append(buf_alloc) + + if len(tensor_sinfo) == 0: + return func, [] + + assert len(prev_root_block.iter_vars) == 0 + assert len(prev_root_block.reads) == 0 + assert len(prev_root_block.writes) == 0 + assert len(prev_root_block.match_buffers) == 0 + assert prev_root_block.name_hint == "root" + assert prev_root_block.init is None + root_block = tir.Block( + iter_vars=[], + reads=[], + writes=[], + name_hint="root", + body=prev_root_block.body, + alloc_buffers=alloc_buffers, + annotations=prev_root_block.annotations, + ) + + updated_func = tir.PrimFunc( + params=params, + body=tir.BlockRealize(iter_values=[], predicate=True, block=root_block), + ret_type=func.ret_type, + buffer_map=buffer_map, + attrs=func.attrs, + ) + return updated_func, tensor_sinfo + + +def _has_symbolic_var(tensor_sinfo: relax.TensorStructInfo) -> bool: + assert isinstance(tensor_sinfo.shape, relax.ShapeExpr) + for dim in tensor_sinfo.shape.values: + if not isinstance(dim, tir.IntImm): + return True + return False + + +def _resolve_tir_var_mapping( # pylint: disable=too-many-locals + func: tir.PrimFunc, + call: relax.Call, + tensor_sinfo: List[relax.TensorStructInfo], +) -> Tuple[List[relax.TensorStructInfo], bool]: + """Resolve the TIR symbolic var relationship across sides of PrimFunc and Relax Function""" + var_map: Dict[tir.Var, tir.PrimExpr] = {} + + n_arg = len(call.args[1].fields) + for i in range(n_arg): + buffer_shape = func.buffer_map[func.params[i]].shape + arg_shape = call.args[1][i].struct_info.shape.values + assert len(buffer_shape) == len(arg_shape) + for v_l, v_r in zip(buffer_shape, arg_shape): + if isinstance(v_l, tir.Var): + var_map[v_l] = v_r + elif not isinstance(v_l, tir.IntImm): + return [], False + + ret_tensors = call.sinfo_args[0] + ret_tensors = ( + [ret_tensors] # type: ignore[assignment] + if isinstance(ret_tensors, relax.TensorStructInfo) + else list(ret_tensors.fields) + ) + for i, ret_tensor in enumerate(ret_tensors): + buffer_shape = func.buffer_map[func.params[n_arg + i]].shape + ret_tensor_shape = ret_tensor.shape.values + assert len(buffer_shape) == len(ret_tensor_shape) + for v_l, v_r in zip(buffer_shape, ret_tensor_shape): + if isinstance(v_l, tir.Var): + var_map[v_l] = v_r + elif not isinstance(v_l, tir.IntImm): + return [], False + + updated_tensor_sinfo = [] + for sinfo in tensor_sinfo: + if not _has_symbolic_var(sinfo): + updated_tensor_sinfo.append(sinfo) + continue + new_shape = [] + for dim in sinfo.shape.values: + new_shape.append(tir.stmt_functor.substitute(dim, var_map)) + updated_tensor_sinfo.append(relax.TensorStructInfo(new_shape, sinfo.dtype)) + return updated_tensor_sinfo, True diff --git a/python/mlc_chat/compiler_pass/pipeline.py b/python/mlc_chat/compiler_pass/pipeline.py new file mode 100644 index 0000000..98922c6 --- /dev/null +++ b/python/mlc_chat/compiler_pass/pipeline.py @@ -0,0 +1,160 @@ +"""The compilation pipeline for LLM applications.""" + +from pathlib import Path +from typing import Any, Dict, List, Optional + +import tvm +from tvm import IRModule +from tvm import dlight as dl +from tvm.relax import register_pipeline # pylint: disable=no-name-in-module +from tvm.relax.frontend import nn + +from mlc_chat.support import logging + +from .attach_to_ir_module import ( + AttachAdditionalPrimFuncs, + AttachLogitProcessFunc, + AttachMemoryPlanAttr, + AttachVariableBounds, +) +from .clean_up_tir_attrs import CleanUpTIRAttrs +from .cublas_dispatch import CublasDispatch +from .estimate_memory_usage import AttachMetadataWithMemoryUsage +from .fuse_add_norm import FuseAddRMSNorm +from .fuse_dequantize_matmul_ewise import FuseDequantizeMatmulEwise +from .fuse_dequantize_take import FuseDequantizeTake +from .fuse_dequantize_transpose import FuseDequantizeTranspose +from .fuse_ft_dequantize_matmul_epilogue import FuseFTDequantizeEpilogue +from .fuse_transpose_matmul import FuseTransposeMatmul +from .lift_global_buffer_alloc import LiftTIRGlobalBufferAlloc +from .rewrite_kv_cache_creation import RewriteKVCacheCreation +from .scatter_tuple_get_item import ScatterTupleGetItem + +logger = logging.getLogger(__name__) + + +@tvm.transform.module_pass(opt_level=0, name="_LogProgress") +class _LogProgress: # pylint: disable=too-few-public-methods + """A dummy compiler pass that does nothing but logging.""" + + def __init__(self, *args): + self.args = args + + def transform_module(self, mod: IRModule, _ctx: tvm.transform.PassContext) -> IRModule: + """A dummy transformation""" + logger.info(*self.args) + return mod + + +@tvm.transform.module_pass(opt_level=0, name="DebugDump") +class _DebugDump: # pylint: disable=too-few-public-methods + """A dummy compiler pass that does nothing but logging. + Only enabled when debug_dump is not None""" + + def __init__(self, file_name: str, file_path: Optional[Path], show_meta: bool = False): + self.file_name = file_name + self.file_path = file_path + self.show_meta = show_meta + + def transform_module(self, mod: IRModule, _ctx: tvm.transform.PassContext) -> IRModule: + """A dummy transformation that dumps the module to file""" + if self.file_path is not None: + # NOTE: We use debug level here to avoid spamming the console + logger.debug("Dumping IR to %s", self.file_path / self.file_name) + with open(self.file_path / self.file_name, "w", encoding="utf-8") as f: + f.write(mod.script(show_meta=self.show_meta)) + return mod + + +@register_pipeline("mlc_llm") +def _mlc_llm_pipeline( # pylint: disable=too-many-arguments + target: tvm.target.Target, + flashinfer: bool = False, + cublas_gemm: bool = False, + faster_transformer: bool = False, # pylint: disable=unused-argument + variable_bounds: Dict[str, int] = None, + additional_tirs: Dict[str, tvm.tir.PrimFunc] = None, + metadata: Dict[str, Any] = None, + ext_mods: List[nn.ExternModule] = None, + debug_dump: Optional[Path] = None, +): + variable_bounds = variable_bounds or {} + additional_tirs = additional_tirs or {} + metadata = metadata or {} + ext_mods = ext_mods or [] + + @tvm.transform.module_pass(opt_level=0) + def _pipeline(mod: tvm.ir.IRModule, _ctx: tvm.transform.PassContext) -> tvm.ir.IRModule: + seq = tvm.transform.Sequential( + [ + # Phase 0. Add additional information for compilation and remove unused Relax func + RewriteKVCacheCreation(target, flashinfer, metadata), + AttachVariableBounds(variable_bounds), + AttachLogitProcessFunc(), + AttachAdditionalPrimFuncs(additional_tirs), + AttachMemoryPlanAttr(), + tvm.tir.transform.BindTarget(tvm.target.Target.current(allow_none=False)), + _DebugDump("debug-phase0.py", debug_dump, show_meta=False), + # Phase 1. Passes on high-level operator graph + _LogProgress("Running TVM Relax graph-level optimizations"), + FuseFTDequantizeEpilogue(), + FuseDequantizeTranspose(), + CublasDispatch() if cublas_gemm else tvm.transform.Sequential([]), + FuseAddRMSNorm(target=target), + FuseTransposeMatmul(), + _DebugDump("debug-phase1.py", debug_dump, show_meta=False), + # Phase 2. Lowering to TIR, inherited TVM Relax's official "zero" pipeline + _LogProgress("Lowering to TVM TIR kernels"), + tvm.relax.transform.LegalizeOps(), + tvm.relax.transform.AnnotateTIROpPattern(), + tvm.relax.transform.FoldConstant(), + tvm.relax.transform.FuseOps(), + tvm.relax.transform.FuseTIR(), + _DebugDump("debug-phase2.py", debug_dump, show_meta=False), + # Phase 3. Passes on TIR + _LogProgress("Running TVM TIR-level optimizations"), + FuseDequantizeMatmulEwise(), + FuseDequantizeTake(), + tvm.relax.transform.DeadCodeElimination(), + CleanUpTIRAttrs(["op_pattern"]), + _DebugDump("debug-phase3.py", debug_dump, show_meta=False), + # Phase 4. Low-level Optimizations + _LogProgress("Running TVM Dlight low-level optimizations"), + dl.ApplyDefaultSchedule( + dl.gpu.Matmul(), + dl.gpu.GEMV(), + dl.gpu.Reduction(), + dl.gpu.GeneralReduction(), + dl.gpu.Fallback(), + ), + _DebugDump("debug-phase4.py", debug_dump, show_meta=False), + _LogProgress("Lowering to VM bytecode"), + LiftTIRGlobalBufferAlloc(), + ( + tvm.tir.transform.ForceNarrowIndexToInt32() + if target.kind.name != "cuda" + else tvm.transform.Sequential([]) + ), + ScatterTupleGetItem(), + tvm.relax.transform.RewriteDataflowReshape(), + tvm.relax.transform.ToNonDataflow(), + tvm.relax.transform.RemovePurityChecking(), + tvm.relax.transform.CallTIRRewrite(), + tvm.relax.transform.StaticPlanBlockMemory(), + AttachMetadataWithMemoryUsage(metadata), + tvm.relax.transform.RewriteCUDAGraph(), + tvm.relax.transform.LowerAllocTensor(), + tvm.relax.transform.KillAfterLastUse(), + tvm.relax.transform.VMBuiltinLower(), + tvm.relax.transform.VMShapeLower(), + tvm.relax.transform.AttachGlobalSymbol(), + _DebugDump("debug-final.py", debug_dump, show_meta=False), + _LogProgress("Compiling external modules"), + tvm.relax.transform.AttachExternModules(ext_mods), + _LogProgress("Compilation complete! Exporting to disk"), + ] + ) + mod = seq(mod) + return mod + + return _pipeline diff --git a/python/mlc_chat/compiler_pass/rewrite_kv_cache_creation.py b/python/mlc_chat/compiler_pass/rewrite_kv_cache_creation.py new file mode 100644 index 0000000..808969e --- /dev/null +++ b/python/mlc_chat/compiler_pass/rewrite_kv_cache_creation.py @@ -0,0 +1,154 @@ +"""A pass that rewrites KV cache creation functions in IRModule.""" + +from typing import Any, Dict + +import tvm +from tvm import IRModule, relax + +from mlc_chat.nn import RopeMode, kv_cache + + +def extract_creation_args(func: relax.Function) -> Dict[str, Any]: + """Extract the KV cache creation args from the given generic creation func.""" + assert isinstance(func.body, relax.SeqExpr) + assert len(func.body.blocks) == 1 + assert isinstance(func.body.blocks[0], relax.DataflowBlock) + assert len(func.body.blocks[0].bindings) == 2 + assert isinstance(func.body.blocks[0].bindings[0], relax.VarBinding) + assert isinstance(func.body.blocks[0].bindings[0].value, relax.Call) + assert isinstance(func.body.blocks[0].bindings[0].value.op, relax.ExternFunc) + assert ( + func.body.blocks[0].bindings[0].value.op.global_symbol + == "mlc.create_paged_kv_cache_generic" + ) + + args = func.body.blocks[0].bindings[0].value.args + assert len(args) == 10 + assert isinstance(args[0], relax.ShapeExpr) + assert len(args[0].values) == 4 + for i in range(1, 9): + assert isinstance(args[i], relax.PrimValue) + assert isinstance(args[i].value, (tvm.tir.IntImm, tvm.tir.FloatImm)) + assert isinstance(args[9], relax.DataTypeImm) + + return { + "max_batch_size": args[0].values[0], + "max_total_seq_len": args[0].values[1], + "prefill_chunk_size": args[0].values[2], + "page_size": args[0].values[3], + "num_hidden_layers": args[1].value.value, + "num_attention_heads": args[2].value.value, + "num_key_value_heads": args[3].value.value, + "head_dim": args[4].value.value, + "rope_mode": args[5].value.value, + "rope_scale": args[6].value.value, + "rope_theta": args[7].value.value, + "rotary_dim": args[8].value.value, + "dtype": args[9].value, + } + + +@tvm.transform.module_pass(opt_level=0, name="RewriteKVCacheCreation") +class RewriteKVCacheCreation: # pylint: disable=too-many-instance-attributes + """Rewrite KV cache creation functions to IRModule.""" + + def __init__( + self, target: tvm.target.Target, flashinfer: bool, metadata: Dict[str, Any] + ) -> None: + """Initializer. + + Parameters + ---------- + target : tvm.target.Target + The target of the model compilation. + + flashinfer : bool + A boolean indicating if flashinfer is enabled. + """ + self.target = target + self.flashinfer = flashinfer + self.metadata = metadata + + def transform_module(self, mod: IRModule, _ctx: tvm.transform.PassContext) -> IRModule: + """Entrypoint""" + func_dict = {} + creation_func = None + for g_var, func in mod.functions_items(): + # Try to find the `create_paged_kv_cache` func. + if g_var.name_hint == "create_paged_kv_cache": + creation_func = func + else: + func_dict[g_var] = func + + if creation_func is None: + return mod + + new_mod = IRModule(func_dict) + if mod.attrs is not None: + new_mod = new_mod.with_attrs(mod.attrs) + + kwargs = extract_creation_args(creation_func) + + bb = relax.BlockBuilder(new_mod) + self.create_tir_paged_kv_cache(bb, kwargs) + self.create_flashinfer_paged_kv_cache(bb, kwargs) + return bb.finalize() + + def create_tir_paged_kv_cache(self, bb: relax.BlockBuilder, kwargs: Dict[str, Any]) -> None: + """Create the TIR-based PagedKVCache""" + max_batch_size = relax.Var( + "max_batch_size_", relax.ShapeStructInfo([kwargs["max_batch_size"]]) + ) + max_total_seq_len = relax.Var( + "max_total_seq_len_", relax.ShapeStructInfo([kwargs["max_total_seq_len"]]) + ) + prefill_chunk_size = relax.Var( + "prefill_chunk_size_", relax.ShapeStructInfo([kwargs["prefill_chunk_size"]]) + ) + page_size = relax.Var("page_size_", relax.ShapeStructInfo([kwargs["page_size"]])) + + with bb.function( + name="create_tir_paged_kv_cache", + params=[max_batch_size, max_total_seq_len, prefill_chunk_size, page_size], + ): + cache = kv_cache.TIRPagedKVCache(target=self.target, **kwargs) + bb.emit_func_output(cache._expr) # pylint: disable=protected-access + + def create_flashinfer_paged_kv_cache( + self, bb: relax.BlockBuilder, kwargs: Dict[str, Any] + ) -> None: + """Create the FlashInfer-based PagedKVCache""" + # Filter the cases which FlashInfer does not support. + if ( # pylint: disable=too-many-boolean-expressions + not self.flashinfer + or str(kwargs["dtype"]) != "float16" + or kwargs["head_dim"] != 128 + or ( + kwargs["rope_mode"] == RopeMode.INLINE + and kwargs["rotary_dim"] != kwargs["head_dim"] + ) + or ( + # bypass GPT-2 since it uses attn_score_scaling_factor + "gpt2" + in self.metadata["model_type"] + ) + ): + return + + max_batch_size = relax.Var( + "max_batch_size_", relax.ShapeStructInfo([kwargs["max_batch_size"]]) + ) + max_total_seq_len = relax.Var( + "max_total_seq_len_", relax.ShapeStructInfo([kwargs["max_total_seq_len"]]) + ) + prefill_chunk_size = relax.Var( + "prefill_chunk_size_", relax.ShapeStructInfo([kwargs["prefill_chunk_size"]]) + ) + page_size = relax.Var("page_size_", relax.ShapeStructInfo([kwargs["page_size"]])) + + with bb.function( + name="create_flashinfer_paged_kv_cache", + params=[max_batch_size, max_total_seq_len, prefill_chunk_size, page_size], + ): + cache = kv_cache.FlashInferPagedKVCache(target=self.target, **kwargs) + bb.emit_func_output(cache._expr) # pylint: disable=protected-access diff --git a/python/mlc_chat/compiler_pass/scatter_tuple_get_item.py b/python/mlc_chat/compiler_pass/scatter_tuple_get_item.py new file mode 100644 index 0000000..281c6ec --- /dev/null +++ b/python/mlc_chat/compiler_pass/scatter_tuple_get_item.py @@ -0,0 +1,51 @@ +"""A compiler pass that scatters TupleGetItem for lazy TupleGetItems.""" + +from typing import Dict + +import tvm +from tvm import relax +from tvm.ir.module import IRModule +from tvm.relax.analysis import remove_all_unused +from tvm.relax.expr import Expr, Var +from tvm.relax.expr_functor import PyExprMutator, mutator + + +@tvm.transform.module_pass(opt_level=0, name="ScatterTupleGetItem") +class ScatterTupleGetItem: # pylint: disable=too-few-public-methods + """A compiler pass that scatters TupleGetItem for lazy TupleGetItems.""" + + def transform_module(self, mod: IRModule, _ctx: tvm.transform.PassContext) -> IRModule: + """IRModule-level transformation""" + return _Scatter(mod).transform() + + +@mutator +class _Scatter(PyExprMutator): # pylint: disable=abstract-method + def __init__(self, mod: IRModule) -> None: + super().__init__(mod) + self.mod = mod + self.var_map: Dict[Var, Expr] = {} + + def transform(self) -> IRModule: + """Entry point""" + for g_var, func in self.mod.functions_items(): + if isinstance(func, relax.Function): + updated_func = self.visit_expr(func) + updated_func = remove_all_unused(updated_func) + self.builder_.update_func(g_var, updated_func) + return self.builder_.get() + + def visit_var_binding_(self, binding: relax.VarBinding): + super().visit_var_binding_(binding) + if isinstance(binding.value, relax.TupleGetItem): + self.var_map[binding.var] = binding.value + + def visit_dataflow_var_( # pylint: disable=arguments-renamed + self, var: relax.DataflowVar + ) -> Expr: + if var in self.var_map: + new_var = self.builder_.emit(self.var_map[var], name_hint=var.name_hint) + self.set_var_remap(var.vid, new_var) + self.var_map.pop(var) + return new_var + return var diff --git a/python/mlc_chat/conversation_template.py b/python/mlc_chat/conversation_template.py new file mode 100644 index 0000000..6ca148f --- /dev/null +++ b/python/mlc_chat/conversation_template.py @@ -0,0 +1,77 @@ +"""The conversation template registry and presets in MLC LLM""" + +from typing import Dict, Optional + +from .protocol.conversation_protocol import Conversation, MessagePlaceholders + + +class ConvTemplateRegistry: + """Global conversation template registry for preset templates.""" + + _conv_templates: Dict[str, Conversation] = {} + + @staticmethod + def register_conv_template(conv_template: Conversation, override: bool = False) -> None: + """Register a new conversation template in the global registry. + Using `override = True` to override the previously registered + template with the same name. + """ + name = conv_template.name + if name is None: + raise ValueError("The template to register should have non-None name.") + if name in ConvTemplateRegistry._conv_templates and not override: + raise ValueError( + "The name of the template has been registered " + f"for {ConvTemplateRegistry._conv_templates[name].model_dump_json()}" + ) + ConvTemplateRegistry._conv_templates[name] = conv_template + + @staticmethod + def get_conv_template(name: str) -> Optional[Conversation]: + """Return the conversation template specified by the given name, + or None if the template is not registered. + """ + return ConvTemplateRegistry._conv_templates.get(name, None) + + +############## Preset Conversation Templates ############## + +# Llama2 +ConvTemplateRegistry.register_conv_template( + Conversation( + name="llama-2", + system_template=f"[INST] <>\n{MessagePlaceholders.SYSTEM.value}\n<>\n\n ", + system_message="You are a helpful, respectful and honest assistant.", + roles={"user": "[INST]", "assistant": "[/INST]", "tool": "[INST]"}, + seps=[" "], + role_content_sep=" ", + role_empty_sep=" ", + stop_str=["[INST]"], + stop_token_ids=[2], + ) +) + +# Gorilla +ConvTemplateRegistry.register_conv_template( + Conversation( + name="gorilla", + system_template=f"{MessagePlaceholders.SYSTEM.value}", + system_message=( + "A chat between a curious user and an artificial intelligence assistant. " + "The assistant provides helpful, detailed, and " + "polite responses to the user's inquiries." + ), + role_templates={ + "user": ( + f"<> {MessagePlaceholders.USER.value} <> " + f"{MessagePlaceholders.FUNCTION.value}" + ), + }, + roles={"user": "USER", "assistant": "ASSISTANT", "tool": "USER"}, + seps=["\n", ""], + role_content_sep=": ", + role_empty_sep=":", + stop_str=[""], + stop_token_ids=[2], + ) +) diff --git a/python/mlc_chat/embeddings/__init__.py b/python/mlc_chat/embeddings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/mlc_chat/embeddings/openai.py b/python/mlc_chat/embeddings/openai.py new file mode 100644 index 0000000..022d55b --- /dev/null +++ b/python/mlc_chat/embeddings/openai.py @@ -0,0 +1,245 @@ +# pylint: disable=missing-docstring +from __future__ import annotations + +from typing import Iterable, List, Optional, Sequence, Tuple + +import numpy as np +from langchain.embeddings import OpenAIEmbeddings # pylint: disable=import-error +from langchain_community.embeddings.openai import ( # pylint: disable=import-error + async_embed_with_retry, + embed_with_retry, +) + +from mlc_chat.support import logging + +logger = logging.getLogger(__name__) + + +class MLCEmbeddings(OpenAIEmbeddings): + def _chunk_tokens(self, texts: Sequence[str]) -> Tuple[List[List], List[int]]: + """Tokenize and chunk texts to fit in the model's context window.""" + if not self.embedding_ctx_length: + raise ValueError( + "embedding_ctx_length must be defined to use _get_len_safe_embeddings." + ) + + try: + import tiktoken # pylint: disable=import-outside-toplevel + except ImportError as err: + raise ImportError( + "Could not import tiktoken python package. " + "This is needed in order to for OpenAIEmbeddings. " + "Please install it with `pip install tiktoken`." + ) from err + + tokens = [] + indices = [] + model_name = self.tiktoken_model_name or self.model + try: + encoding = tiktoken.encoding_for_model(model_name) + except KeyError: + logger.warning("Warning: model not found. Using cl100k_base encoding.") + model = "cl100k_base" + encoding = tiktoken.get_encoding(model) + for i, text in enumerate(texts): + if self.model.endswith("001"): + # See: https://github.com/openai/openai-python/issues/418#issuecomment-1525939500 + # replace newlines, which can negatively affect performance. + text = text.replace("\n", " ") + token = encoding.encode( + text, + allowed_special=self.allowed_special, + disallowed_special=self.disallowed_special, + ) + for j in range(0, len(token), self.embedding_ctx_length): + tokens.append(token[j : j + self.embedding_ctx_length]) + indices.append(i) + return tokens, indices + + def _batch_embed( + self, inputs: Sequence, *, chunk_size: Optional[int] = None + ) -> List[List[float]]: + batched_embeddings: List[List[float]] = [] + _chunk_size = chunk_size or self.chunk_size + _iter: Iterable = range(0, len(inputs), _chunk_size) + if self.show_progress_bar: + try: + from tqdm import tqdm # pylint: disable=import-outside-toplevel + + _iter = tqdm(_iter) + except ImportError: + pass + + for i in _iter: + response = embed_with_retry( + self, + input=inputs[i : i + _chunk_size], + **self._invocation_params, + ) + batched_embeddings.extend(r["embedding"] for r in response["data"]) + return batched_embeddings + + async def _abatch_embed( + self, inputs: Sequence, *, chunk_size: Optional[int] = None + ) -> List[List[float]]: + batched_embeddings: List[List[float]] = [] + _chunk_size = chunk_size or self.chunk_size + _iter: Iterable = range(0, len(inputs), _chunk_size) + if self.show_progress_bar: + try: + from tqdm import tqdm # pylint: disable=import-outside-toplevel + + _iter = tqdm(_iter) + except ImportError: + pass + + for i in _iter: + response = await async_embed_with_retry( + self, + input=inputs[i : i + _chunk_size], + **self._invocation_params, + ) + batched_embeddings.extend(r["embedding"] for r in response["data"]) + return batched_embeddings + + # please refer to + # https://github.com/openai/openai-cookbook/blob/main/examples/Embedding_long_inputs.ipynb + def _get_len_safe_embeddings( # pylint: disable=too-many-locals,unused-argument + self, + texts: List[str], + *, + engine: str, + chunk_size: Optional[int] = None, + ) -> List[List[float]]: + tokens, indices = self._chunk_tokens(texts) + batched_embeddings = self._batch_embed(tokens, chunk_size=chunk_size) + results: List[List[List[float]]] = [[] for _ in range(len(texts))] + num_tokens_in_batch: List[List[int]] = [[] for _ in range(len(texts))] + for idx, tokens_i, batched_emb in zip(indices, tokens, batched_embeddings): + results[idx].append(batched_emb) + num_tokens_in_batch[idx].append(len(tokens_i)) + + embeddings = [] + empty_average = embed_with_retry( + self, + input="", + **self._invocation_params, + )["data"][ + 0 + ]["embedding"] + for _result, num_tokens in zip(results, num_tokens_in_batch): + if len(_result) == 0: + average = empty_average + else: + average = np.average(_result, axis=0, weights=num_tokens) + normalized = (average / np.linalg.norm(average)).tolist() + embeddings.append(normalized) + + return embeddings + + # please refer to + # https://github.com/openai/openai-cookbook/blob/main/examples/Embedding_long_inputs.ipynb + async def _aget_len_safe_embeddings( # pylint: disable=too-many-locals,unused-argument + self, + texts: List[str], + *, + engine: str, + chunk_size: Optional[int] = None, + ) -> List[List[float]]: + tokens, indices = self._chunk_tokens(texts) + batched_embeddings = await self._abatch_embed(tokens, chunk_size=chunk_size) + + results: List[List[List[float]]] = [[] for _ in range(len(texts))] + num_tokens_in_batch: List[List[int]] = [[] for _ in range(len(texts))] + for idx, tokens_i, batched_emb in zip(indices, tokens, batched_embeddings): + results[idx].append(batched_emb) + num_tokens_in_batch[idx].append(len(tokens_i)) + + embeddings = [] + empty_average = ( + await async_embed_with_retry( + self, + input="", + **self._invocation_params, + ) + )[ + "data" + ][0]["embedding"] + for _result, num_tokens in zip(results, num_tokens_in_batch): + if len(_result) == 0: + average = empty_average + else: + average = np.average(_result, axis=0, weights=num_tokens) + normalized = (average / np.linalg.norm(average)).tolist() + embeddings.append(normalized) + + return embeddings + + def embed_documents( + self, texts: List[str], chunk_size: Optional[int] = None + ) -> List[List[float]]: + """Call out to OpenAI's embedding endpoint for embedding search docs. + + Args: + texts: The list of texts to embed. + chunk_size: The chunk size of embeddings. If None, will use the chunk size + specified by the class. + + Returns: + List of embeddings, one for each text. + """ + # NOTE: to keep things simple, as long as the embedding_ctx_length is defined, + # we assume the list may contain texts longer than the maximum context and + # use length-safe embedding function. + if self.embedding_ctx_length: + return self._get_len_safe_embeddings( + texts, engine=self.deployment, chunk_size=chunk_size + ) + + embeddings = self._batch_embed(texts, chunk_size=chunk_size) + return [(np.array(e) / np.linalg.norm(e)).tolist() for e in embeddings] + + async def aembed_documents( + self, texts: List[str], chunk_size: Optional[int] = 0 + ) -> List[List[float]]: + """Call out to OpenAI's embedding endpoint async for embedding search docs. + + Args: + texts: The list of texts to embed. + chunk_size: The chunk size of embeddings. If None, will use the chunk size + specified by the class. + + Returns: + List of embeddings, one for each text. + """ + # NOTE: to keep things simple, as long as the embedding_ctx_length is defined, + # we assume the list may contain texts longer than the maximum context and + # use length-safe embedding function. + if self.embedding_ctx_length: + return await self._aget_len_safe_embeddings(texts, engine=self.deployment) + + embeddings = await self._abatch_embed(texts, chunk_size=chunk_size) + return [(np.array(e) / np.linalg.norm(e)).tolist() for e in embeddings] + + def embed_query(self, text: str) -> List[float]: + """Call out to OpenAI's embedding endpoint for embedding query text. + + Args: + text: The text to embed. + + Returns: + Embedding for the text. + """ + return self.embed_documents([text])[0] + + async def aembed_query(self, text: str) -> List[float]: + """Call out to OpenAI's embedding endpoint async for embedding query text. + + Args: + text: The text to embed. + + Returns: + Embedding for the text. + """ + embeddings = await self.aembed_documents([text]) + return embeddings[0] diff --git a/python/mlc_chat/gradio.py b/python/mlc_chat/gradio.py new file mode 100644 index 0000000..1ab6ae6 --- /dev/null +++ b/python/mlc_chat/gradio.py @@ -0,0 +1,247 @@ +"""Gradio interface for MLC Chat.""" +# pylint: disable=import-error,invalid-name,too-many-instance-attributes,too-many-locals +import argparse +import glob +import os +from typing import Dict, Optional + +import gradio as gr + +from .chat_module import ChatModule + + +def _parse_args(): + args = argparse.ArgumentParser("MLC-Chat Gradio Interface") + args.add_argument( + "--artifact-path", + type=str, + default="dist", + help="Please provide a path containing all the model folders you wish to use.", + ) + args.add_argument( + "--device", + type=str, + default="auto", + help="The description of the device to run on. User should provide a string in the \ + form of 'device_name:device_id' or 'device_name', where 'device_name' is one of \ + 'cuda', 'metal', 'vulkan', 'rocm', 'opencl', 'auto' (automatically detect the \ + local device), and 'device_id' is the device id to run on. If no 'device_id' \ + is provided, it will be set to 0 by default.", + ) + args.add_argument("--port", type=int, default=7860, help="The port number to run gradio.") + args.add_argument("--host", type=str, default="127.0.0.1", help="The local host to run gradio.") + args.add_argument( + "--share", + action="store_true", + help="Whether to create a publicly shareable link for the interface.", + ) + parsed = args.parse_args() + return parsed + + +def _get_all_available_models_under_dir(artifact_path: str) -> Dict[str, str]: + r"""Given the artifact path storing all models, returns a dict mapping available model names + to the correct `model` args passed into ChatModule. + + Note + ---- + We only search for folders under the artifact_path, without recursive search for subfolders. + For each folder, we count it as a valid MLC model folder if either it contains an + `mlc-chat-config.json` file, or it contains a `params` folder which contains an + `mlc-chat-config.json` file. We will map the name of a valid folder to its full path to the + folder containing `mlc-chat-config.json`. + """ + + # step 0. retrieve the absolute path of artifact_path + search_dir = os.path.abspath(artifact_path) + if not os.path.exists(search_dir): + err_msg = ( + f"The artifact path {artifact_path} you provided is neither a valid full path nor a " + "valid path relative to the current working directory. Please provide a correct " + "artifact path.", + ) + raise FileNotFoundError(err_msg) + + # step 1. go through all the folders, build the model dict + model_dict = {} + for path in glob.glob(os.path.join(search_dir, "*")): + if os.path.isdir(path): + model_name = os.path.basename(os.path.normpath(path)) + # check if it contains `mlc-chat-config.json` + if os.path.exists(os.path.join(path, "mlc-chat-config.json")): + model_dict[model_name] = os.path.abspath(path) + # check if it contains `params/mlc-chat-config.json` + elif os.path.exists(os.path.join(path, "params", "mlc-chat-config.json")): + model_dict[model_name] = os.path.abspath(os.path.join(path, "params")) + + return model_dict + + +class GradioModule: + r"""The Gradio module for MLC Chat. Different from ChatModule Python API, Gradio module allows + users to load in a directory of models, watch the streaming in web browser, and switch between + models more easily to compare performance. + + Note: Multimodality will be supported soon, i.e. allowing users to upload an image to chat. + """ + + def __init__(self, artifact_path: str = "dist", device: str = "auto"): + self.artifact_path = artifact_path + self.device_str = device + self.chat_mod: Optional[ChatModule] = None + self.model_dict = _get_all_available_models_under_dir(artifact_path) + + def gradio_reload_model(self, model_name: str): + r"""Reload the model given the user-selected model name.""" + self.chat_mod = ChatModule(self.model_dict[model_name], self.device_str) + + updated_dict = { + "chatbot": None, + "chat_state": [], + "img_list": [], + "image_model": gr.update(interactive=False, visible=False), + "stream_interval": gr.update(interactive=True, visible=True), + "reset_llm_button": gr.update(interactive=True, visible=True), + "stats_button": gr.update(interactive=True, visible=True), + "stats_output": gr.update(placeholder="Click to get runtime statistics.", visible=True), + "text_input": gr.update(interactive=True, placeholder="Type and press enter"), + } + + return list(updated_dict.values()) + + def gradio_reset_model(self): + r"""Reset the current chat model.""" + self.chat_mod.reset_chat() + + updated_dict = { + "chatbot": None, + "chat_state": [], + "img_list": [], + "text_input": gr.update(interactive=True, placeholder="Type and press enter"), + } + + return list(updated_dict.values()) + + def gradio_ask(self, text_input, chatbot): + r"""Display user text input in the chatbot.""" + chatbot = chatbot + [[text_input, None]] + text_input = "" + return text_input, chatbot + + def gradio_answer(self, chatbot, stream_interval): + r"""Generate and display the chat module's response. + Note: Below is a low-level implementation of generate() API, since it's easier + to yield without delta callback.""" + prompt = chatbot[-1][0] + # pylint: disable=protected-access + self.chat_mod._prefill(prompt) + i, new_msg = 0, "" + while not self.chat_mod._stopped(): + self.chat_mod._decode() + if i % stream_interval == 0 or self.chat_mod._stopped(): + new_msg = self.chat_mod._get_message() + chatbot[-1][1] = new_msg + yield chatbot + i += 1 + # pylint: enable=protected-access + + def gradio_stats(self): + """Get runtime statistics.""" + return self.chat_mod.stats() + + +def launch_gradio( + artifact_path: str = "dist", + device: str = "auto", + port: int = 7860, + share: bool = False, + host: str = "127.0.0.1", +): + r"""Launch the gradio interface with a given port, creating a publically sharable link if + specified.""" + + # create a gradio module + mod = GradioModule(artifact_path, device) + + title = """

MLC Chat Gradio Interface

""" + description = ( + """

Welcome to MLC Chat! Pick a model from your local ids to get started.

""" + ) + + with gr.Blocks() as demo: + gr.Markdown(title) + gr.Markdown(description) + + # ---------------------- user interface design ------------------------- + with gr.Row(): + with gr.Column(scale=0.3): + llm_model = gr.Dropdown(list(mod.model_dict.keys()), label="Language Model") + image_model = gr.Dropdown( + ["-None-"], + label="Do you wanna add an image model?", + visible=False, + interactive=False, + ) + image = gr.Image(type="pil", interactive=False, visible=False) + stream_interval = gr.Slider( + minimum=1.0, + maximum=5.0, + value=2.0, + step=1.0, + interactive=True, + visible=False, + label="Stream Interval", + ) + reset_llm_button = gr.Button("Reset chat", visible=False, interactive=False) + stats_button = gr.Button("Get Runtime Statistics", interactive=False, visible=False) + stats_output = gr.Textbox( + show_label=False, + placeholder="Click to get runtime statistics.", + interactive=False, + visible=False, + container=False, + ) + with gr.Column(): + chat_state = gr.State() + img_list = gr.State() + chatbot = gr.Chatbot(label="MLC Chat") + text_input = gr.Textbox( + show_label=False, + placeholder="Select a model to start chatting!", + interactive=False, + container=False, + ) + + # ---------------------- local variables --------------------------- + # type 1. buttons whose visibility change when llm reload + llm_buttons = [ + image_model, + stream_interval, + reset_llm_button, + stats_button, + stats_output, + text_input, + ] + # type 2. buttons whose visibility change when image model reload + # pylint: disable=unused-variable + image_model_buttons = [image, text_input] + # type 3. chatbot state variables + chatbot_vars = [chatbot, chat_state, img_list] + + # -------------------------- handle control -------------------------- + llm_model.change( + mod.gradio_reload_model, [llm_model], chatbot_vars + llm_buttons, queue=False + ) + text_input.submit(mod.gradio_ask, [text_input, chatbot], [text_input, chatbot]).then( + mod.gradio_answer, [chatbot, stream_interval], [chatbot] + ) + reset_llm_button.click(mod.gradio_reset_model, [], chatbot_vars + [text_input]) + stats_button.click(mod.gradio_stats, [], [stats_output]) + + # launch to the web + demo.launch(share=share, enable_queue=True, server_port=port, server_name=host) + + +if __name__ == "__main__": + ARGS = _parse_args() + launch_gradio(ARGS.artifact_path, ARGS.device, ARGS.port, ARGS.share, ARGS.host) diff --git a/python/mlc_chat/help.py b/python/mlc_chat/help.py new file mode 100644 index 0000000..0464bd0 --- /dev/null +++ b/python/mlc_chat/help.py @@ -0,0 +1,142 @@ +"""Help message for CLI arguments.""" +HELP = { + "config": ( + """ +1) Path to a HuggingFace model directory that contains a `config.json` or +2) Path to `config.json` in HuggingFace format, or +3) The name of a pre-defined model architecture. + +A `config.json` file in HuggingFace format defines the model architecture, including the vocabulary +size, the number of layers, the hidden size, number of attention heads, etc. +Example: https://huggingface.co/codellama/CodeLlama-7b-hf/blob/main/config.json. + +A HuggingFace directory often contains a `config.json` which defines the model architecture, +the non-quantized model weights in PyTorch or SafeTensor format, tokenizer configurations, +as well as an optional `generation_config.json` provides additional default configuration for +text generation. +Example: https://huggingface.co/codellama/CodeLlama-7b-hf/tree/main. +""" + ).strip(), + "quantization": """ +The quantization mode we use to compile. If unprovided, will infer from `model`. +""".strip(), + "model": """ +A path to ``mlc-chat-config.json``, or an MLC model directory that contains `mlc-chat-config.json`. +""".strip(), + "model_lib_path": """ +The full path to the model library file to use (e.g. a ``.so`` file). If unspecified, we will use +the provided ``model`` to search over possible paths. +""".strip(), + "model_type": """ +Model architecture such as "llama". If not set, it is inferred from `mlc-chat-config.json`. +""".strip(), + "device_compile": """ +The GPU device to compile the model to. If not set, it is inferred from GPUs available locally. +""".strip(), + "device_quantize": """ +The device used to do quantization such as "cuda" or "cuda:0". Will detect from local available GPUs +if not specified. +""".strip(), + "device_deploy": """ +The device used to deploy the model such as "cuda" or "cuda:0". Will detect from local +available GPUs if not specified. +""".strip(), + "host": """ +The host LLVM triple to compile the model to. If not set, it is inferred from the local CPU and OS. +Examples of the LLVM triple: +1) iPhones: arm64-apple-ios; +2) ARM64 Android phones: aarch64-linux-android; +3) WebAssembly: wasm32-unknown-unknown-wasm; +4) Windows: x86_64-pc-windows-msvc; +5) ARM macOS: arm64-apple-darwin. +""".strip(), + "opt": """ +Optimization flags. MLC LLM maintains a predefined set of optimization flags, +denoted as O0, O1, O2, O3, where O0 means no optimization, O2 means majority of them, +and O3 represents extreme optimization that could potentially break the system. +Meanwhile, optimization flags could be explicitly specified via details knobs, e.g. +--opt="cublas_gemm=1;cudagraph=0". +""".strip(), + "system_lib_prefix": """ +Adding a prefix to all symbols exported. Similar to "objcopy --prefix-symbols". +This is useful when compiling multiple models into a single library to avoid symbol +conflicts. Different from objcopy, this takes no effect for shared library. +""".strip(), + "context_window_size": """ +Option to provide the maximum sequence length supported by the model. +This is usually explicitly shown as context length or context window in the model card. +If this option is not set explicitly, by default, +it will be determined by `context_window_size` or `max_position_embeddings` in `config.json`, +and the latter is usually inaccurate for some models. +""".strip(), + "output_compile": """ +The path to the output file. The suffix determines if the output file is a shared library or +objects. Available suffixes: +1) Linux: .so (shared), .tar (objects); +2) macOS: .dylib (shared), .tar (objects); +3) Windows: .dll (shared), .tar (objects); +4) Android, iOS: .tar (objects); +5) Web: .wasm (web assembly). +""".strip(), + "source": """ +The path to original model weight, infer from `config` if missing. +""".strip(), + "source_format": """ +The format of source model weight, infer from `config` if missing. +""".strip(), + "output_quantize": """ +The output directory to save the quantized model weight. Will create `params_shard_*.bin` and +`ndarray-cache.json` in this directory. +""".strip(), + "conv_template": """ +Conversation template. It depends on how the model is tuned. Use "LM" for vanilla base model +""".strip(), + "output_gen_mlc_chat_config": """ +The output directory for generated configurations, including `mlc-chat-config.json` and tokenizer +configuration. +""".strip(), + "sliding_window_size": """ +(Experimental) The sliding window size in sliding window attention (SWA). +This optional field overrides the `sliding_window_size` in config.json for +those models that use SWA. Currently only useful when compiling Mistral. +This flag subjects to future refactoring. +""".strip(), + "prefill_chunk_size": """ +(Experimental) The chunk size during prefilling. By default, +the chunk size is the same as sliding window or max sequence length. +This flag subjects to future refactoring. +""".strip(), + "attention_sink_size": """ +(Experimental) The number of stored sinks. Only supported on Mistral yet. By default, +the number of sinks is 4. This flag subjects to future refactoring. +""".strip(), + "max_batch_size": """ +The maximum allowed batch size set for batch prefill/decode function. +""".strip(), + """tensor_parallel_shards""": """ +Number of shards to split the model into in tensor parallelism multi-gpu inference. +""".strip(), + "overrides": """ +Model configuration override. Configurations to override `mlc-chat-config.json`. Supports +`context_window_size`, `prefill_chunk_size`, `sliding_window_size`, `attention_sink_size`, +`max_batch_size` and `tensor_parallel_shards`. Meanwhile, model config could be explicitly +specified via details knobs, e.g. --overrides "context_window_size=1024;prefill_chunk_size=128". +""".strip(), + "chatconfig_overrides": """ +Chat configuration override. Configurations to override ChatConfig. Supports `conv_template`, +`context_window_size`, `prefill_chunk_size`, `sliding_window_size`, `attention_sink_size`, +`max_batch_size` and `tensor_parallel_shards`. Meanwhile, model chat could be explicitly +specified via details knobs, e.g. --overrides "context_window_size=1024;prefill_chunk_size=128". +""".strip(), + "debug_dump": """ +Specifies the directory where the compiler will store its IRs for debugging purposes +during various phases of compilation. By default, this is set to `None`, indicating +that debug dumping is disabled. +""".strip(), + "prompt": """ +The prompt of the text generation. +""".strip(), + "generate_length": """ +The target length of the text generation. +""".strip(), +} diff --git a/python/mlc_chat/interface/__init__.py b/python/mlc_chat/interface/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/mlc_chat/interface/bench.py b/python/mlc_chat/interface/bench.py new file mode 100644 index 0000000..a1d4e27 --- /dev/null +++ b/python/mlc_chat/interface/bench.py @@ -0,0 +1,28 @@ +"""Python entrypoint of benchmark.""" +from typing import Optional + +from mlc_chat.chat_module import ChatConfig, ChatModule + +from .chat import ChatConfigOverride + + +def bench( # pylint: disable=too-many-arguments + model: str, + prompt: str, + device: str, + opt: str, + overrides: ChatConfigOverride, + generate_length: int, + model_lib_path: Optional[str], +): + """run the benchmarking""" + # Set up chat config + config = ChatConfig(opt=opt) + # Apply overrides + config = overrides.apply(config) + # Set up ChatModule + cm = ChatModule(model, device, chat_config=config, model_lib_path=model_lib_path) + + output = cm.benchmark_generate(prompt, generate_length=generate_length) + print(f"Generated text:\n{output}\n") + print(f"Statistics:\n{cm.stats(verbose=True)}") diff --git a/python/mlc_chat/interface/chat.py b/python/mlc_chat/interface/chat.py new file mode 100644 index 0000000..0df8bb1 --- /dev/null +++ b/python/mlc_chat/interface/chat.py @@ -0,0 +1,196 @@ +"""Python entrypoint of chat.""" +import dataclasses +import re +import json +from typing import List, Optional, Union + +from prompt_toolkit import prompt as get_prompt # pylint: disable=import-error +from prompt_toolkit.key_binding import KeyBindings # pylint: disable=import-error + +from mlc_chat.callback import StreamToStdout +from mlc_chat.chat_module import ChatConfig, ChatModule, GenerationConfig +from mlc_chat.support import argparse +from mlc_chat.support.config import ConfigOverrideBase + + +@dataclasses.dataclass +class ChatConfigOverride(ConfigOverrideBase): # pylint: disable=too-many-instance-attributes + """Flags for overriding chat config.""" + + conv_template: Optional[str] = None + context_window_size: Optional[int] = None + sliding_window_size: Optional[int] = None + prefill_chunk_size: Optional[int] = None + attention_sink_size: Optional[int] = None + max_batch_size: Optional[int] = None + tensor_parallel_shards: Optional[int] = None + + @staticmethod + def from_str(source: str) -> "ChatConfigOverride": + """Parse model config override values from a string.""" + parser = argparse.ArgumentParser(description="chat config override values") + parser.add_argument("--conv_template", type=str, default=None) + parser.add_argument("--tensor_parallel_shards", type=int, default=None) + parser.add_argument("--context_window_size", type=int, default=None) + parser.add_argument("--sliding_window_size", type=int, default=None) + parser.add_argument("--prefill_chunk_size", type=int, default=None) + parser.add_argument("--attention_sink_size", type=int, default=None) + parser.add_argument("--max_batch_size", type=int, default=None) + + results = parser.parse_args([f"--{i}" for i in source.split(";") if i]) + return ChatConfigOverride( + conv_template=results.conv_template, + tensor_parallel_shards=results.tensor_parallel_shards, + context_window_size=results.context_window_size, + sliding_window_size=results.sliding_window_size, + prefill_chunk_size=results.prefill_chunk_size, + attention_sink_size=results.attention_sink_size, + max_batch_size=results.max_batch_size, + ) + + +@dataclasses.dataclass +class GenerationConfigOverride(ConfigOverrideBase): # pylint: disable=too-many-instance-attributes + """Flags for overriding generation config.""" + + temperature: Optional[float] = None + repetition_penalty: Optional[float] = None + top_p: Optional[float] = None + mean_gen_len: Optional[int] = None + max_gen_len: Optional[int] = None + presence_penalty: Optional[float] = None + frequency_penalty: Optional[float] = None + n: Optional[int] = None # pylint: disable=invalid-name + stop: Optional[Union[str, List[str]]] = None + + @staticmethod + def from_str(source: str) -> "GenerationConfigOverride": + """Parse model config override values from a string.""" + parser = argparse.ArgumentParser(description="generation config override values") + parser.add_argument("--temperature", type=float, default=None) + parser.add_argument("--repetition_penalty", type=float, default=None) + parser.add_argument("--top_p", type=float, default=None) + parser.add_argument("--mean_gen_len", type=int, default=None) + parser.add_argument("--max_gen_len", type=int, default=None) + parser.add_argument("--presence_penalty", type=float, default=None) + parser.add_argument("--frequency_penalty", type=float, default=None) + parser.add_argument("--n", type=int, default=None) + parser.add_argument("--stop", type=str, default=None) + results = parser.parse_args([f"--{i}" for i in source.split(";") if i]) + return GenerationConfigOverride( + temperature=results.temperature, + repetition_penalty=results.repetition_penalty, + top_p=results.top_p, + mean_gen_len=results.mean_gen_len, + max_gen_len=results.max_gen_len, + presence_penalty=results.presence_penalty, + frequency_penalty=results.frequency_penalty, + n=results.n, + stop=results.stop.split(",") if results.stop is not None else None, + ) + + +def _print_help_str(): + help_str = """You can use the following special commands: + /help print the special commands + /exit quit the cli + /stats print out the latest stats (token/sec) + /reset restart a fresh chat + /set [overrides] override settings in the generation config. For example, + `/set temperature=0.5;max_gen_len=100;stop=end,stop` + Note: Separate stop words in the `stop` option with commas (,). + Multi-line input: Use escape+enter to start a new line. +""" + print(help_str) + + +def _set_up_key_bindings(): + kb = KeyBindings() + + @kb.add("escape", "enter") + def _(event): + event.current_buffer.insert_text("\n") + + @kb.add("enter") + def _(event): + event.current_buffer.validate_and_handle() + + return kb + + +def chat( + model: str, + device: str, + opt: str, + overrides: ChatConfigOverride, + model_lib_path: Optional[str], + energy_events_filename: str, +): + """chat with a model.""" + # Set up chat config and generate config + config = ChatConfig(opt=opt) + generate_config = GenerationConfig() + # Apply overrides + config = overrides.apply(config) + # Set up ChatModule + cm = ChatModule(model, device, chat_config=config, model_lib_path=model_lib_path) + _print_help_str() + cm._process_system_prompts() # pylint: disable=protected-access + + # Multi-line input support: set escape+enter as start a new line + kb = _set_up_key_bindings() + + while True: + prompt = get_prompt( + f"{cm._get_role_0()}: ", # pylint: disable=protected-access + key_bindings=kb, + multiline=True, + ) + if prompt[:6] == "/reset": + cm.reset_chat() + elif prompt[:5] == "/exit": + with open(energy_events_filename, 'w', encoding='utf-8') as f: + for event_key, event_value in cm.energy_events.items(): + f.write(f"{event_key} {event_value}\n") + break + elif prompt[:6] == "/stats": + # print(cm.stats(verbose=True), flush=True) + # ----------- prefill ----------- + # throughput: 87.899 tok/s + # total tokens: 10 tok + # total time: 0.114 s + # ------------ decode ------------ + # throughput: 54.603 tok/s + # total tokens: 18 tok + # total time: 0.330 s + # Parse the above metrics into json format + stats = cm.stats(verbose=True) + if stats.startswith("{"): # This is already handled by the backend + print(stats, flush=True) + else: # This is in case the backend has not been changed + stats = stats.strip().split("\n") + float_re = re.compile(r"\d+\.\d+") + int_re = re.compile(r"\d+") + stats_dict = {} + try: + for i in range(0, len(stats), 4): + stats_dict[stats[i].strip('-').strip()] = { + "throughput": f"{float(re.findall(float_re, stats[i + 1])[0])} tok/s", + "total_tokens": f"{int(re.findall(int_re, stats[i + 2])[0])} tok", + "total_time": f"{float(re.findall(float_re, stats[i + 3])[0])} s", + } + print(json.dumps(stats_dict, indent=4), flush=True) + except IndexError: + print(stats, flush=True) + elif prompt[:4] == "/set": + gen_config_overrides = GenerationConfigOverride.from_str(prompt.split()[1]) + generate_config = gen_config_overrides.apply(generate_config) + elif prompt[:5] == "/help": + _print_help_str() + else: + print(f"{cm._get_role_1()}: ") # pylint: disable=protected-access + cm.generate( + prompt, + progress_callback=StreamToStdout(callback_interval=2), + generation_config=generate_config, + ) diff --git a/python/mlc_chat/interface/compile.py b/python/mlc_chat/interface/compile.py new file mode 100644 index 0000000..7688715 --- /dev/null +++ b/python/mlc_chat/interface/compile.py @@ -0,0 +1,230 @@ +"""Python entrypoint of compilation.""" +import dataclasses +import math +from io import StringIO +from pathlib import Path +from typing import Any, Callable, Dict, List, Optional, Tuple + +import numpy as np +from tvm import IRModule, relax, tir +from tvm.ir.transform import Pass, PassContext +from tvm.relax.frontend import nn +from tvm.target import Target + +from mlc_chat import compiler_pass as _ +from mlc_chat import op as op_ext +from mlc_chat.cli.model_metadata import _report_memory_usage +from mlc_chat.model import Model +from mlc_chat.quantization import Quantization +from mlc_chat.support import logging +from mlc_chat.support.config import ConfigBase +from mlc_chat.support.style import bold + +from .compiler_flags import ModelConfigOverride, OptimizationFlags + +logger = logging.getLogger(__name__) + + +@dataclasses.dataclass +class CompileArgs: # pylint: disable=too-many-instance-attributes + """Arguments to MLC LLM's compiler.""" + + config: Path + quantization: Quantization + model: Model + target: Target + opt: OptimizationFlags + build_func: Callable[[IRModule, "CompileArgs", Pass], None] + system_lib_prefix: str + output: Path + overrides: ModelConfigOverride + debug_dump: Optional[Path] + + def __post_init__(self) -> None: + self.opt.update(self.target, self.quantization) + + def display(self) -> None: + """Display the arguments to stdout.""" + out = StringIO() + print(f"{bold('Compiling with arguments:')}", file=out) + print(f" {bold('--config'):<25} {self.config}", file=out) + print(f" {bold('--quantization'):<25} {self.quantization}", file=out) + print(f" {bold('--model-type'):<25} {self.model.name}", file=out) + print(f" {bold('--target'):<25} {self.target.export()}", file=out) + print(f" {bold('--opt'):<25} {self.opt}", file=out) + print(f" {bold('--system-lib-prefix'):<25} \"{self.system_lib_prefix}\"", file=out) + print(f" {bold('--output'):<25} {self.output}", file=out) + print(f" {bold('--overrides'):<25} {self.overrides}", file=out) + # As it's debug only, no need to display + # print(f" {bold('--debug-dump'):<25} {self.debug_dump}", file=out) + print(out.getvalue().rstrip()) + + +def _apply_preproc_to_params( + named_params: List[Tuple[str, nn.Parameter]], + model_config, +) -> Dict[str, tir.PrimFunc]: + extra_tirs: Dict[str, tir.PrimFunc] = {} + for _, param in named_params: + preprocs = param.attrs.get("preprocs", []) + shard_strategy = param.attrs.get("shard_strategy", None) + if shard_strategy is not None and model_config.tensor_parallel_shards > 1: + preprocs.append( + shard_strategy.gen_shard_info( + shards=model_config.tensor_parallel_shards, + weight=param, + ) + ) + if shard_strategy.name not in extra_tirs: + extra_tirs[shard_strategy.name] = shard_strategy.gen_tir( + shards=model_config.tensor_parallel_shards, + weight=param, + ) + param.attrs["preprocs"] = preprocs + return extra_tirs + + +def _compile(args: CompileArgs, model_config: ConfigBase): + def _get_variable_bounds(model_config) -> Dict[str, int]: + if hasattr(model_config, "sliding_window_size"): + return { + "rolling_cache_len": model_config.sliding_window_size, + "kv_seq_len": model_config.sliding_window_size + model_config.prefill_chunk_size, + "seq_len": model_config.prefill_chunk_size, + "batch_size": getattr(model_config, "max_batch_size", 1), + } + return { + "total_seq_len": model_config.context_window_size, + "seq_len": model_config.prefill_chunk_size, + "batch_size": getattr(model_config, "max_batch_size", 1), + } + + def _get_param_metadata(name: str, param: nn.Parameter) -> Dict[str, Any]: + return { + "name": name, + # Record dynamic shape as -1 (e.g. vocab_size) + "shape": [s if isinstance(s, int) else s.name for s in param.shape], + "dtype": param.dtype, + "preprocs": param.attrs["preprocs"], + } + + def _find_kv_cache_bytes(model: nn.Module, model_config) -> int: + all_kv_cache = nn.core._attribute_finder( # pylint: disable=protected-access + model, + prefix="", + condition_yield=lambda x: isinstance(x, nn.KVCache), + ) + result = 0 + for _, kv_cache in all_kv_cache: + result += math.prod(kv_cache.unit_shape) * np.dtype(kv_cache.dtype).itemsize + if getattr(model_config, "sliding_window_size", -1) > 0: + window_size = model_config.sliding_window_size + elif getattr(model_config, "context_window_size", -1) > 0: + window_size = model_config.context_window_size + else: + window_size = 0 + return result * window_size + + model_config = args.overrides.apply(model_config) + with args.target: + op_ext.enable( + target=args.target, + flashinfer=args.opt.flashinfer, + faster_transformer=args.opt.faster_transformer, + ) + # Step 1. Create the quantized model + logger.info("Creating model from: %s", args.config) + if ( + args.quantization.kind == "ft-quant" + and hasattr(model_config, "tensor_parallel_shards") + and model_config.tensor_parallel_shards > 1 + ): + raise NotImplementedError + if ( + hasattr(args.quantization, "linear_weight_layout") + and args.quantization.linear_weight_layout == "KN" + and hasattr(model_config, "tensor_parallel_shards") + and model_config.tensor_parallel_shards > 1 + ): + raise NotImplementedError( + "KN layout (q3f16_0 and q4f16_0) is not supported for tensor parallelism" + ) + model, _ = args.model.quantize[args.quantization.kind](model_config, args.quantization) + kv_cache_bytes = _find_kv_cache_bytes(model, model_config) + # Step 2. Exporting the model to TVM Unity + logger.info("Exporting the model to TVM Unity compiler") + mod, named_params, ext_mods = model.export_tvm( + spec=model.get_default_spec(), # type: ignore + allow_extern=True, + ) + # Step 3. Running relax compilation pipeline + logger.info("Running optimizations using TVM Unity") + additional_tirs = _apply_preproc_to_params(named_params, model_config) + variable_bounds = _get_variable_bounds(model_config) + metadata = { + "model_type": args.model.name, + "quantization": args.quantization.name, + "context_window_size": getattr(model_config, "context_window_size", -1), + "sliding_window_size": getattr(model_config, "sliding_window_size", -1), + "attention_sink_size": getattr(model_config, "attention_sink_size", -1), + "prefill_chunk_size": model_config.prefill_chunk_size, # type: ignore + "tensor_parallel_shards": model_config.tensor_parallel_shards, # type: ignore + "kv_cache_bytes": kv_cache_bytes, + } + logger.info("Registering metadata: %s", metadata) + metadata["params"] = [_get_param_metadata(name, param) for name, param in named_params] + with PassContext(config={"relax.backend.use_cuda_graph": args.opt.cudagraph}): + args.build_func( + mod, + args, + pipeline=relax.get_pipeline( # type: ignore + "mlc_llm", + target=args.target, + flashinfer=args.opt.flashinfer, + cublas_gemm=args.opt.cublas_gemm, + faster_transformer=args.opt.faster_transformer, + variable_bounds=variable_bounds, + additional_tirs=additional_tirs, + ext_mods=ext_mods, + metadata=metadata, + debug_dump=args.debug_dump, + ), + ) + _report_memory_usage(metadata=metadata, config=model_config) + logger.info("Generated: %s", bold(str(args.output))) + + +def compile( # pylint: disable=too-many-arguments,redefined-builtin + config: Dict[str, Any], + quantization: Quantization, + model_type: Model, + target: Target, + opt: OptimizationFlags, + build_func: Callable[[IRModule, CompileArgs, Pass], None], + system_lib_prefix: str, + output: Path, + overrides: ModelConfigOverride, + debug_dump: Optional[Path] = None, +): + """Compile a model given its configuration and quantization format to a specific target.""" + if "model_config" in config: + model_config = config.pop("model_config") + model_config.update(config) + model_config = model_type.config.from_dict(model_config) + else: + model_config = model_type.config.from_dict(config) + model_config.kwargs = {} + args = CompileArgs( + model_config, + quantization, + model_type, + target, + opt, + build_func, + system_lib_prefix, + output, + overrides, + debug_dump, + ) + args.display() + _compile(args, model_config) diff --git a/python/mlc_chat/interface/compiler_flags.py b/python/mlc_chat/interface/compiler_flags.py new file mode 100644 index 0000000..7eeedaf --- /dev/null +++ b/python/mlc_chat/interface/compiler_flags.py @@ -0,0 +1,158 @@ +"""Flags for overriding model config.""" +import dataclasses +from io import StringIO +from typing import Optional + +from mlc_chat.support import argparse, logging +from mlc_chat.support.config import ConfigOverrideBase + +logger = logging.getLogger(__name__) + + +@dataclasses.dataclass +class OptimizationFlags: + """Optimization flags""" + + flashinfer: bool = False + cublas_gemm: bool = False + faster_transformer: bool = False + cudagraph: bool = False + + def __repr__(self) -> str: + out = StringIO() + print(f"flashinfer={int(self.flashinfer)}", file=out, end="") + print(f";cublas_gemm={int(self.cublas_gemm)}", file=out, end="") + print(f";faster_transformer={int(self.faster_transformer)}", file=out, end="") + print(f";cudagraph={int(self.cudagraph)}", file=out, end="") + return out.getvalue().rstrip() + + @staticmethod + def from_str(source: str) -> "OptimizationFlags": + """Parse optimization flags from a string.""" + + if source in OPT_FLAG_PRESET: + return OPT_FLAG_PRESET[source] + + def boolean(value: str) -> bool: + if value == "0": + return False + if value == "1": + return True + raise ValueError(f"Invalid boolean value: {value}") + + parser = argparse.ArgumentParser(description="optimization flags") + parser.add_argument("--flashinfer", type=boolean, default=True) + parser.add_argument("--cublas_gemm", type=boolean, default=False) + parser.add_argument("--faster_transformer", type=boolean, default=False) + parser.add_argument("--cudagraph", type=boolean, default=False) + results = parser.parse_args([f"--{i}" for i in source.split(";") if i]) + return OptimizationFlags( + flashinfer=results.flashinfer, + cublas_gemm=results.cublas_gemm, + faster_transformer=results.faster_transformer, + cudagraph=results.cudagraph, + ) + + def update(self, target, quantization) -> None: + """Update optimization flags based on additional information.""" + + def _flashinfer(target) -> bool: + from mlc_chat.support.auto_target import ( # pylint: disable=import-outside-toplevel + detect_cuda_arch_list, + ) + + if not self.flashinfer: + return False + if target.kind.name != "cuda": + return False + arch_list = detect_cuda_arch_list(target) + for arch in arch_list: + if arch < 80: + logger.warning("flashinfer is not supported on CUDA arch < 80") + return False + return True + + def _cublas_gemm(target, quantization) -> bool: + """correct cublas_gemm flag""" + if not (target.kind.name == "cuda" and quantization.name in ["q0f16", "q0f32"]): + return False + return self.cublas_gemm + + def _faster_transformer(target) -> bool: + """correct faster_transformer flag""" + if not target.kind.name == "cuda": + return False + return self.faster_transformer + + self.flashinfer = _flashinfer(target) + self.cublas_gemm = _cublas_gemm(target, quantization) + self.faster_transformer = _faster_transformer(target) + + +@dataclasses.dataclass +class ModelConfigOverride(ConfigOverrideBase): + """Flags for overriding model config.""" + + context_window_size: Optional[int] = None + sliding_window_size: Optional[int] = None + prefill_chunk_size: Optional[int] = None + attention_sink_size: Optional[int] = None + max_batch_size: Optional[int] = None + tensor_parallel_shards: Optional[int] = None + + def __repr__(self) -> str: + out = StringIO() + print(f"context_window_size={self.context_window_size}", file=out, end="") + print(f";sliding_window_size={self.sliding_window_size}", file=out, end="") + print(f";prefill_chunk_size={self.prefill_chunk_size}", file=out, end="") + print(f";attention_sink_size={self.attention_sink_size}", file=out, end="") + print(f";max_batch_size={self.max_batch_size}", file=out, end="") + print(f";tensor_parallel_shards={self.tensor_parallel_shards}", file=out, end="") + return out.getvalue().rstrip() + + @staticmethod + def from_str(source: str) -> "ModelConfigOverride": + """Parse model config override values from a string.""" + parser = argparse.ArgumentParser(description="model config override values") + parser.add_argument("--context_window_size", type=int, default=None) + parser.add_argument("--sliding_window_size", type=int, default=None) + parser.add_argument("--prefill_chunk_size", type=int, default=None) + parser.add_argument("--attention_sink_size", type=int, default=None) + parser.add_argument("--max_batch_size", type=int, default=None) + parser.add_argument("--tensor_parallel_shards", type=int, default=None) + results = parser.parse_args([f"--{i}" for i in source.split(";") if i]) + return ModelConfigOverride( + context_window_size=results.context_window_size, + sliding_window_size=results.sliding_window_size, + prefill_chunk_size=results.prefill_chunk_size, + attention_sink_size=results.attention_sink_size, + max_batch_size=results.max_batch_size, + tensor_parallel_shards=results.tensor_parallel_shards, + ) + + +OPT_FLAG_PRESET = { + "O0": OptimizationFlags( + flashinfer=False, + cublas_gemm=False, + cudagraph=False, + ), + "O1": OptimizationFlags( + flashinfer=False, + cublas_gemm=True, + faster_transformer=True, + cudagraph=False, + ), + "O2": OptimizationFlags( + flashinfer=True, + cublas_gemm=True, + faster_transformer=True, + cudagraph=False, + ), + "O3": OptimizationFlags( + flashinfer=True, + cublas_gemm=True, + faster_transformer=True, + cudagraph=True, + ), +} diff --git a/python/mlc_chat/interface/convert_weight.py b/python/mlc_chat/interface/convert_weight.py new file mode 100644 index 0000000..1e28417 --- /dev/null +++ b/python/mlc_chat/interface/convert_weight.py @@ -0,0 +1,169 @@ +"""Python entrypoint of weight conversion.""" + +import dataclasses +import math +import os +from io import StringIO +from pathlib import Path + +import numpy as np +from tvm import tir +from tvm.contrib import tvmjs +from tvm.runtime import Device, NDArray +from tvm.runtime import cpu as cpu_device +from tvm.target import Target + +from mlc_chat.loader import LOADER +from mlc_chat.model import Model +from mlc_chat.quantization import Quantization +from mlc_chat.support import logging, tqdm +from mlc_chat.support.preshard import apply_preshard +from mlc_chat.support.style import bold, green + +logger = logging.getLogger(__name__) + + +@dataclasses.dataclass +class ConversionArgs: # pylint: disable=too-many-instance-attributes + """Arguments to MLC LLM's weight conversation and quantization flow.""" + + config: Path + quantization: Quantization + model: Model + device: Device + source: Path + source_format: str + output: Path + + def display(self) -> None: + """Display the arguments to stdout.""" + + def _device_to_str(device: Device) -> str: + return f"{Device.MASK2STR[device.device_type]}:{device.device_id}" + + out = StringIO() + print(f"{bold('Weight conversion with arguments:')}", file=out) + print(f" {bold('--config'):<25} {self.config}", file=out) + print(f" {bold('--quantization'):<25} {self.quantization}", file=out) + print(f" {bold('--model-type'):<25} {self.model.name}", file=out) + print(f" {bold('--device'):<25} {_device_to_str(self.device)}", file=out) + print(f" {bold('--source'):<25} {self.source}", file=out) + print(f" {bold('--source-format'):<25} {self.source_format}", file=out) + print(f" {bold('--output'):<25} {self.output}", file=out) + print(out.getvalue().rstrip()) + + +def _convert_args(args: ConversionArgs) -> None: # pylint: disable=too-many-locals + pre_shards_num = os.getenv("MLC_INTERNAL_PRESHARD_NUM") + # model config & quantization config + model_config = args.model.config.from_file(args.config) + if ( + args.quantization.kind == "ft-quant" + and hasattr(model_config, "tensor_parallel_shards") + and model_config.tensor_parallel_shards > 1 + ): + raise NotImplementedError + if pre_shards_num is not None: + model_config.tensor_parallel_shards = int(pre_shards_num) + model, quantize_map = args.model.quantize[args.quantization.kind]( + model_config, args.quantization + ) + _, _named_params, _ = model.export_tvm( # type: ignore[misc] + spec=model.get_default_spec(), # type: ignore[attr-defined] + allow_extern=True, + ) + named_params = dict(_named_params) + + if pre_shards_num is not None: + preshard_funcs = apply_preshard(quantize_map, named_params, int(pre_shards_num), args) + else: + preshard_funcs = None + + def _check_param(name: str, param: NDArray): + nonlocal named_params + if name not in named_params: + raise ValueError(f"Parameter not found in model: {name}") + if name in param_dict: + raise ValueError(f"Duplication: Parameter {name} already computed") + + # Check shape (possibly dynamic) + def _check_shape(actual: tuple, expect: tuple): # expect can have tir.Var + if len(actual) != len(expect): + return False + for actual_i, expect_i in zip(actual, expect): + assert isinstance(expect_i, (int, tir.Var)) + if isinstance(expect_i, int) and actual_i != expect_i: + return False + return True + + expect_shape = named_params[name].shape + actual_shape = param.shape + if not _check_shape(actual_shape, expect_shape): + raise ValueError( + f"Parameter {name} has shape {param.shape}, but expected {expect_shape}" + ) + # Check dtype + actual_dtype = param.dtype + expect_dtype = named_params[name].dtype + if actual_dtype != expect_dtype: + raise ValueError( + f"Parameter {name} has dtype {param.dtype}, but expected {expect_dtype}" + ) + del named_params[name] + + # load and quantize + param_dict = {} + total_bytes = 0.0 + with Target.from_device(args.device), tqdm.redirect(): + loader = LOADER[args.source_format]( + path=args.source, + extern_param_map=args.model.source[args.source_format](model_config, args.quantization), + quantize_param_map=quantize_map, + ) + for name, param in loader.load(device=args.device, preshard_funcs=preshard_funcs): + _check_param(name, param) + param = param.copyto(cpu_device()) + param_dict[name] = param + total_bytes += math.prod(param.shape) * np.dtype(param.dtype).itemsize + total_params = loader.stats.total_param_num + if named_params: + raise ValueError(f"Parameter not found in source: {', '.join(named_params.keys())}") + # Log necessary statistics + logger.info( + "%s after quantization: %.3f GB", + green("Parameter size"), + total_bytes / (1024**3), + ) + logger.info(f"%s: {total_params:,}", green("Total parameters")) + logger.info( + "%s: %.3f", + green("Bits per parameter"), + total_bytes * 8.0 / total_params, + ) + # dump to output directory + tvmjs.dump_ndarray_cache( + param_dict, + str(args.output), + meta_data={ + "ParamSize": len(param_dict), + "ParamBytes": total_bytes, + "BitsPerParam": total_bytes * 8.0 / total_params, + }, + encode_format="f32-to-bf16", + ) + logger.info("Saved to directory: %s", bold(str(args.output))) + + +def convert_weight( # pylint: disable=too-many-arguments + config: Path, + quantization: Quantization, + model: Model, + device: Device, + source: Path, + source_format: str, + output: Path, +): + """MLC LLM's weight conversation and quantization flow.""" + args = ConversionArgs(config, quantization, model, device, source, source_format, output) + args.display() + _convert_args(args) diff --git a/python/mlc_chat/interface/gen_config.py b/python/mlc_chat/interface/gen_config.py new file mode 100644 index 0000000..35592db --- /dev/null +++ b/python/mlc_chat/interface/gen_config.py @@ -0,0 +1,232 @@ +"""Generator of mlc-chat-config.json and tokenizer configuration.""" + +import dataclasses +import json +import shutil +from pathlib import Path +from typing import Any, Dict, List, Optional + +from mlc_chat.model import Model +from mlc_chat.quantization import Quantization +from mlc_chat.support import convert_tiktoken, logging +from mlc_chat.support.style import bold, green, red + +from .compiler_flags import ModelConfigOverride + +logger = logging.getLogger(__name__) + +FOUND = green("Found") +NOT_FOUND = red("Not found") +FAILED = red("Failed") +VERSION = "0.1.0" + + +@dataclasses.dataclass +class MLCChatConfig: # pylint: disable=too-many-instance-attributes + """Fields in the dumped `mlc-chat-config.json` file.""" + + model_type: str + quantization: str + model_config: Dict[str, Any] + vocab_size: int + context_window_size: int + sliding_window_size: int + prefill_chunk_size: int + attention_sink_size: int + tensor_parallel_shards: int + # Control the behavior of the runtime + mean_gen_len: int = None + max_gen_len: int = None + shift_fill_factor: float = None + # Configuration of text generation + temperature: float = None + presence_penalty: float = None + frequency_penalty: float = None + repetition_penalty: float = None + top_p: float = None + # Conversation template + conv_template: str = None + pad_token_id: int = None + bos_token_id: int = None + eos_token_id: int = None + tokenizer_files: List[str] = dataclasses.field(default_factory=list) + # Version control + version: str = VERSION + + def apply_defaults(self) -> None: + """Apply system default value.""" + defaults = { + "pad_token_id": 0, + "bos_token_id": 1, + "eos_token_id": 2, + "temperature": 0.7, + "presence_penalty": 0.0, + "frequency_penalty": 0.0, + "repetition_penalty": 1.0, + "top_p": 0.95, + "mean_gen_len": 128, + "max_gen_len": 512, + "shift_fill_factor": 0.3, + } + for key, value in defaults.items(): + if getattr(self, key) is None: + setattr(self, key, value) + logger.info("[System default] Setting %s: %s", bold(key), value) + + +def gen_config( # pylint: disable=too-many-locals,too-many-arguments,too-many-branches,too-many-statements + config: Path, + model: Model, + quantization: Quantization, + conv_template: str, + context_window_size: Optional[int], + sliding_window_size: Optional[int], + prefill_chunk_size: Optional[int], + attention_sink_size: Optional[int], + tensor_parallel_shards: Optional[int], + max_batch_size: int, + output: Path, +): + """Entrypoint of MLC Chat configuration generation.""" + # Step 1. Initialize `mlc-chat-config.json` using `config.json` + model_config = ModelConfigOverride( + context_window_size=context_window_size, + sliding_window_size=sliding_window_size, + prefill_chunk_size=prefill_chunk_size, + attention_sink_size=attention_sink_size, + max_batch_size=max_batch_size, + tensor_parallel_shards=tensor_parallel_shards, + ).apply(model.config.from_file(config)) + mlc_chat_config = MLCChatConfig( + model_type=model.name, + quantization=quantization.name, + model_config=model_config.asdict(), + vocab_size=model_config.vocab_size, + context_window_size=getattr(model_config, "context_window_size", -1), + sliding_window_size=getattr(model_config, "sliding_window_size", -1), + prefill_chunk_size=model_config.prefill_chunk_size, + attention_sink_size=getattr(model_config, "attention_sink_size", -1), + tensor_parallel_shards=model_config.tensor_parallel_shards, + conv_template=conv_template, + ) + # Step 2. Load `generation_config.json` and `config.json` for text-generation related configs + for generation_config_filename in ["generation_config.json", "config.json"]: + generation_config = config.parent / generation_config_filename + if generation_config.exists(): + with generation_config.open("r", encoding="utf-8") as in_file: + generation_config_json = json.load(in_file) + for key, value in generation_config_json.items(): + if hasattr(mlc_chat_config, key) and getattr(mlc_chat_config, key) is None: + setattr(mlc_chat_config, key, value) + logger.info("[%s] Setting %s: %s", generation_config_filename, bold(key), value) + else: + logger.info("%s %s: %s", NOT_FOUND, generation_config_filename, generation_config) + + # Step 3. Copy tokenizer configuration + # 3.1. Copy over the files and populate mlc_chat_config + for filename in TOKENIZER_FILES: + file = config.parent / filename + if file.exists(): + mlc_chat_config.tokenizer_files.append(filename) + dest = output / filename + shutil.copy(file, dest) + logger.info("%s tokenizer config: %s. Copying to %s", FOUND, file, bold(str(dest))) + else: + logger.info("%s tokenizer config: %s", NOT_FOUND, file) + # 3.2. If we have `tokenizer.model` but not `tokenizer.json`, try convert it to + # `tokenizer.json` with `transformers`. + tokenizer_json_file = config.parent / "tokenizer.json" + tokenizer_model_file = config.parent / "tokenizer.model" + if tokenizer_model_file.exists() and (not tokenizer_json_file.exists()): + logger.info( + "The model has `tokenizer.model` but not `tokenizer.json`. " + "It is always recommended to prefer JSON instead. " + "Attempting to convert using HuggingFace transformers library" + ) + try: + from transformers import ( # pylint: disable=import-error,import-outside-toplevel + AutoTokenizer, + ) + + tokenizer_json_save_dest = output / "tokenizer.json" + fast_tokenizer = AutoTokenizer.from_pretrained(str(config.parent), use_fast=True) + fast_tokenizer.backend_tokenizer.save(str(tokenizer_json_save_dest)) + mlc_chat_config.tokenizer_files.append("tokenizer.json") + logger.info("Succesfully converted `tokenizer.model` to: %s", tokenizer_json_save_dest) + except Exception: # pylint: disable=broad-exception-caught + logger.warning( + "Convertion to `tokenizer.json` %s with the exception below. " + "Skipping the conversion. Tokenizer will only use `tokenizer.model`", + FAILED, + exc_info=True, + ) + # 3.3. If we still don't have "tokenizer.json" at this point, try looking for "*.tiktoken" files + if (not tokenizer_json_file.exists()) and list(config.parent.glob("*.tiktoken")): + try: + logger.info( + "The model has tiktoken files but not `tokenizer.json`. " + "Attempting to convert from tiktoken files" + ) + convert_tiktoken.convert_tiktoken( + str(config.parent), str(output), mlc_chat_config.context_window_size + ) + mlc_chat_config.tokenizer_files.append("tokenizer.json") + mlc_chat_config.tokenizer_files.append("vocab.json") + mlc_chat_config.tokenizer_files.append("merges.txt") + mlc_chat_config.tokenizer_files.append("special_tokens_map.json") + logger.info("Succesfully converted from tiktoken files to: %s", str(output)) + except Exception: # pylint: disable=broad-exception-caught + logger.exception("%s with the exception below. Skipping", FAILED) + + # Step 4. Load system default value + mlc_chat_config.apply_defaults() + # Step 5. Dump the configuration file to output directory + with (output / "mlc-chat-config.json").open("w", encoding="utf-8") as out_file: + json.dump(dataclasses.asdict(mlc_chat_config), out_file, indent=2) + logger.info("Dumping configuration file to: %s", bold(out_file.name)) + + +TOKENIZER_FILES = [ + "tokenizer.model", + "tokenizer.json", + "vocab.json", + "merges.txt", + "added_tokens.json", + "tokenizer_config.json", +] + +CONV_TEMPLATES = { + "chatml", + "open_hermes_mistral", + "neural_hermes_mistral", + "llama_default", + "llama-2", + "mistral_default", + "gpt2", + "codellama_completion", + "codellama_instruct", + "vicuna_v1.1", + "conv_one_shot", + "redpajama_chat", + "rwkv_world", + "rwkv", + "gorilla", + "guanaco", + "dolly", + "oasst", + "stablelm", + "stablecode_completion", + "stablecode_instruct", + "minigpt", + "moss", + "LM", + "stablelm-3b", + "gpt_bigcode", + "wizardlm_7b", + "wizard_coder_or_math", + "glm", + "custom", # for web-llm only + "phi-2", + "stablelm-2", + "gemma_instruction", +} diff --git a/python/mlc_chat/interface/jit.py b/python/mlc_chat/interface/jit.py new file mode 100644 index 0000000..6d9b131 --- /dev/null +++ b/python/mlc_chat/interface/jit.py @@ -0,0 +1,128 @@ +"""Just-in-time compilation of MLC-Chat models.""" +import dataclasses +import hashlib +import json +import os +import shlex +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path +from typing import Any, Dict + +from tvm.runtime import Device + +from mlc_chat.model import MODELS +from mlc_chat.support import logging +from mlc_chat.support.auto_device import device2str +from mlc_chat.support.constants import ( + MLC_CACHE_DIR, + MLC_DSO_SUFFIX, + MLC_JIT_POLICY, + MLC_TEMP_DIR, +) +from mlc_chat.support.style import blue, bold + +from .compiler_flags import ModelConfigOverride, OptimizationFlags + +logger = logging.getLogger(__name__) + + +def jit(model_path: Path, chat_config: Dict[str, Any], device: Device) -> Path: + """Just-in-time compile a MLC-Chat model.""" + logger.info( + "%s = %s. Can be one of: ON, OFF, REDO, READONLY", + bold("MLC_JIT_POLICY"), + MLC_JIT_POLICY, + ) + if MLC_JIT_POLICY == "OFF": + raise RuntimeError("JIT is disabled by MLC_JIT_POLICY=OFF") + + with open(model_path / "mlc-chat-config.json", "r", encoding="utf-8") as in_file: + mlc_chat_config = json.load(in_file) + model_type = mlc_chat_config.pop("model_type") + quantization = mlc_chat_config.pop("quantization") + + def _get_optimization_flags() -> str: + opt = chat_config.pop("opt", None) + if opt is None: + opt = "O2" + return repr(OptimizationFlags.from_str(opt)) + + def _get_overrides() -> str: + forbid_list = ["context_window_size", "sliding_window_size", "attention_sink_size"] + result = [] + for field in dataclasses.fields(ModelConfigOverride): + value = chat_config.get(field.name, None) + if value is not None: + if field.name in forbid_list and value == -1: + continue + result.append(f"{field.name}={value}") + if not result: + result = ["tensor_parallel_shards=1"] + return ";".join(result) + + def _get_model_config() -> Dict[str, Any]: + model_config = mlc_chat_config.pop("model_config") + model_config.update(mlc_chat_config) + for field in dataclasses.fields(ModelConfigOverride): + value = chat_config.get(field.name, None) + if value is not None: + model_config[field.name] = value + return MODELS[model_type].config.from_dict(model_config).asdict() + + def _run_jit(opt: str, overrides: str, device: str, dst: str): + with tempfile.TemporaryDirectory(dir=MLC_TEMP_DIR) as tmp_dir: + dso_path = os.path.join(tmp_dir, f"lib.{MLC_DSO_SUFFIX}") + cmd = [ + sys.executable, + "-m", + "mlc_chat", + "compile", + str(model_path), + "--opt", + opt, + "--overrides", + overrides, + "--device", + device, + "--output", + dso_path, + ] + logger.info("Compiling using commands below:") + logger.info("%s", blue(shlex.join(cmd))) + subprocess.run(cmd, check=True) + shutil.move(dso_path, dst) + logger.info("Using compiled model lib: %s", bold(dst)) + + hash_key = { + "model_config": _get_model_config(), + "overrides": _get_overrides(), + "opt": _get_optimization_flags(), + "device": device2str(device), + "model_type": model_type, + "quantization": quantization, + } + hash_value = hashlib.md5( + json.dumps( + hash_key, + sort_keys=True, + indent=2, + ).encode("utf-8") + ).hexdigest() + dst = MLC_CACHE_DIR / "model_lib" / f"{hash_value}.so" + if dst.is_file() and MLC_JIT_POLICY in ["ON", "READONLY"]: + logger.info("Using cached model lib: %s", bold(str(dst))) + return dst + if MLC_JIT_POLICY == "READONLY": + raise RuntimeError( + "No cached model lib found, and JIT is disabled by MLC_JIT_POLICY=READONLY" + ) + _run_jit( + opt=hash_key["opt"], + overrides=hash_key["overrides"], + device=hash_key["device"], + dst=str(dst), + ) + return dst diff --git a/python/mlc_chat/interface/openai_api.py b/python/mlc_chat/interface/openai_api.py new file mode 100644 index 0000000..7c7797d --- /dev/null +++ b/python/mlc_chat/interface/openai_api.py @@ -0,0 +1,183 @@ +# pylint: disable=missing-docstring,fixme,too-few-public-methods +""" +Adapted from FastChat's OpenAI protocol: +https://github.com/lm-sys/FastChat/blob/main/fastchat/protocol/openai_api_protocol.py +""" + +import time +from typing import Any, Dict, List, Literal, Optional, Union + +import shortuuid +from pydantic import BaseModel, Field + + +class ToolCalls(BaseModel): + id: str = Field(default_factory=lambda: f"call_{shortuuid.random()}") + type: str = "function" + function: object + + +class ChatMessage(BaseModel): + role: str + content: Union[str, None] + name: Optional[str] = None + tool_calls: Optional[List[ToolCalls]] = None + + +class Function(BaseModel): + description: Optional[str] = None + name: str + parameters: object + + +class Tools(BaseModel): + type: Literal["function"] + function: Dict[str, Any] + + +class ToolChoice(BaseModel): + type: Literal["function"] + function: Dict[str, Any] + + +class ChatCompletionRequest(BaseModel): + model: str + messages: List[ChatMessage] + stream: Optional[bool] = False + temperature: float = None + top_p: float = None + # TODO: replace by presence_penalty and frequency_penalty + repetition_penalty: float = None + mean_gen_len: int = None + # TODO: replace by max_tokens + max_gen_len: int = None + presence_penalty: float = None + frequency_penalty: float = None + n: int = None + stop: Union[str, List[str]] = None + tools: Optional[List[Tools]] = None + tool_choice: Union[Literal["none", "auto"], ToolChoice] = "auto" + # TODO: Implement support for the OpenAI API parameters + # stop: Optional[Union[str, List[str]]] = None + # max_tokens: Optional[int] + # logit_bias + # user: Optional[str] = None + + +class UsageInfo(BaseModel): + prompt_tokens: int = 0 + completion_tokens: Optional[int] = 0 + total_tokens: int = 0 + + +class ChatCompletionResponseChoice(BaseModel): + index: int + message: ChatMessage + finish_reason: Optional[Literal["stop", "length", "tool_calls"]] = None + + +class ChatCompletionResponse(BaseModel): + id: str = Field(default_factory=lambda: f"chatcmpl-{shortuuid.random()}") + object: str = "chat.completion" + created: int = Field(default_factory=lambda: int(time.time())) + choices: List[ChatCompletionResponseChoice] + # TODO: Implement support for the following fields + usage: Optional[UsageInfo] = None + + +class DeltaMessage(BaseModel): + role: Optional[str] = None + content: Optional[str] = None + + +class ChatCompletionResponseStreamChoice(BaseModel): + index: int + delta: DeltaMessage + finish_reason: Optional[Literal["stop", "length"]] = None + + +class ChatCompletionStreamResponse(BaseModel): + id: str = Field(default_factory=lambda: f"chatcmpl-{shortuuid.random()}") + object: str = "chat.completion.chunk" + created: int = Field(default_factory=lambda: int(time.time())) + choices: List[ChatCompletionResponseStreamChoice] + + +class CompletionRequest(BaseModel): + model: str + prompt: Union[str, List[str]] + stream: Optional[bool] = False + temperature: float = None + repetition_penalty: float = None + top_p: float = None + mean_gen_len: int = None + # TODO: replace by max_tokens + max_gen_len: int = None + presence_penalty: float = None + frequency_penalty: float = None + n: int = None + stop: Union[str, List[str]] = None + # TODO: Implement support for the OpenAI API parameters + # suffix + # logprobs + # echo + # best_of + # logit_bias + # user: Optional[str] = None + + +class CompletionResponseChoice(BaseModel): + index: int + text: str + finish_reason: Optional[Literal["stop", "length"]] = None + # TODO: logprobs support + logprobs: Optional[int] = None + + +class CompletionResponse(BaseModel): + id: str = Field(default_factory=lambda: f"cmpl-{shortuuid.random()}") + object: str = "text.completion" + created: int = Field(default_factory=lambda: int(time.time())) + choices: List[CompletionResponseChoice] + usage: UsageInfo + + +class CompletionResponseStreamChoice(BaseModel): + index: int + text: str + finish_reason: Optional[Literal["stop", "length"]] = None + + +class CompletionStreamResponse(BaseModel): + id: str = Field(default_factory=lambda: f"cmpl-{shortuuid.random()}") + object: str = "text.completion.chunk" + created: int = Field(default_factory=lambda: int(time.time())) + choices: List[CompletionResponseStreamChoice] + + +class EmbeddingsRequest(BaseModel): + model: Optional[str] = None + input: Union[str, List[Any]] + user: Optional[str] = None + + +class EmbeddingsResponse(BaseModel): + object: str = "list" + data: List[Dict[str, Any]] + model: Optional[str] = None + usage: UsageInfo + + +class VisualStudioCodeCompletionParameters(BaseModel): + temperature: float = None + top_p: float = None + max_new_tokens: int = None + + +class VisualStudioCodeCompletionRequest(BaseModel): + inputs: str + parameters: VisualStudioCodeCompletionParameters + + +class VisualStudioCodeCompletionResponse(BaseModel): + generated_text: str diff --git a/python/mlc_chat/libinfo.py b/python/mlc_chat/libinfo.py new file mode 100644 index 0000000..4c36cab --- /dev/null +++ b/python/mlc_chat/libinfo.py @@ -0,0 +1,70 @@ +"""Library information. This is a standalone file that can be used to get various info""" +#! pylint: disable=protected-access +import os +import sys + +__version__ = "0.1.dev0" +MLC_LIBRARY_PATH = os.environ.get("MLC_LIBRARY_PATH", None) + + +def get_env_paths(env_var, splitter): + """Get path in env variable""" + if os.environ.get(env_var, None): + return [p.strip() for p in os.environ[env_var].split(splitter)] + return [] + + +def get_dll_directories(): + """Get extra mlc llm dll directories""" + curr_dir = os.path.dirname(os.path.realpath(os.path.expanduser(__file__))) + source_dir = os.path.abspath(os.path.join(curr_dir, "..", "..")) + dll_path = [ + curr_dir, + os.path.join(source_dir, "build"), + os.path.join(source_dir, "build", "Release"), + ] + if MLC_LIBRARY_PATH: + dll_path.append(MLC_LIBRARY_PATH) + if "CONDA_PREFIX" in os.environ: + dll_path.append(os.path.join(os.environ["CONDA_PREFIX"], "lib")) + if sys.platform.startswith("linux") or sys.platform.startswith("freebsd"): + dll_path.extend(get_env_paths("LD_LIBRARY_PATH", ":")) + elif sys.platform.startswith("darwin"): + dll_path.extend(get_env_paths("DYLD_LIBRARY_PATH", ":")) + elif sys.platform.startswith("win32"): + dll_path.extend(get_env_paths("PATH", ";")) + return [os.path.abspath(p) for p in dll_path if os.path.isdir(p)] + + +def find_lib_path(name, optional=False): + """Find mlc llm library + + Parameters + ---------- + name : str + The name of the library + + optional: boolean + Whether the library is required + """ + if sys.platform.startswith("linux") or sys.platform.startswith("freebsd"): + lib_name = f"lib{name}.so" + elif sys.platform.startswith("win32"): + lib_name = f"{name}.dll" + elif sys.platform.startswith("darwin"): + lib_name = f"lib{name}.dylib" + else: + lib_name = f"lib{name}.so" + + dll_paths = get_dll_directories() + lib_dll_path = [os.path.join(p, lib_name) for p in dll_paths] + lib_found = [p for p in lib_dll_path if os.path.exists(p) and os.path.isfile(p)] + if not lib_found: + if not optional: + message = ( + f"Cannot find libraries: {lib_name}\n" + + "List of candidates:\n" + + "\n".join(lib_dll_path) + ) + raise RuntimeError(message) + return lib_found diff --git a/python/mlc_chat/loader/__init__.py b/python/mlc_chat/loader/__init__.py new file mode 100644 index 0000000..cc8ba9c --- /dev/null +++ b/python/mlc_chat/loader/__init__.py @@ -0,0 +1,7 @@ +""" +A subpackage of the compiler that represents mapping between external parameters, quantized +parameters and parameters in MLC-defined models. +""" +from .huggingface_loader import HuggingFaceLoader +from .loader import LOADER, Loader +from .mapping import ExternMapping, QuantizeMapping diff --git a/python/mlc_chat/loader/huggingface_loader.py b/python/mlc_chat/loader/huggingface_loader.py new file mode 100644 index 0000000..5334242 --- /dev/null +++ b/python/mlc_chat/loader/huggingface_loader.py @@ -0,0 +1,222 @@ +"""A weight loader for HuggingFace's PyTorch format""" +import gc +import json +from collections import OrderedDict, defaultdict +from pathlib import Path +from typing import Callable, Dict, Iterator, List, Optional, Tuple + +import numpy as np +from tqdm import tqdm +from tvm.runtime import Device, NDArray +from tvm.runtime.ndarray import array as as_ndarray + +from mlc_chat.support import logging +from mlc_chat.support.preshard import _sharded_param_name +from mlc_chat.support.style import bold + +from .mapping import ExternMapping, QuantizeMapping +from .stats import Stats +from .utils import check_parameter_usage, load_safetensor_shard, load_torch_shard + +logger = logging.getLogger(__name__) + + +class HuggingFaceLoader: # pylint: disable=too-few-public-methods + """A loader loading HuggingFace's PyTorch/SafeTensor format and converts them + to MLC's parameters. + + Attributes + ---------- + stats : Stats + Statistics of the loading process. + + extern_param_map : ExternMapping + The parameter mapping from MLC to HuggingFace PyTorch/SafeTensor. + + torch_to_path : Dict[str, Path] + A mapping from PyTorch/SafeTensor parameter name to the path of the file containing it, + or the path meaning all parameters are stored in a single file. + + cached_files : Dict[Path, Dict[str, np.ndarray]] + A cache of the loaded files. The key is the path of the file, and the value is a mapping + from parameter name to the parameter value. + + quantize_param_map : Optional[QuantizeMapping] + The quantization mapping from MLC to quantized MLC parameters. + """ + + stats: Stats + cached_files: Dict[Path, Dict[str, np.ndarray]] + torch_to_path: Dict[str, Path] + extern_param_map: ExternMapping + quantize_param_map: Optional[QuantizeMapping] + + def __init__( + self, + path: Path, + extern_param_map: ExternMapping, + quantize_param_map: Optional[QuantizeMapping] = None, + ) -> None: + """Create a parameter loader from HuggingFace PyTorch format. + + Parameters + ---------- + path : pathlib.Path + Path to either a JSON indexing file, or a PyTorch bin file. + 1) For JSON indexing file, it is usually `pytorch_model.bin.index.json` + or `model.safetensors.index.json` in the repo, which contains a `weight_map` that + maps each PyTorch parameter to the file containing the weight. + 2) For PyTorch bin file, it is usually `pytorch_model.bin` in the repo, + which contains all the parameters. + 3) For safetensor file, it is usually `model.safetensors` in the repo, + which contains all the parameters. + + extern_param_map : ExternMapping + Maps an MLC parameter to a list of PyTorch/SafeTensor parameters. + + quantize_param_map: Optional[QuantizeMapping] + The quantization mapping from MLC to quantized MLC parameters, default to None, which + means no quantization. + """ + assert path.is_file(), f"Path {path} is not a file" + self.stats = Stats() + self.extern_param_map = extern_param_map + self.cached_files = {} + self.torch_to_path = {} + self.quantize_param_map = quantize_param_map + if path.suffix in (".bin", ".safetensors", ".pt"): + self._load_file(path) + for name in self.cached_files[path].keys(): + self.torch_to_path[name] = path + elif path.suffix == ".json": + with path.open("r", encoding="utf-8") as in_file: + torch_weight_map = json.load(in_file)["weight_map"] + for torch_name, path_str in torch_weight_map.items(): + self.torch_to_path[torch_name] = path.parent / path_str + else: + raise FileNotFoundError(f"Unknown file suffix: {path}") + check_parameter_usage(extern_param_map, set(self.torch_to_path.keys())) + + def load( + self, device: Device, preshard_funcs: Dict[str, Callable] = None + ) -> Iterator[Tuple[str, NDArray]]: + """Load the parameters and yield the MLC parameter and its value. + + Parameters + ---------- + device : Optional[Device] + The device to store the parameter, default to None, which means using CPU. + + Yields + ------ + Tuple[str, NDArray] + The MLC parameter name and its value, quantized if quantization mapping is provided. + """ + mlc_names = _loading_order(self.extern_param_map, self.torch_to_path) + for mlc_name in tqdm(mlc_names): + param = self._load_mlc_param(mlc_name, device=device) + if preshard_funcs is not None and mlc_name in preshard_funcs: + sharded_params = preshard_funcs[mlc_name](param) + for i, sharded_param in enumerate(sharded_params): + sharded_name = _sharded_param_name(mlc_name, i) + yield from self._load_or_quantize(sharded_name, sharded_param, device) + else: + yield from self._load_or_quantize(mlc_name, param, device) + + cached_files = list(self.cached_files.keys()) + for path in cached_files: + self._unload_file(path) + self.stats.log_time_info("HF") + self.stats.log_mem_usage() + + def _load_mlc_param(self, mlc_name: str, device: Optional[Device]) -> NDArray: + torch_names = self.extern_param_map.param_map[mlc_name] + files_required = {self.torch_to_path[p] for p in torch_names} + files_existing = set(self.cached_files.keys()) + files_to_load = files_required - files_existing + files_to_unload = files_existing - files_required + + # Step 1. When there is some file to unloaded: + # - If no pending file load: unloading is deferred as there is no gain in peak memory usage; + # - Need to load files: unload immediately to save memory and make space for the new files. + if files_to_load: + for path in files_to_unload: + self._unload_file(path) + # Step 2. Load all the files needed + for path in files_to_load: + self._load_file(path) + # Step 3. Collect all torch parameters in order + torch_params = [self.cached_files[self.torch_to_path[i]][i] for i in torch_names] + # Step 4. Apply the mapping function + with self.stats.timer("map_time_sec"): + param = self.extern_param_map.map_func[mlc_name](*torch_params) + if device: + return as_ndarray(param, device=device) + return as_ndarray(param) + + def _load_or_quantize(self, mlc_name, param, device: Device): + if self.quantize_param_map and mlc_name in self.quantize_param_map.param_map: + with self.stats.timer("quant_time_sec"): + q_names = self.quantize_param_map.param_map[mlc_name] + q_params = self.quantize_param_map.map_func[mlc_name](param) + device.sync() + for q_name, q_param in zip(q_names, q_params): + logger.info( + '[Quantized] Parameter: "%s", shape: %s, dtype: %s', + bold(q_name), + q_param.shape, + q_param.dtype, + ) + yield q_name, q_param + else: + logger.info( + '[Not quantized] Parameter: "%s", shape: %s, dtype: %s', + bold(mlc_name), + param.shape, + param.dtype, + ) + device.sync() + yield mlc_name, param + + def _load_file(self, path: Path) -> None: + logger.info("Loading HF parameters from: %s", path) + load_func = load_safetensor_shard if path.suffix == ".safetensors" else load_torch_shard + with self.stats.timer("load_time_sec"): + result = {} + for name, param in load_func(path): + result[name] = param + self.stats.mem_add(param.nbytes) + if name not in self.extern_param_map.unused_params: + self.stats.total_param_num += param.size + self.cached_files[path] = result + + def _unload_file(self, path: Path) -> None: + logger.info("Unloading HF weight file: %s", path) + with self.stats.timer("load_time_sec"): + for _, param in self.cached_files[path].items(): + self.stats.mem_rm(param.nbytes) + del self.cached_files[path] + gc.collect() + + +def _loading_order(param_map: ExternMapping, torch_to_path: Dict[str, Path]) -> List[str]: + # Step 1. Build a map from path to torch parameters + path_to_torch: Dict[Path, List[str]] = defaultdict(list) + for torch_name, path in torch_to_path.items(): + path_to_torch[path].append(torch_name) + # Step 2. Build a map from torch parameters to MLC parameters + torch_to_mlc = defaultdict(list) + for mlc_name, torch_names in param_map.param_map.items(): + for torch_name in torch_names: + torch_to_mlc[torch_name].append(mlc_name) + # Step 3. Construct the ordering that ensures file locality + order = OrderedDict() + for _, torch_names in path_to_torch.items(): + for torch_name in torch_names: + for mlc_name in torch_to_mlc[torch_name]: + if mlc_name not in order: + order[mlc_name] = 1 + return list(order.keys()) + + +__all__ = ["HuggingFaceLoader"] diff --git a/python/mlc_chat/loader/loader.py b/python/mlc_chat/loader/loader.py new file mode 100644 index 0000000..e4c397c --- /dev/null +++ b/python/mlc_chat/loader/loader.py @@ -0,0 +1,12 @@ +"""A centralized registry of all existing loaders.""" +from typing import Any, Dict + +from .huggingface_loader import HuggingFaceLoader + +Loader = Any + +LOADER: Dict[str, Any] = { + "huggingface-torch": HuggingFaceLoader, + "huggingface-safetensor": HuggingFaceLoader, + "awq": HuggingFaceLoader, +} diff --git a/python/mlc_chat/loader/mapping.py b/python/mlc_chat/loader/mapping.py new file mode 100644 index 0000000..26d6811 --- /dev/null +++ b/python/mlc_chat/loader/mapping.py @@ -0,0 +1,101 @@ +"""Parameter mapping for converting different LLM implementations to MLC LLM.""" +import dataclasses +from typing import Callable, Dict, List, Set, Union + +import numpy as np +from tvm.runtime import NDArray + +MapFuncVariadic = Union[ + Callable[[], np.ndarray], + Callable[[np.ndarray], np.ndarray], + Callable[[np.ndarray, np.ndarray], np.ndarray], + Callable[[np.ndarray, np.ndarray, np.ndarray], np.ndarray], + Callable[[np.ndarray, np.ndarray, np.ndarray, np.ndarray], np.ndarray], +] + + +@dataclasses.dataclass +class ExternMapping: + """Mapping from a parameter name in MLC LLM's model definition to its potential source, + for example, from MLC parameter "model.layers.2.post_attention_layernorm.weight" to PyTorch's + parameter correspondingly. + + Parameters + ---------- + param_map : Dict[str, List[str]] + A dictionary that maps the name of a parameter to its source. For example, + in Llama2, the source of MLC parameter "model.layers.0.self_attn.qkv_proj.weight" from + huggingface torch are: + + - "model.layers.0.self_attn.q_proj.weight" + - "model.layers.0.self_attn.k_proj.weight" + - "model.layers.0.self_attn.v_proj.weight" + + map_func : Dict[str, Callable[[np.ndarray, ...], np.ndarray]] + A dictionary that maps the name of a parameter to a function that combines the source + parameters into the MLC parameter. For example, for the above example, the function + would be: `lambda q, k, v: np.concatenate([q, k, v], axis=0)`. + + unused_params : Set[str] + Parameter names in the source weights that are not used in the MLC LLM model definition. + """ + + param_map: Dict[str, List[str]] = dataclasses.field(default_factory=dict) + map_func: Dict[str, MapFuncVariadic] = dataclasses.field(default_factory=dict) + unused_params: Set[str] = dataclasses.field(default_factory=set) + + def add_mapping( + self, + map_from: str, + map_to: List[str], + func: MapFuncVariadic, + ) -> None: + """Add a mapping from MLC parameters to source parametes as well as a mapping function.""" + self.param_map[map_from] = map_to + self.map_func[map_from] = func + + def add_unused(self, name: str): + """Add a parameter name in the source parameters to the set of unused parameters.""" + self.unused_params.add(name) + + +@dataclasses.dataclass +class QuantizeMapping: + """Mapping from a parameter in MLC LLM's model definition to its eventual names and values after + quantization. In certain group quantization, for example, `qkv_proj.weight` is mapped to + `qkv_proj.weight_quantized` and `qkv_proj.weight_scale` respectively. If a parameter's name is + not in the mapping, it is assumed to be unchanged, i.e. not quantized. + + Parameters + ---------- + param_map : Dict[str, List[str]] + A dictionary that maps the name of a parameter to its destination. For example, + in certain group quantization, the destinations of MLC parameter "qkv_proj.weight` are: + + - "qkv_proj.weight_quantized" + - "qkv_proj.weight_scale" + + map_func : Dict[str, Callable[NDArray, List[NDArray]]] + A dictionary that maps the name of a parameter to a function that splits the MLC parameter + into the destination parameters. + + Notes + ----- + There are two forms of weight conversion in MLC LLM, one is A) on-the-fly quantization to the + raw fp16/bf16/fp32 weights from HuggingFace, and the other is B) loading pre-quantized weights + from an external framework, e.g. AutoGPTQ, AutoAWQ. From the perspective of parameter + correspondence. + + - In case A), it is recommended that the weight loader take both `ExternMapping` and + `QuantizeMapping` as input, and do quantiaztion on the fly as a raw parameter being + loaded into RAM; + - In case B), a pass over `nn.Module` is recommended to take place first to converts parameters + from its non-quantized form to the quantized one, and then only `ExternMapping` is + used to convert the quantized parameters into the desired form. + """ + + param_map: Dict[str, List[str]] + map_func: Dict[str, Callable[[NDArray], List[NDArray]]] + + +__all__ = ["ExternMapping", "QuantizeMapping"] diff --git a/python/mlc_chat/loader/stats.py b/python/mlc_chat/loader/stats.py new file mode 100644 index 0000000..6a97cf9 --- /dev/null +++ b/python/mlc_chat/loader/stats.py @@ -0,0 +1,95 @@ +"""Statistics of the loading process of parameter loaders""" +import dataclasses +import time +from contextlib import contextmanager + +from mlc_chat.support import logging +from mlc_chat.support.style import green + +logger = logging.getLogger(__name__) + + +@dataclasses.dataclass +class Stats: + """Statistics of the loading process of parameter loaders. + + Attributes + ---------- + load_time_sec : float + Time used in loading the parameters. + + map_time_sec : float + Time used in applying the mapping function, i.e. `ExternMapping.map_func`. + + quant_time_sec : float + Time used in quantizing the parameters, i.e. `QuantizeMapping.quant_func`. + + current_memory_gb : float + The current RAM usage in GB. + + total_memory_gb : float + The total size data loaded from disk in GB. + + max_memory_gb : float + The maximum RAM usage in GB. + + total_param_num: int + Total number of parameters (original non-MLC model weights), excluding unused params. + """ + + load_time_sec: float = 0.0 + map_time_sec: float = 0.0 + quant_time_sec: float = 0.0 + + current_memory_gb: float = 0.0 + total_memory_gb: float = 0.0 + max_memory_gb: float = 0.0 + + total_param_num: int = 0 + + def timer(self, attr): + """A context manager to time the scope and add the time to the attribute.""" + + @contextmanager + def timed_scope(): + start_time = time.time() + yield + elapsed_time = time.time() - start_time + setattr(self, attr, getattr(self, attr) + elapsed_time) + + return timed_scope() + + def mem_add(self, nbytes: int): + """Add the memory usage by the given number of bytes.""" + mem_gb = float(nbytes) / float(1024**3) + self.current_memory_gb += mem_gb + self.total_memory_gb += mem_gb + self.max_memory_gb = max(self.max_memory_gb, self.current_memory_gb) + + def mem_rm(self, nbytes: int): + """Remove the memory usage by the given number of bytes.""" + mem_gb = float(nbytes) / float(1024**3) + self.current_memory_gb -= mem_gb + + def log_time_info(self, weight_format: str): + """Log the time used in loading, pre-quantization and quantization.""" + logger.info( + "%s: " + "%s loading: %.3f sec; " + "Pre-quantization mapping: %.3f sec; " + "Quantization: %.3f sec", + green("Time usage"), + weight_format, + self.load_time_sec, + self.map_time_sec, + self.quant_time_sec, + ) + + def log_mem_usage(self): + """Log the Memory usage information.""" + logger.info( + "%s: Peak RAM: %.3f GB. Total bytes loaded from disk: %.3f GB", + green("RAM usage"), + self.max_memory_gb, + self.total_memory_gb, + ) diff --git a/python/mlc_chat/loader/utils.py b/python/mlc_chat/loader/utils.py new file mode 100644 index 0000000..b35f9a9 --- /dev/null +++ b/python/mlc_chat/loader/utils.py @@ -0,0 +1,66 @@ +"""Common utilities for loading parameters""" +# pylint: disable=too-few-public-methods +from pathlib import Path +from typing import TYPE_CHECKING, Iterator, Set, Tuple + +import numpy as np + +from mlc_chat.support import logging + +if TYPE_CHECKING: + from tvm.runtime import NDArray + + from .mapping import ExternMapping + + +logger = logging.getLogger(__name__) + + +def check_parameter_usage(param_map: "ExternMapping", extern_weights: Set[str]): + """Check that all external parameters have been used and are stored in the weights file.""" + used_extern_names = set(sum(param_map.param_map.values(), [])) + # Check 1. All extern parameters in the weight files are used unless explicitly specified + unused_extern_names = extern_weights - used_extern_names - param_map.unused_params + if unused_extern_names: + logger.warning( + "Unused extern parameters: %s", + ", ".join(sorted(unused_extern_names)), + ) + # Check 2. All extern parameters required are stored in the weight files + nonexistent_extern_names = used_extern_names - extern_weights + if nonexistent_extern_names: + raise ValueError( + "The following extern parameters do not exist in the weight files:\n " + + "\n ".join(sorted(nonexistent_extern_names)), + ) + + +def load_torch_shard(path: Path) -> Iterator[Tuple[str, np.ndarray]]: + """Load and yield PyTorch format parameters.""" + import torch # pylint: disable=import-outside-toplevel + + for name, param in torch.load(path, map_location=torch.device("cpu")).items(): + if param is None: + logger.warning("Encountered None param, skipping it: %s", name) + continue + param = param.detach().cpu() + dtype = str(param.dtype) + if dtype == "torch.bfloat16": + param = param.float() + param = param.numpy() + yield name, param + + +def load_safetensor_shard(path: Path) -> Iterator[Tuple[str, np.ndarray]]: + """Load and yield SafeTensor format parameters.""" + import safetensors # pylint: disable=import-outside-toplevel,import-error + + with safetensors.safe_open(path, framework="pt", device="cpu") as in_file: + for name in in_file.keys(): + param = in_file.get_tensor(name) + param = param.detach().cpu() + dtype = str(param.dtype) + if dtype == "torch.bfloat16": + param = param.float() + param = param.numpy() + yield name, param diff --git a/python/mlc_chat/model/__init__.py b/python/mlc_chat/model/__init__.py new file mode 100644 index 0000000..d7b0baa --- /dev/null +++ b/python/mlc_chat/model/__init__.py @@ -0,0 +1,3 @@ +"""Model definition for the compiler.""" +from .model import MODELS, Model +from .model_preset import MODEL_PRESETS diff --git a/python/mlc_chat/model/baichuan/__init__.py b/python/mlc_chat/model/baichuan/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/mlc_chat/model/baichuan/baichuan_loader.py b/python/mlc_chat/model/baichuan/baichuan_loader.py new file mode 100644 index 0000000..01b8528 --- /dev/null +++ b/python/mlc_chat/model/baichuan/baichuan_loader.py @@ -0,0 +1,70 @@ +""" +This file specifies how MLC's StableLM parameter maps from other formats, for example HuggingFace +PyTorch, HuggingFace safetensors. +""" + +import functools + +import numpy as np + +from mlc_chat.loader import ExternMapping +from mlc_chat.quantization import Quantization + +from .baichuan_model import BaichuanConfig, BaichuanForCausalLM + + +def huggingface(model_config: BaichuanConfig, quantization: Quantization) -> ExternMapping: + """Returns a parameter mapping that maps from the names of MLC LLM parameters to + the names of HuggingFace PyTorch parameters. + + Parameters + ---------- + model_config : GPT2Config + The configuration of the GPT-2 model. + + quantization : Quantization + The quantization configuration. + + Returns + ------- + param_map : ExternMapping + The parameter mapping from MLC to HuggingFace PyTorch. + """ + model = BaichuanForCausalLM(model_config) + if quantization is not None: + model.to(quantization.model_dtype) + _, _named_params, _ = model.export_tvm( # type: ignore[misc] + spec=model.get_default_spec(), + allow_extern=True, + ) + named_parameters = dict(_named_params) + + mapping = ExternMapping() + + for i in range(model_config.num_hidden_layers): + mlp = f"model.layers.{i}.mlp" + mlc_name = f"{mlp}.gate_up_proj.weight" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{mlp}.gate_proj.weight", + f"{mlp}.up_proj.weight", + ], + functools.partial( + lambda gate, up, dtype: np.concatenate([gate, up], axis=0).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + + for mlc_name, mlc_param in named_parameters.items(): + if mlc_name not in mapping.param_map: + mapping.add_mapping( + mlc_name, + [mlc_name], + functools.partial( + lambda x, dtype: x.astype(dtype), + dtype=mlc_param.dtype, + ), + ) + return mapping diff --git a/python/mlc_chat/model/baichuan/baichuan_model.py b/python/mlc_chat/model/baichuan/baichuan_model.py new file mode 100644 index 0000000..5bcedd4 --- /dev/null +++ b/python/mlc_chat/model/baichuan/baichuan_model.py @@ -0,0 +1,252 @@ +""" +Implementation for BAICHUAN architecture. +TODO: add docstring +""" +import dataclasses +from typing import Any, Dict, Optional + +from tvm import te, tir +from tvm.relax.frontend import nn +from tvm.relax.frontend.nn import Tensor, op + +from mlc_chat import op as op_ext +from mlc_chat.support import logging +from mlc_chat.support.config import ConfigBase +from mlc_chat.support.style import bold + +logger = logging.getLogger(__name__) + + +@dataclasses.dataclass +class BaichuanConfig(ConfigBase): # pylint: disable=too-many-instance-attributes + """Configuration of the Baichuan model.""" + + vocab_size: int + hidden_size: int + num_hidden_layers: int + num_attention_heads: int + initializer_range: float + intermediate_size: int + rms_norm_eps: float + use_cache: bool + pad_token_id: int + bos_token_id: int + eos_token_id: int + tie_word_embeddings: bool + context_window_size: int = 0 + prefill_chunk_size: int = 0 + tensor_parallel_shards: int = 1 + kwargs: Dict[str, Any] = dataclasses.field(default_factory=dict) + + def __post_init__(self): + if self.context_window_size == 0: + for name in ["max_position_embeddings", "max_sequence_length"]: + if name in self.kwargs: + self.context_window_size = self.kwargs.pop(name) + logger.info( + "%s not found in config.json. Falling back to %s (%d)", + bold("context_window_size"), + bold(name), + self.context_window_size, + ) + break + else: + raise ValueError( + "Unable to determine the maxmimum sequence length, because none of " + "`context_window_size`, `max_position_embeddings` or `max_sequence_length` is " + "provided in `config.json`." + ) + if self.prefill_chunk_size == 0: + logger.info( + "%s defaults to %s (%d)", + bold("prefill_chunk_size"), + bold("context_window_size"), + self.context_window_size, + ) + self.prefill_chunk_size = self.context_window_size + elif self.prefill_chunk_size > self.context_window_size: + logger.info( + "Overriding %s from %d to %d (%s)", + bold("prefill_chunk_size"), + self.prefill_chunk_size, + self.context_window_size, + bold("context_window_size"), + ) + self.prefill_chunk_size = self.context_window_size + assert self.tensor_parallel_shards == 1, "Baichuan currently does not support sharding." + + +# pylint: disable=invalid-name,missing-docstring + + +class BaichuanAttention(nn.Module): # pylint: disable=too-many-instance-attributes + def __init__(self, config: BaichuanConfig): + self.hidden_size = config.hidden_size + self.num_heads = config.num_attention_heads + self.head_dim = self.hidden_size // self.num_heads + self.max_position_embeddings = config.context_window_size + + self.W_pack = nn.Linear(self.hidden_size, 3 * self.hidden_size, bias=False) + self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=False) + + self.k_cache = nn.KVCache(config.context_window_size, [self.num_heads, self.head_dim]) + self.v_cache = nn.KVCache(config.context_window_size, [self.num_heads, self.head_dim]) + + def forward( # pylint: disable=too-many-locals + self, + hidden_states: Tensor, + attention_mask: Tensor, + total_seq_len: tir.Var, + ): + d, h, t = self.head_dim, self.num_heads, total_seq_len + b, s, _ = hidden_states.shape + assert b == 1, "Only support batch size 1 at this moment." + # Step 1. QKV Projection + qkv = self.W_pack(hidden_states) + qkv = op.reshape(qkv, (b, s, 3 * h, d)) + # Step 2. Apply QK rotary embedding + q, k, v = op_ext.llama_rope(qkv, t, 10000, h, h) + # Step 3. Query and update KVCache + self.k_cache.append(op.squeeze(k, axis=0)) + self.v_cache.append(op.squeeze(v, axis=0)) + k = self.k_cache.view(t) + v = self.v_cache.view(t) + # Step 4. Compute softmax(Q @ K^T / sqrt(d)) @ V + output = op_ext.attention(q, k, v, casual_mask=attention_mask) + # Step 5. Apply output projection + return self.o_proj(output) + + +class BaichuanMLP(nn.Module): + def __init__(self, config: BaichuanConfig): + self.gate_up_proj = nn.Linear( + in_features=config.hidden_size, + out_features=2 * config.intermediate_size, + bias=False, + ) + self.down_proj = nn.Linear(config.intermediate_size, config.hidden_size, bias=False) + + def forward(self, x): + concat_x1_x2 = self.gate_up_proj(x) + x1, x2 = op.split(concat_x1_x2, 2, axis=-1) + return self.down_proj(op.silu(x1) * x2) + + +class BaichuanDecoderLayer(nn.Module): + def __init__(self, config: BaichuanConfig): + norm_eps = config.rms_norm_eps + self.self_attn = BaichuanAttention(config=config) + self.mlp = BaichuanMLP(config) + self.input_layernorm = nn.RMSNorm(config.hidden_size, -1, norm_eps, bias=False) + self.post_attention_layernorm = nn.RMSNorm(config.hidden_size, -1, norm_eps, bias=False) + + def forward(self, hidden_states: Tensor, attention_mask: Tensor, total_seq_len: tir.Var): + out = self.self_attn(self.input_layernorm(hidden_states), attention_mask, total_seq_len) + hidden_states = out + hidden_states + out = self.mlp(self.post_attention_layernorm(hidden_states)) + hidden_states = out + hidden_states + return hidden_states + + +class BaichuanModel(nn.Module): + def __init__(self, config: BaichuanConfig): + assert config.hidden_size % config.num_attention_heads == 0 + self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size) + self.layers = nn.ModuleList( + [BaichuanDecoderLayer(config) for _ in range(config.num_hidden_layers)] + ) + self.norm = nn.RMSNorm(config.hidden_size, -1, config.rms_norm_eps, bias=False) + + def forward(self, input_ids: Tensor, total_seq_len: tir.Var, attention_mask: Tensor): + hidden_states = self.embed_tokens(input_ids) + for layer in self.layers: + hidden_states = layer(hidden_states, attention_mask, total_seq_len) + hidden_states = self.norm(hidden_states) + return hidden_states + + +class BaichuanForCausalLM(nn.Module): + def __init__(self, config: BaichuanConfig): + self.model = BaichuanModel(config) + self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + self.vocab_size = config.vocab_size + self.dtype = "float32" + + def to(self, dtype: Optional[str] = None): + super().to(dtype=dtype) + if dtype is not None: + self.dtype = dtype + + def forward(self, inputs: Tensor, total_seq_len: tir.Var, attention_mask: Tensor): + def _index(x: te.Tensor): # x[:-1,:] + b, s, d = x.shape + return te.compute((b, 1, d), lambda i, _, k: x[i, s - 1, k], name="index") + + hidden_states = self.model(inputs, total_seq_len, attention_mask) + hidden_states = op.tensor_expr_op(_index, name_hint="index", args=[hidden_states]) + logits = self.lm_head(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits + + def prefill(self, inputs: Tensor, total_seq_len: tir.Var): + def _attention_mask(batch_size, seq_len, total_seq_len): + return te.compute( + (batch_size, 1, seq_len, total_seq_len), + lambda b, _, i, j: tir.if_then_else( + i < j - (total_seq_len - seq_len), + tir.min_value(self.dtype), + tir.max_value(self.dtype), + ), + name="attention_mask_prefill", + ) + + batch_size, seq_len = inputs.shape + attention_mask = op.tensor_expr_op( + _attention_mask, + name_hint="attention_mask_prefill", + args=[batch_size, seq_len, total_seq_len], + ) + return self.forward(inputs, total_seq_len, attention_mask) + + def decode(self, inputs: Tensor, total_seq_len: tir.Var): + batch_size, seq_len = inputs.shape + attention_mask = op.full( + shape=[batch_size, 1, seq_len, total_seq_len], + fill_value=tir.max_value(self.dtype), + dtype=self.dtype, + ) + return self.forward(inputs, total_seq_len, attention_mask) + + def softmax_with_temperature(self, logits: Tensor, temperature: Tensor): + return op.softmax(logits / temperature, axis=-1) + + def get_default_spec(self): + batch_size = 1 + mod_spec = { + "prefill": { + "inputs": nn.spec.Tensor([batch_size, "seq_len"], "int32"), + "total_seq_len": int, + "$": { + "param_mode": "packed", + "effect_mode": "packed", + }, + }, + "decode": { + "inputs": nn.spec.Tensor([batch_size, 1], "int32"), + "total_seq_len": int, + "$": { + "param_mode": "packed", + "effect_mode": "packed", + }, + }, + "softmax_with_temperature": { + "logits": nn.spec.Tensor([1, 1, "vocab_size"], "float32"), + "temperature": nn.spec.Tensor([], "float32"), + "$": { + "param_mode": "none", + "effect_mode": "none", + }, + }, + } + return nn.spec.ModuleSpec.from_raw(mod_spec, self) diff --git a/python/mlc_chat/model/baichuan/baichuan_quantization.py b/python/mlc_chat/model/baichuan/baichuan_quantization.py new file mode 100644 index 0000000..2558942 --- /dev/null +++ b/python/mlc_chat/model/baichuan/baichuan_quantization.py @@ -0,0 +1,53 @@ +"""This file specifies how MLC's Baichuan parameters are quantized using group quantization +or other formats.""" +from typing import Tuple + +from tvm.relax.frontend import nn + +from mlc_chat.loader import QuantizeMapping +from mlc_chat.quantization import FTQuantize, GroupQuantize, NoQuantize + +from .baichuan_model import BaichuanConfig, BaichuanForCausalLM + + +def group_quant( + model_config: BaichuanConfig, + quantization: GroupQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a BaichuanLM-architecture model using group quantization.""" + model: nn.Module = BaichuanForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def ft_quant( + model_config: BaichuanConfig, + quantization: FTQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a BaichuanLM-architecture model using FasterTransformer quantization.""" + model: nn.Module = BaichuanForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def no_quant( + model_config: BaichuanConfig, + quantization: NoQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a BaichuanLM model without quantization.""" + model: nn.Module = BaichuanForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + return model, quant_map diff --git a/python/mlc_chat/model/gemma/__init__.py b/python/mlc_chat/model/gemma/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/mlc_chat/model/gemma/gemma_loader.py b/python/mlc_chat/model/gemma/gemma_loader.py new file mode 100644 index 0000000..c839978 --- /dev/null +++ b/python/mlc_chat/model/gemma/gemma_loader.py @@ -0,0 +1,121 @@ +""" +This file specifies how MLC's Gemma parameter maps from other formats, for example HuggingFace +PyTorch, HuggingFace safetensors. +""" + +import functools + +import numpy as np + +from mlc_chat.loader import ExternMapping +from mlc_chat.quantization import Quantization + +from .gemma_model import GemmaConfig, GemmaForCausalLM + + +def huggingface(model_config: GemmaConfig, quantization: Quantization) -> ExternMapping: + """Returns a parameter mapping that maps from the names of MLC LLM parameters to + the names of HuggingFace PyTorch parameters. + + Parameters + ---------- + model_config : GemmaConfig + The configuration of the Gemma model. + + quantization : Quantization + The quantization configuration. + + Returns + ------- + param_map : ExternMapping + The parameter mapping from MLC to HuggingFace PyTorch. + """ + model = GemmaForCausalLM(model_config) + if quantization is not None: + model.to(quantization.model_dtype) + _, _named_params, _ = model.export_tvm( # type: ignore[misc] + spec=model.get_default_spec(), + allow_extern=True, + ) + named_parameters = dict(_named_params) + + mapping = ExternMapping() + + for i in range(model_config.num_hidden_layers): + # Add QKV in self attention + attn = f"model.layers.{i}.self_attn" + mlc_name = f"{attn}.qkv_proj.weight" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{attn}.q_proj.weight", + f"{attn}.k_proj.weight", + f"{attn}.v_proj.weight", + ], + functools.partial( + lambda q, k, v, dtype: np.concatenate([q, k, v], axis=0).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + # Add gates in MLP + mlp = f"model.layers.{i}.mlp" + mlc_name = f"{mlp}.gate_up_proj.weight" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{mlp}.gate_proj.weight", + f"{mlp}.up_proj.weight", + ], + functools.partial( + lambda gate, up, dtype: np.concatenate([gate, up], axis=0).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + # Modify RMS layernorm weights, since Gemma model adds 1 to the weights + # We add 1 to the weights here for efficiency purpose + mlc_name = f"model.layers.{i}.input_layernorm.weight" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [mlc_name], + functools.partial( + lambda x, dtype: (x + 1).astype(dtype), + dtype=named_parameters[mlc_name].dtype, + ), + ) + + mlc_name = f"model.layers.{i}.post_attention_layernorm.weight" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [mlc_name], + functools.partial( + lambda x, dtype: (x + 1).astype(dtype), + dtype=named_parameters[mlc_name].dtype, + ), + ) + + mlc_name = "model.norm.weight" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [mlc_name], + functools.partial( + lambda x, dtype: (x + 1).astype(dtype), + dtype=named_parameters[mlc_name].dtype, + ), + ) + + for mlc_name, mlc_param in named_parameters.items(): + if mlc_name not in mapping.param_map: + mapping.add_mapping( + mlc_name, + [mlc_name], + functools.partial( + lambda x, dtype: x.astype(dtype), + dtype=mlc_param.dtype, + ), + ) + return mapping diff --git a/python/mlc_chat/model/gemma/gemma_model.py b/python/mlc_chat/model/gemma/gemma_model.py new file mode 100644 index 0000000..0145589 --- /dev/null +++ b/python/mlc_chat/model/gemma/gemma_model.py @@ -0,0 +1,386 @@ +"""Implementation for Gemma architecture.""" + +import dataclasses +from typing import Any, Dict, Optional + +from tvm import te, tir +from tvm.relax.frontend import nn +from tvm.relax.frontend.nn import Tensor, op + +from mlc_chat import op as op_ext +from mlc_chat.nn import PagedKVCache, RopeMode +from mlc_chat.support import logging +from mlc_chat.support import tensor_parallel as tp +from mlc_chat.support.config import ConfigBase +from mlc_chat.support.style import bold + +logger = logging.getLogger(__name__) + + +@dataclasses.dataclass +class GemmaConfig(ConfigBase): # pylint: disable=too-many-instance-attributes + """Configuration of the Gemma model.""" + + hidden_size: int + hidden_act: str + intermediate_size: int + attention_bias: bool + num_attention_heads: int + num_key_value_heads: int + head_dim: int + num_hidden_layers: int + rms_norm_eps: float + vocab_size: int + position_embedding_base: int = 0 + context_window_size: int = 0 + prefill_chunk_size: int = 0 + tensor_parallel_shards: int = 1 + max_batch_size: int = 1 + kwargs: Dict[str, Any] = dataclasses.field(default_factory=dict) + + def __post_init__(self): + if self.hidden_act != "gelu": + raise ValueError("Only GeLU is supported as the activation for gemma.") + if self.attention_bias: + raise ValueError('Only "False" attention_bias is supported for gemma') + if self.position_embedding_base == 0: + if "rope_theta" in self.kwargs: + self.position_embedding_base = self.kwargs.pop("rope_theta") + else: + self.position_embedding_base = 10000 + if self.context_window_size == 0: + for name in ["max_position_embeddings", "max_sequence_length"]: + if name in self.kwargs: + self.context_window_size = self.kwargs.pop(name) + logger.info( + "%s not found in config.json. Falling back to %s (%d)", + bold("context_window_size"), + bold(name), + self.context_window_size, + ) + break + else: + raise ValueError( + "Unable to determine the maxmimum sequence length, because none of " + "`context_window_size`, `max_position_embeddings` or `max_sequence_length` is " + "provided in `config.json`." + ) + assert self.num_attention_heads % self.num_key_value_heads == 0 + if self.prefill_chunk_size == 0: + logger.info( + "%s defaults to %s (%d)", + bold("prefill_chunk_size"), + bold("context_window_size"), + self.context_window_size, + ) + self.prefill_chunk_size = self.context_window_size + elif self.prefill_chunk_size > self.context_window_size: + logger.info( + "Overriding %s from %d to %d (%s)", + bold("prefill_chunk_size"), + self.prefill_chunk_size, + self.context_window_size, + bold("context_window_size"), + ) + self.prefill_chunk_size = self.context_window_size + + +# pylint: disable=invalid-name,missing-docstring + + +class GemmaEmbedding(nn.Embedding): + """The embedding module specialized for Gemma so that + it can be shared with the final lm_head. + """ + + def lm_head_forward(self, x: nn.Tensor): + """The lm_head forwarding, which transposes the weight and multiplies + with the input tensor. + """ + weight = nn.op.permute_dims(self.weight) + return nn.op.matmul(x, weight, out_dtype="float32") + + +class GemmaMLP(nn.Module): + def __init__(self, config: GemmaConfig): + super().__init__() + self.intermediate_size = config.intermediate_size // config.tensor_parallel_shards + self.gate_up_proj = nn.Linear( + in_features=config.hidden_size, + out_features=2 * self.intermediate_size, + bias=False, + ) + self.down_proj = nn.Linear(self.intermediate_size, config.hidden_size, bias=False) + + def forward(self, x: Tensor): + concat_x1_x2 = self.gate_up_proj(x) + x1, x2 = op.split(concat_x1_x2, 2, axis=-1) + return self.down_proj(op.gelu(x1) * x2) + + +class GemmaAttention(nn.Module): # pylint: disable=too-many-instance-attributes + def __init__(self, config: GemmaConfig): + self.head_dim = config.head_dim + self.num_q_heads = config.num_attention_heads // config.tensor_parallel_shards + assert ( + config.num_key_value_heads % config.tensor_parallel_shards == 0 + ), f"num_kv_heads({config.num_key_value_heads}) must be divisible by tensor_parallel_shards" + assert ( + config.num_key_value_heads >= config.tensor_parallel_shards + ), f"Too large tensor_parallel_shards, must be smaller than {config.num_key_value_heads}" + self.num_kv_heads = config.num_key_value_heads // config.tensor_parallel_shards + self.qkv_proj = nn.Linear( + in_features=config.hidden_size, + out_features=(self.num_q_heads + 2 * self.num_kv_heads) * self.head_dim, + bias=config.attention_bias, + ) + self.o_proj = nn.Linear( + in_features=self.num_q_heads * self.head_dim, + out_features=config.hidden_size, + bias=config.attention_bias, + ) + + def forward(self, hidden_states: Tensor, paged_kv_cache: PagedKVCache, layer_id: int): + d, h_q, h_kv = self.head_dim, self.num_q_heads, self.num_kv_heads + b, s, _ = hidden_states.shape + # QKV Projection + qkv = self.qkv_proj(hidden_states) + qkv = op.reshape(qkv, (b, s, h_q + h_kv + h_kv, d)) + # Attention + output = op.reshape( + paged_kv_cache.attention_with_fused_qkv(layer_id, qkv, self.num_q_heads), + (b, s, h_q * d), + ) + return self.o_proj(output) + + +class GemmaDecoderLayer(nn.Module): + def __init__(self, config: GemmaConfig): + rms_norm_eps = config.rms_norm_eps + self.self_attn = GemmaAttention(config) + self.mlp = GemmaMLP(config) + # Gemma RMSNorm adds 1 to the weights. It is already fused in the loader + self.input_layernorm = nn.RMSNorm(config.hidden_size, -1, rms_norm_eps, bias=False) + self.post_attention_layernorm = nn.RMSNorm(config.hidden_size, -1, rms_norm_eps, bias=False) + + def _set_tp(): + def _set(layer, hint): + layer.weight.attrs["shard_strategy"] = hint + + hd = config.head_dim + q = self.self_attn.num_q_heads * hd + k = self.self_attn.num_kv_heads * hd + v = self.self_attn.num_kv_heads * hd + i = self.mlp.intermediate_size + _set(self.self_attn.qkv_proj, tp.ShardSingleDim("_shard_qkv", segs=[q, k, v], dim=0)) + _set(self.self_attn.o_proj, tp.ShardSingleDim("_shard_o", dim=1)) + _set(self.mlp.gate_up_proj, tp.ShardSingleDim("_shard_mlp_up", segs=[i, i], dim=0)) + _set(self.mlp.down_proj, tp.ShardSingleDim("_shard_mlp_down", dim=1)) + + self.tensor_parallel_shards = config.tensor_parallel_shards + _set_tp() + + def forward(self, hidden_states: Tensor, paged_kv_cache: PagedKVCache, layer_id: int): + out = self.self_attn(self.input_layernorm(hidden_states), paged_kv_cache, layer_id) + hidden_states = self._apply_residual(out, residual=hidden_states) + out = self.mlp(self.post_attention_layernorm(hidden_states)) + hidden_states = self._apply_residual(out, residual=hidden_states) + return hidden_states + + def _apply_residual(self, out, residual): + if self.tensor_parallel_shards > 1: + return op.ccl_allreduce(out, "sum") + residual + return out + residual + + +class GemmaModel(nn.Module): + def __init__(self, config: GemmaConfig): + self.hidden_size = config.hidden_size + assert config.hidden_size % config.num_attention_heads == 0 + self.embed_tokens = GemmaEmbedding("vocab_size", config.hidden_size) + self.layers = nn.ModuleList( + [GemmaDecoderLayer(config) for _ in range(config.num_hidden_layers)] + ) + self.norm = nn.RMSNorm(config.hidden_size, -1, config.rms_norm_eps, bias=False) + self.tensor_parallel_shards = config.tensor_parallel_shards + + def forward(self, input_embed: Tensor, paged_kv_cache: PagedKVCache): + if self.tensor_parallel_shards > 1: + input_embed = op.ccl_broadcast_from_worker0(input_embed) + hidden_states = input_embed + hidden_states = hidden_states * (self.hidden_size**0.5) + for layer_id, layer in enumerate(self.layers): + hidden_states = layer(hidden_states, paged_kv_cache, layer_id) + hidden_states = self.norm(hidden_states) + return hidden_states + + +class GemmaForCausalLM(nn.Module): # pylint: disable=too-many-instance-attributes + def __init__(self, config: GemmaConfig): + self.model = GemmaModel(config) + self.num_hidden_layers = config.num_hidden_layers + self.num_attention_heads = config.num_attention_heads + self.num_key_value_heads = config.num_key_value_heads + self.head_dim = config.head_dim + self.hidden_size = config.hidden_size + self.vocab_size = config.vocab_size + self.rope_theta = config.position_embedding_base + self.tensor_parallel_shards = config.tensor_parallel_shards + self.dtype = "float32" + + def to(self, dtype: Optional[str] = None): + super().to(dtype=dtype) + if dtype is not None: + self.dtype = dtype + + def batch_forward( + self, + input_embeds: Tensor, + paged_kv_cache: PagedKVCache, + logit_positions: Optional[Tensor] = None, + ): + op_ext.configure() + + hidden_states = self.model(input_embeds, paged_kv_cache) + if logit_positions is not None: + hidden_states = op.take(hidden_states, logit_positions, axis=1) + logits = self.model.embed_tokens.lm_head_forward(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits + + def embed(self, input_ids: Tensor): + return self.model.embed_tokens(input_ids) + + def prefill(self, input_embed: Tensor, paged_kv_cache: PagedKVCache): + op_ext.configure() + + def _index(x: te.Tensor): # x[:-1,:] + b, s, d = x.shape + return te.compute((b, 1, d), lambda i, _, k: x[i, s - 1, k], name="index") + + hidden_states = self.model(input_embed, paged_kv_cache) + hidden_states = op.tensor_expr_op(_index, name_hint="index", args=[hidden_states]) + logits = self.model.embed_tokens.lm_head_forward(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits, paged_kv_cache + + def decode(self, input_embed: Tensor, paged_kv_cache: PagedKVCache): + op_ext.configure() + + hidden_states = self.model(input_embed, paged_kv_cache) + logits = self.model.embed_tokens.lm_head_forward(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits, paged_kv_cache + + def batch_prefill( + self, input_embeds: Tensor, logit_positions: Tensor, paged_kv_cache: PagedKVCache + ): + logits = self.batch_forward(input_embeds, paged_kv_cache, logit_positions) + return logits, paged_kv_cache + + def batch_decode(self, input_embeds: Tensor, paged_kv_cache: PagedKVCache): + logits = self.batch_forward(input_embeds, paged_kv_cache) + return logits, paged_kv_cache + + def batch_verify(self, input_embeds: Tensor, paged_kv_cache: PagedKVCache): + logits = self.batch_forward(input_embeds, paged_kv_cache) + return logits, paged_kv_cache + + def softmax_with_temperature(self, logits: Tensor, temperature: Tensor): + return op.softmax(logits / op.reshape(temperature, (temperature.shape[0], 1, 1)), axis=-1) + + def create_paged_kv_cache( + self, + max_batch_size: tir.Var, + max_total_seq_len: tir.Var, + prefill_chunk_size: tir.Var, + page_size: tir.Var, + ) -> PagedKVCache: + return PagedKVCache.create_generic( + max_batch_size=max_batch_size, + max_total_seq_len=max_total_seq_len, + prefill_chunk_size=prefill_chunk_size, + page_size=page_size, + num_hidden_layers=self.num_hidden_layers, + num_attention_heads=self.num_attention_heads // self.tensor_parallel_shards, + num_key_value_heads=self.num_key_value_heads // self.tensor_parallel_shards, + head_dim=self.head_dim, + rope_mode=RopeMode.NORMAL, + rope_scale=1, + rope_theta=self.rope_theta, + dtype=self.dtype, + ) + + def get_default_spec(self): + mod_spec = { + "embed": { + "input_ids": nn.spec.Tensor([1, "seq_len"], "int32"), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "prefill": { + "input_embed": nn.spec.Tensor([1, "seq_len", self.hidden_size], self.dtype), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "decode": { + "input_embed": nn.spec.Tensor([1, 1, self.hidden_size], self.dtype), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "batch_prefill": { + "input_embeds": nn.spec.Tensor([1, "seq_len", self.hidden_size], self.dtype), + "logit_positions": nn.spec.Tensor(["batch_size"], "int32"), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "batch_decode": { + "input_embeds": nn.spec.Tensor(["batch_size", 1, self.hidden_size], self.dtype), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "batch_verify": { + "input_embeds": nn.spec.Tensor([1, "seq_len", self.hidden_size], self.dtype), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "softmax_with_temperature": { + "logits": nn.spec.Tensor(["batch_size", 1, "vocab_size"], "float32"), + "temperature": nn.spec.Tensor(["batch_size"], "float32"), + "$": { + "param_mode": "none", + "effect_mode": "none", + }, + }, + "create_paged_kv_cache": { + "max_batch_size": int, + "max_total_seq_len": int, + "prefill_chunk_size": int, + "page_size": int, + "$": { + "param_mode": "none", + "effect_mode": "none", + }, + }, + } + return nn.spec.ModuleSpec.from_raw(mod_spec, self) diff --git a/python/mlc_chat/model/gemma/gemma_quantization.py b/python/mlc_chat/model/gemma/gemma_quantization.py new file mode 100644 index 0000000..28b4234 --- /dev/null +++ b/python/mlc_chat/model/gemma/gemma_quantization.py @@ -0,0 +1,38 @@ +"""This file specifies how MLC's Gemma parameters are quantized using group quantization +or other formats.""" + +from typing import Tuple + +from tvm.relax.frontend import nn + +from mlc_chat.loader import QuantizeMapping +from mlc_chat.quantization import GroupQuantize, NoQuantize + +from .gemma_model import GemmaConfig, GemmaForCausalLM + + +def group_quant( + model_config: GemmaConfig, + quantization: GroupQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Gemma-architecture model using group quantization.""" + model: nn.Module = GemmaForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def no_quant( + model_config: GemmaConfig, + quantization: NoQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Llama2 model without quantization.""" + model: nn.Module = GemmaForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + return model, quant_map diff --git a/python/mlc_chat/model/gpt2/__init__.py b/python/mlc_chat/model/gpt2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/mlc_chat/model/gpt2/gpt2_loader.py b/python/mlc_chat/model/gpt2/gpt2_loader.py new file mode 100644 index 0000000..43c4ff1 --- /dev/null +++ b/python/mlc_chat/model/gpt2/gpt2_loader.py @@ -0,0 +1,79 @@ +""" +This file specifies how MLC's GPT-2 parameter maps from other formats, for example HuggingFace +PyTorch, HuggingFace safetensors. +""" +import functools + +from mlc_chat.loader import ExternMapping +from mlc_chat.quantization import Quantization + +from .gpt2_model import GPT2Config, GPT2LMHeadModel + + +def huggingface(model_config: GPT2Config, quantization: Quantization) -> ExternMapping: + """Returns a parameter mapping that maps from the names of MLC LLM parameters to + the names of HuggingFace PyTorch parameters. + + Parameters + ---------- + model_config : GPT2Config + The configuration of the GPT-2 model. + + quantization : Quantization + The quantization configuration. + + Returns + ------- + param_map : ExternMapping + The parameter mapping from MLC to HuggingFace PyTorch. + """ + model = GPT2LMHeadModel(model_config) + if quantization is not None: + model.to(quantization.model_dtype) + _, _named_params, _ = model.export_tvm( # type: ignore[misc] + spec=model.get_default_spec(), + allow_extern=True, + ) + named_parameters = dict(_named_params) + + mapping = ExternMapping() + + mapping.add_mapping( + "lm_head.weight", + ["wte.weight"], + functools.partial( + lambda x, dtype: x.astype(dtype), + dtype=named_parameters["transformer.wte.weight"].dtype, + ), + ) + + for i in range(model_config.n_layer): + mapping.add_unused(f"h.{i}.attn.bias") + + # Transpose c_attn, c_proj and c_fc weights since GPT-2 uses Conv1D + for conv1d_weight_name in ["attn.c_attn", "attn.c_proj", "mlp.c_proj", "mlp.c_fc"]: + src_name = f"h.{i}.{conv1d_weight_name}.weight" + mlc_name = f"transformer.{src_name}" + mapping.add_mapping( + mlc_name, + [src_name], + functools.partial( + lambda x, dtype: x.transpose().astype(dtype), + dtype=named_parameters[mlc_name].dtype, + ), + ) + + for mlc_name, mlc_param in named_parameters.items(): + if mlc_name not in mapping.param_map: + # transformer.h.0.attn.c_attn.weight --> h.0.attn.c_attn.weight + source_name = mlc_name.split(".", 1)[1] + mapping.add_mapping( + mlc_name, + [source_name], + functools.partial( + lambda x, dtype: x.astype(dtype), + dtype=mlc_param.dtype, + ), + ) + + return mapping diff --git a/python/mlc_chat/model/gpt2/gpt2_model.py b/python/mlc_chat/model/gpt2/gpt2_model.py new file mode 100644 index 0000000..911f0dd --- /dev/null +++ b/python/mlc_chat/model/gpt2/gpt2_model.py @@ -0,0 +1,433 @@ +""" +Implementation for GPT-2 architecture. +TODO: add docstring +""" + +import dataclasses +from typing import Any, Dict, Optional + +from tvm import te, tir +from tvm.relax.frontend import nn +from tvm.relax.frontend.nn import Tensor, op + +from mlc_chat import op as op_ext +from mlc_chat.nn import PagedKVCache, RopeMode +from mlc_chat.support import logging +from mlc_chat.support import tensor_parallel as tp +from mlc_chat.support.config import ConfigBase +from mlc_chat.support.style import bold + +logger = logging.getLogger(__name__) + + +@dataclasses.dataclass +class GPT2Config(ConfigBase): # pylint: disable=too-many-instance-attributes + """Configuration of the GPT-2 model.""" + + vocab_size: int + n_embd: int + n_layer: int + n_head: int + layer_norm_epsilon: int + n_inner: int = -1 + context_window_size: int = 0 + prefill_chunk_size: int = 0 + scale_attn_by_inverse_layer_idx: bool = False + tensor_parallel_shards: int = 1 + head_dim: int = 0 + kwargs: Dict[str, Any] = dataclasses.field(default_factory=dict) + + def __post_init__(self): + if self.n_inner is None or self.n_inner == -1: + self.n_inner = 4 * self.n_embd + if self.context_window_size == 0: + for name in ["n_positions", "max_sequence_length"]: + if name in self.kwargs: + self.context_window_size = self.kwargs.pop(name) + logger.info( + "%s not found in config.json. Falling back to %s (%d)", + bold("context_window_size"), + bold(name), + self.context_window_size, + ) + break + else: + raise ValueError( + "Unable to determine the maxmimum sequence length, because none of " + "`context_window_size`, `n_positions` or `max_sequence_length` is " + "provided in `config.json`." + ) + if self.head_dim == 0: + self.head_dim = self.n_embd // self.n_head + assert self.head_dim * self.n_head == self.n_embd + if self.prefill_chunk_size == 0: + logger.info( + "%s defaults to %s (%d)", + bold("prefill_chunk_size"), + bold("context_window_size"), + self.context_window_size, + ) + self.prefill_chunk_size = self.context_window_size + elif self.prefill_chunk_size > self.context_window_size: + logger.info( + "Overriding %s from %d to %d (%s)", + bold("prefill_chunk_size"), + self.prefill_chunk_size, + self.context_window_size, + bold("context_window_size"), + ) + self.prefill_chunk_size = self.context_window_size + + +# pylint: disable=invalid-name,missing-docstring,too-many-locals + + +class GPT2Attention(nn.Module): # pylint: disable=too-many-instance-attributes + def __init__(self, config: GPT2Config): + self.embed_dim = config.n_embd + self.num_heads = config.n_head // config.tensor_parallel_shards + self.head_dim = config.head_dim + self.scale_attn_by_inverse_layer_idx = config.scale_attn_by_inverse_layer_idx + + self.c_attn = nn.Linear( + in_features=self.embed_dim, + out_features=3 * self.num_heads * self.head_dim, + bias=True, + ) + self.c_proj = nn.Linear(self.num_heads * self.head_dim, self.embed_dim, bias=True) + + self.k_cache = nn.KVCache(config.context_window_size, [self.num_heads, self.head_dim]) + self.v_cache = nn.KVCache(config.context_window_size, [self.num_heads, self.head_dim]) + + def forward(self, hidden_states: Tensor, paged_kv_cache: PagedKVCache, layer_id: int): + d, h = self.head_dim, self.num_heads + b, s, _ = hidden_states.shape + + qkv = self.c_attn(hidden_states) + qkv = op.reshape(qkv, (b, s, 3 * h, d)) + + if self.scale_attn_by_inverse_layer_idx: + attn_score_scaling_factor = 1.0 / float(layer_id + 1) + else: + attn_score_scaling_factor = 1.0 + + # Attention + output = op.reshape( + paged_kv_cache.attention_with_fused_qkv( + layer_id, qkv, self.num_heads, attn_score_scaling_factor + ), + (b, s, h * d), + ) + return self.c_proj(output) + + def batch_forward(self, hidden_states: Tensor, paged_kv_cache: PagedKVCache, layer_id: int): + d, h = self.head_dim, self.num_heads + b, s, _ = hidden_states.shape + + qkv = self.c_attn(hidden_states) + qkv = op.reshape(qkv, (b, s, 3 * h, d)) + + if self.scale_attn_by_inverse_layer_idx: + attn_score_scaling_factor = 1.0 / float(layer_id + 1) + else: + attn_score_scaling_factor = 1.0 + + # Attention + output = op.reshape( + paged_kv_cache.attention_with_fused_qkv( + layer_id, qkv, self.num_heads, attn_score_scaling_factor + ), + (b, s, h * d), + ) + return self.c_proj(output) + + +class GPT2MLP(nn.Module): + def __init__(self, config: GPT2Config): + embed_dim = config.n_embd + intermediate_size = config.n_inner // config.tensor_parallel_shards + self.c_fc = nn.Linear(embed_dim, intermediate_size) + self.c_proj = nn.Linear(intermediate_size, embed_dim) + + def forward(self, hidden_states: Tensor): + hidden_states = self.c_fc(hidden_states) + hidden_states = op.gelu(hidden_states, approximate="tanh") + hidden_states = self.c_proj(hidden_states) + return hidden_states + + +class GPT2Block(nn.Module): + def __init__(self, config: GPT2Config): + hidden_size = config.n_embd + self.ln_1 = nn.LayerNorm(hidden_size, eps=config.layer_norm_epsilon) + self.attn = GPT2Attention(config) + self.ln_2 = nn.LayerNorm(hidden_size, eps=config.layer_norm_epsilon) + self.mlp = GPT2MLP(config) + + def _set_tp(): + def _set(param, hint): + param.attrs["shard_strategy"] = hint + + hd = config.head_dim + q = k = v = self.attn.num_heads * hd + _set( + self.attn.c_attn.weight, + tp.ShardSingleDim("_shard_qkv_weight", dim=0, segs=[q, k, v]), + ) + _set( + self.attn.c_attn.bias, + tp.ShardSingleDim("_shard_qkv_bias", dim=0, segs=[q, k, v]), + ) + _set(self.attn.c_proj.weight, tp.ShardSingleDim("_shard_attn_c_proj", dim=1)) + _set( + self.mlp.c_fc.weight, + tp.ShardSingleDim("_shard_c_fc_weight", dim=0), + ) + _set(self.mlp.c_fc.bias, tp.ShardSingleDim("_shard_c_fc_bias", dim=0)) + _set(self.mlp.c_proj.weight, tp.ShardSingleDim("_shard_mlp_c_proj", dim=1)) + + self.tensor_parallel_shards = config.tensor_parallel_shards + _set_tp() + + def forward(self, hidden_states: Tensor, paged_kv_cache: PagedKVCache, layer_id: int): + with tp.shard_bias(self.attn.c_proj, self.tensor_parallel_shards), tp.shard_bias( + self.mlp.c_proj, self.tensor_parallel_shards + ): + hidden_states = self._apply_residual( + self.attn(self.ln_1(hidden_states), paged_kv_cache, layer_id), hidden_states + ) + hidden_states = self._apply_residual(self.mlp(self.ln_2(hidden_states)), hidden_states) + + return hidden_states + + def batch_forward(self, hidden_states: Tensor, paged_kv_cache: PagedKVCache, layer_id: int): + with tp.shard_bias(self.attn.c_proj, self.tensor_parallel_shards), tp.shard_bias( + self.mlp.c_proj, self.tensor_parallel_shards + ): + hidden_states = self._apply_residual( + self.attn.batch_forward(self.ln_1(hidden_states), paged_kv_cache, layer_id), + hidden_states, + ) + hidden_states = self._apply_residual(self.mlp(self.ln_2(hidden_states)), hidden_states) + + return hidden_states + + def _apply_residual(self, out, residual): + if self.tensor_parallel_shards > 1: + return op.ccl_allreduce(out + residual / self.tensor_parallel_shards, "sum") + return out + residual + + +class GPT2Model(nn.Module): + def __init__(self, config: GPT2Config): + assert config.n_embd % config.n_head == 0 + self.wte = nn.Embedding("vocab_size", config.n_embd) + self.wpe = nn.Embedding(config.context_window_size, config.n_embd) + self.h = nn.ModuleList([GPT2Block(config) for _ in range(config.n_layer)]) + self.ln_f = nn.LayerNorm(config.n_embd, eps=config.layer_norm_epsilon) + self.tensor_parallel_shards = config.tensor_parallel_shards + + def forward(self, inputs: Tensor, paged_kv_cache: PagedKVCache): + if self.tensor_parallel_shards > 1: + inputs = op.ccl_broadcast_from_worker0(inputs) + hidden_states = inputs + + # Position Embeddings + # Generate np.arange(offset, offset+seq_len) + # shape[1] indicates the total query length in the batch + input_positions = paged_kv_cache.get_query_positions(inputs.shape[1]) + pos_embd = self.wpe(input_positions) + + # Pass through GPT2Block + hidden_states = inputs + pos_embd + for layer_id, layer in enumerate(self.h): + hidden_states = layer(hidden_states, paged_kv_cache, layer_id) + hidden_states = self.ln_f(hidden_states) + return hidden_states + + def batch_forward(self, inputs: Tensor, paged_kv_cache: PagedKVCache): + if self.tensor_parallel_shards > 1: + inputs = op.ccl_broadcast_from_worker0(inputs) + hidden_states = inputs + + # Position Embeddings + # Generate np.arange(offset, offset+seq_len) + # shape[1] indicates the total query length in the batch + input_positions = paged_kv_cache.get_query_positions(inputs.shape[1]) + pos_embd = self.wpe(input_positions) + + # Pass through GPT2Block + hidden_states = hidden_states + pos_embd + for layer_id, layer in enumerate(self.h): + hidden_states = layer.batch_forward(hidden_states, paged_kv_cache, layer_id) + hidden_states = self.ln_f(hidden_states) + return hidden_states + + +class GPT2LMHeadModel(nn.Module): # pylint: disable=too-many-instance-attributes + def __init__(self, config: GPT2Config): + self.transformer = GPT2Model(config) + self.lm_head = nn.Linear(config.n_embd, "vocab_size", bias=False) + self.n_layer = config.n_layer + self.n_embed = config.n_embd + self.n_head = config.n_head + self.head_dim = config.head_dim + self.tensor_parallel_shards = config.tensor_parallel_shards + self.dtype = "float32" + + def to(self, dtype: Optional[str] = None): + super().to(dtype=dtype) + if dtype is not None: + self.dtype = dtype + + def batch_forward( + self, + input_embeds: Tensor, + paged_kv_cache: PagedKVCache, + logit_positions: Optional[Tensor] = None, + ): + op_ext.configure() + + hidden_states = self.transformer.batch_forward(input_embeds, paged_kv_cache) + if logit_positions is not None: + hidden_states = op.take(hidden_states, logit_positions, axis=1) + logits = self.lm_head(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits + + def embed(self, input_ids: Tensor): + return self.transformer.wte(input_ids) + + def prefill(self, input_embed: Tensor, paged_kv_cache: PagedKVCache): + op_ext.configure() + + def _index(x: te.Tensor): # x[:-1,:] + b, s, d = x.shape + return te.compute((b, 1, d), lambda i, _, k: x[i, s - 1, k], name="index") + + hidden_states = self.transformer(input_embed, paged_kv_cache) + hidden_states = op.tensor_expr_op(_index, name_hint="index", args=[hidden_states]) + logits = self.lm_head(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits, paged_kv_cache + + def decode(self, input_embed: Tensor, paged_kv_cache: PagedKVCache): + op_ext.configure() + + hidden_states = self.transformer(input_embed, paged_kv_cache) + logits = self.lm_head(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits, paged_kv_cache + + def batch_prefill( + self, input_embeds: Tensor, logit_positions: Tensor, paged_kv_cache: PagedKVCache + ): + logits = self.batch_forward(input_embeds, paged_kv_cache, logit_positions) + return logits, paged_kv_cache + + def batch_decode(self, input_embeds: Tensor, paged_kv_cache: PagedKVCache): + logits = self.batch_forward(input_embeds, paged_kv_cache) + return logits, paged_kv_cache + + def batch_verify(self, input_embeds: Tensor, paged_kv_cache: PagedKVCache): + logits = self.batch_forward(input_embeds, paged_kv_cache) + return logits, paged_kv_cache + + def softmax_with_temperature(self, logits: Tensor, temperature: Tensor): + return op.softmax(logits / op.reshape(temperature, (temperature.shape[0], 1, 1)), axis=-1) + + def create_paged_kv_cache( + self, + max_batch_size: tir.Var, + max_total_seq_len: tir.Var, + prefill_chunk_size: tir.Var, + page_size: tir.Var, + ) -> PagedKVCache: + return PagedKVCache.create_generic( + max_batch_size=max_batch_size, + max_total_seq_len=max_total_seq_len, + prefill_chunk_size=prefill_chunk_size, + page_size=page_size, + num_hidden_layers=self.n_layer, + num_attention_heads=self.n_head // self.tensor_parallel_shards, + num_key_value_heads=self.n_head // self.tensor_parallel_shards, + head_dim=self.head_dim, + rope_mode=RopeMode.NONE, + rope_scale=-1, + rope_theta=-1, + dtype=self.dtype, + ) + + def get_default_spec(self): + mod_spec = { + "embed": { + "input_ids": nn.spec.Tensor([1, "seq_len"], "int32"), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "prefill": { + "input_embed": nn.spec.Tensor([1, "seq_len", self.n_embed], self.dtype), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "decode": { + "input_embed": nn.spec.Tensor([1, 1, self.n_embed], self.dtype), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "batch_prefill": { + "input_embeds": nn.spec.Tensor([1, "seq_len", self.n_embed], self.dtype), + "logit_positions": nn.spec.Tensor(["batch_size"], "int32"), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "batch_decode": { + "input_embeds": nn.spec.Tensor(["batch_size", 1, self.n_embed], self.dtype), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "batch_verify": { + "input_embeds": nn.spec.Tensor([1, "seq_len", self.n_embed], self.dtype), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "softmax_with_temperature": { + "logits": nn.spec.Tensor(["batch_size", 1, "vocab_size"], "float32"), + "temperature": nn.spec.Tensor(["batch_size"], "float32"), + "$": { + "param_mode": "none", + "effect_mode": "none", + }, + }, + "create_paged_kv_cache": { + "max_batch_size": int, + "max_total_seq_len": int, + "prefill_chunk_size": int, + "page_size": int, + "$": { + "param_mode": "none", + "effect_mode": "none", + }, + }, + } + return nn.spec.ModuleSpec.from_raw(mod_spec, self) diff --git a/python/mlc_chat/model/gpt2/gpt2_quantization.py b/python/mlc_chat/model/gpt2/gpt2_quantization.py new file mode 100644 index 0000000..b953d8c --- /dev/null +++ b/python/mlc_chat/model/gpt2/gpt2_quantization.py @@ -0,0 +1,69 @@ +"""This file specifies how MLC's GPT-2 parameters are quantized using group quantization +or other formats.""" +from typing import Tuple + +from tvm.relax.frontend import nn + +from mlc_chat.loader import QuantizeMapping +from mlc_chat.quantization import AWQQuantize, FTQuantize, GroupQuantize, NoQuantize + +from .gpt2_model import GPT2Config, GPT2LMHeadModel + + +def group_quant( + model_config: GPT2Config, + quantization: GroupQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a GPT-2-architecture model using group quantization.""" + model: nn.Module = GPT2LMHeadModel(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def ft_quant( + model_config: GPT2Config, + quantization: FTQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a GPT-2-architecture model using FasterTransformer quantization.""" + model: nn.Module = GPT2LMHeadModel(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def awq_quant( + model_config: GPT2Config, + quantization: AWQQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a GPT-2-architecture model using Activation-aware Weight Quantization(AWQ).""" + model: nn.Module = GPT2LMHeadModel(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def no_quant( + model_config: GPT2Config, + quantization: NoQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a GPT-2 model without quantization.""" + model: nn.Module = GPT2LMHeadModel(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + return model, quant_map diff --git a/python/mlc_chat/model/gpt_bigcode/__init__.py b/python/mlc_chat/model/gpt_bigcode/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/mlc_chat/model/gpt_bigcode/gpt_bigcode_loader.py b/python/mlc_chat/model/gpt_bigcode/gpt_bigcode_loader.py new file mode 100644 index 0000000..8d479d3 --- /dev/null +++ b/python/mlc_chat/model/gpt_bigcode/gpt_bigcode_loader.py @@ -0,0 +1,49 @@ +""" +This file specifies how MLC's GPTBigCode parameter maps from other formats, for example HuggingFace +PyTorch, HuggingFace safetensors. +""" +import functools + +from mlc_chat.loader import ExternMapping +from mlc_chat.quantization import Quantization + +from .gpt_bigcode_model import GPTBigCodeConfig, GPTBigCodeForCausalLM + + +def huggingface(model_config: GPTBigCodeConfig, quantization: Quantization) -> ExternMapping: + """Returns a parameter mapping that maps from the names of MLC LLM parameters to + the names of HuggingFace PyTorch parameters. + + Parameters + ---------- + model_config : GPTBigCodeConfig + The configuration of the GPTBigCode model. + + quantization : Quantization + The quantization configuration. + + Returns + ------- + param_map : ExternMapping + The parameter mapping from MLC to HuggingFace PyTorch. + """ + model = GPTBigCodeForCausalLM(model_config) + if quantization is not None: + model.to(quantization.model_dtype) + _, _named_params, _ = model.export_tvm( # type: ignore[misc] + spec=model.get_default_spec(), + allow_extern=True, + ) + named_parameters = dict(_named_params) + + mapping = ExternMapping() + + for mlc_name, mlc_param in named_parameters.items(): + if mlc_name not in mapping.param_map: + mapping.add_mapping( + mlc_name, + [mlc_name], + functools.partial(lambda x, dtype: x.astype(dtype), dtype=mlc_param.dtype), + ) + + return mapping diff --git a/python/mlc_chat/model/gpt_bigcode/gpt_bigcode_model.py b/python/mlc_chat/model/gpt_bigcode/gpt_bigcode_model.py new file mode 100644 index 0000000..10a0291 --- /dev/null +++ b/python/mlc_chat/model/gpt_bigcode/gpt_bigcode_model.py @@ -0,0 +1,289 @@ +""" +Implementation for GPTBigCode architecture. +TODO: add docstring +""" +import dataclasses +from typing import Any, Dict, Optional + +from tvm import te, tir +from tvm.relax.frontend import nn +from tvm.relax.frontend.nn import Tensor, op + +from mlc_chat import op as op_ext +from mlc_chat.support import logging +from mlc_chat.support import tensor_parallel as tp +from mlc_chat.support.config import ConfigBase +from mlc_chat.support.style import bold + +logger = logging.getLogger(__name__) + + +@dataclasses.dataclass +class GPTBigCodeConfig(ConfigBase): # pylint: disable=too-many-instance-attributes + """Configuration of the GPTBigCode model.""" + + n_embd: int + n_inner: int + n_head: int + n_layer: int + n_positions: int + layer_norm_epsilon: float + vocab_size: int + context_window_size: int = 0 + prefill_chunk_size: int = 0 + tensor_parallel_shards: int = 1 + kwargs: Dict[str, Any] = dataclasses.field(default_factory=dict) + + def __post_init__(self): + if self.context_window_size == 0: + if self.n_positions > 0: + self.context_window_size = self.n_positions + logger.info( + "%s not found in config.json. Falling back to %s (%d)", + bold("context_window_size"), + bold("n_positions"), + self.context_window_size, + ) + else: + raise ValueError( + "Unable to determine the maximum sequence length, because none of " + "`context_window_size`, `max_position_embeddings` or `max_sequence_length` is " + "provided in `config.json`." + ) + if self.prefill_chunk_size == 0: + logger.info( + "%s defaults to %s (%d)", + bold("prefill_chunk_size"), + bold("context_window_size"), + self.context_window_size, + ) + self.prefill_chunk_size = self.context_window_size + elif self.prefill_chunk_size > self.context_window_size: + logger.info( + "Overriding %s from %d to %d (%s)", + bold("prefill_chunk_size"), + self.prefill_chunk_size, + self.context_window_size, + bold("context_window_size"), + ) + self.prefill_chunk_size = self.context_window_size + + +# pylint: disable=invalid-name,missing-docstring + + +class GPTBigCodeMLP(nn.Module): + def __init__(self, config: GPTBigCodeConfig): + super().__init__() + self.n_inner = config.n_inner // config.tensor_parallel_shards + self.c_fc = nn.Linear(in_features=config.n_embd, out_features=self.n_inner, bias=True) + self.c_proj = nn.Linear(in_features=self.n_inner, out_features=config.n_embd, bias=True) + + def forward(self, x: Tensor): + hidden_states = self.c_fc(x) + hidden_states = op.gelu(hidden_states) + hidden_states = self.c_proj(hidden_states) + return hidden_states + + +class GPTBigCodeAttention(nn.Module): # pylint: disable=too-many-instance-attributes + def __init__(self, config: GPTBigCodeConfig): + self.n_embd = config.n_embd + self.head_dim = config.n_embd // config.n_head + self.num_q_heads = config.n_head // config.tensor_parallel_shards + self.num_kv_heads = 1 + assert ( + config.tensor_parallel_shards == 1 + ), "GPT bigcode only support tensor parallel shards = 1" + self.c_attn = nn.Linear( + in_features=self.n_embd, + out_features=(self.num_q_heads + 2 * self.num_kv_heads) * self.head_dim, + bias=True, + ) + self.c_proj = nn.Linear( + in_features=self.num_q_heads * self.head_dim, + out_features=config.n_embd, + bias=True, + ) + + self.k_cache = nn.KVCache(config.context_window_size, [self.num_kv_heads, self.head_dim]) + self.v_cache = nn.KVCache(config.context_window_size, [self.num_kv_heads, self.head_dim]) + + def forward( # pylint: disable=too-many-locals + self, + hidden_states: Tensor, + attention_mask: Tensor, + total_seq_len: tir.Var, + ): + d, h_q, h_kv, t = self.head_dim, self.num_q_heads, self.num_kv_heads, total_seq_len + b, s, _ = hidden_states.shape + assert b == 1, "Only support batch size 1 at this moment." + + qkv = self.c_attn(hidden_states) + qkv = op.reshape(qkv, (b, s, h_q + 2 * h_kv, d)) + q, k, v = op.split(qkv, indices_or_sections=[h_q, h_q + h_kv], axis=2) + + self.k_cache.append(op.squeeze(k, axis=0)) + self.v_cache.append(op.squeeze(v, axis=0)) + k = self.k_cache.view(t) + v = self.v_cache.view(t) + output = op_ext.attention(q, k, v, casual_mask=attention_mask) + return self.c_proj(output) + + +class GPTBigCodeBlock(nn.Module): + def __init__(self, config: GPTBigCodeConfig): + self.ln_1 = nn.LayerNorm(config.n_embd, eps=config.layer_norm_epsilon) + self.attn = GPTBigCodeAttention(config) + self.ln_2 = nn.LayerNorm(config.n_embd, eps=config.layer_norm_epsilon) + self.mlp = GPTBigCodeMLP(config) + + def _set_tp(): + def _set(layer, hint): + layer.weight.attrs["shard_strategy"] = hint + + hd = config.n_embd // config.n_head + q = config.n_head * hd + k = 1 * hd + v = 1 * hd + _set(self.attn.c_attn, tp.ShardSingleDim("_shard_c_attn", dim=0, segs=[q, k, v])) + _set(self.attn.c_proj, tp.ShardSingleDim("_shard_c_proj", dim=1)) + _set(self.mlp.c_fc, tp.ShardSingleDim("_shard_mlp_c_fc", dim=0)) + _set(self.mlp.c_proj, tp.ShardSingleDim("_shard_mlp_c_proj", dim=1)) + + self.tensor_parallel_shards = config.tensor_parallel_shards + _set_tp() + + def forward(self, hidden_states: Tensor, attention_mask: Tensor, total_seq_len: tir.Var): + hidden_states = ( + self.attn(self.ln_1(hidden_states), attention_mask, total_seq_len) + hidden_states + ) + hidden_states = self.mlp(self.ln_2(hidden_states)) + hidden_states + return hidden_states + + +class GPTBigCodeModel(nn.Module): + def __init__(self, config: GPTBigCodeConfig): + assert config.n_embd % config.n_head == 0 + self.wte = nn.Embedding("vocab_size", config.n_embd) + self.wpe = nn.Embedding(config.n_positions, config.n_embd) + self.h = nn.ModuleList([GPTBigCodeBlock(config) for _ in range(config.n_layer)]) + self.ln_f = nn.LayerNorm(config.n_embd, eps=config.layer_norm_epsilon) + self.tensor_parallel_shards = config.tensor_parallel_shards + + def forward(self, inputs: Tensor, total_seq_len: tir.Var, attention_mask: Tensor): + if self.tensor_parallel_shards > 1: + inputs = op.ccl_broadcast_from_worker0(inputs) + + # Token Embeddings + t_embd = self.wte(inputs) + + # Position Embeddings + # Generate np.arange(offset, offset+seq_len) + def _input_positions(inputs: te.Tensor, total_seq_len: tir.Var): + b, s = inputs.shape + offset = total_seq_len - s + return te.compute( + (b, s), lambda _, j: (offset + j).astype("int32"), name="input_positions" + ) + + input_positions = op.tensor_expr_op( + _input_positions, + name_hint="input_positions", + args=[inputs, total_seq_len], + ) + pos_embd = self.wpe(input_positions) + + # apply position embeddings + hidden_states = t_embd + pos_embd + for layer in self.h: + hidden_states = layer(hidden_states, attention_mask, total_seq_len) + hidden_states = self.ln_f(hidden_states) + + return hidden_states + + +class GPTBigCodeForCausalLM(nn.Module): + def __init__(self, config: GPTBigCodeConfig): + self.transformer = GPTBigCodeModel(config) + self.lm_head = nn.Linear(config.n_embd, "vocab_size", bias=False) + self.dtype = "float32" + + def to(self, dtype: Optional[str] = None): + super().to(dtype=dtype) + if dtype is not None: + self.dtype = dtype + + def forward(self, inputs: Tensor, total_seq_len: tir.Var, attention_mask: Tensor): + def _index(x: te.Tensor): # x[:-1,:] + b, s, d = x.shape + return te.compute((b, 1, d), lambda i, _, k: x[i, s - 1, k], name="index") + + hidden_states = self.transformer(inputs, total_seq_len, attention_mask) + hidden_states = op.tensor_expr_op(_index, name_hint="index", args=[hidden_states]) + logits = self.lm_head(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits + + def prefill(self, inputs: Tensor, total_seq_len: tir.Var): + def _attention_mask(batch_size, seq_len, total_seq_len): + return te.compute( + (batch_size, 1, seq_len, total_seq_len), + lambda b, _, i, j: tir.if_then_else( + i < j - (total_seq_len - seq_len), + tir.min_value(self.dtype), + tir.max_value(self.dtype), + ), + name="attention_mask_prefill", + ) + + batch_size, seq_len = inputs.shape + attention_mask = op.tensor_expr_op( + _attention_mask, + name_hint="attention_mask_prefill", + args=[batch_size, seq_len, total_seq_len], + ) + return self.forward(inputs, total_seq_len, attention_mask) + + def decode(self, inputs: Tensor, total_seq_len: tir.Var): + batch_size, seq_len = inputs.shape + attention_mask = op.full( + shape=[batch_size, 1, seq_len, total_seq_len], + fill_value=tir.max_value(self.dtype), + dtype=self.dtype, + ) + return self.forward(inputs, total_seq_len, attention_mask) + + def softmax_with_temperature(self, logits: Tensor, temperature: Tensor): + return op.softmax(logits / temperature, axis=-1) + + def get_default_spec(self): + batch_size = 1 + mod_spec = { + "prefill": { + "inputs": nn.spec.Tensor([batch_size, "seq_len"], "int32"), + "total_seq_len": int, + "$": { + "param_mode": "packed", + "effect_mode": "packed", + }, + }, + "decode": { + "inputs": nn.spec.Tensor([batch_size, 1], "int32"), + "total_seq_len": int, + "$": { + "param_mode": "packed", + "effect_mode": "packed", + }, + }, + "softmax_with_temperature": { + "logits": nn.spec.Tensor([1, 1, "vocab_size"], "float32"), + "temperature": nn.spec.Tensor([], "float32"), + "$": { + "param_mode": "none", + "effect_mode": "none", + }, + }, + } + return nn.spec.ModuleSpec.from_raw(mod_spec, self) diff --git a/python/mlc_chat/model/gpt_bigcode/gpt_bigcode_quantization.py b/python/mlc_chat/model/gpt_bigcode/gpt_bigcode_quantization.py new file mode 100644 index 0000000..021cc08 --- /dev/null +++ b/python/mlc_chat/model/gpt_bigcode/gpt_bigcode_quantization.py @@ -0,0 +1,70 @@ +"""This file specifies how MLC's GPTBigCode parameters are quantized using group quantization +or other formats.""" + +from typing import Tuple + +from tvm.relax.frontend import nn + +from mlc_chat.loader import QuantizeMapping +from mlc_chat.quantization import AWQQuantize, FTQuantize, GroupQuantize, NoQuantize + +from .gpt_bigcode_model import GPTBigCodeConfig, GPTBigCodeForCausalLM + + +def group_quant( + model_config: GPTBigCodeConfig, + quantization: GroupQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a GPTBigCode-architecture model using group quantization.""" + model: nn.Module = GPTBigCodeForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def ft_quant( + model_config: GPTBigCodeConfig, + quantization: FTQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a GPTBigCode-architecture model using FasterTransformer quantization.""" + model: nn.Module = GPTBigCodeForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def awq_quant( + model_config: GPTBigCodeConfig, + quantization: AWQQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a GPTBigCode-architecture model using Activation-aware Weight Quantization(AWQ).""" + model: nn.Module = GPTBigCodeForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def no_quant( + model_config: GPTBigCodeConfig, + quantization: NoQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a GPTBigCode model without quantization.""" + model: nn.Module = GPTBigCodeForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + return model, quant_map diff --git a/python/mlc_chat/model/gpt_neox/__init__.py b/python/mlc_chat/model/gpt_neox/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/mlc_chat/model/gpt_neox/gpt_neox_loader.py b/python/mlc_chat/model/gpt_neox/gpt_neox_loader.py new file mode 100644 index 0000000..b7e4027 --- /dev/null +++ b/python/mlc_chat/model/gpt_neox/gpt_neox_loader.py @@ -0,0 +1,89 @@ +""" +This file specifies how MLC's GPTNeoX parameter maps from other formats, for example HuggingFace +PyTorch, HuggingFace safetensors. +""" +import functools + +import numpy as np + +from mlc_chat.loader import ExternMapping +from mlc_chat.quantization import Quantization + +from .gpt_neox_model import GPTNeoXConfig, GPTNeoXForCausalLM + + +def huggingface(model_config: GPTNeoXConfig, quantization: Quantization) -> ExternMapping: + """Returns a parameter mapping that maps from the names of MLC LLM parameters to + the names of HuggingFace PyTorch parameters. + + Parameters + ---------- + model_config : GPTNeoXConfig + The configuration of the GPTNeoX model. + + quantization : Quantization + The quantization configuration. + + Returns + ------- + param_map : ExternMapping + The parameter mapping from MLC to HuggingFace PyTorch. + """ + model = GPTNeoXForCausalLM(model_config) + if quantization is not None: + model.to(quantization.model_dtype) + _, _named_params, _ = model.export_tvm( # type: ignore[misc] + spec=model.get_default_spec(), + allow_extern=True, + ) + named_parameters = dict(_named_params) + + mapping = ExternMapping() + + for i in range(model_config.num_hidden_layers): + # inv_freq/masked_bias/bias is not used in the model + attn = f"gpt_neox.layers.{i}.attention" + mapping.add_unused(f"{attn}.rotary_emb.inv_freq") + mapping.add_unused(f"{attn}.masked_bias") + mapping.add_unused(f"{attn}.bias") + + # change the layout of query_key_value + def transform_qkv_layout(w, dtype): # pylint: disable=invalid-name + num_attention_heads = model_config.num_attention_heads + head_dim = model_config.head_dim + + org_shape = w.shape + w = np.reshape(w, [num_attention_heads, 3 * head_dim, -1]) + qkv = np.split(w, indices_or_sections=3, axis=1) + w = np.concatenate(qkv, axis=0) + w = np.reshape(w, org_shape) + return w.astype(dtype) + + qkv_proj = f"{attn}.query_key_value" + for param_name in ["weight", "bias"]: + mlc_name = f"{qkv_proj}.{param_name}" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [mlc_name], + functools.partial( + transform_qkv_layout, + dtype=mlc_param.dtype, + ), + ) + + for mlc_name, mlc_param in named_parameters.items(): + if mlc_name not in mapping.param_map: + if ".dense_h_to_4h.bias" in mlc_name or ".dense_4h_to_h.bias" in mlc_name: + param_dtype = model_config.ffn_out_dtype + else: + param_dtype = mlc_param.dtype + mapping.add_mapping( + mlc_name, + [mlc_name], + functools.partial( + lambda x, dtype: x.astype(dtype), + dtype=param_dtype, + ), + ) + return mapping diff --git a/python/mlc_chat/model/gpt_neox/gpt_neox_model.py b/python/mlc_chat/model/gpt_neox/gpt_neox_model.py new file mode 100644 index 0000000..130d824 --- /dev/null +++ b/python/mlc_chat/model/gpt_neox/gpt_neox_model.py @@ -0,0 +1,456 @@ +""" +Implementation for GPTNeoX architecture. +TODO: add docstring +""" + +import dataclasses +import logging +from typing import Any, Dict, Optional + +from tvm import te, tir +from tvm.relax.frontend import nn +from tvm.relax.frontend.nn import Tensor, op + +from mlc_chat import op as op_ext +from mlc_chat.nn import PagedKVCache, RopeMode +from mlc_chat.support import tensor_parallel as tp +from mlc_chat.support.config import ConfigBase +from mlc_chat.support.style import bold + +logger = logging.getLogger(__name__) + + +@dataclasses.dataclass +class GPTNeoXConfig(ConfigBase): # pylint: disable=too-many-instance-attributes + """Configuration of the GPTNeoX model.""" + + use_parallel_residual: bool + hidden_size: int + intermediate_size: int + num_attention_heads: int + num_hidden_layers: int + layer_norm_eps: float + vocab_size: int + rotary_pct: float + position_embedding_base: int = 0 + context_window_size: int = 0 + head_dim: int = 0 + prefill_chunk_size: int = 0 + tensor_parallel_shards: int = 1 + ffn_out_dtype: str = "float32" + kwargs: Dict[str, Any] = dataclasses.field(default_factory=dict) + + def __post_init__(self): + if self.context_window_size == 0: + for name in ["max_position_embeddings", "max_sequence_length"]: + if name in self.kwargs: + self.context_window_size = self.kwargs.pop(name) + logger.info( + "%s not found in config.json. Falling back to %s (%d)", + bold("context_window_size"), + bold(name), + self.context_window_size, + ) + break + else: + raise ValueError( + "Unable to determine the maxmimum sequence length, because none of " + "`context_window_size`, `max_position_embeddings` or `max_sequence_length` is " + "provided in `config.json`." + ) + if self.position_embedding_base == 0: + if "rope_theta" in self.kwargs: + self.position_embedding_base = self.kwargs.pop("rope_theta") + else: + self.position_embedding_base = 10000 + if self.head_dim == 0: + self.head_dim = self.hidden_size // self.num_attention_heads + assert self.head_dim * self.num_attention_heads == self.hidden_size + + if self.prefill_chunk_size == 0: + logger.info( + "%s defaults to %s (%d)", + bold("prefill_chunk_size"), + bold("context_window_size"), + self.context_window_size, + ) + self.prefill_chunk_size = self.context_window_size + elif self.prefill_chunk_size > self.context_window_size: + logger.info( + "Overriding %s from %d to %d (%s)", + bold("prefill_chunk_size"), + self.prefill_chunk_size, + self.context_window_size, + bold("context_window_size"), + ) + self.prefill_chunk_size = self.context_window_size + + +# pylint: disable=invalid-name,missing-docstring + + +class GPTNeoXAttention(nn.Module): # pylint: disable=too-many-instance-attributes + """Multi-headed attention from 'Attention Is All You Need' paper""" + + def __init__(self, config: GPTNeoXConfig): + self.rope_theta = config.position_embedding_base + self.hidden_size = config.hidden_size + self.num_attention_heads = config.num_attention_heads // config.tensor_parallel_shards + self.head_dim = config.head_dim + self.query_key_value = nn.Linear( + in_features=self.hidden_size, + out_features=3 * self.num_attention_heads * self.head_dim, + bias=True, + ) + self.dense = nn.Linear( + self.num_attention_heads * self.head_dim, self.hidden_size, bias=True + ) + + def forward(self, hidden_states: Tensor, paged_kv_cache: PagedKVCache, layer_id: int): + # hidden_states: [batch_size, seq_len, hidden_size] + batch_size, seq_len, _ = hidden_states.shape + + # q/k/v states: [batch_size, seq_len, hidden_size] + qkv = self.query_key_value(hidden_states) + qkv = op.reshape(qkv, (batch_size, seq_len, 3 * self.num_attention_heads, self.head_dim)) + + # Attention + output = op.reshape( + paged_kv_cache.attention_with_fused_qkv(layer_id, qkv, self.num_attention_heads), + (batch_size, seq_len, self.head_dim * self.num_attention_heads), + ) + attn_output = self.dense(output) + return attn_output + + def batch_forward(self, hidden_states: Tensor, paged_kv_cache: PagedKVCache, layer_id: int): + # hidden_states: [batch_size, seq_len, hidden_size] + batch_size, seq_len, _ = hidden_states.shape + + # q/k/v states: [batch_size, seq_len, hidden_size] + qkv = self.query_key_value(hidden_states) + qkv = op.reshape(qkv, (batch_size, seq_len, 3 * self.num_attention_heads, self.head_dim)) + + # Attention + output = op.reshape( + paged_kv_cache.attention_with_fused_qkv(layer_id, qkv, self.num_attention_heads), + (batch_size, seq_len, self.head_dim * self.num_attention_heads), + ) + attn_output = self.dense(output) + return attn_output + + +class GPTNeoXMLP(nn.Module): + def __init__(self, config: GPTNeoXConfig): + super().__init__() + out_dtype = config.ffn_out_dtype + self.intermediate_size = config.intermediate_size // config.tensor_parallel_shards + self.dense_h_to_4h = nn.Linear( + config.hidden_size, + self.intermediate_size, + out_dtype=out_dtype, + ) + self.dense_4h_to_h = nn.Linear( + self.intermediate_size, + config.hidden_size, + out_dtype=out_dtype, + ) + + def forward(self, hidden_states: Tensor): + dtype = hidden_states.dtype + if hidden_states.dtype != dtype: + hidden_states = hidden_states.astype(dtype) + hidden_states = self.dense_h_to_4h(hidden_states) + hidden_states = op.gelu(hidden_states) + if hidden_states.dtype != dtype: + hidden_states = hidden_states.astype(dtype) + hidden_states = self.dense_4h_to_h(hidden_states) + if hidden_states.dtype != dtype: + hidden_states = hidden_states.astype(dtype) + return hidden_states + + +class GPTNeoXLayer(nn.Module): + def __init__(self, config: GPTNeoXConfig): + self.input_layernorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.post_attention_layernorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.attention = GPTNeoXAttention(config) + self.mlp = GPTNeoXMLP(config) + self.use_parallel_residual = config.use_parallel_residual + + def _set_tp(): + def _set(param, hint): + param.attrs["shard_strategy"] = hint + + hd = config.head_dim + q = k = v = self.attention.num_attention_heads * hd + _set( + self.attention.query_key_value.weight, + tp.ShardSingleDim("_shard_qkv_weight", dim=0, segs=[q, k, v]), + ) + _set( + self.attention.query_key_value.bias, + tp.ShardSingleDim("_shard_qkv_bias", dim=0, segs=[q, k, v]), + ) + _set(self.attention.dense.weight, tp.ShardSingleDim("_shard_dense", dim=1)) + _set( + self.mlp.dense_h_to_4h.weight, + tp.ShardSingleDim("_shard_dense_h_to_4h_weight", dim=0), + ) + _set(self.mlp.dense_h_to_4h.bias, tp.ShardSingleDim("_shard_dense_h_to_4h_bias", dim=0)) + _set(self.mlp.dense_4h_to_h.weight, tp.ShardSingleDim("_shard_dense_4h_to_h", dim=1)) + + self.tensor_parallel_shards = config.tensor_parallel_shards + _set_tp() + + def forward(self, hidden_states: Tensor, paged_kv_cache: PagedKVCache, layer_id: int): + dtype = hidden_states.dtype + attn_input = self.input_layernorm(hidden_states) + with tp.shard_bias(self.attention.dense, self.tensor_parallel_shards): + attn_output = self.attention( + attn_input, + paged_kv_cache, + layer_id, + ) + if self.use_parallel_residual: + mlp_input = self.post_attention_layernorm(hidden_states) + mlp_output = self.mlp(mlp_input) + hidden_states = mlp_output + attn_output + hidden_states + else: + attn_output = self._apply_residual(attn_output, hidden_states) + mlp_input = self.post_attention_layernorm(attn_output) + with tp.shard_bias(self.mlp.dense_4h_to_h, self.tensor_parallel_shards): + mlp_output = self.mlp(mlp_input) + hidden_states = self._apply_residual(mlp_output.astype(dtype), attn_output) + return hidden_states + + def batch_forward(self, hidden_states: Tensor, paged_kv_cache: PagedKVCache, layer_id: int): + dtype = hidden_states.dtype + attn_input = self.input_layernorm(hidden_states) + with tp.shard_bias(self.attention.dense, self.tensor_parallel_shards): + attn_output = self.attention.batch_forward( + attn_input, + paged_kv_cache, + layer_id, + ) + if self.use_parallel_residual: + mlp_input = self.post_attention_layernorm(hidden_states) + mlp_output = self.mlp(mlp_input) + hidden_states = mlp_output + attn_output + hidden_states + else: + attn_output = self._apply_residual(attn_output, hidden_states) + mlp_input = self.post_attention_layernorm(attn_output) + with tp.shard_bias(self.mlp.dense_4h_to_h, self.tensor_parallel_shards): + mlp_output = self.mlp(mlp_input) + hidden_states = self._apply_residual(mlp_output.astype(dtype), attn_output) + return hidden_states + + def _apply_residual(self, out, residual): + if self.tensor_parallel_shards > 1: + return op.ccl_allreduce(out + residual / self.tensor_parallel_shards, "sum") + return out + residual + + +class GPTNeoXModel(nn.Module): + def __init__(self, config: GPTNeoXConfig): + self.embed_in = nn.Embedding(num="vocab_size", dim=config.hidden_size) + self.layers = nn.ModuleList([GPTNeoXLayer(config) for _ in range(config.num_hidden_layers)]) + self.final_layer_norm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.tensor_parallel_shards = config.tensor_parallel_shards + + def forward(self, inputs: Tensor, paged_kv_cache: PagedKVCache): + if self.tensor_parallel_shards > 1: + inputs = op.ccl_broadcast_from_worker0(inputs) + hidden_states = inputs + + for layer_id, layer in enumerate(self.layers): + hidden_states = layer(hidden_states, paged_kv_cache, layer_id) + hidden_states = self.final_layer_norm(hidden_states) + return hidden_states + + def batch_forward(self, inputs: Tensor, paged_kv_cache: PagedKVCache): + if self.tensor_parallel_shards > 1: + inputs = op.ccl_broadcast_from_worker0(inputs) + hidden_states = inputs + + for layer_id, layer in enumerate(self.layers): + hidden_states = layer.batch_forward(hidden_states, paged_kv_cache, layer_id) + hidden_states = self.final_layer_norm(hidden_states) + return hidden_states + + +class GPTNeoXForCausalLM(nn.Module): # pylint: disable=too-many-instance-attributes + def __init__(self, config: GPTNeoXConfig): + self.gpt_neox = GPTNeoXModel(config) + self.embed_out = nn.Linear( + in_features=config.hidden_size, + out_features="vocab_size", + bias=False, + dtype="float32", + ) + self.num_hidden_layers = config.num_hidden_layers + self.hidden_size = config.hidden_size + self.num_attention_heads = config.num_attention_heads + self.head_dim = config.head_dim + self.vocab_size = config.vocab_size + self.rope_theta = config.position_embedding_base + self.tensor_parallel_shards = config.tensor_parallel_shards + self.dtype = "float32" + self.rotary_pct = config.rotary_pct + + def to(self, dtype: Optional[str] = None): + super().to(dtype=dtype) + if dtype is not None: + self.dtype = dtype + + def batch_forward( + self, + input_embeds: Tensor, + paged_kv_cache: PagedKVCache, + logit_positions: Optional[Tensor] = None, + ): + op_ext.configure() + + hidden_states = self.gpt_neox.batch_forward(input_embeds, paged_kv_cache) + if logit_positions is not None: + hidden_states = op.take(hidden_states, logit_positions, axis=1) + logits = self.embed_out(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits + + def embed(self, input_ids: Tensor): + return self.gpt_neox.embed_in(input_ids) + + def prefill(self, input_embed: Tensor, paged_kv_cache: PagedKVCache): + op_ext.configure() + + def _index(x: te.Tensor): # x[:-1,:] + b, s, d = x.shape + return te.compute((b, 1, d), lambda i, _, k: x[i, s - 1, k], name="index") + + hidden_states = self.gpt_neox(input_embed, paged_kv_cache) + hidden_states = op.tensor_expr_op(_index, name_hint="index", args=[hidden_states]) + logits = self.embed_out(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits, paged_kv_cache + + def decode(self, input_embed: Tensor, paged_kv_cache: PagedKVCache): + op_ext.configure() + + hidden_states = self.gpt_neox(input_embed, paged_kv_cache) + logits = self.embed_out(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits, paged_kv_cache + + def batch_prefill( + self, input_embeds: Tensor, logit_positions: Tensor, paged_kv_cache: PagedKVCache + ): + logits = self.batch_forward(input_embeds, paged_kv_cache, logit_positions) + return logits, paged_kv_cache + + def batch_decode(self, input_embeds: Tensor, paged_kv_cache: PagedKVCache): + logits = self.batch_forward(input_embeds, paged_kv_cache) + return logits, paged_kv_cache + + def batch_verify(self, input_embeds: Tensor, paged_kv_cache: PagedKVCache): + logits = self.batch_forward(input_embeds, paged_kv_cache) + return logits, paged_kv_cache + + def softmax_with_temperature(self, logits: Tensor, temperature: Tensor): + return op.softmax(logits / op.reshape(temperature, (temperature.shape[0], 1, 1)), axis=-1) + + def create_paged_kv_cache( + self, + max_batch_size: tir.Var, + max_total_seq_len: tir.Var, + prefill_chunk_size: tir.Var, + page_size: tir.Var, + ) -> PagedKVCache: + return PagedKVCache.create_generic( + max_batch_size=max_batch_size, + max_total_seq_len=max_total_seq_len, + prefill_chunk_size=prefill_chunk_size, + page_size=page_size, + num_hidden_layers=self.num_hidden_layers, + num_attention_heads=self.num_attention_heads // self.tensor_parallel_shards, + num_key_value_heads=self.num_attention_heads // self.tensor_parallel_shards, + head_dim=self.head_dim, + rope_mode=RopeMode.NORMAL, + rope_scale=1, + rope_theta=self.rope_theta, + dtype=self.dtype, + rotary_dim=int(self.head_dim * self.rotary_pct), + ) + + def get_default_spec(self): + mod_spec = { + "embed": { + "input_ids": nn.spec.Tensor([1, "seq_len"], "int32"), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "prefill": { + "input_embed": nn.spec.Tensor([1, "seq_len", self.hidden_size], self.dtype), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "decode": { + "input_embed": nn.spec.Tensor([1, 1, self.hidden_size], self.dtype), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "batch_prefill": { + "input_embeds": nn.spec.Tensor([1, "seq_len", self.hidden_size], self.dtype), + "logit_positions": nn.spec.Tensor(["batch_size"], "int32"), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "batch_decode": { + "input_embeds": nn.spec.Tensor(["batch_size", 1, self.hidden_size], self.dtype), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "batch_verify": { + "input_embeds": nn.spec.Tensor([1, "seq_len", self.hidden_size], self.dtype), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "softmax_with_temperature": { + "logits": nn.spec.Tensor(["batch_size", 1, "vocab_size"], "float32"), + "temperature": nn.spec.Tensor(["batch_size"], "float32"), + "$": { + "param_mode": "none", + "effect_mode": "none", + }, + }, + "create_paged_kv_cache": { + "max_batch_size": int, + "max_total_seq_len": int, + "prefill_chunk_size": int, + "page_size": int, + "$": { + "param_mode": "none", + "effect_mode": "none", + }, + }, + } + return nn.spec.ModuleSpec.from_raw(mod_spec, self) diff --git a/python/mlc_chat/model/gpt_neox/gpt_neox_quantization.py b/python/mlc_chat/model/gpt_neox/gpt_neox_quantization.py new file mode 100644 index 0000000..9f1daaf --- /dev/null +++ b/python/mlc_chat/model/gpt_neox/gpt_neox_quantization.py @@ -0,0 +1,53 @@ +"""This file specifies how MLC's GPTNeoX parameters are quantized using group quantization +or other formats.""" +from typing import Tuple + +from tvm.relax.frontend import nn + +from mlc_chat.loader import QuantizeMapping +from mlc_chat.quantization import FTQuantize, GroupQuantize, NoQuantize + +from .gpt_neox_model import GPTNeoXConfig, GPTNeoXForCausalLM + + +def group_quant( + model_config: GPTNeoXConfig, + quantization: GroupQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a GPTNeoX-architecture model using group quantization.""" + model: nn.Module = GPTNeoXForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def ft_quant( + model_config: GPTNeoXConfig, + quantization: FTQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a GPTNeoX-architecture model using FasterTransformer quantization.""" + model: nn.Module = GPTNeoXForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def no_quant( + model_config: GPTNeoXConfig, + quantization: NoQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a GPTNeoX model without quantization.""" + model: nn.Module = GPTNeoXForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + return model, quant_map diff --git a/python/mlc_chat/model/llama/__init__.py b/python/mlc_chat/model/llama/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/mlc_chat/model/llama/llama_loader.py b/python/mlc_chat/model/llama/llama_loader.py new file mode 100644 index 0000000..5dd902d --- /dev/null +++ b/python/mlc_chat/model/llama/llama_loader.py @@ -0,0 +1,171 @@ +""" +This file specifies how MLC's Llama parameter maps from other formats, for example HuggingFace +PyTorch, HuggingFace safetensors. +""" +import functools + +import numpy as np + +from mlc_chat.loader import ExternMapping +from mlc_chat.quantization import Quantization + +from .llama_model import LlamaConfig, LlamaForCasualLM +from .llama_quantization import awq_quant + + +def huggingface(model_config: LlamaConfig, quantization: Quantization) -> ExternMapping: + """Returns a parameter mapping that maps from the names of MLC LLM parameters to + the names of HuggingFace PyTorch parameters. + + Parameters + ---------- + model_config : LlamaConfig + The configuration of the Llama model. + + quantization : Quantization + The quantization configuration. + + Returns + ------- + param_map : ExternMapping + The parameter mapping from MLC to HuggingFace PyTorch. + """ + model = LlamaForCasualLM(model_config) + if quantization is not None: + model.to(quantization.model_dtype) + _, _named_params, _ = model.export_tvm( # type: ignore[misc] + spec=model.get_default_spec(), + allow_extern=True, + ) + named_parameters = dict(_named_params) + + mapping = ExternMapping() + + for i in range(model_config.num_hidden_layers): + # Add QKV in self attention + attn = f"model.layers.{i}.self_attn" + mlc_name = f"{attn}.qkv_proj.weight" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{attn}.q_proj.weight", + f"{attn}.k_proj.weight", + f"{attn}.v_proj.weight", + ], + functools.partial( + lambda q, k, v, dtype: np.concatenate([q, k, v], axis=0).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + # Add gates in MLP + mlp = f"model.layers.{i}.mlp" + mlc_name = f"{mlp}.gate_up_proj.weight" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{mlp}.gate_proj.weight", + f"{mlp}.up_proj.weight", + ], + functools.partial( + lambda gate, up, dtype: np.concatenate([gate, up], axis=0).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + # inv_freq is not used in the model + mapping.add_unused(f"{attn}.rotary_emb.inv_freq") + + for mlc_name, mlc_param in named_parameters.items(): + if mlc_name not in mapping.param_map: + mapping.add_mapping( + mlc_name, + [mlc_name], + functools.partial( + lambda x, dtype: x.astype(dtype), + dtype=mlc_param.dtype, + ), + ) + return mapping + + +def awq(model_config: LlamaConfig, quantization: Quantization) -> ExternMapping: + """Returns a parameter mapping that maps from the names of MLC LLM parameters to + the names of AWQ parameters. + Parameters + ---------- + model_config : LlamaConfig + The configuration of the Llama model. + + quantization : Quantization + The quantization configuration. + + Returns + ------- + param_map : ExternMapping + The parameter mapping from MLC to AWQ. + """ + model, _ = awq_quant(model_config, quantization) + _, _named_params, _ = model.export_tvm( # type: ignore[misc] + spec=model.get_default_spec(), # type: ignore[attr-defined] + allow_extern=True, + ) + named_parameters = dict(_named_params) + + mapping = ExternMapping() + + for i in range(model_config.num_hidden_layers): + # Add QKV in self attention + attn = f"model.layers.{i}.self_attn" + for quantize_suffix in ["qweight", "qzeros", "scales"]: + mlc_name = f"{attn}.qkv_proj.{quantize_suffix}" + assert mlc_name in named_parameters + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{attn}.q_proj.{quantize_suffix}", + f"{attn}.k_proj.{quantize_suffix}", + f"{attn}.v_proj.{quantize_suffix}", + ], + functools.partial( + lambda q, k, v, dtype: np.concatenate( + [q, k, v], + axis=1, # AWQ GEMM would transpose the weight + ).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + + # Concat gate and up in MLP + mlp = f"model.layers.{i}.mlp" + for quantize_suffix in ["qweight", "qzeros", "scales"]: + mlc_name = f"{mlp}.gate_up_proj.{quantize_suffix}" + assert mlc_name in named_parameters + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{mlp}.gate_proj.{quantize_suffix}", + f"{mlp}.up_proj.{quantize_suffix}", + ], + functools.partial( + lambda gate, up, dtype: np.concatenate( + [gate, up], + axis=1, # AWQ GEMM would transpose the weight + ).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + + # inv_freq is not used in the model + mapping.add_unused(f"{attn}.rotary_emb.inv_freq") + + for mlc_name, mlc_param in named_parameters.items(): + if mlc_name not in mapping.param_map: + mapping.add_mapping( + mlc_name, + [mlc_name], + functools.partial(lambda x, dtype: x.astype(dtype), dtype=mlc_param.dtype), + ) + return mapping diff --git a/python/mlc_chat/model/llama/llama_model.py b/python/mlc_chat/model/llama/llama_model.py new file mode 100644 index 0000000..6da1d42 --- /dev/null +++ b/python/mlc_chat/model/llama/llama_model.py @@ -0,0 +1,400 @@ +""" +Implementation for Llama2 architecture. +TODO: add docstring +""" + +import dataclasses +from typing import Any, Dict, Optional + +from tvm import te, tir +from tvm.relax.frontend import nn +from tvm.relax.frontend.nn import Tensor, op + +from mlc_chat import op as op_ext +from mlc_chat.nn import PagedKVCache, RopeMode +from mlc_chat.support import logging +from mlc_chat.support import tensor_parallel as tp +from mlc_chat.support.config import ConfigBase +from mlc_chat.support.style import bold + +logger = logging.getLogger(__name__) + + +@dataclasses.dataclass +class LlamaConfig(ConfigBase): # pylint: disable=too-many-instance-attributes + """Configuration of the Llama model.""" + + hidden_size: int + intermediate_size: int + num_attention_heads: int + num_hidden_layers: int + rms_norm_eps: float + vocab_size: int + position_embedding_base: int = 0 + context_window_size: int = 0 + prefill_chunk_size: int = 0 + num_key_value_heads: int = 0 + head_dim: int = 0 + tensor_parallel_shards: int = 1 + max_batch_size: int = 1 + kwargs: Dict[str, Any] = dataclasses.field(default_factory=dict) + + def __post_init__(self): + if self.position_embedding_base == 0: + if "rope_theta" in self.kwargs: + self.position_embedding_base = self.kwargs.pop("rope_theta") + else: + self.position_embedding_base = 10000 + if self.context_window_size == 0: + for name in ["max_position_embeddings", "max_sequence_length"]: + if name in self.kwargs: + self.context_window_size = self.kwargs.pop(name) + logger.info( + "%s not found in config.json. Falling back to %s (%d)", + bold("context_window_size"), + bold(name), + self.context_window_size, + ) + break + else: + raise ValueError( + "Unable to determine the maxmimum sequence length, because none of " + "`context_window_size`, `max_position_embeddings` or `max_sequence_length` is " + "provided in `config.json`." + ) + if self.num_key_value_heads == 0: + self.num_key_value_heads = self.num_attention_heads + if self.head_dim == 0: + self.head_dim = self.hidden_size // self.num_attention_heads + assert self.head_dim * self.num_attention_heads == self.hidden_size + assert self.num_attention_heads % self.num_key_value_heads == 0 + if self.prefill_chunk_size == 0: + logger.info( + "%s defaults to %s (%d)", + bold("prefill_chunk_size"), + bold("context_window_size"), + self.context_window_size, + ) + self.prefill_chunk_size = self.context_window_size + elif self.prefill_chunk_size > self.context_window_size: + logger.info( + "Overriding %s from %d to %d (%s)", + bold("prefill_chunk_size"), + self.prefill_chunk_size, + self.context_window_size, + bold("context_window_size"), + ) + self.prefill_chunk_size = self.context_window_size + + +# pylint: disable=invalid-name,missing-docstring + + +class LlamaFFN(nn.Module): + def __init__(self, config: LlamaConfig): + super().__init__() + self.intermediate_size = config.intermediate_size // config.tensor_parallel_shards + self.gate_up_proj = nn.Linear( + in_features=config.hidden_size, + out_features=2 * self.intermediate_size, + bias=False, + ) + self.down_proj = nn.Linear(self.intermediate_size, config.hidden_size, bias=False) + + def forward(self, x: Tensor): + concat_x1_x2 = self.gate_up_proj(x) + x1, x2 = op.split(concat_x1_x2, 2, axis=-1) + return self.down_proj(op.silu(x1) * x2) + + +class LlamaAttention(nn.Module): # pylint: disable=too-many-instance-attributes + def __init__(self, config: LlamaConfig): + self.head_dim = config.head_dim + self.num_q_heads = config.num_attention_heads // config.tensor_parallel_shards + assert ( + config.num_key_value_heads % config.tensor_parallel_shards == 0 + ), f"num_kv_heads({config.num_key_value_heads}) must be divisible by tensor_parallel_shards" + assert ( + config.num_key_value_heads >= config.tensor_parallel_shards + ), f"Too large tensor_parallel_shards, must be smaller than {config.num_key_value_heads}" + self.num_kv_heads = config.num_key_value_heads // config.tensor_parallel_shards + self.qkv_proj = nn.Linear( + in_features=config.hidden_size, + out_features=(self.num_q_heads + 2 * self.num_kv_heads) * self.head_dim, + bias=False, + ) + self.o_proj = nn.Linear(self.num_q_heads * self.head_dim, config.hidden_size, bias=False) + + def forward(self, hidden_states: Tensor, paged_kv_cache: PagedKVCache, layer_id: int): + d, h_q, h_kv = self.head_dim, self.num_q_heads, self.num_kv_heads + b, s, _ = hidden_states.shape + # QKV Projection + qkv = self.qkv_proj(hidden_states) + qkv = op.reshape(qkv, (b, s, h_q + h_kv + h_kv, d)) + # Attention + output = op.reshape( + paged_kv_cache.attention_with_fused_qkv(layer_id, qkv, self.num_q_heads), + (b, s, h_q * d), + ) + return self.o_proj(output) + + def batch_forward(self, hidden_states: Tensor, paged_kv_cache: PagedKVCache, layer_id: int): + d, h_q, h_kv = self.head_dim, self.num_q_heads, self.num_kv_heads + b, s, _ = hidden_states.shape + # QKV Projection + qkv = self.qkv_proj(hidden_states) + qkv = op.reshape(qkv, (b, s, h_q + h_kv + h_kv, d)) + # Attention + output = op.reshape( + paged_kv_cache.attention_with_fused_qkv(layer_id, qkv, self.num_q_heads), + (b, s, h_q * d), + ) + return self.o_proj(output) + + +class LlamaDecoderLayer(nn.Module): + def __init__(self, config: LlamaConfig): + rms_norm_eps = config.rms_norm_eps + self.self_attn = LlamaAttention(config) + self.mlp = LlamaFFN(config) + self.input_layernorm = nn.RMSNorm(config.hidden_size, -1, rms_norm_eps, bias=False) + self.post_attention_layernorm = nn.RMSNorm(config.hidden_size, -1, rms_norm_eps, bias=False) + + def _set_tp(): + def _set(layer, hint): + layer.weight.attrs["shard_strategy"] = hint + + hd = config.head_dim + q = self.self_attn.num_q_heads * hd + k = self.self_attn.num_kv_heads * hd + v = self.self_attn.num_kv_heads * hd + i = self.mlp.intermediate_size + _set(self.self_attn.qkv_proj, tp.ShardSingleDim("_shard_qkv", segs=[q, k, v], dim=0)) + _set(self.self_attn.o_proj, tp.ShardSingleDim("_shard_o", dim=1)) + _set(self.mlp.gate_up_proj, tp.ShardSingleDim("_shard_mlp_up", segs=[i, i], dim=0)) + _set(self.mlp.down_proj, tp.ShardSingleDim("_shard_mlp_down", dim=1)) + + self.tensor_parallel_shards = config.tensor_parallel_shards + _set_tp() + + def forward(self, hidden_states: Tensor, paged_kv_cache: PagedKVCache, layer_id: int): + out = self.self_attn(self.input_layernorm(hidden_states), paged_kv_cache, layer_id) + hidden_states = self._apply_residual(out, residual=hidden_states) + out = self.mlp(self.post_attention_layernorm(hidden_states)) + hidden_states = self._apply_residual(out, residual=hidden_states) + return hidden_states + + def batch_forward(self, hidden_states: Tensor, paged_kv_cache: PagedKVCache, layer_id: int): + out = self.self_attn.batch_forward( + self.input_layernorm(hidden_states), paged_kv_cache, layer_id + ) + hidden_states = self._apply_residual(out, residual=hidden_states) + out = self.mlp(self.post_attention_layernorm(hidden_states)) + hidden_states = self._apply_residual(out, residual=hidden_states) + return hidden_states + + def _apply_residual(self, out, residual): + if self.tensor_parallel_shards > 1: + return op.ccl_allreduce(out, "sum") + residual + return out + residual + + +class LlamaModel(nn.Module): + def __init__(self, config: LlamaConfig): + assert config.hidden_size % config.num_attention_heads == 0 + self.embed_tokens = nn.Embedding("vocab_size", config.hidden_size) + self.layers = nn.ModuleList( + [LlamaDecoderLayer(config) for _ in range(config.num_hidden_layers)] + ) + self.norm = nn.RMSNorm(config.hidden_size, -1, config.rms_norm_eps, bias=False) + self.tensor_parallel_shards = config.tensor_parallel_shards + + def forward(self, input_embed: Tensor, paged_kv_cache: PagedKVCache): + if self.tensor_parallel_shards > 1: + input_embed = op.ccl_broadcast_from_worker0(input_embed) + hidden_states = input_embed + for layer_id, layer in enumerate(self.layers): + hidden_states = layer(hidden_states, paged_kv_cache, layer_id) + hidden_states = self.norm(hidden_states) + return hidden_states + + def batch_forward(self, input_embeds: Tensor, paged_kv_cache: PagedKVCache): + if self.tensor_parallel_shards > 1: + input_embeds = op.ccl_broadcast_from_worker0(input_embeds) + hidden_states = input_embeds + for layer_id, layer in enumerate(self.layers): + hidden_states = layer.batch_forward(hidden_states, paged_kv_cache, layer_id) + hidden_states = self.norm(hidden_states) + return hidden_states + + +class LlamaForCasualLM(nn.Module): # pylint: disable=too-many-instance-attributes + def __init__(self, config: LlamaConfig): + self.model = LlamaModel(config) + self.lm_head = nn.Linear(config.hidden_size, "vocab_size", bias=False) + self.num_hidden_layers = config.num_hidden_layers + self.num_attention_heads = config.num_attention_heads + self.num_key_value_heads = config.num_key_value_heads + self.head_dim = config.head_dim + self.hidden_size = config.hidden_size + self.vocab_size = config.vocab_size + self.rope_theta = config.position_embedding_base + self.tensor_parallel_shards = config.tensor_parallel_shards + self.dtype = "float32" + + def to(self, dtype: Optional[str] = None): + super().to(dtype=dtype) + if dtype is not None: + self.dtype = dtype + + def batch_forward( + self, + input_embeds: Tensor, + paged_kv_cache: PagedKVCache, + logit_positions: Optional[Tensor] = None, + ): + op_ext.configure() + + hidden_states = self.model.batch_forward(input_embeds, paged_kv_cache) + if logit_positions is not None: + hidden_states = op.take(hidden_states, logit_positions, axis=1) + logits = self.lm_head(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits + + def embed(self, input_ids: Tensor): + return self.model.embed_tokens(input_ids) + + def prefill(self, input_embed: Tensor, paged_kv_cache: PagedKVCache): + op_ext.configure() + + def _index(x: te.Tensor): # x[:-1,:] + b, s, d = x.shape + return te.compute((b, 1, d), lambda i, _, k: x[i, s - 1, k], name="index") + + hidden_states = self.model(input_embed, paged_kv_cache) + hidden_states = op.tensor_expr_op(_index, name_hint="index", args=[hidden_states]) + logits = self.lm_head(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits, paged_kv_cache + + def decode(self, input_embed: Tensor, paged_kv_cache: PagedKVCache): + op_ext.configure() + + hidden_states = self.model(input_embed, paged_kv_cache) + logits = self.lm_head(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits, paged_kv_cache + + def batch_prefill( + self, input_embeds: Tensor, logit_positions: Tensor, paged_kv_cache: PagedKVCache + ): + logits = self.batch_forward(input_embeds, paged_kv_cache, logit_positions) + return logits, paged_kv_cache + + def batch_decode(self, input_embeds: Tensor, paged_kv_cache: PagedKVCache): + logits = self.batch_forward(input_embeds, paged_kv_cache) + return logits, paged_kv_cache + + def batch_verify(self, input_embeds: Tensor, paged_kv_cache: PagedKVCache): + logits = self.batch_forward(input_embeds, paged_kv_cache) + return logits, paged_kv_cache + + def softmax_with_temperature(self, logits: Tensor, temperature: Tensor): + return op.softmax(logits / op.reshape(temperature, (temperature.shape[0], 1, 1)), axis=-1) + + def create_paged_kv_cache( + self, + max_batch_size: tir.Var, + max_total_seq_len: tir.Var, + prefill_chunk_size: tir.Var, + page_size: tir.Var, + ) -> PagedKVCache: + return PagedKVCache.create_generic( + max_batch_size=max_batch_size, + max_total_seq_len=max_total_seq_len, + prefill_chunk_size=prefill_chunk_size, + page_size=page_size, + num_hidden_layers=self.num_hidden_layers, + num_attention_heads=self.num_attention_heads // self.tensor_parallel_shards, + num_key_value_heads=self.num_key_value_heads // self.tensor_parallel_shards, + head_dim=self.head_dim, + rope_mode=RopeMode.NORMAL, + rope_scale=1, + rope_theta=self.rope_theta, + dtype=self.dtype, + ) + + def get_default_spec(self): + mod_spec = { + "embed": { + "input_ids": nn.spec.Tensor([1, "seq_len"], "int32"), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "prefill": { + "input_embed": nn.spec.Tensor([1, "seq_len", self.hidden_size], self.dtype), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "decode": { + "input_embed": nn.spec.Tensor([1, 1, self.hidden_size], self.dtype), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "batch_prefill": { + "input_embeds": nn.spec.Tensor([1, "seq_len", self.hidden_size], self.dtype), + "logit_positions": nn.spec.Tensor(["batch_size"], "int32"), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "batch_decode": { + "input_embeds": nn.spec.Tensor(["batch_size", 1, self.hidden_size], self.dtype), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "batch_verify": { + "input_embeds": nn.spec.Tensor([1, "seq_len", self.hidden_size], self.dtype), + "paged_kv_cache": nn.spec.Object(object_type=PagedKVCache), + "$": { + "param_mode": "packed", + "effect_mode": "none", + }, + }, + "softmax_with_temperature": { + "logits": nn.spec.Tensor(["batch_size", 1, "vocab_size"], "float32"), + "temperature": nn.spec.Tensor(["batch_size"], "float32"), + "$": { + "param_mode": "none", + "effect_mode": "none", + }, + }, + "create_paged_kv_cache": { + "max_batch_size": int, + "max_total_seq_len": int, + "prefill_chunk_size": int, + "page_size": int, + "$": { + "param_mode": "none", + "effect_mode": "none", + }, + }, + } + return nn.spec.ModuleSpec.from_raw(mod_spec, self) diff --git a/python/mlc_chat/model/llama/llama_quantization.py b/python/mlc_chat/model/llama/llama_quantization.py new file mode 100644 index 0000000..0460c98 --- /dev/null +++ b/python/mlc_chat/model/llama/llama_quantization.py @@ -0,0 +1,69 @@ +"""This file specifies how MLC's Llama parameters are quantized using group quantization +or other formats.""" +from typing import Tuple + +from tvm.relax.frontend import nn + +from mlc_chat.loader import QuantizeMapping +from mlc_chat.quantization import AWQQuantize, FTQuantize, GroupQuantize, NoQuantize + +from .llama_model import LlamaConfig, LlamaForCasualLM + + +def group_quant( + model_config: LlamaConfig, + quantization: GroupQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Llama-architecture model using group quantization.""" + model: nn.Module = LlamaForCasualLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def ft_quant( + model_config: LlamaConfig, + quantization: FTQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Llama-architecture model using FasterTransformer quantization.""" + model: nn.Module = LlamaForCasualLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def awq_quant( + model_config: LlamaConfig, + quantization: AWQQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Llama-architecture model using Activation-aware Weight Quantization(AWQ).""" + model: nn.Module = LlamaForCasualLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def no_quant( + model_config: LlamaConfig, + quantization: NoQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Llama2 model without quantization.""" + model: nn.Module = LlamaForCasualLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + return model, quant_map diff --git a/python/mlc_chat/model/mistral/__init__.py b/python/mlc_chat/model/mistral/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/mlc_chat/model/mistral/mistral_loader.py b/python/mlc_chat/model/mistral/mistral_loader.py new file mode 100644 index 0000000..71a8f1a --- /dev/null +++ b/python/mlc_chat/model/mistral/mistral_loader.py @@ -0,0 +1,165 @@ +""" +This file specifies how MLC's Mistral parameter maps from other formats, for example HuggingFace +PyTorch, HuggingFace safetensors. +""" +import functools + +import numpy as np + +from mlc_chat.loader import ExternMapping +from mlc_chat.quantization import Quantization + +from .mistral_model import MistralConfig, MistralForCasualLM +from .mistral_quantization import awq_quant + + +def huggingface(model_config: MistralConfig, quantization: Quantization) -> ExternMapping: + """Returns a parameter mapping that maps from the names of MLC LLM parameters to + the names of HuggingFace PyTorch parameters. + + Parameters + ---------- + model_config : MistralConfig + The configuration of the Mistral model. + + quantization : Quantization + The quantization configuration. + + Returns + ------- + param_map : ExternMapping + The parameter mapping from MLC to HuggingFace PyTorch. + """ + model = MistralForCasualLM(model_config) + if quantization is not None: + model.to(quantization.model_dtype) + _, _named_params, _ = model.export_tvm( # type: ignore[misc] + spec=model.get_default_spec(), + allow_extern=True, + ) + named_parameters = dict(_named_params) + + mapping = ExternMapping() + + for i in range(model_config.num_hidden_layers): + # Add QKV in self attention + attn = f"model.layers.{i}.self_attn" + mlc_name = f"{attn}.qkv_proj.weight" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{attn}.q_proj.weight", + f"{attn}.k_proj.weight", + f"{attn}.v_proj.weight", + ], + functools.partial( + lambda q, k, v, dtype: np.concatenate([q, k, v], axis=0).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + # Add gates in MLP + mlp = f"model.layers.{i}.mlp" + mlc_name = f"{mlp}.gate_up_proj.weight" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{mlp}.gate_proj.weight", + f"{mlp}.up_proj.weight", + ], + functools.partial( + lambda gate, up, dtype: np.concatenate([gate, up], axis=0).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + # inv_freq is not used in the model + mapping.add_unused(f"{attn}.rotary_emb.inv_freq") + + for mlc_name, mlc_param in named_parameters.items(): + if mlc_name not in mapping.param_map: + mapping.add_mapping( + mlc_name, + [mlc_name], + functools.partial( + lambda x, dtype: x.astype(dtype), + dtype=mlc_param.dtype, + ), + ) + return mapping + + +def awq(model_config: MistralConfig, quantization: Quantization) -> ExternMapping: + """Returns a parameter mapping that maps from the names of MLC LLM parameters to + the names of AWQ parameters. + Parameters + ---------- + model_config : MistralConfig + The configuration of the Mistral model. + + quantization : Quantization + The quantization configuration. + + Returns + ------- + param_map : ExternMapping + The parameter mapping from MLC to AWQ. + """ + model, _ = awq_quant(model_config, quantization) + _, _named_params = model.export_tvm( # type: ignore[misc] + spec=model.get_default_spec(), # type: ignore[attr-defined] + allow_extern=True, + ) + named_parameters = dict(_named_params) + + mapping = ExternMapping() + + for i in range(model_config.num_hidden_layers): + # Add QKV in self attention + attn = f"model.layers.{i}.self_attn" + for quantize_suffix in ["qweight", "qzeros", "scales"]: + mlc_name = f"{attn}.qkv_proj.{quantize_suffix}" + assert mlc_name in named_parameters + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{attn}.q_proj.{quantize_suffix}", + f"{attn}.k_proj.{quantize_suffix}", + f"{attn}.v_proj.{quantize_suffix}", + ], + functools.partial( + lambda q, k, v, dtype: np.concatenate([q, k, v], axis=0).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + + # Concat gate and up in MLP + mlp = f"model.layers.{i}.mlp" + for quantize_suffix in ["qweight", "qzeros", "scales"]: + mlc_name = f"{mlp}.gate_up_proj.{quantize_suffix}" + assert mlc_name in named_parameters + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{mlp}.gate_proj.{quantize_suffix}", + f"{mlp}.up_proj.{quantize_suffix}", + ], + functools.partial( + lambda gate, up, dtype: np.concatenate([gate, up], axis=0).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + + # inv_freq is not used in the model + mapping.add_unused(f"{attn}.rotary_emb.inv_freq") + + for mlc_name, mlc_param in named_parameters.items(): + if mlc_name not in mapping.param_map: + mapping.add_mapping( + mlc_name, + [mlc_name], + functools.partial(lambda x, dtype: x.astype(dtype), dtype=mlc_param.dtype), + ) + return mapping diff --git a/python/mlc_chat/model/mistral/mistral_model.py b/python/mlc_chat/model/mistral/mistral_model.py new file mode 100644 index 0000000..d2b5c57 --- /dev/null +++ b/python/mlc_chat/model/mistral/mistral_model.py @@ -0,0 +1,528 @@ +""" +Implementation for Mistral architecture. +""" +import dataclasses +from typing import Any, Dict, Optional + +from tvm import relax as rx +from tvm import te, tir +from tvm.relax.frontend import nn +from tvm.relax.frontend.nn import Tensor, op + +from mlc_chat import op as op_ext +from mlc_chat.support import logging +from mlc_chat.support import tensor_parallel as tp +from mlc_chat.support.config import ConfigBase +from mlc_chat.support.style import bold + +logger = logging.getLogger(__name__) + + +@dataclasses.dataclass +class MistralConfig(ConfigBase): # pylint: disable=too-many-instance-attributes + """Configuration of the Mistral model.""" + + hidden_size: int + intermediate_size: int + num_attention_heads: int + num_hidden_layers: int + rms_norm_eps: float + vocab_size: int + position_embedding_base: int = 0 + num_key_value_heads: int = 0 + head_dim: int = 0 + sliding_window_size: int = 4096 + prefill_chunk_size: int = 0 + attention_sink_size: int = 4 + tensor_parallel_shards: int = 1 + kwargs: Dict[str, Any] = dataclasses.field(default_factory=dict) + + def __post_init__(self): + if self.position_embedding_base == 0: + if "rope_theta" in self.kwargs: + self.position_embedding_base = self.kwargs.pop("rope_theta") + else: + self.position_embedding_base = 10000 + if self.num_key_value_heads == 0: + self.num_key_value_heads = self.num_attention_heads + if self.head_dim == 0: + self.head_dim = self.hidden_size // self.num_attention_heads + assert self.num_attention_heads % self.num_key_value_heads == 0 + assert self.head_dim * self.num_attention_heads == self.hidden_size + assert self.attention_sink_size >= 0 + if self.prefill_chunk_size == 0: + logger.info( + "%s defaults to %s (%d)", + bold("prefill_chunk_size"), + bold("sliding_window_size"), + self.sliding_window_size, + ) + self.prefill_chunk_size = self.sliding_window_size + elif self.prefill_chunk_size > self.sliding_window_size: + logger.info( + "Overriding %s from %d to %d (%s)", + bold("prefill_chunk_size"), + self.prefill_chunk_size, + self.sliding_window_size, + bold("sliding_window_size"), + ) + self.prefill_chunk_size = self.sliding_window_size + + +# pylint: disable=invalid-name,missing-docstring + + +class RotaryEmbedding(nn.Module): + """Cache relative Rotary Embedding.""" + + def __init__(self, config: MistralConfig): + super().__init__() + self.head_dim = config.head_dim + self.position_embedding_base = config.position_embedding_base + + def forward(self, q: Tensor, k: Tensor, q_offset: tir.Var): + def te_op(x: te.Tensor, offset: tir.Var): + dtype = x.dtype + + def compute(b: tir.Var, s: tir.Var, h: tir.Var, d: tir.Var): + head_dim = tir.const(self.head_dim, "int32") + position_embedding_base = tir.const(self.position_embedding_base, "float32") + freq = tir.power( + position_embedding_base, + (d * 2 % head_dim).astype("float32") / head_dim, + ) + freq = (offset + s) / freq + cos = tir.cos(freq).astype(dtype) * x[b, s, h, d] + sin = tir.sin(freq).astype(dtype) * tir.if_then_else( + d < head_dim // 2, + -x[b, s, h, d + head_dim // 2], + x[b, s, h, d - head_dim // 2], + ) + return cos + sin + + return te.compute(x.shape, compute, name="rotary") + + q_embed = op.tensor_expr_op( + te_op, + "rotary_embedding", + args=[q, q_offset], + attrs={"mlc.rotary_embedding_to_all_dims": True}, + ) + k_embed = op.tensor_expr_op( + te_op, "rotary_embedding", args=[k, 0], attrs={"mlc.rotary_embedding_to_all_dims": True} + ) + return q_embed, k_embed + + +class MistralMLP(nn.Module): + """Same as in Llama architecture (LlamaFFN).""" + + def __init__(self, config: MistralConfig): + super().__init__() + self.intermediate_size = config.intermediate_size // config.tensor_parallel_shards + self.gate_up_proj = nn.Linear( + in_features=config.hidden_size, + out_features=2 * self.intermediate_size, + bias=False, + ) + self.down_proj = nn.Linear(self.intermediate_size, config.hidden_size, bias=False) + + def forward(self, x: Tensor): + concat_x1_x2 = self.gate_up_proj(x) + x1, x2 = op.split(concat_x1_x2, 2, axis=-1) + return self.down_proj(op.silu(x1) * x2) + + +class MistralAttention(nn.Module): # pylint: disable=too-many-instance-attributes + """Same as LlamaAttention, but with sliding window attention using a rolling buffer cache.""" + + def __init__(self, config: MistralConfig, rotary_embedding: RotaryEmbedding): + self.rotary_embedding = rotary_embedding + self.hidden_size = config.hidden_size + self.head_dim = config.head_dim + self.num_q_heads = config.num_attention_heads // config.tensor_parallel_shards + self.num_kv_heads = config.num_key_value_heads // config.tensor_parallel_shards + self.sliding_window_size = config.sliding_window_size + self.attention_sink_size = config.attention_sink_size + self.qkv_proj = nn.Linear( + in_features=config.hidden_size, + out_features=(self.num_q_heads + 2 * self.num_kv_heads) * self.head_dim, + bias=False, + ) + self.o_proj = nn.Linear(self.num_q_heads * self.head_dim, config.hidden_size, bias=False) + self.k_cache = RollingKVCacheWithSinks( + self.sliding_window_size, [self.num_kv_heads, self.head_dim] + ) + self.v_cache = RollingKVCacheWithSinks( + self.sliding_window_size, [self.num_kv_heads, self.head_dim] + ) + + def interleave_kv( # pylint: disable=too-many-arguments,too-many-locals + self, + k_cur: Tensor, + v_cur: Tensor, + kv_seq_len: tir.Var, + rolling_cache_len: tir.Var, + cache_offset: tir.Var, + ): + """Unrotate and concatenate currunt and cached k and v""" + h_kv, d = self.num_kv_heads, self.head_dim + kv_s, c, o = kv_seq_len, rolling_cache_len, cache_offset + b = k_cur.shape[0] + + k_cached = op.reshape(self.k_cache.view(c), (b, c, h_kv, d)) + v_cached = op.reshape(self.v_cache.view(c), (b, c, h_kv, d)) + + def _cache_unrotate(x_cached, rolling_cache_len, cache_offset): + return te.compute( + (b, kv_s, h_kv, d), + lambda xb, xs, xh, xd: te.if_then_else( + xs < self.attention_sink_size, + x_cached[xb, xs, xh, xd], + te.if_then_else( + xs < rolling_cache_len - cache_offset + self.attention_sink_size, + x_cached[xb, xs + cache_offset - self.attention_sink_size, xh, xd], + x_cached[xb, xs + cache_offset - rolling_cache_len, xh, xd], + ), + ), + name="cache_unrotate_te", + ) + + def _cache_cur_concat(x_cached, x_cur, rolling_cache_len): + return te.compute( + (b, kv_s, h_kv, d), + lambda xb, xs, xh, xd: te.if_then_else( + xs < rolling_cache_len, + x_cached[xb, xs, xh, xd], + x_cur[xb, xs - rolling_cache_len, xh, xd], + ), + name="cache_cur_concat_te", + ) + + k_cached = op.tensor_expr_op( + _cache_unrotate, + name_hint="te_cache_unrotate_key", + args=[k_cached, c, o], + ) + k = op.tensor_expr_op( + _cache_cur_concat, + name_hint="te_cache_cur_concat_key", + args=[k_cached, k_cur, c], + ) + + v_cached = op.tensor_expr_op( + _cache_unrotate, + name_hint="te_cache_unrotate_value", + args=[v_cached, c, o], + ) + v = op.tensor_expr_op( + _cache_cur_concat, + name_hint="te_cache_cur_concat_value", + args=[v_cached, v_cur, c], + ) + + self.k_cache.override( + op.squeeze(k_cur, axis=0), self.sliding_window_size, self.attention_sink_size + ) + self.v_cache.override( + op.squeeze(v_cur, axis=0), self.sliding_window_size, self.attention_sink_size + ) + + return k, v + + def forward( # pylint: disable=too-many-arguments, too-many-locals + self, + hidden_states: Tensor, + attention_mask: Tensor, + rolling_cache_len: tir.Var, # Number of elements currently in the cache. + kv_seq_len: tir.Var, # Equals to ``seq_len + rolling_cache_len``. + cache_offset: tir.Var, + ): + """Forward pass of MistralAttention, performing QKV.""" + d, h_q, h_kv = self.head_dim, self.num_q_heads, self.num_kv_heads + b, s, _ = hidden_states.shape + assert b == 1, "Only support batch size 1 at this moment." + qkv_cur = self.qkv_proj(hidden_states) + qkv_cur = op.reshape(qkv_cur, (b, s, h_q + 2 * h_kv, d)) + q, k_cur, v_cur = op.split(qkv_cur, [h_q, h_q + h_kv], axis=2) + k, v = self.interleave_kv(k_cur, v_cur, kv_seq_len, rolling_cache_len, cache_offset) + q, k = self.rotary_embedding(q, k, rolling_cache_len) + output = op_ext.attention(q, k, v, attention_mask) + return self.o_proj(output) + + +class RollingKVCacheWithSinks(nn.KVCache): + """ + Rolling buffer cache implementation. + """ + + cache: Optional[rx.Var] + + def override(self, new_element: Tensor, max_cache_size: int, attention_sink_size: int) -> None: + """ + Override cache elements in RollingKVCacheWithSinks. + + Parameters + ---------- + new_element : Tensor + The new tensor to append. + + max_cache_size : int + Max size of the cache. + + attention_sink_size : int + Number of stored attention sinks. + """ + if new_element.dtype != self.dtype: + raise TypeError( + f'RollingKVCacheWithSinks has been set to use dtype "{self.dtype}", ' + f'but got "{new_element.dtype}"' + ) + self.cache = rx.BlockBuilder.current().emit( + rx.Call( + rx.extern("vm.builtin.attention_kv_cache_window_override_with_sinks"), + args=[ + self.cache, + new_element._expr, # pylint: disable=protected-access + rx.PrimValue(max_cache_size), + rx.PrimValue(attention_sink_size), + ], + sinfo_args=[rx.ObjectStructInfo()], + ) + ) + + +class MistralDecoderLayer(nn.Module): + """Exact same as LlamaDecoderLayer.""" + + def __init__(self, config: MistralConfig, rotary_embedding: RotaryEmbedding): + rms_norm_eps = config.rms_norm_eps + self.self_attn = MistralAttention(config, rotary_embedding) + self.mlp = MistralMLP(config) + self.input_layernorm = nn.RMSNorm(config.hidden_size, -1, rms_norm_eps, bias=False) + self.post_attention_layernorm = nn.RMSNorm(config.hidden_size, -1, rms_norm_eps, bias=False) + + def _set_tp(): + def _set(layer, hint): + layer.weight.attrs["shard_strategy"] = hint + + hd = config.head_dim + q = self.self_attn.num_q_heads * hd + k = self.self_attn.num_kv_heads * hd + v = self.self_attn.num_kv_heads * hd + i = self.mlp.intermediate_size + _set(self.self_attn.qkv_proj, tp.ShardSingleDim("_shard_qkv", segs=[q, k, v], dim=0)) + _set(self.self_attn.o_proj, tp.ShardSingleDim("_shard_o", dim=1)) + _set(self.mlp.gate_up_proj, tp.ShardSingleDim("_shard_mlp_up", segs=[i, i], dim=0)) + _set(self.mlp.down_proj, tp.ShardSingleDim("_shard_mlp_down", dim=1)) + + self.tensor_parallel_shards = config.tensor_parallel_shards + _set_tp() + + def forward( # pylint: disable=too-many-arguments + self, + hidden_states: Tensor, + attention_mask: Tensor, + rolling_cache_len: tir.Var, + kv_seq_len: tir.Var, + cache_offset: tir.Var, + ): + """Forward pass of a decoder layer; calculate attention, and add an residual connection.""" + + def _apply_residual(out, residual): + if self.tensor_parallel_shards > 1: + return op.ccl_allreduce(out, "sum") + residual + return out + residual + + out = self.self_attn( + self.input_layernorm(hidden_states), + attention_mask, + rolling_cache_len, + kv_seq_len, + cache_offset, + ) + hidden_states = _apply_residual(out, residual=hidden_states) + out = self.mlp(self.post_attention_layernorm(hidden_states)) + hidden_states = _apply_residual(out, residual=hidden_states) + return hidden_states + + +class MistralModel(nn.Module): + """Exact same as LlamaModel.""" + + def __init__(self, config: MistralConfig): + assert config.hidden_size % config.num_attention_heads == 0 + rotary_embedding = RotaryEmbedding(config) + self.embed_tokens = nn.Embedding("vocab_size", config.hidden_size) + self.layers = nn.ModuleList( + [MistralDecoderLayer(config, rotary_embedding) for _ in range(config.num_hidden_layers)] + ) + self.norm = nn.RMSNorm(config.hidden_size, -1, config.rms_norm_eps, bias=False) + self.tensor_parallel_shards = config.tensor_parallel_shards + + def forward( # pylint: disable=too-many-arguments + self, + inputs: Tensor, + rolling_cache_len: tir.Var, + kv_seq_len: tir.Var, + cache_offset: tir.Var, + attention_mask: Tensor, + ): + """Forward pass of the model, passing through all decoder layers.""" + if self.tensor_parallel_shards > 1: + inputs = op.ccl_broadcast_from_worker0(inputs) + hidden_states = self.embed_tokens(inputs) + for layer in self.layers: + hidden_states = layer( + hidden_states, attention_mask, rolling_cache_len, kv_seq_len, cache_offset + ) + hidden_states = self.norm(hidden_states) + return hidden_states + + +class MistralForCasualLM(nn.Module): + """Same as LlamaForCausalLM, except for the use of sliding window attention.""" + + def __init__(self, config: MistralConfig): + self.model = MistralModel(config) + self.lm_head = nn.Linear(config.hidden_size, "vocab_size", bias=False) + self.sliding_window_size = config.sliding_window_size + self.dtype = "float32" + + def to(self, dtype: Optional[str] = None): + super().to(dtype=dtype) + if dtype is not None: + self.dtype = dtype + + def forward( # pylint: disable=too-many-arguments + self, + inputs: Tensor, + rolling_cache_len: tir.Var, + kv_seq_len: tir.Var, + cache_offset: tir.Var, + attention_mask: Tensor, + ): + """Forward pass.""" + + def _index(x: te.Tensor): # x[:-1,:] + b, s, d = x.shape + return te.compute((b, 1, d), lambda i, _, k: x[i, s - 1, k], name="index") + + hidden_states = self.model( + inputs, rolling_cache_len, kv_seq_len, cache_offset, attention_mask + ) + hidden_states = op.tensor_expr_op(_index, name_hint="index", args=[hidden_states]) + logits = self.lm_head(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits + + def prefill( + self, + inputs: Tensor, + rolling_cache_len: tir.Var, + kv_seq_len: tir.Var, + cache_offset: tir.Var, + ): + """ + Prefilling the prompt. + + Parameters + ---------- + inputs: Tensor + Input tokens, having ``seq_len`` number of tokens. + + rolling_cache_len: tir.Var + Number of elements currently in the cache. + + kv_seq_len: tir.Var + Equals to ``seq_len + rolling_cache_len``. + + cache_offset: tir.Var + Next position to be overrided on the rolling kv cache. + """ + + def _sliding_window_attention_mask( + batch_size, seq_len, rolling_cache_len, kv_seq_len, sliding_window_size + ): + # See `tests/legacy-python/test_sliding_window_mask.py` for its behavior + return te.compute( + (batch_size, 1, seq_len, kv_seq_len), + lambda b, _, i, j: tir.Select( + tir.all( + i + rolling_cache_len >= j, i + rolling_cache_len - j < sliding_window_size + ), + tir.max_value(self.dtype), + tir.min_value(self.dtype), + ), + name="sliding_window_attention_mask_prefill", + ) + + batch_size, seq_len = inputs.shape + attention_mask = op.tensor_expr_op( + _sliding_window_attention_mask, + name_hint="sliding_window_attention_mask_prefill", + args=[ + batch_size, + seq_len, + rolling_cache_len, + kv_seq_len, + self.sliding_window_size, + ], + ) + return self.forward(inputs, rolling_cache_len, kv_seq_len, cache_offset, attention_mask) + + def decode( + self, + inputs: Tensor, + rolling_cache_len: tir.Var, + kv_seq_len: tir.Var, + cache_offset: tir.Var, + ): + """Decoding step.""" + batch_size, seq_len = inputs.shape + attention_mask = op.full( + shape=[batch_size, 1, seq_len, kv_seq_len], + fill_value=tir.max_value(self.dtype), + dtype=self.dtype, + ) + return self.forward(inputs, rolling_cache_len, kv_seq_len, cache_offset, attention_mask) + + def softmax_with_temperature(self, logits: Tensor, temperature: Tensor): + """Softmax.""" + return op.softmax(logits / temperature, axis=-1) + + def get_default_spec(self): + """Needed for ``export_tvm()``.""" + batch_size = 1 + mod_spec = { + "prefill": { + "inputs": nn.spec.Tensor([batch_size, "seq_len"], "int32"), + "rolling_cache_len": int, + "kv_seq_len": int, + "cache_offset": int, + "$": { + "param_mode": "packed", + "effect_mode": "packed", + }, + }, + "decode": { + "inputs": nn.spec.Tensor([batch_size, 1], "int32"), + "rolling_cache_len": int, + "kv_seq_len": int, + "cache_offset": int, + "$": { + "param_mode": "packed", + "effect_mode": "packed", + }, + }, + "softmax_with_temperature": { + "logits": nn.spec.Tensor([1, 1, "vocab_size"], "float32"), + "temperature": nn.spec.Tensor([], "float32"), + "$": { + "param_mode": "none", + "effect_mode": "none", + }, + }, + } + return nn.spec.ModuleSpec.from_raw(mod_spec, self) diff --git a/python/mlc_chat/model/mistral/mistral_quantization.py b/python/mlc_chat/model/mistral/mistral_quantization.py new file mode 100644 index 0000000..e3622fd --- /dev/null +++ b/python/mlc_chat/model/mistral/mistral_quantization.py @@ -0,0 +1,69 @@ +"""This file specifies how MLC's Mistral parameters are quantized using group quantization +or other formats.""" +from typing import Tuple + +from tvm.relax.frontend import nn + +from mlc_chat.loader import QuantizeMapping +from mlc_chat.quantization import AWQQuantize, FTQuantize, GroupQuantize, NoQuantize + +from .mistral_model import MistralConfig, MistralForCasualLM + + +def group_quant( + model_config: MistralConfig, + quantization: GroupQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Mistral-architecture model using group quantization.""" + model: nn.Module = MistralForCasualLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def ft_quant( + model_config: MistralConfig, + quantization: FTQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Mistral-architecture model using FasterTransformer quantization.""" + model: nn.Module = MistralForCasualLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def awq_quant( + model_config: MistralConfig, + quantization: AWQQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Mistral-architecture model using Activation-aware Weight Quantization(AWQ).""" + model: nn.Module = MistralForCasualLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def no_quant( + model_config: MistralConfig, + quantization: NoQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Llama2 model without quantization.""" + model: nn.Module = MistralForCasualLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + return model, quant_map diff --git a/python/mlc_chat/model/mixtral/__init__.py b/python/mlc_chat/model/mixtral/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/mlc_chat/model/mixtral/mixtral_loader.py b/python/mlc_chat/model/mixtral/mixtral_loader.py new file mode 100644 index 0000000..12e96eb --- /dev/null +++ b/python/mlc_chat/model/mixtral/mixtral_loader.py @@ -0,0 +1,129 @@ +""" +This file specifies how MLC's Mixtral parameter maps from other formats, for example HuggingFace +PyTorch, HuggingFace safetensors. +""" +import functools + +import numpy as np + +from mlc_chat.loader import ExternMapping +from mlc_chat.quantization import Quantization + +from .mixtral_model import MixtralConfig, MixtralForCasualLM + + +def huggingface(model_config: MixtralConfig, quantization: Quantization) -> ExternMapping: + """Returns a parameter mapping that maps from the names of MLC LLM parameters to + the names of HuggingFace PyTorch parameters. + + Parameters + ---------- + model_config : MixtralConfig + The configuration of the Mixtral model. + + quantization : Quantization + The quantization configuration. + + Returns + ------- + param_map : ExternMapping + The parameter mapping from MLC to HuggingFace PyTorch. + """ + model = MixtralForCasualLM(model_config) + if quantization is not None: + model.to(quantization.model_dtype) + _, _named_params, _ = model.export_tvm( # type: ignore[misc] + spec=model.get_default_spec(), + allow_extern=True, + ) + named_parameters = dict(_named_params) + + mapping = ExternMapping() + + for i in range(model_config.num_hidden_layers): + # Add QKV in self attention + attn = f"model.layers.{i}.self_attn" + mlc_name = f"{attn}.qkv_proj.weight" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{attn}.q_proj.weight", + f"{attn}.k_proj.weight", + f"{attn}.v_proj.weight", + ], + functools.partial( + lambda q, k, v, dtype: np.concatenate([q, k, v], axis=0).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + + # Add gates in MLP (when MoE is enabled) + mlp = f"model.layers.{i}.block_sparse_moe" + mlc_mlp = f"model.layers.{i}.moe" + mlc_name = f"{mlc_mlp}.e1_e3.weight" + mlc_param = named_parameters[mlc_name] + + def combine_expert_gate_up(*hf_params, dtype): + stack = [] + for i in range(0, len(hf_params), 2): + stack.append(np.concatenate([hf_params[i], hf_params[i + 1]], axis=0)) + return np.stack(stack, axis=0).astype(dtype) + + mapping.add_mapping( + mlc_name, + functools.reduce( + lambda a, b: a + b, + [ + [ + f"{mlp}.experts.{expert_id}.w1.weight", + f"{mlp}.experts.{expert_id}.w3.weight", + ] + for expert_id in range(model_config.num_local_experts) + ], + ), + functools.partial( + combine_expert_gate_up, + dtype=mlc_param.dtype, + ), + ) + + mlc_name = f"{mlc_mlp}.e2.weight" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{mlp}.experts.{expert_id}.w2.weight" + for expert_id in range(model_config.num_local_experts) + ], + functools.partial( + lambda *hf_params, dtype: np.stack(hf_params, axis=0).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + + mlc_name = f"{mlc_mlp}.gate.weight" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [f"{mlp}.gate.weight"], + functools.partial( + lambda x, dtype: x.astype(dtype), + dtype=mlc_param.dtype, + ), + ) + + # inv_freq is not used in the model + mapping.add_unused(f"{attn}.rotary_emb.inv_freq") + + for mlc_name, mlc_param in named_parameters.items(): + if mlc_name not in mapping.param_map: + mapping.add_mapping( + mlc_name, + [mlc_name], + functools.partial( + lambda x, dtype: x.astype(dtype), + dtype=mlc_param.dtype, + ), + ) + return mapping diff --git a/python/mlc_chat/model/mixtral/mixtral_model.py b/python/mlc_chat/model/mixtral/mixtral_model.py new file mode 100644 index 0000000..a2740f1 --- /dev/null +++ b/python/mlc_chat/model/mixtral/mixtral_model.py @@ -0,0 +1,176 @@ +"""Implementation for Mistral architecture.""" +import dataclasses + +from tvm import tir +from tvm.relax.frontend import nn +from tvm.relax.frontend.nn import Tensor, op + +from mlc_chat import op as op_ext +from mlc_chat.model.llama.llama_model import ( + LlamaAttention, + LlamaConfig, + LlamaForCasualLM, + LlamaModel, +) +from mlc_chat.nn import PagedKVCache +from mlc_chat.nn.expert import MixtralExperts +from mlc_chat.support import logging +from mlc_chat.support import tensor_parallel as tp + +logger = logging.getLogger(__name__) + + +@dataclasses.dataclass +class MixtralConfig(LlamaConfig): # pylint: disable=too-many-instance-attributes + """Configuration of the Mixtral model.""" + + num_local_experts: int = 0 + num_experts_per_tok: int = 0 + + +# pylint: disable=invalid-name,missing-docstring,too-many-locals,fixme + + +class MixtralMoE(nn.Module): + """Mixture of experts""" + + def __init__(self, config: MixtralConfig): + super().__init__() + self.num_experts_per_tok = config.num_experts_per_tok + self.num_local_experts = config.num_local_experts + self.intermediate_size = config.intermediate_size // config.tensor_parallel_shards + self.gate = nn.Linear( + in_features=config.hidden_size, + out_features=config.num_local_experts, + bias=False, + ) + self.e1_e3 = MixtralExperts( + self.num_local_experts, + in_features=config.hidden_size, + out_features=2 * self.intermediate_size, + ) + self.e2 = MixtralExperts( + self.num_local_experts, + in_features=self.intermediate_size, + out_features=config.hidden_size, + ) + self.dtype = "float32" + + def forward(self, x: Tensor): + def _expert_forward(x: Tensor, indptr: Tensor): + x1_x3 = self.e1_e3(x, indptr) + x1, x3 = op.split(x1_x3, indices_or_sections=2, axis=-1) + x = self.e2(op.silu(x1) * x3, indptr) + return x + + experts_per_tok = self.num_experts_per_tok # activated experts per token + local_experts = self.num_local_experts # total number of experts + batch_size, seq_len, hidden_size = x.shape + num_tokens = batch_size * seq_len + x = x.reshape(num_tokens, hidden_size) + # gate: [num_tokens, local_experts] + gate: Tensor = self.gate(x) + # expert_weights: [num_tokens, experts_per_tok] + # expert_indices: [num_tokens, experts_per_tok] + expert_weights, expert_indices = op_ext.moe_misc.gating_softmax_topk(gate, experts_per_tok) + use_ft = op_ext.get_store().faster_transformer and self.dtype == "float16" + if num_tokens == 1: + # x: [num_tokens * experts_per_tok, hidden_size] + x = _expert_forward(x, expert_indices) + else: + # cumsum: [num_tokens * local_experts] + cumsum = op_ext.moe_misc.moe_cumsum(expert_indices, local_experts) + # indices: [num_tokens * experts_per_tok] + reverse_indices, token_indices = op_ext.moe_misc.get_indices(cumsum, expert_indices) + if use_ft: + # indptr: [num_local_experts] + indptr = op_ext.moe_misc.get_indptr( + cumsum, local_experts, num_tokens, inclusive=True, out_dtype="int64" + ) + else: + # indptr: [num_local_experts + 1] + indptr = op_ext.moe_misc.get_indptr( + cumsum, local_experts, num_tokens, inclusive=False, out_dtype="int32" + ) + # x: [num_tokens * experts_per_tok, hidden_size] + x = op.take(x, token_indices, axis=0) + x = _expert_forward(x, indptr) + x = op_ext.moe_misc.scatter_output(x, reverse_indices) + # x: [num_tokens, experts_per_tok, hidden_size] + x = x.reshape( # pylint: disable=too-many-function-args + num_tokens, experts_per_tok, hidden_size + ) * expert_weights.reshape( # pylint: disable=too-many-function-args + num_tokens, experts_per_tok, 1 + ) + # x: [num_tokens, hidden_size] + x = op_ext.moe_misc.moe_sum(x, dim=1) + x = x.reshape(batch_size, seq_len, hidden_size) # pylint: disable=too-many-function-args + return x + + +class MixtralDecoderLayer(nn.Module): + """Mixtral decoder layer""" + + def __init__(self, config: MixtralConfig): + eps = config.rms_norm_eps + self.self_attn = LlamaAttention(config) + self.moe = MixtralMoE(config) + self.input_layernorm = nn.RMSNorm(config.hidden_size, -1, eps, bias=False) + self.post_attention_layernorm = nn.RMSNorm(config.hidden_size, -1, eps, bias=False) + + def _set_tp(): + def _set(layer, hint): + layer.weight.attrs["shard_strategy"] = hint + + hd = config.head_dim + q = self.self_attn.num_q_heads * hd + k = self.self_attn.num_kv_heads * hd + v = self.self_attn.num_kv_heads * hd + i = self.moe.intermediate_size + _set(self.self_attn.qkv_proj, tp.ShardSingleDim("_shard_qkv", segs=[q, k, v], dim=0)) + _set(self.self_attn.o_proj, tp.ShardSingleDim("_shard_o", dim=1)) + _set(self.moe.e1_e3, tp.ShardSingleDim("_shard_mlp_up", segs=[i, i], dim=1)) + _set(self.moe.e2, tp.ShardSingleDim("_shard_mlp_down", dim=2)) + + self.tensor_parallel_shards = config.tensor_parallel_shards + _set_tp() + + def forward(self, hidden_states: Tensor, attention_mask: Tensor, total_seq_len: tir.Var): + """Forward pass of a decoder layer; calculate attention, and add an residual connection.""" + out = self.self_attn(self.input_layernorm(hidden_states), attention_mask, total_seq_len) + hidden_states = self._apply_residual(out, residual=hidden_states) + out = self.moe(self.post_attention_layernorm(hidden_states)) + hidden_states = self._apply_residual(out, residual=hidden_states) + return hidden_states + + def batch_forward(self, hidden_states: Tensor, paged_kv_cache: PagedKVCache, layer_id: int): + out = self.self_attn.batch_forward( + self.input_layernorm(hidden_states), paged_kv_cache, layer_id + ) + hidden_states = self._apply_residual(out, residual=hidden_states) + out = self.moe(self.post_attention_layernorm(hidden_states)) + hidden_states = self._apply_residual(out, residual=hidden_states) + return hidden_states + + def _apply_residual(self, out, residual): + if self.tensor_parallel_shards > 1: + return op.ccl_allreduce(out, "sum") + residual + return out + residual + + +class MixtralModel(LlamaModel): + """Exact same as LlamaModel.""" + + def __init__(self, config: MixtralConfig): + super().__init__(config) + self.layers = nn.ModuleList( + [MixtralDecoderLayer(config) for _ in range(config.num_hidden_layers)] + ) + + +class MixtralForCasualLM(LlamaForCasualLM): + """Same as LlamaForCausalLM.""" + + def __init__(self, config: MixtralConfig): + super().__init__(config) + self.model = MixtralModel(config) diff --git a/python/mlc_chat/model/mixtral/mixtral_quantization.py b/python/mlc_chat/model/mixtral/mixtral_quantization.py new file mode 100644 index 0000000..37f7ad5 --- /dev/null +++ b/python/mlc_chat/model/mixtral/mixtral_quantization.py @@ -0,0 +1,61 @@ +"""This file specifies how MLC's Mistral parameters are quantized using group quantization +or other formats.""" +from typing import Tuple + +from tvm.relax.frontend import nn + +from mlc_chat.loader import QuantizeMapping +from mlc_chat.quantization import AWQQuantize, FTQuantize, GroupQuantize, NoQuantize + +from .mixtral_model import MixtralConfig, MixtralForCasualLM + + +def group_quant( + model_config: MixtralConfig, + quantization: GroupQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Mixtral-architecture model using group quantization.""" + model: nn.Module = MixtralForCasualLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def ft_quant( + model_config: MixtralConfig, + quantization: FTQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Mixtral-architecture model using FasterTransformer quantization.""" + model: nn.Module = MixtralForCasualLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def awq_quant( + model_config: MixtralConfig, + quantization: AWQQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Mixtral-architecture model using Activation-aware Weight Quantization(AWQ).""" + raise NotImplementedError("AWQ is not implemented for Mixtral models.") + + +def no_quant( + model_config: MixtralConfig, + quantization: NoQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Mixtral model without quantization.""" + model: nn.Module = MixtralForCasualLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + return model, quant_map diff --git a/python/mlc_chat/model/model.py b/python/mlc_chat/model/model.py new file mode 100644 index 0000000..68d052c --- /dev/null +++ b/python/mlc_chat/model/model.py @@ -0,0 +1,251 @@ +"""A centralized registry of all existing model architures and their configurations.""" + +import dataclasses +from typing import Any, Callable, Dict, Tuple + +from tvm.relax.frontend import nn + +from mlc_chat.loader import ExternMapping, QuantizeMapping +from mlc_chat.quantization.quantization import Quantization + +from .baichuan import baichuan_loader, baichuan_model, baichuan_quantization +from .gemma import gemma_loader, gemma_model, gemma_quantization +from .gpt2 import gpt2_loader, gpt2_model, gpt2_quantization +from .gpt_bigcode import gpt_bigcode_loader, gpt_bigcode_model, gpt_bigcode_quantization +from .gpt_neox import gpt_neox_loader, gpt_neox_model, gpt_neox_quantization +from .llama import llama_loader, llama_model, llama_quantization +from .mistral import mistral_loader, mistral_model, mistral_quantization +from .mixtral import mixtral_loader, mixtral_model, mixtral_quantization +from .phi import phi_loader, phi_model, phi_quantization +from .qwen import qwen_loader, qwen_model, qwen_quantization +from .qwen2 import qwen2_loader, qwen2_model, qwen2_quantization +from .stable_lm import stablelm_loader, stablelm_model, stablelm_quantization + +ModelConfig = Any +"""A ModelConfig is an object that represents a model architecture. It is required to have +a class method `from_file` with the following signature: + + def from_file(cls, path: Path) -> ModelConfig: + ... +""" + +FuncGetExternMap = Callable[[ModelConfig, Quantization], ExternMapping] +FuncQuantization = Callable[[ModelConfig, Quantization], Tuple[nn.Module, QuantizeMapping]] + + +@dataclasses.dataclass +class Model: + """All about a model architecture: its configuration, its parameter loader and quantization. + + Parameters + ---------- + name : str + The name of the model. + + model : Callable[[ModelConfig], nn.Module] + A method that creates the `nn.Module` that represents the model from `ModelConfig`. + + config : ModelConfig + A class that has a `from_file` class method, whose signature is "Path -> ModelConfig". + + source : Dict[str, FuncGetExternMap] + A dictionary that maps the name of a source format to parameter mapping. + + quantize: Dict[str, FuncQuantization] + A dictionary that maps the name of a quantization method to quantized model and the + quantization parameter mapping. + """ + + name: str + config: ModelConfig + model: Callable[[ModelConfig], nn.Module] + source: Dict[str, FuncGetExternMap] + quantize: Dict[str, FuncQuantization] + + +MODELS: Dict[str, Model] = { + "llama": Model( + name="llama", + model=llama_model.LlamaForCasualLM, + config=llama_model.LlamaConfig, + source={ + "huggingface-torch": llama_loader.huggingface, + "huggingface-safetensor": llama_loader.huggingface, + "awq": llama_loader.awq, + }, + quantize={ + "no-quant": llama_quantization.no_quant, + "group-quant": llama_quantization.group_quant, + "ft-quant": llama_quantization.ft_quant, + "awq": llama_quantization.awq_quant, + }, + ), + "mistral": Model( + name="mistral", + model=mistral_model.MistralForCasualLM, + config=mistral_model.MistralConfig, + source={ + "huggingface-torch": mistral_loader.huggingface, + "huggingface-safetensor": mistral_loader.huggingface, + "awq": mistral_loader.awq, + }, + quantize={ + "group-quant": mistral_quantization.group_quant, + "no-quant": mistral_quantization.no_quant, + "ft-quant": mistral_quantization.ft_quant, + }, + ), + "gemma": Model( + name="gemma", + model=gemma_model.GemmaForCausalLM, + config=gemma_model.GemmaConfig, + source={ + "huggingface-torch": gemma_loader.huggingface, + "huggingface-safetensor": gemma_loader.huggingface, + }, + quantize={ + "no-quant": gemma_quantization.no_quant, + "group-quant": gemma_quantization.group_quant, + }, + ), + "gpt2": Model( + name="gpt2", + model=gpt2_model.GPT2LMHeadModel, + config=gpt2_model.GPT2Config, + source={ + "huggingface-torch": gpt2_loader.huggingface, + "huggingface-safetensor": gpt2_loader.huggingface, + }, + quantize={ + "no-quant": gpt2_quantization.no_quant, + "group-quant": gpt2_quantization.group_quant, + "ft-quant": gpt2_quantization.ft_quant, + }, + ), + "mixtral": Model( + name="mixtral", + model=mixtral_model.MixtralForCasualLM, + config=mixtral_model.MixtralConfig, + source={ + "huggingface-torch": mixtral_loader.huggingface, + "huggingface-safetensor": mixtral_loader.huggingface, + }, + quantize={ + "no-quant": mixtral_quantization.no_quant, + "group-quant": mixtral_quantization.group_quant, + "ft-quant": mixtral_quantization.ft_quant, + }, + ), + "gpt_neox": Model( + name="gpt_neox", + model=gpt_neox_model.GPTNeoXForCausalLM, + config=gpt_neox_model.GPTNeoXConfig, + source={ + "huggingface-torch": gpt_neox_loader.huggingface, + "huggingface-safetensor": gpt_neox_loader.huggingface, + }, + quantize={ + "no-quant": gpt_neox_quantization.no_quant, + "group-quant": gpt_neox_quantization.group_quant, + "ft-quant": gpt_neox_quantization.ft_quant, + }, + ), + "gpt_bigcode": Model( + name="gpt_bigcode", + model=gpt_bigcode_model.GPTBigCodeForCausalLM, + config=gpt_bigcode_model.GPTBigCodeConfig, + source={ + "huggingface-torch": gpt_bigcode_loader.huggingface, + "huggingface-safetensor": gpt_bigcode_loader.huggingface, + }, + quantize={ + "no-quant": gpt_bigcode_quantization.no_quant, + "group-quant": gpt_bigcode_quantization.group_quant, + "ft-quant": gpt_bigcode_quantization.ft_quant, + }, + ), + "phi-msft": Model( + name="phi-msft", + model=phi_model.PhiForCausalLM, + config=phi_model.PhiConfig, + source={ + "huggingface-torch": phi_loader.huggingface, + "huggingface-safetensor": phi_loader.huggingface, + }, + quantize={ + "no-quant": phi_quantization.no_quant, + "group-quant": phi_quantization.group_quant, + "ft-quant": phi_quantization.ft_quant, + }, + ), + "phi": Model( + name="phi", + model=phi_model.PhiForCausalLM, + config=phi_model.Phi1Config, + source={ + "huggingface-torch": phi_loader.phi1_huggingface, + "huggingface-safetensor": phi_loader.phi1_huggingface, + }, + quantize={ + "no-quant": phi_quantization.no_quant, + "group-quant": phi_quantization.group_quant, + "ft-quant": phi_quantization.ft_quant, + }, + ), + "qwen": Model( + name="qwen", + model=qwen_model.QWenLMHeadModel, + config=qwen_model.QWenConfig, + source={ + "huggingface-torch": qwen_loader.huggingface, + "huggingface-safetensor": qwen_loader.huggingface, + }, + quantize={ + "no-quant": qwen_quantization.no_quant, + "group-quant": qwen_quantization.group_quant, + "ft-quant": qwen_quantization.ft_quant, + }, + ), + "qwen2": Model( + name="qwen2", + model=qwen2_model.QWen2LMHeadModel, + config=qwen2_model.QWen2Config, + source={ + "huggingface-torch": qwen2_loader.huggingface, + "huggingface-safetensor": qwen2_loader.huggingface, + }, + quantize={ + "no-quant": qwen2_quantization.no_quant, + "group-quant": qwen2_quantization.group_quant, + "ft-quant": qwen2_quantization.ft_quant, + }, + ), + "stablelm_epoch": Model( + name="stablelm_epoch", + model=stablelm_model.StableLMEpochForCausalLM, + config=stablelm_model.StableLMEpochConfig, + source={ + "huggingface-torch": stablelm_loader.huggingface, + "huggingface-safetensor": stablelm_loader.huggingface, + }, + quantize={ + "no-quant": stablelm_quantization.no_quant, + "group-quant": stablelm_quantization.group_quant, + "ft-quant": stablelm_quantization.ft_quant, + }, + ), + "baichuan": Model( + name="baichuan", + model=baichuan_model.BaichuanForCausalLM, + config=baichuan_model.BaichuanConfig, + source={ + "huggingface-torch": baichuan_loader.huggingface, + "huggingface-safetensor": baichuan_loader.huggingface, + }, + quantize={ + "no-quant": baichuan_quantization.no_quant, + "group-quant": baichuan_quantization.group_quant, + "ft-quant": baichuan_quantization.ft_quant, + }, + ), +} diff --git a/python/mlc_chat/model/model_preset.py b/python/mlc_chat/model/model_preset.py new file mode 100644 index 0000000..bacfd43 --- /dev/null +++ b/python/mlc_chat/model/model_preset.py @@ -0,0 +1,495 @@ +"""A builtin set of models available in MLC LLM.""" + +from typing import Any, Dict + +MODEL_PRESETS: Dict[str, Any] = { + "llama2_7b": { + "architectures": ["LlamaForCausalLM"], + "bos_token_id": 1, + "eos_token_id": 2, + "hidden_act": "silu", + "hidden_size": 4096, + "initializer_range": 0.02, + "intermediate_size": 11008, + "max_position_embeddings": 2048, + "model_type": "llama", + "num_attention_heads": 32, + "num_hidden_layers": 32, + "num_key_value_heads": 32, + "pad_token_id": 0, + "pretraining_tp": 1, + "rms_norm_eps": 1e-05, + "rope_scaling": None, + "tie_word_embeddings": False, + "torch_dtype": "float16", + "transformers_version": "4.31.0.dev0", + "use_cache": True, + "vocab_size": 32000, + "context_window_size": 2048, + "prefill_chunk_size": 2048, + }, + "llama2_13b": { + "_name_or_path": "meta-llama/Llama-2-13b-hf", + "architectures": ["LlamaForCausalLM"], + "bos_token_id": 1, + "eos_token_id": 2, + "hidden_act": "silu", + "hidden_size": 5120, + "initializer_range": 0.02, + "intermediate_size": 13824, + "max_position_embeddings": 2048, + "model_type": "llama", + "num_attention_heads": 40, + "num_hidden_layers": 40, + "num_key_value_heads": 40, + "pad_token_id": 0, + "pretraining_tp": 2, + "rms_norm_eps": 1e-05, + "rope_scaling": None, + "tie_word_embeddings": False, + "torch_dtype": "float16", + "transformers_version": "4.31.0.dev0", + "use_cache": True, + "vocab_size": 32000, + "context_window_size": 2048, + "prefill_chunk_size": 2048, + }, + "llama2_70b": { + "architectures": ["LlamaForCausalLM"], + "bos_token_id": 1, + "eos_token_id": 2, + "hidden_act": "silu", + "hidden_size": 8192, + "initializer_range": 0.02, + "intermediate_size": 28672, + "max_position_embeddings": 2048, + "model_type": "llama", + "num_attention_heads": 64, + "num_hidden_layers": 80, + "num_key_value_heads": 8, + "pad_token_id": 0, + "rms_norm_eps": 1e-05, + "tie_word_embeddings": False, + "torch_dtype": "float16", + "transformers_version": "4.31.0.dev0", + "use_cache": True, + "vocab_size": 32000, + "context_window_size": 2048, + "prefill_chunk_size": 2048, + }, + "codellama_7b": { + "_name_or_path": "codellama/CodeLlama-7b-hf", + "architectures": ["LlamaForCausalLM"], + "bos_token_id": 1, + "eos_token_id": 2, + "hidden_act": "silu", + "hidden_size": 4096, + "initializer_range": 0.02, + "intermediate_size": 11008, + "max_position_embeddings": 16384, + "model_type": "llama", + "num_attention_heads": 32, + "num_hidden_layers": 32, + "num_key_value_heads": 32, + "pretraining_tp": 1, + "rms_norm_eps": 1e-05, + "rope_scaling": None, + "rope_theta": 1000000, + "tie_word_embeddings": False, + "torch_dtype": "bfloat16", + "transformers_version": "4.33.0.dev0", + "use_cache": True, + "vocab_size": 32016, + "context_window_size": 2048, + "prefill_chunk_size": 2048, + }, + "codellama_13b": { + "architectures": ["LlamaForCausalLM"], + "bos_token_id": 1, + "eos_token_id": 2, + "hidden_act": "silu", + "hidden_size": 5120, + "initializer_range": 0.02, + "intermediate_size": 13824, + "max_position_embeddings": 16384, + "model_type": "llama", + "num_attention_heads": 40, + "num_hidden_layers": 40, + "num_key_value_heads": 40, + "pretraining_tp": 1, + "rms_norm_eps": 1e-05, + "rope_scaling": None, + "rope_theta": 1000000, + "tie_word_embeddings": False, + "torch_dtype": "bfloat16", + "transformers_version": "4.32.0.dev0", + "use_cache": True, + "vocab_size": 32016, + "context_window_size": 2048, + "prefill_chunk_size": 2048, + }, + "codellama_34b": { + "architectures": ["LlamaForCausalLM"], + "bos_token_id": 1, + "eos_token_id": 2, + "hidden_act": "silu", + "hidden_size": 8192, + "initializer_range": 0.02, + "intermediate_size": 22016, + "max_position_embeddings": 16384, + "model_type": "llama", + "num_attention_heads": 64, + "num_hidden_layers": 48, + "num_key_value_heads": 8, + "pretraining_tp": 1, + "rms_norm_eps": 1e-05, + "rope_scaling": None, + "rope_theta": 1000000, + "tie_word_embeddings": False, + "torch_dtype": "bfloat16", + "transformers_version": "4.32.0.dev0", + "use_cache": True, + "vocab_size": 32016, + "context_window_size": 2048, + "prefill_chunk_size": 2048, + }, + "mistral_7b": { + "architectures": ["MistralForCausalLM"], + "bos_token_id": 1, + "eos_token_id": 2, + "hidden_act": "silu", + "hidden_size": 4096, + "initializer_range": 0.02, + "intermediate_size": 14336, + "max_position_embeddings": 32768, + "model_type": "mistral", + "num_attention_heads": 32, + "num_hidden_layers": 32, + "num_key_value_heads": 8, + "rms_norm_eps": 1e-05, + "rope_theta": 10000.0, + "tie_word_embeddings": False, + "torch_dtype": "bfloat16", + "transformers_version": "4.34.0.dev0", + "use_cache": True, + "vocab_size": 32000, + "sliding_window_size": 4096, + "prefill_chunk_size": 128, + "attention_sink_size": 4, + }, + "gpt2": { + "architectures": ["GPT2LMHeadModel"], + "bos_token_id": 50256, + "eos_token_id": 50256, + "hidden_act": "gelu_new", + "n_embd": 768, + "initializer_range": 0.02, + "n_positions": 1024, + "model_type": "gpt2", + "n_head": 12, + "n_layer": 12, + "layer_norm_epsilon": 1e-05, + "transformers_version": "4.26.0.dev0", + "use_cache": True, + "vocab_size": 50257, + "context_window_size": 2048, + "prefill_chunk_size": 2048, + }, + "gpt_bigcode": { + "activation_function": "gelu_pytorch_tanh", + "architectures": ["GPTBigCodeForCausalLM"], + "attention_softmax_in_fp32": True, + "multi_query": True, + "attn_pdrop": 0.1, + "bos_token_id": 49152, + "embd_pdrop": 0.1, + "eos_token_id": 49152, + "initializer_range": 0.02, + "layer_norm_epsilon": 1e-05, + "model_type": "gpt_bigcode", + "n_embd": 2048, + "n_head": 16, + "n_inner": 8192, + "n_layer": 24, + "n_positions": 2048, + "resid_pdrop": 0.1, + "runner_max_sequence_length": None, + "scale_attention_softmax_in_fp32": True, + "scale_attn_weights": True, + "summary_activation": None, + "summary_first_dropout": 0.1, + "summary_proj_to_labels": True, + "summary_type": "cls_index", + "summary_use_proj": True, + "transformers_version": "4.28.0.dev0", + "use_cache": True, + "vocab_size": 49280, + }, + "Mixtral-8x7B-v0.1": { + "architectures": ["MixtralForCausalLM"], + "attention_dropout": 0.0, + "bos_token_id": 1, + "eos_token_id": 2, + "hidden_act": "silu", + "hidden_size": 4096, + "initializer_range": 0.02, + "intermediate_size": 14336, + "max_position_embeddings": 32768, + "model_type": "mixtral", + "num_attention_heads": 32, + "num_experts_per_tok": 2, + "num_hidden_layers": 32, + "num_key_value_heads": 8, + "num_local_experts": 8, + "output_router_logits": False, + "rms_norm_eps": 1e-05, + "rope_theta": 1000000.0, + "router_aux_loss_coef": 0.02, + "sliding_window": None, + "tie_word_embeddings": False, + "torch_dtype": "bfloat16", + "transformers_version": "4.36.0.dev0", + "use_cache": True, + "vocab_size": 32000, + }, + "redpajama_3b_v1": { + "_name_or_path": "/root/fm/models/rp_3b_800b_real_fp16", + "architectures": ["GPTNeoXForCausalLM"], + "bos_token_id": 0, + "eos_token_id": 0, + "hidden_act": "gelu", + "hidden_size": 2560, + "initializer_range": 0.02, + "intermediate_size": 10240, + "layer_norm_eps": 1e-05, + "max_position_embeddings": 2048, + "model_type": "gpt_neox", + "num_attention_heads": 32, + "num_hidden_layers": 32, + "rotary_emb_base": 10000, + "rotary_pct": 1.0, + "tie_word_embeddings": False, + "torch_dtype": "float16", + "transformers_version": "4.28.1", + "use_cache": True, + "use_parallel_residual": False, + "vocab_size": 50432, + }, + "phi-1_5": { + "_name_or_path": "microsoft/phi-1_5", + "activation_function": "gelu_new", + "architectures": ["PhiForCausalLM"], + "attn_pdrop": 0.0, + "auto_map": { + "AutoConfig": "configuration_phi.PhiConfig", + "AutoModelForCausalLM": "modeling_phi.PhiForCausalLM", + }, + "embd_pdrop": 0.0, + "flash_attn": False, + "flash_rotary": False, + "fused_dense": False, + "initializer_range": 0.02, + "layer_norm_epsilon": 1e-05, + "model_type": "phi-msft", + "n_embd": 2048, + "n_head": 32, + "n_head_kv": None, + "n_inner": None, + "n_layer": 24, + "n_positions": 2048, + "resid_pdrop": 0.0, + "rotary_dim": 32, + "tie_word_embeddings": False, + "torch_dtype": "float16", + "transformers_version": "4.34.1", + "vocab_size": 51200, + }, + "phi-2": { + "_name_or_path": "microsoft/phi-2", + "activation_function": "gelu_new", + "architectures": ["PhiForCausalLM"], + "attn_pdrop": 0.0, + "auto_map": { + "AutoConfig": "configuration_phi.PhiConfig", + "AutoModelForCausalLM": "modeling_phi.PhiForCausalLM", + }, + "embd_pdrop": 0.0, + "flash_attn": False, + "flash_rotary": False, + "fused_dense": False, + "img_processor": None, + "initializer_range": 0.02, + "layer_norm_epsilon": 1e-05, + "model_type": "phi-msft", + "n_embd": 2560, + "n_head": 32, + "n_head_kv": None, + "n_inner": None, + "n_layer": 32, + "n_positions": 2048, + "resid_pdrop": 0.1, + "rotary_dim": 32, + "tie_word_embeddings": False, + "torch_dtype": "float16", + "transformers_version": "4.35.2", + "vocab_size": 51200, + }, + "qwen": { + "architectures": ["QWenLMHeadModel"], + "auto_map": { + "AutoConfig": "configuration_qwen.QWenConfig", + "AutoModelForCausalLM": "modeling_qwen.QWenLMHeadModel", + }, + "attn_dropout_prob": 0.0, + "bf16": False, + "emb_dropout_prob": 0.0, + "hidden_size": 2048, + "intermediate_size": 11008, + "initializer_range": 0.02, + "kv_channels": 128, + "layer_norm_epsilon": 1e-06, + "max_position_embeddings": 8192, + "model_type": "qwen", + "no_bias": True, + "num_attention_heads": 16, + "num_hidden_layers": 24, + "rotary_emb_base": 10000, + "rotary_pct": 1.0, + "scale_attn_weights": True, + "seq_length": 8192, + "tie_word_embeddings": False, + "tokenizer_class": "QWenTokenizer", + "transformers_version": "4.32.0", + "use_cache": True, + "use_dynamic_ntk": True, + "use_flash_attn": "auto", + "use_logn_attn": True, + "vocab_size": 151936, + }, + "qwen2": { + "_name_or_path": "Qwen/Qwen1.5-1.8B-Chat", + "architectures": ["Qwen2ForCausalLM"], + "attention_dropout": 0.0, + "bos_token_id": 151643, + "eos_token_id": 151645, + "hidden_act": "silu", + "hidden_size": 2048, + "initializer_range": 0.02, + "intermediate_size": 5504, + "max_position_embeddings": 4096, + "max_window_layers": 21, + "model_type": "qwen2", + "num_attention_heads": 16, + "num_hidden_layers": 24, + "num_key_value_heads": 16, + "rms_norm_eps": 1e-06, + "rope_theta": 1000000.0, + "sliding_window": 32768, + "tie_word_embeddings": True, + "torch_dtype": "bfloat16", + "transformers_version": "4.37.2", + "use_cache": True, + "use_sliding_window": False, + "vocab_size": 151936, + }, + "stablelm_epoch": { + "architectures": ["StableLMEpochForCausalLM"], + "auto_map": { + "AutoConfig": "configuration_stablelm_epoch.StableLMEpochConfig", + "AutoModelForCausalLM": "modeling_stablelm_epoch.StableLMEpochForCausalLM", + }, + "bos_token_id": 100257, + "eos_token_id": 100257, + "hidden_act": "silu", + "hidden_size": 2048, + "initializer_range": 0.02, + "intermediate_size": 5632, + "max_position_embeddings": 4096, + "model_type": "stablelm_epoch", + "norm_eps": 1e-05, + "num_attention_heads": 32, + "num_heads": 32, + "num_hidden_layers": 24, + "num_key_value_heads": 32, + "rope_pct": 0.25, + "rope_theta": 10000, + "rotary_scaling_factor": 1.0, + "tie_word_embeddings": True, + "torch_dtype": "bfloat16", + "transformers_version": "4.36.2", + "use_cache": True, + "use_qkv_bias": True, + "vocab_size": 100352, + }, + "baichuan": { + "architectures": ["BaichuanForCausalLM"], + "auto_map": { + "AutoConfig": "configuration_baichuan.BaichuanConfig", + "AutoModelForCausalLM": "modeling_baichuan.BaichuanForCausalLM", + }, + "tokenizer_class": "BaichuanTokenizer", + "bos_token_id": 1, + "eos_token_id": 2, + "hidden_size": 4096, + "initializer_range": 0.02, + "intermediate_size": 11008, + "max_position_embeddings": 4096, + "model_max_length": 4096, + "model_type": "baichuan", + "num_attention_heads": 32, + "num_hidden_layers": 32, + "pad_token_id": 0, + "rms_norm_eps": 1e-06, + "_from_model_config": True, + "tie_word_embeddings": False, + "torch_dtype": "bfloat16", + "transformers_version": "4.29.2", + "use_cache": True, + "vocab_size": 125696, + }, + # TODO(mlc-team): enable the model presets when stablized. + # "gemma_2b": { + # "architectures": ["GemmaForCausalLM"], + # "attention_bias": False, + # "bos_token_id": 2, + # "eos_token_id": 1, + # "head_dim": 256, + # "hidden_act": "gelu", + # "hidden_size": 2048, + # "initializer_range": 0.02, + # "intermediate_size": 16384, + # "max_position_embeddings": 8192, + # "model_type": "gemma", + # "num_attention_heads": 8, + # "num_hidden_layers": 18, + # "num_key_value_heads": 1, + # "pad_token_id": 0, + # "rms_norm_eps": 1e-06, + # "rope_theta": 10000.0, + # "torch_dtype": "bfloat16", + # "transformers_version": "4.38.0.dev0", + # "vocab_size": 256000, + # }, + # "gemma_7b": { + # "architectures": ["GemmaForCausalLM"], + # "attention_bias": False, + # "bos_token_id": 2, + # "eos_token_id": 1, + # "head_dim": 256, + # "hidden_act": "gelu", + # "hidden_size": 3072, + # "initializer_range": 0.02, + # "intermediate_size": 24576, + # "max_position_embeddings": 8192, + # "model_type": "gemma", + # "num_attention_heads": 16, + # "num_hidden_layers": 28, + # "num_key_value_heads": 16, + # "pad_token_id": 0, + # "rms_norm_eps": 1e-06, + # "rope_theta": 10000.0, + # "torch_dtype": "bfloat16", + # "transformers_version": "4.38.0.dev0", + # "vocab_size": 256000, + # }, +} diff --git a/python/mlc_chat/model/phi/__init__.py b/python/mlc_chat/model/phi/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/mlc_chat/model/phi/phi_loader.py b/python/mlc_chat/model/phi/phi_loader.py new file mode 100644 index 0000000..d393c61 --- /dev/null +++ b/python/mlc_chat/model/phi/phi_loader.py @@ -0,0 +1,162 @@ +""" +This file specifies how MLC's Phi parameter maps from other formats, for example HuggingFace +PyTorch, HuggingFace safetensors. +""" +import functools + +import numpy as np + +from mlc_chat.loader import ExternMapping +from mlc_chat.quantization import Quantization + +from .phi_model import Phi1Config, PhiConfig, PhiForCausalLM + + +def huggingface(model_config: PhiConfig, quantization: Quantization) -> ExternMapping: + """Returns a parameter mapping that maps from the names of MLC LLM parameters to + the names of HuggingFace PyTorch parameters. + + Parameters + ---------- + model_config : PhiConfig + The configuration of the Phi model. + + quantization : Quantization + The quantization configuration. + + Returns + ------- + param_map : ExternMapping + The parameter mapping from MLC to HuggingFace PyTorch. + """ + model = PhiForCausalLM(model_config) + if quantization is not None: + model.to(quantization.model_dtype) + _, _named_params = model.export_tvm( # pylint: disable=W0632:unbalanced-tuple-unpacking + spec=model.get_default_spec() + ) + named_parameters = dict(_named_params) + mapping = ExternMapping() + + def _add(mlc_name, hf_name): + mapping.add_mapping( + mlc_name, + [hf_name], + functools.partial( + lambda x, dtype: x.astype(dtype), + dtype=named_parameters[mlc_name].dtype, + ), + ) + + if model_config.model_type == "mixformer-sequential": + _add("transformer.embd.weight", "layers.0.wte.weight") + prefix = "transformer.h" + for i in range(model_config.n_layer): + _add(f"{prefix}.{i}.ln.weight", f"layers.{i + 1}.ln.weight") + _add(f"{prefix}.{i}.ln.bias", f"layers.{i + 1}.ln.bias") + _add(f"{prefix}.{i}.mixer.Wqkv.weight", f"layers.{i + 1}.mixer.Wqkv.weight") + _add(f"{prefix}.{i}.mixer.Wqkv.bias", f"layers.{i + 1}.mixer.Wqkv.bias") + _add(f"{prefix}.{i}.mixer.out_proj.weight", f"layers.{i + 1}.mixer.out_proj.weight") + _add(f"{prefix}.{i}.mixer.out_proj.bias", f"layers.{i + 1}.mixer.out_proj.bias") + _add(f"{prefix}.{i}.mlp.fc1.weight", f"layers.{i + 1}.mlp.fc1.weight") + _add(f"{prefix}.{i}.mlp.fc1.bias", f"layers.{i + 1}.mlp.fc1.bias") + _add(f"{prefix}.{i}.mlp.fc2.weight", f"layers.{i + 1}.mlp.fc2.weight") + _add(f"{prefix}.{i}.mlp.fc2.bias", f"layers.{i + 1}.mlp.fc2.bias") + mapping.add_unused(f"layers.{i + 1}.mixer.rotary_emb.inv_freq") + prefix = f"layers.{model_config.n_layer + 1}" + _add("lm_head.ln.weight", f"{prefix}.ln.weight") + _add("lm_head.ln.bias", f"{prefix}.ln.bias") + _add("lm_head.linear.weight", f"{prefix}.linear.weight") + _add("lm_head.linear.bias", f"{prefix}.linear.bias") + + elif model_config.model_type == "phi-msft": + _add("transformer.embd.weight", "transformer.embd.wte.weight") + for mlc_name, _ in named_parameters.items(): + if mlc_name not in mapping.param_map: + _add(mlc_name, mlc_name) + return mapping + + +def phi1_huggingface(model_config: Phi1Config, quantization: Quantization) -> ExternMapping: + """Returns a parameter mapping that maps from the names of MLC LLM parameters to + the names of Phi-1/Phi-1.5 HuggingFace PyTorch parameters. + + Parameters + ---------- + model_config : PhiConfig + The configuration of the Phi model. + + quantization : Quantization + The quantization configuration. + + Returns + ------- + param_map : ExternMapping + The parameter mapping from MLC to HuggingFace PyTorch. + """ + model = PhiForCausalLM(model_config) + if quantization is not None: + model.to(quantization.model_dtype) + _, _named_params = model.export_tvm( # pylint: disable=W0632:unbalanced-tuple-unpacking + spec=model.get_default_spec() + ) + named_parameters = dict(_named_params) + + mapping = ExternMapping() + + def _add(mlc_name, hf_name): + mapping.add_mapping( + mlc_name, + [hf_name], + functools.partial( + lambda x, dtype: x.astype(dtype), + dtype=named_parameters[mlc_name].dtype, + ), + ) + + def _concat_add(mlc_name, hf_names): + mapping.add_mapping( + mlc_name, + hf_names, + functools.partial( + lambda q, k, v, dtype: np.concatenate([q, k, v], axis=0).astype(dtype), + dtype=named_parameters[mlc_name].dtype, + ), + ) + + _add("lm_head.linear.weight", "lm_head.weight") + _add("lm_head.linear.bias", "lm_head.bias") + _add("lm_head.ln.weight", "model.final_layernorm.weight") + _add("lm_head.ln.bias", "model.final_layernorm.bias") + _add("transformer.embd.weight", "model.embed_tokens.weight") + + prefix = "transformer.h" + hf_prefix = "model.layers" + for i in range(model_config.num_hidden_layers): + _add(f"{prefix}.{i}.ln.weight", f"{hf_prefix}.{i}.input_layernorm.weight") + _add(f"{prefix}.{i}.ln.bias", f"{hf_prefix}.{i}.input_layernorm.bias") + _concat_add( + f"{prefix}.{i}.mixer.Wqkv.weight", + [ + f"{hf_prefix}.{i}.self_attn.q_proj.weight", + f"{hf_prefix}.{i}.self_attn.k_proj.weight", + f"{hf_prefix}.{i}.self_attn.v_proj.weight", + ], + ) + _concat_add( + f"{prefix}.{i}.mixer.Wqkv.bias", + [ + f"{hf_prefix}.{i}.self_attn.q_proj.bias", + f"{hf_prefix}.{i}.self_attn.k_proj.bias", + f"{hf_prefix}.{i}.self_attn.v_proj.bias", + ], + ) + _add(f"{prefix}.{i}.mixer.out_proj.weight", f"{hf_prefix}.{i}.self_attn.dense.weight") + _add(f"{prefix}.{i}.mixer.out_proj.bias", f"{hf_prefix}.{i}.self_attn.dense.bias") + _add(f"{prefix}.{i}.mlp.fc1.weight", f"{hf_prefix}.{i}.mlp.fc1.weight") + _add(f"{prefix}.{i}.mlp.fc1.bias", f"{hf_prefix}.{i}.mlp.fc1.bias") + _add(f"{prefix}.{i}.mlp.fc2.weight", f"{hf_prefix}.{i}.mlp.fc2.weight") + _add(f"{prefix}.{i}.mlp.fc2.bias", f"{hf_prefix}.{i}.mlp.fc2.bias") + mapping.add_unused(f"{hf_prefix}.{i}.mixer.rotary_emb.inv_freq") + + return mapping diff --git a/python/mlc_chat/model/phi/phi_model.py b/python/mlc_chat/model/phi/phi_model.py new file mode 100644 index 0000000..421876d --- /dev/null +++ b/python/mlc_chat/model/phi/phi_model.py @@ -0,0 +1,404 @@ +""" +Implementation for Phi architecture. +TODO: add docstring +""" +import dataclasses +from typing import Any, Dict, Optional, Union + +from tvm import te, tir +from tvm.relax.frontend import nn +from tvm.relax.frontend.nn import Tensor, op + +from mlc_chat import op as op_ext +from mlc_chat.support import logging +from mlc_chat.support import tensor_parallel as tp +from mlc_chat.support.config import ConfigBase +from mlc_chat.support.style import bold + +logger = logging.getLogger(__name__) + + +@dataclasses.dataclass +class Phi1Config(ConfigBase): # pylint: disable=too-many-instance-attributes + """Configuration of the Phi-1/Phi-1.5 model.""" + + vocab_size: int = 51200 + hidden_size: int = 2048 + intermediate_size: int = 8192 + num_hidden_layers: int = 24 + num_attention_heads: int = 32 + layer_norm_eps: float = 1e-5 + position_embedding_base: int = 0 + partial_rotary_factor: float = 0.5 + num_key_value_heads: int = 0 + context_window_size: int = 0 + prefill_chunk_size: int = 0 + head_dim: int = 0 + tensor_parallel_shards: int = 1 + kwargs: Dict[str, Any] = dataclasses.field(default_factory=dict) + + def __post_init__(self): + if self.position_embedding_base == 0: + if "rope_theta" in self.kwargs: + self.position_embedding_base = self.kwargs.pop("rope_theta") + else: + self.position_embedding_base = 10000 + if self.context_window_size == 0: + for name in ["max_position_embeddings", "max_sequence_length"]: + if name in self.kwargs: + self.context_window_size = self.kwargs.pop(name) + logger.info( + "%s not found in config.json. Falling back to %s (%d)", + bold("context_window_size"), + bold(name), + self.context_window_size, + ) + break + else: + raise ValueError( + "Unable to determine the maxmimum sequence length, because none of " + "`context_window_size`, `max_position_embeddings` or `max_sequence_length` is " + "provided in `config.json`." + ) + if self.prefill_chunk_size == 0: + self.prefill_chunk_size = self.context_window_size + if self.prefill_chunk_size > self.context_window_size: + self.prefill_chunk_size = self.context_window_size + if self.num_key_value_heads == 0 or self.num_key_value_heads is None: + self.num_key_value_heads = self.num_attention_heads + if self.intermediate_size == 0 or self.intermediate_size is None: + self.intermediate_size = 4 * self.hidden_size + if self.head_dim == 0: + self.head_dim = self.hidden_size // self.num_attention_heads + assert self.head_dim * self.num_attention_heads == self.hidden_size + assert self.num_attention_heads % self.num_key_value_heads == 0 + + +@dataclasses.dataclass +class PhiConfig(ConfigBase): # pylint: disable=too-many-instance-attributes + """Configuration of the Phi-2 model.""" + + model_type: str # "phi", "phi-msft", "mixformer-sequential" + vocab_size: int = 51200 + n_positions: int = 2048 + n_embd: int = 2560 + n_layer: int = 32 + n_inner: int = 0 + n_head: int = 32 + rotary_dim: int = 32 + position_embedding_base: int = 0 + layer_norm_epsilon: float = 1e-5 + context_window_size: int = 0 + prefill_chunk_size: int = 0 + n_head_kv: int = 0 + head_dim: int = 0 + tensor_parallel_shards: int = 1 + kwargs: Dict[str, Any] = dataclasses.field(default_factory=dict) + + def __post_init__(self): + if self.position_embedding_base == 0: + if "rope_theta" in self.kwargs: + self.position_embedding_base = self.kwargs.pop("rope_theta") + else: + self.position_embedding_base = 10000 + if self.context_window_size == 0: + for name in ["max_position_embeddings", "max_sequence_length"]: + if name in self.kwargs: + self.context_window_size = self.kwargs.pop(name) + logger.info( + "%s not found in config.json. Falling back to %s (%d)", + bold("context_window_size"), + bold(name), + self.context_window_size, + ) + break + else: + self.context_window_size = self.n_positions + logger.info( + "%s not found in config.json. Falling back to %s (%d)", + bold("context_window_size"), + "n_positions", + self.context_window_size, + ) + if self.prefill_chunk_size == 0: + self.prefill_chunk_size = self.context_window_size + if self.prefill_chunk_size > self.context_window_size: + self.prefill_chunk_size = self.context_window_size + if self.n_head_kv == 0 or self.n_head_kv is None: + self.n_head_kv = self.n_head + if self.n_inner == 0 or self.n_inner is None: + self.n_inner = 4 * self.n_embd + if self.head_dim == 0: + self.head_dim = self.n_embd // self.n_head + assert self.head_dim * self.n_head == self.n_embd + assert self.n_head % self.n_head_kv == 0 + + @staticmethod + def from_phi1(config: Phi1Config) -> "PhiConfig": + "Build PhiConig from a Phi1Config." + return PhiConfig( + model_type="phi", + vocab_size=config.vocab_size, + n_positions=config.context_window_size, + n_embd=config.hidden_size, + n_layer=config.num_hidden_layers, + n_inner=config.intermediate_size, + n_head=config.num_attention_heads, + rotary_dim=int(config.partial_rotary_factor * config.head_dim), + position_embedding_base=config.position_embedding_base, + layer_norm_epsilon=config.layer_norm_eps, + context_window_size=config.context_window_size, + prefill_chunk_size=config.prefill_chunk_size, + n_head_kv=config.num_key_value_heads, + head_dim=config.head_dim, + tensor_parallel_shards=config.tensor_parallel_shards, + kwargs=config.kwargs, + ) + + +# pylint: disable=invalid-name,missing-docstring + + +class PhiMLP(nn.Module): + def __init__(self, config: PhiConfig): + super().__init__() + self.intermediate_size = config.n_inner // config.tensor_parallel_shards + self.fc1 = nn.Linear(config.n_embd, self.intermediate_size) + self.fc2 = nn.Linear(self.intermediate_size, config.n_embd) + + def forward(self, hidden_states: Tensor): + hidden_states = self.fc1(hidden_states) + hidden_states = op.gelu(hidden_states, approximate="tanh") + hidden_states = self.fc2(hidden_states) + + return hidden_states + + +class PhiCrossAttention(nn.Module): + def __init__(self, config: PhiConfig): # pylint: disable=unused-argument + super().__init__() + + def forward(self, q: Tensor, k: Tensor, v: Tensor, attention_mask: Tensor): + output = op_ext.attention(q, k, v, casual_mask=attention_mask, qk_dtype="float32") + return output + + +class PhiMHA(nn.Module): # pylint: disable=too-many-instance-attributes + def __init__(self, config: PhiConfig): + self.rope_theta = config.position_embedding_base + self.rotary_dim = config.rotary_dim + self.n_head = config.n_head // config.tensor_parallel_shards + assert ( + config.n_head % config.tensor_parallel_shards == 0 + ), f"n_head({config.n_head}) must be divisible by tensor_parallel_shards" + self.n_head_kv = config.n_head_kv // config.tensor_parallel_shards + assert ( + config.n_head_kv % config.tensor_parallel_shards == 0 + ), f"n_head({config.n_head_kv}) must be divisible by tensor_parallel_shards" + self.head_dim = config.head_dim + op_size = self.head_dim * (self.n_head + 2 * self.n_head_kv) + hidden_size = config.n_embd + + self.Wqkv = nn.Linear(hidden_size, op_size, bias=True) + self.out_proj = nn.Linear(self.n_head * self.head_dim, hidden_size, bias=True) + self.inner_cross_attn = PhiCrossAttention(config) + self.k_cache = nn.KVCache(config.context_window_size, [self.n_head_kv, self.head_dim]) + self.v_cache = nn.KVCache(config.context_window_size, [self.n_head_kv, self.head_dim]) + + def forward(self, x: Tensor, attention_mask: Tensor, total_seq_len: tir.Var): + d, h_q, h_kv, t = self.head_dim, self.n_head, self.n_head_kv, total_seq_len + b, s, _ = x.shape + assert b == 1, "Only support batch size 1 at this moment." + # Step 1. QKV Projection + qkv = self.Wqkv(x) + qkv = op.reshape(qkv, (b, s, h_q + h_kv + h_kv, d)) + # Step 2. Apply QK rotary embedding + q, k, v = op_ext.llama_rope(qkv, t, self.rope_theta, h_q, h_kv, rotary_dim=self.rotary_dim) + # Step 3. Query and update KVCache + self.k_cache.append(op.squeeze(k, axis=0)) + self.v_cache.append(op.squeeze(v, axis=0)) + k = self.k_cache.view(t) + v = self.v_cache.view(t) + # Step 4. Compute softmax(Q @ K^T / sqrt(d)) @ V + output = self.inner_cross_attn(q, k, v, attention_mask) + # Step 5. Apply output projection + return self.out_proj(output) + + +class PhiParallelBlock(nn.Module): + def __init__(self, config: PhiConfig): + super().__init__() + + self.ln = nn.LayerNorm(config.n_embd, eps=config.layer_norm_epsilon) + self.mixer = PhiMHA(config) + self.mlp = PhiMLP(config) + + def _set_tp(): + def _set(param, hint): + param.attrs["shard_strategy"] = hint + + hd = config.head_dim + q = self.mixer.n_head * hd + k = self.mixer.n_head_kv * hd + v = self.mixer.n_head_kv * hd + _set( + self.mixer.Wqkv.weight, + tp.ShardSingleDim("_shard_qkv_weight", segs=[q, k, v], dim=0), + ) + _set(self.mixer.Wqkv.bias, tp.ShardSingleDim("_shard_qkv_bias", segs=[q, k, v], dim=0)) + _set(self.mixer.out_proj.weight, tp.ShardSingleDim("_shard_o_weight", dim=1)) + _set(self.mlp.fc1.weight, tp.ShardSingleDim("_shard_mlp_fc1_weight", dim=0)) + _set(self.mlp.fc1.bias, tp.ShardSingleDim("_shard_mlp_fc1_bias", dim=0)) + _set(self.mlp.fc2.weight, tp.ShardSingleDim("_shard_mlp_fc2_weight", dim=1)) + + self.tensor_parallel_shards = config.tensor_parallel_shards + _set_tp() + + def forward(self, hidden_states: Tensor, attention_mask: Tensor, total_seq_len: tir.Var): + residual = hidden_states + hidden_states = self.ln(hidden_states) + + with tp.shard_bias(self.mixer.out_proj, self.tensor_parallel_shards), tp.shard_bias( + self.mlp.fc2, self.tensor_parallel_shards + ): + attn_outputs = self.mixer( + hidden_states, + attention_mask, + total_seq_len, + ) + + feed_forward_hidden_states = self.mlp(hidden_states) + + def _apply_parallel_residual(attn_out, mlp_out, residual): + if self.tensor_parallel_shards > 1: + return op.ccl_allreduce( + attn_out + mlp_out + residual / self.tensor_parallel_shards, "sum" + ) + return attn_out + mlp_out + residual + + hidden_states = _apply_parallel_residual(attn_outputs, feed_forward_hidden_states, residual) + + return hidden_states + + +class PhiCausalLMHead(nn.Module): + def __init__(self, config: PhiConfig) -> None: + super().__init__() + + self.ln = nn.LayerNorm(config.n_embd, eps=config.layer_norm_epsilon) + self.linear = nn.Linear(config.n_embd, "vocab_size") + + def forward(self, hidden_states: Tensor): + hidden_states = self.ln(hidden_states) + logits = self.linear(hidden_states) + + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits + + +class PhiModel(nn.Module): + def __init__(self, config: PhiConfig) -> None: + super().__init__() + self.embd = nn.Embedding("vocab_size", config.n_embd) + self.h = nn.ModuleList([PhiParallelBlock(config) for i in range(config.n_layer)]) + self.tensor_parallel_shards = config.tensor_parallel_shards + + def forward(self, input_ids: Tensor, total_seq_len: tir.Var, attention_mask: Tensor): + if self.tensor_parallel_shards > 1: + input_ids = op.ccl_broadcast_from_worker0(input_ids) + hidden_states = self.embd(input_ids) + for layer in self.h: + hidden_states = layer(hidden_states, attention_mask, total_seq_len) + + return hidden_states + + +class PhiForCausalLM(nn.Module): + def __init__(self, config: Union[PhiConfig, Phi1Config]) -> None: + super().__init__() + + if isinstance(config, Phi1Config): + config = PhiConfig.from_phi1(config) + + self.transformer = PhiModel(config) + self.lm_head = PhiCausalLMHead(config) + self.dtype = "float32" + + def to(self, dtype: Optional[str] = None): + super().to(dtype=dtype) + if dtype is not None: + self.dtype = dtype + + def forward(self, input_ids: Tensor, total_seq_len: tir.Var, attention_mask: Tensor): + def _index(x: te.Tensor): # x[:-1,:] + b, s, d = x.shape + return te.compute((b, 1, d), lambda i, _, k: x[i, s - 1, k], name="index") + + hidden_states = self.transformer(input_ids, total_seq_len, attention_mask) + hidden_states = op.tensor_expr_op(_index, name_hint="index", args=[hidden_states]) + lm_logits = self.lm_head(hidden_states) + + return lm_logits + + def prefill(self, inputs: Tensor, total_seq_len: tir.Var): + def _attention_mask(batch_size, seq_len, total_seq_len): + return te.compute( + (batch_size, 1, seq_len, total_seq_len), + lambda b, _, i, j: tir.if_then_else( + i < j - (total_seq_len - seq_len), + tir.min_value(self.dtype), + tir.max_value(self.dtype), + ), + name="attention_mask_prefill", + ) + + batch_size, seq_len = inputs.shape + attention_mask = op.tensor_expr_op( + _attention_mask, + name_hint="attention_mask_prefill", + args=[batch_size, seq_len, total_seq_len], + ) + return self.forward(inputs, total_seq_len, attention_mask) + + def decode(self, inputs: Tensor, total_seq_len: tir.Var): + batch_size, seq_len = inputs.shape + attention_mask = op.full( + shape=[batch_size, 1, seq_len, total_seq_len], + fill_value=tir.max_value(self.dtype), + dtype=self.dtype, + ) + return self.forward(inputs, total_seq_len, attention_mask) + + def softmax_with_temperature(self, logits: Tensor, temperature: Tensor): + return op.softmax(logits / temperature, axis=-1) + + def get_default_spec(self): + batch_size = 1 + mod_spec = { + "prefill": { + "inputs": nn.spec.Tensor([batch_size, "seq_len"], "int32"), + "total_seq_len": int, + "$": { + "param_mode": "packed", + "effect_mode": "packed", + }, + }, + "decode": { + "inputs": nn.spec.Tensor([batch_size, 1], "int32"), + "total_seq_len": int, + "$": { + "param_mode": "packed", + "effect_mode": "packed", + }, + }, + "softmax_with_temperature": { + "logits": nn.spec.Tensor([1, 1, "vocab_size"], "float32"), + "temperature": nn.spec.Tensor([], "float32"), + "$": { + "param_mode": "none", + "effect_mode": "none", + }, + }, + } + return nn.spec.ModuleSpec.from_raw(mod_spec, self) diff --git a/python/mlc_chat/model/phi/phi_quantization.py b/python/mlc_chat/model/phi/phi_quantization.py new file mode 100644 index 0000000..52089c2 --- /dev/null +++ b/python/mlc_chat/model/phi/phi_quantization.py @@ -0,0 +1,53 @@ +"""This file specifies how MLC's Llama parameters are quantized using group quantization +or other formats.""" +from typing import Tuple + +from tvm.relax.frontend import nn + +from mlc_chat.loader import QuantizeMapping +from mlc_chat.quantization import FTQuantize, GroupQuantize, NoQuantize + +from .phi_model import PhiConfig, PhiForCausalLM + + +def group_quant( + model_config: PhiConfig, + quantization: GroupQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Phi-architecture model using group quantization.""" + model: nn.Module = PhiForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def ft_quant( + model_config: PhiConfig, + quantization: FTQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Phi-architecture model using FasterTransformer quantization.""" + model: nn.Module = PhiForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def no_quant( + model_config: PhiConfig, + quantization: NoQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Phi model without quantization.""" + model: nn.Module = PhiForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + return model, quant_map diff --git a/python/mlc_chat/model/qwen/__init__.py b/python/mlc_chat/model/qwen/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/mlc_chat/model/qwen/qwen_loader.py b/python/mlc_chat/model/qwen/qwen_loader.py new file mode 100644 index 0000000..810efed --- /dev/null +++ b/python/mlc_chat/model/qwen/qwen_loader.py @@ -0,0 +1,70 @@ +""" +This file specifies how MLC's QWen parameter maps from other formats, for example HuggingFace +PyTorch, HuggingFace safetensors. +""" +import functools + +import numpy as np + +from mlc_chat.loader import ExternMapping +from mlc_chat.quantization import Quantization + +from .qwen_model import QWenConfig, QWenLMHeadModel + + +def huggingface(model_config: QWenConfig, quantization: Quantization) -> ExternMapping: + """Returns a parameter mapping that maps from the names of MLC LLM parameters to + the names of HuggingFace PyTorch parameters. + + Parameters + ---------- + model_config : GPT2Config + The configuration of the GPT-2 model. + + quantization : Quantization + The quantization configuration. + + Returns + ------- + param_map : ExternMapping + The parameter mapping from MLC to HuggingFace PyTorch. + """ + model = QWenLMHeadModel(model_config) + if quantization is not None: + model.to(quantization.model_dtype) + _, _named_params, _ = model.export_tvm( # type: ignore[misc] + spec=model.get_default_spec(), + allow_extern=True, + ) + named_parameters = dict(_named_params) + + mapping = ExternMapping() + + for i in range(model_config.num_hidden_layers): + # Add gates in MLP + mlp = f"transformer.h.{i}.mlp" + mlc_name = f"{mlp}.gate_up_proj.weight" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{mlp}.w1.weight", + f"{mlp}.w2.weight", + ], + functools.partial( + lambda gate, up, dtype: np.concatenate([gate, up], axis=0).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + + for mlc_name, mlc_param in named_parameters.items(): + if mlc_name not in mapping.param_map: + mapping.add_mapping( + mlc_name, + [mlc_name], + functools.partial( + lambda x, dtype: x.astype(dtype), + dtype=mlc_param.dtype, + ), + ) + return mapping diff --git a/python/mlc_chat/model/qwen/qwen_model.py b/python/mlc_chat/model/qwen/qwen_model.py new file mode 100644 index 0000000..ef4caca --- /dev/null +++ b/python/mlc_chat/model/qwen/qwen_model.py @@ -0,0 +1,254 @@ +""" +Implementation for QWEN architecture. +TODO: add docstring +""" +import dataclasses +from typing import Any, Dict, Optional + +from tvm import te, tir +from tvm.relax.frontend import nn +from tvm.relax.frontend.nn import Tensor, op + +from mlc_chat import op as op_ext +from mlc_chat.support import logging +from mlc_chat.support.config import ConfigBase +from mlc_chat.support.style import bold + +logger = logging.getLogger(__name__) + + +@dataclasses.dataclass +class QWenConfig(ConfigBase): # pylint: disable=too-many-instance-attributes + """Configuration of the QWen model.""" + + vocab_size: int + hidden_size: int + num_hidden_layers: int + num_attention_heads: int + layer_norm_epsilon: float + scale_attn_weights: bool + kv_channels: int + rotary_emb_base: int + intermediate_size: int + context_window_size: int = 0 + prefill_chunk_size: int = 0 + tensor_parallel_shards: int = 1 + kwargs: Dict[str, Any] = dataclasses.field(default_factory=dict) + + def __post_init__(self): + if self.context_window_size == 0: + for name in ["max_position_embeddings", "max_sequence_length"]: + if name in self.kwargs: + self.context_window_size = self.kwargs.pop(name) + logger.info( + "%s not found in config.json. Falling back to %s (%d)", + bold("context_window_size"), + bold(name), + self.context_window_size, + ) + break + else: + raise ValueError( + "Unable to determine the maxmimum sequence length, because none of " + "`context_window_size`, `max_position_embeddings` or `max_sequence_length` is " + "provided in `config.json`." + ) + if self.prefill_chunk_size == 0: + logger.info( + "%s defaults to %s (%d)", + bold("prefill_chunk_size"), + bold("context_window_size"), + self.context_window_size, + ) + self.prefill_chunk_size = self.context_window_size + elif self.prefill_chunk_size > self.context_window_size: + logger.info( + "Overriding %s from %d to %d (%s)", + bold("prefill_chunk_size"), + self.prefill_chunk_size, + self.context_window_size, + bold("context_window_size"), + ) + self.prefill_chunk_size = self.context_window_size + assert self.tensor_parallel_shards == 1, "QWEN currently does not support sharding." + + +# pylint: disable=invalid-name,missing-docstring + + +class QWenAttention(nn.Module): # pylint: disable=too-many-instance-attributes + def __init__(self, config: QWenConfig): + self.hidden_size = config.hidden_size + self.rope_theta = config.rotary_emb_base + self.num_heads = config.num_attention_heads + self.head_dim = self.hidden_size // self.num_heads + self.projection_size = config.kv_channels * config.num_attention_heads + + self.c_attn = nn.Linear( + in_features=config.hidden_size, + out_features=3 * self.projection_size, + bias=True, + ) + self.c_proj = nn.Linear(config.hidden_size, self.projection_size, bias=False) + + # KV cache for single sequence + self.k_cache = nn.KVCache(config.context_window_size, [self.num_heads, self.head_dim]) + self.v_cache = nn.KVCache(config.context_window_size, [self.num_heads, self.head_dim]) + + def forward( # pylint: disable=too-many-locals + self, + hidden_states: Tensor, + attention_mask: Tensor, + total_seq_len: tir.Var, + ): + d, h, t = self.head_dim, self.num_heads, total_seq_len + b, s, _ = hidden_states.shape + assert b == 1, "Only support batch size 1 at this moment." + # Step 1. QKV Projection + qkv = self.c_attn(hidden_states) + qkv = op.reshape(qkv, (b, s, 3 * h, d)) + # Step 2. Apply QK rotary embedding + q, k, v = op_ext.llama_rope(qkv, t, self.rope_theta, h, h) + # Step 3. Query and update KVCache + self.k_cache.append(op.squeeze(k, axis=0)) + self.v_cache.append(op.squeeze(v, axis=0)) + k = self.k_cache.view(t) + v = self.v_cache.view(t) + # Step 4. Compute softmax(Q @ K^T / sqrt(d)) @ V + output = op_ext.attention(q, k, v, casual_mask=attention_mask) + # Step 5. Apply output projection + return self.c_proj(output) + + +class QWenMLP(nn.Module): + def __init__(self, config: QWenConfig): + self.intermediate_size = config.intermediate_size + self.gate_up_proj = nn.Linear( + in_features=config.hidden_size, + out_features=self.intermediate_size, + bias=False, + ) + self.c_proj = nn.Linear(self.intermediate_size // 2, config.hidden_size, bias=False) + + def forward(self, x: Tensor): + concat_x1_x2 = self.gate_up_proj(x) + x1, x2 = op.split(concat_x1_x2, 2, axis=-1) + return self.c_proj(x1 * op.silu(x2)) + + +class QWenBlock(nn.Module): + def __init__(self, config: QWenConfig): + rms_norm_eps = config.layer_norm_epsilon + self.attn = QWenAttention(config) + self.mlp = QWenMLP(config) + self.ln_1 = nn.RMSNorm(config.hidden_size, -1, rms_norm_eps, bias=False) + self.ln_2 = nn.RMSNorm(config.hidden_size, -1, rms_norm_eps, bias=False) + + def forward(self, hidden_states: Tensor, attention_mask: Tensor, total_seq_len: tir.Var): + out = self.attn(self.ln_1(hidden_states), attention_mask, total_seq_len) + hidden_states = out + hidden_states + out = self.mlp(self.ln_2(hidden_states)) + hidden_states = out + hidden_states + return hidden_states + + +class QWenModel(nn.Module): + def __init__(self, config: QWenConfig): + assert config.hidden_size % config.num_attention_heads == 0 + self.wte = nn.Embedding(config.vocab_size, config.hidden_size) + self.h = nn.ModuleList([QWenBlock(config) for _ in range(config.num_hidden_layers)]) + self.ln_f = nn.RMSNorm(config.hidden_size, -1, config.layer_norm_epsilon, bias=False) + + def forward(self, input_ids: Tensor, total_seq_len: tir.Var, attention_mask: Tensor): + hidden_states = self.wte(input_ids) + for layer in self.h: + hidden_states = layer(hidden_states, attention_mask, total_seq_len) + hidden_states = self.ln_f(hidden_states) + return hidden_states + + +class QWenLMHeadModel(nn.Module): + def __init__(self, config: QWenConfig): + self.transformer = QWenModel(config) + self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + self.vocab_size = config.vocab_size + self.dtype = "float32" + + def to(self, dtype: Optional[str] = None): + super().to(dtype=dtype) + if dtype is not None: + self.dtype = dtype + + def forward(self, inputs: Tensor, total_seq_len: tir.Var, attention_mask: Tensor): + def _index(x: te.Tensor): # x[:-1,:] + b, s, d = x.shape + return te.compute((b, 1, d), lambda i, _, k: x[i, s - 1, k], name="index") + + hidden_states = self.transformer(inputs, total_seq_len, attention_mask) + hidden_states = op.tensor_expr_op(_index, name_hint="index", args=[hidden_states]) + logits = self.lm_head(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits + + def prefill(self, inputs: Tensor, total_seq_len: tir.Var): + def _attention_mask(batch_size, seq_len, total_seq_len): + return te.compute( + (batch_size, 1, seq_len, total_seq_len), + lambda b, _, i, j: tir.if_then_else( + i < j - (total_seq_len - seq_len), + tir.min_value(self.dtype), + tir.max_value(self.dtype), + ), + name="attention_mask_prefill", + ) + + batch_size, seq_len = inputs.shape + attention_mask = op.tensor_expr_op( + _attention_mask, + name_hint="attention_mask_prefill", + args=[batch_size, seq_len, total_seq_len], + ) + return self.forward(inputs, total_seq_len, attention_mask) + + def decode(self, inputs: Tensor, total_seq_len: tir.Var): + batch_size, seq_len = inputs.shape + attention_mask = op.full( + shape=[batch_size, 1, seq_len, total_seq_len], + fill_value=tir.max_value(self.dtype), + dtype=self.dtype, + ) + return self.forward(inputs, total_seq_len, attention_mask) + + def softmax_with_temperature(self, logits: Tensor, temperature: Tensor): + return op.softmax(logits / temperature, axis=-1) + + def get_default_spec(self): + batch_size = 1 + mod_spec = { + "prefill": { + "inputs": nn.spec.Tensor([batch_size, "seq_len"], "int32"), + "total_seq_len": int, + "$": { + "param_mode": "packed", + "effect_mode": "packed", + }, + }, + "decode": { + "inputs": nn.spec.Tensor([batch_size, 1], "int32"), + "total_seq_len": int, + "$": { + "param_mode": "packed", + "effect_mode": "packed", + }, + }, + "softmax_with_temperature": { + "logits": nn.spec.Tensor([1, 1, "vocab_size"], "float32"), + "temperature": nn.spec.Tensor([], "float32"), + "$": { + "param_mode": "none", + "effect_mode": "none", + }, + }, + } + return nn.spec.ModuleSpec.from_raw(mod_spec, self) diff --git a/python/mlc_chat/model/qwen/qwen_quantization.py b/python/mlc_chat/model/qwen/qwen_quantization.py new file mode 100644 index 0000000..c69f583 --- /dev/null +++ b/python/mlc_chat/model/qwen/qwen_quantization.py @@ -0,0 +1,53 @@ +"""This file specifies how MLC's QWen parameters are quantized using group quantization +or other formats.""" +from typing import Tuple + +from tvm.relax.frontend import nn + +from mlc_chat.loader import QuantizeMapping +from mlc_chat.quantization import FTQuantize, GroupQuantize, NoQuantize + +from .qwen_model import QWenConfig, QWenLMHeadModel + + +def group_quant( + model_config: QWenConfig, + quantization: GroupQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a QWen-architecture model using group quantization.""" + model: nn.Module = QWenLMHeadModel(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def ft_quant( + model_config: QWenConfig, + quantization: FTQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Qwen model using FasterTransformer quantization.""" + model: nn.Module = QWenLMHeadModel(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def no_quant( + model_config: QWenConfig, + quantization: NoQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a QWen model without quantization.""" + model: nn.Module = QWenLMHeadModel(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + return model, quant_map diff --git a/python/mlc_chat/model/qwen2/__init__.py b/python/mlc_chat/model/qwen2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/mlc_chat/model/qwen2/qwen2_loader.py b/python/mlc_chat/model/qwen2/qwen2_loader.py new file mode 100644 index 0000000..559a911 --- /dev/null +++ b/python/mlc_chat/model/qwen2/qwen2_loader.py @@ -0,0 +1,88 @@ +""" +This file specifies how MLC's QWen2 parameter maps from other formats, for example HuggingFace +PyTorch, HuggingFace safetensors. +""" + +import functools + +import numpy as np + +from mlc_chat.loader import ExternMapping +from mlc_chat.quantization import Quantization + +from .qwen2_model import QWen2Config, QWen2LMHeadModel + + +def huggingface(model_config: QWen2Config, quantization: Quantization) -> ExternMapping: + """Returns a parameter mapping that maps from the names of MLC LLM parameters to + the names of HuggingFace PyTorch parameters. + + Parameters + ---------- + model_config : GPT2Config + The configuration of the GPT-2 model. + + quantization : Quantization + The quantization configuration. + + Returns + ------- + param_map : ExternMapping + The parameter mapping from MLC to HuggingFace PyTorch. + """ + model = QWen2LMHeadModel(model_config) + if quantization is not None: + model.to(quantization.model_dtype) + _, _named_params, _ = model.export_tvm( # type: ignore[misc] + spec=model.get_default_spec(), + allow_extern=True, + ) + named_parameters = dict(_named_params) + + mapping = ExternMapping() + + for i in range(model_config.num_hidden_layers): + # map attention weight + attn = f"model.layers.{i}.self_attn" + for weight_type in ["weight", "bias"]: + mlc_name = f"{attn}.c_attn.{weight_type}" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{attn}.q_proj.{weight_type}", + f"{attn}.k_proj.{weight_type}", + f"{attn}.v_proj.{weight_type}", + ], + functools.partial( + lambda q, k, v, dtype: np.concatenate([q, k, v], axis=0).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + # map mlp weight + mlp = f"model.layers.{i}.mlp" + mlc_name = f"{mlp}.gate_up_proj.weight" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{mlp}.gate_proj.weight", + f"{mlp}.up_proj.weight", + ], + functools.partial( + lambda gate, up, dtype: np.concatenate([gate, up], axis=0).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + + for mlc_name, mlc_param in named_parameters.items(): + if mlc_name not in mapping.param_map: + mapping.add_mapping( + mlc_name, + [mlc_name], + functools.partial( + lambda x, dtype: x.astype(dtype), + dtype=mlc_param.dtype, + ), + ) + return mapping diff --git a/python/mlc_chat/model/qwen2/qwen2_model.py b/python/mlc_chat/model/qwen2/qwen2_model.py new file mode 100644 index 0000000..f09ccee --- /dev/null +++ b/python/mlc_chat/model/qwen2/qwen2_model.py @@ -0,0 +1,271 @@ +""" +Implementation for QWEN2 architecture. +""" + +import dataclasses +from functools import partial +from typing import Any, Dict, Optional + +from tvm import te, tir +from tvm.relax.frontend import nn +from tvm.relax.frontend.nn import Tensor, op + +from mlc_chat import op as op_ext +from mlc_chat.support import logging +from mlc_chat.support.config import ConfigBase +from mlc_chat.support.style import bold + +logger = logging.getLogger(__name__) + + +@dataclasses.dataclass +class QWen2Config(ConfigBase): # pylint: disable=too-many-instance-attributes + """Configuration of the QWen model.""" + + hidden_act: str + hidden_size: int + intermediate_size: int + num_attention_heads: int + num_hidden_layers: int + num_key_value_heads: int + rms_norm_eps: float + rope_theta: int + vocab_size: int + + context_window_size: int = 0 + prefill_chunk_size: int = 0 + tensor_parallel_shards: int = 1 + dtype: str = "float32" + kwargs: Dict[str, Any] = dataclasses.field(default_factory=dict) + + def __post_init__(self): + if self.context_window_size == 0: + for name in ["max_position_embeddings", "max_sequence_length"]: + if name in self.kwargs: + self.context_window_size = self.kwargs.pop(name) + logger.info( + "%s not found in config.json. Falling back to %s (%d)", + bold("context_window_size"), + bold(name), + self.context_window_size, + ) + break + else: + raise ValueError( + "Unable to determine the maximum sequence length, because none of " + "`context_window_size`, `max_position_embeddings` or `max_sequence_length` is " + "provided in `config.json`." + ) + if self.prefill_chunk_size == 0: + logger.info( + "%s defaults to %s (%d)", + bold("prefill_chunk_size"), + bold("context_window_size"), + self.context_window_size, + ) + self.prefill_chunk_size = self.context_window_size + elif self.prefill_chunk_size > self.context_window_size: + logger.info( + "Overriding %s from %d to %d (%s)", + bold("prefill_chunk_size"), + self.prefill_chunk_size, + self.context_window_size, + bold("context_window_size"), + ) + self.prefill_chunk_size = self.context_window_size + assert self.tensor_parallel_shards == 1, "QWEN currently does not support sharding." + + +# pylint: disable=invalid-name,missing-docstring,too-many-locals + + +class QWen2Attention(nn.Module): # pylint: disable=too-many-instance-attributes + def __init__(self, config: QWen2Config): + head_dim = config.hidden_size // config.num_attention_heads + + self.c_attn = nn.Linear( + in_features=config.hidden_size, + out_features=(2 * config.num_key_value_heads + config.num_attention_heads) * head_dim, + bias=True, + ) + self.o_proj = nn.Linear( + config.num_attention_heads * head_dim, config.hidden_size, bias=False + ) + # KV cache for single sequence + self.k_cache = nn.KVCache( + config.context_window_size, [config.num_key_value_heads, head_dim] + ) + self.v_cache = nn.KVCache( + config.context_window_size, [config.num_attention_heads, head_dim] + ) + + self.hidden_size = config.hidden_size + self.head_dim = head_dim + self.num_attention_heads = config.num_attention_heads + self.num_key_value_heads = config.num_key_value_heads + self.rope_theta = config.rope_theta + + def forward(self, hidden_states: Tensor, attention_mask: Tensor, total_seq_len: tir.Var): + bsz, sl, _ = hidden_states.shape + assert bsz == 1, "Only support batch size 1 at this moment." + # Step 1. QKV Projection + qkv = self.c_attn(hidden_states) + num_heads = 2 * self.num_key_value_heads + self.num_attention_heads + qkv = op.reshape(qkv, (bsz, sl, num_heads, self.head_dim)) + # Step 2. Apply QK rotary embedding + q, k, v = op_ext.llama_rope( + qkv, total_seq_len, self.rope_theta, self.num_attention_heads, self.num_key_value_heads + ) + # Step 3. Query and update KVCache + self.k_cache.append(op.squeeze(k, axis=0)) + self.v_cache.append(op.squeeze(v, axis=0)) + k = self.k_cache.view(total_seq_len) + v = self.v_cache.view(total_seq_len) + # Step 4. Compute softmax(Q @ K^T / sqrt(d)) @ V + output = op_ext.attention(q, k, v, casual_mask=attention_mask) + # Step 5. Apply output projection + return self.o_proj(output) + + +ACT2FN = { + "gelu": partial(nn.gelu, approximate=False), + "relu": nn.relu, + "silu": nn.silu, + "swish": nn.silu, + "gelu_new": partial(nn.gelu, approximate=True), +} + + +class QWen2MLP(nn.Module): + def __init__(self, config: QWen2Config): + self.gate_up_proj = nn.Linear(config.hidden_size, 2 * config.intermediate_size, bias=False) + self.down_proj = nn.Linear(config.intermediate_size, config.hidden_size, bias=False) + self.act_fn = ACT2FN[config.hidden_act] + + def forward(self, x: Tensor): + concat_x1_x2 = self.gate_up_proj(x) + x1, x2 = op.split(concat_x1_x2, 2, axis=-1) + return self.down_proj(self.act_fn(x1) * x2) + + +class QWen2DecoderLayer(nn.Module): + def __init__(self, config: QWen2Config): + self.self_attn = QWen2Attention(config) + self.mlp = QWen2MLP(config) + self.input_layernorm = nn.RMSNorm(config.hidden_size, -1, config.rms_norm_eps, bias=False) + self.post_attention_layernorm = nn.RMSNorm( + config.hidden_size, -1, config.rms_norm_eps, bias=False + ) + + def forward(self, hidden_states: Tensor, attention_mask: Tensor, total_seq_len: tir.Var): + out = self.input_layernorm(hidden_states) + out = self.self_attn(out, attention_mask, total_seq_len) + hidden_states = out + hidden_states + + out = self.post_attention_layernorm(hidden_states) + out = self.mlp(out) + hidden_states = out + hidden_states + return hidden_states + + +class QWen2Model(nn.Module): + def __init__(self, config: QWen2Config): + self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size) + self.layers = nn.ModuleList( + [QWen2DecoderLayer(config) for _ in range(config.num_hidden_layers)] + ) + self.norm = nn.RMSNorm(config.hidden_size, -1, config.rms_norm_eps, bias=False) + + def forward(self, input_ids: Tensor, attention_mask: Tensor, total_seq_len: tir.Var): + hidden_states = self.embed_tokens(input_ids) + for layer in self.layers: + hidden_states = layer(hidden_states, attention_mask, total_seq_len) + hidden_states = self.norm(hidden_states) + return hidden_states + + +class QWen2LMHeadModel(nn.Module): + def __init__(self, config: QWen2Config): + self.model = QWen2Model(config) + self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + self.dtype = config.dtype + + def to(self, dtype: Optional[str] = None): + super().to(dtype=dtype) + if dtype is not None: + self.dtype = dtype + + def forward(self, inputs: Tensor, attention_mask: Tensor, total_seq_len: tir.Var): + def _index(x: te.Tensor): # x[:-1,:] + b, s, d = x.shape + return te.compute((b, 1, d), lambda i, _, k: x[i, s - 1, k], name="index") + + hidden_states = self.model(inputs, attention_mask, total_seq_len) + hidden_states = op.tensor_expr_op(_index, name_hint="index", args=[hidden_states]) + logits = self.lm_head(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits + + def prefill(self, inputs: Tensor, total_seq_len: tir.Var): + def _attention_mask(batch_size, seq_len, total_seq_len): + return te.compute( + (batch_size, 1, seq_len, total_seq_len), + lambda b, _, i, j: tir.if_then_else( + i < j - (total_seq_len - seq_len), + tir.min_value(self.dtype), + tir.max_value(self.dtype), + ), + name="attention_mask_prefill", + ) + + batch_size, seq_len = inputs.shape + attention_mask = op.tensor_expr_op( + _attention_mask, + name_hint="attention_mask_prefill", + args=[batch_size, seq_len, total_seq_len], + ) + return self.forward(inputs, attention_mask, total_seq_len) + + def decode(self, inputs: Tensor, total_seq_len: tir.Var): + batch_size, seq_len = inputs.shape + attention_mask = op.full( + shape=[batch_size, 1, seq_len, total_seq_len], + fill_value=tir.max_value(self.dtype), + dtype=self.dtype, + ) + return self.forward(inputs, attention_mask, total_seq_len) + + @staticmethod + def softmax_with_temperature(logits: Tensor, temperature: Tensor): + return op.softmax(logits / temperature, axis=-1) + + def get_default_spec(self): + batch_size = 1 + mod_spec = { + "prefill": { + "inputs": nn.spec.Tensor([batch_size, "seq_len"], "int32"), + "total_seq_len": int, + "$": { + "param_mode": "packed", + "effect_mode": "packed", + }, + }, + "decode": { + "inputs": nn.spec.Tensor([batch_size, 1], "int32"), + "total_seq_len": int, + "$": { + "param_mode": "packed", + "effect_mode": "packed", + }, + }, + "softmax_with_temperature": { + "logits": nn.spec.Tensor([1, 1, "vocab_size"], "float32"), + "temperature": nn.spec.Tensor([], "float32"), + "$": { + "param_mode": "none", + "effect_mode": "none", + }, + }, + } + return nn.spec.ModuleSpec.from_raw(mod_spec, self) diff --git a/python/mlc_chat/model/qwen2/qwen2_quantization.py b/python/mlc_chat/model/qwen2/qwen2_quantization.py new file mode 100644 index 0000000..a59802d --- /dev/null +++ b/python/mlc_chat/model/qwen2/qwen2_quantization.py @@ -0,0 +1,54 @@ +"""This file specifies how MLC's QWen2 parameters are quantized using group quantization +or other formats.""" + +from typing import Tuple + +from tvm.relax.frontend import nn + +from mlc_chat.loader import QuantizeMapping +from mlc_chat.quantization import FTQuantize, GroupQuantize, NoQuantize + +from .qwen2_model import QWen2Config, QWen2LMHeadModel + + +def group_quant( + model_config: QWen2Config, + quantization: GroupQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a QWen-architecture model using group quantization.""" + model: nn.Module = QWen2LMHeadModel(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def ft_quant( + model_config: QWen2Config, + quantization: FTQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a Qwen model using FasterTransformer quantization.""" + model: nn.Module = QWen2LMHeadModel(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def no_quant( + model_config: QWen2Config, + quantization: NoQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a QWen model without quantization.""" + model: nn.Module = QWen2LMHeadModel(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + return model, quant_map diff --git a/python/mlc_chat/model/stable_lm/__init__.py b/python/mlc_chat/model/stable_lm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/mlc_chat/model/stable_lm/stablelm_loader.py b/python/mlc_chat/model/stable_lm/stablelm_loader.py new file mode 100644 index 0000000..f635c0e --- /dev/null +++ b/python/mlc_chat/model/stable_lm/stablelm_loader.py @@ -0,0 +1,104 @@ +""" +This file specifies how MLC's StableLM parameter maps from other formats, for example HuggingFace +PyTorch, HuggingFace safetensors. +""" + +import functools + +import numpy as np + +from mlc_chat.loader import ExternMapping +from mlc_chat.quantization import Quantization + +from .stablelm_model import StableLMEpochConfig, StableLMEpochForCausalLM + + +def huggingface(model_config: StableLMEpochConfig, quantization: Quantization) -> ExternMapping: + """Returns a parameter mapping that maps from the names of MLC LLM parameters to + the names of HuggingFace PyTorch parameters. + + Parameters + ---------- + model_config : GPT2Config + The configuration of the GPT-2 model. + + quantization : Quantization + The quantization configuration. + + Returns + ------- + param_map : ExternMapping + The parameter mapping from MLC to HuggingFace PyTorch. + """ + model = StableLMEpochForCausalLM(model_config) + if quantization is not None: + model.to(quantization.model_dtype) + _, _named_params, _ = model.export_tvm( # type: ignore[misc] + spec=model.get_default_spec(), + allow_extern=True, + ) + named_parameters = dict(_named_params) + + mapping = ExternMapping() + + for i in range(model_config.num_hidden_layers): + # Add QKV in self attention + attn = f"model.layers.{i}.self_attn" + mlc_name = f"{attn}.qkv_proj.weight" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{attn}.q_proj.weight", + f"{attn}.k_proj.weight", + f"{attn}.v_proj.weight", + ], + functools.partial( + lambda q, k, v, dtype: np.concatenate([q, k, v], axis=0).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + mlc_name = f"{attn}.qkv_proj.bias" + + # The old StableLM 3B model does not have bias term in q, k, v projection + if mlc_name in named_parameters: + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{attn}.q_proj.bias", + f"{attn}.k_proj.bias", + f"{attn}.v_proj.bias", + ], + functools.partial( + lambda q, k, v, dtype: np.concatenate([q, k, v], axis=0).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + # Add gates in MLP + mlp = f"model.layers.{i}.mlp" + mlc_name = f"{mlp}.gate_up_proj.weight" + mlc_param = named_parameters[mlc_name] + mapping.add_mapping( + mlc_name, + [ + f"{mlp}.gate_proj.weight", + f"{mlp}.up_proj.weight", + ], + functools.partial( + lambda gate, up, dtype: np.concatenate([gate, up], axis=0).astype(dtype), + dtype=mlc_param.dtype, + ), + ) + + for mlc_name, mlc_param in named_parameters.items(): + if mlc_name not in mapping.param_map: + mapping.add_mapping( + mlc_name, + [mlc_name], + functools.partial( + lambda x, dtype: x.astype(dtype), + dtype=mlc_param.dtype, + ), + ) + return mapping diff --git a/python/mlc_chat/model/stable_lm/stablelm_model.py b/python/mlc_chat/model/stable_lm/stablelm_model.py new file mode 100644 index 0000000..3a5ce65 --- /dev/null +++ b/python/mlc_chat/model/stable_lm/stablelm_model.py @@ -0,0 +1,266 @@ +""" +Implementation for StableLM architecture. +TODO: add docstring +""" + +import dataclasses +from typing import Any, Dict, Optional + +from tvm import te, tir +from tvm.relax.frontend import nn +from tvm.relax.frontend.nn import Tensor, op + +from mlc_chat import op as op_ext +from mlc_chat.support import logging +from mlc_chat.support.config import ConfigBase +from mlc_chat.support.style import bold + +logger = logging.getLogger(__name__) + + +@dataclasses.dataclass +class StableLMEpochConfig(ConfigBase): # pylint: disable=too-many-instance-attributes + """Configuration of the StableLM model.""" + + vocab_size: int + hidden_size: int + num_hidden_layers: int + num_attention_heads: int + num_key_value_heads: int + norm_eps: float + rope_pct: float + rope_theta: int + intermediate_size: int + use_qkv_bias: bool = False # Default to False for Stable-LM 3B model + context_window_size: int = 0 + prefill_chunk_size: int = 0 + tensor_parallel_shards: int = 1 + kwargs: Dict[str, Any] = dataclasses.field(default_factory=dict) + + def __post_init__(self): + if self.context_window_size == 0: + for name in ["max_position_embeddings", "max_sequence_length"]: + if name in self.kwargs: + self.context_window_size = self.kwargs.pop(name) + logger.info( + "%s not found in config.json. Falling back to %s (%d)", + bold("context_window_size"), + bold(name), + self.context_window_size, + ) + break + else: + raise ValueError( + "Unable to determine the maxmimum sequence length, because none of " + "`context_window_size`, `max_position_embeddings` or `max_sequence_length` is " + "provided in `config.json`." + ) + if self.prefill_chunk_size == 0: + logger.info( + "%s defaults to %s (%d)", + bold("prefill_chunk_size"), + bold("context_window_size"), + self.context_window_size, + ) + self.prefill_chunk_size = self.context_window_size + elif self.prefill_chunk_size > self.context_window_size: + logger.info( + "Overriding %s from %d to %d (%s)", + bold("prefill_chunk_size"), + self.prefill_chunk_size, + self.context_window_size, + bold("context_window_size"), + ) + self.prefill_chunk_size = self.context_window_size + assert self.tensor_parallel_shards == 1, "StableLM currently does not support sharding." + + +# pylint: disable=invalid-name,missing-docstring + + +class StableLMAttention(nn.Module): # pylint: disable=too-many-instance-attributes + def __init__(self, config: StableLMEpochConfig): + self.hidden_size = config.hidden_size + self.rope_theta = config.rope_theta + self.rope_pct = config.rope_pct + self.num_heads = config.num_attention_heads + self.head_dim = self.hidden_size // self.num_heads + self.num_key_value_heads = config.num_key_value_heads + self.num_key_value_groups = self.num_heads // self.num_key_value_heads + self.rotary_ndims = int(self.head_dim * config.rope_pct) + + self.qkv_proj = nn.Linear( + in_features=config.hidden_size, + out_features=(self.num_heads + 2 * self.num_key_value_heads) * self.head_dim, + bias=config.use_qkv_bias, + ) + self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False) + # KV cache for single sequence + self.k_cache = nn.KVCache( + config.context_window_size, [self.num_key_value_heads, self.head_dim] + ) + self.v_cache = nn.KVCache( + config.context_window_size, [self.num_key_value_heads, self.head_dim] + ) + + def forward( # pylint: disable=too-many-locals + self, + hidden_states: Tensor, + attention_mask: Tensor, + total_seq_len: tir.Var, + ): + d, h_q, h_kv, t = self.head_dim, self.num_heads, self.num_key_value_heads, total_seq_len + b, s, _ = hidden_states.shape + assert b == 1, "Only support batch size 1 at this moment." + # Step 1. QKV Projection + qkv = self.qkv_proj(hidden_states) + qkv = op.reshape(qkv, (b, s, h_q + h_kv + h_kv, d)) + # Step 2. Apply QK rotary embedding + q, k, v = op_ext.llama_rope( + qkv, t, self.rope_theta, h_q, h_kv, rotary_dim=self.rotary_ndims + ) + # Step 3. Query and update KVCache + self.k_cache.append(op.squeeze(k, axis=0)) + self.v_cache.append(op.squeeze(v, axis=0)) + k = self.k_cache.view(t) + v = self.v_cache.view(t) + # Step 4. Compute softmax(Q @ K^T / sqrt(d)) @ V + output = op_ext.attention(q, k, v, casual_mask=attention_mask) + # Step 5. Apply output projection + return self.o_proj(output) + + +class StalbeLMMLP(nn.Module): + def __init__(self, config: StableLMEpochConfig): + self.intermediate_size = config.intermediate_size + self.gate_up_proj = nn.Linear( + in_features=config.hidden_size, + out_features=2 * self.intermediate_size, + bias=False, + ) + self.down_proj = nn.Linear(self.intermediate_size, config.hidden_size, bias=False) + + def forward(self, x: Tensor): + concat_x1_x2 = self.gate_up_proj(x) + x1, x2 = op.split(concat_x1_x2, 2, axis=-1) + return self.down_proj(op.silu(x1) * x2) + + +class StableLMDecoderLayer(nn.Module): + def __init__(self, config: StableLMEpochConfig): + norm_eps = config.norm_eps + self.self_attn = StableLMAttention(config) + self.mlp = StalbeLMMLP(config) + self.input_layernorm = nn.LayerNorm(config.hidden_size, eps=norm_eps) + self.post_attention_layernorm = nn.LayerNorm(config.hidden_size, eps=norm_eps) + + def forward(self, hidden_states: Tensor, attention_mask: Tensor, total_seq_len: tir.Var): + out = self.self_attn(self.input_layernorm(hidden_states), attention_mask, total_seq_len) + hidden_states = out + hidden_states + out = self.mlp(self.post_attention_layernorm(hidden_states)) + hidden_states = out + hidden_states + return hidden_states + + +class StableLMEpochModel(nn.Module): + def __init__(self, config: StableLMEpochConfig): + assert config.hidden_size % config.num_attention_heads == 0 + self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size) + self.layers = nn.ModuleList( + [StableLMDecoderLayer(config) for _ in range(config.num_hidden_layers)] + ) + self.norm = nn.LayerNorm(config.hidden_size, eps=config.norm_eps) + + def forward(self, input_ids: Tensor, total_seq_len: tir.Var, attention_mask: Tensor): + hidden_states = self.embed_tokens(input_ids) + for layer in self.layers: + hidden_states = layer(hidden_states, attention_mask, total_seq_len) + hidden_states = self.norm(hidden_states) + return hidden_states + + +class StableLMEpochForCausalLM(nn.Module): + def __init__(self, config: StableLMEpochConfig): + self.model = StableLMEpochModel(config) + self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + self.vocab_size = config.vocab_size + self.dtype = "float32" + + def to(self, dtype: Optional[str] = None): + super().to(dtype=dtype) + if dtype is not None: + self.dtype = dtype + + def forward(self, inputs: Tensor, total_seq_len: tir.Var, attention_mask: Tensor): + def _index(x: te.Tensor): # x[:-1,:] + b, s, d = x.shape + return te.compute((b, 1, d), lambda i, _, k: x[i, s - 1, k], name="index") + + hidden_states = self.model(inputs, total_seq_len, attention_mask) + hidden_states = op.tensor_expr_op(_index, name_hint="index", args=[hidden_states]) + logits = self.lm_head(hidden_states) + if logits.dtype != "float32": + logits = logits.astype("float32") + return logits + + def prefill(self, inputs: Tensor, total_seq_len: tir.Var): + def _attention_mask(batch_size, seq_len, total_seq_len): + return te.compute( + (batch_size, 1, seq_len, total_seq_len), + lambda b, _, i, j: tir.if_then_else( + i < j - (total_seq_len - seq_len), + tir.min_value(self.dtype), + tir.max_value(self.dtype), + ), + name="attention_mask_prefill", + ) + + batch_size, seq_len = inputs.shape + attention_mask = op.tensor_expr_op( + _attention_mask, + name_hint="attention_mask_prefill", + args=[batch_size, seq_len, total_seq_len], + ) + return self.forward(inputs, total_seq_len, attention_mask) + + def decode(self, inputs: Tensor, total_seq_len: tir.Var): + batch_size, seq_len = inputs.shape + attention_mask = op.full( + shape=[batch_size, 1, seq_len, total_seq_len], + fill_value=tir.max_value(self.dtype), + dtype=self.dtype, + ) + return self.forward(inputs, total_seq_len, attention_mask) + + def softmax_with_temperature(self, logits: Tensor, temperature: Tensor): + return op.softmax(logits / temperature, axis=-1) + + def get_default_spec(self): + batch_size = 1 + mod_spec = { + "prefill": { + "inputs": nn.spec.Tensor([batch_size, "seq_len"], "int32"), + "total_seq_len": int, + "$": { + "param_mode": "packed", + "effect_mode": "packed", + }, + }, + "decode": { + "inputs": nn.spec.Tensor([batch_size, 1], "int32"), + "total_seq_len": int, + "$": { + "param_mode": "packed", + "effect_mode": "packed", + }, + }, + "softmax_with_temperature": { + "logits": nn.spec.Tensor([1, 1, "vocab_size"], "float32"), + "temperature": nn.spec.Tensor([], "float32"), + "$": { + "param_mode": "none", + "effect_mode": "none", + }, + }, + } + return nn.spec.ModuleSpec.from_raw(mod_spec, self) diff --git a/python/mlc_chat/model/stable_lm/stablelm_quantization.py b/python/mlc_chat/model/stable_lm/stablelm_quantization.py new file mode 100644 index 0000000..0bb6047 --- /dev/null +++ b/python/mlc_chat/model/stable_lm/stablelm_quantization.py @@ -0,0 +1,53 @@ +"""This file specifies how MLC's StableLM parameters are quantized using group quantization +or other formats.""" +from typing import Tuple + +from tvm.relax.frontend import nn + +from mlc_chat.loader import QuantizeMapping +from mlc_chat.quantization import FTQuantize, GroupQuantize, NoQuantize + +from .stablelm_model import StableLMEpochConfig, StableLMEpochForCausalLM + + +def group_quant( + model_config: StableLMEpochConfig, + quantization: GroupQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a StableLM-architecture model using group quantization.""" + model: nn.Module = StableLMEpochForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def ft_quant( + model_config: StableLMEpochConfig, + quantization: FTQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a StableLM model using FasterTransformer quantization.""" + model: nn.Module = StableLMEpochForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + model = quantization.quantize_model( + model, + quant_map, + "", + ) + return model, quant_map + + +def no_quant( + model_config: StableLMEpochConfig, + quantization: NoQuantize, +) -> Tuple[nn.Module, QuantizeMapping]: + """Quantize a StableLM model without quantization.""" + model: nn.Module = StableLMEpochForCausalLM(model_config) + model.to(quantization.model_dtype) + quant_map = QuantizeMapping({}, {}) + return model, quant_map diff --git a/python/mlc_chat/nn/__init__.py b/python/mlc_chat/nn/__init__.py new file mode 100644 index 0000000..fb1743f --- /dev/null +++ b/python/mlc_chat/nn/__init__.py @@ -0,0 +1,3 @@ +"""Common `nn.Modules` used to define LLMs in this project.""" +from .expert import MixtralExperts +from .kv_cache import FlashInferPagedKVCache, PagedKVCache, RopeMode, TIRPagedKVCache diff --git a/python/mlc_chat/nn/expert.py b/python/mlc_chat/nn/expert.py new file mode 100644 index 0000000..a4ff0cf --- /dev/null +++ b/python/mlc_chat/nn/expert.py @@ -0,0 +1,26 @@ +"""An nn.Module that represents MoE experts""" +from tvm.relax.frontend import nn +from tvm.relax.frontend.nn import Tensor + +from mlc_chat.op import extern, ft_gemm, moe_matmul + + +class MixtralExperts(nn.Module): + """Mixtral experts""" + + def __init__(self, num_local_experts, in_features, out_features): + self.num_local_experts = num_local_experts + self.in_features = in_features + self.out_features = out_features + self.weight = nn.Parameter((num_local_experts, out_features, in_features)) + self.dtype = "float32" + + def forward(self, x: Tensor, indptr: Tensor): # pylint: disable=invalid-name,missing-docstring + assert x.ndim == 2 + if indptr.ndim == 2: + assert indptr.shape[0] == 1 + return moe_matmul.gemv(x, self.weight, indptr) + assert indptr.ndim == 1 + if extern.get_store().faster_transformer and self.dtype == "float16": + return ft_gemm.faster_transformer_moe_gemm(x, self.weight, indptr) + return moe_matmul.group_gemm(x, self.weight, indptr) diff --git a/python/mlc_chat/nn/kv_cache.py b/python/mlc_chat/nn/kv_cache.py new file mode 100644 index 0000000..e956037 --- /dev/null +++ b/python/mlc_chat/nn/kv_cache.py @@ -0,0 +1,1435 @@ +"""Attention KV cache modeling.""" + +# pylint: disable=too-many-statements,too-many-lines +import enum +import math +from typing import Optional, Tuple + +from tvm import relax as rx +from tvm import tir +from tvm.relax.frontend.nn import Object, Tensor +from tvm.runtime import DataType +from tvm.script import tir as T +from tvm.target import Target + +from mlc_chat.op.position_embedding import ( + llama_inplace_rope, + llama_rope_with_position_map, + rope_freq, +) + + +class RopeMode(enum.IntEnum): + """The RoPE mode of the Paged KV cache. + If it is none, the KV cache will not apply RoPE to q and k. + If it is normal, RoPE will be applied to k before adding k to cache. + Otherwise, RoPE will be applied to q/k in attention kernel on-the-fly. + """ + + NONE = 0 + NORMAL = 1 + INLINE = 2 + + +class PagedKVCache(Object): # pylint: disable=too-few-public-methods + """The Paged KV Cache used in LLM batching for efficient attention computation.""" + + @staticmethod + def create_generic( # pylint: disable=too-many-arguments + max_batch_size: tir.Var, + max_total_seq_len: tir.Var, + prefill_chunk_size: tir.Var, + page_size: tir.Var, + num_hidden_layers: int, + num_attention_heads: int, + num_key_value_heads: int, + head_dim: int, + rope_mode: RopeMode, + rope_scale: int, + rope_theta: int, + dtype: str, + rotary_dim: Optional[int] = None, + name: str = "paged_kv_cache", + ) -> "PagedKVCache": + """The generic function of creating a PagedKVCache, + which will be rewritten by functions in compilation pipeline. + """ + if rotary_dim is None: + rotary_dim = head_dim + return PagedKVCache( + _expr=rx.Call( + rx.extern("mlc.create_paged_kv_cache_generic"), + args=[ + rx.ShapeExpr( + [max_batch_size, max_total_seq_len, prefill_chunk_size, page_size] + ), + rx.PrimValue(num_hidden_layers), + rx.PrimValue(num_attention_heads), + rx.PrimValue(num_key_value_heads), + rx.PrimValue(head_dim), + rx.PrimValue(rope_mode), + rx.PrimValue(rope_scale), + rx.PrimValue(rope_theta), + rx.PrimValue(rotary_dim), + rx.DataTypeImm(dtype), + ], + sinfo_args=[rx.ObjectStructInfo()], + ), + _name=name, + ) + + def attention( # pylint: disable=invalid-name, too-many-arguments + self, + layer_id: int, + q: Tensor, + k: Tensor, + v: Tensor, + attn_score_scaling_factor: float = 1.0, + ) -> Tensor: + """Compute attention with the given q/k/v data and in-cache k/v data + on the specified layer. Rotary position embeddings are applied to k/v + within this function. + + - For prefill, the input q and output tensor have shape + (1, total_seq_len, num_attention_heads, head_dim), and the + k/v tensors have shape (1, total_seq_len, num_key_value_heads, head_dim). + - For decode, the input q and output tensor have shape + (batch_size, 1, num_attention_heads, head_dim), and the + k/v tensors have shape (batch_size, 1, num_key_value_heads, head_dim). + """ + # pylint: disable=protected-access + q_shape = q.shape + q = q.reshape(q.shape[0] * q.shape[1], q.shape[2], q.shape[3]) + k = k.reshape(k.shape[0] * k.shape[1], k.shape[2], k.shape[3]) + v = v.reshape(v.shape[0] * v.shape[1], v.shape[2], v.shape[3]) + return Tensor( + _expr=rx.BlockBuilder.current().emit( + rx.call_dps_packed( + "vm.builtin.paged_attention_kv_cache_attention", + [ + self._expr, + rx.PrimValue(layer_id), # type: ignore[arg-type] + rx.PrimValue(attn_score_scaling_factor), + q._expr, + k._expr, + v._expr, + ], + out_sinfo=q._expr.struct_info, + ) + ) + ).reshape(*q_shape) + # pylint: enable=protected-access + + def attention_with_fused_qkv( # pylint: disable=invalid-name + self, + layer_id: int, + qkv: Tensor, + num_qo_heads: int, + attn_score_scaling_factor: float = 1.0, + ) -> Tensor: + """Compute attention with the given fused q/k/v data and in-cache k/v data + on the specified layer. Rotary position embeddings are applied to k/v + within this function. + + - For prefill, the input qkv and output tensor have shape + (1, total_seq_len) for the first two dimensions. + - For decode, the input qkv and output tensor have shape + (batch_size, 1) for the first two dimensions. + - The input qkv have `2 * num_qo_heads + num_kv_heads` at the third dim. + - The output tensor have `num_qo_heads` at the third dim. + - The input qkv and output tensor have `head_dim` at the last dim. + """ + # pylint: disable=protected-access + b, s, _, d = qkv._expr.struct_info.shape + qkv = qkv.reshape(b * s, qkv.shape[2], d) + return Tensor( + _expr=rx.BlockBuilder.current().emit( + rx.call_dps_packed( + "vm.builtin.paged_attention_kv_cache_attention_with_fused_qkv", + [ + self._expr, + rx.PrimValue(layer_id), # type: ignore[arg-type] + rx.PrimValue(attn_score_scaling_factor), + qkv._expr, + ], + out_sinfo=rx.TensorStructInfo((b * s, num_qo_heads, d), qkv.dtype), + ) + ) + ).reshape(b, s, num_qo_heads, d) + + def get_query_positions(self, total_length: tir.PrimExpr) -> Tensor: + """Get the in-sequence positions of each slot in the query, + which are needed for applying positional embeddings in some models. + + Parameters + ---------- + total_length : tir.PrimExpr + The summed-up total sequence length of queries in + the batch being forwarded. + + Returns + ------- + q_positions : Tensor + The in-sequence query positions, in shape `(total_length,)` + """ + return Tensor( + _expr=rx.BlockBuilder.current().emit( + rx.call_pure_packed( + "vm.builtin.paged_attention_kv_cache_get_query_positions", + self._expr, + sinfo_args=rx.TensorStructInfo((total_length,), "int32"), + ) + ) + ) + + # pylint: enable=protected-access + + +class FlashInferPagedKVCache(PagedKVCache): # pylint: disable=too-few-public-methods + """Paged KV cache using FlashInfer (CUDA) kernels.""" + + def __init__( # pylint: disable=too-many-arguments,too-many-locals + self, + max_batch_size: tir.Var, + max_total_seq_len: tir.Var, + prefill_chunk_size: tir.Var, + page_size: tir.Var, + num_hidden_layers: int, + num_attention_heads: int, + num_key_value_heads: int, + head_dim: int, + rope_mode: RopeMode, + rope_scale: int, + rope_theta: int, + rotary_dim: int, + dtype: str, + target: Target, + name: str = "paged_kv_cache", + ) -> None: + """Create a paged KV cache object with FlashInfer kernels. + + Parameters + ---------- + max_batch_size : tir.Var + The maximum allowed batch size of the KV cache. + It is a symbolic variable whose concrete value is specified + at runtime. + max_total_seq_len : tir.Var + The maximum allowed total sequence length of the KV cache. + It is a symbolic variable whose concrete value is specified + at runtime. + prefill_chunk_size : tir.Var + The maximum total sequence length in a prefill. + It is a symbolic variable whose concrete value is specified + at runtime. + page_size : tir.Var + The size (a.k.a. number of tokens) of each page. + It is a symbolic variable whose concrete value is specified + at runtime. + rope_mode : RopeMode + The RoPE mode of the Paged KV cache. + If it is normal, RoPE will be applied to k before adding k to cache. + Otherwise, RoPE will be applied to q/k in attention kernel on-the-fly. + rope_scale : int + The scale of rotary position embedding. + rope_theta : int + The base of rotary position embedding. + rotary_dim : int + The number of dimensions in the embedding that RoPE is applied to. + """ + if rope_mode == RopeMode.INLINE: + assert rotary_dim == head_dim, "FlashInfer RoPE does not support partial rotary dim." + + bb = rx.BlockBuilder.current() # pylint: disable=invalid-name + args = [ + rx.ShapeExpr([max_batch_size, max_total_seq_len, prefill_chunk_size, page_size]), + rx.PrimValue(num_hidden_layers), + rx.PrimValue(num_attention_heads), + rx.PrimValue(num_key_value_heads), + rx.PrimValue(head_dim), + rx.PrimValue(rope_mode), + rx.PrimValue(rope_scale), + rx.PrimValue(rope_theta), + rx.op.zeros((), dtype), + # pylint: disable=line-too-long + # fmt: off + bb.add_func(_kv_cache_transpose_append(num_key_value_heads, head_dim, dtype), "kv_cache_transpose_append"), + rx.extern("paged_kv_cache.attention_kernel_prefill"), + rx.extern("paged_kv_cache.attention_kernel_decode"), + rx.extern("flashinfer.attention_kernel_prefill_with_ragged_kv_cache"), + rx.extern("flashinfer.attention_kernel_prefill_with_ragged_kv_cache_begin_forward"), + rx.extern("flashinfer.attention_kernel_prefill_with_ragged_kv_cache_end_forward"), + rx.extern("paged_kv_cache.attention_kernel_prefill_begin_forward"), + rx.extern("paged_kv_cache.attention_kernel_prefill_end_forward"), + rx.extern("paged_kv_cache.attention_kernel_decode_begin_forward"), + rx.extern("paged_kv_cache.attention_kernel_decode_end_forward"), + rx.extern("flashinfer.merge_state_in_place"), + bb.add_func(llama_rope_with_position_map(rope_theta, rope_scale, head_dim, num_attention_heads, num_key_value_heads, dtype, rotary_dim), "tir_split_rotary"), + bb.add_func(llama_inplace_rope(rope_theta, rope_scale, head_dim, num_attention_heads, num_key_value_heads, dtype, target, rotary_dim), "tir_qk_rotary_inplace"), + bb.add_func(_kv_cache_debug_get_kv(num_hidden_layers, num_key_value_heads, head_dim, dtype), "kv_cache_debug_get_kv"), + # fmt: on + # pylint: enable=line-too-long + ] + super().__init__( + _expr=rx.Call( + rx.extern("vm.builtin.paged_attention_kv_cache_create"), + args=args, + sinfo_args=[rx.ObjectStructInfo()], + ), + _name=name, + ) + + +class TIRPagedKVCache(PagedKVCache): # pylint: disable=too-few-public-methods + """Paged KV cache using TIR kernels.""" + + def __init__( # pylint: disable=too-many-arguments,too-many-locals + self, + max_batch_size: tir.Var, + max_total_seq_len: tir.Var, + prefill_chunk_size: tir.Var, + page_size: tir.Var, + num_hidden_layers: int, + num_attention_heads: int, + num_key_value_heads: int, + rope_mode: RopeMode, + head_dim: int, + rope_scale: int, + rope_theta: int, + rotary_dim: int, + dtype: str, + target: Target, + name: str = "paged_kv_cache", + ) -> None: + """Create a paged KV cache object with TIR kernels. + + Parameters + ---------- + max_batch_size : tir.Var + The maximum allowed batch size of the KV cache. + It is a symbolic variable whose concrete value is specified + at runtime. + max_total_seq_len : tir.Var + The maximum allowed total sequence length of the KV cache. + It is a symbolic variable whose concrete value is specified + at runtime. + prefill_chunk_size : tir.Var + The maximum total sequence length in a prefill. + It is a symbolic variable whose concrete value is specified + at runtime. + page_size : tir.Var + The size (a.k.a. number of tokens) of each page. + It is a symbolic variable whose concrete value is specified + at runtime. + rope_mode : RopeMode + The RoPE mode of the Paged KV cache. + If it is normal, RoPE will be applied to k before adding k to cache. + Otherwise, RoPE will be applied to q/k in attention kernel on-the-fly. + rope_scale : int + The scale of rotary position embedding. + rope_theta : int + The base of rotary position embedding. + rotary_dim : int + The number of dimensions in the embedding that RoPE is applied to. + target : Target + The target to build the model to. + """ + + bb = rx.BlockBuilder.current() + args = [ + rx.ShapeExpr([max_batch_size, max_total_seq_len, prefill_chunk_size, page_size]), + rx.PrimValue(num_hidden_layers), + rx.PrimValue(num_attention_heads), + rx.PrimValue(num_key_value_heads), + rx.PrimValue(head_dim), + rx.PrimValue(rope_mode), + rx.PrimValue(rope_scale), + rx.PrimValue(rope_theta), + rx.op.zeros((), dtype), + # pylint: disable=line-too-long + # fmt: off + bb.add_func(_kv_cache_transpose_append(num_key_value_heads, head_dim, dtype), "kv_cache_transpose_append"), + bb.add_func(_attention_prefill(num_key_value_heads, num_attention_heads, head_dim, dtype, target), "tir_attention_prefill"), + bb.add_func(_attention_decode(num_key_value_heads, num_attention_heads, head_dim, dtype, target), "tir_attention_decode"), + bb.add_func(_attention_prefill_ragged(num_key_value_heads, num_attention_heads, head_dim, dtype, target), "tir_attention_prefill_ragged"), + bb.add_func(_merge_state_inplace(num_key_value_heads, head_dim, dtype, target), "tir_attention_merge_state"), + bb.add_func(llama_rope_with_position_map(rope_theta, rope_scale, head_dim, num_attention_heads, num_key_value_heads, dtype, rotary_dim), "tir_split_rotary"), + bb.add_func(llama_inplace_rope(rope_theta, rope_scale, head_dim, num_attention_heads, num_key_value_heads, dtype, target, rotary_dim), "tir_qk_rotary_inplace"), + bb.add_func(_kv_cache_debug_get_kv(num_hidden_layers, num_key_value_heads, head_dim, dtype), "kv_cache_debug_get_kv"), + # fmt: on + # pylint: enable=line-too-long + ] + super().__init__( + _expr=rx.Call( + rx.extern("vm.builtin.paged_attention_kv_cache_create_reduced"), + args=args, + sinfo_args=[rx.ObjectStructInfo()], + ), + _name=name, + ) + + +# mypy: disable-error-code="attr-defined,valid-type,no-redef" +# pylint: disable=too-many-locals + + +def _kv_cache_transpose_append(num_key_value_heads, head_dim, dtype): + """Return the TIR function that appends new k/v data to PagedKVCache.""" + + # pylint: disable=line-too-long,invalid-name + # fmt: off + @T.prim_func + def tir_kv_cache_transpose_append( + var_pages: T.handle, + var_k_data: T.handle, + var_v_data: T.handle, + var_position_map: T.handle, + ): + T.func_attr({"tir.noalias": T.bool(True)}) + ntoken = T.SizeVar("num_tokens_excluding_cache", "int64") + num_pages = T.int64() + pages = T.match_buffer(var_pages, (num_pages, 2, num_key_value_heads, 16, head_dim), dtype) + k_data = T.match_buffer(var_k_data, (ntoken, num_key_value_heads, head_dim), dtype) + v_data = T.match_buffer(var_v_data, (ntoken, num_key_value_heads, head_dim), dtype) + position_map = T.match_buffer(var_position_map, (ntoken,), "int32") + for global_pos, h, f in T.grid(ntoken, num_key_value_heads, head_dim): + with T.block("k_transpose_append"): + vgpos, vh, vf = T.axis.remap("SSS", [global_pos, h, f]) + T.reads(position_map[vgpos], k_data[vgpos, vh, vf]) + T.writes(pages[position_map[vgpos] // 16, 0, vh, position_map[vgpos] % 16, vf]) + position: T.int32 = position_map[vgpos] # type: ignore + pages[T.floordiv(position, 16), 0, vh, T.floormod(position, 16), vf] = k_data[vgpos, vh, vf] + with T.block("v_transpose_append"): + vgpos, vh, vf = T.axis.remap("SSS", [global_pos, h, f]) + T.reads(position_map[vgpos], k_data[vgpos, vh, vf]) + T.writes(pages[position_map[vgpos] // 16, 1, vh, position_map[vgpos] % 16, vf]) + position: T.int32 = position_map[vgpos] # type: ignore[name-defined,no-redef] + pages[T.floordiv(position, 16), 1, vh, T.floormod(position, 16), vf] = v_data[vgpos, vh, vf] + # fmt: on + # pylint: enable=line-too-long,invalid-name + + return tir_kv_cache_transpose_append + + +def _kv_cache_debug_get_kv(num_hidden_layers, num_key_value_heads, head_dim, dtype): + """Return the TIR function that fetches the k/v data on given positions and layer.""" + + # pylint: disable=line-too-long,invalid-name + # fmt: off + @T.prim_func + def tir_kv_cache_debug_get_kv( + var_pages: T.handle, + var_position_map: T.handle, + var_k_data: T.handle, + var_v_data: T.handle, + layer_id: T.int64, + ): + T.func_attr({"tir.noalias": T.bool(True)}) + seqlen = T.SizeVar("num_tokens_including_cache", "int64") + page_size = T.SizeVar("page_size", "int64") + num_pages = T.int64() + pages = T.match_buffer(var_pages, (num_pages, 2, num_key_value_heads, page_size, head_dim), dtype) + position_map = T.match_buffer(var_position_map, (seqlen,), "int32") + k_data = T.match_buffer(var_k_data, (num_hidden_layers, seqlen, num_key_value_heads, head_dim), dtype) + v_data = T.match_buffer(var_v_data, (num_hidden_layers, seqlen, num_key_value_heads, head_dim), dtype) + for p, h, d in T.grid(seqlen, num_key_value_heads, head_dim): + with T.block("copy0"): + vp, vh, vd = T.axis.remap("SSS", [p, h, d]) + T.reads(position_map[vp], pages[position_map[vp] // page_size, 0:2, vh, position_map[vp] % page_size, vd]) + T.writes(k_data[layer_id, vp, vh, vd], v_data[layer_id, vp, vh, vd]) + position: T.int32 = position_map[vp] # type: ignore[name-defined] + k_data[layer_id, vp, vh, vd] = pages[T.floordiv(position, page_size), 0, vh, T.floormod(position, page_size), vd] + v_data[layer_id, vp, vh, vd] = pages[T.floordiv(position, page_size), 1, vh, T.floormod(position, page_size), vd] + # fmt: on + # pylint: enable=line-too-long,invalid-name + + return tir_kv_cache_debug_get_kv + + +def _rope( # pylint: disable=too-many-arguments + buffer: T.Buffer, + offset: tir.Var, + rotary_dim: int, + theta: tir.Var, + scale: tir.Var, + indices: Tuple[tir.Var, ...], + qkv_dtype="float16", +): + d = indices[-1] + cos_freq, sin_freq = rope_freq(offset * scale, d, rotary_dim, theta, qkv_dtype) + cos = cos_freq * buffer[indices] + sin = sin_freq * tir.if_then_else( + d < rotary_dim // 2, + -buffer[indices[:-1] + (d + rotary_dim // 2,)], + buffer[indices[:-1] + (d - rotary_dim // 2,)], + ) + return cos + sin + + +def _var(dtype): + return T.alloc_buffer((1,), dtype, scope="local") + + +def _attention_prefill(h_kv, h_q, d, dtype, target: Target): # pylint: disable=unused-argument + # pylint: disable=invalid-name + NUM_BLKS = 16 + LOAD_VEC = 8 // ((DataType(dtype).bits + 7) // 8) # 8 bytes + group_size = h_q // h_kv + sm_scale = 1.0 / math.sqrt(float(d)) * math.log2(math.exp(1)) + + num_warps = 4 + tile_x, tile_y, tile_z = 64 // ((DataType(dtype).bits + 7) // 8) // max(d // 128, 1), d, 16 + L_per_cta = tile_x // group_size + + def mask(causal, row, col, kv_len, qo_len): + return T.if_then_else( + causal > 0, + col < kv_len - qo_len + row + 1, + col < kv_len, + ) + + # pylint: disable=line-too-long,too-many-arguments,too-many-branches + # fmt: off + @T.prim_func + def batch_prefill_paged_kv( + _0: T.int32, # pylint: disable=unused-argument + var_q: T.handle, # [total_len, h_q, d] + var_q_indptr: T.handle, # [batch_size + 1] + var_pages: T.handle, # [max_num_pages, 2, h_kv, page_size, d] + var_page_indptr: T.handle, # [batch_size + 1] + var_page_values: T.handle, # [nnz_pages] + var_last_page_len: T.handle, # [b] + var_k_rope_pos_offset: T.handle, # [b] + var_q_rope_position: T.handle, # [total_len] + var_output: T.handle, # [total_len, h_q, d] + var_lse: T.handle, # [total_len, h_q] + causal: T.int32, + rotary_mode: T.int32, + rope_scale: T.float32, + rope_theta: T.float32, + attn_score_scaling_factor: T.float32, + ): + batch_size = T.int32(is_size_var=True) + total_len = T.int32(is_size_var=True) + nnz_pages = T.int32(is_size_var=True) + max_num_pages = T.int32(is_size_var=True) + + q = T.match_buffer(var_q, (total_len, h_q, d), dtype) + q_indptr = T.match_buffer(var_q_indptr, (batch_size + 1,), "int32") + pages = T.match_buffer(var_pages, (max_num_pages, 2, h_kv, 16, d), dtype) + page_indptr = T.match_buffer(var_page_indptr, (batch_size + 1,), "int32") + page_values = T.match_buffer(var_page_values, (nnz_pages,), "int32") + last_page_len = T.match_buffer(var_last_page_len, (batch_size,), "int32") + k_rope_pos_offset = T.match_buffer(var_k_rope_pos_offset, (batch_size,), "int32") + q_rope_position = T.match_buffer(var_q_rope_position, (total_len,), "int32") + output = T.match_buffer(var_output, (total_len, h_q, d), dtype) + lse = T.match_buffer(var_lse, (total_len, h_q), "float32") # pylint: disable=unused-variable + + # kernel code + for lbx in T.thread_binding(NUM_BLKS, thread="blockIdx.x"): + for lby in T.thread_binding(h_kv, thread="blockIdx.y"): + for lty in T.thread_binding(num_warps, thread="threadIdx.y"): + for ltx in T.thread_binding(32, thread="threadIdx.x"): + with T.block("attn"): + bx, by, ty, tx = T.axis.remap("SSSS", [lbx, lby, lty, ltx]) + T.reads() + T.writes() + tile_id = _var("int32") + batch_idx = _var("int32") + batch_tiles = _var("int32") + batch_rows = _var("int32") + iterator = _var("int32") + kv_chunk_len = _var("int32") + + Q_smem = T.alloc_buffer((tile_x, d), dtype, scope="shared") + K_smem = T.alloc_buffer((tile_z, d), dtype, scope="shared") + V_smem = T.alloc_buffer((tile_z, d), dtype, scope="shared") + S_smem = T.alloc_buffer((tile_x, tile_z), "float32", scope="shared") + + S_local = T.alloc_buffer((tile_x, tile_z), "float32", scope="local") + O_local = T.alloc_buffer((tile_x, d), "float32", scope="local") + + m_smem = T.alloc_buffer((tile_x, ), "float32", scope="shared") + m_prev_smem = T.alloc_buffer((tile_x, ), "float32", scope="shared") + d_smem = T.alloc_buffer((tile_x, ), "float32", scope="shared") + + m_new = T.alloc_buffer((math.ceil(tile_x / (32 * num_warps)),), "float32", scope="local") + m_prev = T.alloc_buffer((math.ceil(tile_x / (32 * num_warps)),), "float32", scope="local") + d_new = T.alloc_buffer((math.ceil(tile_x / (32 * num_warps)),), "float32", scope="local") + + ## get tile_no, batch_idx, batch_tiles, batch_rows + tile_id[0] = bx + batch_idx[0] = 0 + batch_rows[0] = (q_indptr[1] - q_indptr[0]) * group_size + batch_tiles[0] = T.ceildiv(batch_rows[0], tile_x) + while T.tvm_thread_invariant(batch_idx[0] < batch_size): + # advance to next tile + while tile_id[0] >= batch_tiles[0] and batch_idx[0] < batch_size: + tile_id[0] -= batch_tiles[0] + batch_idx[0] += 1 + if batch_idx[0] < batch_size: + b_idx: T.int32 = batch_idx[0] + batch_rows[0] = (q_indptr[b_idx + 1] - q_indptr[b_idx]) * group_size + batch_tiles[0] = T.ceildiv(batch_rows[0], tile_x) + + if T.tvm_thread_invariant(batch_idx[0] < batch_size): + b_idx: T.int32 = batch_idx[0] + L_start: T.int32 = q_indptr[b_idx] + tile_id[0] * L_per_cta + H_qo_start: T.int32 = by * group_size + + cur_page_indptr_begin: T.int32 = page_indptr[b_idx] + cur_page_indptr_end: T.int32 = page_indptr[b_idx + 1] + cur_last_page_len: T.int32 = last_page_len[b_idx] + kv_chunk_len[0] = T.if_then_else( + cur_page_indptr_begin != cur_page_indptr_end, + (cur_page_indptr_end - cur_page_indptr_begin - 1) * 16 + cur_last_page_len, + 0 + ) + T.tvm_storage_sync("shared") + + # init states + for i in T.serial(T.ceildiv(tile_x, 32 * num_warps)): + row: T.int32 = i * 32 * num_warps + ty * 32 + tx + if row < tile_x: + m_smem[row] = -5e4 + d_smem[row] = 1.0 + + for li, lj in T.grid(tile_x, tile_y): + with T.block("O_init"): + i, j = T.axis.remap("SS", [li, lj]) + O_local[i, j] = 0.0 + T.tvm_storage_sync("shared") + + # Load Q from gmem to smem + for li, lj in T.grid(tile_x, tile_y): + with T.block("Q_load"): + i, j = T.axis.remap("SS", [li, lj]) + T.reads() + T.writes() + cur_L = L_start + i // group_size + cur_H_qo = H_qo_start + i % group_size + if cur_L < q_indptr[b_idx + 1]: + Q_smem[i, j] = T.if_then_else( + rotary_mode == 1, + _rope(q, q_rope_position[cur_L], d, rope_theta, rope_scale, (cur_L, cur_H_qo, j), dtype), + q[cur_L, cur_H_qo, j] + ) + else: + Q_smem[i, j] = 0.0 + T.tvm_storage_sync("shared") + + for iterator in T.serial(T.ceildiv(kv_chunk_len[0], tile_z)): + L_kv_start: T.int32 = iterator * tile_z + for lz, ly in T.grid(tile_z, tile_y): + with T.block("K_load"): + i, j = T.axis.remap("SS", [lz, ly]) + T.reads() + T.writes() + cur_L = L_kv_start + i + if cur_L < kv_chunk_len[0]: + page_no: T.int32(is_size_var=True) = page_values[cur_page_indptr_begin + T.floordiv(cur_L, 16)] # type: ignore + page_offset: T.int32(is_size_var=True) = T.floormod(cur_L, 16) # type: ignore + K_smem[i, j] = T.if_then_else( + rotary_mode == 1, + _rope(pages, k_rope_pos_offset[b_idx] + cur_L, d, rope_theta, rope_scale, (page_no, 0, by, page_offset, j), dtype), + pages[page_no, 0, by, page_offset, j] + ) + else: + K_smem[i, j] = 0.0 + T.tvm_storage_sync("shared") + for lz, ly in T.grid(tile_z, tile_y): + with T.block("V_load"): + i, j = T.axis.remap("SS", [lz, ly]) + T.reads() + T.writes() + cur_L = L_kv_start + i + if cur_L < kv_chunk_len[0]: + page_no: T.int32(is_size_var=True) = page_values[cur_page_indptr_begin + T.floordiv(cur_L, 16)] # type: ignore + page_offset: T.int32(is_size_var=True) = T.floormod(cur_L, 16) # type: ignore + V_smem[i, j] = pages[page_no, 1, by, page_offset, j] + else: + V_smem[i, j] = 0.0 + T.tvm_storage_sync("shared") + + # Compute S + with T.block(): + for li, lj, lk in T.grid(tile_x, tile_z, tile_y): + with T.block("S_gemm"): + i, j, k = T.axis.remap("SSR", [li, lj, lk]) + with T.init(): + S_local[i, j] = 0.0 + S_local[i, j] += Q_smem[i, k] * K_smem[j, k] * attn_score_scaling_factor * sm_scale + T.tvm_storage_sync("shared") + for li, lj in T.grid(tile_x, tile_z): + with T.block("S_store"): + i, j = T.axis.remap("SS", [li, lj]) + S_smem[i, j] = S_local[i, j] + T.tvm_storage_sync("shared") + + # Update S, m, d + for i in T.serial(T.ceildiv(tile_x, 32 * num_warps)): + row: T.int32 = i * 32 * num_warps + ty * 32 + tx + if row < tile_x: + with T.block("update1"): + m_prev[i] = m_smem[row] + m_new[i] = m_smem[row] + # mask out of kv_chunk_len S + for j in T.serial(tile_z): + if mask(causal, + row=tile_id[0] * L_per_cta + row // group_size, + col=L_kv_start + j, + kv_len=kv_chunk_len[0], + qo_len=q_indptr[b_idx + 1] - q_indptr[b_idx]): + m_new[i] = T.max(m_new[i], S_smem[row, j]) + d_new[i] = d_smem[row] * T.exp2(m_prev[i] - m_new[i]) + + for i in T.serial(T.ceildiv(tile_x, 32 * num_warps)): + row: T.int32 = i * 32 * num_warps + ty * 32 + tx + with T.block("update"): + for j in T.serial(tile_z): + # this is to avoid sync inside condition branch + if row < tile_x: + if mask(causal, + row=tile_id[0] * L_per_cta + row // group_size, + col=L_kv_start + j, + kv_len=kv_chunk_len[0], + qo_len=q_indptr[b_idx + 1] - q_indptr[b_idx]): + S_smem[row, j] = T.exp2(S_smem[row, j] - m_new[i]) + else: + S_smem[row, j] = T.exp2(-5e4 - m_new[i]) + + for i in T.serial(T.ceildiv(tile_x, 32 * num_warps)): + row: T.int32 = i * 32 * num_warps + ty * 32 + tx + if row < tile_x: + with T.block("update"): + for j in T.serial(tile_z): + d_new[i] += S_smem[row, j] + m_smem[row] = m_new[i] + d_smem[row] = d_new[i] + m_prev_smem[row] = m_prev[i] + T.tvm_storage_sync("shared") + + # Update O + with T.block(): + for li, lj, lk in T.grid(tile_x, tile_y, tile_z): + with T.block("O_gemm"): + i, j, k = T.axis.remap("SSR", [li, lj, lk]) + with T.init(): + O_local[i, j] *= T.exp2(m_prev_smem[i] - m_smem[i]) + O_local[i, j] += S_smem[i, k] * V_smem[k, j] + + # Store O from smem to gmem + for li, lj in T.grid(tile_x, tile_y): + with T.block("O_store"): + i, j = T.axis.remap("SS", [li, lj]) + if L_start + i // group_size < q_indptr[b_idx + 1]: + output[L_start + i // group_size, H_qo_start + i % group_size, j] = O_local[i, j] / d_smem[i] + + # Store LSE to gmem + for li in T.grid(tile_x): + with T.block("lse_store"): + i = T.axis.remap("S", [li]) + if L_start + i // group_size < q_indptr[b_idx + 1]: + lse[L_start + i // group_size, H_qo_start + i % group_size] = m_smem[i] + T.log2(d_smem[i]) + + # move to next tile + tile_id[0] += NUM_BLKS + # fmt: on + # pylint: enable=line-too-long,invalid-name,too-many-arguments,too-many-branches + sch = tir.Schedule(batch_prefill_paged_kv) + + def get_tile_size(x, y, t): + cnt = (x * y) // t + assert (x * y) % t == 0 + tile_y = (int)(math.ceil(math.sqrt(cnt))) + while (cnt % tile_y != 0 or y % tile_y != 0) and tile_y <= cnt: + tile_y += 1 + assert tile_y <= cnt + tile_x = cnt // tile_y + return tile_x, tile_y + + def apply_to_qkv_load(sch: tir.Schedule, block): + loop_x, loop_y = sch.get_loops(block)[-2:] + loop = sch.fuse(loop_x, loop_y) + _, ty, tx, vec = sch.split( + loop, factors=[None, num_warps, 32, LOAD_VEC], preserve_unit_iters=True + ) + sch.bind(ty, "threadIdx.y") + sch.bind(tx, "threadIdx.x") + sch.vectorize(vec) + + def apply_to_so_ewise(sch: tir.Schedule, block, tile): + loop_x, loop_y = sch.get_loops(block)[-2:] + xo, xi = sch.split(loop_x, factors=[None, tile[0]]) + yo, yi = sch.split(loop_y, factors=[None, tile[1]]) + sch.reorder(xo, yo, xi, yi) + t = sch.fuse(xo, yo) + ty, tx = sch.split(t, factors=[num_warps, 32]) + sch.bind(ty, "threadIdx.y") + sch.bind(tx, "threadIdx.x") + + def apply_to_gemm( # pylint: disable=too-many-arguments,unused-argument + sch: tir.Schedule, block, tile, read_0, read_1, r_len=8, k_major=False + ): + loop_x, loop_y, loop_z = sch.get_loops(block)[-3:] + xo, xi = sch.split(loop_x, factors=[None, tile[0]]) + yo, yi = sch.split(loop_y, factors=[None, tile[1]]) + sch.reorder(xo, yo, xi, yi) + t = sch.fuse(xo, yo) + ty, tx = sch.split(t, factors=[num_warps, 32]) + sch.bind(ty, "threadIdx.y") + sch.bind(tx, "threadIdx.x") + + ko, ki = sch.split(loop_z, factors=[None, r_len]) + if k_major: + sch.reorder(ko, xi, yi, ki) + else: + sch.reorder(ko, ki, xi, yi) + sch.decompose_reduction(block, ty) + + def apply_to_md(sch, block): + loop = sch.get_loops(block)[-1] + _, ty, tx = sch.split(loop, factors=[None, num_warps, 32]) + sch.bind(ty, "threadIdx.y") + sch.bind(tx, "threadIdx.x") + + tile_s = get_tile_size(tile_x, tile_z, 32 * num_warps) + tile_o = get_tile_size(tile_x, tile_y, 32 * num_warps) + apply_to_gemm(sch, sch.get_block("S_gemm"), tile_s, 0, 1, k_major=True) + apply_to_gemm(sch, sch.get_block("O_gemm"), tile_o, 2, 3, k_major=False) + apply_to_so_ewise(sch, sch.get_block("S_store"), tile_s) + apply_to_so_ewise(sch, sch.get_block("O_init"), tile_o) + apply_to_so_ewise(sch, sch.get_block("O_store"), tile_o) + apply_to_qkv_load(sch, sch.get_block("Q_load")) + apply_to_qkv_load(sch, sch.get_block("K_load")) + apply_to_qkv_load(sch, sch.get_block("V_load")) + apply_to_md(sch, sch.get_block("lse_store")) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) + + +def _attention_decode( + num_kv_heads, + num_qo_heads, + head_dim, + qkv_dtype, + target: Target, # pylint: disable=unused-argument +): + # pylint: disable=invalid-name + qkv_dtype_bytes = 2 + H_qo = num_qo_heads + H_kv = num_kv_heads + D = head_dim + + thread_limit = 512 if str(target.kind) != "webgpu" else 256 + + GROUP_SIZE = H_qo // H_kv + VEC_SIZE = min(max(8 // qkv_dtype_bytes, D // 32), 4) + bdx = D // VEC_SIZE + bdy = GROUP_SIZE + while bdx * bdy > thread_limit and bdy > 1: + bdy //= 2 + gdz = GROUP_SIZE // bdy + threads_per_CTA = max(thread_limit, bdx * bdy) + bdz = threads_per_CTA // (bdx * bdy) + tile_size_per_bdx = 2 if GROUP_SIZE == 1 else 1 + log2e = math.log2(math.exp(1)) + + # pylint: disable=line-too-long,too-many-arguments,too-many-branches + # fmt: off + @T.prim_func + def batch_decode_paged_kv( + _0: T.int32, # pylint: disable=unused-argument + Q_handle: T.handle, + pages_handle: T.handle, + page_table_indptr_handle: T.handle, + page_table_values_handle: T.handle, + last_page_len_handle: T.handle, + k_rope_pos_offset_handle: T.handle, + q_rope_position_handle: T.handle, + output_handle: T.handle, + lse_handle: T.handle, + rotary_mode: T.int32, + rope_scale: T.float32, + rope_theta: T.float32, + attn_score_scaling_factor: T.float32, + ): + T.func_attr({"tir.is_scheduled": 1}) + B = T.int32(is_size_var=True) + nnz_pages = T.int32(is_size_var=True) + max_num_pages = T.int32(is_size_var=True) + + Q = T.match_buffer(Q_handle, (B, H_qo, D), qkv_dtype) + pages = T.match_buffer( + pages_handle, (max_num_pages, 2, H_kv, 16, D), qkv_dtype + ) + page_table_indptr = T.match_buffer(page_table_indptr_handle, (B + 1,), "int32") + page_table_values = T.match_buffer(page_table_values_handle, (nnz_pages,), "int32") + k_rope_pos_offset = T.match_buffer(k_rope_pos_offset_handle, (B,), "int32") + q_rope_position = T.match_buffer(q_rope_position_handle, (B,), "int32") + last_page_len = T.match_buffer(last_page_len_handle, (B,), "int32") + output = T.match_buffer(output_handle, (B, H_qo, D), qkv_dtype) + lse = T.match_buffer(lse_handle, (B, H_qo), "float32") # pylint: disable=unused-variable + + sm_scale = 1.0 / math.sqrt(float(D)) * log2e + + for bx in T.thread_binding(B, thread="blockIdx.x"): + for fused_by_bz in T.thread_binding(H_kv * gdz, thread="blockIdx.y"): + for ty in T.thread_binding(bdy, thread="threadIdx.y"): + for tx in T.thread_binding(bdx, thread="threadIdx.x"): + for tz in T.thread_binding(bdz, thread="threadIdx.z"): + with T.block("attn"): + Q_local = T.alloc_buffer((VEC_SIZE,), qkv_dtype, scope="local") + kv_chunk_len = T.alloc_buffer((1,), "int32", scope="local") + K_smem = T.alloc_buffer((bdz * bdy * tile_size_per_bdx, D), qkv_dtype, scope="shared") + V_smem = T.alloc_buffer((bdz * bdy * tile_size_per_bdx, D), qkv_dtype, scope="shared") + O_allreduce = T.alloc_buffer((bdz, bdy, D), "float32", scope="shared") + md_allreduce = T.alloc_buffer((bdz, bdy, 2), "float32", scope="shared") + S_reduce_local = T.alloc_buffer((1,), "float32", scope="local") + t0 = T.alloc_buffer((1,), "float32", scope="local") + + S_local = T.alloc_buffer((bdy * tile_size_per_bdx), "float32", scope="local") + K_local = T.alloc_buffer((VEC_SIZE,), qkv_dtype, scope="local") + V_local = T.alloc_buffer((VEC_SIZE,), qkv_dtype, scope="local") + m_prev = T.alloc_buffer((1,), "float32", scope="local") + d_prev = T.alloc_buffer((1,), "float32", scope="local") + other_m = T.alloc_buffer((1,), "float32", scope="local") + other_d = T.alloc_buffer((1,), "float32", scope="local") + other_o = T.alloc_buffer((VEC_SIZE,), "float32", scope="local") + st_m = T.alloc_buffer((1,), "float32", scope="local") + st_d = T.alloc_buffer((1,), "float32", scope="local") + O_local = T.alloc_buffer((VEC_SIZE,), "float32", scope="local") + + by: T.int32 = fused_by_bz % H_kv + bz: T.int32 = fused_by_bz // H_kv + batch_idx: T.int32 = bx + cur_page_indptr_begin: T.int32 = page_table_indptr[batch_idx] + cur_page_indptr_end: T.int32 = page_table_indptr[batch_idx + 1] + cur_last_page_len: T.int32 = last_page_len[batch_idx] + kv_chunk_len[0] = T.if_then_else( + cur_page_indptr_begin != cur_page_indptr_end, + (cur_page_indptr_end - cur_page_indptr_begin - 1) * 16 + cur_last_page_len, + 0 + ) + + # init states + st_m[0] = -5e4 + st_d[0] = 1.0 + for vec in T.vectorized(VEC_SIZE): + O_local[vec] = 0.0 + + # load q + for vec in T.vectorized(VEC_SIZE): + Q_local[vec] = T.if_then_else( + rotary_mode == 1, + _rope(Q, q_rope_position[batch_idx], head_dim, rope_theta, rope_scale, (bx, by * GROUP_SIZE + bz * bdy + ty, tx * VEC_SIZE + vec), qkv_dtype), + Q[bx, by * GROUP_SIZE + bz * bdy + ty, tx * VEC_SIZE + vec] + ) + + for iterator in T.serial(T.ceildiv(kv_chunk_len[0], tile_size_per_bdx * bdy * bdz)): + tile_start_s: T.int32(is_size_var=True) = (tz * bdy + ty) * tile_size_per_bdx # type: ignore + tile_start_g: T.int32(is_size_var=True) = ((iterator * bdz + tz) * bdy + ty) * tile_size_per_bdx # type: ignore + # load K from global memory to shared memory + for j in T.serial(tile_size_per_bdx): + row_g: T.int32(is_size_var=True) = tile_start_g + j # type: ignore + if row_g < kv_chunk_len[0]: + page_no: T.int32(is_size_var=True) = page_table_values[cur_page_indptr_begin + T.floordiv(row_g, 16)] # type: ignore + page_offset: T.int32(is_size_var=True) = T.floormod(row_g, 16) # type: ignore + for vec in T.vectorized(VEC_SIZE): + K_smem[tile_start_s + j, tx * VEC_SIZE + vec] = T.if_then_else( + rotary_mode == 1, + _rope(pages, k_rope_pos_offset[batch_idx] + row_g, head_dim, rope_theta, rope_scale, (page_no, 0, by, page_offset, tx * VEC_SIZE + vec), qkv_dtype), + pages[page_no, 0, by, page_offset, tx * VEC_SIZE + vec] + ) + else: + for vec in T.vectorized(VEC_SIZE): + K_smem[tile_start_s + j, tx * VEC_SIZE + vec] = 0.0 + T.tvm_storage_sync("shared") + # load V from global memory to shared memory + for j in T.serial(tile_size_per_bdx): + row_g: T.int32(is_size_var=True) = tile_start_g + j # type: ignore + if row_g < kv_chunk_len[0]: + page_no: T.int32(is_size_var=True) = page_table_values[cur_page_indptr_begin + T.floordiv(row_g, 16)] # type: ignore + page_offset: T.int32(is_size_var=True) = T.floormod(row_g, 16) # type: ignore + for vec in T.vectorized(VEC_SIZE): + V_smem[tile_start_s + j, tx * VEC_SIZE + vec] = pages[page_no, 1, by, page_offset, tx * VEC_SIZE + vec] + else: + for vec in T.vectorized(VEC_SIZE): + V_smem[tile_start_s + j, tx * VEC_SIZE + vec] = 0.0 + T.tvm_storage_sync("shared") + # compute QK + m_prev[0] = st_m[0] + for j in T.serial(bdy * tile_size_per_bdx): + # load K from shared memory to local memory + for vec in T.vectorized(VEC_SIZE): + K_local[vec] = K_smem[tz * bdy * tile_size_per_bdx + j, tx * VEC_SIZE + vec] + # compute S = Q * K * sm_scale + S_reduce_local[0] = 0 + for vec in T.serial(VEC_SIZE): + S_reduce_local[0] += Q_local[vec] * K_local[vec] * attn_score_scaling_factor * sm_scale + + with T.block("block_cross_thread"): + T.reads(S_reduce_local[0]) + T.writes(t0[0]) + T.attr( + T.comm_reducer(lambda x0, y0: x0 + y0, [T.float32(0)]), + "reduce_scope", + T.reinterpret("handle", T.uint64(0)), + ) + T.tvm_thread_allreduce(T.uint32(1), S_reduce_local[0], True, t0[0], tx, dtype="handle") + + if (iterator * bdz + tz) * bdy * tile_size_per_bdx + j < kv_chunk_len[0]: + S_local[j] = t0[0] + else: + S_local[j] = -5e4 + # update st_m + st_m[0] = T.max(st_m[0], S_local[j]) + + # update st_d, st_O + o_scale: T.float32 = T.exp2(m_prev[0] - st_m[0]) + st_d[0] *= o_scale + for j in T.serial(bdy * tile_size_per_bdx): + S_local[j] = T.exp2(S_local[j] - st_m[0]) + st_d[0] += S_local[j] + for j in T.vectorized(VEC_SIZE): + O_local[j] *= o_scale + + # load V from shared memory to local memory + # compute O + for j in T.serial(bdy * tile_size_per_bdx): + for vec in T.vectorized(VEC_SIZE): + V_local[vec] = V_smem[tz * bdy * tile_size_per_bdx + j, tx * VEC_SIZE + vec] + for vec in T.vectorized(VEC_SIZE): + O_local[vec] += V_local[vec] * S_local[j] + + if bdz > 1: + # allreduce over bdz + for vec in T.vectorized(VEC_SIZE): + O_allreduce[tz, ty, tx * VEC_SIZE + vec] = O_local[vec] + md_allreduce[tz, ty, 0] = st_m[0] + md_allreduce[tz, ty, 1] = st_d[0] + T.tvm_storage_sync("shared") + + st_m[0] = -5e4 + st_d[0] = 1.0 + for vec in T.vectorized(VEC_SIZE): + O_local[vec] = 0.0 + + for j in T.serial(bdz): + m_prev[0] = st_m[0] + d_prev[0] = st_d[0] + other_m[0] = md_allreduce[j, ty, 0] + other_d[0] = md_allreduce[j, ty, 1] + for vec in T.vectorized(VEC_SIZE): + other_o[vec] = O_allreduce[j, ty, tx * VEC_SIZE + vec] + st_m[0] = T.max(st_m[0], other_m[0]) + st_d[0] = d_prev[0] * T.exp2(m_prev[0] - st_m[0]) + other_d[0] * T.exp2(other_m[0] - st_m[0]) + for vec in T.serial(VEC_SIZE): + O_local[vec] = O_local[vec] * T.exp2(m_prev[0] - st_m[0]) + other_o[vec] * T.exp2(other_m[0] - st_m[0]) + + # normalize O + for vec in T.serial(VEC_SIZE): + O_local[vec] /= st_d[0] + + # store O to global memory + for vec in T.vectorized(VEC_SIZE): + output[batch_idx, by * GROUP_SIZE + bz * bdy + ty, tx * VEC_SIZE + vec] = O_local[vec] + + # store lse to global memory + lse[batch_idx, by * GROUP_SIZE + bz * bdy + ty] = st_m[0] + T.log2(st_d[0]) + # fmt: on + # pylint: enable=line-too-long,invalid-name,too-many-arguments,too-many-branches + return batch_decode_paged_kv + + +def _merge_state_inplace( + num_heads, head_dim, v_dtype, target: Target +): # pylint: disable=unused-argument + # pylint: disable=invalid-name + v_dtype_bytes = 2 + VEC_SIZE = min(max(8 // v_dtype_bytes, head_dim // 32), 4) + bdx = head_dim // VEC_SIZE + bdy = num_heads + + @T.prim_func + def merge_state_inplace( + v: T.handle, + s: T.handle, + v_other: T.handle, + s_other: T.handle, + ): + T.func_attr({"tir.is_scheduled": 1}) + N = T.int32(is_size_var=True) + H = T.int32(is_size_var=True) + D = T.int32(is_size_var=True) + + V = T.match_buffer(v, (N, H, D), v_dtype) + S = T.match_buffer(s, (N, H), "float32") + V_other = T.match_buffer(v_other, (N, H, D), v_dtype) + S_other = T.match_buffer(s_other, (N, H), "float32") + + for bx in T.thread_binding(N, thread="blockIdx.x"): + for ty in T.thread_binding(bdy, thread="threadIdx.y"): + for tx in T.thread_binding(bdx, thread="threadIdx.x"): + with T.block("merge"): + s_val = _var("float32") + s_other_val = _var("float32") + s_max = _var("float32") + scale = _var("float32") + other_scale = _var("float32") + + v_vec = T.alloc_buffer((VEC_SIZE,), v_dtype, scope="local") + v_other_vec = T.alloc_buffer((VEC_SIZE,), v_dtype, scope="local") + + s_val[0] = S[bx, ty] + s_other_val[0] = S_other[bx, ty] + s_max[0] = T.max(s_val[0], s_other_val[0]) + s_val[0] = T.exp2(s_val[0] - s_max[0]) + s_other_val[0] = T.exp2(s_other_val[0] - s_max[0]) + scale[0] = s_val[0] / (s_val[0] + s_other_val[0]) + other_scale[0] = s_other_val[0] / (s_val[0] + s_other_val[0]) + + # load v + for vec in T.vectorized(VEC_SIZE): + v_vec[vec] = V[bx, ty, tx * VEC_SIZE + vec] + # load v_other + for vec in T.vectorized(VEC_SIZE): + v_other_vec[vec] = V_other[bx, ty, tx * VEC_SIZE + vec] + + # merge + for vec in T.serial(VEC_SIZE): + v_vec[vec] = v_vec[vec] * scale[0] + v_other_vec[vec] * other_scale[0] + + # store v + for vec in T.vectorized(VEC_SIZE): + V[bx, ty, tx * VEC_SIZE + vec] = v_vec[vec] + + # store s + S[bx, ty] = T.log2(s_val[0] + s_other_val[0]) + s_max[0] + + # pylint: enable=invalid-name + return merge_state_inplace + + +def _attention_prefill_ragged( + h_kv, h_q, d, dtype, target: Target +): # pylint: disable=unused-argument + # pylint: disable=invalid-name,line-too-long + NUM_BLKS = 16 + LOAD_VEC = 8 // ((DataType(dtype).bits + 7) // 8) # 8 bytes + group_size = h_q // h_kv + sm_scale = 1.0 / math.sqrt(float(d)) * math.log2(math.exp(1)) + + num_warps = 4 + tile_x, tile_y, tile_z = 64 // ((DataType(dtype).bits + 7) // 8) // max(d // 128, 1), d, 16 + L_per_cta = tile_x // group_size + + def mask(causal, row, col, kv_len, qo_len): + return T.if_then_else( + causal > 0, + col < kv_len - qo_len + row + 1, + col < kv_len, + ) + + # fmt: off + @T.prim_func + def batch_prefill_ragged_kv( # pylint: disable=too-many-arguments,too-many-branches + var_q: T.handle, # [total_len, h_q, d] + var_q_indptr: T.handle, # [batch_size + 1] + var_k: T.handle, # [total_len, h_kv, d] + var_v: T.handle, # [total_len, h_kv, d] + var_kv_indptr: T.handle, # [batch_size + 1] + var_q_rope_position: T.handle, # [total_q_len] + var_k_rope_pos_offset: T.handle, # [b] + var_output: T.handle, # [total_len, h_q, d] + var_lse: T.handle, # [total_len, h_q] + causal: T.int32, + rotary_mode: T.int32, + rope_scale: T.float32, + rope_theta: T.float32, + attn_score_scaling_factor: T.float32 + ): + batch_size = T.int32(is_size_var=True) + qo_len = T.int32(is_size_var=True) + kv_len = T.int32(is_size_var=True) + + q = T.match_buffer(var_q, (qo_len, h_q, d), dtype) + q_indptr = T.match_buffer(var_q_indptr, (batch_size + 1,), "int32") + k = T.match_buffer(var_k, (kv_len, h_kv, d), dtype) + v = T.match_buffer(var_v, (kv_len, h_kv, d), dtype) + kv_indptr = T.match_buffer(var_kv_indptr, (batch_size + 1,), "int32") + q_rope_position = T.match_buffer(var_q_rope_position, (qo_len,), "int32") + k_rope_pos_offset = T.match_buffer(var_k_rope_pos_offset, (batch_size,), "int32") + output = T.match_buffer(var_output, (qo_len, h_q, d), dtype) + lse = T.match_buffer(var_lse, (qo_len, h_q), "float32") # pylint: disable=unused-variable + + # kernel code + for lbx in T.thread_binding(NUM_BLKS, thread="blockIdx.x"): + for lby in T.thread_binding(h_kv, thread="blockIdx.y"): + for lty in T.thread_binding(num_warps, thread="threadIdx.y"): + for ltx in T.thread_binding(32, thread="threadIdx.x"): + with T.block("attn"): + bx, by, ty, tx = T.axis.remap("SSSS", [lbx, lby, lty, ltx]) + T.reads() + T.writes() + tile_id = _var("int32") + batch_idx = _var("int32") + batch_tiles = _var("int32") + batch_rows = _var("int32") + iterator = _var("int32") + kv_chunk_len = _var("int32") + + Q_smem = T.alloc_buffer((tile_x, d), dtype, scope="shared") + K_smem = T.alloc_buffer((tile_z, d), dtype, scope="shared") + V_smem = T.alloc_buffer((tile_z, d), dtype, scope="shared") + S_smem = T.alloc_buffer((tile_x, tile_z), "float32", scope="shared") + + S_local = T.alloc_buffer((tile_x, tile_z), "float32", scope="local") + O_local = T.alloc_buffer((tile_x, d), "float32", scope="local") + + m_smem = T.alloc_buffer((tile_x, ), "float32", scope="shared") + m_prev_smem = T.alloc_buffer((tile_x, ), "float32", scope="shared") + d_smem = T.alloc_buffer((tile_x, ), "float32", scope="shared") + + m_new = T.alloc_buffer((math.ceil(tile_x / (32 * num_warps)),), "float32", scope="local") + m_prev = T.alloc_buffer((math.ceil(tile_x / (32 * num_warps)),), "float32", scope="local") + d_new = T.alloc_buffer((math.ceil(tile_x / (32 * num_warps)),), "float32", scope="local") + + ## get tile_no, batch_idx, batch_tiles, batch_rows + tile_id[0] = bx + batch_idx[0] = 0 + batch_rows[0] = (q_indptr[1] - q_indptr[0]) * group_size + batch_tiles[0] = T.ceildiv(batch_rows[0], tile_x) + while T.tvm_thread_invariant(batch_idx[0] < batch_size): + # advance to next tile + while tile_id[0] >= batch_tiles[0] and batch_idx[0] < batch_size: + tile_id[0] -= batch_tiles[0] + batch_idx[0] += 1 + if batch_idx[0] < batch_size: + b_idx: T.int32 = batch_idx[0] + batch_rows[0] = (q_indptr[b_idx + 1] - q_indptr[b_idx]) * group_size + batch_tiles[0] = T.ceildiv(batch_rows[0], tile_x) + + if T.tvm_thread_invariant(batch_idx[0] < batch_size): + b_idx: T.int32 = batch_idx[0] + L_start: T.int32 = q_indptr[b_idx] + tile_id[0] * L_per_cta + H_qo_start: T.int32 = by * group_size + + kv_chunk_len[0] = kv_indptr[b_idx + 1] - kv_indptr[b_idx] + T.tvm_storage_sync("shared") + + # init states + for i in T.serial(T.ceildiv(tile_x, 32 * num_warps)): + row: T.int32 = i * 32 * num_warps + ty * 32 + tx + if row < tile_x: + m_smem[row] = -5e4 + d_smem[row] = 1.0 + + for li, lj in T.grid(tile_x, tile_y): + with T.block("O_init"): + i, j = T.axis.remap("SS", [li, lj]) + O_local[i, j] = 0.0 + T.tvm_storage_sync("shared") + + # Load Q from gmem to smem + for li, lj in T.grid(tile_x, tile_y): + with T.block("Q_load"): + i, j = T.axis.remap("SS", [li, lj]) + T.reads() + T.writes() + cur_L = L_start + i // group_size + cur_H_qo = H_qo_start + i % group_size + if cur_L < q_indptr[b_idx + 1]: + Q_smem[i, j] = T.if_then_else( + rotary_mode == 1, + _rope(q, q_rope_position[cur_L], d, rope_theta, rope_scale, (cur_L, cur_H_qo, j), dtype), + q[cur_L, cur_H_qo, j] + ) + else: + Q_smem[i, j] = 0.0 + T.tvm_storage_sync("shared") + + for iterator in T.serial(T.ceildiv(kv_chunk_len[0], tile_z)): + L_kv_start: T.int32 = iterator * tile_z + L_kv_base: T.int32 = kv_indptr[b_idx] + for lz, ly in T.grid(tile_z, tile_y): + with T.block("K_load"): + i, j = T.axis.remap("SS", [lz, ly]) + T.reads() + T.writes() + cur_L = L_kv_start + i + if cur_L < kv_chunk_len[0]: + K_smem[i, j] = T.if_then_else( + rotary_mode == 1, + _rope(k, k_rope_pos_offset[b_idx] + cur_L, d, rope_theta, rope_scale, (L_kv_base + cur_L, by, j), dtype), + k[L_kv_base + cur_L, by, j] + ) + else: + K_smem[i, j] = 0.0 + T.tvm_storage_sync("shared") + for lz, ly in T.grid(tile_z, tile_y): + with T.block("V_load"): + i, j = T.axis.remap("SS", [lz, ly]) + T.reads() + T.writes() + cur_L = L_kv_start + i + if cur_L < kv_chunk_len[0]: + V_smem[i, j] = v[L_kv_base + cur_L, by, j] + else: + V_smem[i, j] = 0.0 + T.tvm_storage_sync("shared") + + # Compute S + with T.block(): + for li, lj, lk in T.grid(tile_x, tile_z, tile_y): + with T.block("S_gemm"): + i, j, k = T.axis.remap("SSR", [li, lj, lk]) + with T.init(): + S_local[i, j] = 0.0 + S_local[i, j] += Q_smem[i, k] * K_smem[j, k] * attn_score_scaling_factor * sm_scale + T.tvm_storage_sync("shared") + for li, lj in T.grid(tile_x, tile_z): + with T.block("S_store"): + i, j = T.axis.remap("SS", [li, lj]) + S_smem[i, j] = S_local[i, j] + T.tvm_storage_sync("shared") + + # Update S, m, d + for i in T.serial(T.ceildiv(tile_x, 32 * num_warps)): + row: T.int32 = i * 32 * num_warps + ty * 32 + tx + if row < tile_x: + with T.block("update1"): + m_prev[i] = m_smem[row] + m_new[i] = m_smem[row] + # mask out of kv_chunk_len S + for j in T.serial(tile_z): + if mask(causal, + row=tile_id[0] * L_per_cta + row // group_size, + col=L_kv_start + j, + kv_len=kv_chunk_len[0], + qo_len=q_indptr[b_idx + 1] - q_indptr[b_idx]): + m_new[i] = T.max(m_new[i], S_smem[row, j]) + d_new[i] = d_smem[row] * T.exp2(m_prev[i] - m_new[i]) + + for i in T.serial(T.ceildiv(tile_x, 32 * num_warps)): + row: T.int32 = i * 32 * num_warps + ty * 32 + tx + with T.block("update"): + for j in T.serial(tile_z): + # this is to avoid sync inside condition branch + if row < tile_x: + if mask(causal, + row=tile_id[0] * L_per_cta + row // group_size, + col=L_kv_start + j, + kv_len=kv_chunk_len[0], + qo_len=q_indptr[b_idx + 1] - q_indptr[b_idx]): + S_smem[row, j] = T.exp2(S_smem[row, j] - m_new[i]) + else: + S_smem[row, j] = T.exp2(-5e4 - m_new[i]) + + for i in T.serial(T.ceildiv(tile_x, 32 * num_warps)): + row: T.int32 = i * 32 * num_warps + ty * 32 + tx + if row < tile_x: + with T.block("update"): + for j in T.serial(tile_z): + d_new[i] += S_smem[row, j] + m_smem[row] = m_new[i] + d_smem[row] = d_new[i] + m_prev_smem[row] = m_prev[i] + T.tvm_storage_sync("shared") + + # Update O + with T.block(): + for li, lj, lk in T.grid(tile_x, tile_y, tile_z): + with T.block("O_gemm"): + i, j, k = T.axis.remap("SSR", [li, lj, lk]) + with T.init(): + O_local[i, j] *= T.exp2(m_prev_smem[i] - m_smem[i]) + O_local[i, j] += S_smem[i, k] * V_smem[k, j] + + # Store O from smem to gmem + for li, lj in T.grid(tile_x, tile_y): + with T.block("O_store"): + i, j = T.axis.remap("SS", [li, lj]) + if L_start + i // group_size < q_indptr[b_idx + 1]: + output[L_start + i // group_size, H_qo_start + i % group_size, j] = O_local[i, j] / d_smem[i] + + # Store LSE to gmem + for li in T.grid(tile_x): + with T.block("lse_store"): + i = T.axis.remap("S", [li]) + if L_start + i // group_size < q_indptr[b_idx + 1]: + lse[L_start + i // group_size, H_qo_start + i % group_size] = m_smem[i] + T.log2(d_smem[i]) + + # move to next tile + tile_id[0] += NUM_BLKS + # fmt: on + # pylint: enable=line-too-long,invalid-name,too-many-arguments,too-many-branches + sch = tir.Schedule(batch_prefill_ragged_kv) + + def get_tile_size(x, y, t): + cnt = (x * y) // t + assert (x * y) % t == 0 + tile_y = (int)(math.ceil(math.sqrt(cnt))) + while (cnt % tile_y != 0 or y % tile_y != 0) and tile_y <= cnt: + tile_y += 1 + assert tile_y <= cnt + tile_x = cnt // tile_y + return tile_x, tile_y + + def apply_to_qkv_load(sch: tir.Schedule, block): + loop_x, loop_y = sch.get_loops(block)[-2:] + loop = sch.fuse(loop_x, loop_y) + _, ty, tx, vec = sch.split( + loop, factors=[None, num_warps, 32, LOAD_VEC], preserve_unit_iters=True + ) + sch.bind(ty, "threadIdx.y") + sch.bind(tx, "threadIdx.x") + sch.vectorize(vec) + + def apply_to_so_ewise(sch: tir.Schedule, block, tile): + loop_x, loop_y = sch.get_loops(block)[-2:] + xo, xi = sch.split(loop_x, factors=[None, tile[0]]) + yo, yi = sch.split(loop_y, factors=[None, tile[1]]) + sch.reorder(xo, yo, xi, yi) + t = sch.fuse(xo, yo) + ty, tx = sch.split(t, factors=[num_warps, 32]) + sch.bind(ty, "threadIdx.y") + sch.bind(tx, "threadIdx.x") + + def apply_to_gemm( # pylint: disable=too-many-arguments,unused-argument + sch: tir.Schedule, block, tile, read_0, read_1, r_len=8, k_major=False + ): + loop_x, loop_y, loop_z = sch.get_loops(block)[-3:] + xo, xi = sch.split(loop_x, factors=[None, tile[0]]) + yo, yi = sch.split(loop_y, factors=[None, tile[1]]) + sch.reorder(xo, yo, xi, yi) + t = sch.fuse(xo, yo) + ty, tx = sch.split(t, factors=[num_warps, 32]) + sch.bind(ty, "threadIdx.y") + sch.bind(tx, "threadIdx.x") + + ko, ki = sch.split(loop_z, factors=[None, r_len]) + if k_major: + sch.reorder(ko, xi, yi, ki) + else: + sch.reorder(ko, ki, xi, yi) + sch.decompose_reduction(block, ty) + + def apply_to_md(sch, block): + loop = sch.get_loops(block)[-1] + _, ty, tx = sch.split(loop, factors=[None, num_warps, 32]) + sch.bind(ty, "threadIdx.y") + sch.bind(tx, "threadIdx.x") + + tile_s = get_tile_size(tile_x, tile_z, 32 * num_warps) + tile_o = get_tile_size(tile_x, tile_y, 32 * num_warps) + apply_to_gemm(sch, sch.get_block("S_gemm"), tile_s, 0, 1, k_major=True) + apply_to_gemm(sch, sch.get_block("O_gemm"), tile_o, 2, 3, k_major=False) + apply_to_so_ewise(sch, sch.get_block("S_store"), tile_s) + apply_to_so_ewise(sch, sch.get_block("O_init"), tile_o) + apply_to_so_ewise(sch, sch.get_block("O_store"), tile_o) + apply_to_qkv_load(sch, sch.get_block("Q_load")) + apply_to_qkv_load(sch, sch.get_block("K_load")) + apply_to_qkv_load(sch, sch.get_block("V_load")) + + apply_to_md(sch, sch.get_block("lse_store")) + return sch.mod["main"].with_attr("tir.is_scheduled", 1) diff --git a/python/mlc_chat/op/__init__.py b/python/mlc_chat/op/__init__.py new file mode 100644 index 0000000..3425686 --- /dev/null +++ b/python/mlc_chat/op/__init__.py @@ -0,0 +1,6 @@ +"""Extern module for compiler.""" +from . import moe_matmul, moe_misc +from .attention import attention +from .extern import configure, enable, get_store +from .ft_gemm import faster_transformer_dequantize_gemm +from .position_embedding import llama_rope diff --git a/python/mlc_chat/op/attention.py b/python/mlc_chat/op/attention.py new file mode 100644 index 0000000..02f21a6 --- /dev/null +++ b/python/mlc_chat/op/attention.py @@ -0,0 +1,182 @@ +"""Operators enabled by external modules.""" +import math + +from tvm import tir +from tvm.relax.frontend import nn +from tvm.relax.frontend.nn import op + +from mlc_chat.support import logging + +from . import extern as _extern + +logger = logging.getLogger(__name__) + + +WARN_FLASHINFER_GROUP_SIZE = False +WARN_FLASHINFER_HEAD_DIM = False + + +def attention( # pylint: disable=invalid-name,too-many-locals,too-many-statements,too-many-arguments + q: nn.Tensor, + k: nn.Tensor, + v: nn.Tensor, + casual_mask: nn.Tensor, + attn_score_scaling_factor: float = 1.0, + qk_dtype: str = None, +) -> nn.Tensor: + """Attention with casual mask. + + --- Variables --- + s: sequence length of the current query + t: total sequence length + d: head dimension + h, h_q: number of heads in query + h_kv: number of heads in key and value + b: batch size = 1 + + --- Shapes --- + q: [b, s, h_q, d] + k: [t, h_kv, d] + v: [t, h_kv, d] + o: [1, s, hidden = h_q * d] + + --- Computation --- + + .. code-block:: python + + if h_kv != h_q: + k = k.repeat(h_q // h_kv, axis=1) + v = v.repeat(h_q // h_kv, axis=1) + q -> [b, h, s, d] + k, v -> [b, h, t, d] + attn = q @ k^T / sqrt(d) * attn_score_scaling_factor # [b, h, s, t] + attn = softmax_with_mask(attn, casual_mask, axis=-1) + o = attn @ v # [b, h, s, d] + o -> [b, s, h * d] + + --- Other params --- + qk_dtype: if set, `matmul(Q, K, out_dtype=qk_dtype)`, (otherwise use `q.dtype` as `out_dtype`). + For FlashInfer, if "float32", sets `allow_fp16_qk_reduction` to False; otherwise no effect. + """ + assert q.ndim == 4 and k.ndim in [3, 4] and v.ndim in [3, 4] + b, s, h_q, d = q.shape + t, h_kv, _ = k.shape[-3:] + group_size = h_q // h_kv + assert b == 1, "batch size must be 1" + + def _fallback(): + nonlocal q, k, v, qk_dtype + if k.ndim == 3: + k = op.reshape(k, [b, t, h_kv, d]) + if v.ndim == 3: + v = op.reshape(v, [b, t, h_kv, d]) + if h_kv != h_q: + k = k.repeat(h_q // h_kv, axis=2) + v = v.repeat(h_q // h_kv, axis=2) + q = op.permute_dims(q, [0, 2, 1, 3]) + k = op.permute_dims(k, [0, 2, 1, 3]) + v = op.permute_dims(v, [0, 2, 1, 3]) + model_dtype = q.dtype + if qk_dtype is None: + qk_dtype = model_dtype + attn_weights = op.matmul( # [b, h, s, t] + q, # [b, h, s, d] + op.permute_dims(k, [0, 1, 3, 2]), # [b, h, d, t] + out_dtype=qk_dtype, + ) / math.sqrt(d) + if attn_score_scaling_factor != 1.0: + attn_weights = attn_weights * attn_score_scaling_factor + attn_weights = attn_weights.maximum(tir.min_value(model_dtype)).minimum( + casual_mask.astype(qk_dtype) + ) + attn_weights = op.softmax(attn_weights.astype("float32"), axis=-1).astype(model_dtype) + output = op.matmul(attn_weights, v) # [b, h, s, d] <= [b, h, s, t] x [b, h, t, d] + output = op.permute_dims(output, [0, 2, 1, 3]) # [b, s, h, d] + output = op.reshape(output, [b, s, h_q * d]) # [b, s, h * d] + return output + + # FlashInfer Implementation + if ( + _extern.get_store().flashinfer + and attn_score_scaling_factor == 1.0 + and q.dtype == "float16" + and k.dtype == "float16" + and v.dtype == "float16" + ): + if group_size not in [1, 4, 8]: + global WARN_FLASHINFER_GROUP_SIZE # pylint: disable=global-statement + if not WARN_FLASHINFER_GROUP_SIZE: + WARN_FLASHINFER_GROUP_SIZE = True + logger.warning( + "FlashInfer only supports group size in [1, 4, 8], but got %d. Skip and " + "fallback to default implementation.", + group_size, + ) + return _fallback() + if d not in [128]: + global WARN_FLASHINFER_HEAD_DIM # pylint: disable=global-statement + if not WARN_FLASHINFER_HEAD_DIM: + WARN_FLASHINFER_HEAD_DIM = True + logger.warning( + "FlashInfer only supports head_dim in [128], but got %d. Skip and fallback to " + "default implementation.", + d, + ) + return _fallback() + rope_theta = 0.0 + rope_scale = 1.0 + qkv_layout = 0 # "NHD", N for seq_len, H for num_heads, D for head_dim + rotary_mode = 0 # "kNone" + casual = 1 # True + fp16_qk = 1 # True + if qk_dtype == "float32": + fp16_qk = 0 # False + + # 32MB scratchpad + scratch = op.empty([8192 * 1024], dtype="float32") # pylint: disable=no-member + + def _decode(): + return op.extern( + name="flashinfer.single_decode", + args=[ + q, + k, + v, + scratch, + qkv_layout, + rotary_mode, + rope_scale, + rope_theta, + ], + out=nn.Tensor.placeholder((b, s, h_q * d), dtype="float16"), + ) + + def _prefill(): + return op.extern( + name="flashinfer.single_prefill", + args=[ + q, + k, + v, + scratch, + casual, + qkv_layout, + rotary_mode, + fp16_qk, + rope_scale, + rope_theta, + ], + out=nn.Tensor.placeholder((b, s, h_q * d), dtype="float16"), + ) + + if isinstance(s, int) and s == 1: + func = "decode" + else: + func = "prefill" + return { + "decode": _decode, + "prefill": _prefill, + }[func]() + + # Fallback Implementation + return _fallback() diff --git a/python/mlc_chat/op/extern.py b/python/mlc_chat/op/extern.py new file mode 100644 index 0000000..5fa7e82 --- /dev/null +++ b/python/mlc_chat/op/extern.py @@ -0,0 +1,65 @@ +"""Potential externel modules managed by MLC compilation stack. + +An externl module could contain one or multiple handcrafted kernels, as long as it is provided as +an object file (`.o`), a C++ source file (`.cc`), or a CUDA source file (`.cu`). It can be +integrated into the system pretty smoothly. + +As examples, `flashinfer.py` contains such an example that instructs MLC to compile +"$tvm_home/3rdparty/flashinfer/src/tvm_wrapper.cu" with a specific set of compilation flags and then +link into the generated artifact of MLC LLM. TVM PR #16247 +(https://github.com/apache/tvm/pull/16247/) provides more details of using TVM's +`nn.SourceModule` to integrate C++ and CUDA files, and `nn.ObjectModule` to integrate object files. + +To conveniently use those externel modules, MLC LLM compilation pipeline manages an extra global +singleton `Store: ExternalModuleStore` to store the configured modules. It is supposed to be enabled +before any compilation happens, and configured during a model's `forward` method is invoked. +""" +import dataclasses +from typing import Optional + +from tvm.target import Target + + +@dataclasses.dataclass +class ExternModuleStore: + """Global store of external modules enabled during compilation.""" + + configured: bool = False + target: Optional[Target] = None + flashinfer: bool = False + faster_transformer: bool = False + + +STORE: ExternModuleStore = ExternModuleStore() +"""Singleton of `ExternModuleStore`.""" + + +def enable(target: Target, flashinfer: bool, faster_transformer: bool) -> None: + """Enable external modules. It should be called before any compilation happens.""" + global STORE # pylint: disable=global-statement + STORE = ExternModuleStore( + configured=False, + target=target, + flashinfer=flashinfer, + faster_transformer=faster_transformer, + ) + + +def get_store() -> ExternModuleStore: + """Get the global store of external modules.""" + return STORE + + +def configure() -> None: + """Configure external modules with extra parameters. It should be called during a model's + `forward` method is invoked. + + Parameters + ---------- + """ + store = get_store() + if store.configured: + return + store.configured = True + if store.flashinfer or store.faster_transformer: + assert store.target.kind.name == "cuda" diff --git a/python/mlc_chat/op/ft_gemm.py b/python/mlc_chat/op/ft_gemm.py new file mode 100644 index 0000000..0a4edc6 --- /dev/null +++ b/python/mlc_chat/op/ft_gemm.py @@ -0,0 +1,135 @@ +"""Operators enabled by external modules.""" +import operator +from functools import reduce +from typing import Optional + +from tvm.relax.frontend import nn +from tvm.relax.frontend.nn import op + + +def faster_transformer_dequantize_gemm( # pylint: disable=too-many-arguments + x: nn.Tensor, + weight: nn.Tensor, + scale: nn.Tensor, + bias: Optional[nn.Tensor] = None, + activation: Optional[str] = None, + group_size: Optional[int] = None, +): + """ + Faster Transformer dequantize gemm inference with CutlassFpAIntB + + Parameters + ---------- + x : nn.Tensor + The input tensor, with shape of [*m, k]. + + weight : nn.Tensor + The quantized weight data tensor, with shape of [k, n // num_elem_per_storage]. + + scale : nn.Tensor + The quantized weight scale tensor, with shape of [k // group_size, n]. + + bias : Optional[nn.Tensor] + The optional bias for matmul, with shape broadcastable to [*m, n]. + + group_size : Optional[int] + The optional group size. If not set, then using k as group size. + + Returns + ------ + ret: nn.Tensor + The output tensor of deocde matmul, with shape of [*m, n]. + """ + assert x.dtype == "float16" and x.ndim >= 1 + assert weight.ndim == 2 + assert scale.dtype == "float16" and scale.ndim == 2 + assert x.shape[-1] == weight.shape[0], ( + "Reduction dimension mismatched between x and weight, " + f"{x.shape[-1]} vs {weight.shape[0]}." + ) + assert activation in [ + None, + "relu", + "gelu", + "silu", + "identity", + ], "Supported activations are [None, 'identity', 'gelu', 'silu', 'relu']." + activation = activation if activation else "identity" + m = reduce(operator.mul, x.shape[:-1], 1) + k = x.shape[-1] + n = scale.shape[1] + + if not group_size: + group_size = k + + if bias: + assert bias.dtype == "float16" and bias.ndim >= 1 + bias_stride = ( + bias.shape[-1] + if bias and not reduce(operator.mul, bias.shape, 1) == bias.shape[-1] + else 0 + ) + return op.extern( + name="fastertransformer.gemm_fp16_int_bias", + args=[ + x, + weight, + scale, + bias, + activation, + m, + n, + k, + group_size, + bias_stride, + ], + out=nn.Tensor.placeholder((*x.shape[:-1], scale.shape[1]), dtype="float16"), + ) + return op.extern( + name="fastertransformer.gemm_fp16_int", + args=[x, weight, scale, activation, m, n, k, group_size], + out=nn.Tensor.placeholder((*x.shape[:-1], scale.shape[1]), dtype="float16"), + ) + + +def faster_transformer_moe_gemm( # pylint: disable=too-many-arguments + x: nn.Tensor, + weight: nn.Tensor, + total_rows_before: nn.Tensor, +): + """ + Faster Transformer moe gemm inference with CutlassFpAIntB + + Parameters + ---------- + x : nn.Tensor + The input tensor, with shape of [*m, k]. + + weight : nn.Tensor + The weight data tensor, with shape of [num_experts, n, k]. + + total_rows_before : nn.Tensor + The total rows before tensor the current expert, with shape of [num_experts]. This is the + same as the indptr excluding the first zero element. + + Returns + ------ + ret: nn.Tensor + The output tensor of deocde matmul, with shape of [*m, n]. + """ + assert x.dtype == "float16" and x.ndim >= 1 + assert weight.dtype == "float16" and weight.ndim == 3 + assert x.shape[-1] == weight.shape[-1], ( + "Reduction dimension mismatched between x and weight, " + f"{x.shape[-1]} vs {weight.shape[-1]}." + ) + m = reduce(operator.mul, x.shape[:-1], 1) + num_experts = weight.shape[0] + n = weight.shape[1] + k = x.shape[-1] + + return op.extern( + name="fastertransformer.moe_gemm_fp16_fp16", + args=[x, weight, total_rows_before, m, n, k, num_experts], + out=nn.Tensor.placeholder((*x.shape[:-1], n), dtype="float16"), + ) diff --git a/python/mlc_chat/op/moe_matmul.py b/python/mlc_chat/op/moe_matmul.py new file mode 100644 index 0000000..169140a --- /dev/null +++ b/python/mlc_chat/op/moe_matmul.py @@ -0,0 +1,555 @@ +"""Mixture of Experts operators""" + +from tvm import DataType, tir +from tvm.relax.frontend.nn import Tensor, op +from tvm.script import tir as T + +# mypy: disable-error-code="attr-defined,valid-type,name-defined" +# pylint: disable=too-many-locals,invalid-name,too-many-arguments,too-many-statements + + +def gemv(x: Tensor, w: Tensor, indptr: Tensor) -> Tensor: + """GEMV for project-in (e1-e3) or project-out (e2) in MLP. + + Parameters + ---------- + x : Tensor + For project-in, the input tensor of shape (1, in_features); and for project-out, the input + shape is (experts_per_tok, in_features), where `experts_per_tok` is the number of activated + experts per token. + + w : Tensor + The weight tensor of shape (local_experts, out_features, in_features), where `local_experts` + is the total number of experts. + + indptr : Tensor + The index pointer tensor of shape (1, experts_per_tok), where `experts_per_tok` is the + number of activated experts per token. + + Returns + ------- + out : Tensor + The output tensor of shape (experts_per_tok, out_features), where `experts_per_tok` is the + number of activated experts per token. + """ + (local_experts, out_features, in_features), dtype = w.shape, w.dtype + _, experts_per_tok = indptr.shape + x_leading_dim, _ = x.shape + + def access_x(x, e, j): + return x[0, j] if x_leading_dim == 1 else x[e, j] + + # NOTE: Currently it assumes x.dtype == w.dtype, but the constraint can be relaxed easily. + assert w.shape == [local_experts, out_features, in_features] and w.dtype == dtype + assert x.shape == [x_leading_dim, in_features] and x.dtype == dtype + assert indptr.shape == [1, experts_per_tok] and indptr.dtype == "int32" + assert x_leading_dim in [1, experts_per_tok] + + @T.prim_func(private=True) + def _func( + x: T.Buffer((x_leading_dim, in_features), dtype), + w: T.Buffer((local_experts, out_features, in_features), dtype), + indptr: T.Buffer((1, experts_per_tok), "int32"), + o: T.Buffer((experts_per_tok, out_features), dtype), + ): + T.func_attr({"op_pattern": 4, "tir.noalias": True}) # kOutEWiseFusable + for e in T.thread_binding(experts_per_tok, thread="blockIdx.y"): + with T.block("gemv_o"): + e = T.axis.spatial(experts_per_tok, e) + T.reads(x[:, :], w[indptr[0, e], :, :], indptr[0, e]) + T.writes(o[e, :]) + for i1, i2 in T.grid(out_features, in_features): + with T.block("gemv"): + i, j = T.axis.remap("SR", [i1, i2]) + with T.init(): + o[e, i] = T.cast(T.float16(0), dtype) + o[e, i] += access_x(x, e, j) * w[indptr[0, e], i, j] + + return op.tensor_ir_op( + _func, + "moe_gemv", + args=[x, w, indptr], + out=Tensor.placeholder([experts_per_tok, out_features], dtype), + ) + + +def dequantize_gemv( # pylint: disable=too-many-arguments + x: Tensor, + w: Tensor, + scale: Tensor, + indptr: Tensor, + quantize_dtype: str, + group_size: int, +) -> Tensor: + """GEMV for project-in (e1-e3) or project-out (e2) in MLP but the weight is quantized. + It needs to be dequantized before the GEMV computation. + + Parameters + ---------- + x : Tensor + For project-in, the input tensor of shape (1, in_features); and for project-out, the input + shape is (experts_per_tok, in_features), where `experts_per_tok` is the number of activated + experts per token. + + w : Tensor + The quantized weight tensor of shape (local_experts, out_features, in_features // n), + where n is the number of elements per storage dtype, e.g. if the storage dtype is uint32, + and the quantize dtype is int4, then n is 8. + `local_experts` is the total number of experts including activated and non-active ones. + + scale : Tensor + The scale tensor of shape (local_experts, out_features, in_features // group_size), where + `local_experts` is the total number of experts including activated and non-active ones. + + indptr : Tensor + The index pointer tensor of shape (1, experts_per_tok), where `experts_per_tok` is the + number of activated experts per token. + + quantize_dtype : str + The quantize dtype of the weight tensor, which is usually int3, int4 or fp8, etc. + + group_size : int + The number of elements in each quantization group, e.g. 32 or 128. + + Returns + ------- + out : Tensor + The output tensor of shape (experts_per_tok, out_features), where `experts_per_tok` is the + number of activated experts per token. + """ + (x_leading_dim, in_features), model_dtype = x.shape, x.dtype + (local_experts, out_features, _), storage_dtype = w.shape, w.dtype + _, experts_per_tok = indptr.shape + quantize_dtype_bits = DataType(quantize_dtype).bits + num_elem_per_storage = DataType(storage_dtype).bits // quantize_dtype_bits + num_group = (in_features + group_size - 1) // group_size + num_storage = group_size // num_elem_per_storage * num_group + + def _dequantize(w, s, e, i, j): + tir_bin_mask = tir.const((2**quantize_dtype_bits) - 1, storage_dtype) + tir_max_int = tir.const((2 ** (quantize_dtype_bits - 1)) - 1, model_dtype) + w = w[e, i, j // num_elem_per_storage] + s = s[e, i, j // group_size] + shift = (j % num_elem_per_storage * quantize_dtype_bits).astype(storage_dtype) + w = tir.bitwise_and(tir.shift_right(w, shift), tir_bin_mask).astype(model_dtype) + return (w - tir_max_int) * s + + def access_x(x, e, j): + return x[0, j] if x_leading_dim == 1 else x[e, j] + + assert x.shape == [x_leading_dim, in_features] and x.dtype == model_dtype + assert w.shape == [local_experts, out_features, num_storage] and w.dtype == storage_dtype + assert scale.shape == [local_experts, out_features, num_group] and scale.dtype == model_dtype + assert indptr.shape == [1, experts_per_tok] and indptr.dtype == "int32" + assert x_leading_dim in [1, experts_per_tok] + + @T.prim_func(private=True) + def _func( + x: T.Buffer((x_leading_dim, in_features), model_dtype), + w: T.Buffer((local_experts, out_features, num_storage), storage_dtype), + scale: T.Buffer((local_experts, out_features, num_group), model_dtype), + indptr: T.Buffer((1, experts_per_tok), "int32"), + o: T.Buffer((experts_per_tok, out_features), model_dtype), + ): + T.func_attr({"op_pattern": 4, "tir.noalias": True}) # kOutEWiseFusable + for expert_id in T.thread_binding(experts_per_tok, thread="blockIdx.y"): + with T.block("gemv_o"): + e = T.axis.spatial(experts_per_tok, expert_id) + y = T.alloc_buffer((out_features, in_features), model_dtype) + for i1, i2 in T.grid(out_features, in_features): + with T.block("dequantize"): + i, j = T.axis.remap("SS", [i1, i2]) + y[i, j] = _dequantize(w, scale, indptr[0, e], i, j) + for i1, i2 in T.grid(out_features, in_features): + with T.block("gemv"): + i, j = T.axis.remap("SR", [i1, i2]) + with T.init(): + o[e, i] = T.cast(T.float16(0), model_dtype) + o[e, i] += access_x(x, e, j) * y[i, j] + + return op.tensor_ir_op( + _func, + "moe_dequantize_gemv", + args=[x, w, scale, indptr], + out=Tensor.placeholder([experts_per_tok, out_features], model_dtype), + ) + + +def group_gemm(x: Tensor, w: Tensor, indptr: Tensor): # pylint: disable=too-many-statements + """Group GEMM in MoE models. + + Parameters + ---------- + x : Tensor + Input tensor of shape (batch_size, in_features), where `batch_size` could be dynamic shape. + + w : Tensor + Weight tensor of shape (num_local_experts, out_features, in_features). + `w[i, :, :]` is the weight matrix for the `i`-th local expert. + + indptr : Tensor + Index pointer tensor of shape (num_local_experts + 1, ). + `x[indptr[a] : indptr[a + 1]]` is the input for the `i`-th local expert. + + Returns + ------- + out : Tensor + Output tensor of shape (batch_size, out_features). + """ + # NOTE: Currently it assumes x.dtype == w.dtype, but the constraint can be relaxed easily. + (num_local_experts, out_features, in_features), dtype = w.shape, w.dtype + + assert x.shape[1:] == [in_features] and x.dtype == dtype + assert indptr.shape == [num_local_experts + 1] and indptr.dtype == "int32" + + Ne, N, K = num_local_experts, out_features, in_features + BLK_M, BLK_N, BLK_K = 8, 128, 32 + TX, TY, CTA_COUNT = 8, 32, 1024 + VEC_X, VEC_W, VEC_O, VEC_DOT = 1, 1, 1, 1 + UNROLL = 64 + STORAGE_ALIGN = False + assert BLK_K % 8 == 0 + tiles_per_row = (N + BLK_N - 1) // BLK_N + zero = tir.const(0, dtype) + + @T.prim_func(private=True) + def _func( # pylint: disable=too-many-statements + var_x: T.handle, + var_w: T.handle, + var_indptr: T.handle, + var_o: T.handle, + ): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": True}) + B = T.int32(is_size_var=True) + X = T.match_buffer(var_x, (B, K), dtype) + W = T.match_buffer(var_w, (Ne, N, K), dtype) + indptr = T.match_buffer(var_indptr, (Ne + 1,), "int32") + O = T.match_buffer(var_o, (B, N), dtype) + + for _bx in T.thread_binding(CTA_COUNT, thread="blockIdx.x"): + with T.block("CTA"): + bx = T.axis.spatial(CTA_COUNT, _bx) + T.reads(indptr[:], X[:, :], W[:, :, :]) + T.writes(O[:, :]) + # pylint: disable=redefined-builtin + sum = T.alloc_buffer((2,), "int32", scope="local") + row = T.alloc_buffer((2,), "int32", scope="local") + cur_e = T.alloc_buffer((1,), "int32", scope="local") + tile_id = T.alloc_buffer((1,), "int32", scope="local") + # pylint: enable=redefined-builtin + sum[0] = 0 + sum[1] = T.ceildiv(indptr[1] - indptr[0], BLK_M) * tiles_per_row + row[0] = 0 + row[1] = indptr[1] - indptr[0] + cur_e[0] = 0 + tile_id[0] = bx + while T.tvm_thread_invariant(cur_e[0] < Ne): # pylint: disable=no-member + # move to the current group + while sum[1] <= tile_id[0] and cur_e[0] < Ne: + cur_e[0] += 1 + if cur_e[0] < Ne: + e: T.int32 = cur_e[0] + delta: T.int32 = indptr[e + 1] - indptr[e] + sum[0] = sum[1] + sum[1] += T.ceildiv(delta, BLK_M) * tiles_per_row + row[0] = row[1] + row[1] += delta + # sync threads to make sure all threads have the same tile position + T.tvm_storage_sync("shared") + if T.tvm_thread_invariant(cur_e[0] < Ne): # pylint: disable=no-member + # fetch current tile position + e: T.int32 = cur_e[0] # type: ignore[no-redef] + num_tiles: T.int32 = tile_id[0] - sum[0] + m_offset: T.int32 = BLK_M * T.floordiv(num_tiles, tiles_per_row) + row[0] + n_offset: T.int32 = BLK_N * T.floormod(num_tiles, tiles_per_row) + with T.block("gemm"): + T.reads( + row[1], + X[m_offset : m_offset + BLK_M, :], + W[e, n_offset : n_offset + BLK_N, :], + ) + T.writes(O[m_offset : m_offset + BLK_M, n_offset : n_offset + BLK_N]) + X_tile = T.alloc_buffer((BLK_M, K), dtype, scope="shared") + W_tile = T.alloc_buffer((BLK_N, K), dtype, scope="shared") + O_tile = T.alloc_buffer((BLK_M, BLK_N), dtype, scope="local") + for a0, a1 in T.grid(BLK_M, K): + with T.block("X_shared"): + i, j = T.axis.remap("SS", [a0, a1]) + X_tile[i, j] = T.if_then_else( + m_offset + i < row[1], + X[m_offset + i, j], + zero, + ) + for a0, a1 in T.grid(BLK_N, K): + with T.block("W_shared"): + i, j = T.axis.remap("SS", [a0, a1]) + W_tile[i, j] = T.if_then_else( + n_offset + i < N, + W[e, n_offset + i, j], + zero, + ) + for a0, a1, a2 in T.grid(BLK_M, BLK_N, K): + with T.block("compute"): + i, j, k = T.axis.remap("SSR", [a0, a1, a2]) + with T.init(): + O_tile[i, j] = zero + O_tile[i, j] += X_tile[i, k] * W_tile[j, k] + for a0, a1 in T.grid(BLK_M, BLK_N): + with T.block("store"): + i, j = T.axis.remap("SS", [a0, a1]) + if m_offset + i < row[1] and n_offset + j < N: + O[m_offset + i, n_offset + j] = O_tile[i, j] + # move to next tile + tile_id[0] += CTA_COUNT + + def _schedule(): + sch = tir.Schedule(_func) + + def _cooperative_fetch(block, vec_len): + num_loops = len(sch.get_loops(block)) + sch.compute_at(block, ko, preserve_unit_loops=True) + loops = sch.get_loops(block)[-num_loops:] + ty, tx, _, vec = sch.split( + sch.fuse(*loops), + factors=[TY, TX, None, vec_len], + ) + sch.vectorize(vec) + sch.bind(ty, "threadIdx.y") + sch.bind(tx, "threadIdx.x") + if STORAGE_ALIGN: + sch.storage_align(block, 0, axis=1, factor=8, offset=vec_len) + return block + + main_block = sch.get_block("compute") + x, y, k = sch.get_loops(main_block) + ty, yi = sch.split(y, [TY, None]) + tx, xi, vec_c = sch.split(x, [TX, None, VEC_DOT]) + ko, ki = sch.split(k, factors=[None, BLK_K]) + sch.reorder(ty, tx, ko, ki, yi, xi, vec_c) + sch.bind(ty, "threadIdx.y") + sch.bind(tx, "threadIdx.x") + sch.vectorize(vec_c) + if UNROLL > 0: + sch.annotate(tx, ann_key="pragma_auto_unroll_max_step", ann_val=UNROLL) + sch.annotate(tx, ann_key="pragma_unroll_explicit", ann_val=1) + l2g = sch.get_block("store") + sch.reverse_compute_at(l2g, tx, preserve_unit_loops=True) + _, v = sch.split(sch.get_loops(l2g)[-1], [None, VEC_O]) + sch.vectorize(v) + _cooperative_fetch(sch.get_block("X_shared"), vec_len=VEC_X) + _cooperative_fetch(sch.get_block("W_shared"), vec_len=VEC_W) + sch.decompose_reduction(main_block, ko) + return sch.mod["main"] + + return op.tensor_ir_op( + _schedule(), + "group_gemm", + args=[x, w, indptr], + out=Tensor.placeholder([x.shape[0], out_features], dtype), + ) + + +def dequantize_group_gemm( + x: Tensor, + w: Tensor, + scale: Tensor, + indptr: Tensor, + quantize_dtype: str, + indptr_dtype: str, + group_size: int, +): + """Group GEMM in MoE models but the weight is quantized. + + Parameters + ---------- + x : Tensor + Input tensor of shape (batch_size, in_features), where `batch_size` could be dynamic shape. + + w : Tensor + Weight tensor of shape (num_local_experts, out_features, in_features // n), where n is the + number of elements per storage dtype, e.g. if the storage dtype is uint32, and the quantize + dtype is int4, then n is 8. + + scale : Tensor + The scale tensor of shape (num_local_experts, out_features, in_features // group_size). + + indptr : Tensor + Index pointer tensor of shape (num_local_experts + 1, ). `x[indptr[a] : indptr[a + 1]]` is + the input for the `i`-th local expert. + + group_size : int + The number of elements in each quantization group, e.g. 32 or 128. + + quantize_dtype : str + The quantize dtype of the weight tensor, which is usually int3, int4 or fp8, etc. + + indptr_dtype : str + The dtype of the index pointer tensor, which can be int32 or int64. + + Returns + ------- + out : Tensor + Output tensor of shape (batch_size, out_features). + """ + (_, in_features), model_dtype = x.shape, x.dtype + (num_local_experts, out_features, _), storage_dtype = w.shape, w.dtype + quantize_dtype_bits = DataType(quantize_dtype).bits + num_elem_per_storage = DataType(storage_dtype).bits // quantize_dtype_bits + num_group = (in_features + group_size - 1) // group_size + num_storage = group_size // num_elem_per_storage * num_group + + def _dequantize(w, s, e, i, j): + tir_bin_mask = tir.const((1 << quantize_dtype_bits) - 1, storage_dtype) + tir_max_int = tir.const((2 ** (quantize_dtype_bits - 1)) - 1, model_dtype) + w = w[e, i, j // num_elem_per_storage] + s = s[e, i, j // group_size] + shift = (j % num_elem_per_storage * quantize_dtype_bits).astype(storage_dtype) + w = tir.bitwise_and(tir.shift_right(w, shift), tir_bin_mask).astype(model_dtype) + return (w - tir_max_int) * s + + Ne, N, K = num_local_experts, out_features, in_features + BLK_M, BLK_N, BLK_K = 8, 128, 32 + TX, TY, CTA_COUNT = 8, 32, 1024 + VEC_X, VEC_W, VEC_O, VEC_DOT = 1, 1, 1, 1 + UNROLL = 64 + STORAGE_ALIGN = False + assert BLK_K % 8 == 0 + tiles_per_row = (N + BLK_N - 1) // BLK_N + zero = tir.const(0, model_dtype) + if indptr_dtype == "int64": + indptr = op.pad(indptr, [1, 0], "constant", 0) + + @T.prim_func(private=True) + def _func( + var_x: T.handle, + w: T.Buffer((Ne, N, num_storage), storage_dtype), + scale: T.Buffer((Ne, N, num_group), model_dtype), + indptr: T.Buffer((Ne + 1,), indptr_dtype), + var_o: T.handle, + ): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": True}) + B = T.int32(is_size_var=True) + X = T.match_buffer(var_x, (B, K), model_dtype) + O = T.match_buffer(var_o, (B, N), model_dtype) + for _bx in T.thread_binding(CTA_COUNT, thread="blockIdx.x"): + with T.block("CTA"): + bx = T.axis.spatial(CTA_COUNT, _bx) + T.reads(X[:, :], w[:, :, :], scale[:, :, :], indptr[:]) + T.writes(O[:, :]) + # pylint: disable=redefined-builtin + sum = T.alloc_buffer((2,), indptr_dtype, scope="local") + row = T.alloc_buffer((2,), indptr_dtype, scope="local") + cur_e = T.alloc_buffer((1,), indptr_dtype, scope="local") + tile_id = T.alloc_buffer((1,), indptr_dtype, scope="local") + # pylint: enable=redefined-builtin + sum[0] = 0 + sum[1] = T.ceildiv(indptr[1] - indptr[0], BLK_M) * tiles_per_row + row[0] = 0 + row[1] = indptr[1] - indptr[0] + cur_e[0] = 0 + tile_id[0] = bx + while T.tvm_thread_invariant(cur_e[0] < Ne): # pylint: disable=no-member + # move to the current group + while sum[1] <= tile_id[0] and cur_e[0] < Ne: + cur_e[0] += 1 + if cur_e[0] < Ne: + e = cur_e[0] + delta = indptr[e + 1] - indptr[e] + sum[0] = sum[1] + sum[1] += T.ceildiv(delta, BLK_M) * tiles_per_row + row[0] = row[1] + row[1] += delta + # sync threads to make sure all threads have the same tile position + T.tvm_storage_sync("shared") + if T.tvm_thread_invariant(cur_e[0] < Ne): # pylint: disable=no-member + # fetch current tile position + e = cur_e[0] # type: ignore[no-redef] + num_tiles = tile_id[0] - sum[0] + m_offset = T.floordiv(num_tiles, tiles_per_row) * BLK_M + row[0] + n_offset = T.floormod(num_tiles, tiles_per_row) * BLK_N + with T.block("gemm"): + T.reads( + row[1], + X[m_offset : m_offset + BLK_M, :], + w[e, n_offset : n_offset + BLK_N, :], + scale[e, n_offset : n_offset + BLK_N, :], + ) + T.writes(O[m_offset : m_offset + BLK_M, n_offset : n_offset + BLK_N]) + X_tile = T.alloc_buffer((BLK_M, K), model_dtype, scope="shared") + W_tile = T.alloc_buffer((BLK_N, K), model_dtype, scope="shared") + O_tile = T.alloc_buffer((BLK_M, BLK_N), "float32", scope="local") + for a0, a1 in T.grid(BLK_M, K): + with T.block("X_shared"): + i, j = T.axis.remap("SS", [a0, a1]) + X_tile[i, j] = T.if_then_else( + m_offset + i < row[1], + X[m_offset + i, j], + zero, + ) + for a0, a1 in T.grid(BLK_N, K): + with T.block("W_shared"): + i, j = T.axis.remap("SS", [a0, a1]) + W_tile[i, j] = T.if_then_else( + n_offset + i < N, + _dequantize(w, scale, e, n_offset + i, j), + zero, + ) + for a0, a1, a2 in T.grid(BLK_M, BLK_N, K): + with T.block("compute"): + i, j, k = T.axis.remap("SSR", [a0, a1, a2]) + with T.init(): + O_tile[i, j] = zero + O_tile[i, j] += X_tile[i, k] * W_tile[j, k] + for a0, a1 in T.grid(BLK_M, BLK_N): + with T.block("store"): + i, j = T.axis.remap("SS", [a0, a1]) + if m_offset + i < row[1] and n_offset + j < N: + O[m_offset + i, n_offset + j] = O_tile[i, j] + # move to next tile + tile_id[0] += CTA_COUNT + + def _schedule(): + sch = tir.Schedule(_func) + + def _cooperative_fetch(block, vec_len): + num_loops = len(sch.get_loops(block)) + sch.compute_at(block, ko, preserve_unit_loops=True) + loops = sch.get_loops(block)[-num_loops:] + ty, tx, _, vec = sch.split( + sch.fuse(*loops), + factors=[TY, TX, None, vec_len], + ) + sch.vectorize(vec) + sch.bind(ty, "threadIdx.y") + sch.bind(tx, "threadIdx.x") + if STORAGE_ALIGN: + sch.storage_align(block, 0, axis=1, factor=8, offset=vec_len) + return block + + main_block = sch.get_block("compute") + x, y, k = sch.get_loops(main_block) + ty, yi = sch.split(y, [TY, None]) + tx, xi, vec_c = sch.split(x, [TX, None, VEC_DOT]) + ko, ki = sch.split(k, factors=[None, BLK_K]) + sch.reorder(ty, tx, ko, ki, yi, xi, vec_c) + sch.bind(ty, "threadIdx.y") + sch.bind(tx, "threadIdx.x") + sch.vectorize(vec_c) + if UNROLL > 0: + sch.annotate(tx, ann_key="pragma_auto_unroll_max_step", ann_val=UNROLL) + sch.annotate(tx, ann_key="pragma_unroll_explicit", ann_val=1) + l2g = sch.get_block("store") + sch.reverse_compute_at(l2g, tx, preserve_unit_loops=True) + _, v = sch.split(sch.get_loops(l2g)[-1], [None, VEC_O]) + sch.vectorize(v) + _cooperative_fetch(sch.get_block("X_shared"), vec_len=VEC_X) + _cooperative_fetch(sch.get_block("W_shared"), vec_len=VEC_W) + sch.decompose_reduction(main_block, ko) + return sch.mod["main"] + + return op.tensor_ir_op( + _schedule(), + "dequantize_group_gemm", + args=[x, w, scale, indptr], + out=Tensor.placeholder([x.shape[0], out_features], model_dtype), + ) diff --git a/python/mlc_chat/op/moe_misc.py b/python/mlc_chat/op/moe_misc.py new file mode 100644 index 0000000..e97ef94 --- /dev/null +++ b/python/mlc_chat/op/moe_misc.py @@ -0,0 +1,408 @@ +"""Mixture of Experts operators""" +from functools import reduce +from typing import Tuple, Union + +from tvm import te, tir +from tvm.relax.frontend.nn import Tensor, op +from tvm.script import tir as T +from tvm.target import Target +from tvm.topi.cuda.scan import inclusive_scan +from tvm.topi.cuda.sort import topk as topi_topk + +# mypy: disable-error-code="attr-defined,name-defined" +# pylint: disable=line-too-long,too-many-locals,invalid-name + + +def moe_sum(x: Tensor, dim: int) -> Tensor: + """Compute the sum of the input tensor along the given axis. It is specialized for the MoE + case where `x.ndim == 3` and `x.shape[1] == num_experts_per_tok (which is 2)`. + """ + if x.ndim == 3 and x.shape[1] == 2: + return op.tensor_expr_op( + lambda x: te.compute( + (x.shape[0], x.shape[2]), + lambda i, j: x[i, 0, j] + x[i, 1, j], + name="sum_2", + ), + "sum", + args=[x], + ) + return op.sum(x, axis=dim) + + +def gating_softmax_topk(x: Tensor, k: int) -> Tuple[Tensor, Tensor]: + """Compute the softmax score, choose the top-k experts, and renormalize the selected scores. + + Parameters + ---------- + x : Tensor + The input tensor with shape [batch_size, num_local_experts]. + + k : int + The number of top elements to be selected, which is `num_experts_per_tok` in MoE. + + Returns + ------- + expert_weights: Tensor + The renormalized top-k expert scores with shape [batch_size, k]. + + expert_indices: Tensor + The top-k expert indices with shape [batch_size, k]. + """ + (batch_size, num_local_experts), dtype = x.shape, x.dtype + index_dtype = "int32" + + TX = 1024 + SCAN_LEN = 2 + + # specialized kernel for top 2 case + @T.prim_func(private=True) + def topk_softmax_func( + var_x: T.handle, + var_out: T.handle, + var_out_index: T.handle, + ) -> None: + T.func_attr({"tir.noalias": True, "tir.is_scheduled": True}) + batch_size = T.int64() + x = T.match_buffer(var_x, (batch_size, num_local_experts), dtype) + out = T.match_buffer(var_out, (batch_size, SCAN_LEN), dtype) + out_index = T.match_buffer(var_out_index, (batch_size, SCAN_LEN), index_dtype) + local_top_k = T.alloc_buffer((SCAN_LEN,), dtype=dtype, scope="local") + local_top_k_index = T.alloc_buffer((SCAN_LEN,), dtype=index_dtype, scope="local") + local_top_k_f32 = T.alloc_buffer((SCAN_LEN,), dtype="float32", scope="local") + local_top_k_max = T.alloc_buffer((1,), dtype="float32", scope="local") + for io in T.thread_binding(0, T.ceildiv(batch_size, TX), "blockIdx.x"): + for ii in T.thread_binding(0, TX, "threadIdx.x"): + with T.block("top_k"): + vi = T.axis.spatial(batch_size, io * TX + ii) + T.where(io * TX + ii < batch_size) + with T.block("init"): + local_top_k[0] = T.min_value(dtype) + local_top_k_index[0] = 0 + for k in range(num_local_experts): + with T.block("update"): + vk = T.axis.remap("S", [k]) + # N.B. This snippet is specialized for k = 2 + if x[vi, vk] > local_top_k[0]: + local_top_k[1] = local_top_k[0] + local_top_k_index[1] = local_top_k_index[0] + local_top_k[0] = x[vi, vk] + local_top_k_index[0] = vk + elif x[vi, vk] > local_top_k[1]: + local_top_k[1] = x[vi, vk] + local_top_k_index[1] = vk + for j in T.unroll(SCAN_LEN): + with T.block("cast"): + vj = T.axis.remap("S", [j]) + local_top_k_f32[vj] = T.cast(local_top_k[vj], "float32") + with T.block("max"): + local_top_k_max[0] = T.max(local_top_k_f32[0], local_top_k_f32[1]) + for j in T.unroll(SCAN_LEN): + with T.block("output"): + vj = T.axis.remap("S", [j]) + out[vi, vj] = T.cast( + T.exp(local_top_k_f32[j] - local_top_k_max[0]) + / ( + T.exp(local_top_k_f32[0] - local_top_k_max[0]) + + T.exp(local_top_k_f32[1] - local_top_k_max[0]) + ), + dtype, + ) + out_index[vi, vj] = local_top_k_index[vj] + + if k == 2: + return op.tensor_ir_op( + topk_softmax_func, + "top2_softmax", + args=[x], + out=( + Tensor.placeholder([batch_size, 2], dtype), + Tensor.placeholder([batch_size, 2], index_dtype), + ), + ) + expert_score, expert_indices = op.tensor_expr_op(topi_topk, "topk", args=[x, k, -1, "both", False, index_dtype]) # type: ignore[list-item] + expert_score = op.softmax(expert_score.astype("float32"), axis=-1).astype(dtype) + return expert_score, expert_indices + + +def moe_cumsum(expert_indices: Tensor, num_local_experts: int) -> Tensor: + """An operator that returns the cumsum array in MoE. + + The input `expert_indices` of shape [batch_size, experts_per_tok] indicates the indices of + the activated experts for each instance in a batch. This operator first converts it to + `expert_mask`, a boolean mask with shape [batch_size, num_local_experts], and then computes + cumsum over the transpose-then-flattened array of `expert_mask`. + + A position `(e, b)` in the result `cumsum`, where `e` is the expert id and `b` is the batch id, + indicates a shuffling plan that moves the `b`-th instance that ensures the inputs to the `e`-th + expert is contiguous. + + Parameters + ---------- + expert_indices : Tensor + The topk indices with shape [batch_size, experts_per_tok], int32, where + `experts_per_tok` is the number of activated experts. + + num_local_experts : int + The number of totally experts. + + Returns + ------- + cumsum: Tensor + The cumsum result with shape [num_local_experts * batch_size], int32. + + Example + ------- + Suppose `batch_size` is 4, `experts_per_tok` is 2, the total number of experts is 6, and + `expert_indices` is the 2D tensor below: + + [ + [0, 1], + [1, 2], + [3, 4], + [2, 5], + ] + + , then the `expert_mask` is a tensor of shape [batch_size, num_local_experts] below: + + [ + [1, 1, 0, 0, 0, 0], + [0, 1, 1, 0, 0, 0], + [0, 0, 0, 1, 1, 0], + [0, 0, 1, 0, 0, 1], + ] + + . The result cumsum of the transposed `expert_mask` is a flattened version of 2D tensor below: + + [ + [1, 1, 1, 1], + [2, 3, 3, 3], + [3, 4, 4, 5], + [5, 5, 6, 6], + [6, 6, 7, 7], + [7, 7, 7, 8], + ] + """ + batch_size, experts_per_tok = expert_indices.shape + expert_mask = ( + op.tensor_expr_op( # pylint: disable=too-many-function-args + lambda expert_indices: te.compute( + (batch_size, num_local_experts), + lambda i, j: tir.expr.Select( + reduce( + tir.Or, + [expert_indices[i, k] == j for k in range(experts_per_tok)], + ), + true_value=tir.const(1, "int32"), + false_value=tir.const(0, "int32"), + ), + ), + "expert_mask", + args=[expert_indices], + ) + .permute_dims(1, 0) + .reshape(batch_size * num_local_experts) + ) + with Target.current(allow_none=True) or Target( + { + "kind": "cuda", + "max_num_threads": 1024, + "arch": "sm_50", + } + ): + return op.tensor_expr_op(inclusive_scan, "cumsum", args=[expert_mask, 0, "int32"]) # type: ignore[list-item] + + +def get_indices(cumsum: Tensor, expert_indices: Tensor) -> Tuple[Tensor, Tensor]: + """Returns a 1D tensor of indices that represents the shuffling plan for each instance in a + batch, so that the inputs to each experts are contiguous and the indices for reverse permutation + (scatter) to the original order. + + If `reverse_indices[i] = (b, j)`, it means the `b`-th instance in the batch should be moved to the + `i`-th position in shuffling, and `j` doesn not matter only meaning `expert_indices[b, j]` + corresponds to the expert at position `i` in the shuffling plan. We also compute + `token_indices[i] = b` so that we can use `relax.op.take` for shuffling. + + Effectively it is equivalent to the following Python code: + + .. code-block:: python + + for b in range(batch_size): + for j in range(experts_per_tok): + e = expert_indices[b, j] + reverse_indices[cumsum[e * batch_size + b] - 1] = b * experts_per_tok + j + token_indices[cumsum[e * batch_size + b] - 1 + + Parameters + ---------- + cumsum : Tensor + A flattened 1D tensor whose original shape is [experts_per_tok, batch_size]. + + expert_indices : Tensor + The indices of the experts with shape [batch_size, experts_per_tok]. + + Returns + ------- + reverse_indices : Tensor + The indices for scattering with shape [batch_size * experts_per_tok]. + + token_indices : Tensor + The indices for shuffling with shape [batch_size * experts_per_tok]. + """ + TX = 1024 + batch_size, experts_per_tok = expert_indices.shape + + @T.prim_func(private=True) + def _func( + var_cumsum: T.handle, + var_expert_indices: T.handle, + var_reverse_indices: T.handle, + var_token_indices: T.handle, + ): + T.func_attr({"tir.is_scheduled": 1, "tir.noalias": True}) + batch_size = T.SizeVar("batch_size", "int32") + cumsum_len = T.SizeVar("cumsum_len", "int32") # [experts_per_tok * batch_size] + cumsum = T.match_buffer(var_cumsum, [cumsum_len], "int32") + expert_indices = T.match_buffer(var_expert_indices, [batch_size, experts_per_tok], "int32") + reverse_indices = T.match_buffer( + var_reverse_indices, [batch_size * experts_per_tok], "int32" + ) + token_indices = T.match_buffer(var_token_indices, [batch_size * experts_per_tok], "int32") + for bj_o in T.thread_binding(0, T.ceildiv(batch_size * experts_per_tok, TX), "blockIdx.x"): + for bj_i in T.thread_binding(0, TX, "threadIdx.x"): + with T.block("indices"): + T.reads(expert_indices[:, :], cumsum[:]) + T.writes(reverse_indices[:], token_indices[:]) + if bj_o * TX + bj_i < batch_size * experts_per_tok: + b: T.int32 = T.floordiv(bj_o * TX + bj_i, experts_per_tok) + j: T.int32 = T.floormod(bj_o * TX + bj_i, experts_per_tok) + e: T.int32 = expert_indices[b, j] + reverse_indices[cumsum[e * batch_size + b] - 1] = b * experts_per_tok + j + token_indices[cumsum[e * batch_size + b] - 1] = b + + return op.tensor_ir_op( + _func, + "get_indices", + args=[cumsum, expert_indices], + out=[Tensor.placeholder([batch_size * experts_per_tok], "int32") for _ in range(2)], + ) + + +def get_indptr( + cumsum: Tensor, + num_local_experts: int, + batch_size: Union[int, tir.Var], + inclusive: bool, + out_dtype: str, +) -> Tensor: + """Extract the `indptr` array from MoE cumsum array. The MoE cumsum array is a flattened tensor + whose original shape is [num_local_experts, batch_size], and the `indptr` array is a 1D tensor + of length `num_local_experts + 1`. The range `[indptr[i], indptr[i + 1])` indicates instances in + the batch that corresponds to the `i`-th expert. + + Effectively, this operator is equivalent to the following numpy code: + + .. code-block:: python + + indptr = np.zeros(num_local_experts + 1, dtype=np.int32) + indptr[0] = 0 + for i in range(1, num_local_experts + 1): + indptr[i] = cumsum[i * batch_size - 1] + return indptr + + Parameters + ---------- + cumsum : Tensor + The prefix sum of the sparse array with shape [batch_size * num_local_experts], int32. + + num_local_experts : int + The number of experts. + + batch_size : int | tir.Var + The batch size. Note that the batch size here refers to `batch_size * seq_len` in MoE, + and we name is `batch_size` for simplicity here only because the two dimensions are fused + in Mixtral. + + inclusive : bool + Whether to compute inclusive or exclusive prefix sum as the indptr. If `inclusive` is False, + the 0-th element of the `indptr` array, which always equals to 0, will be omitted. + + out_dtype : str + The output dtype. + + Returns + ------- + indptr : Tensor + The `indptr` array with shape [num_local_experts + 1] if `inclusive` is True, otherwise + [num_local_experts]. The `indptr` array is of type `out_dtype`. + """ + + out_shape = [num_local_experts if inclusive else num_local_experts + 1] + + @T.prim_func(private=True) + def _func_exclusive(var_cumsum: T.handle, var_indptr: T.handle, batch_size: T.int32): + T.func_attr({"tir.noalias": True}) + cumsum = T.match_buffer(var_cumsum, shape=[batch_size * num_local_experts], dtype="int32") + indptr = T.match_buffer(var_indptr, shape=out_shape, dtype=out_dtype) + for vi in T.serial(0, out_shape[0]): + with T.block("indptr"): + i = T.axis.spatial(out_shape[0], vi) + indptr[i] = T.Select(i > 0, cumsum[i * batch_size - 1], T.int32(0)) + + @T.prim_func(private=True) + def _func_inclusive(var_cumsum: T.handle, var_indptr: T.handle, batch_size: T.int32): + T.func_attr({"tir.noalias": True}) + cumsum = T.match_buffer(var_cumsum, shape=[batch_size * num_local_experts], dtype="int32") + indptr = T.match_buffer(var_indptr, shape=out_shape, dtype=out_dtype) + for vi in T.serial(0, out_shape[0]): + with T.block("indptr"): + i = T.axis.spatial(out_shape[0], vi) + indptr[i] = cumsum[(i + 1) * batch_size - 1] + + assert cumsum.ndim == 1 + return op.tensor_ir_op( + _func_inclusive if inclusive else _func_exclusive, + "get_expert_instance_indptr", + args=[cumsum, batch_size], # type: ignore[list-item] + out=Tensor.placeholder(out_shape, out_dtype), + ) + + +def scatter_output(x: Tensor, indices: Tensor) -> Tensor: + """Scatter the output of MoE experts back to the original positions. + + Parameters + ---------- + x : Tensor + The output of MoE experts with shape [batch_size * num_experts_per_tok, hidden_size]. + + indices : Tensor + The indices of the experts with shape [batch_size * num_experts_per_tok]. + + Returns + ------- + out : Tensor + The output of MoE experts with shape [batch_size * num_experts_per_tok, hidden_size]. + """ + dtype = x.dtype + + @T.prim_func(private=True) + def _func(var_x: T.handle, var_indices: T.handle, var_out: T.handle): + T.func_attr({"tir.noalias": True}) + hidden_size = T.int64() + indices_len = T.int64() + x = T.match_buffer(var_x, [indices_len, hidden_size], dtype) + indices = T.match_buffer(var_indices, [indices_len], "int32") + out = T.match_buffer(var_out, [indices_len, hidden_size], dtype) + for i in T.serial(0, indices_len): + for j in T.serial(0, hidden_size): + with T.block("scatter"): + vi, vj = T.axis.remap("SS", [i, j]) + out[indices[vi], vj] = x[vi, vj] + + return op.tensor_ir_op( + _func, + "scatter_output", + args=[x, indices], + out=Tensor.placeholder(x.shape, dtype), + ) diff --git a/python/mlc_chat/op/position_embedding.py b/python/mlc_chat/op/position_embedding.py new file mode 100644 index 0000000..12bdaaa --- /dev/null +++ b/python/mlc_chat/op/position_embedding.py @@ -0,0 +1,376 @@ +"""Operators for positional embeddings, e.g. RoPE.""" + +from typing import Optional, Tuple + +from tvm import tir +from tvm.relax.frontend.nn import Tensor, op +from tvm.script import tir as T +from tvm.target import Target + +# pylint: disable=invalid-name + + +def rope_freq(s: tir.Var, d: tir.Var, d_range: int, theta: float, dtype: str): + """Compute the inverse frequency of RoPE and then return the cosine and sine of it. + + Parameters + ---------- + s : tir.Var + The position index. + + d : tir.Var + The dimension index. + + d_range : int + The maximum dimension index. + + theta : float + The theta value in RoPE, which controls the frequency. + + dtype : str + The data type of the output. + + Returns + ------- + cos_freq : Tensor + The cosine of the inverse frequency. + + sin_freq : Tensor + The sine of the inverse frequency. + """ + freq = s / tir.power(theta, d * 2 % d_range / tir.const(d_range, "float32")) + cos_freq = tir.cos(freq).astype(dtype) + sin_freq = tir.sin(freq).astype(dtype) + return cos_freq, sin_freq + + +# mypy: disable-error-code="attr-defined" + + +def llama_rope( # pylint: disable=too-many-arguments + qkv: Tensor, + total_seq_len: tir.Var, + theta: float, + num_q_heads: int, + num_kv_heads: int, + scale: float = 1.0, + rotary_dim: Optional[int] = None, +) -> Tuple[Tensor, Tensor, Tensor]: + """Llama-style RoPE. Given a fused QKV tensor, it returns three tensors, Q, K, and V, where Q + and K are rotated by RoPE while V remains unchanged. + + Parameters + ---------- + qkv : Tensor + The fused QKV tensor of shape: [batch_size, seq_len, #q_heads + #kv_heads * 2, head_dim] + + total_seq_len : tir.Var + The total sequence length after being concatenated with KVCache. It is used to compute the + offset of RoPE. + + theta : float + The theta value, or "base" in RoPE, which controls the frequency. + + scale : float + The RoPE scaling factor. + + num_q_heads : int + The number of query heads. + + num_kv_heads : int + The number of key/value heads. It differs from `num_q_heads` in group-query attention. + + rotary_dim : Optional[int] + The number of dimensions in the embedding that RoPE is applied to. By default, the + rotary_dim is the same as head_dim. + + Returns + ------- + q : Tensor + The query tensor of shape [batch_size, seq_len, #q_heads, head_dim] w/ RoPE applied + + k : Tensor + The key tensor of shape [batch_size, seq_len, #kv_heads, head_dim] w/ RoPE applied + + v : Tensor + The value tensor of shape [batch_size, seq_len, #kv_heads, head_dim] w/o RoPE applied + """ + _, _, fused_heads, head_dim = qkv.shape + assert fused_heads == num_q_heads + num_kv_heads * 2 + if rotary_dim is None: + rotary_dim = head_dim + dtype = qkv.dtype + scale = tir.const(scale, dtype) + + def _rope( # pylint: disable=too-many-arguments + x: T.Buffer, + b: tir.Var, + s: tir.Var, + h: tir.Var, + d: tir.Var, + offset: tir.Var, + ): + cos_freq, sin_freq = rope_freq((s + offset) * scale, d, rotary_dim, theta, dtype) + cos = cos_freq * x[b, s, h, d] + sin = sin_freq * tir.if_then_else( + d < rotary_dim // 2, + -x[b, s, h, d + rotary_dim // 2], + x[b, s, h, d - rotary_dim // 2], + ) + return cos + sin + + @T.prim_func(private=True) + def fused_rope( # pylint: disable=too-many-locals + var_qkv: T.handle, + var_q: T.handle, + var_k: T.handle, + var_v: T.handle, + total_seq_len: T.int64, + ): + T.func_attr( + { + "op_pattern": 8, # 2 means injective, 8 means opaque + "tir.noalias": T.bool(True), + } + ) + batch_size = T.int64() + seq_len = T.int64() + qkv = T.match_buffer(var_qkv, (batch_size, seq_len, fused_heads, head_dim), dtype) + q = T.match_buffer(var_q, (batch_size, seq_len, num_q_heads, head_dim), dtype) + k = T.match_buffer(var_k, (batch_size, seq_len, num_kv_heads, head_dim), dtype) + v = T.match_buffer(var_v, (batch_size, seq_len, num_kv_heads, head_dim), dtype) + for iters in T.grid(batch_size, seq_len, fused_heads, head_dim): + with T.block("llama_fused_rope"): + b, s, h, d = T.axis.remap("SSSS", iters) + if h < num_q_heads: + q[b, s, h, d] = T.if_then_else( + d < rotary_dim, + _rope(qkv, b, s, h, d, total_seq_len - seq_len), + qkv[b, s, h, d], + ) + elif h < num_q_heads + num_kv_heads: + k[b, s, h - num_q_heads, d] = T.if_then_else( + d < rotary_dim, + _rope(qkv, b, s, h, d, total_seq_len - seq_len), + qkv[b, s, h, d], + ) + else: + v[b, s, h - (num_q_heads + num_kv_heads), d] = qkv[b, s, h, d] + + b, s, _, _ = qkv.shape + return op.tensor_ir_op( # pylint: disable=no-member + fused_rope, + "llama_rope", + args=[qkv, total_seq_len], + out=( + Tensor.placeholder((b, s, num_q_heads, head_dim), dtype), + Tensor.placeholder((b, s, num_kv_heads, head_dim), dtype), + Tensor.placeholder((b, s, num_kv_heads, head_dim), dtype), + ), + ) + + +def llama_rope_with_position_map( # pylint: disable=too-many-arguments + theta: float, + scale: float, + head_dim: int, + num_q_heads: int, + num_kv_heads: int, + dtype: str, + rotary_dim: int = None, +): + """Return the TIR function that computes Llama-style RoPE with q position map. + + Parameters + ---------- + theta : float + The theta value, or "base" in RoPE, which controls the frequency. + + scale : float + The RoPE scaling factor. + + head_dim : int + The number of features on each head. + + num_q_heads : int + The number of query heads. + + num_kv_heads : int + The number of key/value heads. It differs from `num_q_heads` in group-query attention. + + dtype : str + The dtype of qkv data. + + rotary_dim : int + The number of dimensions in the embedding that RoPE is applied to. By default, the + rotary_dim is the same as head_dim. + """ + fused_heads = num_q_heads + num_kv_heads * 2 + if rotary_dim is None: + rotary_dim = head_dim + scale = tir.const(scale, dtype) + + def _rope( # pylint: disable=too-many-arguments + x: T.Buffer, + s: tir.Var, + h: tir.Var, + d: tir.Var, + pos: tir.Var, + ): + cos_freq, sin_freq = rope_freq(pos * scale, d, rotary_dim, theta, dtype) + cos = cos_freq * x[s, h, d] + sin = sin_freq * tir.if_then_else( + d < rotary_dim // 2, + -x[s, h, d + rotary_dim // 2], + x[s, h, d - rotary_dim // 2], + ) + return cos + sin + + @T.prim_func + def fused_rope( # pylint: disable=too-many-locals + var_qkv: T.handle, + var_position_map: T.handle, + var_q: T.handle, + var_k: T.handle, + var_v: T.handle, + apply_rope: T.int32, + ): + T.func_attr( + { + "op_pattern": 8, # 2 means injective, 8 means opaque + "tir.noalias": T.bool(True), + } + ) + seq_len = T.int64() + qkv = T.match_buffer(var_qkv, (seq_len, fused_heads, head_dim), dtype) + q = T.match_buffer(var_q, (seq_len, num_q_heads, head_dim), dtype) + k = T.match_buffer(var_k, (seq_len, num_kv_heads, head_dim), dtype) + v = T.match_buffer(var_v, (seq_len, num_kv_heads, head_dim), dtype) + position_map = T.match_buffer(var_position_map, (seq_len,), "int32") + for iters in T.grid(seq_len, fused_heads, head_dim): + with T.block("llama_fused_rope"): + s, h, d = T.axis.remap("SSS", iters) + if h < num_q_heads: + q[s, h, d] = T.if_then_else( + apply_rope > 0 and d < rotary_dim, + _rope(qkv, s, h, d, position_map[s]), + qkv[s, h, d], + ) + elif h < num_q_heads + num_kv_heads: + k[s, h - num_q_heads, d] = T.if_then_else( + apply_rope > 0 and d < rotary_dim, + _rope(qkv, s, h, d, position_map[s]), + qkv[s, h, d], + ) + else: + v[s, h - (num_q_heads + num_kv_heads), d] = qkv[s, h, d] + + return fused_rope + + +# pylint: disable=line-too-long,too-many-arguments,too-many-nested-blocks,invalid-name + + +def llama_inplace_rope( + theta: float, + scale: float, + head_dim: int, + num_q_heads: int, + num_kv_heads: int, + dtype: str, + target: Target, # pylint: disable=unused-argument + rotary_dim: Optional[int] = None, +): + """Return the TIR function that inplace computes Llama-style RoPE with q position offset. + + Parameters + ---------- + theta : float + The theta value, or "base" in RoPE, which controls the frequency. + + scale : float + The RoPE scaling factor. + + head_dim : int + The number of features on each head. + + num_q_heads : int + The number of query heads. + + num_kv_heads : int + The number of key/value heads. It differs from `num_q_heads` in group-query attention. + + dtype : str + The dtype of qkv data. + + target : Target + The target to build the model to. + + rotary_dim : Optional[int] + The number of dimensions in the embedding that RoPE is applied to. By default, the + rotary_dim is the same as head_dim. + """ + if rotary_dim is None: + rotary_dim = head_dim + + def _rope( + x: T.Buffer, + s: tir.Var, + h: tir.Var, + d: tir.Var, + rope_offset: tir.Var, + instance_offset: tir.Var, + ): + cos_freq, sin_freq = rope_freq((s + rope_offset) * scale, d, rotary_dim, theta, dtype) + cos = cos_freq * x[s + instance_offset, h, d] + sin = sin_freq * tir.if_then_else( + d < rotary_dim // 2, + -x[s + instance_offset, h, d + rotary_dim // 2], + x[s + instance_offset, h, d - rotary_dim // 2], + ) + return cos + sin + + # fmt: off + @T.prim_func + def tir_rotary( # pylint: disable=too-many-locals + var_q: T.handle, + var_k: T.handle, + var_append_len_indptr: T.handle, + var_rope_offsets: T.handle, + _0: T.int32, + _1: T.int32, + _2: T.int32, + _3: T.int32, + _4: T.int32, + _5: T.float32, + _6: T.float32, + ): + T.func_attr({"tir.is_scheduled": 1}) + total_len = T.int32() + batch_size = T.int32() + q = T.match_buffer(var_q, (total_len, num_q_heads, head_dim), dtype) + k = T.match_buffer(var_k, (total_len, num_kv_heads, head_dim), dtype) + rope_offsets = T.match_buffer(var_rope_offsets, (batch_size,), "int32") + append_len_indptr = T.match_buffer(var_append_len_indptr, (batch_size + 1,), "int32") + with T.block(): + for b_h in T.thread_binding(batch_size * (num_q_heads + num_kv_heads), thread="blockIdx.x"): + b: T.int32 = b_h // (num_q_heads + num_kv_heads) + h: T.int32 = b_h % (num_q_heads + num_kv_heads) + instance_offset: T.int32 = append_len_indptr[b] + rope_offset: T.int32 = rope_offsets[b] + append_len: T.int32 = append_len_indptr[b + 1] - append_len_indptr[b] + for s0 in range(T.ceildiv(append_len, 32)): + for s1 in T.thread_binding(32, thread="threadIdx.y"): + for d0 in T.thread_binding(T.ceildiv(head_dim, 4), thread="threadIdx.x"): + for d1 in T.vectorized(4): + s: T.int32 = s0 * 32 + s1 + d: T.int32 = d0 * 4 + d1 + if s < append_len and d < rotary_dim: + if h < num_q_heads: + q[s + instance_offset, h, d] = _rope(q, s, h, d, rope_offset, instance_offset) + else: + k[s + instance_offset, h - num_q_heads, d] = _rope(k, s, h - num_q_heads, d, rope_offset, instance_offset) + return tir_rotary + + +# pylint: enable=line-too-long,too-many-arguments,too-many-nested-blocks,invalid-name diff --git a/python/mlc_chat/protocol/__init__.py b/python/mlc_chat/protocol/__init__.py new file mode 100644 index 0000000..2776756 --- /dev/null +++ b/python/mlc_chat/protocol/__init__.py @@ -0,0 +1,4 @@ +"""The protocols for MLC LLM server""" +from . import openai_api_protocol + +RequestProtocol = openai_api_protocol.CompletionRequest diff --git a/python/mlc_chat/protocol/conversation_protocol.py b/python/mlc_chat/protocol/conversation_protocol.py new file mode 100644 index 0000000..01c145d --- /dev/null +++ b/python/mlc_chat/protocol/conversation_protocol.py @@ -0,0 +1,136 @@ +"""The standard conversation protocol in MLC LLM""" + +from enum import Enum +from typing import Dict, List, Optional, Tuple + +from pydantic import BaseModel, Field, field_validator + + +# The message placeholders in the message prompts according to roles. +class MessagePlaceholders(Enum): + """The message placeholders in the message prompts according to roles.""" + + SYSTEM = "{system_message}" + USER = "{user_message}" + ASSISTANT = "{assistant_message}" + TOOL = "{tool_message}" + FUNCTION = "{function_string}" + + +class Conversation(BaseModel): + """Class that specifies the convention template of conversation + and contains the conversation history. + + Given a conversation template, the corresponding prompt generated out + from it is usually in the following format: + + <><><><><> + <><><><> + ... + <><><><> + <><> + """ + + # Optional name of the template. + name: Optional[str] = None + # The system prompt template, it optionally contains the system + # message placeholder, and the placeholder will be replaced with + # the system message below. + system_template: str = MessagePlaceholders.SYSTEM.value + # The content of the system prompt (without the template format). + system_message: str = "" + # The system token ids to be prepended at the beginning of tokenized + # generated prompt. + system_prefix_token_ids: Optional[List[int]] = None + + # The conversation roles + roles: Dict[str, str] + + # The roles prompt template, it optionally contains the defaults + # message placeholders and will be replaced by actual content + role_templates: Dict[str, str] + + # The conversation history messages. + # Each message is a pair of strings, denoting "(role, content)". + # The content can be None. + messages: List[Tuple[str, Optional[str]]] = Field(default_factory=lambda: []) + + # The separators between messages when concatenating into a single prompt. + # List size should be either 1 or 2. + # - When size is 1, the separator will be used between adjacent messages. + # - When size is 2, seps[0] is used after user message, and + # seps[1] is used after assistant message. + seps: List[str] + + # The separator between the role and the content in a message. + role_content_sep: str = "" + # The separator between the role and empty contents. + role_empty_sep: str = "" + + # The stop criteria + stop_str: List[str] = Field(default_factory=lambda: []) + stop_token_ids: List[int] = Field(default_factory=lambda: []) + + # Function call fields + function_string: str = "" + # whether using function calling or not, helps check for output message format in API call + use_function_calling: bool = False + + def __init__(self, role_templates: Optional[Dict[str, str]] = None, **kwargs): + # Defaults templates which would be overridden by model specific templates + _role_templates: Dict[str, str] = { + "user": MessagePlaceholders.USER.value, + "assistant": MessagePlaceholders.ASSISTANT.value, + "tool": MessagePlaceholders.TOOL.value, + } + if role_templates is not None: + _role_templates.update(role_templates) + super().__init__(role_templates=_role_templates, **kwargs) + + @field_validator("seps") + @classmethod + def check_message_seps(cls, seps: List[str]) -> List[str]: + """Check if the input message separators has size 1 or 2.""" + if len(seps) == 0 or len(seps) > 2: + raise ValueError("seps should have size 1 or 2.") + return seps + + def as_prompt(self) -> str: + """Convert the conversation template and history messages to + a single prompt. + """ + # - Get the system message. + system_msg = self.system_template.replace( + MessagePlaceholders.SYSTEM.value, self.system_message + ) + + # - Get the message strings. + message_list: List[str] = [] + separators = list(self.seps) + if len(separators) == 1: + separators.append(separators[0]) + for role, content in self.messages: # pylint: disable=not-an-iterable + if role not in self.roles.keys(): + raise ValueError(f'Role "{role}" is not a supported role in {self.roles.keys()}') + separator = separators[role == "assistant"] # check assistant role + if content is not None: + message_string = ( + self.roles[role] + + self.role_content_sep + + self.role_templates[role].replace( + MessagePlaceholders[role.upper()].value, content + ) + + separator + ) + else: + message_string = self.roles[role] + self.role_empty_sep + message_list.append(message_string) + + prompt = system_msg + separators[0] + "".join(message_list) + + # Replace the last function string placeholder with actual function string + prompt = self.function_string.join(prompt.rsplit(MessagePlaceholders.FUNCTION.value, 1)) + # Replace with remaining function string placeholders with empty string + prompt = prompt.replace(MessagePlaceholders.FUNCTION.value, "") + + return prompt diff --git a/python/mlc_chat/protocol/openai_api_protocol.py b/python/mlc_chat/protocol/openai_api_protocol.py new file mode 100644 index 0000000..2ae26bf --- /dev/null +++ b/python/mlc_chat/protocol/openai_api_protocol.py @@ -0,0 +1,329 @@ +"""Protocols in MLC LLM for OpenAI API. +Adapted from FastChat's OpenAI protocol: +https://github.com/lm-sys/FastChat/blob/main/fastchat/protocol/openai_api_protocol.py +""" + +# pylint: disable=missing-class-docstring +import time +from typing import Any, Dict, List, Literal, Optional, Tuple, Union + +import shortuuid +from pydantic import BaseModel, Field, field_validator, model_validator + +################ Commons ################ + + +class ListResponse(BaseModel): + object: str = "list" + data: List[Any] + + +class TopLogProbs(BaseModel): + token: str + logprob: float + bytes: Optional[List[int]] + + +class LogProbsContent(BaseModel): + token: str + logprob: float + bytes: Optional[List[int]] + top_logprobs: List[TopLogProbs] = [] + + +class LogProbs(BaseModel): + content: List[LogProbsContent] + + +class UsageInfo(BaseModel): + prompt_tokens: int = 0 + completion_tokens: int = 0 + total_tokens: int = 0 + + def __init__(self, prompt_tokens: int = 0, completion_tokens: int = 0): + super().__init__( + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + total_tokens=prompt_tokens + completion_tokens, + ) + + +################ v1/models ################ + + +class ModelResponse(BaseModel): + """OpenAI "v1/models" response protocol. + API reference: https://platform.openai.com/docs/api-reference/models/object + """ + + id: str + created: int = Field(default_factory=lambda: int(time.time())) + object: str = "model" + owned_by: str = "MLC-LLM" + + +################ v1/completions ################ + + +class CompletionRequest(BaseModel): + """OpenAI completion request protocol. + API reference: https://platform.openai.com/docs/api-reference/completions/create + """ + + model: str + prompt: Union[str, List[int], List[Union[str, List[int]]]] + best_of: int = 1 + echo: bool = False + frequency_penalty: float = 0.0 + presence_penalty: float = 0.0 + logprobs: bool = False + top_logprobs: int = 0 + logit_bias: Optional[Dict[int, float]] = None + max_tokens: int = 16 + n: int = 1 + seed: Optional[int] = None + stop: Optional[Union[str, List[str]]] = None + stream: bool = False + suffix: Optional[str] = None + temperature: float = 1.0 + top_p: float = 1.0 + user: Optional[str] = None + ignore_eos: bool = False + + @field_validator("frequency_penalty", "presence_penalty") + @classmethod + def check_penalty_range(cls, penalty_value: float) -> float: + """Check if the penalty value is in range [-2, 2].""" + if penalty_value < -2 or penalty_value > 2: + raise ValueError("Penalty value should be in range [-2, 2].") + return penalty_value + + @field_validator("logit_bias") + @classmethod + def check_logit_bias( + cls, logit_bias_value: Optional[Dict[int, float]] + ) -> Optional[Dict[int, float]]: + """Check if the logit bias key is given as an integer.""" + if logit_bias_value is None: + return None + for token_id, bias in logit_bias_value.items(): + if abs(bias) > 100: + raise ValueError( + "Logit bias value should be in range [-100, 100], while value " + f"{bias} is given for token id {token_id}" + ) + return logit_bias_value + + @model_validator(mode="after") + def check_logprobs(self) -> "CompletionRequest": + """Check if the logprobs requirements are valid.""" + if self.top_logprobs < 0 or self.top_logprobs > 5: + raise ValueError('"top_logprobs" must be in range [0, 5]') + if not self.logprobs and self.top_logprobs > 0: + raise ValueError('"logprobs" must be True to support "top_logprobs"') + return self + + +class CompletionResponseChoice(BaseModel): + finish_reason: Optional[Literal["stop", "length"]] = None + index: int = 0 + logprobs: Optional[LogProbs] = None + text: str + + +class CompletionResponse(BaseModel): + """OpenAI completion response protocol. + API reference: https://platform.openai.com/docs/api-reference/completions/object + """ + + id: str + choices: List[CompletionResponseChoice] + created: int = Field(default_factory=lambda: int(time.time())) + model: str + object: str = "text_completion" + usage: UsageInfo = Field( + default_factory=lambda: UsageInfo() # pylint: disable=unnecessary-lambda + ) + + +################ v1/chat/completions ################ + + +class ChatFunction(BaseModel): + description: Optional[str] = None + name: str + parameters: Dict + + +class ChatTool(BaseModel): + type: Literal["function"] + function: ChatFunction + + +class ChatFunctionCall(BaseModel): + name: str + arguments: Union[None, Dict[str, Any]] = None + + +class ChatToolCall(BaseModel): + id: str = Field(default_factory=lambda: f"call_{shortuuid.random()}") + type: Literal["function"] + function: ChatFunctionCall + + +class ChatCompletionMessage(BaseModel): + content: Optional[Union[str, List[Dict[str, str]]]] = None + role: Literal["system", "user", "assistant", "tool"] + name: Optional[str] = None + tool_calls: Optional[List[ChatToolCall]] = None + tool_call_id: Optional[str] = None + + +class ChatCompletionRequest(BaseModel): + """OpenAI chat completion request protocol. + API reference: https://platform.openai.com/docs/api-reference/chat/create + """ + + messages: List[ChatCompletionMessage] + model: str + frequency_penalty: float = 0.0 + presence_penalty: float = 0.0 + logprobs: bool = False + top_logprobs: int = 0 + logit_bias: Optional[Dict[int, float]] = None + max_tokens: Optional[int] = None + n: int = 1 + response_format: Literal["text", "json_object"] = "text" + seed: Optional[int] = None + stop: Optional[Union[str, List[str]]] = None + stream: bool = False + temperature: float = 1.0 + top_p: float = 1.0 + tools: Optional[List[ChatTool]] = None + tool_choice: Optional[Union[Literal["none", "auto"], Dict]] = None + user: Optional[str] = None + ignore_eos: bool = False + + @field_validator("frequency_penalty", "presence_penalty") + @classmethod + def check_penalty_range(cls, penalty_value: float) -> float: + """Check if the penalty value is in range [-2, 2].""" + if penalty_value < -2 or penalty_value > 2: + raise ValueError("Penalty value should be in range [-2, 2].") + return penalty_value + + @field_validator("logit_bias") + @classmethod + def check_logit_bias( + cls, logit_bias_value: Optional[Dict[int, float]] + ) -> Optional[Dict[int, float]]: + """Check if the logit bias key is given as an integer.""" + if logit_bias_value is None: + return None + for token_id, bias in logit_bias_value.items(): + if abs(bias) > 100: + raise ValueError( + "Logit bias value should be in range [-100, 100], while value " + f"{bias} is given for token id {token_id}" + ) + return logit_bias_value + + @model_validator(mode="after") + def check_logprobs(self) -> "ChatCompletionRequest": + """Check if the logprobs requirements are valid.""" + if self.top_logprobs < 0 or self.top_logprobs > 5: + raise ValueError('"top_logprobs" must be in range [0, 5]') + if not self.logprobs and self.top_logprobs > 0: + raise ValueError('"logprobs" must be True to support "top_logprobs"') + return self + + +class ChatCompletionResponseChoice(BaseModel): + finish_reason: Optional[Literal["stop", "length", "tool_calls", "error"]] = None + index: int = 0 + message: ChatCompletionMessage + logprobs: Optional[LogProbs] = None + + +class ChatCompletionStreamResponseChoice(BaseModel): + finish_reason: Optional[Literal["stop", "length", "tool_calls"]] = None + index: int = 0 + delta: ChatCompletionMessage + logprobs: Optional[LogProbs] = None + + +class ChatCompletionResponse(BaseModel): + """OpenAI completion response protocol. + API reference: https://platform.openai.com/docs/api-reference/chat/object + """ + + id: str + choices: List[ChatCompletionResponseChoice] + created: int = Field(default_factory=lambda: int(time.time())) + model: str + system_fingerprint: str + object: Literal["chat.completion"] = "chat.completion" + usage: UsageInfo = Field( + default_factory=lambda: UsageInfo() # pylint: disable=unnecessary-lambda + ) + + +class ChatCompletionStreamResponse(BaseModel): + """OpenAI completion stream response protocol. + API reference: https://platform.openai.com/docs/api-reference/chat/streaming + """ + + id: str + choices: List[ChatCompletionStreamResponseChoice] + created: int = Field(default_factory=lambda: int(time.time())) + model: str + system_fingerprint: str + object: Literal["chat.completion.chunk"] = "chat.completion.chunk" + + +################################################ + + +def openai_api_get_unsupported_fields( + request: Union[CompletionRequest, ChatCompletionRequest] +) -> List[str]: + """Get the unsupported fields in the request.""" + unsupported_field_default_values: List[Tuple[str, Any]] = [ + ("best_of", 1), + ("n", 1), + ("response_format", "text"), + ] + + unsupported_fields: List[str] = [] + for field, value in unsupported_field_default_values: + if hasattr(request, field) and getattr(request, field) != value: + unsupported_fields.append(field) + return unsupported_fields + + +def openai_api_get_generation_config( + request: Union[CompletionRequest, ChatCompletionRequest] +) -> Dict[str, Any]: + """Create the generation config from the given request.""" + kwargs: Dict[str, Any] = {} + arg_names = [ + "temperature", + "top_p", + "max_tokens", + "frequency_penalty", + "presence_penalty", + "logprobs", + "top_logprobs", + "logit_bias", + "seed", + "ignore_eos", + ] + for arg_name in arg_names: + kwargs[arg_name] = getattr(request, arg_name) + if kwargs["max_tokens"] is None: + # Setting to -1 means the generation will not stop until + # exceeding model capability or hit any stop criteria. + kwargs["max_tokens"] = -1 + if request.stop is not None: + kwargs["stop_strs"] = [request.stop] if isinstance(request.stop, str) else request.stop + return kwargs diff --git a/python/mlc_chat/protocol/protocol_utils.py b/python/mlc_chat/protocol/protocol_utils.py new file mode 100644 index 0000000..a9a68a1 --- /dev/null +++ b/python/mlc_chat/protocol/protocol_utils.py @@ -0,0 +1,58 @@ +"""Utility functions for request protocols""" + +from typing import Any, Dict, List, Optional + +from pydantic import BaseModel + +from ..serve.config import GenerationConfig +from . import RequestProtocol +from .openai_api_protocol import ChatCompletionRequest as OpenAIChatCompletionRequest +from .openai_api_protocol import CompletionRequest as OpenAICompletionRequest +from .openai_api_protocol import ( + openai_api_get_generation_config, + openai_api_get_unsupported_fields, +) + + +class ErrorResponse(BaseModel): + """The class of error response.""" + + object: str = "error" + message: str + code: int = None + + +def get_unsupported_fields(request: RequestProtocol) -> List[str]: + """Get the unsupported fields of the request. + Return the list of unsupported field names. + """ + if isinstance(request, (OpenAICompletionRequest, OpenAIChatCompletionRequest)): + return openai_api_get_unsupported_fields(request) + raise RuntimeError("Cannot reach here") + + +def get_generation_config( + request: RequestProtocol, + extra_stop_token_ids: Optional[List[int]] = None, + extra_stop_str: Optional[List[str]] = None, +) -> GenerationConfig: + """Create the generation config in MLC LLM out from the input request protocol.""" + kwargs: Dict[str, Any] + if isinstance(request, (OpenAICompletionRequest, OpenAIChatCompletionRequest)): + kwargs = openai_api_get_generation_config(request) + else: + raise RuntimeError("Cannot reach here") + + if extra_stop_token_ids is not None: + stop_token_ids = kwargs.get("stop_token_ids", []) + assert isinstance(stop_token_ids, list) + stop_token_ids += extra_stop_token_ids + kwargs["stop_token_ids"] = stop_token_ids + + if extra_stop_str is not None: + stop_strs = kwargs.get("stop_strs", []) + assert isinstance(stop_strs, list) + stop_strs += extra_stop_str + kwargs["stop_strs"] = stop_strs + + return GenerationConfig(**kwargs) diff --git a/python/mlc_chat/quantization/__init__.py b/python/mlc_chat/quantization/__init__.py new file mode 100644 index 0000000..31016a9 --- /dev/null +++ b/python/mlc_chat/quantization/__init__.py @@ -0,0 +1,6 @@ +"""A subpackage for quantization and dequantization algorithms""" +from .awq_quantization import AWQQuantize +from .ft_quantization import FTQuantize +from .group_quantization import GroupQuantize +from .no_quantization import NoQuantize +from .quantization import QUANTIZATION, Quantization diff --git a/python/mlc_chat/quantization/awq_quantization.py b/python/mlc_chat/quantization/awq_quantization.py new file mode 100644 index 0000000..116582f --- /dev/null +++ b/python/mlc_chat/quantization/awq_quantization.py @@ -0,0 +1,271 @@ +"""AWQ Quantization""" + +from dataclasses import dataclass, field +from typing import Any, Callable, Dict, List, Optional + +from tvm import DataType, DataTypeCode, te, tir, topi +from tvm.relax.frontend import nn +from tvm.runtime import NDArray + +from mlc_chat.loader import QuantizeMapping + +from .utils import convert_uint_to_float, is_final_fc + + +def _make_divisible(c, divisor): # pylint: disable=invalid-name + return (c + divisor - 1) // divisor + + +def _calculate_zeros_width(in_features, group_size=128, pack_num=8): + if group_size >= 128: + size_multiplier = 1 + elif group_size == 64: + size_multiplier = 2 + elif group_size == 32: + size_multiplier = 4 + else: + raise NotImplementedError + + base_width = _make_divisible(in_features // group_size, pack_num) + base_width = _make_divisible(base_width, size_multiplier) * size_multiplier + return base_width + + +@dataclass +class AWQQuantize: # pylint: disable=too-many-instance-attributes + """Configuration for AWQ quantization""" + + name: str + kind: str + group_size: int + quantize_dtype: str # "int3", "int4", "int8" + storage_dtype: str # "uint32" + model_dtype: str # "float16", "float32" + + num_elem_per_storage: int = 0 + num_storage_per_group: int = 0 + max_int_value: int = 0 + + prebuilt_quantize_func: Dict[str, Callable[[NDArray], NDArray]] = field( + default_factory=lambda: {} + ) + + def __post_init__(self): + assert self.kind == "awq" + quantize_dtype = DataType(self.quantize_dtype) + storage_dtype = DataType(self.storage_dtype) + model_dtype = DataType(self.model_dtype) + assert quantize_dtype.type_code == DataTypeCode.INT + assert storage_dtype.type_code == DataTypeCode.UINT + assert model_dtype.type_code == DataTypeCode.FLOAT + if storage_dtype.bits < quantize_dtype.bits: + raise ValueError("Storage unit should be greater or equal to quantized element") + + self.num_elem_per_storage = storage_dtype.bits // quantize_dtype.bits + if self.group_size % self.num_elem_per_storage != 0: + raise ValueError("Group size should be divisible by numbers of elements per storage") + self.num_storage_per_group = self.group_size // self.num_elem_per_storage + self.max_int_value = (2 ** (quantize_dtype.bits - 1)) - 1 + + def quantize_model( + self, + model: nn.Module, + quant_map: QuantizeMapping, + name_prefix: str, + ) -> nn.Module: + """ + Quantize model with awq quantization. + + Parameters + ---------- + model : nn.Module + The non-quantized nn.Module. + + quant_map : QuantizeMapping + The quantize mapping with name mapping and func mapping. + + name_prefix : str + The name prefix for visited weight. + + Returns + ------- + ret : nn.Module + The quantized nn.Module. + """ + + class _Mutator(nn.Mutator): + def __init__(self, config: AWQQuantize, quant_map: QuantizeMapping) -> None: + super().__init__() + self.config = config + self.quant_map = quant_map + + def visit_module(self, name: str, node: nn.Module) -> Any: + """ + The visiting method for awq quantization of nn.Module nodes. + + Parameters + ---------- + name : str + The name of the current node + + node : nn.Module + The current node of nn.Module to mutate. + + Returns + ------- + ret_node : Any + The new node to replace current node. + """ + + if isinstance(node, nn.Linear) and not is_final_fc(name): + return AWQQuantizeLinear.from_linear(node, self.config) + return self.visit(name, node) + + model.to(dtype=self.model_dtype) + mutator = _Mutator(self, quant_map) + model = mutator.visit(name_prefix, model) + return model + + def _dequantize( + self, + weight: te.Tensor, + zeros: te.Tensor, + scale: te.Tensor, + out_shape: Optional[List[tir.PrimExpr]] = None, + ): + float_weight = convert_uint_to_float( + weight, + DataType(self.quantize_dtype).bits, + self.num_elem_per_storage, + self.storage_dtype, + self.model_dtype, + out_shape=[weight.shape[0], weight.shape[1] * self.num_elem_per_storage], + ft_reorder=True, + ) + float_zeros = convert_uint_to_float( + zeros, + DataType(self.quantize_dtype).bits, + self.num_elem_per_storage, + self.storage_dtype, + self.model_dtype, + out_shape=[zeros.shape[0], zeros.shape[1] * self.num_elem_per_storage], + ft_reorder=True, + ) + float_weight = topi.transpose(float_weight) + float_zeros = topi.transpose(float_zeros) + scale = topi.transpose(scale) + return te.compute( + shape=( + [weight.shape[0], weight.shape[1] * self.num_elem_per_storage] + if out_shape is None + else out_shape + ), + fcompute=lambda i, j: tir.multiply( + tir.subtract(float_weight[i, j], float_zeros[i, j // self.group_size]), + scale[i, j // self.group_size], + ), + name="dequantize", + ) + + +class AWQQuantizeLinear(nn.Module): # pylint: disable=too-many-instance-attributes + """An nn.Linear module with AWQ quantization""" + + def __init__( # pylint: disable=too-many-arguments + self, + in_features: int, + out_features: int, + config: AWQQuantize, + bias: bool = True, + out_dtype: Optional[str] = None, + ) -> None: + super().__init__() + self.in_features = in_features + self.out_features = out_features + self.out_dtype = out_dtype + self.config = config + self.qweight = nn.Parameter( + (in_features, out_features // config.num_elem_per_storage), config.storage_dtype + ) + self.qzeros = nn.Parameter( + (in_features // config.group_size, out_features // config.num_elem_per_storage), + config.storage_dtype, + ) + self.scales = nn.Parameter( + (in_features // config.group_size, out_features), config.model_dtype + ) + if bias: + self.bias = nn.Parameter( + (out_features,), config.model_dtype if out_dtype is None else out_dtype + ) + else: + self.bias = None + + @staticmethod + def from_linear(linear: nn.Linear, config: AWQQuantize) -> "AWQQuantizeLinear": + """ + Converts a non-quantized nn.Linear to a group quantized AWQQuantizeLinear + + Parameters + ---------- + linear : nn.Linear + The non-quantized nn.Linear. + + config : AWQQuantize + The awq quantization config. + + Returns + ------- + ret : GroupQuantizeLinear + The awq quantized AWQQuantizeLinear layer. + """ + return AWQQuantizeLinear( + in_features=linear.in_features, + out_features=linear.out_features, + config=config, + bias=getattr(linear, "bias", None) is not None, + out_dtype=linear.out_dtype, + ) + + def forward(self, x: nn.Tensor) -> nn.Tensor: # pylint: disable=invalid-name + """ + Forward method for awq quantized linear layer + + Parameters + ---------- + x : nn.Tensor + The input tensor. + + Returns + ------- + ret : nn.Tensor + The output tensor for the group quantized linear layer. + """ + w = nn.op.tensor_expr_op( # pylint: disable=invalid-name + lambda weight, zeros, scale: self.config._dequantize( # pylint: disable=protected-access + weight, + zeros, + scale, + [tir.IntImm("int64", self.out_features), tir.IntImm("int64", self.in_features)], + ), + name_hint="dequantize", + args=[self.qweight, self.qzeros, self.scales], + ) + w = nn.op.permute_dims(w) # pylint: disable=invalid-name + x = nn.op.matmul(x, w, out_dtype=self.out_dtype) + if self.bias is not None: + x = x + self.bias + return x + + def to(self, dtype: Optional[str] = None) -> None: + """ + Override to() such that we do not convert bias if there is an out_dtype. + Otherwise, we might run into dtype mismatch when computing x + self.bias. + """ + self.qweight.to(dtype=dtype) + self.qzeros.to(dtype=dtype) + self.scales.to(dtype=dtype) + if self.bias is not None and self.out_dtype is None: + self.bias.to(dtype=dtype) + if dtype is not None and isinstance(getattr(self, "dtype", None), str): + self.dtype = dtype # pylint: disable=attribute-defined-outside-init diff --git a/python/mlc_chat/quantization/ft_quantization.py b/python/mlc_chat/quantization/ft_quantization.py new file mode 100644 index 0000000..c30e85b --- /dev/null +++ b/python/mlc_chat/quantization/ft_quantization.py @@ -0,0 +1,396 @@ +"""The FasterTransformer quantization config""" + +from dataclasses import dataclass +from typing import Any, Callable, List, Literal, Optional, Tuple + +import tvm +from tvm import DataType, DataTypeCode, IRModule +from tvm import dlight as dl +from tvm import relax, te, tir +from tvm.relax.frontend import nn +from tvm.runtime import NDArray +from tvm.target import Target + +from ..loader import QuantizeMapping +from ..op import faster_transformer_dequantize_gemm +from ..support import logging +from ..support.auto_target import detect_cuda_arch_list +from ..support.style import bold +from .group_quantization import ( + GroupQuantize, + GroupQuantizeEmbedding, + GroupQuantizeLinear, +) +from .utils import is_final_fc + +logger = logging.getLogger(__name__) + + +@dataclass +class FTQuantize: # pylint: disable=too-many-instance-attributes + """Configuration for FasterTransformer quantization""" + + name: str + kind: str + quantize_dtype: Literal["int4", "int8"] + storage_dtype: Literal["int8"] + model_dtype: Literal["float16"] + group_size: Optional[int] = None + + num_elem_per_storage: int = 0 + max_int_value: int = 0 + + def fallback_group_quantize(self) -> GroupQuantize: + """ + The fallback group quantization config for other parameters. + + Returns + ------ + quantize: GroupQuantize + The group quantization config to fallback. + """ + return GroupQuantize( + name=self.name, + kind="group-quant", + group_size=32, # hardcoded to 32 as only supporting int4 quantization + quantize_dtype=self.quantize_dtype, + storage_dtype="uint32", + model_dtype=self.model_dtype, + linear_weight_layout="NK", + ) + + def __post_init__(self): + assert self.kind == "ft-quant" + quantize_dtype = DataType(self.quantize_dtype) + storage_dtype = DataType(self.storage_dtype) + assert self.quantize_dtype in ["int4", "int8"] + assert storage_dtype.type_code == DataTypeCode.INT + assert self.model_dtype == "float16" + assert self.group_size in [None, 64, 128] + if storage_dtype.bits < quantize_dtype.bits: + raise ValueError("Storage unit should be greater or equal to quantized element") + + self.num_elem_per_storage = storage_dtype.bits // quantize_dtype.bits + self.max_int_value = (2 ** (quantize_dtype.bits - 1)) - 1 + self._quantize_func_cache = {} + + def quantize_model( + self, + model: nn.Module, + quant_map: QuantizeMapping, + name_prefix: str, + ) -> nn.Module: + """ + Quantize model with FasterTransformer quantization + + Parameters + ---------- + model : nn.Module + The non-quantized nn.Module. + + quant_map : QuantizeMapping + The quantize mapping with name mapping and func mapping. + + name_prefix : str + The name prefix for visited weight. + + Returns + ------- + ret : nn.Module + The quantized nn.Module. + """ + + class _Mutator(nn.Mutator): + def __init__(self, config: FTQuantize, quant_map: QuantizeMapping) -> None: + super().__init__() + self.config = config + self.quant_map = quant_map + + def visit_module(self, name: str, node: nn.Module) -> Any: + """ + The visiting method for FasterTransformer quantization of nn.Module nodes. + + Parameters + ---------- + name : str + The name of the current node. + + node : nn.Module + The current node of nn.Module to mutate. + + Returns + ------ + ret_node: Any + The new node to replace current node. + """ + if isinstance(node, nn.Linear): + weight_name = f"{name}.weight" + self.quant_map.param_map[weight_name] = [f"{name}.q_weight", f"{name}.q_scale"] + if ( + # pylint: disable=too-many-boolean-expressions + is_final_fc(name) + or node.out_dtype == "float32" + or (self.config.quantize_dtype == "int4" and node.out_features % 8 != 0) + or (self.config.quantize_dtype == "int8" and node.out_features % 4 != 0) + ): + # Under any of the conditions we fall back to GroupQuantize + # For `is_final_fc()` see https://github.com/mlc-ai/mlc-llm/issues/1723 + # If simply skipping lm_head quantization degrades performance + # Other requirements are from CUTLASS + logger.info( + 'Fallback to GroupQuantize for nn.Linear: "%s", ' + + "weight.shape: %s, out_dtype: %s", + bold(name), + node.weight.shape, + node.out_dtype, + ) + group_quantize = self.config.fallback_group_quantize() + self.quant_map.map_func[weight_name] = group_quantize.quantize_weight + return GroupQuantizeLinear.from_linear(node, group_quantize) + self.quant_map.map_func[weight_name] = self.config.quantize_weight + return FTQuantizeLinear.from_linear(node, self.config) + if isinstance(node, nn.Embedding): + weight_name = f"{name}.weight" + self.quant_map.param_map[weight_name] = [f"{name}.q_weight", f"{name}.q_scale"] + group_quantize = self.config.fallback_group_quantize() + self.quant_map.map_func[weight_name] = group_quantize.quantize_weight + return GroupQuantizeEmbedding.from_embedding(node, group_quantize) + return self.visit(name, node) + + model.to(dtype=self.model_dtype) + mutator = _Mutator(self, quant_map) + model = mutator.visit(name_prefix, model) + return model + + def quantize_weight(self, weight: NDArray) -> List[NDArray]: + """ + Quantize weight with FasterTransformer quantization + + Parameters + ---------- + weight : NDArray + The original weight. + + Returns + ------ + ret: List[NDArray] + The list of FasterTransformer quantized weights. + """ + assert tvm.get_global_func("relax.ext.cutlass", True), ( + "Cutlass should be enabled in TVM runtime to quantize weight, " + "but not enabled in current TVM runtime environment. " + "To enable Cutlass in TVM runtime, please `set(USE_CUTLASS ON)` " + "in config.cmake when compiling TVM from source" + ) + assert len(weight.shape) == 2 + device = weight.device + device_type = device.MASK2STR[device.device_type] + if device_type == "cuda": + target = Target.current() + if target is None: + target = Target.from_device(device) + with target: + + def _create_quantize_func() -> IRModule: + bb = relax.BlockBuilder() # pylint: disable=invalid-name + weight_var = relax.Var( + "weight", relax.TensorStructInfo(weight.shape, weight.dtype) + ) + with bb.function(name="main", params=[weight_var]): + with bb.dataflow(): + lv0 = bb.emit_te( + self._quantize, weight_var + ) # pylint: disable=invalid-name + lv1 = bb.normalize(lv0[0]) + lv2 = bb.emit( + relax.call_pure_packed( + "cutlass.ft_preprocess_weight", + lv1, + detect_cuda_arch_list(target=target)[0], + DataType(self.quantize_dtype).bits == 4, + sinfo_args=lv1.struct_info, + ) + ) + gv = bb.emit_output( + relax.Tuple([lv2, lv0[1]]) + ) # pylint: disable=invalid-name + bb.emit_func_output(gv) + return bb.finalize() + + def _compile_quantize_func(mod: IRModule) -> Callable: + mod = dl.ApplyDefaultSchedule( # type: ignore # pylint: disable=not-callable + dl.gpu.Reduction(), + dl.gpu.GeneralReduction(), + dl.gpu.Fallback(), + )(mod) + ex = relax.build(mod, target=target) + vm = relax.VirtualMachine(ex, device) # pylint: disable=invalid-name + return vm["main"] + + key = str((int(weight.shape[0]), int(weight.shape[1]), weight.dtype, device_type)) + quantize_func = self._quantize_func_cache.get(key, None) + if quantize_func is None: + logger.info("Compiling quantize function for key: %s", key) + quantize_func = _compile_quantize_func(_create_quantize_func()) + self._quantize_func_cache[key] = quantize_func + data = quantize_func(weight) + return data + else: + raise NotImplementedError(f"Device type {device_type} is not supported") + + def _quantize( # pylint: disable=too-many-locals + self, + weight: te.Tensor, + ) -> Tuple[te.Tensor, te.Tensor]: + """FasterTransformer quantization for weight tensor, defined in tensor expression.""" + assert len(weight.shape) == 2 + n, k = weight.shape + + cur_group_size = k if not self.group_size else self.group_size + scale_shape = (tir.ceildiv(k, cur_group_size), n) + r = te.reduce_axis((0, cur_group_size), name="r") + + max_abs = te.compute( + shape=scale_shape, + fcompute=lambda j, i: te.max( + tir.if_then_else( + j * cur_group_size + r < k, + te.abs(weight[i, j * cur_group_size + r]), + te.min_value(self.model_dtype), + ), + axis=r, + ), + name="max_abs_value", + ) + max_int = tir.const(self.max_int_value, self.model_dtype) + scale = te.compute( + scale_shape, + lambda i, j: max_abs[i, j].astype(self.model_dtype) / max_int, + name="scale", + ) + # compute scaled weight + quantize_dtype = DataType(self.quantize_dtype) + bin_mask = tir.const((1 << quantize_dtype.bits) - 1, self.storage_dtype) + scaled_weight = te.compute( + shape=weight.shape, + fcompute=lambda i, j: tir.min( + tir.max( + tir.round(weight[i, j] / scale[j // cur_group_size, i]), + -max_int - 1, + ), + max_int, + ).astype(self.storage_dtype) + & bin_mask, + ) + + quantized_weight_shape = (k, tir.ceildiv(n, self.num_elem_per_storage)) + r = te.reduce_axis((0, self.num_elem_per_storage), name="r") # pylint: disable=invalid-name + quantized_weight = te.compute( + shape=quantized_weight_shape, + fcompute=lambda j, i: tir.sum( + tir.if_then_else( + i * self.num_elem_per_storage + r < n, + scaled_weight[i * self.num_elem_per_storage + r, j] + << ( + r.astype(self.storage_dtype) + * tir.const(quantize_dtype.bits, self.storage_dtype) + ), + tir.const(0, self.storage_dtype), + ), + axis=r, + ), + name="weight", + ) + + return quantized_weight, scale + + +class FTQuantizeLinear(nn.Module): # pylint: disable=too-many-instance-attributes + """An nn.Linear module with FasterTransformer quantization""" + + def __init__( # pylint: disable=too-many-arguments + self, + in_features: int, + out_features: int, + config: FTQuantize, + bias: bool = True, + out_dtype: Optional[str] = None, + ) -> None: + super().__init__() + self.in_features = in_features + self.out_features = out_features + self.out_dtype = out_dtype + self.config = config + cur_group_size = in_features if not config.group_size else config.group_size + self.q_weight = nn.Parameter( + (in_features, tir.ceildiv(out_features, config.num_elem_per_storage)), + config.storage_dtype, + ) + self.q_scale = nn.Parameter( + (tir.ceildiv(in_features, cur_group_size), out_features), config.model_dtype + ) + if bias: + self.bias = nn.Parameter( + (out_features,), config.model_dtype if out_dtype is None else out_dtype + ) + else: + self.bias = None + + @staticmethod + def from_linear(src: nn.Linear, config: FTQuantize) -> "FTQuantizeLinear": + """ + Converts a non-quantized nn.Linear to a FasterTransformer quantized FTQuantizeLinear + + Parameters + ---------- + src : nn.Linear + The non-quantized nn.Linear. + + config : FTQuantize + The FasterTransformer quantization config. + + Returns + ------- + ret : FTQuantizeLinear + The FasterTransformer quantized FTQuantizeLinear layer. + """ + quantized_linear = FTQuantizeLinear( + in_features=src.in_features, + out_features=src.out_features, + config=config, + bias=getattr(src, "bias", None) is not None, + out_dtype=src.out_dtype, + ) + if quantized_linear.bias is not None: + quantized_linear.bias.attrs = src.bias.attrs + return quantized_linear + + def forward(self, x: nn.Tensor) -> nn.Tensor: # pylint: disable=invalid-name + """ + Forward method for FasterTransformer quantized linear layer. + + Parameters + ---------- + x : nn.Tensor + The input tensor. + + Returns + ------- + ret : nn.Tensor + The output tensor for the FasterTransformer quantized linear layer. + """ + return faster_transformer_dequantize_gemm( + x, self.q_weight, self.q_scale, self.bias, group_size=self.config.group_size + ) + + def to(self, dtype: Optional[str] = None) -> None: + """ + Override to() such that we do not convert bias if there is an out_dtype. + Otherwise, we might run into dtype mismatch when computing x + self.bias. + """ + self.q_weight.to(dtype=dtype) + self.q_scale.to(dtype=dtype) + if self.bias is not None and self.out_dtype is None: + self.bias.to(dtype=dtype) + if dtype is not None and isinstance(getattr(self, "dtype", None), str): + self.dtype = dtype # pylint: disable=attribute-defined-outside-init diff --git a/python/mlc_chat/quantization/group_quantization.py b/python/mlc_chat/quantization/group_quantization.py new file mode 100644 index 0000000..baf8662 --- /dev/null +++ b/python/mlc_chat/quantization/group_quantization.py @@ -0,0 +1,664 @@ +"""The group quantization config""" + +from dataclasses import dataclass +from functools import partial +from typing import Any, Callable, List, Literal, Optional, Tuple, Union + +from tvm import DataType, DataTypeCode, IRModule +from tvm import dlight as dl +from tvm import relax, te, tir, topi +from tvm.relax.frontend import nn +from tvm.runtime import NDArray +from tvm.target import Target + +from mlc_chat.loader import QuantizeMapping +from mlc_chat.nn import MixtralExperts +from mlc_chat.support import logging +from mlc_chat.support import tensor_parallel as tp + +from .utils import convert_uint_to_float, is_final_fc + +logger = logging.getLogger(__name__) + + +@dataclass +class GroupQuantize: # pylint: disable=too-many-instance-attributes + """Configuration for group quantization""" + + name: str + kind: str + group_size: int + quantize_dtype: Literal["int3", "int4", "int8"] + storage_dtype: Literal["uint32"] + model_dtype: Literal["float16", "float32"] + linear_weight_layout: Literal["KN", "NK"] + quantize_embedding: bool = True + quantize_final_fc: bool = True + + num_elem_per_storage: int = 0 + num_storage_per_group: int = 0 + max_int_value: int = 0 + + def __post_init__(self): + assert self.kind == "group-quant" + quantize_dtype = DataType(self.quantize_dtype) + storage_dtype = DataType(self.storage_dtype) + model_dtype = DataType(self.model_dtype) + assert quantize_dtype.type_code == DataTypeCode.INT + assert storage_dtype.type_code == DataTypeCode.UINT + assert model_dtype.type_code == DataTypeCode.FLOAT + if storage_dtype.bits < quantize_dtype.bits: + raise ValueError("Storage unit should be greater or equal to quantized element") + + self.num_elem_per_storage = storage_dtype.bits // quantize_dtype.bits + if self.group_size % self.num_elem_per_storage != 0: + raise ValueError("Group size should be divisible by numbers of elements per storage") + self.num_storage_per_group = self.group_size // self.num_elem_per_storage + self.max_int_value = (2 ** (quantize_dtype.bits - 1)) - 1 + self.linear_quant_axis = 0 if self.linear_weight_layout == "KN" else 1 + self._quantize_func_cache = {} + + def quantize_model( + self, + model: nn.Module, + quant_map: QuantizeMapping, + name_prefix: str, + ) -> nn.Module: + """ + Quantize model with group quantization + + Parameters + ---------- + model : nn.Module + The non-quantized nn.Module. + + quant_map : QuantizeMapping + The quantize mapping with name mapping and func mapping. + + name_prefix : str + The name prefix for visited weight. + + Returns + ------- + ret : nn.Module + The quantized nn.Module. + """ + + class _Mutator(nn.Mutator): + def __init__(self, config: GroupQuantize, quant_map: QuantizeMapping) -> None: + super().__init__() + self.config = config + self.quant_map = quant_map + + def visit_module(self, name: str, node: nn.Module) -> Any: + """ + The visiting method for group quantization of nn.Module nodes. + + Parameters + ---------- + name : str + The name of the current node. + + node : nn.Module + The current node of nn.Module to mutate. + + Returns + ------ + ret_node: Any + The new node to replace current node. + """ + if isinstance(node, nn.Linear) and ( + not is_final_fc(name) or self.config.quantize_final_fc + ): + weight_name = f"{name}.weight" + self.quant_map.param_map[weight_name] = [f"{name}.q_weight", f"{name}.q_scale"] + self.quant_map.map_func[weight_name] = partial( + self.config.quantize_weight, + output_transpose=self.config.linear_weight_layout == "KN", + ) + return GroupQuantizeLinear.from_linear(node, self.config) + if isinstance(node, nn.Embedding) and self.config.quantize_embedding: + weight_name = f"{name}.weight" + self.quant_map.param_map[weight_name] = [f"{name}.q_weight", f"{name}.q_scale"] + self.quant_map.map_func[weight_name] = self.config.quantize_weight + return GroupQuantizeEmbedding.from_embedding(node, self.config) + if isinstance(node, MixtralExperts): + weight_name = f"{name}.weight" + self.quant_map.param_map[weight_name] = [f"{name}.q_weight", f"{name}.q_scale"] + self.quant_map.map_func[weight_name] = self.config.quantize_weight + return GroupQuantizeMixtralExperts.from_mixtral_experts(node, self.config) + return self.visit(name, node) + + model.to(dtype=self.model_dtype) + mutator = _Mutator(self, quant_map) + model = mutator.visit(name_prefix, model) + return model + + def _dequantize( + self, + weight: te.Tensor, + scale: te.Tensor, + axis: int, + out_shape: Optional[List[tir.PrimExpr]] = None, + ): + tir_max_int = tir.const(self.max_int_value, self.model_dtype) + float_weight = convert_uint_to_float( + weight, + DataType(self.quantize_dtype).bits, + self.num_elem_per_storage, + self.storage_dtype, + self.model_dtype, + axis=axis, + out_shape=out_shape, + ) + if out_shape is None: + out_shape = weight.shape + out_shape[axis] *= self.num_elem_per_storage + axis = axis if axis >= 0 else len(out_shape) + axis + return te.compute( + shape=out_shape, + fcompute=lambda *idx: tir.multiply( + tir.subtract( + float_weight(*idx), + tir_max_int, + ), + scale(*idx[:axis], idx[axis] // self.group_size, *idx[axis + 1 :]), + ), + name="dequantize", + ) + + def quantize_weight( + self, weight: NDArray, axis: int = -1, output_transpose: bool = False + ) -> List[NDArray]: + """ + Quantize weight with group quantization + + Parameters + ---------- + weight : NDArray + The original weight. + + axis : int + The group axis. + + output_transpose : bool + Whether to transpose the output quantized weight. Only 2D weight is supported. + + Returns + ------ + ret: List[NDArray] + The list of group quantized weights. + """ + device = weight.device + device_type = device.MASK2STR[device.device_type] + axis = axis if axis >= 0 else len(weight.shape) + axis + + def _create_quantize_func() -> IRModule: + bb = relax.BlockBuilder() # pylint: disable=invalid-name + weight_var = relax.Var("weight", relax.TensorStructInfo(weight.shape, weight.dtype)) + with bb.function(name="main", params=[weight_var]): + with bb.dataflow(): + lv = bb.emit_te(self._quantize, weight_var, axis, output_transpose) + gv = bb.emit_output(lv) # pylint: disable=invalid-name + bb.emit_func_output(gv) + return bb.finalize() + + def _compile_quantize_func(mod: IRModule) -> Callable: + if device_type in ["cuda", "rocm", "metal", "vulkan"]: + target = Target.current() + if target is None: + target = Target.from_device(device) + with target: + mod = dl.ApplyDefaultSchedule( # type: ignore # pylint: disable=not-callable + dl.gpu.Reduction(), + dl.gpu.GeneralReduction(), + dl.gpu.Fallback(), + )(mod) + elif device_type == "cpu": + target = "llvm" + mod = relax.transform.LegalizeOps()(mod) + else: + raise NotImplementedError(f"Device type {device_type} is not supported") + ex = relax.build(mod, target=target) + vm = relax.VirtualMachine(ex, device) # pylint: disable=invalid-name + return vm["main"] + + key = ( + f"({weight.shape}, {weight.dtype}, {device_type}, " + f"axis={axis}, output_transpose={output_transpose})" + ) + quantize_func = self._quantize_func_cache.get(key, None) + if quantize_func is None: + logger.info("Compiling quantize function for key: %s", key) + quantize_func = _compile_quantize_func(_create_quantize_func()) + self._quantize_func_cache[key] = quantize_func + return quantize_func(weight) + + def _quantize( # pylint: disable=too-many-locals + self, + weight: te.Tensor, + axis: int = -1, + output_transpose: bool = False, + ) -> Tuple[te.Tensor, te.Tensor]: + """Group quantization for weight tensor, defined in tensor expression.""" + max_int = tir.const(self.max_int_value, self.model_dtype) + shape = weight.shape # pylint: disable=invalid-name + axis = axis if axis >= 0 else len(shape) + axis + k = shape[axis] + quantize_dtype = DataType(self.quantize_dtype) + # compute scale per group + r = te.reduce_axis((0, self.group_size), name="r") # pylint: disable=invalid-name + num_group = tir.ceildiv(k, self.group_size) + scale_shape = (*shape[:axis], num_group, *shape[axis + 1 :]) + max_abs = te.compute( + shape=scale_shape, + fcompute=lambda *idx: te.max( + tir.if_then_else( + idx[axis] * self.group_size + r < k, + te.abs(weight(*idx[:axis], idx[axis] * self.group_size + r, *idx[axis + 1 :])), + te.min_value(self.model_dtype), + ), + axis=r, + ), + name="max_abs_value", + ) + scale = te.compute( + scale_shape, + lambda *idx: max_abs(*idx).astype(self.model_dtype) / max_int, + name="scale", + ) + # compute scaled weight + scaled_weight = te.compute( + shape=weight.shape, + fcompute=lambda *idx: tir.min( + tir.max( + tir.round( + weight(*idx) + / scale(*idx[:axis], idx[axis] // self.group_size, *idx[axis + 1 :]) + + max_int + ), + tir.const(0, self.model_dtype), + ), + max_int * 2, + ).astype(self.storage_dtype), + ) + # compute quantized weight per storage + r = te.reduce_axis((0, self.num_elem_per_storage), name="r") # pylint: disable=invalid-name + num_storage = self.num_storage_per_group * num_group + quantized_weight_shape = (*shape[:axis], num_storage, *shape[axis + 1 :]) + quantized_weight = te.compute( + shape=quantized_weight_shape, + fcompute=lambda *idx: tir.sum( + tir.if_then_else( + idx[axis] * self.num_elem_per_storage + r < k, + scaled_weight( + *idx[:axis], idx[axis] * self.num_elem_per_storage + r, *idx[axis + 1 :] + ) + << (r * quantize_dtype.bits), + 0, + ), + axis=r, + ), + name="weight", + ) + if output_transpose: + if len(quantized_weight.shape) != 2 or len(scale.shape) != 2: + raise ValueError( + "Does not support transpose output quantized weight with ndim != 2" + ) + quantized_weight = topi.transpose(quantized_weight) + scale = topi.transpose(scale) + return quantized_weight, scale + + +class GroupQuantizeLinear(nn.Module): # pylint: disable=too-many-instance-attributes + """An nn.Linear module with group quantization""" + + def __init__( # pylint: disable=too-many-arguments + self, + in_features: int, + out_features: Union[int, tir.Var], + config: GroupQuantize, + bias: bool = True, + out_dtype: Optional[str] = None, + ) -> None: + super().__init__() + self.in_features = in_features + self.out_features = out_features + self.out_dtype = out_dtype + self.config = config + num_group = tir.ceildiv(in_features, config.group_size) + if config.linear_weight_layout == "KN": + self.q_weight = nn.Parameter( + (config.num_storage_per_group * num_group, out_features), config.storage_dtype + ) + self.q_scale = nn.Parameter((num_group, out_features), config.model_dtype) + else: + self.q_weight = nn.Parameter( + (out_features, config.num_storage_per_group * num_group), config.storage_dtype + ) + self.q_scale = nn.Parameter((out_features, num_group), config.model_dtype) + if bias: + self.bias = nn.Parameter( + (out_features,), config.model_dtype if out_dtype is None else out_dtype + ) + else: + self.bias = None + + @staticmethod + def from_linear(src: nn.Linear, config: GroupQuantize) -> "GroupQuantizeLinear": + """ + Converts a non-quantized nn.Linear to a group quantized GroupQuantizeLinear + + Parameters + ---------- + src : nn.Linear + The non-quantized nn.Linear. + + config : GroupQuantize + The group quantization config. + + Returns + ------- + ret : GroupQuantizeLinear + The group quantized GroupQuantizeLinear layer. + """ + # For dynamic shape, src.out_features is `"name"`; src.weight.shape[0] is `tir.Var("name")` + out_features, in_features = src.weight.shape + quantized_linear = GroupQuantizeLinear( + in_features=in_features, + out_features=out_features, + config=config, + bias=getattr(src, "bias", None) is not None, + out_dtype=src.out_dtype, + ) + if quantized_linear.bias is not None: + quantized_linear.bias.attrs = src.bias.attrs + if "shard_strategy" in src.weight.attrs: + shard = src.weight.attrs["shard_strategy"] + _apply_sharding(shard, f"{shard.name}_q_weight", quantized_linear.q_weight) + _apply_sharding(shard, f"{shard.name}_q_scale", quantized_linear.q_scale) + return quantized_linear + + def forward(self, x: nn.Tensor) -> nn.Tensor: # pylint: disable=invalid-name + """ + Forward method for group quantized linear layer. + + Parameters + ---------- + x : nn.Tensor + The input tensor. + + Returns + ------- + ret : nn.Tensor + The output tensor for the group quantized linear layer. + """ + w = nn.op.tensor_expr_op( # pylint: disable=invalid-name + lambda weight, scale: self.config._dequantize( # pylint: disable=protected-access + weight, + scale, + axis=self.config.linear_quant_axis, + out_shape=( + [ + ( + tir.IntImm("int64", self.out_features) + if isinstance(self.out_features, int) + else weight.shape[0] + ), # Reuse same tir.Var for symbolic shape (after Exporter) + tir.IntImm("int64", self.in_features), + ] + if self.config.linear_weight_layout == "NK" + else [ + tir.IntImm("int64", self.in_features), + ( + tir.IntImm("int64", self.out_features) + if isinstance(self.out_features, int) + else weight.shape[1] + ), # Reuse same tir.Var for symbolic shape (after Exporter) + ] + ), + ), + name_hint="dequantize", + args=[self.q_weight, self.q_scale], + ) + if self.config.linear_weight_layout == "NK": + w = nn.op.permute_dims(w) # pylint: disable=invalid-name + x = nn.op.matmul(x, w, out_dtype=self.out_dtype) + if self.bias is not None: + x = x + self.bias + return x + + def to(self, dtype: Optional[str] = None) -> None: + """ + Override to() such that we do not convert bias if there is an out_dtype. + Otherwise, we might run into dtype mismatch when computing x + self.bias. + """ + self.q_weight.to(dtype=dtype) + self.q_scale.to(dtype=dtype) + if self.bias is not None and self.out_dtype is None: + self.bias.to(dtype=dtype) + if dtype is not None and isinstance(getattr(self, "dtype", None), str): + self.dtype = dtype # pylint: disable=attribute-defined-outside-init + + +class GroupQuantizeEmbedding(nn.Module): + """An nn.Embedding module with group quantization""" + + def __init__(self, num: Union[int, tir.Var], dim: int, config: GroupQuantize): + self.num = num + self.dim = dim + self.config = config + num_group = tir.ceildiv(dim, config.group_size) + self.q_weight = nn.Parameter( + (num, config.num_storage_per_group * num_group), config.storage_dtype + ) + self.q_scale = nn.Parameter((num, num_group), config.model_dtype) + + @staticmethod + def from_embedding(embedding: nn.Embedding, config: GroupQuantize) -> "GroupQuantizeEmbedding": + """ + Converts a non-quantized nn.Embedding to a group quantized GroupQuantizeEmbedding + + Parameters + ---------- + linear : nn.Embedding + The non-quantized nn.Embedding. + + config : GroupQuantize + The group quantization config. + + Returns + ------- + ret : GroupQuantizeEmbedding + The group quantized GroupQuantizeEmbedding layer. + """ + num, dim = embedding.weight.shape + return GroupQuantizeEmbedding(num, dim, config) + + def forward(self, x: nn.Tensor): # pylint: disable=invalid-name + """ + Forward method for group quantized embedding layer. + + Parameters + ---------- + x : nn.Tensor + The input tensor. + + Returns + ------- + ret : nn.Tensor + The output tensor for the embedding layer. + """ + w = nn.op.tensor_expr_op( # pylint: disable=invalid-name + lambda weight, scale: self.config._dequantize( # pylint: disable=protected-access + weight, + scale, + axis=-1, + out_shape=[ + ( + tir.IntImm("int64", self.num) + if isinstance(self.num, int) + else weight.shape[0] + ), # Reuse same tir.Var for symbolic shape (after Exporter) + tir.IntImm("int64", self.dim), + ], + ), + name_hint="dequantize", + args=[self.q_weight, self.q_scale], + ) + if x.ndim == 1: + return nn.op.take(w, x, axis=0) + return nn.op.reshape( + nn.op.take(w, nn.op.reshape(x, shape=[-1]), axis=0), + shape=[*x.shape, self.dim], + ) + + def lm_head_forward(self, x: nn.Tensor): + """The lm_head forwarding, which dequantizes the weight + and multiplies it with the input tensor. + + Parameters + ---------- + x : nn.Tensor + The input tensor. + + Returns + ------- + ret : nn.Tensor + The output tensor for the lm_head layer. + """ + w = nn.op.tensor_expr_op( # pylint: disable=invalid-name + lambda weight, scale: self.config._dequantize( # pylint: disable=protected-access + weight, + scale, + axis=-1, + out_shape=[ + ( + tir.IntImm("int64", self.num) + if isinstance(self.num, int) + else weight.shape[0] + ), + tir.IntImm("int64", self.dim), + ], + ), + name_hint="dequantize", + args=[self.q_weight, self.q_scale], + ) + w = nn.op.permute_dims(w) + return nn.op.matmul(x, w, out_dtype="float32") + + +class GroupQuantizeMixtralExperts(nn.Module): # pylint: disable=too-many-instance-attributes + """An MixtralExperts module with group quantization""" + + def __init__( + self, + num_local_experts, + in_features, + out_features, + config: GroupQuantize, + ): # pylint: disable=too-many-arguments + self.num_local_experts = num_local_experts + self.in_features = in_features + self.out_features = out_features + self.config = config + num_group = tir.ceildiv(in_features, config.group_size) + self.q_weight = nn.Parameter( + (num_local_experts, out_features, config.num_storage_per_group * num_group), + config.storage_dtype, + ) + self.q_scale = nn.Parameter( + (num_local_experts, out_features, num_group), config.model_dtype + ) + self.quantize_dtype = config.quantize_dtype + self.group_size = config.group_size + self.dtype = config.model_dtype + if config.linear_weight_layout == "KN": + raise NotImplementedError("GroupQuantizeMixtralExperts does not support KN layout now.") + + @staticmethod + def from_mixtral_experts( + src: "MixtralExperts", config: GroupQuantize + ) -> "GroupQuantizeMixtralExperts": + """ + Converts a non-quantized MixtralExperts to a group quantized GroupQuantizeMixtralExperts + + Parameters + ---------- + src : MixtralExperts + The non-quantized MixtralExperts + + config : GroupQuantize + The group quantization config. + + Returns + ------- + ret : GroupQuantizeMixtralExperts + The group quantized GroupQuantizeMixtralExperts layer. + """ + quantized_mistral_experts = GroupQuantizeMixtralExperts( + num_local_experts=src.num_local_experts, + in_features=src.in_features, + out_features=src.out_features, + config=config, + ) + if "shard_strategy" in src.weight.attrs: + shard = src.weight.attrs["shard_strategy"] + _apply_sharding(shard, f"{shard.name}_q_weight", quantized_mistral_experts.q_weight) + _apply_sharding(shard, f"{shard.name}_q_scale", quantized_mistral_experts.q_scale) + return quantized_mistral_experts + + def forward(self, x: nn.Tensor, indptr: nn.Tensor) -> nn.Tensor: # pylint: disable=invalid-name + """Forward method for group quantized mistral experts. + + Parameters + ---------- + x : nn.Tensor + The input tensor. + + indptr: nn.Tensor + The indptr tensor + + single_batch_decode: bool + Whether to use single-batch decode + + Returns + ------- + ret : nn.Tensor + The output tensor for the group quantized mistral experts layer. + """ + from mlc_chat.op import moe_matmul # pylint: disable=import-outside-toplevel + + assert x.ndim == 2 + if indptr.ndim == 2: # single-batch + assert indptr.shape[0] == 1 + return moe_matmul.dequantize_gemv( + x, + self.q_weight, + self.q_scale, + indptr, + quantize_dtype=self.quantize_dtype, + group_size=self.group_size, + ) + assert indptr.ndim == 1 + return moe_matmul.dequantize_group_gemm( + x, + self.q_weight, + self.q_scale, + indptr, + quantize_dtype=self.quantize_dtype, + indptr_dtype=indptr.dtype, + group_size=self.group_size, + ) + + +def _apply_sharding(shard, name: str, weight: nn.Parameter): + if isinstance(shard, tp.ShardSingleDim): + weight.attrs["shard_strategy"] = tp.ShardSingleDim( + name=name, + dim=shard.dim, + segs=shard.segs, + ) + else: + raise NotImplementedError(f"Unknowing sharding strategy: {shard}") diff --git a/python/mlc_chat/quantization/no_quantization.py b/python/mlc_chat/quantization/no_quantization.py new file mode 100644 index 0000000..b1944c1 --- /dev/null +++ b/python/mlc_chat/quantization/no_quantization.py @@ -0,0 +1,14 @@ +"""The no quantization config""" +from dataclasses import dataclass + + +@dataclass +class NoQuantize: # pylint: disable=too-many-instance-attributes + """Configuration for no quantization""" + + name: str + kind: str + model_dtype: str # "float16", "float32" + + def __post_init__(self): + assert self.kind == "no-quant" diff --git a/python/mlc_chat/quantization/quantization.py b/python/mlc_chat/quantization/quantization.py new file mode 100644 index 0000000..3fab898 --- /dev/null +++ b/python/mlc_chat/quantization/quantization.py @@ -0,0 +1,120 @@ +"""A centralized registry of all existing quantization methods and their configurations.""" +from typing import Any, Dict + +from .awq_quantization import AWQQuantize +from .ft_quantization import FTQuantize +from .group_quantization import GroupQuantize +from .no_quantization import NoQuantize + +Quantization = Any +"""Quantization is an object that represents an quantization algorithm. It is required to +have the following fields: + + name : str + The name of the quantization algorithm, for example, "q4f16_1". + + kind : str + The kind of quantization algorithm, for example, "group-quant", "faster-transformer". + +It is also required to have the following method: + + def quantize_model(self, module: nn.Module) -> nn.Module: + ... + + def quantize_weight(self, weight: tvm.runtime.NDArray) -> List[tvm.runtime.NDArray]: + ... +""" + +QUANTIZATION: Dict[str, Quantization] = { + "q0f16": NoQuantize( + name="q0f16", + kind="no-quant", + model_dtype="float16", + ), + "q0f32": NoQuantize( + name="q0f32", + kind="no-quant", + model_dtype="float32", + ), + "q3f16_0": GroupQuantize( + name="q3f16_0", + kind="group-quant", + group_size=40, + quantize_dtype="int3", + storage_dtype="uint32", + model_dtype="float16", + linear_weight_layout="KN", + quantize_embedding=True, + quantize_final_fc=True, + ), + "q3f16_1": GroupQuantize( + name="q3f16_1", + kind="group-quant", + group_size=40, + quantize_dtype="int3", + storage_dtype="uint32", + model_dtype="float16", + linear_weight_layout="NK", + quantize_embedding=True, + quantize_final_fc=True, + ), + "q4f16_0": GroupQuantize( + name="q4f16_0", + kind="group-quant", + group_size=32, + quantize_dtype="int4", + storage_dtype="uint32", + model_dtype="float16", + linear_weight_layout="KN", + quantize_embedding=True, + quantize_final_fc=True, + ), + "q4f16_1": GroupQuantize( + name="q4f16_1", + kind="group-quant", + group_size=32, + quantize_dtype="int4", + storage_dtype="uint32", + model_dtype="float16", + linear_weight_layout="NK", + quantize_embedding=True, + quantize_final_fc=True, + ), + "q4f32_1": GroupQuantize( + name="q4f32_1", + kind="group-quant", + group_size=32, + quantize_dtype="int4", + storage_dtype="uint32", + model_dtype="float32", + linear_weight_layout="NK", + quantize_embedding=True, + quantize_final_fc=True, + ), + "q4f16_2": GroupQuantize( + name="q4f16_2", + kind="group-quant", + group_size=32, + quantize_dtype="int4", + storage_dtype="uint32", + model_dtype="float16", + linear_weight_layout="NK", + quantize_embedding=False, + quantize_final_fc=False, + ), + "q4f16_autoawq": AWQQuantize( + name="q4f16_autoawq", + kind="awq", + group_size=128, + quantize_dtype="int4", + storage_dtype="uint32", + model_dtype="float16", + ), + "q4f16_ft": FTQuantize( + name="q4f16_ft", + kind="ft-quant", + quantize_dtype="int4", + storage_dtype="int8", + model_dtype="float16", + ), +} diff --git a/python/mlc_chat/quantization/utils.py b/python/mlc_chat/quantization/utils.py new file mode 100644 index 0000000..4159da8 --- /dev/null +++ b/python/mlc_chat/quantization/utils.py @@ -0,0 +1,47 @@ +"""Common utilities for quantization""" + +from typing import List, Optional + +from tvm import te, tir + + +def convert_uint_to_float( # pylint: disable=too-many-arguments + weight: te.Tensor, + bits: int, + num_elem_per_storage: int, + storage_dtype: str, + model_dtype: str, + axis: int = -1, + out_shape: Optional[List[tir.PrimExpr]] = None, + ft_reorder: Optional[bool] = False, +) -> te.Tensor: + """Convert a quantized uint weight to an unquantized float weight.""" + tir_bin_mask = tir.const((1 << bits) - 1, storage_dtype) + if out_shape is None: + out_shape = weight.shape + out_shape[axis] *= num_elem_per_storage + axis = axis if axis >= 0 else len(out_shape) + axis + return te.compute( + shape=out_shape, + fcompute=lambda *idx: tir.bitwise_and( + tir.shift_right( + weight(*idx[:axis], idx[axis] // num_elem_per_storage, *idx[axis + 1 :]), + ( + ( + (idx[axis] % num_elem_per_storage) % 2 * 4 + + (idx[axis] % num_elem_per_storage) // 2 + ) + * bits + if ft_reorder + else (idx[axis] % num_elem_per_storage) * bits + ).astype(storage_dtype), + ), + tir_bin_mask, + ).astype(model_dtype), + ) + + +def is_final_fc(name: str) -> bool: + """Determines whether the parameter is the last layer based on its name.""" + # TODO: use more specious condition to determine final fc # pylint: disable=fixme + return name in ["head", "lm_head"] diff --git a/python/mlc_chat/rest.py b/python/mlc_chat/rest.py new file mode 100644 index 0000000..d2911a1 --- /dev/null +++ b/python/mlc_chat/rest.py @@ -0,0 +1,492 @@ +# pylint: disable=missing-docstring,fixme +import argparse +import ast +import asyncio +import dataclasses +import json +from contextlib import asynccontextmanager +from typing import Dict, List + +import numpy as np +import uvicorn +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import StreamingResponse + +from mlc_chat.chat_module import GenerationConfig +from mlc_chat.support.random import set_global_random_seed + +from .chat_module import ChatModule +from .interface.openai_api import ( + ChatCompletionRequest, + ChatCompletionResponse, + ChatCompletionResponseChoice, + ChatCompletionResponseStreamChoice, + ChatCompletionStreamResponse, + ChatMessage, + CompletionRequest, + CompletionResponse, + CompletionResponseChoice, + CompletionResponseStreamChoice, + CompletionStreamResponse, + DeltaMessage, + EmbeddingsRequest, + EmbeddingsResponse, + ToolCalls, + ToolChoice, + UsageInfo, + VisualStudioCodeCompletionRequest, + VisualStudioCodeCompletionResponse, +) + + +@dataclasses.dataclass +class RestAPIArgs: + """RestAPIArgs is the dataclass that organizes the arguments used for starting a REST API + server.""" + + model: str = dataclasses.field( + metadata={ + "help": ( + """ + The model folder after compiling with MLC-LLM build process. The parameter + can either be the model name with its quantization scheme + (e.g. ``Llama-2-7b-chat-hf-q4f16_1``), or a full path to the model + folder. In the former case, we will use the provided name to search + for the model folder over possible paths. + """ + ) + } + ) + lib_path: str = dataclasses.field( + default=None, + metadata={ + "help": ( + """ + The full path to the model library file to use (e.g. a ``.so`` file). + """ + ) + }, + ) + device: str = dataclasses.field( + default="auto", + metadata={ + "help": ( + """ + The description of the device to run on. User should provide a string in the + form of 'device_name:device_id' or 'device_name', where 'device_name' is one of + 'cuda', 'metal', 'vulkan', 'rocm', 'opencl', 'auto' (automatically detect the + local device), and 'device_id' is the device id to run on. If no 'device_id' + is provided, it will be set to 0 by default. + """ + ) + }, + ) + host: str = dataclasses.field( + default="127.0.0.1", + metadata={ + "help": ( + """ + The host at which the server should be started, defaults to ``127.0.0.1``. + """ + ) + }, + ) + port: int = dataclasses.field( + default=8000, + metadata={ + "help": ( + """ + The port on which the server should be started, defaults to ``8000``. + """ + ) + }, + ) + random_seed: int = dataclasses.field( + default=None, + metadata={ + "help": ( + """ + The random seed to initialize all the RNG used in mlc-chat. By default, + no seed is set. + """ + ) + }, + ) + + +def convert_args_to_argparser() -> argparse.ArgumentParser: + """Convert from RestAPIArgs to an equivalent ArgumentParser.""" + args = argparse.ArgumentParser("MLC Chat REST API") + for field in dataclasses.fields(RestAPIArgs): + name = field.name.replace("_", "-") + field_name = f"--{name}" + # `kwargs` contains `help`, `choices`, and `action` + kwargs = field.metadata.copy() + if field.type == bool: + # boolean arguments do not need to specify `type` + args.add_argument(field_name, default=field.default, **kwargs) + else: + args.add_argument(field_name, type=field.type, default=field.default, **kwargs) + return args + + +session: Dict[str, ChatModule] = {} + + +@asynccontextmanager +async def lifespan(_app: FastAPI): + if ARGS.random_seed is not None: + set_global_random_seed(ARGS.random_seed) + chat_mod = ChatModule( + model=ARGS.model, + device=ARGS.device, + model_lib_path=ARGS.lib_path, + ) + session["chat_mod"] = chat_mod + yield + session.clear() + + +origins = ["*"] + +app = FastAPI(lifespan=lifespan) +app.add_middleware( + CORSMiddleware, + allow_origins=origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +class AsyncCompletionStream: + def __init__(self, generation_config: GenerationConfig): + self.generation_config = generation_config + + def __aiter__(self): + return self + + async def get_next_msg(self): + # pylint: disable=protected-access + if not session["chat_mod"]._stopped(): + session["chat_mod"]._decode(generation_config=self.generation_config) + msg = session["chat_mod"]._get_message() + return msg + # pylint: enable=protected-access + raise StopAsyncIteration + + async def __anext__(self): + if not session["chat_mod"]._stopped(): + task = asyncio.create_task(self.get_next_msg()) + msg = await task + return msg + raise StopAsyncIteration + + +def add_function_call(prompt: List[ChatMessage], function_string: str): + # update content of the last input message to include function string + user_query = prompt[-1].content + prompt[-1].content = f"<> {user_query} <> {function_string}\n" + + +def function_call_util(request: ChatCompletionRequest): + """Performs the necessary actions to add function calls to the prompt + returns True if function calls are added to the prompt else returns False + TODO: Check function name in tools.function['name'] + TODO: Currently auto mode default to generating function calls instead of smartly + checking weather to generate function calls or not + """ + + # return if no tools are provided + if request.tools is None: + return False + + # skip if tool_choice is set to none + if isinstance(request.tool_choice, str) and request.tool_choice == "none": + return False + + if isinstance(request.tool_choice, ToolChoice): + # force the model to use a specific function provided by tool_choice + if request.tool_choice.type != "function": + raise ValueError("Only 'function' tool choice is supported") + for tool in request.tools: + if tool.function["name"] == request.tool_choice.function["name"]: + add_function_call(request.messages, json.dumps(tool.function)) + return True + raise ValueError("ToolChoice.function.name not found in tools") + + if isinstance(request.tool_choice, str): + # Add all the functions to the input prompt + function_list = [] + for tool in request.tools: + if tool.type == "function": + function_list.append(tool.function) + else: + raise ValueError("Only 'function' tool.type is supported") + add_function_call(request.messages, json.dumps(function_list)) + else: + raise ValueError("Invalid toolChoice instance type") + return True + + +def convert_function_str_to_json(stringified_calls): + def parse_function_call(call_str): + node = ast.parse(call_str, mode="eval") + call_node = node.body + if isinstance(call_node, ast.Call): + name = call_node.func.id + arguments = {} + for keyword in call_node.keywords: + arguments[keyword.arg] = ast.literal_eval(keyword.value) + return {"name": name, "arguments": arguments} + return None + + calls = ast.literal_eval(stringified_calls) + result = [parse_function_call(call_str) for call_str in calls] + return result + + +@app.post("/v1/chat/completions") +async def request_chat_completion(request: ChatCompletionRequest): + """ + Creates model response for the given chat conversation. + The messages field contains a list of messages (describing the conversation history). eg: + ```"messages": [{"role": "user", "content": "What's my name?"}, + {"role": "assistant", "content": "Your name is Llama."}, + {"role": "user", "content": "No, that's your name. My name is X."}, + {"role": "assistant", "content": "Ah, my apologies! Your name is X! "}, + {"role": "user", "content": "What is the meaning of life?"}, + ] + ``` + ] + """ + generation_config = GenerationConfig( + temperature=request.temperature, + repetition_penalty=request.repetition_penalty, + presence_penalty=request.presence_penalty, + frequency_penalty=request.frequency_penalty, + top_p=request.top_p, + mean_gen_len=request.mean_gen_len, + max_gen_len=request.max_gen_len, + n=request.n, + stop=request.stop, + ) + + session["chat_mod"].reset_chat() # Reset previous history, KV cache, etc. + + use_function_call = function_call_util(request) + + if request.stream: + session["chat_mod"]._prefill( # pylint: disable=protected-access + input=request.messages, + generation_config=generation_config, + ) + + async def iter_response(): + prev_txt = "" + async for content in AsyncCompletionStream(generation_config=generation_config): + if content: + # Remove the replacement character (U+FFFD) from the response + # This is to handle emojis. An emoji might be made up of multiple tokens. + # In the Rest streaming setting, if an emoji gets truncated in the middle of + # its encoded byte sequence, a replacement character will appear. + valid_content = content.replace("�", "") + chunk = ChatCompletionStreamResponse( + choices=[ + ChatCompletionResponseStreamChoice( + index=0, + delta=DeltaMessage( + role="assistant", content=valid_content[len(prev_txt) :] + ), + finish_reason="stop", + ) + ] + ) + prev_txt = valid_content + yield f"data: {chunk.json(exclude_unset=True)}\n\n" + yield "data: [DONE]\n\n" + + return StreamingResponse(iter_response(), media_type="text/event-stream") + msg = session["chat_mod"].generate( + prompt=request.messages, generation_config=generation_config, stateless=True + ) + if isinstance(msg, str): + msg = [msg] + + choices = [] + for index, msg_i in enumerate(msg): + if use_function_call: + choices.append( + ChatCompletionResponseChoice( + index=index, + message=ChatMessage( + role="assistant", + content=None, + tool_calls=[ + ToolCalls( + function=fn_json_obj, + ) + for fn_json_obj in convert_function_str_to_json(msg_i) + ], + ), + finish_reason="tool_calls", + ) + ) + else: + choices.append( + ChatCompletionResponseChoice( + index=index, + message=ChatMessage( + role="assistant", + content=msg_i, + ), + finish_reason="stop", + ) + ) + + return ChatCompletionResponse( + choices=choices, + # TODO: Fill in correct usage info + usage=UsageInfo(prompt_tokens=0, completion_tokens=0, total_tokens=0), + ) + + +@app.post("/v1/completions") +async def request_completion(request: CompletionRequest): + """ + Creates a completion for a given prompt. + """ + + generation_config = GenerationConfig( + temperature=request.temperature, + repetition_penalty=request.repetition_penalty, + presence_penalty=request.presence_penalty, + frequency_penalty=request.frequency_penalty, + top_p=request.top_p, + mean_gen_len=request.mean_gen_len, + max_gen_len=request.max_gen_len, + n=request.n, + stop=request.stop, + ) + + session["chat_mod"].reset_chat() + # Langchain's load_qa_chain.run expects the input to be a list with the query + if isinstance(request.prompt, list): + if len(request.prompt) > 1: + raise ValueError( + """ + The /v1/completions endpoint currently only supports single message prompts. + Please ensure your request contains only one message + """ + ) + prompt = request.prompt[0] + else: + prompt = request.prompt + + if request.stream: + session["chat_mod"]._prefill( # pylint: disable=protected-access + input=prompt, + generation_config=generation_config, + ) + + async def iter_response(): + prev_txt = "" + async for content in AsyncCompletionStream(generation_config=generation_config): + if content: + chunk = CompletionStreamResponse( + choices=[ + CompletionResponseStreamChoice( + index=0, + text=content[len(prev_txt) :], + finish_reason="stop", + ) + ] + ) + prev_txt = content + yield f"data: {chunk.json(exclude_unset=True)}\n\n" + yield "data: [DONE]\n\n" + + return StreamingResponse(iter_response(), media_type="text/event-stream") + msg = session["chat_mod"].generate(prompt=prompt, generation_config=generation_config) + if isinstance(msg, str): + msg = [msg] + return CompletionResponse( + choices=[ + CompletionResponseChoice(index=index, text=msg[index]) for index in range(len(msg)) + ], + # TODO: Fill in correct usage info + usage=UsageInfo(prompt_tokens=0, completion_tokens=0, total_tokens=0), + ) + + +@app.post("/v1/embeddings") +async def request_embeddings(request: EmbeddingsRequest): + """ + Gets embedding for some text. + """ + inps = [] + if isinstance(request.input, str): + inps.append(request.input) + elif isinstance(request.input, list): + inps = request.input + else: + assert f"Invalid input type {type(request.input)}" + + data = [] + for i, inp in enumerate(inps): + session["chat_mod"].reset_chat() + emb = session["chat_mod"].embed_text(input=inp).numpy() + mean_emb = np.squeeze(np.mean(emb, axis=1), axis=0) + norm_emb = mean_emb / np.linalg.norm(mean_emb) + data.append({"object": "embedding", "embedding": norm_emb.tolist(), "index": i}) + # TODO: Fill in correct usage info + return EmbeddingsResponse( + data=data, usage=UsageInfo(prompt_tokens=0, completion_tokens=0, total_tokens=0) + ) + + +@app.post("/chat/reset") +async def reset(): + """ + Reset the chat for the currently initialized model. + """ + session["chat_mod"].reset_chat() + + +@app.get("/stats") +async def read_stats(): + """ + Get the runtime stats. + """ + return session["chat_mod"].stats() + + +@app.get("/verbose_stats") +async def read_stats_verbose(): + """ + Get the verbose runtime stats. + """ + return session["chat_mod"].stats(verbose=True) + + +@app.post("/v1/llm-vscode/completions") +async def request_llm_vscode(request: VisualStudioCodeCompletionRequest): + """ + Creates a vscode code completion for a given prompt. + Follows huggingface LSP (https://github.com/huggingface/llm-ls) + """ + generation_config = GenerationConfig( + temperature=request.parameters.temperature, + top_p=request.parameters.top_p, + mean_gen_len=request.parameters.max_new_tokens, + max_gen_len=request.parameters.max_new_tokens, + ) + msg = session["chat_mod"].generate(prompt=request.inputs, generation_config=generation_config) + + return VisualStudioCodeCompletionResponse(generated_text=msg) + + +ARGS = convert_args_to_argparser().parse_args() +if __name__ == "__main__": + uvicorn.run("mlc_chat.rest:app", host=ARGS.host, port=ARGS.port, reload=False, access_log=False) diff --git a/python/mlc_chat/serve/__init__.py b/python/mlc_chat/serve/__init__.py new file mode 100644 index 0000000..59185ec --- /dev/null +++ b/python/mlc_chat/serve/__init__.py @@ -0,0 +1,11 @@ +"""Subdirectory of serving.""" + +# Load MLC LLM library by importing base +from .. import base +from .async_engine import AsyncThreadedEngine +from .config import EngineMode, GenerationConfig, KVCacheConfig +from .data import Data, RequestStreamOutput, TextData, TokenData +from .engine import Engine +from .grammar import BNFGrammar, GrammarStateMatcher +from .request import Request +from .server import PopenServer diff --git a/python/mlc_chat/serve/_ffi_api.py b/python/mlc_chat/serve/_ffi_api.py new file mode 100644 index 0000000..282c80c --- /dev/null +++ b/python/mlc_chat/serve/_ffi_api.py @@ -0,0 +1,6 @@ +"""FFI APIs for mlc_chat.serve""" +import tvm._ffi + +# Exports functions registered via TVM_REGISTER_GLOBAL with the "mlc.serve" prefix. +# e.g. TVM_REGISTER_GLOBAL("mlc.serve.TextData") +tvm._ffi._init_api("mlc.serve", __name__) # pylint: disable=protected-access diff --git a/python/mlc_chat/serve/async_engine.py b/python/mlc_chat/serve/async_engine.py new file mode 100644 index 0000000..74058ea --- /dev/null +++ b/python/mlc_chat/serve/async_engine.py @@ -0,0 +1,330 @@ +"""The MLC LLM Asynchronous Serving Engine. +Acknowledgment: Part of the code was adapted from the vLLM project. +""" + +import asyncio +import sys +import threading +from typing import Any, AsyncGenerator, Dict, List, Optional, Tuple, Union + +import tvm + +from ..streamer import TextStreamer +from ..tokenizer import Tokenizer +from . import data +from .config import EngineMode, GenerationConfig, KVCacheConfig +from .engine import ModelInfo, _estimate_max_total_sequence_length, _process_model_args +from .event_trace_recorder import EventTraceRecorder +from .request import Request + + +class AsyncRequestStream: + """The asynchronous stream for requests. + + Each request has its own unique stream. + The stream exposes the method `push` for engine to push new generated + delta text to the stream, and the method `finish` for engine to mark + the finish of generation. + + The stream implements `__aiter__` and `__anext__`, which the engine + can use to iterates all the generated tokens in order asynchronously. + """ + + # The asynchronous queue to hold elements of + # - either a tuple of (str, int, List[str], Optional[str]), denoting the + # delta output text, the number of delta tokens, the logprob JSON strings + # of delta tokens, and the optional finish reason respectively, + # - or an exception. + if sys.version_info >= (3, 9): + _queue: asyncio.Queue[ # pylint: disable=unsubscriptable-object + Union[Tuple[str, int, Optional[List[str]], Optional[str]], Exception] + ] + else: + _queue: asyncio.Queue + # The finish flag. + _finished: bool + + def __init__(self) -> None: + self._queue = asyncio.Queue() + self._finished = False + + def push( + self, + item_or_exception: Union[Tuple[str, int, Optional[List[str]], Optional[str]], Exception], + ) -> None: + """Push a new token to the stream.""" + if self._finished: + # No new item is expected after finish. + self._queue.put_nowait( + RuntimeError( + "The request has already finished. " + "The stream is not supposed to accept new items." + ) + ) + return + self._queue.put_nowait(item_or_exception) + + def finish(self) -> None: + """Mark the finish of the generation in the stream.""" + self._queue.put_nowait(StopIteration()) + self._finished = True + + def __aiter__(self): + return self + + async def __anext__(self) -> Tuple[str, int, Optional[List[str]], Optional[str]]: + result = await self._queue.get() + if isinstance(result, StopIteration): + raise StopAsyncIteration + if isinstance(result, Exception): + raise result + return result + + +class AsyncThreadedEngine: # pylint: disable=too-many-instance-attributes + """The asynchronous engine for generate text asynchronously, + backed by ThreadedEngine. + + This class wraps a synchronous threaded engine that runs on + a standalone thread inside, and exports the asynchronous `generate` + method as the main text generation interface, which yields the + generated tokens. The internal threaded engine keeps running an + event loop that drives the engine. + + Parameters + ---------- + models : Union[ModelInfo, List[ModelInfo]] + One or a list of model info (specifying which models to load and + which device to load to) to launch the engine. + + kv_cache_config : KVCacheConfig + The configuration of the paged KV cache. + + engine_mode : Optional[EngineMode] + The Engine execution mode. + + enable_tracing : bool + A boolean indicating if to enable event logging for requests. + """ + + def __init__( + self, + models: Union[ModelInfo, List[ModelInfo]], + kv_cache_config: KVCacheConfig, + engine_mode: Optional[EngineMode] = None, + enable_tracing: bool = False, + ) -> None: + if isinstance(models, ModelInfo): + models = [models] + ( + model_args, + config_file_paths, + tokenizer_path, + self.max_single_sequence_length, + prefill_chunk_size, + self.conv_template_name, + ) = _process_model_args(models) + self.trace_recorder = EventTraceRecorder() if enable_tracing else None + + if kv_cache_config.max_total_sequence_length is None: + kv_cache_config.max_total_sequence_length = _estimate_max_total_sequence_length( + models, config_file_paths + ) + if kv_cache_config.prefill_chunk_size is None: + kv_cache_config.prefill_chunk_size = prefill_chunk_size + elif kv_cache_config.prefill_chunk_size > prefill_chunk_size: + raise ValueError( + f"The specified prefill chunk size {kv_cache_config.prefill_chunk_size} is " + f"larger than the maximum prefill chunk size {prefill_chunk_size} supported by " + "models. Please specify a smaller prefill chunk size." + ) + + module = tvm.get_global_func("mlc.serve.create_threaded_engine", allow_missing=False)() + self._ffi = { + key: module[key] + for key in [ + "add_request", + "abort_request", + "run_background_loop", + "init_background_engine", + "exit_background_loop", + ] + } + self.tokenizer = Tokenizer(tokenizer_path) + if engine_mode is None: + # The default engine mode: non-speculative + engine_mode = EngineMode() + + # The mapping from request ids to request asynchronous stream. + self._request_tools: Dict[str, Tuple[AsyncRequestStream, TextStreamer]] = {} + + def _background_loop(): + self._ffi["init_background_engine"]( + self.max_single_sequence_length, + tokenizer_path, + kv_cache_config.asjson(), + engine_mode.asjson(), + self._request_stream_callback, + self.trace_recorder, + *model_args, + ) + self._ffi["run_background_loop"]() + + # Create the background engine-driving thread and start the loop. + self._background_loop_thread: threading.Thread = threading.Thread(target=_background_loop) + self._background_loop_thread.start() + # The main thread request handling asyncio event loop, which will + # be lazily initialized. + self._async_event_loop: Optional[asyncio.AbstractEventLoop] = None + self._terminated = False + + def terminate(self): + """Terminate the engine.""" + self._terminated = True + self._ffi["exit_background_loop"]() + self._background_loop_thread.join() + + async def generate( + self, prompt: Union[str, List[int]], generation_config: GenerationConfig, request_id: str + ) -> AsyncGenerator[Tuple[str, int, Optional[List[str]], Optional[str]], Any]: + """Asynchronous text generation interface. + The method is a coroutine that streams a tuple at a time via yield. + Each tuple is contained of + - the delta text in type str, + - the number of delta tokens in type int, + - the logprob JSON strings of delta tokens, + - the optional finish reason in type Optional[str]. + + Parameters + ---------- + prompt : Union[str, List[int]] + The input prompt in forms of text string or a list of token ids. + + generation_config : GenerationConfig + The generation config of the request. + + request_id : str + The unique identifier (in string) or this generation request. + """ + if self._terminated: + raise ValueError("The AsyncThreadedEngine has terminated.") + if self._async_event_loop is None: + # Lazily set the asyncio event loop so that the event + # loop is the main driving event loop of the process. + self._async_event_loop = asyncio.get_event_loop() + + # Create the request with the given id, input data, generation + # config and the created callback. + input_data = data.TextData(prompt) if isinstance(prompt, str) else data.TokenData(prompt) + request = Request(request_id, input_data, generation_config) + + # Create the unique stream of the request. + stream = AsyncRequestStream() + if request_id in self._request_tools: + # Report error in the stream if the request id already exists. + stream.push( + RuntimeError( + f'The request id "{request_id} already exists. ' + 'Please make sure the request id is unique."' + ) + ) + else: + # Record the stream in the tracker + self._request_tools[request_id] = (stream, TextStreamer(self.tokenizer)) + self._ffi["add_request"](request) + + # Iterate the stream asynchronously and yield the token. + try: + async for request_output in stream: + yield request_output + except (Exception, asyncio.CancelledError) as e: # pylint: disable=broad-exception-caught + await self.abort(request_id) + raise e + + async def abort(self, request_id: str) -> None: + """Generation abortion interface. + + Parameter + --------- + request_id : str + The id of the request to abort. + """ + self._abort(request_id) + + def _abort(self, request_id: str): + """Internal implementation of request abortion.""" + self._request_tools.pop(request_id, None) + self._ffi["abort_request"](request_id) + + def _request_stream_callback(self, delta_outputs: List[data.RequestStreamOutput]) -> None: + """The request stream callback function for engine to stream back + the request generation results. + + Parameters + ---------- + delta_outputs : List[data.RequestStreamOutput] + The delta output of each requests. + Check out data.RequestStreamOutput for the fields of the outputs. + + Note + ---- + This callback function uses `call_soon_threadsafe` in asyncio to + schedule the invocation in the event loop, so that the underlying + callback logic will be executed asynchronously in the future rather + than right now. + """ + # Schedule a callback run in the event loop without executing right now. + # NOTE: This function causes GIL during execution. + self._async_event_loop.call_soon_threadsafe( + self._request_stream_callback_impl, delta_outputs + ) + + def _request_stream_callback_impl(self, delta_outputs: List[data.RequestStreamOutput]) -> None: + """The underlying implementation of request stream callback.""" + for delta_output in delta_outputs: + ( + request_id, + delta_token_ids, + delta_logprob_json_strs, + finish_reason, + ) = delta_output.unpack() + tools = self._request_tools.get(request_id, None) + if tools is None: + continue + + self.record_event(request_id, event="start callback") + stream, text_streamer = tools + + self.record_event(request_id, event="start detokenization") + delta_text = text_streamer.put(delta_token_ids) + if finish_reason is not None: + delta_text += text_streamer.finish() + self.record_event(request_id, event="finish detokenization") + + # Push new delta text to the stream. + stream.push((delta_text, len(delta_token_ids), delta_logprob_json_strs, finish_reason)) + if finish_reason is not None: + stream.finish() + self._request_tools.pop(request_id, None) + self.record_event(request_id, event="finish callback") + + def record_event(self, request_id: str, event: str) -> None: + """Record a event for the the input request in the trace + recorder when the recorder exists. + + Parameters + ---------- + request_id : str + The subject request of the event. + + event : str + The event in a string name. + It can have one of the following patterns: + - "start xxx", which marks the start of event "xxx", + - "finish xxx", which marks the finish of event "xxx", + - "yyy", which marks the instant event "yyy". + The "starts" and "finishes" will be automatically paired in the trace recorder. + """ + if self.trace_recorder is None: + return + self.trace_recorder.add_event(request_id, event) diff --git a/python/mlc_chat/serve/config.py b/python/mlc_chat/serve/config.py new file mode 100644 index 0000000..ccc152a --- /dev/null +++ b/python/mlc_chat/serve/config.py @@ -0,0 +1,155 @@ +"""Configuration dataclasses used in MLC LLM serving""" + +import json +from dataclasses import asdict, dataclass, field +from typing import Dict, List, Optional + + +@dataclass +class GenerationConfig: # pylint: disable=too-many-instance-attributes + """The generation configuration dataclass. + + Parameters + ---------- + temperature : float + The value that applies to logits and modulates the next token probabilities. + + top_p : float + In sampling, only the most probable tokens with probabilities summed up to + `top_k` are kept for sampling. + + frequency_penalty : float + Positive values penalize new tokens based on their existing frequency + in the text so far, decreasing the model's likelihood to repeat the same + line verbatim. + + presence_penalty : float + Positive values penalize new tokens based on whether they appear in the text + so far, increasing the model's likelihood to talk about new topics. + + repetition_penalty : float + The penalty term that applies to logits to control token repetition in generation. + It will be suppressed when any of frequency_penalty and presence_penalty is + non-zero. + + logprobs : bool + Whether to return log probabilities of the output tokens or not. + If true, the log probabilities of each output token will be returned. + + top_logprobs : int + An integer between 0 and 5 specifying the number of most likely + tokens to return at each token position, each with an associated + log probability. + `logprobs` must be set to True if this parameter is used. + + logit_bias : Optional[Dict[int, float]] + The bias logit value added to selected tokens prior to sampling. + + max_tokens : Optional[int] + The maximum number of generated tokens, + or None, in which case the generation will not stop + until exceeding model capability or hit any stop criteria. + + seed : Optional[int] + The random seed of the generation. + The seed will be a random value if not specified. + + stop_strs : List[str] + The list of strings that mark the end of generation. + + stop_token_ids : List[int] + The list of token ids that mark the end of generation. + + ignore_eos: bool + When it is true, ignore the eos token and generate tokens until `max_tokens`. + Default is set to False. + """ + + temperature: float = 0.8 + top_p: float = 0.95 + frequency_penalty: float = 0.0 + presence_penalty: float = 0.0 + repetition_penalty: float = 1.0 + logprobs: bool = False + top_logprobs: int = 0 + logit_bias: Optional[Dict[int, float]] = field(default_factory=dict) + + max_tokens: Optional[int] = 128 + seed: Optional[int] = None + stop_strs: List[str] = field(default_factory=list) + stop_token_ids: List[int] = field(default_factory=list) + ignore_eos: bool = False + + def asjson(self) -> str: + """Return the config in string of JSON format.""" + return json.dumps(asdict(self)) + + @staticmethod + def from_json(json_str: str) -> "GenerationConfig": + """Construct a config from JSON string.""" + return GenerationConfig(**json.loads(json_str)) + + +@dataclass +class KVCacheConfig: + """The KV cache initialization configuration. + + Parameters + ---------- + page_size : int + The number of consecutive tokens handled in each page in paged KV cache. + + max_num_sequence : int + The maximum number of sequences that are allowed to processed by the KV + cache at any time. + + max_total_sequence_length : Optional[int] + The maximum total number of tokens whose KV data are allowed to exist + in the KV cache at any time. + Set it to None to enable automatic computation of the max total + sequence length. + + prefill_chunk_size : Optional[int] + The maximum total sequence length in a prefill. + If not specified, it will be automatically inferred from model config. + """ + + page_size: int = 16 + max_num_sequence: int = 32 + max_total_sequence_length: Optional[int] = None + prefill_chunk_size: Optional[int] = None + + def asjson(self) -> str: + """Return the config in string of JSON format.""" + return json.dumps(asdict(self)) + + @staticmethod + def from_json(json_str: str) -> "KVCacheConfig": + """Construct a config from JSON string.""" + return KVCacheConfig(**json.loads(json_str)) + + +@dataclass +class EngineMode: + """The Engine execution mode. + + Parameters + ---------- + enable_speculative : bool + Whether the speculative decoding mode is enabled, default False. + + spec_draft_length : int + The number of tokens to generate in speculative proposal (draft), default 4. + """ + + enable_speculative: bool = False + spec_draft_length: int = 4 + + def asjson(self) -> str: + """Return the config in string of JSON format.""" + return json.dumps(asdict(self)) + + @staticmethod + def from_json(json_str: str) -> "EngineMode": + """Construct a config from JSON string.""" + return EngineMode(**json.loads(json_str)) diff --git a/python/mlc_chat/serve/data.py b/python/mlc_chat/serve/data.py new file mode 100644 index 0000000..15c0a4f --- /dev/null +++ b/python/mlc_chat/serve/data.py @@ -0,0 +1,117 @@ +"""Classes denoting multi-modality data used in MLC LLM serving""" + +from typing import List, Optional, Tuple + +import tvm._ffi +from tvm.runtime import Object + +from . import _ffi_api + + +@tvm._ffi.register_object("mlc.serve.Data") # pylint: disable=protected-access +class Data(Object): + """The base class of multi-modality data (text, tokens, embedding, etc).""" + + def __init__(self): + pass + + +@tvm._ffi.register_object("mlc.serve.TextData") # pylint: disable=protected-access +class TextData(Data): + """The class of text data, containing a text string. + + Parameters + ---------- + text : str + The text string. + """ + + def __init__(self, text: str): + self.__init_handle_by_constructor__(_ffi_api.TextData, text) # type: ignore # pylint: disable=no-member + + @property + def text(self) -> str: + """The text data in `str`.""" + return str(_ffi_api.TextDataGetTextString(self)) # type: ignore # pylint: disable=no-member + + def __str__(self) -> str: + return self.text + + +@tvm._ffi.register_object("mlc.serve.TokenData") # type: ignore # pylint: disable=protected-access +class TokenData(Data): + """The class of token data, containing a list of token ids. + + Parameters + ---------- + token_ids : List[int] + The list of token ids. + """ + + def __init__(self, token_ids: List[int]): + self.__init_handle_by_constructor__(_ffi_api.TokenData, *token_ids) # type: ignore # pylint: disable=no-member + + @property + def token_ids(self) -> List[int]: + """Return the token ids of the TokenData.""" + return list(_ffi_api.TokenDataGetTokenIds(self)) # type: ignore # pylint: disable=no-member + + +@tvm._ffi.register_object("mlc.serve.RequestStreamOutput") # pylint: disable=protected-access +class RequestStreamOutput(Object): + """The generated delta request output that is streamed back + through callback stream function. + It contains four fields (in order): + + request_id : str + The id of the request that the function is invoked for. + + delta_tokens : List[int] + The new generated tokens since the last callback invocation + for the input request. + + delta_logprob_json_strs : Optional[List[str]] + The logprobs JSON strings of the new generated tokens + since last invocation. + + finish_reason : Optional[str] + The finish reason of the request when it is finished, + of None if the request has not finished yet. + + Note + ---- + We do not provide constructor, since in practice only C++ side + instantiates this class. + """ + + def unpack(self) -> Tuple[str, List[int], Optional[List[str]], Optional[str]]: + """Return the fields of the delta output in a tuple. + + Returns + ------- + request_id : str + The id of the request that the function is invoked for. + + delta_tokens : List[int] + The new generated tokens since the last callback invocation + for the input request. + + delta_logprob_json_strs : Optional[List[str]] + The logprobs JSON strings of the new generated tokens + since last invocation. + + finish_reason : Optional[str] + The finish reason of the request when it is finished, + of None if the request has not finished yet. + """ + fields = _ffi_api.RequestStreamOutputUnpack(self) # type: ignore # pylint: disable=no-member + return ( + str(fields[0]), + list(fields[1]), + ( + [str(logprob_json_str) for logprob_json_str in fields[2]] + if fields[2] is not None + else None + ), + str(fields[3]) if fields[3] is not None else None, + ) diff --git a/python/mlc_chat/serve/engine.py b/python/mlc_chat/serve/engine.py new file mode 100644 index 0000000..407fb72 --- /dev/null +++ b/python/mlc_chat/serve/engine.py @@ -0,0 +1,494 @@ +"""The MLC LLM Serving Engine.""" + +import json +import os +import subprocess +import sys +from dataclasses import asdict, dataclass +from pathlib import Path +from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union + +import tvm +from tvm.runtime import Device + +from mlc_chat.serve import data +from mlc_chat.support import logging +from mlc_chat.support.auto_device import detect_device +from mlc_chat.support.style import green + +from ..chat_module import _get_chat_config, _get_lib_module_path, _get_model_path +from ..streamer import TextStreamer +from ..tokenizer import Tokenizer +from . import data +from .config import EngineMode, GenerationConfig, KVCacheConfig +from .event_trace_recorder import EventTraceRecorder +from .request import Request + +logging.enable_logging() +logger = logging.getLogger(__name__) + + +@dataclass +class ModelInfo: + """The model info dataclass. + + Parameters + ---------- + model : str + The identifier of the input model. + It may be a compiled model's id (e.g., "Llama-2-7b-chat-hf-q4f16_1"), + or a full path to a model directory + (e.g., "dist/prebuilt/mlc-chat-Llama-2-7b-chat-hf-q4f16_1") + + device : str + The device where to run the model. + It can be "auto", "device_name" (e.g., "cuda") or + "device_name:device_id" (e.g., "cuda:1"). + + model_lib_path : str + The path to the compiled library of the model. + E.g., "dist/prebuilt/lib/Llama-2-7b-chat-hf-q4f16_1-cuda.so" + """ + + model: str + model_lib_path: str + device: Device = "auto" # type: ignore + + def __post_init__(self): + if isinstance(self.device, str): + self.device = detect_device(self.device) + assert isinstance(self.device, Device) + + +def _create_tvm_module( + creator: str, ffi_funcs: Sequence[str], creator_args: Optional[List[Any]] = None +) -> Dict[str, Callable]: + """Internal method to create a module.""" + if creator_args is None: + creator_args = [] + module = tvm.get_global_func(creator, allow_missing=False)(*creator_args) + return {key: module[key] for key in ffi_funcs} + + +def _process_model_args( + models: List[ModelInfo], +) -> Tuple[List[Any], List[str], str, int, int, Optional[str]]: + """Process the input ModelInfo to get the engine initialization arguments.""" + max_single_sequence_length = int(1e9) + prefill_chunk_size = int(1e9) + tokenizer_path: Optional[str] = None + conv_template_name: Optional[str] = None + config_file_paths: List[str] = [] + + def _convert_model_info(model: ModelInfo) -> List[Any]: + nonlocal max_single_sequence_length, prefill_chunk_size, tokenizer_path, conv_template_name + + device = model.device + model_path, config_file_path = _get_model_path(model.model) + config_file_paths.append(config_file_path) + chat_config = _get_chat_config(config_file_path, user_chat_config=None) + if chat_config.context_window_size: + max_single_sequence_length = min( + max_single_sequence_length, + chat_config.context_window_size, + ) + if chat_config.prefill_chunk_size: + prefill_chunk_size = min(prefill_chunk_size, chat_config.prefill_chunk_size) + if tokenizer_path is None: + tokenizer_path = model_path + if conv_template_name is None: + conv_template_name = chat_config.conv_template + # Try look up model library, and do JIT compile if model library not found. + try: + model_lib_path = _get_lib_module_path( + model=model.model, + model_path=model_path, + chat_config=chat_config, + model_lib_path=model.model_lib_path, + device_name=device.MASK2STR[device.device_type], + config_file_path=config_file_path, + ) + except FileNotFoundError: + from mlc_chat.interface import ( # pylint: disable=import-outside-toplevel + jit, + ) + + model_lib_path = str( + jit.jit( + model_path=Path(model_path), + chat_config=asdict(chat_config), + device=device, + ) + ) + return [model_lib_path, model_path, device.device_type, device.device_id] + + model_args: List[Any] = sum( + (_convert_model_info(model) for model in models), + start=[], + ) + + return ( + model_args, + config_file_paths, + tokenizer_path, + max_single_sequence_length, + prefill_chunk_size, + conv_template_name, + ) + + +def _estimate_max_total_sequence_length( # pylint: disable=too-many-locals + models: List[ModelInfo], config_file_paths: List[str] +) -> int: + """Estimate the max total sequence length (capacity) of the KV cache.""" + assert len(models) != 0 + + kv_bytes_per_token = 0 + params_bytes = 0 + temp_func_bytes = 0 + + for model, config_file_path in zip(models, config_file_paths): + # Read metadata for the parameter size and the temporary memory size. + cmd = [ + sys.executable, + "-m", + "mlc_chat.cli.model_metadata", + model.model_lib_path, + "--print-memory-usage-in-json", + "--mlc-chat-config", + config_file_path, + ] + usage_str = subprocess.check_output(cmd, universal_newlines=True) + usage_json = json.loads(usage_str) + params_bytes += usage_json["params_bytes"] + temp_func_bytes = max(temp_func_bytes, usage_json["temp_func_bytes"]) + + # Read model config and compute the kv size per token. + with open(config_file_path, mode="rt", encoding="utf-8") as file: + json_object = json.load(file) + model_config = json_object["model_config"] + num_layers = model_config["num_hidden_layers"] + hidden_size = model_config["hidden_size"] + num_qo_heads = model_config["num_attention_heads"] + num_kv_heads = model_config["num_key_value_heads"] + tensor_parallel_shards = model_config["tensor_parallel_shards"] + kv_bytes_per_token += ( + (hidden_size / num_qo_heads) + * (num_kv_heads / tensor_parallel_shards) # on single GPU + * num_layers + * 4 # key, value, fp16 + * 1.10 # over estimation to guarantee safety + ) + + # Get single-card GPU size. + gpu_size_bytes = os.environ.get("MLC_GPU_SIZE_BYTES", default=None) + if gpu_size_bytes is None: + gpu_size_bytes = models[0].device.total_global_memory + if gpu_size_bytes is None: + raise ValueError( + "Cannot read total GPU global memory from device. " + 'Please the GPU memory size in bytes through "MLC_GPU_SIZE_BYTES" env variable.' + ) + + max_total_sequence_length = int( + (int(gpu_size_bytes) * 0.97 - params_bytes * 1.04 - temp_func_bytes) / kv_bytes_per_token + ) + assert max_total_sequence_length > 0, ( + "Cannot estimate KV cache capacity. " + f"The model weight size {params_bytes} may be larger than GPU memory size {gpu_size_bytes}" + ) + + total_size = ( + params_bytes * 1.05 + temp_func_bytes + kv_bytes_per_token * max_total_sequence_length + ) + logger.info( + "%s: %d.", + green('Estimated KVCacheConfig "max_total_sequence_length"'), + max_total_sequence_length, + ) + logger.info( + "%s: %.2f MB (Parameters: %.2f MB. KVCache: %.2f MB. Temporary buffer: %.2f MB)", + green("Estimated total single GPU memory usage"), + total_size / 1024 / 1024, + params_bytes / 1024 / 1024, + kv_bytes_per_token * max_total_sequence_length / 1024 / 1024, + temp_func_bytes / 1024 / 1024, + ) + return int(max_total_sequence_length) + + +class Engine: + """The Python interface of request serving engine for MLC LLM. + + The engine can run one or multiple LLM models internally for + text generation. Usually, when there are multiple models, + speculative inference will be activated, where the first model + (index 0) is the main "large model" that has better generation + quality, and all other models are "small" models that used for + speculation. + + The engine receives requests from the "add_request" method. For + an given request, the engine will keep generating new tokens for + the request until finish (under certain criterion). After finish, + the engine will return the generation result through the callback + function provided by the request. + + Parameters + ---------- + models : Union[ModelInfo, List[ModelInfo]] + One or a list of model info (specifying which models to load and + which device to load to) to launch the engine. + + kv_cache_config : KVCacheConfig + The configuration of the paged KV cache. + + request_stream_callback : Optional[Callable[[str, data.TokenData, Optional[str]], None]] + The provided callback function to handle the generation + output. It has the signature of `(str, data.TokenData, bool) -> None`, + where + - the first string is the request id, + - the TokenData contains the generated **delta** token ids since + the last invocation of the callback on the specific request, + - the optional string value denotes the finish reason if the + generation of the request is finished, or None if it has not finished. + + The callback function is optional at construction, but it needs to + be set before the engine executing requests. This can be done via + the `set_request_stream_callback` method. Otherwise, the engine will raise + exception. + + engine_mode : Optional[EngineMode] + The Engine execution mode. + + enable_tracing : bool + A boolean indicating if to enable event logging for requests. + """ + + def __init__( # pylint: disable=too-many-arguments + self, + models: Union[ModelInfo, List[ModelInfo]], + kv_cache_config: KVCacheConfig, + engine_mode: Optional[EngineMode] = None, + request_stream_callback: Optional[Callable[[List[data.RequestStreamOutput]], None]] = None, + enable_tracing: bool = False, + ): + if isinstance(models, ModelInfo): + models = [models] + ( + model_args, + config_file_paths, + tokenizer_path, + self.max_single_sequence_length, + prefill_chunk_size, + self.conv_template_name, + ) = _process_model_args(models) + self._ffi = _create_tvm_module( + "mlc.serve.create_engine", + ffi_funcs=[ + "init", + "add_request", + "abort_request", + "step", + "stats", + "reset", + "get_request_stream_callback", + "set_request_stream_callback", + ], + ) + self.trace_recorder = EventTraceRecorder() if enable_tracing else None + + if kv_cache_config.max_total_sequence_length is None: + kv_cache_config.max_total_sequence_length = _estimate_max_total_sequence_length( + models, config_file_paths + ) + if kv_cache_config.prefill_chunk_size is None: + kv_cache_config.prefill_chunk_size = prefill_chunk_size + elif kv_cache_config.prefill_chunk_size > prefill_chunk_size: + raise ValueError( + f"The specified prefill chunk size {kv_cache_config.prefill_chunk_size} is " + f"larger than the maximum prefill chunk size {prefill_chunk_size} supported by " + "models. Please specify a smaller prefill chunk size." + ) + + if engine_mode is None: + # The default engine mode: non-speculative + engine_mode = EngineMode() + + self._ffi["init"]( + self.max_single_sequence_length, + tokenizer_path, + kv_cache_config.asjson(), + engine_mode.asjson(), + request_stream_callback, + self.trace_recorder, + *model_args, + ) + self.tokenizer = Tokenizer(tokenizer_path) + + def generate( + self, + prompts: Union[str, List[str], List[int], List[List[int]]], + generation_config: Union[GenerationConfig, List[GenerationConfig]], + ) -> Tuple[List[str], List[Optional[List[str]]]]: + """Generate texts for a list of input prompts. + Each prompt can be a string or a list of token ids. + The generation for each prompt is independent. + Return the generation results, one for each prompt. + + Parameters + ---------- + prompts : Union[str, List[str], List[int], List[List[int]]] + One or a list of input prompts for text generation. + Each prompt can be a string or a list of token ids. + + generation_config : Union[GenerationConfig, List[GenerationConfig]] + The generation config for each requests. + If the it is a single GenerationConfig instance, + this config will be shared by all the prompts. + Otherwise, one generation config is required for every + prompt. + + Returns + ------- + output_text : List[str] + The text generation results, one string for each input prompt. + + output_logprobs_str : List[Optional[List[str]]] + The logprob strings of each token for each input prompt, or None + if an input prompt does not require logprobs. + """ + if isinstance(prompts, str): + # `prompts` is a single string. + prompts = [prompts] + else: + assert isinstance(prompts, list), ( + "Input `prompts` is expected to be a string, a list of " + "str, a list of token ids or multiple lists of token ids." + ) + if len(prompts) == 0: + return [], [] + if isinstance(prompts[0], int): + # `prompts` is a list of token ids + prompts = [prompts] # type: ignore + + num_requests = len(prompts) + if not isinstance(generation_config, list): + generation_config = [generation_config] * num_requests + + assert ( + len(generation_config) == num_requests + ), "Number of generation config and number of prompts mismatch" + + num_finished_requests = 0 + output_texts: List[str] = [] + output_logprobs_str: List[Optional[List[str]]] = [] + text_streamers: List[TextStreamer] = [] + for i in range(num_requests): + output_texts.append("") + output_logprobs_str.append([] if generation_config[i].logprobs else None) + text_streamers.append(TextStreamer(self.tokenizer)) + + # Save a copy of the original function callback since `generate` + # overrides the callback function. + # The original callback will be set back later on. + original_callback = self._ffi["get_request_stream_callback"]() + + # Define the callback function for request generation results + def request_stream_callback(delta_outputs: List[data.RequestStreamOutput]): + nonlocal num_finished_requests + for delta_output in delta_outputs: + ( + request_id, + delta_token_ids, + delta_logprob_json_strs, + finish_reason, + ) = delta_output.unpack() + rid = int(request_id) + text_streamer = text_streamers[rid] + if output_logprobs_str[rid] is not None: + assert delta_logprob_json_strs is not None + output_logprobs_str[rid] += delta_logprob_json_strs + + delta_text = text_streamer.put(delta_token_ids) + if finish_reason is not None: + delta_text += text_streamer.finish() + + output_texts[rid] += delta_text + if finish_reason is not None: + num_finished_requests += 1 + + # Override the callback function in engine. + self._ffi["set_request_stream_callback"](request_stream_callback) + + # Add requests to engine. + for req_id, (prompt, generation_cfg) in enumerate(zip(prompts, generation_config)): + input_data = ( + data.TextData(prompt) + if isinstance(prompt, str) + else data.TokenData(prompt) # type: ignore + ) + self.add_request( + Request( + request_id=str(req_id), + inputs=input_data, + generation_config=generation_cfg, + ) + ) + + while num_finished_requests != num_requests: + self.step() + + # Restore the callback function in engine. + self._ffi["set_request_stream_callback"](original_callback) + return output_texts, output_logprobs_str + + def add_request(self, request: Request) -> None: + """Add a new request to the engine. + + Parameters + ---------- + request : Request + The request to add. + """ + self._ffi["add_request"](request) + + def abort_request(self, request_id: str) -> None: + """Abort the generation of the request corresponding to the input request id. + + Parameters + ---------- + request_id : str + The unique id of the request to abort. + """ + self._ffi["abort_request"](request_id) + + def step(self) -> None: + """The main function that the engine takes a step of action. + + At each step, the engine may decide to + - run prefill for one (or more) requests, + - run one-step decode for the all existing requests + ... + + In the end of certain actions (e.g., decode), the engine will + check if any request has finished, and will return the + generation results for those finished requests. + """ + self._ffi["step"]() + + def reset(self) -> None: + """Reset the engine, clean up all running data and statistics.""" + self._ffi["reset"]() + + def stats(self) -> Dict[str, float]: + """The engine runtime statistics. + We collect the following entries: + - single token prefill latency (s/tok): avg latency of processing one token in prefill + - single token decode latency (s/tok): avg latency of processing one token in decode + - engine time for prefill (sec) + - engine time for decode (sec) + - total number of processed tokens in prefill. + - total number of processed tokens in decode. + """ + stats_json_str = self._ffi["stats"]() + return json.loads(stats_json_str) diff --git a/python/mlc_chat/serve/entrypoints/__init__.py b/python/mlc_chat/serve/entrypoints/__init__.py new file mode 100644 index 0000000..3002bf8 --- /dev/null +++ b/python/mlc_chat/serve/entrypoints/__init__.py @@ -0,0 +1,2 @@ +"""The entrypoints for MLC LLM server.""" +from . import debug_entrypoints, openai_entrypoints diff --git a/python/mlc_chat/serve/entrypoints/debug_entrypoints.py b/python/mlc_chat/serve/entrypoints/debug_entrypoints.py new file mode 100644 index 0000000..45da755 --- /dev/null +++ b/python/mlc_chat/serve/entrypoints/debug_entrypoints.py @@ -0,0 +1,48 @@ +"""MLC LLM server debug entrypoints""" +import json +from http import HTTPStatus + +import fastapi + +from ..server import ServerContext +from . import entrypoint_utils + +app = fastapi.APIRouter() + +################ /debug/dump_event_trace ################ + + +@app.post("/debug/dump_event_trace") +async def debug_dump_event_trace(request: fastapi.Request): + """Return the recorded events in Chrome Trace Event Format in JSON string. + The input request payload should have only one field, specifying the + model to query. For example: `{"model": "Llama-2-7b-chat-hf-q0f16"}`. + """ + # Get the raw request body as bytes + request_raw_data = await request.body() + request_json_str = request_raw_data.decode("utf-8") + try: + # Parse the JSON string + request_dict = json.loads(request_json_str) + except json.JSONDecodeError: + return entrypoint_utils.create_error_response( + HTTPStatus.BAD_REQUEST, message=f"Invalid request {request_json_str}" + ) + if "model" not in request_dict: + return entrypoint_utils.create_error_response( + HTTPStatus.BAD_REQUEST, message=f"Invalid request {request_json_str}" + ) + + # - Check the requested model. + model = request_dict["model"] + async_engine = ServerContext.get_engine(model) + if async_engine is None: + return entrypoint_utils.create_error_response( + HTTPStatus.BAD_REQUEST, message=f'The requested model "{model}" is not served.' + ) + if async_engine.trace_recorder is None: + return entrypoint_utils.create_error_response( + HTTPStatus.BAD_REQUEST, message=f'The requested model "{model}" does not enable tracing' + ) + + return json.loads(async_engine.trace_recorder.dump_json()) diff --git a/python/mlc_chat/serve/entrypoints/entrypoint_utils.py b/python/mlc_chat/serve/entrypoints/entrypoint_utils.py new file mode 100644 index 0000000..5a9924b --- /dev/null +++ b/python/mlc_chat/serve/entrypoints/entrypoint_utils.py @@ -0,0 +1,92 @@ +"""Utility functions for server entrypoints""" + +import uuid +from http import HTTPStatus +from typing import Callable, List, Optional, Union + +import fastapi + +from ...protocol import RequestProtocol +from ...protocol.protocol_utils import ErrorResponse, get_unsupported_fields + + +def random_uuid() -> str: + """Generate a random id in hexadecimal string.""" + return uuid.uuid4().hex + + +def create_error_response(status_code: HTTPStatus, message: str) -> fastapi.responses.JSONResponse: + """Create a JSON response that reports error with regarding the input message.""" + return fastapi.responses.JSONResponse( + ErrorResponse(message=message, code=status_code.value).model_dump_json(), + status_code=status_code.value, + ) + + +def check_unsupported_fields( + request: RequestProtocol, +) -> Optional[fastapi.responses.JSONResponse]: + """Check if the request has unsupported fields. Return an error if so.""" + unsupported_fields = get_unsupported_fields(request) + if len(unsupported_fields) != 0: + unsupported_fields = [f'"{field}"' for field in unsupported_fields] + return create_error_response( + HTTPStatus.BAD_REQUEST, + message=f'Request fields {", ".join(unsupported_fields)} are not supported right now.', + ) + return None + + +def check_prompts_length( + prompts: List[List[int]], max_single_sequence_length: int +) -> Optional[fastapi.responses.JSONResponse]: + """Check if the total prompt length exceeds the max single sequence + sequence length allowed by the served model. Return an error if so. + """ + total_length = 0 + for prompt in prompts: + total_length += len(prompt) + if total_length > max_single_sequence_length: + return create_error_response( + HTTPStatus.BAD_REQUEST, + message=f"Request prompt has {total_length} tokens in total," + f" larger than the model capacity {max_single_sequence_length}.", + ) + return None + + +def process_prompts( + input_prompts: Union[str, List[int], List[Union[str, List[int]]]], + ftokenize: Callable[[str], List[int]], +) -> Union[List[List[int]], fastapi.responses.JSONResponse]: + """Convert all input tokens to list of token ids with regard to the + given tokenization function. + For each input prompt, return the list of token ids after tokenization. + """ + error_msg = f"Invalid request prompt {input_prompts}" + + # Case 1. The prompt is a single string. + if isinstance(input_prompts, str): + return [ftokenize(input_prompts)] + + assert isinstance(input_prompts, list) + if len(input_prompts) == 0: + return create_error_response(HTTPStatus.BAD_REQUEST, message=error_msg) + + # Case 2. The prompt is a list of token ids. + if isinstance(input_prompts[0], int): + if not all(isinstance(token_id, int) for token_id in input_prompts): + return create_error_response(HTTPStatus.BAD_REQUEST, message=error_msg) + return [input_prompts] + + # Case 3. A list of prompts. + output_prompts: List[List[int]] = [] + for input_prompt in input_prompts: + is_str = isinstance(input_prompt, str) + is_token_ids = isinstance(input_prompt, list) and all( + isinstance(token_id, int) for token_id in input_prompt + ) + if not (is_str or is_token_ids): + return create_error_response(HTTPStatus.BAD_REQUEST, message=error_msg) + output_prompts.append(ftokenize(input_prompt) if is_str else input_prompt) # type: ignore + return output_prompts diff --git a/python/mlc_chat/serve/entrypoints/openai_entrypoints.py b/python/mlc_chat/serve/entrypoints/openai_entrypoints.py new file mode 100644 index 0000000..de85ab8 --- /dev/null +++ b/python/mlc_chat/serve/entrypoints/openai_entrypoints.py @@ -0,0 +1,537 @@ +"""OpenAI API-compatible server entrypoints in MLC LLM""" + +# pylint: disable=too-many-locals,too-many-return-statements,too-many-statements +import ast +import json +from http import HTTPStatus +from typing import AsyncGenerator, Dict, List, Optional, Union + +import fastapi + +from ...protocol import protocol_utils +from ...protocol.conversation_protocol import Conversation +from ...protocol.openai_api_protocol import ( + ChatCompletionMessage, + ChatCompletionRequest, + ChatCompletionResponse, + ChatCompletionResponseChoice, + ChatCompletionStreamResponse, + ChatCompletionStreamResponseChoice, + ChatFunctionCall, + ChatToolCall, + CompletionRequest, + CompletionResponse, + CompletionResponseChoice, + ListResponse, + LogProbs, + LogProbsContent, + ModelResponse, + UsageInfo, +) +from ..server import ServerContext +from . import entrypoint_utils + +app = fastapi.APIRouter() + +################ v1/models ################ + + +@app.get("/v1/models") +async def request_models(): + """OpenAI-compatible served model query API. + API reference: https://platform.openai.com/docs/api-reference/models + """ + return ListResponse(data=[ModelResponse(id=model) for model in ServerContext.get_model_list()]) + + +################ v1/completions ################ + + +@app.post("/v1/completions") +async def request_completion(request: CompletionRequest, raw_request: fastapi.Request): + """OpenAI-compatible completion API. + API reference: https://platform.openai.com/docs/api-reference/completions/create + """ + # - Check the requested model. + async_engine = ServerContext.get_engine(request.model) + if async_engine is None: + return entrypoint_utils.create_error_response( + HTTPStatus.BAD_REQUEST, message=f'The requested model "{request.model}" is not served.' + ) + request_id = f"cmpl-{entrypoint_utils.random_uuid()}" + async_engine.record_event(request_id, event="receive request") + + # - Check if unsupported arguments are specified. + error = entrypoint_utils.check_unsupported_fields(request) + if error is not None: + return error + + # - Process prompt and check validity. + async_engine.record_event(request_id, event="start tokenization") + prompts = entrypoint_utils.process_prompts(request.prompt, async_engine.tokenizer.encode) + async_engine.record_event(request_id, event="finish tokenization") + if isinstance(prompts, fastapi.responses.JSONResponse): + # Errored when processing the prompts + return prompts + if len(prompts) > 1: + return entrypoint_utils.create_error_response( + HTTPStatus.BAD_REQUEST, + message="Entrypoint /v1/completions only accept single prompt. " + f"However, {len(prompts)} prompts {prompts} are received.", + ) + error = entrypoint_utils.check_prompts_length(prompts, async_engine.max_single_sequence_length) + if error is not None: + return error + prompt = prompts[0] + + # Process generation config. Create request id. + generation_cfg = protocol_utils.get_generation_config(request) + + # Streaming response. + if request.stream: + + async def completion_stream_generator() -> AsyncGenerator[str, None]: + assert request.n == 1 + + # - Echo back the prompt. + if request.echo: + text = async_engine.tokenizer.decode(prompt) + response = CompletionResponse( + id=request_id, + choices=[CompletionResponseChoice(text=text)], + model=request.model, + usage=UsageInfo( + prompt_tokens=len(prompt), + completion_tokens=0, + ), + ) + yield f"data: {response.model_dump_json()}\n\n" + + # - Generate new tokens. + num_completion_tokens = 0 + finish_reason = None + async_engine.record_event(request_id, event="invoke generate") + async for ( + delta_text, + num_delta_tokens, + delta_logprob_json_strs, + finish_reason, + ) in async_engine.generate(prompt, generation_cfg, request_id): + num_completion_tokens += num_delta_tokens + if delta_text == "": + # Ignore empty delta text -- do not yield. + continue + + response = CompletionResponse( + id=request_id, + choices=[ + CompletionResponseChoice( + finish_reason=finish_reason, + text=delta_text, + logprobs=( + LogProbs( + content=[ + LogProbsContent.model_validate_json(logprob_json_str) + for logprob_json_str in delta_logprob_json_strs + ] + ) + if delta_logprob_json_strs is not None + else None + ), + ) + ], + model=request.model, + usage=UsageInfo( + prompt_tokens=len(prompt), + completion_tokens=num_completion_tokens, + ), + ) + yield f"data: {response.model_dump_json()}\n\n" + async_engine.record_event(request_id, event="finish") + + # - Echo the suffix. + if request.suffix is not None: + assert finish_reason is not None + response = CompletionResponse( + id=request_id, + choices=[ + CompletionResponseChoice( + finish_reason=finish_reason, + text=request.suffix, + ) + ], + model=request.model, + usage=UsageInfo( + prompt_tokens=len(prompt), + completion_tokens=num_completion_tokens, + ), + ) + yield f"data: {response.model_dump_json()}\n\n" + + yield "data: [DONE]\n\n" + + return fastapi.responses.StreamingResponse( + completion_stream_generator(), media_type="text/event-stream" + ) + + # Normal response. + output_text = "" if not request.echo else async_engine.tokenizer.decode(prompt) + num_completion_tokens = 0 + finish_reason: Optional[str] = None + logprob_json_strs: Optional[List[str]] = [] if generation_cfg.logprobs else None + async_engine.record_event(request_id, event="invoke generate") + async for ( + delta_text, + num_delta_tokens, + delta_logprob_json_strs, + finish_reason, + ) in async_engine.generate(prompt, generation_cfg, request_id): + if await raw_request.is_disconnected(): + # In non-streaming cases, the engine will not be notified + # when the request is disconnected. + # Therefore, we check if it is disconnected each time, + # and abort the request from engine if so. + await async_engine.abort(request_id) + return entrypoint_utils.create_error_response( + HTTPStatus.BAD_REQUEST, message="The request has disconnected" + ) + output_text += delta_text + num_completion_tokens += num_delta_tokens + if logprob_json_strs is not None: + assert delta_logprob_json_strs is not None + logprob_json_strs += delta_logprob_json_strs + assert finish_reason is not None + suffix = request.suffix if request.suffix is not None else "" + async_engine.record_event(request_id, event="finish") + response = CompletionResponse( + id=request_id, + choices=[ + CompletionResponseChoice( + finish_reason=finish_reason, + text=output_text + suffix, + logprobs=( + LogProbs( + content=[ + LogProbsContent.model_validate_json(logprob_json_str) + for logprob_json_str in logprob_json_strs + ] + ) + if logprob_json_strs is not None + else None + ), + ) + ], + model=request.model, + usage=UsageInfo( + prompt_tokens=len(prompt), + completion_tokens=num_completion_tokens, + ), + ) + return response + + +################ v1/chat/completions ################ + + +def chat_completion_check_message_validity( + messages: List[ChatCompletionMessage], +) -> Optional[str]: + """Check if the given chat messages are valid. Return error message if invalid.""" + for i, message in enumerate(messages): + if message.role == "system" and i != 0: + return f"System prompt at position {i} in the message list is invalid." + if message.role == "tool": + return "Tool as the message author is not supported yet." + if message.tool_call_id is not None: + if message.role != "tool": + return "Non-tool message having `tool_call_id` is invalid." + if isinstance(message.content, list): + if message.role != "user": + return "Non-user message having a list of content is invalid." + return "User message having a list of content is not supported yet." + if message.tool_calls is not None: + if message.role != "assistant": + return "Non-assistant message having `tool_calls` is invalid." + return "Assistant message having `tool_calls` is not supported yet." + return None + + +def check_function_call_usage( + request: ChatCompletionRequest, conv_template: Conversation +) -> Optional[str]: + """Check if function calling is used and update the conversation template. + Return error message if invalid request format for function calling. + """ + + # return if no tools are provided or tool_choice is set to none + if request.tools is None or ( + isinstance(request.tool_choice, str) and request.tool_choice == "none" + ): + conv_template.use_function_calling = False + return None + + # select the tool based on the tool_choice if specified + if isinstance(request.tool_choice, dict): + if request.tool_choice["type"] != "function": + return "Only 'function' tool choice is supported" + + if len(request.tool_choice["function"]) > 1: + return "Only one tool is supported when tool_choice is specified" + + for tool in request.tools: + if tool.function.name == request.tool_choice["function"]["name"]: + conv_template.use_function_calling = True + conv_template.function_string = tool.function.model_dump_json() + return None + + return ( + f"The tool_choice function {request.tool_choice['function']['name']}" + " is not found in the tools list" + ) + + if isinstance(request.tool_choice, str) and request.tool_choice != "auto": + return f"Invalid tool_choice value: {request.tool_choice}" + + function_list = [] + for tool in request.tools: + if tool.type != "function": + return "Only 'function' tool type is supported" + function_list.append(tool.function.model_dump()) + + conv_template.use_function_calling = True + conv_template.function_string = json.dumps(function_list) + return None + + +def convert_function_str_to_json(stringified_calls: str) -> List[Union[Dict, None]]: + """Convert a (possibly list) of function call string to a list of json objects. + Return None for invalid function call string.""" + + def parse_function_call(call_str: str): + node = ast.parse(call_str, mode="eval") + call_node = node.body + if isinstance(call_node, ast.Call) and isinstance(call_node.func, ast.Name): + name = call_node.func.id + arguments = {} + for keyword in call_node.keywords: + arguments[keyword.arg] = ast.literal_eval(keyword.value) + return {"name": name, "arguments": arguments} + return None + + if ( + stringified_calls[0] == "[" and stringified_calls[-1] == "]" + ): # hacky way to check if string list + calls = ast.literal_eval(stringified_calls) + else: + calls = [stringified_calls] + function_calls_json = [parse_function_call(call_str) for call_str in calls] + return function_calls_json + + +@app.post("/v1/chat/completions") +async def request_chat_completion( + request: ChatCompletionRequest, raw_request: fastapi.Request +): # pylint: disable=too-many-branches + """OpenAI-compatible chat completion API. + API reference: https://platform.openai.com/docs/api-reference/chat + """ + # - Check the requested model. + async_engine = ServerContext.get_engine(request.model) + if async_engine is None: + return entrypoint_utils.create_error_response( + HTTPStatus.BAD_REQUEST, message=f'The requested model "{request.model}" is not served.' + ) + request_id = f"chatcmpl-{entrypoint_utils.random_uuid()}" + async_engine.record_event(request_id, event="receive request") + + # - Check if the model supports chat conversation. + conv_template = ServerContext.get_conv_template(request.model) + if conv_template is None: + return entrypoint_utils.create_error_response( + HTTPStatus.BAD_REQUEST, + message=f'The requested model "{request.model}" does not support chat.', + ) + + # - Check if unsupported arguments are specified. + error = entrypoint_utils.check_unsupported_fields(request) + if error is not None: + return error + + # - Process messages and update the conversation template in three steps: + # i. Check the message validity. + # ii. Add the input messages to the conversation template. + # iii. Add the additional message for the assistant. + error_msg = chat_completion_check_message_validity(request.messages) + if error_msg is not None: + return entrypoint_utils.create_error_response(HTTPStatus.BAD_REQUEST, message=error_msg) + + # Check for function calling usage and update the conversation template + error_msg = check_function_call_usage(request, conv_template) + if error_msg is not None: + return entrypoint_utils.create_error_response(HTTPStatus.BAD_REQUEST, message=error_msg) + + for message in request.messages: + role = message.role + content = message.content + assert isinstance(content, str), "Internal error: content is not a string." + if role == "system": + conv_template.system_message = content if content is not None else "" + continue + + assert role != "tool", "Internal error: tool role." + conv_template.messages.append((role, content)) + conv_template.messages.append(("assistant", None)) + + # - Get the prompt from template, and encode to token ids. + # - Check prompt length + async_engine.record_event(request_id, event="start tokenization") + prompts = entrypoint_utils.process_prompts( + conv_template.as_prompt(), async_engine.tokenizer.encode + ) + async_engine.record_event(request_id, event="finish tokenization") + assert isinstance(prompts, list) and len(prompts) == 1, "Internal error" + if conv_template.system_prefix_token_ids is not None: + prompts[0] = conv_template.system_prefix_token_ids + prompts[0] + error = entrypoint_utils.check_prompts_length(prompts, async_engine.max_single_sequence_length) + if error is not None: + return error + prompt = prompts[0] + + # Process generation config. Create request id. + generation_cfg = protocol_utils.get_generation_config( + request, + extra_stop_token_ids=conv_template.stop_token_ids, + extra_stop_str=conv_template.stop_str, + ) + + # Streaming response. + if request.stream: + + async def completion_stream_generator() -> AsyncGenerator[str, None]: + assert request.n == 1 + async_engine.record_event(request_id, event="invoke generate") + async for ( + delta_text, + _, + delta_logprob_json_strs, + finish_reason, + ) in async_engine.generate(prompt, generation_cfg, request_id): + if delta_text == "": + async_engine.record_event(request_id, event="skip empty delta text") + # Ignore empty delta text -- do not yield. + continue + + if conv_template.use_function_calling: + finish_reason = "tool_calls" + + response = ChatCompletionStreamResponse( + id=request_id, + choices=[ + ChatCompletionStreamResponseChoice( + finish_reason=finish_reason, + delta=ChatCompletionMessage(content=delta_text, role="assistant"), + logprobs=( + LogProbs( + content=[ + LogProbsContent.model_validate_json(logprob_json_str) + for logprob_json_str in delta_logprob_json_strs + ] + ) + if delta_logprob_json_strs is not None + else None + ), + ) + ], + model=request.model, + system_fingerprint="", + ) + async_engine.record_event(request_id, event=f"yield delta text {delta_text}") + yield f"data: {response.model_dump_json()}\n\n" + async_engine.record_event(request_id, event="finish") + yield "data: [DONE]\n\n" + + return fastapi.responses.StreamingResponse( + completion_stream_generator(), media_type="text/event-stream" + ) + + # Normal response. + output_text = "" + num_completion_tokens = 0 + finish_reason: Optional[str] = None + logprob_json_strs: Optional[List[str]] = [] if generation_cfg.logprobs else None + async_engine.record_event(request_id, event="invoke generate") + async for ( + delta_text, + num_delta_tokens, + delta_logprob_json_strs, + finish_reason, + ) in async_engine.generate(prompt, generation_cfg, request_id): + if await raw_request.is_disconnected(): + # In non-streaming cases, the engine will not be notified + # when the request is disconnected. + # Therefore, we check if it is disconnected each time, + # and abort the request from engine if so. + await async_engine.abort(request_id) + return entrypoint_utils.create_error_response( + HTTPStatus.BAD_REQUEST, message="The request has disconnected" + ) + output_text += delta_text + num_completion_tokens += num_delta_tokens + if logprob_json_strs is not None: + assert delta_logprob_json_strs is not None + logprob_json_strs += delta_logprob_json_strs + assert finish_reason is not None + + async_engine.record_event(request_id, event="finish") + + if conv_template.use_function_calling: + try: + fn_json_list = convert_function_str_to_json(output_text) + except (SyntaxError, ValueError): + output_text = "Got an invalid function call output from model" + finish_reason = "error" + else: + tool_calls = [ + ChatToolCall( + type="function", + function=ChatFunctionCall( + name=fn_json_obj["name"], arguments=fn_json_obj["arguments"] + ), + ) + for fn_json_obj in fn_json_list + if fn_json_obj is not None + ] + if len(tool_calls) == 0: + output_text = "Got an invalid function call output from model" + finish_reason = "error" + else: + finish_reason = "tool_calls" + + message = ( + ChatCompletionMessage(role="assistant", content=output_text) + if (not conv_template.use_function_calling or finish_reason == "error") + else ChatCompletionMessage(role="assistant", content=None, tool_calls=tool_calls) + ) + + return ChatCompletionResponse( + id=request_id, + choices=[ + ChatCompletionResponseChoice( + finish_reason=finish_reason, + message=message, + logprobs=( + LogProbs( + content=[ + LogProbsContent.model_validate_json(logprob_json_str) + for logprob_json_str in logprob_json_strs + ] + ) + if logprob_json_strs is not None + else None + ), + ) + ], + model=request.model, + system_fingerprint="", + usage=UsageInfo(prompt_tokens=len(prompt), completion_tokens=num_completion_tokens), + ) diff --git a/python/mlc_chat/serve/event_trace_recorder.py b/python/mlc_chat/serve/event_trace_recorder.py new file mode 100644 index 0000000..7a8a817 --- /dev/null +++ b/python/mlc_chat/serve/event_trace_recorder.py @@ -0,0 +1,41 @@ +"""The event trace recorder in MLC LLM serving""" + +import tvm._ffi +from tvm.runtime import Object + +from . import _ffi_api + + +@tvm._ffi.register_object("mlc.serve.EventTraceRecorder") # pylint: disable=protected-access +class EventTraceRecorder(Object): + """The event trace recorder for requests.""" + + def __init__(self) -> None: + """Initialize a trace recorder.""" + self.__init_handle_by_constructor__( + _ffi_api.EventTraceRecorder # type: ignore # pylint: disable=no-member + ) + + def add_event(self, request_id: str, event: str) -> None: + """Record a event for the the input request in the trace recorder. + + Parameters + ---------- + request_id : str + The subject request of the event. + + event : str + The event in a string name. + It can have one of the following patterns: + - "start xxx", which marks the start of event "xxx", + - "finish xxx", which marks the finish of event "xxx", + - "yyy", which marks the instant event "yyy". + The "starts" and "finishes" will be automatically paired in the trace recorder. + """ + return _ffi_api.EventTraceRecorderAddEvent( # type: ignore # pylint: disable=no-member + self, request_id, event + ) + + def dump_json(self) -> str: + """Dump the logged events in Chrome Trace Event Format in JSON string.""" + return _ffi_api.EventTraceRecorderDumpJSON(self) # type: ignore # pylint: disable=no-member diff --git a/python/mlc_chat/serve/grammar.py b/python/mlc_chat/serve/grammar.py new file mode 100644 index 0000000..3df954c --- /dev/null +++ b/python/mlc_chat/serve/grammar.py @@ -0,0 +1,242 @@ +"""Classes handling the grammar guided generation of MLC LLM serving""" +from typing import List, Union + +import tvm._ffi +from tvm.runtime import Object + +from ..tokenizer import Tokenizer +from . import _ffi_api + + +@tvm._ffi.register_object("mlc.serve.BNFGrammar") # pylint: disable=protected-access +class BNFGrammar(Object): + """This class stores the abstract syntax tree (AST) of the Backus-Naur Form (BNF) grammar and + provides utilities to parse and print the AST. User should provide a BNF/EBNF (Extended + Backus-Naur Form) grammar, and use from_ebnf_string to parse and simplify the grammar into an + AST of BNF grammar. + """ + + @staticmethod + def from_ebnf_string( + ebnf_string: str, normalize: bool = True, simplify: bool = True + ) -> "BNFGrammar": + r"""Parse a BNF grammar from a string in BNF/EBNF format. + + This method accepts the EBNF notation from the W3C XML Specification + (https://www.w3.org/TR/xml/#sec-notation), which is a popular standard, with the following + changes: + - Using # as comment mark instead of /**/ + - Using C-style unicode escape sequence \u01AB, \U000001AB, \xAB instead of #x0123 + - Do not support A-B (match A and not match B) yet + + See tests/python/serve/json.ebnf for an example. + + Parameters + ---------- + ebnf_string : str + The grammar string. + + normalize : bool + Whether to normalize the grammar. Default: true. Only set to false for the purpose of + testing. + + In The normalized form of a BNF grammar, every rule is in the form: + `rule_name ::= ("" | (element1_1 element1_2 ...) | (element2_1 element2_2 ...) | ...)`. + + I.e. a list of choices, each choice is a sequence of elements. Elements can be a + character class or a rule reference. And if the rule can be empty, the first choice + will be an empty string. + + simplify : bool + Whether to simplify the grammar to make matching more efficient. Default: true. Not + implemented yet. + + Returns + ------- + grammar : BNFGrammar + The parsed BNF grammar. + """ + return _ffi_api.BNFGrammarFromEBNFString( # type: ignore # pylint: disable=no-member + ebnf_string, normalize, simplify + ) + + def to_string(self) -> str: + """Print the BNF grammar to a string, in standard BNF format. + + Returns + ------- + grammar_string : str + The BNF grammar string. + """ + return str(_ffi_api.BNFGrammarToString(self)) # type: ignore # pylint: disable=no-member + + def __str__(self) -> str: + return self.to_string() + + @staticmethod + def from_json(json_string: str) -> "BNFGrammar": + """Load a BNF grammar from the raw representation of the AST in JSON format. + + Parameters + ---------- + json_string : str + The JSON string. + + Returns + ------- + grammar : BNFGrammar + The loaded BNF grammar. + """ + return _ffi_api.BNFGrammarFromJSON(json_string) # type: ignore # pylint: disable=no-member + + def to_json(self, prettify: bool = True) -> str: + """Serialize the AST. Dump the raw representation of the AST to a JSON file. + + Parameters + ---------- + prettify : bool + Whether to format the JSON string. If False, all whitespaces will be removed. + + Returns + ------- + json_string : str + The JSON string. + """ + return str( + _ffi_api.BNFGrammarToJSON(self, prettify) # type: ignore # pylint: disable=no-member + ) + + @staticmethod + def get_grammar_of_json() -> "BNFGrammar": + """Get the grammar of standard JSON. + + Returns + ------- + grammar : BNFGrammar + The JSON grammar. + """ + return _ffi_api.BNFGrammarGetGrammarOfJSON() # type: ignore # pylint: disable=no-member + + +@tvm._ffi.register_object("mlc.serve.GrammarStateMatcher") # pylint: disable=protected-access +class GrammarStateMatcher(Object): + """A stateful matcher to match tokens to the specified BNF grammar. This class is the core logic + of the grammar-guided generation. + + This class implements the non-deterministic pushdown automaton (NPDA) matching algorithm to + match characters to a BNF grammar. It keep track of the current state of the matching process by + maintaining several stacks internally as possible paths in the NPDA. It also supports + backtracking. + + It is particularly capable of finding the set of tokens that are acceptable for the next step + and storing them in a bitmask. This aids in grammar-guided generation. + + Parameters + ---------- + grammar : BNFGrammar + The BNF grammar to match. + + tokenizer : Union[None, Tokenizer, List[str]] + The tokenizer to use, or the list of tokens. + + (For debug purpose) If None, the matcher will use an empty token set, and can only accept + and match characters. Default: None. + + max_rollback_steps : int + The maximum number of steps to rollback when backtracking. Default: 0. + """ + + def __init__( + self, + grammar: BNFGrammar, + tokenizer: Union[None, Tokenizer, List[str]] = None, + max_rollback_steps: int = 0, + ): + if isinstance(tokenizer, list): + self.__init_handle_by_constructor__( + _ffi_api.GrammarStateMatcherFromTokenTable, # type: ignore # pylint: disable=no-member + grammar, + *tokenizer, + max_rollback_steps, + ) + else: + self.__init_handle_by_constructor__( + _ffi_api.GrammarStateMatcherFromTokenizer, # type: ignore # pylint: disable=no-member + grammar, + tokenizer, + max_rollback_steps, + ) + + def accept_token(self, token_id: int) -> bool: + """Accept one token and update the state of the matcher. + + Parameters + ---------- + token_id : int + The id of the token to accept. + + Returns + ------- + accepted : bool + Whether the token is accepted. + """ + return _ffi_api.GrammarStateMatcherAcceptToken(self, token_id) # type: ignore # pylint: disable=no-member + + def find_next_rejected_tokens(self) -> List[int]: + """Find the ids of the rejected tokens for the next step. + + Returns + ------- + rejected_token_ids : List[int] + A list of rejected token ids. + """ + + return _ffi_api.GrammarStateMatcherFindNextRejectedTokens(self) # type: ignore # pylint: disable=no-member + + def rollback(self, num_tokens: int) -> None: + """Rollback the matcher to a previous state. + + Parameters + ---------- + num_tokens : int + The number of tokens to rollback. It cannot exceed the current number of steps, nor can + it exceed the specified maximum number of rollback steps. + """ + _ffi_api.GrammarStateMatcherRollback(self, num_tokens) # type: ignore # pylint: disable=no-member + + def max_rollback_steps(self) -> int: + """Get the maximum number of rollback steps allowed. + + Returns + ------- + max_rollback_steps : int + The maximum number of rollback steps. + """ + return _ffi_api.GrammarStateMatcherMaxRollbackSteps(self) # type: ignore # pylint: disable=no-member + + def reset_state(self) -> None: + """Reset the matcher to the initial state.""" + _ffi_api.GrammarStateMatcherResetState(self) # type: ignore # pylint: disable=no-member + + def debug_accept_char(self, codepoint: int) -> bool: + """Accept one unicode codepoint to the current state. + + Parameters + ---------- + codepoint : int + The unicode codepoint of the character to be accepted. + """ + return _ffi_api.GrammarStateMatcherDebugAcceptCodepoint( # type: ignore # pylint: disable=no-member + self, codepoint + ) + + def debug_match_complete_string(self, string: str) -> bool: + """Check if a matcher can accept the complete string, and then reach the end of the + grammar. + + Parameters + ---------- + string : str + The string to be matched. + """ + return _ffi_api.GrammarStateMatcherDebugMatchCompleteString(self, string) # type: ignore # pylint: disable=no-member diff --git a/python/mlc_chat/serve/request.py b/python/mlc_chat/serve/request.py new file mode 100644 index 0000000..5c2d8ad --- /dev/null +++ b/python/mlc_chat/serve/request.py @@ -0,0 +1,58 @@ +"""The request class in MLC LLM serving""" + +from typing import List, Union + +import tvm._ffi +from tvm.runtime import Object + +from . import _ffi_api +from .config import GenerationConfig +from .data import Data + + +@tvm._ffi.register_object("mlc.serve.Request") # pylint: disable=protected-access +class Request(Object): + """The user submitted text-generation request, which contains + a unique request id, a list of multi-modal inputs, a set of + generation configuration parameters. + + Parameters + ---------- + request_id : str + The unique identifier of the request. + Different requests should have different ids. + + inputs : List[Data] + The user inputs of a request. Input may have multi-modality. + + generation_config : GenerationConfig + The sampling configuration which may contain temperature, + top_p, repetition_penalty, max_gen_len, etc. + """ + + def __init__( + self, + request_id: str, + inputs: Union[Data, List[Data]], + generation_config: GenerationConfig, + ): + if not isinstance(inputs, list): + inputs = [inputs] + self.__init_handle_by_constructor__( + _ffi_api.Request, # type: ignore # pylint: disable=no-member + request_id, + inputs, + generation_config.asjson(), + ) + + @property + def inputs(self) -> List[Data]: + """The inputs of the request.""" + return _ffi_api.RequestGetInputs(self) # type: ignore # pylint: disable=no-member + + @property + def generation_config(self) -> GenerationConfig: + """The generation config of the request.""" + return GenerationConfig.from_json( + _ffi_api.RequestGetGenerationConfigJSON(self) # type: ignore # pylint: disable=no-member + ) diff --git a/python/mlc_chat/serve/server/__init__.py b/python/mlc_chat/serve/server/__init__.py new file mode 100644 index 0000000..cd4fce2 --- /dev/null +++ b/python/mlc_chat/serve/server/__init__.py @@ -0,0 +1,3 @@ +"""The server related data structure and tools in MLC LLM serve.""" +from .popen_server import PopenServer +from .server_context import ServerContext diff --git a/python/mlc_chat/serve/server/__main__.py b/python/mlc_chat/serve/server/__main__.py new file mode 100644 index 0000000..e57e9f4 --- /dev/null +++ b/python/mlc_chat/serve/server/__main__.py @@ -0,0 +1,71 @@ +"""Entrypoint of RESTful HTTP request server in MLC LLM""" +import argparse +import json + +import fastapi +import uvicorn +from fastapi.middleware.cors import CORSMiddleware + +from .. import async_engine, config +from .server_context import ServerContext + + +def parse_args_and_initialize() -> argparse.Namespace: + """Parse the server arguments and initialize the engine.""" + + args = argparse.ArgumentParser() # pylint: disable=redefined-outer-name + args.add_argument("--model", type=str, required=True) + args.add_argument("--model-lib-path", type=str, required=True) + args.add_argument("--device", type=str, default="auto") + args.add_argument("--max-batch-size", type=int, default=80) + args.add_argument("--max-total-seq-length", type=int) + args.add_argument("--prefill-chunk-size", type=int) + args.add_argument("--enable-tracing", action="store_true") + + args.add_argument("--host", type=str, default="127.0.0.1", help="host name") + args.add_argument("--port", type=int, default=8000, help="port") + args.add_argument("--allow-credentials", action="store_true", help="allow credentials") + args.add_argument("--allowed-origins", type=json.loads, default=["*"], help="allowed origins") + args.add_argument("--allowed-methods", type=json.loads, default=["*"], help="allowed methods") + args.add_argument("--allowed-headers", type=json.loads, default=["*"], help="allowed headers") + + parsed = args.parse_args() + + # Initialize model loading info and KV cache config + model_info = async_engine.ModelInfo( + model=parsed.model, + model_lib_path=parsed.model_lib_path, + device=parsed.device, + ) + kv_cache_config = config.KVCacheConfig( + max_num_sequence=parsed.max_batch_size, + max_total_sequence_length=parsed.max_total_seq_length, + prefill_chunk_size=parsed.prefill_chunk_size, + ) + # Create engine and start the background loop + engine = async_engine.AsyncThreadedEngine( + model_info, kv_cache_config, enable_tracing=parsed.enable_tracing + ) + + ServerContext.add_model(parsed.model, engine) + return parsed + + +if __name__ == "__main__": + # Parse the arguments and initialize the asynchronous engine. + args: argparse.Namespace = parse_args_and_initialize() + app = fastapi.FastAPI() + app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + + # Include the routers from subdirectories. + from ..entrypoints import debug_entrypoints, openai_entrypoints + + app.include_router(openai_entrypoints.app) + app.include_router(debug_entrypoints.app) + uvicorn.run(app, host=args.host, port=args.port, log_level="info") diff --git a/python/mlc_chat/serve/server/popen_server.py b/python/mlc_chat/serve/server/popen_server.py new file mode 100644 index 0000000..09e4688 --- /dev/null +++ b/python/mlc_chat/serve/server/popen_server.py @@ -0,0 +1,119 @@ +"""The MLC LLM server launched in a subprocess.""" +import subprocess +import sys +import time +from pathlib import Path +from typing import Optional + +import psutil +import requests + + +class PopenServer: # pylint: disable=too-many-instance-attributes + """The wrapper of MLC LLM server, which runs the server in + a background subprocess.""" + + def __init__( # pylint: disable=too-many-arguments + self, + model: str, + model_lib_path: str, + device: str = "auto", + *, + max_batch_size: int = 80, + max_total_sequence_length: Optional[int] = None, + enable_tracing: bool = False, + host: str = "127.0.0.1", + port: int = 8000, + ) -> None: + """Please check out `python/mlc_chat/serve/server/__main__.py` + for the server arguments.""" + self.model = model + self.model_lib_path = model_lib_path + self.device = device + self.max_batch_size = max_batch_size + self.max_total_sequence_length = max_total_sequence_length + self.enable_tracing = enable_tracing + self.host = host + self.port = port + self._proc: Optional[subprocess.Popen] = None + + def start(self) -> None: + """Launch the server in a popen subprocess. + Wait until the server becomes ready before return. + """ + cmd = [sys.executable] + cmd += ["-m", "mlc_chat.serve.server"] + cmd += ["--model", self.model] + cmd += ["--model-lib-path", self.model_lib_path] + cmd += ["--device", self.device] + cmd += ["--max-batch-size", str(self.max_batch_size)] + if self.max_total_sequence_length is not None: + cmd += ["--max-total-seq-length", str(self.max_total_sequence_length)] + if self.enable_tracing: + cmd += ["--enable-tracing"] + + cmd += ["--host", self.host] + cmd += ["--port", str(self.port)] + process_path = str(Path(__file__).resolve().parents[4]) + self._proc = subprocess.Popen(cmd, cwd=process_path) # pylint: disable=consider-using-with + # NOTE: DO NOT USE `stdout=subprocess.PIPE, stderr=subprocess.PIPE` + # in subprocess.Popen here. PIPE has a fixed-size buffer with may block + # and hang forever. + + # Try to query the server until it is ready. + openai_v1_models_url = "http://127.0.0.1:8000/v1/models" + query_result = None + timeout = 60 + attempts = 0 + while query_result is None and attempts < timeout: + try: + query_result = requests.get(openai_v1_models_url, timeout=60) + except: # pylint: disable=bare-except + attempts += 1 + time.sleep(1) + + # Check if the subprocess terminates unexpectedly or + # the queries reach the timeout. + process_return_code = self._proc.poll() + if process_return_code is not None: + raise RuntimeError( + "The server fails to launch. " + f'Please check if "{self.model}" is a valid model compiled by MLC LLM.' + ) + if attempts == timeout: + self.terminate() + raise RuntimeError(f"The server fails to launch in {timeout} seconds.") + + def terminate(self) -> None: + """Terminate the server subprocess.""" + if self._proc is None: + return + + # Kill all the child processes. + def kill_child_processes(): + try: + parent = psutil.Process(self._proc.pid) + children = parent.children(recursive=True) + except psutil.NoSuchProcess: + return + + for process in children: + try: + process.kill() + except psutil.NoSuchProcess: + pass + + kill_child_processes() + + # Kill the process. + try: + self._proc.kill() + except OSError: + pass + + # Join the process to avoid zombies. + try: + self._proc.wait(timeout=10.0) + except subprocess.TimeoutExpired: + pass + self._proc = None diff --git a/python/mlc_chat/serve/server/server_context.py b/python/mlc_chat/serve/server/server_context.py new file mode 100644 index 0000000..d382bb7 --- /dev/null +++ b/python/mlc_chat/serve/server/server_context.py @@ -0,0 +1,47 @@ +"""Server context that shared by multiple entrypoint files.""" + +from typing import Dict, List, Optional + +from ...conversation_template import ConvTemplateRegistry +from ...protocol.conversation_protocol import Conversation +from .. import async_engine + + +class ServerContext: + """The global server context, including the running models + and corresponding async engines. + """ + + _models: Dict[str, async_engine.AsyncThreadedEngine] = {} + _conv_templates: Dict[str, Conversation] = {} + + @staticmethod + def add_model(hosted_model: str, engine: async_engine.AsyncThreadedEngine) -> None: + """Add a new model to the server context together with the engine.""" + if hosted_model in ServerContext._models: + raise RuntimeError(f"Model {hosted_model} already running.") + ServerContext._models[hosted_model] = engine + + # Get the conversation template. + if engine.conv_template_name is not None: + conv_template = ConvTemplateRegistry.get_conv_template(engine.conv_template_name) + if conv_template is not None: + ServerContext._conv_templates[hosted_model] = conv_template + + @staticmethod + def get_engine(model: str) -> Optional[async_engine.AsyncThreadedEngine]: + """Get the async engine of the requested model.""" + return ServerContext._models.get(model, None) + + @staticmethod + def get_conv_template(model: str) -> Optional[Conversation]: + """Get the conversation template of the requested model.""" + conv_template = ServerContext._conv_templates.get(model, None) + if conv_template is not None: + return conv_template.model_copy(deep=True) + return None + + @staticmethod + def get_model_list() -> List[str]: + """Get the list of models on serve.""" + return list(ServerContext._models.keys()) diff --git a/python/mlc_chat/streamer.py b/python/mlc_chat/streamer.py new file mode 100644 index 0000000..1eb88af --- /dev/null +++ b/python/mlc_chat/streamer.py @@ -0,0 +1,84 @@ +"""Streamers in MLC LLM.""" + +from typing import List, Union + +import tvm +import tvm._ffi +from tvm.runtime import Object, ShapeTuple + +from . import _ffi_api +from .tokenizer import Tokenizer + + +@tvm._ffi.register_object("mlc.TextStreamer") # pylint: disable=protected-access +class TextStreamer(Object): + """The class that streams back validated utf-8 text strings + that generated by tokenizer. + """ + + def __init__(self, tokenizer: Tokenizer) -> None: + """Create the text streamer from tokenizer""" + self.__init_handle_by_constructor__( + _ffi_api.TextStreamer, tokenizer # type: ignore # pylint: disable=no-member + ) + + def put(self, delta_tokens: Union[List[int], ShapeTuple]) -> str: + """Put new delta tokens into the streamer, and get the UTF-8-valid + delta string. The text streamer may hold some of the input delta tokens + which cannot decode into valid UTF-8 strings. The returned string + is always guaranteed to be UTF-8 valid. + + Parameters + ---------- + delta_tokens : Union[List[int], ShapeTuple] + The new tokens to put into the streamer. + + Returns + ------- + delta_text : str + The decoded delta string after putting the input new tokens. + """ + if isinstance(delta_tokens, list): + delta_tokens = ShapeTuple(delta_tokens) + return _ffi_api.TextStreamerPut( # type: ignore # pylint: disable=no-member + self, delta_tokens + ) + + def finish(self) -> str: + """Return the string decoded by remaining tokens.""" + return _ffi_api.TextStreamerFinish(self) # type: ignore # pylint: disable=no-member + + +@tvm._ffi.register_object("mlc.StopStrHandler") # pylint: disable=protected-access +class StopStrHandler(Object): + """The stop string handler in MLC LLM, which takes input delta tokens + one at a time, and return the output delta token before stopping due to + stop strings.""" + + def __init__(self, stop_strs: List[str], tokenizer: Tokenizer) -> None: + self.__init_handle_by_constructor__( + _ffi_api.StopStrHandler, # type: ignore # pylint: disable=no-member + stop_strs, + tokenizer, + ) + + def put(self, token_id: int) -> List[int]: + """Add new input delta token to the handler, return output + delta tokens before stopping. The stop string handler may hold + some of the input delta token which may be part of a stop string. + The returned tokens are always guaranteed not to be part of stop string. + """ + return list( + _ffi_api.StopStrHandlerPut(self, token_id) # type: ignore # pylint: disable=no-member + ) + + def finish(self) -> List[int]: + """Stop string handling has finished, return remaining cached token ids.""" + return list( + _ffi_api.StopStringHandlerFinish(self) # type: ignore # pylint: disable=no-member + ) + + @property + def stop_triggered(self) -> bool: + """Check if the generation has stopped due to stop string.""" + return _ffi_api.StopStrHandlerStopTriggered(self) # type: ignore # pylint: disable=no-member diff --git a/python/mlc_chat/support/__init__.py b/python/mlc_chat/support/__init__.py new file mode 100644 index 0000000..ca5d7a6 --- /dev/null +++ b/python/mlc_chat/support/__init__.py @@ -0,0 +1,4 @@ +""" +Common utilities used in the Python package. Do not import anything by default, +as they may introduce unnecessary dependencies. +""" diff --git a/python/mlc_chat/support/argparse.py b/python/mlc_chat/support/argparse.py new file mode 100644 index 0000000..81211e8 --- /dev/null +++ b/python/mlc_chat/support/argparse.py @@ -0,0 +1,15 @@ +"""An enhanced argument parser for mlc-chat.""" +import argparse +import sys + + +class ArgumentParser(argparse.ArgumentParser): + """An enhanced argument parser for mlc-chat.""" + + def error(self, message): + """Overrides the behavior when erroring out""" + print("-" * 25 + " Usage " + "-" * 25) + self.print_help() + print("-" * 25 + " Error " + "-" * 25) + print(message, file=sys.stderr) + sys.exit(2) diff --git a/python/mlc_chat/support/auto_config.py b/python/mlc_chat/support/auto_config.py new file mode 100644 index 0000000..a5b73b7 --- /dev/null +++ b/python/mlc_chat/support/auto_config.py @@ -0,0 +1,192 @@ +"""Help function for detecting the model configuration file `config.json`""" +import json +import tempfile +from pathlib import Path +from typing import TYPE_CHECKING + +from . import logging +from .style import bold, green + +if TYPE_CHECKING: + from mlc_chat.model import Model # pylint: disable=unused-import + from mlc_chat.quantization import Quantization # pylint: disable=unused-import + + +logger = logging.getLogger(__name__) + +FOUND = green("Found") + + +def detect_mlc_chat_config(mlc_chat_config: str) -> Path: + """Detect and return the path that points to mlc-chat-config.json. + If `mlc_chat_config` is a directory, it looks for mlc-chat-config.json below it. + + Parameters + --------- + mlc_chat_config : str + The path to `mlc-chat-config.json`, or the directory containing + `mlc-chat-config.json`. + + Returns + ------- + mlc_chat_config_json_path : pathlib.Path + The path points to mlc_chat_config.json. + """ + # pylint: disable=import-outside-toplevel + from mlc_chat.model import MODEL_PRESETS + + from .download import download_mlc_weights + + # pylint: enable=import-outside-toplevel + + if mlc_chat_config.startswith("HF://") or mlc_chat_config.startswith("http"): + mlc_chat_config_path = Path(download_mlc_weights(model_url=mlc_chat_config)) + elif isinstance(mlc_chat_config, str) and mlc_chat_config in MODEL_PRESETS: + logger.info("%s mlc preset model: %s", FOUND, mlc_chat_config) + content = MODEL_PRESETS[mlc_chat_config].copy() + content["model_preset_tag"] = mlc_chat_config + temp_file = tempfile.NamedTemporaryFile( # pylint: disable=consider-using-with + suffix=".json", + delete=False, + ) + logger.info("Dumping config to: %s", temp_file.name) + mlc_chat_config_path = Path(temp_file.name) + with mlc_chat_config_path.open("w", encoding="utf-8") as mlc_chat_config_file: + json.dump(content, mlc_chat_config_file, indent=2) + else: + mlc_chat_config_path = Path(mlc_chat_config) + if not mlc_chat_config_path.exists(): + raise ValueError(f"{mlc_chat_config_path} does not exist.") + + if mlc_chat_config_path.is_dir(): + # search mlc-chat-config.json under path + mlc_chat_config_json_path = mlc_chat_config_path / "mlc-chat-config.json" + if not mlc_chat_config_json_path.exists(): + raise ValueError(f"Fail to find mlc_chat_config.json under {mlc_chat_config_path}.") + else: + mlc_chat_config_json_path = mlc_chat_config_path + + logger.info("%s model configuration: %s", FOUND, mlc_chat_config_json_path) + return mlc_chat_config_json_path + + +def detect_config(config: str) -> Path: + """Detect and return the path that points to config.json. If `config` is a directory, + it looks for config.json below it. + + Parameters + --------- + config : str + The preset name of the model, or the path to `config.json`, or the directory containing + `config.json`. + + Returns + ------- + config_json_path : pathlib.Path + The path points to config.json. + """ + from mlc_chat.model import MODEL_PRESETS # pylint: disable=import-outside-toplevel + + if isinstance(config, str) and config in MODEL_PRESETS: + logger.info("%s preset model: %s", FOUND, config) + content = MODEL_PRESETS[config].copy() + content["model_preset_tag"] = config + temp_file = tempfile.NamedTemporaryFile( # pylint: disable=consider-using-with + suffix=".json", + delete=False, + ) + logger.info("Dumping config to: %s", temp_file.name) + config_path = Path(temp_file.name) + with config_path.open("w", encoding="utf-8") as config_file: + json.dump(content, config_file, indent=2) + else: + config_path = Path(config) + if not config_path.exists(): + raise ValueError(f"{config_path} does not exist.") + + if config_path.is_dir(): + # search config.json under config path + config_json_path = config_path / "config.json" + if not config_json_path.exists(): + raise ValueError(f"Fail to find config.json under {config_path}.") + else: + config_json_path = config_path + + logger.info("%s model configuration: %s", FOUND, config_json_path) + return config_json_path + + +def detect_model_type(model_type: str, config: Path) -> "Model": + """Detect the model type from the configuration file. If `model_type` is "auto", it will be + inferred from the configuration file. Otherwise, it will be used as the model type, and sanity + check will be performed. + + Parameters + ---------- + model_type : str + The model type, for example, "llama". + + config : pathlib.Path + The path to config.json. + + Returns + ------- + model : mlc_chat.compiler.Model + The model type. + """ + + from mlc_chat.model import MODELS # pylint: disable=import-outside-toplevel + + if model_type == "auto": + with open(config, "r", encoding="utf-8") as config_file: + cfg = json.load(config_file) + if "model_type" not in cfg and ( + "model_config" not in cfg or "model_type" not in cfg["model_config"] + ): + raise ValueError( + f"'model_type' not found in: {config}. " + f"Please explicitly specify `--model-type` instead." + ) + model_type = cfg["model_type"] if "model_type" in cfg else cfg["model_config"]["model_type"] + if model_type in ["mixformer-sequential"]: + model_type = "phi-msft" + logger.info("%s model type: %s. Use `--model-type` to override.", FOUND, bold(model_type)) + if model_type not in MODELS: + raise ValueError(f"Unknown model type: {model_type}. Available ones: {list(MODELS.keys())}") + return MODELS[model_type] + + +def detect_quantization(quantization_arg: str, config: Path) -> "Quantization": + """Detect the model quantization scheme from the configuration file or `--quantization` + argument. If `--quantization` is provided, it will override the value on the configuration + file. + + Parameters + ---------- + quantization_arg : str + The quantization scheme, for example, "q4f16_1". + + config : pathlib.Path + The path to mlc-chat-config.json. + + Returns + ------- + quantization : mlc_chat.quantization.Quantization + The model quantization scheme. + """ + from mlc_chat.quantization import ( # pylint: disable=import-outside-toplevel + QUANTIZATION, + ) + + with open(config, "r", encoding="utf-8") as config_file: + cfg = json.load(config_file) + if quantization_arg is not None: + quantization = QUANTIZATION[quantization_arg] + elif "quantization" in cfg: + quantization = QUANTIZATION[cfg["quantization"]] + else: + raise ValueError( + f"'quantization' not found in: {config}. " + f"Please explicitly specify `--quantization` instead." + ) + return quantization diff --git a/python/mlc_chat/support/auto_device.py b/python/mlc_chat/support/auto_device.py new file mode 100644 index 0000000..6d18de4 --- /dev/null +++ b/python/mlc_chat/support/auto_device.py @@ -0,0 +1,87 @@ +"""Automatic detection of the device available on the local machine.""" +import subprocess +import sys +from typing import Dict, Optional + +import tvm +from tvm.runtime import Device + +from . import logging +from .style import bold, green, red + +FOUND = green("Found") +NOT_FOUND = red("Not found") +AUTO_DETECT_DEVICES = ["cuda", "rocm", "metal", "vulkan", "opencl"] +_RESULT_CACHE: Dict[str, bool] = {} + + +logger = logging.getLogger(__name__) + + +def detect_device(device_hint: str) -> Optional[Device]: + """Detect locally available device from string hint.""" + if device_hint == "auto": + device = None + for device_type in AUTO_DETECT_DEVICES: + cur_device = tvm.device(dev_type=device_type, dev_id=0) + if _device_exists(cur_device): + if device is None: + device = cur_device + if device is None: + logger.info("%s: No available device detected", NOT_FOUND) + return None + logger.info("Using device: %s", bold(device2str(device))) + return device + try: + device = tvm.device(device_hint) + except Exception as err: + raise ValueError(f"Invalid device name: {device_hint}") from err + if not _device_exists(device): + raise ValueError(f"Device is not found on your local environment: {device_hint}") + return device + + +def device2str(device: Device) -> str: + """Convert a TVM device object to string.""" + return f"{tvm.runtime.Device.MASK2STR[device.device_type]}:{device.device_id}" + + +def _device_exists(device: Device) -> bool: + device_type = tvm.runtime.Device.MASK2STR[device.device_type] + device_str = device2str(device) + if device_str in _RESULT_CACHE: + return _RESULT_CACHE[device_str] + cmd = [ + sys.executable, + "-m", + "mlc_chat.cli.check_device", + device_type, + ] + prefix = "check_device:" + subproc_outputs = [ + line[len(prefix) :].strip() + for line in subprocess.run( + cmd, + capture_output=True, + text=True, + check=False, + ) + .stdout.strip() + .splitlines() + if line.startswith(prefix) + ] + if subproc_outputs: + if subproc_outputs[0]: + for i in subproc_outputs[0].split(","): + logger.info("%s device: %s:%s", FOUND, device_type, i) + _RESULT_CACHE[f"{device_type}:{i}"] = True + else: + logger.error( + "GPU device detection failed. Please report this issue with the output of command: %s", + " ".join(cmd), + ) + if device_str in _RESULT_CACHE: + return _RESULT_CACHE[device_str] + logger.info("%s device: %s", NOT_FOUND, device_str) + _RESULT_CACHE[device_str] = False + return False diff --git a/python/mlc_chat/support/auto_target.py b/python/mlc_chat/support/auto_target.py new file mode 100644 index 0000000..80041db --- /dev/null +++ b/python/mlc_chat/support/auto_target.py @@ -0,0 +1,398 @@ +"""Helper functions for target auto-detection.""" +import os +from typing import TYPE_CHECKING, Callable, List, Optional, Tuple + +from tvm import IRModule, relax +from tvm._ffi import get_global_func, register_func +from tvm.contrib import ndk, tar, xcode +from tvm.ir.transform import Pass +from tvm.target import Target + +from . import logging +from .auto_device import AUTO_DETECT_DEVICES, detect_device, device2str +from .constants import MLC_MULTI_ARCH +from .style import bold, green, red + +if TYPE_CHECKING: + from mlc_chat.compiler.compile import CompileArgs + + +logger = logging.getLogger(__name__) + +# TODO: add help message on how to specify the target manually # pylint: disable=fixme +HELP_MSG = """TBD""" +FOUND = green("Found") +NOT_FOUND = red("Not found") +BuildFunc = Callable[[IRModule, "CompileArgs", Pass], None] + + +def detect_target_and_host(target_hint: str, host_hint: str = "auto") -> Tuple[Target, BuildFunc]: + """Detect the configuration for the target device and its host, for example, target GPU and + the host CPU. + + Parameters + ---------- + target_hint : str + The hint for the target device. + + host_hint : str + The hint for the host CPU, default is "auto". + """ + target, build_func = _detect_target_gpu(target_hint) + if target.host is None: + target = Target(target, host=_detect_target_host(host_hint)) + if target.kind.name == "cuda": + _register_cuda_hook(target) + return target, build_func + + +def _detect_target_gpu(hint: str) -> Tuple[Target, BuildFunc]: + if hint in ["iphone", "android", "webgpu", "mali", "opencl"]: + hint += ":generic" + if hint == "auto" or hint in AUTO_DETECT_DEVICES: + target: Optional[Target] = None + device = detect_device(hint) + if device is not None: + device_str = device2str(device) + try: + target = Target.from_device(device) + except ValueError: + logger.info("%s: Cannot detect target from device: %s", NOT_FOUND, device_str) + if target is None: + raise ValueError(f"No target detected from device: {hint}. Please specify explicitly") + logger.info( + '%s configuration of target device "%s": %s', + FOUND, + bold(device_str), + target.export(), + ) + return target, _build_default() + if hint in PRESET: + preset = PRESET[hint] + target = Target(preset["target"]) # type: ignore[index] + build = preset.get("build", _build_default) # type: ignore[attr-defined] + return target, build() + if _is_device(hint): + logger.info("Detecting target device: %s", hint) + target = Target.from_device(hint) + logger.info("%s target: %s", FOUND, target.export()) + return target, _build_default() + try: + logger.info("Try creating device target from string: %s", hint) + target = Target(hint) + logger.info("%s target: %s", FOUND, target.export()) + return target, _build_default() + except Exception as err: + logger.info("%s: Failed to create target", NOT_FOUND) + raise ValueError(f"Invalid target: {hint}") from err + + +def _detect_target_host(hint: str) -> Target: + """Detect the host CPU architecture.""" + if hint == "auto": + target_triple = get_global_func("tvm.codegen.llvm.GetDefaultTargetTriple")() + target = Target.from_device("cpu") + logger.info("%s host LLVM triple: %s", FOUND, bold(target.attrs["mtriple"])) + logger.info("%s host LLVM CPU: %s", FOUND, bold(target.attrs["mcpu"])) + return target + target_triple = hint + logger.info("Using LLVM triple specified by --host: %s", bold(target_triple)) + return Target({"kind": "llvm", "mtriple": target_triple}) + + +def _is_device(device: str): + if " " in device: + return False + if device.count(":") != 1: + return False + return True + + +def _add_system_lib_prefix(mod: IRModule, prefix: str, is_system_lib: bool) -> IRModule: + if is_system_lib and prefix: + mod = mod.with_attrs({"system_lib_prefix": prefix}) # type: ignore[dict-item] + elif is_system_lib: + logger.warning( + "%s is not specified when building a static library", + bold("--system-lib-prefix"), + ) + elif prefix: + logger.warning( + "--system-lib-prefix is specified, but it will not take any effect " + "when building the shared library" + ) + return mod + + +def _build_metal_x86_64(): + def build(mod: IRModule, args: "CompileArgs", pipeline=None): + output = args.output + mod = _add_system_lib_prefix(mod, args.system_lib_prefix, is_system_lib=False) + assert output.suffix == ".dylib" + relax.build( + mod, + target=args.target, + pipeline=pipeline, + ).export_library( + str(output), + fcompile=xcode.create_dylib, + sdk="macosx", + arch="x86_64", + ) + + return build + + +def _build_iphone(): + @register_func("tvm_callback_metal_compile", override=True) + def compile_metal(src, target): + if target.libs: + return xcode.compile_metal(src, sdk=target.libs[0]) + return xcode.compile_metal(src) + + def build(mod: IRModule, args: "CompileArgs", pipeline=None): + output = args.output + mod = _add_system_lib_prefix(mod, args.system_lib_prefix, is_system_lib=True) + assert output.suffix == ".tar" + relax.build( + mod, + target=args.target, + pipeline=pipeline, + system_lib=True, + ).export_library( + str(output), + fcompile=tar.tar, + ) + + return build + + +def _build_android(): + def build(mod: IRModule, args: "CompileArgs", pipeline=None): + output = args.output + mod = _add_system_lib_prefix(mod, args.system_lib_prefix, is_system_lib=True) + assert output.suffix == ".tar" + relax.build( + mod, + target=args.target, + pipeline=pipeline, + system_lib=True, + ).export_library( + str(output), + fcompile=tar.tar, + ) + + return build + + +def _build_webgpu(): + def build(mod: IRModule, args: "CompileArgs", pipeline=None): + output = args.output + mod = _add_system_lib_prefix(mod, args.system_lib_prefix, is_system_lib=True) + assert output.suffix == ".wasm" + relax.build( + mod, + target=args.target, + pipeline=pipeline, + system_lib=True, + ).export_library( + str(output), + ) + + return build + + +def _build_mali(): + def build(mod: IRModule, args: "CompileArgs", pipeline=None): + output = args.output + mod = _add_system_lib_prefix(mod, args.system_lib_prefix, is_system_lib=True) + assert output.suffix == ".so" + mod = relax.build( + mod, + target=args.target, + pipeline=pipeline, + system_lib=True, + ) + if "TVM_NDK_CC" in os.environ: + mod.export_library(str(output), fcompile=ndk.create_shared) + else: + mod.export_library(str(output)) + + return build + + +def _build_default(): + def build(mod: IRModule, args: "CompileArgs", pipeline=None): + output = args.output + if output.suffix in [".tar", ".lib"]: + system_lib = True + elif output.suffix in [".so", ".dylib", ".dll"]: + system_lib = False + else: + logger.warning("Unknown output suffix: %s. Assuming shared library.", output.suffix) + system_lib = False + mod = _add_system_lib_prefix(mod, args.system_lib_prefix, is_system_lib=system_lib) + relax.build( + mod, + target=args.target, + pipeline=pipeline, + system_lib=system_lib, + ).export_library( + str(output), + ) + + return build + + +def detect_cuda_arch_list(target: Target) -> List[int]: + """Detect the CUDA architecture list from the target.""" + assert target.kind.name == "cuda", f"Expect target to be CUDA, but got {target}" + if MLC_MULTI_ARCH is not None: + multi_arch = [int(x.strip()) for x in MLC_MULTI_ARCH.split(",")] + else: + assert target.arch.startswith("sm_") + multi_arch = [int(target.arch[3:])] + multi_arch = list(set(multi_arch)) + return multi_arch + + +def _register_cuda_hook(target: Target): + if MLC_MULTI_ARCH is None: + default_arch = target.attrs.get("arch", None) + logger.info("Generating code for CUDA architecture: %s", bold(default_arch)) + logger.info( + "To produce multi-arch fatbin, set environment variable %s. " + "Example: MLC_MULTI_ARCH=70,72,75,80,86,87,89,90", + bold("MLC_MULTI_ARCH"), + ) + multi_arch = None + else: + logger.info("%s %s: %s", FOUND, bold("MLC_MULTI_ARCH"), MLC_MULTI_ARCH) + multi_arch = [int(x.strip()) for x in MLC_MULTI_ARCH.split(",")] + logger.info("Generating code for CUDA architecture: %s", multi_arch) + + @register_func("tvm_callback_cuda_compile", override=True) + def tvm_callback_cuda_compile(code, target): # pylint: disable=unused-argument + """use nvcc to generate fatbin code for better optimization""" + from tvm.contrib import nvcc # pylint: disable=import-outside-toplevel + + if multi_arch is None: + ptx = nvcc.compile_cuda(code, target_format="fatbin") + else: + arch = [] + for compute_version in multi_arch: + arch += ["-gencode", f"arch=compute_{compute_version},code=sm_{compute_version}"] + ptx = nvcc.compile_cuda(code, target_format="fatbin", arch=arch) + return ptx + + +def detect_system_lib_prefix( + target_hint: str, prefix_hint: str, model_name: str, quantization: str +) -> str: + """Detect the iOS / Android system lib prefix to identify the library needed to load the app. + + Parameters + ---------- + target_hint : str + The hint for the target device. + + prefix_hint : str + The hint for the system lib prefix. + """ + if prefix_hint == "auto" and target_hint in ["iphone", "android"]: + prefix = f"{model_name}_{quantization}_".replace("-", "_") + logger.warning( + "%s is automatically picked from the filename, %s, this allows us to use the filename " + "as the model_lib in android/iOS builds. Please avoid renaming the .tar file when " + "uploading the prebuilt.", + bold("--system-lib-prefix"), + bold(prefix), + ) + return prefix + if target_hint not in ["iphone", "android"]: + return "" + return prefix_hint + + +PRESET = { + "iphone:generic": { + "target": { + "kind": "metal", + "max_threads_per_block": 256, + "max_shared_memory_per_block": 32768, + "thread_warp_size": 1, + "libs": ["iphoneos"], + "host": { + "kind": "llvm", + "mtriple": "arm64-apple-darwin", + }, + }, + "build": _build_iphone, + }, + "android:generic": { + "target": { + "kind": "opencl", + "host": { + "kind": "llvm", + "mtriple": "aarch64-linux-android", + }, + }, + "build": _build_android, + }, + "metal:x86-64": { + "target": { + "kind": "metal", + "max_threads_per_block": 256, + "max_shared_memory_per_block": 32768, + "thread_warp_size": 1, + }, + "build": _build_metal_x86_64, + }, + "webgpu:generic": { + "target": { + "kind": "webgpu", + "host": { + "kind": "llvm", + "mtriple": "wasm32-unknown-unknown-wasm", + }, + }, + "build": _build_webgpu, + }, + "opencl:generic": { + "target": { + "kind": "opencl", + }, + }, + "mali:generic": { + "target": { + "kind": "opencl", + "host": { + "kind": "llvm", + "mtriple": "aarch64-linux-gnu", + }, + }, + "build": _build_mali, + }, + "metal:generic": { + "target": { + "kind": "metal", + "max_threads_per_block": 256, + "max_shared_memory_per_block": 32768, + "thread_warp_size": 1, + }, + }, + "vulkan:generic": { + "target": { + "kind": "vulkan", + "max_threads_per_block": 256, + "max_shared_memory_per_block": 32768, + "thread_warp_size": 1, + "supports_float16": 1, + "supports_int16": 1, + "supports_int8": 1, + "supports_8bit_buffer": 1, + "supports_16bit_buffer": 1, + "supports_storage_buffer_storage_class": 1, + }, + }, +} diff --git a/python/mlc_chat/support/auto_weight.py b/python/mlc_chat/support/auto_weight.py new file mode 100644 index 0000000..84d8621 --- /dev/null +++ b/python/mlc_chat/support/auto_weight.py @@ -0,0 +1,177 @@ +"""Help functions for detecting weight paths and weight formats.""" +import json +from pathlib import Path +from typing import List, Optional, Tuple + +from . import logging +from .style import bold, green, red + +logger = logging.getLogger(__name__) + +FOUND = green("Found") +NOT_FOUND = red("Not found") + + +def detect_weight( + weight_path: Path, + config_json_path: Path, + weight_format: str = "auto", +) -> Tuple[Path, str]: + """Detect the weight directory, and detect the weight format. + + Parameters + --------- + weight_path : pathlib.Path + The path to weight files. If `weight_path` is not None, check if it exists. Otherwise, find + `weight_path` in `config.json` or use the same directory as `config.json`. + + config_json_path: pathlib.Path + The path to `config.json`. + + weight_format : str + The hint for the weight format. If it is "auto", guess the weight format. + Otherwise, check the weights are in that format. + Available weight formats: + - auto (guess the weight format) + - huggingface-torch (validate via checking pytorch_model.bin.index.json) + - huggingface-safetensor (validate via checking model.safetensors.index.json) + - awq + - ggml + - gguf + + Returns + ------- + weight_config_path : pathlib.Path + The path that points to the weights config file or the weights directory. + + weight_format : str + The valid weight format. + """ + if weight_path is None: + assert ( + config_json_path is not None and config_json_path.exists() + ), "Please provide config.json path." + + # 1. Find the weight_path in config.json + with open(config_json_path, encoding="utf-8") as i_f: + config = json.load(i_f) + if "weight_path" in config: + weight_path = Path(config["weight_path"]) + logger.info('Found "weight_path" in config.json: %s', weight_path) + if not weight_path.exists(): + raise ValueError(f"weight_path doesn't exist: {weight_path}") + else: + # 2. Find the weights file in the same directory as config.json + weight_path = config_json_path.parent + else: + if not weight_path.exists(): + raise ValueError(f"weight_path doesn't exist: {weight_path}") + + logger.info("Finding weights in: %s", weight_path) + + # check weight format + # weight_format = "auto", guess the weight format. + # otherwise, check the weight format is valid. + if weight_format == "auto": + return _guess_weight_format(weight_path) + + if weight_format not in AVAILABLE_WEIGHT_FORMAT: + raise ValueError( + f"Available weight format list: {AVAILABLE_WEIGHT_FORMAT}, but got {weight_format}" + ) + if weight_format in CHECK_FORMAT_METHODS: + check_func = CHECK_FORMAT_METHODS[weight_format] + weight_config_path = check_func(weight_path) + if not weight_config_path: + raise ValueError(f"The weight is not in {weight_format} format.") + else: + weight_config_path = weight_path + return weight_config_path, weight_format + + +def _guess_weight_format(weight_path: Path) -> Tuple[Path, str]: + possible_formats: List[Tuple[Path, str]] = [] + for weight_format, check_func in CHECK_FORMAT_METHODS.items(): + weight_config_path = check_func(weight_path) + if weight_config_path: + possible_formats.append((weight_config_path, weight_format)) + + if len(possible_formats) == 0: + raise ValueError( + "Fail to detect source weight format. " + "Use `--source-format` to explicitly specify the format." + ) + + weight_config_path, selected_format = possible_formats[0] + logger.info( + "Using source weight configuration: %s. Use `--source` to override.", + bold(str(weight_config_path)), + ) + logger.info( + "Using source weight format: %s. Use `--source-format` to override.", + bold(selected_format), + ) + return weight_config_path, selected_format + + +def _check_pytorch(weight_path: Path) -> Optional[Path]: + pytorch_json_path = weight_path / "pytorch_model.bin.index.json" + if pytorch_json_path.exists(): + logger.info( + "%s source weight format: huggingface-torch. Source configuration: %s", + FOUND, + pytorch_json_path, + ) + return pytorch_json_path + + pytorch_file_path = weight_path / "pytorch_model.bin" + if pytorch_file_path.exists(): + logger.info( + "%s source weight format: huggingface-torch. Source configuration: %s", + FOUND, + pytorch_file_path, + ) + return pytorch_file_path + + logger.info("%s Huggingface PyTorch", NOT_FOUND) + return None + + +def _check_safetensor(weight_path: Path) -> Optional[Path]: + safetensor_json_path = weight_path / "model.safetensors.index.json" + if safetensor_json_path.exists(): + logger.info( + "%s source weight format: huggingface-safetensor. Source configuration: %s", + FOUND, + safetensor_json_path, + ) + return safetensor_json_path + + safetensor_file_path = weight_path / "model.safetensors" + if safetensor_file_path.exists(): + from safetensors.torch import ( # pylint: disable=import-outside-toplevel,import-error + load_file, + ) + + weights = load_file(safetensor_file_path, device="cpu") + weight_map = {key: "model.safetensors" for key in weights} + with open(safetensor_json_path, "w", encoding="utf-8") as file: + json.dump({"weight_map": weight_map}, file, indent=2) + logger.info( + "%s source weight format: huggingface-safetensor. Source configuration: %s", + FOUND, + safetensor_json_path, + ) + return safetensor_json_path + + logger.info("%s Huggingface Safetensor", NOT_FOUND) + return None + + +CHECK_FORMAT_METHODS = { + "huggingface-torch": _check_pytorch, + "huggingface-safetensor": _check_safetensor, +} + +# "ggml", "gguf" are not supported yet. +AVAILABLE_WEIGHT_FORMAT = ["huggingface-torch", "huggingface-safetensor", "awq"] diff --git a/python/mlc_chat/support/config.py b/python/mlc_chat/support/config.py new file mode 100644 index 0000000..e3ccfce --- /dev/null +++ b/python/mlc_chat/support/config.py @@ -0,0 +1,113 @@ +""" +A common base class for configuration. A configuration could be initialized from its constructor, +a JSON string or a JSON file, and irrelevant fields during initialization are automatically moved +to the `kwargs` field. + +Take model configuration as an example: it is usually a JSON file in HuggingFace that contains +the model's hyperparameters. For instance, Vicuna-13b-v1.5-16k contains the following +[JSON file](https://huggingface.co/lmsys/vicuna-13b-v1.5-16k/blob/main/config.json). +The base class allows us to load the configuration from this JSON file, moving irrelevant fields +into `kwargs`, such as `transformers_version` and `use_cache`. +""" +# pylint: disable=too-few-public-methods +import dataclasses +import json +from pathlib import Path +from typing import Any, Dict, Type, TypeVar + +from . import logging +from .style import bold, red + +logger = logging.getLogger(__name__) + +ConfigClass = TypeVar("ConfigClass", bound="ConfigBase") + + +@dataclasses.dataclass +class ConfigBase: + """Base class for configurations, providing a common interface for loading configs from a + JSON file or a dict. It requires the subclasses to be dataclasses, and has an `kwargs` field + that stores the extra fields that are not defined in the dataclass. + """ + + @classmethod + def from_dict(cls: Type[ConfigClass], source: Dict[str, Any]) -> ConfigClass: + """Create a config object from a dictionary. + + Parameters + ---------- + source : Dict[str, Any] + Source to create config from, usually loaded from `config.json` in HuggingFace style. + + Returns + ------- + cfg : ConfigClass + An instance of the config object. + """ + field_names = [field.name for field in dataclasses.fields(cls)] # type: ignore[arg-type] + fields = {k: v for k, v in source.items() if k in field_names} + kwargs = {k: v for k, v in source.items() if k not in field_names} + return cls(**fields, kwargs=kwargs) # type: ignore[call-arg] + + @classmethod + def from_file(cls: Type[ConfigClass], source: Path) -> ConfigClass: + """Create a config object from a file. + + Parameters + ---------- + cfg_cls : Type[ConfigClass] + The config class to create, for example, LlamaConfig. + + source : pathlib.Path + Path to the source file, usually `config.json` in HuggingFace repo. + + Returns + ------- + cfg : ConfigClass + An instance of the config object. + """ + with source.open("r", encoding="utf-8") as in_file: + return cls.from_dict(json.load(in_file)) + + def asdict(self): + """Convert the config object to a dictionary. + + Returns + ------- + Dict[str, Any] + A dictionary representation of the config object. + """ + result = dataclasses.asdict(self) + result.pop("kwargs") + return result + + +class ConfigOverrideBase: + """Base class for ConfigOverride, providing a common interface for overriding configs. + It requires the subclasses to be dataclasses. + """ + + def apply(self, config): + """Apply the overrides to the given config.""" + updated = config.asdict() + for field in dataclasses.fields(self): + key = field.name + value = getattr(self, key) + if value is None: + continue + if key not in updated: + logger.warning( + "%s: Cannot override %s, because %s does not have this field", + red("Warning"), + bold(key), + bold(type(config).__name__), + ) + else: + logger.info( # pylint: disable=logging-fstring-interpolation + f"Overriding {bold(key)} from {updated[key]} to {value}" + ) + updated[key] = value + return type(config).from_dict(updated) + + +__all__ = ["ConfigBase", "ConfigOverrideBase"] diff --git a/python/mlc_chat/support/constants.py b/python/mlc_chat/support/constants.py new file mode 100644 index 0000000..09e4893 --- /dev/null +++ b/python/mlc_chat/support/constants.py @@ -0,0 +1,55 @@ +"""Environment variables used by the MLC LLM.""" +import os +import sys +from pathlib import Path + + +def _check(): + if MLC_JIT_POLICY not in ["ON", "OFF", "REDO", "READONLY"]: + raise ValueError( + 'Invalid MLC_JIT_POLICY. It has to be one of "ON", "OFF", "REDO", "READONLY"' + f"but got {MLC_JIT_POLICY}." + ) + + +def _get_cache_dir() -> Path: + if "MLC_CACHE_DIR" in os.environ: + result = Path(os.environ["MLC_CACHE_DIR"]) + elif sys.platform == "win32": + result = Path(os.environ["LOCALAPPDATA"]) + result = result / "mlc_chat" + elif os.getenv("XDG_CACHE_HOME", None) is not None: + result = Path(os.getenv("XDG_CACHE_HOME")) + result = result / "mlc_chat" + else: + result = Path(os.path.expanduser("~/.cache")) + result = result / "mlc_chat" + result.mkdir(parents=True, exist_ok=True) + if not result.is_dir(): + raise ValueError( + f"The default cache directory is not a directory: {result}. " + "Use environment variable MLC_CACHE_DIR to specify a valid cache directory." + ) + (result / "model_weights").mkdir(parents=True, exist_ok=True) + (result / "model_lib").mkdir(parents=True, exist_ok=True) + return result + + +def _get_dso_suffix() -> str: + if "MLC_DSO_SUFFIX" in os.environ: + return os.environ["MLC_DSO_SUFFIX"] + if sys.platform == "win32": + return "dll" + if sys.platform == "darwin": + return "dylib" + return "so" + + +MLC_TEMP_DIR = os.getenv("MLC_TEMP_DIR", None) +MLC_MULTI_ARCH = os.environ.get("MLC_MULTI_ARCH", None) +MLC_CACHE_DIR: Path = _get_cache_dir() +MLC_JIT_POLICY = os.environ.get("MLC_JIT_POLICY", "ON") +MLC_DSO_SUFFIX = _get_dso_suffix() + + +_check() diff --git a/python/mlc_chat/support/convert_tiktoken.py b/python/mlc_chat/support/convert_tiktoken.py new file mode 100644 index 0000000..9bf0504 --- /dev/null +++ b/python/mlc_chat/support/convert_tiktoken.py @@ -0,0 +1,163 @@ +""" +Adapted from https://gist.github.com/xenova/a452a6474428de0182b17605a98631ee +Generator of mlc-chat-config.json and tokenizer configuration. +""" + +# pylint: disable=import-error +# isort: off +import json +import os +from typing import Dict, List, Optional + +from transformers import AutoTokenizer +from transformers.models.gpt2.tokenization_gpt2 import ( + bytes_to_unicode, +) + +byte_encoder = bytes_to_unicode() + + +def token_bytes_to_string(b): + """Convert a token from bytes to a string""" + return "".join([byte_encoder[ord(char)] for char in b.decode("latin-1")]) + + +def bpe( + mergeable_ranks: Dict[bytes, int], token: bytes, max_rank: Optional[int] = None +) -> List[bytes]: + """Adapted from https://github.com/openai/tiktoken/issues/60#issuecomment-1499977960""" + parts = [bytes([b]) for b in token] + while True: + min_idx = None + min_rank = None + for i, pair in enumerate(zip(parts[:-1], parts[1:])): + rank = mergeable_ranks.get(pair[0] + pair[1]) + if rank is not None and (min_rank is None or rank < min_rank): + min_idx = i + min_rank = rank + if min_rank is None or (max_rank is not None and min_rank >= max_rank): + break + assert min_idx is not None + parts = parts[:min_idx] + [parts[min_idx] + parts[min_idx + 1]] + parts[min_idx + 2 :] + return parts + + +def generate_vocab_and_merges(encoder, mergeable_ranks): + """Generate vocab and merges in huggingface tokenizers format""" + merges = [] + vocab = {} + for token, rank in mergeable_ranks.items(): + vocab[token_bytes_to_string(token)] = rank + + if len(token) == 1: + continue + merged = tuple(bpe(mergeable_ranks, token, max_rank=rank)) + assert len(merged) == 2 + + merges.append(" ".join(map(token_bytes_to_string, merged))) + + # Also add special tokens + vocab.update(encoder._special_tokens) # pylint: disable=protected-access + + return vocab, merges + + +def convert_tiktoken(model_path, output_dir, context_window_size=None): + """Convert tiktoken tokenizers to huggingface tokenizers style""" + tiktoken_tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) + encoder = tiktoken_tokenizer.tokenizer + + vocab, merges = generate_vocab_and_merges(encoder, tiktoken_tokenizer.get_vocab()) + + added_tokens = [ + { + "id": id, + "content": content, + "single_word": False, + "lstrip": False, + "rstrip": False, + "normalized": False, + "special": True, + } + for content, id in encoder._special_tokens.items() # pylint: disable=protected-access + ] + + tokenizer_template = { + "version": "1.0", + "truncation": None, + "padding": None, + "added_tokens": added_tokens, + "normalizer": None, + "pre_tokenizer": { + "type": "ByteLevel", + "add_prefix_space": False, + "trim_offsets": True, + "use_regex": True, + }, + "post_processor": { + "type": "ByteLevel", + "add_prefix_space": True, + "trim_offsets": False, + "use_regex": True, + }, + "decoder": { + "type": "ByteLevel", + "add_prefix_space": True, + "trim_offsets": True, + "use_regex": True, + }, + "model": { + "type": "BPE", + "dropout": None, + "unk_token": None, + "continuing_subword_prefix": "", + "end_of_word_suffix": "", + "fuse_unk": False, + "byte_fallback": False, + "vocab": vocab, + "merges": merges, + }, + } + + tokenizer_config_template = { + "add_prefix_space": False, + "bos_token": "<|endoftext|>", + "clean_up_tokenization_spaces": True, + "eos_token": "<|endoftext|>", + "unk_token": "<|endoftext|>", + } + + tokenizer_name = type(tiktoken_tokenizer).__name__ + + tokenizer_config_template["tokenizer_class"] = tokenizer_name + if context_window_size: + tokenizer_config_template["model_max_length"] = context_window_size + tokenizer_config_template = dict(sorted(tokenizer_config_template.items(), key=lambda x: x[0])) + + os.makedirs(output_dir, exist_ok=True) + + # Save to files + with open(os.path.join(output_dir, "vocab.json"), "w", encoding="utf-8") as fp: + json.dump(vocab, fp, indent=2, ensure_ascii=False) + + with open(os.path.join(output_dir, "tokenizer.json"), "w", encoding="utf-8") as fp: + json.dump(tokenizer_template, fp, indent=2, ensure_ascii=False) + + with open(os.path.join(output_dir, "tokenizer_config.json"), "w", encoding="utf-8") as fp: + json.dump(tokenizer_config_template, fp, indent=2, ensure_ascii=False) + + with open(os.path.join(output_dir, "special_tokens_map.json"), "w", encoding="utf-8") as fp: + json.dump( + { + "bos_token": "<|endoftext|>", + "eos_token": "<|endoftext|>", + "unk_token": "<|endoftext|>", + }, + fp, + indent=2, + ensure_ascii=False, + ) + + with open(os.path.join(output_dir, "merges.txt"), "w", encoding="utf-8") as fp: + fp.write("#version: 0.2\n") + fp.write("\n".join(merges)) diff --git a/python/mlc_chat/support/download.py b/python/mlc_chat/support/download.py new file mode 100644 index 0000000..10b1620 --- /dev/null +++ b/python/mlc_chat/support/download.py @@ -0,0 +1,147 @@ +"""Common utilities for downloading files from HuggingFace or other URLs online.""" +import concurrent.futures as cf +import hashlib +import json +import os +import shutil +import subprocess +import tempfile +from pathlib import Path +from typing import Optional, Tuple + +import requests # pylint: disable=import-error + +from . import logging, tqdm +from .constants import MLC_CACHE_DIR, MLC_TEMP_DIR +from .style import bold + +logger = logging.getLogger(__name__) + + +def _ensure_directory_not_exist(path: Path, force_redo: bool) -> None: + if path.exists(): + if force_redo: + logger.info("Deleting existing directory: %s", path) + shutil.rmtree(path) + else: + raise ValueError(f"Directory already exists: {path}") + else: + path.parent.mkdir(parents=True, exist_ok=True) + + +def git_clone(url: str, destination: Path, ignore_lfs: bool) -> None: + """Clone a git repository into a directory.""" + repo_name = ".tmp" + command = ["git", "clone", url, repo_name] + _ensure_directory_not_exist(destination, force_redo=False) + try: + with tempfile.TemporaryDirectory(dir=MLC_TEMP_DIR) as tmp_dir: + logger.info("[Git] Cloning %s to %s", bold(url), destination) + subprocess.run( + command, + env={"GIT_LFS_SKIP_SMUDGE": "1"}, + cwd=tmp_dir, + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + git_dir = os.path.join(tmp_dir, repo_name) + if not ignore_lfs: + git_lfs_pull(Path(git_dir)) + shutil.move(git_dir, str(destination)) + except subprocess.CalledProcessError as error: + raise ValueError( + f"Git clone failed with return code {error.returncode}: {error.stderr}. " + f"The command was: {command}" + ) from error + + +def git_lfs_pull(repo_dir: Path) -> None: + """Pull files with Git LFS.""" + filenames = ( + subprocess.check_output( + ["git", "-C", str(repo_dir), "lfs", "ls-files", "-n"], + stderr=subprocess.STDOUT, + ) + .decode("utf-8") + .splitlines() + ) + logger.info("[Git LFS] Downloading %d files with Git LFS: %s", len(filenames), filenames) + with tqdm.redirect(): + for file in tqdm.tqdm(filenames): + logger.info("[Git LFS] Downloading %s", file) + subprocess.check_output( + ["git", "-C", str(repo_dir), "lfs", "pull", "--include", file], + stderr=subprocess.STDOUT, + ) + + +def download_file( + url: str, + destination: Path, + md5sum: Optional[str], +) -> Tuple[str, Path]: + """Download a file from a URL to a destination file.""" + with requests.get(url, stream=True, timeout=30) as response: + response.raise_for_status() + with destination.open("wb") as file: + for chunk in response.iter_content(chunk_size=8192): + file.write(chunk) + if md5sum is not None: + hash_md5 = hashlib.md5() + with destination.open("rb") as file: + for chunk in iter(lambda: file.read(8192), b""): + hash_md5.update(chunk) + file_md5 = hash_md5.hexdigest() + if file_md5 != md5sum: + raise ValueError( + f"MD5 checksum mismatch for downloaded file: {destination}. " + f"Expected {md5sum}, got {file_md5}" + ) + return url, destination + + +def download_mlc_weights( # pylint: disable=too-many-locals + model_url: str, + num_processes: int = 4, + force_redo: bool = False, +) -> Path: + """Download weights for a model from the HuggingFace Git LFS repo.""" + prefixes, mlc_prefix = ["HF://", "https://huggingface.co/"], "" + mlc_prefix = next(p for p in prefixes if model_url.startswith(p)) + assert mlc_prefix + + git_url_template = "https://huggingface.co/{user}/{repo}.git" + bin_url_template = "https://huggingface.co/{user}/{repo}/resolve/main/{record_name}" + + if model_url.count("/") != 1 + mlc_prefix.count("/") or not model_url.startswith(mlc_prefix): + raise ValueError(f"Invalid model URL: {model_url}") + user, repo = model_url[len(mlc_prefix) :].split("/") + git_dir = MLC_CACHE_DIR / "model_weights" / user / repo + try: + _ensure_directory_not_exist(git_dir, force_redo=force_redo) + except ValueError: + logger.info("Weights already downloaded: %s", bold(str(git_dir))) + return git_dir + with tempfile.TemporaryDirectory(dir=MLC_TEMP_DIR) as tmp_dir_prefix: + tmp_dir = Path(tmp_dir_prefix) / "tmp" + git_url = git_url_template.format(user=user, repo=repo) + git_clone(git_url, tmp_dir, ignore_lfs=True) + shutil.rmtree(tmp_dir / ".git", ignore_errors=True) + with (tmp_dir / "ndarray-cache.json").open(encoding="utf-8") as in_file: + param_metadata = json.load(in_file)["records"] + with cf.ProcessPoolExecutor(max_workers=num_processes) as executor: + futures = [] + for record in param_metadata: + record_name = record["dataPath"] + file_url = bin_url_template.format(user=user, repo=repo, record_name=record_name) + file_dest = tmp_dir / record_name + file_md5 = record.get("md5sum", None) + futures.append(executor.submit(download_file, file_url, file_dest, file_md5)) + with tqdm.redirect(): + for future in tqdm.tqdm(cf.as_completed(futures), total=len(futures)): + file_url, file_dest = future.result() + logger.info("Downloaded %s to %s", file_url, file_dest) + logger.info("Moving %s to %s", tmp_dir, bold(str(git_dir))) + shutil.move(str(tmp_dir), str(git_dir)) + return git_dir diff --git a/python/mlc_chat/support/logging.py b/python/mlc_chat/support/logging.py new file mode 100644 index 0000000..f2611c7 --- /dev/null +++ b/python/mlc_chat/support/logging.py @@ -0,0 +1,20 @@ +""" +Logging support for MLC. It derives from Python's logging module, and in the future, +it can be easily replaced by other logging modules such as structlog. +""" +import logging + + +def enable_logging(): + """Enable MLC's default logging format""" + logging.basicConfig( + level=logging.INFO, + style="{", + datefmt="%Y-%m-%d %H:%M:%S", + format="[{asctime}] {levelname} {filename}:{lineno}: {message}", + ) + + +def getLogger(name: str): # pylint: disable=invalid-name + """Get a logger according to the given name""" + return logging.getLogger(name) diff --git a/python/mlc_chat/support/preshard.py b/python/mlc_chat/support/preshard.py new file mode 100644 index 0000000..09db02c --- /dev/null +++ b/python/mlc_chat/support/preshard.py @@ -0,0 +1,126 @@ +"""Functions for pre-sharding weights""" +from typing import Any, Dict, List + +from tvm import IRModule +from tvm import dlight as dl +from tvm import relax +from tvm.relax.frontend import nn +from tvm.runtime import Device +from tvm.target import Target + + +def _sharded_param_name(param_name, worker_id): + return f"{param_name}_shard-{worker_id}" + + +def _update_quantize_map( + quantize_map: Any, + named_params: Dict[str, nn.Parameter], + mlc_name: str, + tensor_parallel_shards: int, +): + param_names: List[str] = [mlc_name] + + if mlc_name in quantize_map.param_map: + # the parameter is quantized + quantized_params = quantize_map.param_map[mlc_name] + param_names = quantized_params + quantize_func = quantize_map.map_func[mlc_name] + + for worker_id in range(tensor_parallel_shards): + sharded_mlc_name = _sharded_param_name(mlc_name, worker_id) + quantize_map.param_map[sharded_mlc_name] = [ + _sharded_param_name(param_name, worker_id) for param_name in quantized_params + ] + quantize_map.map_func[sharded_mlc_name] = quantize_func + + for param_name in param_names: + param = named_params.pop(param_name) + for worker_id in range(tensor_parallel_shards): + named_params[_sharded_param_name(param_name, worker_id)] = param + + +def _create_shard_func( + bb: relax.BlockBuilder, param: nn.Parameter, tensor_parallel_shards: int +): # pylint: disable=too-many-locals + shard_strategy = param.attrs.get("shard_strategy", None) + # generate tir shard function + tir_func = shard_strategy.gen_tir(shards=tensor_parallel_shards, weight=param) + tir_func = tir_func.with_attr("global_symbol", f"{shard_strategy.name}_tir") + # add tir shard function to the IRModule + tir_gvar = bb.add_func(tir_func, func_name=f"{shard_strategy.name}_tir") + # create relax function that + # 1. shard weight with tir shard function, result: [num_shards, *sharded_weight_shape] + # 2. split the sharded weight along dim 0, result: num_shards * [1, *sharded_weight_shape] + # 3. squeeze the 0th-dim of all shards, result: num_shards * [*sharded_weight_shape] + weight_shape = param.shape + weight_shape[shard_strategy.dim] = weight_shape[shard_strategy.dim] * tensor_parallel_shards + sharded_weight_shape = [tensor_parallel_shards, *param.shape] + weight_var = relax.Var("weight", relax.TensorStructInfo(weight_shape, param.dtype)) + with bb.function(name=shard_strategy.name, params=[weight_var]): + with bb.dataflow(): + lv0 = bb.emit( + relax.call_tir( + tir_gvar, + weight_var, + out_sinfo=relax.TensorStructInfo(sharded_weight_shape, param.dtype), + ) + ) + lv1 = bb.emit(relax.op.split(lv0, indices_or_sections=tensor_parallel_shards, axis=0)) + output_vars = [] + for i in range(tensor_parallel_shards): + lvi = bb.emit(relax.TupleGetItem(lv1, i)) + squeezed_lvi = bb.emit(relax.op.squeeze(lvi, 0)) + output_vars.append(squeezed_lvi) + gv = bb.emit_output(output_vars) + bb.emit_func_output(gv) + + +def _compile_shard_funcs(mod: IRModule, device: Device): + target = Target.from_device(device) + with target: + mod = relax.transform.LegalizeOps()(mod) + mod = dl.ApplyDefaultSchedule( # type: ignore # pylint: disable=not-callable + dl.gpu.Matmul(), + dl.gpu.GEMV(), + dl.gpu.Reduction(), + dl.gpu.GeneralReduction(), + dl.gpu.Fallback(), + )(mod) + ex = relax.build(mod, target=target) + vm = relax.VirtualMachine(ex, device) + return vm + + +def apply_preshard( + quantize_map: Any, + named_params: Dict[str, nn.Parameter], + tensor_parallel_shards: int, + args: Any, +): + """Update quantize_map and named_params, create shard functions based on shard strategies.""" + model_config = args.model.config.from_file(args.config) + model_config.tensor_parallel_shards = tensor_parallel_shards + model = args.model.model(model_config) + model.to(args.quantization.model_dtype) + + bb = relax.BlockBuilder() + param_to_shard_func = {} + shard_func_names = set() + for name, param in model.state_dict().items(): + shard_strategy = param.attrs.get("shard_strategy", None) + if shard_strategy is not None: + _update_quantize_map(quantize_map, named_params, name, tensor_parallel_shards) + + # create shard functions + param_to_shard_func[name] = shard_strategy.name + if shard_strategy.name not in shard_func_names: + _create_shard_func(bb, param, tensor_parallel_shards) + shard_func_names.add(shard_strategy.name) + + mod = bb.finalize() + vm = _compile_shard_funcs(mod, args.device) + + for name in param_to_shard_func: + param_to_shard_func[name] = vm[param_to_shard_func[name]] + return param_to_shard_func diff --git a/python/mlc_chat/support/random.py b/python/mlc_chat/support/random.py new file mode 100644 index 0000000..0568276 --- /dev/null +++ b/python/mlc_chat/support/random.py @@ -0,0 +1,16 @@ +"""Utility functions for random number generation.""" +import sys + + +def set_global_random_seed(seed): + """Set global random seed for python, numpy, torch and tvm.""" + if "numpy" in sys.modules: + sys.modules["numpy"].random.seed(seed) + if "torch" in sys.modules: + sys.modules["torch"].manual_seed(seed) + if "random" in sys.modules: + sys.modules["random"].seed(seed) + if "tvm" in sys.modules: + set_seed = sys.modules["tvm"].get_global_func("mlc.random.set_seed") + if set_seed: + set_seed(seed) diff --git a/python/mlc_chat/support/style.py b/python/mlc_chat/support/style.py new file mode 100644 index 0000000..5b2272e --- /dev/null +++ b/python/mlc_chat/support/style.py @@ -0,0 +1,62 @@ +"""Printing styles.""" + +from enum import Enum + + +class Styles(Enum): + """Predefined set of styles to be used. + + Reference: + - https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit + - https://stackoverflow.com/a/17303428 + """ + + RED = "\033[91m" + GREEN = "\033[92m" + YELLOW = "\033[93m" + BLUE = "\033[94m" + PURPLE = "\033[95m" + CYAN = "\033[96m" + BOLD = "\033[1m" + UNDERLINE = "\033[4m" + END = "\033[0m" + + +def red(text: str) -> str: + """Return red text.""" + return f"{Styles.RED.value}{text}{Styles.END.value}" + + +def green(text: str) -> str: + """Return green text.""" + return f"{Styles.GREEN.value}{text}{Styles.END.value}" + + +def yellow(text: str) -> str: + """Return yellow text.""" + return f"{Styles.YELLOW.value}{text}{Styles.END.value}" + + +def blue(text: str) -> str: + """Return blue text.""" + return f"{Styles.BLUE.value}{text}{Styles.END.value}" + + +def purple(text: str) -> str: + """Return purple text.""" + return f"{Styles.PURPLE.value}{text}{Styles.END.value}" + + +def cyan(text: str) -> str: + """Return cyan text.""" + return f"{Styles.CYAN.value}{text}{Styles.END.value}" + + +def bold(text: str) -> str: + """Return bold text.""" + return f"{Styles.BOLD.value}{text}{Styles.END.value}" + + +def underline(text: str) -> str: + """Return underlined text.""" + return f"{Styles.UNDERLINE.value}{text}{Styles.END.value}" diff --git a/python/mlc_chat/support/tensor_parallel.py b/python/mlc_chat/support/tensor_parallel.py new file mode 100644 index 0000000..4d58662 --- /dev/null +++ b/python/mlc_chat/support/tensor_parallel.py @@ -0,0 +1,97 @@ +"""Sharding operators for tensor parallelism.""" +import dataclasses +from contextlib import contextmanager +from typing import Any, Dict, List, Optional + +from tvm import te, tir, topi +from tvm.relax.frontend import nn + + +@dataclasses.dataclass +class ShardSingleDim: + """ + Shard a tensor by a single dimension. + + + Parameters + ---------- + name : str + The name of the shard func + + dim : int + The dimension to shard + + segs : Optional[List[int]] + The length of segments along `dim`. Default to None. If specified, + shard a tensor by its "segmented" dimension, where each segment has a different length + and sharded evenly on each worker. + + """ + + name: str + dim: int + segs: Optional[List[int]] = None + + def gen_tir(self, shards: int, weight: nn.Tensor) -> tir.PrimFunc: + """Generate a TIR function that shards the weight tensor by its rows.""" + shape = weight.shape + segs = self.segs or [shape[self.dim]] + assert sum(segs) == shape[self.dim] + w = te.placeholder( + [*shape[: self.dim], shape[self.dim] * shards, *shape[self.dim + 1 :]], + weight.dtype, + name="w", + ) + ws: List[te.Tensor] = [] + offset = 0 + for idx, sub_seg in enumerate(segs): + ws.append( + topi.transpose( + topi.reshape( + te.compute( + (*shape[: self.dim], sub_seg * shards, *shape[self.dim + 1 :]), + lambda *idx: w[ + idx[: self.dim] + + (idx[self.dim] + offset,) # pylint: disable=cell-var-from-loop + + idx[self.dim + 1 :] + ], + name=f"w_{idx}", + ), + (*shape[: self.dim], shards, sub_seg, *shape[self.dim + 1 :]), + ), + [self.dim, *range(self.dim), *range(self.dim + 1, len(shape) + 1)], + ) + ) + offset += sub_seg * shards + o = topi.concatenate(ws, axis=1 + self.dim) + func = te.create_prim_func([w, o]) + return func + + def gen_shard_info(self, shards: int, weight: nn.Tensor) -> Dict[str, Any]: + """Generate shard info for this sharding strategy.""" + return { + "func_name": self.name, + "out_shape": (shards, *weight.shape), + "out_dtype": weight.dtype, + } + + +@contextmanager +def shard_bias(linear: nn.Linear, tensor_parallel_shards: int): + """ + A context manager to shard the bias of a linear into `tensor_parallel_shards` shards. + + + Parameters + ---------- + linear : nn.Linear + The linear layer whose bias would be sharded. + + tensor_parallel_shards : int + The number of shards. + """ + original_bias = linear.bias + if tensor_parallel_shards > 1: + linear.bias = linear.bias / tensor_parallel_shards + yield + linear.bias = original_bias diff --git a/python/mlc_chat/support/tqdm.py b/python/mlc_chat/support/tqdm.py new file mode 100644 index 0000000..9adceca --- /dev/null +++ b/python/mlc_chat/support/tqdm.py @@ -0,0 +1,38 @@ +"""Utils to better use tqdm""" +import contextlib +import inspect +import io + +from tqdm import tqdm +from tqdm.contrib.logging import logging_redirect_tqdm as _redirect_logging + + +@contextlib.contextmanager +def _redirect_print(): + old_print = print + + def new_print(*args, **kwargs): + with io.StringIO() as output: + kwargs["file"] = output + kwargs["end"] = "" + old_print(*args, **kwargs) + content = output.getvalue() + tqdm.write(content) + + try: + inspect.builtins.print = new_print + yield + finally: + inspect.builtins.print = old_print + + +@contextlib.contextmanager +def redirect(): + """Redirect tqdm output to logging and print.""" + + with _redirect_logging(): + with _redirect_print(): + yield + + +__all__ = ["tqdm", "redirect"] diff --git a/python/mlc_chat/tokenizer.py b/python/mlc_chat/tokenizer.py new file mode 100644 index 0000000..6158ef4 --- /dev/null +++ b/python/mlc_chat/tokenizer.py @@ -0,0 +1,55 @@ +"""The tokenizer and related tools in MLC LLM. +This tokenizer essentially wraps and binds the HuggingFace tokenizer +library and sentencepiece. +Reference: https://github.com/mlc-ai/tokenizers-cpp +""" +from typing import List + +import tvm +import tvm._ffi +from tvm.runtime import Object + +from . import _ffi_api + + +@tvm._ffi.register_object("mlc.Tokenizer") # pylint: disable=protected-access +class Tokenizer(Object): + """The tokenizer class in MLC LLM.""" + + def __init__(self, tokenizer_path: str) -> None: + """Create the tokenizer from tokenizer directory path.""" + self.__init_handle_by_constructor__( + _ffi_api.Tokenizer, tokenizer_path # type: ignore # pylint: disable=no-member + ) + + def encode(self, text: str) -> List[int]: + """Encode text into ids. + + Parameters + ---------- + text : str + The text string to encode. + + Returns + ------- + token_ids : List[int] + The list of encoded token ids. + """ + return list(_ffi_api.TokenizerEncode(self, text)) # type: ignore # pylint: disable=no-member + + def decode(self, token_ids: List[int]) -> str: + """Decode token ids into text. + + Parameters + ---------- + token_ids : List[int] + The token ids to decode to string. + + Returns + ------- + text : str + The decoded text string. + """ + return _ffi_api.TokenizerDecode( # type: ignore # pylint: disable=no-member + self, tvm.runtime.ShapeTuple(token_ids) + ) diff --git a/python/setup.py b/python/setup.py new file mode 100644 index 0000000..f866e9a --- /dev/null +++ b/python/setup.py @@ -0,0 +1,131 @@ +# pylint: disable=invalid-name, exec-used +"""Setup MLC LLM package.""" +import os +import shutil + +from setuptools import find_packages, setup +from setuptools.dist import Distribution + +CURRENT_DIR = os.path.dirname(__file__) +CONDA_BUILD = os.getenv("CONDA_BUILD") is not None + + +def get_lib_path(): + """Get library path, name and version""" + # Directly exec libinfo to get the right setup + libinfo_py = os.path.join(CURRENT_DIR, "./mlc_chat/libinfo.py") + libinfo = {"__file__": libinfo_py} + with open(libinfo_py, "rb") as f: + exec(compile(f.read(), libinfo_py, "exec"), libinfo, libinfo) + version = libinfo["__version__"] + + # conda installs libraries into env instead of packaging with pip + if not CONDA_BUILD: + libs = [ + libinfo["find_lib_path"]("mlc_llm")[0], + libinfo["find_lib_path"]("mlc_llm_module")[0], + ] + else: + libs = None + + return libs, version + + +def git_describe_version(original_version): + """Get git describe version.""" + ver_py = os.path.join(CURRENT_DIR, "..", "version.py") + libver = {"__file__": ver_py} + with open(ver_py, "rb") as f: + exec(compile(f.read(), ver_py, "exec"), libver, libver) + _, gd_version = libver["git_describe_version"]() + if gd_version is not None and gd_version != original_version: + print(f"Use git describe based version {gd_version}") + if gd_version is None: + print(f"Use original version {original_version}") + return original_version + return gd_version + + +LIB_LIST, __version__ = get_lib_path() +__version__ = git_describe_version(__version__) + + +class BinaryDistribution(Distribution): + """This class is needed in order to create OS specific wheels.""" + + def has_ext_modules(self): + """Return True for binary distribution.""" + return True + + def is_pure(self): + """Return False for binary distribution.""" + return False + + +def main(): + """The main entrypoint.""" + setup_kwargs = {} + if not CONDA_BUILD: + with open("MANIFEST.in", "w", encoding="utf-8") as fo: + for path in LIB_LIST: + if os.path.isfile(path): + shutil.copy(path, os.path.join(CURRENT_DIR, "mlc_chat")) + _, libname = os.path.split(path) + fo.write(f"include mlc_chat/{libname}\n") + setup_kwargs = {"include_package_data": True} + + setup( + name="mlc_chat", + version=__version__, + description="MLC Chat: an universal runtime running LLMs", + url="https://llm.mlc.ai/", + author="MLC LLM Contributors", + license="Apache 2.0", + # See https://pypi.org/classifiers/ + classifiers=[ + "License :: OSI Approved :: Apache Software License", + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Intended Audience :: Education", + "Intended Audience :: Science/Research", + ], + keywords="machine learning", + zip_safe=False, + packages=find_packages(), + entry_points={ + "console_scripts": [ + "mlc_chat = mlc_chat.__main__:main", + ], + }, + package_dir={"mlc_chat": "mlc_chat"}, + install_requires=[ + "fastapi", + "uvicorn", + "shortuuid", + "torch", + "safetensors", + "requests", + "tqdm", + "tiktoken", + "prompt_toolkit", + ], + distclass=BinaryDistribution, + **setup_kwargs, + ) + + def _remove_path(path): + if os.path.exists(path): + if os.path.isfile(path): + os.remove(path) + elif os.path.isdir(path): + shutil.rmtree(path) + + if not CONDA_BUILD: + # Wheel cleanup + os.remove("MANIFEST.in") + for path in LIB_LIST: + _, libname = os.path.split(path) + _remove_path(f"mlc_chat/{libname}") + + +main() diff --git a/run_scripts/README.md b/run_scripts/README.md new file mode 100644 index 0000000..2483993 --- /dev/null +++ b/run_scripts/README.md @@ -0,0 +1,36 @@ +# MLC run scripts + +This directory includes scripts for running MLC on different targets. +We support runtime through `expect` scripts, to interact with the `mlc_chat` executable for local and jetson execution. + +Interaction with android/ios apps is happening over [phonelab](https://github.com/brave-experiments/blade-public). +Interaction with jetson is also coordinated with [jetsonlab](https://github.com/brave-experiments/jetsonlab-public). + +## Structure + +```bash +├── run-mlc-chat-cli.exp # MLC expect script +├── run-mlc.sh # Wrapper shell script +└── run_expect_all.sh # Script for running all experiments +``` + +## How to run? + +The `run-mlc.sh` script is the entry point for running experiments locally. Outside of this repo, this is used by `jetsonlab` for automated runtime of benchmarks. However, one can invoke the script manually if they desire. + +``` +./run-mlc.sh + +: The path to the model +: The path to the library of the model (e.g. so file) +: The path of the input prompts json file +: The ordinal of the conversation to start from +: The ordinal of the conversation to end at +: The output path for logs and metrics. +: The filename to use for energy events timestamps +: The iteration (i.e. repetition) that this experiment is running. +``` + +## Known issues + +* If you run the expect script on Mac OS, there is an issue where a message "your terminal doesn't support cursor position requests (CPR)" prevents the automation. \ No newline at end of file diff --git a/run_scripts/run-mlc-chat-cli.exp b/run_scripts/run-mlc-chat-cli.exp new file mode 100755 index 0000000..db733e2 --- /dev/null +++ b/run_scripts/run-mlc-chat-cli.exp @@ -0,0 +1,142 @@ +#!/usr/bin/expect + +# Note: This script is used to run the MLC models in interactive mode on jetson devices. +# Author: Stefanos Laskaridis (stefanos@brave.com) + +package require json + +# Check if an argument is provided +if { $argc != 8 } { + puts "Usage: $argv0 model_path model_lib_path input_prompts_filename conversation_from conversation_to output_path events_filename iteration" + exit 1 +} + +# config +set timeout -1 +set sleep_time 5 +set model_path [lindex $argv 0] +set model_lib_path [lindex $argv 1] +set input_prompts_filename [lindex $argv 2] +set conversation_from [expr {int([lindex $argv 3])}] +set conversation_to [expr {int([lindex $argv 4])}] +set output_path [lindex $argv 5] +set events_filename [lindex $argv 6] +set iteration [lindex $argv 7] + +# create output path +exec mkdir -p "$output_path/melt_measurements/" + +set log_path "$output_path/melt_measurements/llm_output_iter${iteration}_conv${conversation_from}.txt" +set measurements "$output_path/melt_measurements/measurements_iter${iteration}_conv${conversation_from}.csv" + +# log file +log_file $log_path + +# build expect prompt based on given model_path +if {[string first "Llama-2" $model_path] != -1} { + set expect_prompt "\[INST\]:\ " +} elseif {[string first "mistral" $model_path] != -1} { + set expect_prompt "\[INST\]:\ " +} elseif {[string first "TinyLlama" $model_path] != -1} { + set expect_prompt "<|im_start|>user: " +} elseif {[string first "stablelm" $model_path] != -1} { + set expect_prompt "<|user|>" +} elseif {[string first "google_gemma" $model_path] != -1} { + set expect_prompt "user: " +} else { + # error + puts "Error: Unknown model for given model_path: $model_path" + exit 1 +} +set expect_prompt "\n$expect_prompt" + +# define store metrics function +proc store_metrics {start_time end_time state measurements} { + set duration [expr {double($end_time - $start_time) / 1000.0}] + set start_time_epoch [expr {$start_time / 1000.0}] + set parsed_state [string map {\n \\n} $state] + exec echo "$start_time_epoch,$duration,\"$parsed_state\"\r" >> "$measurements" +} + +# Read the JSON file +set file_data [read [open $input_prompts_filename r]] +set input_prompts [json::json2dict $file_data] + +# set range +if {$conversation_to > [expr [llength $input_prompts] -1] } { + set conversation_to [expr [llength $input_prompts] -1] +} +set input_prompts [lrange $input_prompts $conversation_from $conversation_to] + + +# init measurements file (write csv header) +exec echo "start_date,duration,state\r" > "$measurements" + +# init variables, this init states are proxy to model loading +set start_time [clock milliseconds] +set state "load_model" + +# build command +set command "spawn mlc_chat chat --model-lib-path $model_lib_path --energy-events $events_filename $model_path" + +# Execute the command +eval $command + +sleep $sleep_time + +# iterate through conversations +foreach conversation $input_prompts { + + # iterate through prompts + foreach prompt $conversation { + + expect -ex $expect_prompt { + + # save metrics of previous prompt (or model load if first iteration) + set end_time [clock milliseconds] + store_metrics $start_time $end_time $state $measurements + + sleep $sleep_time + + # save state vars for next iteration and send the prompt + set state $prompt + + # Send stats on every prompt + send "/stats\r" + sleep 1 + + set start_time [clock milliseconds] + + # escape any \n characters in the prompt + set parsed_prompt [string map {\n \\n} $prompt] + send "$parsed_prompt\r" + } + } + + expect -ex $expect_prompt { + # print stats + send "/stats\r" + } + # expect -ex $expect_prompt { + # send "/reload\r" + # } + # expect -ex $expect_prompt { + # # reload model/context + # send "/reset\r" + # } +} + +# finish +expect -ex $expect_prompt { + + # save last metrics + set end_time [clock milliseconds] + store_metrics $start_time $end_time $prompt $measurements + + sleep $sleep_time + + # exit + send "/exit\r" + sleep 10 + expect eof +} diff --git a/run_scripts/run-mlc.sh b/run_scripts/run-mlc.sh new file mode 100755 index 0000000..1d095d5 --- /dev/null +++ b/run_scripts/run-mlc.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# Note: This script is used to run the MLC models locally. +# Author: Stefanos Laskaridis (stefanos@brave.com) + +# show script usage +if [ $# -ne 8 ] +then + echo "====================================================" + echo "USAGE: $0 model_path model_lib_path input_prompts_filename conversation_from conversation_to output_path events_filename iteration" + echo "Passed parameters: $@" + echo "====================================================" + exit -1 +fi +MODEL_PATH=$1 +MODEL_LIB_PATH=$2 +INPUT_PROMPTS_FILENAME=$3 +CONVERSATION_FROM=$4 +CONVERSATION_TO=$5 +OUTPUT_PATH=$6 +EVENTS_FILENAME=$7 +ITERATION=$8 + +FILE_DIRECTORY="$(dirname "${BASH_SOURCE[0]}")" +REAL_OUTPUT_PATH=$(realpath $OUTPUT_PATH) + +# Check device type and set expect_script accordingly +expect_script="run-mlc-chat-cli.exp" + +# iterate per conversation +for (( i=CONVERSATION_FROM; i for ChatModuleError { + fn from(e: tvm_rt::Error) -> Self { + Self::TvmRuntime(e) + } +} + +pub type Result = result::Result; + +#[derive(Debug, Clone)] +pub struct ChatMessage { + role: String, + content: String, +} + +impl ChatMessage { + pub fn new(role: &str, content: &str) -> Self { + ChatMessage { + role: role.to_owned(), + content: content.to_owned(), + } + } +} + +#[derive(Debug, Clone)] +pub enum Prompt { + String(String), + MessageList(Vec), +} + +impl From<&str> for Prompt { + fn from(s: &str) -> Self { + Prompt::String(s.to_owned()) + } +} + +impl From for Prompt { + fn from(s: String) -> Self { + Prompt::String(s) + } +} + +impl From> for Prompt { + fn from(messages: Vec) -> Self { + Prompt::MessageList(messages) + } +} + +#[derive(Debug, Copy, Clone)] +pub enum PlaceInPrompt { + All = 0, + Begin = 1, + Middle = 2, + End = 3, +} + +impl PlaceInPrompt { + pub fn to_value(&self) -> i32 { + *self as i32 + } +} + +macro_rules! tvm_func_invoke { + // Handle the case with return type + ($self:ident, $func_name:ident($($args:expr),*) -> $ret_type:ty) => { + { + let f = $self.chat_module.get_function(stringify!($func_name), false)?; + let res: $ret_type = f.invoke(vec![$($args.into()),*])?.try_into().expect("call should succeed"); + Ok(res) + } + }; + // Handle the case without return type + ($self:ident, $func_name:ident($($args:expr),*)) => { + { + let f = $self.chat_module.get_function(stringify!($func_name), false)?; + f.invoke(vec![$($args.into()),*])?; + Ok(()) + } + }; +} + +/// Parse the input device identifier into device name and id. +/// +/// # Arguments +/// * `device` - The device identifier to parse. It can be in the format "device_name" (e.g., "cuda") +/// or "device_name:device_id" (e.g., "cuda:1"). +/// +/// # Returns +/// * `device_name` - The name of the device. +/// * `device_id` - The id of the device, or 0 if not specified in the input. +fn parse_device_str(device: &str) -> (&str, i32) { + let device_err_msg = format!( + "Invalid device name: {}. Please enter the device in the form \ + 'device_name:device_id' or 'device_name', where 'device_name' needs to be \ + one of 'cuda', 'metal', 'vulkan', 'rocm', 'opencl', 'auto'.", + device + ); + let device_args: Vec<&str> = device.split(':').collect(); + match device_args.len() { + 1 => (device_args[0], 0), + 2 => (device_args[0], device_args[1].parse::().unwrap()), + _ => panic!("{}", device_err_msg), + } +} + +/// Use user-provided argument `model` to search for a valid model path. +/// We define "valid" as having an `mlc-chat-config.json` right under the folder. +/// +/// # Arguments +/// * `model`: User's input; may be a compiled model's name, or a full path. +/// +/// # Returns +/// * `model_path`: A "valid" path to model folder with `mlc-chat-config.json` existing under it. +/// * `chat_file`: The path to the `mlc-chat-config.json` file. +/// +/// # Panics +/// * If a valid model_path cannot be found. +pub fn get_model_path(model: &str) -> (PathBuf, PathBuf) { + // Note that the order of this list corresponds to our search priority + let candidate_paths = vec![ + PathBuf::from(model), // full path, or just the name + PathBuf::from(format!("{}/params", model)), // Default directory after mlc_llm.build_model() + PathBuf::from(format!("dist/prebuilt/{}", model)), // Using prebuilt workflow + PathBuf::from(format!("dist/{}/params", model)), // Default directory after mlc_llm.build_model() in the current path + PathBuf::from(format!("dist/prebuilt/mlc-chat-{}", model)), // Also prebuilt workflow, but missed prefix + ]; + + // Look for the first folder that has `mlc-chat-config.json` under it + for candidate in &candidate_paths { + let chat_file = candidate.join("mlc-chat-config.json"); + if chat_file.is_file() { + info!("Using model folder: {:?}", candidate.canonicalize().unwrap()); + info!("Using mlc chat config: {:?}", chat_file.canonicalize().unwrap()); + return (candidate.clone(), chat_file); + } + } + + let mut found_folder = false; + let mut valid_dir_str = String::new(); + for candidate in &candidate_paths { + if candidate.is_dir() { + valid_dir_str += &format!("- {:?}\n", candidate.canonicalize().unwrap()); + found_folder = true; + } + } + + if found_folder { + // Error 1: there is a folder, but not an mlc-llm model folder (E1) + let err_msg = format!( + "The model folder provided does not seem to refer to a valid mlc-llm model folder.\n\ + Specifically, we cannot find `mlc-chat-config.json`, a required file. You should \ + provide a path that contains the file.\n\ + According to your input `model`, we looked at folder(s):\n\ + {}\n\ + MLC-Chat consumes models that are processed by the MLC-LLM build process.\n\ + ", + valid_dir_str, + ); + panic!("{}", err_msg); + } else { + // Error 2: cannot find a folder (E0) + let all_paths_str = candidate_paths + .iter() + .map(|path| format!("- {}\n", path.display())) + .collect::(); + let err_msg = format!( + "Cannot find the model folder. We searched over the following possible paths:\n\ + {}\n\ + You can try to pass in `model=/path/to/your-model-path`, and confirm \ + that it contains `mlc-chat-config.json`, among other essential files.\n\ + ", + all_paths_str, + ); + panic!("{}", err_msg); + } +} + +/// Read in the config file in model path, then potentially override with user input. +/// +/// # Arguments +/// * `config_file_path`: &Path +/// `chat_file` returned by a function like `get_model_path()`. +fn get_chat_config(config_file_path: &Path) -> result::Result> { + // Read the base configuration from the file + let file_contents = fs::read_to_string(config_file_path)?; + let final_chat_config = ChatConfig::from_json(&file_contents)?; + Ok(final_chat_config) +} + +/// Look up the model library and return a corresponding `tvm` runtime Module. +/// +/// # Arguments +/// * `model` - A string representing either the name of a compiled model or a full path to it. +/// * `model_path` - The path to the model, as determined by `get_model_path`. +/// * `chat_config` - The chat configuration, possibly with overrides, returned by `get_chat_config`. +/// * `model_lib_path` - An optional string specifying the full path to the model library. This is prioritized if provided. +/// * `device_name` - A string representing the device for which the library model file name will be constructed. +/// * `config_file_path` - The path to the `mlc-chat-config.json` file, used for constructing error messages. +/// +/// # Returns +/// The path pointing to the model library we find. +fn get_lib_module_path( + model: &str, model_path: &Path, chat_config: &ChatConfig, model_lib_path: Option<&str>, device_name: &str, + config_file_path: &Path, +) -> PathBuf { + // 1. Use user's model_lib_path if provided + if let Some(lib_path) = model_lib_path { + let path = Path::new(lib_path); + if path.is_file() { + info!("Using library model: {:?}", path); + return path.to_path_buf(); + } else { + panic!("The `model_lib_path` you passed in is not a file: {:?}.", lib_path); + } + } + + // 2. Generate all possible file names according to OS + let mut candidate_paths = Vec::new(); + if let Some(model_lib) = &chat_config.model_lib { + let candidate_lib_names: Vec = if cfg!(target_os = "linux") { + vec![format!("{}-{}.so", model_lib, device_name)] + } else if cfg!(target_os = "macos") { + vec![ + format!("{}-{}.dylib", model_lib, device_name), + format!("{}-{}.so", model_lib, device_name), + ] + } else if cfg!(target_os = "windows") { + vec![format!("{}-{}.dll", model_lib, device_name)] + } else { + vec![ + format!("{}-{}.dylib", model_lib, device_name), + format!("{}-{}.so", model_lib, device_name), + format!("{}-{}.dll", model_lib, device_name), + ] + }; + + // 3. Generate possible model library paths + let pardir_model_path = model_path.parent().unwrap(); + for lib_name in &candidate_lib_names { + let paths: Vec = vec![ + lib_name.clone(), + format!("dist/prebuilt/lib/{}", lib_name), + format!("dist/{}/{}", model, lib_name), + model_path.join(lib_name).to_string_lossy().into_owned(), + pardir_model_path.join(lib_name).to_string_lossy().into_owned(), + ]; + + candidate_paths.extend(paths); + } + + // 4. Search for model library + for candidate in &candidate_paths { + let candidate_path = Path::new(candidate); + if candidate_path.is_file() { + info!("Using library model: {:?}", candidate_path); + return candidate_path.to_path_buf(); + } + } + + // 5. Error + let mut err_msg = format!( + "Cannot find the model library that corresponds to `{:?}`.\n\ + `{:?}` is either provided in the `chat_config` \ + you passed in, or specified in {:?}.\n\ + We searched over the following possible paths: \n", + model_lib, model_lib, config_file_path + ); + for candidate in &candidate_paths { + err_msg += &format!("- {}\n", candidate); + } + err_msg += &format!( + "If you would like to directly specify the model library path, you may \ + consider passing in the `ChatModule.model_lib_path` parameter." + ); + + panic!("{}", err_msg); + } else { + panic!("Cannot find the model library, you need to either pass it in, or specify in the chat_config file."); + } +} + +/// The ChatModule for MLC LLM. +/// +/// # Examples +/// +/// ``` +/// use mlc_llm::chat_module::ChatModule; +/// +/// // Create a ChatModule instance +/// let cm = ChatModule::new("Llama-2-7b-chat-hf-q4f16_1", "cuda", None, None).unwrap(); +/// +/// // Generate a response for a given prompt +/// let output = cm.generate("what is the meaning of life?", None).unwrap(); +/// +/// // Print prefill and decode performance statistics +/// println!("Statistics: {:?}\n", cm.stats(false).unwrap()); +/// +/// let output = cm.generate("what is Rust?", None).unwrap(); +/// ``` +pub struct ChatModule { + chat_module: Module, + chat_config: ChatConfig, +} + +impl ChatModule { + pub fn new(model: &str, device: &str, model_lib_path: Option<&str>) -> Result { + let device_err_msg = format!( + "Invalid device name: {}. Please enter the device in the form \ + 'device_name:device_id' or 'device_name', where 'device_name' needs to be \ + one of 'cuda', 'metal', 'vulkan', 'rocm', 'opencl', 'auto'.", + device + ); + + let (device_name, device_id) = parse_device_str(device); + + // 1. Get device name and id + let device_type = match device_name { + "cuda" => 2, + "opencl" => 4, + "vulkan" => 7, + "metal" => 8, + "rocm" => 10, + _ => panic!("{}", device_err_msg), + }; + + unsafe { + LLMChatDummyLinkFunc(); + } + + static GLOBAL_FUNC_NAME: &str = "mlc.llm_chat_create"; + let f = Function::get(GLOBAL_FUNC_NAME).ok_or(ChatModuleError::GlobalFuncNotFound)?; + let m: Module = f + .invoke(vec![device_type.into(), device_id.into()]) + .unwrap() + .try_into() + .expect("call should succeed"); + + // 2. Look up the model path + let (model_path, config_file_path) = get_model_path(model); + + // 3. Instantiate chat_config + let chat_config = get_chat_config(&config_file_path).unwrap(); + + // 4. Look up the model library + let model_lib_path = get_lib_module_path( + model, + &model_path, + &chat_config, + model_lib_path, + device_name, + &config_file_path, + ); + + let chat_mod = Self { + chat_module: m, + chat_config, + }; + let model_lib_str = model_lib_path.as_path().display().to_string(); + let model_path_str = model_path.as_path().display().to_string(); + chat_mod.reload(&model_lib_str, &model_path_str, "").unwrap(); + Ok(chat_mod) + } + + /// Reload the chat module from the given library and model path. + fn reload(&self, lib: &str, model_path: &str, app_config_json: &str) -> Result<()> { + tvm_func_invoke!(self, reload(lib, model_path, app_config_json)) + } + + /// Reset the chat session, clear all chat history, and potentially + /// override the original `mlc-chat-config.json`. + pub fn reset_chat(&self) -> Result<()> { + // TODO: add optional user-specified ChatConfig + tvm_func_invoke!(self, reset_chat()) + } + + /// Get the runtime stats of the encoding step, decoding step (and embedding step if exists) + /// of the chat module in text form. + pub fn stats(&self, verbose: bool) -> Result { + if verbose { + return tvm_func_invoke!(self, verbose_runtime_stats_text() -> String); + } + tvm_func_invoke!(self, runtime_stats_text() -> String) + } + + /// Check if the stop condition is met for the current round. + fn stopped(&self) -> Result { + tvm_func_invoke!(self, stopped() -> bool) + } + + /// Get the output message in the current round. + fn get_message(&self) -> Result { + tvm_func_invoke!(self, get_message() -> String) + } + + /// Decode the next token, the decoding result is stored in a buffer and + /// can be retrieved by [get_message]. + fn decode(&self, generation_config: Option<&GenerationConfig>) -> Result<()> { + let generation_config_str = match generation_config { + Some(config) => serde_json::to_string(config).unwrap(), + None => { + let config = GenerationConfig::from_chat_config(&self.chat_config); + serde_json::to_string(&config).unwrap() + } + }; + tvm_func_invoke!(self, decode(generation_config_str)) + } + + /// Load JSON config and override existing configurations for the chat module. + fn load_json_override(&self, config_str: &str, partial_update: bool) -> Result<()> { + tvm_func_invoke!(self, load_json_override(config_str, &partial_update)) + } + + /// Get the configuration of the chat module in a single json string. + fn get_config_json(&self) -> Result { + tvm_func_invoke!(self, get_config_json() -> String) + } + + /// Get the name of role 0 in the conversation. + fn get_role_0(&self) -> Result { + tvm_func_invoke!(self, get_role0() -> String) + } + + /// Get the name of role 1 in the conversation. + fn get_role_1(&self) -> Result { + tvm_func_invoke!(self, get_role1() -> String) + } + + /// A high-level method that returns the full response from the chat module given a user + /// prompt. User can optionally specify which callback method to use upon receiving the + /// response. + /// + /// # Arguments + /// * `prompt` - The user input prompt, i.e. a question to ask the chat module. + /// It can also be the whole conversation history (list of messages with role and content) + /// + /// # Examples + /// ``` + /// // Single prompt case, the `prompt` can be a &str + /// let prompt = "what is the meaning of life?"; + /// + /// // Multi-prompt case, the `prompt` can be Vec + /// let message1 = ChatMessage::new("user", "suppose we already have projects llama, alpaca and vicuna, what do you think would be a great name for the next project?"); + /// let message2 = ChatMessage::new( + /// "assistant", + /// "based on the previous projects, a possible name for the next project could be \"cervidae\" which is the scientific name for deer family. this name reflects the collaboration and teamwork involved in the development of the project, and also nods to the previous projects that have been developed by the team."); + /// let message3 = ChatMessage::new("user", "I like cervidae, but the name is too long!"); + /// let prompt = vec![message1, message2, message3]; + /// ``` + /// + /// * `generation_config` - The generation config object to override the ChatConfig generation settings. + /// + /// # Returns + /// * `output` - The generated full output from the chat module. + pub fn generate( + &self, prompt: impl Into, generation_config: Option<&GenerationConfig>, + ) -> Result> { + // TODO: add progress_callback + let mut new_msgs: Vec = vec![]; + let mut num_return_sequences: usize = 1; + + if let Some(gc) = generation_config { + if let Some(n) = gc.n { + num_return_sequences = n; + } + } + + let prompt = prompt.into(); + for _ in 0..num_return_sequences { + self.reset_chat().unwrap(); + self.prefill(&prompt, true, PlaceInPrompt::All, generation_config) + .unwrap(); + + while !self.stopped().unwrap() { + self.decode(generation_config)?; + } + let new_msg = self.get_message().unwrap(); + new_msgs.push(new_msg); + } + + Ok(new_msgs) + } + + /// Runs the prefill stage for a given input and optionally decodes the first output token. + /// The user can decide where to place the input in the prompt. + /// + /// # Arguments + /// + /// * `input` - A `String` or a `Vec`. The user input prompt, i.e., a question to ask the chat module. + /// It can also be the whole conversation history (list of messages with role and content). + /// + /// # Examples + /// ``` + /// // Single prompt case, the `prompt` can be a &str + /// "what is the meaning of life?"; + /// + /// // Multi-prompt case, the `prompt` can be Vec + /// vec![ + /// ChatMessage::new("user", "Hello, how are you?"), + /// ChatMessage::new("assistant", "I'm fine, thank you. How about you?"), + /// ChatMessage::new("user", "I'm good too."), + /// ] + /// ``` + /// * `decode_next_token` - A boolean indicating whether to decode the next token after prefilling. + /// * `place_in_prompt` - The place of the input message in the prompt, as defined by the `PlaceInPrompt` enum. + /// * `generation_config` - An optional `GenerationConfig` to override the ChatConfig generation settings. + /// + /// # Examples + /// + /// ``` + /// let input = "Hello, how are you?"; + /// let decode_next_token = true; + /// let place_in_prompt = PlaceInPrompt::All; + /// let generation_config = Some(GenerationConfig::new()); + /// + /// prefill(input, decode_next_token, place_in_prompt, generation_config); + /// ``` + fn prefill( + &self, input: &Prompt, decode_next_token: bool, place_in_promt: PlaceInPrompt, + generation_config: Option<&GenerationConfig>, + ) -> Result<()> { + let generation_config_str = match generation_config { + Some(config) => serde_json::to_string(config).unwrap(), + None => { + let config = GenerationConfig::from_chat_config(&self.chat_config); + serde_json::to_string(&config).unwrap() + } + }; + + let input_string = match input { + Prompt::String(inp) => inp.clone(), + Prompt::MessageList(chat_msgs) => { + let mut chat_msgs = chat_msgs.clone(); + if chat_msgs.len() == 1 { + chat_msgs.remove(0).content + } else { + let chat_config = ChatConfig::from_json(&(self.get_config_json()?)).unwrap(); + let mut conv_config = chat_config + .conv_config + .unwrap_or_else(|| ConvConfigBuilder::default().build().unwrap()); + + let role0 = self.get_role_0()?; + let role1 = self.get_role_1()?; + + let last_msg = chat_msgs.last().expect("No last message in the vector").clone(); + if last_msg.role != "user" { + panic!("Last message should be from user."); + } + + let mut messages = Vec::new(); + let msg_len = chat_msgs.len(); + for msg in chat_msgs.into_iter().take(msg_len - 1) { + match msg.role.as_str() { + "user" => messages.push(vec![role0.clone(), msg.content]), + "assistant" => messages.push(vec![role1.clone(), msg.content]), + _ => panic!("Only user and assistant roles are supported."), + } + } + + conv_config.messages = Some(messages); + conv_config.offset = Some(0); + + let mut map = HashMap::new(); + map.insert("conv_config", conv_config); + self.load_json_override(&serde_json::to_string(&map).unwrap(), true)?; + + last_msg.content + } + } + }; + + tvm_func_invoke!( + self, + prefill( + input_string, + &decode_next_token, + place_in_promt.to_value(), + generation_config_str + ) + ) + } +} diff --git a/rust/src/config.rs b/rust/src/config.rs new file mode 100644 index 0000000..a623395 --- /dev/null +++ b/rust/src/config.rs @@ -0,0 +1,273 @@ +use serde::{Deserialize, Serialize}; + +/// A struct that represents user-defined partial configuration for conversation template. +/// +/// This can be passed in to the instantiation of a [ChatModule](crate::chat_module::ChatModule) +/// instance to override the default setting in `mlc-chat-config.json` under the +/// model folder. Note that we will first load the predefined template +/// with the name specified in `conv_template`. +/// +/// Since the configuration is partial, everything will be optional. +#[derive(Clone, Default, Builder, Debug, Serialize, Deserialize)] +#[builder(default)] +pub struct ConvConfig { + /// Token list prefixing the conversation. + prefix_tokens: Option>, + + /// Name of the conversation. + name: Option, + + /// The prompt encoded before starting the chat. + system: Option, + + /// An array that describes the role names of the user and the model. + roles: Option>, + + /// The chat history represented as an array of string pairs. + pub messages: Option>>, + + /// The offset used to begin the chat from the chat history. + pub offset: Option, + + /// Specifies whether we are in chat-bot mode (`0`) or pure LM prompt mode (`1`). + separator_style: Option, + + /// An array of strings indicating the separators to be used after a user message and a model message respectively. + seps: Option>, + + /// A string indicating the separator between a role and a message. + role_msg_sep: Option, + + /// A string indicating the separator to append to a role when there is no message yet. + role_empty_sep: Option, + + /// When the `stop_str` is encountered, the model will stop generating output. + stop_str: Option, + + /// A list of token IDs that act as stop tokens. + stop_tokens: Option>, + + /// Determines whether a beginning-of-string (bos) token should be added before the input tokens. + add_bos: Option, +} + +impl ConvConfig { + pub fn post_init(&mut self) { + if let Some(messages) = &self.messages { + if self.offset.is_none() { + self.offset = Some(messages.len()); + } + } + } +} + +/// A struct that represents user-defined partial configuration for the chat config file. +/// +/// An instance of [ChatConfig] can be passed in to override the default setting. +/// Since the configuration is partial, everything will be optional. +/// +/// Note: This struct is used to represent the chat config during intermediate processing. +#[derive(Builder, Debug, Default, Serialize, Deserialize)] +#[builder(default)] +pub struct ChatConfig { + /// The necessary model library to launch this model architecture. + /// Recommended to reuse model library when possible. + pub model_lib: Option, + + /// Uniquely identifying the model in application. Also used by + /// CLI to specify which model to run. + pub local_id: Option, + + /// The name of the conversation template that this chat uses. + pub conv_template: Option, + + /// Temperature applied to logits before sampling. Encourages diverse outputs if higher. + pub temperature: Option, + + /// Controls the likelihood of the model generating repeated texts. + /// See the CTRL paper for more details: + repetition_penalty: Option, + + /// Determines the set of tokens from which we sample during decoding. + /// More info on top-p sampling: + top_p: Option, + + /// Approximated average number of generated tokens in each round. + mean_gen_len: Option, + + /// Maximum number of tokens to be generated in each round. + max_gen_len: Option, + + /// Fraction of maximum window size to shift when it is exceeded. + shift_fill_factor: Option, + + /// List of tokenizer files of the model. + tokenizer_files: Option>, + + /// Partial overriding configuration for conversation template. + pub conv_config: Option, + + /// The category of the model's architecture (e.g. `llama`, `gpt_neox`, `rwkv`). + model_category: Option, + + /// Name of the model (e.g. `Llama-2-7b-chat-hf`). + model_name: Option, + + /// Tensor parallel degree. + num_shards: Option, + + /// Maximum kv cache window size. + max_window_size: Option, +} + +impl ChatConfig { + pub fn from_json(json_str: &str) -> Result { + serde_json::from_str(json_str) + } +} + +/// A struct that represents user-defined generation configuration. +/// +/// An instance of [GenerationConfig] can be passed into the +/// [ChatModule::generate](crate::chat_module::ChatModule::generate) function +/// to override the default generation settings specified in `mlc-chat-config.json` +/// and `ChatConfig` under the model folder. +/// +/// Once the generation ends, `GenerationConfig` is discarded, as the values +/// are only intended to override the `ChatConfig` generation settings during a +/// single generation, unless it is recurrently passed to the `generate` function. +/// This allows for changing generation settings over time, without permanently +/// overriding the `ChatConfig`. +/// +/// Since the configuration is partial, all fields are optional. +#[derive(Builder, Debug, Default, Serialize, Deserialize)] +#[builder(default)] +pub struct GenerationConfig { + /// The temperature applied to logits before sampling. The default value is + /// `0.7`. A higher temperature encourages more diverse outputs, while a + /// lower temperature produces more deterministic outputs. + temperature: Option, + + /// The repetition penalty controls the likelihood of the model generating + /// repeated texts. The default value is set to `1.0`, indicating that no + /// repetition penalty is applied. Increasing the value reduces the + /// likelihood of repeat text generation. However, setting a high + /// `repetition_penalty` may result in the model generating meaningless + /// texts. The ideal choice of repetition penalty may vary among models. Only + /// Active when presence_penalty and frequency_penalty are both `0.0`. + + /// For more details on how repetition penalty controls text generation, please + /// check out the CTRL paper . + repetition_penalty: Option, + + /// This parameter determines the set of tokens from which we sample during + /// decoding. The default value is set to `0.95`. At each step, we select + /// tokens from the minimal set that has a cumulative probability exceeding + /// the ``top_p` parameter. + + /// For additional information on top-p sampling, please refer to this blog + /// post: . + top_p: Option, + + /// The approximated average number of generated tokens in each round. Used + /// to determine whether the maximum window size would be exceeded. + mean_gen_len: Option, + + /// This parameter determines the maximum length of the generated text. If it is + /// not set, the model will generate text until it encounters a stop token. + max_gen_len: Option, + + /// Number between `-2.0` and `2.0`. Positive values penalize new tokens based on + /// whether they appear in the text so far, increasing the model's likelihood + /// to talk about new topics. Negative values can increase the likelihood of + /// repetition. + presence_penalty: Option, + + /// Number between `-2.0` and `2.0`. Positive values penalize new tokens based on their + /// existing frequency in the text so far, decreasing the model's likelihood to + /// repeat the same line verbatim. Negative values can increase the likelihood of + /// repetition. + frequency_penalty: Option, + + /// This parameter determines the number of text samples to generate. The default + /// value is `1`. Note that this parameter is only used when `stream` is set to + /// `false`. + pub n: Option, + + /// When `stop` is encountered, the model will stop generating output. + /// It can be a string or a list of strings. If it is a list of strings, the model + /// will stop generating output when any of the strings in the list is encountered. + /// Note that this parameter does not override the default stop string of the model. + stop: Option>, +} + +impl GenerationConfig { + pub fn from_chat_config(chat_config: &ChatConfig) -> Self { + Self { + temperature: chat_config.temperature, + repetition_penalty: chat_config.repetition_penalty, + top_p: chat_config.top_p, + mean_gen_len: chat_config.mean_gen_len, + max_gen_len: chat_config.max_gen_len, + presence_penalty: Some(0.0), + frequency_penalty: Some(0.0), + n: Some(0), + stop: None, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_conv_config() { + let mut config = ConvConfig { + messages: Some(vec![vec!["User: Hi".to_string(), "Assistant: Hello".to_string()]]), + offset: None, + ..Default::default() + }; + config.post_init(); + assert_eq!(config.offset, Some(1)); + } + + #[test] + fn test_chat_config() { + let json_data = r#" + { + "model_lib": "some_lib", + "local_id": "id123", + "temperature": 0.7 + } + "#; + + let config = ChatConfig::from_json(json_data).unwrap(); + + assert_eq!(config.model_lib, Some("some_lib".to_string())); + assert_eq!(config.local_id, Some("id123".to_string())); + assert_eq!(config.temperature, Some(0.7)); + let _pretty_json = serde_json::to_string_pretty(&config).unwrap(); + } + + #[test] + fn test_generation_config() { + let chat_config = ChatConfigBuilder::default() + .temperature(Some(0.7)) + .top_p(Some(0.8)) + .mean_gen_len(Some(50)) + .max_gen_len(Some(75)) + .build() + .unwrap(); + + let gen_config = GenerationConfig::from_chat_config(&chat_config); + + assert_eq!(gen_config.temperature, chat_config.temperature); + assert_eq!(gen_config.repetition_penalty, chat_config.repetition_penalty); + assert_eq!(gen_config.top_p, chat_config.top_p); + assert_eq!(gen_config.mean_gen_len, chat_config.mean_gen_len); + assert_eq!(gen_config.max_gen_len, chat_config.max_gen_len); + assert_eq!(gen_config.presence_penalty, Some(0.0)); + assert_eq!(gen_config.frequency_penalty, Some(0.0)); + } +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 0000000..a8315d7 --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#[macro_use] extern crate derive_builder; + +pub mod chat_module; +pub mod config; diff --git a/scripts/build_site.sh b/scripts/build_site.sh new file mode 100755 index 0000000..6340ee8 --- /dev/null +++ b/scripts/build_site.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -euxo pipefail + +cd docs && make html && cd .. + +cd site && jekyll b && cd .. + +rm -rf site/_site/docs +cp -r docs/_build/html site/_site/docs diff --git a/scripts/check_url_validity.py b/scripts/check_url_validity.py new file mode 100644 index 0000000..3cbb29e --- /dev/null +++ b/scripts/check_url_validity.py @@ -0,0 +1,44 @@ +import requests +import argparse +import re +from pathlib import Path + + +def find_urls_in_file(file_path): + with open(file_path, "r") as file: + content = file.read() + + # Regular expression pattern to match URLs + url_pattern = re.compile( + r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+" + ) + + # Find all matches of URLs in the content + urls = re.findall(url_pattern, content) + return [url.strip(">") for url in urls] + + +def main(): + parser = argparse.ArgumentParser( + description="Check validity of links in documentation" + ) + parser.add_argument( + "--directory", type=str, default="docs", help="Directory of documentation." + ) + args = parser.parse_args() + + # traversal the directory and find all rst files + doc_directory = Path(args.directory) + for file_path in doc_directory.glob("**/*.rst"): + print("Checking {}...".format(file_path)) + for url in find_urls_in_file(file_path): + try: + r = requests.get(url) + if r.status_code == 404: + print("404 not found: {}".format(url)) + except Exception as e: + print("Error connecting {}, error: {}".format(url, e)) + + +if __name__ == "__main__": + main() diff --git a/scripts/gh_deploy_site.sh b/scripts/gh_deploy_site.sh new file mode 100755 index 0000000..1b21c52 --- /dev/null +++ b/scripts/gh_deploy_site.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# NOTE: this script is triggered by github action automatically +# when megred into main + +set -euxo pipefail + +scripts/build_site.sh + +git fetch +git checkout -B gh-pages origin/gh-pages +rm -rf docs .gitignore +mkdir -p docs +cp -rf site/_site/* docs +touch docs/.nojekyll + +DATE=`date` +git add docs && git commit -am "Build at ${DATE}" +git push origin gh-pages +git checkout main && git submodule update +echo "Finish deployment at ${DATE}" diff --git a/scripts/local_deploy_site.sh b/scripts/local_deploy_site.sh new file mode 100755 index 0000000..9e75aae --- /dev/null +++ b/scripts/local_deploy_site.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# NOTE: use this script to check local site + +set -euxo pipefail + +scripts/build_site.sh + +cd site && jekyll serve --skip-initial-build --host localhost --baseurl /mlc-llm --port 8888 diff --git a/scripts/prep_emcc_deps.sh b/scripts/prep_emcc_deps.sh new file mode 100755 index 0000000..2c1306c --- /dev/null +++ b/scripts/prep_emcc_deps.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# This file prepares all the necessary dependencies for the web build. +set -euxo pipefail + +emcc --version +npm --version + +TVM_HOME_SET="${TVM_HOME:-}" + +git submodule update --init --recursive + +if [[ -z ${TVM_HOME_SET} ]]; then + echo "Do not find TVM_HOME env variable, use 3rdparty/tvm". + echo "Make sure you set TVM_HOME in your env variable to use emcc build correctly" + export TVM_HOME="${TVM_HOME:-3rdparty/tvm}" +fi + +cd ${TVM_HOME}/web && make +cd - diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..b972149 --- /dev/null +++ b/setup.py @@ -0,0 +1,47 @@ +from distutils.core import setup +from setuptools.dist import Distribution +from setuptools import find_packages +import os + +# Note there is no need to setup when +# running locally. + +CURRENT_DIR = os.path.dirname(__file__) + + +def git_describe_version(original_version): + """Get git describe version.""" + ver_py = os.path.join(CURRENT_DIR, "version.py") + libver = {"__file__": ver_py} + exec(compile(open(ver_py, "rb").read(), ver_py, "exec"), libver, libver) + _, gd_version = libver["git_describe_version"]() + if gd_version is not None and gd_version != original_version: + print("Use git describe based version %s" % gd_version) + return gd_version + + +__version__ = git_describe_version(None) + +setup( + name="mlc_llm", + version=__version__, + description="MLC LLM: Universal Compilation of Large Language Models", + url="https://llm.mlc.ai/", + author="MLC LLM Contributors", + license="Apache 2.0", + # See https://pypi.org/classifiers/ + classifiers=[ + "License :: OSI Approved :: Apache Software License", + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Intended Audience :: Education", + "Intended Audience :: Science/Research", + ], + keywords="machine learning", + zip_safe=False, + packages=find_packages(), + package_dir={"mlc_llm": "mlc_llm"}, + install_requires=["numpy", "torch", "transformers", "scipy", "timm"], + entry_points={"console_scripts": ["mlc_llm_build = mlc_llm.build:main"]}, + distclass=Distribution, +) diff --git a/site/.gitignore b/site/.gitignore new file mode 100644 index 0000000..51b3599 --- /dev/null +++ b/site/.gitignore @@ -0,0 +1,4 @@ +dist +llm-chat-config.json +_includes/stable_diffusion.html +_site diff --git a/site/CNAME b/site/CNAME new file mode 100644 index 0000000..0b04c40 --- /dev/null +++ b/site/CNAME @@ -0,0 +1 @@ +llm.mlc.ai \ No newline at end of file diff --git a/site/_config.yml b/site/_config.yml new file mode 100644 index 0000000..9806232 --- /dev/null +++ b/site/_config.yml @@ -0,0 +1,42 @@ +name: "MLC LLM" +short_name: "MLC LLM" + +url: https://llm.mlc.ai/ + +exclude: [README.md, serve_local.sh] + +plugins: + - jekyll-remote-theme + +remote_theme: mlc-ai/jekyll-theme-mlc + + +# Colorize code snippets with the rogue module if we want to deploy on GH. +highlighter: rouge + +markdown: kramdown + +# The path structure for blog posts. +permalink: /blog/:year/:month/:day/:title.html + +# Number of news stories on the front page. +front_page_news: 8 + +# Base pathname for links. +base: '' + +# make pages for the _projects folder +collections: + projects: + output: true + +course_title: + +# Navigation bar links. +navigation: + - title: Home + link: / + - title: Docs + link: /docs + - title: Github + link: https://github.com/mlc-ai/mlc-llm diff --git a/site/gif/android-demo.gif b/site/gif/android-demo.gif new file mode 100644 index 0000000000000000000000000000000000000000..aec883f59829a4d16afd021d11762c1408e86887 GIT binary patch literal 855466 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 CywZ~Z literal 0 HcmV?d00001 diff --git a/site/gif/ios-demo.gif b/site/gif/ios-demo.gif new file mode 100644 index 0000000000000000000000000000000000000000..7256afec90291677fafbb68a7771c918d178fa2c GIT binary patch literal 6795718 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%0ADN)NlTs|g)bN@JU(!VZOjs;G!V;ZM`% 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

w%_U;4 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%0D)Rn57Xd~ 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

y!WmftXMGAi?6@vJ~3}tRmgz{9^N9Hu)+&7 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%0A7F1Z?m$Is=uxgUBZm+QJk+K=Auz4wE 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�qH^(TH=w(Qg#u*W!QkykJ3dFa+#KHs-y#G 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¥q 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%0D4V(D}jwQY`Bu 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

8~w^KMH?H$^TkjL0d`stkSwHsjRKSZLvacxKgJr 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 z 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¨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�|!!G_!1 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

h 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 z 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±5R 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 z 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%0Aa)MkPv| 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 z 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≤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 ~1S)W1a$91pSG7mw;HJP7brznV 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 z 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

bTdTKH0_Fp1ycE{^B&X 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讀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៼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%0Dn#cs;Y&M!j;BNcE@U6Sce)0z0*YkW|- zI-S}Hu0ubIUg$5k_}aHeAAA^8bqa;GpN;F(%B1=lXoDHnXbl1#(wa0Pc?dQSwYxse z2<|q%p_9hxJ{(6{j4ejJ*Bi`<;CU49U@hp6U=06i#zJhLdLce>!UBiP zLR#+l`^PV-u9=EWGx;ER<)xhEjho8+0WjN!7C6YsR|@vFDLXmr7hpymi zH-`6>3<6sQ-l;|@pBWvdLpA{M3Za#ADF{O%lhzO3^EIOlcV6Fh4Fo1))$NXYUp`?P zh!n@0580v+dZj~`m zgY_&Wj9fAO=lB}I5_o-d+Ib?okAIN(gX5rPlk}2;UF-`(0Z0O${J`TYzLIXeeO6D9JP2c^E%goF|V>T=T3k?PNjfk z)p$%Sm8T)SExx^>l)7e6`qceHk9UWtcWHFX;W?Vs^15(!P{^W|IN5e*m@7!oS38BeDa#DU_Fg{h0s< zi)%+nsw_J6YSRw44x?q?jBLB>sv}6kiQl}bOy1+CO>&5{HJ`9(i|2SU&ncc;!?( z^Cl#ODTS3OO_nL$f+-Wy3~pt{l4ZuRV8)F!=d&^w$}$&QFqcGH$XZz_WLc;zSZE+E zwX7_4vn&l3EKQJB7FJd^Sym1UR@ac$ZdTS_S=N3F*4Hm|P*yg#vTPz2Z0;d#W36lx zvuy7#*gizsWgzv$H0=r&?Cv~5+MiXDl_5QpqwQ5Z?P*-e%E};(3(i2FgIWTFHij%y z*pY9GyoVP&mE}B)6llqEqM>%Aadjf~JIR8;>o=UiJYt2b*J@<121mQBm|Yt|Uj1d| z`hCH96mXGU2eaRFX}H0Y?99`Jbd_Xwo1Ay7#E^0F^ZaM+9>j%>%@*YSAGERE?|%N@ zV`ID5qW@o({4;@4Z(lO_7aNVEbubu&=%K|^=@kB@z^gNEWg9s8d~GsLb<_57=i?U8Zs|B^PgD!V4OO#l7;5-Cg1 z+D!-W;pdmx&a{?xx$fCZ%k1+$Zd>`P{e6Ns>RiNVf$Xk}hb|?dTF^$_8%N;cV%U3F z5;mKsi`KL~@+ghowq=t=q*2;`e$r&n z?3u9%a9pLP=uU=qAU;3(j~hBg5pPcLi-QSo1s5u1ig3jhe4x*vQr9%jMNkM2^}({K zTl7;6TSP{;sn8vNND?f`nqXWIT$W_^I)Q-V4Hm62k&+^()zd7c?(}l%`tx{nr5v$N zS%H%|$(VL7NflJBL}S^_P?$|N(sn$#J^`SuXmtWo!E!7zZ1}c0~m z#`Vq?f@Cwk7a73|!EtK6<7fSUGJ=Oh;j7IfGrj*Gc9^d` zpG1EC*LIJC582(n*;nz~w_cDjHbv7Iu7Ws@`@qIcG5-O{|2nMTcu3f|IoW1now@t? zAHne;+#)^Y*MCcJ{BIe-YC{i^*T>_^vtrrp6PuSK{=B-72;{ccZ=N>an6Oc5O`l)k zv!i}Sn&4_#GpK**xfp*#khLRJ>#$HwJ%=)h*|dyBDH*GHZ^XriGiC5(I`L^mNpXNP zt{i!QmjEcqgmIs$#ASxGT^pV-kPq_mZDm91782RbT1yQ+NlECW3qJXdg>o)j((r)6H5yDKno0OEMdy}yF;(rp<8P*5X zAscLuPidZ+w9uzw50)%WMrhQR(t3qe9#Q1|MDtdeU_zErtgF8?1_C%Vo>VMy69?+~ zjHdD1p$jI#tyKO+LwG}FRNl&*FKf$a{!kwX!HRPFt-Y6+=>i&va}j3Ain|TQKJF&f zfCr&vTD~MV8N^<0;QCNn$fF8+iqbzCx%L@h%7$s2P@w@E&`hS%0r$FNC&EyDKYn_T z+sI3gm!lIZpB=msarcj(FXwnOV;t2^@n|yyBvN#gz#)FT#&>@SlhQZ4MYU5(e@7k0 z{Z+G?f?U3xM8w@FT;_ATh6_BgeRJ=)YnK9Iw?+l>@A*3&k4+tIaRCkE=cO|)zK#8d zm(!|A6c^i(M0<(vVF1aE<@C8I*57v6H6@P!6bU&^4|;y3fO)h~V#_jS{=Cj4nr1N!dL#sFX_~XG1;q4+ zQhCE}T_N8Z0$9tsFO zSUTPSmI`nYH$5uo4J10M_KMcw5CFD?t~%@MDa3F!-!4X7hwDL?M*!;$Dth>B1UgQN zJYJsYPuYxTMcti!rcKcw#(=&n*DT2zbnA{6jQ7{QH6qA()6#q!8lVW{V8x5=CtQ~H zbB%#@dYNm5;e^P;xKW0%X}m-!b09kLl1o_Xnx_cb8$nEzCe7eR665deTd3n>G5t{_ zo=|2yffi3LeavtpB5^9c&M8-1$lZm3>|vlk5olz&^vo7IM*tFz!&S^ut;p}|qT~0D zpi&jF?@Cj>t^du&X5kjQGLXtLa9@sizcno6^)^?kGh>mnfiN%iCJ%i(3)~)YQ?rzv zRFeD$O>T>!f37DW%ZljT2&9t14D>w|#E@53upT*w(4^y#W6&P-gWP?sBRyth4%AC9 zJ-osIw6+SIw2J5cPJRV2?lQIi}&*Y!cPp$zd_>yO8kfV#x4~y`OBls-^+$&bzfS^<) zib#hx&$In1f)?6-H{vMKhz4+)>n3?KxC4G?>+Fpg-Ae z9%}0Y|5_agS_^?k8>T=aLF73vY+>udl;}km@h}b2oDCu;>#fMTRcd;> zz}hUCPsnC&l`Pyw=8+Tt>T;d6J8;}0t2lu1$jp&OWoWP=oym(pF4B^nQ0lq7(OO`# zrjX9eGmH=&h%&#b0I2YQ3cZLJBCerNs%r_D9YGlElZPb&sawU7?$O!MM?-M{LNt!D zG@U}am>NwV<4if~9?nMY<#q??oB^AdvW+7Aqhk4K}qCR&}A;+4alqrSkTOF6MFn$*w_^nC? zC?=7#@9QRK$g2z!qht;f%}7`ZN@w>akyUxrAiaE$Cce-JORD9}Y9^?3RX9Mb3*Sx9 z`WAGnQ-oZmIyN{&Z)?d+a693B9@5AzgWZ>{X{S?Igd?=zq%$1kQtbpT z6Tl1PA(&hNL)KM_C?(jLC{#t89Q2oor~uTvV3|uYpw0EUdGISjU^XG(hfTFiX{6Us z6>EQ@GCE3KpS7M?E(on;DrKlYVLAv0F!}JOLQH?`fuSb4X#huB2Hx7il}Wjc95eSY zMEu>|OOD~!YfqR>hD4nKtJJV!y8c=%vlbS7v-)kf`zHWx7AVRcYa4dk_&!wrm6h)`cN|zE-a%7UQiycOC^}ZY%A%EdOyIQ ze{g&Lsr&iYchA57eZI%>;y~rap~H*c5igDlUYvBlIDPlx{O=1g&OVT8AEjd-bz~p7 zu#dK~oL^q(k?7Fi7}+5Z zagfPu$aexpn1F_g1NQ-FwjoeH0Xz-@J_%e|wHl`)(isIq7Sf;vk+5o2#8pnYVqRn? zJ^bm!6}7KW{}pIjA#}Y2aOZ^oSVa`>DcVo4r}ii$_p#sQl+T=om8PjJM8ezY9ZIIJ za;;6ShE7t*(UMANDTqVou~Py)Q(sr6zMoF*alQ`F5+{ps zWS6EvYSWZX)6`MZ;G${T-f8;x(~M`+z@-_O+6=;JhB<16wP=RDcZT!*4AhYRZF4{s)$*kJEmN609Ed&3vvXRg1$c8v5_y(E6!JV_er4d9J!NBO^2PaB z)XefzW)~^nUijcUs9i)Nze_iKc7aX+a4+IAN(BO(gd0lA1oyS>1OV@b8|b8~s8GbY zCaSWUR#IBI8~a;p)Y4RD<7o6Eh}b~Wa#w7vN>6T{6YBS4*Al(h!8J&`KX!l1ePKVj zQA1kr!#HeWzK6Zr#8!e=LK%oMg67)zQ%8p$?dcj|@lfb|_!mr`9E0dmj~zL)Vpye^ z1cBORQKqE_Ib+bA(6mttg{?BTk^Tb1V##nR{dP5TI_c)kBRC?c;OYBVdZ^I+``VBQ zo-3Kf($GSceLPMZDuqbPgqF<~K_55)QqEO!Gcl2$VQ%Y&@TNu(s+uiWtWj!Fq(#41 z0Z7HZWq`huMp2d&v7S%1|5S<^-1)%KzgD`$%wvlfvKO}{d>|#|%?5;&bx4}Dr8F1; zaD$Ap99U$6H?;?#Qv}_vZWl>6aQ+4`n^IVQo9EiqFYaVJdk^qBXPk&MQkYS_@_fr^ zW+Go;5i!QGDX}2crqA7X^9oGn$!*x-F<@A{NrR3mQ3L+MOl{?=EGq*$#DL`{$-sW% zhsCX3Y6_DyN)euiIMM@{<51Ht95iKWo&u%C;ilcKhDv9E^M)rtgLgpW+uov3bNi>G z%%3NEpRq}kE6Rz&n|2??rLmHIqBAozx-2fwY$fl|^l=+tY~6sH(Js%I&1b$VIA<#9 zgl}F7Y&}YcF0Wtb{i?c#37q|qdBRj$`f;)gh)ptZJO_TI8P_xS+O`-UA=2$a^aA@+ zHAX!H6mTYK#p{hxzNOmDpI5z0weqlQ?LJzYQEBn*biVuKKa9oy2G+zD_<6kF#woL= z`bmSnn6y{C?=VkK;wsxv+>evSFkZWD?Y{rIe?Pj^gWwR?0IeMikX){Qw)_^$@sU;R z+y0fa11avGG9;${2(_PA?)_9GDHwf|+V73GaC@(WUt*Kizw{1pS%Sti<`l?m=nn2c zroQcuKq)i;0amD+IW5?(g(C2Zu_Sb8`WHU>R+Pf>8F)Wh!~AXLBM7iQ8?3|+U)xnc2>!~>fU0S=rKTLlljd;#BdFIv3$-GH~t z(#$BYT;^V7m=Tf+tEG_6Ijt(N&io^`yPVbftMf0@#MR#<%^y>;UpSg2Eye1>Y1W`< zdiL)MeI%uitZNui@Y#jgVq8x9@*%l-YHu1}GPiU24L=Kyw!F^Eg3v+FOP`r9w|yzi zXm@PuC6WWY=L`tNy^l1p5aea|MPQ94jzPe;^7V z#H!JKX?D{fSK4E$!ftNMs8H#~_l>2w&n6}4ct!#H`E9cbT-Ife<@p_p8na5{O8bRf zs|LHLft$+FOUOo_Tc=Oe+&1a#}i$uqh}>G*rO7KnNs)#eOAp!|onGpS4dV$kSs~mZx09Q=iY6l3v_SOoDd z2`dMd>oBL2Ri9Yaxjv0cF4xSC6HS5i#qZ+Qx#=DKK4bDsIY%|Ju(`uL5L<^Jo#Zs7 z8r5kTeHUI@|BBHVrmLqTgAnn9ARazjOFj9do0g+v58dTz&?#3lzn5JSLg*{9cXQUU zJgo88GuWUW7~~B-aSh3KaQy&tv8HaA6DIqN3>s-)3c&*DeH(hqrvZ5hHd)6f4}2spV7n4tg$dqLa4 zUfAdxqHu1X7TbFj27Ye#I$$~ON)G`P_dUhOHuXgDCXcIidHXvmrC$*BtY=2Gs?fS= zhDt;+QIGye`5?7gLbXnS@1yN88MS0xNA|U?;dgI&@7RgHDT$EIkl9F$oM?i_&RWn6 zB4C!nOF7?P?u^GzS?YZVLx!{J94Io|^U{;)Ks@5br{R1-XD4>$PC9d2<)jFH(NASR zu5fj|t5TB5FaIK1mtAvvcQ@RxP1^Ui(9%gzWo4|xdI@hK&at4fivzR9oo&aQgp577 zy8X4Scj6iUfD%W9fcgzs5AMQh-M^J-S}$2PGAS1MvlaaLG;~^vB`R#zK;bNG-ohs8 z#v6xQXEzqza-wc7`?a6lTnT*}b?ZaKks9CYdn_Nq#uF8a!-{aKcmKS7<#~Q*!J}a< zXuJNpamh<*(GTIJvL>`o&8X`Be!55RoQYc5g^$L3%hNs)#KkH=G#zNN9{p{oVOc0i z%?Nl@3ca&Ikq9Te5nk_Q57MD!*~upl$Pk%WdCd2r8=S|CLBF%)w?B3>AfXKfV9fGH zluH?-uZ$0xv<0x*KSIV@c&|WVft2i++o-!W9%()vrl@jrR+=v|PlKef9N9CYe?2i` zIs8v}sp|9=3`1UIx{oK`4PMphlM>G}t4NfNEX`N71oX|udAX%rE9-tTwSYe;xh(Y04h22rNDrz6%b#!1Htaka9`t8>}d)54ZBV)u!5TX4we$135 zI44f3CPXfb7rc<9Of6TLQKM7BUHej>)NFnakC32#T!KFKnN4vXE;)peZ}OfnNxSkp zNcWo8g#WgDMDTI1ttC)_ST#$t1tM(EM%77pOONlq+ESW&T(PpyJ8}F~tRilbMj;lH zOR~b7_2%bCrWS%OWbc7Slqb~I|6(t%Dy`3CvX#L+3Xx1hgotDl`aulg94E?U;|aO9c#6`>^s6GEUsd1ogqr@)CVN&G z<33~kRNBab<>W&H4>n3-xm185Zx#I$}F4TNRud z5b4JG*>*%_EBIm5soO=`Y*b4ygqRxX&M!o=8#C|>$tXH?7uK~Kx2OuqdK~E??q&DN zVJjr3_tZlw(Qd*`Ff?y6(o?S5Zqm;)wBY@zr{aj+RA^Ob(YHu1)z5aXBep^xot=7V z(%4VO3Wk+3M0sO{>}T$KhLvAB+wOB@nN>rH{t#8$Nps8`&bc|3EY09*A|$A^d*f=j z=c_Roheap^C!b?7Ml`MFnb*o7s~qRE*ol5U^oVN=@5aL3k`G)U0zT?o2p#Qxm|m<1HNYwQYj~ZHALJT31F4x(H4mt)vfr5hxr`{5Fh?N ziJ>!Fyd2l}Rm7%QFQ&X!+;29qR>qx%#MAZ3%-K1hM@ocmjPYm53LWQXBrD%=Q3;y% za%sG!ET2AhJ~`5y0>XdY_x*b!O6X6z%+~=s=Hul&w&Jwg*M`V~=Lw1aV#KJ_kvIui zTo=Z=G@<_s|3_R#q8i5mo*-BH_>CStAV#jd_G>UGd+C*tOyEx>c9()U=qWuvk!<*w ziN1fPcAaTy zXjEJ{cxE>63;Zp9Ueo*|*XVl@kibd^2cM)N=qACa{<)VsOQ{VZ@WI&*MXGyxq9jh@ z>7hNFy(8ZRRKD8$mr0`NaEtB%2@)*mO1|~|&JoWYyIt83)*C$y$1Pq_dN*I;;)ERq z&+%$EI`4eIhZXO$hmC3)N5NNR)h5PxU~OA*89Pckorafeo!U-cwXdCOJJaj9aO${<>$s`vxEtzt zw9z?rgHf8)F5aCU8_o9%hA-Xc^Ka1!SkW12QV!&=m>V6wzS7Leu8Wvc4LvPfE$t4& z3Sc8s<40c@hF9JUD-zAD6Gnl7wSn-sqG_#;yY5ATl(c5_qiayzSjtP+!;)x4bVKy@ zW`nVQF?z`jdROlE=%q~PrLO2b_^S8tR4ahZm=EJs|nKQ1Q{mun~o zO&iS(yM$lWy$b=8q5e=(QIL_y#Cgg5$izV8{|-t(=jXgK+FU;JkVh-|zbowK^j}5^ zo;VLC|0_!1Jy+-S>e=IeK?%l-4E{k0F2ZjAh7xrA6L$LtCHORw_D|TYHW^f*U(pf4_BnZv6Yb=ji+2AAK~E zr+Y&z*H8CHg`S)qOep*~{W-1sKOH4VDWTMEZ>#y`8naZNn7OmOiH$X#J{3P*zlnt zZuC8(eUnI~xl({AJ62D}GbLI6*TZg#>a0!ssX-~_oS*eZ{+nSpUY4;@9p(Ql>~@fz zJ5eF1Fg9i!evnZ@Un#7M8#m8C$gDA}6t(%kw32H|czxR&1o)auU$yy{<+Kb@_L1S! zuGP^)FaM#G${CyRRQ{Paol+%Njhpnf|CzryQFWz#Y%(zXpH{Ns2yQAQ|9_yBOjQC` zk&$h%Y!lv3do`U8mglAQC?_?Q1z?k58J8gU>7Zq*!=Txc(HMb701SVUH0wY-l;t|p zH*mW-S2-_dd_{azmhxb(-f&)6DOFg@7u{UUUQxrdgSEg4c9aM*)SV9a>W0)jdO8jPU5TcT><32ELM6tYx9It+%Iks z=Qp+{2XQfnS$r|91n^hUh7o&h5K5p_*!{P&`M=w0j2`>iyt0={ zw-zXQ%;KRd7W0<08&_)%4agJkTgquT6j%wOm4THxm`qw}wYwo%UB@gAOV)SxpcYaosq%$F7`#8?V5a#_=bBZpjh z6Yg$N>&NSPmVz;+m*>yJ1&J-Zif1=$qD*8yzEM8z2Oc!V2^p@@mS64<5os)%H?UNA z;NBw%9Y93H%u*TbP_PR^F8&6uv{x*eUTtdfSmBUm=cXE@{Cl~_kUozk0|$n!t(u(d zrGL}kXh#w+Kf0@f$bi5op|@mKdb_jfdp{oN<)4?r=Uw9 zI8R~S-Q;n{ig3UiN$s1#qMWaer*2QR79UM~R{#6wbu?pJ360SX=4w*>-#C|S`7^41drYO2BmL4)8-awhrRlI5dFUoKJLw{@u&4~|84LUK9ycvsTKQY@YU)iTsr?Lx&EJn4?h(7 zXYjpZ3?~$?ClCKK_-x-@Q$j@jKKRSY{%<$X_kJ8!H&;_1q^v zA0(n#P(X#U0186>8GPO=eHvVpU6tes)=7OD6g>I=a|U1K5!JAu$`_jPD_{JL8dP6- zw0YbxuNR;7te1#X!Ndhvz4y(We?~7I!O1nBZcWk2Tj*uD)Og#oNFB|B4+9VTj%R zas2I`R8aE4+mFwy4q9#Ro#iLtL?VAS%2oMsO6%J2^yI5IhJeM+omCPh8#&5F*>IX2k2L;a9JR^tGO@PWghspH7LR$tbO}#$C zYiy2ssD$F9;e7#{GSAE%+K}Lm5!YajvwQAOfZoFyus>+V-;>+y`~DJuPK3l&_H9Vn zWsTpvV59jixISaTaJ*?W(Q}1dH$_oXnzq8(u&n}}>jNlwZrl?qtOBb?>)d3o$R{;v zirf7mJq$YIqY_!Etffo>f(0uI%uW-Ps9lZk@R;#9)ZEu(7M+QW{>1&8e-uMPrQ8c^ zy~I&_jm0loJOG9*1Q~7Xq$(PiTtI+K5_g=P9kn40Z{|@(SX9CpK zJ598UH<8iI3;iYtNW^Ga^ZAe1GPyE1UUsK(xG1g$#Z29s4y|GG5!IT2%-)lGR>vYz z@5qpDjwe`v8AR%NnOu$WxpRfO`CGMVzijbs{Y{k3?q-u6OEL$UJn-CDH#&36hos-l zJWZ^l@+ucZoU(fk`cea?scSN3G#h1hMni|`PGcL8>$DIDdk)Hz2U%xZm1$RaKP;F_ zSh>Nw;QvugJjNonA}fd44ps0SO!P)mWJ1Wd(VFI%+npAjyw3fnQbD%4jX<(oloh%3 zJF^cT4(_Sk_J@(0o#VEDa<5U9;Vyv~rTxiKwLRZ8K8X^K*s&?HoZ@hZPb24cJpoaLbGDqW5X$mU+{s zM(wVFZDLNDC@{j0(UH!$?}3YxtY$6u8C>yyCa3e%Q@(f#@O=F3aWdPb@4+Ogw8W!= zoq_BN@DY)|i98u7W~n2zH|@uABSn-R*Qm~#1Ym2y@suU_iq`XPA~NJpmetS4#;2)i zH(T9v%P)V&EKUNVT87uSYN@nEm5D)P1$I|;M(!KT1tHFyx>A$a0hklc|)1H(%``e*^dClYdckQ~X zM^6+uzYE|>8T@>H=N$H0$CF0#jtM~}L(RKdIMvtuk{O0F0&!_atG03H{C$2`wPUi) zw>{*(zm9nLv&uv*RpxcCL;d-$H*RMHVUi$r<^$$@S)!(x?(c`c=SO`^&-W5dPS-g| z$D=0C4{~0gZmE#|Oh0^nSZ#8)>p(hLeEs})`|Gps5v0GXOfN{sBPQnu1*Fq$lNTp% zU!VW(CY|j)d~y2Ogmj{%{TYtFbpUFHp0m7`rD_JVkqeS|ARsfrLB8lqL)0dnQXS>GGf8Z6-+C!5bzq^Vk|8nO-HpKKc__pS_5;~0{+KL=nmRW zDIg!9Ee6y|rh$B^cv8v5$BtbMja`NlXlG(cBq>__HeqfqhTsjzdDGq#PN)rquWe=}yH@ zO^W`YCATz3-Ly%IfR_oH^bnQyTRzY~%5Wd`nrgL{Y9dRLDhQIs^GA@IEuFcA$j`>s zIDgx}8bI2mNqUNE&8LH8Ox_H8wGxR#BvgiySx9MrMnob`kSh2kKa08#XSO?e?npXW z`>jimOL(ix0r6==ZNUtQ%q%xx@RDH4@(kKF?70g$-pZHuGjKAHrS*r2`4);XDGVu{ zeo(3hq6(eK3L{111<1V{i8msGiIFXIHoAZh1S(@^=lM1h;gYKz>{ul4N{S7pTFa&h z=7XWKP@`TZOA!d4^ z{P|$BOkuoMI5D3Yuf^~is8E9U*a5<~;r%}B?1Evh0p9_67>z<9Q-3fxI7n4GM4;ct zo(gDHvt-W3I0pYphm)jA#LD1~NTbx{z5aS_?2xgw|Sm z3jVo{!3$Nn-xLmqfsS&SiI}b&z;zWc_hNqc$C9iG6SaqBmA1>;WTITFY|nz4=>vXA zwlsJ<3ff{?@-lt*3~GE8y)Se_1n{r(Vv??+X|Bk(gZg7w-^*d*To`1pRL~O3&B~xG zzwk8Vr5q&k=Ld1HDeO7IXI^u}^12>*zqzo7C2 zRy&lIX&(ebKN1nu_1JLdvS-z{b3X7aM>XqvN?#l-*{j;a=5E@HqH10C?G5Wn)RF-G!MD;gH#2@M~T9=$1of>v>>J?vTUadb>X$YrT zUC#-Nyb!GBB&d7``lSw*#|~Ah!+?Amo$_Gs-O1ofPeA)mXndfwbWji-^BZ62PkSgP zt(Kdt5!L*JBDlm1-{hYJJkEN;iFzy_TmvP4Y|++OR#{xQU&p&|^(i0bLDe9!)(9f? zmqALK5zcURkVp_H_^ORhP}KqazC5bpgGrd7RQA ztPU{9NfE8tlQ16`qc3v0^J(Um(WE`|H#d*x%TRtlz@i&?&*n{wuNK6U2O9td9)4v> z;ZQG2Qj?9RD10y6P0_$nhw=&ES~CjoLCFFYuV?wReg`3?yW9onI6reK06eImMfuR= zdZ>6lMt0cBNgKwtQNvqW9Y_nm`29(TVCxL31fk8tamWShFhl-pocy(OmQ@_YJC!;* z(nHu$d*8=$M2gs!ayjJDau+*ZoTskSSi1RvbVr1;c6N0y(&bTuSND5T0owV{q*cx! z`R4W48#bY1+U}X?9x;8Huz>Y~lT zouUnLnTZ1$puyQ@wD@US=|RtTqT`lppp0X;*vw!#ez1LCog+Nr4IP{VA>^Q5xay%&s{i?F^C0UC)`!3V%DmEAPVuY%q6J z^t2ovd8Bl4W+)UwWoxS?w7Xf`~;hDjlDK;MLGh(`_~q3xG4 z&3SuDwW+;sY+R3@xW3rkzyKW)DK#=Z%-3ui)t>33BA3KO`aJ(X*n1DYCjTzoH$Z^U zlYsORI!Ny=K!5C$_z(wnGsQ4v9U&0+pC&)!d& zviF>s=bYKIU&H;mf9qQ7TI+koRq(-^c6)jRcN>Y&1P`nr9;|e!w;*S4(tKG0UmjI> zmO;KZ3_a?o1ZGRGA>%9nb(CDo8*$2pl)UgLip5>T zgDZSG?qv;$`j@)pQ31TP@|rxU{YOc!;KVi%c|EY5KA2Y*D$7FWu23gE-c7pufhez+ zCQr-=4wi2kBzuan(GA&{(Xvl~ z7*jyz;0DX*v{Tpbl)ecQPD#fDJbya&&~Ee@$U$CMYU3W+!L|x0vnEvUr%cjKh#Z3H z+S7`+G@dMvj2RQ_J^~Ldjio6->raO3SzgxO0N+$*u)qw{p&uqwu@GpD5#`O|9fvjY z%=fBmNKEF<2|h;6_HS-!lIJam=71%&=?F&{^zC`-6TzBpbuZy`Q@2b=IW=Fl(58gY z#V9XIa4tf|N#^k)qbcK)#vLQ}PTnLPjYYnU?tN3!3H1JNKQ%w4-FrBM|IvGX3U>Pn zEt@e=ReCDFJ3}ck&eHLTd0fq0>$4aqX{#&2sPZ+gW{48*BZUnVa2x#U0_x5)Y^*~^ ztiLq7e&;ntym%gRX1NTiB*u;#$cqIJHVl z4e7R=^}<`!SYM7i#Yg1znPv5%YCTI}LS`fT zp#7=e>eq}O>VT_qH0lLCtwO=he%en%+R|%V6%S}Uv*(`9HiMm-!D^qLCxB@kwd#O5 zWgfTcTYJBasL8!o%Tv%+y>RZa)1+aC=-+SH$XN3x&sXTQV*a-Dyai>fKCwCSW#%sunxKQ$P5O?{+T+{Q zb*iBZh?m-$`0e}B7qq9J2ZYIxr!RMd;=a(_+}TS*MDgrMO{pD!+Xd=7oXa1-LBuwFN5dvsS9&a2~~rwosEon~LFt5(yDGUh@M6xf;) zOJsPD5$Vk>pN)PZIacu~BgeY!P*s*w~e^dU*&m`vN^Ga2P4X7w{ z+3tjt`mchcUZQhd)$?vu1|uVQoe|vW>G`Y4Z$0?)yIkjQCQo0|T+D=B%;jDzv|TLX zzg&FAUo3H4uBcwF-MrigyWGsZ{N8rC_2qIKf4Ref|E-GOzllE#!yo73PuuY4U+|YS zU_6kT$9il@4fDtD;MnpNq4DptJ6M`N+@|Hbo=qwJ$L^pDI|P9E^`*j;Qibq_H8*cp zY|D%YR_7mf2PdJ-0^d0V2C}%B#}?=JJjU`gpYYi)?2k*ovM6`|v~b`(r`6D1jJN;z zUPCua>(j=krdodW_Nuzq=Bp*}_B*Se4)P{n}KIHR4B0kU^vr2&) zxQytqx|rqw>ZhW-&NK$mt(ZMTtf#rBVO(+oiZ+sN9NNsT{d-bZk%GAwhBsQ!M~yR= zpZ94++=HVK&|P6{HxHBKn05@SKM!XpaALhH63o4stZEjDqSGN|>Nk`Ue6ukoCDfVD zMaUnzKch$cMvLa!7D~TXkLBb@SPn)KDoG_t2#OJ$#Zeq32wTrQS!_hcnnF&AGJsK{# zyj#2DCH5ES2j6K?DwQzhg-hU=kz5{tJ!|htf2Y)AK+X`2BUD= zvIBQv15X_mO(KNvY}!Y4t6^1JX}!*Kl|lLXxEUsBBj-3N`DffZr@GniXmZgUT#dmi z8aJQIW&T_%=yls0-_?GfI6lkUI4x=aAIc#`@d?J+;KQ#bE66( zvRQ1ClvK)0CJh9}=1KwYDn|*tHFsCS0%)AyS-{_L_7r5{)wbo54=;!!NOxHQUDZ$S zOLvDiJST1@fl}`B4n@)oJRK(?R3zURg_Q?btx!fV8kQNtKyYT%^}=MGWBt2=H&9#p z*6du$zc`u$?Di>F5N=erx#n=)uOv&JH0U5B7$Ny;(ML4w=*T03eDPcJlRZ~3?iDw+0UPzgz(88`=|ybLleOi0d>tK zAy~J9+P>G+?8Xrmd1?t;2)x^gGrP7fV6eY8niMT!DMiq3z~d7y^L|^)nqrH$-Yu^!=1(o_doA7t6nWeKd}wu*bzUB_ z?tSy()T*7tvLce#$B8P$x|`3kGTzO{h3(85->+p^l~&~ACK6&Z>~2~8eBH-G`OIc4 z+OnpA*Y}n|i0xFdWo@aOuebe~?Od=gVQ|p@9 zbbdYjbmq`x4QSmf`sJbti#R1YXg#qfdwgvj%P}QEawC=SF1x1D?Cvzcbc_Q-Y9dUH1=#a=F!AQm}cj zZpQ&2SaMw}=>aT`FE&^2o9A;7Ggr7F_G-56E?>^mN3$#Tplg_x{9L1#Y2L4xcf#cf zrm;Bawv^s`-1j8$1(9NQ8QCz#_d)%K7pJg7&My^1nsk%+VEQcBa% zhbjsDkh9+!R3qc;6?x1w!T(J|<7Hm5FOf0ui#1sTJ|%*t(2-DqUQ71|B;^(*aEu;z zUvsA2U4s_We8LBMLuDF;Eo}dSCq*Bqs$AT1<6k5Cq=J3u_L zo~3!vi7XagiGyr!sH zjqGk;%xy)rW?ekFDTO4Ac&FWWOaB%nD_b`n&=^cC=@u2_2;O2d8}Qfoa`;R+-? zetUDI4?6M5`)Z8J!H^gACm<8CMl!;~`)y~iP61Ul!YAMgTHpcUK#0LW>D9?p&N=b7 zdPSgQNU^oDAyFM9MEP(+e05bZqQ2_hSq%TNtg;Yxi5pAyLYg_gtyD=>VNN*`(Hf5f ze3s%Q8pe_#m0eaPGfM~$hT5;2fwX>9((aYBfw9-y5hC#*j(+j;Q}NB`Anab_OBaO+ zS}Z3k7F62a5{dn(giRlkiL_RVHI})cc*DNf!tLE2B_1*R9Pq^q%N|g*EQKx9kO9zP z(yMD?v8pUXb$A-ub}XW^gvM zHhD`{>JmmQA<>L|K~hR_uoiGB8Xew+IY#~_m+=t3;t0#J)M!yK_E`=!hPA73aF?e} zlHgCgnsm=eAKh!iQ}jn{D#V<}z=c3{XE955{kCPj4$|0$NmOJ7pikp9$B#&3qMGgJ zpslkC8j#BGyIoPq+KGPJk#iAbbK?9y17UNuGzd*Js3*dkD3-34lSsOl4pdl%Ek%L? zF(6!sHX4kg*omn0u20l&i7siu)Hj6I)F096#G~q}ke~z&T~%p49HjuHL<6z|e^fRU zf$P3t!P?>gZAdJmd}R||Y(5f8wKy1Ntw&`CN5prZ2Y_B{fIOvYhRz4j!6fo~I=}4N z1)Cd!0jPLSJ$hDa>M*DaDZ-?ARNY6RB=sPq2_FF(h=!F@ z(Sd~NKsC}JbL4RMVnrL#Xrw36)n{S5TF!UD!d?u6LPledSkW8rSMFhDz!+2U2=>xW z$QtSarpX)*Di7#JV=40Y$_=U&!{Ud+N(kJ}NkiaRwjab#n#AzX1n3z+Y!m3xMKsJ^ zUQi8pOo(?7&{<%W$burti4AUI+SWdb`RaFW!&U8thkf>B_zDbGF>vy05+}C9b!Xvv zl-giB6Uax&mg-8nL}d-3dJu&Kc~Q8ctLy<(EO&VPK5Hi(g#^ho%0fdYbV!b3Y?zKw zg72V`f<=<7Uh*W{G{dZxjIn`AsLGf{axp!Vi`Mu6YD^8R!!sF}NX3t^6=DTyDao}(_zi})35@XSeB77l}~2eg_E0UjR$bUcS4fxAFDwj=66}ADA~rT zyUg||wCs_Jij$f;&F0>86$Z*vxOTG#+~wT~QaN}X=hk!rgfjT+GsX53Rk+yjMBSW zc>9)HMUB!ETP{6PRKAdbuuYTe{Y{eVMS{dztLpah{hHX2(2B>tM$>b%52q*MkFDYz z`^wvm9x2COZpmlKTSZ~fjC1<X*_W%0O)1v~^NZ`%|P9wnT>g zg0#FEfV^#!*nl_6P7#J(7iLAFpV+Ti+780x@Qv)oblKTbWYRgkI zRZrXEgX@wbWz?GnoIv^wi_LJYa^{sr2q|=UlHR$2Z5p&nm{O2 zC;;^{=C)DK!k;g8;8VHf5JTcxdW!1$2oKHZ6g$hbGXwAE?K4UsPe)nG;LkXVat~uM zdPd1fp1v?fy%y4)_l2Uut%r{1`dY}N8uCQ>#wr`90S;dJGXwVy*8u=w5 zu~lD4a*zssyne-xsgK>8*_qZvKhj<_FRvUPI8diL* zPI9C^0wy;o0+N6Uc}Xo1sbCi`VjdokMvpc9xHmw|iWM!>a4)(6-&Cq8jS1d#Lhm@; z{m~UsvGjIboFL=IQ}cSvrW4<&y@Y_mnuukWNYzgRP=tkC?3P4A!3{(Kz@|XLyjkq- zj}96}CoLireo|kb>r#1cA*}kjof5 z%g+PPoiuyPnT8jm1)9RL~B&)Gc~Qp zCL0Lf;V9Kp4|7msc#B4t2NS)QgE#%)BL7)h&^EX-$zSHrBPsWWqb$y=GLVZ7SlLY0kwPRf##|8SL2akG68=*4`tw z!sd*IwtYTa`ixQgj%{AJ;!8dU$$~-z8y}hCx4XPbk+j$#Yw3epL7k zQ0ttz4;ru95d;AqLn=^cXUe;DqZa`DH9JF zO<X(56#@MS8*7Ol-f!*d%DMW1INnF~+r)#aX{9_og4!RRk>aI1%J24w4K0LWH@2H( zgnr%azg~I5{k{K!^q>pznOG~$4a=@AV_jh2$xRYgs`t2*fRFax>5qQ2W7JZlCq=yr zh38fn?p_MW4Qya5`~gKoY8{PpfU?`2(Th-G(Cn*qJ7o>sX3~Mu)A+E>s9B{BqaPJ^ zy<+9lO21#Ovqj1i3HYOTVx$=C0vSeb(t8lKx32GrQ3(8&h!1?q4N|}J-V`qJp~)z! zW(94%Q4eT~NUFGVTm^K%CI%d4{nGEOd23u_#(q}yj;D`q%+z1DyF0S;aMdwlN0se- zE)si}d#dTw;bG*?$ADt(>2s$9c?pF%X~lBqZ)u9jMxe^0K(zR2wJJy!FRZKK&+)}k z%(W*h`4iQT{kqvX49y41YDMcbLNdm-urFKFY^M>hL-g2EREASf3h_s^fQ zI0q+emos2B@A%h!6`$q1?GWIIwpOCzI6mJK8w=A}c;`MU47z<>N#@sQ)ONIA)}JFi z5V2Dy@!XWM3#7nXw|xJXvz5HUBnf5s9Zm);o7pvoz}&aI=QQoeRMJ|gaQwYxQpf|cs@w8?IBeAhyC@4NwRCZ*Glo{| z_PM<4B5|HFQh(>5{iUEasiF0#bZ3qu*g%NXp)bYJvZ6!C! z22FlQ6$2{pKx(d8k2DA1UH4#A!V;X4|p5Zeo8(~%kR*as9B^95{!=kqH^mQ`B zM$SC5+Pg*i^kkA-T53#hV+9M4>fIT9IFd^9444S>p_zK5%G1s;!TkBL`#K;ly+mTS zdE;kcrYC|Wd2!#3bz*D--D5n-EG>O4A1D^8CD-2fDln>KW^?c<7G!aD+90;kL0twL z+lXcG6lOsbVhVxTI^i9KpeVBE?oAntKUHh_ueh-xlgKeBZ2 zQAwXIqjC-@Z1nfvHwwd>%Zr%ND!cu(Ikhmqqh_`14e8jt-Ynil#jv?;`|)L#pFgQm zb4Cw?ce6wvTZleyEOFbZe*kydBgsL*QoMf3Ko_K0<1ttHl_M(k@sZT1LcHSi3y~k^ zQe$cq$~EXcXsGmrj!=vAq=C|f^puI7vdpxFU8u~AjdzR8tbNFZ%$!ravh2K~*|}wv zoA!hE3%*TnAwqYmqj)~v{TM1|XPEjz_H*djNMNbZY|eICNd1RX2=66{<<6HRGZvg62`p;qGrKB+i(x z7$D?OWb<1=^G1N2WatKy$94JT;;7Po&?> z!81H#$bFXnV#*2Li(O;xMXy0oxKxS_fgjiKNgsXtayojSi<2_K%AU>?*+N%HQl6a% zrjbNo-*WN;D@iDciAa9C(umN`MKF*;xf%Eh*$bKa6G<<%i1{Hvz*AnT* zC1#niz>kEHsrC8B&3dROc-WMB6c`1wvVoAO_&u)!ZYL|drh?>62YeF8jDe6y;&iCC z^B4tXx-%-%aV#e;JTHaWe?$G@2~AgUKP=%y8xrd;Mt5zG<4O9w%N(fBf}NV{9_OHs zG*fOrq?@%^VqJ6MtKQA`6&kdT8>;St4J_1Twy1|DVg?ya!%^L^>Iq|V#=;SxDN?8f zLZhbsIys697)73C2`9@>x0)`#UgSx13hIn|v!xlqT4XOo2CbeM+l{qVUr`Cj&Nz}t zhXIs1TE1(dYjXTy>SHWwtQv-2m^hzO5UiRZ@<&btzZGODtsuU0jz##n1QDA}J^@0t zzocwxIs{s%VZ!krnCRE}@S9zvj?h{ETUwxYyH8JA(+0_EUcJFDZCa-jre>;8zo=2b zssxh1ue{#PO}Q^*#B&IFh<1HTrbgqpZB`w9 z7L>Ocq?M)o1uklF3yKVGe@0lqBkn3bi1oe}f~wiaBwtzLe&K=O?>|#5-C{r5kruw1 z7f9iJEzM)z>IEk9losha?_;J^Fx#pjf1i~%i+BuuLK^4!oZFQE&8&kS5&6wggxEx` zHR$|&XHs8VDyl9WySU`%DNsqL<8Zh-MHLIH#ABI)$nNGwjKozu^g(6vFn*30-skYI z;Z-x&yAH>*V{U$z{GsmL-O@&hR6n2;kf7p%B><+|f6E3vJ>vWjeAn5rkI5!LZE?6ipSe&|T|nr0$7{NNhoqPI&7S*|QY>uBJj5GP z*z_U~G!M#nNN->=i%XY26D33}98TZ>8#0JytyNVx88zDGB%bM(%%Oy0Wxfb|8=AC_ zPCSJ9Xur>8FmQap%Q+aayTVhL-#5!}k?ff^T^j8v^}$upNS!g1kKY9G;g!ZxhDgMe zhaD4MowmXc34s4NS2%%vjK*e$C?Dv@o5ygr}v(V z@?;rpANin_63r)|@$&NwmIJsL{H3Z^Zz{Md z$H8_;k;Q9Oy|Vg!>9I&IV-e*ndqJKYceOL_)fBO!VP2ZsEv>n2@4n;Msj|Qp)%l~3 z?}PjW3+Lxm!|K+e&XIZ)Vy1re7;C zDC2O%n0?&<>I}ek8-&Edb(hw(G!mJfi+m_&O<3_8TAPkX&7~;X(5v|oyKbhzvUxPz zsn_2c$A=?r91SZT6}$)Z-@m0rqQH{Q2GlYI%Ce1H3_lB~caXb^VqDBw!=fwGR}k>7 zPl40i?rgWp7S&MwkhiOtWb)_XNPakx6OMVlsG>$Sv?8c0uY5Fn;2H64<8TsVQn^Y|jQV}z;uwcw7_9eJeK+ozU? zdG`C6$nGh3N#ILfWLYSwV{^V5#?*JXBk5bFM$#feACNbmVo5pKqhPJoZ}avM!k(Nh z!TO0OH8Ow~46@x9B%}MTHo| zuyykZijFP%vpiXSk^7yMv9W7Fm@dLRhqo7v-v~I@5T!Oraai@~Nx923p=V-bbd{@S z9PXBk>Rlsd7e_UAFc>+8-*Sw;<G%4P=2NyOpN zPPxn&PqK7>>S6WfVuq6vHR%-aBde@KI)mgdYWH5(+<3Dl*lk^uSp9N@=Nb^+!ERgz zLCCH>r$!1xDBFftLaSEn9ij0kLtZC5py#6G^gL3#@RjA3R_Ig_lJR=k!--arETqk= zM7r))CN*VBT0LpIb%hxHd#{N>ATJZdJ!YSdOAUR_%wXr2is2d$b=wUe{XjiVcif78 zk>sK<{ez{pPt+e%Uz@~;AZc^(#9e5_&&AQa;3nz+5uvbFgM6YfoEq+#1`h2LApX8> z+zt_;@kdX#@RVsz>1Q%e*qZ=MAJG=zkmS;kd#*)_+7?DPoyY_i6^>o> z(Qo&C=A#b&6sp^|FTIc&*dI@C;=8zoz-d>eF6X{5GO|W2mc4ixi`dreS#n%IZGWzA z4&8WUwHN4cuzp>?DmI}ig`fgYa<^FXF3y)yjno^lXvPSw!}V84n||2Hao^H=4_NV) z*SZVSd3;HK;fNSrnfh`mU~zBurR~f;TW<{k$j#80!YZb_=$96SCXPJuYr2_gP&gP5=C=(}KnU*COg}OqP`uBmvIY8%~w{W?>$fIaj z7uS;%=ctwxlai9FmQt0H*4~?4tdjn?B&v-hSvQrr56j!fJij|w)#8xNQ}o)0_cbU& zp+X&gTZ%OMWfd5|s~_Q=W|;KsO{8jf7d4z$sc!G>g1>SrvBdT`qZmebF;bk4QRdek zX)4#J3MwxGo=9e0@~h<2?%5fK8g(Ps`P?1#cH8%_%{~rl>R1B+;Xpmy6Cesvt9ef= z-qG(H$lFI?KWKZ#8If%ZBta3dn|D}Pw^R2M2=zz6cJ8Uy-J8-0PjW;wn)oAY`e<(y zBmFdcpctr>Y4mZVc=HqDel?}zNUM-qaq}7zAzWh_NUuDa&9ezwCdHma&)-epetAWwacFz2ae8y>^W|QusSlM@#4r@*L z{;5qk0Tz#jcX!d6e|F@@g@aK5p>8R}hZ!i+K702idpEme6mTlSJ`6zV8L28w2ym={ z@DM14$`ZVsb4CW%i5T5YNGRq-QkMK)YkqH!r!?%^=#%S{x1c+k-5%(;3cdRl_yJ`q7YdR}-uIhMIG!7Tr%v4mB#G z`%RG#U1aoePv}6A${~Spmu;Ry5+77H78b^**YC_~}tl!qNKvG3gY72}=;;<9jw1#LuXZYPFQY&8K0$ zC9j0s?dv?%NOi%uvcwpfS}@G;o$I&M_179r)U_c zlX=b}^nT;da3Gh}N^RA(0j(4ST{*v(%Ud(i;Pc=^{qPEWeNLAmaYW@2FQaq4Tk{QR zEfyteKB*czoQCSKjL{k8LX@M+*|pnBbzw^NF(EWBYwI(c8gi8yUo|zBD>c>Oo0{5` zn){lXKPa`#Hnn_FYW>>O`b(+psHqLF)K1#mP6O{?Xzt*Eck(xPiov_&n!8lt-P+CF zM(`f1=AN7IUXSM9JMg~y&3$3;{+Qk^I1GDGV!hsp|n>x!7l>dmE8Ih8f-)-@xQb*t81kVm^3Fnm3i zvVj3e4F(eUTn>6ZRu}L18sFN>O7Pum1W?x6SJL`J$N-F?dK#kq4X^Q|PlfDv-S$_? zfS1YunGSW#<6pV@Ke`_87U&b8fKVQMC%A3zmHu{aFJkfWerW>0@v%Sf&u;Hw06qYM zaQuJi_P(Gm;Zo(VVLK$b?Jsnyi$|Wz2Yg=~sDAY!m#`G@$L)OqD(!D>@2kB&{pIcb ze|6YS029G7hR)J+If{|_J|dbmXmdFR7JZzi$d%!_5-VU@Vh|_%esd*WyjNf~L3+k> zHBo+}WHssP_I~vVf<$mF8A*R@Ed|B*dM#D|N}i_uZ@_juoH&O6fb9qt2|qmi=3#jE zT=4%4*v{Yo_TJ8J3!gIvL>@@^Q*u#Q%u0&9S~^-u@UV-tcp~@GZ%s=FT)FK1s~x<= z7VsFlPCBhn3la>G0F$J?dZ#!MXGAOW?WK~zL-IIPk<;)0U%++>WXRuSIscE`9%n5M zcFH^u8+RP!uVFiRlpd%q=7pVT`Qbe3`~6+)r#mqS%6u?B2Gz~Qyo*KY=R-65yZID% zV~rE?>0Y6FuG{X$S^vwho&H|g#=nH^BxHU2-QTBjy89%YvJgh9)346_I~l8A_z$q1 zk>qzN$q8#U8|^jP^gKb@v}oSkVx8B4zk{lzu*4Z8x+R>qVYTtu#HRB#CYP&36|ULD zT4Y1Ey1z5(hl@n-4-7e+{?2TtEEbQ^8Fpsgd)7ZZ&73-*pJe?mOYyqfq%)I@m-zl3#Z`&UQbaaYR5Xz#nWLz8-Phh_r%}n?880F3dA6;07O04y7Am zr1Tf`so+dD@EK%^OJ0C|b~k%^ou9>QkNC#Uvf1&zG}kjfc9RB@PBc$JiE-i^^H;jl zZ)^|VSdYB1tQ(wu_wb<9k*d_X@6TX6gMXdd6FgGta5OmE`mf~n_}B^JUT8yabG_r~ z2kwZ|>mQPmd{z6TO*kz%c-h6A{}U@Kkvg9QuV%fN=LxP?NDjh-I=Cv7b-nkhKbBS? zBx|Mzm3OW!mZAi1E^hrCw|>1?0-sbXWE=J>NL|4AY4E=D8@v}-qaQ1sDLQ=+zv6mU z_Oq%GqUhD_AIk+QqO0Tkl>>TktJ5YT^gj10FIMPRDVWR{mnQYv{*jZ6W9%@4+mo_M zv>>>ojy0`2-Gt^ z;#RA8(%&o3gJo|@(W4UGGFGi*MS2;Wh!ctLu13RM`dA=ur9%>g^?Eg0N&G7|HW2c# z*}^-d1V5{!wNvSo*52bwdQ_k3SKevH7=itBG%ORKVn?O;PGwFKb-FEg!)dYsgN|yo zC1EIKz0HRVE-5p_fQL!vT6+bsq79G(5#k1pa~Mm<@*8~9wL6`7Hd<30YsIhqku(4( zSkqSqk@3V}cvX_=3iJwYn1uCDYwyckj2FRBlIcAa)j}AfbN7r7UKSEXFZ!`X5sz~l*paT zZ9$!o$(^LS=C2079)7fY_Uqk*u!%asWK!`b&50^Aulw^M0~ub$wjt*u@eT6G8#(?GS&O{R)bXsgmyoAY z-`53S%RBpQL!vauzE&?kPz&;<%KNIr=e`%i0H_YioJG@)mK%lG-BtjKAq zD;8A9#ScfCrnx@D!>Z>OTb@Qu3$vq#@0%`u`aNk{{AzgANct_^$S}T6q&m?4WsEmn?%u>Jv;%UmxS-M- zj3ZrlX!kuz_n@&SllyISo-ZEX%J%@be-%qlhjhtFIV9^o(+1x-1kY|j`e9(V9o;@H zac6g+(u~U@E`+Pno1DmWw` z?I0BJc&te|d$2^*g zd3+ob&WMeW$6~Cp(ZN_v%p+!6@c(2n$pVA`cmm{K>p?mJpS-RZFt_dtRAqio{6Dgo z{@cPApCEBWP2Kahy%>ROQ0>9?pBddV9FwQ}^@m zqhFuM(c-;X-=j~#U*8^mesqNYdGsZmRP=ZW!*Kg}8O#6fcqKt@`*<~3Tl8ct-Rkzq z`ZJGrCmS#BZ=Za{#fYA67G~Z){Z{ho-RbwTy6w{+6@8*-TQ#${|IYBm&xYRASMWvt zzshmkiG*G0LG6ElFN(Wz$o~nxXeIdQ@1O^V3)qkTRu2MKIide<_@eE?zo-ZGizWYn zFVed224BG!1yy~|BZ+|38zP*jkx64mGcHEE4(B-vHXbOk@=g+m#6!$5L9oBW4sV)(1Q}x z1+!5>hPpqw`|Cj6JmJ&)q;zKeLc0|P^T@{Ou#!~-k=knj-_(Oj z6}2lvC1hRU9qouYd-nq}dOt7ZO9^myMky=4tY3@RdH(LjWQ7gy^Tq{O@(1U_>eB3R zL;VoQR8oBH)vk^`#euVuugPXh<~BHOO({*w-aSZ*hhBsJ`Fe-QCQg8z!$U zVZY_)Xi17~DFx^q*I`&ElfbbE8EMDhIV;4UWdkc-fzbO1O#Zs2+~}|P+nTb9r1(p| zpDAxuhPcb%U(Fc)#P|EJK4ZAgfi-Gty*N9L$sD@VLh`4YLhrfs_o*rWeSyAzQB$h& zpl}MJ8l$p*%mxlXT7c5KNBoP!UlT}o>~s{vc76UEWdp;skg#?_=ACemK8}!~znw&J z2Mzw$Ysy~%egFM6NGlqX1=t~rZ z4(r!|iFn&o$7%6eipGZ*B=CCFqtSVl15X{7qlFJ#^$I#n^#zaJsxhynowt8K>;F?t z`3ImcU+SX_sGVWE`7UaJ`cfi>@jNnn02+bt+NHS>^qoZ_g}6H5L79W>P|g-lhEkEv zhldM$FOP>`sT>X9KRiC&%LP!ClynP@MKJH)6TO}QJ;taEAw}JZ)a?C20fX1N|6EOZZD_P-;-(<|SOTmenP@B=ur++kw;m)- zB55RaU@nsAc)dFHdUCNX8P8=&yhU2Ag`!CSlleC{54Vrum{TPFa`!80u?lA&oDMotn= z1GKx`)fEACcXiHsS(!KhU?FKiSn?D=Vj50Brr(|ot!O9ZTe6+PC(VF@!EZ!Fz>%`) z`E+;Z(7>1Bq+;gRD1*g2{a8B4)q>T^-83!8dBRD5m*jTr!c5oxPPU9?UhCdU#bt7o zf}Goe54&sQbj$4^nzKayLVA)92w~|#CC9M(2*Q${aPjkTY7T2yV~(Pi8cg^mSujDR zGoam_zk(~0;&6Bdh?AO@q!51Avk;C3c8APteV-$2s^3*r5qGqF zar~TPM@`|pl7x(M-gF6uLaFN6|G4T%SEoxwUI{t;CTGOAXm;JEYWzqd<`RpuQ2Tvv z{G5{i$1}o}Pdir()sI_`9Y=4x-+TJm>l%+x)_{6JaZtgQ`}HoSrn|+}`LCy9us)Ll zNg0T1j>0Y@hV7)^>vwTRKAykQxT7;Bj+#yh0KLaM1 z9Kpc8{e}kRSUG_0SYj4jh z{Vnk4{9r|Z2jB$(_@6!N|J4^iYo;u{f7Z^qm;QXe81&<3-O`LZ zvVJYUj_nmRy>z>A>-~@Irk!4)U(Ne7-oILo|F;7J{z6~;|C}G(bf)%q?O9Xw39b{8 z|0nB|fC(xDmD^^E2v6A6s>RK`K z+iMy?RoWkc0kJUCs^3p55{iV)YNBoa&7QS$Cq1QXl#eWyfT1xTMvs`60-ithD70D7@TBZA8RWjZnep#_tQx-3KwX)D+mDA%QNnm6 z?6{g!go^IURyK`9?!(C0z9pK*r6<2M=Mlu3Xr&P*rA*ZzgRKWmXsU=K7;!MAopj)w zb#hT_MfRd2)`CWJ+SJe7Oo$hS?3CD8JS#5%izX9R5?G7M@RVN#!D|g>6ED2M>--N?n5sK zIX!B|UJnN~?&a(xyn=uIXPZL=sqP=mp^b@0EtYT8tJYM?4N;QdW5W+j+kA+JBjymWgIhO;O?9bn~1b4GV3LGeE7NBU)tfB=E8(h`I-C|;JNO3s9UsMGT01W}? z|E;9wO33~nYf1k?Rj7S4UHV_ND*Rtb-_KXNRW&U8W2<^hOXzPUeYDVa{aS|icEe`z zf0(2{4n_Q3BSB98w``~+D|mjUVYMvYZwL0hm_bDS&uyqxFG<~UJa@-~1Dtu6>fOa| z!bla$njuxXgg!;RTIu>L=!mNIh3X`KaOn2p{a+VHOEF@X$7`8)E>AXJm0zB2)&06W z+sVud?ezx2RXvX6UWT2YAN{%-T2tZxq^Q3b8A9E{rUG!l_1}~cJ~V0(6^@bL<&r5> z_<}=saS_Op7ElUUjsFGiA2Yy=&A^YGGIR;5CnbFP{H zN4nDg!$D9>G{OC`r5GFZgzDj+xzhjOAUM53?uh#`c1Mx9EVsLYG_L)pZYlno4#FDQ zbw=5NC=1EIjs(<*i46?VmMDKDk7Geje)de%-Yo{=BrPxoV^s+r@Voof3~H#@&B;* z-cd~lYMO9*fj~l!2oidcE+E~6l28JM-a9BrucCk?p(DMC3QAQ(KvYmv)PR79G?gZT z1Sz7T2BoP$mOFQ5XYTjinYnwu+4Jr0*>mzgIl%9If6wzi1)_eIKOt)WCDDUo=By4p za3}=ME;mN;%0OsiAosb}i{{32BQk zCzax7?aQ_TIfn%D98#om7D6@}MSm&$XDY?u3&a5xdbWVN#C9+P@<*`yi7YhT0OR$E z&*rhN;JMNM7rxcwh!?*(jQN970+OKM>trpbatG%?J}p-Z>9R0@1L@?k(J9)v@xhy+ zN?q3HRVLURJr(&w&0(isx;^+Hcwgcd0Op^dI4uYP6U)=Jil}ZAx<@6k&d)R;h2tMA z{IAHDY5nJ!63fjqDTL z030yk8WSwG$AbQvp5wPd-A z9$iyC`^CynrQKd-Td*CUy6Z8K?mXn*lA+(@P4$4pSHgF1<$QhIocYDAtBH->kHf1- zmF?JBawwpeD(#@cE+i~+&Do0m{tysx6uX4WUpu0zI^+2a3-B(+i}7-$z{L2+QE;b` zKd31CgsmQ4z_Z&oRe^%3LSW6nXTsiin!^5RC$!-nWNM{smf;rjNpZbKGbgXhXkALPnIoZxnZ z$sYsSdib9zh=CeZ2A|CKt?JQv?ikPY!+Xy-9*2bc+>7=7^-&TC{8xD&9uNd#0Xabc z1RKCciu}i?5>h_)Z(pPWj5w74juc~{NqoO*@h?&gD;=L3Z?02nEh;Vk23H}n=>OEM zT-HDQ-@BDxhba8tZsq?0yT||RZso&QfA1(L`d&r_uipND;i^ADGVE0K>~@7DUXJ_B z{~5UIAFW#ak|vebB_WjqBqJXE$bX~7X|$iI6u3pmfyoVaA9Fj8gLK&mDdyvI74`2i zB&aqPD(v;b9)?7!)F>oMvLc2dvACQ6{xq0fu{D>>r}V#?yzi<}mmqcRxDIY<6uuJ^ySTK{x?4RhlBXDJ57L9Mfzn`_@P-`eUfw&FvKJ|}lre7`Dg;G*$MdZwz( zwZl>p<>^GSwz9lYXt1G_?}P3WJuWmqDc3}V27>p#Pwz8XpU%=f`fF2KXQ(4D#%w&S zh_1%#PZeC5Yj)q}GvqJ<$xpc+Ldrl;sXcU%o+LqaMMdxZOHPc=G=3-W+P`J}r2rxT zPLRt#2I!urbpP!f`U~s-cjl0*qU=Al}KxQ}s8Mum1s{gDWurXS!}oqsocC--PCERenWpA1!yx;M@KZg))qNA-we9VD9nTWq{JIkzin3Qu6Mw8(C zx?m!2{SVvDNOynL=6&D+{ymxTKOzbHcRSu)r`6-GF{gB-95da1-zoUAy>#NNJb84r z_HQL&|79KTVVQE@3Nt9~>#2%JUme|*Up%hN;GzCW>c^t_?K!x@X0v3cblIs$6Auvzrg3^?6+3%ov4nu_51t|o4 zr(o9}S2f?Kz<`g7zVrq^9N)cu(WjuilCV@c#TKDFdHQ0wORbb;i&@c&l!v%@GWO`@)zh4?pd*B9)AlW^4;&S-Z5FCH7pEQYGwwGF&Z4iGcytq2R3?E? zCSD+=YLYKDx?mNMqw>0@RH8=EEdRhn7L9@RR!O^MwBO6nWnDWqs@`s;Aw17<)Ci}z z(sq~L_~l}VE9A@`vCX6^@Dkop+;e*ce~9bW3A9OXw^Am;w({0x?$|e=UqLsDM@JQU zgngc#TWyEZX<2)IU23^)T{|HdK@;b<$?u&DMAN;$N<2KdvT6Ho<=+y3g8(Mb^&i~z zX_{9OMa=Ki{mX@4dO-8jhO@{kk#fouRb(e>i!2a+8MqM|V9f4o&O9d+3$_*ILXM#>uagbqCh+ z;vuiZ|6Pmue=Pt04|e@uEk#zTv}7}J(@K0EVmb}T(VBU%KeG2&$pg#%Vdnvh5aQfZ zv*JMz9Hub?kSS?2P2+>dY#rjrRcVlFNsSPD#h6#YmzIp6S3KtLhkW=X$pbi{^=!um z>LSUepWAzO^_P^>F}UvNiGPuQ4^Z^#y)SbGd_1?K>ur~Lt);_M?i?-c{vCWxQO};`eokv8p#PfS!d#&-lO^aW#EUt75 zIOry!)_?e}P*S7KNR+s7sN-Qw{mF-h5w63$H(H=-^x*wnpATi+GyS)06z~3K@C14N zW1E4L=K%Te%j{n_!#|&}j~h5q8hUc;RaetsOSwY+Nm_~FKvsk^~*|5vzs@AK!It$l2TFHLO|r@l0IEH{2> z>D>JMe*_A&v-IwQ|NEQaO(+dwch2K)%%*+|U+W;L|7al%%q|KIWAC z0BBkiy1V(;D?ce8SordY*gs$Shp(&Z6a?H=VehO@=AYwO;5_hj_n%N;7he&?7Vy_A zKY!Psv+M1Uzo0;UuOH{e7>K`YhL9^zI^-VeFDS5Q7B)|MgxL=t!HUf1>tZ{3Yg*%! z_syo27oH0AvEqE5QMtpljd`e68CaGNnw^2@6#ku=_TUfTaFZIBOq4N(&?E{pE(dWk z2?r;P{XY)Y(e&C{}5s`$SSXT z7>If*;rQ9j8h%g4+$ScI{g|JEN9|>jxOflfqbPv9nVY^`WApi1sL=}Z3V>1WgZ^2i zH)0M2qF7UTRC4@!?V*4)OmC2$M;SL*FCtAb5l_K(|Ex%E`oK5yX$Y;#SBFa@^14pz zm0ubL0YabSGctgx>mamW$`wwvR!VY{54F6u^7z`vG|zQHsbF)%H)REAgC{rm5o<+# zkpfj=&KK@sAW(!HX%hG&s!H*iq8bI7?+66nEm?A#J*7kFj=dFG#~|BXB)1wo6MMr* z!3n<1e)i)wOknk<*Q~C~pt)cHdp(1)rZD+*?-l3{D?@Yh)Qf@7$_3u`a>iG)UDe83 z+W$1w9Nz8+xv)3CmFxXD@wMZ#!=gd;p*?RXtTXODKTfhrQJ{-Zi+mUw{2S2WOJoegsDWln;06POcEO^04L9 zb1!u)cfLA7@LYjhjC6A`<#q;3WB)L?mh(uui2AFZI;nfu6kvJ!K~liyQ%_-*oGgu~ zMX8VR$G7sWGqs))*5$)u>)sL7@7}bFxhk?#%`FzVex5g;t}}K!4Y^-lVZ`_h_XYUB z>{3H|VvGfJU33Lk@u;ncMsyDTp~5)^!jomh3(z;U0sR7F&FDpbt&XC;Z#j*aGH2m` zhL_JV*{$KIxaDkTnu0UGdX%oN#GX1bwS zH`U{QifHP%Fj2L;_V`H})(nX9RP7e|$>AjW+ne+3H9W@Q%UJRlVBGRbL!liDqL%Z3 zCe}bh8~w>8|6$&99TE-qQ>5txIwFLj9$a$r&apkU9KlH*hYH0(D1G1o#zvqHWZqD% zXQ9?6kHqVLlBS?9Ik4C9{K&oixZ)B*o8d|W>8Usjxy|f2lnXyBA+)g08}~RoZo#-~ z*^sbZE26c0A2+yW6Wb~QOY94l#eRErwke@!_AA8LpfOjr!nTUHUsA(9>vi}{Ef*g< zLg~rjVehvuY%xpQfd&QQP?yUs*@jcS_YFQr2r?a>74Sdn+Xd!nNxcRIm@SaH)G~@U zC1w>f(vRO14Vm#6yQp@BA)jJN3Iy>ECI!eGwhhxkF>uTD0xCatn%jTf=u>3@UZ9C5 z+R)dg9Lu_QY*8&YC{gc_Z$TwwI)e4=l%FsInkf9|$xmzY*wql_5JmvOOVEtbGly+e zS=!A#nfR=>pLrjl%|?X+?u#DBMmh(fa!L0j^Ko;!ZEqE5nz;M*7CSKGrE0E-eme7q z^7A82h4CsqQTK`4`U(c$U!Am9pF{}|4L;bMWxlUPEnZQ}y}Fi@YrF=o9DU3dZePpw zASdFe(jY z2smLF;k=~kopK@Elcjl_a+*pa8M4flDCUa-X8`b%>l>OG?WlS`AV3ZsW;rhX)r@}IDfwU1agi+t`V$clO79esf>Y7Z9+Aprs9j@!`z!X04A z38&d5s`n@%iWSkUn-aYR9;AHbR0Uj|C2 zz&xEI;7;c9EVF(*$=fd+RnHC90>Vhng>UQw@Nq{^rjFnvF07zisM>d=fJO{T&`Hw8 zCA2pntIQAZvLD3X3Pv5}s$Dj`ibWH>ftpl!JOCWu3lc1Vgf3>BYViEHm45HpxzKSB z))ev@+?F{CH(WQxM!1&<>74wC9+3mRj7T-C@*33u!l+JyI4DF;4@=1m+CZOP&}6HI z78%EDUb7KoMb>YdCOD(F3T$@Lvx`1b`y-0`*#ItnSGE3}a*7HkC*awjlyrpBa#@DC|v0=8CX=6b4Gh|w#+HD6Tnni z``HcP^IneN0HCO5oU^It8I9c8{lEX!elC$ICqD0xk`B-OMUIY-<`4QJqL>bXOt9!h zOZkXXp;UNoD<`8JCNB%*B$jukoYSs3R|PIusdF0D^zp4qv^k)o#-Q*a94A(yB&hJ% z3t`hZ1S1gmG~n{q6!Fn78+0Hb_2VTk4Pb0N_gxoLXZhmmnU}-joJU$=@v3m)&*=VE z{W}gdxN*di6g(DT`zNEQc*02#00T{U@OdXp@CQk$<86YKG*A<&p6hq%pOnY!0px)~g~ICzwI z2_RcPCOdQHp~GH>Tc(cb)Mscg4)Vjb*&)XHQR^vwiqjGfbiI!2!cX+CpB|4k>5XL< z?p-ur0Tco#z!oWB(`Uo!-G67T-;+XY61Y1KSGz)va0v|Bl-ige8v%gQJYz_JG4^L8^)b`C%N^g%~KEaCx6@V znWH>^p&R3XQB)6ZAC|J62=w=adlqeqD_;Wgo z4WNf?)Tx+-IbZ_#`ay~SFhnCjUiAh^KD+E_Q$ag8P&WHCCzrzw8~%t^P6u=l@sv}x zGn)$9=BsJiFb(6pc#z*Y3Gb)p4b0EhH!o;0yq~t<8tM@Y1Ox-}Il%m_P__=A~p32cV2wUHznDT63Eoml{)3H6cw?t2zh@ zfUQD9Q*$H^xPGR3s0K&0zspi&ovCg|=?jpa{yHy%YUcaJWeDij>3ry$dd!ePdCc_e zjdiyCgMR(89U=#O4&`!AII30^(aO)i)WBWWX_{bzU24Dkv0d$J2U$O^V%XU!L4EVD_p1e- zbBHmr^&;%Vq*3>T(25SG4%Al@W@UF)-R4ek->JYnX^Wa>MZ2-e#n`>3lfQbCTBEt{ z!;akt&k2rvS_LC3L9Kx`4z2A!6vGqwhd;Q-ExKcb0w7Haz}DhnHlA;q!oAdL>e<={ zo@JdQ)$VR9U|)OBNU9F^w`1>|fOLt3ct!Xp!g430w& zUo|;{D=GW=Wc%ytw$W}lOwH4W@LIT z6wnzQdIEi_qsYptGhzVPrO->IQizt;nMik7CFRD`B#ak(W9<7N`BS59ry0h6zhQ|2 zc4*Uw06m^-rWQV)u;51?GK;>0~mAjaW5quuOmg|T+Elp z1n!B+gUa*Y2Q5I>)){TDno^y7RxM_~&f1?WZ|1A1)7`3chM7bSRQ2oe|z~G=C>73!h1~pHy&%Q{XP!A6IY;h))>U&?`=a@T`-@Un-n0z-ApaL0wN-_#RbI6fQjD>3o-1D=C~hav*Od4m^+f#L6j%zt!nA`0(PAQEqb$_%L5@g0Pnw=d^#il6HRX9j zezqh402r{|Z_#KToz9Z~&i~-e7|$Fp^4*#m`#Ue6*bhVXADMSC*!+8G?|G!%x};zI zfQheD5_k=c^Bf81MI`ea8o^+Le;TO&kh=9#Wu%vs&m(=4SJn&jsF>G*y=rxw_ujL0 zpYI>AxDA5DhSbx~e)&K2!g=sd*X7nW!*1zCt8DQUZV=fUXRfy5uVPN-Z=!B(e&=31 z8@>^mzaD#aJt2SV>6`5gG>;RTCkwq<%pN|4-q0`l156Ao;_2EC{ zzi;V#t=G=2=g<8%zWU(y)!%+zU5CVe*N!xw_u4ehZ*24WNINJ7S1icqJr}r-0;D^_4mDffbd&lh-TMvzqfN)F6eV8Mp!a0@wr$(8_GI*de zZ8#vqGzZHUwm9`_fm9%FnkVJ*deOW@-l4_+?dzrf95pqIDwpVPs|uaiZ;Nl=d^}!7 zNaL1v9b2)fU>7I{Os~sYT~zn7u6G^(?9k-W9wu;L{EJhoPj8;I+r+9%ThMq*z`KcN z*Otho;d-~pmf8Df>OWik`QGgHga$>+ByN85>B$mMX>ps1bY=)i9$VIXdoy6*qUKh> z%-f$QN2__lWIU$VgT`+7WuKUx-Ux1N3>dk__IS4$F!dnz`*QBD&F@bbY3RwR{YIL_ z%&c1*kM*~`KDHGfzwSAEDrWUG)cMyMk_14i6>C0525 zWB(V%yq05R6Mj3?h>4)vn58M!kKAu69Gq%N6tQcIBpniexkkby@JE@wpla9}Nfghl znaK#)|1cN2T*8Z&ySW}=q4@o7)Ju-L>qQ32&oo$;>a%=52X_YS@9Js1lM1)ez7ZX5 zz51c;h>q@0k#E+9Yj1vzp#Si<9mR8MhEmujXO6_Ydd?eLLLo}-ezCDQ+`ev0J~AF- zXRWufZbvZ|h_$!1)ZDPQw{wVfaCD2^aB%i3jdgSlZr^Zpj~b-`nOXx=MwuDwvCacnMn^3VQGpx6 zn{#9$-Y-vw+XSs1#@O;HiritGH^d?reT@XqEb>_RmYq3qDfrs2LQ^Smlk?m})4uZn5lKC$V#0@n*YvAtv?}c zZ?xP>$l8(q=|Irv*)V_)BW0ybhexl(hG^5v&j1T0Ms#yJ)qkf5x31PKZ*DT8Y(z`Ye)*~gUAa;&!^AZ1I{u(ad)NM zmDX-^SnX4+)tw7+4(uD5}qIrE6OUg;UqGhn@UNQRgQdPwEm-3d>pQ) z?lYKiDzf5mny=-}?bk^w;Dcj~KCL@8Tn&y3g?~n&W_Mq`2|EEgOW2jPc^5c#>THqN zE-v`kdUbx1lxFkqQK*E`Kz=NlUb!t*Vexp{v~|K(?1S@j-AfO-7-7aDznLVrIbkPK z$Opa{E?M>O*^}&ZMLakD#G4#K%ul3n`=W1QgAdOr*!Cz0)}M%3^*b-@V$|Ixkcu-Q zE)8T625atKP5UCrn_$~Q9Bb1VJZ$^(_OvNkBB;DSRU(Su{ER+vuV&~g<@EXP_l?0RDa{8FyU#;O!5ohaFWhffTJ)EvWs-JM!jpbNRDN_h-kg|tG$attQ=4F8 zLI&~uuuCJ|9C#C}D0IJ^Gd!u<($n<`fAh!jm1BTOFW1gVA;+&Is#vu%oUfw|W~jrc_j3h^>H^#eyqB5p8~{go54ehh*s9gHlD}#LV({oSPUOX7Q6Z_$ z2c;V1deFkGd`{gzZ==CfVB2W6pyR=JkutIO-f!BfKxT0SH(~ zfxM?S?N*rN+P63eChsYLAGw(SEE5P*1#7cHUkE;*A=G4G4o?sk_A4cT5awF`NJZEI zBm-a;0kbLa{a`pEGVTKR)wx7~Dqr_`e!h0Se$pKPq2RfPy%A4u7<~;snec9QFRnfM z9m+TyKZ(Tryp`e+tft18LNll$-t(vv4aW0a$?-k1o@*ysL9;;}RPc=GC-upM34dQI z0K)_zobe#u0^n$d=X$NI!6%RRfU$)4^xj9)rxc!Uy*%pi6G35sun`F}A3e7oePr)` z6>0@Q_^AL={=gR>F@ZtO{qihr#xf5+0czs)`_1)p8v?AIWj=2J>1yYfT=v~oEbbSG zvIiJUKDU0|Yj;KVDWGK{=|-a1P5@8x?$_kxosVLB-i7R)r8mh%k=y_jV+qU1;K75( zgdk+bX%{>!Lv7N3%5N`3#u6J@qIDI22a`&{ektB*= z*1vl!(}=smNI~O*@dG1H6SR`Q(DM}|Rg|%sh_Sk&@ezGv4YIMOyD?iU#Q12Uv38EJ zPKB{vHqa3!Hluts`0TMV?4@)&{TG*6=1AyVnQ}CbvGe~n2-`p%yLZ3D@-h! zOe{N1$b%+UGbYxnCdYS7C@51K5mQ@;>7`aqWGfZz+{+(m>Xc~eoMY-zVd~ms>egxM zK4|JOW9qqT>a}C)jUxJp5PcPie)>dzGBLn?|I>?ZK(rK4(PqGtO~l|%V#pvdbcPtV zN(|p2MxaQMBBUrqQnWrPhD?feC&h)3PA8J$b4X`qh@7JwoK=130z}db>D($Qd51(r znbAbd=!#}3`evzQvov?J^boU*M6*o)eq={>P)r{?v(qeR&@6YxEN|5;f5)r?6hc#F++K0)eKtP+OcRwS>6t@Xn-Kki&(ai zE!*8K@AdRw(nkhq4BV@*eAs09sMC^X8&KLodeCF}bjOl`8mv=%nb$$;(kJ(j$-U$k z)h1a%?&SU)@{5W=I{!e)isir{d1z+vMH~6~AZZw7^-9F5bA7OwL4HlP8h5v9(y%JV zTTSFxy{#C!5;Igp9m)*sogo90O9p6wA$D?%X+}S3HYW1}*>XN4^R7n!($33e`+ldH z=L>BEAL}gNNg-$KtvMOg`I+I)Rg$1L@YLW-e#~)d58%6q^*UrY&G~qHB5_kOb5W!J z$Knv9VkC9ZYJJ`MkG=I)-N=Qek?o3?UvdV34%XNy8kS-$*gz9zkvIxc!XuzOW9HHRh|Pgx?8hB#>l12k6y-*m1`DX$hMEZ+*Qlh+}*LLqjFBO{1nlT<-uBU!J58b*;TkYpxI*2>O@m2pz_m9!NQ#iE5bz|{(htToq+M2KMl|)Wedi?%X6ELPfzW>D_G0=g; z)Cp;6d!O?CpJJiLg2y({ES6p&BcRFnbStYD;8`2aW>72uE@?I~koYE8X*@I2_Cp;& ztpUzXW=ux)PRcSZ)#|AV4O0q%PKWBL>X{iq@e`$ORNYx2Lw=`-mNx+#~(FqV+9J>rk$D&0k0E zb4}h1MHoDGN{$CP&pI}aPdoWf1w}YNan!5qL$*4GVr@>(;7074TvBq#6~H#vuWUI0hW*8+AMkC8!b0BtMx)zZ1Isopdu6L<{?Zk>o{(K>eE+RYg`0V72v^aK`s zI2VEDW(rNb)C`>9r*JIQWwA@=TXN^KvF>WUUi%8)$qo2ak{d_n1@>vvxxF;s&F0a) zaj(^{UYjL;d(!i{Aue~KQXL|;gslfNJ&;6$36 zfSss73gLa4#S`0(jf{Z!KH-&A>joN<1#-cqyUKw@#wY9o(zFnBnh{VHRWb*L?lv5t zw(YS?200dhWqYX#-f5bd#|@I`ZY|C(lmLPD`A5sP7FGFBr8h?{n05$e*2*dhdQsDKiqf&#i&-Wmbr+@px~j z%ib883XIMJ3brnLmV;0A0I(}5p3cis&Tmid1OzCjk~e5 z&?}C%oK)KW@3FAO|4Ay4p#AaiE+D7ui|>1&gJD2^&q99u+f^>l_B2zDZ9n@>V0iOX zwq}MV)4JMzQ7ynfQXnM?znblkp#!kyTuCE`x_$R>T}w3;1O)6>fa~j5qcABVv%oVf zxQe!Y^=e9X=vT)Qz$XW59=WvfYaziHcXr}x>I^X?j2R?YPL*ZBk0rVGv}9a3WF0s$ zRZ+Pd#R5fCt)6YQb=(0+YKQa~hJGmV))N?zVojCTubyN9RnCR!sD4ZJaFcMD)?s<^ ztfZbdT@C7YuC4$l1K^)40b0G}W7k4@1nCa`G*c)mK=pfjJ3uXSW>Fa&+z$IzL7ag_ zgcX3+bKZu>FR9If@_PY%vBYX}_!$;$zBx$d$)`E~)L$)A`{$O44O@UyM#iuUCySOX z0FE_XJ!wXhEf48&gsV~B1^Gj>t->5tU0tMI!GZ%?4bBAY8@|5C&tVW|&;GQI>~UOm zNTt091LFk-c^yzs$Lt_s;TgmRD+Q0!Z?WCm6HI)JkrfQonhkvp0_4kHwR z6*Rvk>;a6vLT>Z|j5gqocqfp|97@nZ?<-g*0$RVDZg-D*b=OJ10PNThD|DSI+W|kL znh`Gt)d@@$Q32oec)e`MG|Pp@Qs5@&jk67O)0w_@fP8CEQ#&A7;AFDA7@{Gxs3S0 zRGo&jD5VU$i8pJoGy2}~ju@tt>_YhQa5eZ?)`c-w%}ru|TrwubxO7U|aNXqARIip} zas%{6Jry*UhV=dkdhe7={+Z|ebI!v)v;m|t1$uTgQKbNs-2C(8x@~L$eWwy2$Z`nv z2J#e8gFO7J+Nq=W-UwHD;hXKEr-06(F?z8lgy$Axljz;YcDlFd;m%GD>%e*em|#3p zcq*P<-8Mm*dlT;*`fZnrrMO->;hyK=iyQJnRyhz$cY-=HY#TDOS@d!4C{#RX|4Eb8 zo)hhl^T+Sbg~SErjhztqkgZDZ*YrlRCZjqWIOF}jLhX21-vZ>!S8qA%tC}p8g0p+U z`;DDWz4E@U6%UUd?tqApv9V0{{AReyNkZAkFEI?ogtDVbC>Ey zQ?CUgjU}(t9X53a*bQHOD68pmtrb^)<*9=CTi1Bd{Te7Izu|8_KSIy3iY41o5k2-O z9n;D_GXzKeoY%a4`ue9qbO zwys=pxTvc)w&*Dkk>$hS3mj*4K?1bfO3@he`Fx!hw@nF3UG-ET=eKUR4 z5|yU8hoBQY_?j=&LVjgIX5p4!ZHs-hvV1NkOSfxVS+4w&B$e<^2~66IB{bp(_HkEUnGfT(-VMh9IbaW`s|yb@a9R zL34aTf-KE01rn|%Bu(Y!zGTFu%{#`MzqhUige5^puOSzE7oNF7e5lV&rlnqeriXP& zU!XS}YP(@3s3=XQ3)(vlXFNDmHdShVn+i_M8r>guriQZgiGOLWf#D8tZ5JQoFKVDmp+NjQZ~?G|$R+ zvgo{lTdi?3N;@9pCFjJoUhZ?oHyXq4X;5sA+2Xz**|PGkgV2}RzkQ9n>-1=}ekpPh zl!$2H?$dcDT@6*buVBkzP*!vB=-wdm?lHG`8IoUVWZMt3mzwHA=I=NlzQ?g4UqWTE zl+k$u$W2S2^=FY^?nT^cZ&}4R-#Z1KQ#E7x>xDI1jz;OZFazAo$t4bf((|dUx3`pp z%B-|YPhTb0tDc#I8*0muuHq_15;ofLT*dto-cwb#7Y5g zghyL-!G|2xN+9q^NT5l?*Y55?4Y%yWmI@o%u}s132XfHpZR3zHqb|&6CJr;m>(q0x z^$o-%GXPlo1%pO39Q9^q@?d(%Y=1zTl*9=ncJ96x{0^o82n*AWup z9Qr|&kA7HCwVi0D8cXcb7jBsAI?8&yimpl}aM?01=9*JgW=3HMLSSxHe|NCdk&A_j z5lVR)IsT`zT{=z$+&vu0ks{Cpgk6xV4%0cD4h`y8ruyR+BWn+-QnOxSYxo)+l&otG z&seB3!TPHo4oqwlb!2P{kum+A!Scx2VMl%G=1`pVk5M66%7IYkHTA7+Zv+7$0K)7;$KQ_~_#bvw3T5M2#X8WNBlLM}QQbYouO0w1^pS8sQu`vZrpiJ#;GM zxGj7itKNHAPcGmMOr18Q@QZEZqWQEDhkJ?`N!BN6T!~nzdOkv`wyFwj7XUC+zwu_N z?$D10xQV!M{@Kwru?_hH=iJBc#^)^>vZ)_69vabpBP7#LG`Va70EH>{@`Arb-Jm1h zsAJ|A53p51MvA!Rt~$KvmfT0WYAYC?)#VZLi32vltg4tXKJ>ZcW#U7GFdc(YQq=aE zE|VhCcNQu%&HMs7cv6z%Wn_3_0}Vd#u+ff*Rd~1r%KCA+zU0h6T^jwkK@ki6;@1Mw z_24P5nbLkVe=|R_y~2XLX=YSf7hQGR!7Di(h!x=lz=TTow1I6bP?hEH&qx>N|N2-nLc%4--|$568Ya^Le{b{mUn`y!(p&+v2Yp ze*jcjh;(yDQbTm}TJ^{H=pr2+lMV}`VT~(q^euQ5%@wnYVxiYBtTEHgo`|Cz1%^18 zOR|Y?KeQ%;)~O>00=1*-ynKH7*&dCwx*msP9y}lplPF-A&IJgzv7i*_6Wr8R<7L7H95z6IF_QMs_itVx=Pb1O!YM@P<+nI z5eXxIKcvYELl7XMYQE*bab@`laTI6wb@yXD%R@2*^Bmx(sS)!v1vhLZzny{#_VO#W z%R_3~y#)|4z*{$Z#F5c#lvK`svJrqi$Cj8I*gnmvrZbQm8L}S-iSt0n1QHK)682+=G~C{% zo$~ajK9zDy2Z2N^XO`xXRM`q*+FjxytMXp+>?RL8lDZguL9PD4?I{d6vi7 zI-YbOJx4;^eI-ea4OGCsvVBU3-nNrq3HH5vjZvxa=R9+U_pthrYr4@1@AlR=V?n~A zJHAXEE%7a<(3D8C2bSFRqhB6{Ub5M>KP3^s&?BI{!blm8|*$H(R{^y16Eizlr3+p|XrSI4h%nRI{nodgdVI`H>qA0_D%ndSq_xmR;UW z0rqp=9<)ksdk%>gp8DiP@F#i=^%B#@Tt`ier+B>#gw3(|PtIVg_}6JuJ%Xz_$rJ#Y zYO}@gaT865~G8ZanjD!y~4Bz=^mzW2W)j76GFT@>FAQ@;WvpCg$ z@g(n6=#au}#{ER&nhpq78__yLGNB6ACso}adlp{pt6*VsmYl-V&~vN}=+5l&8Gk0+ zHVn7#a&h+BrKLZzFV%Gyz$;y})RsW*iDlfdDe1x!$5TWiEK%#1Fk^+_Z!<)DUsPM> zt1$@pF`Iek#lm@(fO<)P(qPwc8~4+;T8lBfvH!4^FNqL>bZaMl*Y3%X%kJDZKIqIF z-(##p_uUSefUk=GY2$|3ci9?PCUjW7Qq1TMg>w!v<<&7E2e_{HP%WR-@#~lw3fE$_ zGY?CdIo!w~xsV9fBL@BBOrFxqk@;sAO?e?{s;gAHrY?sZ3rWxC#EV3~U)EL>YOB2S z=^G}sB`HEujsk%_kog)+!#IsObzZSIw3JVxB>=36a{Eyyae~wl^jy;SGKot{8~5yp z;`oUvlce%|E9{HBexfxwMHT`akcypIZKp= zlN6z5D@(R7x+opvkTVyL^`y{x!0NaZg;Hn2>!stVaHiC(aJkH>#^npl8k+t{^n|fq z;%rP9X@l0Io|p+ODb>Y+)lB@5NI@Uib00b+KF}EdP$uMDCHDgt@8zB={R^>)R`Do_ z9)6SHR7d?baLT6n`(hwh3TQbq> zxK3<3amF>{=+``}^6Y#(gxv3-d8NW)@Vd`g&TM;Lp9V<2?fC@l{PUglg@){$13L4r z16Mxyz+pY)FM;qLLJcXXKtbRdwjO-TDY-$ANT(Rq zgAIDK<^6dPVE4sT{Z)Eege8GW3OA95#uL;HPbd&gY*I;jt=(zrmeu>2aASCz7$di} zLVb!K!Cx8=EIjgUxfnlkR5xGCyVh7)piDQ7pm?yn!p$l@LlsT4IDktTdfsKQk$RmUgxQjW3z% zM9dfZzT&T}j#$3KVf6UabE47GEUZgguRMYZ8mneufZ85%XU z{PLk2g>moP)Ps7)7CYF zzh8;+6?8xFfzeWgOEd z958D}k8+O&4)Ha02}{u|R71I(;hg#e-lisS7k)q&ga3sWSn)fvX*&?bdAvq(;)#pf zI8~^LhV5U4^c;He!E7iI1`sJ6nE*@)FVJ{;X~$3rK<5R)O4n>x`5k#-_MlX)w;s>N{8$6%cZY~TYK^5p##H7I10AC?^CXB(#o&L zA|lALQz z#!hN*39pm_dvHfwFfhIuORM2v8BooGD z=?*PD_e;sgKFkESl&rea9aWh!2-V5zVN(^tn*aU&lfw|MO?8>A>7XfD^XyVT523SLM~@no`q-g=9-%CCO0vMD!WFVm$hJ7SI?vo-57 zHEXkVz`zcO!C5qd=A!Z~Zx}8|^E4k|166Y?r*bTx|FbJ+?Gd+gDu*vSoANuSb0@>z zY8a_Yl-WK9v_KCuK^L?^AN0eN#Ft&Mw}FwEF2*<`v_u~%`AF;ePBcbmv_@~VKp!IP z{d1FYv`CLMNtZMyzsL>&#d+cLpM`SlPWk*cNf^ubN|F&im^fUuTlTNlBZ?;H(wa2zJMw@m# z`?8Ue_C|yDI-|B||8i`9F$39Ho<%k(Gg)jKv~5cUWCQe#MKVd#_B!+AJP&fL0*PX$ znsO6#nLRgsxwZ)lw_^kMQCq|k*H4 zcWl62_vpd^7{7K`Pxuq2T&cz<-j4TnryK_}HDI{J%O;9#Z@7p<*oqIfhO>BPc}6@m z6I)Jr+ISCaceDlIL`c!H7qmf%hy)9W|3Mq1qfN}gJv@Vj!@xA$gK?tvLc~B3ZNX3+ zcz3Axi>Kn(;EbBWI6+$kvbm~HlxLRL^zmwhFu-+2#&2~iIEY8m3SIcZfU6gHfzndu zF6@$uxA+zsHlMG^wWLOl+c?( zC)t5AW?qdmpQp7ED4GJ_M-_mmRcq1EQwxi`)2COig-AJC<*H zdv_{gZDX1BQqd?}w44Rvbg6Jm|5m>`jBAUFm4E>&X8U^Q*%lgmzN;i-1}Gu!I(Luv zu`dL8Fx#FlUJabZ8dQ0-_Zy2h{GLDdz2Ex>;K4I^!G%A}1mJ`@6pK|)A#jJ}QMCoG zZx30^7(L##)3>D^cZ^NpNZj*#|GR#* z#7HFc5!?1Ld*0Rfuyy}vj;yzi@Lr)Ci$RVu$gB4DXd7Et^ND`Lw+=)vk)&AIhGYI` zyW0jH$d-FwbBQ|5cv-Sg^46Zl%;xA1i>o)>OB!Uv>}ER zY(-31%0jGOwipZ!yx7x%fm;5MNf64Hq68M^{D}deHem{}LkSw(3z_7`6fpi#Fw0h~ zz|syYCZqrrh6te}DPnm0^MgbdL?tHt06>gCbu5IQy?PvZa^=gJUv0YUnG_3qKll*# z?1_cA7JMKR=0id+f4e^P(N^voGB9h~idci}o4Ieo!2GQ8Oj|;2&zAZ4fPomDFhb}9 zFogL=oo{;Cf<4eIFoG3p$|>g=Jf!jpgB_4TriOoz0p}ZN1Yw~kk}_Ij8d*U4Y$aBv zX=A#7Sdilx|8UCDMwTZg`p1GTQUs|Y3sCH&DGTt>Mh+NQ(MBCXVEQQq3}Cp<2qXRh zk(eqlz(9%v!GUIu6)BJcnp$EA$(3ouV8NAdZmh)!_CR6*4`|R6rV0zD@u!mk`3gyl z1JOffjrK&^Wg01B*u_n2T=Ahn47?C@8ayzN!y}MN^vNWYmTGF6f5e!8xS49Y>6?Fc z0@5eiGVDY`Ze-a32Bef)sj2te#8Jyl5lSdSWQf^8if78_yb7^@;?0E;%c{;9_YCC|$b($CuZKn!9UEv4KblaNIlb10Mp$Wov|s-0z~5k#8_n4EF5Ps|hgmrxc80YQrCMUcpB<@rslJ(GPZ`AE@cB;6(Lrjz6YtO@vxRJ&BSQOpKrm|#)qLMFd}L} z;-L%bf1V+cwrl?Z18ONK*bA*B?(wG#3qGg;nym-^M;i$|3Qy3Cx~GFA0$G z=Q^-!889;oYFJzlq!#xlo+pBf8f;cVUqiSZF!%r*e_V_@A>p#y3xgP*>3d**+E|89 z|353D&g`tgmgS%iVA#8pRFn6pZsG+A{&itG`Y0r}1ZpWIOWZ@5Mgd}isft?sq zl|XgmsbeXqU;;IFFt_dPAMub!+=Ky=$dOPOY}-y1R!G8^d|(annA-NHQ3Dy0bDWE_FWnlpeh$kNL=tL5pu!fwdqXt<4<2^d@gd|i!7<8$E|52Wh zIe*CE2~{9oC(2PRg5b^{t}sFkl7O{?u?1UuvxXR+@B};{Wg09%h!N^>k9ugs3x}-5 z)C6%j6Ro5rKtsp`(x?s2EhB}3kU|o~aJ+kX;R8bZ2l@6w5HLPtB@#$vJnm5oFNnbi zJxN^+_EwK+c%hH`D@Q!EQ3YaJgDy`ajoa`AQ%sNnc26 z@r@+tj8pgS|Tji53rKrLaynu|Ut-+S9ks~YX|51q8cbjd15=X`p71@tWu$HYC#dB{Zi1B5ZypTTP2 z13B!?6?VjADU7g#PC_j^B0wk5aQaXm?7#~-f#VLI07i9)K?+X*i5Y2IMmf$!9lDFl zI8!JD!L&pSc64J(C}05qcwt~xUD8e*CxIBW#EK~Zz!Mht1ORB~S_3JPc%Cr?xqm!)Nx`>$t5%a2F0~s5*?$8vwB4AL^*vKlYT$yGQ^JQ~Rj-I+ebc)}Wd0 zJ5~HzYN81wxgKJ;%RhWjj;RIL0$!lnWyB(a0DXgFK8Xtjtn|i+WO!%;mWTnousAJC z_K`-js8hjW5GDI@KY$k%Kmf)R3n&#*UzoKtnC# zIKRDdX?CozfCkzgR&Zku#o07B{$WK75Uw6QZi@ugpbb}O|0a>kkVs!runTPjSG0z% z4pQ0xfRGqK3e(_FhJ&My6bRZlwUu_x9s-Vkc)^Zb6$X&oU=Sh5BOZ0Lz|-y^^U8mGwH+boa1E02ie~T~5%&h11t}m-lFLCB zQ2rwcTC2iJEVe@Fc*sLk@k&I*d=DuAAae#D89@@fV1GaZgI=+UX1JN>gj3;79}I>Ew~gg8#5LDkBJ9#B?!SQ2AT4k#Udjl4UMkCEim!Aq<3Ii zPB@TSvSh0?Staa_iBa5paf4*gnP zauW+AVTD>AxMl(bP0&#OTp|F#EZ@DXsBM8Y)F~3aHPaN3?5-`Nb=(g2mlC-%Mc)=c z8X%hz?P)(>IpYqfSZ30Oo!c=ua3nZmHuZrBMwcr?@d06(CVcqzG;#y8As3Qd;Fh(dMRQAim|1_t2a$>(eL#_7bB`n~0b|L8q1Sewb zCpd6AcEAXdAPEu)>Z}JT)@DT>WN)ef0G2P1%11L6>m#b->qIK-OoZ%E#q5A61a;y` ze&XjU!y$ggd77dn$mV(2Op&bR0pX@vLT>?e!g0h5kamX!_D8(tuL>3i+kk{1nhiY6 zPY9BrN|L|_a3>ywU;=yqDat_*p1>xU0)ltuz7s1qno24e__|BeU9K%xqIVVtxEYbZ_Qiv>kWflM7zCt2`@W3oOL^)UjB_@!WGVuaq zA_M!316j_d2Baw>FbYfX<}}9$RA+x=js+p`A1KiasiR}`Ybl(tV=Uka7^NMy#~+>{ z>WFPXG{Xmq5CBXiBT9qP2pO?awuR6Hc=g)XYnX!c;t@m(#Zl^;vQV7s9FhZ@&t#X z$REH#7@(!kFhD$@?qLX!^e7Q?|2%?ou0;aeuzMmAk-#Ayw&x#Wh$+e-Z8V2aqGUxz z2zGEpM><9p>qm=(;~DzEhoUL}e9`%W5g0+DCDj22U_c)7g91jU3Mk+tw%~B?jqpeU z1EkS$@L+N30UWf!X7=a&*wG)lu~n?%1GWHOmgpW-1QUIN0x%^VdqR$+0Cwzx|DND* zDk?Yv&~enPHb_r8>ml4kVy#5wI7Ff)8Uf^%X+_rTnM_UtOAW$2kRh*74i1MYUTpeK zupE+rBK^T4Lk|L70wYFC#y-F!Z!k#OU|>2*8>ZnvxDN=G=Od%!-yrKH{ec_iM~*P% zBeauKpfD$&X9}t8Q)JTz|AI0`M&bdDaw$?JDJx<&eJ7|0>K)?|EouM_VPZvC3L_|B zCXgX5=;%r4upOsEET4cZPof;4VH%c{NtI1M3D5`{0h+dE8hk5CE)Dh|;{hiTBanAH7r^7ctsWn!v zxKh9fVg_pX(Og!70>sogEuh(IVE>|qI^_{|Y(n(z$aerBjSw>-ylf7}GX{D`0z)p% z?5_oof$^B^D+KlSrJ`?D?n6Ix)RK!Sj5bv1S_AAp)Gv8bMC%iuCL_63) zrw9;ByK*p&0zg+WX}0Avj%E{o=wDW77kofd{vlJ(Z{NJD2I_Q$=F8j`LIkX2PyAsH zwn9&FWg}pq3K{_^)IkWcqEHdlYj<(6@W5=a?E~;eJ^rB=lJ8Sn15*J&4uT8@a5PHH z4^+8P{aV8>|4^zS{(*UrLH;PtVErT}D(LdwF)ZciI&rmaT9qaUlyPjS0C^6vN<-cN zpff8lk)8nqnbkcNP<0kES|2G!s`Wl`4qGeIT0nwZ{lXO*xA{tgtB?-wfNe_U>|2e$e80=Fx(fZlL61U`EO-B%pOKY#HB3Bvx-K+9DzjCrYT#AKGDCKHw2o zb!I2=HS5kFwg4u~O#%w8D+wZV07-5mw>m`wBsc z7LWn=*iM>avo(CJKKEyOI(9LJ7EC&p&iALN08ow(3` zuvY%Ts(#|%j7~CI0_q&+1E4ByyiQ-yCkme@rQYWzPNK^2_Iu)C^PJcuz6|@IM$1CN zj-I9*&IFX4vU_bbV)v~oRl-Yqp>-LyZ5D4}&??gQ4=L)%H?_=UF>e}NfdKI!Gq)rz z{|o>Lx1``s12RZ)fAGKnb|(e)WEn)jEJQ-RRK+*Ea)E1LD{?~&cGz)fq!DbRnXhvP zdJP$rqWLUfbo@5h_NzybK^B%KnkMumFyO=Dp~uYEgrBc1SU{=N$oDWHdg4Jpz2e@Q zM*d!u6{c%odSM4xAO1)O>x^s0?a0u>2prXd|)@qpdA(mkJpu z>K^1F9trR$u5ImBADy>lW5Fcbf*sJ@{JU?2vzAf~zpr_8zrVqgaZVPG81 z0vZ8o1YsapzzcdOj3WY!DrW}*X_`NvCA8rQYJdeMV0QXiw)N+I`t2VCco&Kq2HJp{ z2+v5MrV+@31*#w?P*Mbh`shd_A*S`k49xx8MhlCUFhB?fCU`5HF3bUq zXQCX6_v?BGXh3r8fcwI3!pla=DiUG~04m6kq8{V`DO8U;tRW$EXmVHqG`1JApfc+S zydmX>BJL<5P*P?=)ap)2IMSvb)__PK@OWTH9g^q6&F!(&;XI&*8{(vX7Bg*GTg$h>NcK-J7l38iYgwa;cZW4ndH;IShG|BS_iq$GO^vmKrV_}Yd$ z8vdc9Wo2Z%DGI)EY&CA~)^n4f;X&~k-W?hIuPMMn5V+ypr(q4$Q7_Dcn&jadSOJG4 zy$awX9>5hcRss)JX6O$m%nIH+%YhYkfJjgOANs)Ev#Jy@KyMrkNREV>aMKlZivWe7 z7W!SSUj(1zUI>|@m{MEqvt!J6pg1S~P_>R>G}%Z{RS9eyt~H z4Kyxng9XMfkk-z8jwAkHz^wp-Tbwf{op2>s1um|oDNcg40OFs(ev5=t1 zlOJFHi|E5g3JM=~k`xJYAjXdkB@R6qlW9qU|3-FX*>dE}f?6M5{fVK*L<KC^!9v0sf94j(ZP@Z+3bHU1euy|SYs4KwVi+V?lES|oMubkF z%Cce#7zO`|I9wFQ$$=kY2ob6|G1ih1Pd^oTmf>2|7Cq9mz`_UHe{MmpwEDBO5g`;K zcC7h<^Vj9fn>&Br+-vlt0#&oD?yxEb3?v(l&L|UjN9x)uVwS!Ag+292=vPgqcjlg+d1z)8YaruKJVx9H9dLV^q*QET z>QpFmb#YrsUN^7ob;=1NZ*1Q_e8i?_^ z>2t;EifppVF3W7QSJCvuF07PMkh9jN|HZ%OmM+EnQAbt=CZkO!tx5Xu)`8hOmW3$ zs%vq+4QI@8#~yzSa>ybxEa1r3Vr+8CDzD6P%b=F*a?CQ%OmodP-;8t4I`7PL&pyix zv(6y?4D!Zi4!tbWSAoPdb%QDm9n=FGP4s&~=PWh00%l!xp$if^Xt(&@yBc`tajosu z8dL4H+66jH_L*!?{Pmk*o9%YjZL(-Lj$%vw^rC#<>TSW>&WiTk1Q$;D+zB(j_l1M& z{qp2$TCOYRf^Uv?fOSueIjWzx|EPGt58B&##gJdlB4DLc4)*A*%bx4%cyoSwewX{cqQpZ6TpB3Q=*Xx7=TOT`G*7!C_n1m4|v=oVD7NDwZJhhf!%W*`%VYB`MFSO z{M%jccoo1ChVXsAgP#M(2E)3sa7CLNpb3Q-x*!q{g+naeb(c=*Q?0)&Ab zbfObGAc+Nx&!%Sj3|kB`iu_}b@8TxoKeX{+WTvx&`ot>GW)@PQmj0*x9Gr4#=62Qucc z20?tIol-a>H2xuoUOpt3X#LV^Xtq7i}i*|H5Jpf3#r&7ok8B{^5@*7@!KRw37tYL9HkN zaVJ%WTJ)E)(3!w|$y5cFf(dMaoaUqnjRfJJX{Zx}1%bglaVlES=9Hgx zt!#+GnbBiz)ro{XswZRHI&jJsdbWe6M{l^o-A0nD2aO>|DLPQ$R?~Zwl_F#T8`yz; zfCckh15uJt|Brf1in{*5gIz^{fpoHhq^UilPW$;!UrKSeOvLLS6HDBz&QOuK<)3cP z=Ql^T7n?;4A$+fTx91(yfys?(L2G;0-6?mzhriOqie8o%Bj@F%dg;qv_yY31D%Rv6UAtcfyGNt_C2)(2o8V#g7PbO5q;ZKWS^px~ zs@>eAB^fDL#tv*SPpC&cgkS;D3M(b2{Vpy2(Fqaz1GJw3Zw*44-i@4C!7>hTj|(eb zN(wZ_^0O>MYkXwL+W5aWHggo={9~pP^uP{QFlB|jU?i8hY)>^DcxkkT8t`QyLm8tm z07?@d|IjJD;x#V@d=QVirUW!7o^6AV%i_frPO(LPFkJ2{=+6Zf$E=R56L~!9{vOV^ zsgCoV8H;97_i?z!%(0%|n>VqwN)G5!w`eQGTI0Lh^=W1 z6h=_5Lu_&p3}9OCS;cKGGckQ_XJFe_$Bl*cuUDOCPj@w;Fh*{Sf!xA%i@T5hC9>(V zxa@v{;f$r3r#7Czoe#{Rr4f-pBm7YhIgA7YVxUu9Ki~sx)T0q{Y`3@5oa-Hm+};}% zGL7FoYm{HssdeV{a^r1kb2qZju_h$AL9Xw8gL=K~0CXT&S*qxda~_S za~mdk%`N9S&3CR*(`B};SZ;PpDDVViwDMKzU~|15_w zfF2PhiNQg;1~czxHHE}m4X8*m)*}Hec35e%&Ng@9|h?6MduyLV$LC1XBY-Kvw=BOV#o%f)OQ410^c~7nag~SHoOJ zv^_0!Zz@Q3AtHs-r#>yx9u+uI-sfhVM?M0?Jp(j_AM!0sNN6yagD zYZ7>YR26Jx=rk|5VtPb|*dtvZAPlZ#BV558TG1V8$RNd1M;LM{&xCytREL#wh5w^9 z$^t|YI6K+HI!D8E`h#C6REdj|H2KF0-yjTfAR@4XgL>mQa5IPKGJB>7|2rr0QUrlA zRpVzplZubCimZ5uuE>j|!i$Uai?{fToj8m?6MV&pjCPZY$;ga6vy1{kDa|O2(@2fg zC;|qsCLdId+sKXGXo?>I0BaJC-bjw+XpS~R0ppk^0C0@w=#K9Qj~!zG>WC%)K#%c= zkNK#N?ScXJD30uiF#9Nw14)pl@&WUBk7i5vZzk!jK@3;B=s7%>rvks0Zb z=}3`kvXB<3ks&FP1Ze>unUG~-k_61T6F&Kc5CTWu+=37L^8nUg7*ZYh^=S&x^gnVZR(o#~mM37VlPnxjdY zrD>X{iJGaYnybm0t?8Pt37fGgo2yxrvuT^RiJQ5po4d)Iz3H3337o+xoF@q#!)cty ziJZx)oXg3a&FP%TS)0!(ozqF3)oGpAiJjT$nbE18-RYg*37+98p5rN(+)1A2iJs}H zp6kh;s%f6>37_#PpYutd*ZH3HiJ$qYpZm$5ta+dP37`Qg|DXe!pZ`gq2a2Exs-X7i zkqhde4+^0XdYlF-p%rSO7mA_V*`OK9p&ja>A4;JgDxxDwq6NC4C5oacs-p54qATj6 zFAAg1*`hH@qcv)yyE&sbs-ru~qo|pqJqn~jDx`1uqeE(>N2;Smilj@*q$;YUO$wz^ znxSnXrB!OBPRb@%s-;`np;XGHUkauU$|qq;rezwSePX6*s-`T8rfce^Z#tfx0;h9I zrwl5mb&98Xnw+Ylr+wDyW0%n#n?_hl;4636zVDypMOs-&Gu_~*xN~^VMtG9}) zxvHzX%B#KVtG^1Y!78l7O030dtjCJ1$*Qc&%B;=mtj`Lq(JHOeO0Csut=EdJ*{ZGE z%B|h%tpnq9QUC^QK(6I#uIGxb>8h^l%C7C|uI~!3@hY$LO0V^5ulI_t=n4i`AOs$O zjo;ccA5aBy&U zV9){o3a~dL0Z%Xojlc+j5D0`o2#Wx-F)On(OS3g=vp0*gIjgff%d$P7CwqYx_V@tMW zYqn>LwrQ)jYs$r~#xsfZmlS{dkYq^(;xr-~fn(zvHFarV*0C>wX7O)0<3ki$B37Igs zqrkeY>$~vrD_RYrD6LySb~oyUV-1>$|@TytWGpp&$yGum>bC0HG_oDFX(k zi@KQ*3ao1i(@VY8YrWTtz1ge1+snP(>%HF#zTqpr<4eBfYrf~}y{sDws{jWfki5$~ zGCm*(3A+fJu)64rzxk`b`^&%m>%afI|G%RU3Yh=}2QUD43%?m70ec$=s4EJfFbV)H z!4piu6>Py59KfNV2p~`Z2b{nj;{$w42$KN45sbknjKV3b!Yhowo6#zN4T6F+9UY zEHN?Q2MwIVsvyN*48~#n#HxS=41fRvfW;Iu1dIR)j{v>ciwdY9#&b-^bI-JOtjLDfyy^CxH z3BUja0LhUoFIP|rI2;OItjVdY|H^dC$(}63qHHjvY|5yd!g0LHw~Wi!+sdDO#jqSO zvJAbnoV~V8$iqy`gnZ1o%))XU$I0x!h1|W){JqV*z0$0`yZp+&JTO;qy1`t{)O@|* zY`x;F%a>pZ(5%cVEXS9?&K2Cu%#6;|OU}j&&GS6Y@vO*<%*)!m&D~52!hF4#PzZ&P zy#j5{0bK}}fX~x=3B14yo>0BkoX!A@3hYb^yg&;8-MyY6 z0}Z~|+{^sTF2MZH*Q^S|FbvE*y%inP!{Ei}j0(Jf(;!{XmoNV_Z3@+3501SL`k)W_AP$J_#Y=tK z_CO1RJt})6>k>-(1z^?bG!1()kPn{C*R*ij`;ZKZV9C{62-ScOw~Yy+UNfC&{I!K%Oxj=c`A5V#S13Foj6 z+n@;I9o6yd35I|PS9{?Z4&u^#&c`g#5pB!uyyWnl=8Zd z6+P^m%fS?Nn zjtz(K3G=-V{Ll}Xo(WC-<@C)D>wpN4a0tnu*{?7NsvFwh&hIy#&xtPAi;molp5k&m z-~8YUgOJ0Fy$|Og4*ft4h+w~-zzZ4v3V<*R;;;>)U<~JA59h$?=Wq_Z@aN~i-1{I7 z9{<3#&I{SV4)(wf*-+ls9P=~J*~Y-v-cAeWkPWJ!3Np{x)u0NCJiV`-^E96g!{7;? z0MCmY|MKjx^F80;=THs0t@2pk+geZc+3?VneF;;)5BsnV>wpcKfC-?0@iNc!hz<1> z9rD#s+9IFz!d%hUZ4YAp2#!GJnlK31&=2px377!fX5SC?p2Pos4*9?i{GbmnI|%xo z51il#0?^y`I1h{jd#yunC_q3Ob$)%HR+0fC!Ty3Z8J? z>>%yBzz_Hk-DQstpO6draNvf}*p6M`{jd*oF5!_r;rcMt5RBK$pW*qS4?7OQp8w_h zU=PM%51LK=`oIoQ-Ne%0{P~a#5Zvuc&FA!8{pUamU;WqM|KHz#4xz9O-mlt+4gJ;2 z|Jd~1?f}uRpT1X^LS@6ZFJD4^`Lcl;rK(Uhedn_6+ozA+r%5G|E%8h1p`~5+~dcsfg$H|Jh5n zy!kdop$ZvWi0}8djO%>I&emKUrsyBB(xPP=wWL;@6kWu}y4bSQCmGzPTPPoKILif- zl|1U{r+tzEHWhtFP z>M5#yrkW&^Pz1}0E3d#3OD`jjL^4Swmt?X@C+XtLuiSvd@e*S&YGo2du4HEuN8aft zm=i;>XCGe%x$LxcCIJMHK?cc1p;r#k#0+Ej(MOUz5CMdyeX>#H5X(4Oryp!oa;i#W z*a1|~H^u2koI`v}bhm`?dF9PFebJ{MXB7E_u|_q;rk{NZ0);w!auL)r|6lO*1(Vh? z+GY<;A?>H6Lk<~a8&h9FR4YU7e20)cVtgf`QQIu_R9P<~sZBRGF$hAadh?~iO+zXIbIBvtU?Cm2T#39ckWs4*0fP0+)XO-}dGg^ooYndFc-_nBo7O)yCYPe}+_ zM8?eVo~Y^H3&my-MP{S~5t0FMk)Nq3x!Fa1TFL1u|Ey5)gxHs3HVq%E zvDTVvuMhc8pEccfg#n3GY6-W#9N zkw+i-?RQ9k5vIL%+i%C+V1=h_*pqYk!REwuCLF|(SKqnWG;z%4ltx1GT^Tf#HC|hv zeI!9-;^5eMWtLW6d1aL=$sL4St@t(slT}s;e;a<*q5Jiu^O4-`NmgE)pM59+B)e@6 zp+w^}kqJyd3LAS!)`T1bzV;dI5G`BBBM7mS2lC?`FGI;8ICZxR)kjhKxS-n(Ar5{> z3Tr9Bgdz+$2&y^95r{wp3XAiUVbE_DF|>+(vf>HMNyry;|F8*S5ape0z@c>dc-~P` zXCr%EYe)H#AC-jkuOj8nidV#97PVNG-7&0Tg+R(W_ArZ?S?xj))5RXLLm^5GqIs>D zAhkXry;%UlKgO`$RQxlUjPL_b3TaNn070p}`6L_c=uJK_@;(doY<~Ed3Q7zztULmO zHYFjMNUx@<{WVD_itw+DEz2gykfZ!qsaiP{&5QCdgM4avxL!?0tgi~=u0Vx9r z%B_Txk#a<@T$nvgYN$#&(naGi;j@qMgm1Jc(+^w0kABSJ5T60cDlTIxw<(cEOgtvO z^aZ;q5^0O)L}xnHsiZE#j&+K3gfk*_2&@&5AI=y=|337QElQYdUeHSsFBCbsJI-q< zn3zmH*71&atYaPQV24A4frwzSu1pJgs5lDsjzv(^gS%M>Cn(pEzF`Rwg>nWUD6uVw zc*1?2B;)%S>QIO}6dQ}!gFM&K(H>DUg!h}HNvTySE8Q|*m=N1Gw1tV5cJdK`a34Dy zK{BTLEJ7hthQ^j@oI+e8LeX>tAW9WHs!`?;m(kxzys5e-*6@kr0vs$ zt-k#ewbGl)Pz#C2cLb0VhM0^vri!SZ{0aCTv&#%`LK`@Q3g)Pr4EXElcK-OiC6yi*T1;aE9?m4K5V>0Zh(-N*cj1y(!B_-ehK^Si9JTuc{COiQe8c}7dAB5j&4PlxntK`P^SX%b7 z+qhcp4UD+e#UMlBOjNvBGaB-t#xzkV|79q{Iah;QLmUyzWa&d0d#IN)U#z%C+?SmC zo$G%EJ!nF+vo8yC=SR+Cy?(qSdz<)2C_o{$&$-P%h;8yvDU7nyHWp=j_{V4<#^ZcV z%@^Xp1}FZ(RLkAVHXz>GMxVD`8tM}TacNqsZU|l3c4;Xoff3;cp3W!zfHmY7`k0 zq}cONUTKIpWqO$uHm$ZnqYrF+|8EtZ$OfL2Ve(|)BT^~(gx(y6Wp=b;rMVcM1v^?% zknZDsvseWwhA|RMskKyge#Jj%QH_Lb!x4+vT0R;b3{Jcv7r~hjEFsuuBV#+cqVR-3 zn_REuDkOlD;FkN4zKW$ceG^Z8X}SNwOdKt`(aH$AD)N&Mc4T5@{|HgGs0Dax0vr+j zyb?Yp(YRPj{T<3ky56f%rgglc$XynU!XLhQ&UgMF5r>^8*ddUK(()fmE@e?zIgC8U z5EK7^%0N%ct=|dc9jo972?z2Hed1H{X`|5GmS0K?+>$V;$Mw zh9)Kf_kMIqCZTY@!2kuM5Qu$%sO;mOLEssh*e8w2gkS21ip!TRX^6%9u1_dFQ#n9I z!j#{-5CHr$*;zQxBf%2HE6~FVv*-!eScjLPC>#3+RmcuyD2g5lJAA0Q6!VFQnjHBf zg>5jfchI&^zyxP7F#TBvXBZkyhz_&xgk)F;YVtiz;FNWM8POUGkirK_Xaxgn2yu9Y z3L%+H2pS{&qjg9I%eoLzFoajY2HesgaX6C?SsMj836&r%d=Q5kVJTjSsN`BEval3! z2q4h{8Z$(~G@PoE|7kgKD7O?*i&1F9?2tnHa|TYRkQzgzWEvn)z&U_>9xcfQdte6( z^pQ_^h3ps#{ZWrsm?}vE1+&OJKpR05w8dN8BG6llQ4obqfT75U#nTG~{Tr^P_?Aq- z8ble3Q4pF0!yHA}1WL#x=zvDm_%WN2iJmbFNz??@;2&)4vq#Xzf6&5KpaVo$gjvuU zF$AiXP=raaMmCu$WnzgAfyXR)lM|v3QP95{la5e8#`|dsPXGnaI+{^9i(xFc=um}F zsK)-VjQyiWg-i(mREvh}kXF!zVDN=mkVgv%F;M8mlxPZ(^u(xt5Sh zraC>PY|FX(gr)3=voK0(3C3fzO0#sFjwnb?SeamqL~sGJHkq@;2r&%-$d&1csQgM$ z;Gw;2OHT-eUX&JInzL+dLbV79&Fjh2L`~f}G~RIwUd#jn!px%VOaki5xdRvaP|SuD z1qYE1y~G5>Gzd@-Nq!W{seBN$_!inc@t-*tE^xTtdpcNrH6B z;zXgC|C7vcsm-Xw&1JmJm_rNQq>r1tPQ+Y7rc6%gV9!XzOHSYfxztWi;2}|1iNFMx zOgK(e5XthKJCPI``^3-nY>U{TqSSQI2W=9)dJBSN35eK6P+*Dq#2iV}P~o&tRItqo zb;}LqPyXak`s_^ptwEC7si@FjNn%(y~y6M`hDVrBe*$&`52IF*VasCDpb-QwQaVe<%b) z_z*J0(^6GbPF0dRm5WRG2N8tTT-DXQTFp!?(}tYVOC7XSg-~4G)ng^qB!C1d@l{zg zh12{NEk)M5vVwouuW7|r2fcwn@Cz8k$!CSuM9tQ9de&+k*K#2FNzjD3v)6t#7*wc(3>XUnpjUr2*g^vUB!C2_cvXQ3iG%%CaBUKa zr7Nzmf(ZDB1t3_6tO0WvHcv!S3*@z|0lZDuaRaqth5|eFNn3dQR|4iAp z0MQ?4fPYYc0Vvpx_1U@t0020GML-dfrP#8F*|A7kvd9XgJ=${>j3;?kDsk4QMOmh` zTCBC%i){b~KmY*%fS(oHbqZPmKm&$NS*xYltF2nCU|M_S7ng+{r_EZr#oD@E+qD2# z6^MWYSb(qy+p!hgEecu#2mv~vl!#5Vrkz`*btjx8w7RWX$yMB_ZNA~Vi>X~)s#V&% zeO%9N+O#OMABcdiMF5@++`&~{D+*cw2ml<|1FM+SxD7zY&}`nH}Dv<=&-zU*j#`&Hdf>wbjv$fWBpb13=x>mEHh0nAQaV z0~ml1zyd`83=qBF`Ssq#-CoRHi|<8V#k~}rg7@Z1I zBBE}z2ntBdq8p?HL_r*lih!h`uhJzb%?R1Y_ZK{M9QSkIzw5fr^Vgf}^PLXA4qE>s zTmIT_{nbH_W{RF~jhcs7xD>QQl*S1E{?S5^-=^8;x#SQaRXhLzfD93oP>@+O2SxBA zl4DU&%u7gYP;f*~P-jsHwkQg-5xMXbezYhQ?Im`@OL|~Ya%53z#7k_|OXk8$u_I9v z=YEm2u$Jq|?YJZ^I1l0gfYbpnEKmjoR;o(SBQ0r7FKMqX=^QQT!j{n-K4`}&jOwyJ zMQ&N&aoNyn*)V$9*l}623ar!!;v2&MyN+*A2Y}E^%I-_nj@hbmI9?q9#2Ek!z{9aC z4t^_+(JM~LE6#Zww0@wUtxO1ZFR-{XvInHKiJT60~SD`2Y>^-v5tNs%qu;2 zm%v!OogW@jv>Md58a%KXGQE0zb@j&4YA9?ijAQMl*jl*iT7;4Rbw2=1!tXLF_u*La z&0FgXSnZ+5tI6SQ9r1Tp*X|vy-G{B?Io1hc>+!1V2}bLQj_XN&>&emUDX;*DIv#|? z1Jze!3)a%>AI4Vvr5daX=MwZw{w(7{~&Jq6pE_!Bgpy!|b2`+fBF!h-GZ1KU5WR_87Qm80!n z?$>|*xjrxUXYpsq!gRorX|L7lrWk%VIM1YXg}=G%WuIygfdX=em>gSXV_&K z{mtBPgVkyGtazJt@g zivPaqt$nqWef9kPd%1YcH~Uux_qAsBwb%A_j`ww`4$zzj81Vx=irRs`@qvNUfua9_ z(XB{B`SAZ^+lvm^8V}3|4=iR5EY}XKjt{J<4zZkvHsXi2YKL~lhxSf~4*rLZw+@|* z4;f7ejEw*q@=cnegKIN~Zfl3`$A=zNM>x(SPw^u!wIgriBOj+DU;iV&TSxvWM*;aq zW|dJ_f9;wU9)-*tU0*x8aeNd?bsWZdE3`0*Zfw_$lMu;Cxaof!ee3vE%5hBo@$H$Y zD5qO+(oOd_w_?|h?;RiCry}Di(RV)F;u?%L6DKD+k(2z%$+yTUDdg0A@`Fn9C4}JN zAUS=8oUulJbWDC+NoGSGGY=ldQk^_8KFM)9$@M?UyLFOJNjV|DIf5ILiEmDx51zcZ zq%*Ib6pWG?cfvxBPm0A)OVmzFjZe!8Z$n29%Wj=krkqygpH@4?#NFS?rT}VYPG7B^ z)*qif{_6rLJZKOu;0sS=+6%Hz{Y&3-MG9C+%;}ItI@=3&~mXJMU)B zK2V*LQtot7g?->W?=?Q}%a85(724}`KA3VoGMx5fnS#v9*FKR!jGq=C5#lY3?jQEHCus*)DCDi>T zhZh1aLs>c|;JA3qJm*C+)x|vJ?z}&FE%e^%Ow7a@d5U6vZ{^l)S`y%r>;7`)Ua0`( z$>=Hf&fUtjyZcrYwp7B-8kzCZWg7!h$V^Z)iDx9@6ECg5cp#pP^g?#W3)oJkW_jUg z5el?=Y(VgU9wl5)#KGeH^h^Lp1mYHq#8!fS{ zjh6l1=5zZ~-&nPKL%!?73R3B-f)8DXm#v9mtxlbx*5R8!*c)DCy)^2oeiBaK~)}uO1bxGK@hR2YsLIs)qH_{?d&r9qoWlU|x}wy3$Mg+< z7lmw1n3k;F>2n9@>f^#k3*T0bHk>VQMqfGp^YcRj6{DJ1vfD*bKYz^B8fNCr2bVLy z=eeY`vC~iE^a}m9T^9`ZkuP=K*NtZxN?r*9=~#yrN0kc%q0i!CayW9)0=P>CKy#zt z&mg8(Gf!*;|0ri9m`;-7jSzi1>6%{HB0I;S!%GXdp{Ku%f#4rv4+KXpwStfI_X(R8 z(XcW~D?aiOG1f3DbBnRFbt%tvN>kytKq!+fVkPY%t2f`j`eyFYqty)}IuO^b#Z$E> z@-@B~CSFUp3nUw8U4&o=fzsQ!N4j1)6jhAm1@g&HYdV6d7^Wz9i7_c7jb@RU!;uJ7 z4GMeUQf;4;7MV${k~(b^D9902q#I!|z*^0dCB+in{a-*0(Mm3Dqtsh*IrwGH z#k%zs6${mnMs5|_tx`#Hk#J;sw#!Cc3vGu$tBvV{@;ATYd2Zx&`nf*Tb!Si}K;&JJ zKq!j^I{R^J_4FSrft*!WtrS@{e~)msm={nLgS1L0@KbMP;h5*VR54xjB^Wi$RfGW9 zHpA6ep$yWfpxa$MfA5jqLlhF9h12F#QVAIBb#PsI=h4Ah#p7)#fFSi0IGji9Jc5-m z1#(g9f;0R74ovgh=cuh+f2Gj*_mQPhkD|GEc-4C-+V$^=k?D9810x|pn@*hM2PaOMKhiu zOc+sF=c-Rx&JjUs|9n8`0j;3~S!=3V`r84~-A9#=lxl z;EmrA{mdOhF1l%>QYSc6@V@TX1seQWp%c0E*qIGxO$~~>YZ8xp1m@*=xp}7<=MkHp z2>ko8gySWk*9>OMrBzofND0q`O79>Oecms}Hh(Jw;>#F*X03xA*8Ai*$e2`TQd~Xp z2M5ZY8MQSkVQAwCaA|@eB+^nGiqI_Ff)A~72vCE)!ec*)RH-cy3n`$!_#=trIX=;_ zr0+tU9n^Wf86r2=(!NlMu_=7-9MDu$>M0P5$}JhfAcB!5FMI^6q(m-lp4mwO{Kp@r zWVgiYd2-^#@KjT&aIk5>lk39-)`puFw)N=JK4+}J(9MMGZDN93iQ1do-8GuIA|DOV zgb2rEkM7;KDX2?rHSO5L?C`3wn-WN<*+s1~A~PW%B;v7~mam{dD&gsvGh+$wGJax5 zPyTFAsJYr0QirAZdK4^x;f-jJ*g8Uct#J%Lnei&E8 zd`E9dDY1w}J62BQ?{_xt4?tKha!{oM#m8Og3F+YNW1oz(q?h~1X6BmUF}o{dDE-b5 zb;BUteeOZr(H!{y;x_|Y8&ySEx~VG zE9M$YALo7PT=?J*!H&srkLM5#Gx@G;d(rq+Ue)i&8Kq&=Hr8Ot9Buph7f&^y00`C>IC_YF^3=~(NrTg zM-C~CA`Qx<%_CcCFV1bW|L0Guw!O6Rs8q8Gn$OIPP7tiJI6V#4QVO2=NHKCld8k6C z1E#SPLI13Y#8tUFBcib`DbPM@hw(0n+0tXG%e2-GlLFF9I`9M-4n!90O6Wn!!t`$#_A8?g7?=VdKjGnT@yRq{Sr)I zRvP_$ri-$mov5K`t?q8gR90zFHUIK(v3x`k2_gY7@1ibIW?BDhc?UKyZV*IIDFP(N zAt~H0sU(nD1*eq^(~H=;?g7?Dk|0$Qfd(>Ygp%Mwgmp?`ScV1SiQyg8Qc2R%#x+I~ zv#bV7w7ND(uU(VOA98o}#xF!FE{Y;LxD(SwLW||=nJ^z~8Y{E@s21vI=4kGicq94( zxxpHLo|2E`$PCnAL{UBxgy`=u>P4N;3E^Qa)A1M9+Ma0Atk0jGi8VQ-TUAdbv}RJ>NUhYc+61EaIx$PQ9$)D zsEQRD@If@z&aF*?@FiiDB!Eh@(V%U4)6!-~*J5u-|g^aUy z4>&v%5~UjVa#8>0J_Nb;8h&Z8f6|djB1Q|*g$;EFsb~fUCUS{;uCKatcoLY?i-b_s z2H47@j$`CWPrqK6U?2QkrImJ0(WX7;P+BO zme(aG5Mh=Ycw%tst@Op4pf^MbENxiXF-_cV{1;K|^XRy-4(L~}y6=CE-oURyxP4v3 ze)Ux$(kkFxIt&RRW&1?7Gl7)h7my(3clfi?S+%=INV;nRQMogHDU^A6NWw+s4 z*9S~TeVXvAbrnNg=XxzcK&UKQfs+N9 zyNTt2nrVAnuLH+kMUK8#9NQ)Rd%rWAN@H>NbvjGx1I^vXBdtKPf`KaExSDhJBXrv5 zkC5;0-N35zb(p>%Hzxzg!)BBCPunja8(2(fJP3riufcy{(pi#_1&f#Mg~*|whEMLC zC!LOc9Pr_Dw@(gWqIUoB2&L7l9M)T~oVHgD~V4WPDOE+Fv@ zGpGyxO`4TiFvME+vU@E^C=6=!_o{5C0vFOoPErdUIN&D${w)wi5uKpQ1{b#^X|y`s zGbPQJyJw1gYheEs#GDA_O*0MDmmMN-MmzkJh1>7+oqeT}IZRTuNKnBo_*NxXyftz| zC8GOGCC~L)EU5Sm?UYSP#eE6FI*ZCX{be#m;1Vw>J>1*$uaF~*l`}5W7YXhAc5ai7 zxn|hpmTpc1rb^7P7c{4twx1L&L~=a^{h_9#?s5NSq%lue(J~Wxn*|+5R&N7zU)gHy z6j9p0Z@gy25{R+T5hhgrpK0J=SkymQoDlLJXfA1OTC5BYoSc+e$Exq6dik`JLf ze=3EDEU9Fk)Fv*Q{!Gx~WnHl>aho3!z0hCg8)yW4m-??%yTV6%o-o=60aq+q$InRR zCEkuoit7|AwNIA0pjqDO7ybu+T}9Ir_)|C4jnfPtoIa^bUFv9+htPTsHDI6Bpd}_I zGPiWg9gN%P_34^;2Wt8HTI4z3FuM||$1Xx(CZQ1*35?U9b!8F@MY>&%#?xCz?n*BB zoF_1;f7eAX;5(Pl=Y@aE@g9$CB_{!6Cul$JZdNS@&QA`Qt#S|ku?({4ual?I=Y5b6 zpodm3w+V*Dr=r`<<*2(vaF+TjsaV_6Pc|1wxwpOA(wEGXLy(DNO-URmcuS;CtoX5; zH)Y_%PR29&R4`F@`~AGydjb~KtDwHbS|ETPAwVy!75B^37{L~0Rmt>26du_x30k?+ z^$hFEWOI>V)8l1pbTt&airB~qm+RA&Fp^YHk+br9agL$i`bZ8azb|T87WXLaJUKk< zMQP_}J1-0RzK>y-bp1wO_~{CqAr&anT~J2=O3dma02|xBYW481CitYDhm1I@JAP(s zY~SoosWT%5VW^5L1Mqvco=zcY(g1umS2Y9x0GI&qCka3QnbKqX0<6SJsYQYm4L+Rw z*T3aEM%Eu-H|Mq8nE$5_P5rM338RD?Sd3_|%td8DwVvIMT!5srNCzap5`jd-GQj$B zw5YouNkKg`vtH zQ?6V#^Er!^OLzMx0Hc^lsKKf&A&CF}WU^}eBwE#TF^i~CH|tadS=}0=O6=zt>c*^a z(*f|U-FUM&Ly(nlYAg^?=eI}`WOJTS??wIasGPH<8`Fet1EDjhzxyn1R%n5f%WI(R z1IlbkP~{5eM}~iaj6@4`27bdwiiQ8$S6$+eQl|B`OoEjwspFKd=8<4YBfS2dQL69n z_?8zueuYhr&okzqIa|Uh4SQ5{Ln&mwTDBnqTOtn}f#2o=0^030wgAIBUzDSaN}52A zEAVlVpzrvPm{cG?qC2zI#|a($qHKnWNb|K?Ci$R8o7%&^74x#*0N&Egy9KCmuc^Hr zG!bo6X@;!gUr9;ks^$--{b8)F+>6q0}q`x{d>A z_^b(Zzp9Um9r4a+;oXJ4m>}4_u~wzrgWcd0&ITPC`-sJJ!Z)xk2NaU~C(#K>Rl1N> zp!g*Hi(lpzfb#gX{CZID4|^B!ofK#v+S8@(ixs-BrT6%*1ugNT8X$`~bwE*T)M|)O z#W#F6C%%w3Q)?KQ?3QRQSuA2y;l(M_2F zWsT*{sQRM?wSyixpYeJAK2k`%^G?d!Z$nBUgh~RYrvQDUKo4XplSD(pL*_KWX(Em;BFa=0>0-j@8lue9Oj%E4T z(v@8L-=lW%)Hj{Cp4`V4LqmYdGy1ZTy9^72B4t~RG`|d=M*#&Dn)16GBD+sx9P*oA zQ2y-Q=nVoZ_2Okj(v@y^*HdjaBU22>&ivyM_z4Su(`NBS@=9vH|;x z$GrBywhTjB^(cLZ2-i@8^vNje5b*r~9x{pN)aqv9A#k>gT1iBmjYYYB#s~~`pJe69 zCiFu{c+PnszgE;hPK|3}l*>+y-9w|1vzhZ%2d3 zN~$z{@IEe!a8<>5hw4ihef?~B$4>p_66Z9Q?#G$*F3fVKsQ;~M`?w!X#%_(KQ;dBA z{9{m{lk*pn`%@fIKH0u|9w@;705{WzLlNwvp&8c$%Bj1naSA z{?AQ)X6!n5utrWpmCw=9v2!#Pd>RS(-uEZ zDw>EWpv08?K4IF|pi_=5sqN2EJe}e=byqXv7C0@iqs_b(``mo1K3=ob|J`Mf*u?U# zyi&6=CAR$PyR93il@q5$wXxN6@8ZF+S4CW2g0I}#k@D$!>32VCA~|epKs`^UX=$i-RX%(3ncKBR<84P$)AJ*jsw=k78)A6R zEIxF+T#9vV>9{ql^6viG%igv3zt28^&#z8)G~Tl5qSAVKcGfL@-lNn>G31Kt)sO2l zKku`P>%V&5?;SS~d_E8nH+biKFd=T}p%zx`eCYZ4$I`e@wdbE+$9?wxSmz;yz47iU zNvn>zW6QNO{N4FT=w|Jbdh!YUV)GoVso@e9P>sQLrI>5l8K+v#_|@@8{j-DT@0v**%2ZMfqAp=cOj-oSGVOpM zNc+~%=oHBX{{EIhUoEt2%IWSj&&7YbpBQoooP8%(hX}Us2UZ;38=_Bs-A}S0YFlI` zJ+;se^thNm3uQ<57R2dC>}agsp__*c2E}!)Bn%oQa&BEU{V8y7yZ1fCmY8Bg6ILi+ zrWB|zzVj)j8Fh)gW^0UEE$zZSG05{N(9*JY9=qU7da5OYvKOZ8Rd#*vtI4go8)I@q4m9tIa2nrEN4K}(-DBS`* z;_dNh>TxeZ1n(5X0fwFk$smU>2!9|n=adsh<=2qjhaVWlh)5;!pU>1_0OF1jXq&9Z zRLr*5i?2Utcrq`*QKy#~-?4a>(%mkT%BattP=W&`wY(-NXN8=~sxM+|zE;g%d$K<8 zUu1978}0|J*+P8p=e{%uJP#iKC|;bNEm_UeY%cZ&zg8 zFF^ZoA{gw3;$;gwR}!Q$pVlk*q%wPfG%C9F8&7nY`YcYIZ&##o`+Qw}E(JfkBEu`xCH%z8v)?=(93d3c?tVJb{;)8}a@t)AG(d)sy8@jz#VcyneHlMlB7?s7^y ze7XqzO2cZ(h$t=-;PXt;$cH*XD%yt4Q4kAegL}0sz_=7Ho;IOHLa$k>-gvmg{9}H= z($-k`v!z-^L{`rDf43GgX4GpX0?HI4qq?Ew;-B7<`Q zerh)U*P21&6R-O%F48a`2uJ%%MU!Tnrb`J=J1aN{skw4zI`t9zZwt1>;*zOdueBYs zSyvne!Ac_^b=QFBjp&p+=e4$}ql~t;(9%1`w|7Ub{*_=|e9Uuyr}5J6QzyLv=BU{L zO}Q2@^-|N2b}C=;0YZq#8@5%Rbaeo0UKp*J0uy~3lD}dGQllk57xCT5=b&EXZWUID zFfk&sNE_3)JVkju7%)(vo^NA%pqf&(tWvE|Bs9A+)JoHQLvGg*d^Ng0kvlt^K1u@8 z3r@()?Y_e53e!C6&+JWP31&r!(kLkr9DYK<)-k^9(MODkaYZdoZ8tsaREiJR05|kI zfjJQg5#E|lyegQBZh=nXX~1w56J~Q^A^WHQi9!W`CyIY?21k*5Q+U1mN2Wm>E^(I1 zGF8>i?%xnE9!*iXJ9|>tkr3qy9T<4zYzefms+s^2cZ0C!2j%!Y>6rV1e*md+7vOg8 ze)mupESbv7vIh0bpn+ZV!Ho@Ul=>^)`NB2{sw%d{1s+MGa4|i`1UNx%ZCEf>@Ie0%Hp@+R1> zHLr%(qpxi|PH@ikIOht}Zo}bE%AzLkB6pM-BkbYKBK&71{(dH6*|^8jpUh%Iv$N`c zyX$6mYG?-NT)Fqgt0C^Q#mfQRpxqLq>wQ#qx%yqjFAXSWS7@Yjz?Z`044&as#lz$nJn1{8)Xja-nfATj0IY zQ=&3OpXOaHqCF&2A&#WE;Y^m}0n;b?J4U-|FHw@Pj5R*81J6UH!-qpjCMu5x;lRiD}(rZ_@Q~lUzmE2c)X5x*7D6B)e zBmM4XmXYO9h~PB4 zsRBYz0Y>=f>(><$HU`HrOAfkj< z&xlX2l9yQX3Sz{Ux`GN><*6`p&>YZ58@13H1CmHBlf}G%3fzd8)}xJVMM^KIZ{BZ{ z(6j#c_ng5m?gaViMH&(THwol*l(nx37KhX+(U-B8he9wsjdTo>&vhz*7v<4=&MNR1D) zo>jnJKq^C@X5738bP3U z>hElkU&6CT22@d{HY1CoH*OZv3ZsnTU#hMM=dvt1BqA3J8!aSyL{&bIi=X!>yG5w3*{F;*tKEHqvW-X3FD+8*E2}R_rawU> zy_QUkko0*%y&eB@nnf~I5SI#MTt;I{7*v#llrpwOpRr2$Oh^v<-gqbEPHVbS7O$q_ z#h+UOGT&EQiCFQA(5URuDF^93@v=KoQvWp}JM&Oha9`yTJsngLut;EUrkBdG&4w## z(H4fE-(By(J!IFkAGVy=o@X|UY_Dh8Ch|u5oKqUzreLK+aC`avK2wh^5{Y!&y z)UEM}qOgJo8VV%3_F_)OR#PcuT?IfcLddHH!%>p0L9S=5!|+uI_U0^w#bS8+a5_c70Mhv?nT4^w*Dz4K_p{tf(6N_~->`zMHldK9D?`7V_lWlWtfR`%B+z z8ADxR3fk)DW-2wN`8X#sFzcFJklm-QA}oHu5qel61Fu4L_Nf1D^r*wxYU~l!(TnHZyy>(eg00xIBlVLT z5>1Z5zQ+>e1fNf1mWqB!nZVrB!^r>G^*fj30HpLe0u+EVAWm~T_8HojWkE`A*l|v3 zLA0XocpC;OvDfZ`6AY*3(yY`6@CKhyYv2y zeo*c`_T^{u6*AF7cRtQoPu$WIQ0l=NRgUirNLn-Xs zK*qM+>$PQ0Ux^qRWaszSKqh*^dITA4(%a^+BBSD!W*eZ@QkxT;z+zVxfEt4|3 zV?c594{kT-_6!(2AlL8J(;6LQ%v_Q#+aCQTrk#c2RW_6F2zo{mQ`iyUbUKhUC&M)iAMug>dftovUMj31;?tu06ozlO zn0d8`jc$=9{T(bwia!X*xI^;1zAY&9jPzlb2g=xVoTRo^)%%(6{P76f^G3*R_HYoJk<%I~JN4hMjl3jji_G zXeP(ExrPZoH|i5nII0S%6qEtn(S(OI$KL0|{cX9*T)kT3@a?-Sk1<&p03MlFdrLsG z_KhYHK#HFO!T@N@YHMHvXShXZ%e8^j=OeHF6?5+0;L#4Vm=6TO=V>ZFv6Fp47QIL@ zA3gO!AID+3%v+Sb-`!{@#HW0Jfey{qZYmHYNYTo-)s?Shgb?f(U`l^milUj@0=td1 zWoR5^(Kav|pYx5A<^vCcByb;e$8>nDIzFa8JbyUa*o&X)CS}dIv%LVpNp%Lb^CjNg z^MQIIKfG-hF{p3x#x|WzezL^u;v(9rYvT&>l^00JKnsVSZ27}-_-yiA za1{X}ZA*-(b0+A_xGyf;9P*NVWXvJ4OWfpu1tEO`4kP3{pWbTrRJ_f#qgV3n238h- zxO2H~n+diH@*{-vr{e?8eL$0lR7Z)Sun-cR5RC|$dMq~8{xNhfHTPm+cPv0DESxL# zT&vD#;h72)YZ%P3go;KgO@j2FeG=ei2BUkp^QP3j(PU+(L=lP`QsTdHf~KZc@&Ui& zZ|XNUl!lEJAktmMlfj%n*3QBF4OfMYq@zioW=mwecmN78fj=4bzttpb!WZx(KKZB} zlma#Jqo4lr)mW%NYQ&Zp9l#6*V=Fjo@+WS$(dN-95zXoYwPcCruSAMu6(_Fl8}p5G zN#_B@lSl$Z#jwo<9#s&mLMk?rZ$OQsS4}BGjCNCVhKLf0LxG*(jcvQGW63m6FI z$bstM%EN@?gH4pTXnuSFhtP@9pvnkmElPSO9(mL52KO*#vS^mB$yT*jW9)#ep=FxC~vij_}!)Lp*CSrh(3C`*8^hVZ_2;szHi8%s7><+!jlj_Jx~2%%W;QJ=Gn~c=oMi@p0RhtM-Xr} zAf=F+H$9YrKOOqakH)PS9^x4Ot7~y?+I^s!(^KM#*BTES6xnif>F7(Lt_F+k`PFt* zKSb-ItDKC=N@`1>k@VUr9`WF!6(~4)sMc847Jt!cf(MQW6ePmEKfN%0UGe3)dc@|B zkFP673(*O*d}f!EP&7RkQQ2WO!;lo~^W3u;%D}IdWFZNW|9lq;Aqv8Gov{+dS-RI* z4O7D;etV2F3QX2$zZ)p}5KAeLJU6z{`!8H&puQEkUYAPaFf~Qmu{QWsby%SF-q{eO zaQ`YG@-)TxwXO?|s6A8$lh++n^}GK`r6xZ=_^wbz#`7LMw+9}S>hGAQCVlyL!6rNL zoPTw153T&GO_6zZjXCbDZ~yr3jVlp{D64dWziV{D$`0w}(|FLg7%w|s+4Qzswe5GMGH)Svmr-KCLK4ESY9X0- zT6qQ%PS&@ek2%3(3}v-FUB$gsDyDR=-KIpj$GSJEWreb~96DYL|LU8G^s-+wbGrMF z-T6{uKJdi`N1k}}z~sWNfK!;On;00g?#MvLV!!Yh#gZyA;C#IMJ$;m=*618@XLwSQy(H(Kwbf=J0`cjXr z4B5xs5g9plFU2g--P!Hct?3NgRdTsg9eE!P52@J@qf^#^WEoIgmPdJ6Io7kF2;{zK zUDL=ZCQ&|VZlQTt_4n56F+cJPs3{rDVDK4-%!js;CsI|2+AE8W>6R$(Ybphwgsks!KFVy}&sahg~zBTJ%0 zBXrO6|3>IIbT`K>ZNYE29)kl$Snj=n89hWW>knO($g=T|2;4a%fK}^_?A?htBoHQm zBgL0f7b7oWvFF~G&8=j>jnVfC_CMclnqrA6Rn3Rxqi?2R`2)P-Up!_g`ZQz5nS;z^tx@qxuv?ckCw=w1!(*`*%oFbu zPt9Ve7r{^k{QhHUmSKwMdSo=eO~#Q|Wo!Y{Z|k4#`ZWXo-E*KCe+>OyrarHM)xsnkS_Bu)2|zABR+&sU zJFH5NT^3F>8YVyy=;_3Zfo_J_orHULtgc%`a7yx8nIXOrDl@wgNtX?HS2t*(oy&|E zXPoSl=gAQarNTlF8ZGbyI=CDNK?m41iPU^pwTjC$vRDQytS9L%+ay_-T02(}#xBL-m&fiu`1Flcu|JKAK}i+i7_eCU!Oy$gJkF@9%E0`Iz<2_{tSsrwyE|^CVD|pp~r3{`!Ag;Z}yxbx0FC9J+&$n$-r&Dj1P@wia z;FlDuD>}yLupcJZTj~atGrQ@XxFiHQVnC+3nqylz`qXHiXD68-r|M%{Ysa2`Hj-)0 zNg7%S6Ruu#Z_YM$i8KtnI%X6-*Ob7kX&iiQL#*@D&SRnMP|ao(>3XK~bD8&{vTV$k zv!6ZmR7Vmu6BwTCNgCnd>TAf)LdB(6C^I2KQFrO97HfE43H~ z0O*IM9so7I1q#|SKkkThd@gmZO!MMfKU1qWP#!Xg9JVQJM#KYu`B7>6HknP!AP3w5 zuT;xau4@C#V z#c09W6eN{a69lN<8megIv5m;ImF-r4R#MSpxsVoZ=UNzaKz)6H`tG5ybo@2*QA^8Y zk7A~XIw`w_W+{$U+4LC^giK=a5Mv55yU*<{b|W1s7S@~2#1=Q|8uj=(=Eq}j^Pl~PxteN!GU`iwS?0eT zGWF0sYJC1p3k`nGO?{)~i&oo4?o{1>w5DwkMay5rIGz#W;kV9T?^^7Sh~LW7Oh1TP z?!@!NiF-mDW8t0KWwI|8)CmfXVehZo{eBb9!r3+%kiDZ}n$eZx_&9GyXp_iWvs`t? z=+XXfSG>f^KD>%S@sUiD%GgZ{>mTy(oa8pMo}4di&KBH>|H)c1S~hycKeacbfVKF^ z8diQ#!7rgp`^n7Nyz}V0={ItLtvECjo3Dx1@{VIYC-j$}b;sPyNc3yUo%giOjRU$} z3iOUu4a?V$Y3BAvj=Rq8{;t-la83W_Pm}ZSZ>n|YBIhXigJGbrQ)uV1xb?}Ws~3C3 z(au%1(UXxo7yCtw@7Im3Psg8M98_7q-*g&1o$k0ed@y)r&6mb0OBUJ-`o?V=M23@@P zVs__7#Ql?!yIBfSh>K^rcymX`Nl=uUfL>342pLA(Bu?7UI{G-$r3UX+ z`q~)UM$Efijnf4S1 zso-%!%P9g9Ar{AkdsQA)Lj-!v1D=^wA^%85an_IODZ>Gd6vPTIA&v%P4|=Oi!;Uhtj3swo&5}qCxkkavUGdX z=>G22G}o{o1Wz$ozB5MkUqMolt{;p74B zZGGAkC5$QSk8kNFZ<)qYHJf4{$zY+cT49diLOFR5uOb~iBvr-^B!Y#LZ2{_q^b2l+ z;+Vjb+4XwJb9AJ*k67o5qt4z73^I;SrysJ@WP;PIYxhe-%k~$m$%8viiONy^3PKil>Ue=CRS}LoVPcE9 z*I>^~ck&LvI&6(#Up!TCCWI9Lenp)vVT=?Yxz;;?Vc6`nJ)j=Wx5EgtoY?(yau;6m8m(|{0dSN~+NjeXreewr5jly!L z@^j|JwSOJJALd0&fM8+ve1}N*l}1x}A-Z53Z;^rzOF2V+ssaja?u8eN{>repfRMy8 zJ)yZK{FUa40nmNSKKg4RiyzSZtp|k_2~Avtl7yg-e2TY>QPG~oH~>Jf#HCfjrh~~Y zdY9feOy3wIy14_%_vD-hTvnGjZ+6qR81Tg=7LT)n`2=BpyG8Mha^ne91&3TJIB}Vn zdlJI%*(Rp#d1Cj1(yM0plYUS=&Xrj~EK|eAu!>$R?XDMzUO|xYeIo*~1DRNWI{J9! z1I>hx)YP=s=9pX^62$uEp#^$y-+9L6ME_P3b$5xv8sozHX465kAox>NPb1xE7~`ps zCxeYk-7b6r9WBTXhSwBLESSt;>3ZfRgGp9-d+-=Rx}GtX7dG-7B&TG9RTBafU*jTZ!C=Q2d-9R3LU>2#j{e&iGja;no2CkPB+rMA7el% zvVetwUI*}wJmYT@G<9S4N!gMDjo=T5jP-NOySNet8}?_XwFN=+X+j{OZdz+*P6QG% zkyR>*G>ST?5ngCwnb%oUrUxnWfrO-b)BLkF3&kg7zg8*t!Ggx^zxPY^hDInlIDL z7A>BSSEV4(0@G*(qn68;L0$ayvBTy51*^bZEJ4{Z!{`CuxF~Li`Q@AN?IB@W8&Ev^ zv-ENKi!rK|ykuL?JcZuYY!M*h%!HQNVU_?#HHH$(_xUI+#rInRpj-&DWp;^kfqsGr6w?GlSn$GM<<|# zm6(SfQe{t8{$7fZm?%TMZVgAuy^mr%#6hgTvczL8#h3b}c7Z)Y@H?8!Qp+qbbcxov z4@<~(^}v|yMBw(m#J%%w%KU&NvS(U|H;S3jZRmfDo%cIifBg3oBZ!s63Z?d@V$@zi z?Y*g8d$e{@K4Qm?J!)@Ci&`~9j2NxmqO{Z~Rjt(;)#mHIukVld5BFbiu5+$)-q-uQ zU(d(mDNt8QZAXMW`g-TcM}m;<>7s>`s^IHgH6f)tev{E`_BM}gV_7>Oji>WcysWqu zTQ603$M)w4(MzeZwHHivf0)IMeXt`rbXX0q(MRq(#3cLLog8qMn0_AsjF+3TUD$N7 zjTE#wM)KEBlSibv)w?B-L6={a?1Y}hw(pBD{*~3>E*8u(FyGEW2Juse|EA3_2j353 z&R>ejiJX{_U~6kKqdz7Ib0(pfhXtXGE7xma6~fXT>U+b? z{IX#yGzM5I&w@!VcBms+% z_9tfi%&|-lfLe0{Iksu1jVT8(T7AZpq)_&QNUz;_A(3Eums~4G>KOQ(DE>GTuH$<* z8M5K3O2I{W>b#KF;wGrXCwv6;QV0HRca}_t{@^1q00R^t`f)pV7?1V4M8zdbnq&X6 zD`0Cyy_xU`M)=&)wwX9Vbp&>-_#Psw)zhq7Ee&qlhsi`wqU*KH|lZkbZJnsHsr|DO1KMn3v7& zO;l6v`bRg^=V8%BL8n_dzz#3`M!r;Xf*Gd#SLF!8T$ZJk!wWl^n}6d=Vw$_5Pd&1s zG&17MIJ#~S;0$Cmv$8f&k#!Ll=2pXSFT}Qj2q@cm-_O8z5S>&P9WlIfmy-|uV-Wt}=buIu_dG}0`0H83SMbU3KMn~le`vF@tWHkzD)5~zHy9i5Do@e{VDe0Cr7%1>byozxMNVI9XMpQbzP3*v<2-KW|zJdm;nL%D|N_xFqCeREo z7pX%-C;?F;)4v8fU9Ioj+>`h;Ow;S1D*F8CR|`o*kyP~Jz#fCG+^&yJ-#!wniB2JRNRgrM*Uv`}I^w3K zG@TSz)pZ4pX-+vrY7yF?PpTkQ9@BWGw15g-AmAC*9+F$v^f-gviRBbSGN8$u{>PG? z5<-}Yy5tzCK+_5$M9wZm5=bZs{iA1APg3Jqj&54oFke1T@X(iAV~xiMV1Qi`S^mZuQh{rs)2Yz4g`C+hx?E^ojzqq@;@Mooz7l)G9e{xZ#~o_lx)Z zsO~Ra5lgaw#v~*N3PYj4R_&4 z@la^{vDEc%qmL@C@PC{ozs1TBHkwrUE3$k0No;GW9!yAixu4|(O@S~x<#{|BUqSIr zh@W(UroAyerw&Sz>F{knu}TQBfacCrjNmGZ)-=WQM>h-BLU9#>9^6Gj^fCSLjz;Z5 zP5G_5ySOi~rpl_CiiQ%yHEbMbG|2%uF=zfAV~lBVO%^HT+9_F|eFnXnbf*YHjXPIf z9!-i{|5iii`Xj)HkK&*0j1Cmt*xb?K-$<=p$v z=6K}WCtDq018*zV1z)U1`(N!6N- zUGrJEaY@koaF4QQKfcHDA2(Dv$H6IslJ3g~)I!Bl0x!yi;=4||GEJ@3GuWp3dfk;K zJo-3nwxR~H(#^t=fzMweGwzQ{?-R2hL%2T^^i;5_V5IB8C?%D3kG#+_|4){Q z7mxNCZ z65zE-dh&%>@{NH@hJ>{Mx_=8BT}5kUUBtcM<8^Vdw}n9H!=FdJkB2bL>Zx7$J`*>3gpp)q;2;W6DqhlZCT?8YiMtttjJD zg;xbYqUdH)V|z7Om~}H1>3dUubF}x-Ndyp*6>TW5m$(Z1U3XKSo1uZWJq?}aS-dL> z+5pFV2zY;-eX(+maC51p3NnrMIYHCkp&Q-GZIgQ0S-l?WL%`GO(!((uXp9tg0 za`UZBkUh}We7T#I<4VQPAlPFdaVlNa4>hn37c}x-z$6E!37Amm7&rvx=j-p>Q}8VI z5nqTG6`AMXLQfCVxA5hOY>ItoFXUJdBTf90coit9?dxv#{NqlY>;8wgrNgErg(M!c zk&{IVLUrDZ4I@&aL7AWIUYDHJcqko(8%1WA#a+&<%8|O!i7j#8UE9RVT!kCQ^O;wo zxIAR?N|v8Ppz+l?;b`-Rc_{FDn5uX&8;)9z0UcfVKrrt zGiY8XS?gus7hzViXI`(c>tz&mZB_xYXi(?%Hc5{#ui>+3)N>Q^u5-qGgYShf{(LZt|%ySNHjv}m@>MUAv zfQ=6)zZOGUH32MCQkEOlWLpo>7LJYDc4mNzWt8iCH|oP z*z$!Rsh9noE1UT$%l1eHPEYlZYxpv03A(@D-e0{xx>{Oxi!2KRL*%AkhW^gZa24@2 zpnOOKmg$L&YIyWi$Lig;h3>9&QGyWZ^y-2+Q{MpZ<4{WSjdh2M{2d-Y|LWBBxi6vJ zhXY5J(LSFpgg(Ulxc}Ilvk7@B-QBUC{xkB|;o8rIydj+a3wOFd?^g_e4{j#2>5qHSv^QJCkF5K*NKYQAmwcj2Eo}MW))q!;=R(&VIwJY_ ziKBCg6a2;H`07Sm>@%uO_CHJ`j5L8+Ounvr-UZ`&KU#yHrMaNL$&SeINn~eIInzC{ z#?yTI`rj*Gw|gHLN98|ByWcbJ@HqGFGH2y92`@yPX8a?g-(>))57EYvIc0czYV}x!CX(&geIOWmW-u$AsRygOu*Bf8>e%AN`f8zgpegAuzpS$FJ!a;uoV^8#kukh!d zjkjWq%dWJ4KW6^;y$367wqR%}V+&LGoqiu*0LJ$>Sqj>PFlp0+0=ELY~6ucpp2SY`t zF??r+Pe~_M2*bwOF+};jk@lDfxe`lygPH)wk+QNOA#t=$FtVt>;W$}^D32-p_PJ97 zXrmEH4G3yV|9rPr6ub}`gej#HiFW#fh-Db z#>dAviO5ok4l2JADoRCag(6J2YkkTlyjNS@Y^UZgu@+A(^SL-yf9?5hPXR23IRQbG zg06o^a(jm{8>k1LbtV)7NWX`Z=QNNpv{fhhkB4y#Ct);v_POW7Cx#1o8s~=abK_V< zqi=3XOzzkdc~wSToyViq2Ir;UP3mpAXp3%m!_BD*`?-qJ1i|4tl&y3jq zq}Gfl2*QJTsBVcl|D~QEo1zph{j8-m5Vb+Gy>PQ8XI(22IXc1Nn@h1Pfq`ziTAyqgjm6U=1#%Ml3u`KWKFP zv2R+uYZ}GM9XJ-%zcMOno0owCGIFGiS74*jF}FG$qUKs+{TP-o{cMc>>Av1Di2)@K z1tVOa0t*T9*Bx5d=G5k(Xr=LYifMK-5YT;_fsV;LnD&{{k6G8mQQd*J0)WM-h2al~ zG1O}!oM2Uk=wqV$rqgBZ$zA(6_yRW)ofP2@$;6_%B4Nl&5VC>J$2voFKGCg#5HnPg zn0f{3DNOYA8XNE;@k3Gid6IDG$JkziH<1482Tb@g(-gkm~BA?Jq$vBp3PLM2QA?taY3Rf-GbXgv0`xShXovjEO9gyf!mG zFfWiNq&~rrUd5-OT}dB1E!bmXv#U^XP|yn-;~>P`d<}%^n8dechwl_=2a0ZY z_Q@Q`^B8Duj*~9D?M#ao<4qHVfc%YL2V9(|2BcAGJ4H?dBc(CH5D}IFFC2L`nZB`~ zy2z1;!;feM&=rdVbV@*smFOJ*1OydQxRK0%K2fX`j<=Qm`;W;#(a(h`~9+LwWgw)8_7YF$%H4mA{TQ@P|^UVX#bqBIoiXy zM90c}h1G;%e0B&alPA77+6nk5a=Ho;Td>Lfu4p9nmxZD~V_MCUp(@T0)388;Cqk zl7s{}S#}EoJ&9l0$1#V4cAH}(dXj{~rsLVn@UpVv;sIX+4#)5_{Ho_TvjY;|F232y0JbdzQsFNJasYrw@cY|5m1hGST#?fFmyFnYISgRz zzLafP!9lSCd|l`iUVJMrhQ=16L^o5NOGXUg<6IQ9jTK-YEbJ7r8hT5r1IE2jF?n!W zFqQ&nfPV9fOtB#gC1uCYOv5((>D>+LuPnuFCqPqSBAi1X>`7Ui1rQ8NbO6j{Vo<$d zPVM##>=2M^PfGtekqq-gN-U&x&M;5gJ{m(Byv5{)aW=dU@K|E>i~o{>GRtqvkdl5x zfhm%D2bR)IB;GL}Sg%&ngv+5H?9b63?QEECndh&7qn#4K@iDu`Nj!Kk?M)tLCY22U zMFX_|ma3S4R~L^l;;tZ|G(=|SD=etq-z817m78)vQA6!1KtD1azz?SxelEup_E!9!tqh=zRH{_bv zs59#c^m@9GlY1kEB3>|Wks~=r3dPn#=9?iHrmSO;mC=vI7--s+TZ4|Tz}BuY672xn zPOWh?XMGEqf4b)3{#OTmOV#(OIe;~Zs#)7w*S%wU0h1l25)C!;J?*lcUF@c;1Vtz_ zb)xxh4V7C77mbTSEZ57-#flV)modzoR+-vPtxlSlCAO}@QHW5AD+8!8j7@Gm-4GEg z-b6NN2TkdQp?!uOg zHG>q<)>^c6`S(}6>+@q@{rd!N2C}CWALCsLW5@^pixtJh1zl-0z`enj48Y~-&`X#> zd$KEX-*jXlGBC&?57VBSi{)r$g1Rn$g9T$=H@3TAZNy)tCo< zWT}4%K85i>MHDC`?4y=V+?^(^hjxz(ynKi{|Qi7kjyU(C|J7HZW6s6-=Zy46RSvhv{Z9f>xY4+|L@4D z+WV(Xt9K6ecWbS|M?Cs0C0Pc`Ss0Udl6hNzUQWqG8b&f-p5IA^061gW8m#PGs7#Su^GR(XHr0l;g?wF2GUW&Ge`?Q2hr@AKMZwA~(Av)ePzQ%i!et z1nA@&zzG<)c@B^*E@Eu>bjp^>T!-pvb_h&C{QDBU zx+3Sv?K%&h*?YzJ?|(fGYd2Im;dN(2|%xWmttZIeBtYHv)VaindoF2 zACTSY`O%4$TB-&bi>GANanud@#Xz{G#e`tT!;8NZ7`9pY|CgM`l;&Iy0Bvcz)12nL zy5_o=qL>>A>j5KwGv36j1|)!VTuCk3;JCuXq&dSh4v-&W){F1YH~Q%aOx&}=zfW$| z*{-(e$-gXyZTGh7og%1a6aTSb{^MDK?#@mx{|n-&vbdp$qD17TV+fU4k8^+pl{APP z4pX*9aEzA^1qv$P`0OJRNCnAX0I(Rf5!UEUr%3Ctwwxgkc4h449_?xp1v>)X{Pk5@ z0`06CtiuTzi->m-snG_$byQGPc7d;2h?S{W3OVPVeb7`(i55hVXMKI5r_Zjv3qT(n z3$L`gOk|7t{1#d3@cz*5clcX$y(e(Cu5gaLQl4G$&VNqM3!h&OPz&x*QJo~~xmaYk zVmR>Soe9*3Q>XkQe7ILUomYRl#?hHVeZi*awRzY62&1sj4r9^-NipR#bkUyJD-!Ns zKP;d(A!@s!|K#)TPGf?~7wF$}XjqOVVGK`}t`?;iI;TBm30@!XS*feFd<=|S!km6D zQ&}FYH4L+R1r6sO3H|f%QI1>>yh!X*>3B{o)4mrO+ualL`V3bmWQ~Qkh2nf6YiamW z770f(2{b5Q2U+1iE8;r*NGIm^Xykq}FppH{hQY{82byE9k3gBRo0~dgou070l{H|{ zY{jhFA;EGySr1skd9pR%cao~jhK(894{-31nHQNHe{mxO$5W|wA2|WE*Lkczc;Z_b z^GP*tLg>|g;gLW`rUcoy+T8c0kasMva#++p^B^%$x)y#0Qi~q?OeiDs#cW#o_g_ds z`|-HD;+c7pu*8Vd#(8f7c^g;A$tI_#Ic*dKr~k~9@YRTYX)?epet&5$%bdZIiSAan zTC~wXPDH^7s^*Lr095DWOw2>tzR;G2d|S)Vo4KBwZ|tf~%2lc52yroDzvQ5lJ-z+m zxC8fJ)k%B#p4_Cu>ckbO?f2NkF8kNoGqJ&|Z_jobN5l3Wg6UBwZza_P)*zttTyAnu z%`b*X1{hWzL6rU?@eijy=ChOnDVQlsyu)RSw$ zRZJQR`jQ5)xCmDR?VkMmXDrmuyT&DXq8BI0Tu_U8v5dEi-rHuLwZ-(l+{Fy}X+;y< zJI%{9Bvo@H?w6EP4QfW_p_5Y#(`Rzryenff!6ZYZFoS1YVlQ__7i@tan0rN&wWKq; zcF7R~kNfEn!@Ao_!~=35H+vVnNKS-vkegO}Ex#CMf|Fc3|6NJKxCCI)Aj~XHstS_h z(wXRTQ-B9+&3n+hBGIq^3=UJOD4vYnc>{A#c(oK@Ei9gEqnqiSjGUM=B1CS5Oqk=E zrIQ~R{}NL-x1}zrr&0P9A{5r7W1g>Tc^yfuxzJ2JY&W1TU2FW$pkvJwNEwMG0=M#o ze-?gABWuMHt2#?WO-y;IxQB%={gekORsahwBzBaZAQW{r$y~^4Xfc^V;%$>>PR0pB zsw8HvKH$4e^eS&_W(|2dP&aAyIJQ$lYm#zFs#p6(enUUCnOmdTRxiWg&-+G1W2Z@S z?)OIz2WDkwUQ})MyU21Hx^r5^Z`zn@jNDMM(4AFBZ=ZmK0t zArE66r1MP!PLlq+;pVuF4t8>!pucga-c9F@mu?*qiiMkJx561k_yV*2b!vFd#}6*6m_viR0|uUZX%k99lyRE}g(<6FPL*Uv9?lS$0Kybb7b6M|u| zti1k+!1mXrKO*^UBNlYR2F=O{ziarF&l+c=PG6V*yWv+Q7MhEJbyR?q1k~u7<`N`2 zDruqwG`NN46VXm#(`oz={-*pC?~dxbqN(I+*KG~)xz$dBf)5^mP4n*l#CSPZ*mb}e z^ItC2I98Sol*sVpwkOm_p9^ZM2raQ+OY!;>HK`lhn&vN@vuiV$YPkU7ND{h!309$LvSz4?KtVTTN>nHR<6c-5vGe58 z?WDxSsk`JK{y6<1Vr(@J*Bl=G!=*Lwsxg>zsU3ryc#Z8IWE;GftnLDo?>RM}Ie~p~V5-)NcG@gm{%2M0lszNt)5a9)fRgPy z6Fr^8n(e-Mx{J5cxLQ7?cB62G6?U^?ZM`w9MD_^tV~US7h*)UKPHAjTMNhImdi zz4bemc8qGv37+sWnjGiX)%hH;R?$t1s{BkhfpaZt=Abg)V!tEyo6Z&Z#oiZ5@Ks=# z^?L~r*XHd^Z0_9eqVI#>kKFc;i~diZ^q+mo=g_O5?LJjghYyAl)jKf}_D$96#9O2| zM=8Zllhk`SyD(qwS@bU5=00PSqW#saHW5F{j`zo(+z_(_N4JyI#HZ!ne3n1ot}k z-Omk4BDY6Yo_ZepWDcu|^1-oyP_?8@22H@2iU`%+X5c<273I1x2hT$qaIs>j0(3^v{l_OE?TVs zD(wgA)~ZwuIF(>V(loHBE=sN)%QcRpK~?juQTW(vgxR?`0;q)%&+|7|9n-5+S>wI6 zvF4&Al>Ui1bDKJw#^6=}2p(f$#|HA>_HG4G;#ac`0719Gh;R}>2U3kN3fR6ResWoD z-UAIOoctZIb?N*;s{Nr9sP@XH+C&XPIir0ic+8-m#=JkD)L%zIDi+#NEkCa-zm_yZ zI)lU$$BD)_v;i&@kqS3BLt|CVNaA+?rGNlNkJeb8K}I!O5P3$TVlL66lH#n!a!$)6 z)!RlOK4yo%>g9el!ubpF-&M_wopio%8I3ZhoyU`=jZ6Axx;4!`zU5#5d^sWR%>l{3 zsmuD39oNGx`gFF_kk#J3@Qd^h^XRZ3e^=Vl5A|T%8Z-#AsUq{JRwW!vxKZiX>WIV@5Mtsaa$Y2 zGT%p7{NEuLOhW85oKrsJbS9~^a(%zTaUU2^3Dn9L?L3-dW)oyCSR$UJ5}4eN2bzyd z1gF`PQVHLvd)6^tRMu6#WqYF|;BV28aLtf7xM!m2b`sT~@;zRLOCPUK+!YLhBS->% zyz8fk(F?W&M`ky%GVYYRY5EguX;%d-|sbccV-G+_&*4ng+QvFS$r!jzlsEfMcP7YMgR|al5?~ z(XOYrXMAtz$H{}|p`1ziTju>HYLQ=LNWRH%Bsib*Xc4lwjo`y9=S1g4agZI&Z6!Cu zy9M3=RZX4;Ff4!yjir%_PY>11aPeTM(BfAFhM-3qoEgI!w(VnT$uCuz@v;6KU`*Ag z285Sm#Du8$Y97g3ZF4~%{RjUo;PXSP$?>a?Tmbo-%UZulC`VPPoVFvU9E%X#r4f#4 zZA>N+ocG;5%Ull2s>+Ffr_dr^xY~l-sXnRn7axtTk&siF&Hw=%iBZbCJ!&S@`hWs* z90?+m^gG}@s>#;DBepgP0zko8?}~#O<%MjT#@1LZfFV+`PHW!JbO8fjVu0lNB<3*| zio~95d)(n9tFB((3U|gh4{aW#NDHh+w~?3=&TI7?>^uS!pF6y`$m8q&k5j7GJ)zgV zr;1&DMnkN@zEqY=agLnKMHSYg#_j)3%NAhP@5m1;Qe9^tb*)i>sO$Sr@|ER1S`rAU zb8BD}5rt`>vE|u(t`nE40yeBH0h-(^FUCf=CxK%uH)wLaGi4W7YxeinPtK2;G_&81 zPBIZs1G6Nk~4aNUD4ouuh67VjWliMPS&nVt-ddv5<|d>yaBd zPPeDQ&Du`ua>F7Qx@lmbCFenF=!U z7p#4oE({n`kD1}$c{`FCdd7rSfDE4aM;G#-(E?lOEr+o7Y0yy`%?{CdA>stsZHbZI zJW-0o?bQl>o~%hyq$Q270sP2of$vG)sMG(vLuFS9&kck%#L1mgIZ}*NycgCOMG1?P z%lC}v$0*b1S@Jf!M_Co{Q*PInN!6RN9 zDN2x#A~kM|QM{HER7ud zgKB^l@$GvAwIC+~+p3XaQ2*Px8Uv%}1~8vEYo;aLWoDV|lih9M+UcpUG=A*)}EzxBZ{C&n{#^K0_^X0*ZPcjk3~_Z_k3 zM&3RjTcipOp4U{Kf23}G`0BZS(xIZDHUpEu>%_<$x|qf z1%+g8jrEVjxeDG%FRF9b*R)&BksP26cb2X^D(&A0whpkFbqIB+54HQw-Z(?*{3;>& zO^5;QhxUC_N(X~MS2MfOE}E0BNS)W-WKXj;GlW}X9U{AE4;r`TScOG!Qi{Y8YNHOX zvY*T@l$1U9CwO$*#bf4)%r?@;45X;~LeCKv7h=Pmq4gKuM%_Y1PnnczoTodUWOYS+ z=#Kg<^WWF*|9;9u=gx32ohu0{g?s;`vv1jEpC0rFsg&nqzCk^yw6o}X$K$j%@A)uWCz;@) z%bK@OBj(|e&KId<;eKPXv;!fj6CvIsq1mq(_6!?xcf)jT8ZbuTNgrjCJ(!|at9Xaq~>aCsrSUKHwC3+ol|4rVR zNc{wxzoUHPPy4!`BJjlCy7KeOfNL+KM~xHL>D3qUN!MLdjb&Q$UqgTFW%M=^8@6M?(?Hwr5ZmJF|eorn7A2Q?61XjOfQCyeh{2wLg1py zz141&7m9OaQH3h<*y@0;Z`T>!m&-wY(-+Ml)C29pYvtWHi|PZJR^4!#s1LKD^=F}u zyow#4|HSM4bsh@8q`p}(dlUXuNm8$R>6Oy0;-qPMxohIS=Y*1Re)#gM%ZL+x| zV#+(^Pe&96H?#(KyPqySHQRMm{#tHY7PL6|bZ}omL-iI=xgsGY1`q%uFn}PyhDeMA zKu!PyD~999>4fa?t(CYW2)AyDc3ah0DqPZg9p6?xkpVM}WYuY}namL|YqFbc zubnPH{J%;0j)wVa?HnQdsgB0Q`dh0_scvV}@(b(N-WyY$FFw3-!lkh3bv3WP_FQbT zpYCe;*zLbPRjSwha-%Qo?Ayk4_p8rCC}PUH`aP{%xZ5TUhnb$X?FkIGUYY)z_TA}1 zNuN(MZ(e_$tx!+BYtY+qu-IVs!r^Ui=i!IeN7H2neO*T%dx8!>z3uD%`57BW$!^%+ zbFz)k5q5mn|K{xLT(w@g;Xv=N!vhU{a5Rz!00mR% z{#Fu4qQDn4Pk!MiY9dZdWwJ=u{+Cq5xWIOr)NPksy6j%Vc822b{q0Oukl+qRgVAdz zOPjB8CtF|oU?;~|Q*bxe+|p||&)U6lH{UMwV7I_2QE;!&HQ#Hm$g{3-uh_TiVDGvA zxZu~4;P+l%OT+dWzm`S*KKNRW0txL`#4~#DSEBiv_N&sQzwK9JG=&aoaxJ|NY75<) z4(dumza7+9Bno|NsLA*K*4R+j^sTA6>)W>%t>Xlt!{(0n-iIwcdrgNg`+t8se1!!G ze{UUS^!eU~=X>$}e@S^w;iHZvOP{08755iMUF)HTN8Ou=!asU;@_l~1*{^%?qxXB) z;g7!Kap9l+=kI-f4qWcN_&Ip<`|#%wfK22VOTy%PJWS5td^|!W^ZghH(GocsrML1u z8H0N?pNz9U{eCjRktA}8=PB?#ofN2VKAjTj{(d@*m=HOek^10!_Ez?5^VvJai|=Q% zs$`<)a~e#3=kwb9E$0jRGDqi&##*AkmdvgEel1&jv~*g-ZZ3!4J0-Ogk-HT5{a($; zqJFXF$#f0``Sr0qMbgh~Kq$$0IkV_Vh6$~Z^@t?P1XTs=xkl0$nTwE`kUJec(%i#J z#)=9u8Cg5+;Yf^Pv0*DrmNw*XI|st%UYe@UipaEkuEAAtSzk?;9%1BE4RTIDQY@?H?6XS}GWUmQcdk_ak!wV)U+idvZfvQ$ z{!wx1cZ+K0db6X59MB(?@Ap;6jW9jB!!wFyuQ4VZ67c1Q|M;`U8vXwBMOIYP%R-6r zHT;NtDTyELV&fvy{TSj6NVfBbyF%t3b5K>1YiWsP@F@S}Y4C&|WXRrK}RV?wqkDb_Pp zjNSOLdl4t8PE^(K3GH!-;*&H_eeMvR%5mx5lXQRcS%ELw6Y}j~2CmU+whR1(^7Tok z$r}<5G9A3yZMxi^nS_gJ60a$LnuSTL;o+aWtA#SABfc6{)2P&G4d>#jE41ZjmPnB^ zJknx@KzcJ)N9(xIQhwQq>`nbp9_WkJqo0D43ja zmp?0-ORJNr*Lmx0dse(MQzzX$`Sx+d+4D`Rdf5q`cY(!=n9mCJ@*gJOh4!A69;ekS ze$|>$iWw(rJq5;dqznQ)Q6*Qnq|dfG~(VF^Oxm9NCtRS~PREK8|Og2-r~3<%B#I*}ZG`m+bB_0&_u;&`etS zLWAnzLK%dfQ?fIK)D;QOiA^BNXFxxU%kGKRM-c_?;@u4-V&<_?H*ud_=KTjq$azhqEAq&m<07Y1nXH~Q=EziDFaP*%O?QsC z!}g63=4aP#H(T2iy_rplk_l$i(5J0pJ~&7(?9{+unhW^^`t{C!2d zaQ%KWMr>$bF^?V}X4w2LBZSt{y8-psFt0AHx^v{Sr|2_Y0QLRHHARCv^X)?Irrz5U5C>S}CD zJoc2TgiiH!;`Hq%2bIxuTwu*H??5@-$qhb<(eBt{j1?B&=C5)3xwFnwEL*4XU%RtU z-FhnFs{85PXLs{oGpceJrA+_XKH+tH!jV;)>^MnP@$>_$l#r43kaO%w8s_chtNBt* zgmJLg{Q(&;)8yiX`e}^*UoNZe58tjYgY1%84jpLwRwfL8w?8k{b5iMBv(E{wwun0N z&+J?O`uKU*;@=-(KdqV7(5unM@;{RU?{AVBU5?!Ocl^Jg{J6ru6HMm+KPcatIq==X z=+D~6f4_TXqjL2fI5@%@NkB32KavQ%`VhjUfzAB7cFfJ50^w@TqyO$Nc$A;X(8CW# z#X=-S7~_B3e?A~d2^WN|DC91&;&0E~6ZLt_DedSb=p_CRtpuTUnf->lG1~ck0OZn0 zmmz#O>e5Q=hMz`J5+wu#yS35Uaz+-8po*Qz3C84})!MjhYKS=HFfayIN!$K~@(Ge$ z!daJTE2eEE<~b0>>>S%V5)J1B$5lpuY9|*^r{o!qWo)C!%KFbQE|zB}t|K;%c%C*k zfJTKgChwHIbC{N*I!-i*9JLjHkWIlarMv9GU&tdx?g?pVO|W+cON1r3$R@g4Cz4mk zx)&yT_ayqRBtE`O^oJ(}$|eO{CxwP3g%u{f&62HI zOrvm4g;je0es950nf6QkcDnX-X$7*A070};N&#hddg!=L(ia7YSQ)ukte|6Iup>r> z^G+xe_DaS%?+wCkL`tA{s((7*B z%3bX@P@ZuMQ@3p3soA=xzT*|#e~E8`M9+d&He5UsX`OQt;nZ z0gAaWR<1DKrtq6uzSxr2e`E}>PFus>tZ(g4i8vo_?9t~~iovh}cR5L*leaVY0*<&s zWoRz5NoH?$CWSMQs}(7fKxU`|eN0+ZeqZzgqWr26O0#6S)m11zpT{I4-W2XwIti;# zcZRp+DZMU!@f(Vl6Lf_K_<+B@CP)r zsbvN5nzdq5c;3c<%A1$N1|nDgxT(z(+RL#mT>>=$4HrQ?Y#ktL4zRKh9>300hyvm;MJoNOCp)>Ohk zO^seyotN~{VgtI$PX$`}@AV7nh-Q3o^Hgv1%vv)!w2{;m^4A(V*Ca8Z%Sg*2X8M&; z0nl7)suENT;{pN>yA+6_rK#8QF#oiP!WSZeEe7+R##t>7pj8_dl`bq^G{9GEU%Uq% zNPo6$aN2sQ8slcAlSRPPKXr$IYnzl~Z8xf04UtWt6X!l%S84G8%mFd|Ltc0)#0ylT z%V{Oo#8BV`W&e{(2Ytyn+%9=~|1%%7m+rM*zzYKcQrheS=rILhJb=hF3BT5X{%j~! z`QW6G!dHI{(cGgaNVd=f_&PL|5^c2;W%*K@w5X~9S@#0?4`E%qEo1?ekTzNIH*`9> zkU-<0N+IFnWGM5}YZ$WgKCt1d5QIceC?_Jt!`bHe*X#ESZSs|kAQR%?=XBe3B6@e^ z@66W&R#k0xyCt1%=`dAfTXKHSg{2?DN+KAQn;_N^4GKFdGYghn{ynZx@8^eI?_8Qo z+~r>2dVdl1saH}zO_luR)EjzM>aK3md)Peqrw@Cle?*~w^kM(_vwr;Z{;9tHnUDSN z{`Ak?8CXylSb8|{e!4mKP~cTD%ryt;y*O}}@az7Wq~aCc_-!E(`y8^&wy#sD53B~f z5*X|e>=mn2b&LzRB9>v>2?!h;oNqE=^zUGkp%dfn4Iw7J&tU5s)b;eeiMJWXz^%LB z40!m)nX;|Ru1!{Z0?WYFDj#5X6jVhb-685+auN}h&a#(N|WZNx@T)h34OU}lW$A+$k+Vo8b32gM%1Dn9P_H_IRswO zNtT5XudQRG@`E^A1T+h7cY*T8d!U4@ZqWRb6n!Y&F;LuzBn~%1<_5IZ9Vb9W_m~AU z?I5=OkhCYI)}fzE&f zrpQd%_ix>;;pbyZPW!)oT0HgK#t$~cQNL+k{J|IJ@ zcDm*i_@Q}9H6YmcHKf{668bT=a1BHb+_f)av=fJzDos32W? z`JR|L=gyhA=iW1OW9I$~`@`<+6>z2JMI?BSRtt{nUW`>pDS zPYn5)vH#0&smmd|d6Ce#`=}s)w>w2AD-X>CBNURt29K38Xj;1zGE-C)O=&TI&lGd8 z0Z&z!5^pvs@L%yOlBqF^`$({ySxK$<&PEf=5l{Sd2uiozz*{eiv zr*KTszW-Wpkvg)kg!J%*6Ur%!#8e9e--*LDWP6t>rlj&58c~wJy$vEw*kWb#aM+sK z31xV7&|D=fS~ewsz0OGDx>T_-?>VJD_v-*6uxNn)RQWUV6MIs^gFDuiVlStgP1EZB z$5eHnAt{VUwo`5#iD4^?uf@3B+QFaEv6A+J^-(wdH>4=jq7s8*sGpGL#f zsVI`FRY0fs*QSA`v>&V0#K#Sj5v66!V#Zi?4d96XL)?6w5akYl-F9}HTT&2`;jX_eaNtM>;TC3w)i;uNt z9pkt2iN|$z#V+v&$o9Uzw%A0v)R)6(!F@x5cIL`0Z1_3cX9kW zoV?zSNPA(J{-hD3Tkqg>GEUv^>BS|0N@wdSvWBTVFUW{lrLQZ)Bk@c}3tNnvDAs>Nw<#E~ib@!Oes<|nL_6JE_DPqSQ!G-rNPThQvhb97w~dgFwxNh60y zX_1A0xZURBLwj=R1y?p9wCxqDg`J%Duh8B?X@ierg_06GX_3a0)Px9PB)KyF+vJ18 zu;yn<6>MF|fE5N^!c+Sy-ZxlgKaZ9$Id$jR=>;K<2k#fDb2BMO)$kE&DY*|>w<5_e zIG#5II9~NGs7YVk?k658`Mh`4w9XfgEdp6Yd&LCQe||8=eKeb`ea|u|U*JiDR;3?8 z{Kh$Y0zWUj^c=QrZP@rpTe3|@#WsVu{9Le3tyMMhRn|7+;xH4k%{-MajW=FxwAQf6 zoFoa@|NL}_TC+_nC<*X(;T|JPb8y3i7ICMH;!*r&Rg&n0;S4S^f0#X1>}4$Tc!~dI zD(#)@VSR>Wh}x3jP>1J~((gH#b@l^r5uVI{p!bD#T@0N{w(i1aq&Jlt|~(%;lH!n)^d;g5=mLw{LJU5paeA1jAGV>5lX@Ve*ZLEEG2 zy-`8`FT8_o>1BO-!1JCL4g9CwOSe>Ls^|OZ?R?^otlyGlgYU2P{x4IZe{p0cBt5$> z1FVMA|3QWNem|MI{Ej^=rFqoN6?tE9?ec+0wZQYInfh(d*zikzBjpqk_Fy5s3RSg* z#>pMn)jk$N_5Jl2_HP#!*c8UHR6+-I_)EBtvFlfabOQ?Ql+V$PKd$j3u%{23iTBrE zvP3aIPlbigiIWi9hq9KCT)4pY-T|Z<&qx=-^j4oGI)oD=DX@*?_tQc@7lYU*)E{yT ze4E1|O;Bt&vDB&&{wgtLNhBUJl(&RZP%#o#5_WGh5;n#`YXKr?!MS@N)8{CfG_o@T z_%$<7GL2x{j=Xoqk-UN^9*l$!kzxmZ?PH$$L!rnhlC&to)fjk_u^28I93s|6 zqQR1wh+#?!dooJgSjqzuqKHVqFB&pLDZKz%EFg?>!OXw+ylKG<0949F+**!IooH+- zCIst%rig&XSj12aQP3R_6NAQLX~ar1;S@vY*h45$1e!=Njwy{8$_cPofV&3ZwK%jg z2w)PWy2s9wZvmFOg2fs%szDK{4p9FwL{BGmEC%@Vm$SnaXzu`|I};ga;@J%o2%AXk zOVD@`u_-L^keR4S3=mZf{-}m6E&%98n0s}EcoStG2AH-Go`=E~H29h@aFhjqDu(8< z1n{q#?;EEGbuM^4CM<%{JKd%e(BOicKPS$kI)3tu;ye|gOM^+KAuQ9VgVSj9(&)R= z7`~?6Jx^n%OJ|WxXR}P_2u|nBOXu!N=lz<_f1WNtmmwsXA!3;!7Mvl0&C8JL%8>b* zk*uyLN0+H2nW_3Wl0QIsDYul5DjKx&f-ql67b$UKWF-tKURVp*XNbeXYftQ}eQ}-MI`K4*)3jbF8v512hU?B& z>r{p%Ud2=AEM|`WH&kenL{u>zUa<%+!r`~2y9`M{V7|D-0avBlswf!Pgx8QL;)-SZ z&(^|{gloD!yEUXYP#~4;@5#@y0?YaERVn^9=LVu735kfparhIsq6T$|XNizzY;E10 znId7(KDou6ECCoKxu-!Sk*?ZcELiWZ29zm#o=b7N6RE9AHBsTpklzC?oPeVI61rOH zDC7FzL$UH!(1o~84!dg2HR$@&Ns2ZeY7Ga+zzxZ3;Y>uG6>r`SaPtdv ziwD1IftHH^(i%7`E6<5+RnrE8xv0#w9$wJ3RSmb1l~n!W0{-RIa)MU0);vtwK+-r6 zAl-OO@VF*Mg229!=s?v^o6jc7hvuH|v7IK9v$a6aJ-i+;F}se{ug)P`+}gc%BE8er z@I_S~z$Pt7ZPqbm6tH{W$S450E-Af;8zIOd z{FzqXG`#0=UG$(o!|g8Z346+bs+6m6@0KGBd+;I}ZjTkeSb>Zr zG`H)cW!RR!5gxXvQSVZ#UWO&0Hv!1@fK|BnTlh)F)7Og)xwK37aFlc}F2Y}r^x{Rd zgNpK^%(%QbJ#_xJha0Wanjpt~zu+AFCIQUISHg=Rt8^TcF7;+^k_x(@oXqGHZ||dM zf|#C)g>n~#vy#HM2APmbj5@6F4{%0a4gEU`|0cj#O%yq{aQh)Jc!4bl2na3Spl3xRi+w*g42 zT@Tm?$1a8U9ZE(cL=gidB|}e){XBMoB2qO&JW_r+hx)SeWn7>qk<2ZUq)SrP<K3xEY6G3Hrh7qvZn+%brt|(AHkYLfIL5^h(no67xin>d%N#6J96C7WQ@-|=}72dz8%V${GG zK5iv}A;g9V$0uQm3aLnIs|2P=;jn$lH3swcTlTX03A6wlVDZjc?*KbsOb$1f0d-6! zx{k#t`oOVpjk*`oE|=xFM`YH0tSPmy{AVDc)B@Y9Pr_LH_UAHyexLZM)e=mxwDlW{ zHaTQ5ujsk4+vm_(i2VZTfn;mUAjv^{JYiiMo#(*{#Ky5ut9kqy4Za?SPo(0NyD#Tj zS#8(=3&#(l$rM{nv(AL&c4RrUzk$x*zMx2Ip2;=_ytnq>ldlq6Kpf7A_*og#e~;jU z?-v40zx&A}9&UV=XmDJl7F@^r77@(MaHLxoz3{>+yjSgBVj6nku?N!>>t~o*COlZg zD_KKC;HHHFs!Oc>mz47!0QdqQVj|nm3;xQG(EmHklZMARw@GlY03cUrki&WtfSevk zlE@~~E%x-+m!Ewf4wPXZglCfBU~n%mUI->)TM4@IXGub8iEx)=@!4q+*|2Q6y@Hv0 zeqs%DQUv#cG$sL@5ujmR+>$Ghu0cY-EZoo=;*3=P~B4N6-Xt%q6 zxA*&Q|IO~;-MwMCy^#ldqtExoiuT6)_da~zo4nbZzPs;dBs;MW7P1Fk^EOTFNMv7w zc76N5872t)it4|!CT4#BdZQ>_KKBg1V@mOO|1Jov6Z0rjBG~A4bM*Yq2>L{6{Pt?? zPh$2g0)9g_^a{JiWsAB-9#&*YP$HB=+lqg7yT4^D^?&iZNf>Y-s~}2ovMqhXt(c$&- z_A#(qz7OJeWRI9pVmbH01&(jX*(|2*lEU=K_-#5JSLsQqX@xQ+W=h|((THl%Vbs@E zS(!vfg)Q_pG&8wO8&F?AZEEKVI!%^$!Adj`JUpeDE@{K>UO=Me85T0OKTxV?b#JuK z?HE@ZR_Ik&J=&SBsrRQLi-e~_encaGRPaSmm+~Bk{zi-{?w>ep1{l(3yC;0gH zCJ)VPRQLb8`NYj}3&H=GPyFXHU80)8VY_(bHdkZ%+WFh!vByG_ z(^MtacIm`xx$DuNpWl{FeO88IDDOR3KJ(jnuaABEAs!j9Ia3j$+ijt8Ts2+gvvng9 zv;Bv#346IaUV8F-=krvdL2$1k@rM45%|oWnpP#31OlKsvezaz`f?R}=(=C4#7KR-s z*zyqNJaYJ~N4m*vb6r);P<#4#*4)7}BKoqMk|}0NH*T=Phs`9d=R_|aT5($5Y)6yC z;X2ioDImbfcV%{T(1~Cu+eR$Tdmh8U!fjVcX_AgCzDw}U3=!n;-fNtpB zmV%Ta0@``8;9PuhN5i+j{p z4&F8)UnSIy8H#(hP4Zg7=~q*QRz#$+p`Nm_Mj1ODOr^eZ&8p1U#Qe=c#V>yRB-$L; z4Ixu2k5?1))&ZX%nLh}5#Aos_N_J1rK0#DWLdEn#AzCEm(c>?alg}PoxvQqPTY0uz z6GiQ|9%sJ_|8LYxIL;j$TfFW6IS8+w;x8BfKMUdi_iE-NHyKP&?f*#4)HVVBdo@$l zLbbTP@4u>%IT=^_`W(oNJ<-jjqr{99MUj0YI2)lp!Bb_xWb7OT+ok!Y9Aaw~Ax=FSg5byf1dDs~)TpZS{{`?AALvbM6WZ zQ(QJjd`d3c>*`2m#i=E_Uk7D@Qi2Dg_wg@NmixP|Qs8X&ua8Elr?QUIFOy+}5I6lk z1w`WfvySCAo3dJ4mx!C8g+B;co&6Mj@K9|grS|te9AEGBN*E=3GOx{~bm#(r&{XEP ztjx;UhN-qt{E!`wZ@A!EU%=`lUUvcX&kb@Y8S(lVw$luIQ=&k@?PJO=b36W?o4OXP z|NO?cgdjMcyZDa1S0wGlf+%&kH;oC`U?!Z-mY2ezlLS{{N_K@DpWt4x-XpHUVI zsVAHF!zr(b$Ou?sVmu9F*$~7K>`biM)tf~8uegL}t|L*;R>BC=j46=mLrLMjXwsp( zZOt0B_r@B6s5OQa1R94ViVT=Hd1lY>B{k$IP@x3p4;_Y2M~SlCLIYGVJU0jZGIB78 zKGERnRdBWQ9^iCWMCFmGOAQaNNKgRgN&Zve5c{|I7>U$0?u|-3Tq5xsdwjBI8O(@|{Z9O^Yw7?eKK(TF*m>uwDc^8yP|VdfoV3{_I&-JA_bQga z-Hw6*-N^jJlTEY8P_E+xx|~qcQ*ms^{INI3OF}<8vr_Sjs8!>?5GAJ%C2# z{nsmyb@i`qsUv%)%mc5yk+SZ?2)pnXxp@j)z}cdvy?!%s$)18kqxGbc>0|0#PNEK| zCJH*zP6%!*;CC6*!%zt`)<2wxhx>Zf{xnU8V$86Le1ASibrT*G@vNSuaLWw!^nb2F zs$1F~q<=A`chVr)KqcoK1tx1eO!swE{IJSYNiOJNO5k=8I^Fk471Ky`Xpw&?){Q^pqav>hs~J5W z$4gVVSCPLR3k5<(UqR&}o{pjmvmUW@I_4lRfjSbl&IQ7(kWV4kG@jumdRW_ z$p?+S>&KpvO5Et>V6V2TFt28(Zcciz*MCz6uD0~C7faZiv#Eib(<$uD!7@3Z8;v?+%YDMuD5CqXG^ zxhWT&DOU?AH)kn0bg6ifsrZ(ugu$swhoR75A(F1tTL?d-sK0a1|L1&aHT-!lJTy2k zbpBl$@Sh=kutfSj7*3#aP=@kCfG(kl%$<86)ds8Q2=mT{JnISN3YOU#I;R2{W@I+0 z(KcE8uckq8A*<`YAYLE{S|L^gBS)$G1D>)XrN_(=xB8mGIe0w%2s*^~bDBcLnYlU2 zVHO zM$^c>2P!IMFT@aTTmBod!D*R*G;_|oS!;1Y6N-o&_-al@_AMTgUoFeLW1kje`Pg%l zerJR)^e6BWmCNOnN8}eoKVxNxcuWjJJ{)`6$3EagYe)vl4Y3#WML%Uv%R+nwDvFI# zsyv~O0QFcvr5XSp1Ch~B=8ZXq9_8LKgv@Hvaf$`zVhQM!VnN3apjs1V(FTi-1rFcl z5Gdx;HxJEvnrYqfgTw(uy~h8cR?3gzpQ&hSwcwYCNZr^ zHek$_Nj!unSDt#42W#XqPLc$7w_M0T$QE&FtO`)A3cP6KoqL=2RuhK2052qq0Aw*O z=uoZ#j%dREo>RsJ>lv=Td|U+xKLt(DWYnojb~Ui6JO`b7)B>5d!dvF7$d^SYz|ju4 z_|GCSI*oAXC7t~vaZT93S75a(Z+DefR;%nzTG{Ytkmbo^L$J);3dGlf6$Qh0G{HM0 z!6qe^1jt-$adECB80ZF5nXeMC1S+s76i;on=|u^-eZ}{ASYSSYBLzETRgQmB#oMH# zf?Mkjmf#^@Huz@!$SdVev(wY8p-OY6UMcm6N2ERlHJL4?wO~7NS1!SFB3uheUlpKsh^yW-FN4JH5zSAQk_KojN?OjUa~`_WX&*H{rUL z8pOw%iWoJ(j)cw?)j*?&vK$Ahwq&2UH?%#4h5UlcwFZu8`Zy})f_?=sT$~3xbm#71 zo)*A;3g(UHP)h+hi{*HVWG@g6ToFz9X^kXJ6`4C=Dp^cp_=390opiO7 zAtL|&1U%RZMuxJQFE)~!D|%mB14q8}zkQalmO*&HHp*+MBJ!<9ssHBRJ~p&7MaYQp zG>go%|LlPK`x`b`!JC}%_u~Ln4?w~rnT zZvhILb8>R2a`(@5E7Tb16jQlP$Ya&%oh6XB{mHsB&Wx5sI?FVs`2IX-3VS+>ThB6f%;4q22 zj}&U}G4M&Gv;Sz2H;q>3cC^ValpQtn9f>axBTBXhXGXjz!+lc|(JmzoG~l!)M-1Ob zYJCm?)UtE}w5gW!>6D(q+M~VJ%R+vIXjCxca?M!@7XmtxPlMmRRZSdZa_W0e4f2#f z!#}b*U)SS)RM|$;?`_jdHT24>Zjfk5;qTLTCP#)?4v-zci&wFi@Lt+eBgr1H^1RmPiJ-ANu)l#-v$ZVhm_3`{?xJK4?Ti|4lLUZ z!$BQt1^t(vU_)lB(kO7~0r28L@I7(-*Q6g`h9dIb(<$OIc>(MB(=7H6JEaxRu^EB9 z#h=7xTE_F>vfX}{R+I3iC)8;pv?ZqVdv7@+WVw@uCwXCv4EAqjZVlNvp4B|sdbkSX z`^tKN;T15OI45Y(W~v5$qHe;NfbATfa_pcw7u235c$_@S5U}znK&qt+lwF|fJ9s%Dz-QIwn%>?18a4@ z)ZWSC!mVdH_)_ViVPcr^G6@#HRqOB#mYO8@Lw)f&j~e-N{3AO&d>*F$3apc*QA)^r z#Q2h*pmja!s{r`?ZNTTYE-DjUSO|Kl_kCFs7_?|5GsTE-xK>$Ym|?)CeRGusN}mEs z``Ti-1q!d;d^ugJPMQtC{jN~;%!3E$o%azk`A(gNul-bArvAwU;djP}AEYyl#53Q< zxIZ)DcJ<(Vf!Z&caKmt>)g)~6WBaID`xbA}zknBzfBQ)%S&XaC5Uf6KvIUg3zN6?r z)8Rts`a!lj;O1Ew53>H@J`nH_owWY&O=-l_pnu)3{#u91 zenaiDXD7-F9_ZceIL0sH*z7X!#l!bqRqrg%XcTqdLX1a3cSiQzUtbDJ`lIB9^L|-f zJ@jSJ)WHvNxqez77$P?pyr<@GnOKnaOl}jdW#A^QS{~B(f}VJ88%4HHSexG2pmOi) zGC}vxuiw8`{AN_LUHvySu*tsPPV~ETNag2umYvDr*Lrt%f5Xwqti^X3|H)AuJZC-r z&amOlbN*cLQjhwYe-KapFaE>7g!%#u=YNR8c>nbOrJaS^+#Q;kf9@$GS-%%dVL!Dk z{xeE>Cr`P`ra`Lo0@p+?oJ3xX>i%lhg+bf||rt_}{Dx&>d0%K9>w@%|*X^UCn&kD&?p_9SgDpLb&~`h6JlaNvDEB+3VY zH$K3-hqv?cmqbmMAAD%1uvd4H*TiX8l#175Dc40tSLE1|Yr56z;gUo$f+}J7|>wg{yBhW>n77Xl_ zF(A8Mxy8(yY9gJe_r}7^x_T;7HThNmDXop64;IvM^K%->=D3CM!~ymn(l2TIuVQEI zMzw)Zl2?{<+lJ+uS^Q3mfr!ZfsG@FRynvIA4m?(DnR^CN8$%E>iYS0(Enr-@O4}B* zt5w@Q)*be^{rJ_4=nSdc&uVra4o3VZ2#?}4Ror*;%GES^Q_QftxKI3*<4aTS5}IJ* zQ;ReDi7w-j4BgtdRtVgzGEtC8Spmyn{{f4iaU}nkDz?E0weX)HJn58_AzW3tWPRRw z&v2hTlIH!*`UHLblr|?AX*!Ymx~3#e&`dnE>FIlQY8vJG@#zKMvxCj4Dt(JWl?ZBC zCx$jAhqft>(hAAx=i`JdSQzYeiH%sn2a!V8E;hvuq49mDA{MPmB#C5cxg4O3@Kb@w z4p*`$-305Dh5l7^eUoRWvfYOEj>3$hj}$qT6EzaJOAUBGxv3@jqW)BWdS`9x6(iPCxKudQ<4-B2_|T!A z%)dE!iRO(a$7E9OGd>z=@RQ(MY+_}h>-Yjai4y|t`2jPk2)NI2O6V;^!FcX#A_Giv z0W_8Eo&!Z0GHW@&)Xe&YeomN*YPCs%UCh6%Fy9xPpOfVbP3X`l)orl{l55dOeMP)% zR?Qe5>M~TyqeoWmb>#I1Nr5V^X>rL}2QMkPE>22KX@3zRJ?~4IzLTZTFP{n%*!lR$ z)HRd3^pbu}Og1jrOQ1|u;(ALxX~k*mKE@`Rm_-A5j9Cf`?EYdE&(khZF7CD2vK;1I zW5VPj=8syl<9cewlxrV;0nf*5PlPXC??qZ(?69#K}?obqRl2_)XMBg=$I3EGE zPcBS6EG;JI?`FlsW$)->(T99B4LKjLgFPC0)Z*ba?64q~a@JwCI#j@2H<+|~m-f3R zo^K<1D*T%OYdTNq1=E1e4#1{rnK72;ddH!86K!?y)Nk~kidR+IFd8$T5R|B~k3q9p zsy}D9n*JG&B44sxklg|Kc3^@QC&Yhr3?$skaMEk? z(eOvli#(9=bB;4r`)qxRIgKx`(pU&PyU)_OUeqdwJAjsW&GIoH`VKg(??Xn1Z=!Qpd|!E7X%<2z!YJckl-g$69Z z#M{YnjRt!YrJ?^3fA*To_SOnKd}uMv!FuVpl{{^6R-?(5P|Ie~K8@=oR9x z6kTq($ZHzmgb|t)L)cvv)rD}-60^ncYzCDWFAOkhCZ%xEmx5ec1~@ji5u@9Nk?e(G zsve>cDY~tpdD}2b=PV=BcGI?vzlXE^uR^$@rU+(m+c3(MBTxv}3%!_KVMbJk{Ms?N zZ5UNTscsua?#3@JOsdsPYmF=1O<(;-!^pJGdehy!@4~bZZCdZh?qT`ysafl7t;oa4 z!}{BWS?8!}LqLUx?QO%T_owOWkWG(=*B9o4gl3IV?4I`IAr>PXW=#oBo{o%{7T7U0 zv*wHnPba<*%MZ?GEd`sNF0z-F(`d8SGIlRF-4Ls}O0%{)Cod1%ORI%Zv-Xw>FE6hU z>*b$j9X*>~KB1S^D}?5qZ`i&4l0s}YILx~yoV){GT-t0WA46tj_XBJ0SnWQbX<=Du zfVM7u{r!}tcKO=7ty$XSF#Aid!u4SA=Y+2({W%U?sXn3mArG&9nh#uV`h;CyKExri z80;_k6W+xA0HRC{!TSC&0%X#ONGk^;;u?LqZ8B+5?L)`lpJ-&{%8s7+nbt$KYX}*5O!r+u**M+nqjFVb><1}Iv&Z?EI0ploLN3Z0v=roLFYzl8;OwO5f zM={Vj8J7f17v~zgu&PXAs{A`TWT;qN{_VvyxE;)L)F)iRQ@WixO*59949%ahG210k zVo`3uJlWv|T~X1Ko|R+q_P+PbqVeAqQs2*Y5<;0Pqlkp#{HSh|4En+&P=>VO*dOCN z>8J^H`{emo2W_1#>~!!7)Zs zqSP%78p6^CeNRnWx|iT<2@UEC$W}Jm`1d}KC6fZ|l6LrAZfA#wy?P#~K0@k~7G8jq z3$Kgyx&{TJ&R5BI#B}*}!9Q|A^w>e6d`I0qTLZf#y7PaL*GXiK`uN`v!3RVeGJnt_ z!Q@-E5S7yF6p+S=25a|N8QSlP9&==Q!XK-q6#=5(n3C!4NIoMe43xn_KC$Wxsf2&@ ztMySCgLa*%u=M)d2U6S@dB?qa#?{dbi05_d>AcORCA?Ns-FowmbYgp^iLtGe$H zK__m@O7`cs3h$4!!XJ?1YL~H3Al_w)PRC#v_T4(2lFJM$+c4^D1M)ba z7t<-c#}jAU68`TpmShCF9EH(_i$~)OkRP>$3CP0gp+W`x2esm-ky2^JGVYMxIA6tX zymkf04!uTb;O@Xz?at}CxNj{`DPhHD&{Y!mHe3PP5y7|~7%`vTEF z^va#%%J+<$Wyq9YUrOYS-ToLVV2X!!r4^%n3EoFx#DKR9yF+0BAyqx}j-$8~j{xP8 z!u67pl*k}@yAN%rpqK%rTwFHNsUVN9%DvV>Ypy5v?$DrOSMEmB^s(EgBVWekw>2qk_9zJj6kzZ6dZdr;sLpo z6}9*=MYK%ae1@Xe22?5|Mrb7bsc;JmsbGd@kM8bx{x&4V_kJNtfJs=x7hl`3CH%y{ zE19>YCbO^6Q6;%kfH*uNuN8ya!dV?HBV!tjX)@H)gKa;xiu4F~JQI+G>r`?JY-LWot`~kC zpqh{?@Um5W^xwc>`Tg;Y&OcG_tGBJDUT8~X3Vfv~r4v89!Qb&A14}jqR80TCF$DH83!XD> zhK3v>Ld@hwNJb+TzEA7T2CTZDSX7PJAMugN3K`%h-}5p8>_7PmDRI9_=luBT+u|o# zOTw%TqjDUL3Y=yHDaC!;EUW;P@%i7$D--!M-|0k+sqMB+MI29`}~Wa15Y zBo-$4%?6u#WWMxgU`VRXDttxU--=$un?)P+OC>5zlA3!fH#@D&h`++v%rP=_lb*IaR4FZF=87G99yUXS6h+&T1i+wx_2#Aj*e zH&M;Xm=M}AVMbymPAyAi=y}w1RjXmEw0>OYV%T4^HMYe{+9CCrv9QK|`Z3W8Ck*%W z`=FS`u$2kSm`rgGR5cSSny90t(?vNp)*l0%mbPv+jVFyl2dZK&mN0L=;bu5Not1|_ zLf+9%i1oIakdHK#eD2q%Yuq#X{AXPlo}i;&2GwBwND^RN*E2T~qtg)ZHN;lLj;9~7 zA{eeJV2aYi#&3TOv-_B0G1j*$^+&0Nds`_u92TS^FfFK+ z(4?6azT5_Vv?O(^CF*?Cl{RZCt)l$n7}}hKq0Pm~mF`{4`krVp>moc5CLtW@6XuA^JJH*w@dE9lD{>4iVnP)^25CP} zgqZE>CFCIz88LM?4#Y$^Mph-7F`-B<`9 zg~Wl}@y4)es5sd4YCO`4)(|8KpG?&lh_|+k+lB-SsK+fx?D`LeX$*A7U`95StZW@~!Mv$eq4B*>AL|khrAqnm{Lh zR|~&j`El=8Q+A7Ag#vaSjROoDt)B0*qx;NMmr6cE4+CeG}Piq%5Di|`I9q9=4Ye3o}-zw z%X4SfWM{V`XZHqYkA7!-;`x9ipjatRh^WsC6DmjYbka!wGxXU_!f+O&0Ab=eAAe-XViaOHiE|msV^(e>x3Av+lP4kbSGYfAG z3F?$v@6bX+C2_v@iwd3|HcR3>KySNXa5~)9hcLchp^gMHzH7PBn89|;==JJC0j62Z zC8@geLnU-!*DYyGaW-4Ax^8Uh=9Re`#xq0!Icpx3grUp8!Abp9NZlL76%ivTlf03&KgugcX6V+J8`u4E0^DZk^DkxP+}Q z7vc2NmQDoIM@1N7P64(h_4HgEz7y#4SFNi7-tl&;@zuDlNkfOXUm}%5Y0rO2vJ?NA zS3q+{&F~1|KwZDC33XIzVS+4#(H(*#vIjvLWG-k?%=}02JFzq(k{Cy+Kg}F%eMK9CFCG+bh|AS z-k8cd6jeKP;vc_Il*a?Tx0qD*oO%2AY(vexZ-}u(;hPvdZ7*!n{M&CviY_Ld`4hbs zke)LZpMrXUJ>?dbiEmI3K{1Z4xNW=Ovwu-{y^OOkkGJi7g-3%V98QOCM^e?5IUMIR zw3X__OD_MVs3WP`P&);#|Q;th0;?6Mbz^ z(X!Vcy)b+(n}yFz1Uh$2IeHtTzO3np*q>P1RK%?n{P|w`n%Yr){U$b_Y{}c&HlE?j zRl8HXYm=?rgt79U@7`)oszM&5N~B}QR;`$X7Xmm{c1MYA7mv5kyf<~FPuD*Q|E}vK z#<`u|m`$mUGz&wG%L>I|200(KeU|+*w`g^O1JtO4dcJ z5b(zC7?3l~u)+$R8;wj{<%& z=pyCxVruc@m|d}WA5WOgE^&RM&ALdV+!DS@b0V+S4!=)QF;2AO657=(F&s@Ruq0gX zdf6tn>kWO8gv+Cbd83MfiqGXMC`8j*jJ6L8_co(_=S|Ta1hdxBEr%G_r}sKe;{Jww z_n);R_jdGnI89D0-qQ+3x4mxDZ388Zi`AStYq2!Abe-0Bq&X0C}D<3w1Jzg z&)zVk^HN)qAZEB;6@zldh)FD~wRICMwQc{A-gQtptnFY(!MsvyG!=K5ZdsY{;8pXU zA$OEELXCYK2^SD?s-xI+5s^H|=hx`@FwxeKIKlrFA4-C~i90*BFOG>IqNK=7OuTG;|W5lPS}D1tF> z@c807xR)YJFK;|OFGPOL)ta|>?_Y>|_V}AmT$4TW-&?lG`}KSOQvC1Aa4dYws^DaA zZM^01!S5@{qwP;sb^O}0Y=p~SIs*RuzLq}wyH=Kb*XKs&;^^0x7N0*ivR7w^TeGiy zuyQw77pMRJ{K3kHbdptFB_E1_- z!$dh%7mZ0b6&RK2k6y7`<& zkiL>r5JJ7s3+0lY#XN9h)EtsT~wyi)Ce1}!b0zu%DhM&A=ea0)|gj5Rl)aC zQPZ(D>97p8S1f&I8(gariRqeCc!$QHG+n>RD1RhfF-T$}mq$z0jTjfXZ#rTU;^+Qx z{xGEnJ2=j1qCBo-WO;nma^S8{i{%A}urhvW0*mSRQHHZDa376xF=}rOAsLd8tPc#hm8vEN36&+>rMSiceZ>Ls5DS%MN6kOj}GQ!^d&Wg*rqkg4LbW&B`@J$s;H z_oXv$Mb&SiXGNj4BU?pxj|UGKe@Mte)XlEy0^w2}5Q^z4E!<~W6wt6NTI(`y!{ruQ zyQ}Wb+(BtnVF?*B%c+NSB+yQ3!G=n#K2+qU>QJJzJ#DoAH}>wspUwDR{JxQpM3C5& z+BJ%zW@`(AP z>*MYVJy}z*?qX)EPFLYcVie#?6v02>HjpREf_xjNOsxyAD~6C4u_b#5M2R3WlR=h} zuo>{Z;I>;1`sb<|Tm>jzxrTfrPqBnA<4;X98%)YxNF=0VZ2WxdSL9A3sL13+oJTtf z1xD!;m6JGvMX`|&iTl(@1KAz*Sffp=3z-O!@*$7mj(TDZlLgAf11rWsKd6>N2ULZbMnt6r|fwA2GPA+Q@qAmt@90FqwLHO!!4Eq{}#rQP}aw;Tx@e> zx!gVHJ18R@DN>(7>6YZUxNTv;yA-`^04krs*QDkR$|C(-OqXQeAI6b?9l3QHRqnrp zMvBVqD~hCbVZbvW;KU&e7~lpegmx(Kwqg28#kIP_ktn0f4ujvvT{k{>D9 zt6OW_tNazDkyu80qLaYW*8EOC>gYwNt#01oPYKqdd|Me77!)s)n&E~-lv z`FFLG+hU-=VvR-o`;NLDOLN^~%?(xiu1`B~pH^?gTHo;>cqDf$e~}SypRPK1 z*3hwX@It(ElmCbBbjRwiZt?Dusvm(T9iMM*#Cr$@4l$ISYecdVeU#OQ;rBb&$$}*M z*#(ZG(jQDyy_I-R?=^X=?!o5m7PzJCOJ)k0i49g^NmVbNluX|b5loV8O6v#tJf7o$%94za#++ub6u{9Rff5Bd zD%(90b2}sK)`Qpuem~MoHS=kGjbeNA_n!KRdUao9=?YyrT)vKF(NJ`d3tosQ^?eK5 zqqF$^@#yiAzK}t#o-Ae0*%>f6i zTA!Q(-@69|?$P4>s>m0>(zU1_WKwwKL+I|abDykjWMcLGxleDl$}I8PdS1nR_Y9NN@WNY;h&^+Kd^lUurI_|b=*&6tz>bQs& z$9S03Oz_A!sVej%lAAVw6kw|Nc`Gm%k%4<@fYLwF3)AK!5g(fpnlg;H!UHHF!b^09 zFy>%oL>E%dgggD7M&3_E8r+O^&@3E(5e~^(!%svAGlfyijmy!I9v}5Dm?xdWRmJg% zhwDBoWk3N+2Kf_A5+5}dAovQk7vccxTHZ&=qJpZsP0T=sCE3n|o_zSLfQX+vtlG9#h=jFyt>F3a zFzrOT#VA9>WOPcG)gpAo|61`Xv7gL}EYMMZVI2rC##@E$0sWX;tor8y6C`7Q5e^W1 zPj%2`H8Nun3Ww7~6JaR@IfbVK zF^sI2oHpxGlnvZVwtHB>!Zho_0HXo00#Rg*>QU9CI&_)iPm4z^P+n>UQx%eDcjk+B z7v~{?70a`O-^>xqIzP3cZf2H0j8q0w3C9PZ#M6}VV@-4fMY~ub zeeyJo1|8J;Q@FQFv2rMo!!~v`C35NN zixLPke3%5+;#8SHB1>s=OYr8=%qBbVbvP?C11GAGIu=H<*1srEumhW5`W5#&T@}!N zuKtsnqRf@U2t<`4RODEi8CLJ`=iBrwcHk5+pTkmeMvq5L|3zs{yCjzLy-@#1%0NW@ z0P=iinHr~*89;&;yFjgK>~hKe2=)F+Hxik@9$IQOVKlhl5cxBI)PApLywz4A=dm`7 zaK3Cto{kH(oRsx*P8Re<4>Y1=4X}|4%VN`Vyx|I`=2p-~nlw)9=FnYuv8kC%r{)*r z+s?C%#W*I>m7o(vUuLmAMq~r=R!ZT%E-@pv{cZ!P*D>~hjJA;5`Y00K+`afr zKG)Un^W%FI`7m&a8mB*VF+{mUbMLzzx*&X& zoo6`b>MJe(Jn1ea?q4PLc&^gm@_qhhFf60WH`dipD|ji7AA{syF0EXis9IU&U;R?G zdcgnrmup8?)n}sWHKI=g5S0ZyH}qcEy$~g6#?IO=;>}(@beJmp1+0X%+OTp`(Gqun zl{t8^#bG3vMQ&W;gr-P2a6$I)`ijv8Fqka(xdmoS4qfH(k+Dp!@EM|14Z>-@Pw^dI zSKGNvrvc0*dJjUW4m7DuW;78`_Yhwx+&`qo&kIaVOtHO15&2(N>q*rdEvb}Vg69H) zm4Y+P&-($k#PHi;Uvv9A90rgn16b`3QWRQ_5=x&%C+0G#oaGTJ`5@6d&S$ql??8kG z=HZMzJ>mI7*v{_^AVMuxLHYi~xWc$c-H!Z~G4JQ*<-*`)aX@5POsWy_)J#Pd!mW%5 z9pv!U09sBpRlT|ULw}|M+!ax|n&>pPti%@zDH4oo{ASn*C|2%Y5R4Zo#eJ6(ancT6 zcqN6A|8*c&OQj`GXp?V<)H+g|0C-3b^xnKXJFX4T`vE`c*RcauO*&KuRJh1Gb#5=SApc@6NU#)JTfi?T!-+<>hdVIj5SEq_%e z0dmz?5{c8b!zBZWcf=)_U6gfrM=&O0*+@G~gTr5~zxJevtw7HqQp8YK=jZMU-tJnQUNW+Wo}JqZXlK^%^SjE7lOHSmsxqBWlyHjdkNrj}ZIaT}W< zV;a2y&bQ!Yd>;U-JKrD4C0zqK72m3IssJG#HH2~SjfIrqbB>W%hn34yAH7=2`CWa> zm%4o-P*dPYz6Hy7<=#8rqgYEAgJ;4Vo8lIBmHLHTygal!54%mmwQdbOeAnb3TX$7p zWV3f985tIETOX#{FFkYUnLuHcj2Lxr<-K^8><|sd3h(ORB494P--u@QPEW0L#S%c@ zBrU?nyOHA5%z9^9=>B?#7L`YDi9UG_xcGaDPO-Vu-3?Mba3=Y58lvQS*4*^72~FGL z!7T2{)#7>YtZqQ;sjRq{N{iPc@qyWhXQtxM25*t{;CXwuczb)x+PAa^_6YQcN|q7& zWh+YNi2D_JQwJCL)O;eAdv`F?68K4+uE#r|A{(aHONs2I{DUN(hw{;3f@FIsdxAq2 zUz+|nYb~(Qu$&Fv#K-Gj)G8hT$k|(6AbcMBv|O}1??w9So>+VfB; zDf~;5C;%u#c4p>f!$mFTg1tpPk2y(!yNvEU^ueB7@`+^qg)an&cY8~cWXHT{(D&hx zy20J{dZBi}kbHg%px~At=@y{$@vR~i+r;UMG!Z|pb3$>%Y+n3>nu|91nV#Tuf~#V= z=lN}XEaN5b+UtzL5?8H@JUiv{?--2mc1wB$a@w_jUp8uoWs^L#Ebh6#M zUBj<==-)Jai3q@h4k<`2cuNjpGJStHGKEq3;4tJ+c=x8P%$7>`)+3p1!|v_JnlfMPyT7>0eD&`B8YHt5*}W4l^DVvm z+bfyf*MDweWxls`f4?Oo^tOBNGji`k_fwYE{jc2zKV*KKb^pN091_1hB$qvU6iJCd z5K}4rYyF4?&k0Y60Q#S-*ptm#{Z&7TXHopmSL`X8@R`*9r|Cz&K{(Jq>qn1tX#S!f z{Z3>55A~z}{)+woC-oy6UIjxoMl-Yqn9>xe!Xx#p76h+|!5C8Ub$+C=)6y3u6D9Pag?$6Wd3%le6zadt3aLpl8pZPyG|q`7$94e z+_Pn$(SFZWx08Q=#l9Hu@;_OzufF_)Odec*uN@Tlz1KMJ_ItneYxVDg&a;ExKYEDo zT^;t*x?dd)an)QMkBR@dI+;}YvtmDE=zjfk!M^7D*RuDI>$A1UdpGA>>FzfdJFjbQ zF85k~-26Tqyob9wop;AwpMR~v-CUjh__N#z&Br4~_7E@}27^%f1hhjvL=O%zu-JS6 z7qS;(au`B4mQN%;)Jx`f82aDQk7S97LiaSDo^d1JrTx45k*QaU&|meVqS{V74y%-j zIC9^@%@HBk(5#A+DsRz8fOYd>4-1jHj`5NJmx}1FtC|Hj(~83D!SQ|m#E8G>N2jh2kQm0>n4HU0xNcMijxbvJwaF2N5R9%s*g^^70|m4f{46nS#-G zsE()7|Ju1N*GV7w?{;p_M&`T!ZReKOnTJFqsZOU?QJct@+AU|lt|P=W>G|;OAQyCE zp^cK49LZwdzNOeB?tNv6hp-W0x9Gx9*B#r6r6t_bujbbADmU-ZPpg-|T6##U(e#mv zVa0OXaiL3!X{Gd1?|(7V9WqEnsITmroV72ES3hqVT{-Y;QRYo1P~AA9t!mq~BFj{K zD#KDQs)VxZ^w`hg=y^XLkl!tmSd(rvN?qPIXuP@pW3m4?d?{qW`BEt@xUJq2tvYv? z^hUrF`H<$T45(Y6K0>ZErU<0IfTBrDik-0#7Mj_y;9hi z+~beC!q(xhqqpk?Yn;G@MWPOf=-3jeV8*eeoff ztTCThV@KiEhLPtN}Qte{n#4j465F4Yf1 z3JCF}3pV>icKnU~mEGc#!cE0O2bWh@e0&Sdx~)w2-5zM?FIQ-`w9k(pJn#6uGAnw| zZj$sz&2szXiUCN3QQ?c9ir_r(P5s*og;k6SENPdNsZXf|9ZtfrP88qT&!u@3ZF;pq z7k^%I|3(hO&S7Iw+R`K8e_u+|KRySksJx39Rmb!zLYW7b$wY+3!mt5$8XQc4%HsI6 zlnARs{`2i5A~He3s`w1bX8FrF$u!EX`B#JzgVfxMcl>gm<~A>PdCW#|9`mnKcy87DP2zST}ha8 zbl$O@(tEg)hDJ>((@1d=5ZP($9sZ#ebLRWE$5iqCaU_23rjkd z7;;NNS1!Q+o>o*GWZ74dH6x$K`L)tM#bRXm*b6gt%Kp%bQ;LcK|0%6l#4tbfH(D{a zi1F(mT5;*nf zUYTE=08+Y~#PT~IDhut4V$�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�~FjR<=r^e9bbSSDWN-sqaK@Ihkqz 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

H8>4UW4!+KJhg@o5r1M&|Nq=Xce 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 z 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%0DPowhUgDhDULSy!-sk)3Wo9c)N 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