C · Platform Notes

Platform Notes

#include <usearch/geospatial_index.h> 3.0.0-rc.1 · 4c3e5023

§ Sequence Indexes

A sequence index searches byte strings, UTF-8 text, DNA, RNA and proteins under edit distances and alignments, in RAM only. Variable-length entries travel as Apache Arrow tapes, usearch_tape_view_t: the elements end to end, and count + 1 offsets around them, 32-bit or 64-bit as its layout says, so sequences are bytes with byte offsets:

usearch_sequence_index_t sequences = NULL;
usearch_sequence_init_ram(&(usearch_sequence_init_ram_t) {
    .index = &sequences, .outcome = &outcome, .metric = usearch_sequence_metric_levenshtein_utf8_k});

char const tape[] = "kittensittingmitten";
uint64_t const offsets[4] = {0, 6, 13, 19};
usearch_key_t words[3] = {{.bytes[15] = 1}, {.bytes[15] = 2}, {.bytes[15] = 3}};
usearch_sequence_insert(&(usearch_sequence_insert_t) {.index = sequences,
                                                      .executor = executor,
                                                      .outcome = &outcome,
                                                      .count = 3,
                                                      .keys = {words, sizeof(usearch_key_t)},
                                                      .sequences = {.data = tape,
                                                                    .layout = usearch_tape_u64_k,
                                                                    .offsets = {.u64 = offsets},
                                                                    .scalar = usearch_scalar_u8_k}});

uint64_t const query_offsets[2] = {0, 6};
usearch_sequence_search(&(usearch_sequence_search_t) {.index = sequences,
                                                      .executor = executor,
                                                      .outcome = &outcome,
                                                      .count = 1,
                                                      .queries = {.data = "sitten",
                                                                  .layout = usearch_tape_u64_k,
                                                                  .offsets = {.u64 = query_offsets},
                                                                  .scalar = usearch_scalar_u8_k},
                                                      .matches = &matches,
                                                      .arena = arena,
                                                      .wanted = 3});

The kinds are Levenshtein over bytes or UTF-8 code points, Needleman-Wunsch with a linear gap, and overlap, the share of fixed-width windows one side misses of the other. Needleman-Wunsch takes usearch_sequence_weights_t, a 256×256 substitution matrix with a gap cost, and the overlap kind takes window_widths in bytes, each refused by every other kind with usearch_status_config_mismatch_k. The verbs mirror the dense ones: init_ram, init_vram, load_ram, load_vram, view_ram, reserve, clear, insert, refine, search with a predicate, get writing tapes into an arena, cursor_next writing keys and their links, absorb, split, save, metadata_read, the _buffer forms of load_ram, save and metadata_read, and describe, while count also reports a key’s total and largest byte lengths. A key already holding values_per_key entries rejects the whole insertion with usearch_status_overflow_k. The engine serves every kind, and answers view_ram with usearch_status_unsupported_k; refine, absorb and split serve RAM indexes, while sample, health and cursors with starts and links serve RAM indexes and NVMe views. The stand-in serves every kind by textbook dynamic programming, quadratic and unbanded, and reports alignment similarities negated.

§ Shape Indexes

A shape index searches 3D point clouds of any size, like molecules, conformers and protein backbones, in RAM or VRAM. Its tape is points end to end, x, y and z interleaved, with offsets that count points rather than scalars:

usearch_shapes_index_t conformers = NULL;
usearch_shapes_init_ram(&(usearch_shapes_init_ram_t) {.index = &conformers,
                                                      .outcome = &outcome,
                                                      .metric = usearch_shapes_metric_wasserstein_procrustes_k,
                                                      .quantization = usearch_scalar_f16_k});

float const atoms[5][3] = {{0, 0, 0}, {1.1f, 0, 0}, {0, 0, 0}, {1.5f, 0, 0}, {0, 1.5f, 0}};
uint64_t const point_offsets[3] = {0, 2, 5}; // a 2-atom and a 3-atom entry
usearch_shapes_insert(&(usearch_shapes_insert_t) {.index = conformers,
                                                  .executor = executor,
                                                  .outcome = &outcome,
                                                  .count = 2,
                                                  .keys = {molecules, sizeof(usearch_key_t)},
                                                  .shapes = {.data = atoms,
                                                             .layout = usearch_tape_u64_k,
                                                             .offsets = {.u64 = point_offsets},
                                                             .scalar = usearch_scalar_f32_k}});

Chamfer and Hausdorff pair every point with its nearest partner after aligning the principal axes under all eight sign flips, keeping the best. Wasserstein-Procrustes pairs points by an exact transport plan, alternated with the best rotation or reflection. Gromov-Wasserstein compares each side’s internal distances. All four are invariant to rotation, reflection, translation and point order. A non-zero points freezes the count, rejecting insertions of any other with usearch_status_shape_mismatch_k. The verbs are the sequence ones, where get converts to an outputs_scalar and returns each shape in its inserted pose, and count reports a key’s total and largest point counts. The engine serves all four kinds in RAM and on NVMe over f16 or f32 points, refusing other quantizations and view_ram with usearch_status_unsupported_k, and Chamfer and Hausdorff on NVIDIA GPUs; sample, health and cursors with starts and links serve RAM indexes and NVMe views. The stand-in scores Chamfer and Hausdorff in the frame the points arrive in, refuses the two transport kinds with usearch_status_unknown_kind_k, refuses quantizations other than f16 and f32 with usearch_status_unsupported_k, and refuses every VRAM verb with usearch_status_device_k.

§ Geospatial Indexes

A geospatial index searches observations on and above the Earth, like photos, video frames, drone footage and sensor fixes, in RAM, on NVMe or on NVIDIA GPUs. Each entry is one usearch_geospatial_observation_t of eleven doubles in natural units: latitude and longitude in WGS-84 degrees, altitude in metres above the ellipsoid, the horizontal accuracy, the heading, pitch, heading accuracy and field of view of the device’s view cone, how far it saw, and the first and last Unix second it covers. A device that recorded nothing for a field leaves it NaN, never zero, and each NaN only widens the tests that read it: an unknown accuracy counts as exact, an unknown view as looking everywhere, an unknown time as always. A key may hold several observations, up to the values_per_key its creation froze, like a camera’s frames or a vehicle’s fixes:

#include <math.h> // `NAN` - a field no device recorded

usearch_geospatial_index_t places = NULL;
usearch_geospatial_init_ram(&(usearch_geospatial_init_ram_t) {.index = &places, .outcome = &outcome});

usearch_geospatial_observation_t const stops[3] = {
    // latitude, longitude, altitude, accuracy, heading, pitch, heading accuracy, field, range, begin, end
    {51.5308, -0.1238, NAN, 5, NAN, NAN, NAN, NAN, NAN, NAN, NAN},
    {51.5320, -0.1233, NAN, 5, NAN, NAN, NAN, NAN, NAN, NAN, NAN},
    {51.5033, -0.1196, NAN, 5, NAN, NAN, NAN, NAN, NAN, NAN, NAN},
};
usearch_geospatial_insert(&(usearch_geospatial_insert_t) {.index = places,
                                                          .executor = executor,
                                                          .outcome = &outcome,
                                                          .count = 3,
                                                          .keys = {stop_keys, sizeof(usearch_key_t)},
                                                          .observations = stops});

usearch_geospatial_within reports every entry a relation admits inside the tolerance, however many there turn out to be, as one tape per query in the arena:

usearch_geospatial_observation_t const station = {51.5308, -0.1238, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN};
usearch_matches_tape_view_t nearby = {0}; // `nearby.offsets[1]` entries, their chords in metres
usearch_geospatial_within(&(usearch_geospatial_within_t) {.index = places,
                                                          .executor = executor,
                                                          .outcome = &outcome,
                                                          .count = 1,
                                                          .queries = &station,
                                                          .relation = usearch_geospatial_relation_near_k,
                                                          .tolerance = {.metres = 500},
                                                          .matches = &nearby,
                                                          .arena = arena});

usearch_geospatial_search ranks the wanted nearest entries of each query the relation admits instead, where a zero radius ranks by distance alone. Every score is the chord between the two positions in metres, altitude included, which ranks as the great-circle arc does. near_k admits entries within the radius widened by three standard deviations of both positions’ accuracies combined. visible_k also needs a clear line of sight over the handle’s terrain, and a handle without one refuses it. sees_k needs the entry’s view cone to hold the query, which cameras see this place, and seen_k the query’s cone to hold the entry, what this camera sees; both clear the terrain too when the handle has one. The tolerance’s seconds widen the query’s interval on both ends, and a query without a time matches entries of any time. The terrain is a north-up grid of heights above the ellipsoid, borrowed in place or filled on demand by a callback; a checkpoint never carries it, so every load and view names it again, and a GPU index takes borrowed heights alone. The remaining verbs are the shapes ones, where get returns observations within the stored rounding, centimetres and milliseconds among them, count reports a key’s entries, and sample places its draws at their Earth-fixed positions. The engine serves every relation in RAM, on NVMe and on NVIDIA GPUs, answering view_ram with usearch_status_unsupported_k; sample, health and cursors with starts and links serve RAM indexes and NVMe views. The stand-in scans every entry exactly, keeps the doubles inserted, refuses every terrain and so visible_k, and refuses every VRAM verb with usearch_status_device_k.

§ What This Binding Does Not Expose

  • Batched admission. The predicate is one call per key; the span-admission form exists only in the C++ layer, since a C callback per candidate batch would cost more than it amortizes.
  • Custom metrics. A metric of your own, over payloads of your own, belongs to the C++ baseline tier, whose index takes any batched metric you write; see include/usearch/basic/README.md.

§ Design

  • This is the C ABI itself. Every name is the one c/usearch/ declares: a verb usearch_dense_<verb> takes a usearch_dense_<verb>_t, and closed sets are C enums with name tables, never strings.
  • The completion chooses, the verb stays. A NULL executor runs a verb on the calling thread, an executor with the default completion blocks on it, and usearch_completion_deferred_k submits it, all with the same argument struct.
  • Deferred calls report through the same outcome. usearch_outcome_status polls and usearch_outcome_wait blocks, both with the acquire ordering the final store pairs with.
  • No life of its own. Threads live only in executors, and a deferred call only on its executor’s thread 0; every *_free waits for the calls naming its handle first.
  • Arenas are lent and reused, never shared. Every output whose length scales with the call lands in an arena the caller passes; each call rewinds it, so the previous call’s views go stale the moment the next one starts.
  • Nothing costs anything until it is used. Submitting a call allocates nothing, and an arena allocates only when a call writes into it.
  • One shape for every call. Each verb takes one struct set by designator, so a call names only what it changes and the struct grows without breaking source.
  • Failures are statuses. The library never logs or aborts, and a dense verb a residency lacks answers usearch_status_unsupported_k rather than missing from the build.

API

Types

usearch_geospatial_observation_tOne observation as devices record it: every field in its natural unit, and NaN wherever nothing was recorded.
geospatial_index.h:118 ↗
struct usearch_geospatial_observation_t

An unknown accuracy counts as exact, an unknown view as looking everywhere without limit, and an unknown time as always, so a missing field only ever widens the tests that read it. The index stores a quantized copy that rounds every field outward, and reads it back through usearch_geospatial_get within that rounding: centimetres, a 2⁻¹⁶ turn of heading, a 2⁻⁸ turn of pitch and field, whole metres of accuracy and range, and milliseconds.

Fields

latitude_degreesdoubleDegrees north on WGS-84, south negative.
longitude_degreesdoubleDegrees east on WGS-84, west negative.
altitude_metresdoubleMetres above the WGS-84 ellipsoid.
accuracy_metresdoubleOne standard deviation of the horizontal position, in metres.
heading_degreesdoubleWhere the device looked: degrees clockwise from true north.
pitch_degreesdoubleDegrees above the horizon.
heading_accuracy_degreesdoubleOne standard deviation of the heading, in degrees, which widens the field of view by three on each side: ROS orientation_covariance, or a compass's own.
field_of_view_degreesdoubleThe full field of view in degrees: 2·atan(18 mm / EXIF FocalLengthIn35mmFilm) for a landscape frame; MISB tag 16; the ROS CameraInfo matrix.
range_metresdoubleHow far the device saw, in metres along the slant.
begin_secondsdoubleThe first instant the observation covers, in Unix seconds, UTC.
end_secondsdoubleThe last instant the observation covers, in Unix seconds, UTC: a clip's creation date plus its duration, or the sources of begin_seconds.
usearch_geospatial_relation_tWhich entries count as matches of a query; the radius and time window of usearch_geospatial_tolerance_t narrow every relation alike.
geospatial_index.h:173 ↗
enum usearch_geospatial_relation_t

Values

usearch_geospatial_relation_unknown_k0
usearch_geospatial_relation_near_k1Within the radius of each other, widened by three standard deviations of both positions combined.
usearch_geospatial_relation_visible_k2Near, with a clear line of sight over the handle's terrain; refused by a handle without one.
usearch_geospatial_relation_sees_k3Near, and the entry's view cone holds the query: "which cameras see this place".
usearch_geospatial_relation_seen_k4Near, and the query's view cone holds the entry: "what does this camera see".
usearch_geospatial_terrain_callback_tFills rows × columns heights of a terrain, in metres above the WGS-84 ellipsoid, starting at row first_row and column first_column, with heights_stride bytes between rows; a cell the source does not have stays NaN, which never blocks a sight line.
geospatial_index.h:197 ↗
typedef void(*) usearch_geospatial_terrain_callback_t(size_t first_row, size_t first_column, size_t rows, size_t columns, float *heights, size_t heights_stride, void *state);

Called concurrently from the executor's threads.

usearch_geospatial_terrain_tThe elevation every line of sight is tested over: a north-up geographic grid of heights, given by exactly one of a borrowed buffer, a fill callback or a dataset path.
geospatial_index.h:210 ↗
struct usearch_geospatial_terrain_t

A zeroed terrain means none. Heights are metres above the WGS-84 ellipsoid, like observation altitudes, so orthometric grids such as the Copernicus DEM's need the geoid's height added first. The grid is treated as locally flat at the latitude of its middle row, which keeps distances within 1 % across a one-degree tile.

Fields

rowssize_tThe grid's row count; rows run south from the north edge.
columnssize_tThe grid's column count; columns run east from the west edge.
north_latitude_degreesdoubleThe latitude of the centre of cell [0,0], in degrees.
west_longitude_degreesdoubleThe longitude of the centre of cell [0,0], in degrees.
row_step_degreesdoubleThe step between rows, in degrees, positive.
column_step_degreesdoubleThe step between columns, in degrees, positive.
heightsfloat const *The whole grid, row-major, borrowed for the handle's lifetime.
heights_stridesize_tThe bytes between consecutive rows of heights; zero means packed.
callbackusearch_geospatial_terrain_callback_tOr a handler filling windows of the grid on demand, called with state; VRAM handles refuse it with usearch_status_unsupported_k, as no device calls the host.
statevoid *The opaque pointer passed back to every callback call.
pathusearch_bytes_view_tOr a dataset on disk, which only an NVMe residency will read; RAM and VRAM handles refuse it with usearch_status_unsupported_k.
usearch_geospatial_tolerance_tWhat "close" means numerically for one search: the radius around each query position, and the slack its time interval allows.
geospatial_index.h:689 ↗
struct usearch_geospatial_tolerance_t

Zero metres means no radius: the search ranks by distance alone. Position uncertainty widens the radius by three standard deviations of the query's and the entry's accuracy combined, so a pair whose true positions lie within it matches at 98.9 % confidence. An entry matches in time when its interval and the query's, widened by seconds on each side, overlap; a query without a time matches every entry.

Fields

metresdoubleRadius in metres; zero means none.
secondsdoubleSlack added to both ends of the query's time interval, in seconds.