Skip to content

Commit a644e2a

Browse files
committed
Adding simple averaging consensus.
1 parent 7635bcd commit a644e2a

File tree

3 files changed

+123
-0
lines changed

3 files changed

+123
-0
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
2+
export ConsensusActor, NoConsensusActor, AveragingConsensusAlgorithm, AveragingConsensusMessage, create_averaging_consensus_participant, gradient_term
3+
4+
abstract type ConsensusActor end
5+
6+
function gradient_term(actor::ConsensusActor, λ::Vector{<:Real}, data::Any)
7+
return 0
8+
end
9+
10+
mutable struct NoConsensusActor <: ConsensusActor
11+
end
12+
13+
struct AveragingConsensusMessage
14+
λ::Vector{Float64}
15+
k::Int
16+
data::Any
17+
end
18+
19+
struct ConsensusFinishedMessage
20+
λ::Vector{Float64}
21+
k::Int
22+
actor::ConsensusActor
23+
end
24+
25+
@kwdef mutable struct AveragingConsensusAlgorithm <: DistributedAlgorithm
26+
message_queue::Dict{Int,Vector{AveragingConsensusMessage}} = Dict{Int,Vector{AveragingConsensusMessage}}()
27+
first_message = true
28+
k::Int = 0
29+
max_iter::Int = 50
30+
λ::Vector{Float64} = Vector{Real}()
31+
32+
initial_λ::Real
33+
α::Real
34+
actor::ConsensusActor
35+
36+
finish_callback::Function
37+
end
38+
39+
function on_exchange_message(algorithm_data::AveragingConsensusAlgorithm, carrier::Carrier, message::AveragingConsensusMessage, meta::Any)
40+
if message.k >= algorithm_data.max_iter
41+
# abort if iteration count is reached
42+
algorithm_data.finish_callback(algorithm_data, carrier)
43+
return
44+
end
45+
46+
if algorithm_data.first_message
47+
algorithm_data.first_message = false
48+
algorithm_data.λ = ones(length(message.λ)) .* algorithm_data.initial_λ
49+
50+
for addr in others(carrier, "")
51+
send_to_other(carrier, AveragingConsensusMessage(algorithm_data.λ, 0, message.data), addr)
52+
end
53+
end
54+
queue = get!(algorithm_data.message_queue, message.k, [])
55+
56+
push!(queue, message)
57+
58+
if length(queue) == length(others(carrier, ""))
59+
avgλ = sum(m.λ for m in queue) ./ length(queue)
60+
algorithm_data.λ .+= algorithm_data.α .* (avgλ .- algorithm_data.λ) .+ gradient_term(algorithm_data.actor, algorithm_data.λ, message.data)
61+
62+
algorithm_data.k += message.k + 1
63+
delete!(algorithm_data.message_queue, message.k)
64+
65+
for addr in others(carrier, "")
66+
send_to_other(carrier, AveragingConsensusMessage(algorithm_data.λ, algorithm_data.k, message.data), addr)
67+
end
68+
end
69+
end
70+
71+
function create_averaging_consensus_participant(finish_callback::Function, consensus_actor::ConsensusActor; initial_λ::Real=10, α::Real=0.3, max_iter::Int=50)
72+
appl_consensus_actor = isnothing(consensus_actor) ? NoConsensusActor() : consensus_actor
73+
74+
return AveragingConsensusAlgorithm(finish_callback=finish_callback, initial_λ=initial_λ, α=α, actor=appl_consensus_actor, max_iter=max_iter)
75+
end
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export LinearCostEconomicDispatchConsensusActor
2+
3+
@kwdef mutable struct LinearCostEconomicDispatchConsensusActor <: ConsensusActor
4+
cost::Real
5+
P_max::Real
6+
ρ::Real = 0.01
7+
ϵ::Real = 0.1
8+
P_min::Real = 0
9+
N_guess::Int = 10
10+
P::Vector{Float64} = Vector{Real}()
11+
end
12+
13+
function DistributedResourceOptimization.gradient_term(actor::LinearCostEconomicDispatchConsensusActor, λ::Vector{<:Real}, P_target::Vector{<:Real})
14+
# linearized inverted quadratic cost function aP¹ + bP minus the target
15+
16+
actor.P = clamp.((λ .- actor.cost) ./ actor.ϵ, actor.P_min, actor.P_max)
17+
term = - actor.ρ .* (actor.P .- P_target ./ actor.N_guess)
18+
return term
19+
end
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using Mango
2+
using DistributedResourceOptimization
3+
using Test
4+
5+
@testset "TestAveragingConsensusWithSC" begin
6+
finished = false
7+
# describe quadratic cost function with quasi-linear behavior
8+
actor_one = create_averaging_consensus_participant(LinearCostEconomicDispatchConsensusActor(cost=10, P_max=30, N_guess=3), max_iter=100) do _,_
9+
finished = true
10+
end
11+
actor_two = create_averaging_consensus_participant(LinearCostEconomicDispatchConsensusActor(cost=15, P_max=10, N_guess=3), max_iter=100) do _,_
12+
end
13+
actor_three = create_averaging_consensus_participant(LinearCostEconomicDispatchConsensusActor(cost=12, P_max=22, N_guess=3), max_iter=100) do _,_
14+
end
15+
16+
P_target = [10, 30, 40, 45, 60, 10]
17+
initial_message = AveragingConsensusMessage([10] .* ones(length(P_target)), 0, P_target)
18+
19+
wait(start_distributed_optimization([actor_one, actor_two, actor_three], initial_message))
20+
21+
wait(Threads.@spawn begin
22+
while actor_one.k < 50
23+
sleep(0.01)
24+
end
25+
end)
26+
27+
@test isapprox(actor_one.λ, actor_two.λ, atol=1)
28+
@test finished
29+
end

0 commit comments

Comments
 (0)