@@ -68,9 +68,14 @@ def lift_topology(
68
68
dict
69
69
The lifted topology.
70
70
"""
71
- G = self ._generate_graph_from_data (data )
72
- adj = nx .adjacency_matrix (G ).toarray ()
73
-
71
+ # Make adjacency matrix from data
72
+ N = data .num_nodes
73
+ adj = np .zeros ((N , N ))
74
+ for i , j in data .edge_index .T :
75
+ adj [i , j ] = 1
76
+ adj [j , i ] = 1
77
+
78
+ # Create the latent clique model and fit using Gibbs sampling
74
79
mod = _LatentCliqueModel (
75
80
adj ,
76
81
init = self .init ,
@@ -80,11 +85,11 @@ def lift_topology(
80
85
it = self .it if self .it is not None else data .num_edges
81
86
mod .sample (sample_hypers = True , num_iters = it , do_gibbs = True , verbose = verbose )
82
87
83
- adj = mod . Z . T @ mod . Z
84
- adj = np . minimum ( adj - np . diag ( np . diag ( adj )), 1 )
85
- g = nx . from_numpy_matrix ( adj )
86
- edges = torch . LongTensor ( list ( g . edges ()), device = data . edge_index . device ).T
87
- edges = torch .cat ([ edges , edges . flip ( 0 )], dim = 1 )
88
+ # Translate fitted model to a new topology
89
+ cic = mod . Z . T @ mod . Z
90
+ adj = np . minimum ( cic - np . diag ( np . diag ( cic )), 1 )
91
+ edges = np . array ( np . where ( adj ) ).T
92
+ edges = torch .LongTensor ( edges ). to ( data . edge_index . device )
88
93
new_data = torch_geometric .data .Data (x = data .x , edge_index = edges )
89
94
return SimplicialCliqueLifting ().lift_topology (new_data )
90
95
@@ -115,7 +120,7 @@ class _LatentCliqueModel:
115
120
Adjacency matrix of the input graph.
116
121
edge_prob_mean : float
117
122
Mean of the prior distribution of pie ~ Beta
118
- where edge_prob_mean must be in (0, 1].
123
+ where edge_prob_mean must be in (0, 1].
119
124
When edge_prob_var is one, the value of edge_prob is fixed and not sampled.
120
125
edge_prob_var : float
121
126
Uncertainty of the prior distribution of pie ~ Beta(a, b)
@@ -147,7 +152,7 @@ class _LatentCliqueModel:
147
152
Probability of an edge observation.
148
153
lamb : float
149
154
Rate parameter of the Poisson distribution for the number of cliques.
150
- It does not influence parameter learning. But is sampled for the
155
+ It does not influence parameter learning. But is sampled for the
151
156
likelihood computation.
152
157
153
158
**Note**: The values of (K, N) are used interchanged from the paper notation.
@@ -170,14 +175,26 @@ def __init__(
170
175
# Initialize clique cover matrix
171
176
self ._init_Z ()
172
177
178
+ # Initialize parameters
179
+ self ._init_params ()
180
+
181
+ # Initialize hyperparameters
182
+ self ._init_hyperparams (edge_prob_mean , edge_prob_var )
183
+
173
184
# Current number of clusters
174
185
self .K = self .Z .shape [0 ]
175
186
176
- # Initialize parameters
187
+ def _init_params (self ):
188
+ """Initialize the parameters of the model."""
177
189
self .alpha = 1.0
178
190
self .sigma = 0.5
179
191
self .c = 0.5
180
- self .edge_prob = edge_prob_mean
192
+ self .edge_prob = 0.98
193
+
194
+ def _init_hyperparams (self , edge_prob_mean , edge_prob_var ):
195
+ # Validate the edge probability parameters
196
+ assert 0 < edge_prob_mean <= 1
197
+ assert edge_prob_var >= 0
181
198
182
199
# Parameter prior hyper-parameters
183
200
# The priors of alpha, sigma, c and uninformative, so their are set to
@@ -376,26 +393,32 @@ def sample_hypers(self, step_size=0.1):
376
393
a = self ._edge_prob_params [0 ]
377
394
b = self ._edge_prob_params [1 ]
378
395
# lp ratio comes from a beta distribution
379
- lp_ratio = (a - 1 ) * (
380
- np . log ( edge_prob_prop ) - np . log ( self . edge_prob )
381
- ) + ( b - 1 ) * (np .log (1 - edge_prob_prop ) - np .log (1 - self .edge_prob ))
396
+ lp_ratio = (a - 1 ) * (np . log ( edge_prob_prop ) - np . log ( self . edge_prob )) + (
397
+ b - 1
398
+ ) * (np .log (1 - edge_prob_prop ) - np .log (1 - self .edge_prob ))
382
399
lratio = ll_new - ll_old + lp_ratio
383
400
r = np .log (np .random .rand ())
384
401
if r < lratio :
385
402
self .edge_prob = edge_prob_prop
386
403
387
404
c_prop = self .c + step_size * np .random .randn ()
388
- if c_prop > - self .sigma :
389
- lp_ratio = (self ._c_params [0 ] - 1 ) * (
390
- np .log (c_prop ) - np .log (self .c )
391
- ) + self ._c_params [1 ] * (self .c - c_prop )
405
+ if c_prop > - 1 * self .sigma :
392
406
ll_new = self .log_lik (c = c_prop )
407
+ c_diff_new = c_prop + self .sigma
408
+ lp_new = stats .gamma .logpdf (
409
+ c_diff_new , self ._c_params [0 ], scale = 1 / self ._c_params [1 ]
410
+ )
411
+
393
412
ll_old = self .log_lik ()
394
- lratio = ll_new - ll_old + lp_ratio
413
+ c_diff_old = self .c + self .sigma
414
+ lp_old = stats .gamma .logpdf (
415
+ c_diff_old , self ._c_params [0 ], scale = 1 / self ._c_params [1 ]
416
+ )
417
+
418
+ lratio = ll_new - ll_old + lp_new - lp_old
395
419
r = np .log (np .random .rand ())
396
420
if r < lratio :
397
421
self .c = c_prop
398
-
399
422
# Sample c
400
423
c_prop = self .c + step_size * np .random .randn ()
401
424
@@ -789,7 +812,7 @@ def _sample_from_ibp(K, alpha, sigma, c):
789
812
adj = np .minimum (cic - np .diag (np .diag (cic )), 1 )
790
813
791
814
# delete edges with prob 1 - exp(pi^)
792
- prob = np .exp (- (1 - pie )** 2 )
815
+ prob = np .exp (- (( 1 - pie ) ** 2 ) )
793
816
triu_mask = np .triu (np .ones_like (adj ), 1 )
794
817
adj = np .random .binomial (1 , prob , adj .shape ) * adj * triu_mask
795
818
adj = adj + adj .T
0 commit comments