summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--eval/CMakeLists.txt1
-rw-r--r--eval/src/tests/instruction/generic_cell_cast/CMakeLists.txt9
-rw-r--r--eval/src/tests/instruction/generic_cell_cast/generic_cell_cast_test.cpp69
-rw-r--r--eval/src/vespa/eval/eval/tensor_function.cpp9
-rw-r--r--eval/src/vespa/eval/eval/tensor_function.h14
-rw-r--r--eval/src/vespa/eval/eval/test/reference_operations.cpp12
-rw-r--r--eval/src/vespa/eval/eval/test/reference_operations.h1
-rw-r--r--eval/src/vespa/eval/instruction/CMakeLists.txt1
-rw-r--r--eval/src/vespa/eval/instruction/generic_cell_cast.cpp55
-rw-r--r--eval/src/vespa/eval/instruction/generic_cell_cast.h19
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 &param = 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