Phylogeny Matrix
PortfolioOptimisers.phylogeny_matrix — Function
phylogeny_matrix(plr::PhylogenyResult{<:MatNum}, args...; kwargs...)Fallback no-op for returning a validated phylogeny matrix result as-is.
This method provides a generic interface for handling precomputed phylogeny matrices wrapped in a PhylogenyResult. It simply returns the input object unchanged, enabling consistent downstream workflows for constraint generation and analysis.
Arguments
plr::PhylogenyResult{<:MatNum}: Phylogeny matrix result object.args...: Additional positional arguments (ignored).kwargs...: Additional keyword arguments (ignored).
Returns
- The input
plrobject.
Examples
julia> plr = PhylogenyResult(; X = [0 1 0; 1 0 1; 0 1 0]);julia> phylogeny_matrix(plr)PhylogenyResult X ┴ 3×3 Matrix{Int64}Related
phylogeny_matrix(nte::AbstractNetworkEstimator, X::MatNum; dims::Int = 1, kwargs...)Compute the phylogeny matrix for a network estimator.
Builds the network from X and returns the binary matrix of the pairs nte.sep counts as related, with self-loops removed. Which neighbourhood that is comes from the separation, through _phylogeny_matrix: HopCount gives the hop ball, the clamped power sum sum(A^i for i in 0:n) the network family has always used; PathLength gives the radius ball, separation_matrix thresholded at separation_budget.
The hop ball is the range connection matrix
The hop branch computes the range connection matrix of walks of length at most n, which is NetworkEstimator's Equations 13.1 and 13.2. Writing $\mathbf{A}$ for the binary adjacency matrix and $\mathbf{I}_n$ for the identity,
\[\begin{align} \mathbf{B}_{k} &= \mathbf{1}_{x \geq 1}\left(\mathbf{A}^{k} + \mathbf{I}_n\right) - \mathbf{I}_n\,, \\ \mathbf{B}_{1,\,l} &= \mathbf{1}_{x \geq 1}\left(\sum_{k=1}^{l} \mathbf{B}_{k}\right)\,, \end{align}\]
Where:
- $\mathbf{1}_{x \geq 1}(\cdot)$: Element-wise indicator of the entries that are at least one.
- $\mathbf{B}_{k}$: Pairs joined by at least one walk of length exactly $k$.
- $\mathbf{B}_{1,\,l}$: Pairs joined by at least one walk of length at most $l$.
The code accumulates sum(A^i for i in 0:n), clamps to 0 or 1, and subtracts the identity, which is the same selection written once rather than shell by shell.
The result is Int under either separation
Selection changes; the values do not. PhylogenyResult's matrix is Int here as everywhere else, because no consumer of one wants a number: SemiDefinitePhylogeny is weight-inert (A ⊙ W == 0 is the same constraint at any magnitude), IntegerPhylogeny counts an integer cardinality, and centrality_vector binarises before any centrality algorithm runs. The graded reading of a separation lives on Proximity instead.
What the radius ball buys, measured
It barely re-ranks, and on the PMFG not at all. Compare a hop shell against the equal-cardinality prefix of the path-length ordering: on a 20-asset PMFG the two sets are identical at every shell — 0 pairs differ out of 54, 121, 165 and 186. On the minimum spanning tree they are identical at the shells of 19 and 48, and differ by 1, 1, 3 and 2 pairs at the shells of 84, 115, 144 and 170. Both structures are selected by distance in the first place, so a path length refines a hop count rather than rivalling it. A reader who takes the radius ball for a conceptually different neighbourhood will be wrong.
What it buys is intermediate cardinalities between the shells. Over the same PMFG the hop knob relates 54, then 121, then 165 of the 190 pairs; a caller wanting about 100 cannot ask for it. Sweeping dmax across the same graph reaches 36, 55, 100, 122, 151 and 179. That is the whole gain, and it is real for SemiDefinitePhylogeny and IntegerPhylogeny, whose constraint strength is that cardinality.
PathLength's default budget relates everything reachable
PathLength() leaves dmax = nothing, which separation_budget resolves to the observed diameter — so no reachable pair sits outside it and the matrix is all ones off the diagonal. Measured: 190 of 190 pairs on both branches. This is the honest reading of an unstated budget rather than a fall-back, but it is the opposite end of the dial from HopCount's default n = 1: a caller who swaps one separation for the other and changes nothing else gets the maximal ball where they had the minimal one. State a numeric dmax to select anything narrower.
Algorithm
- Build the structure
nte.sepmeasures over withseparation_graph, givingg. One structure is built per call and both readers below share it. - Resolve
nte.sepagainstgwithresolve_separation. A budget that is already a value passes through and builds nothing. - Select the related pairs with
_phylogeny_matrix, through the branch the resolved separation names. - Wrap the selection in a
PhylogenyResult.
Arguments
nte: NetworkEstimator estimator.X: Data matrix (observations × assets).dims: Dimension along which to perform the computation.kwargs...: Additional keyword arguments.
Returns
plr::PhylogenyResult{<:Matrix{Int}}: Phylogeny matrix representing asset relationships.1for a related pair,0otherwise,0on the diagonal.
Related
phylogeny_matrix(cle::ClE_Cl,
X::MatNum; branchorder::Symbol = :optimal, dims::Int = 1,
kwargs...)Compute the phylogeny matrix for a clustering estimator or result.
This function clusterises the data, cuts the tree into the optimal number of clusters, and constructs a binary phylogeny matrix indicating shared cluster membership, with self-loops removed.
Algorithm
- Partition the assets with
clusterise, giving theClustersresultres. - Read the cluster of every asset with
assignments. - Build the
assets × res.kmembership matrixP, whose entry is one when the asset belongs to the cluster of its column. - Multiply
Pby its own transpose, which is one exactly for a pair that shares a cluster, and subtract the identity to clear the diagonal. - Wrap the selection in a
PhylogenyResult.
Arguments
cle: Clustering estimator or result.X: Data matrix (observations × assets).branchorder: Branch ordering strategy for hierarchical clustering.dims: Dimension along which to perform the computation.kwargs...: Additional keyword arguments.
Returns
P::Matrix{Int}: Phylogeny matrix representing cluster relationships.
Related
phylogeny_matrix(pl::NwE_ClE_Cl, pr::AbstractPriorResult;
kwargs...)Compute the phylogeny matrix from asset returns in a prior result using a network or clustering estimator.
phylogeny_matrix applies the specified network or clustering estimator to the asset returns matrix contained in the prior result object, producing a phylogeny matrix for use in constraint generation, centrality analysis, or portfolio construction.
Algorithm
- Pick the asset returns matrix
Xfrom the carrier thatx_srcnames, withreturns_matrix_picker. - Call the asset-returns method of
phylogeny_matrixwithX, passing both carriers on asprandrd, and return the phylogeny result it produces.
Arguments
pl: Network estimator, clusters estimator, or clustering result.pr: Prior result or returns result. Both carry the asset returns matrixXand the feature matrixZ, so either can supply them.rd: The returns result to use. Read forXonly whenx_srcis:data, and passed on to the estimator tree.x_src: Which returns matrix the clustering, phylogeny and centrality estimators read::priortakes the prior result'sX,:datatakes the raw returns result'sX. Ignored when no returns result is available, in which case the prior result'sXis used.kwargs...: Additional keyword arguments passed to the estimator.
Returns
plr::PhylogenyResult: Result object containing the phylogeny matrix.
Related