def positional_encoding(g, pos_enc_dim, DATASET_NAME=None): """ Graph positional encoding v/ Laplacian eigenvectors """ # Laplacian,for the pyg try: g.pos_enc = torch.load('data/ogbn/laplacian_' + DATASET_NAME[5:] + '.pt', map_location='cpu') if g.pos_enc.size(1) != pos_enc_dim: os.remove('data/ogbn/laplacian_' + DATASET_NAME[5:] + '.pt') L = get_laplacian(g.edge_index, normalization='sym', dtype=torch.float64) L = csr_matrix((L[1], (L[0][0], L[0][1])), shape=(g.num_nodes, g.num_nodes)) # Eigenvectors with scipy # EigVal, EigVec = sp.linalg.eigs(L, k=pos_enc_dim+1, which='SR') EigVal, EigVec = sp.linalg.eigs(L, k=pos_enc_dim + 1, which='SR', tol=1e-2) # for 40 PEs EigVec = EigVec[:, EigVal.argsort()] # increasing order g.pos_enc = torch.from_numpy(EigVec[:, 1:pos_enc_dim + 1].astype( np.float32)).float() torch.save(g.pos_enc.cpu(), 'data/ogbn/laplacian_' + DATASET_NAME[5:] + '.pt') except: L = get_laplacian(g.edge_index, normalization='sym', dtype=torch.float64) L = csr_matrix((L[1], (L[0][0], L[0][1])), shape=(g.num_nodes, g.num_nodes)) # Eigenvectors with scipy # EigVal, EigVec = sp.linalg.eigs(L, k=pos_enc_dim+1, which='SR') EigVal, EigVec = sp.linalg.eigs(L, k=pos_enc_dim + 1, which='SR', tol=1e-2) # for 40 PEs EigVec = EigVec[:, EigVal.argsort()] # increasing order g.pos_enc = torch.from_numpy(EigVec[:, 1:pos_enc_dim + 1].astype( np.float32)).float() torch.save(g.pos_enc.cpu(), 'data/ogbn/laplacian_' + DATASET_NAME[5:] + '.pt') # add astype to discards the imaginary part to satisfy the version change pytorch1.5.0 # # Eigenvectors with numpy # EigVal, EigVec = np.linalg.eig(L.toarray()) # idx = EigVal.argsort() # increasing order # EigVal, EigVec = EigVal[idx], np.real(EigVec[:,idx]) # g.ndata['pos_enc'] = torch.from_numpy(np.abs(EigVec[:,1:pos_enc_dim+1])).float() return g
def test_get_laplacian(): edge_index = torch.tensor([[0, 1, 1, 2], [1, 0, 2, 1]], dtype=torch.long) edge_weight = torch.tensor([1, 2, 2, 4], dtype=torch.float) lap = get_laplacian(edge_index, edge_weight) assert lap[0].tolist() == [[0, 1, 1, 2, 0, 1, 2], [1, 0, 2, 1, 0, 1, 2]] assert lap[1].tolist() == [-1, -2, -2, -4, 1, 4, 4] lap_sym = get_laplacian(edge_index, edge_weight, normalization='sym') assert lap_sym[0].tolist() == lap[0].tolist() assert lap_sym[1].tolist() == [-0.5, -1, -0.5, -1, 1, 1, 1] lap_rw = get_laplacian(edge_index, edge_weight, normalization='rw') assert lap_rw[0].tolist() == lap[0].tolist() assert lap_rw[1].tolist() == [-1, -0.5, -0.5, -1, 1, 1, 1]
def forward(self, x, edge_index, batch): # computing the graph laplacian and adjacency matrix batch_nodes = batch.size(0) if edge_index.size(1) != 0: L_indices, L_values = get_laplacian(edge_index) L = torch.sparse.FloatTensor( L_indices, L_values, torch.Size([batch_nodes, batch_nodes])) A = torch.diag(torch.diag(L.to_dense())) - L.to_dense() # entropy computation entropies = self.compute_entropy(x, L, A, batch) # Eq. (8) else: A = torch.zeros([batch_nodes, batch_nodes]) norm = torch.norm(x, dim=1).unsqueeze(-1) entropies = norm / norm # graph convolution and probability scores probabilities = self.gc1(entropies, edge_index) probabilities = self.gc2(probabilities, edge_index) probabilities = self.gc3(probabilities, edge_index) probabilities = torch.sigmoid(probabilities) # conditional expectation; Algorithm 1 gamma = entropies.sum() loss = self.loss_fn(entropies, probabilities, A, gamma) # Eq. (9) mewis = self.conditional_expectation(entropies, probabilities, A, loss, gamma) # graph reconstruction; Eq. (10) x_pooled, adj_pooled = self.graph_reconstruction(mewis, x, A) edge_index_pooled, batch_pooled = self.to_edge_index( adj_pooled, mewis, batch) return x_pooled, edge_index_pooled, batch_pooled, loss, mewis
def __norm__(self, edge_index, num_nodes: Optional[int], edge_weight: OptTensor, normalization: Optional[str], lambda_max, dtype: Optional[int] = None, batch: OptTensor = None): edge_index, edge_weight = remove_self_loops(edge_index, edge_weight) edge_index, edge_weight = get_laplacian(edge_index, edge_weight, normalization, dtype, num_nodes) if batch is not None and lambda_max.numel() > 1: lambda_max = lambda_max[batch[edge_index[0]]] edge_weight = (2.0 * edge_weight) / lambda_max edge_weight.masked_fill_(edge_weight == float('inf'), 0) edge_index, edge_weight = add_self_loops(edge_index, edge_weight, fill_value=-1., num_nodes=num_nodes) assert edge_weight is not None return edge_index, edge_weight
def __call__(self, data: Data) -> Data: from scipy.sparse.linalg import eigs, eigsh eig_fn = eigs if not self.is_undirected else eigsh num_nodes = data.num_nodes edge_index, edge_weight = get_laplacian( data.edge_index, normalization='sym', num_nodes=num_nodes, ) L = to_scipy_sparse_matrix(edge_index, edge_weight, num_nodes) eig_vals, eig_vecs = eig_fn( L, k=self.k + 1, which='SR' if not self.is_undirected else 'SA', return_eigenvectors=True, **self.kwargs, ) eig_vecs = np.real(eig_vecs[:, eig_vals.argsort()]) pe = torch.from_numpy(eig_vecs[:, 1:self.k + 1]) sign = -1 + 2 * torch.randint(0, 2, (self.k, )) pe *= sign data = add_node_attr(data, pe, attr_name=self.attr_name) return data
def norm(edge_index, num_nodes, edge_weight, normalization, lambda_max, dtype=None, batch=None): edge_index, edge_weight = remove_self_loops(edge_index, edge_weight) edge_index, edge_weight = get_laplacian(edge_index, edge_weight, normalization, dtype, num_nodes) if batch is not None and torch.is_tensor(lambda_max): lambda_max = lambda_max[batch[edge_index[0]]] edge_weight = (2.0 * edge_weight) / lambda_max edge_weight[edge_weight == float('inf')] = 0 edge_index, edge_weight = add_self_loops(edge_index, edge_weight, fill_value=-1, num_nodes=num_nodes) return edge_index, edge_weight
def positional_encoding(g, pos_enc_dim, framework = 'dgl'): """ Graph positional encoding v/ Laplacian eigenvectors """ # Laplacian,for the pyg if framework == 'pyg': L = get_laplacian(g.edge_index,normalization='sym',dtype = torch.float64) L = csr_matrix((L[1], (L[0][0], L[0][1])), shape=(g.num_nodes, g.num_nodes)) # Eigenvectors with scipy # EigVal, EigVec = sp.linalg.eigs(L, k=pos_enc_dim+1, which='SR') EigVal, EigVec = sp.linalg.eigs(L, k=pos_enc_dim + 1, which='SR', tol=1e-2) # for 40 PEs EigVec = EigVec[:, EigVal.argsort()] # increasing order g.pos_enc = torch.from_numpy(EigVec[:, 1:pos_enc_dim + 1].astype(np.float32)).float() # add astype to discards the imaginary part to satisfy the version change pytorch1.5.0 elif framework == 'dgl': A = g.adjacency_matrix_scipy(return_edge_ids=False).astype(float) N = sp.diags(dgl.backend.asnumpy(g.in_degrees()).clip(1) ** -0.5, dtype=float) L = sp.eye(g.number_of_nodes()) - N * A * N # Eigenvectors with scipy # EigVal, EigVec = sp.linalg.eigs(L, k=pos_enc_dim+1, which='SR') EigVal, EigVec = sp.linalg.eigs(L, k=pos_enc_dim + 1, which='SR', tol=1e-2) # for 40 PEs EigVec = EigVec[:, EigVal.argsort()] # increasing order g.ndata['pos_enc'] = torch.from_numpy(EigVec[:, 1:pos_enc_dim + 1].astype(np.float32)).float() # add astype to discards the imaginary part to satisfy the version change pytorch1.5.0 # # Eigenvectors with numpy # EigVal, EigVec = np.linalg.eig(L.toarray()) # idx = EigVal.argsort() # increasing order # EigVal, EigVec = EigVal[idx], np.real(EigVec[:,idx]) # g.ndata['pos_enc'] = torch.from_numpy(np.abs(EigVec[:,1:pos_enc_dim+1])).float() return g
def get_laplacian_mat(edge_index, edge_weight, num_node, normalization='sym'): # todo: change back """ return a laplacian (torch.sparse.tensor)""" edge_index, edge_weight = get_laplacian( edge_index, edge_weight, normalization=normalization) # see https://bit.ly/3c70FJK for format return torch.sparse.FloatTensor(edge_index, edge_weight, torch.Size([num_node, num_node]))
def process(self): """ Prepares the data for PyTorch Geometric. """ unprocessed = self.data_read(self.h, self.subs) num_samples, timestamps = unprocessed.shape[0], unprocessed.shape[-1] # Turn the data into PyTorch Geometric Graphs data_list = list() for sample in range(num_samples): x = unprocessed[sample, :, :, 0] y = unprocessed[sample, :, :, 1] y2 = unprocessed[sample, :, :, 2] edge_index, edge_attr, rows, cols = create_edge_index_attribute(x) y_edge_index, y_edge_attr, _, _ = create_edge_index_attribute(y) y2_edge_index, y2_edge_attr, _, _ = create_edge_index_attribute(y2) y_distr = normal.Normal(y.mean(dim=1), y.std(dim=1)) y2_distr = normal.Normal(y2.mean(dim=1), y2.std(dim=1)) y_lap_ei, y_lap_ea = get_laplacian(y_edge_index, y_edge_attr) y2_lap_ei, y2_lap_ea = get_laplacian(y2_edge_index, y2_edge_attr) y_lap = to_dense_adj(y_lap_ei, edge_attr=y_lap_ea) y2_lap = to_dense_adj(y2_lap_ei, edge_attr=y2_lap_ea) data = Data(x=x, edge_index=edge_index, edge_attr=edge_attr, y=y, y_edge_index=y_edge_index, y_edge_attr=y_edge_attr, y_distr=y_distr, y2=y2, y2_edge_index=y2_edge_index, y2_edge_attr=y2_edge_attr, y2_distr=y2_distr, y_lap=y_lap, y2_lap=y2_lap) data.num_nodes = rows data_list.append(data) if self.pre_filter is not None: data_list = [data for data in data_list if self.pre_filter(data)] if self.pre_transform is not None: data_list = [self.pre_transform(data) for data in data_list] data, slices = self.collate(data_list) torch.save((data, slices), self.processed_paths[0])
def calculate_laplacian(edge_index, num_nodes, edge_weight, dtype=None): edge_index, edge_weight = remove_self_loops(edge_index, edge_weight) edge_index, edge_weight = get_laplacian(edge_index, edge_weight, 'sym', dtype, num_nodes) edge_weight[edge_weight == float('inf')] = 0 edge_index, edge_weight = add_self_loops(edge_index, edge_weight, fill_value=-1, num_nodes=num_nodes) return edge_index, edge_weight
def forward(self, x, k, edge_index=None, batch=None): r""" Calculate graph regularization term $R=||X^T L X||_F$ """ num_nodes = x.shape[-2] xdim = x.shape[-1] if edge_index is None: edge_index = knn_graph(x, k=k, batch=batch, loop=False) lap_index, lap_val = get_laplacian(edge_index, normalization="rw", num_nodes=num_nodes) res = self.propagate(edge_index=lap_index, x=x, edge_weight=lap_val) # print(res.shape) # [B, F * F] # Frobenius Norm (intrinstically same) return (torch.norm(res, dim=-1, p="fro")**2).mean()
def __call__(self, data): edge_weight = data.edge_attr if edge_weight is not None and edge_weight.numel() != data.num_edges: edge_weight = None edge_index, edge_weight = get_laplacian(data.edge_index, edge_weight, self.normalization, num_nodes=data.num_nodes) L = to_scipy_sparse_matrix(edge_index, edge_weight, data.num_nodes) eig_fn = eigsh if self.normalization == 'sym' else eigs lambda_max = eig_fn(L, k=1, which='LM', return_eigenvectors=False) data.lambda_max = float(lambda_max.real) return data
def get_prec(self, data): triangle_mask = data.edge_index[0] < data.edge_index[1] edge_index = torch.stack([ data.edge_index[0][triangle_mask], data.edge_index[1][triangle_mask] ]) x_query = F.embedding(edge_index[0], data.x) x_key = F.embedding(edge_index[1], data.x) x = torch.cat([x_query, x_key], dim=1) x = F.dropout(x, p=self.dropout, training=self.training) x = self.activation(self.reg_fc1(x)) x = F.dropout(x, p=self.dropout, training=self.training) x = F.softplus(self.reg_fc2(x)) und_edge_index = torch.stack([ torch.cat([edge_index[0], edge_index[1]], dim=0), torch.cat([edge_index[1], edge_index[0]], dim=0) ]) und_edge_weight = torch.cat([x.view(-1), x.view(-1)], dim=0) L_edge_index, L_edge_weight = get_laplacian( und_edge_index, edge_weight=und_edge_weight, num_nodes=data.x.size(0)) L = to_dense_adj(L_edge_index, edge_attr=L_edge_weight)[0] return L + torch.eye(L.size(0), dtype=L.dtype, device=L.device)
def MyDataset(dataset, Lev, s, n, FrameType='Haar', add_feature=False, QM7=False): if FrameType == 'Haar': D1 = lambda x: np.cos(x / 2) D2 = lambda x: np.sin(x / 2) DFilters = [D1, D2] elif FrameType == 'Linear': D1 = lambda x: np.square(np.cos(x / 2)) D2 = lambda x: np.sin(x) / np.sqrt(2) D3 = lambda x: np.square(np.sin(x / 2)) DFilters = [D1, D2, D3] elif FrameType == 'Quadratic': # not accurate so far D1 = lambda x: np.cos(x / 2) ** 3 D2 = lambda x: np.multiply((np.sqrt(3) * np.sin(x / 2)), np.cos(x / 2) ** 2) D3 = lambda x: np.multiply((np.sqrt(3) * np.sin(x / 2) ** 2), np.cos(x / 2)) D4 = lambda x: np.sin(x / 2) ** 3 DFilters = [D1, D2, D3, D4] else: raise Exception('Invalid FrameType') r = len(DFilters) dataset1 = list() label=list() for i in range(len(dataset)): if add_feature: raise Exception('this function has not been completed') # will add this function as required else: if QM7: x_qm7 = torch.ones(dataset[i].num_nodes, num_features) data1 = Data(x=x_qm7, edge_index=dataset[i].edge_index, y=dataset[i].y) data1.y_origin = dataset[i].y_origin else: data1 = Data(x=dataset[i].x, edge_index=dataset[i].edge_index, y=dataset[i].y) if QM7: label.append(dataset[i].y_origin) # get graph Laplacian num_nodes = data1.x.shape[0] L = get_laplacian(dataset[i].edge_index, num_nodes=num_nodes, normalization='sym') L = sparse.coo_matrix((L[1].numpy(), (L[0][0, :].numpy(), L[0][1, :].numpy())), shape=(num_nodes, num_nodes)) # calculate lambda max lobpcg_init = np.random.rand(num_nodes, 1) lambda_max, _ = lobpcg(L, lobpcg_init) lambda_max = lambda_max[0] J = np.log(lambda_max / np.pi) / np.log(s) + Lev - 1 # dilation level to start the decomposition # get matrix operators d = get_operator(L, DFilters, n, s, J, Lev) for m in range(1, r): for q in range(Lev): if (m == 1) and (q == 0): d_aggre = d[m, q] else: d_aggre = sparse.vstack((d_aggre, d[m, q])) d_aggre = sparse.vstack((d[0, Lev - 1], d_aggre)) ###stack the n x n matrix data1.d = [d_aggre] # get d_index a = [i for i in range((r - 1) * Lev + 1)]##len=3 data1.d_index=[[a[i // num_nodes] for i in range(len(a) * num_nodes)]]##3*num [0,1,2;,0,1,2...] # append data1 into dataset1 dataset1.append(data1) if QM7: mean = torch.mean(torch.Tensor(label)).item() std = torch.sqrt(torch.var(torch.Tensor(label))).item() return dataset1, r, mean, std else: return dataset1, r
def main(): parser = ArgumentParser(description="GraphZoom") parser.add_argument("-d", "--dataset", type=str, default="arxiv", \ help="input dataset") parser.add_argument("-o", "--coarse", type=str, default="lamg", \ help="choose either simple_coarse or lamg_coarse, [simple, lamg]") parser.add_argument("-c", "--mcr_dir", type=str, default="/opt/matlab/R2018A/", \ help="directory of matlab compiler runtime (only required by lamg_coarsen)") parser.add_argument("-s", "--search_ratio", type=int, default=12, \ help="control the search space in graph fusion process (only required by lamg_coarsen)") parser.add_argument("-r", "--reduce_ratio", type=int, default=2, \ help="control graph coarsening levels (only required by lamg_coarsen)") parser.add_argument("-v", "--level", type=int, default=1, \ help="number of coarsening levels (only required by simple_coarsen)") parser.add_argument("-n", "--num_neighs", type=int, default=2, \ help="control k-nearest neighbors in graph fusion process") parser.add_argument("-l", "--lda", type=float, default=0.1, \ help="control self loop in adjacency matrix") parser.add_argument("-e", "--embed_path", type=str, default="./embed_results/", \ help="path of embedding result") parser.add_argument("-m", "--embed_method", type=str, default="node2vec", \ help="graph embedding method") parser.add_argument("-f", "--fusion", default=True, action="store_false", \ help="whether use graph fusion") parser.add_argument("-p", "--power", default=False, action="store_true", \ help="Strong power of graph filter, set True to enhance filter power") parser.add_argument("-g", "--sage_model", type=str, default="mean", \ help="aggregation function in graphsage") parser.add_argument("-w", "--sage_weighted", default=True, action="store_false", \ help="whether consider weighted reduced graph") args = parser.parse_args() dataset = args.dataset feature_path = "dataset/{}/{}-feats.npy".format(dataset, dataset) fusion_input_path = "dataset/{}/{}.mtx".format(dataset, dataset) reduce_results = "./reduction_results/" mapping_path = "{}Mapping.mtx".format(reduce_results) d = PygNodePropPredDataset(name=f"ogbn-{dataset}") os.makedirs(reduce_results, exist_ok=True) os.makedirs(f"dataset/{dataset}", exist_ok=True) if args.fusion: coarsen_input_path = "dataset/{}/fused_{}.mtx".format(dataset, dataset) else: coarsen_input_path = "dataset/{}/{}.mtx".format(dataset, dataset) ######Load Data###### print("%%%%%% Loading Graph Data %%%%%%") lp_index, lp_weight = get_laplacian(to_undirected(d[0].edge_index, d[0].num_nodes)) laplacian = to_scipy_sparse_matrix(lp_index, lp_weight) if args.coarse == "lamg": if os.path.exists(fusion_input_path): print("Laplacian matrix in mtx already exists.") else: print("Saving laplacian matrix in mtx...") file = open(fusion_input_path, "wb") mmwrite(fusion_input_path, laplacian) file.close() ## whether node features are required if args.fusion: feature = d[0].x.numpy() ######Graph Fusion###### if args.fusion: print("%%%%%% Starting Graph Fusion %%%%%%") fusion_start = time.process_time() laplacian = graph_fusion(laplacian, feature, args.num_neighs, args.mcr_dir, args.coarse,\ fusion_input_path, args.search_ratio, reduce_results, mapping_path, dataset) fusion_time = time.process_time() - fusion_start ######Graph Reduction###### print("%%%%%% Starting Graph Reduction %%%%%%") reduce_start = time.process_time() if args.coarse == "simple": G, projections, laplacians, level = sim_coarse(laplacian, args.level) reduce_time = time.process_time() - reduce_start elif args.coarse == "lamg": os.system('./run_coarsening.sh {} {} {} n {}'.format(args.mcr_dir, \ coarsen_input_path, args.reduce_ratio, reduce_results)) reduce_time = read_time("{}CPUtime.txt".format(reduce_results)) G = mtx2graph("{}Gs.mtx".format(reduce_results)) level = read_levels("{}NumLevels.txt".format(reduce_results)) projections, laplacians = construct_proj_laplacian(laplacian, level, reduce_results) else: raise NotImplementedError edge_index = torch.tensor(list(G.edges)).t().contiguous().view(2, -1) edge_index = to_undirected(edge_index, len(G.nodes())) ######Embed Reduced Graph###### print("%%%%%% Starting Graph Embedding %%%%%%") if args.embed_method == "node2vec": embed_start = time.process_time() embeddings = node2vec(edge_index) else: raise NotImplementedError embed_time = time.process_time() - embed_start ######Refinement###### print("%%%%%% Starting Graph Refinement %%%%%%") refine_start = time.process_time() embeddings = refinement(level, projections, laplacians, embeddings, args.lda, args.power) refine_time = time.process_time() - refine_start ######Save Embeddings###### os.makedirs(args.embed_path, exist_ok=True) np.save(args.embed_path + "embeddings.npy", embeddings) ######Report timing information###### print("%%%%%% CPU time %%%%%%") if args.fusion: total_time = fusion_time + reduce_time + embed_time + refine_time print(f"Graph Fusion Time: {fusion_time:.3f}") else: total_time = reduce_time + embed_time + refine_time print("Graph Fusion Time: 0") print(f"Graph Reduction Time: {reduce_time:.3f}") print(f"Graph Embedding Time: {embed_time:.3f}") print(f"Graph Refinement Time: {refine_time:.3f}") print(f"Total Time = Fusion_time + Reduction_time + Embedding_time + Refinement_time = {total_time:.3f}")
def mgpool(x, pos, edge_index, batch, mask=None): adj_values = torch.ones(edge_index.shape[1]).cuda() cluster = graclus(edge_index) cluster, perm = consecutive_cluster(cluster) index = torch.stack([cluster, torch.arange(0, x.shape[0]).cuda()], dim=0) values = torch.ones(cluster.shape[0], dtype=torch.float).cuda() uniq, inv, counts = torch.unique(cluster, return_inverse=True, return_counts=True) newsize = uniq.shape[0] origsize = x.shape[0] new_batch = pool_batch(perm, batch) # Compute random walk graph laplacian: laplacian_index, laplacian_weights = get_laplacian(edge_index, normalization='rw') laplacian_index, laplacian_weights = torch_sparse.coalesce( laplacian_index, laplacian_weights, m=origsize, n=origsize) index, values = torch_sparse.coalesce(index, values, m=newsize, n=origsize) # P^T matrix new_feat = torch_sparse.spmm(index, values, m=newsize, n=origsize, matrix=x) # P^T X new_pos = torch_sparse.spmm(index, values, m=newsize, n=origsize, matrix=pos) # P^T POS new_adj, new_adj_val = torch_sparse.spspmm(index, values, edge_index, adj_values, m=newsize, k=origsize, n=origsize, coalesced=True) # P^T A index, values = torch_sparse.transpose(index, values, m=newsize, n=origsize, coalesced=True) # P new_adj, new_adj_val = torch_sparse.spspmm(new_adj, new_adj_val, index, values, m=newsize, k=origsize, n=newsize, coalesced=True) # (P^T A) P # Precompute QP : values = torch.ones(cluster.shape[0], dtype=torch.float).cuda() index, values = torch_sparse.spspmm(laplacian_index, laplacian_weights, index, values, m=origsize, k=origsize, n=newsize, coalesced=True) return new_adj, new_feat, new_pos, new_batch, index, values, origsize, newsize
np.random.seed(args.seed) torch.manual_seed(args.seed) # Training on CPU/GPU device device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") print(device) # load dataset dataname = args.dataset rootname = osp.join(osp.abspath(''), 'data', dataname) dataset = Planetoid(root=rootname, name=dataname) num_nodes = dataset[0].x.shape[0] L = get_laplacian(dataset[0].edge_index, num_nodes=num_nodes, normalization='sym') L = sparse.coo_matrix( (L[1].numpy(), (L[0][0, :].numpy(), L[0][1, :].numpy())), shape=(num_nodes, num_nodes)) lobpcg_init = np.random.rand(num_nodes, 1) lambda_max, _ = lobpcg(L, lobpcg_init) lambda_max = lambda_max[0] # extract decomposition/reconstruction Masks FrameType = args.FrameType if FrameType == 'Haar': D1 = lambda x: np.cos(x / 2) D2 = lambda x: np.sin(x / 2)