Source code for deeprobust.graph.global_attack.base_attack

import os.path as osp

import numpy as np
import scipy.sparse as sp
import torch
from torch.nn.modules.module import Module

from deeprobust.graph import utils


[docs]class BaseAttack(Module): """Abstract base class for target attack classes. Parameters ---------- model : model to attack nnodes : int number of nodes in the input graph attack_structure : bool whether to attack graph structure attack_features : bool whether to attack node features device: str 'cpu' or 'cuda' """ def __init__(self, model, nnodes, attack_structure=True, attack_features=False, device='cpu'): super(BaseAttack, self).__init__() self.surrogate = model self.nnodes = nnodes self.attack_structure = attack_structure self.attack_features = attack_features self.device = device self.modified_adj = None self.modified_features = None if model is not None: self.nclass = model.nclass self.nfeat = model.nfeat self.hidden_sizes = model.hidden_sizes
[docs] def attack(self, ori_adj, n_perturbations, **kwargs): """Generate attacks on the input graph. Parameters ---------- ori_adj : scipy.sparse.csr_matrix Original (unperturbed) adjacency matrix. n_perturbations : int Number of edge removals/additions. Returns ------- None. """ pass
[docs] def check_adj(self, adj): """Check if the modified adjacency is symmetric and unweighted. """ assert np.abs(adj - adj.T).sum() == 0, "Input graph is not symmetric" assert adj.tocsr().max() == 1, "Max value should be 1!" assert adj.tocsr().min() == 0, "Min value should be 0!"
[docs] def check_adj_tensor(self, adj): """Check if the modified adjacency is symmetric, unweighted, all-zero diagonal. """ assert torch.abs(adj - adj.t()).sum() == 0, "Input graph is not symmetric" assert adj.max() == 1, "Max value should be 1!" assert adj.min() == 0, "Min value should be 0!" diag = adj.diag() assert diag.max() == 0, "Diagonal should be 0!" assert diag.min() == 0, "Diagonal should be 0!"
[docs] def save_adj(self, root=r'/tmp/', name='mod_adj'): """Save attacked adjacency matrix. Parameters ---------- root : root directory where the variable should be saved name : str saved file name Returns ------- None. """ assert self.modified_adj is not None, \ 'modified_adj is None! Please perturb the graph first.' name = name + '.npz' modified_adj = self.modified_adj if type(modified_adj) is torch.Tensor: sparse_adj = utils.to_scipy(modified_adj) sp.save_npz(osp.join(root, name), sparse_adj) else: sp.save_npz(osp.join(root, name), modified_adj)
[docs] def save_features(self, root=r'/tmp/', name='mod_features'): """Save attacked node feature matrix. Parameters ---------- root : root directory where the variable should be saved name : str saved file name Returns ------- None. """ assert self.modified_features is not None, \ 'modified_features is None! Please perturb the graph first.' name = name + '.npz' modified_features = self.modified_features if type(modified_features) is torch.Tensor: sparse_features = utils.to_scipy(modified_features) sp.save_npz(osp.join(root, name), sparse_features) else: sp.save_npz(osp.join(root, name), modified_features)