JuMP model state: private API

PortfolioOptimisers.SHARED_STATE — Constant
SHARED_STATE

The names of the Model State entries that a nested risk build shares with its enclosing build, under the bare key.

It is the complement of Per-Build Risk State. A name belongs here if and only if its entry is not a function of the weights under optimisation and is not a presence flag of one build. The inner and the outer build then want the same object, and a prefix would break the sharing that it exists to protect. shared_get, shared_has and shared_set! check each name against this set, so the classification holds at run time, in addition to the seam-lock test.

The comments in the source group the names and give the reason for each group. A new name here states that a nested build can safely read the copy of the enclosing build. Check that statement before you add a name.

Related

source
PortfolioOptimisers.assert_shared_state — Function
assert_shared_state(name::Symbol)

Check that name is on SHARED_STATE, the list of the Model State entries that a nested build reads under the bare key.

shared_get, shared_has and shared_set! call it first. A read of an entry of one build without its prefix then fails at the call, and does not read the copy of the enclosing build in silence. That silent read is the defect that once broke IndependentVariableTracking.

Validation

  • name in SHARED_STATE. Otherwise an ArgumentError names the two verbs for Per-Build Risk State, state_get and state_build!.

Returns

  • nothing.
source
PortfolioOptimisers.state_key — Function
state_key(prefix::Symbol, name::Symbol)
state_key(prefix::Symbol, name::Symbol, i)

Compose the Model State key of the entry name under prefix, and at the measure index i when it is given.

It is the one place that spells the two conventions of the namespace. A key separates entries on two axes:

  • prefix separates one build from another, so a nested risk build cannot collide with the build that encloses it.
  • i separates one measure from another inside one build, so two ConditionalValueatRisk measures in one vector get their own scratch entries.

The seam-lock test checks that no builder composes a key by hand. A builder reaches Model State through state_get, state_has, state_set! and state_build!.

The key is the concatenation of the parts, with no delimiter, so two different sets of parts can give one key: (:tr_dr_, 11) and (:tr_dr_1, 1) both give :tr_dr_11. A delimiter would change every key that a caller reads at the top level, so the spelling stays, and assert_state_key_free refuses the collision when an entry is registered.

Related

source
PortfolioOptimisers.assert_state_key_free — Function
assert_state_key_free(model::JuMP.Model, key::Symbol)

Check that the Model State key key is not registered, so that a registration cannot replace an entry.

state_key joins its parts with no delimiter, so a name that ends in a digit at a low index gives the same key as a shorter name at a higher index: state_key(p, :tr_dr_, 11) == state_key(p, :tr_dr_1, 1). Without this check the second registration replaces the first. The model then holds one entry where the build expects two, and a constraint binds the wrong variable. That is a wrong answer and not a crash, so the registration raises.

A delimiter is not the fix. It changes every key that a caller reads at the top level: state_key(Symbol(""), :ret_, 1) is :ret_1, a key that callers read. It also still lets one builder replace the entry of another under a key that both spell correctly. The check refuses both cases. A second registration under one key has no valid use. To build an entry once, use state_build!, which returns the existing entry, and to set a flag, use mark_state!, which has no effect the second time.

Validation

  • !haskey(model, key). Otherwise an ArgumentError names the key and the two verbs that accept a repeat.

Returns

  • nothing.

Related

source
PortfolioOptimisers.state_set! — Function
state_set!(model::JuMP.Model, prefix::Symbol, name::Symbol, val)
state_set!(model::JuMP.Model, prefix::Symbol, name::Symbol, i, val)

Register val under the Model State key of name and prefix, and return val.

A nested risk build, such as a risk tracking measure, passes a non-empty prefix, so the entries it makes (:X, :net_X, :W, :dd and others) do not collide with the entries of the outer model. The empty prefix gives the bare key.

The method with i registers the scratch of one measure, such as :cvar_risk_ or :z_cvar_, at the measure index i, so two measures of one type in one build get their own entries. state_key composes both parts of the key.

Algorithm

  1. Compose the key with state_key.
  2. Check with assert_state_key_free that the key is free.
  3. Register val under the key.

Validation

Related

source
PortfolioOptimisers.state_has — Function
state_has(model::JuMP.Model, prefix::Symbol, name::Symbol)
state_has(model::JuMP.Model, prefix::Symbol, name::Symbol, i)

Return true when the Model State entry name is registered under prefix, and at the index i when it is given.

Related

source
PortfolioOptimisers.state_get — Function
state_get(model::JuMP.Model, prefix::Symbol, name::Symbol)
state_get(model::JuMP.Model, prefix::Symbol, name::Symbol, i)

Return the Model State entry name under prefix, and at the index i when it is given.

Use a named accessor, such as get_w or get_net_X, where one exists. Its error names the builder of the entry, where this error names only the key.

Validation

  • The entry is registered. Otherwise an ArgumentError names the key and states that a reader ran before the builder of the entry.

Related

source
PortfolioOptimisers.state_build! — Function
state_build!(f, model::JuMP.Model, prefix::Symbol, name::Symbol)
state_build!(f, model::JuMP.Model, prefix::Symbol, name::Symbol, i)

Return the Model State entry name under prefix, and build it with f() the first time.

Every risk and constraint builder uses it to build an entry once. An entry that an earlier measure of the same build, or an outer build, already registered comes back unchanged. The builder f registers each companion entry that it makes with state_set!.

This verb composes the key, not the caller, so a new Model State entry gets the prefix of its build with no more work.

Algorithm

  1. Compose the key with state_key.
  2. When the key is registered, return its entry.
  3. Otherwise call f(), giving val.
  4. Register val under the key and return it.

Related

source
PortfolioOptimisers.mark_state! — Function
mark_state!(model::JuMP.Model, prefix::Symbol, name::Symbol)
mark_state!(model::JuMP.Model, prefix::Symbol, name::Symbol, i)

Record that this build has name, under prefix and at the index i when it is given.

The entry is a presence flag of one build. It holds true and nothing more, and a reader tests it with state_has. A second call has no effect. The prefix keeps the flags of a nested build out of the enclosing build. This is the half of Per-Build Risk State that does not depend on the weights.

Returns

  • nothing.

Related

source
PortfolioOptimisers.nested_prefix — Function
nested_prefix(prefix::Symbol, tag::Symbol)
nested_prefix(prefix::Symbol, tag::Symbol, i)

Compose the Model State prefix that a nested build passes down to its own builders.

It gives a prefix and not a key, so no entry of the nested build can take the key of an entry of the enclosing build. tag names the kind of nesting (:tr_iv_, :tr_dv_, :tr_ir_, :tr_dr_, :gain_). The method with i adds the measure index and a closing _, so nested_prefix(:a_, :tr_dr_, 3) is :a_tr_dr_3_. The index keeps a tracking measure inside a tracking measure free of collisions.

Related

source
PortfolioOptimisers.nested_index — Function
nested_index(tag::Symbol, i)

Compose the Model State measure index that the parts of a composite measure pass down.

It is the twin of nested_prefix on the other axis of the key. A composite measure that builds its parts in the same build, such as GenericValueatRiskRange over its loss and gain sides, separates the parts by index and not by prefix. The parts share the infrastructure entries of the build, and neither may build them a second time. tag names the part (:loss_, :gain_), and the composition nests: nested_index(:gain_, nested_index(:loss_, 2)) is :gain_loss_2, so a range inside a range stays free of collisions.

The result is an index and not a key.

Related

source