Reference

Contents

Index

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.

source
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!.

source
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 size
  • N_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.
source
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.

source
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.

source
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.

source
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 of VariableRefs or AffExprs.
  • 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.
source
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 of model that 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.
source
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.
source
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 of VariableRefs or AffExprs.
  • 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.
source
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!.
source
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 of VariableRefs or AffExprs.
  • 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.
source
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!.
source
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 of VariableRefs or AffExprs.
  • 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.
source
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!.
source
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 of VariableRefs or AffExprs.
  • 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.
source
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!.
source
NearOptimalAlternatives._alt_solve_diagnostics — Method
_alt_solve_diagnostics(model) -> NamedTuple

Read-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).

source
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.

source
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.

source
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.
source
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 of model that are not allowed to be changed when seeking for alternatives.
  • variables::AbstractArray{T,N}: the variables of model for 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.
source
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 to model, for which alternatives are sought.
  • optimality_gap::Float64: maximum gap in objective value between initial_solution and alternative solutions.
  • metric::Distances.SemiMetric: distance metric used to compute distance between alternative solutions and initial_solution.
  • fixed_variables::Dict{MOI.VariableIndex, Float64}: solution values for fixed variables of the original problem.
source
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 of x.
  • fixed_variables::Dict{VariableRef, Float64}: dictionary containing the values of the fixed variables.
source
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 of x.
source
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 of x.
  • fixed_variables::Dict{MOI.VariableIndex, Float64}: a dictionary containing the values of the fixed variables.
source
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 of x.
  • fixed_variables::Dict{MOI.VariableIndex, Float64}: a dictionary containing the values of the fixed variables.
source
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 optional model -> restore! callback, applied once per direction, immediately after that direction's first point succeeds — the same one-shot idea as generate_alternatives_optimization!'s reconfigure_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 either nothing, or a model -> nothing closure that restores the original config; that closure is applied at the start of the next direction, right after update_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.

source
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 of model that are not allowed to be changed when seeking for alternatives.
source
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 model for 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 of model that are not allowed to be changed when seeking for alternatives.
  • reconfigure_solver!::Union{Nothing,Function}=nothing: an optional model -> nothing callback 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 when n_alternatives == 1 (no further solves would use it).
source
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 are C*(1 + g_i) for g_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 face C* itself is never sampled — at C* the only near-optimal point is x* (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 of METHOD_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: when true, 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 it false for 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.

source
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.
source
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 of model for 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.
source
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.

source
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.
source
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.
source