Skip to main content

Probabilistic logit-based graph model and utilities

Project description

Logit Graph

A probabilistic logit-based graph model and utilities for fitting, simulating, and comparing random graph models to real-world networks. The package provides a scikit-learn-like API to fit the Logit Graph (LG) model and benchmark it against classic random graph models (ER, WS, BA, optionally GRG), using spectral distances and a Graph Information Criterion (GIC).

  • PyPI: logit-graph
  • Python: >=3.9
  • License: MIT

Table of Contents

Installation

Install from PyPI:

pip install logit-graph

For local development within this repo (recommended — uses uv):

make install        # creates .venv and installs with viz/notebook/progress extras
make install-dev    # also installs pytest, ruff, mypy
make install-torch  # also installs optional PyTorch backend

Or manually:

pip install -e ".[viz,notebook,progress]"

For full research environment (notebooks, plotting), use requirements.txt or environment.yml.

Examples

Two self-contained notebooks live under notebooks/examples/. They install logit-graph from PyPI and work without a repo checkout (except when you already have the package installed locally in editable mode).

Notebook What it shows
pypi_estimate_d_sigma.ipynb Simulate an LG graph with known n=200, d, and σ; recover via AIC and σ̂ via the Layer-2 offset logit
pypi_fit_real_network.ipynb Fit a real Facebook ego network (SNAP 686.edges) where LG wins (lowest GIC vs ER / WS / BA)

Run from the repo root (after make install):

jupyter notebook notebooks/examples/pypi_estimate_d_sigma.ipynb
jupyter notebook notebooks/examples/pypi_fit_real_network.ipynb

Quickstart

The recommended workflow uses the paper-consistent Layer-2 offset logit (feature_mode="incremental", β=1): pick via AIC, estimate σ̂, then compare fitted models with spectral GIC.

1. Simulate a graph and recover , σ̂

import numpy as np
from logit_graph import simulate_graph, select_d_ensemble, estimate_sigma_from_graph

N, D_TRUE, SIGMA_TRUE = 200, 1, -4.0

adj, meta = simulate_graph(
    N, D_TRUE, sigma=SIGMA_TRUE, n_iter=30_000,
    feature_mode="incremental", target_density=0.10, seed=42, return_meta=True,
)

d_hat, aic_stats = select_d_ensemble(
    graphs=[adj],
    d_candidates=[0, 1, 2, 3],
    feature_mode="incremental",
)
sigma_hat = estimate_sigma_from_graph(adj, d_hat, feature_mode="incremental")

print(f"true  d={D_TRUE}, σ={SIGMA_TRUE:.3f}")
print(f"est   d̂={d_hat}, σ̂={sigma_hat:.3f}")

See notebooks/examples/pypi_estimate_d_sigma.ipynb for the full notebook.

2. Fit a real network and compare LG vs ER / WS / BA

import networkx as nx
from logit_graph import GraphModelComparator, estimate_sigma_from_graph, select_d_ensemble

G = nx.read_edgelist("686.edges", nodetype=int)   # SNAP Facebook ego net
G = nx.convert_node_labels_to_integers(nx.Graph(G))
adj = nx.to_numpy_array(G)

d_hat, _ = select_d_ensemble([adj], [0, 1, 2, 3], "incremental")
sigma_hat = estimate_sigma_from_graph(adj, d_hat, "incremental")

comparator = GraphModelComparator(
    d_list=[d_hat],                    # LG only at the AIC-selected d̂
    lg_params={
        "max_iterations": 5000,
        "patience": 500,
        "edge_delta": None,
        "min_gic_threshold": 5,
        "er_p": 0.05,
        "check_interval": 50,
    },
    other_model_n_runs=2,
    dist_type="KL",
    verbose=False,
    other_models=["ER", "WS", "BA"],
    other_model_grid_points=5,
).compare(original_graph=G, graph_filepath="facebook_686")

print(comparator.summary_df.sort_values("gic_value"))
print(f"d̂={d_hat}, σ̂={sigma_hat:+.4f}")

On ego network 686, LG typically achieves the lowest GIC. See notebooks/examples/pypi_fit_real_network.ipynb (downloads the graph from SNAP if needed).

3. Direct spectral fit with LogitGraphFitter

For a fixed d, LogitGraphFitter runs a GIC-guided edge-swap search to produce a fitted graph whose spectrum matches the original:

import networkx as nx
from logit_graph import LogitGraphFitter

G = nx.karate_club_graph()

fitter = LogitGraphFitter(
    d=1, n_iteration=5000, patience=500, dist_type="KL", verbose=True,
).fit(G)

print(f"GIC={fitter.metadata['gic_value']:.4f}, σ={fitter.metadata['sigma']:.4f}")

This is the MCMC-style spectral matcher. For paper-consistent model selection, prefer select_d_ensemble + GraphModelComparator as in step 2.

Public API

All symbols below are importable from logit_graph. The paper-consistent path for estimation is simulate_graphselect_d_ensembleestimate_sigma_from_graphGraphModelComparator.

simulate_graph

Generate a random graph at fixed (n, d, σ).

from logit_graph import simulate_graph

adj = simulate_graph(
    n=200, d=1, sigma=-4.0, n_iter=30_000,
    feature_mode="incremental", target_density=0.10, seed=42,
)
# adj, meta = simulate_graph(..., return_meta=True)  → also returns σ, β, density, …
Parameter Description
n, d, sigma Graph size, feature depth, logit intercept
n_iter Gibbs iterations (d≥1) or ignored (d=0, direct ER)
feature_mode "incremental" (default paper mode), "bounded", or "full"
target_density Used when calibrating β if sigma is omitted
return_meta If True, return (adj, meta) with fitted σ, β, density

select_d_ensemble

Pick by AIC over candidate depths using the Layer-2 offset logit.

from logit_graph import select_d_ensemble

d_hat, aic_stats = select_d_ensemble(
    graphs=[adj],                      # list of adjacency matrices
    d_candidates=[0, 1, 2, 3],
    feature_mode="incremental",
    extra_penalty_per_d=0.0,           # add e.g. 3.0 to penalise larger d
)
# aic_stats[d] → {"aic", "ll", "sigma_hat", "n_obs", …}

estimate_sigma_from_graph

Offset-logit estimate of σ̂ at a fixed d (same estimator used inside the AIC table).

from logit_graph import estimate_sigma_from_graph

sigma_hat = estimate_sigma_from_graph(adj, d=1, feature_mode="incremental")

GraphModelComparator

Compare LG (at one or more d values) against baseline models using spectral GIC (lower = better).

from logit_graph import GraphModelComparator

comparator = GraphModelComparator(
    d_list=[d_hat],                    # usually the AIC-selected d̂ only
    lg_params={                        # passed to LogitGraphFitter internally
        "max_iterations": 5000,
        "patience": 500,
        "edge_delta": None,
        "min_gic_threshold": 5,
        "er_p": 0.05,
        "check_interval": 50,
    },
    other_model_n_runs=2,
    dist_type="KL",                    # "KL", "L1", or "L2"
    verbose=False,
    other_models=["ER", "WS", "BA"],   # optionally include "GRG"
    other_model_grid_points=5,
).compare(original_graph=G, graph_filepath="my_graph")

comparator.summary_df                      # per-model GIC and attributes
comparator.fitted_graphs_data["LG"]        # {"graph", "metadata", "attributes"}

When d_list has multiple entries, the comparator searches over d internally and keeps the best LG fit. For paper consistency, pass d_list=[d_hat] where d_hat comes from select_d_ensemble.

LogitGraphFitter

Sklearn-style fitter: given a fixed d, estimate σ via offset logit and search for a graph minimising spectral GIC.

Parameter Default Description
d 0 Neighborhood depth for degree-sum features
n_iteration 10000 Max edge-swap / Gibbs iterations
warm_up 500 Burn-in before GIC tracking
patience 2000 Early-stop patience
dist_type "KL" Spectral distance: "KL", "L1", "L2"
min_gic_threshold 5 Min GIC drop to reset patience
er_p 0.05 ER probability for warm-start graph
verbose True Print progress

After fit(G): fitter.fitted_graph, fitter.metadata (sigma, gic_value, best_iteration, …).

Other exports

Symbol Role
LogitGraphSimulation Lower-level multi-run LG simulation (used inside the comparator)
LogitRegEstimator Layer-2 offset logit on pair features; returns AIC stats
calculate_graph_attributes Density, clustering, diameter, assortativity, …
recommended_iterations Suggested Gibbs length as a function of n
build_pair_dataset, pair_feature, pair_feature_layer2 Feature construction for custom pipelines
GraphModel Core Gibbs / edge-swap engine
AICSweepConfig, SigmaSweepConfig, PRESETS Experiment presets under logit_graph.experiments

Model Overview

The Logit Graph model defines edge probabilities using a logistic function of local degree-sum features:

P(edge i–j) = sigmoid(σ · (deg_d(i) + deg_d(j)))

where deg_d(v) is the sum of degrees in the d-hop neighborhood of vertex v, and σ is the fitted scale parameter.

Fitting uses an iterative edge-swap procedure guided by the spectral density of the normalized Laplacian. At each step, edges are proposed and accepted/rejected based on a GIC-like criterion comparing spectral histograms.

GIC (Graph Information Criterion) is defined as:

GIC = 2 · spectral_distance(original, fitted) + 2 · |θ|

where |θ| is the number of free parameters (1 for LG). This penalizes model complexity analogously to AIC.

Supported spectral distances:

  • KL — KL divergence between normalized Laplacian spectral density histograms (default)
  • L1 — Manhattan distance
  • L2 — Euclidean distance

Supported baseline models for comparison:

Model Description
ER Erdős–Rényi (edge probability p)
WS Watts–Strogatz small-world
BA Barabási–Albert preferential attachment
GRG Geometric Random Graph (random geometric)

Project Structure

logit-graph/
├── src/logit_graph/          # Package source
│   ├── __init__.py           # Public exports (see Public API section)
│   ├── simulation.py         # High-level fitter and comparator classes
│   ├── graph.py              # GraphModel: MCMC edge-swap engine (core LG generation)
│   ├── logit_estimator.py    # Parameter estimation via logistic regression (sklearn / statsmodels / torch)
│   ├── gic.py                # GraphInformationCriterion: spectral density + GIC formula
│   ├── model_selection.py    # Model selection utilities and grid search helpers
│   ├── param_estimator.py    # Low-level sigma/alpha/beta parameter estimators
│   ├── degrees_counts.py     # degree_vertex / get_sum_degrees helpers
│   └── utils.py              # Miscellaneous utilities
│
├── tests/                    # Pytest test suite
│   ├── conftest.py
│   ├── test_graph_model.py   # GraphModel unit tests
│   ├── test_logit_estimator.py
│   ├── test_gic.py
│   ├── test_degrees_counts.py
│   ├── test_graph_helpers.py
│   ├── test_bugfixes.py
│   └── test_param_and_model_selection_smoke.py
│
├── notebooks/                # Reproducible analysis notebooks
│   ├── examples/             # PyPI-friendly tutorials (simulated + real data)
│   ├── base/                 # Core model validation and synthetic experiments
│   ├── anova/                # ANOVA-based graph comparison experiments
│   ├── connectomes_datasets/ # Brain connectome analysis
│   ├── human_connectomes/    # Human connectome experiments
│   ├── misc_datasets/        # Social networks: Facebook, Twitter, Reddit, Twitch, G+
│   ├── more_baselines/       # Additional baseline model comparisons
│   ├── dim_red/              # Dimensionality reduction experiments
│   ├── kde/                  # KDE-based density estimation experiments
│   ├── scale_free_tests/     # Scale-free network tests
│   ├── citation/             # Citation network experiments
│   └── playground/           # Exploratory / scratch notebooks
│
├── data/                     # Network datasets (not required for pip install)
│   ├── brain_graph/          # Brain connectivity data
│   ├── connectomes/          # Connectome datasets
│   ├── citation_networks/    # arXiv HEP-Th citation network
│   ├── facebook_large/       # Facebook SNAP dataset
│   ├── git_web_ml/           # GitHub ML social graph
│   ├── reddit_connected/     # Reddit connected-community graphs
│   ├── reddit_threads/       # Reddit thread graphs
│   ├── twitch/, twitch_gamers/ # Twitch social network datasets
│   ├── soc-flickr/, soc-orkut/, soc-youtube/, soc-academia/, soc-hamsterster/
│   └── misc/                 # Miscellaneous small graphs
│
├── images/                   # Generated figures used in the paper
├── runs/                     # Saved comparator outputs (.pkl)
├── scripts/                  # Helper scripts
├── pyproject.toml            # Package metadata and dependencies
├── requirements.txt          # Full research environment dependencies
├── environment.yml           # Conda environment spec
├── Makefile                  # Dev workflow (see below)
└── uv.lock                   # Locked dependency versions

Key source files

File Responsibility
simulation.py LogitGraphFitter, LogitGraphSimulation, GraphModelComparator — the main user-facing classes
graph.py GraphModel — MCMC-style edge-swap engine; initialized from an ER graph, iteratively proposes edge changes driven by the logit probability
logit_estimator.py Estimates σ (and optionally α, β) via logistic regression on degree-sum features; supports sklearn, statsmodels, and an optional PyTorch backend
gic.py GraphInformationCriterion — computes normalized Laplacian spectral density and evaluates GIC for any supported model
model_selection.py Grid search over d and baseline model parameters; aggregates results into a summary DataFrame
param_estimator.py Low-level MLE routines for model parameters
degrees_counts.py Fast degree_vertex and get_sum_degrees helpers used throughout

Development

All common tasks are available via make. Run make (or make help) to list them:

  .venv             Create virtual environment
  install           Install package in editable mode with all extras
  install-dev       Install dev / test dependencies
  install-torch     Install with optional PyTorch support
  lock              Regenerate uv.lock
  sync              Sync environment from lockfile
  test              Run test suite
  test-cov          Run tests with coverage report
  lint              Lint source code with ruff
  lint-fix          Auto-fix lint issues
  format            Format code with ruff
  typecheck         Run mypy type checking
  check             Run all checks (lint + types + tests)
  build             Build sdist and wheel
  publish           Upload to PyPI
  clean             Remove caches and build artifacts
  clean-all         Remove everything including .venv

Running tests

make test
# or with coverage:
make test-cov

Adding a new notebook

Place it in the appropriate subdirectory under notebooks/. The make nb-citation and make nb-playground targets show how to execute notebooks non-interactively via nbconvert.

Troubleshooting

  • Plotting and notebooks require optional dependencies in requirements.txt.
  • If igraph or pycairo fail to install, install their system packages or wheels first.
  • For very large graphs, lower max_iterations/patience or compare fewer models at once.
  • The PyTorch backend in logit_estimator.py is optional. If torch is not installed, the sklearn/statsmodels backend is used automatically.

Citation

If you use this package in academic work, please cite:

Ottoni, M. (2025). Logit Graph: probabilistic logit-based graph modeling and selection.
GitHub repository. https://github.com/mbottoni/logit-graph

A formal citation entry will be added upon publication.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

logit_graph-0.1.3.tar.gz (74.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

logit_graph-0.1.3-py3-none-any.whl (64.7 kB view details)

Uploaded Python 3

File details

Details for the file logit_graph-0.1.3.tar.gz.

File metadata

  • Download URL: logit_graph-0.1.3.tar.gz
  • Upload date:
  • Size: 74.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.4

File hashes

Hashes for logit_graph-0.1.3.tar.gz
Algorithm Hash digest
SHA256 a6d727bbd1691e5a0539ef5f3b6725e13b8f473922a63d0b33ae7dea92177721
MD5 f8748ab1607c83ce96123bdc954cfcc8
BLAKE2b-256 86ee05e75a2c8edc0e9a5247d11d8344f4523e4e45aa069807afe7d0eec3f467

See more details on using hashes here.

File details

Details for the file logit_graph-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: logit_graph-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 64.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.4

File hashes

Hashes for logit_graph-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 a826d92b356a86c32927ab49eb5455764f4aa6bab874ae490cbfacbc943b2987
MD5 31460a6cef5048bd44133da1a0dcf405
BLAKE2b-256 acaf715aa0b08924d80f6a398c6ceca986a1fdfa9df2461c1732aa8585ab3d3e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page