Similarity Matrices

PortfolioOptimisers.MaximumDistanceSimilarityType
struct MaximumDistanceSimilarity <: AbstractNonNegativeSimilarityMatrixAlgorithm

Subtracts the squared distance from a ceiling placed above the largest squared distance.

The ceiling is what makes the result non-negative for a distance matrix of any scale. The source states the transformation as $\rho_{i,\,j} = 1 - d_{i,\,j}^{2}$, which this reproduces exactly whenever $\max(\mathbf{D}) \leq 1$ — the case for SimpleDistance, SimpleAbsoluteDistance and CorrelationDistance. Above that the source's form goes negative and this one does not.

Mathematical definition

\[\begin{align} S_{i,\,j} &= \left\lceil\max(\mathbf{D})^2\right\rceil - D_{i,\,j}^2\,, \end{align}\]

Where:

  • $S_{i,\,j}$: Similarity between assets $i$ and $j$.
  • $\mathbf{D}$: Distance matrix.
  • $D_{i,\,j}$: Distance between assets $i$ and $j$.
Warning

The transformation is defined only for a finite distance matrix. An infinite entry makes ceil(Inf^2) - Inf^2, which is NaN, and every other entry Inf. This is not a corner case: LogDistance maps an exactly zero correlation to an infinite distance, and this member is the default of both DBHT and LoGo. assert_similarity_domain refuses it on the PMFG path.

Algorithm

The branch of distance_to_similarity that this tag selects runs these steps.

  1. Take the largest entry of D, square that one scalar, and round it up with ceil, giving the ceiling. The ceiling is the square of the maximum rounded up, not the maximum of the squares; the two agree because a distance is never negative.
  2. Square every entry of D.
  3. Subtract the squared entries of step 2 from the single scalar of step 1, giving the similarity matrix. The element type of D is carried through, so a Float32 distance matrix gives a Float32 similarity matrix.

Related

References

  • [5] D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025). Section 13.1.4.2, footnote 7.
source
PortfolioOptimisers.ExponentialSimilarityType
struct ExponentialSimilarity <: AbstractNonNegativeSimilarityMatrixAlgorithm

Maps a distance of any magnitude into $(0,\,1]$ by $e^{-D}$.

The one member of the family with no domain at all: it is non-negative for every finite distance, and it takes an infinite one too, where exp(-Inf) is 0 exactly. This is the member to reach for when the distance is unbounded, as under LogDistance, DistanceDistance or VariationInfoDistance with normalise = false.

Mathematical definition

\[\begin{align} S_{i,\,j} &= e^{-D_{i,\,j}}\,, \end{align}\]

Where:

  • $S_{i,\,j}$: Similarity between assets $i$ and $j$.
  • $\mathbf{D}$: Distance matrix.
  • $D_{i,\,j}$: Distance between assets $i$ and $j$.

Algorithm

The branch of distance_to_similarity that this tag selects runs these steps.

  1. Negate every entry of D.
  2. Exponentiate the negated entries, giving the similarity matrix. The element type of D is carried through, so a Float32 distance matrix gives a Float32 similarity matrix.

Related

source
PortfolioOptimisers.GeneralExponentialSimilarityType
struct GeneralExponentialSimilarity{__T_coef, __T_power} <: AbstractNonNegativeSimilarityMatrixAlgorithm

Applies $e^{-c D^{p}}$, adding a scale and an exponent to the exponential transformation.

coef sets how fast the similarity decays with distance and power sets the shape of that decay, so a single distance matrix can be sharpened towards its nearest neighbours or flattened across the universe. It inherits ExponentialSimilarity's freedom from a domain, and reduces to it at coef = 1, power = 1.

Mathematical definition

\[\begin{align} S_{i,\,j} &= e^{-c \cdot D_{i,\,j}^p}\,, \end{align}\]

Where:

  • $S_{i,\,j}$: Similarity between assets $i$ and $j$.
  • $\mathbf{D}$: Distance matrix.
  • $D_{i,\,j}$: Distance between assets $i$ and $j$.
  • $c$: Scale factor.
  • $p$: Exponent.

Algorithm

The branch of distance_to_similarity that this tag selects runs these steps.

  1. Read power and coef off the tag.
  2. Raise every entry of D to power.
  3. Multiply the result of step 2 by -coef. coef is a scalar, so it scales the whole matrix at once.
  4. Exponentiate entrywise, giving the similarity matrix. The element type of D is carried through whenever neither field is wider than it, which is the case for the Integer defaults: a Float32 distance matrix gives a Float32 similarity matrix.

Fields

  • coef: Coefficient for the the distance matrix when computing the similarity matrix.
  • power: Exponent for the the distance matrix when computing the similarity matrix.

Constructors

GeneralExponentialSimilarity(;    coef::Number = 1,    power::Number = 1) -> GeneralExponentialSimilarity

Keywords correspond to the struct's fields.

Validation

  • isfinite(coef) && coef > 0.
  • power > 0.

Examples

julia> GeneralExponentialSimilarity()GeneralExponentialSimilarity   coef ┼ Int64: 1  power ┴ Int64: 1

Related

source
PortfolioOptimisers.ComplementSimilarityType
struct ComplementSimilarity <: AbstractNonNegativeSimilarityMatrixAlgorithm

Takes the linear complement $1 - D$, the exact counterpart of a metric that is itself one minus a similarity.

It is the only member that recovers a named similarity rather than a monotone stand-in for one, which is why default_similarity uses it as the fallback. It is also the only member with both a domain and a pairing to get right, and both warnings below are about that.

Mathematical definition

\[\begin{align} S_{i,\,j} &= 1 - D_{i,\,j}\,, \end{align}\]

Where:

  • $S_{i,\,j}$: Similarity between assets $i$ and $j$.
  • $\mathbf{D}$: Distance matrix.
  • $D_{i,\,j}$: Distance between assets $i$ and $j$.

This recovers the named similarity counterpart of every distance that is itself one minus a similarity. Distances.CosineDist returns the cosine similarity, Distances.Jaccard the Ruzicka similarity, Distances.BrayCurtis the Sørensen–Dice similarity, and Distances.CorrDist the Pearson correlation.

The domain is `D <= 1`

The result is only correlation-like when $\mathbf{D} \in [0,\,1]$. Above 1 the similarity is negative.

What happens next depends on the path. On the FeatureDistance path the value is kept, lands outside the $[-1,\,1]$ range that plot_clusters assumes, and is silently clipped there rather than flagged. On the PMFG path the same input is refused by assert_similarity_domain, because PMFG_T2s's consumers cannot take a negative weight. Symmetry and the unit diagonal survive either way.

The rule is D <= 1, not "the metric is unbounded". Distances.CosineDist and Distances.CorrDist are bounded — by 2, not by 1 — and are refused whenever they exceed 1, which CorrDist does at every negative correlation. In-library sources that exceed 1: LogDistance, DistanceDistance — whose Distances.Euclidean default puts most of its entries above 1 — and VariationInfoDistance with normalise = false. Use ExponentialSimilarity or GeneralExponentialSimilarity for a member with no domain at all.

The pairing is not checked

This member is the honest inverse of a specific set of metrics, listed above. Paired with any other distance it returns a number that is in domain, non-negative, and wrong — and nothing catches it, on any path.

SimpleDistance is $\sqrt{(1 - \rho) / 2}$, so an ordinary correlation gives a D that is a perfectly legal bounded distance, and this member reports a plausible-looking similarity for it. No check placed anywhere can detect that. default_similarity pairs a metric with its inverse on the FeatureDistance path; NetworkEstimator's alg, DBHT's sim and LoGo's sim take a member with no reference to the distance estimator that produced $\mathbf{D}$, so on those the pairing is the caller's to get right.

Algorithm

The branch of distance_to_similarity that this tag selects runs these steps.

  1. Build the unit in the element type of D with one(eltype(D)). Writing the unit in that element type is what carries it through, so a Float32 distance matrix gives a Float32 similarity matrix.
  2. Subtract every entry of D from that unit, giving the similarity matrix.

Related

source
PortfolioOptimisers.AngularSimilarityType
struct AngularSimilarity <: AbstractSimilarityMatrixAlgorithm

Recovers a correlation from a normalised angular distance by $\cos(\pi D)$.

It is the only member that returns the codependence itself rather than a monotone transformation of it, and the only one that can go negative. Both facts follow from the same thing: it is the exact algebraic inverse of AngularDist, and a correlation is signed.

Mathematical definition

\[\begin{align} S_{i,\,j} &= \cos\left(\pi D_{i,\,j}\right)\,, \end{align}\]

Where:

  • $S_{i,\,j}$: Similarity between assets $i$ and $j$.
  • $\mathbf{D}$: Distance matrix.
  • $D_{i,\,j}$: Distance between assets $i$ and $j$.

For an angular distance $D_{i,\,j} = \arccos(\rho_{i,\,j}) / \pi$ this recovers $\rho_{i,\,j}$ exactly, without reference to the data the distance was computed from. Against AngularDist the recovered cosine matches the one computed from the features to floating-point precision. It maps $[0,\,1] \to [1,\,-1]$, so the similarity is bounded and the diagonal is unity whenever the distance matrix has a zero diagonal.

Where this member is correct, and where it is refused

It is correct on the FeatureDistance path, which is the one path that pairs a metric with its inverse: default_similarity selects this member for AngularDist, and the recovered $\rho$ is exact.

It is not a member of AbstractNonNegativeSimilarityMatrixAlgorithm, so it cannot be given to NetworkEstimator's alg, DBHT's sim or LoGo's sim — those refuse it at construction. The exclusion is permanent rather than pending a domain precondition. Even paired correctly this member returns a negative wherever $\rho_{i,\,j} < 0$, which is ordinary data, and PMFG_T2s's consumers cannot take a negative weight.

The pairing is not checked

Paired with a distance that is not an angular one, this member returns a number that is not a correlation. SimpleDistance is $\sqrt{(1 - \rho) / 2}$ and shares this member's $[0,\,1]$ range exactly, so it type-checks: an ordinary positive correlation can map through $\cos(\pi D)$ to a large negative number that is not a weak negative correlation, but nonsense.

Algorithm

The branch of distance_to_similarity that this tag selects runs these steps.

  1. Multiply every entry of D by pi. pi is an Irrational, which takes the element type of the number it multiplies rather than widening it, so a Float32 distance matrix stays Float32.
  2. Take the cosine of each scaled entry, giving the similarity matrix.

The sign follows from step 2 alone. $\cos(\pi D)$ is positive below $D = 0.5$, crosses zero at $D = 0.5$ and is negative above it, which is why this member is not admitted to AbstractNonNegativeSimilarityMatrixAlgorithm.

Related

References

  • [44] S. Van Dongen and A. J. Enright. Metric distances derived from cosine similarity and Pearson and Spearman correlations. arXiv preprint arXiv:1208.3145 (2012).
source

References

[5]
D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025).
[44]
S. Van Dongen and A. J. Enright. Metric distances derived from cosine similarity and Pearson and Spearman correlations, arXiv preprint arXiv:1208.3145 (2012).