// Copyright (C) 2019-2020 Zilliz. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software distributed under the License // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express // or implied. See the License for the specific language governing permissions and limitations under the License #include #include "common/LoadInfo.h" #include "common/Types.h" #include "index/IndexFactory.h" #include "knowhere/comp/index_param.h" #include "segcore/reduce/Reduce.h" #include "segcore/reduce_c.h" #include "test_utils/DataGen.h" #include "test_utils/PbHelper.h" #include "test_utils/indexbuilder_test_utils.h" #include "pb/schema.pb.h" #include "pb/plan.pb.h" #include "query/Plan.h" #include "query/Utils.h" #include "query/PlanImpl.h" #include "query/PlanNode.h" #include "query/PlanProto.h" #include "query/SearchBruteForce.h" #include "query/ExecPlanNodeVisitor.h" #include "segcore/Collection.h" #include "segcore/SegmentSealed.h" #include "segcore/SegmentGrowing.h" #include "segcore/SegmentGrowingImpl.h" #include "test_utils/AssertUtils.h" #include "test_utils/DataGen.h" #include "test_utils/GenExprProto.h" using namespace milvus; using namespace milvus::index; using namespace milvus::query; using namespace milvus::segcore; using namespace knowhere; using milvus::index::VectorIndex; using milvus::segcore::LoadIndexInfo; const int64_t ROW_COUNT = 100 * 1000; // TEST(Float16, Insert) { // int64_t N = ROW_COUNT; // constexpr int64_t size_per_chunk = 32 * 1024; // auto schema = std::make_shared(); // auto float16_vec_fid = schema->AddDebugField( // "float16vec", DataType::VECTOR_FLOAT16, 32, knowhere::metric::L2); // auto i64_fid = schema->AddDebugField("counter", DataType::INT64); // schema->set_primary_field_id(i64_fid); // auto dataset = DataGen(schema, N); // // auto seg_conf = SegcoreConfig::default_config(); // auto segment = CreateGrowingSegment(schema, empty_index_meta); // segment->PreInsert(N); // segment->Insert(0, // N, // dataset.row_ids_.data(), // dataset.timestamps_.data(), // dataset.raw_); // auto float16_ptr = dataset.get_col(float16_vec_fid); // SegmentInternalInterface& interface = *segment; // auto num_chunk = interface.num_chunk(); // ASSERT_EQ(num_chunk, upper_div(N, size_per_chunk)); // auto row_count = interface.get_row_count(); // ASSERT_EQ(N, row_count); // for (auto chunk_id = 0; chunk_id < num_chunk; ++chunk_id) { // auto float16_span = interface.chunk_data( // float16_vec_fid, chunk_id); // auto begin = chunk_id * size_per_chunk; // auto end = std::min((chunk_id + 1) * size_per_chunk, N); // auto size_of_chunk = end - begin; // for (int i = 0; i < size_of_chunk; ++i) { // // std::cout << float16_span.data()[i] << " " << float16_ptr[i + begin * 32] << std::endl; // ASSERT_EQ(float16_span.data()[i], float16_ptr[i + begin * 32]); // } // } // } TEST(Float16, ExecWithoutPredicateFlat) { auto schema = std::make_shared(); auto vec_fid = schema->AddDebugField( "fakevec", DataType::VECTOR_FLOAT16, 32, knowhere::metric::L2); schema->AddDebugField("age", DataType::FLOAT); auto i64_fid = schema->AddDebugField("counter", DataType::INT64); schema->set_primary_field_id(i64_fid); const char* raw_plan = R"(vector_anns: < field_id: 100 query_info: < topk: 5 round_decimal: 3 metric_type: "L2" search_params: "{\"nprobe\": 10}" > placeholder_tag: "$0" >)"; auto plan_str = translate_text_plan_to_binary_plan(raw_plan); auto plan = CreateSearchPlanByExpr(*schema, plan_str.data(), plan_str.size()); int64_t N = ROW_COUNT; auto dataset = DataGen(schema, N); auto segment = CreateGrowingSegment(schema, empty_index_meta); segment->PreInsert(N); segment->Insert(0, N, dataset.row_ids_.data(), dataset.timestamps_.data(), dataset.raw_); auto vec_ptr = dataset.get_col(vec_fid); auto num_queries = 5; auto ph_group_raw = CreatePlaceholderGroup(num_queries, 32, 1024); auto ph_group = ParsePlaceholderGroup(plan.get(), ph_group_raw.SerializeAsString()); Timestamp timestamp = 1000000; auto sr = segment->Search(plan.get(), ph_group.get(), timestamp); std::vector> results; auto json = SearchResultToJson(*sr); std::cout << json.dump(2); } TEST(Float16, GetVector) { auto metricType = knowhere::metric::L2; auto schema = std::make_shared(); auto pk = schema->AddDebugField("pk", DataType::INT64); auto random = schema->AddDebugField("random", DataType::DOUBLE); auto vec = schema->AddDebugField( "embeddings", DataType::VECTOR_FLOAT16, 128, metricType); schema->set_primary_field_id(pk); std::map index_params = { {"index_type", "IVF_FLAT"}, {"metric_type", metricType}, {"nlist", "128"}}; std::map type_params = {{"dim", "128"}}; FieldIndexMeta fieldIndexMeta( vec, std::move(index_params), std::move(type_params)); auto config = SegcoreConfig::default_config(); config.set_chunk_rows(1024); config.set_enable_interim_segment_index(true); std::map filedMap = {{vec, fieldIndexMeta}}; IndexMetaPtr metaPtr = std::make_shared(100000, std::move(filedMap)); auto segment_growing = CreateGrowingSegment(schema, metaPtr, 1, config); auto segment = dynamic_cast(segment_growing.get()); int64_t per_batch = 5000; int64_t n_batch = 20; int64_t dim = 128; for (int64_t i = 0; i < n_batch; i++) { auto dataset = DataGen(schema, per_batch); auto fakevec = dataset.get_col(vec); auto offset = segment->PreInsert(per_batch); segment->Insert(offset, per_batch, dataset.row_ids_.data(), dataset.timestamps_.data(), dataset.raw_); auto num_inserted = (i + 1) * per_batch; auto ids_ds = GenRandomIds(num_inserted); auto result = segment->bulk_subscript(vec, ids_ds->GetIds(), num_inserted); auto vector = result.get()->mutable_vectors()->float16_vector(); EXPECT_TRUE(vector.size() == num_inserted * dim * sizeof(float16)); for (size_t i = 0; i < num_inserted; ++i) { auto id = ids_ds->GetIds()[i]; for (size_t j = 0; j < 128; ++j) { EXPECT_TRUE( reinterpret_cast(vector.data())[i * dim + j] == fakevec[(id % per_batch) * dim + j]); } } } } TEST(Float16, RetrieveEmpty) { auto schema = std::make_shared(); auto fid_64 = schema->AddDebugField("i64", DataType::INT64); auto DIM = 16; auto fid_vec = schema->AddDebugField( "vector_64", DataType::VECTOR_FLOAT16, DIM, knowhere::metric::L2); schema->set_primary_field_id(fid_64); int64_t N = 100; int64_t req_size = 10; auto choose = [=](int i) { return i * 3 % N; }; auto segment = CreateSealedSegment(schema); auto plan = std::make_unique(*schema); std::vector values; { for (int i = 0; i < req_size; ++i) { proto::plan::GenericValue val; val.set_int64_val(choose(i)); values.push_back(val); } } auto term_expr = std::make_shared( milvus::expr::ColumnInfo( fid_64, DataType::INT64, std::vector()), values); plan->plan_node_ = std::make_unique(); plan->plan_node_->plannodes_ = milvus::test::CreateRetrievePlanByExpr(term_expr); std::vector target_offsets{fid_64, fid_vec}; plan->field_ids_ = target_offsets; auto retrieve_results = segment->Retrieve( nullptr, plan.get(), 100, DEFAULT_MAX_OUTPUT_SIZE, false); Assert(retrieve_results->fields_data_size() == target_offsets.size()); auto field0 = retrieve_results->fields_data(0); auto field1 = retrieve_results->fields_data(1); Assert(field0.has_scalars()); auto field0_data = field0.scalars().long_data(); Assert(field0_data.data_size() == 0); Assert(field1.vectors().float16_vector().size() == 0); } TEST(Float16, ExecWithPredicate) { auto schema = std::make_shared(); schema->AddDebugField( "fakevec", DataType::VECTOR_FLOAT16, 16, knowhere::metric::L2); schema->AddDebugField("age", DataType::FLOAT); auto i64_fid = schema->AddDebugField("counter", DataType::INT64); schema->set_primary_field_id(i64_fid); const char* raw_plan = R"(vector_anns: < field_id: 100 predicates: < binary_range_expr: < column_info: < field_id: 101 data_type: Float > lower_inclusive: true, upper_inclusive: false, lower_value: < float_val: -1 > upper_value: < float_val: 1 > > > query_info: < topk: 5 round_decimal: 3 metric_type: "L2" search_params: "{\"nprobe\": 10}" > placeholder_tag: "$0" >)"; int64_t N = ROW_COUNT; auto dataset = DataGen(schema, N); auto segment = CreateGrowingSegment(schema, empty_index_meta); segment->PreInsert(N); segment->Insert(0, N, dataset.row_ids_.data(), dataset.timestamps_.data(), dataset.raw_); auto plan_str = translate_text_plan_to_binary_plan(raw_plan); auto plan = CreateSearchPlanByExpr(*schema, plan_str.data(), plan_str.size()); auto num_queries = 5; auto ph_group_raw = CreatePlaceholderGroup(num_queries, 16, 1024); auto ph_group = ParsePlaceholderGroup(plan.get(), ph_group_raw.SerializeAsString()); auto sr = segment->Search(plan.get(), ph_group.get(), 1L << 63); int topk = 5; query::Json json = SearchResultToJson(*sr); std::cout << json.dump(2); } // TEST(BFloat16, Insert) { // int64_t N = ROW_COUNT; // constexpr int64_t size_per_chunk = 32 * 1024; // auto schema = std::make_shared(); // auto bfloat16_vec_fid = schema->AddDebugField( // "bfloat16vec", DataType::VECTOR_BFLOAT16, 32, knowhere::metric::L2); // auto i64_fid = schema->AddDebugField("counter", DataType::INT64); // schema->set_primary_field_id(i64_fid); // auto dataset = DataGen(schema, N); // // auto seg_conf = SegcoreConfig::default_config(); // auto segment = CreateGrowingSegment(schema, empty_index_meta); // segment->PreInsert(N); // segment->Insert(0, // N, // dataset.row_ids_.data(), // dataset.timestamps_.data(), // dataset.raw_); // auto bfloat16_ptr = dataset.get_col(bfloat16_vec_fid); // SegmentInternalInterface& interface = *segment; // auto num_chunk = interface.num_chunk(); // ASSERT_EQ(num_chunk, upper_div(N, size_per_chunk)); // auto row_count = interface.get_row_count(); // ASSERT_EQ(N, row_count); // for (auto chunk_id = 0; chunk_id < num_chunk; ++chunk_id) { // auto bfloat16_span = interface.chunk_data( // bfloat16_vec_fid, chunk_id); // auto begin = chunk_id * size_per_chunk; // auto end = std::min((chunk_id + 1) * size_per_chunk, N); // auto size_of_chunk = end - begin; // for (int i = 0; i < size_of_chunk; ++i) { // // std::cout << float16_span.data()[i] << " " << float16_ptr[i + begin * 32] << std::endl; // ASSERT_EQ(bfloat16_span.data()[i], bfloat16_ptr[i + begin * 32]); // } // } // } TEST(BFloat16, ExecWithoutPredicateFlat) { auto schema = std::make_shared(); auto vec_fid = schema->AddDebugField( "fakevec", DataType::VECTOR_BFLOAT16, 32, knowhere::metric::L2); schema->AddDebugField("age", DataType::FLOAT); auto i64_fid = schema->AddDebugField("counter", DataType::INT64); schema->set_primary_field_id(i64_fid); const char* raw_plan = R"(vector_anns: < field_id: 100 query_info: < topk: 5 round_decimal: 3 metric_type: "L2" search_params: "{\"nprobe\": 10}" > placeholder_tag: "$0" >)"; auto plan_str = translate_text_plan_to_binary_plan(raw_plan); auto plan = CreateSearchPlanByExpr(*schema, plan_str.data(), plan_str.size()); int64_t N = ROW_COUNT; auto dataset = DataGen(schema, N); auto segment = CreateGrowingSegment(schema, empty_index_meta); segment->PreInsert(N); segment->Insert(0, N, dataset.row_ids_.data(), dataset.timestamps_.data(), dataset.raw_); auto vec_ptr = dataset.get_col(vec_fid); auto num_queries = 5; auto ph_group_raw = CreatePlaceholderGroup(num_queries, 32, 1024); auto ph_group = ParsePlaceholderGroup(plan.get(), ph_group_raw.SerializeAsString()); Timestamp timestamp = 1000000; auto sr = segment->Search(plan.get(), ph_group.get(), timestamp); std::vector> results; auto json = SearchResultToJson(*sr); std::cout << json.dump(2); } TEST(BFloat16, GetVector) { auto metricType = knowhere::metric::L2; auto schema = std::make_shared(); auto pk = schema->AddDebugField("pk", DataType::INT64); auto random = schema->AddDebugField("random", DataType::DOUBLE); auto vec = schema->AddDebugField( "embeddings", DataType::VECTOR_BFLOAT16, 128, metricType); schema->set_primary_field_id(pk); std::map index_params = { {"index_type", "IVF_FLAT"}, {"metric_type", metricType}, {"nlist", "128"}}; std::map type_params = {{"dim", "128"}}; FieldIndexMeta fieldIndexMeta( vec, std::move(index_params), std::move(type_params)); auto config = SegcoreConfig::default_config(); config.set_chunk_rows(1024); config.set_enable_interim_segment_index(true); std::map filedMap = {{vec, fieldIndexMeta}}; IndexMetaPtr metaPtr = std::make_shared(100000, std::move(filedMap)); auto segment_growing = CreateGrowingSegment(schema, metaPtr, 1, config); auto segment = dynamic_cast(segment_growing.get()); int64_t per_batch = 5000; int64_t n_batch = 20; int64_t dim = 128; for (int64_t i = 0; i < n_batch; i++) { auto dataset = DataGen(schema, per_batch); auto fakevec = dataset.get_col(vec); auto offset = segment->PreInsert(per_batch); segment->Insert(offset, per_batch, dataset.row_ids_.data(), dataset.timestamps_.data(), dataset.raw_); auto num_inserted = (i + 1) * per_batch; auto ids_ds = GenRandomIds(num_inserted); auto result = segment->bulk_subscript(vec, ids_ds->GetIds(), num_inserted); auto vector = result.get()->mutable_vectors()->bfloat16_vector(); EXPECT_TRUE(vector.size() == num_inserted * dim * sizeof(bfloat16)); for (size_t i = 0; i < num_inserted; ++i) { auto id = ids_ds->GetIds()[i]; for (size_t j = 0; j < 128; ++j) { EXPECT_TRUE( reinterpret_cast(vector.data())[i * dim + j] == fakevec[(id % per_batch) * dim + j]); } } } } TEST(BFloat16, RetrieveEmpty) { auto schema = std::make_shared(); auto fid_64 = schema->AddDebugField("i64", DataType::INT64); auto DIM = 16; auto fid_vec = schema->AddDebugField( "vector_64", DataType::VECTOR_BFLOAT16, DIM, knowhere::metric::L2); schema->set_primary_field_id(fid_64); int64_t N = 100; int64_t req_size = 10; auto choose = [=](int i) { return i * 3 % N; }; auto segment = CreateSealedSegment(schema); auto plan = std::make_unique(*schema); std::vector values; std::vector retrieve_ints; for (int i = 0; i < req_size; ++i) { values.emplace_back(choose(i)); proto::plan::GenericValue val; val.set_int64_val(i); retrieve_ints.push_back(val); } auto term_expr = std::make_shared( expr::ColumnInfo(fid_64, DataType::INT64), retrieve_ints); auto expr_plan = std::make_shared(DEFAULT_PLANNODE_ID, term_expr); plan->plan_node_ = std::make_unique(); plan->plan_node_->plannodes_ = std::move(expr_plan); std::vector target_offsets{fid_64, fid_vec}; plan->field_ids_ = target_offsets; auto retrieve_results = segment->Retrieve( nullptr, plan.get(), 100, DEFAULT_MAX_OUTPUT_SIZE, false); Assert(retrieve_results->fields_data_size() == target_offsets.size()); auto field0 = retrieve_results->fields_data(0); auto field1 = retrieve_results->fields_data(1); Assert(field0.has_scalars()); auto field0_data = field0.scalars().long_data(); Assert(field0_data.data_size() == 0); Assert(field1.vectors().bfloat16_vector().size() == 0); } TEST(BFloat16, ExecWithPredicate) { auto schema = std::make_shared(); schema->AddDebugField( "fakevec", DataType::VECTOR_BFLOAT16, 16, knowhere::metric::L2); schema->AddDebugField("age", DataType::FLOAT); auto i64_fid = schema->AddDebugField("counter", DataType::INT64); schema->set_primary_field_id(i64_fid); const char* raw_plan = R"(vector_anns: < field_id: 100 predicates: < binary_range_expr: < column_info: < field_id: 101 data_type: Float > lower_inclusive: true, upper_inclusive: false, lower_value: < float_val: -1 > upper_value: < float_val: 1 > > > query_info: < topk: 5 round_decimal: 3 metric_type: "L2" search_params: "{\"nprobe\": 10}" > placeholder_tag: "$0" >)"; int64_t N = ROW_COUNT; auto dataset = DataGen(schema, N); auto segment = CreateGrowingSegment(schema, empty_index_meta); segment->PreInsert(N); segment->Insert(0, N, dataset.row_ids_.data(), dataset.timestamps_.data(), dataset.raw_); auto plan_str = translate_text_plan_to_binary_plan(raw_plan); auto plan = CreateSearchPlanByExpr(*schema, plan_str.data(), plan_str.size()); auto num_queries = 5; auto ph_group_raw = CreatePlaceholderGroup(num_queries, 16, 1024); auto ph_group = ParsePlaceholderGroup(plan.get(), ph_group_raw.SerializeAsString()); Timestamp timestamp = 1000000; auto sr = segment->Search(plan.get(), ph_group.get(), timestamp); int topk = 5; query::Json json = SearchResultToJson(*sr); std::cout << json.dump(2); }