Tools
PortfolioOptimisers.jl is a complex codebase which uses a variety of general purpose tools including functions, constants and types.
Utility functions
We strive to be as type-stable, inferrable, and immutable as possible in order to improve robustness, performance, and correctness. These functions help us achieve these goals.
PortfolioOptimisers.traverse_concrete_subtypes — Function
traverse_concrete_subtypes(t, ctarr::Option{<:AbstractVector} = nothing) -> AbstractVectorRecursively traverse all subtypes of the given abstract type t and collect all struct types into ctarr.
A struct type is not the same as a concrete type. InteractiveUtils.subtypes reports a parametric struct as its UnionAll, and a UnionAll is not concrete, so a parametric struct is collected under its bare name and isconcretetype is false for it. What every entry does satisfy is isstructtype. A caller that needs concrete types must instantiate the parameters itself.
Algorithm
- When
ctarrisnothing, make it an emptyVector{Any}. The accumulator is threaded through the recursion, so one array collects every branch. - Read
sts, the direct subtypes oft, withInteractiveUtils.subtypes. - For each subtype
stofsts, take one of two branches:stis not a struct type, so it is a further abstract type: call this function again onstwith the samectarr.stis a struct type: pushstontoctarr. The test isisstructtypeand notisconcretetype, which is why a parametric struct is collected.
- Return
ctarr.
The recursion descends the whole tree below t, so an abstract type at any depth is opened and never collected. The order of ctarr is the depth-first order of the tree, which follows the order that InteractiveUtils.subtypes reports.
Arguments
t: An abstract type whose subtypes will be traversed.ctarr: Optional. An array to collect the struct types into. If not provided, a new empty array is created.
Returns
types::Vector{Any}: An array holding every struct type that is a subtype, direct or indirect, oft. A parametric struct appears as itsUnionAll.
Examples
julia> abstract type MyAbstract endjulia> struct MyConcrete1 <: MyAbstract endjulia> struct MyConcrete2 <: MyAbstract endjulia> PortfolioOptimisers.traverse_concrete_subtypes(MyAbstract)2-element Vector{Any}: MyConcrete1 MyConcrete2Related
PortfolioOptimisers.concrete_typed_array — Function
concrete_typed_array(A::AbstractArray) -> Array{Union{...}}Convert an AbstractArray A to a concrete typed array, where each element is of the same type as the elements of A.
This is useful for converting arrays with abstract element types to arrays with concrete element types, which can improve performance in some cases.
Algorithm
- Read the concrete type of every element of
Awithtypeof.(A). - Build the element type
Union{typeof.(A)...}, the union of exactly those types. An element type that no element carries is absent from the union. - Splat
Ainto a vector of that element type, which flattensAto one dimension. - Reshape the vector back to
size(A), and return it.
The elements are copied into a new array, and each keeps its own type. The union is built from the values, so the result is only as narrow as the array's contents allow: an Any array holding one Int64 comes back as a Vector{Int64}.
Arguments
A: The input array.
Returns
A_new::Vector{Union{...}}: A new array with the same shape asA, but with a concrete element type inferred from the elements ofA.
Examples
julia> A = Any[1, 2.0, 3];julia> PortfolioOptimisers.concrete_typed_array(A)3-element Vector{Union{Float64, Int64}}: 1 2.0 3Related
PortfolioOptimisers.factory — Method
factory(a::Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
<:AbstractResult}, args...; kwargs...) -> a
factory(a::AbstractVector{<:Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
<:AbstractResult}}, args...; kwargs...) -> VectorNo-op factory function for constructing objects with a uniform interface.
Defining methods which dispatch on the first argument allows for a consistent factory interface across different types.
factory and port_opt_view are the two propagation mechanisms in this library. They are duals: factory threads runtime values (prior moments, observation weights, previous portfolio weights) down through a composed struct tree; port_opt_view threads an index selection (a subset of assets or observations) down through the same tree.
The vector method is the one forwarding contract for every vector-valued propagation field: it applies factory to each element and forwards args... and kwargs... unchanged, so a family that admits a vector of estimators, algorithms, or results needs no method of its own. A family that needs more than the forward, such as a concrete element type (concrete_typed_array_if_abstract), defines its own more specific method.
Algorithm
The scalar method:
- Return
aunchanged, and dropargs...andkwargs.... This method is the leaf of the recursion, and it is what makes an untagged type safe to call the verb on.
The vector method:
- For each element
aiofa, callfactoryonai, and forwardargs...andkwargs...unchanged. - Collect the results into a new vector, in the order of
a, and return it.
A @propagatable struct with at least one @fprop- or @wprop-tagged field carries a generated method that dominates the scalar method. That method rebuilds the struct with its keyword constructor, sending each @fprop field through factory_child and each @wprop field through _wprop.
Arguments
a: Indicates no object should be constructed, or a vector whose elements are rebuilt one by one.args...: Arbitrary positional arguments (ignored by the scalar method, forwarded by the vector method).kwargs...: Arbitrary keyword arguments (ignored by the scalar method, forwarded by the vector method).
Returns
a: The input unchanged.v::Vector: The element-wise rebuilds, for the vector method.
Examples
julia> factory(nothing, 1, 2; x = 3)julia> factory(MeanValue())MeanValue w ┴ nothingRelated
PortfolioOptimisers.@propagatable — Macro
@propagatable exprDefine a struct and automatically generate its propagation methods from five orthogonal, stackable field tags:
@fprop(factory propagation): tagged fields receivefactory_childcalls whenfactoryis invoked, recursing runtime values (observation weights, prior results, solvers, …) down the composition tree. Afactory(x, args...)method is always generated (it is the identity when no field is tagged@fprop/@wprop).@wprop(weights replacement): tagged observation-weights fields are replaced by an incomingObsWeightsargument via_wprop(and left unchanged when none is threaded). Use@wpropfor the weights slot and@fpropfor sub-estimators — a weights field that defaults tonothingmust become the incoming weights, which conflicts with@fprop'snothing-passthrough.@vprop(view propagation): tagged fields receiveport_opt_viewcalls when a view (an index selection) is propagated, recursing into composed children and slicing data arrays. Aport_opt_viewmethod is generated only when at least one field is tagged@vprop.@pprop(prior selection): tagged fields are selected from the same-named field on a prior result viasel(getfield(x, :f), getproperty(pr, :f)).@cprop(context selection): tagged fields are selected against a threaded optimiser value (a solver) found by type viasel(getfield(x, :f), _ctx(args...)).
When at least one field is tagged @pprop or @cprop, a second method factory(x, pr::AbstractPriorResult, args...) is generated. It selects @pprop/@cprop fields as above and threads @fprop-only fields with pr (factory_child(getfield(x, :f), pr, args...)); a field tagged both @pprop and @fprop is prior-selected in this method (@pprop wins). It then calls resolve_deferred_quantities on the selected struct — the identity unless the type declares a method — so the Deferred-Quantity resolution runs last and a slot that holds one sees the solver, the observation weights and the children already settled. Because this method is more specific than the general factory(x, args...), it is chosen whenever a prior is passed.
Untagged fields pass through unchanged in every method, regardless of type — tagging is explicit and opt-in. The tags are independent and the relevant field sets genuinely diverge. @pprop and @cprop are mutually exclusive on one field (a value comes from exactly one source); legal stacks are @pprop @fprop (sub-estimator) and @pprop @wprop (weights slot).
Two consequences of the emitted code are contracts on the declaration, and both are checked where the struct is written rather than at the first call:
- Every generated method rebuilds the struct as
StructName(; field = …)over all fields, tagged or not, so every field name must also be a keyword of the outer constructor. Akwargs...slurp does not satisfy this: it accepts the keyword and then discards it. - The prior method reads
getproperty(pr, :field), so every@ppropfield name must be a property of a prior result (seeprior_result_property_pool).
The macro registers each declaration in PROPAGATABLE_CONTRACTS, and check_propagatable_contracts checks the whole registry once the module is complete. A mistyped field name therefore fails at precompilation with a suggest_declared_key suggestion, rather than surfacing as a MethodError at the first factory call. forward_prior leans on the same contract.
The tag set itself is data. PROP_TAG_NAMES holds the rows, prop_tag_expr holds each tag's field transform, and PROP_TAG_CHANNELS holds each channel's gate and tag precedence, so a new propagation channel is a table row rather than an edit at seven sites.
Composes with @concrete (put @propagatable outermost):
@propagatable @concrete struct MyMeasure <: RiskMeasure @pprop @wprop w # prior factory selects pr.w; ObsWeights factory fills w @pprop sigma # prior-selected from pr.sigma @fprop alg # threaded (recursed) with pr / args config # passed through unchanged function MyMeasure(w, sigma, alg, config) return new{typeof(w), typeof(sigma), typeof(alg), typeof(config)}(w, sigma, alg, config) endend@wprop drives two channels at once, and they do different things to the same field: factory replaces it with an incoming ObsWeights value, while obs_weights_view indexes the value already there, to a set of observations. A weights field therefore needs no second tag to join the observation-axis view.
The generated factory/port_opt_view/obs_weights_view methods are added to the PortfolioOptimisers functions, so @propagatable works correctly for types defined in external packages.
Docstrings on the enclosing definition are forwarded correctly via Base.@__doc__.
Algorithm
- Find the struct with
propagatable_find_struct, which givesstruct_nodeandrebuild, the function that puts a replacement struct back inside the same chain of wrapping macros. - Read
type_headandbodyoffstruct_node, and readstruct_nameofftype_headwithpropagatable_bare_name, which drops the type parameters and the supertype. - Parse the body with
propagatable_parse_body, which givestagged, the field names per tag;all_fields, every declared field in declaration order; andnew_body, the body with every tag stripped. - Build
new_structfromnew_body, andchainfromrebuild(new_struct).chainis the original declaration with the tags gone, so@concreteand Julia both see an ordinary struct. - Bind
POMODto the module that defines the macro, and qualify every emitted name against it. A bare name would resolve in the caller's module, wherefunction factory(…)declares a new function of the caller's own and the method never reachesPortfolioOptimisers.factory. That failure is silent, because the declaration compiles and the type never joins the propagation chain. - Emit the
factorymethod. Whenprop_channel_activeholds for thefactorychannel, the body is a call to the keyword constructor whose pairs come fromprop_channel_pairs; otherwise the body isxitself. This method is always emitted, so an untagged@propagatablestruct still answersfactorywith the identity. - When the
viewchannel is active, emitport_opt_view(x::StructName, i, args...), whose channel threadsibeforeargs.... - When the
obschannel is active, emitobs_weights_view(x::StructName, i), whose channel threadsiand takes no tail. - When the
priorchannel is active, emitfactory(x::StructName, pr::AbstractPriorResult, args...; kwargs...). Its body selects every tagged field offxand then hands the selected struct toresolve_deferred_quantities, so the Deferred-Quantity resolution runs last. A Deferred Quantity and a Calibration Rule therefore see the solver, the observation weights and the children in the state the optimisation settled them in, and a rule may callERMorRRM. This method is more specific than the one of step 6, so a call that threads a prior chooses it. - Build
pprop_tuple, the@pprop-tagged field names as a tuple of quoted symbols. - Return one escaped block holding, in order:
Base.@__doc__ chain, so a docstring on the declaration reaches the struct; the emitted methods; and the call topropagatable_register!that records the type andpprop_tuple.
Steps 6 to 9 differ only in the method head and in the arguments that the channel threads. Each reads its gate and its tag precedence off PROP_TAG_CHANNELS, so a new channel is a row of that table, a branch in prop_tag_expr and a stub macro, rather than an edit at seven sites.
Related
PortfolioOptimisers.factory_child — Function
factory_child(v, args...; kwargs...) -> Any
Per-field recursion helper called by @propagatable-generated factory methods.
Dispatches on the field value type: estimators, algorithms, and results recurse via factory; observation-weight fields (::Nothing or ::StatsBase.AbstractWeights) are replaced by the incoming ObsWeights argument; everything else passes through unchanged.
Algorithm
The method that Julia selects is the algorithm.
vis an estimator, an algorithm or a result: returnfactoryofv, forwardingargs...andkwargs.... The recursion descends one level of the struct tree.vis an array of them: apply step 1 to each element, and collect the results into a new vector.vis anything else: returnvunchanged. A data field, a scalar and anothingall take this branch.
Step 3 is why a nothing field is not filled in by this verb. A weights field that must be replaced when it holds nothing carries @wprop and reaches _wprop instead.
Related
PortfolioOptimisers.@fprop — Macro
@fprop fieldField tag for use inside a @propagatable struct body. Marks the field as participating in factory propagation — factory_child will be called on it when factory is invoked on the enclosing struct.
Algorithm
The tag never expands. @propagatable runs first and consumes it, so the steps below are what happens to the tagged field, not what this macro does.
propagatable_parse_bodypeels the tag off the field, records the field name under:fprop, and puts the stripped field into the struct body. Neither Julia nor a wrapped macro such as@concreteever sees the tag.prop_channel_activereads the recorded name. The channels this tag gates are thefactorychannel and theobschannel, and each active channel makes@propagatableemit one method.prop_channel_pairsbuilds the keyword pair of the field for each emitted method, andprop_tag_exprgives the value:factorychannel:factory_child(x.field, args...; kwargs...), which recurses into the child.obschannel:obs_weights_view(x.field, i), which recurses into the child on the observation axis.
The generated method rebuilds the struct with its keyword constructor, so every validation the constructor carries runs again on the propagated value.
Step 3 is the whole meaning of the tag. The same tag has two transforms, because a channel decides what a tag means. The obs channel does not gate on @fprop, so a struct whose only tag is @fprop gains no obs_weights_view method; the tag is consulted there only when a sibling field carries @wprop.
This macro body itself raises an error. It is reached only when the tag is written outside a @propagatable struct body, where nothing consumed it.
Related
PortfolioOptimisers.@vprop — Macro
@vprop fieldField tag for use inside a @propagatable struct body. Marks the field as participating in port_opt_view propagation — port_opt_view will be called on it when a view (index selection) is propagated through the enclosing struct.
Orthogonal to @fprop; the two may be stacked on one field (@fprop @vprop field) when it participates in both factory and view propagation.
Algorithm
The tag never expands. @propagatable runs first and consumes it, so the steps below are what happens to the tagged field, not what this macro does.
propagatable_parse_bodypeels the tag off the field, records the field name under:vprop, and puts the stripped field into the struct body. Neither Julia nor a wrapped macro such as@concreteever sees the tag.prop_channel_activereads the recorded name. The channels this tag gates are theviewchannel alone, and each active channel makes@propagatableemit one method.prop_channel_pairsbuilds the keyword pair of the field for each emitted method, andprop_tag_exprgives the value:port_opt_view(x.field, i, args...). The channel forwards the threaded tail and no keywords.- The generated method rebuilds the struct with its keyword constructor, so every validation the constructor carries runs again on the propagated value.
Step 3 is the whole meaning of the tag. @vprop appears in one channel, so it carries one transform and no channel can give it a second meaning. The index that the method threads selects assets; the observation axis has its own verb, obs_weights_view.
This macro body itself raises an error. It is reached only when the tag is written outside a @propagatable struct body, where nothing consumed it.
Related
PortfolioOptimisers.@pprop — Macro
@pprop fieldField tag for use inside a @propagatable struct body. Marks the field as prior-selected: when factory(x, pr::AbstractPriorResult, …) is invoked, the field is set to sel(getfield(x, :field), getproperty(pr, :field)) — the risk-measure value if present, else the same-named moment from the prior result.
Orthogonal to, and stackable with, @wprop (@pprop @wprop w gives a weights field both a prior factory and an ObsWeights factory) or @fprop; @pprop wins in the prior method. Mutually exclusive with @cprop on a single field.
Algorithm
The tag never expands. @propagatable runs first and consumes it, so the steps below are what happens to the tagged field, not what this macro does.
propagatable_parse_bodypeels the tag off the field, records the field name under:pprop, and puts the stripped field into the struct body. Neither Julia nor a wrapped macro such as@concreteever sees the tag.prop_channel_activereads the recorded name. The channels this tag gates are thepriorchannel alone, and each active channel makes@propagatableemit one method.prop_channel_pairsbuilds the keyword pair of the field for each emitted method, andprop_tag_exprgives the value:sel(x.field, getproperty(pr, :field)). The prior result supplies the property of the same name as the field, so the tag names no source of its own.- The generated method rebuilds the struct with its keyword constructor, so every validation the constructor carries runs again on the propagated value.
Step 3 is the whole meaning of the tag. @pprop is first in the prior channel's precedence, so a field carrying both @pprop and @fprop takes the prior transform on that channel and the factory transform on the factory channel.
This macro body itself raises an error. It is reached only when the tag is written outside a @propagatable struct body, where nothing consumed it.
Related
PortfolioOptimisers.@wprop — Macro
@wprop fieldField tag for use inside a @propagatable struct body. Marks the field as an observation-weights slot: when factory(x, w::ObsWeights, …) is invoked, the field is replaced by the incoming weights via _wprop; when no ObsWeights is threaded, it is left unchanged.
Distinct from @fprop, which recurses into a sub-estimator value and leaves a nothing value untouched. A weights field defaults to nothing (meaning "uniform") and must become the incoming weights — so it cannot share @fprop's nothing-handling without the two semantics colliding. Use @wprop for the w/weights field and @fprop for sub-estimators.
Algorithm
The tag never expands. @propagatable runs first and consumes it, so the steps below are what happens to the tagged field, not what this macro does.
propagatable_parse_bodypeels the tag off the field, records the field name under:wprop, and puts the stripped field into the struct body. Neither Julia nor a wrapped macro such as@concreteever sees the tag.prop_channel_activereads the recorded name. The channels this tag gates are thefactorychannel and theobschannel, and each active channel makes@propagatableemit one method.prop_channel_pairsbuilds the keyword pair of the field for each emitted method, andprop_tag_exprgives the value:factorychannel:_wprop(x.field, args...; kwargs...), which replaces the field with an incomingObsWeightsand keeps it when none is threaded.obschannel:nothing_scalar_array_getindex(x.field, i), which indexes the value already there to the selected observations. Indexing rather than viewing is what keeps theAbstractWeightssubtype.
The generated method rebuilds the struct with its keyword constructor, so every validation the constructor carries runs again on the propagated value.
Step 3 is the whole meaning of the tag. The two channels do different things to the same field, which is the one place a reader learns that factory and obs_weights_view are not two names for one operation. @wprop is also the only tag that gates the obs channel, so a field opts a struct into obs_weights_view by carrying this tag and no second one.
This macro body itself raises an error. It is reached only when the tag is written outside a @propagatable struct body, where nothing consumed it.
Related
PortfolioOptimisers.@cprop — Macro
@cprop fieldField tag for use inside a @propagatable struct body. Marks the field as context-selected: when factory(x, pr::AbstractPriorResult, …) is invoked, the field is set to sel(getfield(x, :field), _ctx(args...)) — the risk-measure value if present, else the threaded optimiser value (a solver) located by type in the variadic tail. Used for slv fields, whose source is a threaded argument rather than the prior. Mutually exclusive with @pprop on a single field.
Algorithm
The tag never expands. @propagatable runs first and consumes it, so the steps below are what happens to the tagged field, not what this macro does.
propagatable_parse_bodypeels the tag off the field, records the field name under:cprop, and puts the stripped field into the struct body. Neither Julia nor a wrapped macro such as@concreteever sees the tag.prop_channel_activereads the recorded name. The channels this tag gates are thepriorchannel alone, and each active channel makes@propagatableemit one method.prop_channel_pairsbuilds the keyword pair of the field for each emitted method, andprop_tag_exprgives the value:sel(x.field, _ctx(args...))._ctxfinds the value by type in the threaded tail, so the source is an argument and not the prior result.- The generated method rebuilds the struct with its keyword constructor, so every validation the constructor carries runs again on the propagated value.
Step 3 is the whole meaning of the tag. @cprop follows @pprop in the prior channel's precedence, and the two are mutually exclusive on one field.
This macro body itself raises an error. It is reached only when the tag is written outside a @propagatable struct body, where nothing consumed it.
Related
PortfolioOptimisers.@forward_properties — Macro
@forward_properties T begin
forward(loc)
forward(loc, names...)
alias(exposed, loc)
compute(exposed, loc; broadcast)
compute(exposed, fn)
swap(field, loc)
swap(field, fn)
endGenerate the Base.getproperty / Base.propertynames pair for type T from a block of declarative forwarding rules, so the property-forwarding decision lives in one declared surface instead of a hand-written getproperty body. T may be a bare type name or a parametric/UnionAll signature (Foo{<:Any, Nothing, <:Any}), so a swap can be specialised per type parameter.
All names are written as bare identifiers. Every rule names its source via a locator — a bare name a (the field a of the receiver) or a dotted path a.b.c (the receiver-rooted path obj.a.b.c, any depth). Nesting is simply more dots; a depth-≥2 path guards each intermediate and throws a PropertyPathError naming the path when a node is nothing.
forward/alias/compute only add new virtual names and so resolve after the receiver's own fields; swap replaces the value of an existing field and so resolves before the field check.
Rules
forward(loc): forward all properties of the value atloc(sym in propertynames(value)?getproperty(value, sym)).forward(loc, names...): forward only the named subset from the value atloc.alias(exposed, loc): exposeexposedas the value atloc(renaming).compute(exposed, loc; broadcast): exposeexposedvia a dotted locator (depth ≥ 2);broadcastmaps the final hop over a vector penultimate value.compute(exposed, fn): exposeexposedasfn(obj);fnmust be an anonymous function (a lambda), which would otherwise be ambiguous with a dotted path.swap(field, loc)/swap(field, fn): override an existing field's value with the value atloc(bare name, e.g.swap(L, M), or dotted path) or withfn(obj). Unlike the others it takes precedence over the own-field check, and is the only rule that may name a real field. Typically specialised on a parametricT(swap(L, M)onRegression{<:Any, Nothing, <:Any}). The locator form reads throughgetfieldand is recursion-safe; in the function form the body must read the swapped field viagetfield(obj, :field), neverobj.field, since dot-access on the swapped field re-entersgetpropertyand recurses (StackOverflowError). Other fields may use dot-access freely.
Algorithm
blockis not abegin … endblock: raise an error.- Make three empty vectors:
swap_branches,getprop_branchesandpropname_contribs. - For each rule of the block, skipping a
LineNumberNode:- The rule is not a call: raise an error naming it.
- Read
marker, the rule name, andargs, its arguments. When the first argument is a:parametersnode, read thebroadcastoption out of it and drop it fromargs. Any other option raises an error. markerisforward: flatten the locator withforward_flatten_pathand buildwalkwithforward_walk_expr. With no further argument, push a branch that returnsgetproperty(walk, sym)whensymis inpropertynames(walk), and contribute every one of those names. With further arguments, check that each is a bare identifier, push a branch that matchessymagainst that name set, and contribute the named subset.markerisalias: check the exposed name, buildwalkfrom the locator, push a branch that matches the exposed name and returnswalk, and contribute the name.markeriscompute: check the exposed name. An anonymous-function source pushes a branch returningfn(x), andbroadcastwith that form raises an error. A dotted source buildswalkwith thebroadcastflag and pushes the matching branch. Any other source raises an error. Contribute the exposed name.markerisswap: as forcompute, but a bare name is also a legal source, and the branch is pushed ontoswap_branchesrather thangetprop_branches.markeris anything else: raise an error naming it.
- Build
Base.getproperty(x::T, sym::Symbol)in this order: theswapbranches; the own-field check, which returnsgetfield(x, sym); the remaining branches in declaration order; andgetfield(x, sym)as the fallthrough, which raises the standard error for an absent field. - Build
Base.propertynames(x::T)fromfieldnames(T)followed by every contributed name, and return the unique names as a tuple. - Return both definitions in one escaped block.
Step 4 is where the two orderings in the first paragraph come from: a swap runs before the own-field check, so it replaces a real field, and every other rule runs after it, so it can only add a name. Within each group the first branch that matches wins, and the order of the branches is the declaration order of the rules.
Related
View functions
NestedClustered optimisations need to index the asset universe in order to produce the inner optimisations. These indexing operations are implemented as views, indexing, and custom index generators.
PortfolioOptimisers.port_opt_view — Method
port_opt_view(x, i, args...; kwargs...) -> nothing_scalar_array_view(x, i)Sub-select an estimator, result, or algorithm to the asset/observation index i.
port_opt_view is the index-selection counterpart of factory: where factory threads runtime values down a composed struct tree, port_opt_view threads an index selection — restricting every data-bearing field and composed child to the subset i. It is the mechanism that makes meta-optimisers (NestedClustered, SubsetResampling) and cross-validation variants operate on subproblems with identical struct shapes.
Callers do not normally call port_opt_view directly; it is driven by meta-optimisers and cross-validation internals. It is public (not exported) because extension authors who implement a new composed estimator may need to define a method. Use @vprop on data-bearing fields to have the method generated automatically.
This universal fallback handles leaf values: arrays are sliced via nothing_scalar_array_view; scalars, nothing, estimators without data fields, and algorithms pass through unchanged. Composed structs that recurse into children define their own (more specific) method — emitted by @vprop or hand-written.
The threaded tail args... (typically the returns matrix X for the JuMP families) and any kwargs are accepted and dropped here, so a macro-threaded port_opt_view(child, i, X) never MethodErrors on a leaf field.
Algorithm
- Drop
args...andkwargs.... This method is the leaf of the recursion, so it threads nothing further. - Return
nothing_scalar_array_viewofxati, whose own algorithm names the rule for each leaf type.
Related
PortfolioOptimisers.port_opt_view — Method
port_opt_view(x::VecScalar, i, args...) -> nothing_scalar_array_view(x, i)First-class port_opt_view method for VecScalar: slices the vector component and preserves the scalar component, delegating to nothing_scalar_array_view.
Algorithm
- Drop
args.... - Return
nothing_scalar_array_viewofxati, whoseVecScalarmethod viewsx.vatiand carriesx.sthrough.
This method exists so that a VecScalar reaching the verb with a threaded tail resolves here rather than through the universal leaf method. Both routes give the same value.
Related
PortfolioOptimisers.port_opt_view — Method
port_opt_view(x::AbstractVector{<:Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm, <:AbstractResult}}, i, args...; kwargs...) -> VectorGeneric vector method for port_opt_view: view each element of x at the index selection i.
This is the index-selection twin of the vector factory method, and it is the one forwarding contract for every vector-valued propagation field. The tail args... (typically the returns matrix X) and every keyword reach each element unchanged, so a family that admits a vector of estimators, algorithms, or results needs no method of its own.
Without it such a vector falls through to the universal leaf method port_opt_view(x, i, args...), which slices the vector itself through nothing_scalar_array_view — the asset index would select elements instead of assets. A family that needs more than the forward, such as a concrete element type (concrete_typed_array_if_abstract) or a passthrough, defines its own more specific method.
Algorithm
- For each element
xiofx, callport_opt_viewonxiati, and forwardargs...andkwargs...unchanged. - Collect the results into a new vector, in the order of
x, and return it.
The length of x is unchanged, because i reaches the elements and never the outer vector. The result is a comprehension, so its element type is whatever Julia infers; a family that needs a concrete element type wraps the call in concrete_typed_array_if_abstract.
Arguments
x: Vector of estimators, algorithms, results, ornothing.i: Index selection.args...: Threaded tail, forwarded to each element.kwargs...: Keyword arguments, forwarded to each element.
Returns
v::Vector: The element-wise views.
Related
PortfolioOptimisers.obs_weights_view — Method
obs_weights_view(x, i) -> typeof(x)Sub-select an estimator's observation weights to the observations i.
obs_weights_view is the observation-axis counterpart of port_opt_view, and it is generated by @propagatable from the tags a struct already carries: @wprop marks the field that holds the weights, so that field is indexed, and @fprop marks a composed child, so the verb recurses into it. Every other field is carried through unchanged, and the struct's type does not change.
Why the observation axis needs its own verb
port_opt_view threads one index into every @vprop-tagged field, and at its call sites — the meta-optimisers and the cross-validation splitters — that index selects assets. An observation weight is one value per row of the sample, so slicing it there would be wrong. The two axes are told apart by which verb is called, not by the index.
factory reads the same @wprop tag on the same field, and does a different thing with it: it replaces the field with an incoming ObsWeights value, at every level of the tree at once. That is why a slice cannot go through factory. A SimpleVariance holding a weighted mean and an unweighted dispersion comes back from factory with both weighted, which is a different estimator; here each field is indexed on its own, so a field that held nothing still holds nothing.
Algorithm
- Return
xunchanged. This universal fallback reads neither its index nor the fields ofx. An estimator that carries no weights, and one whose struct is not@propagatable, therefore behave as they did before the verb existed.
A @propagatable struct with at least one @wprop-tagged field carries a generated method that dominates this one. That method rebuilds the struct with the same constructor, indexing each @wprop field to i through nothing_scalar_array_getindex and recursing into each @fprop field through this verb. A hand-written type that holds weights outside that tag must define its own method, or its weights keep their full-sample length and the windowed call raises.
Arguments
x: Estimator, algorithm, result, weights vector, ornothing. The argument is untyped, because the variance estimators subtypeStatsBase.CovarianceEstimatorwhile the expected returns estimators subtypeAbstractEstimator, and both reach this verb.i: Index or indices of the observations to keep.
Returns
x: The value, with every observation-weights field indexed toi.
Related
port_opt_viewfactory@wpropPROP_TAG_CHANNELSnothing_scalar_array_getindexObsWeightsrealised_vol: the site that drives this verb.
PortfolioOptimisers.obs_weights_view — Method
obs_weights_view(
x::AbstractVector{<:Union{Nothing, var"#s53", var"#s52", var"#s51"} where {var"#s53"<:AbstractEstimator, var"#s52"<:AbstractAlgorithm, var"#s51"<:AbstractResult}},
i
) -> Any
Vector overload of obs_weights_view. Applies the verb to every element, so an @fprop-tagged field holding a vector of composed children is not silently skipped.
Algorithm
- For each element
xiofx, callobs_weights_viewonxiati. - Collect the results into a new vector, in the order of
x, and return it.
The length of x is unchanged, because i selects observations inside each element and never elements of x. Without this method such a vector reaches the universal fallback and comes back with full-sample weights.
Related
Summary statistics
Some estimators and constraints are based on summary statistics of vectors. These types are used to dispatch the appropriate functions and encapsulate auxiliary data such as weights.
PortfolioOptimisers.VectorToScalarMeasure — Type
abstract type VectorToScalarMeasure <: AbstractAlgorithmAbstract supertype for algorithms mapping a vector of real values to a single real value.
VectorToScalarMeasure provides a unified interface for algorithms that reduce a vector of real numbers to a scalar, such as minimum, mean, median, or maximum. These are used in constraint generation and centrality-based portfolio constraints to aggregate asset-level metrics.
Interfaces
In order to implement a new vector-to-scalar measure that works seamlessly with the library, subtype VectorToScalarMeasure and implement the following method:
Reduction method
vec_to_real_measure(measure::VectorToScalarMeasure, val::VecNum) -> Number: Reducesvalto a single scalar.
Arguments
measure: Concrete subtype instance.val: Vector of real values to reduce.
Returns
score::Number: Computed scalar.
Related
PortfolioOptimisers.MinValue — Type
struct MinValue <: VectorToScalarMeasureAlgorithm for reducing a vector of real values to its minimum.
Mathematical definition
\[\begin{align} \mathrm{MinValue}(\boldsymbol{v}) &= \underset{i}{\min}\ v_{i}\,. \end{align}\]
Where:
- $\boldsymbol{v}$: The vector to reduce, of length $n$.
- $v_{i}$: Its $i$-th entry, $i = 1,\ldots,n$.
The reduction carries no weights, so a weighted call gives the same value as an unweighted one.
Constructors
MinValue() -> MinValueExamples
julia> PortfolioOptimisers.vec_to_real_measure(MinValue(), [1.2, 3.4, 0.7])0.7Related
PortfolioOptimisers.MeanValue — Type
struct MeanValue{__T_w} <: VectorToScalarMeasureAlgorithm for reducing a vector of real values to its optionally weighted mean.
Mathematical definition
\[\begin{align} \mathrm{MeanValue}(\boldsymbol{v}) &= \frac{1}{n} \sum_{i=1}^{n} v_{i}\,, &&w = \mathrm{nothing}\,, \\ \mathrm{MeanValue}(\boldsymbol{v}) &= \frac{\sum_{i=1}^{n} w_{i} v_{i}}{\sum_{i=1}^{n} w_{i}}\,, &&\mathrm{otherwise}\,. \end{align}\]
Where:
- $\boldsymbol{v}$: The vector to reduce, of length $n$.
- $v_{i}$: Its $i$-th entry, $i = 1,\ldots,n$.
- $w_{i}$: The $i$-th observation weight, from the field
w.
The weighted form normalises by the total weight, so a weight vector scaled by a positive constant gives the same value. w must carry one entry per entry of $\boldsymbol{v}$.
Fields
w: Optional observation weights vectorobservations × 1, or a concrete subtype ofDynamicAbstractWeights. Ifnothing, the computation is unweighted.
Constructors
MeanValue(; w::Option{<:ObsWeights} = nothing,) -> MeanValueKeywords correspond to the struct's fields.
Validation
- If
wis notnothing,!isempty(w).
Propagated parameters
When factory is called on this type, the following @wprop-tagged field is automatically propagated:
w: Replaced with the incomingObsWeights.
Observation weight parameters
When obs_weights_view is called on this type, the following fields are automatically indexed to the selected observations:
w: Indexed to the selected observations viaobs_weights_view.
Examples
julia> PortfolioOptimisers.vec_to_real_measure(MeanValue(), [1.2, 3.4, 0.7])1.7666666666666666Related
PortfolioOptimisers.MedianValue — Type
struct MedianValue{__T_w} <: VectorToScalarMeasureAlgorithm for reducing a vector of real values to its optionally weighted median.
Mathematical definition
\[\begin{align} \mathrm{MedianValue}(\boldsymbol{v}) &= Q_{\boldsymbol{v}}(0.5)\,, \\ \mathrm{MedianValue}(\boldsymbol{v}) &= Q_{\boldsymbol{v}, \boldsymbol{w}}(0.5)\,. \end{align}\]
Where:
- $\boldsymbol{v}$: The vector to reduce, of length $n$.
- $\boldsymbol{w}$: The observation weights, from the field
w. The first line is the casew = nothing. - $Q_{\boldsymbol{v}}(p)$: The $p$-quantile of $\boldsymbol{v}$.
- $Q_{\boldsymbol{v}, \boldsymbol{w}}(p)$: The weighted $p$-quantile of $\boldsymbol{v}$, as
StatsBasedefines it.
Both forms are quantiles, and both interpolate. Neither is an order statistic, so the result need not be an entry of $\boldsymbol{v}$. On a vector of even length the unweighted form averages the two middle entries, and the weighted form interpolates between the two entries that bracket half the weight mass.
Fields
w: Optional observation weights vectorobservations × 1, or a concrete subtype ofDynamicAbstractWeights. Ifnothing, the computation is unweighted.
Constructors
MedianValue(; w::Option{<:ObsWeights} = nothing,) -> MedianValueKeywords correspond to the struct's fields.
Validation
- If
wis notnothing,!isempty(w).
Propagated parameters
When factory is called on this type, the following @wprop-tagged field is automatically propagated:
w: Replaced with the incomingObsWeights.
Observation weight parameters
When obs_weights_view is called on this type, the following fields are automatically indexed to the selected observations:
w: Indexed to the selected observations viaobs_weights_view.
Examples
julia> PortfolioOptimisers.vec_to_real_measure(MedianValue(), [1.2, 3.4, 0.7])1.2Related
PortfolioOptimisers.MaxValue — Type
struct MaxValue <: VectorToScalarMeasureAlgorithm for reducing a vector of real values to its maximum.
Mathematical definition
\[\begin{align} \mathrm{MaxValue}(\boldsymbol{v}) &= \underset{i}{\max}\ v_{i}\,. \end{align}\]
Where:
- $\boldsymbol{v}$: The vector to reduce, of length $n$.
- $v_{i}$: Its $i$-th entry, $i = 1,\ldots,n$.
The reduction carries no weights, so a weighted call gives the same value as an unweighted one.
Constructors
MaxValue() -> MaxValueExamples
julia> PortfolioOptimisers.vec_to_real_measure(MaxValue(), [1.2, 3.4, 0.7])3.4Related
PortfolioOptimisers.StdValue — Type
struct StdValue{__T_w, __T_corrected} <: VectorToScalarMeasureAlgorithm for reducing a vector of real values to its optionally weighted standard deviation. The unweighted default is safe and the weighted default is not: corrected = true under a plain StatsBase.Weights raises an ArgumentError, because that type declares no bias correction. Pass an AnalyticWeights, a FrequencyWeights or a ProbabilityWeights, or set corrected = false.
Mathematical definition
\[\begin{align} \mathrm{StdValue}(\boldsymbol{v}) &= \sqrt{\mathrm{VarValue}(\boldsymbol{v})}\,. \end{align}\]
Where:
- $\boldsymbol{v}$: The vector to reduce, of length $n$.
- $\mathrm{VarValue}(\boldsymbol{v})$: The variance under the same
wand the samecorrected, whose four denominatorsVarValuestates.
corrected selects the denominator of the variance, and the square root carries that choice through. The unweighted default corrected = true divides by $n - 1$.
Fields
w: Optional observation weights vectorobservations × 1, or a concrete subtype ofDynamicAbstractWeights. Ifnothing, the computation is unweighted.
corrected: Whether to apply Bessel's correction.
Constructors
StdValue(; w::Option{<:ObsWeights} = nothing, corrected::Bool = true,) -> StdValueKeywords correspond to the struct's fields.
Validation
- If
wis notnothing,!isempty(w).
Propagated parameters
When factory is called on this type, the following @wprop-tagged field is automatically propagated:
w: Replaced with the incomingObsWeights.
Observation weight parameters
When obs_weights_view is called on this type, the following fields are automatically indexed to the selected observations:
w: Indexed to the selected observations viaobs_weights_view.
Examples
julia> PortfolioOptimisers.vec_to_real_measure(StdValue(), [1.2, 3.4, 0.7])1.4364307617610164Related
VectorToScalarMeasureMeanValueVarValue: the four denominators thatcorrectedand the type ofwselect.StandardisedValue: reachesStatistics.stdwith ameankeyword, which is how it makes the deviation be taken about the mean that itsmvproduced.vec_to_real_measurefactoryobs_weights_view
PortfolioOptimisers.VarValue — Type
struct VarValue{__T_w, __T_corrected} <: VectorToScalarMeasureAlgorithm for reducing a vector of real values to its optionally weighted variance. The weighted default raises: a plain StatsBase.Weights declares no bias correction, so corrected = true under it raises an ArgumentError rather than returning a value. Pass one of the three corrected weight types below, or set corrected = false.
Mathematical definition
\[\begin{align} \mathrm{VarValue}(\boldsymbol{v}) &= \frac{1}{d} \sum_{i=1}^{n} w_{i} \left(v_{i} - \bar{v}\right)^{2}\,, \\ \bar{v} &= \frac{\sum_{i=1}^{n} w_{i} v_{i}}{\sum_{i=1}^{n} w_{i}}\,. \end{align}\]
Where:
- $\boldsymbol{v}$: The vector to reduce, of length $n$.
- $w_{i}$: The $i$-th observation weight. The unweighted case is $w_{i} = 1$.
- $\bar{v}$: The mean of $\boldsymbol{v}$ under those weights.
- $d$: The denominator, which
correctedand the type ofwtogether select.
$d$ takes one of four values:
w = nothing: $d = n - 1$ whencorrectedistrue, and $d = n$ when it isfalse.w::AnalyticWeights: $d = \sum w_{i} - \sum w_{i}^{2} / \sum w_{i}$ whencorrectedistrue.w::FrequencyWeights: $d = \sum w_{i} - 1$ whencorrectedistrue.w::ProbabilityWeights: $d = \left(\sum w_{i}\right)(m - 1) / m$ whencorrectedistrue, where $m$ is the count of non-zero weights.
With corrected = false every weighted case takes $d = \sum w_{i}$.
$d$ is selected by the type of $\boldsymbol{w}$ and not by its values, so two numerically identical weight vectors of different types give different variances.
Fields
w: Optional observation weights vectorobservations × 1, or a concrete subtype ofDynamicAbstractWeights. Ifnothing, the computation is unweighted.
corrected: Whether to apply Bessel's correction.
Constructors
VarValue(; w::Option{<:ObsWeights} = nothing, corrected::Bool = true,) -> VarValueKeywords correspond to the struct's fields.
Validation
- If
wis notnothing,!isempty(w).
Propagated parameters
When factory is called on this type, the following @wprop-tagged field is automatically propagated:
w: Replaced with the incomingObsWeights.
Observation weight parameters
When obs_weights_view is called on this type, the following fields are automatically indexed to the selected observations:
w: Indexed to the selected observations viaobs_weights_view.
Examples
julia> PortfolioOptimisers.vec_to_real_measure(VarValue(), [1.2, 3.4, 0.7])2.0633333333333335Related
PortfolioOptimisers.SumValue — Type
struct SumValue <: VectorToScalarMeasureAlgorithm for reducing a vector of real values to its sum.
Mathematical definition
\[\begin{align} \mathrm{SumValue}(\boldsymbol{v}) &= \sum_{i=1}^{n} v_{i}\,. \end{align}\]
Where:
- $\boldsymbol{v}$: The vector to reduce, of length $n$.
- $v_{i}$: Its $i$-th entry, $i = 1,\ldots,n$.
The reduction carries no weights. MeanValue is the weighted sum normalised by the total weight, so a weighted sum is that value multiplied by the total weight.
Constructors
SumValue() -> SumValueExamples
julia> PortfolioOptimisers.vec_to_real_measure(SumValue(), [1.2, 3.4, 0.7])5.3Related
PortfolioOptimisers.ProdValue — Type
struct ProdValue <: VectorToScalarMeasureAlgorithm for reducing a vector of real values to its product.
Mathematical definition
\[\begin{align} \mathrm{ProdValue}(\boldsymbol{v}) &= \prod_{i=1}^{n} v_{i}\,. \end{align}\]
Where:
- $\boldsymbol{v}$: The vector to reduce, of length $n$.
- $v_{i}$: Its $i$-th entry, $i = 1,\ldots,n$.
The reduction carries no weights. One zero entry gives zero, and the product of many entries below one underflows, so this reduction is for a short vector of values near one.
Constructors
ProdValue() -> ProdValueExamples
julia> PortfolioOptimisers.vec_to_real_measure(ProdValue(), [1.2, 3.4, 0.7])2.856Related
PortfolioOptimisers.ModeValue — Type
struct ModeValue <: VectorToScalarMeasureAlgorithm for reducing a vector of real values to its mode.
Mathematical definition
\[\begin{align} \mathrm{ModeValue}(\boldsymbol{v}) &= \underset{u \in \boldsymbol{v}}{\arg\max}\ \left| \left\{ i : v_{i} = u \right\} \right|\,. \end{align}\]
Where:
- $\boldsymbol{v}$: The vector to reduce, of length $n$.
- $u$: A value that $\boldsymbol{v}$ carries.
- $\left| \cdot \right|$: The count of a set.
StatsBase.mode breaks a tie by the first value that reaches the highest count, so the result is a value of the input and never an average of two. The comparison is exact equality, so this reduction is for a vector of repeated exact values and not for a continuous one.
Constructors
ModeValue() -> ModeValueExamples
julia> PortfolioOptimisers.vec_to_real_measure(ModeValue(), [1.2, 3.4, 0.7, 1.2])1.2Related
PortfolioOptimisers.StandardisedValue — Type
struct StandardisedValue{__T_mv, __T_sv} <: VectorToScalarMeasureAlgorithm for reducing a vector of real values to its optionally weighted mean divided by its optionally weighted standard deviation. A weighted factory call can make the reduction raise: factory replaces the w field of both mv and sv with the incoming ObsWeights, and sv keeps its default corrected = true, which raises an ArgumentError under a plain StatsBase.Weights. Thread an AnalyticWeights, a FrequencyWeights or a ProbabilityWeights, or declare sv = StdValue(; corrected = false).
Mathematical definition
\[\begin{align} z &= \frac{\hat{\mu}}{\tilde{\sigma}}\,, \\ \tilde{\sigma} &= \begin{cases} 1 & \hat{\sigma} \ \mathrm{undefined} \\ \sqrt{\varepsilon} & \hat{\sigma} = 0 \\ \hat{\sigma} & \mathrm{otherwise} \end{cases}\,. \end{align}\]
Where:
- $z$: Standardised value.
- $\hat{\mu}$: The value computed by
mv. - $\hat{\sigma}$: The value computed by
sv, taken about $\hat{\mu}$. - $\tilde{\sigma}$: The guarded denominator.
- $\varepsilon$: Machine epsilon of the element type of $\hat{\sigma}$.
$\hat{\sigma}$ is undefined on a vector of one entry, because a corrected standard deviation needs two. The first case then gives $\tilde{\sigma} = 1$ and $z = \hat{\mu}$, so the reduction is defined on every non-empty vector.
Algorithm
- Reduce
valwithmv, givingm. - Reduce
valwithsv, and passmas themeankeyword, givings. The deviation is therefore always taken about the mean that step 1 produced, so weightingmvwithout weightingsvchanges the denominator too. - Guard
s:sisNaN: replace it withone(s).sis an exact zero: replace it withsqrt(eps(eltype(s))). The test is an equality, so a smallsis not guarded: on the constant vector[2.0, 2.0, 2.0]the result is1.342e8, which is2 / sqrt(eps(Float64)).- Otherwise: keep
s.
- Return
m / s.
Fields
mv: The mean value measure used for the numerator.
sv: The standard deviation measure used for the denominator.
Constructors
StandardisedValue(; mv::MeanValue = MeanValue(), sv::StdValue = StdValue(),) -> StandardisedValueKeywords correspond to the struct's fields.
Propagated parameters
When factory is called on this type, the following @fprop-tagged fields are automatically propagated:
Examples
julia> PortfolioOptimisers.vec_to_real_measure(StandardisedValue(), [1.2, 3.4, 0.7])1.2299003291330186julia> PortfolioOptimisers.vec_to_real_measure(StandardisedValue(), [0.37])0.37Related
PortfolioOptimisers.factory — Method
factory(a::Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
<:AbstractResult}, args...; kwargs...) -> a
factory(a::AbstractVector{<:Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
<:AbstractResult}}, args...; kwargs...) -> VectorNo-op factory function for constructing objects with a uniform interface.
Defining methods which dispatch on the first argument allows for a consistent factory interface across different types.
factory and port_opt_view are the two propagation mechanisms in this library. They are duals: factory threads runtime values (prior moments, observation weights, previous portfolio weights) down through a composed struct tree; port_opt_view threads an index selection (a subset of assets or observations) down through the same tree.
The vector method is the one forwarding contract for every vector-valued propagation field: it applies factory to each element and forwards args... and kwargs... unchanged, so a family that admits a vector of estimators, algorithms, or results needs no method of its own. A family that needs more than the forward, such as a concrete element type (concrete_typed_array_if_abstract), defines its own more specific method.
Algorithm
The scalar method:
- Return
aunchanged, and dropargs...andkwargs.... This method is the leaf of the recursion, and it is what makes an untagged type safe to call the verb on.
The vector method:
- For each element
aiofa, callfactoryonai, and forwardargs...andkwargs...unchanged. - Collect the results into a new vector, in the order of
a, and return it.
A @propagatable struct with at least one @fprop- or @wprop-tagged field carries a generated method that dominates the scalar method. That method rebuilds the struct with its keyword constructor, sending each @fprop field through factory_child and each @wprop field through _wprop.
Arguments
a: Indicates no object should be constructed, or a vector whose elements are rebuilt one by one.args...: Arbitrary positional arguments (ignored by the scalar method, forwarded by the vector method).kwargs...: Arbitrary keyword arguments (ignored by the scalar method, forwarded by the vector method).
Returns
a: The input unchanged.v::Vector: The element-wise rebuilds, for the vector method.
Examples
julia> factory(nothing, 1, 2; x = 3)julia> factory(MeanValue())MeanValue w ┴ nothingRelated
PortfolioOptimisers.factory — Method
factory(a::Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
<:AbstractResult}, args...; kwargs...) -> a
factory(a::AbstractVector{<:Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
<:AbstractResult}}, args...; kwargs...) -> VectorNo-op factory function for constructing objects with a uniform interface.
Defining methods which dispatch on the first argument allows for a consistent factory interface across different types.
factory and port_opt_view are the two propagation mechanisms in this library. They are duals: factory threads runtime values (prior moments, observation weights, previous portfolio weights) down through a composed struct tree; port_opt_view threads an index selection (a subset of assets or observations) down through the same tree.
The vector method is the one forwarding contract for every vector-valued propagation field: it applies factory to each element and forwards args... and kwargs... unchanged, so a family that admits a vector of estimators, algorithms, or results needs no method of its own. A family that needs more than the forward, such as a concrete element type (concrete_typed_array_if_abstract), defines its own more specific method.
Algorithm
The scalar method:
- Return
aunchanged, and dropargs...andkwargs.... This method is the leaf of the recursion, and it is what makes an untagged type safe to call the verb on.
The vector method:
- For each element
aiofa, callfactoryonai, and forwardargs...andkwargs...unchanged. - Collect the results into a new vector, in the order of
a, and return it.
A @propagatable struct with at least one @fprop- or @wprop-tagged field carries a generated method that dominates the scalar method. That method rebuilds the struct with its keyword constructor, sending each @fprop field through factory_child and each @wprop field through _wprop.
Arguments
a: Indicates no object should be constructed, or a vector whose elements are rebuilt one by one.args...: Arbitrary positional arguments (ignored by the scalar method, forwarded by the vector method).kwargs...: Arbitrary keyword arguments (ignored by the scalar method, forwarded by the vector method).
Returns
a: The input unchanged.v::Vector: The element-wise rebuilds, for the vector method.
Examples
julia> factory(nothing, 1, 2; x = 3)julia> factory(MeanValue())MeanValue w ┴ nothingRelated
PortfolioOptimisers.factory — Method
factory(a::Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
<:AbstractResult}, args...; kwargs...) -> a
factory(a::AbstractVector{<:Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
<:AbstractResult}}, args...; kwargs...) -> VectorNo-op factory function for constructing objects with a uniform interface.
Defining methods which dispatch on the first argument allows for a consistent factory interface across different types.
factory and port_opt_view are the two propagation mechanisms in this library. They are duals: factory threads runtime values (prior moments, observation weights, previous portfolio weights) down through a composed struct tree; port_opt_view threads an index selection (a subset of assets or observations) down through the same tree.
The vector method is the one forwarding contract for every vector-valued propagation field: it applies factory to each element and forwards args... and kwargs... unchanged, so a family that admits a vector of estimators, algorithms, or results needs no method of its own. A family that needs more than the forward, such as a concrete element type (concrete_typed_array_if_abstract), defines its own more specific method.
Algorithm
The scalar method:
- Return
aunchanged, and dropargs...andkwargs.... This method is the leaf of the recursion, and it is what makes an untagged type safe to call the verb on.
The vector method:
- For each element
aiofa, callfactoryonai, and forwardargs...andkwargs...unchanged. - Collect the results into a new vector, in the order of
a, and return it.
A @propagatable struct with at least one @fprop- or @wprop-tagged field carries a generated method that dominates the scalar method. That method rebuilds the struct with its keyword constructor, sending each @fprop field through factory_child and each @wprop field through _wprop.
Arguments
a: Indicates no object should be constructed, or a vector whose elements are rebuilt one by one.args...: Arbitrary positional arguments (ignored by the scalar method, forwarded by the vector method).kwargs...: Arbitrary keyword arguments (ignored by the scalar method, forwarded by the vector method).
Returns
a: The input unchanged.v::Vector: The element-wise rebuilds, for the vector method.
Examples
julia> factory(nothing, 1, 2; x = 3)julia> factory(MeanValue())MeanValue w ┴ nothingRelated
PortfolioOptimisers.factory — Method
factory(a::Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
<:AbstractResult}, args...; kwargs...) -> a
factory(a::AbstractVector{<:Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
<:AbstractResult}}, args...; kwargs...) -> VectorNo-op factory function for constructing objects with a uniform interface.
Defining methods which dispatch on the first argument allows for a consistent factory interface across different types.
factory and port_opt_view are the two propagation mechanisms in this library. They are duals: factory threads runtime values (prior moments, observation weights, previous portfolio weights) down through a composed struct tree; port_opt_view threads an index selection (a subset of assets or observations) down through the same tree.
The vector method is the one forwarding contract for every vector-valued propagation field: it applies factory to each element and forwards args... and kwargs... unchanged, so a family that admits a vector of estimators, algorithms, or results needs no method of its own. A family that needs more than the forward, such as a concrete element type (concrete_typed_array_if_abstract), defines its own more specific method.
Algorithm
The scalar method:
- Return
aunchanged, and dropargs...andkwargs.... This method is the leaf of the recursion, and it is what makes an untagged type safe to call the verb on.
The vector method:
- For each element
aiofa, callfactoryonai, and forwardargs...andkwargs...unchanged. - Collect the results into a new vector, in the order of
a, and return it.
A @propagatable struct with at least one @fprop- or @wprop-tagged field carries a generated method that dominates the scalar method. That method rebuilds the struct with its keyword constructor, sending each @fprop field through factory_child and each @wprop field through _wprop.
Arguments
a: Indicates no object should be constructed, or a vector whose elements are rebuilt one by one.args...: Arbitrary positional arguments (ignored by the scalar method, forwarded by the vector method).kwargs...: Arbitrary keyword arguments (ignored by the scalar method, forwarded by the vector method).
Returns
a: The input unchanged.v::Vector: The element-wise rebuilds, for the vector method.
Examples
julia> factory(nothing, 1, 2; x = 3)julia> factory(MeanValue())MeanValue w ┴ nothingRelated
PortfolioOptimisers.factory — Method
factory(a::Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
<:AbstractResult}, args...; kwargs...) -> a
factory(a::AbstractVector{<:Union{Nothing, <:AbstractEstimator, <:AbstractAlgorithm,
<:AbstractResult}}, args...; kwargs...) -> VectorNo-op factory function for constructing objects with a uniform interface.
Defining methods which dispatch on the first argument allows for a consistent factory interface across different types.
factory and port_opt_view are the two propagation mechanisms in this library. They are duals: factory threads runtime values (prior moments, observation weights, previous portfolio weights) down through a composed struct tree; port_opt_view threads an index selection (a subset of assets or observations) down through the same tree.
The vector method is the one forwarding contract for every vector-valued propagation field: it applies factory to each element and forwards args... and kwargs... unchanged, so a family that admits a vector of estimators, algorithms, or results needs no method of its own. A family that needs more than the forward, such as a concrete element type (concrete_typed_array_if_abstract), defines its own more specific method.
Algorithm
The scalar method:
- Return
aunchanged, and dropargs...andkwargs.... This method is the leaf of the recursion, and it is what makes an untagged type safe to call the verb on.
The vector method:
- For each element
aiofa, callfactoryonai, and forwardargs...andkwargs...unchanged. - Collect the results into a new vector, in the order of
a, and return it.
A @propagatable struct with at least one @fprop- or @wprop-tagged field carries a generated method that dominates the scalar method. That method rebuilds the struct with its keyword constructor, sending each @fprop field through factory_child and each @wprop field through _wprop.
Arguments
a: Indicates no object should be constructed, or a vector whose elements are rebuilt one by one.args...: Arbitrary positional arguments (ignored by the scalar method, forwarded by the vector method).kwargs...: Arbitrary keyword arguments (ignored by the scalar method, forwarded by the vector method).
Returns
a: The input unchanged.v::Vector: The element-wise rebuilds, for the vector method.
Examples
julia> factory(nothing, 1, 2; x = 3)julia> factory(MeanValue())MeanValue w ┴ nothingRelated
PortfolioOptimisers.vec_to_real_measure — Function
vec_to_real_measure(
measure::Num_VecToScaM,
val::Union{<:VecNum, NTuple{N, <:Number} where {N}};
kwargs...
) -> NumberReduce a vector of real values to a single real value using a specified measure.
vec_to_real_measure applies a reduction algorithm (such as minimum, mean, median, or maximum) to a vector of real numbers, as specified by the concrete subtype of VectorToScalarMeasure. This is used in constraint generation and centrality-based portfolio constraints to aggregate asset-level metrics.
Algorithm
The method that Julia selects is the algorithm. measure names the reduction, and the type parameter of a weighted measure names the branch, MeanValue{Nothing} against MeanValue{<:ObsWeights}, so the branch is chosen at compile time and the field is never tested at run time.
measureis aNumber: return it, and read nothing ofval.measureis aFunction: returnmeasure(val).measureis aMinValue, aMaxValue, aSumValueor aProdValue: returnminimum,maximum,sumorprodofval.measureis aModeValue: returnStatsBase.modeofval.measureis aMeanValueor aMedianValue: returnStatistics.meanorStatistics.medianofval, with the weightsmeasure.wwhen the measure carries them. A tuple iscollected first on the weighted branch.measureis aStdValueor aVarValue: returnStatistics.stdorStatistics.varofval, withcorrected = measure.corrected, with the weightsmeasure.wwhen the measure carries them, and withkwargs...forwarded. A tuple iscollected first on the weighted branch.measureis aStandardisedValue: follow that type's own algorithm, which reduces twice and guards the denominator.
Step 1 is the case that makes a plain number a legal measure: a caller that already holds the value writes it where a reduction goes, and the seam needs no second signature.
Arguments
measure: One of three things.::VectorToScalarMeasure: The reduction to apply toval.::Number: The value to return, whatevervalholds.::Function: Applied tovaldirectly, asmeasure(val).
val: A vector or tuple of real values to be reduced. A tuple is accepted wherever a vector is, and the weighted reductionscollectit first, becauseStatisticsneeds anAbstractVectorbeside its weights. It is ignored whenmeasureis aNumber.kwargs...: Forwarded to the underlying reduction. Only theStdValueandVarValuereductions read them.
Returns
score::Number: Computed value according tomeasure.
Examples
julia> PortfolioOptimisers.vec_to_real_measure(MaxValue(), [1.2, 3.4, 0.7])3.4julia> PortfolioOptimisers.vec_to_real_measure(0.9, [1.2, 3.4, 0.7])0.9Related