@@ -97,6 +97,7 @@ def _neg_log_likelihood_grad(x, y, k, s):
9797 """
9898 Analytic gradient of the negative log-likelihood w.r.t. the *bounded*
9999 parameters (x, y). Derived by differentiating formula (13).
100+ Parameters should be in their bounded form (x > 0, 0 < y < 1) for correct gradients.
100101 """
101102 N = len (x )
102103 grad_x = np .empty (N , dtype = np .float64 )
@@ -222,23 +223,40 @@ def jac(v):
222223@dataclass (slots = True )
223224class EnhancedConfigurationModelFilter (GraphOperator ):
224225 """
225- Enhanced Configuration Model (ECM) filter for weighted, undirected
226- similarity graphs.
227-
228- Replaces the original JAX-based implementation with Numba + NumPy kernels:
229- - @njit kernels for the negative log-likelihood, its analytic gradient,
230- and the p-value computation.
231- - Reparameterisation-based optimisation (no explicit bounds): the domain
232- constraints x > 0 and 0 < y < 1 are enforced implicitly by mapping the
233- unconstrained optimisation variables through smooth homeomorphisms before
234- evaluating the objective — exactly as in the original MaxentGraph design.
226+ Filter an undirected weighted similarity graph using the Enhanced
227+ Configuration Model (ECM).
228+
229+ This operator fits the ECM null model to the input graph and returns a new
230+ graph whose edge weights are the ECM p-values associated with the observed
231+ edge weights. The model preserves, in expectation, both the degree sequence
232+ and the strength sequence of the input graph.
233+
234+ The implementation follows the maximum-likelihood formulation of the ECM
235+ for weighted undirected networks. Internally, it estimates the node-wise
236+ model parameters ``x`` and ``y`` by minimizing the negative log-likelihood
237+ in a reparameterized unconstrained space and then computes an ECM-based
238+ p-value for each observed edge.
239+
240+ Parameters
241+ ----------
242+ x_transform_idx : int, default=0
243+ Index selecting the reparameterization used for the positive ECM
244+ parameters ``x``. The index refers to :data:`R_to_zero_to_inf`, whose
245+ entries map from the real line to the open interval ``(0, inf)``.
246+
247+ y_transform_idx : int, default=0
248+ Index selecting the reparameterization used for the bounded ECM
249+ parameters ``y``. The index refers to :data:`R_to_zero_to_one`, whose
250+ entries map from the real line to the open interval ``(0, 1)``.
235251
236252 Paper: https://arxiv.org/abs/1706.00230
237253 Code: https://gitlab.liris.cnrs.fr/coregraphie/aliplosone/-/blob/main/Backbones/ecm.py
238254 """
239255 supported_modes = ["similarity" ]
240256
241- # Indices into R_to_zero_to_inf / R_to_zero_to_one (0 = exp/sigmoid)
257+ # Reparameterization choices for the ECM variables:
258+ # x: R -> (0, inf)
259+ # y: R -> (0, 1)
242260 x_transform_idx : int = 0
243261 y_transform_idx : int = 0
244262
@@ -276,14 +294,16 @@ def _undirected(self, G: Graph) -> Graph:
276294 y_inv_transform (v0_bounded [num_nodes :]),
277295 ])
278296
279- # ---- Objective + analytic gradient in unconstrained space ---------
297+ # ---- Objective + gradient in unconstrained space ---------
280298 fun , jac = _make_objective (
281299 num_nodes , k , s ,
282300 x_transform , x_inv_transform ,
283301 y_transform , y_inv_transform ,
284302 )
285303
286- # ---- Optimise (no explicit bounds — domain enforced by transforms) -
304+ # ---- Optimize in reparameterized space -----------------------------
305+ # The optimizer works on unconstrained variables; valid ECM parameter
306+ # domains are enforced by the x/y transforms before each evaluation.
287307 res = so .minimize (
288308 fun = fun ,
289309 jac = jac ,
0 commit comments