Distances of Distances

PortfolioOptimisers.DistanceDistanceType
struct DistanceDistance{__T_metric, __T_args, __T_kwargs, __T_power, __T_alg} <: AbstractDistanceEstimator

Measures how differently two assets relate to the whole universe, by applying a metric to a distance matrix.

Two assets are close under this estimator when their columns of the base distance matrix are close — that is, when they stand at similar distances from every other asset. This is a second-order reading: it can separate two assets that are equally far apart under Distance but occupy different positions in the universe. It wraps a metric from Distances.jl around a base Distance built from power and alg.

Note

power = 1 reproduces the base distance exactly, so the distance-of-distances matrix is the same as at power = nothing. Only $p \geq 2$ changes the result. See Distance for the formula of each algorithm.

Mathematical definition

\[\begin{align} _{g}\tilde{d}_{i,\,j} &= \lVert_{g}\boldsymbol{D}_{i} - _{g}\boldsymbol{D}_{j}\rVert\,, \end{align}\]

Where:

  • $_{g}\tilde{d}_{i,\,j}$: General distance of distances between assets $i$ and $j$.
  • $_{g}\boldsymbol{D}_{i}$: Column $i$ of the generalised distance matrix (see AbstractDistanceAlgorithm).
  • $\lVert \cdot \rVert$: Metric used to compute the distance of distances, metric.

The source states this at the default metric, the Euclidean norm. A base distance matrix is symmetric, so the column and the row give the same answer.

Fields

  • metric: Distance metric used for the distances of distances computations.
  • args: Additional positional arguments for the distances of distances metric.
  • kwargs: Additional keyword arguments for the distances of distances metric.
  • power: Optional matrix exponent. nothing and 1 both give the base distance, so only power >= 2 changes the result.
  • alg: Distance algorithm.

Constructors

DistanceDistance(;    metric::Distances.Metric = Distances.Euclidean(),    args::Tuple = (),    kwargs::NamedTuple = (;),    power::Option{<:Integer} = nothing,    alg::AbstractDistanceAlgorithm = SimpleDistance()) -> DistanceDistance

Keywords correspond to the struct's fields. power and alg are forwarded to a Distance, so they carry the meaning and the defaults documented there.

Validation

  • If power is not nothing, power >= 1.

Examples

julia> DistanceDistance()DistanceDistance  metric ┼ Distances.Euclidean: Distances.Euclidean(0.0)    args ┼ Tuple{}: ()  kwargs ┼ @NamedTuple{}: NamedTuple()   power ┼ nothing     alg ┴ SimpleDistance()
The default metric leaves the unit interval

The Euclidean norm of two columns of a bounded distance matrix is not itself bounded by 1.

ComplementSimilarity is therefore out of domain against this estimator's own default, and assert_similarity_domain refuses the pair on the PMFG path. Use ExponentialSimilarity or GeneralExponentialSimilarity, which have no domain.

Related

References

  • [5] D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025). Section 12.1.1, Equation 12.1.
source
PortfolioOptimisers.distanceMethod
distance(de::DistanceDistance, ce::StatsBase.CovarianceEstimator, X::MatNum;
         dims::Int = 1, kwargs...)

Compute the distance-of-distances matrix from a covariance estimator and data matrix.

Algorithm

  1. Build a base Distance carrying de.power and de.alg, and call distance on it with ce, X, dims and kwargs, giving the base distance matrix D.
  2. Apply de.metric to D with Distances.pairwise, forwarding the estimator's own de.args and de.kwargs, giving the distance-of-distances matrix.

An alg of CanonicalDistance redirects inside step 1, by the type of ce, so the algorithm that produces D is chosen before the metric is applied. distance carries the redirect table.

Distances.pairwise treats a column of D as one observation. The call passes dims = 2 itself, before the splat of de.kwargs, so a dims in de.kwargs overrides it. Which axis is read does not change the answer here, because a base distance matrix is symmetric. It matters only when de.args supplies a second matrix of a different shape.

Arguments

  • de: Distance-of-distances estimator.
  • ce: Covariance estimator.
  • X: Data matrix (observations × assets).
  • dims: Dimension along which to perform the computation.
  • kwargs...: Additional keyword arguments passed to the base distance computation of step 1. They never reach Distances.pairwise, which is served by the kwargs field of de.

Returns

  • D::Matrix{<:Number}: Matrix of pairwise distances of distances.

Related

source
PortfolioOptimisers.distanceMethod
distance(de::DistanceDistance, rho::MatNum, args...; kwargs...)

Compute the distance-of-distances matrix from a correlation or covariance matrix.

Algorithm

  1. Build a base Distance carrying de.power and de.alg, and call distance on it with rho, args and kwargs, giving the base distance matrix D.
  2. Apply de.metric to D with Distances.pairwise, forwarding the estimator's own de.args and de.kwargs, giving the distance-of-distances matrix.

An alg of CanonicalDistance takes one step before step 1, and rebuilds itself with SimpleDistance. There is no covariance estimator on this route, so the redirect table cannot select a row and its fallback is taken.

Distances.pairwise reads the columns of D, as it does on the covariance-estimator route above. The call passes dims = 2 itself, and a dims in de.kwargs overrides it.

Arguments

  • de: Distance-of-distances estimator.
  • rho: Correlation or covariance matrix.
  • args...: Additional arguments. They are forwarded to the base distance of step 1, which ignores them. They are not the args field of de, which is what reaches Distances.pairwise.
  • kwargs...: Additional keyword arguments passed to the base distance computation of step 1. They never reach Distances.pairwise, which is served by the kwargs field of de.

Returns

  • D::Matrix{<:Number}: Matrix of pairwise distances of distances.

Related

source
PortfolioOptimisers.cor_and_distMethod
cor_and_dist(de::DistanceDistance, ce::StatsBase.CovarianceEstimator, X::MatNum;
             dims::Int = 1, kwargs...)

Compute both the correlation matrix and the distance-of-distances matrix from a covariance estimator and data matrix.

Algorithm

  1. Build a base Distance carrying de.power and de.alg, and call cor_and_dist on it with ce, X, dims and kwargs, giving the correlation matrix rho and the base distance matrix D from one pass. This is the pass that the distance sibling cannot share.
  2. Apply de.metric to D with Distances.pairwise, forwarding the estimator's own de.args and de.kwargs, giving the distance-of-distances matrix.
  3. Return rho unchanged beside that matrix. The metric is applied to the distance matrix alone, so the correlation this method returns is the base estimator's own.

The second element is the matrix that distance returns for the same de, ce and X, so a caller that needs both quantities pays for one correlation rather than two.

Arguments

  • de: Distance-of-distances estimator.
  • ce: Covariance estimator.
  • X: Data matrix (observations × assets).
  • dims: Dimension along which to perform the computation.
  • kwargs...: Additional keyword arguments passed to the base distance computation of step 1. They never reach Distances.pairwise, which is served by the kwargs field of de.

Returns

  • (rho::Matrix{<:Number}, D::Matrix{<:Number}): Tuple of correlation matrix and distance-of-distances matrix.

Related

source

References

[5]
D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025).