diff --git a/Project.toml b/Project.toml index 405b8c7..3dc80fd 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "Distances" uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" -version = "0.10.11" +version = "0.10.12" [deps] LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" diff --git a/README.md b/README.md index 6123e77..5b2cf3d 100644 --- a/README.md +++ b/README.md @@ -165,14 +165,20 @@ At the top of this hierarchy is an abstract class **PreMetric**, which is define d(x, x) == 0 for all x d(x, y) >= 0 for all x, y -**SemiMetric** is a abstract type that refines **PreMetric**. Formally, a *semi-metric* is a *pre-metric* that is also symmetric, as +**SemiMetric** is an abstract type that refines **PreMetric**. Formally, a *semi-metric* is a *pre-metric* that is also symmetric, as d(x, y) == d(y, x) for all x, y -**Metric** is a abstract type that further refines **SemiMetric**. Formally, a *metric* is a *semi-metric* that also satisfies triangle inequality, as +**Metric** is an abstract type that further refines **SemiMetric**. Formally, a *metric* is a *semi-metric* that also satisfies triangle inequality, as d(x, z) <= d(x, y) + d(y, z) for all x, y, z +**MinkowskiMetric** is an abstract type that encompasses a family of metrics defined by the formula + + d(x, y) = sum(w .* (x - y) .^ p) ^ (1 / p) + +where the `p` parameter defines the metric and `w` is a potential weight vector (all 1's by default). + This type system has practical significance. For example, when computing pairwise distances between a set of vectors, you may only perform computation for half of the pairs, derive the values immediately for the remaining half by leveraging the symmetry of *semi-metrics*. Note diff --git a/src/Distances.jl b/src/Distances.jl index 854e28c..dc1f35a 100644 --- a/src/Distances.jl +++ b/src/Distances.jl @@ -9,6 +9,7 @@ export PreMetric, SemiMetric, Metric, + MinkowskiMetric, # generic functions result_type, diff --git a/src/generic.jl b/src/generic.jl index 090bddb..c91a2ec 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -21,6 +21,29 @@ abstract type SemiMetric <: PreMetric end # abstract type Metric <: SemiMetric end +""" + MinkowskiMetric <: Metric + +A Minkowski metric is a metric that is defined by the formula: + +`d(x, y) = sum(w .* (x - y) .^ p) ^ (1 / p)` + +where the `p` parameter defines the metric +and `w` is a potential weight vector (all 1's by default). + +Common Minkowski metrics: +* `Cityblock`/`WeightedCityblock`: Minkowski metric with `p=1`; +* `Euclidean`/`WeightedEuclidean`: Minkowski metric with `p=2`; +* `Chebyshev`: Limit of `d` as `p` approaches infinity; +* `Minkowski`/`WeightedMinkowski`: generic Minkowski metric for any `p`. + +## Notes +* The difference between `Minkowski`/`WeightedMinkowski` and `MinkowskiMetric` + is that while the first are generic Minkowski metrics, + the second is the parent type of these metrics. +""" +abstract type MinkowskiMetric <: Metric end + evaluate(dist::PreMetric, a, b) = dist(a, b) # Generic functions diff --git a/src/metrics.jl b/src/metrics.jl index 8595e2a..bbc8a83 100644 --- a/src/metrics.jl +++ b/src/metrics.jl @@ -12,13 +12,15 @@ abstract type UnionSemiMetric <: SemiMetric end abstract type UnionMetric <: Metric end +abstract type UnionMinkowskiMetric <: MinkowskiMetric end + ########################################################### # # Metric types # ########################################################### -struct Euclidean <: UnionMetric +struct Euclidean <: UnionMinkowskiMetric thresh::Float64 end @@ -52,7 +54,7 @@ julia> pairwise(Euclidean(1e-12), x, x) """ Euclidean() = Euclidean(0) -struct WeightedEuclidean{W} <: UnionMetric +struct WeightedEuclidean{W} <: UnionMinkowskiMetric weights::W end @@ -94,10 +96,10 @@ struct WeightedSqEuclidean{W} <: UnionSemiMetric weights::W end -struct Chebyshev <: UnionMetric end +struct Chebyshev <: UnionMinkowskiMetric end -struct Cityblock <: UnionMetric end -struct WeightedCityblock{W} <: UnionMetric +struct Cityblock <: UnionMinkowskiMetric end +struct WeightedCityblock{W} <: UnionMinkowskiMetric weights::W end @@ -105,10 +107,10 @@ struct TotalVariation <: UnionMetric end struct Jaccard <: UnionMetric end struct RogersTanimoto <: UnionMetric end -struct Minkowski{T <: Real} <: UnionMetric +struct Minkowski{T <: Real} <: UnionMinkowskiMetric p::T end -struct WeightedMinkowski{W,T <: Real} <: UnionMetric +struct WeightedMinkowski{W,T <: Real} <: UnionMinkowskiMetric weights::W p::T end @@ -197,7 +199,7 @@ struct NormRMSDeviation <: PreMetric end # Union types const metrics = (Euclidean,SqEuclidean,PeriodicEuclidean,Chebyshev,Cityblock,TotalVariation,Minkowski,Hamming,Jaccard,RogersTanimoto,CosineDist,ChiSqDist,KLDivergence,RenyiDivergence,BrayCurtis,JSDivergence,SpanNormDist,GenKLDivergence) const weightedmetrics = (WeightedEuclidean,WeightedSqEuclidean,WeightedCityblock,WeightedMinkowski,WeightedHamming) -const UnionMetrics = Union{UnionPreMetric,UnionSemiMetric,UnionMetric} +const UnionMetrics = Union{UnionPreMetric,UnionSemiMetric,UnionMetric,UnionMinkowskiMetric} ########################################################### # @@ -208,6 +210,7 @@ const UnionMetrics = Union{UnionPreMetric,UnionSemiMetric,UnionMetric} parameters(::UnionPreMetric) = nothing parameters(::UnionSemiMetric) = nothing parameters(::UnionMetric) = nothing +parameters(::UnionMinkowskiMetric) = nothing parameters(d::PeriodicEuclidean) = d.periods for dist in weightedmetrics @eval parameters(d::$dist) = d.weights