PH: Rename vars, remove return value

pull/32/head
Alinson S. Xavier 2 years ago
parent 3961aedaf5
commit 03bf1c4c04
Signed by: isoron
GPG Key ID: 0DA8E4B9E1109DCA

@ -6,106 +6,95 @@ using TimerOutputs
import JuMP import JuMP
const to = TimerOutput() const to = TimerOutput()
function optimize!(model::JuMP.Model, method::ProgressiveHedging)::FinalResult function optimize!(model::JuMP.Model, method::ProgressiveHedging)::Nothing
mpi = MpiInfo(MPI.COMM_WORLD) mpi = MpiInfo(MPI.COMM_WORLD)
iterations = Array{IterationInfo,1}(undef, 0) iterations = PHIterationInfo[]
if method.consensus_vars === nothing consensus_vars = [var for var in all_variables(model) if is_binary(var)]
method.consensus_vars = nvars = length(consensus_vars)
[var for var in all_variables(model) if is_binary(var)] weights = ones(nvars)
if method.initial_weights !== nothing
weights = copy(method.initial_weights)
end end
nvars = length(method.consensus_vars) target = zeros(nvars)
if method.weights === nothing if method.initial_target !== nothing
method.weights = [1.0 for _ in 1:nvars] target = copy(method.initial_target)
end end
if method.initial_global_consensus_vals === nothing params = PHSubProblemParams(
method.initial_global_consensus_vals = [0.0 for _ in 1:nvars]
end
ph_sp_params = SpParams(
ρ = method.ρ, ρ = method.ρ,
λ = [method.λ_default for _ in 1:nvars], λ = [method.λ for _ in 1:nvars],
global_consensus_vals = method.initial_global_consensus_vals, target = target,
) )
ph_subproblem = sp = PHSubProblem(model, model[:obj], consensus_vars, weights)
SubProblem(model, model[:obj], method.consensus_vars, method.weights)
set_optimizer_attribute(model, "Threads", method.num_of_threads)
while true while true
it_time = @elapsed begin iteration_time = @elapsed begin
solution = solve_subproblem(ph_subproblem, ph_sp_params) solution = solve_subproblem(sp, params, method.inner_method)
MPI.Barrier(mpi.comm) MPI.Barrier(mpi.comm)
global_obj = compute_global_objective(mpi, solution) global_obj = compute_global_objective(mpi, solution)
global_consensus_vals = compute_global_consensus(mpi, solution) target = compute_target(mpi, solution)
update_λ_and_residuals!( update_λ_and_residuals!(solution, params, target)
solution,
ph_sp_params,
global_consensus_vals,
)
global_infeas = compute_global_infeasibility(solution, mpi) global_infeas = compute_global_infeasibility(solution, mpi)
global_residual = compute_global_residual(mpi, solution) global_residual = compute_global_residual(mpi, solution)
if has_numerical_issues(global_consensus_vals) if has_numerical_issues(target)
break break
end end
end end
total_elapsed_time = compute_total_elapsed_time(it_time, iterations) total_elapsed_time =
it = IterationInfo( compute_total_elapsed_time(iteration_time, iterations)
it_num = length(iterations) + 1, current_iteration = PHIterationInfo(
sp_consensus_vals = solution.consensus_vals, global_infeas = global_infeas,
global_consensus_vals = global_consensus_vals,
sp_obj = solution.obj,
global_obj = global_obj, global_obj = global_obj,
it_time = it_time,
total_elapsed_time = total_elapsed_time,
global_residual = global_residual, global_residual = global_residual,
global_infeas = global_infeas, iteration_number = length(iterations) + 1,
iteration_time = iteration_time,
sp_vals = solution.vals,
sp_obj = solution.obj,
target = target,
total_elapsed_time = total_elapsed_time,
) )
iterations = [iterations; it] push!(iterations, current_iteration)
print_progress(mpi, it, method.print_interval) print_progress(mpi, current_iteration, method.print_interval)
if should_stop(mpi, iterations, method.termination_criteria) if should_stop(mpi, iterations, method.termination)
break break
end end
end end
return
return FinalResult(
last(iterations).global_obj,
last(iterations).sp_consensus_vals,
last(iterations).global_infeas,
last(iterations).it_num,
last(iterations).total_elapsed_time,
)
end end
function compute_total_elapsed_time( function compute_total_elapsed_time(
it_time::Float64, iteration_time::Float64,
iterations::Array{IterationInfo,1}, iterations::Array{PHIterationInfo,1},
)::Float64 )::Float64
length(iterations) > 0 ? length(iterations) > 0 ?
current_total_time = last(iterations).total_elapsed_time : current_total_time = last(iterations).total_elapsed_time :
current_total_time = 0 current_total_time = 0
return current_total_time + it_time return current_total_time + iteration_time
end end
function compute_global_objective(mpi::MpiInfo, s::SpSolution)::Float64 function compute_global_objective(
mpi::MpiInfo,
s::PhSubProblemSolution,
)::Float64
global_obj = MPI.Allreduce(s.obj, MPI.SUM, mpi.comm) global_obj = MPI.Allreduce(s.obj, MPI.SUM, mpi.comm)
global_obj /= mpi.nprocs global_obj /= mpi.nprocs
return global_obj return global_obj
end end
function compute_global_consensus(mpi::MpiInfo, s::SpSolution)::Array{Float64,1} function compute_target(mpi::MpiInfo, s::PhSubProblemSolution)::Array{Float64,1}
sp_consensus_vals = s.consensus_vals sp_vals = s.vals
global_consensus_vals = MPI.Allreduce(sp_consensus_vals, MPI.SUM, mpi.comm) target = MPI.Allreduce(sp_vals, MPI.SUM, mpi.comm)
global_consensus_vals = global_consensus_vals / mpi.nprocs target = target / mpi.nprocs
return global_consensus_vals return target
end end
function compute_global_residual(mpi::MpiInfo, s::SpSolution)::Float64 function compute_global_residual(mpi::MpiInfo, s::PhSubProblemSolution)::Float64
n_vars = length(s.consensus_vals) n_vars = length(s.vals)
local_residual_sum = abs.(s.residuals) local_residual_sum = abs.(s.residuals)
global_residual_sum = MPI.Allreduce(local_residual_sum, MPI.SUM, mpi.comm) global_residual_sum = MPI.Allreduce(local_residual_sum, MPI.SUM, mpi.comm)
return sum(global_residual_sum) / n_vars return sum(global_residual_sum) / n_vars
end end
function compute_global_infeasibility( function compute_global_infeasibility(
solution::SpSolution, solution::PhSubProblemSolution,
mpi::MpiInfo, mpi::MpiInfo,
)::Float64 )::Float64
local_infeasibility = norm(solution.residuals) local_infeasibility = norm(solution.residuals)
@ -113,9 +102,13 @@ function compute_global_infeasibility(
return global_infeas return global_infeas
end end
function solve_subproblem(sp::SubProblem, ph_sp_params::SpParams)::SpSolution function solve_subproblem(
sp::PHSubProblem,
params::PHSubProblemParams,
method::SolutionMethod,
)::PhSubProblemSolution
G = length(sp.consensus_vars) G = length(sp.consensus_vars)
if norm(ph_sp_params.λ) < 1e-3 if norm(params.λ) < 1e-3
@objective(sp.mip, Min, sp.obj) @objective(sp.mip, Min, sp.obj)
else else
@objective( @objective(
@ -124,40 +117,31 @@ function solve_subproblem(sp::SubProblem, ph_sp_params::SpParams)::SpSolution
sp.obj + sp.obj +
sum( sum(
sp.weights[g] * sp.weights[g] *
ph_sp_params.λ[g] * params.λ[g] *
(sp.consensus_vars[g] - ph_sp_params.global_consensus_vals[g]) (sp.consensus_vars[g] - params.target[g]) for g in 1:G
for g in 1:G
) + ) +
(ph_sp_params.ρ / 2) * sum( (params.ρ / 2) * sum(
sp.weights[g] * sp.weights[g] * (sp.consensus_vars[g] - params.target[g])^2 for
( g in 1:G
sp.consensus_vars[g] -
ph_sp_params.global_consensus_vals[g]
)^2 for g in 1:G
) )
) )
end end
optimize!(sp.mip, XavQiuWanThi2019.Method()) optimize!(sp.mip, method)
obj = objective_value(sp.mip) obj = objective_value(sp.mip)
sp_consensus_vals = value.(sp.consensus_vars) sp_vals = value.(sp.consensus_vars)
return SpSolution( return PhSubProblemSolution(obj = obj, vals = sp_vals, residuals = zeros(G))
obj = obj,
consensus_vals = sp_consensus_vals,
residuals = zeros(G),
)
end end
function update_λ_and_residuals!( function update_λ_and_residuals!(
solution::SpSolution, solution::PhSubProblemSolution,
ph_sp_params::SpParams, params::PHSubProblemParams,
global_consensus_vals::Array{Float64,1}, target::Array{Float64,1},
)::Nothing )::Nothing
n_vars = length(solution.consensus_vals) n_vars = length(solution.vals)
ph_sp_params.global_consensus_vals = global_consensus_vals params.target = target
for n in 1:n_vars for n in 1:n_vars
solution.residuals[n] = solution.residuals[n] = solution.vals[n] - params.target[n]
solution.consensus_vals[n] - ph_sp_params.global_consensus_vals[n] params.λ[n] += params.ρ * solution.residuals[n]
ph_sp_params.λ[n] += ph_sp_params.ρ * solution.residuals[n]
end end
end end
@ -179,22 +163,22 @@ end
function print_progress( function print_progress(
mpi::MpiInfo, mpi::MpiInfo,
iteration::IterationInfo, iteration::PHIterationInfo,
print_interval, print_interval,
)::Nothing )::Nothing
if !mpi.root if !mpi.root
return return
end end
if iteration.it_num % print_interval != 0 if iteration.iteration_number % print_interval != 0
return return
end end
@info @sprintf( @info @sprintf(
"Current iteration %8d %20.6e %20.6e %12.2f %% %8.2f %8.2f", "%8d %20.6e %20.6e %12.2f %% %8.2f %8.2f",
iteration.it_num, iteration.iteration_number,
iteration.global_obj, iteration.global_obj,
iteration.global_infeas, iteration.global_infeas,
iteration.global_residual * 100, iteration.global_residual * 100,
iteration.it_time, iteration.iteration_time,
iteration.total_elapsed_time iteration.total_elapsed_time
) )
end end
@ -209,21 +193,21 @@ end
function should_stop( function should_stop(
mpi::MpiInfo, mpi::MpiInfo,
iterations::Array{IterationInfo,1}, iterations::Array{PHIterationInfo,1},
criteria::TerminationCriteria, termination::PHTermination,
)::Bool )::Bool
if length(iterations) >= criteria.max_iterations if length(iterations) >= termination.max_iterations
if mpi.root if mpi.root
@info "Iteration limit reached. Stopping." @info "Iteration limit reached. Stopping."
end end
return true return true
end end
if length(iterations) < criteria.min_iterations if length(iterations) < termination.min_iterations
return false return false
end end
if last(iterations).total_elapsed_time > criteria.max_time if last(iterations).total_elapsed_time > termination.max_time
if mpi.root if mpi.root
@info "Time limit reached. Stopping." @info "Time limit reached. Stopping."
end end
@ -233,9 +217,9 @@ function should_stop(
curr_it = last(iterations) curr_it = last(iterations)
prev_it = iterations[length(iterations)-1] prev_it = iterations[length(iterations)-1]
if curr_it.global_infeas < criteria.min_feasibility if curr_it.global_infeas < termination.min_feasibility
obj_change = abs(prev_it.global_obj - curr_it.global_obj) obj_change = abs(prev_it.global_obj - curr_it.global_obj)
if obj_change < criteria.min_improvement if obj_change < termination.min_improvement
if mpi.root if mpi.root
@info "Feasibility limit reached. Stopping." @info "Feasibility limit reached. Stopping."
end end

@ -4,81 +4,34 @@
using JuMP, MPI, TimerOutputs using JuMP, MPI, TimerOutputs
mutable struct TerminationCriteria Base.@kwdef mutable struct PHTermination
max_iterations::Int max_iterations::Int = 1000
max_time::Float64 max_time::Float64 = 14400.0
min_feasibility::Float64 min_feasibility::Float64 = 1e-3
min_improvement::Float64 min_improvement::Float64 = 1e-3
min_iterations::Int min_iterations::Int = 2
function TerminationCriteria(;
max_iterations::Int = 1000,
max_time::Float64 = 14400.0,
min_feasibility::Float64 = 1e-3,
min_improvement::Float64 = 1e-3,
min_iterations::Int = 2,
)
return new(
max_iterations,
max_time,
min_feasibility,
min_improvement,
min_iterations,
)
end
end end
Base.@kwdef mutable struct IterationInfo Base.@kwdef mutable struct PHIterationInfo
it_num::Int global_infeas::Float64
sp_consensus_vals::Array{Float64,1}
global_consensus_vals::Array{Float64,1}
sp_obj::Float64
global_obj::Float64 global_obj::Float64
it_time::Float64
total_elapsed_time::Float64
global_residual::Float64 global_residual::Float64
global_infeas::Float64 iteration_number::Int
end iteration_time::Float64
sp_vals::Array{Float64,1}
mutable struct ProgressiveHedging <: SolutionMethod sp_obj::Float64
consensus_vars::Union{Array{VariableRef,1},Nothing} target::Array{Float64,1}
weights::Union{Array{Float64,1},Nothing} total_elapsed_time::Float64
initial_global_consensus_vals::Union{Array{Float64,1},Nothing}
num_of_threads::Int
ρ::Float64
λ_default::Float64
print_interval::Int
termination_criteria::TerminationCriteria
function ProgressiveHedging(;
consensus_vars::Union{Array{VariableRef,1},Nothing} = nothing,
weights::Union{Array{Float64,1},Nothing} = nothing,
initial_global_consensus_vals::Union{Array{Float64,1},Nothing} = nothing,
num_of_threads::Int = 1,
ρ::Float64 = 1.0,
λ_default::Float64 = 0.0,
print_interval::Int = 1,
termination_criteria::TerminationCriteria = TerminationCriteria(),
)
return new(
consensus_vars,
weights,
initial_global_consensus_vals,
num_of_threads,
ρ,
λ_default,
print_interval,
termination_criteria,
)
end
end end
struct FinalResult Base.@kwdef mutable struct ProgressiveHedging <: SolutionMethod
obj::Float64 initial_weights::Union{Vector{Float64},Nothing} = nothing
vals::Any initial_target::Union{Vector{Float64},Nothing} = nothing
infeasibility::Float64 ρ::Float64 = 1.0
total_iteration_num::Int λ::Float64 = 0.0
wallclock_time::Float64 print_interval::Int = 1
termination::PHTermination = PHTermination()
inner_method::SolutionMethod = XavQiuWanThi2019.Method()
end end
struct SpResult struct SpResult
@ -86,23 +39,23 @@ struct SpResult
vals::Array{Float64,1} vals::Array{Float64,1}
end end
Base.@kwdef mutable struct SubProblem Base.@kwdef mutable struct PHSubProblem
mip::JuMP.Model mip::JuMP.Model
obj::AffExpr obj::AffExpr
consensus_vars::Array{VariableRef,1} consensus_vars::Array{VariableRef,1}
weights::Array{Float64,1} weights::Array{Float64,1}
end end
Base.@kwdef struct SpSolution Base.@kwdef struct PhSubProblemSolution
obj::Float64 obj::Float64
consensus_vals::Array{Float64,1} vals::Array{Float64,1}
residuals::Array{Float64,1} residuals::Array{Float64,1}
end end
Base.@kwdef mutable struct SpParams Base.@kwdef mutable struct PHSubProblemParams
ρ::Float64 ρ::Float64
λ::Array{Float64,1} λ::Array{Float64,1}
global_consensus_vals::Array{Float64,1} target::Array{Float64,1}
end end
struct MpiInfo struct MpiInfo
@ -118,9 +71,3 @@ struct MpiInfo
return new(comm, rank, is_root, nprocs) return new(comm, rank, is_root, nprocs)
end end
end end
Base.@kwdef struct Callbacks
before_solve_subproblem::Any
after_solve_subproblem::Any
after_iteration::Any
end

@ -1,4 +1,4 @@
using Cbc using HiGHS
using MPI using MPI
using JuMP using JuMP
using UnitCommitment using UnitCommitment
@ -9,29 +9,32 @@ function fixture(path::String)::String
return "$basedir/../../../../fixtures/$path" return "$basedir/../../../../fixtures/$path"
end end
# 1. Initialize MPI # Initialize MPI
MPI.Init() MPI.Init()
# 2. Configure progressive hedging method # Configure progressive hedging method
ph = UnitCommitment.ProgressiveHedging() ph = UnitCommitment.ProgressiveHedging()
# 3. Read problem instance # Read problem instance
instance = UnitCommitment.read( instance = UnitCommitment.read(
[fixture("case14.json.gz"), fixture("case14.json.gz")], [fixture("case14.json.gz"), fixture("case14.json.gz")],
ph, ph,
) )
# 4. Build JuMP model # Build JuMP model
model = UnitCommitment.build_model( model = UnitCommitment.build_model(
instance = instance, instance = instance,
optimizer = optimizer_with_attributes(Cbc.Optimizer, "LogLevel" => 0), optimizer = optimizer_with_attributes(
HiGHS.Optimizer,
MOI.Silent() => true,
),
) )
# 5. Run the decentralized optimization algorithm # Run the decentralized optimization algorithm
UnitCommitment.optimize!(model, ph) UnitCommitment.optimize!(model, ph)
# 6. Fetch the solution # Fetch the solution
solution = UnitCommitment.solution(model, ph) solution = UnitCommitment.solution(model, ph)
# 7. Close MPI # Close MPI
MPI.Finalize() MPI.Finalize()

Loading…
Cancel
Save