Averaging Consensus¶
The Averaging Consensus algorithm distributes a parameter vector \(\lambda\) across \(N\) agents via a gossip-style protocol. Each agent maintains its own copy of \(\lambda\) and iteratively averages it with values received from neighbours. An optional gradient term allows each agent to steer the consensus towards locally desirable values.
Algorithm¶
Let \(\lambda_i^k\) be the value held by agent \(i\) at iteration \(k\). The update rule is:
where
\(\bar{\lambda}^k\) is the average of all received values at iteration \(k\)
\(\alpha \in (0,1]\) is the step size (mixing parameter)
\(\nabla_i^k = \text{gradient\_term}(\text{actor}_i, \lambda_i^k, \text{data})\) is the local gradient correction (zero by default)
The algorithm runs for a fixed number of iterations (max_iter) after which each agent
calls a user-supplied finish_callback.
Usage¶
>>> from distributed_resource_optimization import (
... create_averaging_consensus_participant,
... create_averaging_consensus_start,
... start_distributed_optimization,
... )
>>> finished = []
>>> def on_finish(algo, carrier):
... finished.append(algo._lam.copy())
>>> actors = [
... create_averaging_consensus_participant(on_finish, initial_lam=v, max_iter=200)
... for v in [1.0, 5.0, 10.0]
... ]
>>> start = create_averaging_consensus_start(1.0, data=None)
>>> asyncio.run(start_distributed_optimization(actors, start))
>>> len(finished) > 0
True
>>> np.allclose(actors[0]._lam, actors[1]._lam, atol=1e-2)
True
Parameters¶
Parameter |
Default |
Description |
|---|---|---|
|
— |
Called with |
|
|
|
|
|
Starting scalar value broadcast to all \(\lambda\) components |
|
|
Mixing step size |
|
|
Number of gossip rounds before finishing |
Local Gradient Corrections¶
To steer the consensus, subclass ConsensusActor
and override gradient_term():
>>> from distributed_resource_optimization import ConsensusActor
>>> class PushToTarget(ConsensusActor):
... def __init__(self, target, step=0.05):
... self.target = np.asarray(target)
... self.step = step
... def gradient_term(self, lam, data):
... return self.step * (self.target - lam)
The data argument carries whatever was embedded in the initial
AveragingConsensusMessage — useful for
passing problem data alongside the consensus.
Economic Dispatch¶
The built-in
LinearCostEconomicDispatchConsensusActor
implements consensus-based economic dispatch. Each agent has a linear cost and power
limits; the gradient term pushes \(\lambda\) toward the clearing price at which
supply equals demand:
>>> from distributed_resource_optimization import (
... LinearCostEconomicDispatchConsensusActor,
... create_averaging_consensus_participant,
... AveragingConsensusMessage,
... start_distributed_optimization,
... )
>>> actors = [
... create_averaging_consensus_participant(
... lambda *_: None,
... LinearCostEconomicDispatchConsensusActor(cost=10, p_max=100, n_guess=3),
... max_iter=100,
... ),
... create_averaging_consensus_participant(
... lambda *_: None,
... LinearCostEconomicDispatchConsensusActor(cost=10, p_max=100, n_guess=3),
... max_iter=100,
... ),
... create_averaging_consensus_participant(
... lambda *_: None,
... LinearCostEconomicDispatchConsensusActor(cost=10, p_max=100, n_guess=3),
... max_iter=100,
... ),
... ]
>>> p_target = [10, 30, 40, 45, 60, 10]
>>> msg = AveragingConsensusMessage(lam=np.ones(len(p_target)) * 10, k=0, data=p_target)
>>> asyncio.run(start_distributed_optimization(actors, msg))
>>> np.allclose(actors[0]._lam, actors[1]._lam, atol=1e-3)
True
Note
The algorithm terminates after exactly max_iter gossip rounds — there is no
residual-based stopping criterion. In a fully connected graph convergence is typically
fast (10–30 rounds); increase max_iter for larger or sparser networks.
See Also¶
create_averaging_consensus_participant(),AveragingConsensusAlgorithmConsensusActor,NoConsensusActorLinearCostEconomicDispatchConsensusActor