Base Constraint Generation: private API
PortfolioOptimisers.ComparisonOperator — Type
const ComparisonOperator = Union{typeof(==), typeof(<=), typeof(>=)}Union type representing supported comparison operators for constraint generation.
This type is used to specify which comparison operators are valid for defining constraints. It includes equality and both directions of inequality.
The group exists to be a type bound rather than a check. A field annotated with it refuses a fourth operator where the value is stored, so CentralityConstraint(; comp = <) raises a TypeError from the keyword constructor, and no constraint generator carries a branch for an operator it can never receive.
Related
PortfolioOptimisers.comparison_sign_ineq_flag — Function
comparison_sign_ineq_flag(op::ComparisonOperator)
comparison_sign_ineq_flag(op::AbstractString)Return the multiplicative sign and inequality flag for a given comparison operator.
This is the one table mapping a comparison operator to the pair every constraint generator needs: the sign that files a >= row as a <= row, and the flag that sorts the row into the inequality block or the equality block. A parsed constraint carries its operator as a String, and a constraint estimator carries it as a function, so the table dispatches on both spellings and neither caller writes its own copy.
Arguments
op::ComparisonOperator: The comparison operator, as a function.op::AbstractString: The comparison operator, as the string aParsingResultcarries.
Validation
- A string
opis one of"==","<="or">=". Anything else raises anArgumentErrornaming the three. - A function
opoutsideComparisonOperatormatches no method and raises aMethodError. A field bounded by that alias refuses such an operator earlier, at construction.
Returns
sign::Int: The multiplicative sign for the constraint.is_inequality::Bool:trueif the operator is an inequality,falsefor equality.
Examples
julia> PortfolioOptimisers.comparison_sign_ineq_flag(==)(1, false)julia> PortfolioOptimisers.comparison_sign_ineq_flag(<=)(1, true)julia> PortfolioOptimisers.comparison_sign_ineq_flag(>=)(-1, true)julia> PortfolioOptimisers.comparison_sign_ineq_flag(">=")(-1, true)Related
PortfolioOptimisers.resolve_axis_name — Function
resolve_axis_name(name, nx::AbstractVector, sdict::AbstractDict) -> Option{Vector}Resolve one name against an axis: an axis entry names itself, a sdict key expands to its members, and an unknown name gives nothing.
This is the precedence every name-taking constraint generator uses — asset first, then group — written once. The caller diagnoses the nothing, because the suggestion pool differs by caller. The member vector is a copy: sdict is the caller's UniverseSets dictionary, which is configuration reused across folds and optimisers, so de-duplication must never edit it.
Algorithm
- When
nameis onnx, return[name]. The axis is read before the dictionary, so an asset and a group of one name resolve to the asset, and a group of that name is unreachable. - Otherwise read
members, the entrysdictholds forname, ornothingwhen it holds none. - Return
unique(members), so a group naming one asset twice contributes one member.
Arguments
name: The name to resolve.nx: The axis, usually the asset universe.sdict: Dictionary mapping group names to vectors of member names.
Returns
[name]ifnameis on the axis.unique(members)ifnameis a key ofsdict.nothingifnameis neither.
Examples
An asset name wins over a group of the same name, and a repeated member collapses.
julia> nx = ["A", "B", "C"];julia> sdict = Dict("A" => ["B", "C"], "G" => ["A", "A", "B"]);julia> PortfolioOptimisers.resolve_axis_name("A", nx, sdict)1-element Vector{String}: "A"julia> PortfolioOptimisers.resolve_axis_name("G", nx, sdict)2-element Vector{String}: "A" "B"julia> isnothing(PortfolioOptimisers.resolve_axis_name("Q", nx, sdict))trueRelated
PortfolioOptimisers.axis_name_indices — Function
axis_name_indices(members, nx::AbstractVector, on_missing) -> VectorMap resolved member names to axis indices, drop the members that miss the axis, and report them once through on_missing.
on_missing takes the vector of missing members and decides the policy, so a caller can throw, warn, or stay silent without a second copy of the mapping. on_missing is not called when every member is on the axis.
Algorithm
- Map each member to
idx, its first index onnx, or tonothingwhen it is on no index. A repeated axis name is therefore reachable only at its first index. - Collect
missing_members, the members whose index isnothing. - Drop the
nothings fromidx. - When
missing_membersis not empty, callon_missingon it. The call comes after step 3, so a callback that throws throws with the surviving indices already discarded, and a callback that warns leaves the caller holding them.
Arguments
members: Member names, asresolve_axis_namereturns them.nx: The axis, usually the asset universe.on_missing: Callable applied to the vector of members that miss the axis.
Returns
idx: Axis indices of the members that are on the axis, in the order ofmembers. The element type isIntwhen no member missed the axis, andUnion{Nothing, Int}when one did, because step 3 removes thenothings from the vector and not from its type.
Examples
julia> nx = ["A", "B", "C"];julia> PortfolioOptimisers.axis_name_indices(["C", "A"], nx, m -> nothing)2-element Vector{Int64}: 3 1julia> missed = String[];julia> PortfolioOptimisers.axis_name_indices(["A", "Z", "C"], nx, m -> append!(missed, m))2-element Vector{Union{Nothing, Int64}}: 1 3julia> missed1-element Vector{String}: "Z"Related