Skip to content

Commit 6b74af4

Browse files
authored
Add docs to internal functions (#236)
1 parent 05cde2a commit 6b74af4

7 files changed

Lines changed: 462 additions & 9 deletions

src/MOI_wrapper.jl

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,27 @@
33
# Use of this source code is governed by an MIT-style license that can be found
44
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
55

6-
_is_variable(v::MOI.VariableIndex) = !_is_parameter(v)
6+
"""
7+
_is_parameter(v::MOI.VariableIndex)::Bool
78
9+
Return `true` if `v` encodes a parameter (index above `PARAMETER_INDEX_THRESHOLD`).
10+
"""
811
function _is_parameter(v::MOI.VariableIndex)
912
return PARAMETER_INDEX_THRESHOLD < v.value <= PARAMETER_INDEX_THRESHOLD_MAX
1013
end
1114

15+
"""
16+
_is_variable(v::MOI.VariableIndex)::Bool
17+
18+
Return `true` if `v` is a true decision variable (not a parameter).
19+
"""
20+
_is_variable(v::MOI.VariableIndex) = !_is_parameter(v)
21+
22+
"""
23+
_has_parameters(f)::Bool
24+
25+
Return `true` if any term in `f` contains a parameter index.
26+
"""
1227
function _has_parameters(f::MOI.ScalarAffineFunction{T}) where {T}
1328
for term in f.terms
1429
if _is_parameter(term.variable)
@@ -67,6 +82,13 @@ function _has_parameters(f::MOI.VectorQuadraticFunction)
6782
return false
6883
end
6984

85+
"""
86+
_cache_multiplicative_params!(model, f)
87+
88+
Record all parameters that appear in `p*v` (or higher) product terms into
89+
`model.multiplicative_parameters_pv`. This is used to reject dual queries for
90+
parameters whose dual is not well-defined.
91+
"""
7092
function _cache_multiplicative_params!(
7193
model::Optimizer{T},
7294
f::ParametricQuadraticFunction{T},
@@ -265,6 +287,13 @@ function MOI.get(model::Optimizer, tp::Type{MOI.VariableIndex}, attr::String)
265287
return MOI.get(model.optimizer, tp, attr)
266288
end
267289

290+
"""
291+
_add_variable(model, inner_vi)
292+
293+
Validate that `inner_vi` is not a parameter index and return it unchanged.
294+
Raises an error if the inner solver accidentally created a variable index in
295+
the parameter range.
296+
"""
268297
function _add_variable(model::Optimizer, inner_vi)
269298
if _is_parameter(inner_vi)
270299
error(
@@ -307,6 +336,11 @@ function MOI.supports_add_constrained_variables(
307336
return MOI.supports_add_constrained_variables(model.optimizer, S)
308337
end
309338

339+
"""
340+
_assert_parameter_is_finite(set::MOI.Parameter)
341+
342+
Throw an `AssertionError` if `set.value` is not finite.
343+
"""
310344
function _assert_parameter_is_finite(set::MOI.Parameter{T}) where {T}
311345
if !isfinite(set.value)
312346
throw(
@@ -356,6 +390,12 @@ function MOI.add_constrained_variables(
356390
return _add_variable.(model, inner_vis), inner_ci
357391
end
358392

393+
"""
394+
_add_to_constraint_map!(model, ci)
395+
396+
Register `ci` in `model.constraint_outer_to_inner` (mapping outer to inner
397+
index). Specialized methods also increment the affine or quadratic counter.
398+
"""
359399
function _add_to_constraint_map!(model::Optimizer, ci)
360400
model.constraint_outer_to_inner[ci] = ci
361401
return
@@ -472,6 +512,13 @@ function MOI.delete(model::Optimizer, v::MOI.VariableIndex)
472512
return
473513
end
474514

515+
"""
516+
_delete_variable_index_constraint(model, d, F, S, v)
517+
518+
Remove stale entries from constraint dict `d` after variable `v` is deleted.
519+
Specialized for `VectorOfVariables` (prunes invalidated constraints) and
520+
`VariableIndex` (removes the entry keyed by `v`). The default method is a no-op.
521+
"""
475522
_delete_variable_index_constraint(model, d, F, S, v) = nothing
476523

477524
function _delete_variable_index_constraint(
@@ -843,6 +890,12 @@ function MOI.modify(
843890
return
844891
end
845892

893+
"""
894+
_add_constraint_direct_and_cache_map!(model, f, set)
895+
896+
Add constraint `(f, set)` directly to the inner optimizer and register it in
897+
`model.constraint_outer_to_inner`. Used for constraints with no parameters.
898+
"""
846899
function _add_constraint_direct_and_cache_map!(model::Optimizer, f, set)
847900
ci = MOI.add_constraint(model.optimizer, f, set)
848901
_add_to_constraint_map!(model, ci)
@@ -862,6 +915,13 @@ function MOI.add_constraint(
862915
return _add_constraint_direct_and_cache_map!(model, f, set)
863916
end
864917

918+
"""
919+
_add_constraint_with_parameters_on_function(model, f, set)
920+
921+
Add a constraint whose function `f` contains at least one parameter.
922+
Constructs the appropriate `Parametric*Function`, caches it, and registers the
923+
outer-to-inner constraint index mapping. Dispatches on `f` type.
924+
"""
865925
function _add_constraint_with_parameters_on_function(
866926
model::Optimizer,
867927
f::MOI.ScalarAffineFunction{T},
@@ -913,6 +973,13 @@ function MOI.add_constraint(
913973
return outer_ci
914974
end
915975

976+
"""
977+
_add_vi_constraint(model, pf, set)
978+
979+
Add a parametric affine constraint as a variable bound in the inner optimizer
980+
(i.e. `VariableIndex`-in-set form). Used when `ConstraintsInterpretation` allows
981+
converting single-variable constraints with a unit coefficient into bounds.
982+
"""
916983
function _add_vi_constraint(
917984
model::Optimizer,
918985
pf::ParametricAffineFunction{T},
@@ -1026,6 +1093,11 @@ function _add_constraint_with_parameters_on_function(
10261093
return outer_ci
10271094
end
10281095

1096+
"""
1097+
_is_affine(f::MOI.ScalarQuadraticFunction)::Bool
1098+
1099+
Return `true` if `f` has no quadratic terms (i.e., reduces to an affine function).
1100+
"""
10291101
_is_affine(f::MOI.ScalarQuadraticFunction) = isempty(f.quadratic_terms)
10301102

10311103
function MOI.add_constraint(
@@ -1062,6 +1134,11 @@ function MOI.add_constraint(
10621134
return _add_constraint_with_parameters_on_function(model, f, set)
10631135
end
10641136

1137+
"""
1138+
_is_vector_affine(f::MOI.VectorQuadraticFunction)::Bool
1139+
1140+
Return `true` if `f` has no quadratic terms (i.e., reduces to a vector affine function).
1141+
"""
10651142
_is_vector_affine(f::MOI.VectorQuadraticFunction) = isempty(f.quadratic_terms)
10661143

10671144
function _add_constraint_with_parameters_on_function(
@@ -1283,6 +1360,12 @@ function MOI.get(model::Optimizer, attr::MOI.ObjectiveFunction)
12831360
return MOI.get(model.original_objective_cache, attr)
12841361
end
12851362

1363+
"""
1364+
_empty_objective_function_caches!(model::Optimizer)
1365+
1366+
Clear all cached objective function data and reset `original_objective_cache`.
1367+
Called before setting a new objective to avoid stale cached terms.
1368+
"""
12861369
function _empty_objective_function_caches!(model::Optimizer{T}) where {T}
12871370
model.affine_objective_cache = nothing
12881371
model.quadratic_objective_cache = nothing

src/ParametricOptInterface.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,33 @@ struct ParameterIndex
2424
index::Int64
2525
end
2626

27+
"""
28+
p_idx(vi::MOI.VariableIndex)::ParameterIndex
29+
30+
Convert a `VariableIndex` that represents a parameter into a `ParameterIndex`
31+
by stripping the `PARAMETER_INDEX_THRESHOLD` offset.
32+
"""
2733
function p_idx(vi::MOI.VariableIndex)::ParameterIndex
2834
return ParameterIndex(vi.value - PARAMETER_INDEX_THRESHOLD)
2935
end
3036

37+
"""
38+
v_idx(pi::ParameterIndex)::MOI.VariableIndex
39+
40+
Convert a `ParameterIndex` back into a `VariableIndex` by adding the
41+
`PARAMETER_INDEX_THRESHOLD` offset.
42+
"""
3143
function v_idx(pi::ParameterIndex)::MOI.VariableIndex
3244
return MOI.VariableIndex(pi.index + PARAMETER_INDEX_THRESHOLD)
3345
end
3446

47+
"""
48+
p_val(vi::MOI.VariableIndex)::Int64
49+
p_val(ci::MOI.ConstraintIndex)::Int64
50+
51+
Return the integer parameter index (1-based position in `model.parameters`)
52+
corresponding to the given variable or constraint index.
53+
"""
3554
function p_val(vi::MOI.VariableIndex)::Int64
3655
return vi.value - PARAMETER_INDEX_THRESHOLD
3756
end
@@ -299,6 +318,11 @@ function Optimizer{T}(
299318
return Optimizer{T}(inner; kwargs...)
300319
end
301320

321+
"""
322+
_parameter_in_model(model, v)
323+
324+
Return `true` if `v` is a parameter index and that parameter exists in `model`.
325+
"""
302326
function _parameter_in_model(model::Optimizer, v::MOI.VariableIndex)
303327
return _is_parameter(v) && haskey(model.parameters, p_idx(v))
304328
end

src/cubic_parser.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ function _Monomial{T}(coefficient::T, var::MOI.VariableIndex) where {T}
2222
end
2323

2424
"""
25-
_monomial_degree(m::_Monomial) -> Int
25+
_monomial_degree(m::_Monomial)::Int
2626
2727
Total degree of a monomial (number of variable/parameter factors).
2828
"""
@@ -72,7 +72,7 @@ struct _ParsedCubicExpression{T}
7272
end
7373

7474
"""
75-
_expand_to_monomials(arg, ::Type{T}) where {T} -> Union{Vector{_Monomial{T}}, Nothing}
75+
_expand_to_monomials(arg, ::Type{T}) where {T}::Union{Vector{_Monomial{T}}, Nothing}
7676
7777
Expand an expression argument to a list of monomials.
7878
Returns `nothing` if the expression is not a valid polynomial.
@@ -389,7 +389,7 @@ function _combine_like_monomials(monomials::Vector{_Monomial{T}}) where {T}
389389
end
390390

391391
"""
392-
_classify_monomial(m::_Monomial) -> Symbol
392+
_classify_monomial(m::_Monomial)::Symbol
393393
394394
Classify a monomial by its structure.
395395
"""
@@ -423,7 +423,7 @@ function _classify_monomial(m::_Monomial)
423423
end
424424

425425
"""
426-
_parse_cubic_expression(f::MOI.ScalarNonlinearFunction, ::Type{T}) where {T} -> Union{_ParsedCubicExpression{T}, Nothing}
426+
_parse_cubic_expression(f::MOI.ScalarNonlinearFunction, ::Type{T}) where {T}::Union{_ParsedCubicExpression{T}, Nothing}
427427
428428
Parse a ScalarNonlinearFunction and return a _ParsedCubicExpression if it represents
429429
a valid cubic polynomial (with parameters multiplying at most quadratic variable terms).

src/duals.jl

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,20 @@
33
# Use of this source code is governed by an MIT-style license that can be found
44
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
55

6+
"""
7+
_compute_dual_of_parameters!(model::Optimizer)
8+
9+
Populate `model.dual_value_of_parameters` with the sensitivity of the optimal
10+
objective with respect to each parameter.
11+
12+
The dual of parameter `p` is computed as `∂obj*/∂p`, accumulated from:
13+
- Each constraint containing `p`: `dual_λ * ∂f/∂p` (negated, since the
14+
parameter appears in the constraint's RHS shift)
15+
- The parametric objective: `±∂obj/∂p` (sign depends on `MIN_SENSE`)
16+
17+
For `pp` quadratic terms the product rule gives two symmetric contributions;
18+
diagonal terms (`p_i == p_j`) are halved to avoid double-counting.
19+
"""
620
function _compute_dual_of_parameters!(model::Optimizer{T}) where {T}
721
n = model.number_of_parameters_in_model
822
if length(model.dual_value_of_parameters) != n
@@ -26,6 +40,14 @@ function _compute_dual_of_parameters!(model::Optimizer{T}) where {T}
2640
return
2741
end
2842

43+
"""
44+
_update_duals_from_affine_constraints!(model::Optimizer)
45+
46+
Iterate over all scalar affine constraint types and accumulate parameter dual
47+
contributions from each into `model.dual_value_of_parameters`.
48+
The inner-dict call is a type-instability barrier so Julia specializes
49+
`_compute_parameters_in_ci!` on the concrete `(F, S)` types.
50+
"""
2951
function _update_duals_from_affine_constraints!(model::Optimizer)
3052
for (F, S) in keys(model.affine_constraint_cache.dict)
3153
affine_constraint_cache_inner = model.affine_constraint_cache[F, S]
@@ -35,6 +57,12 @@ function _update_duals_from_affine_constraints!(model::Optimizer)
3557
return
3658
end
3759

60+
"""
61+
_update_duals_from_vector_affine_constraints!(model::Optimizer)
62+
63+
Iterate over all vector affine constraint types and accumulate parameter dual
64+
contributions from each into `model.dual_value_of_parameters`.
65+
"""
3866
function _update_duals_from_vector_affine_constraints!(model::Optimizer)
3967
for (F, S) in keys(model.vector_affine_constraint_cache.dict)
4068
vector_affine_constraint_cache_inner =
@@ -45,6 +73,12 @@ function _update_duals_from_vector_affine_constraints!(model::Optimizer)
4573
return
4674
end
4775

76+
"""
77+
_update_duals_from_quadratic_constraints!(model::Optimizer)
78+
79+
Iterate over all scalar quadratic constraint types and accumulate parameter
80+
dual contributions from each into `model.dual_value_of_parameters`.
81+
"""
4882
function _update_duals_from_quadratic_constraints!(model::Optimizer)
4983
for (F, S) in keys(model.quadratic_constraint_cache.dict)
5084
quadratic_constraint_cache_inner =
@@ -55,6 +89,22 @@ function _update_duals_from_quadratic_constraints!(model::Optimizer)
5589
return
5690
end
5791

92+
"""
93+
_compute_parameters_in_ci!(model, constraint_cache_inner)
94+
_compute_parameters_in_ci!(model, pf, ci)
95+
96+
Accumulate the dual contribution of parameters appearing in constraint `ci`
97+
into `model.dual_value_of_parameters`.
98+
99+
For affine terms the contribution is `-λ * c` where `λ` is the constraint
100+
dual and `c` is the parameter coefficient. For `pp` quadratic terms the product
101+
rule applies: each parameter receives `-λ * c * value_of_other_parameter`;
102+
diagonal terms (`p_i == p_j`) are halved to avoid double-counting the
103+
symmetric representation.
104+
105+
The `DoubleDictInner` overload is a function barrier; the concrete `pf`
106+
overloads are where the computation happens.
107+
"""
58108
function _compute_parameters_in_ci!(
59109
model::Optimizer,
60110
constraint_cache_inner::DoubleDicts.DoubleDictInner{F,S,V},
@@ -114,6 +164,16 @@ function _compute_parameters_in_ci!(
114164
return
115165
end
116166

167+
"""
168+
_update_duals_from_objective!(model, pf)
169+
170+
Accumulate the sensitivity of the parametric objective with respect to each
171+
parameter into `model.dual_value_of_parameters`.
172+
173+
The sign convention matches the objective sense: `+` for minimization, `-` for
174+
maximization. Specialized methods exist for `ParametricQuadraticFunction` and
175+
`ParametricCubicFunction` to handle higher-order terms via the product rule.
176+
"""
117177
function _update_duals_from_objective!(model::Optimizer{T}, pf) where {T}
118178
is_min = MOI.get(model.optimizer, MOI.ObjectiveSense()) == MOI.MIN_SENSE
119179
for param in affine_parameter_terms(pf)
@@ -200,13 +260,26 @@ function MOI.get(
200260
return model.dual_value_of_parameters[p_val(cp)]
201261
end
202262

263+
"""
264+
_is_additive(model, cp)
265+
266+
Return `true` if parameter `cp` appears only in additive (affine/constant)
267+
positions. Returns `false` if it appears in any `p*v` product term, in which
268+
case its dual is not well-defined and `ConstraintDual` will error.
269+
"""
203270
function _is_additive(model::Optimizer, cp::MOI.ConstraintIndex)
204271
if cp.value in model.multiplicative_parameters_pv
205272
return false
206273
end
207274
return true
208275
end
209276

277+
"""
278+
_update_duals_from_vector_quadratic_constraints!(model::Optimizer)
279+
280+
Iterate over all vector quadratic constraint types and accumulate parameter
281+
dual contributions from each into `model.dual_value_of_parameters`.
282+
"""
210283
function _update_duals_from_vector_quadratic_constraints!(model::Optimizer)
211284
for (F, S) in keys(model.vector_quadratic_constraint_cache.dict)
212285
vector_quadratic_constraint_cache_inner =

0 commit comments

Comments
 (0)