#5606·faiss

clone_index() loses ArrayInvertedListsPanorama storag

Author: leemeiiCreated Sep 8, 2026Updated Sep 8, 2026

Environment

  • FAISS v1.15.0
  • faiss-cpu==1.15.0

Description

Calling the public faiss.clone_index() API on a trained and populated IndexIVFFlatPanorama returns an object whose Python type is still IndexIVFFlatPanorama, but whose inverted lists have been downgraded to ordinary ArrayInvertedLists.

The clone cannot execute its normal search() method and raises:

RuntimeError: IndexIVFFlatPanorama requires ArrayInvertedListsPanorama

The original index searches successfully and returns exact zero distances. This is a deterministic clone/API correctness failure, not an ANN recall miss, quantization effect, tie, or tolerance issue.

Minimal reproduction

python
import faiss
import numpy as np

d = 4
x = np.array([[1, 1, 1, 1], [11, 11, 11, 11]], "float32")
train = np.array([[0, 0, 0, 0]] * 2 + [[10, 10, 10, 10]] * 2, "float32")

index = faiss.index_factory(d, "IVF2,FlatPanorama2_2")
index.cp.min_points_per_centroid = 1
index.nprobe = 2
index.train(train)
index.add(x)

print(type(index).__name__, type(index.invlists).__name__)
print(index.search(x, 1)[0].ravel().tolist())

clone = faiss.clone_index(index)
print(type(clone).__name__, type(clone.invlists).__name__)
clone.search(x, 1)

Observed:

IndexIVFFlatPanorama ArrayInvertedListsPanorama
[0.0, 0.0]
IndexIVFFlatPanorama ArrayInvertedLists
RuntimeError: IndexIVFFlatPanorama requires ArrayInvertedListsPanorama

Root cause

clone_IndexIVF explicitly recognizes IndexIVFFlatPanorama, but the generic clone_InvertedLists helper checks the base class first:

cpp
if (auto* ails = dynamic_cast<const ArrayInvertedLists*>(invlists)) {
    return new ArrayInvertedLists(*ails);
}

Because ArrayInvertedListsPanorama derives from ArrayInvertedLists, the base-class branch wins and the copied storage is downgraded to ArrayInvertedLists.

The cloned object later reaches IndexIVFFlatPanorama::get_InvertedListScanner, which requires ArrayInvertedListsPanorama. The dynamic cast fails and throws.

Suggested fix

Handle ArrayInvertedListsPanorama before ArrayInvertedLists in clone_InvertedLists, or provide a dedicated Panorama storage clone helper.

Please add a regression test verifying that:

  1. A valid IndexIVFFlatPanorama can be cloned with faiss.clone_index();
  2. The clone retains ArrayInvertedListsPanorama;
  3. Both original and clone can execute search();
  4. Both return identical distances and IDs.