Phylogeny Constraints
PortfolioOptimisers.SemiDefinitePhylogenyEstimator — Type
struct SemiDefinitePhylogenyEstimator{__T_pl, __T_p} <: AbstractPhylogenyConstraintEstimatorForbids co-movement between related assets through a semidefinite relaxation, refitting the structure from returns.
The estimator holds the source that builds the relatedness matrix and the penalty factor p. phylogeny_constraints refits the source against a returns matrix and returns a SemiDefinitePhylogeny, which carries the equations this constraint solves.
Which pairs a network source relates — and therefore how strong the constraint is — comes from its AbstractSeparationAlgorithm, not from anything set here. The constraint is weight-inert: A ⊙ W == 0 is the same constraint at any magnitude, so the separation changes the cardinality of the forbidden set and nothing else.
NetworkEstimator(; sep = PathLength()) relates every reachable pair. A bare PathLength leaves dmax = nothing, which resolves to the observed diameter, so nothing is outside the budget — measured, 190 of 190 pairs — and this estimator then forbids all pairwise co-movement. It is the opposite end of the dial from HopCount's default n = 1. State a numeric dmax to select anything narrower.
Fields
pl: Network estimator or clustering estimator. A precomputedPhylogenyResultorClustersis not accepted: this slot says how to build the phylogeny for whatever universe the estimator is given, and a precomputed one answers for a fixed universe instead. Pass the constraint result if you already have the structure.
p: Non-negative penalty factor on the trace of the semidefinite matrix variable. It is read only when the model does not already minimise a variance: a variance objective is itself a trace against that variable, so it pulls the relaxation down on its own and no second term is added. Whatpbuys is how closely the matrix variable tracks the outer product it stands for, and nothing else: the gap is small under a variance objective, which closes it itself, and wider under a tail measure such as conditional value at risk. The relatedness rows hold either way.
Constructors
SemiDefinitePhylogenyEstimator(; pl::NwE_ClE = NetworkEstimator(), p::Number = 0.05) -> SemiDefinitePhylogenyEstimatorKeywords correspond to the struct's fields. The default p = 0.05 is the value the source's own worked example uses.
Validation
p >= 0.plis bounded byNwE_ClE: a precomputedPhylogenyResultorClustersis rejected by the type, not by a check, so the keyword constructor raisesTypeErrorrather than deferring the problem to a solve. BuildSemiDefinitePhylogenyinstead, which is whatphylogeny_constraints(estimator, X)returns.
Examples
julia> SemiDefinitePhylogenyEstimator()SemiDefinitePhylogenyEstimator pl ┼ NetworkEstimator │ ce ┼ PortfolioOptimisersCovariance │ │ ce ┼ Covariance │ │ │ me ┼ SimpleExpectedReturns │ │ │ │ w ┴ nothing │ │ │ ce ┼ GeneralCovariance │ │ │ │ ce ┼ StatsBase.SimpleCovariance: StatsBase.SimpleCovariance(true) │ │ │ │ w ┴ nothing │ │ │ alg ┼ FullMoment() │ │ │ w ┴ nothing │ │ mp ┼ MatrixProcessing │ │ │ pdm ┼ Posdef │ │ │ │ alg ┼ UnionAll: NearestCorrelationMatrix.Newton │ │ │ │ kwargs ┴ @NamedTuple{}: NamedTuple() │ │ │ dn ┼ nothing │ │ │ dt ┼ nothing │ │ │ alg ┼ nothing │ │ │ order ┴ NTuple{4, Symbol}: (:pdm, :dn, :dt, :alg) │ de ┼ Distance │ │ power ┼ nothing │ │ alg ┴ CanonicalDistance() │ alg ┼ KruskalTree │ │ args ┼ Tuple{}: () │ │ kwargs ┴ @NamedTuple{}: NamedTuple() │ sep ┼ HopCount │ │ n ┴ Int64: 1 p ┴ Float64: 0.05Related
References
- [70] D. Cajas. A Graph Theory Approach to Portfolio Optimization. Available at SSRN 4602019 (2023).
- [71] D. Cajas. A Graph Theory Approach to Portfolio Optimization Part II. Available at SSRN 4667426 (2023).
- [5] D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025). Sections 13.1.7.2 and 13.2.4.2.
PortfolioOptimisers.SemiDefinitePhylogeny — Type
struct SemiDefinitePhylogeny{__T_A, __T_p} <: AbstractPhylogenyConstraintResultDrives the product of weights of every related pair of assets to zero through a semidefinite relaxation.
Relatedness is whatever the source that built A calls related: a neighbourhood over a network, or membership of one cluster.
p sets the size of the relaxation gap and not the strength of the constraint. A ⊙ W == 0 is written whether or not p is read, so a larger p forbids no more pairs. It only makes W track the outer product it stands for more closely. A wider gap lets a pair of related assets both hold weight while their entry of W absorbs the product.
Mathematical definition
The relaxation replaces the outer product of the weights with a symmetric matrix variable and bounds it below through a Schur complement. Where the objective is already a trace against that variable — a variance — the constraint form applies:
\[\begin{align} \underset{\boldsymbol{w},\, \mathbf{W}}{\min}\quad & \mathrm{tr}(\mathbf{\Sigma} \mathbf{W})\\ \textrm{s.t.}\quad & \begin{bmatrix} \mathbf{W} & \boldsymbol{w} \\ \boldsymbol{w}^\intercal & k \end{bmatrix} \succeq 0\,,\\ & \mathbf{A} \odot \mathbf{W} = 0\,,\\ & \mathbf{W} \in \mathbb{S}^{N}\,,\quad \boldsymbol{w} \in \mathcal{W}\,. \end{align}\]
For every other risk measure nothing in the objective pulls the relaxation down, so a penalty term does it instead:
\[\begin{align} \underset{\boldsymbol{w},\, \mathbf{W}}{\min}\quad & \phi(\boldsymbol{w}) + p\, \mathrm{tr}(\mathbf{W})\,. \end{align}\]
Where:
- $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
- $\mathbf{W}$: Symmetric $N \times N$ matrix variable that relaxes $\boldsymbol{w}\boldsymbol{w}^\intercal / k$.
- $\mathbf{A}$: Relatedness matrix, the
Afield. - $p$: Penalty factor, the
pfield. - $k$: Budget scaling / homogenisation variable.
- $\mathbf{\Sigma}$: Covariance matrix.
- $\phi$: Risk measure of the optimiser.
- $\odot$: Hadamard product.
- $\mathbb{S}^{N}$: Set of real symmetric $N \times N$ matrices.
- $\mathcal{W}$: Rest of the feasible set.
Fields
A: Symmetric relatedness matrix with a zero diagonal. A network source gives the range connection matrix, a clustering source the adjacency label matrix. Stored as given.
p: Non-negative penalty factor on the trace of the semidefinite matrix variable. It is read only when the model does not already minimise a variance: a variance objective is itself a trace against that variable, so it pulls the relaxation down on its own and no second term is added. Whatpbuys is how closely the matrix variable tracks the outer product it stands for, and nothing else: the gap is small under a variance objective, which closes it itself, and wider under a tail measure such as conditional value at risk. The relatedness rows hold either way.
Constructors
SemiDefinitePhylogeny( A::MatNum_PhRMatNum, p::Number) -> SemiDefinitePhylogenySemiDefinitePhylogeny(; A::MatNum_PhRMatNum, p::Number = 0.05) -> SemiDefinitePhylogenyKeywords correspond to the struct's fields. The default p = 0.05 is the value the source's own worked example uses.
Validation
LinearAlgebra.issymmetric(A)andall(iszero, LinearAlgebra.diag(A)).p >= 0.
Examples
julia> SemiDefinitePhylogeny([0.0 1.0; 1.0 0.0], 0.05)SemiDefinitePhylogeny A ┼ 2×2 Matrix{Float64} p ┴ Float64: 0.05Related
set_sdp_constraints!set_sdp_frc_constraints!set_sdp_phylogeny_constraints!: writesA ⊙ W == 0for every result of this type, and addsp * tr(W)to the objective penalty only when the model carries no variance.SemiDefinitePhylogenyEstimatorAbstractPhylogenyConstraintResultphylogeny_constraints
References
- [70] D. Cajas. A Graph Theory Approach to Portfolio Optimization. Available at SSRN 4602019 (2023).
- [71] D. Cajas. A Graph Theory Approach to Portfolio Optimization Part II. Available at SSRN 4667426 (2023).
- [5] D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025). Sections 13.1.7.2 and 13.2.4.2.
PortfolioOptimisers.IntegerPhylogenyEstimator — Type
struct IntegerPhylogenyEstimator{__T_pl, __T_B} <: AbstractPhylogenyConstraintEstimatorCaps how many related assets may be held at once, refitting the structure from returns.
The estimator holds the source that builds the relatedness matrix and the cap B. phylogeny_constraints refits the source against a returns matrix and returns an IntegerPhylogeny, which carries the equations this constraint solves.
Which pairs a network source relates comes from its AbstractSeparationAlgorithm, and B is an integer cardinality counted over them. The relatedness itself stays binary under either separation: PhylogenyResult's matrix is Int, and a graded one would not be countable here.
NetworkEstimator(; sep = PathLength()) relates every reachable pair. A bare PathLength leaves dmax = nothing, which resolves to the observed diameter, so nothing is outside the budget — measured, 190 of 190 pairs. It is the opposite end of the dial from HopCount's default n = 1. State a numeric dmax to select anything narrower.
Fields
pl: Network estimator or clustering estimator. A precomputedPhylogenyResultorClustersis not accepted: this slot says how to build the phylogeny for whatever universe the estimator is given, and a precomputed one answers for a fixed universe instead. Pass the constraint result if you already have the structure.
B: Right-hand side ofA * z <= B, wherezis the held indicator: the largest number of assets that may be held out of each row ofA. A scalar applies to every row. A vector states one bound per row, so its length must match the row count of the storedAand not the number of assets. On an estimator the rows do not exist yet, so a vector is only checked against the largest number of clusters the clustering estimator can return.
Constructors
IntegerPhylogenyEstimator(; pl::NwE_ClE = NetworkEstimator(), B::Int_VecInt = 1) -> IntegerPhylogenyEstimatorKeywords correspond to the struct's fields.
Validation
Bis validated withassert_nonempty_nonneg_finite_val.AbstractVector: It is additionally validated withvalidate_length_integer_phylogeny_constraint_B.
plis bounded byNwE_ClE: a precomputedPhylogenyResultorClustersis rejected by the type, not by a check, so the keyword constructor raisesTypeErrorrather than deferring the problem to a solve. BuildIntegerPhylogenyinstead, which is whatphylogeny_constraints(estimator, X)returns.
Examples
julia> IntegerPhylogenyEstimator()IntegerPhylogenyEstimator pl ┼ NetworkEstimator │ ce ┼ PortfolioOptimisersCovariance │ │ ce ┼ Covariance │ │ │ me ┼ SimpleExpectedReturns │ │ │ │ w ┴ nothing │ │ │ ce ┼ GeneralCovariance │ │ │ │ ce ┼ StatsBase.SimpleCovariance: StatsBase.SimpleCovariance(true) │ │ │ │ w ┴ nothing │ │ │ alg ┼ FullMoment() │ │ │ w ┴ nothing │ │ mp ┼ MatrixProcessing │ │ │ pdm ┼ Posdef │ │ │ │ alg ┼ UnionAll: NearestCorrelationMatrix.Newton │ │ │ │ kwargs ┴ @NamedTuple{}: NamedTuple() │ │ │ dn ┼ nothing │ │ │ dt ┼ nothing │ │ │ alg ┼ nothing │ │ │ order ┴ NTuple{4, Symbol}: (:pdm, :dn, :dt, :alg) │ de ┼ Distance │ │ power ┼ nothing │ │ alg ┴ CanonicalDistance() │ alg ┼ KruskalTree │ │ args ┼ Tuple{}: () │ │ kwargs ┴ @NamedTuple{}: NamedTuple() │ sep ┼ HopCount │ │ n ┴ Int64: 1 B ┴ Int64: 1Related
References
- [72] F. Ricca and A. Scozzari. Portfolio optimization through a network approach: network assortative mixing and portfolio diversification. European Journal of Operational Research 312, 700–717 (2024).
- [70] D. Cajas. A Graph Theory Approach to Portfolio Optimization. Available at SSRN 4602019 (2023).
- [71] D. Cajas. A Graph Theory Approach to Portfolio Optimization Part II. Available at SSRN 4667426 (2023).
- [5] D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025). Sections 13.1.7.1 and 13.2.4.1.
PortfolioOptimisers.IntegerPhylogeny — Type
struct IntegerPhylogeny{__T_A, __T_B} <: AbstractPhylogenyConstraintResultCaps at B the number of related assets a mixed-integer model may hold at once.
Relatedness is whatever the source that built A calls related: a neighbourhood over a network, or membership of one cluster. A network source with B = 1 forbids holding two assets that are neighbours. A clustering source with B = 1 holds at most one asset per cluster.
Mathematical definition
The cap is a mutually exclusive investment constraint on the held binary:
\[\begin{align} \underset{\boldsymbol{w}}{\mathrm{opt}}\quad & \phi(\boldsymbol{w})\\ \textrm{s.t.}\quad & \mathbf{A} \boldsymbol{z} \leq \boldsymbol{B}\,,\\ & \boldsymbol{\ell} \odot \boldsymbol{z} \leq \boldsymbol{w} \leq \boldsymbol{u} \odot \boldsymbol{z}\,,\\ & \boldsymbol{z} \in \{0, 1\}^{N}\,,\quad \boldsymbol{w} \in \mathcal{W}\,. \end{align}\]
Where:
- $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
- $\boldsymbol{z}$: Held binary, one entry per asset.
- $\mathbf{A}$: Stored relatedness rows, the
Afield. - $\boldsymbol{B}$: Cap, the
Bfield. - $\boldsymbol{\ell}$, $\boldsymbol{u}$: Lower and upper weight bounds.
- $\phi$: Objective function of the optimiser.
- $\odot$: Hadamard product.
- $\mathcal{W}$: Rest of the feasible set.
Algorithm
The constructor rewrites the matrix it is given, so the stored A is a derived quantity:
- Check that the diagonal of
Ais zero and thatAis symmetric. - Add the identity to
A, which puts each asset in its own row. - Drop the repeated rows of the sum with
unique(...; dims = 1), giving the storedA. One row survives per distinct neighbourhood or cluster, so the stored matrix is usually shorter than it is wide. - Check that
Bis non-empty, non-negative and finite. - Check
size(A, 1) == length(B)whenBis a vector, against the row count of step 3 and not against the number of assets.
Fields
A: Row set of the relatedness matrix, stored asunique(A + I; dims = 1)and not as the matrix passed in. The identity puts each asset in its own row, and the deduplication drops rows that repeat. One row per distinct neighbourhood or cluster survives, which is why the stored matrix is usually shorter than it is wide.
B: Right-hand side ofA * z <= B, wherezis the held indicator: the largest number of assets that may be held out of each row ofA. A scalar applies to every row. A vector states one bound per row, so its length must match the row count of the storedAand not the number of assets. On an estimator the rows do not exist yet, so a vector is only checked against the largest number of clusters the clustering estimator can return.
Constructors
IntegerPhylogeny( A::MatNum_PhRMatNum, B::Int_VecInt) -> IntegerPhylogenyIntegerPhylogeny(; A::MatNum_PhRMatNum, B::Int_VecInt = 1) -> IntegerPhylogenyKeywords correspond to the struct's fields.
Validation
LinearAlgebra.issymmetric(A)andall(iszero, LinearAlgebra.diag(A)).Bis validated withassert_nonempty_nonneg_finite_val.AbstractVector:size(unique(A + LinearAlgebra.I; dims = 1), 1) == length(B).
Examples
julia> IntegerPhylogeny(; A = [0.0 1.0; 1.0 0.0], B = 2)IntegerPhylogeny A ┼ 1×2 Matrix{Float64} B ┴ Int64: 2Related
set_iplg_constraints!mip_constraintsIntegerPhylogenyEstimatorAbstractPhylogenyConstraintResultphylogeny_constraints
References
- [72] F. Ricca and A. Scozzari. Portfolio optimization through a network approach: network assortative mixing and portfolio diversification. European Journal of Operational Research 312, 700–717 (2024).
- [70] D. Cajas. A Graph Theory Approach to Portfolio Optimization. Available at SSRN 4602019 (2023).
- [71] D. Cajas. A Graph Theory Approach to Portfolio Optimization Part II. Available at SSRN 4667426 (2023).
- [5] D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025). Sections 13.1.7.1 and 13.2.4.1.
PortfolioOptimisers.CentralityConstraint — Type
struct CentralityConstraint{__T_A, __T_B, __T_comp} <: AbstractCentralityConstraintBounds the average centrality of the portfolio against a threshold read off the centrality vector itself.
The constraint diversifies by the influence an asset has in the network, rather than by its weight. centrality_constraints turns it into one row of a LinearConstraint.
Mathematical definition
\[\begin{align} \underset{\boldsymbol{w}}{\mathrm{opt}}\quad & \phi(\boldsymbol{w})\\ \textrm{s.t.}\quad & \boldsymbol{c}^\intercal \boldsymbol{w} \mathbin{\square} \bar{c}\,,\\ & \boldsymbol{w} \in \mathcal{W}\,. \end{align}\]
Where:
- $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
- $\boldsymbol{c}$: Centrality vector the estimator in
Acomputes. - $\bar{c}$: Threshold, the
Bfield. - $\square$: Comparison operator, the
compfield. - $\phi$: Objective function of the optimiser.
- $\mathcal{W}$: Rest of the feasible set.
The source states this constraint as an equality against a desired average centrality. Either sense of the inequality is admitted here as well.
When B is a VectorToScalarMeasure, $\bar{c}$ is that measure of $\boldsymbol{c}$, so the threshold moves with the graph and the constraint always has a feasible point.
Fields
A: Centrality estimator. Its centrality vector is the row of the generated linear constraint.
B: Right-hand side of the constraint. A number is the threshold itself. AVectorToScalarMeasurederives the threshold from the centrality vectorAproduces, so the constraint always has a feasible point. The measure reads that vector, never the row aftercomphas flipped its sign, soMinValue()gives the smallest entry under<=and under>=alike.
comp: Comparison operator for the centrality constraint.==builds an equality row, every other operator an inequality row.
Constructors
CentralityConstraint(; A::CentralityEstimator = CentralityEstimator(), B::Num_VecToScaM = MinValue(), comp::ComparisonOperator = <=) -> CentralityConstraintKeywords correspond to the struct's fields.
Validation
- Every argument is bounded by its type and by nothing else:
AbyCentralityEstimator,BbyNum_VecToScaM, andcompbyComparisonOperator. A value outside a bound raises aTypeErrorfrom the keyword constructor. - No value is checked. A non-finite
Breaches the generated row unchanged:centrality_constraintsstores aNaNor anInfright-hand side and the model carries it to the solver.
Examples
julia> CentralityConstraint()CentralityConstraint A ┼ CentralityEstimator │ pl ┼ NetworkEstimator │ │ ce ┼ PortfolioOptimisersCovariance │ │ │ ce ┼ Covariance │ │ │ │ me ┼ SimpleExpectedReturns │ │ │ │ │ w ┴ nothing │ │ │ │ ce ┼ GeneralCovariance │ │ │ │ │ ce ┼ StatsBase.SimpleCovariance: StatsBase.SimpleCovariance(true) │ │ │ │ │ w ┴ nothing │ │ │ │ alg ┼ FullMoment() │ │ │ │ w ┴ nothing │ │ │ mp ┼ MatrixProcessing │ │ │ │ pdm ┼ Posdef │ │ │ │ │ alg ┼ UnionAll: NearestCorrelationMatrix.Newton │ │ │ │ │ kwargs ┴ @NamedTuple{}: NamedTuple() │ │ │ │ dn ┼ nothing │ │ │ │ dt ┼ nothing │ │ │ │ alg ┼ nothing │ │ │ │ order ┴ NTuple{4, Symbol}: (:pdm, :dn, :dt, :alg) │ │ de ┼ Distance │ │ │ power ┼ nothing │ │ │ alg ┴ CanonicalDistance() │ │ alg ┼ KruskalTree │ │ │ args ┼ Tuple{}: () │ │ │ kwargs ┴ @NamedTuple{}: NamedTuple() │ │ sep ┼ HopCount │ │ │ n ┴ Int64: 1 │ ct ┼ DegreeCentrality │ │ kind ┼ Int64: 0 │ │ kwargs ┴ @NamedTuple{}: NamedTuple() B ┼ MinValue() comp ┴ typeof(<=): <=Related
References
PortfolioOptimisers.phylogeny_constraints — Function
phylogeny_constraints(plc::Option{<:PlCE_PlC}, X::MatNum; dims::Int = 1, kwargs...)
phylogeny_constraints(plcs::VecPlCE_PlC, args...; kwargs...)Generate phylogeny-based portfolio constraints from an estimator or result.
phylogeny_constraints constructs constraint objects based on phylogenetic, res, or network structures among assets. It supports both semi-definite and integer constraint forms, accepting either an estimator (which wraps a phylogeny or clustering model and penalty parameters) or a precomputed result. If plc is nothing, returns nothing.
If plcs is a vector, this method broadcasts over each element, returning a vector of constraint results.
Arguments
plc: A phylogeny constraint estimator, result, ornothing.X: Data matrix (observations × assets) (ignored whenplcis not an estimator).dims: Dimension along which to perform the computation.kwargs...: Additional keyword arguments passed to the underlying phylogeny matrix routine (ignored whenestis not an estimator).
Returns
res: Constraint result.SemiDefinitePhylogeny: For semi-definite constraint estimators/results.IntegerPhylogeny: For integer constraint estimators/results.nothing: Ifestisnothing.
Related
phylogeny_constraints(
plc::AbstractPhylogenyConstraintEstimator,
pr::Union{AbstractPriorResult, ReturnsResult};
rd,
x_src,
kwargs...
) -> Union{IntegerPhylogeny, SemiDefinitePhylogeny{<:AbstractMatrix{var"#s137"}, <:Number} where var"#s137"<:(Union{var"#s136", var"#s53"} where {var"#s136"<:Number, var"#s53"<:AbstractJuMPScalar})}
Compute phylogeny constraints from asset returns in a prior result using a phylogeny constraint estimator.
phylogeny_constraints delegates to the asset-returns variant by extracting X from pr (or rd if provided and x_src is :data).
Algorithm
- Pick the asset returns matrix
Xfrom the carrier thatx_srcnames, withreturns_matrix_picker. - Call the asset-returns method of
phylogeny_constraintswithX, passing both carriers on asprandrd, and return the constraint result it produces.
Arguments
plc: Phylogeny constraint estimator.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
- Phylogeny constraint result.
Related
PortfolioOptimisers.centrality_constraints — Function
centrality_constraints(ccs::CC_VecCC, X::MatNum; dims::Int = 1, strict::Bool = false,
kwargs...)Reduce one or more CentralityConstraint estimators to a LinearConstraint.
Each estimator contributes one row. The coefficients of the row are the centrality vector of the asset network, and the right-hand side is read off that vector or given as a number. A constraint whose centrality vector is empty or all zero contributes no row, because the row it would build holds for every set of weights: two assets under BetweennessCentrality give the zero vector, and a lone such constraint makes this function return nothing. The drop is reported through strict_diagnostic, so strict decides whether it raises or warns.
Algorithm
- Check that
ccsis not empty when it is a vector. - Check
dimswithassert_dims, and read the asset countNfrom the axis ofXthatdimsdoes not name.Nis the width of every row the loop builds. - For each
ccinccs, compute the centrality vectorAwith the estimator incc.A, alongdims. - Report
ccthroughstrict_diagnosticwithzero_centrality_msgand skip it whenAis empty or all zero. - Read the sign
dand the inequality flagflag_ineqofcc.compfromcomparison_sign_ineq_flag. - Derive the right-hand side
Bfromcc.Bagainst the unscaledAwithvec_to_real_measure, and scale it byd. - Scale
Abyd, which negates a>=row so that every inequality is stored in theA w <= Bsense. - Append
AandBto the inequality pair or to the equality pair, asflag_ineqselects. - Reshape each flat accumulator into
Ncolumns, giving one row per surviving constraint, and build theLinearConstraintfrom the halves that hold a row. Returnnothingwhen neither half does.
Step 6 runs before step 7 by design. Deriving the threshold from the scaled row negates it a second time, which cancels the flip and turns a MinValue() into a MaxValue() with the wrong sign.
Arguments
ccs: A singleCentralityConstraintor a vector of them.X: Data matrix.dims: Dimension along which to perform the computation.strict: Iftrue, a constraint whose centrality vector is empty or all zero raises anArgumentError; iffalse, it issues a warning. The constraint is dropped either way.kwargs...: Additional keyword arguments passed to the centrality estimator.
Validation
!isempty(ccs)whenccsis a vector. A breach raises anIsEmptyError.dims in (1, 2). The check is made withassert_dims, which raises theDomainError.- A centrality vector that is empty or all zero raises an
ArgumentErrorwhenstrictistrue, and issues a warning otherwise. The constraint is dropped either way. - No value of
cc.Bis checked. ANaNor anInfthreshold reaches the returned right-hand side unchanged.
Returns
lc::Option{<:LinearConstraint}: The assembled inequality and equality rows, ornothingwhen every constraint was skipped.
Related
CentralityConstraintLinearConstraintPartialLinearConstraintcentrality_vectorcomparison_sign_ineq_flag: the one sign and flag table, shared withget_linear_constraints.assert_dimsstrict_diagnosticzero_centrality_msg
centrality_constraints(ccs::Option{<:LinearConstraint}, args...; kwargs...)No-op fallback for centrality-based constraint propagation.
This method returns the input LinearConstraint object or nothing unchanged. It is used to pass through an already constructed centrality-based constraint object, enabling composability and uniform interface handling in constraint generation workflows.
Arguments
ccs: An existingLinearConstraintobject ornothing.args...: Additional positional arguments (ignored).kwargs...: Additional keyword arguments (ignored).
Returns
ccs::Option{<:LinearConstraint}: The input constraint object ornothing, unchanged.
Related
centrality_constraints(
ccs::Union{CentralityConstraint, AbstractVector{<:CentralityConstraint}},
pr::Union{AbstractPriorResult, ReturnsResult};
rd,
x_src,
kwargs...
) -> Union{Nothing, LinearConstraint}
Compute centrality constraints from asset returns in a prior result using a centrality constraint estimator.
centrality_constraints delegates to the asset-returns variant by extracting X from pr (or rd if provided and x_src is :data).
Algorithm
- Pick the asset returns matrix
Xfrom the carrier thatx_srcnames, withreturns_matrix_picker. - Call the asset-returns method of
centrality_constraintswithX, passing both carriers on asprandrd, and return the constraint result it produces.
Arguments
ccs: Centrality constraint estimator or vector thereof.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.strictis read by the asset-returns variant, which reports a dropped zero centrality vector through it.
Returns
- Centrality constraint result.
Related
References
- [5]
- D. Cajas. Advanced Portfolio Optimization: A Cutting-edge Quantitative Approach (Springer Nature Switzerland, 2025).
- [70]
- D. Cajas. A Graph Theory Approach to Portfolio Optimization. Available at SSRN 4602019 (2023).
- [71]
- D. Cajas. A Graph Theory Approach to Portfolio Optimization Part II. Available at SSRN 4667426 (2023).
- [72]
- F. Ricca and A. Scozzari. Portfolio optimization through a network approach: network assortative mixing and portfolio diversification. European Journal of Operational Research 312, 700–717 (2024).