File size: 11,101 Bytes
2d448dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
from .MyUtils import color_print, pyg_data_to_dgl_graph

import torch

import dgl
from dgl.data import FraudAmazonDataset, FraudYelpDataset

import torch_geometric
from torch_geometric.transforms import GDC
from torch_geometric.data import Data, Dataset, DataLoader
from torch_geometric.utils import to_dense_adj, dense_to_sparse

from sklearn.model_selection import train_test_split

from tqdm import tqdm

def splitDataset(index, label, train_ratio):
    idx_train, idx_rest, y_train, y_rest = train_test_split(index, label[index], stratify=label[index],
                                                            train_size=train_ratio,
                                                            random_state=2, shuffle=True)
    idx_valid, idx_test, y_valid, y_test = train_test_split(idx_rest, y_rest, stratify=y_rest,
                                                            test_size=0.5,
                                                            random_state=2, shuffle=True)
    train_mask = torch.zeros([len(label)]).bool()
    val_mask = torch.zeros([len(label)]).bool()
    test_mask = torch.zeros([len(label)]).bool()
    train_mask[idx_train] = 1
    val_mask[idx_valid] = 1
    test_mask[idx_test] = 1

    return train_mask, val_mask, test_mask

def nodeSelect(graph_pyg, nodes_per_subgraph):
    num = graph_pyg.x.shape[0]
    num_selected = num - num % nodes_per_subgraph

    if num_selected:
        feat_selected=graph_pyg.x[:num_selected]
        label_selected=graph_pyg.y[:num_selected]
        train_mask_selected=graph_pyg.train_mask[:num_selected]
        test_mask_selected=graph_pyg.test_mask[:num_selected]
        val_mask_selected=graph_pyg.val_mask[:num_selected]
        
        edge_index_selected_mask=(graph_pyg.edge_index[0] < num_selected) & (graph_pyg.edge_index[1] < num_selected)
        edge_index_unselected=graph_pyg.edge_index[:,~edge_index_selected_mask]
        edge_index_selected=graph_pyg.edge_index[:,edge_index_selected_mask]

        graph_pyg_selected=Data(x=feat_selected,
                                edge_index=edge_index_selected,
                                y=label_selected,
                                train_mask=train_mask_selected,
                                val_mask=val_mask_selected,
                                test_mask=test_mask_selected)
        
        print(f'num_seleced: {num_selected}/{num}')

    return graph_pyg_selected, edge_index_unselected

def nodeSample(graph_pyg, nodes_per_subgraph):
    color_print(f'!!!!! Start node sampling')
    num = graph_pyg.x.shape[0]
    num_selected = num - num % nodes_per_subgraph

    subgraphs=[]

    for i in tqdm(range(num_selected//nodes_per_subgraph)):
        start_idx = i * nodes_per_subgraph
        
        subset=torch.arange(start_idx,start_idx+nodes_per_subgraph)
        
        sub_edge_index = torch_geometric.utils.subgraph(subset,graph_pyg.edge_index,num_nodes=graph_pyg.num_nodes)[0] - i*nodes_per_subgraph
        sub_x=graph_pyg.x[start_idx:start_idx + nodes_per_subgraph]
        sub_new_x=graph_pyg.new_x[start_idx:start_idx + nodes_per_subgraph]
        sub_y=graph_pyg.y[start_idx:start_idx + nodes_per_subgraph]
        sub_adj=to_dense_adj(sub_edge_index,max_num_nodes=nodes_per_subgraph)[0]
        sub_train_mask=graph_pyg.train_mask[start_idx:start_idx+nodes_per_subgraph]
        sub_val_mask=graph_pyg.val_mask[start_idx:start_idx+nodes_per_subgraph]
        sub_test_mask=graph_pyg.test_mask[start_idx:start_idx+nodes_per_subgraph]

        sub_data=Data(x=sub_x,new_x=sub_new_x,edge_index=sub_edge_index,y=sub_y,adj=sub_adj,train_mask=sub_train_mask,val_mask=sub_val_mask,test_mask=sub_test_mask)

        subgraphs.append(sub_data)
    
    color_print(f'!!!!! Finish node sampling')

    return subgraphs

def sampleCheck(node_groups, nodes_per_subgraph):
    cnt=0
    wrong_num=0
    for g in node_groups:
        if g.x.shape[0]!=nodes_per_subgraph:
            color_print(g.x.shape[0],cnt)
            color_print(f'!!!!! Sampling check false')
            wrong_num=wrong_num+1
        cnt=cnt+1
    if not wrong_num:
        color_print(f'!!!!! Sampling check true')

def loadDataset(dataset, train_ratio):
    if dataset=='yelp':
        graph_dgl = FraudYelpDataset()[0]
        graph_dgl = dgl.to_homogeneous(graph_dgl,ndata=['feature', 'label', 'train_mask', 'val_mask', 'test_mask'])
        graph_dgl = dgl.add_self_loop(graph_dgl)

        feat = graph_dgl.ndata['feature'].float()
        label = graph_dgl.ndata['label'].long()
        edge_index = torch.stack(graph_dgl.edges(etype=('_N', '_E', '_N')))
        index = list(range(len(label)))

        train_mask, val_mask, test_mask = splitDataset(index,label,train_ratio)

        graph_pyg=Data(x=feat,edge_index=edge_index,y=label,train_mask=train_mask,val_mask=val_mask,test_mask=test_mask)
        graph_dgl.ndata['train_mask']=train_mask
        graph_dgl.ndata['val_mask']=val_mask
        graph_dgl.ndata['test_mask']=test_mask

    elif dataset=='amazon':
        graph_dgl = FraudAmazonDataset()[0]
        graph_dgl = dgl.to_homogeneous(graph_dgl,ndata=['feature', 'label', 'train_mask', 'val_mask', 'test_mask'])
        graph_dgl = dgl.add_self_loop(graph_dgl)

        feat = graph_dgl.ndata['feature'].float()
        label = graph_dgl.ndata['label'].long()
        edge_index = torch.stack(graph_dgl.edges(etype=('_N', '_E', '_N')))
        index = list(range(3305, len(label)))

        train_mask, val_mask, test_mask = splitDataset(index,label,train_ratio)

        graph_pyg=Data(x=feat,edge_index=edge_index,y=label,train_mask=train_mask,val_mask=val_mask,test_mask=test_mask)
        graph_dgl.ndata['train_mask']=train_mask
        graph_dgl.ndata['val_mask']=val_mask
        graph_dgl.ndata['test_mask']=test_mask

    elif dataset=='blogcatalog':
        bc_path = f'../bc.pt'
        graph_pyg = torch.load(bc_path)[0]
        graph_pyg.edge_index = graph_pyg.edge_indexes[0]
        graph_dgl = pyg_data_to_dgl_graph(graph_pyg)

        label = graph_dgl.ndata['label'].long()
        index = list(range(len(label)))
        train_mask, val_mask, test_mask = splitDataset(index,label,train_ratio)

        graph_pyg.train_mask=train_mask
        graph_pyg.val_mask=val_mask
        graph_pyg.test_mask=test_mask
        graph_dgl.ndata['train_mask']=train_mask
        graph_dgl.ndata['val_mask']=val_mask
        graph_dgl.ndata['test_mask']=test_mask
    else:
        print(f'{dataset}-graph_dgl not exist')
        return None

    
    color_print(f'{dataset}_graph_dgl: ')
    print(f'{graph_dgl}')
    color_print(f'{dataset}_graph_pyg: ')
    print(f'{graph_pyg}')
    color_print(f'train_val_test split')
    print(f'train_num: {train_mask.sum()}; val_num: {val_mask.sum()}; test_num: {test_mask.sum()}')

    return graph_dgl, graph_pyg, train_mask, val_mask, test_mask

def mergeGraphDataList(args, graph_pyg, syn_relation_dict):
    # 初始化合并后的组件
    x, y, new_x, edge_index = [], [], [], []
    adj_matrices = []
    ddpm_train_mask, ddpm_val_mask, ddpm_test_mask = [], [], []
    
    cum_num_nodes = 0  # 累积节点数,用于更新edge_index

    new_x_ssupgcl=syn_relation_dict['graph_pyg_ssupgcl_new_x']

    for data in syn_relation_dict['syn_relation_list']:
    # for data in syn_relation_dict['new_data_list']:
        # 更新节点特征和标签
        x.append(data.x)
        new_x.append(data.new_x)
        y.append(data.y)
        
        # 更新edge_index
        current_edge_index = data.edge_index + cum_num_nodes
        edge_index.append(current_edge_index)
        
        # 更新邻接矩阵
        adj_matrices.append(data.adj)
        
        # 更新掩码
        ddpm_train_mask.append(data.train_mask)
        ddpm_val_mask.append(data.val_mask)
        ddpm_test_mask.append(data.test_mask)
        
        # 更新节点累计数
        cum_num_nodes += data.num_nodes

    node_per_subgraph=args.nodes_per_subgraph
    num_unselected=graph_pyg.x.shape[0] % node_per_subgraph
    num_unselected

    # 合并所有组件
    new_x = torch.cat(new_x, dim=0)
    new_x = torch.cat([new_x, new_x_ssupgcl[-num_unselected:]],dim=0)

    x = torch.cat(x, dim=0)
    x = torch.cat([x, graph_pyg.x[-num_unselected:]],dim=0)
    y = torch.cat(y, dim=0)
    y = torch.cat([y, graph_pyg.y[-num_unselected:]],dim=0)
    edge_index = torch.cat(edge_index, dim=1)
    # edge_index = torch.cat([edge_index, ddpm_yelp_unselected_edge_index], dim=1)
    edge_index = torch.cat([edge_index, syn_relation_dict['unselected_edge_index']], dim=1)
    # edge_index = torch.cat([edge_index, syn_relation_dict['yelp_unselected_edge_index']], dim=1)
    ddpm_train_mask = torch.cat(ddpm_train_mask, dim=0)
    ddpm_train_mask = torch.cat([ddpm_train_mask, graph_pyg.train_mask[-num_unselected:]], dim=0)
    ddpm_val_mask = torch.cat(ddpm_val_mask, dim=0)
    ddpm_val_mask = torch.cat([ddpm_val_mask, graph_pyg.val_mask[-num_unselected:]], dim=0)
    ddpm_test_mask = torch.cat(ddpm_test_mask, dim=0)
    ddpm_test_mask = torch.cat([ddpm_test_mask, graph_pyg.test_mask[-num_unselected:]], dim=0)

    # 创建新的Data对象
    if args.dataset=='blogcatalog' and args.GuiDDPM_sample_with_guidance: # blogcatalog 要降维
        merged_data = Data(x=new_x, edge_index=edge_index, y=y, train_mask=ddpm_train_mask, val_mask=ddpm_val_mask, test_mask=ddpm_test_mask)
    else:
        merged_data = Data(x=x, edge_index=edge_index, y=y, train_mask=ddpm_train_mask, val_mask=ddpm_val_mask, test_mask=ddpm_test_mask)
    
    return merged_data
    
def GDCAugment(graph_pyg_type, avg_degree):
    # 应用 GDC 转换
    gdc = GDC(self_loop_weight=1, normalization_in='sym',
            normalization_out='col', diffusion_kwargs=dict(method='ppr', alpha=0.05),
            sparsification_kwargs=dict(method='threshold', avg_degree=avg_degree), exact=True)
    graph_pyg_type.transform = gdc
    gdc_transformed_data = gdc(graph_pyg_type)  # 应用 GDC 转换
    
    return gdc_transformed_data


def data4WFusionTrain(graph_pyg, graph_syn, graph_gdc_list):
    new_edges_dict = {}
    new_edges_dict[('node', 'relation0', 'node')] = (graph_pyg.edge_index[0], graph_pyg.edge_index[1])
    new_edges_dict[('node', 'relation1', 'node')] = (graph_syn.edge_index[0], graph_syn.edge_index[1])

    for idx, graph_gdc in enumerate(graph_gdc_list, start=2):
        new_edges_dict[(f'node', f'relation{idx}', f'node')] = (graph_gdc.edge_index[0], graph_gdc.edge_index[1])

    # 设置节点总数
    num_nodes = graph_syn.x.shape[0]

    # 创建异构图时显式指定节点的数量
    graph_WFusion = dgl.heterograph(new_edges_dict, num_nodes_dict={'node': num_nodes})
    
    # 设置节点特征和标签
    graph_WFusion.nodes['node'].data['feature'] = graph_syn.x
    graph_WFusion.nodes['node'].data['label'] = graph_syn.y
    graph_WFusion.nodes['node'].data['train_mask'] = graph_syn.train_mask
    graph_WFusion.nodes['node'].data['test_mask'] = graph_syn.test_mask
    graph_WFusion.nodes['node'].data['val_mask'] = graph_syn.val_mask

    return graph_WFusion