<jupyter_start><jupyter_text>Linear model and OptimizerDefining the linear model and linear optimizer classes needed for the algorithm 1<jupyter_code>import torch
import abc

from abc import ABC


# The linear model which implemented by pytorch
class TorchLinearModel(torch.nn.Module):
    def __init__(self, n):
        super(TorchLinearModel, self).__init__()
        self.linear = torch.nn.Linear(n, 1, bias=False)

    def forward(self, x):
        y_pred = self.linear(x)
        return y_pred
    
    
# The abstract optimizer model which should have model, optimizer, and criterion
class Optimizer(ABC):
    def __init__(self, model, optimizer, criterion):
        self.model = model
        self.optimizer = optimizer
        self.criterion = criterion

    @abc.abstractmethod
    def optimize(self, x_data, y_data, old_weight, regularizer_term):
        torch_old_weight = torch.from_numpy(np.array(old_weight, dtype=np.float32))
        self.model.linear.weight.data = torch_old_weight
        for iterinner in range(5):
            self.optimizer.zero_grad()
            y_pred = self.model(x_data)
            loss1 = self.criterion(y_pred, y_data)
            loss2 = 1 / (2 * regularizer_term) * torch.mean((self.model.linear.weight - torch_old_weight) ** 2) 
            loss = loss1 + loss2
            loss.backward()
            self.optimizer.step()

        return self.model.linear.weight.data.numpy()
    
    
# The Linear optimizer model which implemented by pytorch    
class TorchLinearOptimizer(Optimizer):
    def __init__(self, model):
        criterion = torch.nn.MSELoss(reduction='mean')
        optimizer = torch.optim.RMSprop(model.parameters())
        super(TorchLinearOptimizer, self).__init__(model, optimizer, criterion)

    def optimize(self, x_data, y_data, old_weight, regularizer_term):
        return super(TorchLinearOptimizer, self).optimize(x_data, y_data, old_weight, regularizer_term)
    
    
# The linear model in Networked Linear Regression section of the paper    
class LinearModel:
    def __init__(self, degree, features, label):
        
        mtx1 = 2 * degree * np.dot(features.T, features).astype('float64')
        mtx1 += 1 * np.eye(mtx1.shape[0])
        mtx1_inv = np.linalg.inv(mtx1)
        
        mtx2 = 2 * degree * np.dot(features.T, label).T
        
        self.mtx1_inv = mtx1_inv
        self.mtx2 = mtx2

    def forward(self, x):
        mtx2 = x + self.mtx2
        mtx_inv = self.mtx1_inv

        return np.dot(mtx_inv, mtx2)


# The Linear optimizer in Networked Linear Regression section of the paper    
class LinearOptimizer(Optimizer):

    def __init__(self, model):
        super(LinearOptimizer, self).__init__(model, None, None)

    def optimize(self, x_data, y_data, old_weight, regularizer_term):
        return self.model.forward(old_weight)<jupyter_output><empty_output><jupyter_text>Create SBMCreating the SBM with two clusters $|C_1| = |C_2| = 100$. Two nodes within the same cluster are connected by an edge with probability pin, Two nodes from different clusters are connected by an edge with probability pout.Each node $i ∈ V$ represents a local dataset consisting of $m$ feature vectors $x^{(i,1)} , . . . , x^{(i,m)} \in R^n$ . The feature vectors are i.i.d. realizations of a standard Gaussian random vector $x ∼ N(0,I)$.The labels $y^{(i)}_1,...,y^{(i)}_m \in R$ of the nodes $i \in V$ are generated according to the linear model $y^{(i)}_r = (x^{(i,r)})^T \bar{w}^{(i)} + \epsilon$, with $\epsilon ∼ N(0, \sigma)$, weight vector $w^{(i)} = (2, 2)^T$ for $i \in C_1$ and $w^{(i)} = (−2,2)^T$ for $i \in C_2$ .<jupyter_code>from torch.autograd import Variable
from graspy.simulations import sbm


def get_sbm_2blocks_data(m=5, n=2, pin=0.5, pout=0.001, noise_sd=0, is_torch_model=True):
    '''
    :param m, n: shape of features vector for each node
    :param pin: the probability of edges inside each cluster
    :param pout: the probability of edges between the clusters
    :param noise_sd: the standard deviation of the noise for calculating the labels
    
    :return B: adjacency matrix of the graph
    :return weight_vec: a list containing the edges's weights of the graph
    :return true_labels: a list containing the true labels of the nodes
    :return datapoints: a dictionary containing the data of each node in the graph needed for the algorithm 1 
    '''
    N1, N2 = 100, 100

    # generate graph G which is a SBM wich 2 clusters
    G = sbm(n=[N1, N2], p=[[pin, pout],[pout, pin]], )
    '''
    G: generated SBM graph with 2 clusters
    '''

    N = len(G)
    E = int(len(np.argwhere(G > 0))/2)
    '''
    N: total number of nodes
    E: total number of edges
    '''
    
    
    # create B(adjacency matrix) and edges's weights vector(weight_vec) based on the graph G
    B = np.zeros((E, N))
    '''
    B: adjacency matrix of the graph with the shape of E*N
    '''
    weight_vec = np.zeros(E)
    '''
    weight_vec: a list containing the edges's weights of the graph with the shape of E
    '''
    
    cnt = 0
    for i, j in np.argwhere(G > 0):
        if i > j:
            continue
        B[cnt, i] = 1
        B[cnt, j] = -1

        weight_vec[cnt] = 1
        cnt += 1
        
    
    # define weight vectors for each cluster of the graph
    
    W1 = np.array([2, 2])
    '''
    W1: the weigh vector for the first cluster
    '''
    W2 = np.array([-2, 2])
    '''
    W2: the weigh vector for the second cluster
    '''
    
    
    # create the data of each node needed for the algorithm 1 
    
    node_degrees = np.array((1.0 / (np.sum(abs(B), 0)))).ravel()
    '''
    node_degrees: a list containing the nodes degree for the alg1 (1/N_i)
    '''
    
    datapoints = {}
    '''
    datapoints: a dictionary containing the data of each node in the graph needed for the algorithm 1,
    which are features, label, degree, and also the optimizer model for each node
    '''
    true_labels = []
    '''
    true_labels: the true labels for the nodes of the graph
    '''
    for i in range(N):
        features = np.random.normal(loc=0.0, scale=1.0, size=(m, n))
        '''
        features: the feature vector of node i which are i.i.d. realizations of a standard Gaussian random vector x~N(0,I)
        '''
        
        if i < N1: # nodes in the first cluster
            label = np.dot(features, W1) + np.random.normal(0,noise_sd)
        else: # nodes in the second cluster
            label = np.dot(features, W2) + np.random.normal(0,noise_sd)
        '''
        label: the label of the node i that is generated according to the linear model y = x^T w + e
        '''
            
        true_labels.append(label)
            
        if is_torch_model:
            model = TorchLinearModel(n)
            optimizer = TorchLinearOptimizer(model)
        else:
            
            model = LinearModel(node_degrees[i], features, label)
            optimizer = LinearOptimizer(model)            
        '''
        model : the linear model for the node i 
        optimizer : the optimizer model for the node i 
        '''    
            
        datapoints[i] = {
            'features': Variable(torch.from_numpy(features)).to(torch.float32),
            'degree': node_degrees[i],
            'label': Variable(torch.from_numpy(label)).to(torch.float32),
            'optimizer': optimizer
        }
        

    return B, weight_vec, np.array(true_labels), datapoints<jupyter_output><empty_output><jupyter_text>Penalty FunctionDefining the penalty functions for the algorithm 1 which are norm1, norm2, or mocha<jupyter_code># The abstract penalty function which has a function update
class Penalty(ABC):
    def __init__(self, lambda_lasso, weight_vec, Sigma, n):
        self.lambda_lasso = lambda_lasso
        self.weight_vec = weight_vec
        self.Sigma = Sigma

    @abc.abstractmethod
    def update(self, new_u):
        pass


# The norm2 penalty function
class Norm2Pelanty(Penalty):
    def __init__(self, lambda_lasso, weight_vec, Sigma, n):
        super(Norm2Pelanty, self).__init__(lambda_lasso, weight_vec, Sigma, n)
        self.limit = np.array(lambda_lasso * weight_vec)

    def update(self, new_u):
        normalized_u = np.where(np.linalg.norm(new_u, axis=1) >= self.limit)
        new_u[normalized_u] = (new_u[normalized_u].T * self.limit[normalized_u] / np.linalg.norm(new_u[normalized_u], axis=1)).T
        return new_u


# The MOCHA penalty function
class MOCHAPelanty(Penalty):
    def __init__(self, lambda_lasso, weight_vec, Sigma, n):
        super(MOCHAPelanty, self).__init__(lambda_lasso, weight_vec, Sigma, n)
        self.normalize_factor = 1 + np.dot(2 * self.Sigma, 1/(self.lambda_lasso * self.weight_vec))

    def update(self, new_u):
        for i in range(new_u.shape[1]):
            new_u[:, i] /= self.normalize_factor

        return new_u

    
# The norm1 penalty function
class Norm1Pelanty(Penalty):
    def __init__(self, lambda_lasso, weight_vec, Sigma, n):
        super(Norm1Pelanty, self).__init__(lambda_lasso, weight_vec, Sigma, n)
        self.limit = np.array([np.zeros(n) for i in range(len(weight_vec))])
        for i in range(n):
            self.limit[:, i] = lambda_lasso * weight_vec

    def update(self, new_u):
        normalized_u = np.where(abs(new_u) >= self.limit)
        new_u[normalized_u] = self.limit[normalized_u] * new_u[normalized_u] / abs(new_u[normalized_u])
        return new_u<jupyter_output><empty_output><jupyter_text>Algorithm 1<jupyter_code>def algorithm_1(K, D, weight_vec, datapoints, true_labels, samplingset, lambda_lasso, penalty_func_name='norm1', calculate_score=False):
    '''
    :param K: the number of iterations 
    :param D: the block incidence matrix 
    :param weight_vec: a list containing the edges's weights of the graph
    :param datapoints: a dictionary containing the data of each node in the graph needed for the algorithm 1 
    :param true_labels: a list containing the true labels of the nodes
    :param samplingset: the sampling set 
    :param lambda_lasso: the parameter lambda 
    :param penalty_func_name: the name of the penalty function used in the algorithm
    
    
    :return iteration_scores: the mean squared error of the predicted weight vectors in each iteration
    :return new_w: the predicted weigh vectors for each node
    '''
    
    Sigma = np.diag(np.full(weight_vec.shape, 0.9 / 2))
    '''
    Sigma: the block diagonal matrix Sigma
    '''
    T_matrix = np.diag(np.array((1.0 / (np.sum(abs(B), 0)))).ravel())
    '''
    T_matrix: the block diagonal matrix T
    '''

    E, N = B.shape
    '''
    shape of the graph
    '''
    m, n = datapoints[0]['features'].shape
    '''
    shape of the feature vectors of each node in the graph
    '''

    # define the penalty function
    if penalty_func_name == 'norm1':
        penalty_func = Norm1Pelanty(lambda_lasso, weight_vec, Sigma, n)
    elif penalty_func_name == 'norm2':
        penalty_func = Norm2Pelanty(lambda_lasso, weight_vec, Sigma, n)
    elif penalty_func_name == 'mocha':
        penalty_func = MOCHAPelanty(lambda_lasso, weight_vec, Sigma, n)
    else:
        raise Exception('Invalid penalty name')

    new_w = np.array([np.zeros(n) for i in range(N)])
    '''
    new_w: the primal variable of the algorithm 1
    '''
    new_u = np.array([np.zeros(n) for i in range(E)])
    '''
    new_u: the dual variable of the algorithm 1
    '''
    
    # starting algorithm 1
    
    iteration_scores = []
    for iterk in range(K):
        # if iterk % 100 == 0:
        #     print ('iter:', iterk)
        
        prev_w = np.copy(new_w)

        # line 2 algorithm 1
        hat_w = new_w - np.dot(T_matrix, np.dot(D.T, new_u))

        
        for i in range(N):
            
            if i in samplingset:  # line 6 algorithm 1
                optimizer = datapoints[i]['optimizer']
                new_w[i] = optimizer.optimize(datapoints[i]['features'], 
                                              datapoints[i]['label'], 
                                              hat_w[i], 
                                              datapoints[i]['degree'])
             
            else: 
                new_w[i] = hat_w[i]

        # line 9 algortihm 1
        tilde_w = 2 * new_w - prev_w
        new_u = new_u + np.dot(Sigma, np.dot(D, tilde_w))

        # line 10 algortihm 1
        new_u = penalty_func.update(new_u)

        # calculate the MSE of the predicted weight vectors
        if calculate_score:
            Y_pred = []
            for i in range(N):
                Y_pred.append(np.dot(datapoints[i]['features'], new_w[i]))

            iteration_scores.append(mean_squared_error(true_labels.reshape(N, m), Y_pred))

    # print (np.max(abs(new_w - prev_w)))

    return iteration_scores, new_w

# calculate the mean squared error of the predicted labels
def get_score(datapoints, predicted_w, true_labels):
    predicted_labels = []
    for i in range(len(datapoints)):
        predicted_labels.append(np.dot(datapoints[i]['features'], predicted_w[i]))
    predicted_labels = np.array(predicted_labels)

    return mean_squared_error(true_labels, predicted_labels)
PENALTY_FUNCS = ['norm1', 'norm2', 'mocha']

N = 200
K=500
M=0.2<jupyter_output><empty_output><jupyter_text>Different Lambdas for each penalty function with pout=0.001<jupyter_code>POUT = 0.001
lambda_lassos = [1.0, 0.1, 0.05, 0.01, 0.001]
colors = ['steelblue', 'darkorange', 'green', 'darkred', 'purple']
x1 = [i for i in range(K)]
for penalty_func in PENALTY_FUNCS:
    
    plt.close()
    MSEs = {}
    for i, lambda_lasso in enumerate(lambda_lassos):
        B, weight_vec, true_labels, datapoints = get_sbm_2blocks_data(pin=0.5, pout=POUT)
        
        samplingset = random.sample([i for i in range(N)], k=int(M * N))
                
        iteration_scores, predicted_w = algorithm_1(K, B, weight_vec, datapoints, true_labels, samplingset, lambda_lasso, penalty_func, calculate_score=True)
        
        alg1_score = get_score(datapoints, predicted_w, true_labels)
        MSEs[lambda_lasso] = alg1_score
        
        plt.plot(x1, iteration_scores, label='lambda='+str(lambda_lasso), c=colors[i])
    
    print('penalty_func:', penalty_func)
    print('MSE:', MSEs)
    
    plt.xlabel('iteration')
    plt.ylabel('MSE')
    plt.title('Penalty function : %s' % penalty_func)
    plt.legend(loc='best')
    plt.show()<jupyter_output>penalty_func: norm1
MSE: {1.0: 5.297490064677504e-07, 0.1: 3.251260255306933e-09, 0.05: 6.33656259549361e-08, 0.01: 1.6433964569125135, 0.001: 5.33093657758464}<jupyter_text>Different sampling ratios for each penalty functions with pout=0.001<jupyter_code>POUT = 0.001
lambda_lassos = {'norm1': 0.1, 'norm2': 0.1, 'mocha': 1.0}
sampling_ratios = [0.2, 0.4, 0.6, 0.8, 1.0]
colors = ['steelblue', 'darkorange', 'green', 'darkred', 'purple']
for penalty_func in PENALTY_FUNCS:
    
    MSEs = {}
    for i, sampling_ratio in enumerate(sampling_ratios):
        B, weight_vec, true_labels, datapoints = get_sbm_2blocks_data(pin=0.5, pout=POUT)
        
        samplingset = random.sample([i for i in range(N)], k=int(sampling_ratio * N))
                
        lambda_lasso = lambda_lassos[penalty_func]
        _, predicted_w = algorithm_1(300, B, weight_vec, datapoints, true_labels, samplingset, lambda_lasso, penalty_func)
        
        alg1_score = get_score(datapoints, predicted_w, true_labels)
        MSEs[sampling_ratio] = alg1_score
        
    
    print('penalty_func:', penalty_func)
    print('MSE:', MSEs)
    
    plt.close()
    plt.plot(list(MSEs.keys()), list(MSEs.values()))
    plt.xlabel('M')
    plt.ylabel('MSE')
    plt.title('Penalty function : %s' % penalty_func)
    plt.show()<jupyter_output>penalty_func: norm1
MSE: {0.2: 8.89351555065316e-05, 0.4: 8.098248292499353e-07, 0.6: 1.1380283260083147e-08, 0.8: 5.2830674410165274e-09, 1.0: 2.6912206264740643e-09}<jupyter_text>Different pouts and sampling ratios for each penalty function with fixed lambda<jupyter_code>lambda_lasso = 0.01
pouts = [0.0, 0.01, 0.05, 0.1, 0.2, 0.4, 0.6]
colors = ['steelblue', 'darkorange', 'green', 'darkred', 'purple']

for penalty_func in PENALTY_FUNCS:
    print('penalty_func:', penalty_func)

    for i, sampling_ratio in enumerate([0.2, 0.4, 0.6]):
        MSEs = {}
        for pout in pouts:
        
            B, weight_vec, true_labels, datapoints = get_sbm_2blocks_data(pin=0.5, pout=pout, is_torch_model=False)

        
            samplingset = random.sample([i for i in range(N)], k=int(sampling_ratio * N))


            _, predicted_w = algorithm_1(3000, B, weight_vec, datapoints, true_labels, samplingset, lambda_lasso, penalty_func)

            alg1_score = get_score(datapoints, predicted_w, true_labels)
            MSEs[pout] = alg1_score
        plt.plot(list(MSEs.keys()), list(MSEs.values()), label='M=' + str(sampling_ratio), c=colors[i])

        print('M:', sampling_ratio)
        print('MSE:', MSEs)
    
    plt.xlabel('p_out')
    plt.ylabel('MSE')
    plt.legend(loc='best')
    plt.title('Penalty function : %s' % penalty_func)
    plt.show()
    plt.close()<jupyter_output>penalty_func: norm1
M: 0.2
MSE: {0.0: 5.133162130227324e-15, 0.01: 2.119195132684033e-05, 0.05: 0.001556188189872617, 0.1: 2.9406092557944055, 0.2: 3.8816694807787124, 0.4: 3.8172068600400046, 0.6: 3.1777104488238077}
M: 0.4
MSE: {0.0: 5.22607280546741e-15, 0.01: 5.032851179960787e-06, 0.05: 0.00015999583763840494, 0.1: 0.0005754165789393921, 0.2: 2.8557173232538986, 0.4: 2.443348012612449, 0.6: 3.234036954575031}
M: 0.6
MSE: {0.0: 4.867558403695794e-15, 0.01: 3.585407455028897e-06, 0.05: 6.202577268127514e-05, 0.1: 0.0002670828821806939, 0.2: 0.001278082932221612, 0.4: 3.1643477864465166, 0.6: 1.591088594811716}<jupyter_text>Different pouts and noises for each penalty function with fixed lambda and sampling ratio<jupyter_code>lambda_lasso = 0.01
sampling_ratio = 0.6
pouts = [0.0, 0.01, 0.05, 0.1, 0.2, 0.4, 0.6]
colors = ['steelblue', 'darkorange', 'green', 'darkred', 'purple']

for penalty_func in PENALTY_FUNCS:
    print('penalty_func:', penalty_func)

    for i, noise in enumerate([0.01, 0.1, 1.0]):
        MSEs = {}
        for pout in pouts:

            B, weight_vec, true_labels, datapoints = get_sbm_2blocks_data(pin=0.5, pout=pout, noise_sd=noise, is_torch_model=False)


            samplingset = random.sample([i for i in range(N)], k=int(sampling_ratio * N))


            _, predicted_w = algorithm_1(2000, B, weight_vec, datapoints, true_labels, samplingset, lambda_lasso, penalty_func)

            alg1_score = get_score(datapoints, predicted_w, true_labels)
            MSEs[pout] = alg1_score

        plt.plot(list(MSEs.keys()), list(MSEs.values()), label='noise=' + str(noise), c=colors[i])

        print('noise', noise)
        print('MSE:', MSEs)

    plt.xlabel('p_out')
    plt.ylabel('MSE')
    plt.legend(loc='best')
    plt.title('Penalty function : %s' % penalty_func)
    plt.show()
    plt.close()<jupyter_output>penalty_func: norm1
noise 0.01
MSE: {0.0: 0.00011197575268964638, 0.01: 0.0001113493411921468, 0.05: 0.00017596075564701088, 0.1: 0.00040157658522407076, 0.2: 0.0012107779314913098, 0.4: 1.5029878697776047, 0.6: 1.9601897075308596}
noise 0.1
MSE: {0.0: 0.008577871749918854, 0.01: 0.00756354257296714, 0.05: 0.009295587389047483, 0.1: 0.008394006385521308, 0.2: 0.012428896783235827, 0.4: 1.8425551332403032, 0.6: 2.863643548409676}
noise 1.0
MSE: {0.0: 0.7089546721943265, 0.01: 0.7034259751840699, 0.05: 0.8402843217708259, 0.1: 0.8295689674650317, 0.2: 0.7378204184967657, 0.4: 2.151569546537637, 0.6: 2.819068308111323}<jupyter_text>test<jupyter_code>sampling_ratio = 0.6
pouts = [0.01, 0.05, 0.1, 0.2]
colors = ['steelblue', 'darkorange', 'green', 'darkred', 'purple']
PIN = 0.5

for penalty_func in PENALTY_FUNCS:
    print('------------------------------------')
    print('penalty_func:', penalty_func)
    for sampling_ratio in [0.2, 0.25, 0.3, 0.4]:
        print('sampling_ratio:', sampling_ratio)
        for i, lambda_lasso in enumerate([0.1, 0.05, 0.01, 0.005, 0.0001]):
            MSEs = {}
            for pout in pouts:

                B, weight_vec, true_labels, datapoints = get_sbm_2blocks_data(pin=PIN, pout=pout, is_torch_model=False)

                samplingset = random.sample([i for i in range(N)], k=int(sampling_ratio * N))

                _, predicted_w = algorithm_1(2000, B, weight_vec, datapoints, true_labels, samplingset, lambda_lasso, penalty_func)

                alg1_score = get_score(datapoints, predicted_w, true_labels)
                MSEs[pout] = alg1_score

            plt.plot(list(MSEs.keys()), list(MSEs.values()), label='lambda=' + str(lambda_lasso), c=colors[i])

            print('lambda_lasso', lambda_lasso)
            print('MSE:', MSEs)

        plt.xlabel('p_out')
        plt.ylabel('MSE')
        plt.legend(loc='best')
        plt.title('Penalty function : %s' % penalty_func)
        plt.show()
        plt.close()
lambda_lassos = {'norm1': 0.1, 'norm2': 0.1, 'mocha': 1.0}
colors = ['hotpink', 'steelblue', 'darkorange', 'green', 'darkred', 'purple', 'dimgrey']
pouts = [0.0, 0.001, 0.1, 0.2, 0.6]
noises = [0, 0.01, 0.1]

for penalty_func in PENALTY_FUNCS:
    
    for i, pout in enumerate(pouts):
        
        MSEs = {}
        for noise in noises:
            B, weight_vec, true_labels, datapoints = get_sbm_2blocks_data(pin=0.5, pout=pout, noise_sd=noise)

            samplingset = random.sample([i for i in range(N)], k=int(M * N))

            lambda_lasso = lambda_lassos[penalty_func]
            _, predicted_w = algorithm_1(K, B, weight_vec, datapoints, true_labels, samplingset, lambda_lasso, penalty_func)

            alg1_score = get_score(datapoints, predicted_w, true_labels)
            MSEs[noise] = alg1_score

        plt.plot(list(MSEs.keys()), list(MSEs.values()), label='pout='+str(pout), c=colors[i])
        print('penalty_func:', penalty_func, 'pout:', pout)
        print('noise MSE:', MSEs)
        
    plt.xlabel('noise')
    plt.ylabel('MSE')
    plt.title('Penalty function : %s' % penalty_func)
    plt.legend(loc='best')
    plt.show()
    plt.close()<jupyter_output>penalty_func: norm1 pout: 0.0
noise MSE: {0: 1.4178859367823341e-08, 0.01: 9.505896873029422e-05, 0.1: 0.009111485494220953}
penalty_func: norm1 pout: 0.001
noise MSE: {0: 4.3078900246935826e-08, 0.01: 8.319461811038074e-05, 0.1: 0.01137399524769252}
penalty_func: norm1 pout: 0.1
noise MSE: {0: 4.331300872864871, 0.01: 4.052339321458038, 0.1: 4.376707755344356}
penalty_func: norm1 pout: 0.2
noise MSE: {0: 4.420807548171662, 0.01: 3.9681697450624727, 0.1: 3.886810065986162}
penalty_func: norm1 pout: 0.6
noise MSE: {0: 5.606866326530658, 0.01: 4.759506436648439, 0.1: 6.03700550381998}
{'fraction_non_alphanumeric': 0.1238487851942812, 'fraction_numerical': 0.0768792211209543, 'mean_word_length': 2.9119917653113743, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '618886', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 9289, 'n_tokens_neox': 8100, 'n_words': 1962, 'path': 'penalty_functions.ipynb', 'repo_name': 'sahelyiyi/FederatedLearning', 'size': '22802'}