<reponame>Cyalpaca/fed-att
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @python: 3.6

import copy
import torch
import torch.nn.functional as F
from scipy import linalg
import numpy as np

from src.agg.avg import average_weights


def aggregate_stable_avg(w_clients, w_server, metric, dp, attack_count=0):
    """
    stable_avg aggregation
    :param w_clients: list of client model parameters
    :param w_server: server model parameters
    :param metric: similarity
    :param dp: magnitude of randomization
    :return: updated server model parameters
    :attack_count: number of byz client
    """
    if attack_count == 0:
        return average_weights(w_clients, dp)
    else:
        assert len(w_clients) > 2 * attack_count + 2, "Too many byz client."
    valid_count = len(w_clients) - attack_count - 2
    distance = {}
    w_valid = []
    for k in w_server.keys():
        distance[k] = torch.zeros(len(w_clients)).cpu()
    for i in range(valid_count):
        w_valid.append(copy.deepcopy(w_clients[i]))
        for k in w_server.keys():
            w_valid[i][k] = torch.zeros_like(w_server[k]).cpu()

    for k in w_server.keys():
        for i in range(0, len(w_clients)):
            distance[k][i] = torch.from_numpy(np.array(linalg.norm(w_server[k] - w_clients[i][k], ord=metric)))
    for k in distance.keys():
        index = np.arange(len(distance[k]))
        z = dict(zip(index, distance[k]))
        seq_z = sorted(z.items(), key=lambda x: x[1])
        index, distance[k] = zip(*seq_z)
        for i in range(valid_count):
            w_valid[i][k] = w_clients[index[i]][k]

    return average_weights(w_valid, dp)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @python: 3.6

import copy
import torch
import torch.nn.functional as F
from scipy import linalg
import numpy as np

from src.agg.avg import average_weights


def aggregate_stable_avg(w_clients, w_server, metric, dp, attack_count=0):
    """
    stable_avg aggregation
    :param w_clients: list of client model parameters
    :param w_server: server model parameters
    :param metric: similarity
    :param dp: magnitude of randomization
    :return: updated server model parameters
    :attack_count: number of byz client
    """
    if attack_count == 0:
        return average_weights(w_clients, dp)
    else:
        assert len(w_clients) > 2 * attack_count + 2, "Too many byz client."
    valid_count = len(w_clients) - attack_count - 2
    distance = {}
    w_valid = []
    for k in w_server.keys():
        distance[k] = torch.zeros(len(w_clients)).cpu()
    for i in range(valid_count):
        w_valid.append(copy.deepcopy(w_clients[i]))
        for k in w_server.keys():
            w_valid[i][k] = torch.zeros_like(w_server[k]).cpu()

    for k in w_server.keys():
        for i in range(0, len(w_clients)):
            distance[k][i] = torch.from_numpy(np.array(linalg.norm(w_server[k] - w_clients[i][k], ord=metric)))
    for k in distance.keys():
        index = np.arange(len(distance[k]))
        z = dict(zip(index, distance[k]))
        seq_z = sorted(z.items(), key=lambda x: x[1])
        index, distance[k] = zip(*seq_z)
        for i in range(valid_count):
            w_valid[i][k] = w_clients[index[i]][k]

    return average_weights(w_valid, dp)
#!/usr/bin/env python # -*- coding: utf-8 -*- # @python: 3.6 stable_avg aggregation     :param w_clients: list of client model parameters     :param w_server: server model parameters     :param metric: similarity     :param dp: magnitude of randomization     :return: updated server model parameters     :attack_count: number of byz client