| 61 | throw std::invalid_argument( |
| 62 | "number of centroid rows must match index num_clusters" |
| 63 | ); |
| 64 | } |
| 65 | if (static_cast<size_t>(cluster_ids_array.shape(0)) != |
| 66 | static_cast<size_t>(data_array.shape(0))) { |
| 67 | throw std::invalid_argument( |
| 68 | "cluster_ids length must match number of rows in data" |
| 69 | ); |
| 70 | } |
| 71 | |
| 72 | index_->construct( |
| 73 | data_array.data(), |
| 74 | centroids_array.data(), |
| 75 | cluster_ids_array.data(), |
| 76 | fast_quantization, |
| 77 | num_threads |
| 78 | ); |
| 79 | built_ = true; |
| 80 | } |
| 81 | |
| 82 | py::tuple search( |
| 83 | py::handle queries, |
| 84 | size_t k, |
| 85 | size_t nprobe, |
| 86 | bool high_accuracy = true, |
| 87 | size_t num_threads = 1 |
| 88 | ) { |
| 89 | auto query_array = ensure_2d_array<float>(queries, "queries"); |
| 90 | if (!built_) { |
| 91 | throw std::runtime_error("IvfIndex must be built or loaded before search"); |
| 92 | } |
| 93 | if (static_cast<size_t>(query_array.shape(1)) != dim_) { |
| 94 | throw std::invalid_argument("query dimension does not match index dim"); |
| 95 | } |
| 96 | if (k == 0 || k > max_elements_) { |
| 97 | throw std::invalid_argument("k must be between 1 and max_elements"); |
| 98 | } |
| 99 | if (nprobe == 0) { |
| 100 | throw std::invalid_argument("nprobe must be positive"); |
| 101 | } |
| 102 | |
| 103 | const size_t nq = static_cast<size_t>(query_array.shape(0)); |
| 104 | const auto shape = |
| 105 | std::vector<ssize_t>{static_cast<ssize_t>(nq), static_cast<ssize_t>(k)}; |
| 106 | auto ids = py::array_t<rabitqlib::PID>(shape); |
| 107 | auto dists = py::array_t<float>(shape); |
| 108 | auto ids_buf = ids.mutable_unchecked<2>(); |
| 109 | auto dists_buf = dists.mutable_unchecked<2>(); |
| 110 |
no test coverage detected