#[cfg(not(feature = "std"))]
use num_traits::Float;
#[inline]
#[must_use]
pub fn tanimoto_similarity(intersection: usize, size_a: usize, size_b: usize) -> f64 {
debug_assert!(
intersection <= size_a,
"intersection ({intersection}) must be <= size_a ({size_a})"
);
debug_assert!(
intersection <= size_b,
"intersection ({intersection}) must be <= size_b ({size_b})"
);
let union = size_a + size_b - intersection;
if union == 0 {
return 1.0;
}
#[allow(clippy::cast_precision_loss)]
{
intersection as f64 / union as f64
}
}
#[inline]
#[must_use]
pub fn dice_similarity(intersection: usize, size_a: usize, size_b: usize) -> f64 {
debug_assert!(
intersection <= size_a,
"intersection ({intersection}) must be <= size_a ({size_a})"
);
debug_assert!(
intersection <= size_b,
"intersection ({intersection}) must be <= size_b ({size_b})"
);
let sum = size_a + size_b;
if sum == 0 {
return 1.0;
}
#[allow(clippy::cast_precision_loss)]
{
(2 * intersection) as f64 / sum as f64
}
}
#[inline]
#[must_use]
pub fn overlap_similarity(intersection: usize, size_a: usize, size_b: usize) -> f64 {
debug_assert!(
intersection <= size_a,
"intersection ({intersection}) must be <= size_a ({size_a})"
);
debug_assert!(
intersection <= size_b,
"intersection ({intersection}) must be <= size_b ({size_b})"
);
let min = size_a.min(size_b);
if min == 0 {
return if size_a == 0 && size_b == 0 { 1.0 } else { 0.0 };
}
#[allow(clippy::cast_precision_loss)]
{
intersection as f64 / min as f64
}
}
#[inline]
#[must_use]
pub fn cosine_similarity(intersection: usize, size_a: usize, size_b: usize) -> f64 {
debug_assert!(
intersection <= size_a,
"intersection ({intersection}) must be <= size_a ({size_a})"
);
debug_assert!(
intersection <= size_b,
"intersection ({intersection}) must be <= size_b ({size_b})"
);
let product = size_a * size_b;
if product == 0 {
return if size_a == 0 && size_b == 0 { 1.0 } else { 0.0 };
}
#[allow(clippy::cast_precision_loss)]
{
intersection as f64 / (product as f64).sqrt()
}
}
#[inline]
#[must_use]
pub fn johnson_similarity(
common_edges: usize,
common_vertices: usize,
vertices_first: usize,
edges_first: usize,
vertices_second: usize,
edges_second: usize,
) -> f64 {
let numerator = common_edges + common_vertices;
let denom_first = vertices_first + edges_first;
let denom_second = vertices_second + edges_second;
let denominator = denom_first * denom_second;
if denominator == 0 {
return 1.0;
}
#[allow(clippy::cast_precision_loss)]
{
let numerator_sq = (numerator * numerator) as f64;
numerator_sq / denominator as f64
}
}
#[inline]
#[must_use]
pub fn tversky_similarity(
intersection: usize,
size_a: usize,
size_b: usize,
alpha: f64,
beta: f64,
) -> f64 {
debug_assert!(
intersection <= size_a,
"intersection ({intersection}) must be <= size_a ({size_a})"
);
debug_assert!(
intersection <= size_b,
"intersection ({intersection}) must be <= size_b ({size_b})"
);
debug_assert!(alpha >= 0.0, "alpha ({alpha}) must be >= 0");
debug_assert!(beta >= 0.0, "beta ({beta}) must be >= 0");
let diff_a = size_a - intersection;
let diff_b = size_b - intersection;
#[allow(clippy::cast_precision_loss)]
let denom = intersection as f64 + alpha * diff_a as f64 + beta * diff_b as f64;
if denom == 0.0 {
return 1.0;
}
#[allow(clippy::cast_precision_loss)]
{
intersection as f64 / denom
}
}
#[inline]
#[must_use]
pub fn kulczynski_similarity(intersection: usize, size_a: usize, size_b: usize) -> f64 {
debug_assert!(
intersection <= size_a,
"intersection ({intersection}) must be <= size_a ({size_a})"
);
debug_assert!(
intersection <= size_b,
"intersection ({intersection}) must be <= size_b ({size_b})"
);
if size_a == 0 && size_b == 0 {
return 1.0;
}
if size_a == 0 || size_b == 0 {
return 0.0;
}
#[allow(clippy::cast_precision_loss)]
{
let ratio_a = intersection as f64 / size_a as f64;
let ratio_b = intersection as f64 / size_b as f64;
ratio_a.midpoint(ratio_b)
}
}
#[inline]
#[must_use]
pub fn braun_blanquet_similarity(intersection: usize, size_a: usize, size_b: usize) -> f64 {
debug_assert!(
intersection <= size_a,
"intersection ({intersection}) must be <= size_a ({size_a})"
);
debug_assert!(
intersection <= size_b,
"intersection ({intersection}) must be <= size_b ({size_b})"
);
let max = size_a.max(size_b);
if max == 0 {
return 1.0;
}
#[allow(clippy::cast_precision_loss)]
{
intersection as f64 / max as f64
}
}
#[inline]
#[must_use]
pub fn sokal_sneath_similarity(intersection: usize, size_a: usize, size_b: usize) -> f64 {
debug_assert!(
intersection <= size_a,
"intersection ({intersection}) must be <= size_a ({size_a})"
);
debug_assert!(
intersection <= size_b,
"intersection ({intersection}) must be <= size_b ({size_b})"
);
let mismatches = size_a + size_b - 2 * intersection;
let denom = intersection + 2 * mismatches;
if denom == 0 {
return 1.0;
}
#[allow(clippy::cast_precision_loss)]
{
intersection as f64 / denom as f64
}
}
#[inline]
#[must_use]
pub fn mcconnaughey_similarity(intersection: usize, size_a: usize, size_b: usize) -> f64 {
debug_assert!(
intersection <= size_a,
"intersection ({intersection}) must be <= size_a ({size_a})"
);
debug_assert!(
intersection <= size_b,
"intersection ({intersection}) must be <= size_b ({size_b})"
);
let product = size_a * size_b;
if product == 0 {
return if size_a == 0 && size_b == 0 { 1.0 } else { 0.0 };
}
let diff_a = size_a - intersection;
let diff_b = size_b - intersection;
#[allow(clippy::cast_precision_loss)]
{
let numerator = (intersection * intersection) as f64 - (diff_a * diff_b) as f64;
numerator / product as f64
}
}
pub trait GraphSimilarities {
fn common_edges(&self) -> usize;
fn common_vertices(&self) -> usize;
fn first_graph_vertices(&self) -> usize;
fn first_graph_edges(&self) -> usize;
fn second_graph_vertices(&self) -> usize;
fn second_graph_edges(&self) -> usize;
#[inline]
fn edge_jaccard_similarity(&self) -> f64 {
tanimoto_similarity(
self.common_edges(),
self.first_graph_edges(),
self.second_graph_edges(),
)
}
#[inline]
fn edge_dice_similarity(&self) -> f64 {
dice_similarity(self.common_edges(), self.first_graph_edges(), self.second_graph_edges())
}
#[inline]
fn edge_overlap_similarity(&self) -> f64 {
overlap_similarity(self.common_edges(), self.first_graph_edges(), self.second_graph_edges())
}
#[inline]
fn edge_cosine_similarity(&self) -> f64 {
cosine_similarity(self.common_edges(), self.first_graph_edges(), self.second_graph_edges())
}
#[inline]
fn vertex_jaccard_similarity(&self) -> f64 {
tanimoto_similarity(
self.common_vertices(),
self.first_graph_vertices(),
self.second_graph_vertices(),
)
}
#[inline]
fn vertex_dice_similarity(&self) -> f64 {
dice_similarity(
self.common_vertices(),
self.first_graph_vertices(),
self.second_graph_vertices(),
)
}
#[inline]
fn vertex_overlap_similarity(&self) -> f64 {
overlap_similarity(
self.common_vertices(),
self.first_graph_vertices(),
self.second_graph_vertices(),
)
}
#[inline]
fn vertex_cosine_similarity(&self) -> f64 {
cosine_similarity(
self.common_vertices(),
self.first_graph_vertices(),
self.second_graph_vertices(),
)
}
#[inline]
fn edge_tversky_similarity(&self, alpha: f64, beta: f64) -> f64 {
tversky_similarity(
self.common_edges(),
self.first_graph_edges(),
self.second_graph_edges(),
alpha,
beta,
)
}
#[inline]
fn vertex_tversky_similarity(&self, alpha: f64, beta: f64) -> f64 {
tversky_similarity(
self.common_vertices(),
self.first_graph_vertices(),
self.second_graph_vertices(),
alpha,
beta,
)
}
#[inline]
fn edge_kulczynski_similarity(&self) -> f64 {
kulczynski_similarity(
self.common_edges(),
self.first_graph_edges(),
self.second_graph_edges(),
)
}
#[inline]
fn vertex_kulczynski_similarity(&self) -> f64 {
kulczynski_similarity(
self.common_vertices(),
self.first_graph_vertices(),
self.second_graph_vertices(),
)
}
#[inline]
fn edge_braun_blanquet_similarity(&self) -> f64 {
braun_blanquet_similarity(
self.common_edges(),
self.first_graph_edges(),
self.second_graph_edges(),
)
}
#[inline]
fn vertex_braun_blanquet_similarity(&self) -> f64 {
braun_blanquet_similarity(
self.common_vertices(),
self.first_graph_vertices(),
self.second_graph_vertices(),
)
}
#[inline]
fn edge_sokal_sneath_similarity(&self) -> f64 {
sokal_sneath_similarity(
self.common_edges(),
self.first_graph_edges(),
self.second_graph_edges(),
)
}
#[inline]
fn vertex_sokal_sneath_similarity(&self) -> f64 {
sokal_sneath_similarity(
self.common_vertices(),
self.first_graph_vertices(),
self.second_graph_vertices(),
)
}
#[inline]
fn edge_mcconnaughey_similarity(&self) -> f64 {
mcconnaughey_similarity(
self.common_edges(),
self.first_graph_edges(),
self.second_graph_edges(),
)
}
#[inline]
fn vertex_mcconnaughey_similarity(&self) -> f64 {
mcconnaughey_similarity(
self.common_vertices(),
self.first_graph_vertices(),
self.second_graph_vertices(),
)
}
#[inline]
fn johnson_similarity(&self) -> f64 {
johnson_similarity(
self.common_edges(),
self.common_vertices(),
self.first_graph_vertices(),
self.first_graph_edges(),
self.second_graph_vertices(),
self.second_graph_edges(),
)
}
}