diff options
-rw-r--r-- | eval/CMakeLists.txt | 1 | ||||
-rw-r--r-- | eval/src/tests/instruction/generic_cell_cast/CMakeLists.txt | 9 | ||||
-rw-r--r-- | eval/src/tests/instruction/generic_cell_cast/generic_cell_cast_test.cpp | 69 | ||||
-rw-r--r-- | eval/src/vespa/eval/eval/tensor_function.cpp | 9 | ||||
-rw-r--r-- | eval/src/vespa/eval/eval/tensor_function.h | 14 | ||||
-rw-r--r-- | eval/src/vespa/eval/eval/test/reference_operations.cpp | 12 | ||||
-rw-r--r-- | eval/src/vespa/eval/eval/test/reference_operations.h | 1 | ||||
-rw-r--r-- | eval/src/vespa/eval/instruction/CMakeLists.txt | 1 | ||||
-rw-r--r-- | eval/src/vespa/eval/instruction/generic_cell_cast.cpp | 55 | ||||
-rw-r--r-- | eval/src/vespa/eval/instruction/generic_cell_cast.h | 19 |
10 files changed, 190 insertions, 0 deletions
diff --git a/eval/CMakeLists.txt b/eval/CMakeLists.txt index 66270bb323b..05ac4cb78ab 100644 --- a/eval/CMakeLists.txt +++ b/eval/CMakeLists.txt @@ -50,6 +50,7 @@ vespa_define_module( src/tests/instruction/dense_tensor_peek_function src/tests/instruction/dense_xw_product_function src/tests/instruction/fast_rename_optimizer + src/tests/instruction/generic_cell_cast src/tests/instruction/generic_concat src/tests/instruction/generic_create src/tests/instruction/generic_join diff --git a/eval/src/tests/instruction/generic_cell_cast/CMakeLists.txt b/eval/src/tests/instruction/generic_cell_cast/CMakeLists.txt new file mode 100644 index 00000000000..3c7e9c5b83f --- /dev/null +++ b/eval/src/tests/instruction/generic_cell_cast/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(eval_generic_cell_cast_test_app TEST + SOURCES + generic_cell_cast_test.cpp + DEPENDS + vespaeval + GTest::GTest +) +vespa_add_test(NAME eval_generic_cell_cast_test_app COMMAND eval_generic_cell_cast_test_app) diff --git a/eval/src/tests/instruction/generic_cell_cast/generic_cell_cast_test.cpp b/eval/src/tests/instruction/generic_cell_cast/generic_cell_cast_test.cpp new file mode 100644 index 00000000000..26dd9737fe1 --- /dev/null +++ b/eval/src/tests/instruction/generic_cell_cast/generic_cell_cast_test.cpp @@ -0,0 +1,69 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/eval/eval/simple_value.h> +#include <vespa/eval/eval/fast_value.h> +#include <vespa/eval/eval/value_codec.h> +#include <vespa/eval/instruction/generic_cell_cast.h> +#include <vespa/eval/eval/interpreted_function.h> +#include <vespa/eval/eval/test/reference_operations.h> +#include <vespa/eval/eval/test/gen_spec.h> +#include <vespa/vespalib/util/stringfmt.h> +#include <vespa/vespalib/gtest/gtest.h> + +using namespace vespalib; +using namespace vespalib::eval; +using namespace vespalib::eval::instruction; +using namespace vespalib::eval::test; + +using vespalib::make_string_short::fmt; + +GenSpec::seq_t N_16ths = [] (size_t i) noexcept { return (i + 1.0) / 16.0; }; + +GenSpec G() { return GenSpec().seq(N_16ths); } + +const std::vector<GenSpec> layouts = { + G(), + G().idx("x", 3), + G().idx("x", 3).idx("y", 5), + G().idx("x", 3).idx("y", 5).idx("z", 7), + G().map("x", {"a","b","c"}), + G().map("x", {"a","b","c"}).map("y", {"foo","bar"}), + G().map("x", {"a","b","c"}).map("y", {"foo","bar"}).map("z", {"i","j","k","l"}), + G().idx("x", 3).map("y", {"foo", "bar"}).idx("z", 7), + G().map("x", {"a","b","c"}).idx("y", 5).map("z", {"i","j","k","l"}) +}; + +TensorSpec perform_generic_cell_cast(const TensorSpec &a, CellType to, const ValueBuilderFactory &factory) +{ + Stash stash; + auto lhs = value_from_spec(a, factory); + auto res_type = ValueType::make_type(to, lhs->type().dimensions()); + auto my_op = GenericCellCast::make_instruction(res_type, lhs->type(), stash); + InterpretedFunction::EvalSingle single(factory, my_op); + return spec_from_value(single.eval(std::vector<Value::CREF>({*lhs}))); +} + +void test_generic_cell_cast_with(const ValueBuilderFactory &factory) { + for (const auto &layout : layouts) { + for (TensorSpec lhs : { layout.cpy().cells_float(), + layout.cpy().cells_double() }) + { + for (CellType ct : { CellType::FLOAT, CellType::DOUBLE }) { + SCOPED_TRACE(fmt("\n===\nLHS: %s\n===\n", lhs.to_string().c_str())); + auto expect = ReferenceOperations::cell_cast(lhs, ct); + auto actual = perform_generic_cell_cast(lhs, ct, factory); + EXPECT_EQ(actual, expect); + } + } + } +} + +TEST(GenericCellCastTest, generic_cell_cast_works_for_simple_values) { + test_generic_cell_cast_with(SimpleValueBuilderFactory::get()); +} + +TEST(GenericCellCastTest, generic_cell_cast_works_for_fast_values) { + test_generic_cell_cast_with(FastValueBuilderFactory::get()); +} + +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/eval/src/vespa/eval/eval/tensor_function.cpp b/eval/src/vespa/eval/eval/tensor_function.cpp index 4ea7348f61e..ba5e0bb08a6 100644 --- a/eval/src/vespa/eval/eval/tensor_function.cpp +++ b/eval/src/vespa/eval/eval/tensor_function.cpp @@ -5,6 +5,7 @@ #include "operation.h" #include "visit_stuff.h" #include "string_stuff.h" +#include <vespa/eval/instruction/generic_cell_cast.h> #include <vespa/eval/instruction/generic_concat.h> #include <vespa/eval/instruction/generic_create.h> #include <vespa/eval/instruction/generic_join.h> @@ -311,6 +312,14 @@ Lambda::visit_self(vespalib::ObjectVisitor &visitor) const //----------------------------------------------------------------------------- +InterpretedFunction::Instruction +CellCast::compile_self(const ValueBuilderFactory &, Stash &stash) const +{ + return instruction::GenericCellCast::make_instruction(result_type(), child().result_type(), stash); +} + +//----------------------------------------------------------------------------- + void Peek::push_children(std::vector<Child::CREF> &children) const { diff --git a/eval/src/vespa/eval/eval/tensor_function.h b/eval/src/vespa/eval/eval/tensor_function.h index ed1106cccc1..0576ba1fafe 100644 --- a/eval/src/vespa/eval/eval/tensor_function.h +++ b/eval/src/vespa/eval/eval/tensor_function.h @@ -355,6 +355,20 @@ public: //----------------------------------------------------------------------------- +class CellCast : public Op1 +{ +private: + using Super = Op1; +public: + CellCast(const ValueType &result_type_in, const TensorFunction &child_in) + : Super(result_type_in, child_in) + {} + bool result_is_mutable() const override { return true; } + InterpretedFunction::Instruction compile_self(const ValueBuilderFactory &factory, Stash &stash) const override; +}; + +//----------------------------------------------------------------------------- + class Peek : public Node { using Super = Node; diff --git a/eval/src/vespa/eval/eval/test/reference_operations.cpp b/eval/src/vespa/eval/eval/test/reference_operations.cpp index 5edd1a4f9a0..c89dc7b39ba 100644 --- a/eval/src/vespa/eval/eval/test/reference_operations.cpp +++ b/eval/src/vespa/eval/eval/test/reference_operations.cpp @@ -72,6 +72,18 @@ vespalib::string rename_dimension(const vespalib::string &name, const std::vecto } // namespace <unnamed> +TensorSpec ReferenceOperations::cell_cast(const TensorSpec &a, CellType to) { + ValueType a_type = ValueType::from_spec(a.type()); + ValueType res_type = ValueType::make_type(to, a_type.dimensions()); + TensorSpec result(res_type.to_spec()); + if (res_type.is_error()) { + return result; + } + for (const auto & [ addr, value ]: a.cells()) { + result.add(addr, value); + } + return result; +} TensorSpec ReferenceOperations::concat(const TensorSpec &a, const TensorSpec &b, const std::string &concat_dim) { ValueType a_type = ValueType::from_spec(a.type()); diff --git a/eval/src/vespa/eval/eval/test/reference_operations.h b/eval/src/vespa/eval/eval/test/reference_operations.h index 588215853f9..02651e3971c 100644 --- a/eval/src/vespa/eval/eval/test/reference_operations.h +++ b/eval/src/vespa/eval/eval/test/reference_operations.h @@ -28,6 +28,7 @@ struct ReferenceOperations { // start at 1. using PeekSpec = tensor_function::Peek::Spec; + static TensorSpec cell_cast(const TensorSpec &a, CellType to); static TensorSpec concat(const TensorSpec &a, const TensorSpec &b, const std::string &concat_dim); static TensorSpec create(const vespalib::string &type, const CreateSpec &spec, const std::vector<TensorSpec> &children); static TensorSpec join(const TensorSpec &a, const TensorSpec &b, join_fun_t function); diff --git a/eval/src/vespa/eval/instruction/CMakeLists.txt b/eval/src/vespa/eval/instruction/CMakeLists.txt index 97838f2adb9..7a299738e78 100644 --- a/eval/src/vespa/eval/instruction/CMakeLists.txt +++ b/eval/src/vespa/eval/instruction/CMakeLists.txt @@ -15,6 +15,7 @@ vespa_add_library(eval_instruction OBJECT dense_tensor_peek_function.cpp dense_xw_product_function.cpp fast_rename_optimizer.cpp + generic_cell_cast.cpp generic_concat.cpp generic_create.cpp generic_join.cpp diff --git a/eval/src/vespa/eval/instruction/generic_cell_cast.cpp b/eval/src/vespa/eval/instruction/generic_cell_cast.cpp new file mode 100644 index 00000000000..0785627974e --- /dev/null +++ b/eval/src/vespa/eval/instruction/generic_cell_cast.cpp @@ -0,0 +1,55 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "generic_cell_cast.h" +#include <vespa/eval/eval/value.h> +#include <vespa/eval/eval/wrap_param.h> +#include <vespa/vespalib/util/stash.h> +#include <vespa/vespalib/util/typify.h> +#include <cassert> + +using namespace vespalib::eval::tensor_function; + +namespace vespalib::eval::instruction { + +using State = InterpretedFunction::State; +using Instruction = InterpretedFunction::Instruction; + +namespace { + +template <typename ICT, typename OCT> +void my_generic_cell_cast_op(State &state, uint64_t param_in) { + const auto &res_type = unwrap_param<ValueType>(param_in); + const Value &a = state.peek(0); + auto input_cells = a.cells().typify<ICT>(); + auto output_cells = state.stash.create_uninitialized_array<OCT>(input_cells.size()); + auto pos = output_cells.begin(); + for (ICT value : input_cells) { + *pos++ = (OCT) value; + } + assert(pos == output_cells.end()); + Value &result_ref = state.stash.create<ValueView>(res_type, a.index(), TypedCells(output_cells)); + state.pop_push(result_ref); +} + +struct SelectGenericCellCastOp { + template <typename ICT, typename OCT> + static auto invoke() { + return my_generic_cell_cast_op<ICT, OCT>; + } +}; + +} // namespace <unnamed> + +InterpretedFunction::Instruction +GenericCellCast::make_instruction(const ValueType &result_type, + const ValueType &input_type, + Stash &stash) +{ + assert(input_type.dimensions() == result_type.dimensions()); + auto ¶m = stash.create<ValueType>(result_type); + auto op = typify_invoke<2,TypifyCellType,SelectGenericCellCastOp>( + input_type.cell_type(), result_type.cell_type()); + return Instruction(op, wrap_param<ValueType>(param)); +} + +} // namespace diff --git a/eval/src/vespa/eval/instruction/generic_cell_cast.h b/eval/src/vespa/eval/instruction/generic_cell_cast.h new file mode 100644 index 00000000000..b13c32cf689 --- /dev/null +++ b/eval/src/vespa/eval/instruction/generic_cell_cast.h @@ -0,0 +1,19 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include <vespa/eval/eval/value_type.h> +#include <vespa/eval/eval/interpreted_function.h> + +namespace vespalib::eval { struct ValueBuilderFactory; } + +namespace vespalib::eval::instruction { + +struct GenericCellCast { + static InterpretedFunction::Instruction + make_instruction(const ValueType &result_type, + const ValueType &input_type, + Stash &stash); +}; + +} // namespace |