Preferences: private API

PortfolioOptimisers.PREFERENCE_KEYSConstant
PREFERENCE_KEYS

The Preferences.jl keys read at package load to seed the global config defaults (see apply_preferences!):

Preferences.jl offers no way to enumerate the keys a project has set, so a misspelled key cannot be detected and is silently ignored (the shipped default applies) — misspelled or invalid values under these keys fail closed at load. A name inside the "show_nothing_fields_by_type" table that matches no type is a value the package cannot refuse either: a type outside the package can render through @define_pretty_show, so an unknown name is accepted and does nothing.

A valid value is applied, but a value that widens a guard is announced with a warning (see relaxed_preferences_msg): a preference file is data, it travels with a cloned project, and it applies before any user code runs.

Related

source
PortfolioOptimisers.PREFERENCE_DISTANCESConstant
PREFERENCE_DISTANCES

Enumerated allowlist mapping the names accepted by the "suggestion_distance" preference to their StringDistances.StringDistance objects. Membership and dispatch are one Dict — the same single-source-of-truth discipline as the equation parser's function allowlist (docs/adr/0025-enumerated-parser-allowlist.md): an unknown name fails closed at load with a typed error carrying a did_you_mean suggestion.

Supported names: "levenshtein", "damerau_levenshtein", "jaro", "jaro_winkler", "ratcliff_obershelp".

Related

source
PortfolioOptimisers.apply_preferences!Function
apply_preferences!(prefs::AbstractDict{<:AbstractString})

Apply load-time preference values to the global config defaults (EQUATION_LIMITS, RESOURCE_LIMITS, STRING_DISTANCE, COMPACT_SHOW, SHOW_NOTHING_FIELDS). Called by the package __init__ with the PREFERENCE_KEYS values read via Preferences.load_preference; nothing values (unset preferences) are skipped and keep the shipped default.

Fails closed on an invalid value: it throws a typed ArgumentError naming the key and value, so the package refuses to load rather than silently running with a value the project got wrong. Values are applied through the set_*! setters, so they receive the same validation as runtime calls.

A valid value is applied whatever its size — the caps exist to turn an OOM kill into a typed error, not to second-guess a sizing choice, and a project on a large machine may legitimately raise one. A value that widens a guard is announced with a @warn built by relaxed_preferences_msg, because the channel needs no code: a LocalPreferences.toml is data, it travels with a cloned project, and it applies before any user code runs. Widening means a raised RESOURCE_LIMITS or EQUATION_LIMITS cap, or a lowered STRING_DISTANCE suggestion threshold. A value that tightens a guard, or that equals the default it replaces, is silent. The comparison is against the default in effect when the preference is applied, which at load is the shipped default. See the amendment of docs/adr/0041-one-resource-cap-per-sink.md.

To persist a configuration, put the keys in the active project's LocalPreferences.toml, e.g.:

[PortfolioOptimisers]
equation_max_length = 512
equation_max_depth = 64
max_n_sim = 50_000
max_n_subsets = 1_000
max_frontier = 1_000
max_bins = 500
max_hop_count = 100
max_search_grid = 10_000
max_ep_grid = 500
suggestion_min_score = 0.8
suggestion_distance = "damerau_levenshtein"
compact_show = 4
show_nothing_fields = true

[PortfolioOptimisers.show_nothing_fields_by_type]
SimpleVariance = false

Algorithm

  1. Start relaxations empty. It collects one (key, default, value) triple per guard the preferences widen.
  2. Read the two equation keys. When either is set, check that every set value is a positive integer, read the current EQUATION_LIMITS default, record a triple for each value above its default, and apply both through set_equation_limits!. A key the project left unset keeps the value it already has.
  3. Read the seven resource keys and repeat step 2 against RESOURCE_LIMITS and set_resource_limits!.
  4. Read "suggestion_min_score". When it is set, check that it is a real number, record a triple when it is below the current threshold, and apply it through set_string_distance!. A lower threshold widens the guard, which is the opposite direction from a cap.
  5. Read "suggestion_distance". When it is set, check that it is a string, and look it up in PREFERENCE_DISTANCES. An unknown name raises, and the message carries a did_you_mean suggestion. Apply the resolved distance through set_string_distance!.
  6. Read "compact_show". When it is set, check that it is a boolean or an integer, and apply it through set_compact_show!. This key guards nothing, so it records no triple.
  7. Hand prefs to apply_show_preferences!, which reads "show_nothing_fields" and "show_nothing_fields_by_type". Neither key guards anything, so neither records a triple.
  8. When relaxations is not empty, emit the text of relaxed_preferences_msg as a warning.

Arguments

  • prefs: One entry per key of PREFERENCE_KEYS. A nothing value means the project set no preference for that key, and the shipped default stands.

Validation

  • Each of the nine cap keys is a positive integer that is not a Bool.
  • "suggestion_min_score" is a real number that is not a Bool.
  • "suggestion_distance" is a string, and it names an entry of PREFERENCE_DISTANCES.
  • "compact_show" is a Bool or an Integer.
  • "show_nothing_fields" is a Bool, and "show_nothing_fields_by_type" is a table of Bool values, both checked by apply_show_preferences!.
  • A breach of any rule above raises an ArgumentError that names the key and the value, so the package refuses to load.

Returns

  • nothing.

Related

source
PortfolioOptimisers.apply_show_preferences!Function
apply_show_preferences!(
    prefs::AbstractDict{<:AbstractString}
)

Apply the two load-time preferences of SHOW_NOTHING_FIELDS: the global switch and the per-name table. Called by apply_preferences!, which owns the contract of the preference channel; nothing values (unset preferences) are skipped and keep the shipped default.

Fails closed on an invalid value, as apply_preferences! does: a typed ArgumentError names the key and the value. A name inside the table that matches no type is not refused; see PREFERENCE_KEYS.

Algorithm

  1. Read "show_nothing_fields". When it is set, check that it is a Bool, and apply it through the one-argument form of set_show_nothing_fields!.
  2. Read "show_nothing_fields_by_type". When it is set, check that it is a table, then check that every value is a Bool, and apply each entry through the per-name form of set_show_nothing_fields!, with the key converted to a Symbol.

Arguments

Validation

  • "show_nothing_fields" is a Bool.
  • "show_nothing_fields_by_type" is a dictionary, and every value in it is a Bool.
  • A breach of either rule raises an ArgumentError that names the key and the value.

Returns

  • nothing.

Related

source
PortfolioOptimisers.__init__Function
__init__()

Package load hook: reads the PREFERENCE_KEYS preferences of the active project via Preferences.load_preference and applies them to the global config defaults through apply_preferences!. An invalid preference value fails closed — the package refuses to load — rather than running with a value the project got wrong.

This is the one channel that reaches the guards without running code: a LocalPreferences.toml is data, it travels with a cloned project or a template, and it is read here, before any user code. A valid value is therefore applied but not silent — a value that widens a guard is announced with a warning (see relaxed_preferences_msg).

Algorithm

  1. Read every key of PREFERENCE_KEYS with Preferences.load_preference, giving nothing for a key the active project did not set.
  2. Pass the resulting dictionary to apply_preferences!, which validates each value and applies it.

Returns

  • nothing.

Related

source
PortfolioOptimisers.relaxed_preferences_msgFunction
relaxed_preferences_msg(
    relaxations::AbstractVector
) -> String

Build the warning text for the load-time preferences that widened a guard (see apply_preferences!). One line per key: the key, the default it replaced, and the value the project asked for.

A preference file is data. It ships with a cloned project or a template, it is often untracked, and __init__ applies it at using PortfolioOptimisers, before any user code runs. A value that tightens a guard needs no announcement, so the warning names the widened guards alone: the RESOURCE_LIMITS and EQUATION_LIMITS caps a file raised, and a STRING_DISTANCE suggestion threshold it lowered (a lower threshold admits more candidates, which is the info-leak direction of docs/adr/0026-lenient-constraint-names-with-suggestions.md).

Never interpolates the whole preference dictionary, so a key the message does not name stays out of the log — the same info-leak-safe message discipline as unknown_variable_msg.

Algorithm

  1. Open msg with the number of widened guards and the sentence that says when a preference applies.
  2. Append one line per triple, naming the key, the default it replaced, and the value the project asked for.
  3. Close msg with the two repairs: delete the key, or widen the guard for one scope with a with_* helper.

Arguments

  • relaxations: One (key, default, value) triple per widened guard, in PREFERENCE_KEYS order.

Returns

  • msg::String: Multi-line warning text, one line per triple.

Related

source