From f0ef24c1239f3fae67c7e1cbca7508edeffe4c92 Mon Sep 17 00:00:00 2001 From: Jake Stevens Date: Wed, 28 Oct 2026 13:38:02 -0700 Subject: [PATCH] Package Native, Vulkan, and GGML as an `executorch-native` wheel (#24069) Summary: Add a separately installable Linux SDK wheel containing the Native runtime, Vulkan engine and shaders, and GGML CPU engine. Its CMake package lets consumers build the shipped vision runner without rebuilding the runtime, and the package coexists with `executorch`. Keep the standalone wheel recipe in `backends/native/python` and its Python wrappers alongside the runtime in `backends/native/pybindings`. CMake installs those sources into `executorch_native`; the source tree does not need a nested copy of the import package. Python execution bindings are added in the following diff. Authored with assistance from Codex. Reviewed By: SS-JIA Differential Revision: D126459370 --- .../native/examples/vision/CMakeLists.txt | 29 ++- backends/native/pybindings/CMakeLists.txt | 11 ++ backends/native/pybindings/__init__.py | 12 ++ backends/native/pybindings/__main__.py | 24 +++ backends/native/python/CMakeLists.txt | 183 ++++++++++++++++++ backends/native/python/README.md | 104 ++++++++++ .../python/executorch_native-config.cmake | 10 + backends/native/python/exports.map | 9 + backends/native/python/pyproject.toml | 24 +++ backends/native/python/tests/CMakeLists.txt | 28 +++ backends/native/python/tests/make_fixture.py | 83 ++++++++ backends/native/python/tests/sdk_smoke.cpp | 52 +++++ 12 files changed, 566 insertions(+), 3 deletions(-) create mode 100644 backends/native/pybindings/CMakeLists.txt create mode 100644 backends/native/pybindings/__init__.py create mode 100644 backends/native/pybindings/__main__.py create mode 100644 backends/native/python/CMakeLists.txt create mode 100644 backends/native/python/README.md create mode 100644 backends/native/python/executorch_native-config.cmake create mode 100644 backends/native/python/exports.map create mode 100644 backends/native/python/pyproject.toml create mode 100644 backends/native/python/tests/CMakeLists.txt create mode 100644 backends/native/python/tests/make_fixture.py create mode 100644 backends/native/python/tests/sdk_smoke.cpp diff --git a/backends/native/examples/vision/CMakeLists.txt b/backends/native/examples/vision/CMakeLists.txt index 76bfdb282f3..7508029763a 100644 --- a/backends/native/examples/vision/CMakeLists.txt +++ b/backends/native/examples/vision/CMakeLists.txt @@ -4,11 +4,34 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. +cmake_minimum_required(VERSION 3.24) + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + project(native_vision_runner LANGUAGES CXX) + include(GNUInstallDirs) + find_package(executorch_native CONFIG REQUIRED) + set(EXECUTORCH_ROOT "${executorch_native_PREFIX}/include/executorch") + add_subdirectory(../runner_utils runner_utils) + set(_native_vision_runtime executorch_native::runtime) + + if(NOT TARGET gflags AND NOT TARGET gflags::gflags) + find_package(gflags REQUIRED) + endif() + if(TARGET gflags::gflags) + set(_native_runner_gflags gflags::gflags) + else() + set(_native_runner_gflags gflags) + endif() +else() + set(_native_vision_runtime native_engine native_package native_runtime + native_vulkan_engine + ) +endif() + add_executable(run_classification run_classification.cpp) target_link_libraries( - run_classification - PRIVATE ${_native_runner_gflags} native_engine native_package - native_runner_utils native_runtime native_vulkan_engine + run_classification PRIVATE ${_native_runner_gflags} native_runner_utils + ${_native_vision_runtime} ) target_compile_options( run_classification PRIVATE $<$:/EHsc> diff --git a/backends/native/pybindings/CMakeLists.txt b/backends/native/pybindings/CMakeLists.txt new file mode 100644 index 00000000000..09848fdb792 --- /dev/null +++ b/backends/native/pybindings/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +install( + FILES __init__.py __main__.py + DESTINATION . + COMPONENT executorch_native +) diff --git a/backends/native/pybindings/__init__.py b/backends/native/pybindings/__init__.py new file mode 100644 index 00000000000..96b85c49193 --- /dev/null +++ b/backends/native/pybindings/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +from pathlib import Path + + +def cmake_prefix_path() -> str: + """Return the installed Native SDK prefix for CMake consumers.""" + return str(Path(__file__).resolve().parent) diff --git a/backends/native/pybindings/__main__.py b/backends/native/pybindings/__main__.py new file mode 100644 index 00000000000..48a41b6b117 --- /dev/null +++ b/backends/native/pybindings/__main__.py @@ -0,0 +1,24 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +import argparse +from pathlib import Path + +from executorch_native import cmake_prefix_path + + +def main() -> None: + parser = argparse.ArgumentParser(description="Locate the ExecuTorch Native SDK") + paths = parser.add_mutually_exclusive_group(required=True) + paths.add_argument("--cmake-prefix-path", action="store_true") + paths.add_argument("--example-path", action="store_true") + args = parser.parse_args() + prefix = Path(cmake_prefix_path()) + print(prefix / "share/examples/vision" if args.example_path else prefix) + + +if __name__ == "__main__": + main() diff --git a/backends/native/python/CMakeLists.txt b/backends/native/python/CMakeLists.txt new file mode 100644 index 00000000000..345ff430e86 --- /dev/null +++ b/backends/native/python/CMakeLists.txt @@ -0,0 +1,183 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +cmake_minimum_required(VERSION 3.26) +project(executorch_native_wheel LANGUAGES C CXX) + +if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") + message(FATAL_ERROR "The Native SDK wheel currently supports Linux only") +endif() + +get_filename_component( + EXECUTORCH_ROOT "${CMAKE_CURRENT_LIST_DIR}/../../.." ABSOLUTE +) +set(BUILD_SHARED_LIBS OFF) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_INSTALL_LIBDIR lib) +set(EXECUTORCH_OPTIMIZE_SIZE OFF) +set(EXECUTORCH_BUILD_NATIVE ON) +set(EXECUTORCH_BUILD_NATIVE_VULKAN ON) +set(EXECUTORCH_BUILD_NATIVE_GGML ON) +set(EXECUTORCH_BUILD_NATIVE_VISION_RUNNER OFF) +set(EXECUTORCH_BUILD_SHARED OFF) +set(EXECUTORCH_BUILD_XNNPACK OFF) +set(EXECUTORCH_BUILD_VULKAN OFF) +set(EXECUTORCH_BUILD_EXECUTOR_RUNNER OFF) +set(EXECUTORCH_BUILD_PYBIND OFF) +set(EXECUTORCH_BUILD_TESTS OFF) +set(EXECUTORCH_BUILD_CPUINFO OFF) +set(EXECUTORCH_BUILD_PTHREADPOOL OFF) +set(EXECUTORCH_BUILD_PORTABLE_OPS OFF) +set(BUILD_TESTING OFF) +foreach( + feature + NATIVE + AVX + AVX2 + AVX_VNNI + BMI2 + FMA + F16C + AVX512 + AVX512_VBMI + AVX512_VNNI + AVX512_BF16 + AMX_TILE + AMX_INT8 + AMX_BF16 +) + set(GGML_${feature} + OFF + CACHE BOOL "Portable wheel CPU baseline" FORCE + ) +endforeach() + +set(GGML_BACKEND_DL OFF) +set(GGML_CPU ON) +set(GGML_OPENMP OFF) + +add_subdirectory("${EXECUTORCH_ROOT}" executorch EXCLUDE_FROM_ALL) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/sdk.cpp" "") +add_library(executorch_native_sdk SHARED "${CMAKE_CURRENT_BINARY_DIR}/sdk.cpp") +target_link_libraries( + executorch_native_sdk + PRIVATE + "$" +) +target_compile_features(executorch_native_sdk INTERFACE cxx_std_20) +target_include_directories( + executorch_native_sdk INTERFACE $ +) +target_link_options( + executorch_native_sdk PRIVATE + "LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/exports.map" + "LINKER:--no-undefined" "LINKER:--gc-sections" +) +set_target_properties( + executorch_native_sdk + PROPERTIES + OUTPUT_NAME executorch_native + EXPORT_NAME runtime + INSTALL_RPATH "$ORIGIN" + LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/exports.map" + LINK_LIBRARY_OVERRIDE + "WHOLE_ARCHIVE,native_api,native_engine,native_runtime,native_package,native_graph,native_vulkan_engine,native_ggml_engine" +) + +add_subdirectory(../pybindings pybindings) + +install( + TARGETS executorch_native_sdk + EXPORT ExecuTorchNativeTargets + LIBRARY DESTINATION lib COMPONENT executorch_native +) +install( + EXPORT ExecuTorchNativeTargets + NAMESPACE executorch_native:: + DESTINATION lib/cmake/executorch_native + COMPONENT executorch_native +) +install( + FILES executorch_native-config.cmake + DESTINATION lib/cmake/executorch_native + COMPONENT executorch_native +) +install( + DIRECTORY ../runtime/ + DESTINATION include/executorch/backends/native/runtime + COMPONENT executorch_native + FILES_MATCHING + PATTERN "*.h" + PATTERN "test" EXCLUDE + PATTERN "vulkan" EXCLUDE + PATTERN "ggml" EXCLUDE + PATTERN "Json.h" EXCLUDE +) +install( + FILES ../runtime/vulkan/VulkanEngine.h + DESTINATION include/executorch/backends/native/runtime/vulkan + COMPONENT executorch_native +) +install( + FILES ../runtime/engine/ggml/GgmlEngine.h + DESTINATION include/executorch/backends/native/runtime/engine/ggml + COMPONENT executorch_native +) +install( + DIRECTORY ../examples/vision ../examples/runner_utils + DESTINATION share/examples + COMPONENT executorch_native + FILES_MATCHING + PATTERN "CMakeLists.txt" + PATTERN "run_classification.cpp" + PATTERN "runner_utils.cpp" + PATTERN "runner_utils.h" +) +install( + FILES ../examples/runner_utils/runner_utils.h + DESTINATION include/executorch/backends/native/examples/runner_utils + COMPONENT executorch_native +) +install( + FILES "${EXECUTORCH_ROOT}/LICENSE" + DESTINATION share/licenses + COMPONENT executorch_native +) +install( + FILES "${EXECUTORCH_ROOT}/backends/native/third-party/llama.cpp/LICENSE" + DESTINATION share/licenses/ggml + COMPONENT executorch_native +) +foreach( + license + third-party/flatbuffers/LICENSE + third-party/json/LICENSE.MIT + backends/vulkan/third-party/volk/LICENSE.md + backends/vulkan/third-party/Vulkan-Headers/LICENSE.md + backends/vulkan/third-party/VulkanMemoryAllocator/LICENSE.txt +) + get_filename_component(dependency "${license}" DIRECTORY) + install( + FILES "${EXECUTORCH_ROOT}/${license}" + DESTINATION "share/licenses/${dependency}" + COMPONENT executorch_native + ) +endforeach() + +set(EXECUTORCH_NATIVE_EXTRA_LICENSES + "" + CACHE STRING "License files for external libraries bundled into the wheel" +) +foreach(license IN LISTS EXECUTORCH_NATIVE_EXTRA_LICENSES) + get_filename_component(dependency "${license}" DIRECTORY) + get_filename_component(dependency "${dependency}" NAME) + install( + FILES "${license}" + DESTINATION "share/licenses/dependencies/${dependency}" + COMPONENT executorch_native + ) +endforeach() diff --git a/backends/native/python/README.md b/backends/native/python/README.md new file mode 100644 index 00000000000..48279f1893e --- /dev/null +++ b/backends/native/python/README.md @@ -0,0 +1,104 @@ +# ExecuTorch Native SDK wheel + +This directory contains the standalone wheel build recipe. Python sources live +alongside the runtime in [`../pybindings`](../pybindings); CMake installs them as +`executorch_native`. The wheel does not depend on the `executorch` distribution. + +`executorch-native` packages the Native runtime, Vulkan engine (including compiled +shaders), and GGML CPU engine. It installs into `executorch_native`, independently +of the existing `executorch` distribution. It has no PyTorch dependency and does +not install exporters or Python execution bindings. Export `.ptn` files using a +matching version of `executorch` on the development machine. + +The initial wheel supports Linux. It contains C++20 headers and a shared library; +it is independent of the CPython ABI, but platform and C++ ABI compatibility still +apply. Vulkan execution requires a system Vulkan loader and a compatible driver. + +## Use the installed SDK + +The SDK requires the libzip runtime. If your wheel does not bundle it, install +the system package before running an SDK application, for example: + +```sh +sudo apt install libzip5 +``` + +The package name varies by distribution and release. Wheels repaired with +`auditwheel` normally bundle libzip, as described below. + +After obtaining a built wheel: + +```sh +python -m pip install executorch_native-*.whl +cmake -S "$(python -m executorch_native --example-path)" -B vision-build \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="$(python -m executorch_native --cmake-prefix-path)" +cmake --build vision-build --parallel +vision-build/run_classification --package model.ptn --input input.bin \ + --expected expected.bin +``` + +This compiles only the runner and its small I/O utilities. The example also +requires the gflags development package (for example `libgflags-dev` on Ubuntu). +It needs neither an ExecuTorch checkout nor a shader compiler, GGML, or libzip +development package. The wheel is a prototype; it is not yet published to PyPI. + +Other applications can consume the same SDK: + +```cmake +find_package(executorch_native CONFIG REQUIRED) +target_link_libraries(my_application PRIVATE executorch_native::runtime) +``` + +Both `ptn::VulkanEngineHost` and `ptn::GgmlEngineHost` are available through this +target. Native headers retain their `executorch/backends/native/...` include +paths. The wheel owns no files under the `executorch` Python package and exports +only the `ptn` API from its shared library, keeping bundled implementation symbols +private when an application also loads ExecuTorch. + +## Build a wheel + +Build from a complete ExecuTorch source checkout with its pinned submodules +initialized. Build dependencies are the same as the Native CMake build: a C++20 +compiler, libzip development files, and `glslc` from Vulkan SDK 1.4.341.1 or newer. + +```sh +python -m pip install build auditwheel patchelf +python -m build --wheel backends/native/python --outdir dist/native \ + -Ccmake.define.EXECUTORCH_NATIVE_EXTRA_LICENSES=/path/to/libzip/LICENSE +auditwheel repair dist/native/*.whl --wheel-dir dist/native/repaired +``` + +Supply the license files for external libraries bundled by the build or repair +step through `EXECUTORCH_NATIVE_EXTRA_LICENSES` (a semicolon-separated list). +Distribute the repaired wheel: repair bundles shared dependencies such as libzip +and assigns a compatible manylinux tag. For release wheels, build in the chosen +manylinux image for each supported architecture; building on a newer Linux host +does not establish compatibility with older systems. GGML uses portable CPU +settings, with native tuning and optional x86 instruction sets disabled, rather +than assuming the build machine's instruction set is available on every consumer. +Keep the existing ExecuTorch release version; the wheel reads `version.txt`. + +The build reuses existing CMake targets and installs only the Native wheel +component. Building source distributions from this subdirectory is not supported; +build wheels directly from the complete checkout. + +## Validate the installed wheel + +Generate the small test model on the build machine, then configure the tests +against an installed, repaired wheel in a fresh environment: + +```sh +python backends/native/python/tests/make_fixture.py --flatc /path/to/flatc \ + --output /tmp/native-fixture +cmake -S backends/native/python/tests -B sdk-test \ + -DCMAKE_PREFIX_PATH="$(python -m executorch_native --cmake-prefix-path)" \ + -DNATIVE_TEST_PACKAGE=/tmp/native-fixture/add.ptn +cmake --build sdk-test +ctest --test-dir sdk-test --output-on-failure +``` + +Add `-DNATIVE_TEST_VULKAN=ON` to test both engines when a Vulkan device is +available. The installed vision example also accepts this fixture with +`--input /tmp/native-fixture/input.bin --expected /tmp/native-fixture/expected.bin`. +The fixture generator needs `flatc`; consumers of the wheel do not. diff --git a/backends/native/python/executorch_native-config.cmake b/backends/native/python/executorch_native-config.cmake new file mode 100644 index 00000000000..db1d1fc8354 --- /dev/null +++ b/backends/native/python/executorch_native-config.cmake @@ -0,0 +1,10 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +include("${CMAKE_CURRENT_LIST_DIR}/ExecuTorchNativeTargets.cmake") +get_filename_component( + executorch_native_PREFIX "${CMAKE_CURRENT_LIST_DIR}/../../.." ABSOLUTE +) diff --git a/backends/native/python/exports.map b/backends/native/python/exports.map new file mode 100644 index 00000000000..1aa1887ed6e --- /dev/null +++ b/backends/native/python/exports.map @@ -0,0 +1,9 @@ +{ + global: + _ZN3ptn*; + _ZNK3ptn*; + _ZTIN3ptn*; + _ZTSN3ptn*; + _ZTVN3ptn*; + local: *; +}; diff --git a/backends/native/python/pyproject.toml b/backends/native/python/pyproject.toml new file mode 100644 index 00000000000..da272f5137a --- /dev/null +++ b/backends/native/python/pyproject.toml @@ -0,0 +1,24 @@ +[build-system] +requires = ["scikit-build-core>=0.11", "pyyaml", "zstd", "certifi"] +build-backend = "scikit_build_core.build" + +[project] +name = "executorch-native" +dynamic = ["version"] +description = "ExecuTorch Native C++ SDK with Vulkan and GGML engines" +readme = "README.md" +requires-python = ">=3.11" +license = "BSD-3-Clause" + +[tool.scikit-build] +cmake.version = ">=3.26,<4" +build.targets = ["executorch_native_sdk"] +install.components = ["executorch_native"] +wheel.install-dir = "executorch_native" +wheel.packages = [] +wheel.py-api = "py3" + +[tool.scikit-build.metadata.version] +provider = "scikit_build_core.metadata.regex" +input = "../../../version.txt" +regex = "(?P[^\\s]+)" diff --git a/backends/native/python/tests/CMakeLists.txt b/backends/native/python/tests/CMakeLists.txt new file mode 100644 index 00000000000..d72b529669e --- /dev/null +++ b/backends/native/python/tests/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +cmake_minimum_required(VERSION 3.26) +project(native_sdk_test LANGUAGES CXX) +find_package(executorch_native CONFIG REQUIRED) +enable_testing() + +set(NATIVE_TEST_PACKAGE + "" + CACHE FILEPATH "Path to the add.ptn test fixture" +) +option(NATIVE_TEST_VULKAN "Run the SDK test on a Vulkan device" OFF) +if(NOT EXISTS "${NATIVE_TEST_PACKAGE}") + message(FATAL_ERROR "Generate add.ptn with make_fixture.py first") +endif() + +add_executable(sdk_smoke sdk_smoke.cpp) +target_link_libraries(sdk_smoke PRIVATE executorch_native::runtime) +add_test(NAME native_sdk_ggml COMMAND sdk_smoke "${NATIVE_TEST_PACKAGE}" ggml) +if(NATIVE_TEST_VULKAN) + add_test(NAME native_sdk_vulkan COMMAND sdk_smoke "${NATIVE_TEST_PACKAGE}" + vulkan + ) +endif() diff --git a/backends/native/python/tests/make_fixture.py b/backends/native/python/tests/make_fixture.py new file mode 100644 index 00000000000..a9852eabc49 --- /dev/null +++ b/backends/native/python/tests/make_fixture.py @@ -0,0 +1,83 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +import argparse +import json +import struct +import subprocess +import zipfile +from pathlib import Path + + +def make_graph() -> dict[str, object]: + meta = { + "dtype": "FLOAT", + "sizes": [{"min": 1, "max": 1}, {"min": 6, "max": 6}], + "dim_order": [0, 1], + } + tensor = {"value_type": "TensorArg", "value": {"name": "x"}} + return { + "nodes": [ + {"name": "x", "op_kind": "PLACEHOLDER", "outputs": [{"name": "x"}]}, + { + "name": "add", + "op_kind": "CALL_FUNCTION", + "target": "torch.ops.aten.add.Tensor", + "inputs": [ + {"name": "self", "arg": tensor}, + {"name": "other", "arg": tensor}, + { + "name": "alpha", + "arg": {"value_type": "IntArg", "value": {"value": 1}}, + }, + ], + "outputs": [{"name": "add"}], + }, + { + "name": "output", + "op_kind": "OUTPUT", + "inputs": [ + {"arg": {"value_type": "TensorArg", "value": {"name": "add"}}} + ], + }, + ], + "inputs": ["x"], + "outputs": ["add"], + "tensor_values": [{"name": name, "meta": meta} for name in ("x", "add")], + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Generate the Native SDK add fixture") + parser.add_argument("--flatc", required=True) + parser.add_argument("--output", type=Path, required=True) + args = parser.parse_args() + schema = Path(__file__).resolve().parents[2] / "serialization/native_graph.fbs" + program = { + "version": "1", + "methods": [ + { + "name": "forward", + "graph": make_graph(), + "output_specs": [{"name": "add", "kind": "USER_OUTPUT"}], + } + ], + } + args.output.mkdir(parents=True, exist_ok=True) + source = args.output / "program.json" + source.write_text(json.dumps(program)) + subprocess.run( + [args.flatc, "-b", "-o", str(args.output), str(schema), str(source)], + check=True, + ) + with zipfile.ZipFile(args.output / "add.ptn", "w") as package: + package.write(args.output / "program.bin", "program.ptg") + (args.output / "input.bin").write_bytes(struct.pack("<6f", 1, 2, 3, 4, 5, 6)) + (args.output / "expected.bin").write_bytes(struct.pack("<6f", 2, 4, 6, 8, 10, 12)) + + +if __name__ == "__main__": + main() diff --git a/backends/native/python/tests/sdk_smoke.cpp b/backends/native/python/tests/sdk_smoke.cpp new file mode 100644 index 00000000000..34d7c363797 --- /dev/null +++ b/backends/native/python/tests/sdk_smoke.cpp @@ -0,0 +1,52 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +int main(int argc, char** argv) { + if (argc != 3) { + return 1; + } + try { + std::unique_ptr host; + if (std::string_view(argv[2]) == "vulkan") { + host = ptn::VulkanEngineHost::create(); + } else { + host = ptn::GgmlEngineHost::create(); + } + const auto model = ptn::Model::load_file(argv[1]); + auto session = model.create_session(*host); + const std::array shape{1, 6}; + const std::array strides{6, 1}; + const std::array input{1, 2, 3, 4, 5, 6}; + std::array output{}; + const std::array inputs{ptn::ConstTensorView( + ptn::kFloat, shape, strides, input.data(), sizeof(input))}; + std::array outputs{ptn::MutableTensorView( + ptn::kFloat, shape, strides, output.data(), sizeof(output))}; + session.run("forward", inputs, outputs); + for (size_t i = 0; i < output.size(); ++i) { + if (output[i] != 2 * input[i]) { + std::fprintf(stderr, "output mismatch at %zu: %g\n", i, output[i]); + return 1; + } + } + std::printf("%s: PASS\n", host->name().c_str()); + return 0; + } catch (const std::exception& error) { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } +}