diff options
author | Arne H Juul <arnej27959@users.noreply.github.com> | 2020-11-03 13:26:07 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-11-03 13:26:07 +0100 |
commit | 0b902f8a83015de3f37c8adb449f360add305780 (patch) | |
tree | 8802556daa7d2de9ea12364f389e5dd75dbb947c /eval | |
parent | 83b771a2507be0b3783c2d8329c48125dbb108a1 (diff) | |
parent | 17a344730bca802521110c56c37f47ed16965a03 (diff) |
Merge pull request #15132 from vespa-engine/arnej/add-generic-peek-instruction
Arnej/add generic peek instruction
Diffstat (limited to 'eval')
-rw-r--r-- | eval/CMakeLists.txt | 1 | ||||
-rw-r--r-- | eval/src/tests/instruction/generic_peek/CMakeLists.txt | 9 | ||||
-rw-r--r-- | eval/src/tests/instruction/generic_peek/generic_peek_test.cpp | 236 | ||||
-rw-r--r-- | eval/src/vespa/eval/eval/tensor_function.cpp | 19 | ||||
-rw-r--r-- | eval/src/vespa/eval/instruction/CMakeLists.txt | 1 | ||||
-rw-r--r-- | eval/src/vespa/eval/instruction/generic_peek.cpp | 352 | ||||
-rw-r--r-- | eval/src/vespa/eval/instruction/generic_peek.h | 29 |
7 files changed, 646 insertions, 1 deletions
diff --git a/eval/CMakeLists.txt b/eval/CMakeLists.txt index b0418c4f80d..19b75c7ff46 100644 --- a/eval/CMakeLists.txt +++ b/eval/CMakeLists.txt @@ -42,6 +42,7 @@ vespa_define_module( src/tests/instruction/generic_join src/tests/instruction/generic_map src/tests/instruction/generic_merge + src/tests/instruction/generic_peek src/tests/instruction/generic_reduce src/tests/instruction/generic_rename src/tests/tensor/default_value_builder_factory diff --git a/eval/src/tests/instruction/generic_peek/CMakeLists.txt b/eval/src/tests/instruction/generic_peek/CMakeLists.txt new file mode 100644 index 00000000000..11732c865ec --- /dev/null +++ b/eval/src/tests/instruction/generic_peek/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_peek_test_app TEST + SOURCES + generic_peek_test.cpp + DEPENDS + vespaeval + GTest::GTest +) +vespa_add_test(NAME eval_generic_peek_test_app NO_VALGRIND COMMAND eval_generic_peek_test_app) diff --git a/eval/src/tests/instruction/generic_peek/generic_peek_test.cpp b/eval/src/tests/instruction/generic_peek/generic_peek_test.cpp new file mode 100644 index 00000000000..3874b254ad8 --- /dev/null +++ b/eval/src/tests/instruction/generic_peek/generic_peek_test.cpp @@ -0,0 +1,236 @@ +// 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/tensor_function.h> +#include <vespa/eval/eval/value_codec.h> +#include <vespa/eval/instruction/generic_peek.h> +#include <vespa/eval/eval/interpreted_function.h> +#include <vespa/eval/eval/test/tensor_model.hpp> +#include <vespa/vespalib/util/stringfmt.h> +#include <vespa/vespalib/util/overload.h> +#include <vespa/vespalib/gtest/gtest.h> +#include <vespa/vespalib/stllike/asciistream.h> +#include <stdlib.h> +#include <variant> + +using namespace vespalib; +using namespace vespalib::eval; +using namespace vespalib::eval::instruction; +using namespace vespalib::eval::test; + +using vespalib::make_string_short::fmt; + +std::vector<Layout> peek_layouts = { + {x(4)}, + {x(4),y(5)}, + {x(4),y(5),z(3)}, + float_cells({x(4),y(5),z(3)}), + {x({"-1","0","2"})}, + {x({"-1","0","2"}),y({"-2","0","1"}),z({"-2","-1","0","1","2"})}, + float_cells({x({"-1","0","2"}),y({"-2","0","1"})}), + {x(4),y({"-2","0","1"}),z(3)}, + {x({"-1","0","2"}),y(5),z({"-2","-1","0","1","2"})}, + float_cells({x({"-1","0","2"}),y(5),z({"-2","-1","0","1","2"})}) +}; + +using PeekSpec = GenericPeek::SpecMap; + +TensorSpec reference_peek(const TensorSpec ¶m, const vespalib::string &result_type, const PeekSpec &spec) { + TensorSpec result(result_type); + ValueType param_type = ValueType::from_spec(param.type()); + auto is_mapped_dim = [&](const vespalib::string &name) { + size_t dim_idx = param_type.dimension_index(name); + assert(dim_idx != ValueType::Dimension::npos); + const auto ¶m_dim = param_type.dimensions()[dim_idx]; + return param_dim.is_mapped(); + }; + TensorSpec::Address addr; + for (const auto & [dim_name, label_or_child] : spec) { + std::visit(vespalib::overload + { + [&](const TensorSpec::Label &label) { + addr.emplace(dim_name, label); + }, + [&](const size_t &child_value) { + // here, label_or_child is a size_t specifying the value + // we pretend a child produced + if (is_mapped_dim(dim_name)) { + // (but cast to signed first, to allow labels like the string "-2") + addr.emplace(dim_name, vespalib::make_string("%zd", ssize_t(child_value))); + } else { + addr.emplace(dim_name, child_value); + } + } + }, label_or_child); + } + for (const auto &cell: param.cells()) { + bool keep = true; + TensorSpec::Address my_addr; + for (const auto &binding: cell.first) { + auto pos = addr.find(binding.first); + if (pos == addr.end()) { + my_addr.emplace(binding.first, binding.second); + } else { + if (!(pos->second == binding.second)) { + keep = false; + } + } + } + if (keep) { + result.add(my_addr, cell.second); + } + } + return spec_from_value(*value_from_spec(result, SimpleValueBuilderFactory::get())); +} + + +TensorSpec perform_generic_peek(const TensorSpec &a, const ValueType &result_type, + PeekSpec spec, const ValueBuilderFactory &factory) +{ + auto param = value_from_spec(a, factory); + EXPECT_FALSE(param->type().is_error()); + EXPECT_FALSE(result_type.is_error()); + Stash stash; + std::vector<Value::CREF> my_stack; + my_stack.push_back(*param); + size_t child_idx = 0; + for (auto & [dim_name, label_or_child] : spec) { + if (std::holds_alternative<size_t>(label_or_child)) { + // here, label_or_child is a size_t specifying the value + // this child should produce (but cast to signed first, + // to allow negative values) + ssize_t child_value = std::get<size_t>(label_or_child); + my_stack.push_back(stash.create<DoubleValue>(child_value)); + // overwrite label_or_child, now it should be the index of + // the child for make_instruction + label_or_child = child_idx++; + } + } + auto my_op = GenericPeek::make_instruction(param->type(), result_type, spec, factory, stash); + InterpretedFunction::EvalSingle single(factory, my_op); + return spec_from_value(single.eval(my_stack)); +} + +TensorSpec tensor_function_peek(const TensorSpec &a, const ValueType &result_type, + PeekSpec spec, const ValueBuilderFactory &factory) +{ + Stash stash; + auto param = value_from_spec(a, factory); + EXPECT_FALSE(param->type().is_error()); + EXPECT_FALSE(result_type.is_error()); + std::vector<Value::CREF> my_stack; + my_stack.push_back(*param); + const auto &func_double = tensor_function::inject(ValueType::double_type(), 1, stash); + std::map<vespalib::string, std::variant<TensorSpec::Label, TensorFunction::CREF>> func_spec; + for (auto & [dim_name, label_or_child] : spec) { + if (std::holds_alternative<size_t>(label_or_child)) { + // here, label_or_child is a size_t specifying the value + // this child should produce (but cast to signed first, + // to allow negative values) + ssize_t child_value = std::get<size_t>(label_or_child); + my_stack.push_back(stash.create<DoubleValue>(double(child_value))); + func_spec.emplace(dim_name, func_double); + } else { + auto label = std::get<TensorSpec::Label>(label_or_child); + func_spec.emplace(dim_name, label); + } + } + const auto &func_param = tensor_function::inject(param->type(), 0, stash); + const auto &peek_node = tensor_function::peek(func_param, func_spec, stash); + auto my_op = peek_node.compile_self(factory, stash); + InterpretedFunction::EvalSingle single(factory, my_op); + return spec_from_value(single.eval(my_stack)); +} + +vespalib::string to_str(const PeekSpec &spec) { + vespalib::asciistream os; + os << "{ "; + for (const auto & [dim, label_or_index] : spec) { + os << dim << " : "; + if (std::holds_alternative<size_t>(label_or_index)) { + os << "[" << ssize_t(std::get<size_t>(label_or_index)) << "] "; + } else { + auto label = std::get<TensorSpec::Label>(label_or_index); + if (label.is_mapped()) { + os << "'" << label.name << "' "; + } else { + os << "(" << ssize_t(label.index) << ") "; + } + } + } + os << "}"; + return os.str(); +} + +void verify_peek_equal(const TensorSpec &input, + const PeekSpec &spec, + const ValueBuilderFactory &factory) +{ + ValueType param_type = ValueType::from_spec(input.type()); + std::vector<vespalib::string> reduce_dims; + for (const auto & [dim_name, ignored] : spec) { + reduce_dims.push_back(dim_name); + } + if (reduce_dims.empty()) return; + ValueType result_type = param_type.reduce(reduce_dims); + auto expect = reference_peek(input, result_type.to_spec(), spec); + SCOPED_TRACE(fmt("peek input: %s\n peek spec: %s\n peek result %s\n", + input.to_string().c_str(), + to_str(spec).c_str(), + expect.to_string().c_str())); + auto actual = perform_generic_peek(input, result_type, spec, factory); + EXPECT_EQ(actual, expect); + auto from_func = tensor_function_peek(input, result_type, spec, factory); + EXPECT_EQ(from_func, expect); +} + +void fill_dims_and_check(const TensorSpec &input, + PeekSpec spec, + std::vector<ValueType::Dimension> dimensions, + const ValueBuilderFactory &factory) +{ + if (dimensions.empty()) { + verify_peek_equal(input, spec, factory); + return; + } + auto dim = dimensions.back(); + dimensions.pop_back(); + fill_dims_and_check(input, spec, dimensions, factory); + for (int64_t label_value : {-2, -1, 0, 1, 3}) { + if (dim.is_indexed()) { + size_t index = label_value; + if (index >= dim.size) continue; + TensorSpec::Label label(index); + spec.insert_or_assign(dim.name, label); + } else { + TensorSpec::Label label(make_string("%" PRId64, label_value)); + spec.insert_or_assign(dim.name, label); + } + fill_dims_and_check(input, spec, dimensions, factory); + } + for (int64_t child_value : {-2, -1, 0, 1, 3}) { + spec.insert_or_assign(dim.name, size_t(child_value)); + fill_dims_and_check(input, spec, dimensions, factory); + } +} + +void test_generic_peek_with(const ValueBuilderFactory &factory) { + for (const auto & layout : peek_layouts) { + TensorSpec input = spec(layout, N()); + ValueType input_type = ValueType::from_spec(input.type()); + const auto &dims = input_type.dimensions(); + PeekSpec spec; + fill_dims_and_check(input, spec, dims, factory); + } +} + +TEST(GenericPeekTest, generic_peek_works_for_simple_values) { + test_generic_peek_with(SimpleValueBuilderFactory::get()); +} + +TEST(GenericPeekTest, generic_peek_works_for_fast_values) { + test_generic_peek_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 57036f04e9a..8464aa14b59 100644 --- a/eval/src/vespa/eval/eval/tensor_function.cpp +++ b/eval/src/vespa/eval/eval/tensor_function.cpp @@ -13,6 +13,7 @@ #include <vespa/eval/instruction/generic_join.h> #include <vespa/eval/instruction/generic_map.h> #include <vespa/eval/instruction/generic_merge.h> +#include <vespa/eval/instruction/generic_peek.h> #include <vespa/eval/instruction/generic_reduce.h> #include <vespa/eval/instruction/generic_rename.h> #include <vespa/vespalib/objects/objectdumper.h> @@ -494,8 +495,23 @@ Peek::push_children(std::vector<Child::CREF> &children) const } Instruction -Peek::compile_self(EngineOrFactory, Stash &) const +Peek::compile_self(EngineOrFactory engine, Stash &stash) const { + if (engine.is_factory()) { + instruction::GenericPeek::SpecMap generic_spec; + size_t child_idx = 0; + for (const auto & [dim_name, label_or_child] : spec()) { + std::visit(vespalib::overload { + [&](const TensorSpec::Label &label) { + generic_spec.emplace(dim_name, label); + }, + [&](const TensorFunction::Child &) { + generic_spec.emplace(dim_name, child_idx++); + } + }, label_or_child); + } + return instruction::GenericPeek::make_instruction(param_type(), result_type(), generic_spec, engine.factory(), stash); + } return Instruction(op_tensor_peek, wrap_param<Peek>(*this)); } @@ -613,6 +629,7 @@ const TensorFunction &peek(const TensorFunction ¶m, const std::map<vespalib: for (const auto &dim_spec: spec) { dimensions.push_back(dim_spec.first); } + assert(!dimensions.empty()); ValueType result_type = param.result_type().reduce(dimensions); return stash.create<Peek>(result_type, param, spec); } diff --git a/eval/src/vespa/eval/instruction/CMakeLists.txt b/eval/src/vespa/eval/instruction/CMakeLists.txt index 2cd0577acc9..52f411dc543 100644 --- a/eval/src/vespa/eval/instruction/CMakeLists.txt +++ b/eval/src/vespa/eval/instruction/CMakeLists.txt @@ -5,6 +5,7 @@ vespa_add_library(eval_instruction OBJECT generic_concat generic_create generic_join + generic_peek generic_reduce generic_map generic_merge diff --git a/eval/src/vespa/eval/instruction/generic_peek.cpp b/eval/src/vespa/eval/instruction/generic_peek.cpp new file mode 100644 index 00000000000..651ce4df28a --- /dev/null +++ b/eval/src/vespa/eval/instruction/generic_peek.cpp @@ -0,0 +1,352 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "generic_peek.h" +#include <vespa/eval/eval/nested_loop.h> +#include <vespa/eval/eval/wrap_param.h> +#include <vespa/vespalib/util/overload.h> +#include <vespa/vespalib/util/stash.h> +#include <vespa/vespalib/util/typify.h> +#include <vespa/vespalib/util/visit_ranges.h> +#include <cassert> + +using namespace vespalib::eval::tensor_function; + +namespace vespalib::eval::instruction { + +using State = InterpretedFunction::State; +using Instruction = InterpretedFunction::Instruction; + +namespace { + +static constexpr size_t npos = -1; + +using Spec = GenericPeek::SpecMap; + +size_t count_children(const Spec &spec) +{ + size_t num_children = 0; + for (const auto & [dim_name, child_or_label] : spec) { + if (std::holds_alternative<size_t>(child_or_label)) { + ++num_children; + } + } + return num_children; +} + +struct DimSpec { + vespalib::stringref name; + GenericPeek::MyLabel child_or_label; + bool has_child() const { + return std::holds_alternative<size_t>(child_or_label); + } + bool has_label() const { + return std::holds_alternative<TensorSpec::Label>(child_or_label); + } + size_t get_child_idx() const { + return std::get<size_t>(child_or_label); + } + vespalib::stringref get_label_name() const { + auto label = std::get<TensorSpec::Label>(child_or_label); + assert(label.is_mapped()); + return label.name; + } + size_t get_label_index() const { + auto label = std::get<TensorSpec::Label>(child_or_label); + assert(label.is_indexed()); + return label.index; + } +}; + +struct ExtractedSpecs { + using Dimension = ValueType::Dimension; + struct MyComp { + bool operator() (const Dimension &a, const Spec::value_type &b) { return a.name < b.first; } + bool operator() (const Spec::value_type &a, const Dimension &b) { return a.first < b.name; } + }; + std::vector<Dimension> dimensions; + std::vector<DimSpec> specs; + + ExtractedSpecs(bool indexed, + const std::vector<Dimension> &input_dims, + const Spec &spec) + { + auto visitor = overload + { + [&](visit_ranges_first, const auto &a) { + if (a.is_indexed() == indexed) dimensions.push_back(a); + }, + [&](visit_ranges_second, const auto &) { + // spec has unknown dimension + abort(); + }, + [&](visit_ranges_both, const auto &a, const auto &b) { + if (a.is_indexed() == indexed) { + dimensions.push_back(a); + const auto & [spec_dim_name, child_or_label] = b; + assert(a.name == spec_dim_name); + specs.emplace_back(DimSpec{a.name, child_or_label}); + } + } + }; + visit_ranges(visitor, + input_dims.begin(), input_dims.end(), + spec.begin(), spec.end(), MyComp()); + } + ~ExtractedSpecs(); +}; +ExtractedSpecs::~ExtractedSpecs() = default; + +struct DenseSizes { + std::vector<size_t> size; + std::vector<size_t> stride; + size_t cur_size; + + DenseSizes(const std::vector<ValueType::Dimension> &dims) + : size(), stride(), cur_size(1) + { + for (const auto &dim : dims) { + assert(dim.is_indexed()); + size.push_back(dim.size); + } + stride.resize(size.size()); + for (size_t i = size.size(); i-- > 0; ) { + stride[i] = cur_size; + cur_size *= size[i]; + } + } +}; + +/** Compute input offsets for all output cells */ +struct DensePlan { + size_t in_dense_size; + size_t out_dense_size; + std::vector<size_t> loop_cnt; + std::vector<size_t> in_stride; + size_t verbatim_offset = 0; + struct Child { + size_t idx; + size_t stride; + size_t limit; + }; + std::vector<Child> children; + + DensePlan(const ValueType &input_type, const Spec &spec) + { + const ExtractedSpecs mine(true, input_type.dimensions(), spec); + DenseSizes sizes(mine.dimensions); + in_dense_size = sizes.cur_size; + out_dense_size = 1; + auto pos = mine.specs.begin(); + for (size_t i = 0; i < mine.dimensions.size(); ++i) { + const auto &dim = mine.dimensions[i]; + if ((pos == mine.specs.end()) || (dim.name < pos->name)) { + loop_cnt.push_back(sizes.size[i]); + in_stride.push_back(sizes.stride[i]); + out_dense_size *= sizes.size[i]; + } else { + assert(dim.name == pos->name); + if (pos->has_child()) { + children.push_back(Child{pos->get_child_idx(), sizes.stride[i], sizes.size[i]}); + } else { + assert(pos->has_label()); + size_t label_index = pos->get_label_index(); + assert(label_index < sizes.size[i]); + verbatim_offset += label_index * sizes.stride[i]; + } + ++pos; + } + } + assert(pos == mine.specs.end()); + } + + /** Get initial offset (from verbatim labels and child values) */ + template <typename Getter> + size_t get_offset(const Getter &get_child_value) const { + size_t offset = verbatim_offset; + for (size_t i = 0; i < children.size(); ++i) { + size_t from_child = get_child_value(children[i].idx); + if (from_child < children[i].limit) { + offset += from_child * children[i].stride; + } else { + return npos; + } + } + return offset; + } + + template<typename F> void execute(size_t offset, const F &f) const { + run_nested_loop<F>(offset, loop_cnt, in_stride, f); + } +}; + +struct SparseState { + std::vector<vespalib::string> view_addr; + std::vector<vespalib::stringref> view_refs; + std::vector<const vespalib::stringref *> lookup_refs; + std::vector<vespalib::stringref> output_addr; + std::vector<vespalib::stringref *> fetch_addr; + + SparseState(std::vector<vespalib::string> view_addr_in, size_t out_dims) + : view_addr(std::move(view_addr_in)), + view_refs(view_addr.size()), + lookup_refs(view_addr.size()), + output_addr(out_dims), + fetch_addr(out_dims) + { + for (size_t i = 0; i < view_addr.size(); ++i) { + view_refs[i] = view_addr[i]; + lookup_refs[i] = &view_refs[i]; + } + for (size_t i = 0; i < out_dims; ++i) { + fetch_addr[i] = &output_addr[i]; + } + } + ~SparseState(); +}; +SparseState::~SparseState() = default; + +struct SparsePlan { + size_t out_mapped_dims; + std::vector<DimSpec> lookup_specs; + std::vector<size_t> view_dims; + + SparsePlan(const ValueType &input_type, + const GenericPeek::SpecMap &spec) + : out_mapped_dims(0), + view_dims() + { + ExtractedSpecs mine(false, input_type.dimensions(), spec); + lookup_specs = std::move(mine.specs); + auto pos = lookup_specs.begin(); + for (size_t dim_idx = 0; dim_idx < mine.dimensions.size(); ++dim_idx) { + const auto & dim = mine.dimensions[dim_idx]; + if ((pos == lookup_specs.end()) || (dim.name < pos->name)) { + ++out_mapped_dims; + } else { + assert(dim.name == pos->name); + view_dims.push_back(dim_idx); + ++pos; + } + } + assert(pos == lookup_specs.end()); + } + + ~SparsePlan(); + + template <typename Getter> + SparseState make_state(const Getter &get_child_value) const { + std::vector<vespalib::string> view_addr; + for (const auto & dim : lookup_specs) { + if (dim.has_child()) { + int64_t child_value = get_child_value(dim.get_child_idx()); + view_addr.push_back(vespalib::make_string("%" PRId64, child_value)); + } else { + view_addr.push_back(dim.get_label_name()); + } + } + assert(view_addr.size() == view_dims.size()); + return SparseState(std::move(view_addr), out_mapped_dims); + } +}; +SparsePlan::~SparsePlan() = default; + +struct PeekParam { + const ValueType res_type; + DensePlan dense_plan; + SparsePlan sparse_plan; + size_t num_children; + const ValueBuilderFactory &factory; + + PeekParam(const ValueType &input_type, + const ValueType &res_type_in, + const GenericPeek::SpecMap &spec_in, + const ValueBuilderFactory &factory_in) + : res_type(res_type_in), + dense_plan(input_type, spec_in), + sparse_plan(input_type, spec_in), + num_children(count_children(spec_in)), + factory(factory_in) + { + assert(dense_plan.in_dense_size == input_type.dense_subspace_size()); + assert(dense_plan.out_dense_size == res_type.dense_subspace_size()); + } +}; + +template <typename ICT, typename OCT, typename Getter> +Value::UP +generic_mixed_peek(const ValueType &res_type, + const Value &input_value, + const SparsePlan &sparse_plan, + const DensePlan &dense_plan, + const ValueBuilderFactory &factory, + const Getter &get_child_value) +{ + auto input_cells = input_value.cells().typify<ICT>(); + size_t bad_guess = 1; + auto builder = factory.create_value_builder<OCT>(res_type, + sparse_plan.out_mapped_dims, + dense_plan.out_dense_size, + bad_guess); + size_t filled_subspaces = 0; + size_t dense_offset = dense_plan.get_offset(get_child_value); + if (dense_offset != npos) { + SparseState state = sparse_plan.make_state(get_child_value); + auto view = input_value.index().create_view(sparse_plan.view_dims); + view->lookup(state.lookup_refs); + size_t input_subspace; + while (view->next_result(state.fetch_addr, input_subspace)) { + auto dst = builder->add_subspace(state.output_addr).begin(); + auto input_offset = input_subspace * dense_plan.in_dense_size; + dense_plan.execute(dense_offset + input_offset, + [&](size_t idx) { *dst++ = input_cells[idx]; }); + ++filled_subspaces; + } + } + if ((sparse_plan.out_mapped_dims == 0) && (filled_subspaces == 0)) { + for (auto & v : builder->add_subspace({})) { + v = OCT{}; + } + } + return builder->build(std::move(builder)); +} + +template <typename ICT, typename OCT> +void my_generic_peek_op(State &state, uint64_t param_in) { + const auto ¶m = unwrap_param<PeekParam>(param_in); + const Value & input_value = state.peek(param.num_children); + const size_t last_child = param.num_children - 1; + auto get_child_value = [&] (size_t child_idx) { + size_t stack_idx = last_child - child_idx; + return int64_t(state.peek(stack_idx).as_double()); + }; + auto up = generic_mixed_peek<ICT,OCT>(param.res_type, input_value, + param.sparse_plan, param.dense_plan, + param.factory, get_child_value); + const Value &result = *state.stash.create<Value::UP>(std::move(up)); + // num_children does not include the "input" param + state.pop_n_push(param.num_children + 1, result); +} + +struct SelectGenericPeekOp { + template <typename ICT, typename OCT> static auto invoke() { + return my_generic_peek_op<ICT,OCT>; + } +}; + +//----------------------------------------------------------------------------- + +} // namespace <unnamed> + +Instruction +GenericPeek::make_instruction(const ValueType &input_type, + const ValueType &res_type, + const SpecMap &spec, + const ValueBuilderFactory &factory, + Stash &stash) +{ + const auto ¶m = stash.create<PeekParam>(input_type, res_type, spec, factory); + auto fun = typify_invoke<2,TypifyCellType,SelectGenericPeekOp>(input_type.cell_type(), res_type.cell_type()); + return Instruction(fun, wrap_param<PeekParam>(param)); +} + +} // namespace diff --git a/eval/src/vespa/eval/instruction/generic_peek.h b/eval/src/vespa/eval/instruction/generic_peek.h new file mode 100644 index 00000000000..d31b47238cb --- /dev/null +++ b/eval/src/vespa/eval/instruction/generic_peek.h @@ -0,0 +1,29 @@ +// 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/tensor_spec.h> +#include <vespa/eval/eval/interpreted_function.h> +#include <map> + +namespace vespalib { class Stash; } +namespace vespalib::eval { struct ValueBuilderFactory; } + +namespace vespalib::eval::instruction { + +//----------------------------------------------------------------------------- + +struct GenericPeek { + using MyLabel = std::variant<TensorSpec::Label, size_t>; + using SpecMap = std::map<vespalib::string, MyLabel>; + + static InterpretedFunction::Instruction + make_instruction(const ValueType &input_type, + const ValueType &res_type, + const SpecMap &spec, + const ValueBuilderFactory &factory, + Stash &stash); +}; + +} // namespace |