Quick Tutorial

PatANN Essentials: A Quick Implementation Guide #

This tutorial provides complete guidance for using PatANN to implement vector similarity search across all supported platforms - Python (Linux, macOS, Windows), Android (Java/Kotlin), and iOS (Swift/Objective-C). The documentation covers all operational modes including synchronous execution, asynchronous processing, and parallel search capabilities to address various performance and implementation requirements.

PatANN maintains rigorous API consistency across all supported platforms. Unless explicitly noted for platform-specific variations, all code examples shown in one language maintain equivalent functionality in other implementations. You can access complete working examples for each platform in PatANN GitHub repository. A detailed API reference will be published shortly.

We strongly recommend reading the Key Innovations section to understand PatANN’s architectural advantages before proceeding with implementation. This foundational knowledge will help you make informed decisions about index configuration and query strategies.

Installation #

Python #

Install the Python package using pip:

pip install patann

Android #

Add the dependency to your app’s build.gradle and sync gradle:

dependencies {
    implementation "com.mesibo.api:patann:latest.release"
}

iOS #

Add to your Podfile and run pod install:

pod 'patann'

Step-by-Step Usage Guide #

1. Importing and Initializing PatANN #

You will need to import and initialize PatANN before using any APIs. The exact steps vary slightly depending on your platform

Python:

import patann

Java/Kotlin (Android): Import and initialize with context

import com.mesibo.patannIndex.PatANN;

PatANN.init(context);

Swift: Import the framework

import patann

Objective-C: Import the framework

import "patann/patann.h"

2. Create an Index #

PatANN provides both in-memory (volatile) and on-disk (persistent) index options. The index creation requires specifying the vector dimension. For persistent storage, you also need to provide a valid storage path and a unique index name. This is the first step that must be completed before proceeding with parameter configuration, vector additions (indexing), or query operations.

In-memory index

# Python
dimension = 128
annIndex = patannIndex.createInstance(dimension)

On-disk index

// Swift
let annIndex = PatANN.createOnDiskInstance(dimension, path, "index_name")
// Objective-C
PatANNObjC *annIndex = [PatANNObjC createOnDiskInstance:128 path:path name:@"index_name"];

Refer to the In-Memory and On-Disk Indexing section to learn more about In-Memory and On-Disk indexing PatANN supports.

3. Index Configuration #

PatANN provides several tuning parameters to optimize search quality and performance for specific use cases. While the default settings work well for most scenarios, you can adjust these optional parameters when you need fine-grained control over your search characteristics.

Distance Type #

Sets the distance metric used for final similarity comparisons:

// Java
annIndex.setDistanceType(PatANNDistanceType.L2_SQUARE);  // or COSINE

Constellation Size #

Controls the number of connected points each node maintains in the graph.

// Swift
annIndex.setConstellationSize(16)

Higher values create richer constellations, improving search quality at the cost of memory. This is analogous to HNSWLib’s M parameter that controls neighbor count but unlike HNSWLib it is not fixed and only represents median.

Search Radius #

Sets the search radius parameter which controls the exploration scope.

// Java
annIndex.setRadius(100);

The radius influences how extensively the algorithm explores during indexing & queries, affecting both build time and search accuracy.

Pattern Probes #

Sets the number of pattern probes to use during indexing and search:

annIndex.setPatternProbes(2)

Higher values increase search quality at the cost of longer search times.

Allocation Units #

Unlike other implementations that require pre-allocation of fixed index capacity, PatANN employs dynamic memory growth. You specify allocation units to control memory expansion in optimal chunks, enabling efficient resource usage without compromising performance.

Refer to the Elastic Capacity section to learn more.

# Python
annIndex.setAllocationUnits(100000)

4. Add Vectors #

PatANN supports both individual and batch vector addition with asynchronous processing by default.

Single vector addition #

# Python
vector_id = annIndex.addVector(vector)  # vector must be a float32 array

Batch addition #

// Swift
annIndex.addVectors(vectors)

PatANN operates entirely asynchronously – all operations including addVector() and addVectors() return immediately while processing continues in the background. This non-blocking architecture enables applications to maintain responsiveness during potentially lengthy indexing operations, which is particularly critical for mobile platforms to avoid Application Not Responding (ANR) errors.

You can monitor indexing progress by setting up a listener that triggers when processing completes. You should only register this listener after adding all your vectors. The listener executes exactly once per registration, so you’ll need to register it again if you want to track subsequent indexing operations.

// Java
public class MyExample implements PatANNIndexListener {
    @Override
    public void PatANNOnIndexUpdate(PatANN ann, long indexed, long total) {
        if (indexed == total && annIndex.isIndexReady()) {
            Log.d("PatANN", "Index is ready, proceeding with query");
            startQuery();
        }
    }
}

// Register index listener
annIndex.setIndexListener(myExample);

However, if your application requires synchronous behavior, PatANN provides the waitForIndexReady() API. This blocks execution until all vectors are processed and the index is fully prepared for queries.

# Python
annIndex.waitForIndexReady()

New implementations should exclusively use the asynchronous pattern, especially in mobile environments where synchronous operations could trigger ANR (Application Not Responding) errors. The synchronous API remains available for legacy apps but is not recommended for new apps.

All search operations in PatANN require creating a query session. These sessions are reusable for multiple queries, allowing you to maintain search context and optimize performance. You can create multiple independent query sessions to queue and execute parallel searches. Each session maintains its own search parameters and state while operating completely independently from others.

# Python
top_k = 10
query = annIndex.createQuerySession(100, top_k)  # (radius, k)
query.query(query_vector, top_k)

The search radius parameter controls the trade-off between search speed and accuracy. Higher values increase result quality but also increase query time.

Getting Results #

PatANN provides both synchronous and asynchronous result retrieval, following the same pattern as indexing operations. While synchronous method is available, the recommended approach uses asynchronous callback listeners to maintain optimal application performance and responsiveness.

# Python
class QueryListener(patannIndex.PatANNQueryListener):
    def __init__(self, parent):
        patannIndex.PatANNQueryListener.__init__(self)
        self.parent = parent

    def PatANNOnResult(self, query):
        print("Query completed")
        result_ids = query.getResults()
        result_distances = query.getResultDists()
        
        print(f"Found {len(result_ids)} results")
        for i in range(len(result_ids)):
            print(f"Result {i}: ID={result_ids[i]}, Distance={result_distances[i]}")
        
        query.destroy()
        return 0

query.setListener(query_listener)

Or, synchronously

# Python
result_ids = query.getResults()
result_distances = query.getResultDists()

7. Batch Query (Advanced) #

For high-throughput scenarios, PatANN offers batch query APIs that deliver substantially better performance than individual queries. These bulk operations maintain the same synchronous and asynchronous patterns as standard queries. This feature is only exposed in the C++ and Python APIs.

# Python
# Create a bulk query session
bqs = annIndex.createBulkQuerySession(search_radius)

# Execute batch of queries
bqs.query(query_vectors, k)

# Get results
results = bqs.getResults()

8. Clean Up Resources #

Always clean up resources when done:

// Swift
query.destroy()  // Clean up query session
annIndex.destroy()  // Clean up index

Complete Implementations #

For complete working implementations across platforms, refer to the following examples inPatANN GitHub repository.:

  1. Python: Check patann_async_example.py for a complete asynchronous example
  2. Java/Android: See PatANNExample.java for both synchronous and asynchronous implementations

These examples provide detailed implementations including proper error handling and resource management.