From c52fef4d3893cf1e6b56ba31cf9660a4080f2377 Mon Sep 17 00:00:00 2001 From: Volodymyr Kysenko Date: Mon, 17 Oct 2022 15:07:53 -0700 Subject: [PATCH 1/5] Generate dot() in the Metal backend (#7085) * dot() support for Metal backend) * Restrict dot() to floats --- src/CodeGen_Metal_Dev.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/CodeGen_Metal_Dev.cpp b/src/CodeGen_Metal_Dev.cpp index 569d8415c82d..a2e26939f074 100644 --- a/src/CodeGen_Metal_Dev.cpp +++ b/src/CodeGen_Metal_Dev.cpp @@ -91,6 +91,7 @@ class CodeGen_Metal_Dev : public CodeGen_GPU_Dev { void visit(const Allocate *op) override; void visit(const Free *op) override; void visit(const Cast *op) override; + void visit(const VectorReduce *op) override; void visit(const Atomic *op) override; }; @@ -223,6 +224,20 @@ void CodeGen_Metal_Dev::CodeGen_Metal_C::visit(const Min *op) { print_expr(Call::make(op->type, "min", {op->a, op->b}, Call::Extern)); } +void CodeGen_Metal_Dev::CodeGen_Metal_C::visit(const VectorReduce *op) { + if (op->op == VectorReduce::Add && op->type.is_float() && (op->type.lanes() == 1)) { + if (const Mul *maybe_mul = op->value.as()) { + string a = print_expr(maybe_mul->a); + string b = print_expr(maybe_mul->b); + ostringstream rhs; + rhs << "dot(" << a << ", " << b << ")"; + print_assignment(op->type, rhs.str()); + return; + } + } + CodeGen_GPU_C::visit(op); +} + void CodeGen_Metal_Dev::CodeGen_Metal_C::visit(const Div *op) { int bits; if (is_const_power_of_two_integer(op->b, &bits)) { From 52c69913768e65e6b15241e5c520791ca57f0eea Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Wed, 19 Oct 2022 17:42:58 -0700 Subject: [PATCH 2/5] Fix subtle CMake Install bugs (#7103) * Update CMakeLists.txt * Update CMakeLists.txt --- CMakeLists.txt | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ecaa9ef6cd08..d70fd72b6d70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,28 @@ option(TARGET_SPIRV "Include SPIR-V target" OFF) add_subdirectory(dependencies) +## +# Declare options +## + +# Declare these options after we include dependencies (since it declares Halide_ENABLE_RTTI etc) +# but before we add any subdirectories, since any option you test before it is defined is +# implicitly false the *first* time that the build file is processed, and there are some +# out-of-order dependencies here (e.g, code in src/ eventually checks WITH_UTILS). +# This is especially subtle since it means that some options can end up with different +# values if you build a target as part of the initial CMake run, so (e.g.) a `make install` +# from as totally clean build might neglect to install some pieces. + +option(WITH_TESTS "Build tests" "${PROJECT_IS_TOP_LEVEL}") +option(WITH_TUTORIALS "Build tutorials" "${PROJECT_IS_TOP_LEVEL}") +option(WITH_DOCS "Build documentation" OFF) +option(WITH_UTILS "Build utils" "${PROJECT_IS_TOP_LEVEL}") +cmake_dependent_option( + WITH_PYTHON_BINDINGS "Build Python bindings" "${PROJECT_IS_TOP_LEVEL}" + "Halide_ENABLE_RTTI AND Halide_ENABLE_EXCEPTIONS" OFF +) + + ## # Add source directories ## @@ -101,7 +123,6 @@ add_subdirectory(tools) # Add tests, tutorials, etc. if we're not being imported into another CMake project. ## -option(WITH_TESTS "Build tests" "${PROJECT_IS_TOP_LEVEL}") if (WITH_TESTS) message(STATUS "Building tests enabled") add_subdirectory(test) @@ -109,10 +130,6 @@ else () message(STATUS "Building tests disabled") endif () -cmake_dependent_option( - WITH_PYTHON_BINDINGS "Build Python bindings" "${PROJECT_IS_TOP_LEVEL}" - "Halide_ENABLE_RTTI AND Halide_ENABLE_EXCEPTIONS" OFF -) if (WITH_PYTHON_BINDINGS) message(STATUS "Building Python bindings enabled") add_subdirectory(python_bindings) @@ -120,7 +137,6 @@ else () message(STATUS "Building Python bindings disabled") endif () -option(WITH_TUTORIALS "Build tutorials" "${PROJECT_IS_TOP_LEVEL}") if (WITH_TUTORIALS) message(STATUS "Building tutorials enabled") add_subdirectory(tutorial) @@ -128,7 +144,6 @@ else () message(STATUS "Building tutorials disabled") endif () -option(WITH_DOCS "Build documentation" OFF) if (WITH_DOCS) message(STATUS "Building docs enabled") add_subdirectory(doc) @@ -136,7 +151,6 @@ else () message(STATUS "Building docs disabled") endif () -option(WITH_UTILS "Build utils" "${PROJECT_IS_TOP_LEVEL}") if (WITH_UTILS) message(STATUS "Building utils enabled") add_subdirectory(util) From c57222a4261190d27bdfb9c57310c4302f9d46a1 Mon Sep 17 00:00:00 2001 From: Andrew Adams Date: Thu, 20 Oct 2022 12:26:46 -0700 Subject: [PATCH 3/5] Fix some dead links to the 'master' branch (#7107) --- README_cmake.md | 6 +++--- README_python.md | 2 +- apps/bgu/bgu_generator.cpp | 2 +- python_bindings/test/correctness/extern.py | 2 -- tutorial/lesson_11_cross_compilation.cpp | 2 +- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/README_cmake.md b/README_cmake.md index 32846c159a78..920d8583571f 100644 --- a/README_cmake.md +++ b/README_cmake.md @@ -935,8 +935,8 @@ generators were imported (and hence won't be built). Otherwise, it will be set to false. This variable may be used to conditionally set properties on ``. -Please see [test/integration/xc](https://github.com/halide/Halide/tree/master/test/integration/xc) for a simple example -and [apps/hannk](https://github.com/halide/Halide/tree/master/apps/hannk) for a complete app that uses it extensively. +Please see [test/integration/xc](https://github.com/halide/Halide/tree/main/test/integration/xc) for a simple example +and [apps/hannk](https://github.com/halide/Halide/tree/main/apps/hannk) for a complete app that uses it extensively. If `PYSTUB` is specified, then a Python Extension will be built that wraps the Generator with CPython glue to allow use of the Generator @@ -1190,7 +1190,7 @@ without broader approval. Confine dependencies to the `dependencies/` subtree. Any variables that are specific to languages that are not enabled should, of course, be avoided. But of greater concern are variables that are easy to misuse or should not be overridden for our end-users. The following (non-exhaustive) -list of variables shall not be used in code merged into master. +list of variables shall not be used in code merged into main. | Variable | Reason | Alternative | |---------------------------------|-----------------------------------------------|---------------------------------------------------------------------------------------------------------| diff --git a/README_python.md b/README_python.md index d46fedfc7af5..50de16f38f2d 100644 --- a/README_python.md +++ b/README_python.md @@ -772,7 +772,7 @@ in future releases. ## License The Python bindings use the same -[MIT license](https://github.com/halide/Halide/blob/master/LICENSE.txt) as +[MIT license](https://github.com/halide/Halide/blob/main/LICENSE.txt) as Halide. Python bindings provided by Connelly Barnes (2012-2013), Fred Rotbart (2014), diff --git a/apps/bgu/bgu_generator.cpp b/apps/bgu/bgu_generator.cpp index 1b2cff5b1dc7..653e5ed87c5a 100644 --- a/apps/bgu/bgu_generator.cpp +++ b/apps/bgu/bgu_generator.cpp @@ -1,6 +1,6 @@ // A Halide implementation of bilateral-guided upsampling. -// Adapted from https://github.com/google/bgu/blob/master/src/halide/bgu.cpp +// Adapted from https://github.com/google/bgu/tree/master/src/halide // Copyright 2016 Google Inc. // diff --git a/python_bindings/test/correctness/extern.py b/python_bindings/test/correctness/extern.py index 2b8d8c52dad6..32fa55290a07 100644 --- a/python_bindings/test/correctness/extern.py +++ b/python_bindings/test/correctness/extern.py @@ -50,8 +50,6 @@ def test_extern(): assert False, 'Did not see expected exception!' lib_path = "the_sort_function.so" - #lib_path = "/home/rodrigob/code/references/" \ - # "Halide_master/python_bindings/tests/the_sort_function.nohere.so" load_error = load_library_into_llvm(lib_path) assert load_error == False diff --git a/tutorial/lesson_11_cross_compilation.cpp b/tutorial/lesson_11_cross_compilation.cpp index 63652f7078fc..8cf3b050d39c 100644 --- a/tutorial/lesson_11_cross_compilation.cpp +++ b/tutorial/lesson_11_cross_compilation.cpp @@ -147,7 +147,7 @@ int main(int argc, char **argv) { // toolchain. There are several small examples of this in the // Halide repository under the apps folder. See HelloAndroid and // HelloiOS here: - // https://github.com/halide/Halide/tree/master/apps/ + // https://github.com/halide/Halide/tree/main/apps/ printf("Success!\n"); return 0; } From 603012171c9ac025989d2c16cf8cb9362e056e00 Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Fri, 21 Oct 2022 10:26:49 -0700 Subject: [PATCH 4/5] Attempt to fix pip build issues (#7098) --- .github/workflows/pip.yml | 43 ++++++++++++++++++++++++--------------- setup.py | 1 + 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml index 57181663900e..1bbd0a370503 100644 --- a/.github/workflows/pip.yml +++ b/.github/workflows/pip.yml @@ -29,7 +29,7 @@ jobs: arch: [ x86_64, aarch64 ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3.1.0 - name: Log in to GitHub Container Registry uses: docker/login-action@v2.0.0 @@ -67,9 +67,6 @@ jobs: runs-on: ${{ matrix.runner }} - env: - CMAKE_PREFIX_PATH: ${{ github.workspace }}/local - strategy: fail-fast: false matrix: @@ -83,28 +80,29 @@ jobs: arch: x86_64;arm64 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3.1.0 - name: Cache LLVM build folder id: cache-llvm - uses: actions/cache@v3 + uses: actions/cache@v3.0.11 with: - path: local + path: local-llvm key: llvmorg-${{ env.LLVM_VER }}-${{ runner.os }} - uses: ilammy/msvc-dev-cmd@v1 - uses: lukka/get-cmake@latest - - uses: actions/checkout@v3 + - uses: actions/checkout@v3.1.0 if: steps.cache-llvm.outputs.cache-hit != 'true' with: + path: llvm-src repository: llvm/llvm-project ref: llvmorg-${{ env.LLVM_VER }} - name: Configure LLVM if: steps.cache-llvm.outputs.cache-hit != 'true' run: > - cmake -G Ninja -S llvm -B build + cmake -G Ninja -S llvm-src/llvm -B llvm-build -DCMAKE_BUILD_TYPE=Release "-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64" "-DLLVM_TARGETS_TO_BUILD=X86;ARM;NVPTX;AArch64;Mips;Hexagon;WebAssembly" @@ -116,29 +114,40 @@ jobs: -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZSTD=OFF -DLLVM_ENABLE_ZLIB=OFF + -DLLVM_ENABLE_OCAMLDOC=OFF + -DLLVM_ENABLE_BINDINGS=OFF + -DLLVM_ENABLE_IDE=OFF - name: Build LLVM if: steps.cache-llvm.outputs.cache-hit != 'true' - run: cmake --build build + run: cmake --build llvm-build - name: Install LLVM if: steps.cache-llvm.outputs.cache-hit != 'true' - run: cmake --install build --prefix local + run: cmake --install llvm-build --prefix local-llvm + + # Remove the LLVM source tree after building it, otherwise we can + # run out of local space while building halide + - name: Clean LLVM Source + if: steps.cache-llvm.outputs.cache-hit != 'true' + shell: bash + run: rm -rf llvm-src - name: Configure Halide if: runner.os == 'Windows' run: > - cmake -G "Visual Studio 17 2022" -T ClangCL -A "${{ matrix.arch }}" -S . -B build + cmake -G "Visual Studio 17 2022" -T ClangCL -A "${{ matrix.arch }}" -S . -B halide-build -DWITH_DOCS=NO -DWITH_PYTHON_BINDINGS=NO -DWITH_TESTS=NO -DWITH_TUTORIALS=NO -DWITH_UTILS=NO + -DLLVM_DIR=${{ github.workspace }}/local-llvm/lib/cmake/llvm - name: Configure Halide if: runner.os != 'Windows' run: > - cmake -G Ninja -S . -B build + cmake -G Ninja -S . -B halide-build -DCMAKE_BUILD_TYPE=Release "-DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }}" -DWITH_DOCS=NO @@ -146,16 +155,18 @@ jobs: -DWITH_TESTS=NO -DWITH_TUTORIALS=NO -DWITH_UTILS=NO + -DLLVM_DIR=${{ github.workspace }}/local-llvm/lib/cmake/llvm - name: Build Halide - run: cmake --build build --config Release + run: cmake --build halide-build --config Release - name: Install Halide - run: cmake --install build --config Release --prefix local + run: cmake --install halide-build --config Release --prefix local-halide - name: Build wheels uses: pypa/cibuildwheel@v2.10.2 env: + CMAKE_PREFIX_PATH: ${{ github.workspace }}/local-halide CIBW_BUILD: "cp38-${{ matrix.pytag }} cp39-${{ matrix.pytag }} cp310-${{ matrix.pytag }}" CIBW_ARCHS_MACOS: "universal2" @@ -168,7 +179,7 @@ jobs: name: Make SDist runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3.1.0 - run: pipx run build --sdist - uses: actions/upload-artifact@v3 with: diff --git a/setup.py b/setup.py index b16320ff581b..9ef51e07538f 100644 --- a/setup.py +++ b/setup.py @@ -27,6 +27,7 @@ "-DHalide_Python_INSTALL_IMPORTED_DEPS=ON", "-DWITH_TESTS=NO", "-DWITH_TUTORIALS=NO", + "-DCMAKE_PREFIX_PATH=$ENV{CMAKE_PREFIX_PATH}", "--no-warn-unused-cli", ], ) From 39d86f31a451ce6d7e8b7b8d9852f04f224ee133 Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Mon, 24 Oct 2022 09:37:40 -0700 Subject: [PATCH 5/5] Add evaluate() and evaluate_may_gpu() to Python bindings (#7108) * Add evaluate() and evaluate_may_gpu() to Python bindings * pacify clang-tidy --- .../src/halide/halide_/PyBuffer.cpp | 4 +- python_bindings/src/halide/halide_/PyBuffer.h | 2 + python_bindings/src/halide/halide_/PyFunc.cpp | 39 +++++++++- .../src/halide/halide_/PyTuple.cpp | 26 +++++++ python_bindings/src/halide/halide_/PyTuple.h | 5 ++ python_bindings/test/correctness/division.py | 74 +++++++++++-------- 6 files changed, 115 insertions(+), 35 deletions(-) diff --git a/python_bindings/src/halide/halide_/PyBuffer.cpp b/python_bindings/src/halide/halide_/PyBuffer.cpp index 7fb2ff84142d..67f642413bb2 100644 --- a/python_bindings/src/halide/halide_/PyBuffer.cpp +++ b/python_bindings/src/halide/halide_/PyBuffer.cpp @@ -195,8 +195,6 @@ Type format_descriptor_to_type(const std::string &fd) { return Type(); } -namespace { - py::object buffer_getitem_operator(Buffer<> &buf, const std::vector &pos) { if ((size_t)pos.size() != (size_t)buf.dimensions()) { throw py::value_error("Incorrect number of dimensions."); @@ -228,6 +226,8 @@ py::object buffer_getitem_operator(Buffer<> &buf, const std::vector &pos) { return py::object(); } +namespace { + py::object buffer_setitem_operator(Buffer<> &buf, const std::vector &pos, const py::object &value) { if ((size_t)pos.size() != (size_t)buf.dimensions()) { throw py::value_error("Incorrect number of dimensions."); diff --git a/python_bindings/src/halide/halide_/PyBuffer.h b/python_bindings/src/halide/halide_/PyBuffer.h index 8b108c4e2abc..e675f6dd5d60 100644 --- a/python_bindings/src/halide/halide_/PyBuffer.h +++ b/python_bindings/src/halide/halide_/PyBuffer.h @@ -10,6 +10,8 @@ void define_buffer(py::module &m); Type format_descriptor_to_type(const std::string &fd); +py::object buffer_getitem_operator(Buffer<> &buf, const std::vector &pos); + template diff --git a/python_bindings/src/halide/halide_/PyFunc.cpp b/python_bindings/src/halide/halide_/PyFunc.cpp index c693758b6eba..c2da6afa0b9c 100644 --- a/python_bindings/src/halide/halide_/PyFunc.cpp +++ b/python_bindings/src/halide/halide_/PyFunc.cpp @@ -80,6 +80,31 @@ py::object realization_to_object(const Realization &r) { return to_python_tuple(r); } +py::object evaluate_impl(const py::object &expr, bool may_gpu) { + Tuple t = to_halide_tuple(expr); + Func f("evaluate_func_" + std::to_string(t.size())); + f() = t; + if (may_gpu) { + Internal::schedule_scalar(f); + } + + std::optional r; + { + py::gil_scoped_release release; + + r = f.realize(); + } + if (r->size() == 1) { + return buffer_getitem_operator((*r)[0], {}); + } else { + py::tuple result(r->size()); + for (size_t i = 0; i < r->size(); i++) { + result[i] = buffer_getitem_operator((*r)[i], {}); + } + return result; + } +} + } // namespace void define_func(py::module &m) { @@ -87,8 +112,6 @@ void define_func(py::module &m) { define_var_or_rvar(m); define_loop_level(m); - // TODO: add ParamMap support. - // Deliberately not supported, because they don't seem to make sense for Python: // - set_custom_allocator() // - set_custom_do_task() @@ -380,6 +403,18 @@ void define_func(py::module &m) { add_schedule_methods(func_class); define_stage(m); + + m.def( + "evaluate", [](const py::object &expr) -> py::object { + return evaluate_impl(expr, false); + }, + py::arg("expr")); + + m.def( + "evaluate_may_gpu", [](const py::object &expr) -> py::object { + return evaluate_impl(expr, true); + }, + py::arg("expr")); } } // namespace PythonBindings diff --git a/python_bindings/src/halide/halide_/PyTuple.cpp b/python_bindings/src/halide/halide_/PyTuple.cpp index 234c7019f611..81f0499f26e2 100644 --- a/python_bindings/src/halide/halide_/PyTuple.cpp +++ b/python_bindings/src/halide/halide_/PyTuple.cpp @@ -3,6 +3,31 @@ namespace Halide { namespace PythonBindings { +Tuple to_halide_tuple(const py::object &o) { + try { + Expr e = o.cast(); + return Tuple(e); + } catch (...) { + // fall thru + } + + try { + py::tuple t = o.cast(); + if (t.empty()) { + throw py::value_error("Cannot use a zero-length tuple-of-Expr"); + } + std::vector v(t.size()); + for (size_t i = 0; i < t.size(); i++) { + v[i] = t[i].cast(); + } + return Tuple(v); + } catch (...) { + // fall thru + } + + throw py::value_error("Expected an Expr or tuple-of-Expr."); +} + void define_tuple(py::module &m) { // Halide::Tuple isn't surfaced to the user in Python; // we define it here to allow PyBind to do some automatic @@ -42,6 +67,7 @@ void define_tuple(py::module &m) { o << ""; return o.str(); }); + py::implicitly_convertible(); // If we autoconvert from vector, we must also special-case FuncRef, alas diff --git a/python_bindings/src/halide/halide_/PyTuple.h b/python_bindings/src/halide/halide_/PyTuple.h index 997b86077653..58afd1dc2051 100644 --- a/python_bindings/src/halide/halide_/PyTuple.h +++ b/python_bindings/src/halide/halide_/PyTuple.h @@ -18,6 +18,11 @@ inline py::tuple to_python_tuple(const T &ht) { return pt; } +// in: convertible-to-Expr, or tuple-of-convertible-to-Expr +// out: Halide::Tuple +// throws exception if not convertible +Tuple to_halide_tuple(const py::object &o); + } // namespace PythonBindings } // namespace Halide diff --git a/python_bindings/test/correctness/division.py b/python_bindings/test/correctness/division.py index 63581a6bdc87..f49a6f3f6820 100644 --- a/python_bindings/test/correctness/division.py +++ b/python_bindings/test/correctness/division.py @@ -1,24 +1,13 @@ import halide as hl -# TODO: Func.evaluate() needs a wrapper added; -# this is a temporary equivalent for testing purposes -def _evaluate(e): - # TODO: support zero-dim Func, Buffers - buf = hl.Buffer(type = e.type(), sizes = [1]) - f = hl.Func(); - x = hl.Var() - f[x] = e; - f.realize(buf) - return buf[0] - -def test_division(): - f32 = hl.Param(hl.Float(32), 'f32', -32.0) - f64 = hl.Param(hl.Float(64), 'f64', 64.0) - i16 = hl.Param(hl.Int(16), 'i16', -16) - i32 = hl.Param(hl.Int(32), 'i32', 32) - u16 = hl.Param(hl.UInt(16), 'u16', 16) - u32 = hl.Param(hl.UInt(32), 'u32', 32) +f32 = hl.Param(hl.Float(32), 'f32', -32.0) +f64 = hl.Param(hl.Float(64), 'f64', 64.0) +i16 = hl.Param(hl.Int(16), 'i16', -16) +i32 = hl.Param(hl.Int(32), 'i32', 32) +u16 = hl.Param(hl.UInt(16), 'u16', 16) +u32 = hl.Param(hl.UInt(32), 'u32', 32) +def test_types(): # Verify that the types match the rules in match_types() assert (f32 / f64).type() == hl.Float(64) assert (f32 // f64).type() == hl.Float(64) @@ -41,28 +30,51 @@ def test_division(): assert (i16 / f64).type() == hl.Float(64) assert (i16 // f64).type() == hl.Float(64) +def test_division(): # Verify that division semantics match those for Halide # (rather than python); this differs for int/int which # defaults to float (rather than floordiv) in Python3. # Also test that // always floors the result, even for float. - assert _evaluate(f32 / f64) == -0.5 - assert _evaluate(f32 // f64) == -1.0 + assert hl.evaluate(f32 / f64) == -0.5 + assert hl.evaluate(f32 // f64) == -1.0 + + assert hl.evaluate(i16 / i32) == -1 + assert hl.evaluate(i16 // i32) == -1 + assert hl.evaluate(i32 / i16) == -2 + + assert hl.evaluate(u16 / u32) == 0 + assert hl.evaluate(u16 // u32) == 0 - assert _evaluate(i16 / i32) == -1 - assert _evaluate(i16 // i32) == -1 - assert _evaluate(i32 / i16) == -2 + assert hl.evaluate(u16 / i32) == 0 + assert hl.evaluate(i32 // u16) == 2 - assert _evaluate(u16 / u32) == 0 - assert _evaluate(u16 // u32) == 0 + assert hl.evaluate(u16 / f32) == -0.5 + assert hl.evaluate(u16 // f32) == -1.0 - assert _evaluate(u16 / i32) == 0 - assert _evaluate(i32 // u16) == 2 + assert hl.evaluate(i16 / f64) == -0.25 + assert hl.evaluate(i16 // f64) == -1.0 - assert _evaluate(u16 / f32) == -0.5 - assert _evaluate(u16 // f32) == -1.0 +def test_division_tupled(): + # Same as test_division, but using the tuple variant + assert hl.evaluate((f32 / f64, f32 // f64)) == (-0.5, -1.0) + assert hl.evaluate((i16 / i32, i16 // i32, i32 / i16)) == (-1, -1, -2) + assert hl.evaluate((u16 / u32, u16 // u32)) == (0, 0) + assert hl.evaluate((u16 / i32, i32 // u16)) == (0, 2) + assert hl.evaluate((u16 / f32, u16 // f32)) == (-0.5, -1.0) + assert hl.evaluate((i16 / f64, i16 // f64)) == (-0.25, -1.0) - assert _evaluate(i16 / f64) == -0.25 - assert _evaluate(i16 // f64) == -1.0 +def test_division_gpu(): + # Allow GPU usage -- don't use f64 since not all GPU backends support that + f = hl.cast(hl.Float(32), f64) + assert hl.evaluate_may_gpu((f32 / f, f32 // f)) == (-0.5, -1.0) + assert hl.evaluate_may_gpu((i16 / i32, i16 // i32, i32 / i16)) == (-1, -1, -2) + assert hl.evaluate_may_gpu((u16 / u32, u16 // u32)) == (0, 0) + assert hl.evaluate_may_gpu((u16 / i32, i32 // u16)) == (0, 2) + assert hl.evaluate_may_gpu((u16 / f32, u16 // f32)) == (-0.5, -1.0) + assert hl.evaluate_may_gpu((i16 / f, i16 // f)) == (-0.25, -1.0) if __name__ == "__main__": + test_types() test_division() + test_division_tupled() + test_division_gpu()