use burn_core as burn;
use burn::config::Config;
use burn::module::{Content, DisplaySettings, Module, ModuleDisplay};
use burn::tensor::Tensor;
use burn_linalg::lp_norm;
#[derive(Config, Debug)]
pub struct PairwiseDistanceConfig {
#[config(default = 2.0)]
pub p: f64,
#[config(default = 1e-6)]
pub eps: f64,
}
impl PairwiseDistanceConfig {
pub fn init(&self) -> PairwiseDistance {
PairwiseDistance {
p: self.p,
eps: self.eps,
}
}
}
#[derive(Module, Debug)]
#[module(custom_display)]
pub struct PairwiseDistance {
pub p: f64,
pub eps: f64,
}
impl ModuleDisplay for PairwiseDistance {
fn custom_settings(&self) -> Option<DisplaySettings> {
DisplaySettings::new()
.with_new_line_after_attribute(false)
.optional()
}
fn custom_content(&self, content: Content) -> Option<Content> {
content.add("p", &self.p).add("eps", &self.eps).optional()
}
}
impl PairwiseDistance {
pub fn forward(&self, x1: Tensor<2>, x2: Tensor<2>) -> Tensor<1> {
let diff = (x1 - x2).add_scalar(self.eps);
lp_norm(diff, self.p, 1).squeeze_dim(1)
}
}
#[cfg(test)]
mod tests {
use super::*;
use burn::tensor::TensorData;
use burn::tensor::Tolerance;
type FT = f32;
#[test]
fn pairwise_distance_p2() {
let device = Default::default();
let x1 = Tensor::<2>::from_data(TensorData::from([[1.0, 2.0], [3.0, 4.0]]), &device);
let x2 = Tensor::<2>::from_data(TensorData::from([[1.0, 1.0], [0.0, 0.0]]), &device);
let output = PairwiseDistanceConfig::new().init().forward(x1, x2);
let expected = TensorData::from([1.0, 5.0]);
output
.into_data()
.assert_approx_eq::<FT>(&expected, Tolerance::default());
}
#[test]
fn pairwise_distance_p1() {
let device = Default::default();
let x1 = Tensor::<2>::from_data(TensorData::from([[1.0, 2.0], [3.0, 4.0]]), &device);
let x2 = Tensor::<2>::from_data(TensorData::from([[1.0, 1.0], [0.0, 0.0]]), &device);
let output = PairwiseDistanceConfig::new()
.with_p(1.0)
.init()
.forward(x1, x2);
let expected = TensorData::from([1.0, 7.0]);
output
.into_data()
.assert_approx_eq::<FT>(&expected, Tolerance::default());
}
#[test]
fn display() {
let layer = PairwiseDistanceConfig::new()
.with_p(2.0)
.with_eps(0.5)
.init();
assert_eq!(
alloc::format!("{layer}"),
"PairwiseDistance {p: 2, eps: 0.5}"
);
}
}