Messages: private API
PortfolioOptimisers.did_you_mean — Function
did_you_mean(name::AbstractString, candidates) -> StringReturn a "did you mean" suffix naming the closest match to name among candidates, or an empty string when no candidate reaches the global STRING_DISTANCE min_score threshold (or candidates is empty). The suffix reads " (did you mean X?)", with the match in place of X.
Do not wrap the suffix in a code span that also carries escaped backticks. JuliaFormatter mis-pairs the backticks and deletes the spaces around the neighbouring code spans, which breaks the rendering.
Used to enrich "variable not in asset universe" messages (see unknown_variable_msg) with a typo suggestion. The distance and threshold are read from the active STRING_DISTANCE config — global default via set_string_distance!, task-scoped override via with_string_distance; the threshold gating means a name legitimately absent from a meta-optimiser cluster/subset (no close neighbour) draws no suggestion.
Algorithm
- Return an empty string when
candidatesis empty, because there is nothing to search. - Read the active
STRING_DISTANCEconfiguration intosd. - Search
candidatesfor the entry nearest tonameundersd.dist, keeping only a match whose normalised similarity reachessd.min_score, givingmatch. - Return an empty string when step 3 finds no match. Otherwise return the suffix that names
match.
Arguments
name::AbstractString: The offending name the caller wrote.candidates: Collection of valid names to search.
Returns
msg::String: The suffix" (did you mean X?)", or an empty string when no candidate reaches the threshold.
Related
PortfolioOptimisers.suggest_declared_key — Function
suggest_declared_key(key, candidates) -> String
Suggest the nearest candidates entry to a mistyped declaration key: a macro block key, a dictionary key, a struct field name, or a keyword of a generated constructor.
Wraps did_you_mean in a looser scoped configuration than the global default: Damerau-Levenshtein (so a transposed pair costs one edit, not two) at min_score = 0.5. The strict global default exists to keep near-miss probes from echoing real asset names back to the caller; that boundary does not apply here, because the candidates are compile-time constants — block keys, dictionary keys and field names — with nothing to leak. At the default 0.7 under plain Levenshtein, short keys never match: nuon scores 0.5 against noun, so the suggestion would be dead code.
Arguments
key: The mistyped declaration key, converted to aString.candidates: Collection of valid keys, each converted to aString.
Returns
msg::String: The suffix thatdid_you_meanreturns under the looser configuration.
Related
PortfolioOptimisers.unknown_variable_msg — Function
unknown_variable_msg(v, nx, key; candidates = nx, axis = "asset") -> StringBuild the warning/error text for a constraint or view variable v that is absent from the universe nx (stored under key). Names the variable and the universe size only — never the full universe — and appends a did_you_mean suggestion when a close match exists.
candidates is the pool searched for the typo suggestion (default: the universe nx). Callers whose valid namespace is broader than the raw universe — e.g. name_to_val!, where a key may name a group rather than an asset — pass a wider pool (asset names plus group/set keys) so the suggestion can name a mistyped group. The reported universe size is always length(nx) regardless of candidates.
axis names the universe the variable was looked up in. It defaults to "asset" because that is the axis every constraint resolved against before ExposureConstraintEstimator; a re-based constraint resolves its names against the factor universe and passes "factor", so the message names the axis the user actually wrote in.
consequence states what the failure cost, because the unit of a drop differs by shape. A value keyed by the name loses that entry alone, which is name_to_val! and the default "term dropped". A row is a joint statement over several names with one right-hand side, so it goes whole — fitting a + c == 0.05 as a == 0.05 would assert something the caller never wrote — and get_linear_constraints passes "row dropped". Saying "term dropped" there sent a reader looking for a row that was still fitted.
Shared by get_linear_constraints, Black-Litterman view generation, entropy-pooling view generation, and name_to_val! so the message (and its info-leak-safe shape) lives in exactly one place.
Arguments
v: The variable name that is absent from the universe.nx: The universe the lookup failed against. Only its length reaches the message.key: The key the universe is stored under.candidates = nx: Pool searched for the typo suggestion.axis::AbstractString = "asset": Name of the universe the variable was looked up in.consequence::AbstractString = "term dropped": What the failure cost —"row dropped"where the row is the unit.
Returns
msg::String: The diagnostic text, with adid_you_meansuffix when a close match exists.
Related
PortfolioOptimisers.misaligned_axis_msg — Function
misaligned_axis_msg(declared, names, axis, key, sym) -> StringBuild the error text for a universe declared under key that disagrees with the axis sym of the data it will be used against — declared against names.
Position is the only link between a name and a column, so a disagreement is not a naming inconvenience: every constraint row, bound and group would be attached to the wrong column and the optimisation would succeed with the wrong answer. The message therefore names what to fix, not just what is wrong.
Two disagreements are reported differently because they have different causes. Different lengths mean the two describe different universes — usually a stale sets against freshly sliced data. Equal lengths mean they describe the same universe in a different order, and the first differing position is the whole diagnosis. Names the sizes and the first differing pair only — never either universe in full, the same info-leak-safe discipline as unknown_variable_msg.
Algorithm
- Build
detailthrough the branch that the two lengths select. - When the lengths differ,
detailnames both counts and nothing else, because the two describe different universes. - When the lengths agree, find
i, the first position at which the two disagree, and letdetailname the shared count,i, and the pair ati. - Build the message from
detail, the axis, the key, and the repair to make.
Arguments
declared: The universe declared underkey.names: The axis of the data the universe is used against.axis: Name of the axis, for example"asset".key: The key the declared universe is stored under.sym: Field of the returns data that carries the correct axis, named in the repair.
Returns
msg::String: The error text.
Related
PortfolioOptimisers.strict_diagnostic — Function
strict_diagnostic(msg::AbstractString, strict::Bool) -> NothingReport a term that cannot contribute a row: throw an ArgumentError under strict, warn otherwise, and in both cases the offending term is dropped.
strict governs what is droppable: a name that resolves against nothing, a row whose coefficients carry no information, as with a zero centrality vector, and a holding a factor attribution cannot attribute, in an asset the prior could not estimate or at an observation with no return. Nothing else is refused, and a malformed entry throws unconditionally, because there is no reading of it to fall back to. Every such diagnostic in the library routes through here, so the strictness policy is one edit.
Algorithm
- Throw an
ArgumentErrorcarryingmsgwhenstrictistrue, which ends the call. - Otherwise emit
msgas a warning and return.
Arguments
msg: The diagnostic text, built byunknown_variable_msgor one of its siblings.strict: Iftrue, throws anArgumentError; iffalse, issues a warning.
Validation
- The call raises an
ArgumentErrorcarryingmsgwhenstrictistrue.
Returns
nothing.
Related
PortfolioOptimisers.missing_group_assets_msg — Function
missing_group_assets_msg(group, missing_assets, nx, key) -> StringBuild the warning/error text for a group that resolves in the asset sets but whose members missing_assets are absent from the asset universe nx (stored under key). Names the group, the offending member names (which are caller input, not internal state), and the universe size only — never the full universe or the input value dictionary — and appends a did_you_mean suggestion for the first missing member.
Shared by name_to_val! so the info-leak-safe message shape lives in exactly one place, alongside unknown_variable_msg and empty_row_msg.
Arguments
group: The group name that resolved in the asset sets.missing_assets: The member names absent from the asset universe.nx: The asset universe. Only its length reaches the message.key: The key the asset universe is stored under.
Returns
msg::String: The diagnostic text, with adid_you_meansuffix for the first missing member.
Related
PortfolioOptimisers.empty_row_msg — Function
empty_row_msg(eqn, nx, key; noun::AbstractString = "constraint",
axis::AbstractString = "asset") -> StringBuild the warning/error text for a parsed equation eqn whose every name resolved against the universe nx (stored under key) and whose row is nonetheless all zero, so it is dropped. Names the equation and the universe size only — never the full universe or the parsed struct. noun is "constraint" for linear constraints or "view" for Black-Litterman views; axis names the universe, as in unknown_variable_msg.
A name that misses the universe never reaches this message: the row is the unit, so an unresolved name takes the whole row with it at the name, reported by unknown_variable_msg with consequence = "row dropped" (see docs/adr/0125-a-view-row-that-names-a-departed-asset-is-dropped-whole.md). What is left here summed to zero for a different reason: coefficients that cancel, as in A - A == 0.0, which parses to the single term 0.0*A, or a row that carries no variable at all, as in 1 == 0.004. Reporting a typo for either would send a user hunting for one that is not there — the same discipline as empty_projected_row_msg and zero_centrality_msg.
Shared by get_linear_constraints and Black-Litterman view generation.
Arguments
eqn: The parsed equation that resolved and still summed to zero.nx: The universe the terms resolved against. Only its length reaches the message.key: The key the universe is stored under.noun::AbstractString = "constraint":"constraint"for a linear constraint,"view"for a Black-Litterman view.axis::AbstractString = "asset": Name of the universe, as inunknown_variable_msg.
Returns
msg::String: The diagnostic text.
Related
PortfolioOptimisers.empty_projected_row_msg — Function
empty_projected_row_msg(eqn, nf, key, n; noun::AbstractString = "constraint") -> StringBuild the warning/error text for a re-based equation eqn whose terms did resolve against the factor universe nf (stored under key), but whose projection through the loadings is an all-zero row over n assets.
This diagnosis exists only under a re-basis, and it is a different failure from empty_row_msg: there the row's own coefficients came to zero, here they did not and the basis annihilated them. The real cause is a factor no asset loads on, and naming the equation's own arithmetic for it would send a user auditing coefficients that are fine.
Arguments
eqn: The re-based equation whose projection is an all-zero row.nf: The factor universe the terms resolved against. Only its length reaches the message.key: The key the factor universe is stored under.n: Number of assets the row was projected over.noun::AbstractString = "constraint":"constraint"for a linear constraint,"view"for a view.
Returns
msg::String: The diagnostic text.
Related
PortfolioOptimisers.zero_centrality_msg — Function
zero_centrality_msg(alg, n) -> StringBuild the warning/error text for a centrality constraint whose centrality vector carries no information, so the row it would build is dropped.
The vector is either empty or all zero. Either way the row reads 0' w <= B, which every set of weights satisfies, so it constrains nothing. This is a fact about the graph and never a mistyped name: two assets under BetweennessCentrality give the zero vector, because no vertex lies on a shortest path between two others. Reporting it through unknown_variable_msg or one of its siblings would send a reader hunting for a typo that is not there, which is why this message is its own.
Names the centrality algorithm and the length of the vector only — never its entries — the same info-leak-safe discipline as empty_row_msg.
Arguments
alg: The centrality algorithm that produced the vector. Only its type name reaches the message.n: Length of the centrality vector. Zero means the vector is empty.
Returns
msg::String: The diagnostic text.
Related
PortfolioOptimisers.gross_budget_bounds_msg — Function
gross_budget_bounds_msg(lb, ub) -> StringBuild the error text for a gross budget (gbgt) whose weight bounds lb and ub admit no short position. With no negative bound the gross exposure equals the net exposure, so the net budget (bgt) already owns the constraint and gbgt has nothing left to express.
Names the size of the bounds and the failed predicate only — never the bound values — the same info-leak-safe discipline as unknown_variable_msg and its siblings. Scalar or absent bounds have no size, so the message names the bounds without a count.
Algorithm
- Take
n, the greater of the two bound lengths, counting a bound that is not a vector as zero. - Build
scope, which names the bounds alone whennis zero and names them with the asset count otherwise. - Build the message from the fixed explanation and
scope.
Arguments
lb: Lower weight bound. Only its length reaches the message.ub: Upper weight bound. Only its length reaches the message.
Returns
msg::String: The error text.
Related
PortfolioOptimisers.failed_solve_msg — Function
failed_solve_msg(trials::AbstractDict; max_line_length::Integer = 200) -> StringBuild the warning text for a JuMP model that no configured solver could solve satisfactorily (see JuMPResult). One line per failed stage of each solver trial: the solver name, the stage that failed (set_optimizer, optimize!, or assert_is_solved_and_feasible), and the first line of the error truncated to max_line_length characters — so a JuMP termination status stays visible.
Never interpolates the whole trials dictionary, the solver settings, or full exception payloads into the log; the raw data remains available on the returned JuMPResult.trials. This is the same info-leak-safe message discipline as unknown_variable_msg and its siblings. Solver names and stages are sorted so the message is deterministic.
Algorithm
- Open
msgwith the trial count, so the reader sees how many solvers were tried before the detail. - For each solver name, in sorted order, read its entry into
trial. - Read the stages of
trial, givingstages. An entry that is not a dictionary is wrapped as the single stage:trial, so a solver that failed before any stage was recorded still reports. - For each stage, in sorted order, skip
:settings, because the solver settings are caller input and never reach a log. - Append one line per remaining stage: the solver name, the stage, and the first line of its error from
first_error_line.
Arguments
trials::AbstractDict: One entry per solver trial, keyed by solver name.max_line_length::Integer = 200: Maximum number of characters of each error line.
Returns
msg::String: The warning text, one line per failed stage.
Related
PortfolioOptimisers.first_error_line — Function
first_error_line(err, max_line_length::Integer) -> String
Render the first line of an error for a log message, truncated to max_line_length characters (a trailing … marks the cut). Exceptions render via showerror, so the line carries the exception type and message; anything else renders via repr.
Algorithm
- Render
errintos, throughshowerrorwhenerris anExceptionand throughreprotherwise. - Take
line, the text ofsup to its first newline. - Return
lineunchanged when it fitsmax_line_length, and otherwise return its firstmax_line_lengthcharacters followed by….
Arguments
err: The error to render.max_line_length::Integer: Maximum number of characters the returned line may hold before the cut.
Returns
line::String: The rendered first line, truncated when needed.
Related