GroupMultiNeSS 0.0.2__tar.gz → 0.0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
1
  import numpy as np
2
2
  from typing import List
3
- from utils import truncated_svd, truncated_eigen_decomposition
3
+
4
+ from .utils import truncated_svd, truncated_eigen_decomposition
4
5
 
5
6
 
6
7
  def MASE(As: np.array, d_shared: int, d_individs: List[int]):
@@ -42,7 +43,8 @@ def MASE(As: np.array, d_shared: int, d_individs: List[int]):
42
43
  return ps, u_joint, rs
43
44
 
44
45
 
45
- def ASE(A: np.array, d: int, check_if_symmetric=True):
46
+ def ASE(A: np.array, d: int, check_if_symmetric: bool = True,
47
+ indef_inner_prod: bool = True, return_signature=False):
46
48
  """
47
49
  Compute the Adjacency Spectral Embedding (ASE) of a symmetric adjacency matrix.
48
50
 
@@ -54,6 +56,11 @@ def ASE(A: np.array, d: int, check_if_symmetric=True):
54
56
  Embedding dimension.
55
57
  check_if_symmetric : bool, optional
56
58
  Whether to check symmetry of A. Default is True.
59
+ indef_inner_prod: bool, optional
60
+ If True, indefinite inner product with signature (p, d - p) is assumed (GRDPG model).
61
+ If False, standard inner product is assumed (RDPG model)
62
+ return_signature: bool, optional
63
+ Whether to return_signature of the inner product. Only used when indef_inner_prod is TrueDefault is False.
57
64
 
58
65
  Returns
59
66
  -------
@@ -67,6 +74,14 @@ def ASE(A: np.array, d: int, check_if_symmetric=True):
67
74
  """
68
75
  if check_if_symmetric:
69
76
  assert np.allclose(A, A.T), "A should be a symmetric matrix"
70
- eigvecs, eigvals, _ = truncated_svd(A, max_rank=d)
71
- eigvals = np.sqrt(eigvals)
72
- return eigvecs @ np.diag(eigvals)
77
+ eigvals, eigvecs = truncated_eigen_decomposition(A, max_rank=d, which="LM" if indef_inner_prod else "LA")
78
+ if indef_inner_prod:
79
+ sort_idx = np.argsort(eigvals)[::-1]
80
+ eigvals = eigvals[sort_idx]
81
+ eigvecs = eigvecs[:, :sort_idx]
82
+ signature = np.where(eigvals > 0, 1., -1)
83
+ ase = eigvecs @ np.diag(np.sqrt(np.abs(eigvals)))
84
+ return (ase, signature) if return_signature else ase
85
+ else:
86
+ ase = eigvecs @ np.diag(np.sqrt(np.clip(eigvals, a_min=0, a_max=None)))
87
+ return ase
@@ -6,6 +6,39 @@ from .utils import sigmoid, generate_matrices_given_pairwise_max_cosines
6
6
 
7
7
 
8
8
  class LatentPositionGenerator:
9
+ """
10
+ Generator of latent positions for MultiNeSS model.
11
+
12
+ This class simulates multiple network layers that share a common latent component and
13
+ layer-specific deviations. It supports normal and Bernoulli edge generation models
14
+ and can impose angle constraints between latent spaces.
15
+
16
+ Parameters
17
+ ----------
18
+ n_nodes : int
19
+ Number of nodes in each network layer.
20
+ n_layers : int
21
+ Number of network layers to generate.
22
+ edge_distrib : {'normal', 'bernoulli'}, default='normal'
23
+ Type of edge distribution. If 'normal', edges are generated with Gaussian noise.
24
+ If 'bernoulli', edges are drawn as Bernoulli random variables.
25
+ noise_sigma : float, default=1.0
26
+ Standard deviation of the Gaussian noise for normal-distributed edges.
27
+ loops_allowed : bool, default=True
28
+ Whether self-loops (diagonal entries) are allowed.
29
+ d_shared : int, default=2
30
+ Dimension of the shared latent space component.
31
+ d_individs : int or list of int, default=2
32
+ Dimension(s) of the individual latent spaces for each layer.
33
+ s_vu : float, default=0.0
34
+ Target cosine similarity between shared and individual latent components.
35
+ s_uu : float, default=0.0
36
+ Target cosine similarity among individual latent components.
37
+ comps_max_cosine_mat : np.ndarray, optional
38
+ Custom cosine similarity matrix between components.
39
+ min_V_max_U_eigval_ratio : float, optional
40
+ Ratio to scale individual latent components relative to the shared component.
41
+ """
9
42
  def __init__(self, n_nodes, n_layers, *,
10
43
  edge_distrib: str = "normal",
11
44
  noise_sigma: float = 1.,
@@ -110,6 +143,36 @@ class LatentPositionGenerator:
110
143
 
111
144
 
112
145
  class GroupLatentPositionGenerator(LatentPositionGenerator):
146
+ """
147
+ Generator of latent positions for GroupMultiNeSS model.
148
+
149
+ Parameters
150
+ ----------
151
+ n_nodes : int
152
+ Number of nodes in each network layer.
153
+ n_layers : int
154
+ Number of network layers to generate.
155
+ group_indices : list of int
156
+ Group index for each layer, specifying which group latent component applies.
157
+ edge_distrib : {'normal', 'bernoulli'}, default='normal'
158
+ Type of edge distribution for generated networks.
159
+ noise_sigma : float, default=1.0
160
+ Standard deviation of Gaussian noise for normal-distributed edges.
161
+ loops_allowed : bool, default=True
162
+ Whether self-loops (diagonal entries) are allowed.
163
+ d_shared : int, default=2
164
+ Dimension of the shared latent space component.
165
+ d_individs : int or list of int, default=2
166
+ Dimension(s) of individual latent spaces for each layer.
167
+ d_groups : int or list of int, default=2
168
+ Dimension(s) of group-specific latent spaces.
169
+ s_vw, s_vu, s_ww, s_wu, s_uu : float, default=0.0
170
+ Target cosine similarities among shared, group, and individual components.
171
+ comps_max_cosine_mat : np.ndarray, optional
172
+ Predefined cosine similarity matrix among all latent components.
173
+ min_V_max_W_eigval_ratio : float, optional
174
+ Ratio to scale group latent spaces relative to the shared component.
175
+ """
113
176
  def __init__(self, n_nodes: int, n_layers: int, *,
114
177
  group_indices: List[int],
115
178
  edge_distrib: str = "normal",
@@ -11,7 +11,6 @@ from .utils import if_scalar_or_given_length_array, fill_diagonals, \
11
11
  soft_thresholding_operator
12
12
 
13
13
 
14
-
15
14
  class BaseGroupMultiNeSS(BaseMultiNeSS):
16
15
  """
17
16
  Base class for group-structured MultiNeSS models.
@@ -1,8 +1,8 @@
1
1
  import numpy as np
2
- from scipy.sparse.linalg import svds
2
+ from scipy.sparse.linalg import svds, eigsh
3
3
  from typing import List, Union, Tuple
4
4
  from copy import copy
5
- from scipy.sparse.linalg import eigsh
5
+ from scipy.linalg import sqrtm
6
6
  from sklearn.model_selection import KFold
7
7
 
8
8
 
@@ -197,18 +197,13 @@ def soft_thresholding_operator(A, threshold, max_rank=None):
197
197
  return u @ np.diag(s) @ vt
198
198
 
199
199
 
200
- def matrix_power(A: np.array, p: Union[int, float], eps=1e-8):
201
- if isinstance(p, int):
202
- return np.linalg.matrix_power(A, p)
203
- u, s, vt = np.linalg.svd(A)
204
- assert np.all(s > eps), "float powers defined only for matrices with all positive singular values"
205
- return u @ np.diag(s ** p) @ vt
206
-
207
-
208
200
  def frobenius_error(A_true, A_pred, relative=False, include_offdiag=True):
209
201
  assert A_true.shape == A_pred.shape
210
202
  assert A_true.ndim <= 2
211
- mask = np.ones(A_true.shape, dtype=bool) if include_offdiag else ~np.eye(A_true.shape[0], dtype=bool)
203
+ if include_offdiag or A_true.ndim == 1:
204
+ mask = np.ones(A_true.shape, dtype=bool)
205
+ else:
206
+ mask = ~np.eye(A_true.shape[0], dtype=bool)
212
207
  abs_error = np.sqrt(np.sum((A_true - A_pred) ** 2, where=mask))
213
208
  return abs_error / np.sqrt(np.sum(A_true ** 2, where=mask)) if relative else abs_error
214
209
 
@@ -280,7 +275,9 @@ def rectangular_eye(n: int, d: int):
280
275
  return eye
281
276
 
282
277
 
283
- def generate_matrices_given_pairwise_max_cosines(n: int, ds: list[int], pairwise_cos_mat: np.ndarray):
278
+ def generate_matrices_given_pairwise_max_cosines(n: int,
279
+ ds: list[int], pairwise_cos_mat: np.ndarray,
280
+ eps=1e-6):
284
281
  assert pairwise_cos_mat.ndim == 2
285
282
  assert len(ds) == len(pairwise_cos_mat)
286
283
  assert np.allclose(pairwise_cos_mat, pairwise_cos_mat.T), "pairwise_cos_mat should be symmetric"
@@ -288,11 +285,16 @@ def generate_matrices_given_pairwise_max_cosines(n: int, ds: list[int], pairwise
288
285
  assert np.allclose(np.diag(pairwise_cos_mat), 1)
289
286
  assert np.all(pairwise_cos_mat >= 0) & np.all(pairwise_cos_mat <= 1), "pairwise_cos_mat entries should be in [0, 1]"
290
287
  m = len(ds)
291
- stacked_Vs = np.random.randn(n, np.sum(ds))
288
+ d_total = sum(ds)
289
+ stacked_Vs = np.random.randn(n, d_total)
292
290
  cur_gram_mat = stacked_Vs.T @ stacked_Vs / n
293
291
  target_gram_mat = np.block([[pairwise_cos_mat[i, j] * rectangular_eye(ds[i], ds[j]) for j in range(m)]
294
292
  for i in range(m)])
295
- stacked_Vs = stacked_Vs @ matrix_power(cur_gram_mat, -0.5) @ matrix_power(target_gram_mat, 0.5)
293
+
294
+ sqrt_cur_gram_mat = np.real(sqrtm(cur_gram_mat + eps * np.eye(d_total)))
295
+ sqrt_target_gram_mat = np.real(sqrtm(target_gram_mat + eps * np.eye(d_total)))
296
+
297
+ stacked_Vs = stacked_Vs @ np.linalg.pinv(sqrt_cur_gram_mat) @ sqrt_target_gram_mat
296
298
  cumsum_ds = [0] + list(np.cumsum(ds))
297
299
  return [stacked_Vs[:, s: e] for s, e in zip(cumsum_ds[:-1], cumsum_ds[1:])]
298
300
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GroupMultiNeSS
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  Summary: GroupMultiNeSS package
5
5
  Author: Alexander Kagan
6
6
  Author-email: <amkagan@umich.edu>
@@ -34,11 +34,11 @@ Dynamic: summary
34
34
 
35
35
  # GroupMultiNeSS
36
36
 
37
- GroupMultiNeSS is a package for statistical modeling of multilayer networks. It implements multiple approaches allowing to extract shared, group, and individual latent structures from a collection of networks on a shared set of nodes. Specifically, it contains the implementation of fitting sampling procedures for the following models:
37
+ GroupMultiNeSS is a package for statistical modeling of multilayer networks. It implements multiple approaches allowing to extract shared, group, and individual latent structures from a collection of networks on a shared set of nodes. Specifically, it contains the implementation of fitting and sampling procedures for the following models:
38
38
  - GroupMultiNeSS [Kagan et al. (2025)] - likelihood based approach with nuclear norm penalization, accounts for the additional group latent structure
39
39
  - MultiNeSS [[MacDonald et al. (2021)](https://arxiv.org/abs/2012.14409)] - likelihood based approach with nuclear norm penalization
40
40
  - MultiNeSS [[Tian et al. (2024)](https://arxiv.org/abs/2412.02151)] - likelihood based approach with pre-estimation of latent ranks via Shared Space Hunting algorithm
41
- - COSIE ([Arroyo et al.](https://arxiv.org/abs/1906.10026)) - spectral-based Multiple Adjacency Spectral Embedding algorithm
41
+ - COSIE [[Arroyo et al. (2020)](https://arxiv.org/abs/1906.10026)] - spectral-based Multiple Adjacency Spectral Embedding algorithm
42
42
 
43
43
  ## Installation
44
44
 
@@ -81,7 +81,7 @@ print(group_multiness.make_final_error_report(S_true, Qs_true, Rs_true, Ps=Ps_tr
81
81
 
82
82
  MIT License
83
83
 
84
- Copyright (c) 2024 Alexander Kagan
84
+ Copyright (c) 2025 Alexander Kagan
85
85
 
86
86
  Permission is hereby granted, free of charge, to any person obtaining a copy
87
87
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GroupMultiNeSS
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  Summary: GroupMultiNeSS package
5
5
  Author: Alexander Kagan
6
6
  Author-email: <amkagan@umich.edu>
@@ -34,11 +34,11 @@ Dynamic: summary
34
34
 
35
35
  # GroupMultiNeSS
36
36
 
37
- GroupMultiNeSS is a package for statistical modeling of multilayer networks. It implements multiple approaches allowing to extract shared, group, and individual latent structures from a collection of networks on a shared set of nodes. Specifically, it contains the implementation of fitting sampling procedures for the following models:
37
+ GroupMultiNeSS is a package for statistical modeling of multilayer networks. It implements multiple approaches allowing to extract shared, group, and individual latent structures from a collection of networks on a shared set of nodes. Specifically, it contains the implementation of fitting and sampling procedures for the following models:
38
38
  - GroupMultiNeSS [Kagan et al. (2025)] - likelihood based approach with nuclear norm penalization, accounts for the additional group latent structure
39
39
  - MultiNeSS [[MacDonald et al. (2021)](https://arxiv.org/abs/2012.14409)] - likelihood based approach with nuclear norm penalization
40
40
  - MultiNeSS [[Tian et al. (2024)](https://arxiv.org/abs/2412.02151)] - likelihood based approach with pre-estimation of latent ranks via Shared Space Hunting algorithm
41
- - COSIE ([Arroyo et al.](https://arxiv.org/abs/1906.10026)) - spectral-based Multiple Adjacency Spectral Embedding algorithm
41
+ - COSIE [[Arroyo et al. (2020)](https://arxiv.org/abs/1906.10026)] - spectral-based Multiple Adjacency Spectral Embedding algorithm
42
42
 
43
43
  ## Installation
44
44
 
@@ -81,7 +81,7 @@ print(group_multiness.make_final_error_report(S_true, Qs_true, Rs_true, Ps=Ps_tr
81
81
 
82
82
  MIT License
83
83
 
84
- Copyright (c) 2024 Alexander Kagan
84
+ Copyright (c) 2025 Alexander Kagan
85
85
 
86
86
  Permission is hereby granted, free of charge, to any person obtaining a copy
87
87
  of this software and associated documentation files (the "Software"), to deal
@@ -1,10 +1,10 @@
1
1
  # GroupMultiNeSS
2
2
 
3
- GroupMultiNeSS is a package for statistical modeling of multilayer networks. It implements multiple approaches allowing to extract shared, group, and individual latent structures from a collection of networks on a shared set of nodes. Specifically, it contains the implementation of fitting sampling procedures for the following models:
3
+ GroupMultiNeSS is a package for statistical modeling of multilayer networks. It implements multiple approaches allowing to extract shared, group, and individual latent structures from a collection of networks on a shared set of nodes. Specifically, it contains the implementation of fitting and sampling procedures for the following models:
4
4
  - GroupMultiNeSS [Kagan et al. (2025)] - likelihood based approach with nuclear norm penalization, accounts for the additional group latent structure
5
5
  - MultiNeSS [[MacDonald et al. (2021)](https://arxiv.org/abs/2012.14409)] - likelihood based approach with nuclear norm penalization
6
6
  - MultiNeSS [[Tian et al. (2024)](https://arxiv.org/abs/2412.02151)] - likelihood based approach with pre-estimation of latent ranks via Shared Space Hunting algorithm
7
- - COSIE ([Arroyo et al.](https://arxiv.org/abs/1906.10026)) - spectral-based Multiple Adjacency Spectral Embedding algorithm
7
+ - COSIE [[Arroyo et al. (2020)](https://arxiv.org/abs/1906.10026)] - spectral-based Multiple Adjacency Spectral Embedding algorithm
8
8
 
9
9
  ## Installation
10
10
 
@@ -47,7 +47,7 @@ print(group_multiness.make_final_error_report(S_true, Qs_true, Rs_true, Ps=Ps_tr
47
47
 
48
48
  MIT License
49
49
 
50
- Copyright (c) 2024 Alexander Kagan
50
+ Copyright (c) 2025 Alexander Kagan
51
51
 
52
52
  Permission is hereby granted, free of charge, to any person obtaining a copy
53
53
  of this software and associated documentation files (the "Software"), to deal
@@ -3,7 +3,7 @@ from pathlib import Path
3
3
  this_directory = Path(__file__).parent
4
4
  LONG_DESCRIPTION = (this_directory / "README.md").read_text()
5
5
 
6
- VERSION = '0.0.2'
6
+ VERSION = '0.0.4'
7
7
  DESCRIPTION = 'GroupMultiNeSS package'
8
8
 
9
9
  setup(
File without changes
File without changes