* -------------------------------------------------------------------------
* This file is part of the IndexSDK project.
* Copyright (c) 2025 Huawei Technologies Co.,Ltd.
*
* IndexSDK is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*/
#include <numeric>
#include <cmath>
#include <random>
#include <gtest/gtest.h>
#include <cstring>
#include <sys/time.h>
#include <faiss/index_io.h>
#include <cstdlib>
#include <mockcpp/mockcpp.hpp>
#include "faiss/ascendhost/include/index/AscendIndexCluster.h"
#include "faiss/impl/AuxIndexStructures.h"
#include "acl.h"
#include "ErrorCode.h"
namespace ascend {
constexpr int DIM = 512;
constexpr size_t BASE_SIZE = 1000;
constexpr int CUBE_ALIGN = 16;
constexpr int CAP = 1200;
const std::vector<int> DEVICES = { 0 };
constexpr int FAST_RAND_MAX = 0x7FFF;
const unsigned int TABLELEN_REDUNDANCY = 48;
unsigned int g_seed = 10;
static int32_t StubAclrtSetDevice(int32_t)
{
return 1;
}
TEST(TestAscendIndexUTCluster, init_invalid_input)
{
int dim = DIM;
int capacity = CAP;
auto metricType = faiss::MetricType::METRIC_INNER_PRODUCT;
faiss::ascend::AscendIndexCluster index;
auto ret = index.Init(dim, capacity, metricType, DEVICES);
EXPECT_EQ(ret, 0);
index.Finalize();
MOCKER(aclrtSetDevice).stubs().will(invoke(StubAclrtSetDevice));
ret = index.Init(dim, capacity, metricType, DEVICES);
EXPECT_EQ(ret, APP_ERR_ACL_SET_DEVICE_FAILED);
index.Finalize();
GlobalMockObject::verify();
int invalidCap = 12000001;
ret = index.Init(dim, invalidCap, metricType, DEVICES);
EXPECT_EQ(ret, APP_ERR_INVALID_PARAM);
index.Finalize();
ret = index.Init(dim, 0, metricType, DEVICES);
EXPECT_EQ(ret, APP_ERR_INVALID_PARAM);
index.Finalize();
ret = index.Init(dim + 1, capacity, metricType, DEVICES);
EXPECT_EQ(ret, APP_ERR_INVALID_PARAM);
index.Finalize();
ret = index.Init(dim, capacity, faiss::MetricType::METRIC_L2, DEVICES);
EXPECT_EQ(ret, APP_ERR_INVALID_PARAM);
index.Finalize();
int64_t resourceSize = static_cast<int64_t>(2) * static_cast<int64_t>(1024 * 1024 * 1024);
ret = index.Init(dim, capacity, metricType, DEVICES, resourceSize * 3);
EXPECT_EQ(ret, APP_ERR_INVALID_PARAM);
index.Finalize();
ret = index.Init(dim, capacity, metricType, DEVICES, 0);
EXPECT_EQ(ret, APP_ERR_INVALID_PARAM);
index.Finalize();
ret = index.Init(dim, capacity, metricType, { 0, 1 });
EXPECT_EQ(ret, APP_ERR_INVALID_PARAM);
index.Finalize();
}
TEST(TestAscendIndexUTCluster, add_invalid_input)
{
int addn = BASE_SIZE;
int dim = DIM;
int capacity = CAP;
std::vector<float> addVec(addn * dim, 0);
std::vector<uint32_t> ids(addn, 0);
auto metricType = faiss::MetricType::METRIC_INNER_PRODUCT;
faiss::ascend::AscendIndexCluster index;
auto ret = index.Init(dim, capacity, metricType, DEVICES);
EXPECT_EQ(ret, 0);
ret = index.AddFeatures(addn, addVec.data(), ids.data());
EXPECT_EQ(ret, 0);
ret = index.AddFeatures(addn / CUBE_ALIGN * CUBE_ALIGN - 1, addVec.data(), ids.data());
EXPECT_EQ(ret, 0);
MOCKER(aclrtSetDevice).stubs().will(invoke(StubAclrtSetDevice));
ret = index.AddFeatures(addn, addVec.data(), ids.data());
EXPECT_EQ(ret, APP_ERR_ACL_SET_DEVICE_FAILED);
GlobalMockObject::verify();
ret = index.AddFeatures(capacity + 1, addVec.data(), ids.data());
EXPECT_EQ(ret, APP_ERR_INVALID_PARAM);
ret = index.AddFeatures(0, addVec.data(), ids.data());
EXPECT_EQ(ret, APP_ERR_INVALID_PARAM);
ret = index.AddFeatures(addn, nullptr, ids.data());
EXPECT_EQ(ret, APP_ERR_INVALID_PARAM);
ret = index.AddFeatures(addn, addVec.data(), nullptr);
EXPECT_EQ(ret, APP_ERR_INVALID_PARAM);
index.Finalize();
}
TEST(TestAscendIndexUTCluster, compute_invalid_input)
{
std::vector<float> addVec(BASE_SIZE * DIM, 0);
std::vector<uint32_t> ids(BASE_SIZE, 0);
auto metricType = faiss::MetricType::METRIC_INNER_PRODUCT;
faiss::ascend::AscendIndexCluster index;
auto ret = index.Init(DIM, CAP, metricType, DEVICES);
EXPECT_EQ(ret, 0);
ret = index.AddFeatures(BASE_SIZE, addVec.data(), ids.data());
EXPECT_EQ(ret, 0);
uint32_t codeNum = BASE_SIZE / 100;
uint32_t codeStartIdx = 0;
int nq = 127;
float threshold = 0.75;
std::vector<uint32_t> queryIdxArr(nq, 0);
std::vector<std::vector<float>> resDistArr(nq);
std::vector<std::vector<uint32_t>> resIdxArr(nq);
ret = index.ComputeDistanceByThreshold(
queryIdxArr, codeStartIdx, codeNum, threshold, true, resDistArr, resIdxArr);
EXPECT_EQ(ret, 0);
MOCKER(aclrtSetDevice).stubs().will(invoke(StubAclrtSetDevice));
ret = index.ComputeDistanceByThreshold(
queryIdxArr, codeStartIdx, codeNum, threshold, true, resDistArr, resIdxArr);
EXPECT_EQ(ret, APP_ERR_ACL_SET_DEVICE_FAILED);
GlobalMockObject::verify();
std::vector<uint32_t> queryTemp;
ret = index.ComputeDistanceByThreshold(
queryTemp, codeStartIdx, codeNum, threshold, true, resDistArr, resIdxArr);
EXPECT_EQ(ret, APP_ERR_INVALID_PARAM);
ret = index.ComputeDistanceByThreshold(
queryIdxArr, codeStartIdx, 0, threshold, true, resDistArr, resIdxArr);
EXPECT_EQ(ret, APP_ERR_INVALID_PARAM);
resDistArr.resize(0);
ret = index.ComputeDistanceByThreshold(
queryIdxArr, codeStartIdx, 0, threshold, true, resDistArr, resIdxArr);
EXPECT_EQ(ret, APP_ERR_INVALID_PARAM);
resIdxArr.resize(nq - 1);
ret = index.ComputeDistanceByThreshold(
queryIdxArr, codeStartIdx, 0, threshold, true, resDistArr, resIdxArr);
EXPECT_EQ(ret, APP_ERR_INVALID_PARAM);
index.Finalize();
}
TEST(TestAscendIndexUTCluster, All)
{
int addn = BASE_SIZE;
int dim = DIM;
int capacity = CAP;
uint32_t codeNum = BASE_SIZE / 1000;
uint32_t codeStartIdx = 0;
int nq = 127;
bool aboveFilter = true;
float threshold = 0.75;
std::vector<float> addVec(addn * dim, 0);
std::vector<uint32_t> queryIdxArr(nq, 0);
std::vector<uint32_t> ids(addn, 0);
auto metricType = faiss::MetricType::METRIC_INNER_PRODUCT;
faiss::ascend::AscendIndexCluster index;
auto ret = index.Init(dim, capacity, metricType, DEVICES);
EXPECT_EQ(ret, 0);
ret = index.AddFeatures(addn, addVec.data(), ids.data());
EXPECT_EQ(ret, 0);
std::vector<std::vector<float>> resDistArr(nq);
std::vector<std::vector<uint32_t>> resIdxArr(nq);
ret = index.ComputeDistanceByThreshold(
queryIdxArr, codeStartIdx, codeNum, threshold, aboveFilter, resDistArr, resIdxArr);
EXPECT_EQ(ret, 0);
nq = 1;
std::vector<uint32_t> queryIdxArr02(nq, 0);
std::vector<std::vector<float>> resDistArr02(nq);
std::vector<std::vector<uint32_t>> resIdxArr02(nq);
ret = index.ComputeDistanceByThreshold(
queryIdxArr02, codeStartIdx, codeNum, threshold, aboveFilter, resDistArr02, resIdxArr02);
EXPECT_EQ(ret, 0);
index.Finalize();
}
TEST(TestAscendIndexUTCluster, ComputeDistanceByIdx)
{
int queryN = 2;
const int maxNum = 10;
const size_t addn = 100;
const int64_t resourceSize = 1 * 1024 * 1024 * 1024;
int dim = DIM;
faiss::ascend::AscendIndexCluster index;
std::vector<int> deviceList = { 0 };
auto metricType = faiss::MetricType::METRIC_INNER_PRODUCT;
auto ret = index.Init(dim, CAP, metricType, deviceList, resourceSize);
EXPECT_EQ(ret, 0);
std::vector<uint16_t> addVecFp16(addn * dim, 1);
std::vector<int64_t> ids(addn);
std::iota(ids.begin(), ids.end(), 0);
ret = index.AddFeatures(addn, addVecFp16.data(), ids.data());
EXPECT_EQ(ret, 0);
std::vector<uint16_t> queries;
queries.assign(addVecFp16.begin(), addVecFp16.begin() + queryN * dim);
std::vector<int> num(queryN, maxNum);
std::vector<float> distances(queryN * maxNum);
std::vector<uint32_t> indice(queryN * maxNum, 1);
unsigned int tableLen = 10000;
std::vector<float> table(tableLen + TABLELEN_REDUNDANCY);
ret = index.ComputeDistanceByIdx(queryN, queries.data(), num.data(), indice.data(), distances.data());
EXPECT_EQ(ret, 0);
index.Finalize();
}
TEST(TestAscendIndexUTCluster, SearchByThreshold)
{
int queryN = 2;
const size_t addn = 100;
const int64_t resourceSize = 1 * 1024 * 1024 * 1024;
float threshold = 0.768;
int topk = 200;
int dim = DIM;
faiss::ascend::AscendIndexCluster index;
std::vector<int> deviceList = { 0 };
auto metricType = faiss::MetricType::METRIC_INNER_PRODUCT;
auto ret = index.Init(dim, CAP, metricType, deviceList, resourceSize);
EXPECT_EQ(ret, 0);
std::vector<uint16_t> addVecFp16(addn * dim, 1);
std::vector<int64_t> ids(addn);
std::iota(ids.begin(), ids.end(), 0);
ret = index.AddFeatures(addn, addVecFp16.data(), ids.data());
EXPECT_EQ(ret, 0);
std::vector<uint16_t> queries;
queries.assign(addVecFp16.begin(), addVecFp16.begin() + queryN * dim);
std::vector<int> num(queryN);
std::vector<int64_t> idxs(queryN * topk);
std::vector<float> distances(queryN * topk);
unsigned int tableLen = 10000;
std::vector<float> table(tableLen + TABLELEN_REDUNDANCY, 1.0);
ret = index.SearchByThreshold(queryN, queries.data(), threshold, topk,
num.data(), idxs.data(), distances.data(), tableLen, table.data());
EXPECT_EQ(ret, 0);
index.Finalize();
}
* Case Description: 测试正常情况
* Preset Condition: 所有参数都有效,n在范围内,features和indices不为空,且所有索引值有效
* Test Steps: 1.准备输入数据 2.调用AddFeatures方法
* Expected Result: 返回APP_ERR_OK
*/
TEST(TestAscendIndexUTCluster, AddFeatures_ShouldReturnOk_WhenAllParamsAreValid)
{
int queryN = 2;
const int64_t resourceSize = 1 * 1024 * 1024 * 1024;
float threshold = 0.768;
int dim = DIM;
faiss::ascend::AscendIndexCluster index;
std::vector<int> deviceList = { 0 };
auto metricType = faiss::MetricType::METRIC_INNER_PRODUCT;
auto ret = index.Init(dim, CAP, metricType, deviceList, resourceSize);
EXPECT_EQ(ret, 0);
int n = 5;
std::vector<uint16_t> features(n * dim, 1);
std::vector<int64_t> indices(n, 1);
APP_ERROR result = index.AddFeatures(n, features.data(), indices.data());
EXPECT_EQ(result, APP_ERR_OK);
index.Finalize();
}
* Case Description: 测试n=0的情况
* Preset Condition: n=0
* Test Steps: 1.设置n=0 2.调用AddFeatures方法
* Expected Result: 返回APP_ERR_INVALID_PARAM
*/
TEST(TestAscendIndexUTCluster, AddFeatures_ShouldReturnInvalidParam_WhenNIsZero)
{
const int64_t resourceSize = 1 * 1024 * 1024 * 1024;
int dim = DIM;
faiss::ascend::AscendIndexCluster index;
std::vector<int> deviceList = { 0 };
auto metricType = faiss::MetricType::METRIC_INNER_PRODUCT;
auto ret = index.Init(dim, CAP, metricType, deviceList, resourceSize);
EXPECT_EQ(ret, 0);
int n = 0;
const uint16_t* features = nullptr;
const int64_t* indices = nullptr;
APP_ERROR result = index.AddFeatures(n, features, indices);
EXPECT_EQ(result, APP_ERR_INVALID_PARAM);
index.Finalize();
}
* Case Description: 测试未初始化的情况
* Preset Condition: 当isInitialized为false时
* Test Steps: 1.设置isInitialized为false 2.调用SearchByThreshold方法
* Expected Result: 返回APP_ERR_INVALID_PARAM
*/
TEST(TestAscendIndexUTCluster, SearchByThreshold_ShouldReturnInvalidParam_WhenNotInitialized)
{
const int64_t resourceSize = 1 * 1024 * 1024 * 1024;
int dim = DIM;
faiss::ascend::AscendIndexCluster index;
std::vector<int> deviceList = { 0 };
auto metricType = faiss::MetricType::METRIC_INNER_PRODUCT;
auto ret = index.Init(dim, CAP, metricType, deviceList, resourceSize);
EXPECT_EQ(ret, 0);
int n = 5;
std::vector<uint16_t> features(n * dim, 1);
std::vector<int64_t> indices(n, 1);
APP_ERROR result = index.AddFeatures(n, features.data(), indices.data());
EXPECT_EQ(result, 0);
const uint16_t* queries = nullptr;
float threshold = 0.0f;
int topk = 1;
int* num = nullptr;
int64_t* labels = nullptr;
float* distances = nullptr;
unsigned int tableLen = 0;
const float* table = nullptr;
result = index.SearchByThreshold(n, queries, threshold, topk, num, labels, distances, tableLen, table);
EXPECT_EQ(result, APP_ERR_INVALID_PARAM);
index.Finalize();
}
TEST(TestAscendIndexUTCluster, GetNTotal)
{
const size_t addn = 100;
const int64_t resourceSize = 1 * 1024 * 1024 * 1024;
faiss::ascend::AscendIndexCluster index;
std::vector<int> deviceList = { 0 };
auto metricType = faiss::MetricType::METRIC_INNER_PRODUCT;
auto ret = index.Init(DIM, CAP, metricType, deviceList, resourceSize);
EXPECT_EQ(ret, 0);
std::vector<uint16_t> addVecFp16(addn * DIM, 1);
std::vector<int64_t> ids(addn);
std::iota(ids.begin(), ids.end(), 0);
ret = index.AddFeatures(addn, addVecFp16.data(), ids.data());
EXPECT_EQ(ret, 0);
int num = index.GetNTotal();
EXPECT_EQ(num, addn);
index.Finalize();
}
}