Thank you for the reply!
DGLGraph(num_nodes=2708, num_edges=13232,
ndata_schemes={}
edata_schemes={}) torch.Size([2708, 1433])
I am using the following function to create structural perturbations in the graph like adding new edges and deleting existing ones.
def DICE(self, g, labels, ptb_ratio):
"""DICE (disconnect internally, connect externally) is a heuristic structure perturbation
where, for each perturbation, we randomly choose whether to insert or remove an edge.
Edges are only removed between nodes from the same classes,
and only inserted between nodes from different classes.
This function perturbs the graph methodically for non-targeted attacks.
It is used for poisoning/evasion attacks on graphs. The graph is perturbed first
and then used for training in case of poisoning. For evasion, the original graph
is trained first and then perturbed and tested on
:param g: DGL Graph
:param labels: labels for the nodes
:param ptb_ratio: structural perturbation ratio
:return: Perturbed DGL Graph
"""
if ptb_ratio == 0:
return g
total_edges = g.number_of_edges()
total_nodes = g.number_of_nodes()
num_perturbations = int(ptb_ratio * (total_edges - total_nodes) / 2)
# dividing by 2 since dgl graph edges are directional
print("Number of Perturbations to Graph:", num_perturbations)
# choose number edges to remove first out of total perturbations
edge_removal = np.random.choice(2, num_perturbations )
num_removal = edge_removal.sum()
# edge removal first, for edges with same labels
all_edges = g.edges()
# all_edges_eids = g.edge_ids(all_edges[0], all_edges[1])
zipped_all_edges = list(zip(*all_edges))
# dictionary or edge id storing
# all_edges_dict = {edge:eid for eid,edge in enumerate(zipped_all_edges)}
possible_removal_candidates = list(filter(lambda edge:
labels[edge[0].item()] == labels[edge[1].item()] and
edge[0] != edge[1],
zipped_all_edges ))
random.shuffle(possible_removal_candidates)
remove_edges_src_dst = possible_removal_candidates[:num_removal]
remove_edge_dst_src = [(e[1],e[0]) for e in remove_edges_src_dst]
remove_edges = remove_edges_src_dst + remove_edge_dst_src
edges_src, edges_dst = list(zip(*remove_edges))
check_edges = (g.has_edges_between(edges_src, edges_dst))
check_edges_mask = check_edges.numpy().astype(np.bool)
edges_src = np.array(edges_src)
edges_dst = np.array(edges_dst)
edges_src = edges_src[check_edges_mask]
edges_dst = edges_dst[check_edges_mask]
remove_edges_eid = g.edge_ids(edges_src, edges_dst)
g.remove_edges(remove_edges_eid)
# choose remaining edges to be added
num_additions = num_perturbations - num_removal
src_nodes = np.random.choice(total_nodes, size=num_additions*2)
dst_nodes = np.random.choice(total_nodes, size=num_additions*2)
check_edges = (g.has_edges_between(src_nodes, dst_nodes))
check_edges_mask = check_edges.numpy().astype(np.bool)
src_nodes = src_nodes[np.logical_not(check_edges_mask)]
dst_nodes = dst_nodes[np.logical_not(check_edges_mask)]
possible_addition_candidates = list(filter(lambda edge:
labels[edge[0]] != labels[edge[1]] and
edge[0] != edge[1],
zip(src_nodes, dst_nodes) ))
random.shuffle(possible_addition_candidates)
additional_edges = possible_addition_candidates[:num_additions]
src_nodes, dst_nodes = list(zip(*additional_edges))
g.add_edges(src_nodes, dst_nodes)
g.add_edges(dst_nodes, src_nodes)
return g
If I comment out the line “g.remove_edges(remove_edges_eid)” everything works fine. The problem is seen only when I remove existing edges. Addition of edges is not creating problems.