Reference
Contents
Index
NearOptimalAlternatives.METHOD_DISPATCH_INITIALNearOptimalAlternatives.METHOD_DISPATCH_UPDATENearOptimalAlternatives.AlternativeSolutionsNearOptimalAlternatives.MetaheuristicProblemNearOptimalAlternatives.PSOGANearOptimalAlternatives.PSOGAMetaheuristics.final_stage!Metaheuristics.initialize!Metaheuristics.update_state!NearOptimalAlternatives.DWV_initial!NearOptimalAlternatives.DWV_update!NearOptimalAlternatives.Dist_initial!NearOptimalAlternatives.Dist_update!NearOptimalAlternatives.HSJ_initial!NearOptimalAlternatives.HSJ_update!NearOptimalAlternatives.Min_Max_Variables_initial!NearOptimalAlternatives.Min_Max_Variables_update!NearOptimalAlternatives.Random_Vector_initial!NearOptimalAlternatives.Random_Vector_update!NearOptimalAlternatives.Spores_initial!NearOptimalAlternatives.Spores_update!NearOptimalAlternatives._alt_solve_diagnosticsNearOptimalAlternatives._apply_warm_start!NearOptimalAlternatives._arclength_sampleNearOptimalAlternatives._safe_dualsNearOptimalAlternatives._safe_solve_timeNearOptimalAlternatives.add_solution!NearOptimalAlternatives.create_alternative_generating_problemNearOptimalAlternatives.create_alternative_generating_problem!NearOptimalAlternatives.create_objectiveNearOptimalAlternatives.extract_boundsNearOptimalAlternatives.extract_constraintNearOptimalAlternatives.extract_objectiveNearOptimalAlternatives.generate_alternatives_arclength!NearOptimalAlternatives.generate_alternatives_metaheuristicsNearOptimalAlternatives.generate_alternatives_optimization!NearOptimalAlternatives.generate_alternatives_sweep!NearOptimalAlternatives.is_better_psogaNearOptimalAlternatives.run_alternative_generating_problem!NearOptimalAlternatives.update_objective_function!NearOptimalAlternatives.update_solutions!NearOptimalAlternatives.update_solutions!NearOptimalAlternatives.update_solutions!
NearOptimalAlternatives.METHOD_DISPATCH_INITIAL — Constant
Maps each modeling_method symbol to its corresponding initial-direction function, called once to pick the first direction. Used by create_alternative_generating_problem! to dynamically select the appropriate function based on the specified method.
NearOptimalAlternatives.METHOD_DISPATCH_UPDATE — Constant
Maps each modeling_method symbol to its corresponding update function, called on every iteration after the first to pick the next direction. Used by update_objective_function! to dynamically select the appropriate function based on the method specified in create_alternative_generating_problem!.
NearOptimalAlternatives.AlternativeSolutions — Type
Structure holding the solutions for the near-optimal alternatives.
tags is optional per-solution metadata (e.g. the discovery direction and budget level a point came from in a budget sweep). It is empty for the classic single-budget generators and is only populated by generate_alternatives_sweep!.
NearOptimalAlternatives.MetaheuristicProblem — Type
Structure representing a problem that can be solved by Metaheuristics.jl and the algorithm to solve it.NearOptimalAlternatives.PSOGA — Type
Structure holding all parameters for PSOGA (Particle Swarm optimization for Generating Alternatives).NearOptimalAlternatives.PSOGA — Method
PSOGA(;
N = 100,
N_solutions = 1,
C1 = 2.0,
C2 = 2.0,
ω = 0.8,
v = Float64[],
flock == Metaheuristics.xf_indiv[],
subBest = Metaheuristics.xf_indiv[],
information = Information(),
options = Options(),
)Construct a PSOGA Metaheuristic algorithm.
Arguments
N: total population sizeN_solutions::Int: number of solutions sought. This is the same as the number of subpopulations searching for a solution.C1::Float64: cognitive parameter. Used to compute velocity based on own best solution.- `C2::Float64: social parameter. Used to compute velocity based on best solution in subpopulation.
ω::Float64: inertia parameter. Used to compute velocity to ensure not too large changes.v::Array{Float64}: array of velocities per individual.flock::Array: array of all current positions of each of the individuals.subBest::Array: array of best solutions per subpopulation.maximise_total::Bool: if true, we maximise the sum of distances between a point and all centroids of other subpopulations, else we maximise the minimum distance between a point and the centroids of other subpopulations.
Metaheuristics.final_stage! — Method
final_stage( status, parameters::PSOGA, problem, information, options, args...; kwargs... )
Perform concluding operations after solving a problem using PSOGA. Called by main loop of Metaheuristics.
Metaheuristics.initialize! — Method
initialize!( status, parameters::PSOGA, problem, information, options, args...; kwargs... )
initialize all parameters used when solving a problem using PSOGA. Called by main loop of Metaheuristics.
Metaheuristics.update_state! — Method
update_state( status, parameters::PSOGA, problem, information, options, args...; kwargs... )
Perform one iteration of PSOGA. Called by main loop of Metaheuristics.
NearOptimalAlternatives.DWV_initial! — Method
DWV_initial!(
model::JuMP.Model,
variables::AbstractArray{T,N},
fixed_variables::Vector{VariableRef};
weights::Vector{Float64} = zeros(length(variables)),
metric::Distances.SemiMetric = SqEuclidean(),
old_objective::AffExpr = JuMP.objective_function(model),
) where {T<:Union{VariableRef,AffExpr},N}Initialize the objective of a JuMP model using the Directionally Weighted Variables method to generate alternative solutions. This function sets a new objective that minimizes the weighted sum of the decision variables, where weights are uniformly chosen between -1 and 1, based on the original objective function. Fixed variables are locked at their optimal values.
Arguments
model::JuMP.Model: a solved JuMP model whose objective is to be redefined for alternative generation.variables::AbstractArray{T,N}: the variables involved in the objective, typically a vector or matrix ofVariableRefs orAffExprs.fixed_variables::Vector{VariableRef}: variables to be fixed at their current values to avoid changes in alternatives.weights::Vector{Float64}: optional vector of weights for each variable; will be internally overwritten based on variable values.metric::Distances.SemiMetric: unused in this method (included for consistency with other alternative generation methods).old_objective::AffExpr: the original objective function of the model, used to determine variable weights.
Behavior
- Variables are randomly minimized or maximized, based on the original objective function.
- Fixed variables are frozen at their optimal values using
fix(...). - The objective is set to minimize the weighted sum of the variables, encouraging sparsity or deviation from the original.
NearOptimalAlternatives.DWV_update! — Method
DWV_update!(
model::JuMP.Model,
variables::AbstractArray{T,N},
weights::Vector{Float64}
) where {T<:Union{VariableRef,AffExpr},N}NearOptimalAlternatives.Dist_initial! — Method
Dist_initial!(
model::JuMP.Model,
variables::AbstractArray{T,N},
fixed_variables::Vector{VariableRef};
weights::Vector{Float64} = zeros(length(variables)),
metric::Distances.SemiMetric = SqEuclidean(),
) where {T<:Union{VariableRef,AffExpr},N}Initialize a JuMP model's objective to maximize the distance between the current solution and a reference solution, based on a specified metric.
This function is typically used in the context of generating diverse solutions (alternatives) to an optimization problem by first defining a distance-based objective that measures how different a new solution is from an existing (optimal) one.
Arguments
model::JuMP.Model: a JuMP model that has been previously solved.variables::AbstractArray{T,N}: the variables of the model to consider in the distance computation.fixed_variables::Vector{VariableRef}: a subset of all variables ofmodelthat are not allowed to be changed when seeking for alternatives.weights::Vector{Float64}: optional weights to influence the distance calculation (currently not used directly but reserved for extensions).metric::Distances.SemiMetric: the distance metric used to compute dissimilarity (default is squared Euclidean distance).
Behavior
- Extracts the current solution values of
variables. - Sets the model's objective to maximize the distance between the current variable values and the solution.
- Changes the model's objective sense to
Max.
NearOptimalAlternatives.Dist_update! — Method
Dist_update!(
model::JuMP.Model,
variables::AbstractArray{T,N};
weights::Vector{Float64} = zeros(length(variables)),
metric::Distances.SemiMetric = Cityblock(),
) where {T<:Union{VariableRef,AffExpr},N}Update a JuMP model's objective function to include an additional distance term for generating multiple diverse alternatives (as in Modeling to Generate Alternatives).
This function builds upon a previously defined objective by incrementally adding a distance term between the current solution and a new reference solution. It is typically used after Dist_initial! or a prior call to MGA_Dist_update!.
Arguments
model::JuMP.Model: the JuMP model being updated to generate further alternatives.variables::AbstractArray{T,N}: the variables to consider in the distance computation.weights::Vector{Float64}: optional weights for the distance metric (currently not directly used).metric::Distances.SemiMetric: the distance metric used to compute dissimilarity (default is Cityblock distance).
Behavior
- Evaluates the current objective function to retrieve the cumulative distance so far.
- Computes the distance between the current variable values and their previous optimal values.
- Updates the objective function to maximize the sum of the cumulative and new distances.
- Resets the model's objective sense to
Max.
NearOptimalAlternatives.HSJ_initial! — Method
HSJ_initial!(
model::JuMP.Model,
variables::AbstractArray{T,N},
fixed_variables::Vector{VariableRef};
weights::Vector{Float64} = zeros(length(variables)),
metric::Distances.SemiMetric = SqEuclidean(),
) where {T<:Union{VariableRef,AffExpr}, N}Initialize the objective of a JuMP model using the HSJ (Hop-Skip-Jump) method to generate alternative solutions.
This function sets a new objective that minimizes the weighted sum of the decision variables, where weights are based on the sign (non-zero value) of the original optimal solution. Fixed variables are locked at their optimal values.
Arguments
model::JuMP.Model: a solved JuMP model whose objective is to be redefined for alternative generation.variables::AbstractArray{T,N}: the variables involved in the objective, typically a vector or matrix ofVariableRefs orAffExprs.fixed_variables::Vector{VariableRef}: variables to be fixed at their current values to avoid changes in alternatives.weights::Vector{Float64}: optional vector of weights for each variable; will be internally overwritten based on variable values.metric::Distances.SemiMetric: unused in this method (included for consistency with other alternative generation methods).
Behavior
- Variables that are zero in the original solution receive weight 0; others receive weight 1.
- Fixed variables are frozen at their optimal values using
fix(...). - The objective is set to minimize the weighted sum of the variables, encouraging sparsity or deviation from the original.
NearOptimalAlternatives.HSJ_update! — Method
HSJ_update!(
model::JuMP.Model,
variables::AbstractArray{T,N};
weights::Vector{Float64} = zeros(length(variables)),
metric::Distances.SemiMetric = SqEuclidean(),
) where {T<:Union{VariableRef,AffExpr}, N}Update the objective of a JuMP model using the HSJ method to generate the next alternative solution.
This function redefines the objective based on the current optimal solution of the model, using weights determined by the sign of each variable's value (non-zero implies weight 1).
Arguments
model::JuMP.Model: the JuMP model to be updated.variables::AbstractArray{T,N}: the decision variables involved in the updated objective.weights::Vector{Float64}: optional vector of weights; will be overwritten based on current variable values.metric::Distances.SemiMetric: unused in this method (included for interface consistency).
Behavior
- Variables with a zero value receive weight 0; all others receive weight 1.
- A new objective is set: minimize the weighted sum of the variables.
- This function does not re-fix any variables; it is typically called iteratively after
HSJ_initial!.
NearOptimalAlternatives.Min_Max_Variables_initial! — Method
Min_Max_Variables_initial!(
model::JuMP.Model,
variables::AbstractArray{T,N},
fixed_variables::Vector{VariableRef};
weights::Vector{Float64} = zeros(length(variables)),
metric::Distances.SemiMetric = SqEuclidean(),
) where {T<:Union{VariableRef,AffExpr}, N}Initialize the objective of a JuMP model using the Min/Max Variables method to generate alternative solutions. This function sets a new objective that minimizes the weighted sum of the decision variables, where weights are randomized between -1, 0 and 1. Fixed variables are locked at their optimal values.
Arguments
model::JuMP.Model: a solved JuMP model whose objective is to be redefined for alternative generation.variables::AbstractArray{T,N}: the variables involved in the objective, typically a vector or matrix ofVariableRefs orAffExprs.fixed_variables::Vector{VariableRef}: variables to be fixed at their current values to avoid changes in alternatives.weights::Vector{Float64}: optional vector of weights for each variable; will be internally overwritten based on variable values.metric::Distances.SemiMetric: unused in this method (included for consistency with other alternative generation methods).
Behavior
- Variables are randomly minimized or maximized.
- Fixed variables are frozen at their optimal values using
fix(...). - The objective is set to minimize the weighted sum of the variables, encouraging sparsity or deviation from the original.
NearOptimalAlternatives.Min_Max_Variables_update! — Method
Min_Max_Variables_update!(
model::JuMP.Model,
variables::AbstractArray{T,N};
weights::Vector{Float64} = zeros(length(variables)),
metric::Distances.SemiMetric = SqEuclidean(),
) where {T<:Union{VariableRef,AffExpr}, N}Update the objective of a JuMP model using the Min/Max Variables method to generate the next alternative solution. Update the weights randomly between -1, 0 and 1.
Arguments
model::JuMP.Model: the JuMP model to be updated.variables::AbstractArray{T,N}: the decision variables involved in the updated objective.weights::Vector{Float64}: optional vector of weights; will be overwritten based on current variable values.metric::Distances.SemiMetric: unused in this method (included for interface consistency).
Behavior
- Variables are randomly minimized or maximized.
- A new objective is set: minimize the weighted sum of the variables.
- This function does not re-fix any variables; it is typically called iteratively after
Min_Max_Variables_initial!.
NearOptimalAlternatives.Random_Vector_initial! — Method
Random_Vector_initial!(
model::JuMP.Model,
variables::AbstractArray{T,N},
fixed_variables::Vector{VariableRef};
weights::Vector{Float64} = zeros(length(variables)),
metric::Distances.SemiMetric = SqEuclidean(),
) where {T<:Union{VariableRef,AffExpr}, N}Initialize the objective of a JuMP model using the Min/Max Variables method to generate alternative solutions. This function sets a new objective that minimizes the weighted sum of the decision variables, where weights are uniformly chosen between -1 and 1. Fixed variables are locked at their optimal values.
Arguments
model::JuMP.Model: a solved JuMP model whose objective is to be redefined for alternative generation.variables::AbstractArray{T,N}: the variables involved in the objective, typically a vector or matrix ofVariableRefs orAffExprs.fixed_variables::Vector{VariableRef}: variables to be fixed at their current values to avoid changes in alternatives.weights::Vector{Float64}: optional vector of weights for each variable; will be internally overwritten based on variable values.metric::Distances.SemiMetric: unused in this method (included for consistency with other alternative generation methods).
Behavior
- Variables are randomly minimized or maximized.
- Fixed variables are frozen at their optimal values using
fix(...). - The objective is set to minimize the weighted sum of the variables, encouraging sparsity or deviation from the original.
NearOptimalAlternatives.Random_Vector_update! — Method
Random_Vector_update!(
model::JuMP.Model,
variables::AbstractArray{T,N};
weights::Vector{Float64} = zeros(length(variables)),
metric::Distances.SemiMetric = SqEuclidean(),
) where {T<:Union{VariableRef,AffExpr}, N}Update the objective of a JuMP model using the Min/Max Variables method to generate the next alternative solution. Update the weights uniformly chosen between -1 and 1.
Arguments
model::JuMP.Model: the JuMP model to be updated.variables::AbstractArray{T,N}: the decision variables involved in the updated objective.weights::Vector{Float64}: optional vector of weights; will be overwritten based on current variable values.metric::Distances.SemiMetric: unused in this method (included for interface consistency).
Behavior
- Variables are randomly minimized or maximized.
- A new objective is set: minimize the weighted sum of the variables.
- This function does not re-fix any variables; it is typically called iteratively after
Random_Vector_initial!.
NearOptimalAlternatives.Spores_initial! — Method
Spores_initial!(
model::JuMP.Model,
variables::AbstractArray{T,N},
fixed_variables::Vector{VariableRef};
weights::Vector{Float64} = zeros(length(variables)),
metric::Distances.SemiMetric = SqEuclidean(),
) where {T<:Union{VariableRef,AffExpr}, N}Initialize the objective of a JuMP model using the Spores method to generate alternative solutions. This function sets a new objective that minimizes the weighted sum of the decision variables, where weights are based on the variable value of the original optimal solution. Fixed variables are locked at their optimal values. Every variable in variables must have a finite upper bound: weights are value(v) / upper_bound(v), so an unbounded variable has no reference capacity to score against and throws an ArgumentError (matching the corresponding hard error in Calliope's own relative_deployment SPORES scoring algorithm, rather than silently skipping it). A variable whose upper bound is exactly 0 is skipped instead, since its weight contribution is 0 regardless.
Arguments
model::JuMP.Model: a solved JuMP model whose objective is to be redefined for alternative generation.variables::AbstractArray{T,N}: the variables involved in the objective, typically a vector or matrix ofVariableRefs orAffExprs.fixed_variables::Vector{VariableRef}: variables to be fixed at their current values to avoid changes in alternatives.weights::Vector{Float64}: optional vector of weights for each variable; will be internally overwritten based on variable values.metric::Distances.SemiMetric: unused in this method (included for consistency with other alternative generation methods).
Behavior
- Variables are updated based on the potential value that they could have had.
- Fixed variables are frozen at their optimal values using
fix(...). - The objective is set to minimize the weighted sum of the variables, encouraging sparsity or deviation from the original.
NearOptimalAlternatives.Spores_update! — Method
Spores_update!(
model::JuMP.Model,
variables::AbstractArray{T,N};
weights::Vector{Float64} = zeros(length(variables)),
metric::Distances.SemiMetric = SqEuclidean(),
) where {T<:Union{VariableRef,AffExpr}, N}Update the objective of a JuMP model using the Spores method to generate the next alternative solution. This function redefines the objective based on the current optimal solution of the model, updating the weights with respect to the current variable values. Every variable in variables must have a finite upper bound, for the same reason as Spores_initial!: an unbounded variable throws an ArgumentError rather than being silently skipped.
Arguments
model::JuMP.Model: the JuMP model to be updated.variables::AbstractArray{T,N}: the decision variables involved in the updated objective.weights::Vector{Float64}: optional vector of weights; will be overwritten based on current variable values.metric::Distances.SemiMetric: unused in this method (included for interface consistency).
Behavior
- Variables with are updated based on the previously optimal solution.
- A new objective is set: minimize the weighted sum of the variables.
- This function does not re-fix any variables; it is typically called iteratively after
Spores_initial!.
NearOptimalAlternatives._alt_solve_diagnostics — Method
_alt_solve_diagnostics(model) -> NamedTupleRead-only per-solve diagnostics (solve time, simplex/barrier iteration counts, termination status) for the structured "alternative_solved" log record emitted by generate_alternatives_optimization!. Pure introspection - does not call optimize! or touch model/solver state. Each getter is individually guarded since not every solver/status exposes every attribute (e.g. an LP solved by an interior-point method with no crossover reports simplex_iterations = 0, not an error, but some solvers do not implement the iteration-count attributes at all).
NearOptimalAlternatives._apply_warm_start! — Method
_apply_warm_start!(all_vars, primal, cons, dual_vals)Load a previously captured solution into the model's MOI warm-start attributes: variable primal starts and constraint dual starts. Applied only right before an optimize!. Solvers that warm start (SCS and other first-order/ADMM methods, simplex) use these as the initial iterate; interior-point solvers ignore them. Each block is guarded so a solver that rejects a start attribute does not abort the sweep.
NearOptimalAlternatives._arclength_sample — Method
_arclength_sample(solve_and_record, gap, n_budget)Marching pseudo-arclength predictor–corrector over the budget parameter. solve_and_record(g) -> (ok, cost, diversity) solves at relative gap g and records the point on success. Solves the two endpoints [gap/n_budget, gap] to anchor and normalise the (cost, diversity) metric, then marches from the tight end: a finite-difference tangent predicts the next budget so each step advances a fixed target arclength Δs, and the exact LP solve is the corrector. A budget that fails to solve is stepped over (monotone step-size control), so the march always advances toward the full budget and terminates.
NearOptimalAlternatives._safe_duals — Method
Read constraint duals, returning an empty vector if no dual solution is available.
NearOptimalAlternatives._safe_solve_time — Method
Solver-reported solve time of the last optimize! (MOI.SolveTimeSec), returning NaN if the solver does not expose it.
NearOptimalAlternatives.add_solution! — Method
add_solution!(
problem::MetaheuristicProblem,
result::Metaheuristics.State,
metric::Distances.SemiMetric
)Modify a Metaheuristic problem representing the alternative generating problem for the original LP using a newly found alternative solution. This function can be used when one wants to iteratively run a metaheuristic to find alternative solutions one by one.
Arguments:
problem::MetaheuristicProblem: problem to be modified by adding a solution.result::Metaheuristics.State: result containing the optimal solution to add to the objective function.metric::Distances.SemiMetric: metric used to evaluate distance between alternatives.
NearOptimalAlternatives.create_alternative_generating_problem! — Method
create_alternative_generating_problem!(
model::JuMP.Model,
optimality_gap::Float64,
fixed_variables::Vector{VariableRef},
variables::AbstractArray{T,N},
weights::Vector{Float64};
method::Symbol = :HSJ,
metric::Distances.SemiMetric = SqEuclidean(),
) where {T<:Union{VariableRef,AffExpr},N}Transform a JuMP model into a model solving its corresponding modelling-for-generating-alternatives problem.
Arguments
model::JuMP.Model: a solved JuMP model for which alternatives are generated.optimality_gap::Float64: the maximum percentage deviation (>= 0) an alternative may have compared to the optimal solution.fixed_variables::Vector{VariableRef}=[]: a subset of all variables ofmodelthat are not allowed to be changed when seeking for alternatives.variables::AbstractArray{T,N}: the variables ofmodelfor which are considered when generating alternatives.weights::Vector{Float64}: a vector of weights used to update the objective function.method::Symbol = :HSJ: the method used to model the problem for generating alternatives.metric::Distances.SemiMetric = SqEuclidean(): the metric used to maximise the difference between alternatives and the optimal solution.
NearOptimalAlternatives.create_alternative_generating_problem — Method
problem = create_alternative_generating_problem(
model::JuMP.Model,
algorithm::Metaheuristics.Algorithm,
initial_solution::OrderedDict{VariableRef, Float64},
optimality_gap::Float64,
metric::Distances.SemiMetric,
fixed_variables::Dict{VariableRef, Float64}
)Create the Metaheuristic problem representing the alternative generating problem for the original LP.
Arguments:
model::JuMP.Model: JuMP model representing the original LP.algorithm::Metaheuristics.Algorithm: Metaheuristic algorithm to solve the alternative generating problem.initial_solution::OrderedDict{VariableRef, Float64}: (near-)optimal solution tomodel, for which alternatives are sought.optimality_gap::Float64: maximum gap in objective value betweeninitial_solutionand alternative solutions.metric::Distances.SemiMetric: distance metric used to compute distance between alternative solutions andinitial_solution.fixed_variables::Dict{MOI.VariableIndex, Float64}: solution values for fixed variables of the original problem.
NearOptimalAlternatives.create_objective — Method
objective = create_objective(
model::JuMP.Model,
solution::OrderedDict{JuMP.VariableRef, Float64},
optimality_gap::Float64,
metric::Distances.SemiMetric,
index_map::Dict{Int64, Int64},
fixed_variables::Dict{VariableRef, Float64}
)Create an objective function supported by Metaheuristics.jl for the alternative generating problem.
Arguments
model::JuMP.Model: solved JuMP model of the original lp problem.solution::OrderedDict{JuMP.VariableRef, Float64}: solution value of the original lp problem excluding fixed variables.optimality_gap::Float64: maximum difference between objective value of optimal solution and alternative solutions.metric::Distances.SemiMetric: distance metric used to measure distance between solutions.index_map::Dict{Int64, Int64}: dictionary mapping indices in the JuMP/MathOptInterface model to indices ofx.fixed_variables::Dict{VariableRef, Float64}: dictionary containing the values of the fixed variables.
NearOptimalAlternatives.extract_bounds — Method
bounds = extract_bounds(
model::JuMP.Model,
index_map::Dict{Int64, Int64}
)Transform the bounds from a JuMP Model into a matrix of bounds readable by Metaheuristics.jl.
Arguments
model::JuMP.Model: solved JuMP model of the original lp problem.index_map::Dict{Int64, Int64}: dictionary mapping indices in the JuMP/MathOptInterface model to indices ofx.
NearOptimalAlternatives.extract_constraint — Method
constraint = extract_constraint(
constraint::MOI.ConstraintFunction,
x::Vector{Float64},
index_map::Dict{Int64, Int64},
fixed_variables::Dict{MOI.VariableIndex, Float64}
)Convert a constraint from a MathOptInterface function into a julia function of x. Supports only ScalarAffineFunction and VariableIndex constraints.
Arguments
constraint::MOI.ConstraintFunction: constraint transform into a julia function.x::Vector{Float64}: a vector representing an individual in the metaheuristic population.index_map::Dict{Int64, Int64}: a dictionary mapping indices in the MathOptInterface model to indices ofx.fixed_variables::Dict{MOI.VariableIndex, Float64}: a dictionary containing the values of the fixed variables.
NearOptimalAlternatives.extract_objective — Method
objective = extract_objective(
objective::JuMP.AffExpr,
x::Vector{Float64},
index_map::Dict{Int64, Int64},
fixed_variables::Dict{MOI.VariableIndex, Float64}
)Convert the objective from a MathOptInterface function into a julia function of x. Supports only linear single-objective functions.
Arguments
objective::JuMP.AffExpr: the objective function to transform into a julia function.x::Vector{Float64}: a vector representing an individual in the metaheuristic population.index_map::Dict{Int64, Int64}: a dictionary mapping indices in the MathOptInterface model to indices ofx.fixed_variables::Dict{MOI.VariableIndex, Float64}: a dictionary containing the values of the fixed variables.
NearOptimalAlternatives.generate_alternatives_arclength! — Method
result = generate_alternatives_arclength!(
model::JuMP.Model,
optimality_gap::Float64,
variables::AbstractArray{T,N},
n_directions::Int64;
n_budget::Int64 = 6,
weights = zeros(length(variables)),
modeling_method::Symbol = :Spores,
metric::Distances.SemiMetric = SqEuclidean(),
fixed_variables::Vector{VariableRef} = VariableRef[],
) where {T<:Union{VariableRef,AffExpr},N}Like generate_alternatives_sweep!, but distributes the n_budget points along each direction's near-optimal front by arclength rather than by uniform budget steps. Uniform-budget sampling clusters points where the front is flat (extra budget stops buying diversity — redundant) and under-samples the steep "knee"; arclength spacing places points evenly along the trade-off curve, which is the MGA-relevant notion of a well-distributed front.
Arclength control follows the pseudo-arclength predictor–corrector scheme (Keller 1977; Allgower & Georg 1990), specialised to the parametric-LP front: the two budget endpoints are solved to anchor and normalise the (cost, diversity) metric, then the curve is marched monotonically from the tight budget to the full budget. At each step a finite-difference tangent (normalised arclength travelled per unit budget over the last successful step) is used as the predictor to choose the next budget so the step advances a fixed target arclength Δs; the corrector is the exact LP solve at that budget (its optimum lies on the front). The step is reduced/marched past a budget that fails to solve (the standard continuation step-control), so a pathological budget is stepped over rather than aborting the run. This equidistributes points along the trade-off curve — sparse on the flat plateau (tangent flattens ⇒ larger budget steps), dense at the knee — instead of clustering by uniform budget.
(A finite-difference tangent is used because the only solver viable at this scale, barrier, exposes no basis sensitivity; with a simplex corrector the tangent could come from exact LP sensitivity dx/dB.)
Arguments
Same as generate_alternatives_sweep!; n_budget >= 2 (the two endpoints are always solved first). By default no warm starting is used — arclength solves budgets out of order, so there is no monotone previous iterate to seed from.
reconfigure_solver!::Union{Nothing,Function}=nothing: an optionalmodel -> restore!callback, applied once per direction, immediately after that direction's first point succeeds — the same one-shot idea asgenerate_alternatives_optimization!'sreconfigure_solver!, but fired per direction instead of once for the whole call. Use it to switch the solver's algorithm (e.g. barrier to dual simplex) so the rest of that direction's (out-of-order) points warm-start off the resulting basis. It should return eithernothing, or amodel -> nothingclosure that restores the original config; that closure is applied at the start of the next direction, right afterupdate_objective_function!.
Result
An AlternativeSolutions; each tags entry records (direction, budget_idx, budget_level, gap, diversity_objective, solve_time), where budget_idx is the placement order within the direction. Plot diversity_objective against objective_values to see the arclength-distributed front.
NearOptimalAlternatives.generate_alternatives_metaheuristics — Method
result = generate_alternatives_metaheuristics(
model::JuMP.Model,
optimality_gap::Float64,
n_alternatives::Int64,
metaheuristic_algorithm::Metaheuristics.Algorithm;
metric::Distances.Metric = SqEuclidean(),
selected_variables::Vector{VariableRef} = []
)Generate n_alternatives solutions to model which are as distant from the optimum and each other, but with a maximum optimality_gap, using a metaheuristic algorithm.
Arguments
model::JuMP.Model: a solved JuMP model for which alternatives are generated.optimality_gap::Float64: the maximum percentage deviation (>=0) an alternative may have compared to the optimal solution.n_alternatives: the number of alternative solutions sought.metaheuristic_algorithm::Metaheuristics.Algorithm: algorithm used to search for alternative solutions.metric::Distances.Metric=SqEuclidean(): the metric used to maximise the difference between alternatives and the optimal solution.fixed_variables::Vector{VariableRef}=[]: a subset of all variables ofmodelthat are not allowed to be changed when seeking for alternatives.
NearOptimalAlternatives.generate_alternatives_optimization! — Method
results = generatealternativesoptimization!( model::JuMP.Model, optimalitygap::Float64, variables::AbstractArray{T,N}, nalternatives::Int64; modelingmethod::Symbol = :MaxDistance, metric::Distances.SemiMetric = SqEuclidean(), fixedvariables::Vector{VariableRef} = VariableRef[], reconfiguresolver!::Union{Nothing,Function} = nothing, ) where {T<:Union{VariableRef,AffExpr},N} Generate n_alternatives solutions to model which are as distant from the optimum and each other, but with a maximum optimality_gap, using optimization.
Arguments
model::JuMP.Model: a solved JuMP model for which alternatives are generated.optimality_gap::Float64: the maximum percentage deviation (>=0) an alternative may have compared to the optimal solution.- variables::AbstractArray{T,N}: the variables of
modelfor which are considered when generating alternatives. n_alternatives: the number of alternative solutions sought.modeling_method::Symbol = :Max_Distance: the method used to model the problem for generating alternatives.metric::Distances.Metric=SqEuclidean(): the metric used to maximise the difference between alternatives and the optimal solution.fixed_variables::Vector{VariableRef}=[]: a subset of all variables ofmodelthat are not allowed to be changed when seeking for alternatives.reconfigure_solver!::Union{Nothing,Function}=nothing: an optionalmodel -> nothingcallback invoked once, after alternative #1 is found and before alternative #2 is sought. Use it to switch the attached solver's algorithm (e.g. from barrier to primal simplex) so that alternatives #2.. are warm-started from alternative #1's basis instead of solved cold each time - the feasible region is fixed from here on (only the objective changes between iterations), which is the favourable case for a primal-simplex warm start. The callback should only set solver attributes (e.g.model -> set_optimizer_attribute(model, "Method", 0)for Gurobi); this function handles the mandatory re-solve that JuMP requires after any attribute change. Ignored whenn_alternatives == 1(no further solves would use it).
NearOptimalAlternatives.generate_alternatives_sweep! — Method
result = generate_alternatives_sweep!(
model::JuMP.Model,
optimality_gap::Float64,
variables::AbstractArray{T,N},
n_directions::Int64;
n_budget::Int64 = 6,
weights = zeros(length(variables)),
modeling_method::Symbol = :Spores,
metric::Distances.SemiMetric = SqEuclidean(),
fixed_variables::Vector{VariableRef} = VariableRef[],
) where {T<:Union{VariableRef,AffExpr},N}Generate near-optimal alternatives by sweeping the cost budget along each search direction, rather than returning a single point per direction.
This reuses the existing modelling-for-generating-alternatives machinery (create_alternative_generating_problem! / update_objective_function!): any modeling_method that discovers a direction (:Spores, :HSJ, :Max_Distance, ...) is supported unchanged. For each of the n_directions discovered directions, the objective (the direction) is held fixed while the original_objective budget constraint is re-solved at n_budget cost levels spanning [C*, (1 + optimality_gap)·C*], collecting every intermediate optimum. The result is a dense near-optimal front per direction instead of just its boundary endpoint.
Each direction's sweep ends at the full budget, so the model is left exactly where the classic single-budget generator would leave it — direction-discovery methods that accumulate from the previous solution (e.g. SPORES, HSJ) advance identically to generate_alternatives_optimization!.
Arguments
model::JuMP.Model: a solved JuMP model for which alternatives are generated.optimality_gap::Float64: the maximum relative deviation (>= 0) defining the near-optimal cost budget, i.e. the top of the sweep range.variables::AbstractArray{T,N}: the variables carrying the MGA direction.n_directions::Int64: the number of search directions to discover and sweep.n_budget::Int64 = 6: the number of cost levels per direction (>= 1). The levels areC*(1 + g_i)forg_i = optimality_gap · i / n_budget,i = 1…n_budget: evenly spaced strictly inside the near-optimal region, with the last level at the full budget. The cost-optimal faceC*itself is never sampled — atC*the only near-optimal point isx*(zero diversity, which MGA never seeks), and that face also has empty interior and breaks interior-point solvers run without crossover.weights: optional initial direction weights (accumulated in place).modeling_method::Symbol = :Spores: the direction-discovery method, any key ofMETHOD_DISPATCH_INITIAL/METHOD_DISPATCH_UPDATE.metric::Distances.SemiMetric = SqEuclidean(): metric forwarded to the method.fixed_variables::Vector{VariableRef} = []: variables frozen at their optimum.warm_start::Bool = false: whentrue, seed each solve from the previous optimum (variable primal starts + constraint dual starts, via MOI). Successive budget levels differ only in the budget RHS, so the previous solution is an excellent start for solvers that exploit it (SCS and other first-order/ADMM methods, simplex). Leave itfalsefor interior-point solvers that ignore or dislike start values (e.g. Gurobi/HiGHS barrier) — the default keeps their behaviour unchanged.
As a failsafe, any budget whose solve does not reach a feasible optimum (e.g. an interior-point numerical error on a tight budget) is logged and skipped, so one bad solve cannot abort the whole sweep — the result simply contains fewer points.
Result
An AlternativeSolutions holding n_directions * n_budget points. Each point's tags entry records (direction = k, budget_idx = b, budget_level = L, diversity_objective = d, solve_time = t), where d is the optimal diversity-objective value at that budget and t is the solver-reported solve time for that single solve; and objective_values[i] is the realised original-objective cost of point i. Plotting diversity_objective against objective_values traces the near-optimal front for each direction.
NearOptimalAlternatives.is_better_psoga — Method
isbetterpsoga( A::T, B::T, centroids::Vector{Any}, subpopa::Int64, subpopb::Int64, maximisetotal::Bool ) where {T <: Metaheuristics.xFghsolution}
Compare two solutions of the PSOGA algorithm with respect to their distance to the optimal solution and other alternatives.
Arguments
A: solution in PSOGA to be compared.B: solution in PSOGA to be compared.centroids::Vector{Any}: vector of centroids per subpopulation. A centroid is the average point of all solutions in a subpopulation.subpop::Int64: index of the subpopulation solution A and B are in. Note that they are always in the same, since we only compare within subpopulations or with themselves.maximise_total::Bool: if true, we maximise the sum of distances between a point and all centroids of other subpopulations, else we maximise the minimum distance between a point and the centroids of other subpopulations.
NearOptimalAlternatives.run_alternative_generating_problem! — Method
result = run_alternative_generating_problem!(
problem::MetaheuristicProblem
)Optimize the problem using the specified metaheuristic algorithm and return the result.
NearOptimalAlternatives.update_objective_function! — Method
update_objective_function!(
model::JuMP.Model,
variables::AbstractArray{T,N};
weights::Vector{Float64} = zeros(length(variables)),
method::Symbol = :HSJ,
metric::Distances.SemiMetric = SqEuclidean()) where {T<:Union{VariableRef,AffExpr},N}
)Add a previously found solution to a modelling-for-generating-alternatives problem. Used for iteratively finding multiple alternative solutions.
Arguments
model::JuMP.Model: a solved JuMP model for which alternatives are generated.variables::AbstractArray{T,N}: the variables ofmodelfor which are considered when generating alternatives.weights::Vector{Float64}: a vector of weights used to update the objective function.method::Symbol = :HSJ: the method used to model the problem for generating alternatives.metric::Distances.SemiMetric = SqEuclidean(): the metric used to maximise the difference between alternatives and the optimal solution.
NearOptimalAlternatives.update_solutions! — Method
update_solutions!(results::AlternativeSolutions, model::JuMP.Model)Update the set of results AlternativeSolutions with the variable values obtained when solving the JuMP model model.
NearOptimalAlternatives.update_solutions! — Method
update_solutions!(results::AlternativeSolutions, model::JuMP.Model)Update the set of results AlternativeSolutions with the variable values obtained when solving using Metaheuristics.
Arguments
results::AlternativeSolutions: set of solutions to add a new solution to.state::Metaheuristics.State: contains results to metaheuristic solve.initial:solution::OrderedDict{VariableRef, Float64}: used to identify the indices of the metaheuristic solution in the JuMP model.fixed_variables::Dict{MOI.VariableIndex, Float64}: set of fixed variables and their solution values.model::JuMP.Model: original model for which alternative solutions are found.
NearOptimalAlternatives.update_solutions! — Method
update_solutions!(results::AlternativeSolutions, model::JuMP.Model)Update the set of results AlternativeSolutions with the variable values obtained when solving using PSOGA.
Arguments
results::AlternativeSolutions: set of solutions to add a new solution to.state::Metaheuristics.State: contains results to metaheuristic solve.subBest::Vector{Any}: contains the best results per subpopulation of PSOGA, which are the actual results of solving.initial:solution::OrderedDict{VariableRef, Float64}: used to identify the indices of the metaheuristic solution in the JuMP model.fixed_variables::Dict{MOI.VariableIndex, Float64}: set of fixed variables and their solution values.model::JuMP.Model: original model for which alternative solutions are found.