Distance: private API

PortfolioOptimisers.RhoDistanceAlgorithmType
const RhoDistanceAlgorithm = Union{SimpleDistance, SimpleAbsoluteDistance,
                                   LogDistance, CorrelationDistance}

Union of the correlation-based distance algorithms: those whose distance matrix is a pure function of a correlation matrix via _dist_from_cor. Excludes VariationInfoDistance (information-theoretic, computed from the data matrix) and CanonicalDistance (a redirect that selects one of the others from the covariance estimator).

Related

source
PortfolioOptimisers._as_correlationFunction
_as_correlation(rho::MatNum, sym::Symbol = :rho) -> MatNum

Coerce a square matrix to a correlation matrix, converting it from a covariance matrix when its diagonal says it is one.

The value of the diagonal decides, never the type. A matrix whose diagonal is all ones is already a correlation matrix and is returned as the same object; any other diagonal is read as the variances of a covariance matrix. This is the same test the matrix processing pipeline applies, so the two layers agree on what a correlation matrix is. The square-matrix check runs here, once, for every correlation-based algorithm's matrix entry point. The conversion round-trips: the correlation of a covariance matrix built from a correlation matrix and a vector of standard deviations is that correlation matrix again.

Algorithm

  1. Check that rho is square, reporting the failure under the name sym.
  2. Read the diagonal of rho into s. LinearAlgebra.diag allocates, so rho is never written to.
  3. When every entry of s is one, return rho itself. Steps 4 and 5 do not run.
  4. Otherwise replace s with its square roots, giving the standard deviations.
  5. Divide rho by the outer product of s with StatsBase.cov2cor, giving a new correlation matrix.

Arguments

  • rho: Correlation matrix assets × assets, or the covariance matrix to convert. It is never mutated on either route: step 2 copies the diagonal, and step 5 builds a new matrix.
  • sym: Name to report the square-matrix failure under.

Validation

  • rho is square.

Returns

  • rho::MatNum: Correlation matrix assets x assets.

Related

source
PortfolioOptimisers._absguardFunction
_absguard(rho::MatNum) -> MatNum

Supply the magnitude of rho to the two algorithms that are defined on it, without allocating when the magnitude is already rho.

This is an allocation guard, not a branch in the mathematics. abs.(rho) equals rho entry for entry whenever no entry of rho is negative, so both arms return the same numbers for every input, -0.0 included; the guard only decides whether a second matrix is built. Shared by SimpleAbsoluteDistance and LogDistance. A NaN compares false against zero, so a matrix holding one takes the allocating arm; abs(NaN) is NaN, so that entry is NaN on either arm.

Algorithm

  1. Test every entry of rho against zero. The test reads the whole matrix, which is the intended reading of the two algorithms: both take the magnitude of every entry.
  2. When no entry is negative, return rho itself, the same object the caller passed.
  3. Otherwise return abs.(rho), a new matrix. One negative entry allocates the copy for all of them.

Arguments

  • rho: Correlation matrix assets × assets.

Returns

  • rho::MatNum: The magnitude of the argument. It is the argument itself when the argument holds no negative entry.

Related

source
PortfolioOptimisers._dist_from_corFunction
_dist_from_cor(alg::RhoDistanceAlgorithm, power::Option{<:Integer}, rho::MatNum) -> MatNum

Turn a correlation matrix into a distance matrix, for one of the four correlation-based algorithms.

This is the shared kernel behind the distance and cor_and_dist entry points: they differ only in how they obtain rho, never in the transform they apply to it. Eight methods cover the four algorithms of RhoDistanceAlgorithm at each of the two power cases. Every method allocates its own result, and clamp! writes only into that allocation, so rho is never mutated.

Mathematical definition

Distance states the eight closed forms and the scaling $s$. Each algorithm's own docstring states the base case and the range it is defined on.

Algorithm

power selects the method, so the base case never raises rho to a power.

  1. SimpleAbsoluteDistance and LogDistance replace rho with its magnitude through _absguard. SimpleDistance and CorrelationDistance do not, and read the signed correlation.
  2. When power is an Integer, raise rho to it entry by entry. When power is nothing, leave rho as it is.
  3. SimpleDistance scales $1 - \rho$ by 1//2 for an odd power and by 1//1 for an even one, and by 1//2 in the base case. The scale is a Rational, so the element type of rho is carried through: a Float32 correlation matrix gives a Float32 distance matrix, as it does under the other three algorithms. The other three apply no scaling.
  4. The three square-root algorithms clamp the radicand into $[0,\,1]$ with clamp! and take its square root. LogDistance instead takes $-\log$ and floors the result at zero with max.

Arguments

  • alg: Distance algorithm.
  • power: Optional matrix exponent. nothing and 1 both give the base distance, so only power >= 2 changes the result.
  • rho: Correlation matrix assets × assets.

Returns

  • D::MatNum: Distance matrix assets x assets, in the units the distance algorithm defines.

Related

source
PortfolioOptimisers.assert_dimsFunction
assert_dims(dims::Integer)
assert_dims(
    dims::Integer,
    sym::Union{AbstractString, Symbol}
)

Assert that dims selects a valid matrix dimension (dims in (1, 2)).

Arguments

  • dims: Dimension selector to check.
  • sym: Symbolic name used in the error message.

Validation

  • dims in (1, 2), which raises a DomainError naming sym and dims.

Returns

  • nothing.

Related

source
PortfolioOptimisers.dims_orientedFunction
dims_oriented(
    dims::Integer,
    A::Union{Nothing, AbstractMatrix}
) -> Any

Validate dims and return the matrices with the observations along the rows.

The guard and the orientation are one call, so a caller cannot orient a matrix without validating dims. This is the single decision point: a leaf that spelled the guard and the transpose by hand could omit the guard and answer a dims of 3 with the raw input.

Algorithm

  1. Validate dims with assert_dims.
  2. Return each matrix untouched when dims is 1, because the observations already lie along the rows.
  3. Return the transpose of each matrix when dims is 2. A nothing passes through unchanged.

Arguments

  • dims: Dimension along which the observations lie.
  • A, B, Cs...: Matrices to orient. A nothing passes through unchanged, so an optional matrix needs no branch of its own.

Validation

Returns

  • A: The oriented matrix, when one matrix is given.
  • (A, B, Cs...): A tuple of the oriented matrices, when more than one is given.

Related

source