Regularisation Constraints: private API
PortfolioOptimisers.AbstractRegularisationEstimator — Type
abstract type AbstractRegularisationEstimator <: AbstractEstimatorAbstract supertype for all portfolio weight regularisation estimators.
A regularisation term penalises the norm of the weight vector, which is the Lagrangian form of a constraint on that norm.
Related
References
- [131] V. DeMiguel, L. Garlappi, F. J. Nogales and R. Uppal. A Generalized Approach to Portfolio Optimization: Improving Performance by Constraining Portfolio Norms. Management Science 55, 798–812 (2009).
PortfolioOptimisers.VecL2Reg — Type
const VecL2Reg = AbstractVector{<:L2Regularisation}Alias for a vector of L2Regularisation objects.
Represents a collection of L2-norm regularisation terms to be added to the optimisation objective.
Related
PortfolioOptimisers.L2Reg_VecL2Reg — Type
const L2Reg_VecL2Reg = Union{<:L2Regularisation, <:VecL2Reg}Alias for a single or vector of L2 regularisation terms.
Matches either a single L2Regularisation or a vector of them (VecL2Reg).
Related
PortfolioOptimisers.VecLpReg — Type
const VecLpReg = AbstractVector{<:LpRegularisation}Alias for a vector of LpRegularisation objects.
Represents a collection of Lp-norm regularisation terms to be added to the optimisation objective.
Related
PortfolioOptimisers.LpReg_VecLpReg — Type
const LpReg_VecLpReg = Union{<:LpRegularisation, <:VecLpReg}Alias for a single or vector of Lp regularisation terms.
Matches either a single LpRegularisation or a vector of them (VecLpReg).
Related
PortfolioOptimisers.set_l1_regularisation! — Function
set_l1_regularisation!(model::JuMP.Model, l1_val::Number)
set_l1_regularisation!(args...)Add an L1 regularisation penalty to the objective function.
The penalty is applied as given, so l1_val must be commensurate with the scale of the objective it is added to.
Mathematical definition
\[\begin{align} \text{penalty} &= \lambda_1 \lVert \boldsymbol{w} \rVert_1\,. \end{align}\]
Where:
- $\lambda_1$: L1 regularisation coefficient.
- $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
Arguments
model::JuMP.Model: The JuMP optimisation model.l1_val::Number: L1 regularisation coefficient.
Returns
nothing.
Details
l1_val::Number: Introduces the auxiliary variablet_l1, bounds it with[t_l1; w] in MOI.NormOneCone, and addsl1_val * t_l1to the objective penalty viaadd_to_objective_penalty!.args...: No-op, used when no L1 regularisation is configured.
Related
PortfolioOptimisers._set_l2_regularisation! — Function
_set_l2_regularisation!(model::JuMP.Model, i::Integer, w::VecNum,
l2::L2Regularisation{<:Any, <:SOCRiskExpr}, sc::Number)
_set_l2_regularisation!(model::JuMP.Model, i::Integer, w::VecNum,
l2::L2Regularisation{<:Any, <:SquaredSOCRiskExpr}, sc::Number)
_set_l2_regularisation!(model::JuMP.Model, i::Integer, w::VecNum,
l2::L2Regularisation{<:Any, <:QuadRiskExpr}, args...)
_set_l2_regularisation!(model::JuMP.Model, i::Integer, w::VecNum,
l2::L2Regularisation{<:Any, <:RSOCRiskExpr}, sc::Number)Add the i-th L2 regularisation penalty to the objective, in the formulation given by l2.alg.
The coefficient l2.val is applied as given, so it must be consistent with the formulation it multiplies: SOCRiskExpr penalises $\lVert \boldsymbol{w} \rVert_2$, whereas SquaredSOCRiskExpr, QuadRiskExpr and RSOCRiskExpr penalise $\lVert \boldsymbol{w} \rVert_2^2$. The same val therefore does not produce the same penalty across formulations.
QuadRiskExpr yields a quadratic objective term rather than a conic one; add_to_objective_penalty! promotes an affine penalty expression to a JuMP.QuadExpr to accommodate it.
Mathematical definition
\[\begin{align} \text{penalty} &= \lambda_2 \lVert \boldsymbol{w} \rVert_2\,, \\ \text{penalty} &= \lambda_2 \lVert \boldsymbol{w} \rVert_2^2\,. \end{align}\]
Where:
- $\lambda_2$: L2 regularisation coefficient, the
valfield ofl2. - $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
Arguments
model::JuMP.Model: The JuMP optimisation model.i::Integer: Index of the term, used to suffix the variables and constraints it creates so that multiple terms do not collide.w::VecNum: Portfolio weights variable.l2::L2Regularisation: The L2 regularisation term.sc::Number: Constraint scale factor.
Returns
nothing.
Details
l2::L2Regularisation{<:Any, <:SOCRiskExpr}: Introducest_l2_i, constrains[t_l2_i; w] in SecondOrderConeso thatt_l2_i >= norm(w, 2), and penalisesval * t_l2_i.l2::L2Regularisation{<:Any, <:SquaredSOCRiskExpr}: Introducest_l2_i, constrains[t_l2_i; w] in SecondOrderConeso thatt_l2_i >= norm(w, 2), and penalisesval * t_l2_i^2.l2::L2Regularisation{<:Any, <:QuadRiskExpr}: Introduces no auxiliary variable, and penalisesval * dot(w, w)directly.l2::L2Regularisation{<:Any, <:RSOCRiskExpr}: Introducest_l2_i, constrains[t_l2_i; 0.5; w] in RotatedSecondOrderConeso thatt_l2_i >= norm(w, 2)^2, and penalisesval * t_l2_i.
Related
PortfolioOptimisers.set_l2_regularisation! — Function
set_l2_regularisation!(model::JuMP.Model, l2s::L2Reg_VecL2Reg)
set_l2_regularisation!(args...)Add one or more L2 regularisation penalties to the objective function.
Each L2Regularisation carries its own coefficient and cone formulation, so several may be combined in a single model. The penalties are applied as given, so each coefficient must be commensurate with the scale of the objective it is added to, and with the formulation used to express it.
Mathematical definition
\[\begin{align} \text{penalty} &= \sum_{i} \lambda_{2,\, i} \lVert \boldsymbol{w} \rVert_2\,. \end{align}\]
Where:
- $\lambda_{2,\, i}$: L2 regularisation coefficient of the $i$-th term.
- $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
Arguments
model::JuMP.Model: The JuMP optimisation model.l2s::L2Reg_VecL2Reg: One or more L2 regularisation terms.
Returns
nothing.
Details
l2s::L2Reg_VecL2Reg: Dispatches each term to_set_l2_regularisation!, which selects the cone formulation from the term'salgfield. Variables and constraints are suffixed by the term's index, so terms do not collide.args...: No-op, used when no L2 regularisation is configured.
Related
PortfolioOptimisers.set_lp_regularisation! — Function
set_lp_regularisation!(model::JuMP.Model, lps::LpReg_VecLpReg)
set_lp_regularisation!(args...)Add one or more Lp regularisation penalties to the objective function.
Each LpRegularisation carries its own norm order and coefficient, so several may be combined in a single model. The penalties are applied as given, so each coefficient must be commensurate with the scale of the objective it is added to.
The same estimator also specifies a p-norm constraint when it is placed in the lpc field of JuMPOptimiser instead of the lp field, in which case its val field is an upper bound on the p-norm rather than a coefficient. See set_weight_norm_p_constraints!.
Mathematical definition
\[\begin{align} \text{penalty} &= \sum_{i} \lambda_{p,\, i} \lVert \boldsymbol{w} \rVert_{p_i}\,. \end{align}\]
Where:
- $\lambda_{p,\, i}$: Regularisation coefficient of the $i$-th term, its
valfield. - $p_i$: Norm order of the $i$-th term, its
pfield. - $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
Arguments
model::JuMP.Model: The JuMP optimisation model.lps::LpReg_VecLpReg: One or more Lp regularisation terms.
Returns
nothing.
Details
lps::LpReg_VecLpReg: For each term, introduces the auxiliary variablest_lp_iandr_lp_i, bounds $\lVert \boldsymbol{w} \rVert_{p_i}$ above byt_lp_iwith a set ofMOI.PowerConeconstraints, and addsval * t_lp_ito the objective penalty viaadd_to_objective_penalty!. Variables and constraints are suffixed by the term's index, so terms do not collide.args...: No-op, used when no Lp regularisation is configured.
Related
PortfolioOptimisers.set_linf_regularisation! — Function
set_linf_regularisation!(model::JuMP.Model, linf::Number)
set_linf_regularisation!(args...)Add an L∞ regularisation penalty to the objective function.
The penalty is applied as given, so linf must be commensurate with the scale of the objective it is added to and with the range of values the weights can take.
Mathematical definition
\[\begin{align} \text{penalty} &= \lambda_\infty \lVert \boldsymbol{w} \rVert_\infty\,. \end{align}\]
Where:
- $\lambda_\infty$: L∞ regularisation coefficient.
- $\boldsymbol{w}$: Portfolio weights vector $N \times 1$.
Arguments
model::JuMP.Model: The JuMP optimisation model.linf::Number: L∞ regularisation coefficient.
Returns
nothing.
Details
linf::Number: Introduces the auxiliary variablet_linf, bounds it with[t_linf; w] in MOI.NormInfinityCone, and addslinf * t_linfto the objective penalty viaadd_to_objective_penalty!.args...: No-op, used when no L∞ regularisation is configured.
Related
PortfolioOptimisers.squared_norm_radius_msg — Function
squared_norm_radius_msg(alg) -> StringBuild the refusal message of assert_ambiguity_radius_formulation, naming the formulation that was paired with a rule.
The message is written once and every refusing method reads it, so the two formulations that refuse a radius today, and any that a later type adds, all say the same thing. The formulation and the rule family are named with nameof, not printed, for the reason assert_calibrated_slots states: a printed type carries a module prefix wherever the name is not visible from Main.
Arguments
alg: The formulation that penalises the squared norm.
Returns
msg::String: The refusal message.
Related
PortfolioOptimisers.assert_ambiguity_radius_formulation — Function
assert_ambiguity_radius_formulation(val, alg) -> NothingRefuse an ambiguity radius that was placed beside a formulation which penalises the squared norm.
L2Regularisation emits val * norm(w, 2) under SOCRiskExpr, and val * norm(w, 2)^2 under the three others. Only the first is the Wasserstein form, so only the first reads val as a radius. A plain number stays legal with every formulation, because a number is whatever coefficient the caller meant it to be. A rule of the radius family states that the number is a radius, and that statement is false beside a squared penalty.
The check is a set of methods rather than a list of types in one body, so a formulation that a later change adds refuses a radius by adding one method here. The permissive fallback is the first method, and each refusing method names one formulation of the family.
Algorithm
- The pair is anything but a radius rule beside a squared formulation: return
nothing. - A radius rule stands beside a
VarianceFormulation, which coversQuadRiskExprandSquaredSOCRiskExpr: refuse. - A radius rule stands beside an
RSOCRiskExpr: refuse.
Arguments
val: The coefficient slot's occupant: a number, or anAbstractAmbiguityRadiusCalibrationAlgorithm.alg: The formulation the coefficient multiplies.
Returns
nothing.
Related
PortfolioOptimisers.assert_penalty_coefficient_role — Function
assert_penalty_coefficient_role(x) -> NothingRefuse a norm-ceiling rule that was placed in a slot which reads its number as a penalty coefficient.
The val field of LpRegularisation is the one dual-use slot in the library. JuMPOptimiser's lp field adds val * norm(w, p) to the objective, where val is an ambiguity radius, and its lpc field bounds norm(w, p) <= val * k, where val is a norm ceiling. One field cannot carry two bounds, so Num_AmbRadNormCeilCal admits both rule families and the slot's owner settles which reading is legal.
This is the penalty half. A rule of the norm-ceiling family states that the number is a ceiling, and a ceiling has no reading as a penalty coefficient: its reciprocal is a floor on the effective number of assets, which says nothing about how strongly the objective should shrink the weights. A plain number stays legal on both routes, because a number is whatever quantity the caller meant it to be.
The check is a set of methods rather than a list of types in one body, on the same terms as assert_ambiguity_radius_formulation. It runs in JuMPOptimiser's constructor, where the caller wrote the field, and again in factory for a term that reached the objective by another route.
Algorithm
- The occupant is anything but a norm-ceiling role: return
nothing. - The occupant is an
LpRegularisation: check itsval. - The occupant is a vector of them: check each.
- The occupant is an
AbstractNormCeilingCalibrationAlgorithm: refuse.
Arguments
x: A regularisation term, a vector of them, or the occupant of avalslot.
Returns
nothing.
Related
PortfolioOptimisers.assert_norm_ceiling_role — Function
assert_norm_ceiling_role(x) -> NothingRefuse an ambiguity-radius rule that was placed in a slot which reads its number as a norm ceiling.
This is the norm-constraint half of the pair assert_penalty_coefficient_role opens, and it carries that method's reading unchanged. A rule of the ambiguity-radius family states that the number is a radius, the coefficient of a norm penalty in the objective. JuMPOptimiser.lpc bounds the norm instead, so the statement is false there.
Algorithm
- The occupant is anything but a radius role: return
nothing. - The occupant is an
LpRegularisation: check itsval. - The occupant is a vector of them: check each.
- The occupant is an
AbstractAmbiguityRadiusCalibrationAlgorithm: refuse.
Arguments
x: A norm-constraint term, a vector of them, or the occupant of avalslot.
Returns
nothing.
Related
PortfolioOptimisers.norm_ceiling_factory — Function
norm_ceiling_factory(x, pr::AbstractPriorResult, slv = nothing)Resolve the norm ceiling in val against prior result pr, and return an LpRegularisation holding the number.
It is the norm-constraint counterpart of factory on the same type, and it is a second verb because the two routes read one field as two quantities. A factory call on the lpc field would refuse the ceiling rule that belongs there, and would resolve the rule under :lpreg_val, the key of the penalty slot.
The two verbs differ in the guard and in the key, and in nothing else. Each refuses the family that has no reading on its own route, this one through assert_norm_ceiling_role. Each states the term's own norm order in the CalibrationContext it resolves the slot against, because one rule placed in lp or in lpc serves every term and each term carries its own p.
The fallback carries its argument through unchanged, which is the route nothing takes. A term whose val is already a number is returned by identity instead, because the resolution gives back the number it holds.
Algorithm
- The argument is neither a term nor a vector of them: return it unchanged.
- The argument is a vector of terms: resolve each of them, and return the vector of the results.
- The argument is one term: refuse a radius rule with
assert_norm_ceiling_role. - State the term's own
pin aCalibrationContext, then resolve the slot under the key:lpcagainst it, givingval. - Pair the declaration with this resolver through
assert_declared_calibration_resolver, which refuses the declared slot when the resolution above did not reach it. valis the number the term already holds: return the term itself. Otherwise rebuild the term through the keyword constructor.
Arguments
x: The norm-constraint term, or a vector of them.pr: Prior result the rule reads.slv: Effective solver, ornothing.
Returns
- The term, or the vector of terms, with each
valholding a number.
Related
References
- [131]
- V. DeMiguel, L. Garlappi, F. J. Nogales and R. Uppal. A Generalized Approach to Portfolio Optimization: Improving Performance by Constraining Portfolio Norms. Management Science 55, 798–812 (2009).