Questo progetto risolve numericamente le equazioni di massima entropia per modelli di reti complesse dirette. I modelli da implementare sono quattro, in ordine crescente di complessità:
- Vincoli: sequenza di gradi in/out (k_out_i, k_in_i) per ogni nodo i
- Incognite: 2N moltiplicatori di Lagrange (x_i, y_i), uno per out-degree e uno per in-degree
- Equazioni (nella parametrizzazione esponenziale, con θ tali che x=exp(-θ)):
k_out_i = Σ_{j≠i} (x_i * y_j) / (1 + x_i * y_j) k_in_i = Σ_{j≠i} (x_j * y_i) / (1 + x_j * y_i) - Dimensione del sistema: 2N equazioni in 2N incognite
- Con degree reduction: il numero di incognite scende al numero di valori distinti di (k_out, k_in)
- Status: ✅ COMPLETATO — converge al 100% fino a N=50k con FP-GS
- Vincoli: sequenza di strengths in/out (s_out_i, s_in_i)
- Incognite: 2N moltiplicatori (β_out_i, β_in_i)
- Equazioni (pesi interi, distribuzione geometrica):
s_out_i = Σ_{j≠i} (β_out_i * β_in_j) / (1 - β_out_i * β_in_j) s_in_i = Σ_{j≠i} (β_out_j * β_in_i) / (1 - β_out_j * β_in_i) - ATTENZIONE: richiede β_out_i * β_in_j < 1 per ogni coppia (i,j) — vincolo di feasibility
- Dimensione: 2N equazioni
- Con strength reduction: il numero di incognite scende al numero di valori distinti di (s_out, s_in)
- Status: ✅ COMPLETATO — converge con θ-Newton Anderson(10) fino a N=10k.
- Vincoli: sequenza di gradi E strengths (k_out_i, k_in_i, s_out_i, s_in_i)
- Incognite: 4N moltiplicatori (x_i, y_i, β_out_i, β_in_i)
- Equazioni:
k_out_i = Σ_{j≠i} (x_i * y_j) / (1 + x_i * y_j) k_in_i = Σ_{j≠i} (x_j * y_i) / (1 + x_j * y_i) [come nel DCM] s_out_i = Σ_{j≠i} p_ij · β_out_i · β_in_j / (1 - β_out_i · β_in_j) s_in_i = analoga - Dimensione: 4N equazioni in 4N incognite
- In pratica, prima si risolve il DCM (riusare le routine esistenti), poi si risolve un DWCM condizionato alla topologia del DCM.
- È una versione approssimata del DECM con più ampie possibilità di convergenza.
- Status: ✅ COMPLETATO — two-step solver (DCM → conditioned DWCM), benchmark N=5k
- Vincoli: sequenza di gradi E strengths (k_out_i, k_in_i, s_out_i, s_in_i)
- Incognite: 4N moltiplicatori (x_i, y_i, β_out_i, β_in_i)
- Equazioni:
k_out_i = Σ_{j≠i} (x_i * y_j) / (1 + x_i * y_j * (1/(1 - β_out_i * β_in_j) - 1)) k_in_i = analoga s_out_i = Σ_{j≠i} [valore atteso del peso condizionato alla topologia] s_in_i = analoga - Dimensione: 4N equazioni in 4N incognite
- Questo è il modello più difficile da far convergere: le equazioni di grado e strength sono accoppiate (β entra nelle equazioni di k)
- Status: DA FARE
Lezione appresa dalle Fasi 1-5: solo i metodi Fixed-Point scalano e convergono in modo affidabile per N grande. I metodi Newton-like (Newton pieno, Broyden, LM) richiedono O(N²) RAM e non scalano oltre N≈500. L-BFGS scala in RAM ma non converge in modo affidabile sul DWCM puro per reti eterogenee; tuttavia funziona sul aDECM con warm-start dalla soluzione two-step. Pertanto, il DECM va implementato usando prioritariamente i metodi seguenti.
- Aggiorna ciascun moltiplicatore isolandolo dalla rispettiva equazione
- Ordine Gauss-Seidel: aggiorna prima θ_out (o x), poi θ_in (o y) usando i valori appena aggiornati
- Convergenza lineare, O(N) per iterazione, RAM O(N)
- Damping opzionale α ∈ (0, 1] — usare α=1.0 di default, ridurre se oscilla
- Anderson acceleration (depth 5-10) per accelerare la convergenza: mantiene una storia dei residui e usa mixing lineare per predire il passo successivo
- Multi-start con 4 inizializzazioni: "strengths"/"degrees", "normalized", "uniform", "random"
- Dove funziona: DCM a tutte le scale; DWCM per reti non troppo eterogenee; aDECM (parte pesata)
- Dove fallisce: DWCM con nodi hub (β → 1), causa oscillazioni
- Riscrittura del fixed-point in θ-space: per ogni nodo i, Newton 1D sull'equazione
F_i(θ) = Σ_{j≠i} 1/expm1(θ_out_i + θ_in_j) - s_out_i = 0 - Step di Newton per nodo: Δθ_i = -F_i / F'_i, clippato a ±max_step
- Non può mai produrre β > 1 (perché θ resta sempre > 0)
- Ordine Gauss-Seidel: aggiorna θ_out prima, poi θ_in con valori freschi
- Anderson acceleration (depth 10) per convergenza superlineare
- Multi-start con 4 inizializzazioni
- Dove funziona: DWCM a tutte le scale, incluse reti con hub estremi; aDECM (parte pesata)
- Questo è il metodo di riferimento per DWCM e probabilmente per DECM
- Step 1: risolvere il DCM con FP-GS → ottenere (x*, y*)
- Step 2: con p_ij fissati dal DCM, risolvere la parte pesata con θ-Newton Anderson(10)
- Codice:
src/solvers/adecm_solver.py - Funziona perché il aDECM è separabile: le equazioni di grado non dipendono da β
- Non applicabile al DECM (dove le equazioni di k dipendono da β)
- Chunked computation per N > 5000: non materializzare mai la matrice N×N completa, calcolare a blocchi di chunk×N
- Backend selezionabile: ogni solver accetta un parametro
backend("auto","pytorch","numba")."auto"(default): PyTorch dense per N ≤ 5000, Numba scalar per N > 5000"pytorch": forza PyTorch (dense o chunked a seconda di N)"numba": forza Numba JIT-compilato (O(N) RAM, nessuna matrice N×N)- Fallback automatico con warning se il backend richiesto non è disponibile
- Numba è opzionale:
pip install dcms[numba]
- Zero-strength/zero-degree nodes: fissare θ = θ_MAX e non aggiornare mai
- Convergenza: ‖F‖∞ < tol (default 1e-5)
- Profiling: ogni solver restituisce
SolverResultcon theta, converged, iterations, residuals, elapsed_time, peak_ram_bytes
- Struttura del progetto:
src/models/,src/solvers/,src/benchmarks/,src/utils/,tests/ SolverResultdataclass con profiling- Degree/strength reduction
- Generatore reti di test:
src/utils/wng.py→k_s_generator_pl
- Equazioni, gradiente, Hessiano diagonale
- FP-GS converge al 100% su tutte le taglie testate (N=10 … 50k)
- Codice:
src/models/dcm.py,src/solvers/fixed_point.py
- Testato fino a N=50k
- FP-GS α=1.0: il più veloce, < 10 iterazioni tipicamente
- Benchmark:
src/benchmarks/dcm_comparison.py,src/benchmarks/dcm_scaling.py
- Equazioni e modello:
src/models/dwcm.py - FP-GS β-space: funziona ma oscilla su reti con hub (β → 1)
- θ-Newton Anderson(10): risolve il problema hub, converge su tutti i seed testati a N=5k
- Benchmark:
src/benchmarks/dwcm_comparison.py(flag--phase4,--sizes,--fast) - Codice:
src/solvers/fixed_point_dwcm.py(varianti "gauss-seidel", "theta-newton")
- Modello:
src/models/adecm.py— equazioni condizionate W_ij = p_ij · β_out_i · β_in_j / (1 − β_out_i · β_in_j) - Two-step solver:
src/solvers/adecm_solver.py— DCM → conditioned DWCM (θ-Newton Anderson) - FP solver per la parte pesata:
src/solvers/fixed_point_adecm.py— varianti FP-GS, Jacobi, θ-Newton + Anderson - Metodi joint su 4N variabili:
residual_joint,neg_log_likelihood_joint,constraint_error_joint - 46 unit test:
tests/test_adecm.py - Benchmark N=1k:
src/benchmarks/adecm_comparison.py - README aggiornato con sezione aDECM (modello 1.3 + tabella N=1k)
- Implementare le equazioni del DECM in
src/models/decm.py- 4N incognite: (x_i, y_i, β_out_i, β_in_i)
- Le equazioni di grado e di strength sono accoppiate — non separabili come nel aDECM
- Equazione di grado: il termine
1/(1 - β_out_i * β_in_j)entra nel denominatore di k_out_i - Implementare:
residual,neg_log_likelihood,hessian_diag,jacobian(per N piccolo),initial_theta,constraint_error,max_relative_error
- Implementare il solver in
src/solvers/fixed_point_decm.py:- Strategia consigliata: FP-GS alternato
- Aggiornare (x, y) con le equazioni di grado tenendo (β_out, β_in) fissi
- Aggiornare (β_out, β_in) con θ-Newton tenendo (x, y) fissi
- Ripetere fino a convergenza globale
- Anderson acceleration sul vettore completo (4N)
- Strategia consigliata: FP-GS alternato
- ATTENZIONE: le equazioni di grado del DECM sono diverse da quelle del DCM — il termine β entra nel denominatore
- ATTENZIONE al vincolo β_out_i * β_in_j < 1
- Testare prima su N=10 con soluzione nota, poi N=100, 1k, 5k
- Se non converge con la Strategia consigliata, provare:
- Damping aggressivo (α=0.1-0.3) sulle prime 50 iterazioni, poi rilasciare
- Continuazione: risolvere prima il aDECM, poi usare quella soluzione come init per il DECM
- Anderson depth più alto (20-30)
- Benchmark: creare
src/benchmarks/decm_comparison.py
- Aggiornare il README con le sezioni DECM (modello, API, tabelle di performance)
- Tabella comparativa per tutti i modelli: convergenza, iterazioni, tempo, RAM per N=1k, 5k, 10k
- Raccomandazione finale: quale metodo per quale modello a quale scala
Queste informazioni servono per evitare di ripetere esperimenti falliti:
| Metodo | DCM | DWCM | aDECM | Motivo del fallimento |
|---|---|---|---|---|
| FP-GS α=1.0 | ✅ 100% | ⚠ oscilla su hub | ✅ (parte pesata) | β > 1 causa periodo-3 su DWCM |
| FP-GS α=0.3-0.5 | ✅ più lento | ⚠ stesso problema | ✅ | damping non basta per DWCM hub |
| FP-GS Anderson(10) | ✅ | ⚠ migliora ma non risolve hub | ✅ | mixing history contaminata su DWCM |
| θ-Newton Anderson(10) | N/A | ✅ 100% fino a N=5k | ✅ | metodo di riferimento per peso |
| Two-step (DCM→DWCM cond.) | N/A | N/A | ✅ | separabile, robusto |
| L-BFGS (2N) | ✅ DCM | ❌ DWCM eterogeneo | N/A | non converge su reti power-law |
| Newton pieno | ✅ N≤500 | ✅ N≤500 | ✅ N≤500 | O(N²) RAM, non scala |
| Broyden | ✅ N≤500 | ⚠ instabile | N/A | rank-1 update diverge |
| LM (full Jacobian) | ✅ N≤500 | ✅ N≤500 | ✅ N≤500 | O(N²) RAM, non scala |
| LM (diag Hessian) | ⚠ lento | ❌ | ⚠ | non converge affidabilmente |
Conclusione: usare FP-GS + θ-Newton con Anderson per la parte pesata. Per il DECM, provare prima la strategia alternata (FP-GS topo + θ-Newton peso) con warm-start da aDECM.
- Un metodo "converge" se ‖F(θ)‖∞ < 1e-5
- Per reti da 10k nodi, almeno un metodo deve convergere in meno di 15 minuti
- Il codice deve essere testabile con
pytestsu sistemi piccoli (CI-friendly, < 30 sec)
- Paper di riferimento: Squartini & Garlaschelli, New J. Phys. 13 (2011) 083001
- Implementazione di riferimento: NEMtropy (https://github.com/nicoloval/NEMtropy)
- Anderson acceleration: Walker & Ni, SIAM J. Numer. Anal. 49(4), 2011
- NON usare scipy.optimize.fsolve o root come black-box — vogliamo controllare il solver
- NON materializzare matrici NxN dense per N > 5000
- NON implementare Newton pieno, Broyden, o LM full-Jacobian per il DECM a scala grande — non scalano e abbiamo già verificato
- NON dichiarare un metodo "non funzionante" dopo un singolo tentativo — provare almeno 4 inizializzazioni diverse
- NON ignorare i NaN — se compaiono, loggare dove e con quali parametri e fermarsi con grazia
- NON riscrivere le parti DCM/DWCM/aDECM del README quando aggiungi il DECM — solo appendere
Il codice per generare reti sintetiche è in src/utils/wng.py. La funzione è k_s_generator_pl.
k, s = k_s_generator_pl(N, rho=1e-3, seed=42)
# k: tensor (2N,) = [k_out | k_in]
# s: tensor (2N,) = [s_out | s_in]Il solver ha successo se i vincoli ricostruiti dai θ_trovati coincidono con quelli osservati.
| Scopo | N nodi | Dove usarlo |
|---|---|---|
| Unit test (CI) | 10-50 | tests/, deve girare in < 5 sec |
| Validazione | 100-1000 | verifica convergenza |
| Scaling | 5k, 10k | benchmark RAM/tempo |