//  [python]
def _interest_fusion(self, X, A, layer, reuse):

        with tf.name_scope('CGAT'):
            ## CGAT Layer ('cluster_embedding'):

            #  当层数为两层时，所有节点的非零元素就变为了所有节点数，即完全图，所以问题出在图粗化的矩阵变换？
            #  pp1 = tf.sort(tf.cast(tf.count_nonzero(A, -1), tf.float32)[0], direction='DESCENDING', axis=-1)

            # add self-loop
            A_bool = tf.cast(tf.greater(A, 0), A.dtype)
            A_bool = A_bool * (tf.ones([self.max_n_nodes,self.max_n_nodes]) - tf.eye(self.max_n_nodes)) + tf.eye(self.max_n_nodes)

            # normalize graph
            # gat只需要对attention权重进行归一化，边权重保持为1即可，gcn才需要对邻接矩阵归一化。这里的A的归一化是为了后面的感受野的平均操作
            D = tf.reduce_sum(A_bool, axis=-1) # B*L
            D = tf.sqrt(D)[:, None] + K.epsilon() # B*1*L
            A = (A_bool / D) / tf.transpose(D, perm=(0,2,1)) # B*L*L / B*1*L / B*L*1

            # >>>>>> ablation_study wo/CGAT >>>>>>>>>>

            #  簇i感受野，一阶/二阶/三阶邻居平均，作为master query，x_q对应公式中的mi。一阶邻域包含自身，因为self-loop
            X_q = tf.matmul(A, X) # B*L*F
            X_q = tf.matmul(A, X_q)
            #  X_q = tf.matmul(A, X_q)

            # 公式6，分配矩阵S，计算簇i与节点j的隶属程度，也即attention score（X_q和X用来求S）
            if not self.query_shared:
                _, f_1 = self._attention_gat(X_q, X, 'f2_layer'+str(layer), False, return_alpha=True)
                _, f_2 = self._attention_fcn(self.target_item_embedding, X, 'f1_layer'+str(layer), False, return_alpha=True)
            if self.query_shared:
                _, f_1 = self._attention_gat(X_q, X, 'f2_shared', reuse, return_alpha=True)
                _, f_2 = self._attention_fcn(self.target_item_embedding, X, 'f1_shared', reuse, return_alpha=True)

            # i和j的隶属关系要限制在一阶邻域（而不是两两item都计算隶属度，而且这样后面embedding是根据所有item来聚合的）（fix前后效果差距明显）
            # 但A是归一化后的矩阵，原始是0/0～1，而非0/1的，所以要用A_bool。
            #  S = A * f_1 + A * tf.transpose(f_2, (0,2,1)) # B*L*1 x B*L*1 -> B*L*L
            #  S = A_bool * f_1 + A_bool * tf.transpose(f_2, (0,2,1)) # B*L*1 x B*L*1 -> B*L*L
            S = A_bool * tf.expand_dims(f_1,1) + A_bool * tf.transpose(tf.expand_dims(f_2,1), (0,2,1)) # B*L*1 x B*L*1 -> B*L*L

            ## >>>>>>> ablation_study token2token gat >>>>>>  用center node而非mean作为master
            # method1
            #  X_k = tf.layers.dense(X, 40)
            #  S = A * tf.matmul(X_k, tf.transpose(X_k, (0,2,1))) # B*L*L
            # method2
#              X_k = tf.layers.dense(X, 40)
            #  #  f_1 = tf.layers.dense(X_k, 1)  # B*L*F x F*1 -> B*L*1
            #  #  f_2 = tf.layers.dense(X_k, 1) # B*L*F x F*1 -> B*L*1
            #  f_1 = tf.squeeze(tf.layers.dense(X_k, 1),-1)  # B*L*F x F*1 -> B*L*1
            #  f_2 = tf.squeeze(tf.layers.dense(X_k, 1),-1) # B*L*F x F*1 -> B*L*1
            #  #  S = A_bool * f_1 + A_bool * tf.transpose(f_2, (0,2,1)) # B*L*1 x B*L*1 -> B*L*L
            #  S = A_bool * tf.expand_dims(f_1,1) + A_bool * tf.transpose(tf.expand_dims(f_2,1), (0,2,1)) # B*L*1 x B*L*1 -> B*L*L
            ## <<<<<<< ablation_study token2token gat <<<<<<

            ## >>>>>>> ablation_study master2token gat >>>>>>  用center node而非mean作为master
            #  X_q = tf.matmul(A, X) # B*L*F
            #  X_q = tf.layers.dense(X_q, 40)
            #  X_k = tf.layers.dense(X, 40)
            #  f_1 = tf.layers.dense(X_q, 1)  # B*L*F x F*1 -> B*L*1
            #  f_2 = tf.layers.dense(X_k, 1) # B*L*F x F*1 -> B*L*1
            #  S = A * f_1 + A * tf.transpose(f_2, (0,2,1)) # B*L*1 x B*L*1 -> B*L*L
            ## <<<<<<< ablation_study master2token gat <<<<<<

            ##  loss
            #  S_gram = tf.matmul(S, tf.transpose(S, (0,2,1)))
            #  LP_loss = A - S_gram
            #  LP_loss = tf.norm(LP_loss, axis=(-1, -2))
            #  self.LP_loss = K.mean(LP_loss)
#
            #  entr = tf.negative(tf.reduce_sum(tf.multiply(S, K.log(S + K.epsilon())), axis=-1))
            #  entr_loss = K.mean(entr, axis=-1)
            #  self.entr_loss = K.mean(entr_loss)

            S = tf.nn.leaky_relu(S)
            # 有0元素再经过softmax，变为非0，虽然很小，且后面graph粗化时候，有mask的过程，但是mask只会mask不要的节点。原始graph中两个需要节点之间的无边状态0，会被修正为非0元素。
            #  S = tf.nn.softmax(S, axis=-1)
            #  S = S * A_bool
            # softmax和bool改为如下形式
            boolean_mask = tf.equal(A_bool, tf.ones_like(A_bool))
            mask_paddings = tf.ones_like(S) * (-(2 ** 32) + 1)
            S = tf.nn.softmax(
                tf.where(boolean_mask, S, mask_paddings),
                #  name="att_weights",
                axis = -1
            )
            #  if self.is_train_stage:
            #  S = tf.nn.dropout(S, 1.0 - 0.5)
            Xc = tf.matmul(S, X) # B*L*L x B*L*F -> B*L*F
            #  Xc = tf.contrib.layers.bias_add(Xc)
            Xc += X
            Xc = tf.nn.leaky_relu(Xc)

            # <<<<<< ablation_study wo/CGAT <<<<<<<<<<

            ## >>>>>>> ablation_study GCN >>>>>> 
            #  简单GCN，权重即经过归一化的拉普拉斯矩阵矩阵
            #  Z = tf.layers.dense(X, 40, use_bias=False)
            #  Xc = tf.matmul(A, Z)
            #  Xc = tf.nn.leaky_relu(Xc)
            ## <<<<<<< ablation_study GCN <<<<<<

            ## >>>>>>> ablation_study GCN >>>>>> 
            #  加权GCN，余弦相似度卷积，权重即节点之间的余弦相似度
            #  X_norm = tf.nn.l2_normalize(X,dim=2)
            #  S = tf.matmul(X_norm, tf.transpose(X_norm, (0,2,1))) # B*L*L
            #  S = tf.nn.softmax(S, axis=-1)
            #  S = S * A_bool
            #  Xc = tf.matmul(S, X) # B*L*L x B*L*F -> B*L*F
            #  Xc = tf.nn.l2_normalize(Xc,dim=2)
            ## <<<<<<< ablation_study GCN <<<<<<

            ## >>>>>>> ablation_study GCN >>>>>> 
            ## 加权GCN, 概率簇余弦相似度的卷积
            #  X_clu = tf.matmul(A, X) # B*L*L x B*L*1 = B*L*1
            #  X_norm = tf.nn.l2_normalize(X,dim=2)
            #  X_clu_norm = tf.nn.l2_normalize(X_clu,dim=2)
            #  S = tf.matmul(X_clu_norm, tf.transpose(X_norm, (0,2,1))) # B*L*L
            #  S = tf.nn.softmax(S, axis=-1)
            #  S = S * A_bool
            #  Xc = tf.matmul(S, X) # B*L*L x B*L*F -> B*L*F
            #  Xc = tf.nn.l2_normalize(Xc,dim=2)
            ## <<<<<<< ablation_study GCN <<<<<<

            ## >>>>>>> ablation_study GCN >>>>>> 
            ## maybe Leconv: 局部极值卷积网络
            #  term1 = tf.layers.dense(X, 40, use_bias=False) # B*L*F x F*1 -> B*L*1
            #  term2 = tf.layers.dense(X, 40, use_bias=False) # B*L*F x F*1 -> B*L*1
            #  term2 = term2 * tf.reduce_sum(A, -1, keepdims=True) # B*L*1 x B*L*1 = B*L*1
            #  term3 = tf.layers.dense(X, 40, use_bias=False) # B*L*F x F*1 -> B*L*1
            #  term3 = tf.matmul(A, term3) # B*L*L x B*L*1 = B*L*1
            #  Xc = tf.nn.leaky_relu(term1 + term2 - term3, -1) # B*L*1 -> B*L
            ## <<<<<<< ablation_study GCN <<<<<<

            ## >>>>>>> ablation_study GCN >>>>>> 
            ## maybe, 局部注意力极值卷积网络
            #  X_k = tf.layers.dense(X, 40)
            #  f_1 = tf.layers.dense(X_k, 1)  # B*L*F x F*1 -> B*L*1
            #  f_2 = tf.layers.dense(X_k, 1) # B*L*F x F*1 -> B*L*1
            #  S = A_bool * f_1 + A_bool * tf.transpose(f_2, (0,2,1)) # B*L*1 x B*L*1 -> B*L*L
            #  S = tf.nn.leaky_relu(S)
            #  S = tf.nn.softmax(S, axis=-1)
            #  S = S * A_bool
            #  term1 = X
            #  term2 = X * tf.reduce_sum(S, -1, keepdims=True)
            #  term3 = tf.matmul(S, X) # B*L*L x B*L*1 = B*L*1
            #  X = tf.squeeze(term1 + term2 - term3, -1) # B*L*1 -> B*L
            ## <<<<<<< ablation_study GCN <<<<<<


        with tf.name_scope('AnPool'):
            ## QPool Layer  ('cluster_selection'):

            #  #  与target item做query，缺点是最终挑出来的都是与target item相似的item，不具有区分度。 我们同时考虑簇中心的份和相似度得分，将关键锚节点提取。
            #  #  _attention_fcn中的softmax将正负无穷置为0~1，下面的先mask再topk就没有问题
            cluster_score = f_1 + f_2

            ## >>>>>>> ablation_study Pool topk >>>>>> 
            #  分别只考虑‘簇中心得分’和‘query相关得分’
            #  从特征角度，当与自身的相似度越小（即与其它邻域相似度越高），该节点越成为簇的中心，越代表用户的核心兴趣，越该被留下来
            #  cluster_score = f_1
            #  从特征角度，只考虑query的得分
            #  cluster_score = f_2
            ## <<<<<<< ablation_study Pool topk <<<<<<

            ## >>>>>>> ablation_study Pool topk >>>>>> 
            ## 从图结构角度，根据度来确定topk节点
            # 非零元素数都是一样大的，因为阈值太小，所以是完全图，与节点数一致
            # 但最新的结果，差距也不大
            # 用这个，后面会在收敛的时候导致embedding都是一样的（反映在cluster_score都是一样的，即节点数量分之一），然后greater_equal导致并不会缩小图的规模。
            #  cluster_score = tf.cast(tf.count_nonzero(A, -1), tf.float32)
            #  cluster_score = tf.nn.softmax(cluster_score, axis=-1)
            ## <<<<<<< ablation_study Pool topk <<<<<<

            ## >>>>>>> ablation_study Pool topk >>>>>> 
            #  从特征+图结构角度，用LEConv (metrix version)根据自身结构获得topk重要节点，不考虑外部query
            #  term1 = tf.layers.dense(Xc, 1, use_bias=False) # B*L*F x F*1 -> B*L*1
            #  term2 = tf.layers.dense(Xc, 1, use_bias=False) # B*L*F x F*1 -> B*L*1
            #  term2 = term2 * tf.reduce_sum(A, -1, keepdims=True) # B*L*1 x B*L*1 = B*L*1
            #  term3 = tf.layers.dense(Xc, 1, use_bias=False) # B*L*F x F*1 -> B*L*1
            #  term3 = tf.matmul(A, term3) # B*L*L x B*L*1 = B*L*1
            #  #  cluster fitness
            #  cluster_score = tf.squeeze(term1 + term2 - term3, -1) # B*L*1 -> B*L
            #  # sigmoid就是对所有元素执行一个0-1的压缩，并非进行归一化，所以先squeeze还是后squeeze都是一样的。
            #  cluster_score = tf.sigmoid(cluster_score)
            #  # sigmoid/softmax将正负无穷置为0~1，下面的先mask再topk就没有问题
            #  #  cluster_score = tf.nn.softmax(cluster_score, axis=-1) # B*L
            ## <<<<<<< ablation_study Pool topk <<<<<<

            #  对传播得到的锚节点/簇进行weighted readout（只readout筛选后的item，效果更好）
            cluster_fea = tf.reduce_sum(X*tf.expand_dims(cluster_score,-1), 1)
            #  cluster_fea = tf.reduce_sum(Xc*tf.expand_dims(cluster_score,-1), 1) # 和上面差不多
            #  cluster_fea = tf.reduce_sum(Xc*tf.expand_dims(self.real_mask, -1), 1) # 效果极差

            # Multiply X and y to make layer differentiable，这句是tokpool的实现中说的
            # 看看效果如何，好的话后面就直接对X进行sum pooling再输出到最后就行
            #  #  X = X * K.sigmoid(cluster_score)
            #  X2 = X2 * tf.expand_dims(cluster_score, -1)
            #  # 或者直接用cluster_output
            # 效果不好，会影响后面的agru
            #  X2 = cluster_output

            # >>>>>> sagpool 利用gcn对节点进行打分
            #  Z = tf.layers.dense(X, 1, use_bias=False) # B*L*F x F*1 -> B*L*1
            #  # score有可能是负值
            #  node_score = tf.squeeze(tf.matmul(A, Z), -1) # B*L*L x B*L*1 = B*L*1 -> B*L

            # >>>>>> diffpool，训练两个矩阵，一个是特征变换矩阵，一个是分配矩阵
            #  Z = tf.layers.dense(X, 40, use_bias=False)
            #  Z = tf.matmul(A, Z)
            #  # k是每层固定的池化率（对于我们来说不合适，我们需要按每个序列的长度来池化）
            #  S = tf.layers.dense(X, k, use_bias=False)
            #  S = tf.matmul(A, S)
            #  # 对分配矩阵加权
            #  _, alphas = self._attention(self.target_item_embedding, X, var_id = str(k), return_alpha=True)
            #  S = S * tf.expand_dims(alphas, -1)
            #  S = tf.keras.activations.softmax(S, axis=-1)

            #  # TODO 辅助loss：get auxiliary loss
            #  # Link prediction loss
            #  #  第一个正则表示的是辅助链路预测目标。
            #  S_gram = tf.matmul(S, tf.transpose(S, (0,2,1)))
            #  LP_loss = A - S_gram
            #  LP_loss = tf.norm(LP_loss, axis=(-1, -2))
            #  LP_loss = K.mean(LP_loss)
            #  self.auxiliary_loss += LP_loss
            #  # Entropy loss
            #  #  第二个正则表示尽量让每一行的节点分配接近one-hot向量，即一个节点分配到一个cluster内。
            #  # 我们基于attention计算的loss也可以考虑这个
            #  entr = tf.negative(tf.reduce_sum(tf.multiply(S, K.log(S + K.epsilon())), axis=-1))
            #  entr_loss = K.mean(entr, axis=-1)
            #  entr_loss = K.mean(entr_loss)
            #  self.auxiliary_loss += entr_loss

            #  # pooled
            #  X = tf.matmul(tf.transpose(S, (0,2,1)), Z)
            #  A = tf.matmul(
            #      tf.matmul(
            #          tf.transpose(S, (0,2,1)),
            #          A),
            #      S)
            # <<<<<< diffpool，训练两个矩阵，一个是特征变换矩阵，一个是分配矩阵

            # 不用softmax，用min-max归一化，softmax不适合mask场景
            # TODO 上面改写为bool后，这里就不需要了,需要
            #  # 忽略了一个问题，余弦相似度是-1~1的，后面还有mask的过程，所以这里应该先min-max归一化（非余弦相似度计算得到的score也能用），min-max标准化比softmax更适合mask场景
            #  cluster_score = (cluster_score - tf.reduce_min(cluster_score, -1, keepdims=True)) / (tf.reduce_max(cluster_score, -1, keepdims=True) - tf.reduce_min(cluster_score, -1, keepdims=True))
#

            ## 提取topk/topthread节点数
            # 提取ratio比率的高分节点（即，将其余节点mask掉）
            #  num_nodes = tf.reduce_sum(self.mask, 1) # B
            #  to_keep = tf.math.ceil(self.pool_ratio * tf.cast(num_nodes, tf.float32)) # B

            # future 根据最终目标的兴趣序列长度，决定每次选择的topk
            num_nodes = tf.reduce_sum(self.mask, 1) # B
            boolean_pool = tf.greater(num_nodes, self.pool_length)
            to_keep = tf.where(boolean_pool, 
                               tf.cast(self.pool_length + (self.real_sequence_length - self.pool_length)/self.pool_layers*(self.pool_layers-layer-1), tf.int32), 
                               num_nodes)  # B

            ## update mask
            ## 更新topk节点数量的mask，每个graph的topk不同
            cluster_score = cluster_score * self.real_mask # B*L
            if 'kwai' in socket.gethostname():
                sorted_score = tf.contrib.framework.sort(cluster_score, direction='DESCENDING', axis=-1) # B*L
            else:
                sorted_score = tf.sort(cluster_score, direction='DESCENDING', axis=-1) # B*L
            if 'kwai' in socket.gethostname():
            #  if 'rl3' in socket.gethostname():
                n_graphs = tf.shape(Xc)[0] 
                target_index = tf.stack([tf.range(n_graphs), tf.cast(to_keep, tf.int32)], 1) # B*2
                target_score = tf.gather_nd(sorted_score, target_index) # indices[:-1]=(B) + data[indices[-1]=() --> (B)
            else:
                # 为了避免搜到mask的零元素，但缺点在于如果score学到的都是一样的分数，则一个节点也拿不出来了
                #  target_score = tf.gather_nd(sorted_score, tf.expand_dims(tf.cast(to_keep, tf.int32), -1), batch_dims=1)
                target_score = tf.gather_nd(sorted_score, tf.expand_dims(tf.cast(to_keep, tf.int32), -1), batch_dims=1) + K.epsilon()
            #  topk_mask = tf.greater_equal(cluster_score, tf.expand_dims(target_score, -1)) # B*L + B*1 -> B*L
            topk_mask = tf.greater(cluster_score, tf.expand_dims(target_score, -1)) # B*L + B*1 -> B*L
            #  topk_mask = tf.greater(cluster_score, tf.expand_dims(target_score, -1)) # B*L + B*1 -> B*L
            self.mask = tf.cast(topk_mask, tf.int32)
            self.real_mask = tf.cast(self.mask, tf.float32)
            self.sequence_length = tf.reduce_sum(self.mask, 1)

            #  对传播得到的锚节点/簇进行weighted readout（只readout筛选后/mask的node/cluster，效果更好）
            #  cluster_fea = tf.reduce_sum(X*tf.expand_dims(cluster_score,-1)*tf.expand_dims(self.real_mask, -1), 1)

            #  pp1 = tf.reduce_max(tf.reduce_sum(self.mask, 1))
            #  pp2 = tf.shape(self.mask)
            #  pp3 = tf.shape(X)
            #  self.pp1=tf.Print(pp1,["pp1:", pp1]) # 要对应名称，因为执行顺序可能不一样
            #  self.pp2=tf.Print(pp2,["pp2:", pp2])
            #  self.pp3=tf.Print(pp3,["pp3:", pp3])

            #  pp1 = num_nodes
            #  pp2 = to_keep
            #  pp3 = tf.reduce_mean(num_nodes)
            #  pp4 = tf.reduce_mean(to_keep)
            #  pp5 = tf.reduce_sum(self.mask, 1) # B
            #  pp6 = tf.reduce_mean(tf.reduce_sum(self.mask, 1))
            
            #  [pp1:][20 15 16...]
            #  [pp2:][8 6 7...]
            #  [pp3:][24]
            #  [pp4:][10.0196]
            #  [pp5:][20 15 16...]
            #  [pp6:][24]

            #  pp1 = tf.shape(target_index)
            #  pp2 = tf.shape(target_score)
            #  [pp1:][2500 2]
            #  [pp2:][2500]

            #  pp1 = target_score
            #  pp2 = tf.reduce_mean(target_score)
            #  [pp1:][0.0714285746 0.0833333358 0.1...]
            #  [pp2:][0.057143867]

            #  pp1 = target_score
            #  pp2 = tf.reduce_max(cluster_score, axis=-1)
            #  pp3 = tf.reduce_min(cluster_score, axis=-1)
            #  pp4 = tf.reduce_sum(cluster_score, axis=-1)
            #  pp5 = tf.reduce_mean(cluster_score, axis=-1)
            #  [pp1:][0.0833333358 0.0769230798 0.0833333358...]
            #  [pp2:][0.0833333358 0.0769230798 0.0833333358...]
            #  [pp3:][0 0 0...]
            #  [pp4:][1 1.00000012 1...]
            #  [pp5:][0.02] # 1/50

            #  pp1 = tf.sort(cluster_score[0], direction='DESCENDING', axis=-1)
            #  pp1 = tf.sort(A[0][0], direction='DESCENDING', axis=-1)
            #  pp2 = tf.sort(tf.cast(tf.count_nonzero(A, -1), tf.float32)[0], direction='DESCENDING', axis=-1)
            #  pp2 = tf.reduce_sum(cluster_score[0])/tf.cast(num_nodes[0],tf.float32)
            #  pp2 = tf.sort(cluster_score[0], direction='ASCENDING', axis=-1)
            #  pp2 = tf.sort(A[0][0], direction='ASCENDING', axis=-1)
            #  pp3 = pp1[3:6]
            #  pp3 = self.sequence_length[0] # 调用的是其它执行图的数据，所以和num_nodes结果不一致。
            #  pp4 = pp1[7:10]
            #  pp5 = target_score
            #  pp5 = tf.reduce_sum(self.mask, 1)
            #  pp6 = num_nodes[0]

            #  self.pp1=tf.Print(pp1,["pp1:", pp1]) # 要对应名称，因为执行顺序可能不一样
            #  self.pp2=tf.Print(pp2,["pp2:", pp2])
            #  self.pp3=tf.Print(pp3,["pp3:", pp3])
            #  self.pp4=tf.Print(pp4,["pp4:", pp4])
            #  self.pp5=tf.Print(pp5,["pp5:", pp5])
            #  self.pp6=tf.Print(pp6,["pp6:", pp6])
#
            if self.watch_A_S_A:
                #  当一层时，这个地方的所有节点的非零元素还正常
                pp4 = tf.sort(tf.cast(tf.count_nonzero(A, -1), tf.float32)[0], direction='DESCENDING', axis=-1)
                #  pp1 = tf.count_nonzero(A[0])
                pp1 = tf.count_nonzero(tf.count_nonzero(A[0], -1), -1)
                self.pp4=tf.Print(pp4,["pp4:", pp4])
                self.pp1=tf.Print(pp1,["pp1:", pp1]) # 要对应名称，因为执行顺序可能不一样

            #### 将粗化改为度量学习 >>>>>>>>>> 
            ## graph_coarsening
            ## 根据新mask提取分配矩阵，再根据分配矩阵粗化图，确保连通性，而非生硬的mask原矩阵（之前少了一次转置的mask，因为后面还有S的转置）
            #  S = S * tf.expand_dims(self.real_mask, -1)  # B*L*L x B*L*1(mask: B*C*1) = B*L*L(mask: B*C*L)
            # 这里的mask和之前根据A_bool的mask都是必要的
            S = S * tf.expand_dims(self.real_mask, -1) * tf.expand_dims(self.real_mask, -2)

            if self.watch_A_S_A:
                # S的度比A的度分布小，因为在S乘了一个A_bool之后是一致的，但又更新了mask，所以整体上应该更小一些。
                pp5 = tf.sort(tf.cast(tf.count_nonzero(S, -1), tf.float32)[0], direction='DESCENDING', axis=-1)
                self.pp5=tf.Print(pp5,["pp5:", pp5])
                #  pp2 = tf.count_nonzero(S[0])
                pp2 = tf.count_nonzero(tf.count_nonzero(S[0], -1), -1)
                self.pp2=tf.Print(pp2,["pp2:", pp2])

            # 下面执行之后[pp1:][18 17 17...] 变为 [pp5:][21 21 21...](50,50)
            #  A = tf.matmul(tf.matmul(S, A),
            # 这里应该改为A_bool（添加self-loop后的），簇的分配主要看分配矩阵的权重，而非原始图的边权重，只要看边有没有就行了
            A = tf.matmul(tf.matmul(S, A_bool),
                          tf.transpose(S, (0,2,1))) # B*C*L x B*L*L x B*L*C = B*C*C
            # 下面绝对是错的，执行之后变为(50,50)
            #  A = tf.matmul(tf.matmul(tf.transpose(S, (0,2,1)), A),
                          #  S) # B*C*L x B*L*L x B*L*C = B*C*C
            #  A = A * tf.expand_dims(self.real_mask, -1) * tf.expand_dims(self.real_mask, -2)

            # 为了避免越发dense，进行边的提取，利用前边的超参（加了之后，A的节点度就不再完全一样了，即不再是完全图）
            # TODO 不提取看看效果，速度超慢，完全图，太dense了
            A_flatten = tf.reshape(A, [tf.shape(A)[0],-1])
            if 'kwai' in socket.gethostname():
                sorted_A_flatten = tf.contrib.framework.sort(A_flatten, direction='DESCENDING', axis=-1) # B*L -> B*L
            else:
                sorted_A_flatten = tf.sort(A_flatten, direction='DESCENDING', axis=-1) # B*L -> B*L
            # 上面必须更新sequence_length的长度，不然找到的threshold_score就是0，体现为提取到所有50个节点
            #  num_edges = tf.cast(self.sequence_length * self.sequence_length, tf.float32)
            # 这种边的数量的前提是完全图，所以不合理，很容易取到0元素，所以还是用count_nonzero，对每个batch进行count
            num_edges = tf.cast(tf.count_nonzero(A, [1,2]), tf.float32) # B
            to_keep_edge = tf.cast(tf.math.ceil(num_edges * (1-self.threshold)), tf.int32)
            if 'kwai' in socket.gethostname():
            #  if 'rl3' in socket.gethostname():
                n_graphs = tf.shape(X)[0]
                threshold_index = tf.stack([tf.range(n_graphs), tf.cast(to_keep_edge, tf.int32)], 1) # B*2
                #  threshold_score = tf.batch_gather(sorted_A_flatten, threshold_index) # indices[:-1]=(B) + data[indices[-1]=() --> (B)
                threshold_score = tf.gather_nd(sorted_A_flatten, threshold_index) # indices[:-1]=(B) + data[indices[-1]=() --> (B)
            else:
                threshold_score = tf.gather_nd(sorted_A_flatten, tf.expand_dims(tf.cast(to_keep_edge, tf.int32), -1), batch_dims=1) # indices[:-1]=(B) + data[indices[-1]=() --> (B)
            A = tf.cast(tf.greater_equal(A, tf.expand_dims(tf.expand_dims(threshold_score, -1), -1)), tf.float32)

            if self.watch_A_S_A:
                # 这里S的节点度的分布应该平均比粗化前的A的节点度的分布要小，刚开始确实是这样，但是后面的时候就基本和A一致了，说明S学成了全1矩阵，也不是，可能是attention学成了一样，然后greater_equal全部取了出来
                #  当一层时，这里所有节点的非零元素就变为了所有节点数，即完全图，所以问题的确出在图粗化的矩阵变换
                # 这个应该比上面的A和S的分布还要小。早起step的时候还正常，后面就每个节点的度都趋向于一样，即完全图了
                pp6 = tf.sort(tf.cast(tf.count_nonzero(A, -1), tf.float32)[0], direction='DESCENDING', axis=-1)
                self.pp6=tf.Print(pp6,["pp6:", pp6])
                #  pp3 = tf.count_nonzero(A[0])
                # 统计非零行/列，1)和上面每个节点的度都是一样的，说明就是完全图；2)和上面的S的非零行是一样的，说明实现无误，问题出在我们的图很小，所以这种处理方式最终相当于三跳相连，如果在thread步上生成的图很dense的话，这里三跳之后就可能是完全图了。
                # 但再设计一个超参不合理，所以这里可以和前面threshold联动，如果前面小（因为取top threshold，所以相当于门槛高），则图稀疏，这里则池化后门槛可以设置的低一些，即top threshold应该取大一点
                # 这样的话，其实对这个超参不敏感了，前后联动，会抵消影响
                pp3 = tf.count_nonzero(tf.count_nonzero(A[0], -1), -1)
                self.pp3=tf.Print(pp3,["pp3:", pp3])
#
            ## >>>>>>> ablation_study wo / weighted gcn >>>>>>
            # 上面的A为0/1，S为0~1；这里S为0/1，A为0~1；
            #  S = tf.ones_like(A)
            #  S = S * tf.expand_dims(self.real_mask, -1)  # B*L*L x B*L*1(mask: B*C*1) = B*L*L(mask: B*C*L)
            #  A = tf.matmul(tf.matmul(S, A),
                          #  tf.transpose(S, (0,2,1))) # B*C*L x B*L*L x B*L*C = B*C*C
            ## <<<<<<< ablation_study weighted gcn <<<<<<

            ## >>>>>>> wo/S, 生硬的mask原矩阵 >>>>>>
            #  A = A * tf.expand_dims(self.real_mask, -1)  # B*L*L x B*L*1(mask: B*C*1) = B*L*L(mask: B*C*L)

            ## <<<<<<<  wo/S, 生硬的mask原矩阵 <<<<<<

        #  return X, A
        return Xc, A, cluster_fea, cluster_score