Rust API reference
Every public function and type of the kwker crate, with its signature and description (Kwker 0.1.0). The functions are at the crate root (kwker::sort, kwker::top_k, ...); kwker::dist and kwker::arrow are modules.
Kwker: sorting, selection, top-k, argsort and key-value sorting of numeric slices, in place, with the fastest engine for the CPU chosen once at run time (AVX-512 or AVX2 on x86-64, NEON or SVE on ARM, portable elsewhere).
use kwker::Order;
let mut v = vec![3.5f64, 0.5, 2.0, 1.0];
kwker::sort(&mut v);
assert_eq!(v, [0.5, 1.0, 2.0, 3.5]);
let scores = [12, 7, 30, 18];
let (top, at): (Vec<i32>, Vec<usize>) = kwker::top_k(&scores, 2, Order::DESCENDING, true);
assert_eq!((top, at), (vec![30, 18], vec![2, 3]));
Key types: u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, and the low-precision floats F16,
Bf16, F8E5M2 and F8E4M3. Every engine returns the same result; isa names the one in use.
Sorting, selection and top-k
sort Page
pub fn sort<T: Sortable>(v: &mut [T])
Sorts v in place, smallest first.
Arguments
v: the keys: integers, floats or the low-precision float types (anySortabletype).
Example
let mut v = vec![30, 10, 20];
kwker::sort(&mut v);
assert_eq!(v, [10, 20, 30]);
Notes
- Not stable: equal keys may change places (only NaNs with different bits can tell).
- Nearly sorted or duplicate-heavy input may use a side buffer of up to about
v.len() / 4keys.
Remarks: Not stable (rule 5): keys that are equal but can be told apart (NaNs with different bits) may change places. Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
Examples: Quickstart: Kwker Core: Sort an array, Core concepts: In place or a copy, Runtime controls: Fallback switches, Sorting: Sort in place
sort_descending Page
pub fn sort_descending<T: Sortable>(v: &mut [T])
Sorts v in place, largest first.
Arguments
v: the keys (anySortabletype).
Example
let mut v = vec![1.5f32, 3.0, 2.25];
kwker::sort_descending(&mut v);
assert_eq!(v, [3.0, 2.25, 1.5]);
Notes
- NaNs go last;
+0.0comes before-0.0.
Examples: Quickstart: Kwker Core: Largest first, or a sorted copy, Core concepts: Special float values, Sorting: Largest first, Sorting: Missing values (NaN)
sort_by_order Page
pub fn sort_by_order<T: Sortable>(v: &mut [T], order: Order)
Sorts v in place in the given order: ascending or descending, NaNs first or last.
Arguments
v: the keys (anySortabletype).order: the direction and NaN placement, for exampleOrder::DESCENDING.
Example
use kwker::{Direction, NanPlacement, Order};
let mut v = vec![2.0f64, f64::NAN, 1.0];
kwker::sort_by_order(&mut v, Order { direction: Direction::Ascending, nans: NanPlacement::First });
assert!(v[0].is_nan());
assert_eq!(v[1..], [1.0, 2.0]);
Notes
- Descending is the exact reverse of ascending for all keys but NaNs, which stay together at the chosen end.
Examples: Core concepts: Special float values, Sorting: Missing values (NaN)
select_nth Page
pub fn select_nth<T: Sortable>(v: &mut [T], k: usize)
Puts the key a full sort would put at position k into v[k], smaller or equal keys before it and larger or
equal keys after it, like slice::select_nth_unstable.
Arguments
v: the keys (anySortabletype).k: the position to fill, belowv.len().
Example
let mut v = vec![50, 10, 40, 20, 30];
kwker::select_nth(&mut v, 2);
assert_eq!(v[2], 30); // the median
Panics
If k >= v.len().
Notes
- Only
v[k]is in its sorted place; the keys on either side are in no particular order.
Examples: Quickstart: Kwker Core: The median, without a full sort, Top-k and selection: The median and other positions
select_nth_by_order Page
pub fn select_nth_by_order<T: Sortable>(v: &mut [T], k: usize, order: Order)
select_nth in the given order: with Order::DESCENDING, v[k] gets the (k+1)-th largest key.
Arguments
v: the keys (anySortabletype).k: the position to fill, belowv.len().order: the direction and NaN placement.
Panics
If k >= v.len().
partial_sort Page
pub fn partial_sort<T: Sortable>(v: &mut [T], k: usize)
Puts the k smallest keys of v, sorted, into v[..k].
Arguments
v: the keys (anySortabletype).k: how many keys to sort;k >= v.len()sorts all ofv.
Example
let mut v = vec![50, 10, 40, 20, 30];
kwker::partial_sort(&mut v, 2);
assert_eq!(v[..2], [10, 20]);
Notes
- The keys after
v[..k]are in no particular order.
Remarks: The first k positions hold exactly what a full sort puts there; the rest hold the other keys in any order (rule 8). Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
Examples: Top-k and selection: Sort only the beginning
partial_sort_by_order Page
pub fn partial_sort_by_order<T: Sortable>(v: &mut [T], k: usize, order: Order)
partial_sort in the given order: with Order::DESCENDING, the k largest keys, largest first.
Arguments
v: the keys (anySortabletype).k: how many keys to sort;k >= v.len()sorts all ofv.order: the direction and NaN placement.
argsort Page
pub fn argsort<T: Sortable, I: Index>(v: &[T], order: Order) -> Vec<I>
Returns the positions that sort v: v[p[0]], v[p[1]], ... is in order. Equal keys keep their input order.
Arguments
v: the keys (anySortabletype); not changed.order: the direction and NaN placement, for exampleOrder::ASCENDING.
Returns
One position per key, in the index type I you choose (u32, usize, ...; see Index).
Example
use kwker::Order;
let p: Vec<u32> = kwker::argsort(&[30, 10, 20], Order::ASCENDING);
assert_eq!(p, [1, 2, 0]);
Panics
If v.len() does not fit the index type I.
Remarks: Stable (rule 6): equal keys keep their input order, so the same input always gives the same positions. Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
Examples: Quickstart: Kwker Core: Get the order, not the sorted data, Order and ranking: The order of an array: argsort, Large data: Use several cores, Languages: The same calls in every language
argsort_into Page
pub fn argsort_into<T: Sortable>(v: &[T], order: Order, out: &mut [u64])
argsort into a buffer you provide, with no result vector.
Arguments
v: the keys (anySortabletype); not changed.order: the direction and NaN placement.out: receives the positions; it must havev.len()entries.
Panics
If out.len() != v.len().
Remarks: Stable (rule 6): equal keys keep their input order, so the same input always gives the same positions. Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
argselect Page
pub fn argselect<T: Sortable, I: Index>(v: &[T], k: usize, order: Order) -> Vec<I>
Returns the positions of the k first keys of v in order, in no particular order: the positions of
top_k's keys.
Arguments
v: the keys (anySortabletype); not changed.k: how many positions; more thanv.len()returns them all.order: the direction and NaN placement.
Returns
min(k, v.len()) positions in the index type I you choose.
Example
use kwker::Order;
let mut at: Vec<u32> = kwker::argselect(&[12, 7, 30, 18], 2, Order::ASCENDING);
at.sort();
assert_eq!(at, [0, 1]);
Panics
If v.len() does not fit the index type I.
Remarks: The positions of the stable order's first k keys, in any order (rule 6). Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
Examples: Top-k and selection: Only the positions
top_k Page
pub fn top_k<T: Sortable, I: Index>(
v: &[T],
k: usize,
order: Order,
sorted: bool,
) -> (Vec<T>, Vec<I>)
Returns the k first keys of v in order and their positions: the k smallest with Order::ASCENDING,
the k largest with Order::DESCENDING.
Arguments
v: the keys (anySortabletype); not changed.k: how many keys; more thanv.len()returns them all.order: the direction and NaN placement.sorted:truereturns them in order;falsereturns the same keys in no particular order, which is faster.
Returns
(values, positions): min(k, v.len()) keys and their positions in v, in the index type I you choose
(u32, usize, ...).
Example
use kwker::Order;
let (top, at): (Vec<i32>, Vec<usize>) = kwker::top_k(&[12, 7, 30, 18], 2, Order::DESCENDING, true);
assert_eq!(top, [30, 18]);
assert_eq!(at, [2, 3]);
Panics
If v.len() does not fit the index type I.
Notes
- Equal keys go to the earlier position first, so the result is the same on every run.
Remarks: The first k keys of the stable order and their positions; equal keys keep their input order (rule 9). Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
Examples: Quickstart: Kwker Core: Find the top results, Use Kwker from Rust: The program, Top-k and selection: The k largest or smallest values, Languages: The same calls in every language
top_k_into Page
pub fn top_k_into<T: Sortable, I: Index>(
v: &[T],
order: Order,
sorted: bool,
values: &mut [T],
indices: &mut [I],
) -> usize
top_k into buffers you provide, with no result vectors.
Arguments
v: the keys (anySortabletype); not changed.order,sorted: as intop_k.values,indices: receive the keys and their positions;kis the shorter of the two, at mostv.len().
Returns
k, the number of entries written.
Panics
If v.len() does not fit the index type I.
Notes
- The scratch buffers are kept per thread between calls;
top_k_into_withuses aWorkspaceyou own.
Remarks: The first k keys of the stable order and their positions; equal keys keep their input order (rule 9). Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
top_k_indices_into Page
pub fn top_k_indices_into<T: Sortable>(
v: &[T],
order: Order,
sorted: bool,
out: &mut [u64],
) -> usize
The positions of top_k into a buffer you provide, with no result vectors.
Arguments
v: the keys (anySortabletype); not changed.order,sorted: as intop_k.out: receives the positions;kisout.len(), at mostv.len().
Returns
k, the number of positions written.
Sortable Page
pub trait Sortable: Copy + Sealed { }
Key types sort accepts: u32, i32, f32, u64, i64, f64 (the engines), and the 8- and 16-bit keys u8,
i8, u16, i16, F16, Bf16, F8E5M2, F8E4M3 (counting sorts).
Direction Page
pub enum Direction {
Ascending,
Descending,
}
Sort direction.
Variants
Ascending: Smallest first.Descending: Largest first.
NanPlacement Page
pub enum NanPlacement {
Last,
First,
}
Where NaN keys go (floating-point keys; ignored for integers), independent of the direction.
Variants
Last: After every other key.First: Before every other key.
Order Page
pub struct Order {
pub direction: Direction,
pub nans: NanPlacement,
}
An ordering policy: direction and NaN placement. Order::default() is the order of sort.
Fields
direction: Direction of the non-NaN keys.nans: Position of the NaN keys.
Order::ASCENDING Page
pub const ASCENDING: Order;
Ascending, NaNs last (the order of sort).
Order::DESCENDING Page
pub const DESCENDING: Order;
Descending, NaNs last.
Index Page
pub trait Index: Copy + Send + Sync + 'static { }
Index types of index-producing operations (u8, u16, u32, u64, usize). An index type of b bits needs
len <= 2^b (lane-relative indices: the lane length).
Key-value sorts
sort_kv_stable Page
pub fn sort_kv_stable<K: Sortable, V: Copy>(keys: &mut [K], values: &mut [V])
Sorts keys in place, smallest first, and moves values with them. Equal keys keep their values in input order.
Arguments
keys: the sort keys (anySortabletype).values: one value per key, of anyCopytype.
Example
let mut keys = vec![2, 1, 2, 1];
let mut names = vec!["c", "a", "d", "b"];
kwker::sort_kv_stable(&mut keys, &mut names);
assert_eq!(keys, [1, 1, 2, 2]);
assert_eq!(names, ["a", "b", "c", "d"]);
Panics
If keys and values have different lengths.
Notes
- Uses buffers of about the input's size;
sort_kvis faster when the order of equal keys does not matter.
Remarks: Stable (rule 6): equal keys keep their input order, so the same input always gives the same positions. Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
Examples: Sorting keys with values: Keep equal keys in order: the stable sort
sort_kv_stable_by_order Page
pub fn sort_kv_stable_by_order<K: Sortable, V: Copy>(keys: &mut [K], values: &mut [V], order: Order)
sort_kv_stable in the given order. Equal keys keep their values in input order in every order.
Arguments
keys: the sort keys (anySortabletype).values: one value per key, of anyCopytype.order: the direction and NaN placement, for exampleOrder::DESCENDING.
Panics
If keys and values have different lengths.
sort_with_indices Page
pub fn sort_with_indices<K: Sortable, I: Index + Sortable + Ord>(
keys: &mut [K],
order: Order,
) -> Vec<I>
Sorts keys in place and returns where each sorted key came from: a sort and an argsort in one call. Use the
positions to reorder other columns the same way.
Arguments
keys: the keys (anySortabletype).order: the direction and NaN placement, for exampleOrder::ASCENDING.
Returns
One input position per key, as u32 or u64. Equal keys keep their input order.
Example
use kwker::Order;
let mut keys = vec![30, 10, 20];
let from: Vec<u32> = kwker::sort_with_indices(&mut keys, Order::ASCENDING);
assert_eq!(keys, [10, 20, 30]);
assert_eq!(from, [1, 2, 0]);
Panics
If keys.len() does not fit the index type I.
sort_kv Page
pub fn sort_kv<K: Sortable, V: Copy>(keys: &mut [K], values: &mut [V])
Sorts keys in place, smallest first, and moves values with them: each value stays with its key.
Arguments
keys: the sort keys (anySortabletype).values: one value per key, of anyCopytype.
Example
let mut prices = vec![30, 10, 20];
let mut items = vec!['c', 'a', 'b'];
kwker::sort_kv(&mut prices, &mut items);
assert_eq!(prices, [10, 20, 30]);
assert_eq!(items, ['a', 'b', 'c']);
Panics
If keys and values have different lengths.
Notes
- Not stable: the values of equal keys may come out in any order (
sort_kv_stablekeeps their input order). - 4- and 8-byte values on the AVX-512 and AVX2 engines sort without allocating; others use buffers of about the
input's size (
sort_kv_withtakes aWorkspaceyou own).
Remarks: Unless the stable form is asked for, pairs with equal keys may come out in any order; the stable form keeps their input order (rule 6). Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
Examples: Core concepts: In place or a copy, Sorting keys with values: Sort keys and values together
sort_kv_by_order Page
pub fn sort_kv_by_order<K: Sortable, V: Copy>(keys: &mut [K], values: &mut [V], order: Order)
sort_kv in the given order.
Arguments
keys: the sort keys (anySortabletype).values: one value per key, of anyCopytype.order: the direction and NaN placement, for exampleOrder::DESCENDING.
Panics
If keys and values have different lengths.
select_nth_kv Page
pub fn select_nth_kv<K: Sortable, V: Copy>(keys: &mut [K], values: &mut [V], k: usize)
select_nth on key-value pairs: the pair a full sort_kv would put at position k moves there, with smaller
or equal keys before it and larger or equal keys after it.
Arguments
keys: the keys (anySortabletype).values: one value per key, of anyCopytype; each moves with its key.k: the position to fill, belowkeys.len().
Panics
If k >= keys.len() or keys and values have different lengths.
Notes
- Only position
kis in its sorted place; the pairs on either side are in no particular order.
select_nth_kv_by_order Page
pub fn select_nth_kv_by_order<K: Sortable, V: Copy>(
keys: &mut [K],
values: &mut [V],
k: usize,
order: Order,
)
select_nth_kv in the given order.
Arguments
keys,values,k: as inselect_nth_kv.order: the direction and NaN placement, for exampleOrder::DESCENDING.
Panics
If k >= keys.len() or keys and values have different lengths.
partial_sort_kv Page
pub fn partial_sort_kv<K: Sortable, V: Copy>(keys: &mut [K], values: &mut [V], k: usize)
partial_sort on key-value pairs: puts the k pairs with the smallest keys, sorted, at the front.
Arguments
keys: the keys (anySortabletype).values: one value per key, of anyCopytype; each moves with its key.k: how many pairs to sort;k >= keys.len()sorts them all.
Panics
If keys and values have different lengths.
Notes
- The pairs after the first
kare in no particular order.
Remarks: The first k positions hold exactly what a full sort puts there; the rest hold the other keys in any order (rule 8). Unless the stable form is asked for, pairs with equal keys may come out in any order; the stable form keeps their input order (rule 6).
partial_sort_kv_by_order Page
pub fn partial_sort_kv_by_order<K: Sortable, V: Copy>(
keys: &mut [K],
values: &mut [V],
k: usize,
order: Order,
)
partial_sort_kv in the given order: with Order::DESCENDING, the k pairs with the largest keys.
Arguments
keys,values,k: as inpartial_sort_kv.order: the direction and NaN placement.
Panics
If keys and values have different lengths.
Examples: Sorting keys with values: Only the first k pairs
KeyValue Page
pub struct KeyValue<K, V> {
pub key: K,
pub value: V,
}
A key and its value in one record (#[repr(C)]: the key first). Sorted by the key only.
Fields
key: The key the records are ordered by.value: The value carried along with it.
Key types
F16 Page
pub struct F16(pub u16);
IEEE 754 binary16 (half precision) bits. Ordered as a float; F16::to_f32 decodes exactly.
F16::to_f32 Page
pub fn to_f32(self) -> f32
The value as f32 (exact).
Bf16 Page
pub struct Bf16(pub u16);
bfloat16 bits (the top half of an f32). Ordered as a float; Bf16::to_f32 decodes exactly.
Bf16::to_f32 Page
pub fn to_f32(self) -> f32
The value as f32 (exact: the bits are its top half).
F8E5M2 Page
pub struct F8E5M2(pub u8);
OCP FP8 E5M2 bits (1 sign, 5 exponent, 2 mantissa bits; infinities and NaNs as in IEEE 754).
F8E5M2::to_f32 Page
pub fn to_f32(self) -> f32
The value as f32 (exact).
F8E4M3 Page
pub struct F8E4M3(pub u8);
OCP FP8 E4M3 bits, the "FN" variant (1 sign, 4 exponent, 3 mantissa bits; no infinities, NaN = S.1111.111).
F8E4M3::to_f32 Page
pub fn to_f32(self) -> f32
The value as f32 (exact).
128-bit keys
sort_u128 Page
pub fn sort_u128(v: &mut [u128], order: Order)
Sorts unsigned 128-bit keys in place.
Arguments
v: the keys.order: the direction, for exampleOrder::ASCENDING(128-bit integers have no NaNs).
Example
use kwker::Order;
let mut v = vec![3u128 << 100, 1, 2];
kwker::sort_u128(&mut v, Order::ASCENDING);
assert_eq!(v, [1, 2, 3u128 << 100]);
Notes
- Uses a buffer of
v.len()keys; under a smallerset_scratch_limitit sorts in place, more slowly.
sort_i128 Page
pub fn sort_i128(v: &mut [i128], order: Order)
sort_u128 for signed 128-bit keys.
Arguments
v: the keys.order: the direction, for exampleOrder::ASCENDING.
select_nth_u128 Page
pub fn select_nth_u128(v: &mut [u128], k: usize, order: Order)
Puts the key a full sort would put at position k into v[k], the keys ordered before it in front of it and
the others after it.
Arguments
v: unsigned 128-bit keys.k: the position to fill, belowv.len().order: the direction, for exampleOrder::ASCENDING.
Panics
If k >= v.len().
select_nth_i128 Page
pub fn select_nth_i128(v: &mut [i128], k: usize, order: Order)
select_nth_u128 for signed 128-bit keys.
Arguments
v,k,order: as inselect_nth_u128.
Panics
If k >= v.len().
partial_sort_u128 Page
pub fn partial_sort_u128(v: &mut [u128], k: usize, order: Order)
Puts the k first keys of v in order, sorted, into v[..k].
Arguments
v: unsigned 128-bit keys.k: how many keys to sort;k >= v.len()sorts all ofv.order: the direction, for exampleOrder::ASCENDING.
Notes
- The keys after
v[..k]are in no particular order.
partial_sort_i128 Page
pub fn partial_sort_i128(v: &mut [i128], k: usize, order: Order)
partial_sort_u128 for signed 128-bit keys.
Arguments
v,k,order: as inpartial_sort_u128.
top_k_u128 Page
pub fn top_k_u128(v: &[u128], k: usize, order: Order) -> (Vec<u128>, Vec<u64>)
Returns the k first keys of v in order, sorted, and their positions.
Arguments
v: unsigned 128-bit keys; not changed.k: how many keys; more thanv.len()returns them all.order: the direction, for exampleOrder::DESCENDINGfor the largest.
Returns
(values, positions), min(k, v.len()) of each. Equal keys go to the earlier position first.
top_k_i128 Page
pub fn top_k_i128(v: &[i128], k: usize, order: Order) -> (Vec<i128>, Vec<u64>)
top_k_u128 for signed 128-bit keys.
Arguments
v,k,order: as intop_k_u128.
Returns
(values, positions), min(k, v.len()) of each.
argsort_u128 Page
pub fn argsort_u128(v: &[u128], order: Order) -> Vec<u64>
Returns the positions that sort v. Equal keys keep their input order.
Arguments
v: unsigned 128-bit keys; not changed.order: the direction, for exampleOrder::ASCENDING.
Returns
One position per key.
argsort_i128 Page
pub fn argsort_i128(v: &[i128], order: Order) -> Vec<u64>
argsort_u128 for signed 128-bit keys.
Arguments
v,order: as inargsort_u128.
Returns
One position per key.
sort_kv_u128 Page
pub fn sort_kv_u128<V: Copy>(keys: &mut [u128], values: &mut [V], order: Order)
Sorts unsigned 128-bit keys in place and moves values with them. Equal keys keep their values in input order.
Arguments
keys: the sort keys.values: one value per key, of anyCopytype.order: the direction, for exampleOrder::ASCENDING.
Panics
If keys and values have different lengths.
Notes
- Uses 16 bytes per key plus a copy of the values as scratch.
sort_kv_i128 Page
pub fn sort_kv_i128<V: Copy>(keys: &mut [i128], values: &mut [V], order: Order)
sort_kv_u128 for signed 128-bit keys.
Arguments
keys,values,order: as insort_kv_u128.
Panics
If keys and values have different lengths.
Permutations, partitions and streams
partition_by_threshold Page
pub fn partition_by_threshold<T: Sortable>(v: &mut [T], t: T, order: Order) -> usize
Moves the keys that come before t in order to the front of v and returns how many there are.
Arguments
v: the keys (anySortabletype).t: the threshold; keys equal to it go to the back part.order: the direction and NaN placement, for exampleOrder::ASCENDING(front = keys belowt).
Returns
The size of the front part.
Example
use kwker::Order;
let mut v = vec![5, 1, 7, 3, 9];
let n = kwker::partition_by_threshold(&mut v, 5, Order::ASCENDING);
assert_eq!(n, 2);
assert!(v[..n].iter().all(|&x| x < 5));
Notes
- Neither part keeps its input order.
partition_by Page
pub fn partition_by<T, F: FnMut(&T) -> bool>(v: &mut [T], pred: F) -> usize
Moves the elements for which pred returns true to the front of v and returns how many there are.
Arguments
v: the elements, of any type.pred: the test for the front part.
Returns
The size of the front part.
Notes
- Neither part keeps its input order;
stable_partition_bykeeps both.
stable_partition_by Page
pub fn stable_partition_by<T: Copy, F: FnMut(&T) -> bool>(v: &mut [T], pred: F) -> usize
partition_by that keeps both parts in their input order.
Arguments
v: the elements, of anyCopytype.pred: the test for the front part.
Returns
The size of the front part.
Notes
- Uses a buffer for the back part.
partition_multiway Page
pub fn partition_multiway<T: Sortable>(v: &mut [T], splitters: &[T], order: Order) -> Vec<usize>
Splits v into buckets by a list of splitters: bucket 0 holds the keys before splitters[0], bucket i the
keys from splitters[i - 1] up to splitters[i], and the last bucket the keys from the last splitter on.
Arguments
v: the keys (anySortabletype); reordered bucket by bucket.splitters: the bucket bounds, sorted inorder.order: the direction and NaN placement, for exampleOrder::ASCENDING.
Returns
The bucket boundaries: splitters.len() + 2 offsets from 0 to v.len(); bucket i is
v[b[i]..b[i + 1]].
Example
use kwker::Order;
let mut v = vec![7, 1, 15, 3, 12, 8];
let b = kwker::partition_multiway(&mut v, &[5, 10], Order::ASCENDING);
assert_eq!(b, [0, 2, 4, 6]);
Panics
If splitters is not sorted in order.
Notes
- The keys inside a bucket are in no particular order.
is_valid Page
pub fn is_valid(validity: &[u8], i: usize) -> bool
Reads bit i of an Arrow-style validity bitmap: true for a value, false for a null.
Arguments
validity: the bitmap, least significant bit first within each byte.i: the element's position.
argsort_nullable Page
pub fn argsort_nullable<T: Sortable>(
values: &[T],
validity: &[u8],
order: Order,
nulls_first: bool,
) -> Vec<usize>
Returns the positions that sort an array with nulls: the values in order, the nulls together first or last.
Equal values and the nulls keep their input order.
Arguments
values: the values (anySortabletype); not changed.validity: an Arrow-style bitmap, one bit per value (set = valid, clear = null).order: the direction and NaN placement, for exampleOrder::ASCENDING.nulls_first:trueputs the nulls first;falseputs them last.
Returns
One position per value.
sort_nullable Page
pub fn sort_nullable<T: Sortable>(
values: &mut [T],
validity: &mut [u8],
order: Order,
nulls_first: bool,
) -> usize
Sorts an array with nulls in place: the values in order, the nulls together first or last. The bitmap is
rewritten to match.
Arguments
values: the values (anySortabletype).validity: an Arrow-style bitmap, one bit per value (set = valid, clear = null).order: the direction and NaN placement, for exampleOrder::ASCENDING.nulls_first:trueputs the nulls first;falseputs them last.
Returns
The number of valid values.
sort_kv_nullable Page
pub fn sort_kv_nullable<K: Sortable, V: Copy>(
keys: &mut [K],
validity: &mut [u8],
values: &mut [V],
order: Order,
nulls_first: bool,
) -> usize
Sorts keys with nulls and moves values with them: the valid keys in order (equal keys keep their input
order), the null keys together first or last. The bitmap is rewritten to match.
Arguments
keys: the keys (anySortabletype).validity: an Arrow-style bitmap, one bit per key (set = valid, clear = null).values: one value per key, of anyCopytype.order: the direction and NaN placement, for exampleOrder::ASCENDING.nulls_first:trueputs the nulls first;falseputs them last.
Returns
The number of valid keys.
Permutation Page
pub struct Permutation {
/* private fields */
}
A reordering of n elements: element i of the result is element indices[i] of the input, the form
crate::argsort returns. Use it to sort several columns by one key column: build it once with
Permutation::sorting, then apply it to every column.
Example
use kwker::{Order, Permutation};
let ages = [41, 23, 35];
let mut names = ["Ana", "Bo", "Cy"];
let p = Permutation::sorting(&ages, Order::ASCENDING);
p.apply(&mut names);
assert_eq!(names, ["Bo", "Cy", "Ana"]);
Permutation::identity Page
pub fn identity(n: usize) -> Self
The permutation that leaves n elements in place.
Permutation::sorting Page
pub fn sorting<T: Sortable>(keys: &[T], order: Order) -> Self
The permutation that sorts keys in order. Equal keys keep their input order.
Arguments
keys: the sort keys (anySortabletype).order: the direction and NaN placement, for exampleOrder::ASCENDING.
Permutation::from_indices Page
pub fn from_indices(idx: Vec<usize>) -> Option<Self>
A permutation from its indices: element i of the result is element idx[i] of the input.
Returns
None unless every index of 0..idx.len() occurs exactly once.
Permutation::len Page
pub fn len(&self) -> usize
Number of elements.
Permutation::is_empty Page
pub fn is_empty(&self) -> bool
True for the permutation of nothing.
Permutation::as_slice Page
pub fn as_slice(&self) -> &[usize]
The gather indices.
Permutation::into_vec Page
pub fn into_vec(self) -> Vec<usize>
The gather indices, by value.
Permutation::is_identity Page
pub fn is_identity(&self) -> bool
True if every element stays in place.
Permutation::gather Page
pub fn gather<V: Copy>(&self, src: &[V]) -> Vec<V>
Returns src reordered: out[i] = src[indices[i]].
Permutation::gather_into Page
pub fn gather_into<V: Copy>(&self, src: &[V], out: &mut [V])
gather into out, which must have the permutation's length.
Permutation::scatter Page
pub fn scatter<V: Copy>(&self, src: &[V]) -> Vec<V>
Returns src with the reordering undone: out[indices[i]] = src[i].
Permutation::apply Page
pub fn apply<V>(&self, data: &mut [V])
Reorders data in place, as gather would, without copying it.
Notes
- Uses one bit of scratch per element.
Permutation::apply_inverse Page
pub fn apply_inverse<V>(&self, data: &mut [V])
Undoes apply in place, as scatter would.
Permutation::inverse Page
pub fn inverse(&self) -> Self
The inverse permutation: applying a permutation and then its inverse leaves data unchanged.
Permutation::then Page
pub fn then(&self, next: &Permutation) -> Permutation
The permutation that applies self, then next: x.apply(self); x.apply(next) equals
x.apply(self.then(next)).
TopKStream Page
pub struct TopKStream<T: Sortable> {
/* private fields */
}
The k first keys (in an Order) of data that arrives in chunks, without keeping the data.
push each chunk, then read the result. Streams filled separately, for example
one per thread with push_at, combine with merge.
Example
use kwker::{Order, TopKStream};
let mut s = TopKStream::new(2, Order::DESCENDING);
s.push(&[5, 1, 9]);
s.push(&[7, 3]);
let (top, at) = s.result(true);
assert_eq!(top, [9, 7]);
assert_eq!(at, [2, 3]);
Notes
- Positions count from 0 across the whole stream; equal keys go to the earlier position first.
- Keeps at most
2 * kcandidates in memory.
TopKStream::new Page
pub fn new(k: usize, order: Order) -> Self
An empty stream that keeps the k first keys in order.
TopKStream::push Page
pub fn push(&mut self, chunk: &[T])
Adds the next chunk; its positions continue after the elements pushed so far.
TopKStream::push_at Page
pub fn push_at(&mut self, chunk: &[T], first: u64)
Adds a chunk whose first element has stream position first, so chunks can come in any order.
TopKStream::merge Page
pub fn merge(&mut self, other: &TopKStream<T>)
Adds the candidates of another stream with the same k and order.
TopKStream::seen Page
pub fn seen(&self) -> u64
The number of elements pushed so far: the position push gives the next element.
TopKStream::result Page
pub fn result(&self, sorted: bool) -> (Vec<T>, Vec<u64>)
Returns the k first keys (fewer if fewer were pushed) and their stream positions.
Arguments
sorted:truereturns them in order;falsein no particular order.
Rows and segments
sort_rows Page
pub fn sort_rows<T: Sortable>(data: &mut [T], row_len: usize, order: Order)
Sorts every row of a row-major table in place.
Arguments
data: the table, row after row (anySortabletype).row_len: the number of keys in a row.order: the direction and NaN placement, for exampleOrder::ASCENDING.
Example
use kwker::Order;
let mut t = vec![3, 1, 2, 9, 7, 8];
kwker::sort_rows(&mut t, 3, Order::ASCENDING);
assert_eq!(t, [1, 2, 3, 7, 8, 9]);
Panics
If row_len does not divide data.len() (empty data takes any row_len).
Remarks: Not stable (rule 5): keys that are equal but can be told apart (NaNs with different bits) may change places. Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
Examples: Sorting: Sort each row
sort_segments Page
pub fn sort_segments<T: Sortable, I: Index>(data: &mut [T], offsets: &[I], order: Order)
Sorts every segment data[offsets[i]..offsets[i + 1]] in place: segments of any length, one after another.
Arguments
data: the keys (anySortabletype).offsets: the segment bounds, nondecreasing and at mostdata.len();[0, end_0, end_1, ..., data.len()]covers all ofdata.order: the direction and NaN placement, for exampleOrder::ASCENDING.
Example
use kwker::Order;
let mut v = vec![2, 1, 9, 8, 7];
kwker::sort_segments(&mut v, &[0u32, 2, 5], Order::ASCENDING);
assert_eq!(v, [1, 2, 7, 8, 9]);
Notes
- Keys outside the segments are left as they are.
Remarks: Not stable (rule 5): keys that are equal but can be told apart (NaNs with different bits) may change places. Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
top_k_rows Page
pub fn top_k_rows<T: Sortable, I: Index>(
data: &[T],
row_len: usize,
k: usize,
order: Order,
sorted: bool,
values: &mut [T],
indices: &mut [I],
)
The k first keys of every row and their positions within the row, as crate::top_k gives for one row.
Arguments
data: the table, row after row (anySortabletype).row_len: the number of keys in a row.k: keys per row, at mostrow_len.order: the direction and NaN placement, for exampleOrder::DESCENDINGfor the largest.sorted:truereturns each row's keys in order;falsein no particular order, which is faster.values,indices: receivekentries per row; rowi's are ati * k..(i + 1) * k.
Example
use kwker::Order;
let scores = [0.1f32, 0.7, 0.2, 0.9, 0.3, 0.8];
let (mut top, mut at) = (vec![0.0f32; 2], vec![0u32; 2]);
kwker::top_k_rows(&scores, 3, 1, Order::DESCENDING, true, &mut top, &mut at);
assert_eq!(top, [0.7, 0.9]);
assert_eq!(at, [1, 0]);
Panics
If row_len does not divide data.len(), k > row_len, or an output does not hold rows x k entries.
Remarks: The first k keys of the stable order and their positions; equal keys keep their input order (rule 9). Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
top_k_segments Page
pub fn top_k_segments<T: Sortable, I: Index, J: Index>(
data: &[T],
offsets: &[J],
k: usize,
order: Order,
sorted: bool,
values: &mut [T],
indices: &mut [I],
)
The k first keys of every segment and their positions within the segment.
Arguments
data: the keys (anySortabletype).offsets: the segment bounds, as insort_segments.k: keys per segment.order: the direction and NaN placement.sorted:truereturns each segment's keys in order;falsein no particular order.values,indices: receivekslots per segment; segmenti's are ati * k..(i + 1) * k.
Notes
- A segment shorter than
kfills only its first slots; the others are left as they are.
Examples: Top-k and selection: Top-k per group
argsort_rows Page
pub fn argsort_rows<T: Sortable, I: Index>(
data: &[T],
row_len: usize,
order: Order,
indices: &mut [I],
)
The positions that sort every row, counted within the row, as crate::argsort gives for one row.
Arguments
data: the table, row after row (anySortabletype); not changed.row_len: the number of keys in a row.order: the direction and NaN placement, for exampleOrder::ASCENDING.indices: receives one position per key.
Panics
If row_len does not divide data.len() or indices.len() != data.len().
Remarks: Stable (rule 6): equal keys keep their input order, so the same input always gives the same positions. Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
sort_rows_indexed Page
pub fn sort_rows_indexed<T: Sortable>(
data: &[T],
row_len: usize,
order: Order,
values: &mut [T],
indices: &mut [u64],
)
Sorts every row into values and writes where each sorted key came from into indices, like
torch.sort(stable=True): a sort and an argsort in one call.
Arguments
data: the table, row after row (anySortabletype); not changed.row_len: the number of keys in a row.order: the direction and NaN placement, for exampleOrder::ASCENDING.values: receives the sorted rows.indices: receives the positions within each row; equal keys keep their input order.
Panics
If row_len does not divide data.len() or an output does not hold data.len() entries.
kth_rows Page
pub fn kth_rows<T: Sortable>(
data: &[T],
row_len: usize,
k: usize,
order: Order,
nan_first: bool,
values: &mut [T],
indices: &mut [u64],
)
The key at position k of every sorted row and its position in the row, like torch.kthvalue. With
k = (row_len - 1) / 2 this is each row's median.
Arguments
data: the table, row after row (anySortabletype); not changed.row_len: the number of keys in a row.k: the sorted position, belowrow_len.order: the direction and NaN placement, for exampleOrder::ASCENDING.nan_first:truereturns a row's first NaN when it holds one, astorch.mediandoes.values,indices: receive one entry per row.
Example
use kwker::Order;
let t = [5, 1, 3, 9, 7, 8];
let (mut med, mut at) = (vec![0; 2], vec![0u64; 2]);
kwker::kth_rows(&t, 3, 1, Order::ASCENDING, false, &mut med, &mut at);
assert_eq!(med, [3, 8]);
assert_eq!(at, [2, 2]);
Panics
If row_len does not divide data.len(), k >= row_len, or an output does not hold one entry per row.
Notes
- Among equal keys the earlier position counts first.
argsort_segments Page
pub fn argsort_segments<T: Sortable, I: Index, J: Index>(
data: &[T],
offsets: &[J],
order: Order,
indices: &mut [I],
)
The positions that sort every segment, counted within the segment, written at the segment's place in indices.
Arguments
data: the keys (anySortabletype); not changed.offsets: the segment bounds, as insort_segments.order: the direction and NaN placement.indices: one slot per key; slots outside the segments are left as they are.
argpartition_rows Page
pub fn argpartition_rows<T: Sortable, I: Index>(
data: &[T],
row_len: usize,
kth: usize,
order: Order,
indices: &mut [I],
)
numpy.argpartition for every row: per row, the positions arranged so that slot kth holds the position of the
row's key at sorted position kth, the slots before it the positions of smaller keys and the slots after it the
others.
Arguments
data: the table, row after row (anySortabletype); not changed.row_len: the number of keys in a row.kth: the sorted position, belowrow_len.order: the direction and NaN placement.indices: receives one position (within the row) per key.
Panics
If row_len does not divide data.len(), kth >= row_len, or indices.len() != data.len().
Notes
- The slots before
kthare in no particular order; those after it are in increasing position order.
argpartition_segments Page
pub fn argpartition_segments<T: Sortable, I: Index, J: Index>(
data: &[T],
offsets: &[J],
kth: usize,
order: Order,
indices: &mut [I],
)
argpartition_rows for segments of any length, written at each segment's place in indices.
Arguments
data: the keys (anySortabletype); not changed.offsets: the segment bounds, as insort_segments.kth: the sorted position.order: the direction and NaN placement.indices: one slot per key.
Notes
- A segment with
kth + 1keys or fewer gets its full argsort.
sort_rows_mt Page
pub fn sort_rows_mt<T: Sortable + Send + Sync>(
data: &mut [T],
row_len: usize,
order: Order,
threads: usize,
)
sort_rows on several threads; the result is the same.
Arguments
data,row_len,order: as insort_rows.threads: the number of threads; 0 uses the default (crate::default_threads).
Remarks: Not stable (rule 5): keys that are equal but can be told apart (NaNs with different bits) may change places. Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for. Threads change only the speed: the result follows the same rules on any thread count (rule 10).
argsort_rows_mt Page
pub fn argsort_rows_mt<T: Sortable + Send + Sync, I: Index>(
data: &[T],
row_len: usize,
order: Order,
indices: &mut [I],
threads: usize,
)
argsort_rows on several threads; the result is the same.
Arguments
data,row_len,order,indices: as inargsort_rows.threads: the number of threads; 0 uses the default (crate::default_threads).
Remarks: Stable (rule 6): equal keys keep their input order, so the same input always gives the same positions. Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for. Threads change only the speed: the result follows the same rules on any thread count (rule 10).
sort_rows_indexed_mt Page
pub fn sort_rows_indexed_mt<T: Sortable + Send + Sync>(
data: &[T],
row_len: usize,
order: Order,
values: &mut [T],
indices: &mut [u64],
threads: usize,
)
sort_rows_indexed on several threads; the result is the same.
Arguments
data,row_len,order,values,indices: as insort_rows_indexed.threads: the number of threads; 0 uses the default (crate::default_threads).
top_k_rows_mt Page
pub fn top_k_rows_mt<T: Sortable + Send + Sync, I: Index>(
data: &[T],
row_len: usize,
k: usize,
order: Order,
sorted: bool,
values: &mut [T],
indices: &mut [I],
threads: usize,
)
top_k_rows on several threads; the result is the same.
Arguments
data,row_len,k,order,sorted,values,indices: as intop_k_rows.threads: the number of threads; 0 uses the default (crate::default_threads).
Remarks: The first k keys of the stable order and their positions; equal keys keep their input order (rule 9). Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for. Threads change only the speed: the result follows the same rules on any thread count (rule 10).
sort_segments_mt Page
pub fn sort_segments_mt<T: Sortable + Send + Sync, I: Index>(
data: &mut [T],
offsets: &[I],
order: Order,
threads: usize,
)
sort_segments on several threads; the result is the same.
Arguments
data,offsets,order: as insort_segments.threads: the number of threads; 0 uses the default (crate::default_threads).
Remarks: Not stable (rule 5): keys that are equal but can be told apart (NaNs with different bits) may change places. Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for. Threads change only the speed: the result follows the same rules on any thread count (rule 10).
Along an axis
sort_axis_strided Page
pub unsafe fn sort_axis_strided<T: Sortable>(
base: *mut T,
shape: &[usize],
strides: &[isize],
axis: usize,
order: Order,
)
Sorts every lane along axis of a strided N-dimensional array in place, like numpy.sort(a, axis).
Arguments
base: pointer to the array's first element (anySortabletype).shape: the array's dimensions.strides: the step between neighbours in each dimension, in elements (any sign).axis: the dimension to sort along.order: the direction and NaN placement, for exampleOrder::ASCENDING.
Safety
Every element reachable through shape and strides from base must be valid and writable, and no two index
tuples may address the same element (a stride of 0 along axis excepted).
sort_axis Page
pub fn sort_axis<T: Sortable>(data: &mut [T], shape: &[usize], axis: usize, order: Order)
Sorts every lane along axis of a row-major (C-order) array in place, like numpy.sort(a, axis).
Arguments
data: the array's elements in row-major order (anySortabletype).shape: the array's dimensions; their product must bedata.len().axis: the dimension to sort along.order: the direction and NaN placement, for exampleOrder::ASCENDING.
Example
use kwker::Order;
// a 2 x 3 array sorted down its columns (axis 0)
let mut a = vec![4, 2, 9, 1, 5, 3];
kwker::sort_axis(&mut a, &[2, 3], 0, Order::ASCENDING);
assert_eq!(a, [1, 2, 3, 4, 5, 9]);
Panics
If the shape's product is not data.len() or axis >= shape.len().
argsort_axis_strided Page
pub unsafe fn argsort_axis_strided<T: Sortable, I: Index>(
base: *const T,
shape: &[usize],
strides: &[isize],
axis: usize,
order: Order,
out: *mut I,
out_strides: &[isize],
)
The positions that sort every lane along axis of a strided array, counted within the lane, like
numpy.argsort(a, axis). Equal keys keep their input order.
Arguments
base,shape,strides,axis,order: the input, as insort_axis_strided.out: pointer to the output array, of the same shape.out_strides: the output's strides, in elements.
Safety
Every element reachable through shape / strides from base must be readable, every element through
shape / out_strides from out writable, and no two index tuples may address the same output element.
sort_axis_indexed_strided Page
pub unsafe fn sort_axis_indexed_strided<T: Sortable, I: Index>(
base: *const T,
shape: &[usize],
strides: &[isize],
axis: usize,
order: Order,
vals: *mut T,
out: *mut I,
out_strides: &[isize],
)
Sorts every lane along axis of a strided array into vals and writes each sorted key's position in its lane
into out, like torch.sort(x, dim, stable=True), without a transposed copy.
Arguments
base,shape,strides,axis,order: the input, as insort_axis_strided.vals: pointer to the sorted-values output, of the input's shape.out: pointer to the positions output, of the same shape; null for the sorted values only.out_strides: the strides of both outputs, in elements.
Safety
Every element reachable through shape / strides from base must be readable, every element through
shape / out_strides from vals and (unless null) out writable, no two index tuples may address the same
output element, and the outputs may not overlap each other or the input.
kth_axis_strided Page
pub unsafe fn kth_axis_strided<T: Sortable>(
base: *const T,
shape: &[usize],
strides: &[isize],
axis: usize,
k: usize,
order: Order,
nan_first: bool,
vals: *mut T,
out: *mut u64,
out_strides: &[isize],
)
The key at sorted position k of every lane along axis of a strided array, and its position in the lane, like
torch.kthvalue(x, k + 1, dim) or torch.median(x, dim).
Arguments
base,shape,strides,axis,order: the input, as insort_axis_strided.k: the sorted position, counted from 0.nan_first:truereturns a lane's first NaN when it holds one, astorch.mediandoes.vals,out: pointers to the value and position outputs, one entry per lane.out_strides: the outputs' strides for the input's shape; the entry foraxisis not used.
Safety
Every element reachable through shape / strides from base must be readable, and every result slot through
the other dimensions of out_strides from vals and out writable and distinct.
quantile_axis_strided Page
pub unsafe fn quantile_axis_strided<T: Sortable + Send + Sync>(
base: *const T,
shape: &[usize],
strides: &[isize],
axis: usize,
qs: &[f64],
ignore_nan: bool,
interp: Interpolation,
below: *mut T,
above: *mut T,
weight: *mut f64,
out_strides: &[isize],
threads: usize,
)
The order statistics behind torch.quantile(x, qs, dim) for every lane along axis of a strided array: for each
quantile the two neighbouring keys and the weight between them (the result is below + weight * (above - below),
as crate::quantile_lane gives for one lane).
Arguments
base,shape,strides,axis: the input, as insort_axis_strided.qs: the quantiles, each in[0, 1].ignore_nan:trueskips NaNs, liketorch.nanquantile.interp: the interpolation rule, as intorch.quantile.below,above,weight: pointers to the outputs; each lane gets a row ofqs.len()entries.out_strides: the outputs' strides (in rows) for the input's shape; the entry foraxisis not used.threads: the number of threads; 0 uses the default.
Safety
Every element reachable through shape / strides from base must be readable, and every output row writable.
quantile_axis_strided_ex Page
pub unsafe fn quantile_axis_strided_ex<T: Sortable + Send + Sync>(
base: *const T,
shape: &[usize],
strides: &[isize],
axis: usize,
qs: &[f64],
ignore_nan: bool,
interp: Interpolation,
f32_ranks: bool,
below: *mut T,
above: *mut T,
weight: *mut f64,
out_strides: &[isize],
threads: usize,
)
quantile_axis_strided that can match older torch releases bit for bit on float32 input.
Arguments
f32_ranks:truecomputes the ranksq * (n - 1)in float32, as torch 2.12 and earlier do for float32 input; the other arguments are as inquantile_axis_strided.
Safety
As quantile_axis_strided.
argsort_axis Page
pub fn argsort_axis<T: Sortable, I: Index>(
data: &[T],
shape: &[usize],
axis: usize,
order: Order,
out: &mut [I],
)
The positions that sort every lane along axis of a row-major (C-order) array, counted within the lane, like
numpy.argsort(a, axis). Equal keys keep their input order.
Arguments
data: the array's elements in row-major order (anySortabletype); not changed.shape: the array's dimensions; their product must bedata.len().axis: the dimension to sort along.order: the direction and NaN placement.out: receives the positions, row-major, same shape.
Panics
If data or out does not hold the shape's product of elements.
MAX_DIMS Page
pub const MAX_DIMS: usize = 64;
At most this many dimensions (NumPy's limit): the lane iteration keeps its state on the stack.
Searching
lower_bound Page
pub fn lower_bound<T: Sortable>(sorted: &[T], key: T, order: Order) -> usize
The first position in a sorted slice whose key does not come before key, like C++ std::lower_bound.
Arguments
sorted: keys sorted inorder(anySortabletype).key: the key to look for.order: the ordersortedis in, for exampleOrder::ASCENDING.
Example
use kwker::Order;
assert_eq!(kwker::lower_bound(&[10, 20, 20, 30], 20, Order::ASCENDING), 1);
upper_bound Page
pub fn upper_bound<T: Sortable>(sorted: &[T], key: T, order: Order) -> usize
The first position in a sorted slice whose key comes after key, like C++ std::upper_bound.
Arguments
sorted: keys sorted inorder(anySortabletype).key: the key to look for.order: the ordersortedis in, for exampleOrder::ASCENDING.
Example
use kwker::Order;
assert_eq!(kwker::upper_bound(&[10, 20, 20, 30], 20, Order::ASCENDING), 3);
equal_range Page
pub fn equal_range<T: Sortable>(sorted: &[T], key: T, order: Order) -> Range<usize>
The positions in a sorted slice that hold key.
Arguments
sorted: keys sorted inorder(anySortabletype).key: the key to look for.order: the ordersortedis in, for exampleOrder::ASCENDING.
Returns
The range from lower_bound to upper_bound; empty when key is not there.
Notes
-0.0and+0.0count as equal, as in NumPy.
searchsorted_into Page
pub fn searchsorted_into<T: Sortable>(
sorted: &[T],
queries: &[T],
side: Side,
order: Order,
out: &mut [u64],
)
Finds where each query would go in a sorted slice, like numpy.searchsorted and torch.searchsorted.
Arguments
sorted: keys sorted inorder(anySortabletype; the order is not checked).queries: the keys to look up.side:Side::Leftgives each query'slower_bound;Side::Rightitsupper_bound.order: the ordersortedis in.out: receives one position per query.
Panics
If out.len() != queries.len().
Remarks: -0.0 and +0.0 compare equal here, as in NumPy (rule 14).
searchsorted Page
pub fn searchsorted<T: Sortable>(sorted: &[T], queries: &[T], side: Side, order: Order) -> Vec<u64>
Finds where each query would go in a sorted slice, like numpy.searchsorted and torch.searchsorted.
Arguments
sorted: keys sorted inorder(anySortabletype; the order is not checked).queries: the keys to look up.side:Side::Leftgives each query'slower_bound;Side::Rightitsupper_bound.order: the ordersortedis in.
Returns
One position per query.
Example
use kwker::{Order, Side};
let at = kwker::searchsorted(&[10, 20, 30], &[5, 20, 35], Side::Left, Order::ASCENDING);
assert_eq!(at, [0, 1, 3]);
Remarks: -0.0 and +0.0 compare equal here, as in NumPy (rule 14).
Examples: Searching sorted data: Where does a value go? searchsorted
bucketize Page
pub fn bucketize<T: Sortable>(values: &[T], boundaries: &[T], right: bool, order: Order) -> Vec<u64>
The bucket of every value between sorted boundaries, like torch.bucketize.
Arguments
values: the values to place (anySortabletype).boundaries: the bucket edges, sorted inorder.right:falseputs a value equal to an edge in the bucket below it;truein the bucket above it.order: the orderboundariesis in, for exampleOrder::ASCENDING.
Returns
One bucket number per value, from 0 to boundaries.len().
Example
use kwker::Order;
let b = kwker::bucketize(&[1, 5, 10], &[2, 6], false, Order::ASCENDING);
assert_eq!(b, [0, 1, 2]);
Remarks: -0.0 and +0.0 compare equal here, as in NumPy (rule 14).
Examples: Searching sorted data: Which bucket? bucketize
bucket_counts Page
pub fn bucket_counts<T: Sortable>(
values: &[T],
boundaries: &[T],
right: bool,
order: Order,
) -> Vec<u64>
Counts the values in each bucket of bucketize: a histogram with the edges you give.
Arguments
values,boundaries,right,order: as inbucketize.
Returns
boundaries.len() + 1 counts.
Remarks: -0.0 and +0.0 compare equal here, as in NumPy (rule 14).
Examples: Searching sorted data: How many in each bucket? bucket_counts
bucket_counts_into Page
pub fn bucket_counts_into<T: Sortable>(
values: &[T],
boundaries: &[T],
right: bool,
order: Order,
counts: &mut [u64],
)
bucket_counts into a buffer you provide.
Arguments
values,boundaries,right,order: as inbucketize.counts: receivesboundaries.len() + 1counts (overwritten).
Panics
If counts.len() != boundaries.len() + 1.
Remarks: -0.0 and +0.0 compare equal here, as in NumPy (rule 14).
Side Page
pub enum Side {
Left,
Right,
}
Which end of a run of equal keys searchsorted reports: Left is the first position whose key does not come
before the query (lower_bound); Right is the first position whose key comes after it (upper_bound).
Variants
Left: The lower bound.Right: The upper bound.
Ranks
rank_into Page
pub fn rank_into<T: Sortable>(v: &[T], order: Order, ties: RankTies, out: &mut [u64])
rank into a buffer you provide.
Arguments
v: the keys (anySortabletype); not changed.order: the direction and NaN placement, for exampleOrder::ASCENDING.ties: how equal keys are ranked (seeRankTies).out: receives one rank per key.
Panics
If out.len() != v.len().
Remarks: Ordinal ranks give equal keys increasing ranks in input order (rule 6). Equal values are one key here, as in NumPy: -0.0 and +0.0 are equal, and so are all NaNs (rule 13).
rank Page
pub fn rank<T: Sortable>(v: &[T], order: Order, ties: RankTies) -> Vec<u64>
The rank of every key, from 1, like SQL RANK() or pandas rank().
Arguments
v: the keys (anySortabletype); not changed.order: the direction and NaN placement, for exampleOrder::ASCENDING(rank 1 = the smallest).ties: how equal keys are ranked (seeRankTies).
Returns
One rank per key, in input order.
Example
use kwker::{Order, RankTies};
let r = kwker::rank(&[30, 10, 20, 10], Order::ASCENDING, RankTies::Min);
assert_eq!(r, [4, 1, 3, 1]);
Notes
- Keys are equal when their values are:
-0.0equals+0.0, and all NaNs are one value.
Remarks: Ordinal ranks give equal keys increasing ranks in input order (rule 6). Equal values are one key here, as in NumPy: -0.0 and +0.0 are equal, and so are all NaNs (rule 13).
Examples: Order and ranking: Ranks
rank_average Page
pub fn rank_average<T: Sortable>(v: &[T], order: Order) -> Vec<f64>
Average ranks, from 1: equal keys share the mean of their group's ranks, like scipy.stats.rankdata and pandas
rank(method="average"). Spearman's correlation uses these.
Arguments
v: the keys (anySortabletype); not changed.order: the direction and NaN placement, for exampleOrder::ASCENDING.
Returns
One rank per key, in input order.
Remarks: Equal values are one key here, as in NumPy: -0.0 and +0.0 are equal, and so are all NaNs (rule 13).
Examples: Order and ranking: Ranks
percent_rank Page
pub fn percent_rank<T: Sortable>(v: &[T], order: Order) -> Vec<f64>
SQL PERCENT_RANK(): (rank - 1) / (n - 1) for every key, between 0 and 1, with equal keys sharing their
lowest rank.
Arguments
v: the keys (anySortabletype); not changed.order: the direction and NaN placement, for exampleOrder::ASCENDING.
Returns
One value per key, in input order; all 0 for fewer than two keys.
Remarks: Equal values are one key here, as in NumPy: -0.0 and +0.0 are equal, and so are all NaNs (rule 13).
Examples: Order and ranking: Ranks
inverse_ranks Page
pub fn inverse_ranks(ranks: &[u64]) -> Option<Vec<u64>>
Turns ranks back into a sort order: from ranks 1..=n (as rank with RankTies::Ordinal returns), the
positions that sort the keys, as crate::argsort would give.
Arguments
ranks: one rank per key, each of1..=nexactly once.
Returns
The positions, or None if ranks is not a permutation of 1..=n.
rank_rows Page
pub fn rank_rows<T: Sortable>(
data: &[T],
row_len: usize,
order: Order,
ties: RankTies,
out: &mut [u64],
)
rank within every row of a row-major table.
Arguments
data: the table, row after row (anySortabletype); not changed.row_len: the number of keys in a row.order: the direction and NaN placement.ties: how equal keys are ranked (seeRankTies).out: receives one rank per key.
Panics
If row_len does not divide data.len() or out.len() != data.len().
count_inversions Page
pub fn count_inversions<T: Sortable>(v: &[T], order: Order) -> u64
Counts the pairs of keys that are out of order: positions i < j whose keys come the other way round in
order. This is the number of discordant pairs in Kendall's tau.
Arguments
v: the keys (anySortabletype); not changed.order: the direction and NaN placement, for exampleOrder::ASCENDING.
Example
use kwker::Order;
assert_eq!(kwker::count_inversions(&[3, 1, 2], Order::ASCENDING), 2);
Notes
- Equal keys are never an inversion;
-0.0equals+0.0, and all NaNs are one value.
RankTies Page
pub enum RankTies {
Ordinal,
Min,
Max,
Dense,
}
How rank ranks equal keys.
Variants
Ordinal: Every key its own rank, equal keys in index order (the stable order): a permutation of1..=n.Min: Equal keys share the lowest rank of their group (SQLRANK, pandasmethod="min").Max: Equal keys share the highest rank of their group (pandasmethod="max").Dense: Equal keys share one rank and the ranks have no gaps (SQLDENSE_RANK, pandasmethod="dense").
Quantiles
select_ranks Page
pub fn select_ranks<T: Sortable>(v: &mut [T], ranks: &[usize], order: Order)
Several crate::select_nth calls at once: afterwards v[r] holds the key a full sort would put there, for
every r in ranks, and the keys between two selected positions lie between their keys.
Arguments
v: the keys (anySortabletype).ranks: the positions to fill, each belowv.len(); any order, repeats allowed.order: the direction and NaN placement, for exampleOrder::ASCENDING.
Panics
If a rank is v.len() or more.
quantile_lane_mt Page
pub fn quantile_lane_mt<T: Sortable + Send + Sync>(
v: &[T],
qs: &[f64],
ignore_nan: bool,
interp: Interpolation,
below: &mut [T],
above: &mut [T],
weight: &mut [f64],
threads: usize,
)
quantile_lane of one large lane on several threads; v is not changed.
Arguments
v: the values (anySortabletype).qs,ignore_nan,interp,below,above,weight: as inquantile_lane.threads: the number of threads; 0 uses the default.
quantile_lane Page
pub fn quantile_lane<T: Sortable>(
v: &mut [T],
qs: &[f64],
ignore_nan: bool,
interp: Interpolation,
below: &mut [T],
above: &mut [T],
weight: &mut [f64],
ranks: &mut Vec<usize>,
)
The order statistics behind torch.quantile for one lane of values: for each quantile, the two neighbouring keys
and the weight between them. The quantile is below + weight * (above - below).
Arguments
v: the values (anySortabletype); reordered.qs: the quantiles, each in[0, 1].ignore_nan:trueskips NaNs, liketorch.nanquantile.interp: the interpolation rule (seeInterpolation).below,above,weight: receive one entry per quantile.ranks: scratch space, reused between calls.
Notes
- A lane holding a NaN (with
ignore_nan: holding only NaNs) gives NaN for every quantile, as torch does.
quantiles Page
pub fn quantiles<T: Sortable>(v: &mut [T], qs: &[f64], order: Order) -> Vec<T>
The keys at the given quantiles: quantile q is the key at sorted position floor(q * (n - 1)).
Arguments
v: the keys (anySortabletype); reordered asselect_ranksdoes.qs: the quantiles, each in[0, 1].order: the direction and NaN placement, for exampleOrder::ASCENDING.
Returns
One key per quantile.
Example
use kwker::Order;
let mut v: Vec<i32> = (1..=11).collect();
assert_eq!(kwker::quantiles(&mut v, &[0.0, 0.5, 1.0], Order::ASCENDING), [1, 6, 11]);
Panics
If v is empty or a quantile is outside [0, 1].
median Page
pub fn median<T: Sortable>(v: &mut [T], order: Order) -> T
The median of v; for an even count, the lower of the two middle keys.
Arguments
v: the keys (anySortabletype); reordered ascrate::select_nthdoes.order: the direction and NaN placement, for exampleOrder::ASCENDING.
Example
use kwker::Order;
let mut v = vec![5, 1, 4, 2];
assert_eq!(kwker::median(&mut v, Order::ASCENDING), 2);
Panics
If v is empty.
Interpolation Page
pub enum Interpolation {
Linear,
Lower,
Higher,
Midpoint,
Nearest,
}
How torch.quantile interpolates between the two keys around a quantile.
Variants
Linear: lower + (higher - lower) x the fractional part of the rankLower: the lower order statisticHigher: the higher order statisticMidpoint: their meanNearest: the nearer one (ties to the even rank, as torch)
Unique values
unique_inverse Page
pub fn unique_inverse<T: Sortable + Send + Sync>(
data: &[T],
inverse: Option<&mut [u64]>,
want_counts: bool,
threads: usize,
) -> (Vec<T>, Vec<u64>)
The distinct keys of data, sorted, like numpy.unique and torch.unique, with optional group numbers and
counts.
Arguments
data: the keys (anySortabletype); not changed.inverse: if given, receives each key's position among the distinct keys (one entry per key).want_counts:truealso counts each distinct key.threads: the number of threads; 1 uses the calling thread only.
Returns
(values, counts): the distinct keys, smallest first, and their counts (empty unless want_counts).
Example
let mut inv = vec![0u64; 4];
let (vals, counts) = kwker::unique_inverse(&[3, 1, 3, 2], Some(&mut inv), true, 1);
assert_eq!(vals, [1, 2, 3]);
assert_eq!(counts, [1, 1, 2]);
assert_eq!(inv, [2, 0, 2, 1]);
Panics
If inverse is given and its length differs from data.len().
Notes
-0.0and+0.0are one key (given as the zero that comes first in the input); every NaN is a key of its own, listed last. Both follow torch.
unique_into Page
pub fn unique_into<T: Sortable + Send + Sync>(data: &[T], out: &mut [T], threads: usize) -> usize
The distinct keys of data, sorted, into a buffer you provide.
Arguments
data: the keys (anySortabletype); not changed.out: as long asdata; receives the distinct keys inout[..m].threads: the number of threads; 1 uses the calling thread only.
Returns
m, the number of distinct keys.
Panics
If out.len() != data.len().
Remarks: -0.0 and +0.0 are one value; every NaN is a value of its own, listed last (rule 15).
unique_inverse_into Page
pub fn unique_inverse_into<T: Sortable + Send + Sync>(
data: &[T],
values: &mut [T],
inverse: Option<&mut [u64]>,
counts: Option<&mut [u64]>,
threads: usize,
) -> usize
unique_inverse into buffers you provide.
Arguments
data: the keys (anySortabletype); not changed.values: as long asdata; receives the distinct keys invalues[..m].inverse: if given, receives each key's position among the distinct keys.counts: if given, as long asdata; receives the counts incounts[..m].threads: the number of threads; 1 uses the calling thread only.
Returns
m, the number of distinct keys.
Panics
If a buffer given is not as long as data.
Sorted sets
set_op Page
pub fn set_op<T: Sortable>(a: &[T], b: &[T], op: SetOp, multiset: bool, order: Order) -> Vec<T>
A set operation on two sorted slices: intersection, union, difference or symmetric difference. The result is sorted too.
Arguments
a,b: keys sorted inorder(anySortabletype; the order is not checked).op: the operation (seeSetOp).multiset:falsetreats each input as a set (every value at most once in the result);truecounts copies, as C++std::set_intersectionand friends do: a value withcacopies inaandcbinbappearsmin(ca, cb)times in the intersection,max(ca, cb)in the union,max(ca - cb, 0)in the difference and|ca - cb|in the symmetric difference.order: the order both inputs are in, for exampleOrder::ASCENDING.
Example
use kwker::{Order, SetOp};
let both = kwker::set_op(&[1, 2, 2, 3], &[2, 2, 4], SetOp::Intersection, false, Order::ASCENDING);
assert_eq!(both, [2]);
let both = kwker::set_op(&[1, 2, 2, 3], &[2, 2, 4], SetOp::Intersection, true, Order::ASCENDING);
assert_eq!(both, [2, 2]);
Notes
- Values compare as NumPy compares them:
-0.0equals+0.0, and all NaNs are one value.
Remarks: Equal values are one key here, as in NumPy: -0.0 and +0.0 are equal, and so are all NaNs (rule 13).
Examples: Groups, merges and sets: Set operations
set_intersection Page
pub fn set_intersection<T: Sortable>(a: &[T], b: &[T], multiset: bool, order: Order) -> Vec<T>
The keys in both a and b: set_op with SetOp::Intersection.
Arguments
a,b,multiset,order: as inset_op.
set_union Page
pub fn set_union<T: Sortable>(a: &[T], b: &[T], multiset: bool, order: Order) -> Vec<T>
The keys in a or b: set_op with SetOp::Union.
Arguments
a,b,multiset,order: as inset_op.
set_difference Page
pub fn set_difference<T: Sortable>(a: &[T], b: &[T], multiset: bool, order: Order) -> Vec<T>
The keys in a but not in b: set_op with SetOp::Difference.
Arguments
a,b,multiset,order: as inset_op.
set_symmetric_difference Page
pub fn set_symmetric_difference<T: Sortable>(
a: &[T],
b: &[T],
multiset: bool,
order: Order,
) -> Vec<T>
The keys in exactly one of a and b: set_op with SetOp::SymmetricDifference.
Arguments
a,b,multiset,order: as inset_op.
intersection_indices Page
pub fn intersection_indices<T: Sortable>(
a: &[T],
b: &[T],
multiset: bool,
order: Order,
) -> (Vec<u64>, Vec<u64>)
Where the common keys of two sorted slices are, in each of them, like numpy.intersect1d(return_indices=True).
Use it to line up data that belongs to the keys.
Arguments
a,b: keys sorted inorder(anySortabletype).multiset:falsegives each common value's first position in each input;truepairs up the firstmin(ca, cb)copies.order: the order both inputs are in.
Returns
(positions in a, positions in b), in increasing order.
row_intersect_count Page
pub fn row_intersect_count<T: Sortable>(a: &[T], ka: usize, b: &[T], kb: usize, out: &mut [u64])
For every row, how many keys two tables have in common: for example recall@k, retrieved ids against the true ones.
Arguments
a: rows ofkakeys each, row after row (anySortabletype).ka: the number of keys in a row ofa.b: the same number of rows, ofkbkeys each.kb: the number of keys in a row ofb.out: receives one count per row.
Panics
If the shapes do not agree.
Notes
- Counts are exact when the keys within each row are distinct; values compare as values (
-0.0equals+0.0, all NaNs are one value).
row_unique_count Page
pub fn row_unique_count<T: Sortable>(a: &[T], k: usize, out: &mut [u64])
The number of distinct values in every row of a row-major table.
Arguments
a: the table, rows ofkkeys (anySortabletype).k: the number of keys in a row.out: receives one count per row.
Panics
If a.len() != out.len() * k.
Notes
- Values compare as values:
-0.0equals+0.0, and all NaNs are one value.
isin_into Page
pub fn isin_into<T: Sortable>(elements: &[T], test: &[T], invert: bool, out: &mut [bool])
Tests every element for membership in a set of keys, like numpy.isin and torch.isin.
Arguments
elements: the keys to test (anySortabletype).test: the set, in any order.invert:truegivestruefor elements that are not intest.out: receives one answer per element.
Example
let mut hit = vec![false; 4];
kwker::isin_into(&[1, 5, 3, 7], &[3, 1], false, &mut hit);
assert_eq!(hit, [true, false, true, false]);
Panics
If out.len() != elements.len().
Notes
-0.0equals+0.0; a NaN equals nothing, as in torch and NumPy.
SetOp Page
pub enum SetOp {
Intersection,
Union,
Difference,
SymmetricDifference,
}
Which operation set_op computes.
Variants
Intersection: Keys in both.Union: Keys in either.Difference:aminusb.SymmetricDifference: Keys in exactly one of them.
Merging
merge Page
pub fn merge<T: Sortable>(a: &[T], b: &[T], out: &mut [T], order: Order)
Merges two sorted slices into one sorted output. On equal keys, a's come first.
Arguments
a,b: keys sorted inorder(anySortabletype).out: receives thea.len() + b.len()merged keys.order: the order both inputs are in, for exampleOrder::ASCENDING.
Example
use kwker::Order;
let mut out = vec![0; 5];
kwker::merge(&[1, 4, 6], &[2, 3], &mut out, Order::ASCENDING);
assert_eq!(out, [1, 2, 3, 4, 6]);
merge_kv Page
pub fn merge_kv<K: Sortable, V: Copy>(
a_keys: &[K],
a_values: &[V],
b_keys: &[K],
b_values: &[V],
out_keys: &mut [K],
out_values: &mut [V],
order: Order,
)
Merges two sorted key-value sequences; each value moves with its key. On equal keys, a's come first.
Arguments
a_keys,a_values: the first sequence, keys sorted inorder.b_keys,b_values: the second sequence.out_keys,out_values: receive the merged pairs.order: the order both inputs are in.
merge_in_place Page
pub fn merge_in_place<T: Sortable>(v: &mut [T], mid: usize, order: Order)
Merges the sorted halves v[..mid] and v[mid..] in place. On equal keys, the first half's come first.
Arguments
v: the keys (anySortabletype), both halves sorted inorder.mid: where the second half starts.order: the order the halves are in.
Notes
- Uses a buffer the size of the smaller half.
kway_merge Page
pub fn kway_merge<T: Sortable>(runs: &[&[T]], out: &mut [T], order: Order)
Merges any number of sorted runs into one sorted output. Equal keys keep run order, then position.
Arguments
runs: the runs, each sorted inorder(anySortabletype).out: receives all the keys; its length is the runs' total.order: the order the runs are in, for exampleOrder::ASCENDING.
Example
use kwker::Order;
let mut out = vec![0; 6];
kwker::kway_merge(&[&[1, 5], &[2, 6], &[3, 4]], &mut out, Order::ASCENDING);
assert_eq!(out, [1, 2, 3, 4, 5, 6]);
Examples: Groups, merges and sets: Merge sorted lists: kway_merge
kway_merge_kv Page
pub fn kway_merge_kv<K: Sortable, V: Copy>(
keys: &[&[K]],
values: &[&[V]],
out_keys: &mut [K],
out_values: &mut [V],
order: Order,
)
kway_merge of key-value runs: each value moves with its key.
Arguments
keys: the key runs, each sorted inorder.values: one value run per key run, of the same lengths.out_keys,out_values: receive the merged pairs.order: the order the runs are in.
kway_merge_with Page
pub fn kway_merge_with<T: Sortable, F: FnMut(usize, usize)>(runs: &[&[T]], order: Order, emit: F)
The merged order of sorted runs, without moving anything: calls emit(run, index) for every element in merged
order, so you can move records, rows or other data along.
Arguments
runs: the runs, each sorted inorder(anySortabletype).order: the order the runs are in.emit: called once per element with its run number and its position in that run.
Notes
- Equal keys come in run order, then position order, as in
kway_merge.
merge_sorted_into Page
pub fn merge_sorted_into<T: Sortable>(sorted: &mut Vec<T>, new: &mut [T], order: Order)
Adds new keys to a sorted vector: sorts new and merges it in, without sorting sorted again.
Arguments
sorted: keys already sorted inorder; grows bynew.len().new: the keys to add; sorted in place.order: the order, for exampleOrder::ASCENDING.
Notes
- On equal keys, those already in
sortedcome first.
Several sort keys
lexsort Page
pub fn lexsort(keys: &[(&dyn KeyColumn, Order)]) -> Permutation
The order that sorts rows by several key columns: by the first column, ties by the second, and so on, each column
in its own order. Apply the result to every column of the table with Permutation::apply.
Arguments
keys: the key columns (slices or vectors of anySortabletype) with their orders, most significant first.
Returns
A Permutation; rows with equal keys in every column keep their input order.
Example
use kwker::Order;
let city = vec![2, 1, 2];
let age = vec![30, 40, 20];
let p = kwker::lexsort(&[(&city, Order::ASCENDING), (&age, Order::DESCENDING)]);
assert_eq!(p.as_slice(), [1, 0, 2]);
Panics
If the columns have different lengths.
Examples: Order and ranking: Sort by several columns
lexsort_mt Page
pub fn lexsort_mt(keys: &[(&dyn KeyColumn, Order)], threads: usize) -> Permutation
lexsort on several threads; the result is the same.
Arguments
keys: as inlexsort.threads: the number of threads; 0 uses the default.
lexsort_into Page
pub fn lexsort_into(keys: &[(&dyn KeyColumn, Order)], threads: usize, out: &mut [u64])
lexsort into a buffer you provide, on several threads.
Arguments
keys: as inlexsort.threads: the number of threads; 0 uses the default.out: receives one row position per row.
Panics
If a column's length differs from out.len().
lex_top_k Page
pub fn lex_top_k(keys: &[(&dyn KeyColumn, Order)], k: usize) -> Vec<usize>
The first k rows of lexsort's order without sorting every row, like SQL ORDER BY a, b LIMIT k.
Arguments
keys: as inlexsort.k: how many rows.
Returns
The positions of the first k rows (fewer if there are fewer rows), in order.
Examples: Order and ranking: Sort by several columns
lex_select_nth Page
pub fn lex_select_nth(keys: &[(&dyn KeyColumn, Order)], k: usize) -> usize
The row at position k of lexsort's order, without sorting.
Arguments
keys: as inlexsort.k: the position, below the number of rows.
Returns
The row's position in the input.
Panics
If k is not below the number of rows or the columns have different lengths.
KeyColumn Page
pub trait KeyColumn: Sync {
fn rows(&self) -> usize;
}
A key column for lexsort: any slice or vector of a Sortable key type.
Records and fields
argsort_field Page
pub fn argsort_field<K: Sortable>(
records: &[u8],
n: usize,
stride: usize,
offset: usize,
order: Order,
) -> Vec<u64>
The order of fixed-size binary records by one numeric field, without moving the records.
Arguments
records: the records' bytes, one record after another.n: the number of records.stride: the size of a record, in bytes.offset: where the key field starts in a record, in bytes (it need not be aligned).order: the direction and NaN placement, for exampleOrder::ASCENDING.
Returns
One record number per record: record p[0] has the first key. Equal keys keep their input order.
Example
// 3 records of 12 bytes: an id (u32) and a price (f64, unaligned at offset 4)
let mut bytes = Vec::new();
for (id, price) in [(7u32, 2.5f64), (8, -1.0), (9, 0.25)] {
bytes.extend_from_slice(&id.to_ne_bytes());
bytes.extend_from_slice(&price.to_ne_bytes());
}
let p = kwker::argsort_field::<f64>(&bytes, 3, 12, 4, kwker::Order::ASCENDING);
assert_eq!(p, vec![1, 2, 0]);
Panics
If n records of stride bytes do not fit in records.
argsort_field_into Page
pub fn argsort_field_into<K: Sortable>(
records: &[u8],
n: usize,
stride: usize,
offset: usize,
order: Order,
out: &mut [u64],
)
argsort_field into a buffer you provide.
Arguments
records,n,stride,offset,order: as inargsort_field.out: receivesnrecord numbers.
top_k_field Page
pub fn top_k_field<K: Sortable>(
records: &[u8],
n: usize,
stride: usize,
offset: usize,
k: usize,
order: Order,
) -> Vec<u64>
The first k records by one numeric field, without sorting them all: SQL ORDER BY field LIMIT k over records
that stay where they are.
Arguments
records,n,stride,offset: as inargsort_field.k: how many records.order: the direction and NaN placement, for exampleOrder::DESCENDINGfor the largest.
Returns
The first min(k, n) record numbers, in order. Equal keys keep their input order.
argsort_by_key Page
pub fn argsort_by_key<T, K: Sortable>(v: &[T], order: Order, key: impl FnMut(&T) -> K) -> Vec<u64>
The order of any objects by a numeric key you extract, without moving the objects.
Arguments
v: the objects, of any type.order: the direction and NaN placement, for exampleOrder::ASCENDING.key: returns an object's key; called once per object.
Returns
One position per object; equal keys keep their input order. crate::sort_by_extracted_key moves the objects.
take_records Page
pub fn take_records(src: &[u8], record_size: usize, idx: &[u64], dst: &mut [u8])
Copies records by position: record j of dst becomes record idx[j] of src.
Arguments
src: the source records' bytes.record_size: the size of a record, in bytes.idx: the source record of each output record; any positions, repeats allowed.dst: receivesidx.len()records.
Panics
If a position is out of range or dst does not hold idx.len() records.
is_permutation Page
pub fn is_permutation(perm: &[u64]) -> bool
Whether perm holds every position of 0..perm.len() exactly once, as permute_in_place requires.
permute_in_place Page
pub fn permute_in_place<T>(v: &mut [T], perm: &[u64])
Reorders v in place so that position j gets the element that was at perm[j]: applies an argsort's result.
Arguments
v: the elements, of any type.perm: a permutation of0..v.len().
Example
let mut names = vec!["c", "a", "b"];
kwker::permute_in_place(&mut names, &[1, 2, 0]);
assert_eq!(names, ["a", "b", "c"]);
Panics
If perm is not a permutation of 0..v.len() (nothing is moved).
Notes
- Uses one bit of scratch per element; the elements are swapped, not copied.
Examples: Order and ranking: Reorder records in place
permute_records_in_place Page
pub fn permute_records_in_place(records: &mut [u8], record_size: usize, perm: &[u64])
permute_in_place for raw records of a fixed size.
Arguments
records:perm.len()records ofrecord_sizebytes.record_size: the size of a record, in bytes.perm: a permutation of0..perm.len().
Panics
If the sizes do not match or perm is not a permutation (nothing is moved).
sort_by_extracted_key Page
pub fn sort_by_extracted_key<T, K: Sortable>(
v: &mut [T],
order: Order,
key: impl FnMut(&T) -> K,
how: ObjectMove,
)
Sorts objects of any type by a numeric key you extract. Equal keys keep their input order.
Arguments
v: the objects; they need not beCloneorCopy.order: the direction and NaN placement, for exampleOrder::ASCENDING.key: returns an object's key; called once per object.how: how the objects move into place (seeObjectMove);ObjectMove::Autopicks.
Notes
- Objects are moved, never duplicated or dropped.
sort_with_indicesgives the sorting order alone.
sort_objects_by_key Page
pub fn sort_objects_by_key<T, K: Sortable>(v: &mut [T], order: Order, key: impl FnMut(&T) -> K)
Sorts objects of any type by a numeric key you extract. Equal keys keep their input order.
Arguments
v: the objects; they need not beCloneorCopy.order: the direction and NaN placement, for exampleOrder::ASCENDING.key: returns an object's key; called once per object.
Example
use kwker::Order;
let mut files = vec![("b.txt".to_string(), 300u64), ("a.txt".to_string(), 100)];
kwker::sort_objects_by_key(&mut files, Order::ASCENDING, |f| f.1);
assert_eq!(files[0].0, "a.txt");
sort_records_by_key Page
pub fn sort_records_by_key<T: Copy, K: Sortable>(
v: &mut [T],
order: Order,
key: impl FnMut(&T) -> K,
)
Sorts Copy records of any size by a numeric key you extract. Equal keys keep their input order.
Arguments
v: the records.order: the direction and NaN placement, for exampleOrder::ASCENDING.key: returns a record's key; called once per record.
sort_records_stable Page
pub fn sort_records_stable<K: Sortable, V: Copy>(r: &mut [KeyValue<K, V>])
sort_records that keeps records with equal keys in input order.
Arguments
r: the records.
Notes
- Uses buffers of about the input's size.
sort_records_stable_by_order Page
pub fn sort_records_stable_by_order<K: Sortable, V: Copy>(r: &mut [KeyValue<K, V>], order: Order)
sort_records_stable in the given order.
Arguments
r: the records.order: the direction and NaN placement, for exampleOrder::DESCENDING.
sort_records Page
pub fn sort_records<K: Sortable, V: Copy>(r: &mut [KeyValue<K, V>])
Sorts KeyValue records by key, smallest first.
Arguments
r: the records.
Example
use kwker::KeyValue;
let mut r = vec![KeyValue { key: 3, value: 'c' }, KeyValue { key: 1, value: 'a' }];
kwker::sort_records(&mut r);
assert_eq!(r[0].value, 'a');
Notes
- Not stable: records with equal keys may come out in any order (
sort_records_stablekeeps their order).
sort_records_by_order Page
pub fn sort_records_by_order<K: Sortable, V: Copy>(r: &mut [KeyValue<K, V>], order: Order)
sort_records in the given order.
Arguments
r: the records.order: the direction and NaN placement, for exampleOrder::DESCENDING.
select_nth_records Page
pub fn select_nth_records<K: Sortable, V: Copy>(r: &mut [KeyValue<K, V>], k: usize, order: Order)
select_nth_kv on records: the record a full sort would put at position k moves there, with records of
smaller or equal keys before it and the others after it.
Arguments
r: the records.k: the position to fill, belowr.len().order: the direction and NaN placement.
Panics
If k >= r.len().
partial_sort_records Page
pub fn partial_sort_records<K: Sortable, V: Copy>(r: &mut [KeyValue<K, V>], k: usize, order: Order)
partial_sort_kv on records: puts the k records with the first keys, sorted, at the front.
Arguments
r: the records.k: how many records to sort;k >= r.len()sorts them all.order: the direction and NaN placement.
ObjectMove Page
pub enum ObjectMove {
Auto,
Gather,
Cycles,
}
How sort_by_extracted_key moves the objects into their sorted places.
Variants
Auto:Gatherfor objects up to 128 bytes,Cyclesabove (no n x size buffer for large objects).Gather: Each object moved once into a buffer in sorted order, then the buffer moved back (n x size bytes of scratch).Cycles: The permutation's cycles followed with swaps in place (one bit of scratch per object).
Masked and nullable data
top_k_masked Page
pub fn top_k_masked<T: Sortable>(
v: &[T],
mask: &[bool],
k: usize,
order: Order,
sorted: bool,
) -> (Vec<T>, Vec<u64>)
The k first keys among the positions a mask selects, and their positions in v. No fill value or copy of the
other keys is needed.
Arguments
v: the keys (anySortabletype).mask: one flag per key;truekeys take part.k: how many keys.order: the direction and NaN placement, for exampleOrder::DESCENDINGfor the largest.sorted:truereturns them in order;falsein no particular order.
Returns
(values, positions). Equal keys go to the earlier position first.
Example
use kwker::Order;
let (top, at) = kwker::top_k_masked(&[5, 9, 7, 8], &[true, false, true, true], 2, Order::DESCENDING, true);
assert_eq!(top, [8, 7]);
assert_eq!(at, [3, 2]);
Panics
If mask.len() != v.len().
Examples: Top-k and selection: Top-k with a filter
top_k_mask_u8 Page
pub fn top_k_mask_u8<T: Sortable>(
v: &[T],
mask: &[u8],
k: usize,
order: Order,
sorted: bool,
) -> (Vec<T>, Vec<u64>)
top_k_masked with one byte per key (nonzero takes part), as NumPy and PyTorch store bool arrays.
Arguments
v,k,order,sorted: as intop_k_masked.mask: one byte per key.
top_k_valid Page
pub fn top_k_valid<T: Sortable>(
v: &[T],
validity: &[u8],
k: usize,
order: Order,
sorted: bool,
) -> (Vec<T>, Vec<u64>)
top_k_masked with an Arrow-style validity bitmap (bit set = takes part).
Arguments
v,k,order,sorted: as intop_k_masked.validity: one bit per key, least significant bit first.
argsort_masked Page
pub fn argsort_masked<T: Sortable>(v: &[T], mask: &[bool], order: Order) -> Vec<u64>
The positions that sort the keys a mask selects, without filling the others with a sentinel or copying the array. Equal keys keep their input order.
Arguments
v: the keys (anySortabletype).mask: one flag per key;truekeys take part.order: the direction and NaN placement, for exampleOrder::ASCENDING.
Returns
One position (into v) per selected key.
argsort_mask_u8 Page
pub fn argsort_mask_u8<T: Sortable>(v: &[T], mask: &[u8], order: Order) -> Vec<u64>
argsort_masked with one byte per key (nonzero takes part).
Arguments
v,order: as inargsort_masked.mask: one byte per key.
argsort_valid Page
pub fn argsort_valid<T: Sortable>(v: &[T], validity: &[u8], order: Order) -> Vec<u64>
argsort_masked with an Arrow-style validity bitmap (bit set = takes part).
Arguments
v,order: as inargsort_masked.validity: one bit per key, least significant bit first.
argsort_where Page
pub fn argsort_where<T: Sortable>(v: &[T], order: Order, pred: impl Fn(&T) -> bool) -> Vec<u64>
argsort_masked of the keys for which pred returns true.
Arguments
v,order: as inargsort_masked.pred: the test for taking part.
sort_masked Page
pub fn sort_masked<T: Sortable>(v: &mut [T], mask: &[bool], order: Order)
Sorts the keys at the positions a mask selects among those positions; the other positions are left as they are.
Arguments
v: the keys (anySortabletype).mask: one flag per key;truepositions take part.order: the direction and NaN placement, for exampleOrder::ASCENDING.
Example
use kwker::Order;
let mut v = vec![9, 0, 3, 0, 1];
kwker::sort_masked(&mut v, &[true, false, true, false, true], Order::ASCENDING);
assert_eq!(v, [1, 0, 3, 0, 9]);
sort_mask_u8 Page
pub fn sort_mask_u8<T: Sortable>(v: &mut [T], mask: &[u8], order: Order)
sort_masked with one byte per key (nonzero takes part).
Arguments
v,order: as insort_masked.mask: one byte per key.
sort_valid Page
pub fn sort_valid<T: Sortable>(v: &mut [T], validity: &[u8], order: Order)
sort_masked with an Arrow-style validity bitmap (bit set = takes part).
Arguments
v,order: as insort_masked.validity: one bit per key, least significant bit first.
sort_where Page
pub fn sort_where<T: Sortable>(v: &mut [T], order: Order, pred: impl Fn(&T) -> bool)
sort_masked of the keys for which pred returns true.
Arguments
v,order: as insort_masked.pred: the test for taking part; called once per key, before the sort.
top_k_by_group Page
pub fn top_k_by_group<T: Sortable, G: Sortable>(
v: &[T],
groups: &[G],
k: usize,
order: Order,
) -> (Vec<G>, Vec<usize>, Vec<u64>)
The k first keys of every group, for group labels in any order: SQL
ROW_NUMBER() OVER (PARTITION BY g ORDER BY v) <= k.
Arguments
v: the keys (anySortabletype).groups: one group label per key, in any order.k: keys per group.order: the direction and NaN placement, for exampleOrder::DESCENDINGfor the largest.
Returns
(labels, offsets, positions): the distinct labels, smallest first; group i's results are
positions[offsets[i]..offsets[i + 1]], positions into v in order, at most k per group.
Example
use kwker::Order;
let (labels, offsets, at) = kwker::top_k_by_group(&[5, 9, 7, 8], &[1, 0, 1, 0], 1, Order::DESCENDING);
assert_eq!(labels, [0, 1]);
assert_eq!(offsets, [0, 1, 2]);
assert_eq!(at, [1, 2]);
Examples: Top-k and selection: Top-k per group
Group-by
group_codes Page
pub fn group_codes(keys: &[(&dyn KeyColumn, Order)], threads: usize) -> Groups
Numbers the groups of equal rows across one or more key columns, like pandas groupby or SQL GROUP BY. Group
g is the g-th distinct key tuple in sorted order. Pass the codes to the group_* reductions.
Arguments
keys: the key columns (slices or vectors of anySortabletype) with their orders, most significant first, as incrate::lexsort.threads: the number of threads; 1 uses the calling thread only.
Returns
A Groups: each row's group code, and each group's first row and size.
Example
use kwker::{GroupRows, Order};
let city = vec![2, 1, 2];
let sales = vec![10.0, 5.0, 2.5];
let g = kwker::group_codes(&[(&city, Order::ASCENDING)], 1);
assert_eq!(g.codes, [1, 0, 1]);
let (sum, count) = kwker::group_sum(&g.codes, g.len(), &sales, GroupRows { valid: None, skip_nan: false }, 1);
assert_eq!(sum, [5.0, 12.5]);
assert_eq!(count, [1, 2]);
Panics
If the columns have different lengths, or 2^32 rows or more.
Notes
- Keys group by value, as pandas and Polars do:
-0.0and+0.0are one group, and all NaNs are one group.
Examples: Groups, merges and sets: Group several key columns: group_codes
group_codes_into Page
pub fn group_codes_into(
keys: &[(&dyn KeyColumn, Order)],
threads: usize,
codes: &mut [u32],
first: &mut [u64],
sizes: &mut [u64],
) -> usize
group_codes into buffers you provide.
Arguments
keys,threads: as ingroup_codes.codes: receives each row's group code; its length is the number of rows.first,sizes: as long ascodes; receive each group's first row and size in their firstgroupsentries.
Returns
The number of groups.
group_sum Page
pub fn group_sum<V: GroupValue>(
codes: &[u32],
groups: usize,
values: &[V],
rows: GroupRows<'_>,
threads: usize,
) -> (Vec<V::Sum>, Vec<u64>)
The sum and the number of values in every group.
Arguments
codes: each row's group, belowgroupsorNO_GROUP(fromgroup_codes).groups: the number of groups.values: one value per row (any integer or float type).rows: which rows have a value (seeGroupRows).threads: the number of threads; 1 uses the calling thread only.
Returns
(sums, counts), one entry per group. Sums are f64 for floats, i64 or u64 (wrapping) for integers.
Notes
- A NaN value that is not skipped makes its group's sum NaN.
group_count Page
pub fn group_count<V: GroupValue>(
codes: &[u32],
groups: usize,
values: &[V],
rows: GroupRows<'_>,
threads: usize,
) -> Vec<u64>
The number of rows with a value in every group.
Arguments
codes,groups,values,rows,threads: as ingroup_sum.
group_min_max Page
pub fn group_min_max<V: GroupValue>(
codes: &[u32],
groups: usize,
values: &[V],
rows: GroupRows<'_>,
max: bool,
threads: usize,
) -> (Vec<V>, Vec<u64>)
The smallest or largest value in every group, and the number of values.
Arguments
codes,groups,values,rows,threads: as ingroup_sum.max:falsefor the smallest,truefor the largest.
Returns
(values, counts), one entry per group; the value of a group with count 0 is unspecified.
Notes
- NaN is passed over unless all of a group's values are NaN, as in Polars and Arrow.
group_end_rows Page
pub fn group_end_rows<V: GroupValue>(
codes: &[u32],
groups: usize,
values: &[V],
rows: GroupRows<'_>,
last: bool,
threads: usize,
) -> Vec<u64>
The first or last row with a value in every group.
Arguments
codes,groups,values,rows,threads: as ingroup_sum.last:falsefor the first row,truefor the last.
Returns
One row number per group; NO_ROW for a group without a value.
group_median Page
pub fn group_median<V: GroupValue>(
codes: &[u32],
groups: usize,
values: &[V],
rows: GroupRows<'_>,
threads: usize,
) -> (Vec<f64>, Vec<u64>)
The exact median of every group: the middle value, or the mean of the two middle values.
Arguments
codes,groups,values,rows,threads: as ingroup_sum.
Returns
(medians, counts), one entry per group; a group without values has median NaN.
group_quantile Page
pub fn group_quantile<V: GroupValue>(
codes: &[u32],
groups: usize,
values: &[V],
rows: GroupRows<'_>,
q: f64,
interp: Interpolation,
threads: usize,
) -> (Vec<f64>, Vec<u64>)
The exact q quantile of every group, like pandas groupby().quantile(q, interpolation).
Arguments
codes,groups,values,rows,threads: as ingroup_sum.q: the quantile, in[0, 1].interp: the interpolation rule (seeInterpolation).
Returns
(quantiles, counts), one entry per group; a group without values gives NaN.
Panics
If q is outside [0, 1].
Notes
- Matches pandas bit for bit with
rows.skip_nanset.
group_cumsum Page
pub fn group_cumsum<V: GroupValue>(
codes: &[u32],
groups: usize,
values: &[V],
rows: GroupRows<'_>,
out: &mut [V::Sum],
)
Each row's running sum within its group, in row order, like pandas groupby().cumsum().
Arguments
codes,groups,values,rows: as ingroup_sum.out: receives one running sum per row (f64for floats,i64oru64for integers).
Panics
If out.len() != codes.len().
Notes
- A row without a group or a value gets NaN (integers: 0) and leaves its group's sum unchanged.
group_shift Page
pub fn group_shift<V: GroupValue>(
codes: &[u32],
groups: usize,
values: &[V],
periods: i64,
out: &mut [f64],
)
Each row gets the value periods rows earlier in its group, like pandas groupby().shift(periods).
Arguments
codes,groups,values: as ingroup_sum.periods: how many rows back; negative looks ahead.out: receives onef64per row; NaN where there is no such row.
Panics
If out.len() != codes.len().
group_diff Page
pub fn group_diff<V: GroupValue>(
codes: &[u32],
groups: usize,
values: &[V],
periods: i64,
out: &mut [f64],
)
Each row's value minus the value periods rows earlier in its group, like pandas groupby().diff(periods).
Arguments
codes,groups,values: as ingroup_sum.periods: how many rows back; negative looks ahead.out: receives onef64per row; NaN where there is no such row.
Panics
If out.len() != codes.len().
group_cumcount Page
pub fn group_cumcount(codes: &[u32], groups: usize, ascending: bool, out: &mut [u64])
Each row's position within its group, from 0, like pandas groupby().cumcount().
Arguments
codes: each row's group, belowgroupsorNO_GROUP.groups: the number of groups.ascending:truecounts from the group's first row;falsefrom its last.out: receives one position per row;u64::MAXfor rows without a group.
Panics
If out.len() != codes.len() or a code is out of range.
group_rank Page
pub fn group_rank<V: GroupValue + Sortable>(
codes: &[u32],
groups: usize,
values: &[V],
rows: GroupRows<'_>,
method: GroupRankMethod,
pct: bool,
order: Order,
out: &mut [f64],
)
Each row's rank within its group, from 1, like pandas groupby().rank(method, ascending, pct).
Arguments
codes,groups,values,rows: as ingroup_sum.method: how equal values are ranked (seeGroupRankMethod).pct:truedivides each rank by its group's number of values.order:Order::ASCENDINGranks the smallest first;Order::DESCENDINGthe largest.out: receives onef64rank per row; NaN for rows without a value or a group.
Panics
If out.len() != codes.len().
Notes
- Matches pandas bit for bit;
-0.0equals+0.0.
group_stats Page
pub fn group_stats<V: GroupValue>(
codes: &[u32],
groups: usize,
values: &[V],
rows: GroupRows<'_>,
threads: usize,
) -> GroupStats<V>
The sum, count, smallest and largest value of every group, in one pass over the data.
Arguments
codes,groups,values,rows,threads: as ingroup_sum.
Returns
A GroupStats with one entry per group in each field.
run_lengths Page
pub fn run_lengths<T: Sortable>(v: &[T]) -> (Vec<T>, Vec<usize>)
Run-length encoding: each run of equal neighbouring keys and its length.
Arguments
v: the keys (anySortabletype).
Returns
(keys, lengths), one entry per run.
Example
let (keys, lens) = kwker::run_lengths(&[7, 7, 3, 7]);
assert_eq!(keys, [7, 3, 7]);
assert_eq!(lens, [2, 1, 1]);
group_boundaries Page
pub fn group_boundaries<T: Sortable>(sorted: &[T]) -> Vec<usize>
Where each run of equal keys starts in a sorted slice.
Arguments
sorted: the keys, sorted (anySortabletype).
Returns
The start of every run, then sorted.len(): group g is sorted[b[g]..b[g + 1]]. Empty for an empty slice.
unique_counts Page
pub fn unique_counts<T: Sortable>(v: &mut [T], order: Order) -> (Vec<T>, Vec<usize>)
Sorts v, then returns each distinct key and how often it occurs: a frequency table.
Arguments
v: the keys (anySortabletype); sorted in place.order: the direction and NaN placement, for exampleOrder::ASCENDING.
Returns
(keys, counts), the keys in order.
sort_unique Page
pub fn sort_unique<T: Sortable>(v: &mut [T], order: Order) -> usize
Sorts v and moves its distinct keys, in order, to the front.
Arguments
v: the keys (anySortabletype).order: the direction and NaN placement, for exampleOrder::ASCENDING.
Returns
The number of distinct keys, m; they are in v[..m].
Example
use kwker::Order;
let mut v = vec![3, 1, 3, 2, 1];
let m = kwker::sort_unique(&mut v, Order::ASCENDING);
assert_eq!(v[..m], [1, 2, 3]);
group_by_key Page
pub fn group_by_key<K: Sortable, V: Copy>(
keys: &mut [K],
values: &mut [V],
order: Order,
) -> Vec<usize>
Groups key-value pairs by key: sorts them by key (values of equal keys keep their input order) and returns where each group starts.
Arguments
keys: the keys (anySortabletype); sorted in place.values: one value per key, of anyCopytype; moved with the keys.order: the direction and NaN placement, for exampleOrder::ASCENDING.
Returns
The group boundaries, as group_boundaries gives them.
reduce_by_key Page
pub fn reduce_by_key<K: Sortable, V: Reducible>(
keys: &[K],
values: &[V],
op: Reduction,
order: Order,
) -> (Vec<K>, Vec<V>)
Aggregates the values of each distinct key: a group-by with sum, min, max, first or last.
Arguments
keys: one key per value (anySortabletype); not changed.values: the values (integers or floats).op: the aggregation (seeReduction).order: the order of the result's keys, for exampleOrder::ASCENDING.
Returns
(keys, results): each distinct key and its group's result.
Example
use kwker::{Order, Reduction};
let (keys, totals) = kwker::reduce_by_key(&[2, 1, 2], &[10, 5, 3], Reduction::Sum, Order::ASCENDING);
assert_eq!(keys, [1, 2]);
assert_eq!(totals, [5, 13]);
Panics
If keys and values have different lengths.
Notes
- Float sums add in input order; first and last are by input order.
Examples: Groups, merges and sets: Totals per key: reduce_by_key
mean_by_key Page
pub fn mean_by_key<K: Sortable, V: Reducible>(
keys: &[K],
values: &[V],
order: Order,
) -> (Vec<K>, Vec<f64>)
The mean of the values of each distinct key.
Arguments
keys: one key per value (anySortabletype).values: the values (integers or floats).order: the order of the result's keys.
Returns
(keys, means); each mean sums its values as f64 in input order.
count_by_key Page
pub fn count_by_key<K: Sortable>(keys: &[K], order: Order) -> (Vec<K>, Vec<u64>)
How often each distinct key occurs, without changing keys.
Arguments
keys: the keys (anySortabletype).order: the order of the result's keys.
Returns
(keys, counts).
Examples: Groups, merges and sets: Totals per key: reduce_by_key
reduce_by_key_with Page
pub fn reduce_by_key_with<K: Sortable, V: Copy, A>(
keys: &[K],
values: &[V],
order: Order,
init: impl FnMut(&V) -> A,
fold: impl FnMut(&mut A, &V),
) -> (Vec<K>, Vec<A>)
Your own aggregation per distinct key: init starts a group from its first value, fold adds the next ones, in
input order.
Arguments
keys: one key per value (anySortabletype).values: the values, of anyCopytype; a struct or tuple carries several columns.order: the order of the result's keys.init: makes a group's starting accumulator from its first value.fold: adds a value to an accumulator.
Returns
(keys, accumulators).
Example
use kwker::Order;
// the largest value and the count, per key
let (keys, acc) = kwker::reduce_by_key_with(&[1, 2, 1], &[4, 9, 6], Order::ASCENDING,
|&v| (v, 1), |a, &v| { a.0 = a.0.max(v); a.1 += 1; });
assert_eq!(keys, [1, 2]);
assert_eq!(acc, [(6, 2), (9, 1)]);
NO_GROUP Page
pub const NO_GROUP: u32 = u32::MAX;
The group code of a row that belongs to no group; reductions skip such rows.
NO_ROW Page
pub const NO_ROW: u64 = u64::MAX;
The row number group_end_rows gives a group without a row that has a value.
Groups Page
pub struct Groups {
pub codes: Vec<u32>,
pub first: Vec<u64>,
pub sizes: Vec<u64>,
}
The groups of a table's rows, from group_codes.
Fields
codes: Per row, its group: 0..len, the groups in the key columns' lexicographic order.first: Per group, its first row.sizes: Per group, its number of rows.
Groups::len Page
pub fn len(&self) -> usize
The number of groups.
Groups::is_empty Page
pub fn is_empty(&self) -> bool
No rows, no groups.
GroupValue Page
pub trait GroupValue: Copy + Send + Sync + PartialOrd + 'static {
type Sum;
}
The value types the group reductions accept: the integer and float types.
GroupRows Page
pub struct GroupRows<'a> {
pub valid: Option<&'a [u8]>,
pub skip_nan: bool,
}
Which rows a group reduction reads.
Example
use kwker::GroupRows;
// every row has a value, and NaN counts as no value (as pandas does)
let rows = GroupRows { valid: None, skip_nan: true };
Fields
valid: Per row: nonzero = the row has a value (None: every row has one).skip_nan: NaN counts as no value (pandas); else NaN is a value (Polars, Arrow).
GroupRankMethod Page
pub enum GroupRankMethod {
Average,
Min,
Max,
First,
Dense,
}
How group_rank ranks equal values, as pandas groupby().rank(method=...).
Variants
Average: The mean of the ranks equal values takeMin: The lowest of themMax: The highest of themFirst: Each value its own rank, equal values in row order (pandas "first")Dense: One rank per distinct value, no gaps
GroupStats Page
pub struct GroupStats<V: GroupValue> {
pub sum: Vec<V::Sum>,
pub count: Vec<u64>,
pub numbers: Vec<u64>,
pub min: Vec<V>,
pub max: Vec<V>,
}
The results of group_stats, one entry per group in each field.
Fields
sum: Sums of the values (floats: NaN when a NaN value is not skipped).count: Counts of the values.numbers: Counts of the values that are not NaN.min: Smallest values (NaN for a group of NaNs only; unspecified for a group without values).max: Largest values (as min).
Reduction Page
pub enum Reduction {
Sum,
Min,
Max,
First,
Last,
}
The built-in aggregations of reduce_by_key.
Variants
Sum: The sum (integers wrap on overflow; floats summed left to right in input order).Min: The smallest value in the ascending order ofcrate::sort(-0.0 below +0.0, NaNs above everything).Max: The largest value in that order.First: The group's first value in input order.Last: The group's last value in input order.
Reducible Page
pub trait Reducible: Sortable { }
The value types reduce_by_key accepts: the integer and float types.
Rolling windows
median_filter3x3 Page
pub fn median_filter3x3<T: WindowKey>(
src: &[T],
planes: usize,
h: usize,
w: usize,
pad: WindowPad,
out: &mut [T],
)
A 3 x 3 median filter over images or other 2-D planes, like kornia.filters.median_blur: each output value is
the median of the 3 x 3 window around the input value.
Arguments
src:planesplanes ofhxwvalues, row-major, one plane after another.planes: the number of planes (for example images x channels).h,w: the height and width of a plane.pad: what the windows see past the edges (seeWindowPad).out: receives planes ofhxw, or(h - 2)x(w - 2)withWindowPad::Valid.
Example
use kwker::{median_filter3x3, WindowPad};
let img = [1u8, 9, 2, 8, 3, 7, 4, 6, 5];
let mut out = [0u8; 9];
median_filter3x3(&img, 1, 3, 3, WindowPad::Replicate, &mut out);
assert_eq!(out[4], 5);
Notes
- A window holding a NaN gives its first NaN, as
torch.mediandoes.
median_filter3x3_backward Page
pub fn median_filter3x3_backward<T: WindowGrad>(
src: &[T],
out: &[T],
grad_out: &[T],
planes: usize,
h: usize,
w: usize,
pad: WindowPad,
grad_in: &mut [T],
)
The gradient of median_filter3x3: each output's gradient goes to the input value its median came from, as
with the index torch.median returns.
Arguments
src: the forward pass's input.out: the forward pass's output.grad_out: the gradient of the output, in its layout.planes,h,w,pad: as in the forward pass.grad_in: receives the gradient of the input (planesplanes ofhxw, overwritten).
median_filter5x5 Page
pub fn median_filter5x5<T: WindowKey>(
src: &[T],
planes: usize,
h: usize,
w: usize,
pad: WindowPad,
out: &mut [T],
)
A 5 x 5 median filter, as median_filter3x3.
Arguments
src,planes,h,w,pad: as inmedian_filter3x3.out: receives planes ofhxw, or(h - 4)x(w - 4)withWindowPad::Valid.
Example
use kwker::{median_filter5x5, WindowPad};
let img: Vec<u8> = (0..25).map(|i| (i * 7 % 25) as u8).collect();
let mut out = vec![0u8; 25];
median_filter5x5(&img, 1, 5, 5, WindowPad::Replicate, &mut out);
assert_eq!(out[12], 12);
Notes
WindowPad::Reflectneeds planes of at least 3 x 3;WindowPad::Validat least 5 x 5.
median_filter5x5_backward Page
pub fn median_filter5x5_backward<T: WindowGrad>(
src: &[T],
out: &[T],
grad_out: &[T],
planes: usize,
h: usize,
w: usize,
pad: WindowPad,
grad_in: &mut [T],
)
The gradient of median_filter5x5, as median_filter3x3_backward.
Arguments
src,out,grad_out,planes,h,w,pad,grad_in: as inmedian_filter3x3_backward.
median_filter7x7 Page
pub fn median_filter7x7<T: WindowKey>(
src: &[T],
planes: usize,
h: usize,
w: usize,
pad: WindowPad,
out: &mut [T],
)
A 7 x 7 median filter, as median_filter3x3.
Arguments
src,planes,h,w,pad: as inmedian_filter3x3.out: receives planes ofhxw, or(h - 6)x(w - 6)withWindowPad::Valid.
Example
use kwker::{median_filter7x7, WindowPad};
let img: Vec<u8> = (0..49).map(|i| (i * 10 % 49) as u8).collect();
let mut out = vec![0u8; 49];
median_filter7x7(&img, 1, 7, 7, WindowPad::Replicate, &mut out);
assert_eq!(out[24], 24);
Notes
WindowPad::Reflectneeds planes of at least 4 x 4;WindowPad::Validat least 7 x 7.
median_filter7x7_backward Page
pub fn median_filter7x7_backward<T: WindowGrad>(
src: &[T],
out: &[T],
grad_out: &[T],
planes: usize,
h: usize,
w: usize,
pad: WindowPad,
grad_in: &mut [T],
)
The gradient of median_filter7x7, as median_filter3x3_backward.
Arguments
src,out,grad_out,planes,h,w,pad,grad_in: as inmedian_filter3x3_backward.
rolling_quantile Page
pub fn rolling_quantile<T: Reducible>(
src: &[T],
window: usize,
q: f64,
interp: Interpolation,
out: &mut [f64],
)
A rolling quantile: out[i] is the q quantile of the last window values up to src[i], like pandas
Series.rolling(window).quantile(q).
Arguments
src: the values (integers or floats).window: the window length, at least 1.q: the quantile, in[0, 1].interp: the interpolation rule;crate::Interpolation::Linearmatches pandas bit for bit.out: receives onef64per value.
Panics
If window is 0, q is outside [0, 1], or out.len() != src.len().
Notes
- The first
window - 1outputs are NaN, and so is the output of every window holding a NaN, as in pandas.
Examples: Top-k and selection: Medians and quantiles over a sliding window
rolling_median Page
pub fn rolling_median<T: Reducible>(src: &[T], window: usize, out: &mut [f64])
A rolling median, like pandas Series.rolling(window).median() and bottleneck's move_median. For an even
window, the mean of the two middle values.
Arguments
src: the values (integers or floats).window: the window length, at least 1.out: receives onef64per value; the firstwindow - 1are NaN.
Example
let mut out = vec![0.0; 5];
kwker::rolling_median(&[1, 9, 2, 8, 3], 3, &mut out);
assert!(out[0].is_nan() && out[1].is_nan());
assert_eq!(out[2..], [2.0, 8.0, 3.0]);
Examples: Top-k and selection: Medians and quantiles over a sliding window
rolling_mad Page
pub fn rolling_mad<T: Reducible>(src: &[T], window: usize, out: &mut [f64])
A rolling median absolute deviation: for the last window values, the median of |x - m|, m being their
median. Robust outlier filters (Hampel filters) use it.
Arguments
src: the values (integers or floats).window: the window length, at least 1.out: receives onef64per value; the firstwindow - 1are NaN.
Panics
If window is 0 or out.len() != src.len().
Notes
- Unscaled: multiply by 1.4826 for the scale of a normal distribution.
- Matches
np.median(np.abs(v - np.median(v)))per window, bit for bit on float64 values; a window holding a NaN gives NaN.
Examples: Top-k and selection: Medians and quantiles over a sliding window
expanding_quantile Page
pub fn expanding_quantile<T: Reducible>(src: &[T], q: f64, interp: Interpolation, out: &mut [f64])
An expanding quantile: out[i] is the q quantile of all values up to src[i], like pandas
Series.expanding().quantile(q).
Arguments
src: the values (integers or floats).q: the quantile, in[0, 1].interp: the interpolation rule;crate::Interpolation::Linearmatches pandas bit for bit.out: receives onef64per value.
Panics
If q is outside [0, 1], out.len() != src.len(), or the length does not fit u32.
Notes
- NaN values are skipped; the output is NaN until the first value that is not NaN.
expanding_median Page
pub fn expanding_median<T: Reducible>(src: &[T], out: &mut [f64])
An expanding median, like pandas Series.expanding().median(): for an even count, the mean of the two middle
values.
Arguments
src: the values (integers or floats).out: receives onef64per value.
rank_filter Page
pub fn rank_filter<T: WindowKey>(
src: &[T],
planes: usize,
h: usize,
w: usize,
k: usize,
rank: usize,
out: &mut [T],
)
A k x k rank filter over valid windows (k 3, 5 or 7): every output is the key of rank rank (0 = the smallest, k *
k - 1 the largest) among the k x k input keys of its window - minimum, percentile, median and maximum filters of
images.
Arguments
src:planesplanes ofhxwkeys, row-major.k: the window side: 3, 5 or 7 (handwat leastk).rank: the rank in the window, belowk * k.out: receivesplanesplanes of(h - k + 1)x(w - k + 1)keys; output (y, x) is the window whose top-left key is input (y, x).
Example
let img: Vec<u8> = (0..16).collect();
let mut out = vec![0u8; 4];
kwker::rank_filter(&img, 1, 4, 4, 3, 0, &mut out); // the minimum of every 3 x 3 window
assert_eq!(out, [0, 1, 4, 5]);
Notes
- Floats: keys equal in value (-0.0 and +0.0) may come out as either; a window holding a NaN gives an unspecified key.
Panics
If k is not 3, 5 or 7, rank >= k * k, a plane is smaller than k x k, or a slice length does not match.
WindowPad Page
pub enum WindowPad {
Reflect,
Replicate,
Zeros,
Valid,
}
What the median filters see past the edges of a plane.
Variants
Reflect: Mirror without repeating the edge (-1 -> 1,h -> h - 2;F.pad(mode="reflect")); planes of at least 2 x 2.Replicate: Repeat the edge (-1 -> 0;F.pad(mode="replicate")).Zeros: Zeros outside the plane (F.padwith a constant 0,kornia.filters.median_blur).Valid: No padding: output planes of (h - 2) x (w - 2); planes of at least 3 x 3.
WindowKey Page
pub trait WindowKey: Copy + PartialOrd + Default + Send + Sync + Sealed + 'static { }
The value types of the median filters: the 8- to 64-bit integers, f32 and f64.
WindowGrad Page
pub trait WindowGrad: WindowKey + Add<Output = Self> { }
The value types of the median filters' gradients: f32 and f64.
Statistics
cdf_distance_f64 Page
pub fn cdf_distance_f64(a: &[f64], b: &[f64], kind: CdfDistance) -> f64
How far apart two samples' distributions are: SciPy's two-sample Kolmogorov-Smirnov statistic,
wasserstein_distance or energy_distance (unweighted).
Arguments
a,b: the two samples, in any order; not changed.kind: which distance (seeCdfDistance).
Returns
The distance; NaN if a sample is empty or holds a NaN.
Notes
- Sums run in float64 in value order; SciPy's pairwise sums can differ in the last bits.
cdf_distance_f32 Page
pub fn cdf_distance_f32(a: &[f32], b: &[f32], kind: CdfDistance) -> f64
How far apart two samples' distributions are: SciPy's two-sample Kolmogorov-Smirnov statistic,
wasserstein_distance or energy_distance (unweighted).
Arguments
a,b: the two samples, in any order; not changed.kind: which distance (seeCdfDistance).
Returns
The distance; NaN if a sample is empty or holds a NaN.
Notes
- Sums run in float64 in value order; SciPy's pairwise sums can differ in the last bits.
histogram_f32 Page
pub fn histogram_f32(x: &[f32], lo: f64, hi: f64, out: &mut [u64])
Counts values in equal-width bins, like np.histogram(x, bins, range=(lo, hi)), bin for bin.
Arguments
x: the values.lo,hi: the range the bins cover; the last bin includeshi.out: receives one count per bin; its length is the number of bins.
Panics
If out is empty, or lo or hi is not finite, or lo > hi.
Notes
- Values outside the range and NaNs are not counted. With
lo == hithe range widens by 0.5 on each side, as in NumPy.
histogram_f64 Page
pub fn histogram_f64(x: &[f64], lo: f64, hi: f64, out: &mut [u64])
Counts values in equal-width bins, like np.histogram(x, bins, range=(lo, hi)), bin for bin.
Arguments
x: the values.lo,hi: the range the bins cover; the last bin includeshi.out: receives one count per bin; its length is the number of bins.
Panics
If out is empty, or lo or hi is not finite, or lo > hi.
Notes
- Values outside the range and NaNs are not counted. With
lo == hithe range widens by 0.5 on each side, as in NumPy.
average_precision Page
pub fn average_precision<T: Sortable>(scores: &[T], positive: &[u8]) -> f64
The average precision of a binary classifier's scores, like scikit-learn's average_precision_score.
Arguments
scores: one score per example (anySortabletype); higher means more likely positive.positive: one label per example; nonzero means positive.
Returns
The average precision; NaN if a score is NaN, 0.0 if no example is positive.
Panics
If positive.len() != scores.len().
roc_auc Page
pub fn roc_auc<T: Sortable>(scores: &[T], positive: &[u8]) -> f64
The area under the ROC curve of a binary classifier's scores, like scikit-learn's roc_auc_score: the chance
that a random positive scores above a random negative, ties counting half.
Arguments
scores: one score per example (anySortabletype).positive: one label per example; nonzero means positive.
Returns
The area, between 0 and 1; NaN if a score is NaN or one class is missing.
Example
let auc = kwker::roc_auc(&[0.9f32, 0.2, 0.7, 0.4], &[1, 0, 1, 0]);
assert_eq!(auc, 1.0);
Panics
If positive.len() != scores.len().
trim_mean Page
pub fn trim_mean<T: Reducible>(
data: &[T],
shape: &[usize],
axis: usize,
proportion: f64,
out: &mut [f64],
)
The trimmed mean of every lane along axis, like scipy.stats.trim_mean: the mean without the
floor(proportion * n) smallest and as many largest values.
Arguments
data: a row-major array (integers or floats).shape: its dimensions; their product must bedata.len().axis: the dimension to average along.proportion: the share to cut from each end, from 0 to 0.5.out: receives one mean per lane, in row-major order of the other dimensions.
Example
let mut out = [0.0];
kwker::trim_mean(&[1, 2, 3, 4, 100], &[5], 0, 0.2, &mut out);
assert_eq!(out[0], 3.0);
Panics
If the shape does not match, axis is out of range, out does not hold one value per lane, or proportion
cuts more than all values.
Notes
- NaN sorts above every number, so a lane keeps a NaN in its mean only when it holds more NaNs than are cut.
- Sums run in float64; SciPy sums in the value type, so float32 results can differ in the last bits.
weighted_quantile Page
pub fn weighted_quantile<T: Reducible, W: Reducible>(a: &[T], w: &[W], qs: &[f64], out: &mut [T])
Weighted quantiles, like NumPy's np.quantile(a, qs, weights=w, method="inverted_cdf"), value for value.
Arguments
a: the values (integers or floats); not empty.w: one weight per value, not negative, with a positive total.qs: the quantiles, each in[0, 1].out: receives one value per quantile.
Panics
If a is empty, w.len() != a.len(), or out.len() != qs.len().
Notes
- A NaN in
amakes every result NaN, as in NumPy.
CdfDistance Page
pub enum CdfDistance {
KolmogorovSmirnov,
Wasserstein,
Energy,
}
Which distance cdf_distance_f64 and cdf_distance_f32 compute.
Variants
KolmogorovSmirnov: sup |F_a - F_b| (the two-sample Kolmogorov-Smirnov statistic).Wasserstein: The integral of |F_a - F_b| (the 1-D Wasserstein-1 / earth mover's distance).Energy: sqrt(2) x the square root of the integral of (F_a - F_b)^2 (the energy distance, SciPy's convention).
CdfDistance::terms_f64 Page
pub fn terms_f64(a: &[f64], b: &[f64], squared: bool, terms: &mut [f64], deltas: &mut [f64]) -> bool
The two arrays SciPy's _cdf_distance reduces, for the pooled sorted values all of both samples: the CDF
difference at each value but the last (|F_a - F_b|, or its square) and the gap to the next value - the
same float64 operations as SciPy (searchsorted(side="right") / size, np.diff), so the caller's reduction of
the two arrays gives SciPy's result bit for bit.
Arguments
a,b: the two samples, in any order; not changed.squared: false gives|F_a - F_b|(Wasserstein, general p), true(F_a - F_b)^2(energy).terms,deltas: receivea.len() + b.len() - 1values each.
Returns
false (nothing written) when a sample is empty or holds a NaN, or an output has another length.
Language-model sampling
top_n_sigma_rows Page
pub fn top_n_sigma_rows(x: &[f32], cols: usize, n: f32, fill: f32, out: &mut [f32])
Top-n-sigma sampling filter: in each row of logits, every logit below max - n * std becomes fill.
Arguments
x: the logits, row-major,colsper row.cols: the number of logits in a row (the vocabulary size).n: how many standard deviations below the maximum to keep.fill: the value for removed logits, usuallyf32::NEG_INFINITY.out: receives the filtered logits; as long asx.
Notes
- The standard deviation is the sample one (
n - 1in the denominator, astorch.std), from float64 sums. - A row holding a NaN keeps every logit.
min_p_rows Page
pub fn min_p_rows(x: &[f32], cols: usize, p: f32, fill: f32, out: &mut [f32])
Min-p sampling filter: in each row of logits, every logit whose probability is below p times the top
probability becomes fill.
Arguments
x: the logits, row-major,colsper row.cols: the number of logits in a row.p: the share of the top probability to keep, in(0, 1].fill: the value for removed logits, usuallyf32::NEG_INFINITY.out: receives the filtered logits; as long asx.
top_k_filter_rows Page
pub fn top_k_filter_rows(x: &[f32], cols: usize, ks: &[u64], fill: f32, out: &mut [f32])
Top-k sampling filter with a k per row, like vLLM's apply_top_k_only: in each row, every logit below the
row's k-th largest becomes fill.
Arguments
x: the logits, row-major,colsper row.cols: the number of logits in a row.ks: each row'sk; 0 orcolsand more keep the row whole.fill: the value for removed logits, usuallyf32::NEG_INFINITY.out: receives the filtered logits; as long asx.
Example
let mut out = [0.0f32; 4];
kwker::top_k_filter_rows(&[1.0, 4.0, 3.0, 2.0], 4, &[2], f32::NEG_INFINITY, &mut out);
assert_eq!(out, [f32::NEG_INFINITY, 4.0, 3.0, f32::NEG_INFINITY]);
Notes
- Logits equal to the
k-th largest stay, so ties can keep more thank. NaN counts as the largest value.
top_p_rows Page
pub fn top_p_rows<T: Logit>(
logits: &[T],
row_len: usize,
p: f64,
ids: &mut Vec<u32>,
ends: &mut Vec<usize>,
)
Nucleus (top-p) sampling: for each row of logits, the tokens with the highest probabilities whose total first
reaches p, as Hugging Face and vLLM choose them.
Arguments
logits: the logits, row-major,row_lenper row (f32orf64).row_len: the number of logits in a row (the vocabulary size).p: the probability mass to keep, above 0; 1 or more keeps every token with a nonzero probability.ids: receives each row's kept token positions, most probable first, appended row after row.ends: receives the end of each row's tokens inids.
Example
let (mut ids, mut ends) = (Vec::new(), Vec::new());
kwker::top_p_rows(&[0.0f32, 5.0, 4.0, -3.0], 4, 0.9, &mut ids, &mut ends);
assert_eq!(ids, [1, 2]);
assert_eq!(ends, [2]);
Panics
If row_len is 0 or does not divide logits.len(), row_len is 2^32 or more, or p is not above 0.
Notes
- Probabilities are computed in float64; a token right at the cut-off may fall either way, as between float32 implementations. NaN logits are never kept.
Logit Page
pub trait Logit: Sortable { }
The logit types top_p_rows accepts: f32 and f64.
As-of lookups
asof_indices Page
pub fn asof_indices(
left_t: &[i64],
right_t: &[i64],
by: Option<(&[i64], &[i64])>,
dir: AsofDirection,
out: &mut [i64],
)
An as-of join: for each left row, the right row with the nearest earlier (or later) time, like pandas
merge_asof and Polars join_asof.
Arguments
left_t: the left rows' times, in any order.right_t: the right rows' times, in any order.by: optional key columns(left_keys, right_keys); a left row then only matches right rows with an equal key.dir: which match (seeAsofDirection).out: receives each left row's matching right row, or -1 for none.
Example
use kwker::AsofDirection;
let mut out = [0i64; 2];
kwker::asof_indices(&[5, 12], &[0, 10, 20], None, AsofDirection::Backward, &mut out);
assert_eq!(out, [0, 1]);
Panics
If out.len() != left_t.len() or a by column's length differs from its side's times.
AsofDirection Page
pub enum AsofDirection {
Backward,
Forward,
Nearest,
}
Which right row an as-of join matches.
Variants
Backward: The right row with the largest time at or before the left time (among equal times the last in input order).Forward: The right row with the smallest time at or after the left time (among equal times the first in input order).Nearest: The closer of the backward and the forward match (a tie takes the backward one, as pandas).
Strings and bytes
sort_bytes Page
pub fn sort_bytes<S: AsRef<[u8]>>(v: &mut [S])
Sorts strings in place in byte order; for UTF-8 text that is code point order.
Arguments
v: the strings:String,&str,Vec<u8>,&[u8]or anything else that isAsRef<[u8]>.
Example
let mut v = vec!["pear", "apple", "fig"];
kwker::sort_bytes(&mut v);
assert_eq!(v, ["apple", "fig", "pear"]);
Notes
- Not stable, which only matters for strings that are equal (and so cannot be told apart).
argsort_bytes Page
pub fn argsort_bytes<S: AsRef<[u8]>>(v: &[S]) -> Permutation
The order that sorts strings in byte order, without moving them. Equal strings keep their input order.
Arguments
v: the strings (anythingAsRef<[u8]>).
Returns
A Permutation: apply it to the strings or to data that belongs to them.
collation_key Page
pub fn collation_key(s: &[u8], c: Collation, out: &mut Vec<u8>)
Appends the sort key of s under c to out: plain byte order of the keys is the collation's order.
Arguments
s: the string's bytes.c: the order (seeCollation).out: the buffer the key is appended to.
unique_strings Page
pub fn unique_strings<S: AsRef<[u8]>>(v: &[S], c: Collation) -> Vec<u64>
The distinct strings under a collation, in sorted order.
Arguments
v: the strings (anythingAsRef<[u8]>).c: the order (seeCollation); strings equal under it ("ABC" and "abc" without case) count as one.
Returns
The input position of each distinct string's first occurrence, in sorted order.
Example
use kwker::Collation;
let at = kwker::unique_strings(&["b", "A", "a", "B"], Collation::AsciiCaseless);
assert_eq!(at, [1, 0]);
argsort_strings Page
pub fn argsort_strings<S: AsRef<[u8]>>(v: &[S], c: Collation) -> Permutation
The order that sorts strings under a collation, without moving them. Equal strings keep their input order.
Arguments
v: the strings (anythingAsRef<[u8]>).c: the order (seeCollation).
Returns
A Permutation: the input positions in sorted order.
Example
use kwker::Collation;
let p = kwker::argsort_strings(&["file10", "file2", "file1"], Collation::Natural);
assert_eq!(p.as_slice(), [2, 1, 0]);
Examples: Strings: The order of strings
argsort_ucs4 Page
pub fn argsort_ucs4(data: &[u32], width: usize, c: Collation) -> Permutation
The order of fixed-width UCS-4 strings (NumPy 'U' arrays) under a collation. Equal strings keep their input
order.
Arguments
data: the strings' code points,widthper string; trailing zeros are padding.width: the number of code points per string.c: the order (seeCollation).
Returns
A Permutation: the input positions in sorted order.
sort_strings Page
pub fn sort_strings<S: AsRef<[u8]>>(v: &mut [S], c: Collation)
Sorts strings in place under a collation. Equal strings keep their input order.
Arguments
v: the strings (anythingAsRef<[u8]>).c: the order (seeCollation).
Examples: Strings: Sort a list of strings
argsort_strings_table Page
pub fn argsort_strings_table<S: AsRef<[u8]>>(v: &[S], weights: &[u8; 256]) -> Permutation
The order of strings under a custom character order given as byte weights, like a COBOL ALPHABET clause.
Strings compare by their bytes' weights, a prefix first.
Arguments
v: the strings (anythingAsRef<[u8]>).weights: one weight per byte value; bytes of equal weight count as the same character.
Returns
A Permutation; strings with equal weights keep their input order.
sort_strings_table Page
pub fn sort_strings_table<S: AsRef<[u8]>>(v: &mut [S], weights: &[u8; 256])
Sorts strings in place under byte weights, as argsort_strings_table orders them.
Arguments
v: the strings (anythingAsRef<[u8]>).weights: one weight per byte value.
sort_by_string_key Page
pub fn sort_by_string_key<T, S: AsRef<[u8]>>(v: &mut [T], c: Collation, key: impl FnMut(&T) -> S)
Sorts objects of any type by a string key you extract, such as a name field or a path. Equal keys keep their input order.
Arguments
v: the objects; they need not beClone.c: the order (seeCollation).key: returns an object's key; called once per object.
Notes
- For Unicode or locale orders, pass sort keys from a collation library (for example ICU's
getSortKeybytes) withCollation::Bytes.
sort_by_comparator Page
pub fn sort_by_comparator<T>(v: &mut [T], cmp: impl FnMut(&T, &T) -> Ordering)
Sorts with a comparison function, for orders no key can express. This is Rust's standard stable sort.
Arguments
v: the elements.cmp: compares two elements.
Notes
- Kwker's fast paths need keys: when the order allows, extract one with
crate::sort_by_extracted_keyorsort_by_string_keyinstead.
Collation Page
pub enum Collation {
Bytes,
AsciiCaseless,
Natural,
NaturalCaseless,
}
A string order: plain bytes, ASCII without case, or natural order with numbers by value.
Variants
Bytes: Plain byte order (UTF-8 strings: code point order).AsciiCaseless: ASCII letters compared without case ('a' = 'A'); every other byte as is.Natural: Runs of ASCII digits compared by their numeric value ("file2" before "file10"; leading zeros ignored, so "a7" and "a007" are equal); everything else by bytes. A digit run sorts where its first digit would in byte order.NaturalCaseless:Collation::NaturalandCollation::AsciiCaselesstogether.
TABLE_EBCDIC_037 Page
pub const TABLE_EBCDIC_037: [u8; 256] = _;
Byte weights for argsort_strings_table that sort ASCII (Latin-1) text in IBM EBCDIC code page 037 order, a
mainframe's order: space, punctuation, lowercase, uppercase, then digits.
TABLE_FROM_EBCDIC_037 Page
pub const TABLE_FROM_EBCDIC_037: [u8; 256] = _;
The reverse of TABLE_EBCDIC_037: byte weights that sort EBCDIC 037 bytes in Latin-1 (ASCII) order.
Packed int4
sort_int4_packed Page
pub fn sort_int4_packed(data: &mut [u8], n: usize, signed: bool, order: Order)
Sorts 4-bit integers packed two per byte (low nibble first) in place.
Arguments
data: the packed values.n: how many 4-bit values to sort.signed:falsefor 0..=15;truefor -8..=7.order: the direction, for exampleOrder::ASCENDING.
Example
use kwker::Order;
let mut d = kwker::pack_int4(&[9, 2, 7, 1]);
kwker::sort_int4_packed(&mut d, 4, false, Order::ASCENDING);
assert_eq!(kwker::unpack_int4(&d, 4), [1, 2, 7, 9]);
Panics
If data holds fewer than n.div_ceil(2) bytes.
argsort_int4_packed Page
pub fn argsort_int4_packed(data: &[u8], n: usize, signed: bool, order: Order) -> Vec<u32>
The positions that sort packed 4-bit integers. Equal values keep their input order.
Arguments
data,n,signed,order: as insort_int4_packed.
Returns
One position per value.
Panics
If data holds fewer than n.div_ceil(2) bytes or n exceeds u32::MAX.
top_k_int4_packed Page
pub fn top_k_int4_packed(data: &[u8], n: usize, k: usize, signed: bool, order: Order) -> Vec<u32>
The positions of the k first packed 4-bit integers in order, without sorting them all.
Arguments
data,n,signed,order: as insort_int4_packed.k: how many; more thannreturns them all.
Returns
The positions, in order; equal values in input order.
Panics
If data holds fewer than n.div_ceil(2) bytes or n exceeds u32::MAX.
pack_int4 Page
pub fn pack_int4(values: &[u8]) -> Vec<u8>
Packs 4-bit values two per byte, low nibble first; each value keeps its low 4 bits.
Arguments
values: one value per byte.
Returns
values.len().div_ceil(2) bytes.
unpack_int4 Page
pub fn unpack_int4(data: &[u8], n: usize) -> Vec<u8>
Unpacks the first n 4-bit values of data, one per byte (0..=15; signed values as their low 4 bits).
Arguments
data: the packed values.n: how many values.
Sparse arrays
coo_coalesce Page
pub fn coo_coalesce<I: Index, V: SparseValue>(
rows: &[I],
cols: &[I],
vals: &[V],
shape: (usize, usize),
reduce: Reduce,
) -> Result<(Vec<I>, Vec<I>, Vec<V>), SparseError>
Sorts sparse coordinates (COO format) row by row and combines duplicates, like scipy.sparse's
sum_duplicates.
Arguments
rows,cols,vals: the entries, one triple per nonzero.shape:(rows, columns)of the matrix.reduce: how duplicate coordinates combine (seeReduce).
Returns
(rows, cols, vals), sorted by row, then column, each coordinate once; or a SparseError.
Notes
- Duplicates combine in input order (float sums reproducible) when
bits(rows - 1) + bits(columns - 1) + bits(nonzeros)is at most 64; otherwise in no particular order.
coo_to_csr Page
pub fn coo_to_csr<I: Index, V: SparseValue>(
rows: &[I],
cols: &[I],
vals: &[V],
shape: (usize, usize),
reduce: Reduce,
) -> Result<(Vec<I>, Vec<I>, Vec<V>), SparseError>
Converts sparse coordinates (COO) to compressed rows (CSR), combining duplicates, like scipy.sparse's
tocsr().
Arguments
rows,cols,vals: the entries, one triple per nonzero.shape:(rows, columns)of the matrix.reduce: how duplicate coordinates combine (seeReduce).
Returns
(indptr, indices, data): row i's columns, ascending, are indices[indptr[i]..indptr[i + 1]] and its
values the same range of data; or a SparseError.
Example
use kwker::Reduce;
let (indptr, indices, data) =
kwker::coo_to_csr(&[0u32, 1, 0], &[1u32, 0, 1], &[1.0f64, 2.0, 3.0], (2, 2), Reduce::Sum).unwrap();
assert_eq!(indptr, [0, 1, 2]);
assert_eq!(indices, [1, 0]);
assert_eq!(data, [4.0, 2.0]);
coo_to_csc Page
pub fn coo_to_csc<I: Index, V: SparseValue>(
rows: &[I],
cols: &[I],
vals: &[V],
shape: (usize, usize),
reduce: Reduce,
) -> Result<(Vec<I>, Vec<I>, Vec<V>), SparseError>
Converts sparse coordinates (COO) to compressed columns (CSC), combining duplicates.
Arguments
rows,cols,vals,shape,reduce: as incoo_to_csr.
Returns
(indptr, indices, data): column j's rows, ascending, are indices[indptr[j]..indptr[j + 1]]; or a
SparseError.
csr_to_csc Page
pub fn csr_to_csc<I: Index + Sync, V: Copy + Send + Sync>(
indptr: &[I],
indices: &[I],
data: &[V],
shape: (usize, usize),
) -> Result<(Vec<I>, Vec<I>, Vec<V>), SparseError>
Converts compressed rows (CSR) to compressed columns (CSC) without a sort. Duplicates are kept.
Arguments
indptr:rows + 1offsets from 0 toindices.len().indices: each nonzero's column.data: each nonzero's value.shape:(rows, columns)of the matrix.
Returns
(colptr, rows, data): column j's rows, ascending, are rows[colptr[j]..colptr[j + 1]]; or a
SparseError.
csr_to_csc_into Page
pub fn csr_to_csc_into<I: Index + Sync, V: Copy + Send + Sync>(
indptr: &[I],
indices: &[I],
data: &[V],
shape: (usize, usize),
colptr: &mut [I],
rows: &mut [I],
out: &mut [V],
) -> Result<(), SparseError>
csr_to_csc into buffers you provide.
Arguments
indptr,indices,data,shape: as incsr_to_csc.colptr: receivescolumns + 1offsets.rows,out: receive each nonzero's row and value.
csr_to_csc_mt Page
pub fn csr_to_csc_mt<I: Index + Sync, V: Copy + Send + Sync>(
indptr: &[I],
indices: &[I],
data: &[V],
shape: (usize, usize),
threads: usize,
) -> Result<(Vec<I>, Vec<I>, Vec<V>), SparseError>
csr_to_csc on several threads; the result is the same.
Arguments
indptr,indices,data,shape: as incsr_to_csc.threads: the number of threads; 0 uses the default. Small matrices use fewer.
csr_to_csc_into_mt Page
pub fn csr_to_csc_into_mt<I: Index + Sync, V: Copy + Send + Sync>(
indptr: &[I],
indices: &[I],
data: &[V],
shape: (usize, usize),
colptr: &mut [I],
rows: &mut [I],
out: &mut [V],
threads: usize,
) -> Result<(), SparseError>
csr_to_csc_into on several threads; the result is the same.
Arguments
indptr,indices,data,shape,colptr,rows,out: as incsr_to_csc_into.threads: the number of threads; 0 uses the default.
csc_to_csr Page
pub fn csc_to_csr<I: Index + Sync, V: Copy + Send + Sync>(
colptr: &[I],
rows: &[I],
data: &[V],
shape: (usize, usize),
) -> Result<(Vec<I>, Vec<I>, Vec<V>), SparseError>
Converts compressed columns (CSC) to compressed rows (CSR) without a sort.
Arguments
colptr:columns + 1offsets.rows: each nonzero's row.data: each nonzero's value.shape:(rows, columns)of the matrix.
Returns
(indptr, cols, data); or a SparseError.
csc_to_csr_mt Page
pub fn csc_to_csr_mt<I: Index + Sync, V: Copy + Send + Sync>(
colptr: &[I],
rows: &[I],
data: &[V],
shape: (usize, usize),
threads: usize,
) -> Result<(Vec<I>, Vec<I>, Vec<V>), SparseError>
csc_to_csr on several threads; the result is the same.
Arguments
colptr,rows,data,shape: as incsc_to_csr.threads: the number of threads; 0 uses the default.
Reduce Page
pub enum Reduce {
Sum,
Prod,
Min,
Max,
Mean,
}
How duplicate coordinates combine.
Variants
Sum: The sum of the values (wrapping for integers).Prod: The product (wrapping for integers).Min: The smallest value (floats: a NaN only when every value is NaN).Max: The largest value (floats: asMin).Mean: The mean (integers: the sum divided by the count, truncated toward zero).
SparseValue Page
pub trait SparseValue: Copy + Sealed { }
The value types of the sparse operations: the integer and float types.
SparseError Page
pub enum SparseError {
Lengths,
OutOfRange,
ShapeTooLarge,
IndexTooNarrow,
}
Why a sparse operation refused its arguments.
Variants
Lengths: rows, cols and vals differ in length.OutOfRange: A row or column index is outside the shape.ShapeTooLarge: The shape needs more than 64 key bits (bits(nrows) + bits(ncols)).IndexTooNarrow: The index type cannot hold the shape or the number of nonzeros.
Apache Arrow
arrow_argsort Page
pub unsafe fn arrow_argsort(
schema: &ArrowSchema,
array: &ArrowArray,
options: ArrowSortOptions,
) -> Result<Vec<u64>, ArrowError>
The order that sorts an Apache Arrow array, read in place through the Arrow C data interface: what
pyarrow.compute.array_sort_indices returns.
Arguments
schema: the array'sArrowSchema, as pyarrow, arrow-rs, Arrow C++, DuckDB or Polars export it.array: theArrowArrayit describes.options: direction, null placement and dictionary handling (seeArrowSortOptions).
Returns
One row position per row, or an ArrowError for an invalid or unsupported array.
Safety
schema and array must be live (not released) C data interface structures describing the same array, with
buffers as long as their format, length and offset imply.
Notes
- Arrow's rules: equal values keep their input order,
-0.0equals+0.0, NaNs come after every other value and sit with the nulls. Strings compare as bytes. - Supported: numbers, dates and times, decimals, booleans, strings and binaries (also views), fixed-size binary, dictionaries, structs, lists, maps, run-end encoded arrays and unions.
arrow_argsort_into Page
pub unsafe fn arrow_argsort_into(
schema: &ArrowSchema,
array: &ArrowArray,
options: ArrowSortOptions,
out: &mut [u64],
) -> Result<(), ArrowError>
arrow_argsort into a buffer you provide.
Arguments
schema,array,options: as inarrow_argsort.out: receives one row position per row.
Panics
If out's length differs from the array's.
Safety
As arrow_argsort.
arrow_top_k Page
pub unsafe fn arrow_top_k(
schema: &ArrowSchema,
array: &ArrowArray,
k: usize,
options: ArrowSortOptions,
) -> Result<Vec<u64>, ArrowError>
The first k rows of arrow_argsort's order without sorting every row, like SQL ORDER BY ... LIMIT k.
Arguments
schema,array,options: as inarrow_argsort.k: how many rows; more than the array's length returns them all.
Returns
The row positions, in order, or an ArrowError.
Safety
As arrow_argsort.
arrow_top_k_into Page
pub unsafe fn arrow_top_k_into(
schema: &ArrowSchema,
array: &ArrowArray,
options: ArrowSortOptions,
out: &mut [u64],
) -> Result<(), ArrowError>
arrow_top_k into a buffer you provide; k is out.len(), at most the array's length.
Arguments
schema,array,options: as inarrow_argsort.out: receives the firstkrow positions.
Safety
As arrow_argsort.
arrow_argsort_chunks_into Page
pub unsafe fn arrow_argsort_chunks_into(
schema: &ArrowSchema,
arrays: &[&ArrowArray],
options: ArrowSortOptions,
out: &mut [u64],
) -> Result<(), ArrowError>
arrow_argsort of a chunked array (a pyarrow ChunkedArray, or a stream's batches) as one array, without
combining the chunks first.
Arguments
schema: the type every chunk has.arrays: the chunks, in order.options: as inarrow_argsort.out: receives the row positions across all chunks; fewer entries than rows gives the first rows of the order, asarrow_top_k_into.
Returns
Ok(()), or an ArrowError; types not supported here are Unsupported (combine the chunks, then call
arrow_argsort).
Safety
schema must describe every chunk, and each chunk must be a live structure as for arrow_argsort.
arrow_dense_ranks Page
pub unsafe fn arrow_dense_ranks(
schema: &ArrowSchema,
array: &ArrowArray,
ranks: &mut [u32],
) -> Result<usize, ArrowError>
Dense ranks of a string or binary Arrow array: each row's rank among the distinct values in byte order. Group-by keys use this.
Arguments
schema,array: a string, large string, string view, binary or fixed-size binary array.ranks: receives one rank per row, counted from 0 in byte order; null rows also get 0.
Returns
The number of distinct values, or Unsupported for other types.
Panics
If ranks' length differs from the array's.
Safety
As arrow_argsort.
arrow_dense_ranks_mt Page
pub unsafe fn arrow_dense_ranks_mt(
schema: &ArrowSchema,
array: &ArrowArray,
threads: usize,
ranks: &mut [u32],
) -> Result<usize, ArrowError>
arrow_dense_ranks on several threads; the ranks are the same.
Arguments
schema,array,ranks: as inarrow_dense_ranks.threads: the number of threads; 0 uses the default.
Safety
As arrow_argsort.
ArrowSchema Page
pub struct ArrowSchema {
pub format: *const c_char,
pub name: *const c_char,
pub metadata: *const c_char,
pub flags: i64,
pub n_children: i64,
pub children: *mut *mut ArrowSchema,
pub dictionary: *mut ArrowSchema,
pub release: Option<unsafe fn(*mut ArrowSchema)>,
pub private_data: *mut c_void,
}
The Arrow C data interface's schema (struct ArrowSchema), as an exporter (pyarrow, arrow-rs, Arrow C++, DuckDB,
Polars) fills it. The fields are the specification's (arrow.apache.org/docs/format/CDataInterface.html).
ArrowArray Page
pub struct ArrowArray {
pub length: i64,
pub null_count: i64,
pub offset: i64,
pub n_buffers: i64,
pub n_children: i64,
pub buffers: *mut *const c_void,
pub children: *mut *mut ArrowArray,
pub dictionary: *mut ArrowArray,
pub release: Option<unsafe fn(*mut ArrowArray)>,
pub private_data: *mut c_void,
}
The Arrow C data interface's array (struct ArrowArray); the fields are the specification's.
ArrowSortOptions Page
pub struct ArrowSortOptions {
pub descending: bool,
pub nulls_first: bool,
pub by_codes: bool,
}
How arrow_argsort and arrow_top_k order rows. The default is ascending, nulls last, dictionaries by value.
Fields
descending: Largest first (NaNs and nulls stay wherenulls_firstputs them).nulls_first: Nulls (then NaNs) before the values instead of after them.by_codes: Dictionary-encoded arrays: order by the codes instead of the values they stand for.
ArrowError Page
pub enum ArrowError {
Invalid(&'static str),
Unsupported(String),
}
Why an Arrow array could not be ordered.
Variants
Invalid: Not a usable array: released, a NULL format or buffer, a negative length or offset, decreasing offsets.Unsupported: A valid array of a type without an order here (nested types, day-time / month-day-nano intervals, run-end encoding).
Files larger than memory
sort_file Page
pub fn sort_file<T: Sortable + Send + Sync>(
input: &Path,
output: &Path,
opts: &ExternalSort,
) -> Result<ExternalStats>
Sorts a binary file of numbers into another file, using a fixed amount of memory: files larger than memory work.
Arguments
input: a file of native-endianTvalues, back to back.output: the file to write; it may beinput.opts: the memory budget, order and threads (seeExternalSort).
Returns
What the sort did (see ExternalStats), or an I/O error.
Notes
- A file length that is not a whole number of values is an error. On an error the output is incomplete; the temporary run files are removed either way.
Examples: Large data: Sort a file larger than memory
sort_file_with Page
pub fn sort_file_with<T: Sortable + Send + Sync>(
input: &Path,
output: &Path,
opts: &ExternalSort,
ctl: &Control<'_>,
) -> Result<ExternalStats>
sort_file with cancellation and progress reports (see Control).
Arguments
input,output,opts: as insort_file.ctl: the cancellation flag and progress callback; progress counts keys through every phase.
Returns
As sort_file; a cancelled sort returns an error of kind Interrupted.
sort_file_records Page
pub fn sort_file_records<K: Sortable>(
input: &Path,
output: &Path,
record_size: usize,
key_offset: usize,
opts: &ExternalSort,
) -> Result<ExternalStats>
Sorts a file of fixed-size records by a numeric key field, using a fixed amount of memory. Records with equal keys keep their file order.
Arguments
input: a file of records,record_sizebytes each, back to back.output: the file to write; it may beinput.record_size: the size of a record, in bytes.key_offset: where the native-endianKkey starts in a record.opts: the memory budget, order and threads (seeExternalSort).
Returns
What the sort did (see ExternalStats), or an error: InvalidInput when the key does not fit in a record,
InvalidData when the file is not a whole number of records, or an I/O error.
Notes
- Records move unchanged, padding and all.
sort_file_records_with Page
pub fn sort_file_records_with<K: Sortable>(
input: &Path,
output: &Path,
record_size: usize,
key_offset: usize,
opts: &ExternalSort,
ctl: &Control<'_>,
) -> Result<ExternalStats>
sort_file_records with cancellation and progress reports (see Control), counted in records.
Arguments
input,output,record_size,key_offset,opts: as insort_file_records.ctl: the cancellation flag and progress callback.
ExternalSort Page
pub struct ExternalSort {
pub memory: usize,
pub temp_dir: Option<PathBuf>,
pub order: Order,
pub threads: usize,
pub sync: bool,
}
Options of sort_file and sort_file_records. ExternalSort::default() uses 256 MiB, ascending order, one
thread.
Fields
memory: The memory budget for buffers, in bytes (default 256 MiB; at least 1 MiB is used).temp_dir: Directory of the run files (default: the output file's directory).order: The order of the output.threads: Threads of each in-memory run sort (1:crate::sort_by_order; more:crate::sort_mt).sync: Flush the output to the storage device (fsync) before returning (default false: the output is complete in the page cache when the call returns, as aftersort -o; a crash soon after may lose it).
ExternalStats Page
pub struct ExternalStats {
pub keys: u64,
pub runs: usize,
pub merge_passes: usize,
pub bytes_spilled: u64,
pub read_time: Duration,
pub sort_time: Duration,
pub merge_time: Duration,
pub write_time: Duration,
}
What sort_file did: sizes and where the time went.
Fields
keys: Keys sorted.runs: Sorted runs spilled (0: the input fit the budget and was sorted in memory).merge_passes: Merge passes over the data (1 when the first merge could buffer every run).bytes_spilled: Bytes written to run files, all passes.read_time: Time the calling thread spent reading (the input and the run files) or waiting for reads.sort_time: Time in the in-memory sorts.merge_time: Time in the merges' computation.write_time: Time writing (run files and the output, fsync included) or waiting for writes.
Distributed sorting
dist::sample Page
pub fn sample<T: Sortable>(v: &[T], m: usize, base: u64, seed: u64) -> Vec<Splitter<T>>
A stratified random sample of m elements of v: one element at a random index in each of m equal strata
(deterministic for a seed), with positions base + index. All of v when m >= v.len().
dist::sample_sorted Page
pub fn sample_sorted<T: Sortable>(sorted: &[T], m: usize, base: u64) -> Vec<Splitter<T>>
The regular sample of locally sorted data (PSRS): the m elements at ranks (j + 1) n / (m + 1), positions
base + index. All of sorted when m >= sorted.len().
Splitter Page
pub struct Splitter<T> {
pub key: T,
pub pos: u64,
}
A splitter: a key and the position that breaks ties between equal keys (see the module documentation).
Fields
key: The key.pos: Its position:base + indexof the element it was taken from.
PartitionDesc Page
pub struct PartitionDesc<T> {
pub index: usize,
pub lo: Option<Splitter<T>>,
pub hi: Option<Splitter<T>>,
pub order: Order,
}
One partition of a Partitioning: the (key, position) range [lo, hi) (None: unbounded on that side). A
worker given only its descriptor can tell which elements are its own (PartitionDesc::contains).
Fields
index: The partition's number (0 = first in the order).lo: The first (key, position) inside,Nonefor the first partition.hi: The first (key, position) past it,Nonefor the last partition.order: The order the range is in.
PartitionDesc::contains Page
pub fn contains(&self, key: T, pos: u64) -> bool
Whether the element (key, pos) belongs to this partition.
dist::DistError Page
pub enum DistError {
Malformed,
WrongType(String),
Unordered,
}
Why Partitioning::from_bytes refused its input.
Variants
Malformed: Not a serialized partitioning, a newer format, or truncated.WrongType: Serialized for another key type (its name).Unordered: The splitters are not in order.
Partitioning Page
pub struct Partitioning<T: Sortable> {
/* private fields */
}
Splits a key space into parts ranges for distributed sorting: build it once from a sample of every worker's data
(or from exact quantiles), send it to the workers, and each worker sends its elements in partition j to worker
j. After every worker sorts what it received, the partitions concatenated in order are sorted.
Example
use kwker::{Order, Partitioning};
let p = Partitioning::exact(&[50, 10, 40, 20, 30, 60], 3, 0, Order::ASCENDING);
assert_eq!(p.parts(), 3);
assert_eq!(p.counts(&[50, 10, 40, 20, 30, 60], 0), [2, 2, 2]);
Notes
- Elements are (key, position) pairs, so equal keys can fall into different partitions and the split stays balanced with many duplicates.
- Send it with
Partitioning::to_bytes;Partitioning::descriptorsgives each worker its own range.
Partitioning::from_sample Page
pub fn from_sample(sample: &[Splitter<T>], parts: usize, order: Order) -> Self
The partitioning whose splitters are the elements of rank j * len / parts (j = 1 .. parts) of sample
(gathered from every worker), by (key, position) in order: near-equal partitions to the sample's accuracy.
An empty sample gives one partition. Panics if parts is 0.
Partitioning::exact Page
pub fn exact(v: &[T], parts: usize, base: u64, order: Order) -> Self
Partitions with exactly balanced sizes: each of the parts partitions gets floor(n / parts) or
ceil(n / parts) elements of v, whatever the duplicates.
Arguments
v: the keys (anySortabletype).parts: the number of partitions, at least 1.base: the position ofv[0]in the whole data set.order: the order the partitions follow, for exampleOrder::ASCENDING.
Panics
If parts is 0. An empty v gives one partition.
Partitioning::from_splitters Page
pub fn from_splitters(s: Vec<Splitter<T>>, order: Order) -> Result<Self, DistError>
The partitioning with these splitters (in order by (key, position) in order; equal ones give empty
partitions), DistError::Unordered otherwise.
Partitioning::parts Page
pub fn parts(&self) -> usize
The number of partitions (splitters + 1).
Partitioning::splitters Page
pub fn splitters(&self) -> &[Splitter<T>]
The splitters.
Partitioning::order Page
pub fn order(&self) -> Order
The order.
Partitioning::part_of Page
pub fn part_of(&self, key: T, pos: u64) -> usize
The partition of the element (key, pos).
Partitioning::classify Page
pub fn classify(&self, v: &[T], base: u64, out: &mut [u32])
The partition of each element of v (positions base + index) into out (as long as v).
Partitioning::counts Page
pub fn counts(&self, v: &[T], base: u64) -> Vec<usize>
The number of elements of v (positions base + index) in each partition.
Partitioning::partition Page
pub fn partition(&self, v: &mut [T], base: u64) -> Vec<usize>
Reorders v (positions base + index, taken before the reordering) by partition, in place, and returns the
partition boundaries (parts() + 1 offsets: 0, ..., v.len()). Unordered inside a partition. Scratch: 4 bytes
per element.
Partitioning::partition_into Page
pub fn partition_into(&self, v: &[T], base: u64, out: &mut [T]) -> Vec<usize>
The elements of v (positions base + index) by partition into out (as long as v), stably (input order
inside a partition); returns the partition boundaries.
Partitioning::partition_indices Page
pub fn partition_indices(&self, v: &[T], base: u64) -> (Vec<u64>, Vec<usize>)
The indices of v's elements (positions base + index) grouped by partition, stably (ascending inside a
partition), and the partition boundaries: send indices[off[j]..off[j + 1]] (keys, payloads, records) to
worker j.
Partitioning::partition_indices_into Page
pub fn partition_indices_into(&self, v: &[T], base: u64, ix: &mut [u64]) -> Vec<usize>
Partitioning::partition_indices into ix (as long as v); returns the partition boundaries.
Partitioning::split_points Page
pub fn split_points(&self, sorted: &[T], base: u64) -> Vec<usize>
The partition boundaries of locally sorted data: sorted in this order with equal keys kept in their
original order (a stable sort; positions base + index in sorted) - parts() + 1 offsets, one binary search
per splitter.
Partitioning::descriptors Page
pub fn descriptors(&self) -> Vec<PartitionDesc<T>>
The partitions' descriptors (ranges), one per partition.
Partitioning::to_bytes Page
pub fn to_bytes(&self) -> Vec<u8>
A compact byte form to send to workers: a header (format, key type, order), then each splitter's order key and position (little-endian u64s).
Partitioning::from_bytes Page
pub fn from_bytes(b: &[u8]) -> Result<Self, DistError>
The partitioning Partitioning::to_bytes wrote (for the same key type).
Threads
set_task_runner Page
pub fn set_task_runner(runner: Option<RunnerFn>, ctx: *mut c_void)
Runs Kwker's parallel work on your thread pool instead of its own threads, for example PyTorch's intra-op threads, so the two do not compete for the same cores.
Arguments
runner: your runner (seeRunnerFn);Nonegoes back to Kwker's own threads.ctx: a pointer passed torunnerunchanged.
Notes
- The tasks of a phase are independent: a runner may run them one after another; results do not change.
set_max_threads Page
pub fn set_max_threads(n: usize)
Caps the number of threads all parallel work in the process uses together, so many callers share one budget instead of each starting its own threads.
Arguments
n: the cap; 0 (the default) means the CPUs the process may run on.
Notes
- Work that finds the budget spent runs on its calling thread. Results never depend on the cap.
Remarks: Threads change only the speed: the result follows the same rules on any thread count (rule 10).
max_threads Page
pub fn max_threads() -> usize
The cap set_max_threads set: the CPUs the process may run on when it is 0.
Remarks: Threads change only the speed: the result follows the same rules on any thread count (rule 10).
parallel_threads Page
pub fn parallel_threads(reset: bool) -> (usize, usize)
How many threads run parallel work now, and the most since the last reset.
Arguments
reset:truerestarts the peak count from now.
Returns
(now, peak).
set_worker_cpus Page
pub fn set_worker_cpus(cpus: Option<&[usize]>)
Pins Kwker's worker threads to the given CPUs (Linux only; ignored elsewhere).
Arguments
cpus: the CPU numbers; workerwruns oncpus[w % cpus.len()].Noneor an empty list turns pinning off (the default). The calling thread is not pinned.
set_default_threads Page
pub fn set_default_threads(n: usize)
Sets how many threads multithreaded calls use when you pass threads = 0. The default is 1: Kwker starts no
threads unless asked.
Arguments
n: the thread count; 0 means the CPUs this process may run on.
Remarks: Threads change only the speed: the result follows the same rules on any thread count (rule 10).
default_threads Page
pub fn default_threads() -> usize
The thread count calls with threads = 0 use (see set_default_threads).
Remarks: Threads change only the speed: the result follows the same rules on any thread count (rule 10).
sort_mt Page
pub fn sort_mt<T: Sortable + Send + Sync>(v: &mut [T], order: Order, threads: usize)
crate::sort_by_order on several threads; the result is the same.
Arguments
v: the keys (anySortabletype).order: the direction and NaN placement, for exampleOrder::ASCENDING.threads: the number of threads; 0 usesdefault_threads.
Example
use kwker::Order;
let mut v: Vec<u32> = (0..1_000_000).rev().collect();
kwker::sort_mt(&mut v, Order::ASCENDING, 4);
assert!(v.windows(2).all(|w| w[0] <= w[1]));
Notes
- Small inputs (below
MT_MINkeys) and inputs the single-thread sort handles best (nearly sorted, few distinct values) use one thread. - Uses a buffer of
v.len()keys.
Remarks: Not stable (rule 5): keys that are equal but can be told apart (NaNs with different bits) may change places. Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for. Threads change only the speed: the result follows the same rules on any thread count (rule 10).
Examples: Sorting: Big arrays: use more cores
sort_mt_with Page
pub fn sort_mt_with<T: Sortable + Send + Sync>(
v: &mut [T],
order: Order,
threads: usize,
ctl: &Control<'_>,
) -> Result<(), Cancelled>
sort_mt with cancellation and progress reports (see Control).
Arguments
v,order,threads: as insort_mt.ctl: the cancellation flag and progress callback.
Returns
Ok(()), or Err(Cancelled): v then holds its keys in an unspecified order.
Remarks: Not stable (rule 5): keys that are equal but can be told apart (NaNs with different bits) may change places. Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for. Threads change only the speed: the result follows the same rules on any thread count (rule 10).
sort_indexed_mt Page
pub fn sort_indexed_mt<T: Sortable + Send + Sync>(
data: &[T],
order: Order,
values: Option<&mut [T]>,
indices: &mut [u64],
threads: usize,
)
Sorts on several threads and returns where each sorted key came from: crate::sort_with_indices without
changing data.
Arguments
data: the keys (anySortabletype); not changed.order: the direction and NaN placement.values: if given, receives the sorted keys.indices: receives the input position of each sorted key; equal keys keep their input order.threads: the number of threads; 0 usesdefault_threads.
Examples: Large data: Use several cores
sort_indexed_mt_with Page
pub fn sort_indexed_mt_with<T: Sortable + Send + Sync>(
data: &[T],
order: Order,
values: Option<&mut [T]>,
indices: &mut [u64],
threads: usize,
ctl: &Control<'_>,
) -> Result<(), Cancelled>
sort_indexed_mt with cancellation and progress reports (see Control).
Arguments
data,order,values,indices,threads: as insort_indexed_mt.ctl: the cancellation flag and progress callback.
Returns
Ok(()), or Err(Cancelled): data is unchanged and the outputs are unspecified.
kth_mt Page
pub fn kth_mt<T: Sortable + Send + Sync>(
v: &[T],
k: usize,
order: Order,
nan_first: bool,
threads: usize,
) -> (T, u64)
The key at sorted position k and its position in the input, on several threads.
Arguments
v: the keys (anySortabletype); not changed.k: the sorted position, belowv.len().order: the direction and NaN placement.nan_first:truereturns the first NaN whenvholds one, astorch.mediandoes.threads: the number of threads; 0 usesdefault_threads.
Returns
(key, position); among equal keys the earlier position counts first.
Panics
If k >= v.len().
sort_kv_mt Page
pub fn sort_kv_mt<K: Sortable + Send + Sync, V: Copy + Send + Sync>(
keys: &mut [K],
values: &mut [V],
order: Order,
threads: usize,
)
crate::sort_kv_stable_by_order on several threads: sorts keys and moves values with them, equal keys
keeping their input order. The result is the same as on one thread.
Arguments
keys: the sort keys (anySortabletype).values: one value per key, of anyCopytype.order: the direction and NaN placement.threads: the number of threads; 0 usesdefault_threads.
Panics
If keys and values have different lengths.
Notes
- Uses a copy of the keys and values as scratch.
Remarks: Unless the stable form is asked for, pairs with equal keys may come out in any order; the stable form keeps their input order (rule 6). Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for. Threads change only the speed: the result follows the same rules on any thread count (rule 10).
sort_kv_mt_with Page
pub fn sort_kv_mt_with<K: Sortable + Send + Sync, V: Copy + Send + Sync>(
keys: &mut [K],
values: &mut [V],
order: Order,
threads: usize,
ctl: &Control<'_>,
) -> Result<(), Cancelled>
sort_kv_mt with cancellation and progress reports (see Control).
Arguments
keys,values,order,threads: as insort_kv_mt.ctl: the cancellation flag and progress callback.
Returns
Ok(()), or Err(Cancelled) with keys and values unchanged.
Remarks: Unless the stable form is asked for, pairs with equal keys may come out in any order; the stable form keeps their input order (rule 6). Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for. Threads change only the speed: the result follows the same rules on any thread count (rule 10).
reduce_by_key_mt Page
pub fn reduce_by_key_mt<K: Sortable + Send + Sync, V: Reducible + Send + Sync>(
keys: &[K],
values: &[V],
op: Reduction,
order: Order,
threads: usize,
) -> (Vec<K>, Vec<V>)
crate::reduce_by_key on several threads.
Arguments
keys,values,op,order: as incrate::reduce_by_key.threads: the number of threads; 0 usesdefault_threads.
Notes
- Results equal one thread's, except float sums, which add per-thread partial sums and can differ in the last bits (the same for a given thread count).
MT_MIN Page
pub const MT_MIN: usize = _;
Below this many keys the multithreaded sorts use one thread.
TaskFn Page
pub type TaskFn = unsafe fn(*mut c_void, usize);
One task of a parallel phase (C ABI): task(arg, i).
RunnerFn Page
pub type RunnerFn = unsafe fn(*mut c_void, usize, TaskFn, *mut c_void);
A thread-pool runner for set_task_runner (C ABI): runner(ctx, n, task, arg) must call task(arg, i) once
for every i in 0..n, in any order and on any threads, and return when all have returned.
SELECT_MT_MIN Page
pub const SELECT_MT_MIN: usize = _;
Below this many keys the multithreaded selections use one thread.
Plans and workspaces
argsort_into_with Page
pub fn argsort_into_with<T: Sortable>(v: &[T], order: Order, out: &mut [u64], ws: &mut Workspace)
crate::argsort_into with a Workspace you own, so repeated calls allocate nothing.
Arguments
v,order,out: as incrate::argsort_into.ws: the workspace; its buffers are kept for the next call.
Remarks: Stable (rule 6): equal keys keep their input order, so the same input always gives the same positions. Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
top_k_into_with Page
pub fn top_k_into_with<T: Sortable, I: Index>(
v: &[T],
order: Order,
sorted: bool,
values: &mut [T],
indices: &mut [I],
ws: &mut Workspace,
) -> usize
crate::top_k_into with a Workspace you own, so repeated calls allocate nothing.
Arguments
v,order,sorted,values,indices: as incrate::top_k_into.ws: the workspace; its buffers are kept for the next call.
Returns
k, the number of entries written.
Remarks: The first k keys of the stable order and their positions; equal keys keep their input order (rule 9). Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
top_k_indices_into_with Page
pub fn top_k_indices_into_with<T: Sortable>(
v: &[T],
order: Order,
sorted: bool,
out: &mut [u64],
ws: &mut Workspace,
) -> usize
crate::top_k_indices_into with a Workspace you own, so repeated calls allocate nothing.
Arguments
v,order,sorted,out: as incrate::top_k_indices_into.ws: the workspace; its buffers are kept for the next call.
Returns
k, the number of positions written.
sort_kv_with Page
pub fn sort_kv_with<K: Sortable, V: Copy>(
keys: &mut [K],
values: &mut [V],
order: Order,
ws: &mut Workspace,
)
crate::sort_kv_by_order with a Workspace you own, so repeated calls allocate nothing.
Arguments
keys,values,order: as incrate::sort_kv_by_order.ws: the workspace; its buffers are kept for the next call.
Panics
If keys and values have different lengths.
Notes
- Call
Workspace::reserve_kvfirst to stay allocation-free even under a scratch limit of 0.
Remarks: Unless the stable form is asked for, pairs with equal keys may come out in any order; the stable form keeps their input order (rule 6). Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
sort_kv_stable_with Page
pub fn sort_kv_stable_with<K: Sortable, V: Copy>(
keys: &mut [K],
values: &mut [V],
order: Order,
ws: &mut Workspace,
)
crate::sort_kv_stable_by_order with a Workspace you own, so repeated calls allocate nothing.
Arguments
keys,values,order: as incrate::sort_kv_stable_by_order.ws: the workspace; its buffers are kept for the next call.
Panics
If keys and values have different lengths.
Remarks: Stable (rule 6): equal keys keep their input order, so the same input always gives the same positions. Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
select_nth_kv_with Page
pub fn select_nth_kv_with<K: Sortable, V: Copy>(
keys: &mut [K],
values: &mut [V],
k: usize,
order: Order,
ws: &mut Workspace,
)
crate::select_nth_kv_by_order with a Workspace you own, so repeated calls allocate nothing.
Arguments
keys,values,k,order: as incrate::select_nth_kv_by_order.ws: the workspace; its buffers are kept for the next call.
Panics
If k >= keys.len() or keys and values have different lengths.
partial_sort_kv_with Page
pub fn partial_sort_kv_with<K: Sortable, V: Copy>(
keys: &mut [K],
values: &mut [V],
k: usize,
order: Order,
ws: &mut Workspace,
)
crate::partial_sort_kv_by_order with a Workspace you own, so repeated calls allocate nothing.
Arguments
keys,values,k,order: as incrate::partial_sort_kv_by_order.ws: the workspace; its buffers are kept for the next call.
Panics
If keys and values have different lengths.
Remarks: The first k positions hold exactly what a full sort puts there; the rest hold the other keys in any order (rule 8). Unless the stable form is asked for, pairs with equal keys may come out in any order; the stable form keeps their input order (rule 6).
Workspace Page
pub struct Workspace {
/* private fields */
}
Scratch buffers you own and pass to the _with functions; they are kept between calls, so repeated calls
allocate nothing once the buffers have grown. Use one per thread.
Example
use kwker::{Order, Workspace};
let mut ws = Workspace::with_capacity(1000);
let mut out = vec![0u64; 3];
for _ in 0..10 {
kwker::argsort_into_with(&[30i64, 10, 20], Order::ASCENDING, &mut out, &mut ws);
}
assert_eq!(out, [1, 2, 0]);
Workspace::new Page
pub fn new() -> Self
An empty workspace (allocates on first use).
Workspace::with_capacity Page
pub fn with_capacity(n: usize) -> Self
A workspace sized for inputs of n keys (no allocation in calls up to that size).
Workspace::reserve_argsort Page
pub fn reserve_argsort(&mut self, n: usize)
Grows the buffers to what an argsort of 64-bit keys of up to n keys takes (argsort_into_with: its order
keys, the small-input ranks), so that later calls allocate nothing - also under a scratch limit of 0, which
otherwise leaves them the slower in-place merge sort of the indices.
Workspace::reserve_kv Page
pub fn reserve_kv(&mut self, n: usize, value_bytes: usize)
Grows the buffers for key-value calls of up to n pairs with value_bytes-byte values (sort_kv_with and
the other _kv_with forms: then no allocation in them, also under a scratch limit of 0).
Workspace::capacity_bytes Page
pub fn capacity_bytes(&self) -> usize
The bytes held.
Workspace::release Page
pub fn release(&mut self)
Frees the buffers.
Plan Page
pub struct Plan {
/* private fields */
}
Settings chosen once and applied to every call: an order, a thread count, a scratch limit and algorithm classes,
plus a Workspace kept between calls. Results equal the free functions' with the same settings; a plan only
changes where memory comes from and how many threads run.
Example
let mut plan = kwker::Plan::new(kwker::Order::DESCENDING).scratch_limit(Some(0));
let mut v = vec![3u64, 1, 2];
plan.sort(&mut v); // descending, allocation-free
assert_eq!(v, [3, 2, 1]);
let mut ix = vec![0u64; 3];
plan.argsort_into(&[5i64, 7, 6], &mut ix);
assert_eq!(ix, [1, 2, 0]);
Plan::new Page
pub fn new(order: Order) -> Plan
A plan sorting in order, single-threaded, under the calling thread's own scratch limit.
Plan::threads Page
pub fn threads(self, threads: usize) -> Plan
Threads for Plan::sort (more than 1: crate::sort_mt; 0: the process default, crate::default_threads).
Plan::scratch_limit Page
pub fn scratch_limit(self, bytes: Option<usize>) -> Plan
The scratch limit every call of the plan runs under (crate::set_scratch_limit; the thread's own limit is
restored afterwards). Some(0): allocation-free in-place operations.
Plan::algorithms Page
pub fn algorithms(self, a: Algorithms) -> Plan
The algorithm classes every call of the plan runs under (crate::set_algorithms; the thread's own setting is
restored afterwards).
Plan::order Page
pub fn order(&self) -> Order
The plan's order.
Plan::thread_count Page
pub fn thread_count(&self) -> usize
The plan's thread count (0: the process default).
Plan::tune_threads Page
pub fn tune_threads<T: Sortable + Send + Sync>(
&mut self,
sample: &[T],
max_threads: usize,
reps: usize,
) -> usize
Picks the plan's thread count by timing: sorts copies of sample on 1, 2, 4, ... threads and keeps the fastest.
Arguments
sample: an input like the ones the plan will sort (same size and kind of keys).max_threads: the most threads to try; 0 means the process's CPUs.reps: runs per thread count; the best one counts.
Returns
The chosen thread count. Results never depend on it.
Plan::tune_algorithms Page
pub fn tune_algorithms<T: Sortable + Send + Sync>(
&mut self,
sample: &[T],
reps: usize,
) -> Algorithms
Picks the plan's algorithm classes by timing: sorts copies of sample with every class set and keeps one only if
it beats crate::Algorithms::ALL by 3%. Useful for inputs that mislead a shortcut.
Arguments
sample: an input like the ones the plan will sort.reps: runs per set; the best one counts.
Returns
The chosen classes. Results never depend on them.
Plan::algorithm_set Page
pub fn algorithm_set(&self) -> Option<Algorithms>
The plan's algorithm classes (None: the calling thread's own).
Plan::workspace Page
pub fn workspace(&mut self) -> &mut Workspace
The plan's buffers (to pre-size or release them).
Plan::sort Page
pub fn sort<T: Sortable + Send + Sync>(&mut self, v: &mut [T])
Sorts v in the plan's order (on the plan's threads: crate::sort_mt).
Plan::select_nth Page
pub fn select_nth<T: Sortable>(&mut self, v: &mut [T], k: usize)
crate::select_nth_by_order in the plan's order.
Plan::partial_sort Page
pub fn partial_sort<T: Sortable>(&mut self, v: &mut [T], k: usize)
crate::partial_sort_by_order in the plan's order.
Plan::sort_kv Page
pub fn sort_kv<K: Sortable + Send + Sync, V: Copy + Send + Sync>(
&mut self,
keys: &mut [K],
values: &mut [V],
)
crate::sort_kv_by_order in the plan's order, through the plan's workspace (sort_kv_with); on the plan's
threads crate::sort_kv_mt (stable - also an order of the unstable sort).
Plan::sort_kv_stable Page
pub fn sort_kv_stable<K: Sortable + Send + Sync, V: Copy + Send + Sync>(
&mut self,
keys: &mut [K],
values: &mut [V],
)
crate::sort_kv_stable_by_order in the plan's order, through the plan's workspace (sort_kv_stable_with); on
the plan's threads crate::sort_kv_mt.
Plan::select_nth_kv Page
pub fn select_nth_kv<K: Sortable, V: Copy>(&mut self, keys: &mut [K], values: &mut [V], k: usize)
crate::select_nth_kv_by_order in the plan's order, through the plan's workspace.
Plan::partial_sort_kv Page
pub fn partial_sort_kv<K: Sortable, V: Copy>(&mut self, keys: &mut [K], values: &mut [V], k: usize)
crate::partial_sort_kv_by_order in the plan's order, through the plan's workspace.
Plan::argsort_into Page
pub fn argsort_into<T: Sortable + Send + Sync>(&mut self, v: &[T], out: &mut [u64])
The stable permutation of v in the plan's order into out, through the plan's workspace; on the plan's threads
crate::sort_indexed_mt (the same permutation).
Plan::top_k_into Page
pub fn top_k_into<T: Sortable, I: Index>(
&mut self,
v: &[T],
sorted: bool,
values: &mut [T],
indices: &mut [I],
) -> usize
crate::top_k_into in the plan's order, through the plan's workspace (sorted: the k keys in order); returns k.
Cancellation and progress
sort_rows_with Page
pub fn sort_rows_with<T: Sortable>(
data: &mut [T],
row_len: usize,
order: Order,
ctl: &Control<'_>,
) -> Result<(), Cancelled>
crate::sort_rows with cancellation and progress: rows in blocks of about 64K keys, a check before each block.
Remarks: Not stable (rule 5): keys that are equal but can be told apart (NaNs with different bits) may change places. Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
sort_segments_with Page
pub fn sort_segments_with<T: Sortable, I: Index>(
data: &mut [T],
offsets: &[I],
order: Order,
ctl: &Control<'_>,
) -> Result<(), Cancelled>
crate::sort_segments with cancellation and progress: a check before each segment and after each ~64K keys of
small segments.
Remarks: Not stable (rule 5): keys that are equal but can be told apart (NaNs with different bits) may change places. Keys follow the key order: -0.0 before +0.0, every NaN in one block, last unless NaNs-first is asked for.
Cancelled Page
pub struct Cancelled;
The operation stopped at a cancellation check (Control::cancel_on).
Control Page
pub struct Control<'a> {
/* private fields */
}
A cancellation flag and a progress callback for the _with operations (sort_rows_with, sort_segments_with,
crate::sort_file_with); both optional, Control::new() = neither.
Control::new Page
pub fn new() -> Self
No cancellation, no progress.
Control::cancel_on Page
pub fn cancel_on(self, flag: &'a AtomicBool) -> Self
Stops the operation at its next check once flag is true.
Control::progress Page
pub fn progress(self, f: &'a dyn Fn(u64, u64) + Sync) -> Self
Calls f(done, total) after each block of work (keys; done reaches total when the operation completes).
Control::is_cancelled Page
pub fn is_cancelled(&self) -> bool
True once the cancellation flag is set (for callers driving their own blocks of work).
Control::report_progress Page
pub fn report_progress(&self, done: u64, total: u64)
Calls the progress callback, if any, with (done, total) (for callers driving their own blocks of work).
Engines, memory and algorithm classes
set_scratch_policy Page
pub fn set_scratch_policy(p: ScratchPolicy)
Sets how Kwker keeps its large scratch buffers between calls (process-wide).
Arguments
p: the policy (seeScratchPolicy); a smaller cache releases kept buffers above it at once.
Example
let old = kwker::scratch_policy();
kwker::set_scratch_policy(kwker::ScratchPolicy { cache_bytes: 0, ..old });
let mut v: Vec<u64> = (0..1_000_000u64).map(|i| i.wrapping_mul(0x9E37_79B9_7F4A_7C15)).collect();
kwker::sort_mt(&mut v, kwker::Order::ASCENDING, 2);
assert_eq!(kwker::scratch_use(false).cached, 0); // nothing kept after the call
kwker::set_scratch_policy(old);
scratch_policy Page
pub fn scratch_policy() -> ScratchPolicy
The scratch policy in force.
set_scratch_allocator Page
pub fn set_scratch_allocator(a: Option<&'static dyn ScratchAllocator>)
Sends Kwker's large scratch buffers to your allocator (process-wide).
Arguments
a: the allocator;Nonegoes back to Kwker's own.
Notes
- Kwker keeps no cache of your buffers: each goes back to you when its call ends.
scratch_use Page
pub fn scratch_use(reset_peak: bool) -> ScratchUse
How much scratch memory Kwker holds now (process-wide).
Arguments
reset_peak:truerestarts the peak count from the bytes in use now.
Returns
A ScratchUse: bytes in use, bytes kept for later calls, and the peak.
buffer_alloc Page
pub fn buffer_alloc(bytes: usize) -> Option<(*mut u8, usize)>
Memory for large results that language bindings create on every call (for example a NumPy array), taken from Kwker's buffer cache so it does not have to be faulted in again. Linux only.
Arguments
bytes: the size needed.
Returns
(pointer, capacity) for buffer_free, uninitialized; None off Linux, below 4 MB, or with
ScratchPolicy::mapped off (allocate as usual then).
buffer_free Page
pub unsafe fn buffer_free(p: *mut u8, cap: usize)
Gives a buffer_alloc block back to the cache.
Safety
p and cap must be as buffer_alloc returned them; give a block back once and do not use it after.
release_scratch Page
pub fn release_scratch()
Returns every kept scratch buffer to the system and frees this thread's argsort workspace.
capabilities Page
pub fn capabilities() -> Capabilities
The capabilities of this build on this machine (features and caches are read once and cached).
set_isa Page
pub fn set_isa(cap: Option<Isa>) -> Isa
Caps the engine (instruction set) this process's later calls run on, at run time. Results never change, only speed: use it for A/B timings, debugging, or matching another machine.
Arguments
cap: the highest engine to use, for exampleSome(Isa::Avx2)on an AVX-512 machine orSome(Isa::Portable);Nonerestores the best engine the CPU (and theKWKER_ISAenvironment variable) allow.
Returns
The engine now in use.
Example
let best = kwker::set_isa(None);
assert_eq!(kwker::set_isa(Some(kwker::Isa::Portable)), kwker::Isa::Portable);
let mut v = vec![3u32, 1, 2];
kwker::sort(&mut v);
assert_eq!(v, [1, 2, 3]);
assert_eq!(kwker::set_isa(None), best);
Notes
- Process-wide. A cap above what the CPU runs gives the best engine it does.
Remarks: The engine changes only the speed, never a result (rule 11).
Examples: Runtime controls: Fallback switches
set_scratch_limit Page
pub fn set_scratch_limit(bytes: Option<usize>)
Limits the extra memory this thread's later sorts may allocate. A fast path that needs more is skipped: the results are the same, some inputs sort slower.
Arguments
bytes: the limit;Some(0)allocates nothing at all,None(the default) means no limit.
Example
kwker::set_scratch_limit(Some(0));
let mut v: Vec<u64> = (0..100_000u64).map(|i| i * 7919 % 1000).collect();
kwker::sort(&mut v); // no allocation: the counting path is skipped, the in-place recursion sorts
assert!(v.windows(2).all(|w| w[0] <= w[1]));
kwker::set_scratch_limit(None);
Notes
- It covers the in-place operations (sort, select, partial sort, key-value sorts), not functions that return new vectors (argsort, top-k, rank, ...).
- Without the
stdfeature (no_std) the limit is one for the whole program.
Remarks: The scratch limit changes only the speed and memory use, never a result (rule 12).
Examples: Large data: Limit the extra memory
scratch_limit Page
pub fn scratch_limit() -> Option<usize>
This thread's scratch limit (set_scratch_limit); None = no limit.
Remarks: The scratch limit changes only the speed and memory use, never a result (rule 12).
Examples: Large data: Limit the extra memory
set_algorithms Page
pub fn set_algorithms(a: Algorithms) -> Algorithms
Allows only the given algorithm classes in this thread's later calls. A skipped class changes only the time, never the result. Use it for predictable run times, or to isolate a path when a result looks wrong.
Arguments
a: the classes to allow (seeAlgorithms); the default isAlgorithms::ALL.
Returns
The previous setting.
Example
use kwker::Algorithms;
let prev = kwker::set_algorithms(Algorithms::ALL - Algorithms::COUNTING);
let mut v: Vec<u32> = (0..100_000u32).map(|i| i % 7).collect();
kwker::sort(&mut v); // seven values: sorted by partitioning, not counted
assert!(v.windows(2).all(|w| w[0] <= w[1]));
kwker::set_algorithms(prev);
Notes
- The helper threads of multithreaded calls follow the caller's setting. Without the
stdfeature the setting is one for the whole program.
algorithms Page
pub fn algorithms() -> Algorithms
This thread's permitted algorithm classes (set_algorithms).
isa Page
pub fn isa() -> Isa
The engine (instruction set) sorts run on, on this machine.
Notes
KWKER_ISA=avx2orKWKER_ISA=portable, set before the first sort, caps it;set_isachanges it at run time.
Remarks: The engine changes only the speed, never a result (rule 11).
Examples: Runtime controls: Fallback switches
is_accelerated Page
pub fn is_accelerated() -> bool
True if sorts run on one of the SIMD engines (false: the portable fallback).
build_info Page
pub fn build_info() -> &'static str
How this build was made: the engines compiled in and the compiler, or why only the portable fallback was built.
isa says which engine this machine runs.
scratch_bound Page
pub fn scratch_bound(op: ScratchOp, n: usize, key_bytes: usize, value_bytes: usize) -> usize
The most scratch memory an operation allocates for a given input size on this machine, with no scratch limit.
Use it to size set_scratch_limit or to check memory headroom before a call.
Arguments
op: the operation (seeScratchOp).n: the number of keys.key_bytes: the size of a key, in bytes.value_bytes: the size of a value, for key-value operations (0 otherwise).
Returns
An upper bound in bytes.
observe Page
pub fn observe<R>(f: impl FnOnce() -> R) -> (R, Observation)
Runs f and reports which internal stages its Kwker calls went through and how long each took: a look inside
for performance questions.
Arguments
f: the code to observe.
Returns
(f's result, the report); see Observation.
Example
let mut v: Vec<u32> = (0..100_000u32).map(|i| i.wrapping_mul(2654435761)).collect();
let ((), o) = kwker::observe(|| kwker::sort(&mut v));
assert!(v.windows(2).all(|w| w[0] <= w[1]));
if o.traced {
assert!(!o.stages.is_empty());
}
println!("{o}");
Notes
- The x86 engines (AVX-512, AVX2) report their stages; the portable, NEON and SVE engines report the engine only.
- Process-wide: calls on other threads at the same time are counted too.
ScratchPolicy Page
pub struct ScratchPolicy {
pub cache_bytes: usize,
pub mapped: bool,
pub huge_pages: bool,
}
How Kwker keeps the large scratch buffers its calls allocate (process-wide; see set_scratch_policy).
Fields
cache_bytes: Bytes of released buffers kept for later calls (default 1 GiB): a repeated large call then takes no page faults. 0 returns every buffer to the system when its call ends.mapped: Large buffers (4 MiB and more; 64 KiB for some key-value buffers) from the library's own 2 MiB-aligned anonymous mappings (Linux; the default) - false: from the global allocator like the small ones.huge_pages: Ask for transparent huge pages on new mappings (MADV_HUGEPAGE). Off by default (an advised fault may compact memory first: erratic slowdowns were measured); the environment variable KWKER_HUGEPAGE switches it on at startup.
ScratchAllocator Page
pub trait ScratchAllocator: Send + Sync {
fn alloc(&self, bytes: usize, align: usize) -> *mut u8;
unsafe fn free(&self, p: *mut u8, bytes: usize, align: usize);
}
Your allocator for Kwker's scratch buffers, such as a database's memory pool or an arena (see
set_scratch_allocator). It is called from any thread, concurrently.
ScratchUse Page
pub struct ScratchUse {
pub in_use: usize,
pub cached: usize,
pub peak: usize,
}
What the library's scratch buffers hold (scratch_use).
Fields
in_use: Bytes of buffers in use by running calls now (process-wide).cached: Bytes of released buffers kept for later calls (ScratchPolicy::cache_bytes;release_scratchfrees them).peak: The most bytes in use at once since the last reset.
Capabilities Page
pub struct Capabilities {
pub version: &'static str,
pub isa: Isa,
pub engines_built: Vec<Isa>,
pub engines_usable: Vec<Isa>,
pub features: Vec<&'static str>,
pub sve_vector_bits: Option<u32>,
pub l1d: Option<usize>,
pub l2: Option<usize>,
pub l3: Option<usize>,
pub cpus: usize,
pub default_threads: usize,
pub scratch_limit: Option<usize>,
pub build: &'static str,
}
What Kwker does on this machine. From capabilities.
Fields
version: The library version.isa: The engine sorts run on (crate::isa).engines_built: The engines this build compiled in (the portable fallback always).engines_usable: Those of them this CPU can run.features: The CPU features the engines use, as detected (x86: AVX-512 subsets, AVX2, BMI2, ...; AArch64: NEON, SVE, SVE2).sve_vector_bits: AArch64 with SVE: the vector length in bits.l1d: Data cache sizes in bytes (L1d, L2, L3), where the CPU or the OS reports them.l2: L2 (per core).l3: Last-level cache.cpus: The CPUs this process may run on.default_threads: The thread count ofthreads = 0calls (crate::default_threads).scratch_limit: The calling thread's scratch limit (crate::scratch_limit).build: How the build was made (crate::build_info).
Capabilities::to_json Page
pub fn to_json(&self) -> String
As one JSON object (the C API's kwker_capabilities_json, Python's kwker.capabilities()).
VERSION Page
pub const VERSION: &str = "v26";
Version of the engine (shared numbering of the 32- and 64-bit sorts).
Isa Page
pub enum Isa {
Avx512,
Avx2,
Portable,
Neon,
Simd128,
Sse42,
}
The code that sorts on this machine.
Variants
Avx512: The AVX-512 engine.Avx2: The AVX2 engine.Portable: The portable fallback (slice::sort_unstable).Neon: AArch64: the NEON engine (Advanced SIMD) for 32-bit keys from 16K keys on Neoverse-class cores, the portable fallback for the rest (where it measured faster: 64-bit keys, small inputs, Apple cores).Simd128: WebAssembly SIMD128 (wasm32 builds with the simd128 target feature): the NEON engine's algorithms on SIMD128 lanes for small 32-bit inputs and the radix sort's buckets, the portable radix sort above.Sse42: x86-64 without AVX2 (SSE4.2): the NEON engine's algorithms on SSE4.2 lanes where they measured faster, the portable fallback for the rest.
Algorithms Page
pub struct Algorithms(_);
Classes of algorithms the sorts may use beyond their comparison core (set_algorithms). The core - in-place
partitioning (sampled pivots or value midpoints), the sorting networks and the depth-guarded fallback - always
runs; each class adds optional paths that a call takes only when its input suits them.
Algorithms::ADAPTIVE Page
pub const ADAPTIVE: Algorithms;
Order detection: sorted, reversed, all-equal and nearly sorted inputs finish early, and runs of equal keys (timestamps, repeated readings) sort as one entry per run.
Algorithms::COUNTING Page
pub const COUNTING: Algorithms;
Distribution paths: few distinct values (matched and counted), a dominant or a few heavy values, counting and rank-counting sorts of narrow or few-valued inputs, hashed counts, the varying-bits counting sort and key compaction. Without it, 8- and 16-bit keys sort by comparison too.
Algorithms::RADIX Page
pub const RADIX: Algorithms;
Radix distribution passes: the MSD / LSD radix sorts and value-space bucket passes (ARM and the portable engine's large inputs, the 128-bit and stable key-value sorts' top levels).
Algorithms::ALL Page
pub const ALL: Algorithms;
Every class (the default).
Algorithms::NONE Page
pub const NONE: Algorithms;
The comparison core alone.
Algorithms::bits Page
pub const fn bits(self) -> u32
The class bits (1 ADAPTIVE, 2 COUNTING, 4 RADIX).
Algorithms::from_bits Page
pub const fn from_bits(bits: u32) -> Algorithms
From class bits; bits above ALL are ignored.
Algorithms::contains Page
pub const fn contains(self, other: Algorithms) -> bool
Whether every class of other is in self.
ScratchOp Page
pub enum ScratchOp {
Sort,
Select,
SortKv,
TopK,
SortKvStable,
Argsort,
Rank,
ReduceByKey,
Sort128,
}
The operations scratch_bound covers.
Variants
Sort:sort,sort_by_orderand the typed sortsSelect:select_nth,partial_sortSortKv:sort_kv(and its ordered form)TopK:top_kSortKvStable:sort_kv_stableArgsort:argsortwith u32 indices, the output includedRank:rank, the output includedReduceByKey:reduce_by_key, the outputs includedSort128:sort_u128/sort_i128
Stage Page
pub struct Stage {
pub id: u16,
pub name: &'static str,
pub about: &'static str,
pub count: u64,
pub cycles: u64,
}
One engine stage that ran under observe: its id and name in the engines' stage list (for example r32.vbits, the
32-bit root's varying-bits counting sort; rec64.comp.keys, keys the 64-bit recursion sorted on its composite
path), what it is, how often it ran - for the .keys counting sites the keys it handled - and the CPU cycles spent
in it (timed stages, nested stages included; 0 for the others).
Fields
id: The stage id.name: Its name (stage Nwhere the build has no stage list).about: What it is.count: Times it ran (counting sites: keys handled).cycles: Cycles spent in it (timed stages; the cycle counter's units - TSC on x86).
Observation Page
pub struct Observation {
pub isa: Isa,
pub traced: bool,
pub stages: Vec<Stage>,
pub scratch_peak: usize,
}
What the calls inside observe did.
Fields
isa: The engine in use (crate::isa).traced: Whether this build records the engines' stages: the x86 engines (AVX-512, AVX2); false for the portable, NEON and SVE paths, whose stages are not traced (thenstagesis empty).stages: The stages that ran, by id.scratch_peak: The most bytes of the library's scratch buffers (crate::ScratchPolicy) in use at once during the observation, above those in use when it began (process-wide, every engine; 0 for an observation nested in another).
Observation::stage Page
pub fn stage(&self, name: &str) -> Option<&Stage>
The stage name if it ran.
Observation::to_json Page
pub fn to_json(&self) -> String
The report as JSON: {"isa":"avx512","traced":true,"scratch_peak":0,"stages":[{"id":40,"name":"r32.enter", "about":"...","count":1,"cycles":0},...]}.
Notes
Floating-point keys sort -0.0 before +0.0 and every NaN after the other values (positive NaNs first,
then negative ones, each sign by f64::total_cmp), so the output depends only on the input's bit patterns. The full rules are in Behavior.