acap/distance.rs
1//! Abstract notions of distance.
2
3use num_traits::{Num, NumAssign, Signed};
4
5/// A number type suitable for distance values.
6///
7/// This trait is automatically implemented for all types that support the required operations.
8pub trait Value: Copy + Num + NumAssign + Signed + PartialOrd {}
9
10/// Blanket [`Value`] implementation.
11impl<T: Num + NumAssign + Signed + Copy + PartialOrd> Value for T {}
12
13/// A distance between two points.
14///
15/// An implementation may be an actual numerical distance, or an [order embedding] of the true
16/// distance. This allows for optimizations whenever distances can be compared more efficiently
17/// than their exact values can be computed. Implementors must satisfy, for all distances `$x$` and
18/// `$y$`:
19///
20/// ```math
21/// \begin{aligned}
22/// x.\mathrm{value}() &< y.\mathrm{value}() & &\iff& x.\mathrm{value}() &< y \\
23/// & & &\iff& x &< y.\mathrm{value}() \\
24/// & & &\iff& x &< y
25/// \end{aligned}
26/// ```
27///
28/// Any monotonically increasing function can be used to create an order embedding. For example,
29/// [`EuclideanDistance`] holds a squared distance, rather than the distance itself. Comparisons
30/// still behave correctly because `$x \mapsto x^2$` is an increasing function. This lets us avoid
31/// computing relatively expensive square roots until we need the `value()` itself, at which point
32/// the inverse function `$x \mapsto \sqrt{x}$` must be applied.
33///
34/// [order embedding]: https://en.wikipedia.org/wiki/Order_embedding
35/// [`EuclideanDistance`]: crate::euclid::EuclideanDistance
36pub trait Distance
37where
38 Self: Copy,
39 Self: Into<<Self as Distance>::Value>,
40 Self: PartialOrd<<Self as Distance>::Value>,
41 Self: PartialOrd,
42{
43 /// The type of actual numerical distances.
44 type Value: Value + PartialOrd<Self>;
45
46 /// Get the real numerical value of this distance.
47 fn value(self) -> Self::Value {
48 self.into()
49 }
50}
51
52/// Any numerical distance value can be a [`Distance`].
53impl<T: Value> Distance for T {
54 type Value = T;
55}
56
57/// A space with some notion of distance between points.
58///
59/// There are no restrictions on the distances returned by spaces that implement only `Proximity`.
60/// In particular, they may be asymmetric or even negative. If a space meets the restrictions of
61/// the [`Metric`] trait, it should be implemented as well. Spaces that satisfy those rules, at
62/// least approximately, often allow for more accurate and efficient searches.
63///
64/// `Proximity<T>` is generic, to allow comparisons between objects of related but distinct types.
65/// For example:
66///
67/// ```
68/// # use acap::cos::{angular_distance, AngularDistance};
69/// # use acap::distance::Proximity;
70/// // A GPS coordinate
71/// struct Gps {
72/// lat: f64,
73/// long: f64,
74/// }
75/// # type HaversineDistance = f64;
76/// # fn haversine_distance(a: &Gps, b: &Gps) -> HaversineDistance {
77/// # 0.0
78/// # }
79///
80/// // For computing distances between GPS coordinates
81/// impl Proximity for Gps {
82/// type Distance = HaversineDistance;
83///
84/// fn distance(&self, other: &Self) -> Self::Distance {
85/// haversine_distance(self, other)
86/// }
87/// }
88///
89/// // A point of interest with a known location, name, ...
90/// struct PointOfInterest {
91/// location: Gps,
92/// name: String,
93/// // ...
94/// }
95///
96/// // Compute the distance between a GPS coordinate and a point of interest,
97/// // by delegating to the Proximity impl for Gps
98/// impl Proximity<PointOfInterest> for Gps {
99/// type Distance = <Gps as Proximity>::Distance;
100///
101/// fn distance(&self, other: &PointOfInterest) -> Self::Distance {
102/// self.distance(&other.location)
103/// }
104/// }
105/// ```
106///
107/// With those implementations available, you could use a [`NearestNeighbors<Gps, PointOfInterest>`]
108/// instance to find the closest point(s) of interest to any GPS location.
109///
110/// [`NearestNeighbors<Gps, PointOfInterest>`]: crate::knn::NearestNeighbors
111pub trait Proximity<T: ?Sized = Self> {
112 /// The type that represents distances.
113 type Distance: Distance;
114
115 /// Calculate the distance between this point and another one.
116 fn distance(&self, other: &T) -> Self::Distance;
117}
118
119// See https://github.com/rust-lang/rust/issues/38078
120/// Shorthand for `K::Distance::Value`.
121pub type DistanceValue<K, V = K> = <<K as Proximity<V>>::Distance as Distance>::Value;
122
123/// Blanket [`Proximity`] implementation for references.
124impl<'k, 'v, K: Proximity<V>, V> Proximity<&'v V> for &'k K {
125 type Distance = K::Distance;
126
127 fn distance(&self, other: &&'v V) -> Self::Distance {
128 (*self).distance(*other)
129 }
130}
131
132/// Marker trait for [metric spaces].
133///
134/// A metric must be symmetric and obey the [triangle inequality]. More precisely, let `$x$`,
135/// `$y$`, and `$z$` be any elements of a metric space, and let
136/// `$d(x, y) = x.\mathrm{distance}(y).\mathrm{value}()$`. Then the following rules must hold:
137///
138/// ```math
139/// \begin{aligned}
140/// d(x, x) &= 0 \\
141/// d(x, y) &= d(y, x) & \text{(symmetry)} \\
142/// d(x, z) &\le d(x, y) + d(y, z) & \text{(triangle inequality)}
143/// \end{aligned}
144/// ```
145///
146/// Those conditions also imply the following condition:
147///
148/// ```math
149/// \begin{aligned}
150/// d(x, y) &\ge \rlap{0}\phantom{d(x, y) + d(y, z)} & \text{\phantom{(triangle inequality)}\llap{(non-negativity)}}
151/// \end{aligned}
152/// ```
153/// Because we do not prohibit `$d(x, y) = 0$` for distinct `$x$` and `$y$`, these spaces are more
154/// properly known as [pseudometric spaces]. This distinction is usually unimportant.
155///
156/// [metric spaces]: https://en.wikipedia.org/wiki/Metric_space
157/// [triangle inequality]: https://en.wikipedia.org/wiki/Triangle_inequality
158/// [pseudometric spaces]: https://en.wikipedia.org/wiki/Pseudometric_space
159pub trait Metric<T: ?Sized = Self>: Proximity<T> {}
160
161/// Blanket [`Metric`] implementation for references.
162impl<'k, 'v, K: Metric<V>, V> Metric<&'v V> for &'k K {}