Push zunrplorkwkt #70
@@ -1,6 +1,6 @@
|
|||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use crate::colgroup::{ColGroup, MatrixGroupOps};
|
use crate::colgroup::{chunked_presence_count, ColGroup, MatrixGroupOps};
|
||||||
use crate::tempbitvec::{TempBitVec, TempBitVecBuilder};
|
use crate::tempbitvec::{TempBitVec, TempBitVecBuilder};
|
||||||
use crate::tempintvec::{TempCompactIntVec, TempCompactIntVecBuilder};
|
use crate::tempintvec::{TempCompactIntVec, TempCompactIntVecBuilder};
|
||||||
|
|
||||||
@@ -11,25 +11,7 @@ use super::persistent::PersistentBitMatrix;
|
|||||||
impl MatrixGroupOps for PersistentBitMatrix {
|
impl MatrixGroupOps for PersistentBitMatrix {
|
||||||
fn partial_group_presence_count(&self, g: &ColGroup, _threshold: u32) -> io::Result<TempCompactIntVec> {
|
fn partial_group_presence_count(&self, g: &ColGroup, _threshold: u32) -> io::Result<TempCompactIntVec> {
|
||||||
// Bit matrices store 0/1 — threshold is structurally always 1.
|
// Bit matrices store 0/1 — threshold is structurally always 1.
|
||||||
let n = self.n();
|
chunked_presence_count(self.n(), &g.indices, |b, c| b.inc_present_fast(self.col_view(c)))
|
||||||
if g.indices.len() < 255 {
|
|
||||||
let mut builder = TempCompactIntVecBuilder::new(n)?;
|
|
||||||
for &c in &g.indices {
|
|
||||||
builder.inc_present_fast(self.col_view(c));
|
|
||||||
}
|
|
||||||
builder.freeze()
|
|
||||||
} else {
|
|
||||||
let mut result = TempCompactIntVecBuilder::new(n)?;
|
|
||||||
for chunk in g.indices.chunks(254) {
|
|
||||||
let mut chunk_b = TempCompactIntVecBuilder::new(n)?;
|
|
||||||
for &c in chunk {
|
|
||||||
chunk_b.inc_present_fast(self.col_view(c));
|
|
||||||
}
|
|
||||||
let frozen = chunk_b.freeze()?;
|
|
||||||
result.add(frozen.view());
|
|
||||||
}
|
|
||||||
result.freeze()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn partial_group_sum(&self, g: &ColGroup) -> io::Result<TempCompactIntVec> {
|
fn partial_group_sum(&self, g: &ColGroup) -> io::Result<TempCompactIntVec> {
|
||||||
|
|||||||
@@ -111,11 +111,7 @@ impl PersistentBitMatrix {
|
|||||||
match self {
|
match self {
|
||||||
Self::Columnar(m) => m.col(c).get(slot) as u32,
|
Self::Columnar(m) => m.col(c).get(slot) as u32,
|
||||||
Self::Packed(m) => m.col_slice(c).get(slot) as u32,
|
Self::Packed(m) => m.col_slice(c).get(slot) as u32,
|
||||||
Self::Sparse(m) => {
|
Self::Sparse(m) => m.get(c, slot),
|
||||||
let mut buf = vec![0u32; m.n_cols()];
|
|
||||||
m.fill_row(slot, &mut buf);
|
|
||||||
buf[c]
|
|
||||||
}
|
|
||||||
Self::Implicit { .. } => 1,
|
Self::Implicit { .. } => 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,6 +151,18 @@ impl PersistentSparseBitMatrix {
|
|||||||
(start, end)
|
(start, end)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Column-major point lookup: value at column `c`, slot `slot`, as 0/1.
|
||||||
|
///
|
||||||
|
/// Avoids `fill_row`'s `n_cols`-wide buffer: walks the row's own decode
|
||||||
|
/// — 1 entry for a singleton row, k̄ entries for a multi-genome row —
|
||||||
|
/// via [`for_each_genome_in_row`](Self::for_each_genome_in_row), instead
|
||||||
|
/// of allocating and scanning a full-width buffer for a single cell.
|
||||||
|
pub fn get(&self, c: usize, slot: usize) -> u32 {
|
||||||
|
let mut found = false;
|
||||||
|
self.for_each_genome_in_row(slot, |g| found |= g == c);
|
||||||
|
found as u32
|
||||||
|
}
|
||||||
|
|
||||||
pub fn row(&self, slot: usize) -> Box<[bool]> {
|
pub fn row(&self, slot: usize) -> Box<[bool]> {
|
||||||
let mut out = vec![false; self.n_cols];
|
let mut out = vec![false; self.n_cols];
|
||||||
self.fill_row_bool(slot, &mut out);
|
self.fill_row_bool(slot, &mut out);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use crate::tempbitvec::{TempBitVec, TempBitVecBuilder};
|
use crate::tempbitvec::{TempBitVec, TempBitVecBuilder};
|
||||||
use crate::tempintvec::TempCompactIntVec;
|
use crate::tempintvec::{TempCompactIntVec, TempCompactIntVecBuilder};
|
||||||
|
|
||||||
// ── ColGroup ──────────────────────────────────────────────────────────────────
|
// ── ColGroup ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -21,6 +21,33 @@ impl ColGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shared chunked-accumulation body behind every `partial_group_presence_count`
|
||||||
|
/// impl: below 255 columns, accumulate directly (`u8` counters never
|
||||||
|
/// overflow); at or above, split into chunks of 254, accumulate each chunk
|
||||||
|
/// into its own builder, and merge chunks with `add`. `inc` supplies the
|
||||||
|
/// one thing that differs per matrix kind — how to bump the counter for
|
||||||
|
/// column `c` — so the chunking/merging logic itself is written once.
|
||||||
|
pub(crate) fn chunked_presence_count(
|
||||||
|
n: usize,
|
||||||
|
indices: &[usize],
|
||||||
|
mut inc: impl FnMut(&mut TempCompactIntVecBuilder, usize),
|
||||||
|
) -> io::Result<TempCompactIntVec> {
|
||||||
|
if indices.len() < 255 {
|
||||||
|
let mut builder = TempCompactIntVecBuilder::new(n)?;
|
||||||
|
for &c in indices { inc(&mut builder, c); }
|
||||||
|
builder.freeze()
|
||||||
|
} else {
|
||||||
|
let mut result = TempCompactIntVecBuilder::new(n)?;
|
||||||
|
for chunk in indices.chunks(254) {
|
||||||
|
let mut chunk_b = TempCompactIntVecBuilder::new(n)?;
|
||||||
|
for &c in chunk { inc(&mut chunk_b, c); }
|
||||||
|
let frozen = chunk_b.freeze()?;
|
||||||
|
result.add(frozen.view());
|
||||||
|
}
|
||||||
|
result.freeze()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── MatrixGroupOps ────────────────────────────────────────────────────────────
|
// ── MatrixGroupOps ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Per-matrix group aggregations.
|
/// Per-matrix group aggregations.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use rayon::prelude::*;
|
|||||||
|
|
||||||
use crate::bitmatrix::{pairwise_matrix, pairwise2_matrix};
|
use crate::bitmatrix::{pairwise_matrix, pairwise2_matrix};
|
||||||
use crate::builder::PersistentCompactIntVecBuilder;
|
use crate::builder::PersistentCompactIntVecBuilder;
|
||||||
use crate::colgroup::{ColGroup, MatrixGroupOps};
|
use crate::colgroup::{chunked_presence_count, ColGroup, MatrixGroupOps};
|
||||||
use crate::format::{HEADER_SIZE, OVERFLOW_ENTRY_SIZE};
|
use crate::format::{HEADER_SIZE, OVERFLOW_ENTRY_SIZE};
|
||||||
use crate::meta::MatrixMeta;
|
use crate::meta::MatrixMeta;
|
||||||
use crate::reader::PersistentCompactIntVec;
|
use crate::reader::PersistentCompactIntVec;
|
||||||
@@ -186,53 +186,33 @@ impl PackedCompactIntMatrix {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pair_partial_bray(&self, i: usize, j: usize) -> u64 {
|
// Every pair formula below delegates to `IntSliceView` (`views.rs`) —
|
||||||
self.col_view(i).iter().zip(self.col_view(j).iter()).map(|(a, b)| a.min(b) as u64).sum()
|
// the canonical implementation, also used by `ColumnarCompactIntMatrix`
|
||||||
}
|
// via `PersistentCompactIntVec`. No formula is re-derived here.
|
||||||
fn pair_partial_euclidean(&self, i: usize, j: usize) -> f64 {
|
|
||||||
self.col_view(i).iter().zip(self.col_view(j).iter())
|
|
||||||
.map(|(a, b)| { let d = a as f64 - b as f64; d * d }).sum()
|
|
||||||
}
|
|
||||||
fn pair_partial_threshold_jaccard(&self, i: usize, j: usize, t: u32) -> (u64, u64) {
|
|
||||||
self.col_view(i).iter().zip(self.col_view(j).iter())
|
|
||||||
.fold((0u64, 0u64), |(inter, uni), (a, b)| {
|
|
||||||
let ap = a >= t; let bp = b >= t;
|
|
||||||
(inter + (ap & bp) as u64, uni + (ap | bp) as u64)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fn pair_partial_relfreq_bray(&self, i: usize, j: usize, si: f64, sj: f64) -> f64 {
|
|
||||||
if si == 0.0 || sj == 0.0 { return 0.0; }
|
|
||||||
self.col_view(i).iter().zip(self.col_view(j).iter())
|
|
||||||
.map(|(a, b)| (a as f64 / si).min(b as f64 / sj)).sum()
|
|
||||||
}
|
|
||||||
fn pair_partial_relfreq_euclidean(&self, i: usize, j: usize, si: f64, sj: f64) -> f64 {
|
|
||||||
if si == 0.0 || sj == 0.0 { return 0.0; }
|
|
||||||
self.col_view(i).iter().zip(self.col_view(j).iter())
|
|
||||||
.map(|(a, b)| { let d = a as f64 / si - b as f64 / sj; d * d }).sum()
|
|
||||||
}
|
|
||||||
fn pair_partial_hellinger(&self, i: usize, j: usize, si: f64, sj: f64) -> f64 {
|
|
||||||
if si == 0.0 || sj == 0.0 { return 0.0; }
|
|
||||||
self.col_view(i).iter().zip(self.col_view(j).iter())
|
|
||||||
.map(|(a, b)| { let d = (a as f64 / si).sqrt() - (b as f64 / sj).sqrt(); d * d }).sum()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn partial_bray_dist_matrix(&self) -> Array2<u64> {
|
pub(crate) fn partial_bray_dist_matrix(&self) -> Array2<u64> {
|
||||||
pairwise_matrix(self.n_cols, |i, j| self.pair_partial_bray(i, j))
|
pairwise_matrix(self.n_cols, |i, j| self.col_view(i).partial_bray_dist(self.col_view(j)))
|
||||||
}
|
}
|
||||||
pub(crate) fn partial_euclidean_dist_matrix(&self) -> Array2<f64> {
|
pub(crate) fn partial_euclidean_dist_matrix(&self) -> Array2<f64> {
|
||||||
pairwise_matrix(self.n_cols, |i, j| self.pair_partial_euclidean(i, j))
|
pairwise_matrix(self.n_cols, |i, j| self.col_view(i).partial_euclidean_dist(self.col_view(j)))
|
||||||
}
|
}
|
||||||
pub(crate) fn partial_threshold_jaccard_dist_matrix(&self, t: u32) -> (Array2<u64>, Array2<u64>) {
|
pub(crate) fn partial_threshold_jaccard_dist_matrix(&self, t: u32) -> (Array2<u64>, Array2<u64>) {
|
||||||
pairwise2_matrix(self.n_cols, |i, j| self.pair_partial_threshold_jaccard(i, j, t))
|
pairwise2_matrix(self.n_cols, |i, j| self.col_view(i).partial_threshold_jaccard_dist(self.col_view(j), t))
|
||||||
}
|
}
|
||||||
pub(crate) fn partial_relfreq_bray_dist_matrix(&self, col_sums: &Array1<u64>) -> Array2<f64> {
|
pub(crate) fn partial_relfreq_bray_dist_matrix(&self, col_sums: &Array1<u64>) -> Array2<f64> {
|
||||||
pairwise_matrix(self.n_cols, |i, j| self.pair_partial_relfreq_bray(i, j, col_sums[i] as f64, col_sums[j] as f64))
|
pairwise_matrix(self.n_cols, |i, j| {
|
||||||
|
self.col_view(i).partial_relfreq_bray_dist(self.col_view(j), col_sums[i] as f64, col_sums[j] as f64)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
pub(crate) fn partial_relfreq_euclidean_dist_matrix(&self, col_sums: &Array1<u64>) -> Array2<f64> {
|
pub(crate) fn partial_relfreq_euclidean_dist_matrix(&self, col_sums: &Array1<u64>) -> Array2<f64> {
|
||||||
pairwise_matrix(self.n_cols, |i, j| self.pair_partial_relfreq_euclidean(i, j, col_sums[i] as f64, col_sums[j] as f64))
|
pairwise_matrix(self.n_cols, |i, j| {
|
||||||
|
self.col_view(i).partial_relfreq_euclidean_dist(self.col_view(j), col_sums[i] as f64, col_sums[j] as f64)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
pub(crate) fn partial_hellinger_euclidean_dist_matrix(&self, col_sums: &Array1<u64>) -> Array2<f64> {
|
pub(crate) fn partial_hellinger_euclidean_dist_matrix(&self, col_sums: &Array1<u64>) -> Array2<f64> {
|
||||||
pairwise_matrix(self.n_cols, |i, j| self.pair_partial_hellinger(i, j, col_sums[i] as f64, col_sums[j] as f64))
|
pairwise_matrix(self.n_cols, |i, j| {
|
||||||
|
self.col_view(i).partial_hellinger_euclidean_dist(self.col_view(j), col_sums[i] as f64, col_sums[j] as f64)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,25 +511,9 @@ impl PersistentCompactIntMatrixBuilder {
|
|||||||
|
|
||||||
impl MatrixGroupOps for PersistentCompactIntMatrix {
|
impl MatrixGroupOps for PersistentCompactIntMatrix {
|
||||||
fn partial_group_presence_count(&self, g: &ColGroup, threshold: u32) -> io::Result<TempCompactIntVec> {
|
fn partial_group_presence_count(&self, g: &ColGroup, threshold: u32) -> io::Result<TempCompactIntVec> {
|
||||||
let n = self.n();
|
chunked_presence_count(self.n(), &g.indices, |b, c| {
|
||||||
if g.indices.len() < 255 {
|
b.inc_predicate_fast(self.col_view(c), |v| v >= threshold)
|
||||||
let mut builder = TempCompactIntVecBuilder::new(n)?;
|
})
|
||||||
for &c in &g.indices {
|
|
||||||
builder.inc_predicate_fast(self.col_view(c), |v| v >= threshold);
|
|
||||||
}
|
|
||||||
builder.freeze()
|
|
||||||
} else {
|
|
||||||
let mut result = TempCompactIntVecBuilder::new(n)?;
|
|
||||||
for chunk in g.indices.chunks(254) {
|
|
||||||
let mut chunk_b = TempCompactIntVecBuilder::new(n)?;
|
|
||||||
for &c in chunk {
|
|
||||||
chunk_b.inc_predicate_fast(self.col_view(c), |v| v >= threshold);
|
|
||||||
}
|
|
||||||
let frozen = chunk_b.freeze()?;
|
|
||||||
result.add(frozen.view());
|
|
||||||
}
|
|
||||||
result.freeze()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn partial_group_sum(&self, g: &ColGroup) -> io::Result<TempCompactIntVec> {
|
fn partial_group_sum(&self, g: &ColGroup) -> io::Result<TempCompactIntVec> {
|
||||||
|
|||||||
@@ -138,107 +138,66 @@ impl PersistentCompactIntVec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── Distance methods ──────────────────────────────────────────────────────
|
// ── Distance methods ──────────────────────────────────────────────────────
|
||||||
|
//
|
||||||
|
// Thin delegates to `IntSliceView` (see `views.rs`) — the canonical,
|
||||||
|
// zero-copy implementation of every formula below. Kept here only for
|
||||||
|
// API convenience (`&PersistentCompactIntVec` in, no `.view()` needed at
|
||||||
|
// call sites); no formula is re-derived.
|
||||||
|
|
||||||
pub fn bray_dist(&self, other: &PersistentCompactIntVec) -> f64 {
|
pub fn bray_dist(&self, other: &PersistentCompactIntVec) -> f64 {
|
||||||
let sum_min = self.partial_bray_dist(other);
|
self.view().bray_dist(other.view())
|
||||||
let denom = self.sum() + other.sum();
|
|
||||||
if denom == 0 { 0.0 } else { 1.0 - 2.0 * sum_min as f64 / denom as f64 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn partial_bray_dist(&self, other: &PersistentCompactIntVec) -> u64 {
|
pub fn partial_bray_dist(&self, other: &PersistentCompactIntVec) -> u64 {
|
||||||
assert_eq!(self.n, other.len(), "length mismatch");
|
self.view().partial_bray_dist(other.view())
|
||||||
self.iter().zip(other.iter()).map(|(a, b)| a.min(b) as u64).sum()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn relfreq_bray_dist(&self, other: &PersistentCompactIntVec) -> f64 {
|
pub fn relfreq_bray_dist(&self, other: &PersistentCompactIntVec) -> f64 {
|
||||||
assert_eq!(self.n, other.len(), "length mismatch");
|
self.view().relfreq_bray_dist(other.view())
|
||||||
let sa = self.sum() as f64;
|
|
||||||
let sb = other.sum() as f64;
|
|
||||||
if sa == 0.0 && sb == 0.0 { return 0.0; }
|
|
||||||
1.0 - self.partial_relfreq_bray_dist(other, sa, sb)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn partial_relfreq_bray_dist(&self, other: &PersistentCompactIntVec, sum_a: f64, sum_b: f64) -> f64 {
|
pub fn partial_relfreq_bray_dist(&self, other: &PersistentCompactIntVec, sum_a: f64, sum_b: f64) -> f64 {
|
||||||
assert_eq!(self.n, other.len(), "length mismatch");
|
self.view().partial_relfreq_bray_dist(other.view(), sum_a, sum_b)
|
||||||
self.iter().zip(other.iter())
|
|
||||||
.map(|(a, b)| {
|
|
||||||
let pa = if sum_a > 0.0 { a as f64 / sum_a } else { 0.0 };
|
|
||||||
let pb = if sum_b > 0.0 { b as f64 / sum_b } else { 0.0 };
|
|
||||||
pa.min(pb)
|
|
||||||
})
|
|
||||||
.sum()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn euclidean_dist(&self, other: &PersistentCompactIntVec) -> f64 {
|
pub fn euclidean_dist(&self, other: &PersistentCompactIntVec) -> f64 {
|
||||||
self.partial_euclidean_dist(other).sqrt()
|
self.view().euclidean_dist(other.view())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn partial_euclidean_dist(&self, other: &PersistentCompactIntVec) -> f64 {
|
pub fn partial_euclidean_dist(&self, other: &PersistentCompactIntVec) -> f64 {
|
||||||
assert_eq!(self.n, other.len(), "length mismatch");
|
self.view().partial_euclidean_dist(other.view())
|
||||||
self.iter().zip(other.iter())
|
|
||||||
.map(|(a, b)| { let d = a as f64 - b as f64; d * d })
|
|
||||||
.sum()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn relfreq_euclidean_dist(&self, other: &PersistentCompactIntVec) -> f64 {
|
pub fn relfreq_euclidean_dist(&self, other: &PersistentCompactIntVec) -> f64 {
|
||||||
let sa = self.sum() as f64;
|
self.view().relfreq_euclidean_dist(other.view())
|
||||||
let sb = other.sum() as f64;
|
|
||||||
if sa == 0.0 && sb == 0.0 { return 0.0; }
|
|
||||||
self.partial_relfreq_euclidean_dist(other, sa, sb).sqrt()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn partial_relfreq_euclidean_dist(&self, other: &PersistentCompactIntVec, sum_a: f64, sum_b: f64) -> f64 {
|
pub fn partial_relfreq_euclidean_dist(&self, other: &PersistentCompactIntVec, sum_a: f64, sum_b: f64) -> f64 {
|
||||||
assert_eq!(self.n, other.len(), "length mismatch");
|
self.view().partial_relfreq_euclidean_dist(other.view(), sum_a, sum_b)
|
||||||
self.iter().zip(other.iter())
|
|
||||||
.map(|(a, b)| {
|
|
||||||
let pa = if sum_a > 0.0 { a as f64 / sum_a } else { 0.0 };
|
|
||||||
let pb = if sum_b > 0.0 { b as f64 / sum_b } else { 0.0 };
|
|
||||||
let d = pa - pb;
|
|
||||||
d * d
|
|
||||||
})
|
|
||||||
.sum()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hellinger_euclidean_dist(&self, other: &PersistentCompactIntVec) -> f64 {
|
pub fn hellinger_euclidean_dist(&self, other: &PersistentCompactIntVec) -> f64 {
|
||||||
let sa = self.sum() as f64;
|
self.view().hellinger_euclidean_dist(other.view())
|
||||||
let sb = other.sum() as f64;
|
|
||||||
if sa == 0.0 && sb == 0.0 { return 0.0; }
|
|
||||||
self.partial_hellinger_euclidean_dist(other, sa, sb).sqrt()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn partial_hellinger_euclidean_dist(&self, other: &PersistentCompactIntVec, sum_a: f64, sum_b: f64) -> f64 {
|
pub fn partial_hellinger_euclidean_dist(&self, other: &PersistentCompactIntVec, sum_a: f64, sum_b: f64) -> f64 {
|
||||||
assert_eq!(self.n, other.len(), "length mismatch");
|
self.view().partial_hellinger_euclidean_dist(other.view(), sum_a, sum_b)
|
||||||
self.iter().zip(other.iter())
|
|
||||||
.map(|(a, b)| {
|
|
||||||
let pa = if sum_a > 0.0 { (a as f64 / sum_a).sqrt() } else { 0.0 };
|
|
||||||
let pb = if sum_b > 0.0 { (b as f64 / sum_b).sqrt() } else { 0.0 };
|
|
||||||
let d = pa - pb;
|
|
||||||
d * d
|
|
||||||
})
|
|
||||||
.sum()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hellinger_dist(&self, other: &PersistentCompactIntVec) -> f64 {
|
pub fn hellinger_dist(&self, other: &PersistentCompactIntVec) -> f64 {
|
||||||
self.hellinger_euclidean_dist(other) / std::f64::consts::SQRT_2
|
self.view().hellinger_dist(other.view())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn threshold_jaccard_dist(&self, other: &PersistentCompactIntVec, threshold: u32) -> f64 {
|
pub fn threshold_jaccard_dist(&self, other: &PersistentCompactIntVec, threshold: u32) -> f64 {
|
||||||
let (intersection, union) = self.partial_threshold_jaccard_dist(other, threshold);
|
self.view().threshold_jaccard_dist(other.view(), threshold)
|
||||||
if union == 0 { 0.0 } else { 1.0 - intersection as f64 / union as f64 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn partial_threshold_jaccard_dist(&self, other: &PersistentCompactIntVec, threshold: u32) -> (u64, u64) {
|
pub fn partial_threshold_jaccard_dist(&self, other: &PersistentCompactIntVec, threshold: u32) -> (u64, u64) {
|
||||||
assert_eq!(self.n, other.len(), "length mismatch");
|
self.view().partial_threshold_jaccard_dist(other.view(), threshold)
|
||||||
self.iter().zip(other.iter())
|
|
||||||
.fold((0u64, 0u64), |(inter, uni), (a, b)| {
|
|
||||||
let ap = a >= threshold;
|
|
||||||
let bp = b >= threshold;
|
|
||||||
(inter + (ap & bp) as u64, uni + (ap | bp) as u64)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn jaccard_dist(&self, other: &PersistentCompactIntVec) -> f64 {
|
pub fn jaccard_dist(&self, other: &PersistentCompactIntVec) -> f64 {
|
||||||
self.threshold_jaccard_dist(other, 1)
|
self.view().jaccard_dist(other.view())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user