use crate::utils::{BinSafe, Sig};
use mem_dbg::*;
use num_primitive::{PrimitiveNumberAs, PrimitiveUnsigned};
use rdst::RadixKey;
use std::fmt::Display;
pub trait ShardEdge<S, const K: usize>: Default + Display + Clone + Copy + Send + Sync {
type SortSigVal<V: BinSafe>: RadixKey + Send + Sync + Copy + PartialEq;
type LocalSig: Sig;
type Vertex: PrimitiveUnsigned + PrimitiveNumberAs<usize>;
fn set_up_shards(&mut self, n: usize, eps: f64);
fn set_up_graphs(&mut self, n: usize, max_shard: usize) -> (f64, bool);
fn shard_high_bits(&self) -> u32;
fn num_sort_keys(&self) -> usize;
fn sort_key(&self, sig: S) -> usize;
fn edge_hash(&self, sig: Self::LocalSig) -> u64;
fn num_shards(&self) -> usize {
1 << self.shard_high_bits()
}
fn num_vertices(&self) -> usize;
fn shard(&self, sig: S) -> usize;
fn local_sig(&self, sig: S) -> Self::LocalSig;
fn local_edge(&self, local_sig: Self::LocalSig) -> [usize; K];
fn edge(&self, sig: S) -> [usize; K];
#[inline(always)]
fn remixed_hash(&self, sig: S) -> u64 {
super::mix64(self.edge_hash(self.local_sig(sig)))
}
}
macro_rules! fixed_point_inv_64 {
($x:expr, $n:expr) => {
(($x as u64 * $n as u64) >> 32) as usize
};
}
macro_rules! fixed_point_inv_128 {
($x:expr, $n:expr) => {
(($x as u128 * $n as u128) >> 64) as usize
};
}
fn sharding_high_bits(n: usize, eps: f64) -> u32 {
let t = (n as f64 * eps * eps / 2.0).max(1.);
(t.log2() - t.ln().max(1.).log2()).floor() as u32
}
#[cfg(feature = "mwhc")]
mod mwhc {
use crate::utils::SigVal;
use super::*;
#[derive(Debug, Clone, Copy, MemSize, MemDbg)]
#[mem_size(flat)]
#[cfg_attr(feature = "epserde", derive(epserde::Epserde), epserde(deep_copy))]
pub struct Mwhc3Shards {
seg_size: usize,
shard_bits_shift: u32,
}
impl Default for Mwhc3Shards {
fn default() -> Self {
Self {
seg_size: 0,
shard_bits_shift: 63,
}
}
}
impl Display for Mwhc3Shards {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"MWHC (shards) Number of shards: 2^{} Number of vertices per shard: {}",
self.shard_high_bits(),
self.seg_size * 3
)
}
}
fn edge(shard: usize, seg_size: usize, sig: [u64; 2]) -> [usize; 3] {
let mut start = shard * seg_size * 3;
let v0 = fixed_point_inv_64!(sig[0] as u32, seg_size) + start;
start += seg_size;
let v1 = fixed_point_inv_64!(sig[1] >> 32, seg_size) + start;
start += seg_size;
let v2 = fixed_point_inv_64!(sig[1] as u32, seg_size) + start;
[v0, v1, v2]
}
fn dup_edge_high_bits(arity: usize, n: usize, c: f64, eta: f64) -> u32 {
let n = n as f64;
match arity {
3 => (0.5
* (n.log2() + 1. + 3. * c.log2() - 3. * 3_f64.log2() + (-(1. - eta).ln()).log2()))
.floor() as u32,
_ => unimplemented!("only arity 3 is supported, got {arity}"),
}
}
impl ShardEdge<[u64; 2], 3> for Mwhc3Shards {
type SortSigVal<V: BinSafe> = SigVal<[u64; 2], V>;
type LocalSig = [u64; 2];
type Vertex = u32;
fn set_up_shards(&mut self, n: usize, eps: f64) {
self.shard_bits_shift =
63 - sharding_high_bits(n, eps).min(dup_edge_high_bits(3, n, 1.23, 0.001));
}
fn set_up_graphs(&mut self, _n: usize, max_shard: usize) -> (f64, bool) {
self.seg_size = ((max_shard as f64 * 1.23) / 3.).ceil() as usize;
if self.shard_high_bits() != 0 {
self.seg_size = self.seg_size.next_multiple_of(128);
}
assert!(self.seg_size * 3 - 1 <= Self::Vertex::MAX as usize);
(1.23, false)
}
#[inline(always)]
fn shard_high_bits(&self) -> u32 {
63 - self.shard_bits_shift
}
fn num_sort_keys(&self) -> usize {
1
}
#[inline(always)]
fn sort_key(&self, _sig: [u64; 2]) -> usize {
0
}
#[inline(always)]
fn edge_hash(&self, sig: [u64; 2]) -> u64 {
sig[1]
}
#[inline(always)]
fn shard(&self, sig: [u64; 2]) -> usize {
(sig[0] >> self.shard_bits_shift >> 1) as usize
}
#[inline(always)]
fn num_vertices(&self) -> usize {
self.seg_size * 3
}
#[inline(always)]
fn local_sig(&self, sig: [u64; 2]) -> Self::LocalSig {
sig
}
#[inline(always)]
fn local_edge(&self, local_sig: Self::LocalSig) -> [usize; 3] {
edge(0, self.seg_size, local_sig)
}
#[inline(always)]
fn edge(&self, sig: [u64; 2]) -> [usize; 3] {
edge(self.shard(sig), self.seg_size, sig)
}
}
#[derive(Debug, Clone, Copy, MemSize, MemDbg, Default)]
#[mem_size(flat)]
#[cfg_attr(feature = "epserde", derive(epserde::Epserde), epserde(deep_copy))]
pub struct Mwhc3NoShards {
seg_size: usize,
}
impl Display for Mwhc3NoShards {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"MWHC (no shards) Number of vertices per shard: {}",
self.seg_size * 3
)
}
}
impl ShardEdge<[u64; 2], 3> for Mwhc3NoShards {
type SortSigVal<V: BinSafe> = SigVal<[u64; 2], V>;
type LocalSig = [u64; 2];
type Vertex = usize;
fn set_up_shards(&mut self, _n: usize, _eps: f64) {}
fn set_up_graphs(&mut self, n: usize, _max_shard: usize) -> (f64, bool) {
self.seg_size = ((n as f64 * 1.23) / 3.).ceil() as usize;
(1.23, false)
}
#[inline(always)]
fn shard_high_bits(&self) -> u32 {
0
}
fn num_sort_keys(&self) -> usize {
1
}
#[inline(always)]
fn sort_key(&self, _sig: [u64; 2]) -> usize {
0
}
#[inline(always)]
fn edge_hash(&self, sig: [u64; 2]) -> u64 {
sig[1]
}
#[inline(always)]
fn shard(&self, _sig: [u64; 2]) -> usize {
0
}
#[inline(always)]
fn num_vertices(&self) -> usize {
self.seg_size * 3
}
#[inline(always)]
fn local_sig(&self, sig: [u64; 2]) -> Self::LocalSig {
sig
}
fn local_edge(&self, local_sig: Self::LocalSig) -> [usize; 3] {
let seg_size = self.seg_size;
let v0 = fixed_point_inv_128!(local_sig[0], seg_size);
let v1 = fixed_point_inv_128!(local_sig[1], seg_size) + seg_size;
let v2 = fixed_point_inv_128!(local_sig[0] ^ local_sig[1], seg_size) + 2 * seg_size;
[v0, v1, v2]
}
#[inline(always)]
fn edge(&self, sig: [u64; 2]) -> [usize; 3] {
self.local_edge(sig)
}
}
}
#[cfg(feature = "mwhc")]
pub use mwhc::*;
mod fuse {
use crate::utils::{BinSafe, SigVal};
use super::*;
use lambert_w::lambert_w0;
use rdst::RadixKey;
#[derive(Debug, Clone, Copy, MemSize, MemDbg)]
#[mem_size(flat)]
#[cfg_attr(feature = "epserde", derive(epserde::Epserde), epserde(deep_copy))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FuseLge3Shards {
shard_bits_shift: u32,
log2_seg_size: u32,
l: u32,
}
impl Default for FuseLge3Shards {
fn default() -> Self {
Self {
shard_bits_shift: 63,
log2_seg_size: 0,
l: 0,
}
}
}
impl Display for FuseLge3Shards {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Fuse (shards) Number of shards: 2^{} Segment size: 2^{} Number of segments: {}",
self.shard_high_bits(),
self.log2_seg_size,
self.l + 2
)
}
}
fn edge_1(shard: usize, log2_seg_size: u32, l: u32, sig: [u64; 1]) -> [usize; 3] {
let start = (shard * (l as usize + 2)) << log2_seg_size;
let v0 = start + fixed_point_inv_128!(sig[0], (l as u64) << log2_seg_size);
let seg_size = 1 << log2_seg_size;
let seg_size_mask = seg_size - 1;
let mut v1 = v0 + seg_size;
v1 ^= (sig[0] as usize) & seg_size_mask;
let mut v2 = v1 + seg_size;
v2 ^= ((sig[0] >> log2_seg_size) as usize) & seg_size_mask;
[v0, v1, v2]
}
fn edge_2(log2_seg_size: u32, l: u32, sig: [u64; 2]) -> [usize; 3] {
let v0 = fixed_point_inv_128!(sig[0], (l as u64) << log2_seg_size);
let segment_size = 1 << log2_seg_size;
let segment_mask = segment_size - 1;
let mut v1 = v0 + segment_size;
v1 ^= (sig[1] >> 32) as usize & segment_mask;
let mut v2 = v1 + segment_size;
v2 ^= sig[1] as u32 as usize & segment_mask;
[v0, v1, v2]
}
impl FuseLge3Shards {
const MAX_LIN_SIZE: usize = 800_000;
const HALF_MAX_LIN_SHARD_SIZE: usize = 50_000;
const MIN_FUSE_SHARD: usize = 10_000_000;
fn c(arity: usize, n: usize) -> f64 {
match arity {
3 => {
debug_assert!(n > 2 * Self::HALF_MAX_LIN_SHARD_SIZE);
if n <= Self::MIN_FUSE_SHARD / 2 {
1.125
} else if n <= Self::MIN_FUSE_SHARD {
1.12
} else if n <= 2 * Self::MIN_FUSE_SHARD {
1.11
} else {
1.105
}
}
_ => unimplemented!("only arity 3 is supported, got {arity}"),
}
}
fn lin_log2_seg_size(arity: usize, n: usize) -> u32 {
match arity {
3 => {
debug_assert!(n <= 2 * Self::HALF_MAX_LIN_SHARD_SIZE);
(0.85 * (n.max(1) as f64).ln()).floor().max(1.) as u32
}
_ => unimplemented!("only arity 3 is supported, got {arity}"),
}
}
const A: f64 = 0.41;
const B: f64 = -3.0;
pub fn log2_seg_size(arity: usize, n: usize) -> u32 {
match arity {
3 => if n <= 2 * Self::MIN_FUSE_SHARD {
let n = n.max(1) as f64;
n.ln() / (3.33_f64).ln() + 2.25
} else {
let n = n.max(1) as f64;
Self::A * n.ln() * n.ln().max(1.).ln() + Self::B
}
.floor() as u32,
_ => unimplemented!("only arity 3 is supported, got {arity}"),
}
}
fn dup_edge_high_bits(arity: usize, n: usize, c: f64, eta: f64) -> u32 {
let n = n as f64;
match arity {
3 => {
let subexpr = (1. / (2. * Self::A))
* (-n / (2. * c * (1. - eta).ln()) - 2. * Self::B).log2();
(n.log2() - subexpr / (2.0_f64.ln() * lambert_w0(subexpr))).floor() as u32
}
_ => unimplemented!("only arity 3 is supported, got {arity}"),
}
}
}
#[derive(Debug, Clone, Copy, MemSize, MemDbg)]
#[repr(transparent)]
pub struct LowSortSigVal<V: BinSafe>(SigVal<[u64; 2], V>);
impl<V: BinSafe> RadixKey for LowSortSigVal<V> {
const LEVELS: usize = 8;
fn get_level(&self, level: usize) -> u8 {
(self.0.sig[1] >> ((level % 8) * 8)) as u8
}
}
impl<V: BinSafe> PartialEq for LowSortSigVal<V> {
fn eq(&self, other: &Self) -> bool {
self.0.sig[1] == other.0.sig[1]
}
}
impl ShardEdge<[u64; 2], 3> for FuseLge3Shards {
type SortSigVal<V: BinSafe> = LowSortSigVal<V>;
type LocalSig = [u64; 1];
type Vertex = u32;
fn set_up_shards(&mut self, n: usize, eps: f64) {
self.shard_bits_shift = 63
- if n <= Self::MAX_LIN_SIZE {
(n / Self::HALF_MAX_LIN_SHARD_SIZE).max(1).ilog2()
} else {
sharding_high_bits(n, eps)
.min(Self::dup_edge_high_bits(3, n, 1.105, 0.001))
.min((n / Self::MIN_FUSE_SHARD).max(1).ilog2()) };
}
fn set_up_graphs(&mut self, n: usize, max_shard: usize) -> (f64, bool) {
let (c, lge);
(c, self.log2_seg_size, lge) = if n <= 100 {
(1.23, Self::lin_log2_seg_size(3, max_shard), true)
} else if n <= Self::MAX_LIN_SIZE {
(1.125, Self::lin_log2_seg_size(3, max_shard), true)
} else {
(
Self::c(3, max_shard),
Self::log2_seg_size(3, max_shard),
false,
)
};
self.l = ((c * max_shard as f64).ceil() as usize)
.div_ceil(1 << self.log2_seg_size)
.saturating_sub(2)
.max(1)
.try_into()
.unwrap();
assert!(((self.l as usize + 2) << self.log2_seg_size) - 1 <= u32::MAX as usize);
(c, lge)
}
#[inline(always)]
fn shard_high_bits(&self) -> u32 {
63 - self.shard_bits_shift
}
fn num_sort_keys(&self) -> usize {
self.l as usize
}
#[inline(always)]
fn sort_key(&self, sig: [u64; 2]) -> usize {
fixed_point_inv_128!(sig[1], self.l)
}
#[inline(always)]
fn edge_hash(&self, sig: Self::LocalSig) -> u64 {
sig[0]
}
#[inline(always)]
fn shard(&self, sig: [u64; 2]) -> usize {
(sig[0] >> self.shard_bits_shift >> 1) as usize
}
#[inline(always)]
fn num_vertices(&self) -> usize {
(self.l as usize + 2) << self.log2_seg_size
}
#[inline(always)]
fn local_sig(&self, sig: [u64; 2]) -> Self::LocalSig {
[sig[1]]
}
#[inline(always)]
fn local_edge(&self, local_sig: Self::LocalSig) -> [usize; 3] {
edge_1(0, self.log2_seg_size, self.l, local_sig)
}
#[inline(always)]
fn edge(&self, sig: [u64; 2]) -> [usize; 3] {
edge_1(self.shard(sig), self.log2_seg_size, self.l, [sig[1]])
}
}
#[derive(Debug, Clone, Copy, MemSize, MemDbg, Default)]
#[mem_size(flat)]
#[cfg_attr(feature = "epserde", derive(epserde::Epserde), epserde(deep_copy))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FuseLge3NoShards {
log2_seg_size: u32,
l: u32,
}
impl Display for FuseLge3NoShards {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Fuse (no shards) Segment size: 2^{} Number of segments: {}",
self.log2_seg_size,
self.l + 2
)
}
}
impl FuseLge3NoShards {
fn c(arity: usize, n: usize) -> f64 {
match arity {
3 => {
if n <= FuseLge3Shards::MAX_LIN_SIZE {
0.168 + (300000_f64).ln().ln() / (n as f64 + 200000.).ln().max(1.).ln()
} else {
FuseLge3Shards::c(3, n)
}
}
_ => unimplemented!("only arity 3 is supported, got {arity}"),
}
}
pub fn log2_seg_size(arity: usize, n: usize) -> u32 {
let n = n.max(1) as f64;
match arity {
3 =>
{
(n.ln() / (3.33_f64).ln() + 2.25).floor() as u32
}
_ => unimplemented!("only arity 3 is supported, got {arity}"),
}
}
fn set_up_graphs(&mut self, n: usize, max_vertices: u128) -> (f64, bool) {
let (c, lge);
(c, self.log2_seg_size, lge) = if n <= 100 {
(1.23, FuseLge3Shards::lin_log2_seg_size(3, n), true)
} else if n <= 2 * FuseLge3Shards::HALF_MAX_LIN_SHARD_SIZE {
(1.13, FuseLge3Shards::lin_log2_seg_size(3, n), true)
} else {
(Self::c(3, n), Self::log2_seg_size(3, n).min(18), false)
};
let num_vertices = (c * n as f64).ceil() as u128;
assert!(
num_vertices <= max_vertices,
"FuseLge3NoShards does not support more than {max_vertices} vertices, but you are requesting {num_vertices}"
);
self.l = num_vertices
.div_ceil(1 << self.log2_seg_size)
.saturating_sub(2)
.max(1)
.try_into()
.unwrap();
(c, lge)
}
}
impl ShardEdge<[u64; 2], 3> for FuseLge3NoShards {
type SortSigVal<V: BinSafe> = SigVal<[u64; 2], V>;
type LocalSig = [u64; 2];
type Vertex = usize;
fn set_up_shards(&mut self, _n: usize, _eps: f64) {}
fn set_up_graphs(&mut self, n: usize, _max_shard: usize) -> (f64, bool) {
FuseLge3NoShards::set_up_graphs(self, n, Self::Vertex::MAX as u128 + 1)
}
#[inline(always)]
fn shard_high_bits(&self) -> u32 {
0
}
fn num_sort_keys(&self) -> usize {
self.l as usize
}
#[inline(always)]
fn sort_key(&self, sig: [u64; 2]) -> usize {
fixed_point_inv_128!(sig[0], self.l)
}
#[inline(always)]
fn edge_hash(&self, sig: [u64; 2]) -> u64 {
sig[1]
}
#[inline(always)]
fn shard(&self, _sig: [u64; 2]) -> usize {
0
}
#[inline(always)]
fn num_vertices(&self) -> usize {
(self.l as usize + 2) << self.log2_seg_size
}
#[inline(always)]
fn local_sig(&self, sig: [u64; 2]) -> Self::LocalSig {
sig
}
#[inline(always)]
fn local_edge(&self, local_sig: Self::LocalSig) -> [usize; 3] {
edge_2(self.log2_seg_size, self.l, local_sig)
}
#[inline(always)]
fn edge(&self, sig: [u64; 2]) -> [usize; 3] {
edge_2(self.log2_seg_size, self.l, sig)
}
}
impl ShardEdge<[u64; 1], 3> for FuseLge3NoShards {
type SortSigVal<V: BinSafe> = SigVal<[u64; 1], V>;
type LocalSig = [u64; 1];
type Vertex = u32;
fn set_up_shards(&mut self, _n: usize, _eps: f64) {}
fn set_up_graphs(&mut self, n: usize, _max_shard: usize) -> (f64, bool) {
FuseLge3NoShards::set_up_graphs(self, n, Self::Vertex::MAX as u128 + 1)
}
#[inline(always)]
fn shard_high_bits(&self) -> u32 {
0
}
fn num_sort_keys(&self) -> usize {
self.l as usize
}
#[inline(always)]
fn sort_key(&self, sig: [u64; 1]) -> usize {
fixed_point_inv_128!(sig[0], self.l)
}
#[inline(always)]
fn edge_hash(&self, sig: [u64; 1]) -> u64 {
sig[0]
}
#[inline(always)]
fn shard(&self, _sig: [u64; 1]) -> usize {
0
}
#[inline(always)]
fn local_sig(&self, sig: [u64; 1]) -> Self::LocalSig {
sig
}
#[inline(always)]
fn num_vertices(&self) -> usize {
(self.l as usize + 2) << self.log2_seg_size
}
#[inline(always)]
fn local_edge(&self, sig: Self::LocalSig) -> [usize; 3] {
edge_1(0, self.log2_seg_size, self.l, sig)
}
#[inline(always)]
fn edge(&self, sig: [u64; 1]) -> [usize; 3] {
edge_1(0, self.log2_seg_size, self.l, sig)
}
}
#[derive(Debug, Clone, Copy, MemSize, MemDbg)]
#[mem_size(flat)]
#[cfg_attr(feature = "epserde", derive(epserde::Epserde), epserde(deep_copy))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Default)]
pub struct Fuse3NoShards {
log2_seg_size: u32,
l: u32,
}
impl Display for Fuse3NoShards {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Fuse3 (no shards, no LGE) Segment size: 2^{} Number of segments: {}",
self.log2_seg_size,
self.l + 2
)
}
}
impl Fuse3NoShards {
fn c(n: usize) -> f64 {
let n = n.max(2) as f64;
0.875 + 0.25 * (1.0_f64).max((1e6_f64).ln() / n.ln())
}
fn log2_seg_size(n: usize) -> u32 {
let n = n.max(1) as f64;
(n.ln() / (3.33_f64).ln() + 2.25).floor() as u32
}
}
impl ShardEdge<[u64; 1], 3> for Fuse3NoShards {
type SortSigVal<V: BinSafe> = SigVal<[u64; 1], V>;
type LocalSig = [u64; 1];
type Vertex = u32;
fn set_up_shards(&mut self, _n: usize, _eps: f64) {}
fn set_up_graphs(&mut self, n: usize, _max_shard: usize) -> (f64, bool) {
let c = Self::c(n);
self.log2_seg_size = Fuse3NoShards::log2_seg_size(n);
let num_vertices = (c * n as f64).ceil() as u128;
assert!(
num_vertices <= Self::Vertex::MAX as u128 + 1,
"Fuse3NoShards does not support more than {} vertices, but you are requesting {num_vertices}",
Self::Vertex::MAX as u128 + 1
);
self.l = num_vertices
.div_ceil(1 << self.log2_seg_size)
.saturating_sub(2)
.max(1)
.try_into()
.unwrap();
(c, false) }
#[inline(always)]
fn shard_high_bits(&self) -> u32 {
0
}
fn num_sort_keys(&self) -> usize {
self.l as usize
}
#[inline(always)]
fn sort_key(&self, sig: [u64; 1]) -> usize {
fixed_point_inv_128!(sig[0], self.l)
}
#[inline(always)]
fn edge_hash(&self, sig: [u64; 1]) -> u64 {
sig[0]
}
#[inline(always)]
fn shard(&self, _sig: [u64; 1]) -> usize {
0
}
#[inline(always)]
fn local_sig(&self, sig: [u64; 1]) -> Self::LocalSig {
sig
}
#[inline(always)]
fn num_vertices(&self) -> usize {
(self.l as usize + 2) << self.log2_seg_size
}
#[inline(always)]
fn local_edge(&self, sig: Self::LocalSig) -> [usize; 3] {
edge_1(0, self.log2_seg_size, self.l, sig)
}
#[inline(always)]
fn edge(&self, sig: [u64; 1]) -> [usize; 3] {
edge_1(0, self.log2_seg_size, self.l, sig)
}
}
impl ShardEdge<[u64; 2], 3> for Fuse3NoShards {
type SortSigVal<V: BinSafe> = SigVal<[u64; 1], V>;
type LocalSig = [u64; 1];
type Vertex = u32;
fn set_up_shards(&mut self, _n: usize, _eps: f64) {}
fn set_up_graphs(&mut self, n: usize, _max_shard: usize) -> (f64, bool) {
let c = Self::c(n);
self.log2_seg_size = Fuse3NoShards::log2_seg_size(n);
let num_vertices = (c * n as f64).ceil() as u128;
assert!(
num_vertices <= Self::Vertex::MAX as u128 + 1,
"Fuse3NoShards does not support more than {} vertices, but you are requesting {num_vertices}",
Self::Vertex::MAX as u128 + 1
);
self.l = num_vertices
.div_ceil(1 << self.log2_seg_size)
.saturating_sub(2)
.max(1)
.try_into()
.unwrap();
(c, false)
}
#[inline(always)]
fn shard_high_bits(&self) -> u32 {
0
}
fn num_sort_keys(&self) -> usize {
self.l as usize
}
#[inline(always)]
fn sort_key(&self, sig: [u64; 2]) -> usize {
fixed_point_inv_128!(sig[1], self.l)
}
#[inline(always)]
fn edge_hash(&self, sig: [u64; 1]) -> u64 {
sig[0]
}
#[inline(always)]
fn shard(&self, _sig: [u64; 2]) -> usize {
0
}
#[inline(always)]
fn local_sig(&self, sig: [u64; 2]) -> Self::LocalSig {
[sig[1]]
}
#[inline(always)]
fn num_vertices(&self) -> usize {
(self.l as usize + 2) << self.log2_seg_size
}
#[inline(always)]
fn local_edge(&self, sig: Self::LocalSig) -> [usize; 3] {
edge_1(0, self.log2_seg_size, self.l, sig)
}
#[inline(always)]
fn edge(&self, sig: [u64; 2]) -> [usize; 3] {
edge_1(0, self.log2_seg_size, self.l, [sig[1]])
}
}
#[derive(Debug, Clone, Copy, MemSize, MemDbg)]
#[mem_size(flat)]
#[cfg_attr(feature = "epserde", derive(epserde::Epserde), epserde(deep_copy))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Fuse3Shards {
shard_bits_shift: u32,
log2_seg_size: u32,
l: u32,
}
impl Default for Fuse3Shards {
fn default() -> Self {
Self {
shard_bits_shift: 63,
log2_seg_size: 0,
l: 0,
}
}
}
impl Display for Fuse3Shards {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Fuse3 (shards, no LGE) Number of shards: 2^{} Segment size: 2^{} Number of segments: {}",
self.shard_high_bits(),
self.log2_seg_size,
self.l + 2
)
}
}
impl Fuse3Shards {
const MIN_SHARD: usize = 10_000_000;
fn c(n: usize) -> f64 {
let n = n.max(2) as f64;
0.875 + 0.25 * (1.0_f64).max((1e6_f64).ln() / n.ln())
}
const A: f64 = 0.41;
const B: f64 = -3.0;
const LARGE_SHARD_THRESHOLD: usize = 100_000_000;
fn log2_seg_size(n: usize) -> u32 {
let n = n.max(1) as f64;
if (n as usize) <= Self::LARGE_SHARD_THRESHOLD {
(n.ln() / (3.33_f64).ln() + 2.25).floor() as u32
} else {
(Self::A * n.ln() * n.ln().max(1.).ln() + Self::B).floor() as u32
}
}
fn dup_edge_high_bits(n: usize, c: f64, eta: f64) -> u32 {
let n = n as f64;
let subexpr =
(1. / (2. * Self::A)) * (-n / (2. * c * (1. - eta).ln()) - 2. * Self::B).log2();
(n.log2() - subexpr / (2.0_f64.ln() * lambert_w0(subexpr))).floor() as u32
}
}
impl ShardEdge<[u64; 2], 3> for Fuse3Shards {
type SortSigVal<V: BinSafe> = LowSortSigVal<V>;
type LocalSig = [u64; 1];
type Vertex = u32;
fn set_up_shards(&mut self, n: usize, eps: f64) {
self.shard_bits_shift = 63
- if n <= Self::MIN_SHARD {
0
} else {
sharding_high_bits(n, eps)
.min(Self::dup_edge_high_bits(n, 1.125, 0.001))
.min((n / Self::MIN_SHARD).max(1).ilog2())
};
}
fn set_up_graphs(&mut self, _n: usize, max_shard: usize) -> (f64, bool) {
let c = Self::c(max_shard);
self.log2_seg_size = Self::log2_seg_size(max_shard);
self.l = ((c * max_shard as f64).ceil() as usize)
.div_ceil(1 << self.log2_seg_size)
.saturating_sub(2)
.max(1)
.try_into()
.unwrap();
assert!(((self.l as usize + 2) << self.log2_seg_size) - 1 <= u32::MAX as usize);
(c, false) }
#[inline(always)]
fn shard_high_bits(&self) -> u32 {
63 - self.shard_bits_shift
}
fn num_sort_keys(&self) -> usize {
self.l as usize
}
#[inline(always)]
fn sort_key(&self, sig: [u64; 2]) -> usize {
fixed_point_inv_128!(sig[1], self.l)
}
#[inline(always)]
fn edge_hash(&self, sig: Self::LocalSig) -> u64 {
sig[0]
}
#[inline(always)]
fn shard(&self, sig: [u64; 2]) -> usize {
(sig[0] >> self.shard_bits_shift >> 1) as usize
}
#[inline(always)]
fn num_vertices(&self) -> usize {
(self.l as usize + 2) << self.log2_seg_size
}
#[inline(always)]
fn local_sig(&self, sig: [u64; 2]) -> Self::LocalSig {
[sig[1]]
}
#[inline(always)]
fn local_edge(&self, local_sig: Self::LocalSig) -> [usize; 3] {
edge_1(0, self.log2_seg_size, self.l, local_sig)
}
#[inline(always)]
fn edge(&self, sig: [u64; 2]) -> [usize; 3] {
edge_1(self.shard(sig), self.log2_seg_size, self.l, [sig[1]])
}
}
#[derive(Debug, Clone, Copy, MemSize, MemDbg)]
#[mem_size(flat)]
#[cfg_attr(feature = "epserde", derive(epserde::Epserde), epserde(deep_copy))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Default)]
pub struct FuseLge3FullSigs(FuseLge3Shards);
impl Display for FuseLge3FullSigs {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
fn edge_2_big(
shard: usize,
shard_bits_shift: u32,
log2_seg_size: u32,
l: u32,
sig: [u64; 2],
) -> [usize; 3] {
let start = (shard * (l as usize + 2)) << log2_seg_size;
let v0 = start
+ fixed_point_inv_128!(
sig[0].rotate_right(shard_bits_shift).rotate_right(1),
(l as u64) << log2_seg_size
);
let segment_size = 1 << log2_seg_size;
let segment_mask = segment_size - 1;
let mut v1 = v0 + segment_size;
v1 ^= (sig[1] >> 32) as usize & segment_mask;
let mut v2 = v1 + segment_size;
v2 ^= sig[1] as u32 as usize & segment_mask;
[v0, v1, v2]
}
impl ShardEdge<[u64; 2], 3> for FuseLge3FullSigs {
type SortSigVal<V: BinSafe> = SigVal<[u64; 2], V>;
type LocalSig = [u64; 2];
type Vertex = u32;
fn set_up_shards(&mut self, n: usize, eps: f64) {
self.0.set_up_shards(n, eps);
}
fn set_up_graphs(&mut self, n: usize, max_shard: usize) -> (f64, bool) {
self.0.set_up_graphs(n, max_shard)
}
#[inline(always)]
fn shard_high_bits(&self) -> u32 {
self.0.shard_high_bits()
}
fn num_sort_keys(&self) -> usize {
self.0.num_sort_keys()
}
#[inline(always)]
fn sort_key(&self, sig: [u64; 2]) -> usize {
fixed_point_inv_64!(
sig[0].rotate_right(self.0.shard_bits_shift).rotate_right(1) >> 32,
self.0.l
)
}
#[inline(always)]
fn edge_hash(&self, sig: [u64; 2]) -> u64 {
sig[1]
}
#[inline(always)]
fn shard(&self, sig: [u64; 2]) -> usize {
(sig[0] >> self.0.shard_bits_shift >> 1) as usize
}
#[inline(always)]
fn num_vertices(&self) -> usize {
self.0.num_vertices()
}
#[inline(always)]
fn local_sig(&self, sig: [u64; 2]) -> Self::LocalSig {
sig
}
#[inline(always)]
fn local_edge(&self, local_sig: Self::LocalSig) -> [usize; 3] {
edge_2_big(
0,
self.0.shard_bits_shift,
self.0.log2_seg_size,
self.0.l,
local_sig,
)
}
#[inline(always)]
fn edge(&self, sig: [u64; 2]) -> [usize; 3] {
edge_2_big(
self.shard(sig),
self.0.shard_bits_shift,
self.0.log2_seg_size,
self.0.l,
sig,
)
}
}
}
pub use fuse::*;