Skip to content
18

Linear Constraints

PortfolioOptimisers.PartialLinearConstraint Type
julia
struct PartialLinearConstraint{__T_A, __T_B} <: AbstractConstraintResult

PartialLinearConstraint stores the coefficient matrix A and right-hand side vector B for a group of linear constraints. It can represent equality or inequality constraints depending on whether the instance is stored in the eq or ineq fields of LinearConstraint.

Fields

  • A: Linear constraint coefficient matrix.

  • B: Linear constraint response vector.

Constructors

julia
PartialLinearConstraint(;
    A::MatNum,
    B::VecNum
) -> PartialLinearConstraint

Keywords correspond to the struct's fields.

Validation

  • !isempty(A).

  • !isempty(B).

Examples

julia
julia> PartialLinearConstraint(; A = [1.0 2.0; 3.0 4.0], B = [5.0, 6.0])
PartialLinearConstraint
  A ┼ 2×2 Matrix{Float64}
  B ┴ Vector{Float64}: [5.0, 6.0]

Related

source
PortfolioOptimisers.LinearConstraint Type
julia
struct LinearConstraint{__T_ineq, __T_eq} <: AbstractConstraintResult

LinearConstraint holds both the inequality and equality constraints for a portfolio optimisation problem, each represented by a PartialLinearConstraint.

Mathematical definition

AineqxBineqAeqx=Beq.

Where:

  • A: Constraint coefficient matrix.

  • B: Constraint response vector.

  • ineq: Subscript for inequality constraints.

  • eq: Subscript for equality constraints.

  • x: Constrained variable.

Fields

  • ineq: Optional inequality constraints.

  • eq: Optional equality constraints.

Constructors

julia
LinearConstraint(;
    ineq::Option{<:PartialLinearConstraint} = nothing,
    eq::Option{<:PartialLinearConstraint} = nothing
) -> LinearConstraint

Keywords correspond to the struct's fields.

Validation

  • Both eq and ineq cannot be nothing at the same time, !(isnothing(ineq) && isnothing(eq)).

Examples

julia
julia> ineq = PartialLinearConstraint(; A = [1.0 2.0; 3.0 4.0], B = [5.0, 6.0]);

julia> eq = PartialLinearConstraint(; A = [7.0 8.0; 9.0 10.0], B = [11.0, 12.0]);

julia> LinearConstraint(; ineq = ineq, eq = eq)
LinearConstraint
  ineq ┼ PartialLinearConstraint
       │   A ┼ 2×2 Matrix{Float64}
       │   B ┴ Vector{Float64}: [5.0, 6.0]
    eq ┼ PartialLinearConstraint
       │   A ┼ 2×2 Matrix{Float64}
       │   B ┴ Vector{Float64}: [11.0, 12.0]

Related

source
PortfolioOptimisers.VecLc Type
julia
abstract type AbstractArray{var"#s875"<:LinearConstraint, 1}

Alias for an abstract vector of LinearConstraint elements.

Related

source
PortfolioOptimisers.Lc_VecLc Type
julia
const Lc_VecLc = Union{<:LinearConstraint, <:VecLc}

Alias for a union of a single LinearConstraint or a vector of them.

Related

source
PortfolioOptimisers.LinearConstraintEstimator Type
julia
struct LinearConstraintEstimator{__T_val, __T_key} <: AbstractConstraintEstimator

Container for one or more linear constraint equations to be parsed and converted into constraint matrices.

Fields

  • val: Constraint equation(s) to parse.

  • key: Key to specify the asset universe in sets.dict. If nothing, the key is taken from sets.key.

Constructors

julia
LinearConstraintEstimator(;
    val::EqnType,
    key::Option{<:AbstractString} = nothing
) -> LinearConstraintEstimator

Keywords correspond to the struct's fields.

Validation

  • !isempty(val).

Examples

julia
julia> lce = LinearConstraintEstimator(; val = ["w_A + w_B == 1", "w_A >= 0.1"]);

julia> sets = AssetSets(; key = "nx", dict = Dict("nx" => ["w_A", "w_B"]));

julia> linear_constraints(lce, sets)
LinearConstraint
  ineq ┼ PartialLinearConstraint
       │   A ┼ 1×2 LinearAlgebra.Transpose{Float64, Matrix{Float64}}
       │   B ┴ Vector{Float64}: [-0.1]
    eq ┼ PartialLinearConstraint
       │   A ┼ 1×2 LinearAlgebra.Transpose{Float64, Matrix{Float64}}
       │   B ┴ Vector{Float64}: [1.0]

Related

source
PortfolioOptimisers.LcE_Lc Type
julia
const LcE_Lc = Union{<:LinearConstraintEstimator, <:LinearConstraint}

Alias for a union of linear constraint estimator and linear constraint types.

Related

source
PortfolioOptimisers.VecLcE Type
julia
abstract type AbstractArray{var"#s875"<:LinearConstraintEstimator, 1}

Alias for an abstract vector of LinearConstraintEstimator elements.

Related

source
PortfolioOptimisers.LcE_VecLcE Type
julia
const LcE_VecLcE = Union{<:LinearConstraintEstimator, <:VecLcE}

Alias for a union of a single LinearConstraintEstimator or a vector of them.

Related

source
PortfolioOptimisers.VecLcE_Lc Type
julia
abstract type AbstractArray{var"#s875"<:(Union{var"#s875", var"#s874"} where {var"#s875"<:LinearConstraintEstimator, var"#s874"<:LinearConstraint}), 1}

Alias for an abstract vector of LcE_Lc elements.

Related

source
PortfolioOptimisers.LcE_Lc_VecLcE_Lc Type
julia
const LcE_Lc_VecLcE_Lc = Union{<:LcE_Lc, <:VecLcE_Lc}

Alias for a union of LcE_Lc or a vector of them.

Related

source
PortfolioOptimisers.ParsingResult Type
julia
struct ParsingResult{__T_vars, __T_coef, __T_op, __T_rhs, __T_eqn} <: AbstractParsingResult

Structured result for standard linear constraint equation parsing.

ParsingResult is the canonical output of parse_equation for standard linear constraints. It stores all information needed to construct constraint matrices for portfolio optimisation, including variable names, coefficients, the comparison operator, right-hand side value, and a formatted equation string.

Fields

  • vars: Variable names in the parsed constraint expression.

  • coef: Coefficients corresponding to the constraint variables.

  • op: Comparison operator (==, <=, or >=).

  • rhs: Right-hand side value of the constraint.

  • eqn: Formatted string representation of the constraint equation.

Related

source
PortfolioOptimisers.VecPR Type
julia
abstract type AbstractArray{var"#s875"<:ParsingResult, 1}

Alias for an abstract vector of ParsingResult elements.

Related

source
PortfolioOptimisers.PR_VecPR Type
julia
const PR_VecPR = Union{<:ParsingResult, <:VecPR}

Alias for a union of a single ParsingResult or a vector of them.

Related

source
PortfolioOptimisers.replace_group_by_assets Function
julia
replace_group_by_assets(res::PR_VecPR,
                        sets::AssetSets; bl_flag::Bool = false, ep_flag::Bool = false,
                        rho_flag::Bool = false)

If res is a vector of ParsingResult objects, this function will be applied to each element of the vector.

Expand group or special variable references in a ParsingResult to their corresponding asset names.

This function takes a ParsingResult containing variable names (which may include group names, prior(...) expressions, or correlation views like (A, B)), and replaces these with the actual asset names from the provided AssetSets. It supports Black-Litterman-style group expansion, entropy pooling prior views, and correlation view parsing for advanced constraint generation.

Arguments

  • res: A ParsingResult object containing variables and coefficients to be expanded.

  • sets: An AssetSets object specifying the asset universe and groupings.

  • bl_flag: If true, enables Black-Litterman-style group expansion.

  • ep_flag: If true, enables expansion of prior(...) expressions for entropy pooling.

  • rho_flag: If true, enables expansion of correlation views (A, B) for entropy pooling.

Validation

  • bl_flag can only be true if both ep_flag and rho_flag are false.

  • rho_flag can only be true if ep_flag is also true.

Details

  • Group names in res.vars are replaced by the corresponding asset names from sets.dict.

  • If bl_flag is true, coefficients for group references are divided equally among the assets in the group.

  • If ep_flag is true, expands prior(asset) or prior(group) expressions for entropy pooling.

  • If rho_flag is true, expands correlation view expressions (A, B) or prior(A, B) for entropy pooling, mapping them to asset pairs.

  • If a variable or group is not found in sets.dict, it is skipped.

Returns

  • res::ParsingResult: A new ParsingResult with all group and special variable references expanded to asset names.

Examples

julia
julia> sets = AssetSets(; key = "nx", dict = Dict("nx" => ["A", "B", "C"], "group1" => ["A", "B"]));

julia> res = parse_equation("group1 + 2C == 1")
ParsingResult
  vars ┼ Vector{String}: ["C", "group1"]
  coef ┼ Vector{Float64}: [2.0, 1.0]
    op ┼ String: "=="
   rhs ┼ Float64: 1.0
   eqn ┴ SubString{String}: "2.0*C + group1 == 1.0"

julia> replace_group_by_assets(res, sets)
ParsingResult
  vars ┼ Vector{String}: ["C", "A", "B"]
  coef ┼ Vector{Float64}: [2.0, 1.0, 1.0]
    op ┼ String: "=="
   rhs ┼ Float64: 1.0
   eqn ┴ String: "2.0*C + 1.0*A + 1.0*B == 1.0"

Related

source
PortfolioOptimisers.estimator_to_val Function
julia
estimator_to_val(dict::EstValType, sets::AssetSets, val::Option{<:Number} = nothing, key::Option{<:AbstractString} = nothing; strict::Bool = false)

Return value for assets or groups, based on a mapping and asset sets.

The function creates the vector and sets the values for assets or groups as specified by dict, using the asset universe and groupings in sets. If a key in dict is not found in the asset sets, the function either throws an error or issues a warning, depending on the strict flag.

Arguments

  • arr: The array to be modified in-place.

  • dict: A dictionary, vector of pairs, or single pair mapping asset or group names to values.

  • sets: The AssetSets containing the asset universe and group definitions.

  • val: The default value to assign to assets not specified in dict.

  • key: (Optional) Key in the AssetSets to specify the asset universe for constraint generation. When provided, takes precedence over key field of AssetSets.

  • strict: If true, throws an error if a key in dict is not found in the asset sets; if false, issues a warning.

Details

  • Iterates over the (key, value) pairs in dict.

Warning

If the same asset is found in subsequent iterations, its value will be overwritten in favour of the most recent one. To ensure determinism, use an OrderedDict or a vector of pairs.

  • If a key in dict matches an asset in the universe, the corresponding entry in arr is set to the specified value.

  • If a key matches a group in sets, all assets in the group are set to the specified value using group_to_val!.

  • If a key is not found and strict is true, an ArgumentError is thrown; otherwise, a warning is issued.

  • The operation is performed in-place on arr.

Returns

  • arr::VecNum: Value array.

Related

source
julia
estimator_to_val(val::Option{<:Number}, args...; kwargs...)

Fallback no-op for value mapping in asset/group estimators.

This method returns the input value val as-is, without modification or mapping. It serves as a fallback for cases where the input is already a numeric value, a vector of numeric values, or nothing, and no further processing is required.

Arguments

  • val: A value of type Nothing or a single numeric value.

  • args...: Additional positional arguments (ignored).

  • kwargs...: Additional keyword arguments (ignored).

Returns

  • val::Option{<:Number}: The input val, unchanged.

Related

source
julia
estimator_to_val(val::VecNum, sets::AssetSets, ::Any = nothing,
                 key::Option{<:AbstractString} = nothing; kwargs...)

Return a numeric vector for asset/group estimators, validating length against asset universe.

This method checks that the input vector val matches the length of the asset universe in sets, and returns it unchanged if valid. It is used as a fast path for workflows where the value vector is already constructed and requires only defensive validation.

Arguments

  • val: Numeric vector to be mapped to assets/groups.

  • sets: AssetSets containing the asset universe and group definitions.

  • ::Any: Fill value for API consistency (ignored).

  • key: (Optional) Key in the AssetSets to specify the asset universe for constraint generation. When provided, takes precedence over key field of AssetSets.

  • kwargs...: Additional keyword arguments (ignored).

Returns

  • val::VecNum: The input vector, unchanged.

Validation

  • length(val) == length(sets.dict[ifelse(isnothing(key), sets.key, key)].

Related

source
julia
estimator_to_val(val::MatNum, sets::AssetSets, ::Any = nothing,
                 key::Option{<:AbstractString} = nothing; dims::Int = 2, kwargs...)

Return a numeric matrix for asset/group estimators, validating length against asset universe.

This method checks that size of dims of the input matrix val matches the length of the asset universe in sets, and returns it unchanged if valid. It is used as a fast path for workflows where the value vector is already constructed and requires only defensive validation.

Arguments

  • val: Numeric matrix to be mapped to assets/groups.

  • sets: AssetSets containing the asset universe and group definitions.

  • ::Any: Fill value for API consistency (ignored).

  • key: (Optional) Key in the AssetSets to specify the asset universe for constraint generation. When provided, takes precedence over key field of AssetSets.

  • dims: Dimension along which to validate the matrix size.

  • kwargs...: Additional keyword arguments (ignored).

Returns

  • val::VecNum: The input vector, unchanged.

Validation

  • size(val, dims) == length(sets.dict[ifelse(isnothing(key), sets.key, key)].

Related

source
julia
estimator_to_val(
    ::UniformValues,
    sets::AssetSets;
    ...
) -> Any
estimator_to_val(
    ::UniformValues,
    sets::AssetSets,
    ;
    ...
) -> Any
estimator_to_val(
    ::UniformValues,
    sets::AssetSets,
    ,
    key::Union{Nothing, AbstractString};
    datatype,
    kwargs...
) -> Any

Return a uniform value vector for all assets in the universe defined by sets.

Each entry equals 1/N where N is the number of assets.

Related

source
PortfolioOptimisers.parse_equation Function
julia
parse_equation(eqn::EqnType;
               ops1::Tuple = ("==", "<=", ">="), ops2::Tuple = (:call, :(==), :(<=), :(>=)),
               datatype::DataType = Float64, kwargs...)

Parse a linear constraint equation from a string into a structured ParsingResult.

Arguments

  • eqn: The equation string to parse.

    • eqn::AbstractVector: Each element needs to meet the criteria below.

    • eqn::AbstractString: Must contain exactly one comparison operator from ops1.

      • ops1: Tuple of valid comparison operators as strings.
    • eqn::Expr: Must contain exactly one comparison operator from ops1.

      • ops2: Tuple of valid comparison operator expressions.
  • datatype: The numeric type to use for coefficients and right-hand side.

  • kwargs...: Additional keyword arguments, ignored.

Validation

  • The equation must contain exactly one valid comparison operator from ops1.

  • Both sides of the equation must be valid Julia expressions.

Details

  • If eqn::AbstractVector, the function is applied element-wise.

  • The function first checks for invalid operator patterns (e.g., "++").

  • It searches for the first occurrence of a valid comparison operator from ops1 in the equation string. Errors if there are more than one or none.

  • The equation is split into left- and right-hand sides using the detected operator.

  • If eqn::AbstractString:

    • Both sides are parsed into Julia expressions using Meta.parse.
  • If eqn::Expr:

    • Expression is ready as is.
  • Numeric functions and constants (e.g., Inf) are recursively evaluated.

  • All terms are moved to the left-hand side and collected, separating coefficients and variables.

  • The constant term is moved to the right-hand side, and the equation is formatted for display.

  • The result is returned as a ParsingResult containing the collected information.

Returns

  • If eqn::Str_Expr:

    • res::ParsingResult: Structured parsing result.
  • If eqn::AbstractVector:

    • res::Vector{ParsingResult}: Vector of structured parsing results.

Examples

julia
julia> parse_equation("w_A + 2w_B <= 1")
ParsingResult
  vars ┼ Vector{String}: ["w_A", "w_B"]
  coef ┼ Vector{Float64}: [1.0, 2.0]
    op ┼ String: "<="
   rhs ┼ Float64: 1.0
   eqn ┴ SubString{String}: "w_A + 2.0*w_B <= 1.0"

Related

source
PortfolioOptimisers.linear_constraints Function
julia
linear_constraints(lcs::Option{<:LinearConstraint}, args...; kwargs...)

No-op fallback for returning an existing LinearConstraint object or nothing.

This method is used to pass through an already constructed LinearConstraint object or nothing without modification. It enables composability and uniform interface handling in constraint generation workflows, allowing functions to accept either raw equations or pre-built constraint objects.

Arguments

  • lcs: An existing LinearConstraint object or nothing.

  • args...: Additional positional arguments (ignored).

  • kwargs...: Additional keyword arguments (ignored).

Returns

  • lcs::Option{<:LinearConstraint}: The input, unchanged.

Related

source
julia
linear_constraints(eqn::EqnType,
                   sets::AssetSets; ops1::Tuple = ("==", "<=", ">="),
                   key::Option{<:AbstractString} = nothing;
                   ops2::Tuple = (:call, :(==), :(<=), :(>=)), datatype::DataType = Float64,
                   strict::Bool = false, bl_flag::Bool = false)

Parse and convert one or more linear constraint equations into a LinearConstraint object.

This function parses one or more constraint equations (as strings, expressions, or vectors thereof), replaces group or asset references using the provided AssetSets, and constructs the corresponding constraint matrices. The result is a LinearConstraint object containing both equality and inequality constraints, suitable for use in portfolio optimisation routines.

Arguments

  • eqn: A single constraint equation (as AbstractString or Expr), or a vector of such equations.

  • sets: An AssetSets object specifying the asset universe and groupings.

  • ops1: Tuple of valid comparison operators as strings.

  • ops2: Tuple of valid comparison operators as expression heads.

  • datatype: Numeric type for coefficients and right-hand side.

  • strict: If true, throws an error if a variable or group is not found in sets; if false, issues a warning.

  • bl_flag: If true, enables Black-Litterman-style group expansion.

Details

  • Each equation is parsed using parse_equation, supporting both string and expression input.

  • Asset and group references in the equations are expanded using replace_group_by_assets and the provided sets.

  • The function separates equality and inequality constraints, assembling the corresponding matrices and right-hand side vectors.

  • Input validation is performed using @argcheck to ensure non-empty and consistent constraints.

  • Returns nothing if no valid constraints are found after parsing and expansion.

Returns

  • lcs::LinearConstraint: An object containing the assembled equality and inequality constraints, or nothing if no constraints are present.

Examples

julia
julia> sets = AssetSets(; key = "nx", dict = Dict("nx" => ["w_A", "w_B", "w_C"]));

julia> linear_constraints(["w_A + w_B == 1", "w_A >= 0.1"], sets)
LinearConstraint
  ineq ┼ PartialLinearConstraint
       │   A ┼ 1×3 LinearAlgebra.Transpose{Float64, Matrix{Float64}}
       │   B ┴ Vector{Float64}: [-0.1]
    eq ┼ PartialLinearConstraint
       │   A ┼ 1×3 LinearAlgebra.Transpose{Float64, Matrix{Float64}}
       │   B ┴ Vector{Float64}: [1.0]

Related

source
julia
linear_constraints(lcs::LcE_VecLcE,
                   sets::AssetSets; datatype::DataType = Float64, strict::Bool = false,
                   bl_flag::Bool = false)

If lcs is a vector of LinearConstraintEstimator objects, this function is broadcast over the vector.

This method is a wrapper calling:

julia
linear_constraints(lcs.val, sets, lcs.key; datatype = datatype, strict = strict, bl_flag = bl_flag)

It is used for type stability and to provide a uniform interface for processing constraint estimators, as well as simplifying the use of multiple estimators simultaneously.

Related

source
PortfolioOptimisers.get_linear_constraints Function
julia
get_linear_constraints(lcs::PR_VecPR, sets::AssetSets,
                       key::Option{<:AbstractString} = nothing;
                       datatype::DataType = Float64, strict::Bool = false)

Convert parsed linear constraint equations into a LinearConstraint object.

get_linear_constraints takes one or more ParsingResult objects (as produced by parse_equation), expands variable names using the provided AssetSets, and assembles the corresponding constraint matrices and right-hand side vectors. The result is a LinearConstraint object containing both equality and inequality constraints, suitable for use in portfolio optimisation routines.

Arguments

  • lcs: A single ParsingResult or a vector of such objects, representing parsed constraint equations.

  • sets: An AssetSets object specifying the asset universe and groupings.

  • datatype: Numeric type for coefficients and right-hand side.

  • strict: If true, throws an error if a variable or group is not found in sets; if false, issues a warning.

Details

  • For each constraint, variable names are matched to the asset universe in sets.

  • Coefficient vectors are assembled for each constraint, with entries corresponding to the order of assets in sets.

  • Constraints are separated into equality (==) and inequality (<=, >=) types.

  • The function validates that all constraints reference valid assets or groups, using @argcheck for defensive programming.

  • Returns nothing if no valid constraints are found after processing.

Returns

  • lcs::LinearConstraint: An object containing the assembled equality and inequality constraints, or nothing if no constraints are present.

Related

source
PortfolioOptimisers.AbstractParsingResult Type
julia
abstract type AbstractParsingResult <: AbstractConstraintResult

Abstract supertype for all equation parsing result types in PortfolioOptimisers.jl.

All concrete and/or abstract types representing parsing results should be subtypes of AbstractParsingResult.

Related

source
PortfolioOptimisers.group_to_val! Function
julia
group_to_val!(nx::VecStr, sdict::AbstractDict, key::Any, val::Number,
              arr::VecNum, strict::Bool, nxkey::AbstractString)

Set values in a vector for all assets belonging to a specified group.

group_to_val! maps the assets in group key to their corresponding indices in the asset universe nx, and sets the corresponding entries in the vector arr to the value val. If the group is not found, the function either throws an error or issues a warning, depending on the strict flag.

Arguments

  • nx: Vector of asset names.

  • sdict: Dictionary mapping group names to vectors of asset names.

  • key: Name of the group of assets to set values for.

  • val: The value to assign to the assets in the group.

  • arr: The array to be modified in-place.

  • strict: If true, throws an error if key is not found in sdict; if false, issues a warning.

  • nxkey: Name of the asset-universe key in sets.dict (e.g. "nx"), used only to name the universe in the diagnostic message — see unknown_variable_msg / missing_group_assets_msg.

Details

  • If key is found in sdict, all assets in the group are mapped to their indices in nx, and the corresponding entries in arr are set to val.

  • If key is not found and strict is true, an ArgumentError is thrown; otherwise, a warning is issued.

  • Diagnostic messages name only the universe size (never the full universe or the input value dictionary), routed through the shared builders in 02_Tools.jl.

Returns

  • nothing. The operation is performed in-place on arr.

Related

source
PortfolioOptimisers._parse_equation Function
julia
_parse_equation(lhs, opstr::AbstractString, rhs; datatype::DataType = Float64)

Parse and canonicalise a linear constraint equation from Julia expressions.

_parse_equation takes the left-hand side (lhs) and right-hand side (rhs) of a constraint equation, both as Julia expressions, and a comparison operator string (opstr). It evaluates numeric functions, moves all terms to the left-hand side, collects coefficients and variables, and returns a ParsingResult with the canonicalised equation.

Arguments

  • lhs: Left-hand side of the equation as a Julia expression.

  • opstr: Comparison operator as a string.

  • rhs: Right-hand side of the equation as a Julia expression.

  • datatype: Numeric type for coefficients and right-hand side.

Details

  • Recursively evaluates numeric functions and constants (e.g., Inf) on both sides.

  • Moves all terms to the left-hand side (lhs - rhs == 0).

  • Collects and sums like terms, separating variables and constants.

  • Moves the constant term to the right-hand side, variables to the left.

  • Formats the simplified equation as a string.

  • Returns a ParsingResult containing variable names, coefficients, operator, right-hand side value, and formatted equation.

Returns

  • res::ParsingResult: Structured result with canonicalised variables, coefficients, operator, right-hand side, and formatted equation.

Related

source
PortfolioOptimisers.rethrow_parse_error Function
julia
rethrow_parse_error(expr; side = :lhs)

Internal utility for error handling during equation parsing.

rethrow_parse_error is used to detect and handle incomplete or invalid expressions encountered while parsing constraint equations. It is called on both sides of an equation during parsing to ensure that the expressions are valid and complete. If an incomplete expression is detected, a Meta.ParseError is thrown; otherwise, the function returns nothing.

Arguments

  • expr: The parsed Julia expression to check. Can be an Expr, Nothing, or any other type.

  • side: Symbol indicating which side of the equation is being checked (:lhs or :rhs). Used for error messages.

Details

  • If expr is Nothing (the side is empty, e.g. a truncated equation string), a Meta.ParseError is thrown — the parser fails closed rather than assuming zero, because a silently-assumed zero constraint is one the author never wrote. Callers who mean zero must write it explicitly.

  • If expr is an incomplete expression (expr.head == :incomplete), a Meta.ParseError is thrown with a descriptive message.

  • For all other cases, the function returns nothing and does not modify the input.

Validation

  • Throws a Meta.ParseError if the expression is empty or incomplete.

Returns

  • nothing.

Related

source
PortfolioOptimisers.format_term Function
julia
format_term(coeff, var)

Format a single term in a linear constraint equation as a string.

format_term takes a coefficient and a variable name and returns a string representation suitable for display in a canonicalised linear constraint equation. Handles special cases for coefficients of 1 and -1 to avoid redundant notation.

Arguments

  • coeff: Numeric coefficient for the variable.

  • var: Variable name as a string.

Details

  • If coeff == 1, returns "$var" (no explicit coefficient).

  • If coeff == -1, returns "-$(var)" (no explicit coefficient).

  • Otherwise, returns "$(coeff)*$(var)".

Returns

  • term_str::String: The formatted term as a string.

Related

source
PortfolioOptimisers.collect_terms! Function
julia
collect_terms!(expr, coeff, terms)

Recursively collect and expand terms from a Julia expression for linear constraint parsing.

collect_terms! traverses a Julia expression tree representing a linear equation, expanding and collecting all terms into a vector of (coefficient, variable) pairs. It handles numeric constants, variables, and arithmetic operations (+, -, *, /), supporting canonicalisation of linear constraint equations for further processing.

Arguments

  • expr: The Julia expression to traverse.

  • coeff: The current numeric coefficient to apply.

  • terms: A vector to which (coefficient, variable) pairs are appended in-place. Each pair is of the form (Float64, Option{<:String}), where Nothing indicates a constant term.

Details

  • expr:
    • Number: Appends (coeff * oftype(coeff, expr), nothing) to terms.

    • Symbol: Appends (coeff, string(expr)) to terms.

    • Expr:

      • For multiplication (*), distributes the coefficient to the numeric part.

      • For division (/), divides the coefficient by the numeric denominator.

      • For addition (+), recursively collects terms from all arguments.

      • For subtraction (-), recursively collects terms from all arguments except the last, which is negated.

      • For all other expressions, treats as a variable and appends as (coeff, string(expr)).

Returns

  • nothing. The function modifies terms in-place.

Related

source
PortfolioOptimisers._collect_terms Function
julia
_collect_terms(expr::Union{Symbol, Expr, <:Number})

Expand and collect all terms from a Julia expression representing a linear constraint equation.

_collect_terms takes a Julia expression (such as the left-hand side of a constraint equation), recursively traverses its structure, and returns a vector of (coefficient, variable) pairs. It supports numeric constants, variables, and arithmetic operations (+, -, *, /), and is used to canonicalise linear constraint equations for further processing.

Arguments

  • expr: The Julia expression to expand.

Details

  • Calls collect_terms! internally with an initial coefficient of 1.0 and an empty vector.

  • Numeric constants are collected as (coefficient, nothing).

  • Variables are collected as (coefficient, variable_name).

  • Arithmetic expressions are recursively expanded and collected.

Returns

  • terms::Vector{Tuple{Float64, Option{<:String}}}: A vector of (coefficient, variable) pairs, where variable is a string for variable terms or nothing for constant terms.

Related

source
PortfolioOptimisers.allowed_functions Constant
julia
allowed_functions = Dict{Symbol, Function}(:+ => +, :- => -, :* => *, :/ => /,
                                           :^ => ^, :sqrt => sqrt, :cbrt => cbrt,
                                           :exp => exp, :exp2 => exp2, :exp10 => exp10,
                                           :log => log, :log2 => log2, :log10 => log10,
                                           :abs => abs, :min => min, :max => max)

Enumerated table of the functions permitted in equation parsing, mapping each allowed name directly to its function object. Evaluating constraint/view strings crosses a trust boundary (config files, spreadsheets, UI), so the parser must be able to call only these 16 mathematical functions. Using an explicit Symbol => Function table — rather than resolving a name against Base with getfield(Base, fname) — bounds that capability to exactly this table: a name absent from the keys fails closed with a Meta.ParseError, and the set of callable functions cannot drift from the set of allowed names, because they are the same list. See docs/adr/0025-enumerated-parser-allowlist.md.

The prior(...) marker is deliberately absent from this table: it names assets/groups (not numbers) and is expanded structurally by eval_numeric_functions/replace_group_by_assets, never evaluated numerically.

source
PortfolioOptimisers.eval_numeric_functions Function
julia
eval_numeric_functions(expr, datatype::DataType = Float64)

Recursively evaluate numeric functions and constants in a Julia expression.

eval_numeric_functions traverses a Julia expression tree and evaluates any sub-expressions that are purely numeric, including standard mathematical functions and constants (such as Inf). This is used to simplify constraint equations before further parsing and canonicalisation.

When an allowlisted function is actually evaluated (all its arguments are numeric), its arguments are coerced to datatype (a float type) first, so the arithmetic happens in the same numeric domain the optimiser will use rather than in machine Int64. This prevents integer literals from combining and wrapping — e.g. 2^64 yields 1.8446744073709552e19 rather than silently wrapping to 0, and 2^-1 yields 0.5 rather than a DomainError. Numeric literals that survive inside an unevaluated (nonlinear) subexpression are left untouched, so 2^z still renders as 2 ^ z.

Only the functions enumerated in allowed_functions may be evaluated; any other call head fails closed with a Meta.ParseError. The prior(...) marker is handled structurally (see replace_group_by_assets) and throws a Meta.ParseError if given purely numeric arguments.

Arguments

  • expr: The Julia expression to evaluate. Can be a Number, Symbol, or Expr.

  • datatype: Float type into which numeric arguments are coerced before an allowlisted function is evaluated.

Details

  • expr:
    • Number: It is returned as-is.

    • :Inf: Returns Inf.

    • Expr: Representing a function call whose arguments are all numeric, the allowlisted function is evaluated (on arguments coerced to datatype) and replaced with its result.

    • Otherwise, the function recurses into sub-expressions, returning a new expression with numeric parts evaluated.

Returns

  • The evaluated expression, with all numeric sub-expressions replaced by their computed values. Non-numeric or symbolic expressions are returned in their original or partially simplified form.

Related

source
PortfolioOptimisers.has_invalid_plus Function
julia
has_invalid_plus(expr)

Check whether a Julia expression contains an invalid + operator in a constraint context.

Internal helper used during linear constraint parsing to detect unsupported + operator usage in constraint expressions.

Arguments

  • expr: Julia expression to check.

Returns

  • Bool: true if the expression contains an invalid +, false otherwise.
source
PortfolioOptimisers.port_opt_view Method
julia
port_opt_view(
    sets::AssetSets,
    i,
    args...
) -> AssetSets{var"#s179", var"#s1791", <:AbstractDict{var"#s873", var"#s872"}} where {var"#s179"<:AbstractString, var"#s1791"<:AbstractString, var"#s873"<:AbstractString, var"#s872"}

Return a view of an AssetSets restricted to the assets at index i.

Slices all key-prefixed groups by i, and derives unique-entry ukey-prefixed groups from the corresponding sliced key group.

Related

source
PortfolioOptimisers._expr_depth_exceeds Function
julia
_expr_depth_exceeds(x, limit::Integer) -> Bool

Return true if the expression tree x is deeper than limit.

Guards the Expr form of parse_equation against a deeply nested AST that no string length cap covers. The check itself recurses at most limit + 1 frames deep and short-circuits the moment the limit is breached, so it cannot exhaust the stack it protects.

source