directedstructure 0.1.1__tar.gz → 0.2.3__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.
- directedstructure-0.2.3/.github/workflows/release.yml +79 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/.gitignore +2 -0
- directedstructure-0.2.3/CMakeLists.txt +66 -0
- directedstructure-0.2.3/Makefile +102 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/PKG-INFO +87 -24
- directedstructure-0.2.3/README.md +151 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/TODO.md +47 -1
- directedstructure-0.2.3/bindings/python/bindings.cpp +356 -0
- directedstructure-0.2.3/examples/data/generate_synthetic_networks.py +139 -0
- directedstructure-0.2.3/examples/data/networks/championship_2021.gml +6196 -0
- directedstructure-0.2.3/examples/data/networks/english-football-2025.gml +59986 -0
- directedstructure-0.2.3/examples/data/networks/example_3_nodes.gml +103 -0
- directedstructure-0.2.3/examples/data/networks/snr025_sigC2_rep07.gml +2951 -0
- directedstructure-0.2.3/examples/data/networks/snr150_sigC5_rep09.gml +3003 -0
- directedstructure-0.2.3/examples/directed_undirected_communities.py +76 -0
- directedstructure-0.2.3/examples/highschool_30_group_assignments_comparison.png +0 -0
- directedstructure-0.2.3/examples/highschool_30_neutral_inference.gml +7461 -0
- directedstructure-0.2.3/examples/highschool_30_original_inference.gml +13306 -0
- directedstructure-0.2.3/examples/highschool_51_group_assignments_comparison.png +0 -0
- directedstructure-0.2.3/examples/highschool_51_neutral_inference.gml +4660 -0
- directedstructure-0.2.3/examples/highschool_51_original_inference.gml +7490 -0
- directedstructure-0.2.3/examples/network_properties.csv +11 -0
- directedstructure-0.2.3/examples/node_properties.csv +493 -0
- directedstructure-0.2.3/examples/python/directed_undirected_communities.py +76 -0
- directedstructure-0.2.3/examples/python/prediction.py +1 -0
- directedstructure-0.2.3/examples/python/testing.py +110 -0
- directedstructure-0.1.1/examples/testing.py → directedstructure-0.2.3/examples/python/testing_fixed.py +10 -2
- directedstructure-0.2.3/examples/sampling.py +1 -0
- directedstructure-0.2.3/examples/speed_test.py +1 -0
- directedstructure-0.2.3/examples/testing.png +0 -0
- directedstructure-0.2.3/examples/testing.py +242 -0
- directedstructure-0.2.3/examples/testing_coincidence_comparison.png +0 -0
- directedstructure-0.2.3/examples/testing_fixed.py +104 -0
- directedstructure-0.2.3/examples/testing_graph.png +0 -0
- directedstructure-0.2.3/examples/testing_inferences.gml +3621 -0
- directedstructure-0.2.3/examples/testing_node_properties.png +0 -0
- directedstructure-0.2.3/examples/testing_node_properties_from_samples.png +0 -0
- directedstructure-0.2.3/examples/testing_num_groups.png +0 -0
- directedstructure-0.2.3/examples/testing_samples.csv +1001 -0
- directedstructure-0.2.3/node_properties.csv +457 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/pyproject.toml +21 -13
- {directedstructure-0.1.1 → directedstructure-0.2.3/python/directedstructure}/src/directedstructure/__init__.py +8 -8
- directedstructure-0.2.3/python/directedstructure/src/directedstructure/_version.py +24 -0
- directedstructure-0.2.3/python/directedstructure/src/directedstructure/core.py +491 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3/python/directedstructure}/src/directedstructure/input_output.py +344 -77
- {directedstructure-0.1.1 → directedstructure-0.2.3}/src/MCMC_bookkeeping.cpp +30 -59
- directedstructure-0.2.3/src/MCMC_core.cpp +547 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/src/MCMC_core.h +16 -50
- {directedstructure-0.1.1 → directedstructure-0.2.3}/src/MCMC_proposals.cpp +181 -161
- directedstructure-0.2.3/src/fit.cpp +186 -0
- directedstructure-0.2.3/src/fit.h +57 -0
- directedstructure-0.2.3/src/fit_result.cpp +113 -0
- directedstructure-0.2.3/src/fit_result.h +48 -0
- directedstructure-0.2.3/src/globals.cpp +16 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/src/globals.h +12 -15
- {directedstructure-0.1.1 → directedstructure-0.2.3}/src/helpers.cpp +6 -4
- directedstructure-0.2.3/tests/README.md +125 -0
- directedstructure-0.2.3/tests/data/inputs/wins_ties_3node_point_eval.csv +12 -0
- directedstructure-0.2.3/tests/data/outputs/triangle_coupled_coincidence.csv +3 -0
- directedstructure-0.2.3/tests/data/outputs/triangle_independent_coincidence.csv +3 -0
- directedstructure-0.2.3/tests/data/outputs/wins_ties_3node_coincidence.csv +3 -0
- directedstructure-0.2.3/tests/data/outputs/wins_ties_3node_point_eval_components.csv +2 -0
- directedstructure-0.2.3/tests/mcmc_core_test.cpp +470 -0
- directedstructure-0.2.3/tests/python/test_package.py +637 -0
- directedstructure-0.1.1/.github/workflows/cd.yml +0 -95
- directedstructure-0.1.1/.github/workflows/ci.yml +0 -77
- directedstructure-0.1.1/.github/workflows/release.yml +0 -114
- directedstructure-0.1.1/CMakeLists.txt +0 -25
- directedstructure-0.1.1/Makefile +0 -62
- directedstructure-0.1.1/README.md +0 -88
- directedstructure-0.1.1/examples/data/samples/friends.csv +0 -4491
- directedstructure-0.1.1/examples/data/samples/highschool_51.csv +0 -1075
- directedstructure-0.1.1/examples/testing.png +0 -0
- directedstructure-0.1.1/examples/visualization.py +0 -12
- directedstructure-0.1.1/src/MCMC_core.cpp +0 -728
- directedstructure-0.1.1/src/MCMC_merge_split.cpp +0 -738
- directedstructure-0.1.1/src/consensus.cpp +0 -132
- directedstructure-0.1.1/src/consensus.h +0 -27
- directedstructure-0.1.1/src/directedstructure/_version.py +0 -34
- directedstructure-0.1.1/src/directedstructure/_version.pyi +0 -4
- directedstructure-0.1.1/src/directedstructure/bayes_factor.py +0 -101
- directedstructure-0.1.1/src/directedstructure/clusterings.py +0 -130
- directedstructure-0.1.1/src/directedstructure/core.py +0 -571
- directedstructure-0.1.1/src/directedstructure/prediction.py +0 -126
- directedstructure-0.1.1/src/directedstructure/sample.py +0 -338
- directedstructure-0.1.1/src/directedstructure/sample_analysis.py +0 -126
- directedstructure-0.1.1/tests/mcmc_core_test.cpp +0 -515
- directedstructure-0.1.1/tests/test_package.py +0 -9
- {directedstructure-0.1.1 → directedstructure-0.2.3}/.copier-answers.yml +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/.git_archival.txt +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/.gitattributes +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/.github/CONTRIBUTING.md +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/.github/dependabot.yml +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/.github/release.yml +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/.pre-commit-config.yaml +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/.readthedocs.yaml +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/LICENSE +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/docs/conf.py +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/docs/examples/prediction.md +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/docs/examples/speed_test.md +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/docs/examples/visualization.md +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/docs/index.md +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/docs/modules.md +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/business_depts.gml +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/friends.gml +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/friends_raw.gml +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/highschool_30.gml +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/highschool_30_raw.gml +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/highschool_51.gml +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/highschool_51_raw.gml +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/highschool_84.gml +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/highschool_84_raw.gml +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/process_friendship_nominations.py +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/take_samples.py +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/test_2.gml +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/prediction.py +0 -0
- {directedstructure-0.1.1/examples → directedstructure-0.2.3/examples/python}/consensus.py +0 -0
- {directedstructure-0.1.1/examples → directedstructure-0.2.3/examples/python}/sampling.py +0 -0
- {directedstructure-0.1.1/examples → directedstructure-0.2.3/examples/python}/speed_test.py +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/testing copy.png +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/noxfile.py +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3/python/directedstructure}/src/directedstructure/MCMC_core.pyi +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3/python/directedstructure}/src/directedstructure/_core.pyi +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3/python/directedstructure}/src/directedstructure/_graph_cache.py +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3/python/directedstructure}/src/directedstructure/_util.py +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3/python/directedstructure}/src/directedstructure/py.typed +0 -0
- {directedstructure-0.1.1 → directedstructure-0.2.3}/src/helpers.h +0 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
release:
|
|
6
|
+
types:
|
|
7
|
+
- published
|
|
8
|
+
pull_request:
|
|
9
|
+
paths:
|
|
10
|
+
- .github/workflows/release.yml
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
env:
|
|
17
|
+
FORCE_COLOR: 3
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
make_sdist:
|
|
21
|
+
name: Make SDist
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
with:
|
|
26
|
+
fetch-depth: 0
|
|
27
|
+
|
|
28
|
+
- name: Build SDist
|
|
29
|
+
run: pipx run build --sdist
|
|
30
|
+
|
|
31
|
+
- uses: actions/upload-artifact@v4
|
|
32
|
+
with:
|
|
33
|
+
name: cibw-sdist
|
|
34
|
+
path: dist/*.tar.gz
|
|
35
|
+
|
|
36
|
+
build_wheels:
|
|
37
|
+
name: Wheel on ${{ matrix.os }}
|
|
38
|
+
runs-on: ${{ matrix.os }}
|
|
39
|
+
strategy:
|
|
40
|
+
fail-fast: false
|
|
41
|
+
matrix:
|
|
42
|
+
os: [macos-14, ubuntu-latest, windows-latest]
|
|
43
|
+
|
|
44
|
+
steps:
|
|
45
|
+
- uses: actions/checkout@v4
|
|
46
|
+
with:
|
|
47
|
+
fetch-depth: 0
|
|
48
|
+
|
|
49
|
+
- name: Install cibuildwheel
|
|
50
|
+
run: python -m pip install cibuildwheel==2.20
|
|
51
|
+
|
|
52
|
+
- name: Build wheels
|
|
53
|
+
env:
|
|
54
|
+
CIBW_ARCHS_LINUX: x86_64
|
|
55
|
+
CIBW_ARCHS_WINDOWS: AMD64
|
|
56
|
+
run: cibuildwheel --output-dir wheelhouse
|
|
57
|
+
|
|
58
|
+
- uses: actions/upload-artifact@v4
|
|
59
|
+
with:
|
|
60
|
+
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
|
|
61
|
+
path: wheelhouse/*.whl
|
|
62
|
+
|
|
63
|
+
upload_all:
|
|
64
|
+
needs: [build_wheels, make_sdist]
|
|
65
|
+
environment: pypi
|
|
66
|
+
permissions:
|
|
67
|
+
id-token: write
|
|
68
|
+
contents: read
|
|
69
|
+
runs-on: ubuntu-latest
|
|
70
|
+
steps:
|
|
71
|
+
- uses: actions/download-artifact@v4
|
|
72
|
+
with:
|
|
73
|
+
pattern: cibw-*
|
|
74
|
+
path: dist
|
|
75
|
+
merge-multiple: true
|
|
76
|
+
|
|
77
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
78
|
+
with:
|
|
79
|
+
verbose: true
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.15...3.26)
|
|
2
|
+
project(${SKBUILD_PROJECT_NAME} LANGUAGES CXX)
|
|
3
|
+
|
|
4
|
+
option(BUILD_TESTING "Build Catch2-based native tests" OFF)
|
|
5
|
+
|
|
6
|
+
set(PYBIND11_FINDPYTHON ON)
|
|
7
|
+
find_package(pybind11 CONFIG REQUIRED)
|
|
8
|
+
|
|
9
|
+
# Build a reusable core library so other language bindings can link against it
|
|
10
|
+
add_library(directedstructure_core STATIC
|
|
11
|
+
src/MCMC_core.cpp
|
|
12
|
+
src/MCMC_proposals.cpp
|
|
13
|
+
src/MCMC_bookkeeping.cpp
|
|
14
|
+
src/helpers.cpp
|
|
15
|
+
src/globals.cpp
|
|
16
|
+
src/fit_result.cpp
|
|
17
|
+
src/fit.cpp)
|
|
18
|
+
target_include_directories(directedstructure_core PUBLIC ${CMAKE_SOURCE_DIR}/src)
|
|
19
|
+
set_target_properties(directedstructure_core PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
|
20
|
+
target_compile_features(directedstructure_core PUBLIC cxx_std_17)
|
|
21
|
+
target_compile_options(directedstructure_core PRIVATE -O3 -march=native)
|
|
22
|
+
|
|
23
|
+
# Python bindings: a small pybind11 module that links to the core library
|
|
24
|
+
pybind11_add_module(MCMC_core MODULE bindings/python/bindings.cpp)
|
|
25
|
+
target_link_libraries(MCMC_core PRIVATE directedstructure_core)
|
|
26
|
+
target_include_directories(MCMC_core PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
|
27
|
+
target_compile_features(MCMC_core PRIVATE cxx_std_17)
|
|
28
|
+
set_target_properties(
|
|
29
|
+
MCMC_core PROPERTIES LIBRARY_OUTPUT_DIRECTORY
|
|
30
|
+
${CMAKE_SOURCE_DIR}/python/directedstructure/src/directedstructure)
|
|
31
|
+
install(TARGETS MCMC_core LIBRARY DESTINATION ${SKBUILD_PROJECT_NAME})
|
|
32
|
+
|
|
33
|
+
if(BUILD_TESTING)
|
|
34
|
+
enable_testing()
|
|
35
|
+
|
|
36
|
+
find_package(Catch2 3 QUIET)
|
|
37
|
+
if(NOT Catch2_FOUND)
|
|
38
|
+
include(FetchContent)
|
|
39
|
+
FetchContent_Declare(
|
|
40
|
+
Catch2
|
|
41
|
+
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
|
42
|
+
GIT_TAG v3.7.1
|
|
43
|
+
GIT_SHALLOW TRUE
|
|
44
|
+
)
|
|
45
|
+
FetchContent_MakeAvailable(Catch2)
|
|
46
|
+
list(APPEND CMAKE_MODULE_PATH "${catch2_SOURCE_DIR}/extras")
|
|
47
|
+
endif()
|
|
48
|
+
|
|
49
|
+
add_executable(mcmc_core_test
|
|
50
|
+
tests/mcmc_core_test.cpp
|
|
51
|
+
src/MCMC_core.cpp
|
|
52
|
+
src/MCMC_proposals.cpp
|
|
53
|
+
src/MCMC_bookkeeping.cpp
|
|
54
|
+
src/helpers.cpp
|
|
55
|
+
src/globals.cpp
|
|
56
|
+
src/fit_result.cpp
|
|
57
|
+
src/fit.cpp
|
|
58
|
+
)
|
|
59
|
+
target_include_directories(mcmc_core_test PRIVATE tests src)
|
|
60
|
+
target_compile_features(mcmc_core_test PRIVATE cxx_std_17)
|
|
61
|
+
target_compile_options(mcmc_core_test PRIVATE -Wall -Wextra)
|
|
62
|
+
target_link_libraries(mcmc_core_test PRIVATE Catch2::Catch2WithMain)
|
|
63
|
+
|
|
64
|
+
include(CTest)
|
|
65
|
+
add_test(NAME mcmc_core_test COMMAND mcmc_core_test)
|
|
66
|
+
endif()
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Makefile to build the pybind11 extension module and the C++ test harness
|
|
2
|
+
|
|
3
|
+
PYTHON ?= python3
|
|
4
|
+
CXX ?= g++
|
|
5
|
+
BUILD_DIR := build
|
|
6
|
+
SRC_DIR := src
|
|
7
|
+
PKG_DIR := $(SRC_DIR)/directedstructure
|
|
8
|
+
MODULE_NAME := MCMC_core
|
|
9
|
+
EXT_SUFFIX := $(shell $(PYTHON) -c "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX') or '')")
|
|
10
|
+
PYBIND11_INCLUDES := $(shell $(PYTHON) -m pybind11 --includes 2>/dev/null)
|
|
11
|
+
# Prefer embed flags so executables link against libpython; fall back to standard ldflags
|
|
12
|
+
PY_LDFLAGS := $(shell $(PYTHON)-config --embed --ldflags 2>/dev/null || $(PYTHON)-config --ldflags 2>/dev/null || python3-config --embed --ldflags 2>/dev/null || python3-config --ldflags 2>/dev/null || echo "")
|
|
13
|
+
MKDIR_P := mkdir -p
|
|
14
|
+
|
|
15
|
+
CXXFLAGS := -std=c++17 -Wall -Wextra -O3 -march=native -fPIC -I$(SRC_DIR) -DDIRECTEDSTRUCTURE_PYBIND
|
|
16
|
+
DEBUG_CXXFLAGS := -std=c++17 -Wall -Wextra -O0 -g -fPIC -I$(SRC_DIR) -DDIRECTEDSTRUCTURE_PYBIND
|
|
17
|
+
TEST_CXXFLAGS := -std=c++17 -Wall -Wextra -O3 -march=native -fPIC -I$(SRC_DIR)
|
|
18
|
+
TEST_DEBUG_CXXFLAGS := -std=c++17 -Wall -Wextra -O0 -g -fPIC -I$(SRC_DIR)
|
|
19
|
+
|
|
20
|
+
SOURCES := $(SRC_DIR)/MCMC_core.cpp \
|
|
21
|
+
$(SRC_DIR)/MCMC_proposals.cpp \
|
|
22
|
+
$(SRC_DIR)/MCMC_bookkeeping.cpp \
|
|
23
|
+
$(SRC_DIR)/MCMC_merge_split.cpp \
|
|
24
|
+
$(SRC_DIR)/consensus.cpp \
|
|
25
|
+
$(SRC_DIR)/helpers.cpp
|
|
26
|
+
OBJECTS := $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(SOURCES))
|
|
27
|
+
TEST_CORE_OBJECTS := $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%_test.o,$(SOURCES))
|
|
28
|
+
|
|
29
|
+
TEST_SOURCES := tests/mcmc_core_test.cpp
|
|
30
|
+
TEST_OBJECTS := $(patsubst tests/%.cpp,$(BUILD_DIR)/tests_%.o,$(TEST_SOURCES))
|
|
31
|
+
DEBUG_OBJECTS := $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%_debug.o,$(SOURCES))
|
|
32
|
+
DEBUG_TEST_CORE_OBJECTS := $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%_test_debug.o,$(SOURCES))
|
|
33
|
+
|
|
34
|
+
MODULE_TARGET := $(PKG_DIR)/$(MODULE_NAME)$(EXT_SUFFIX)
|
|
35
|
+
TEST_BIN := $(BUILD_DIR)/mcmc_core_test
|
|
36
|
+
TEST_BIN_DEBUG := $(BUILD_DIR)/mcmc_core_test_debug
|
|
37
|
+
CSV_BIN := AddHealth_analysis/run_sampler_to_csv
|
|
38
|
+
|
|
39
|
+
.PHONY: all clean test csv
|
|
40
|
+
|
|
41
|
+
all: $(MODULE_TARGET) $(TEST_BIN)
|
|
42
|
+
|
|
43
|
+
$(BUILD_DIR):
|
|
44
|
+
$(MKDIR_P) $(BUILD_DIR)
|
|
45
|
+
|
|
46
|
+
$(PKG_DIR):
|
|
47
|
+
$(MKDIR_P) $(PKG_DIR)
|
|
48
|
+
|
|
49
|
+
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp | $(BUILD_DIR)
|
|
50
|
+
$(CXX) $(CXXFLAGS) $(PYBIND11_INCLUDES) -c $< -o $@
|
|
51
|
+
|
|
52
|
+
$(BUILD_DIR)/tests_%.o: tests/%.cpp | $(BUILD_DIR)
|
|
53
|
+
$(CXX) $(TEST_CXXFLAGS) -c $< -o $@
|
|
54
|
+
|
|
55
|
+
$(BUILD_DIR)/addhealth_%.o: AddHealth_analysis/%.cpp | $(BUILD_DIR)
|
|
56
|
+
$(CXX) $(TEST_CXXFLAGS) -c $< -o $@
|
|
57
|
+
|
|
58
|
+
$(BUILD_DIR)/%_test.o: $(SRC_DIR)/%.cpp | $(BUILD_DIR)
|
|
59
|
+
$(CXX) $(TEST_CXXFLAGS) -c $< -o $@
|
|
60
|
+
|
|
61
|
+
$(MODULE_TARGET): $(OBJECTS) | $(PKG_DIR)
|
|
62
|
+
$(CXX) $(CXXFLAGS) -shared $^ -o $@ $(PY_LDFLAGS)
|
|
63
|
+
|
|
64
|
+
$(TEST_BIN): $(TEST_OBJECTS) $(TEST_CORE_OBJECTS) | $(BUILD_DIR)
|
|
65
|
+
$(CXX) $(TEST_CXXFLAGS) $^ -o $@
|
|
66
|
+
|
|
67
|
+
$(CSV_BIN): $(BUILD_DIR)/addhealth_run_sampler_to_csv.o $(TEST_CORE_OBJECTS)
|
|
68
|
+
$(CXX) $(TEST_CXXFLAGS) $^ -o $@
|
|
69
|
+
|
|
70
|
+
# Debug build for valgrind
|
|
71
|
+
$(BUILD_DIR)/%_debug.o: $(SRC_DIR)/%.cpp | $(BUILD_DIR)
|
|
72
|
+
$(CXX) $(DEBUG_CXXFLAGS) $(PYBIND11_INCLUDES) -c $< -o $@
|
|
73
|
+
|
|
74
|
+
$(BUILD_DIR)/tests_%_debug.o: tests/%.cpp | $(BUILD_DIR)
|
|
75
|
+
$(CXX) $(TEST_DEBUG_CXXFLAGS) -c $< -o $@
|
|
76
|
+
|
|
77
|
+
$(BUILD_DIR)/%_test_debug.o: $(SRC_DIR)/%.cpp | $(BUILD_DIR)
|
|
78
|
+
$(CXX) $(TEST_DEBUG_CXXFLAGS) -c $< -o $@
|
|
79
|
+
|
|
80
|
+
DEBUG_TEST_OBJECTS := $(patsubst tests/%.cpp,$(BUILD_DIR)/tests_%_debug.o,$(TEST_SOURCES))
|
|
81
|
+
|
|
82
|
+
$(TEST_BIN_DEBUG): $(DEBUG_TEST_OBJECTS) $(DEBUG_TEST_CORE_OBJECTS) | $(BUILD_DIR)
|
|
83
|
+
$(CXX) $(TEST_DEBUG_CXXFLAGS) $(DEBUG_TEST_OBJECTS) $(DEBUG_TEST_CORE_OBJECTS) -o $@
|
|
84
|
+
|
|
85
|
+
clean:
|
|
86
|
+
rm -rf $(BUILD_DIR) $(MODULE_TARGET)
|
|
87
|
+
rm -f $(PKG_DIR)/$(MODULE_NAME)*.so $(PKG_DIR)/$(MODULE_NAME)*.pyd
|
|
88
|
+
rm -f $(PKG_DIR)/$(MODULE_NAME)*.dll
|
|
89
|
+
rm -f $(PKG_DIR)/$(MODULE_NAME)*.dylib
|
|
90
|
+
rm -f $(PKG_DIR)/$(MODULE_NAME)*.pdb
|
|
91
|
+
rm -f $(PKG_DIR)/$(MODULE_NAME)*.exp
|
|
92
|
+
|
|
93
|
+
# Run the C++ harness
|
|
94
|
+
test: $(TEST_BIN)
|
|
95
|
+
./$(TEST_BIN)
|
|
96
|
+
|
|
97
|
+
# Build the C++ sampler-to-CSV binary only
|
|
98
|
+
csv: $(CSV_BIN)
|
|
99
|
+
|
|
100
|
+
# Run with valgrind (requires valgrind to be installed)
|
|
101
|
+
test-valgrind: $(TEST_BIN_DEBUG)
|
|
102
|
+
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose ./$(TEST_BIN_DEBUG)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: directedstructure
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Infer communities, hierarchies, and their connection in directed graphs
|
|
5
5
|
Author-Email: Maximilian Jerdee <mjerdee@santafe.edu>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -25,7 +25,7 @@ Project-URL: Changelog, https://github.com/maxjerdee/directedstructure/releases
|
|
|
25
25
|
Requires-Python: <3.14,>=3.10
|
|
26
26
|
Requires-Dist: matplotlib>=3.8.0
|
|
27
27
|
Requires-Dist: numpy>=1.26.0
|
|
28
|
-
Requires-Dist: pandas
|
|
28
|
+
Requires-Dist: pandas<3.0,>=2.2.0
|
|
29
29
|
Requires-Dist: networkx>=3.0
|
|
30
30
|
Requires-Dist: tqdm>=4.65.0
|
|
31
31
|
Description-Content-Type: text/markdown
|
|
@@ -57,13 +57,17 @@ Description-Content-Type: text/markdown
|
|
|
57
57
|
|
|
58
58
|
##### Maximilian Jerdee, Elizabeth Bruch, Mark Newman
|
|
59
59
|
|
|
60
|
-
This
|
|
60
|
+
This Python package uses Bayesian inference to identify communities and hierarchies of nodes in a directed network, as well as measure the interaction between those structures.
|
|
61
61
|
|
|
62
|
-
We model community structure using a stochastic block model, hierarchy structure with a Bradley-Terry model, and their interaction according to our work
|
|
62
|
+
We model community structure using a stochastic block model, hierarchy structure with a Bradley-Terry model, and their interaction according to our work (link forthcoming).
|
|
63
63
|
|
|
64
64
|
## Installation
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
```bash
|
|
67
|
+
pip install directedstructure
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Or build locally by cloning this repository and running
|
|
67
71
|
|
|
68
72
|
```bash
|
|
69
73
|
pip install .
|
|
@@ -71,50 +75,109 @@ pip install .
|
|
|
71
75
|
|
|
72
76
|
in the base directory (requires a C++ compiler).
|
|
73
77
|
|
|
74
|
-
##
|
|
75
|
-
Once installed, the package can be used to identify the network structures.
|
|
78
|
+
## Usage
|
|
76
79
|
|
|
77
|
-
|
|
78
|
-
We recommend using [NetworkX](https://networkx.org/) to load the network and then using the `directedstructure` package to infer the node grouping and hierarchy.
|
|
80
|
+
The public API has three objects: `Config`, `fit`, and `Result`.
|
|
79
81
|
|
|
82
|
+
### Load a network
|
|
80
83
|
|
|
81
84
|
```python
|
|
82
85
|
import directedstructure as ds
|
|
83
86
|
import networkx as nx
|
|
84
|
-
import pandas as pd
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
+
G = nx.read_gml("examples/data/networks/friends.gml")
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Edges may carry a `type` attribute (`'dominant'` or `'neutral'`) to distinguish directed pairwise comparisons from symmetric ties. If no type information is provided all edges are treated as dominant.
|
|
92
|
+
|
|
93
|
+
### Configure the model
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
config = ds.Config(
|
|
97
|
+
groups_model="general_canonical", # community structure preset
|
|
98
|
+
hierarchy_model="bradley_terry_ties", # auto-selected if omitted
|
|
99
|
+
interaction="coupled", # coupling between communities and hierarchy
|
|
100
|
+
)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`Config` is a frozen dataclass — see the API docs for the full list of parameters (mixing variation, degree correction, individual/group depth, etc.).
|
|
104
|
+
|
|
105
|
+
### Run inference
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
result = ds.fit(
|
|
109
|
+
config, G,
|
|
110
|
+
num_samples=1000, # posterior samples to collect
|
|
111
|
+
sweeps_per_sample=10, # MCMC sweeps between samples
|
|
112
|
+
timeout=60.0, # wall-clock limit in seconds
|
|
113
|
+
seed=42,
|
|
114
|
+
)
|
|
88
115
|
```
|
|
89
116
|
|
|
90
|
-
###
|
|
117
|
+
### Inspect results
|
|
118
|
+
|
|
91
119
|
```python
|
|
92
|
-
|
|
120
|
+
# Per-node hierarchy scores: posterior mean and uncertainty
|
|
121
|
+
scores = result.score_means # np.ndarray, shape [n_nodes]
|
|
122
|
+
uncertainty = result.score_stds # np.ndarray, shape [n_nodes]
|
|
123
|
+
|
|
124
|
+
# Network-level parameter summary: mean and std of each inferred parameter
|
|
125
|
+
net_df = result.network_properties()
|
|
126
|
+
|
|
127
|
+
# Integer community labels for the consensus sample
|
|
128
|
+
partition = result.consensus_partition()
|
|
129
|
+
|
|
130
|
+
# n×n co-assignment frequency matrix
|
|
131
|
+
comatrix = result.coincidence_matrix()
|
|
93
132
|
|
|
133
|
+
# High-level summary dict
|
|
134
|
+
print(result.summary)
|
|
135
|
+
# {'num_samples': 500, 'mean_num_groups': 3.2, 'mdl_value': 412.1}
|
|
136
|
+
|
|
137
|
+
# Full posterior samples DataFrame
|
|
138
|
+
samples_df = result.samples_df()
|
|
94
139
|
```
|
|
95
140
|
|
|
96
|
-
###
|
|
141
|
+
### Visualization
|
|
142
|
+
|
|
97
143
|
```python
|
|
98
|
-
|
|
144
|
+
import matplotlib.pyplot as plt
|
|
99
145
|
|
|
146
|
+
fig, ax = ds.plot_node_properties(result)
|
|
147
|
+
plt.savefig("node_scores.png")
|
|
148
|
+
|
|
149
|
+
fig, ax = ds.plot_MCMC_entropy(result) # convergence diagnostic
|
|
100
150
|
```
|
|
101
151
|
|
|
152
|
+
Export the graph with inferred attributes for use in Gephi or other tools:
|
|
102
153
|
|
|
154
|
+
```python
|
|
155
|
+
ds.write_gml_with_inferences(G, "output.gml", result)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Fixing model components
|
|
159
|
+
|
|
160
|
+
Pass fixed values in `Config` to test submodels. For example, to fix group structure from node attributes and infer hierarchy only:
|
|
103
161
|
|
|
104
|
-
### Full samples
|
|
105
|
-
To get a more complete picture of the inference, we can consider the full posterior distribution of Monte Carlo samples
|
|
106
162
|
```python
|
|
107
|
-
|
|
163
|
+
config = ds.Config(group_attribute="department", interaction="independent")
|
|
164
|
+
result = ds.fit(config, G)
|
|
108
165
|
```
|
|
109
166
|
|
|
110
|
-
|
|
167
|
+
Or fix the number of groups:
|
|
111
168
|
|
|
169
|
+
```python
|
|
170
|
+
config = ds.Config(num_groups=4)
|
|
171
|
+
result = ds.fit(config, G)
|
|
172
|
+
```
|
|
112
173
|
|
|
113
|
-
###
|
|
114
|
-
As this package focuses on the potential link between community and hierarchy, we can swap out the models considered of either community or hierarchy in isolation.
|
|
174
|
+
### Parallel tempering
|
|
115
175
|
|
|
116
|
-
|
|
176
|
+
For complex posteriors, enable parallel tempering:
|
|
117
177
|
|
|
178
|
+
```python
|
|
179
|
+
result = ds.fit(config, G, num_tempering_chains=4, beta=1.0)
|
|
180
|
+
```
|
|
118
181
|
|
|
119
|
-
Further usage examples can be found in the `examples
|
|
182
|
+
Further usage examples can be found in the `examples/python/` directory of the
|
|
120
183
|
repository and the [package documentation][rtd-link].
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# directedstructure
|
|
2
|
+
|
|
3
|
+
[![Documentation Status][rtd-badge]][rtd-link]
|
|
4
|
+
[![PyPI version][pypi-version]][pypi-link]
|
|
5
|
+
[![PyPI platforms][pypi-platforms]][pypi-link]
|
|
6
|
+
|
|
7
|
+
<!-- SPHINX-START -->
|
|
8
|
+
|
|
9
|
+
<!-- prettier-ignore-start -->
|
|
10
|
+
[actions-badge]: https://github.com/maxjerdee/directedstructure/workflows/CI/badge.svg
|
|
11
|
+
[actions-link]: https://github.com/maxjerdee/directedstructure/actions
|
|
12
|
+
[conda-badge]: https://img.shields.io/conda/vn/conda-forge/directedstructure
|
|
13
|
+
[conda-link]: https://github.com/conda-forge/directedstructure-feedstock
|
|
14
|
+
[github-discussions-badge]: https://img.shields.io/static/v1?label=Discussions&message=Ask&color=blue&logo=github
|
|
15
|
+
[github-discussions-link]: https://github.com/maxjerdee/directedstructure/discussions
|
|
16
|
+
[pypi-link]: https://pypi.org/project/directedstructure/
|
|
17
|
+
[pypi-platforms]: https://img.shields.io/pypi/pyversions/directedstructure
|
|
18
|
+
[pypi-version]: https://img.shields.io/pypi/v/directedstructure
|
|
19
|
+
[rtd-badge]: https://readthedocs.org/projects/directedstructure/badge/?version=latest
|
|
20
|
+
[rtd-link]: https://directedstructure.readthedocs.io/en/latest/?badge=latest
|
|
21
|
+
|
|
22
|
+
<!-- prettier-ignore-end -->
|
|
23
|
+
|
|
24
|
+
### Infer communities, hierarchies, and their connection in directed graphs
|
|
25
|
+
|
|
26
|
+
##### Maximilian Jerdee, Elizabeth Bruch, Mark Newman
|
|
27
|
+
|
|
28
|
+
This Python package uses Bayesian inference to identify communities and hierarchies of nodes in a directed network, as well as measure the interaction between those structures.
|
|
29
|
+
|
|
30
|
+
We model community structure using a stochastic block model, hierarchy structure with a Bradley-Terry model, and their interaction according to our work (link forthcoming).
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install directedstructure
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Or build locally by cloning this repository and running
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install .
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
in the base directory (requires a C++ compiler).
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
The public API has three objects: `Config`, `fit`, and `Result`.
|
|
49
|
+
|
|
50
|
+
### Load a network
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
import directedstructure as ds
|
|
54
|
+
import networkx as nx
|
|
55
|
+
|
|
56
|
+
G = nx.read_gml("examples/data/networks/friends.gml")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Edges may carry a `type` attribute (`'dominant'` or `'neutral'`) to distinguish directed pairwise comparisons from symmetric ties. If no type information is provided all edges are treated as dominant.
|
|
60
|
+
|
|
61
|
+
### Configure the model
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
config = ds.Config(
|
|
65
|
+
groups_model="general_canonical", # community structure preset
|
|
66
|
+
hierarchy_model="bradley_terry_ties", # auto-selected if omitted
|
|
67
|
+
interaction="coupled", # coupling between communities and hierarchy
|
|
68
|
+
)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`Config` is a frozen dataclass — see the API docs for the full list of parameters (mixing variation, degree correction, individual/group depth, etc.).
|
|
72
|
+
|
|
73
|
+
### Run inference
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
result = ds.fit(
|
|
77
|
+
config, G,
|
|
78
|
+
num_samples=1000, # posterior samples to collect
|
|
79
|
+
sweeps_per_sample=10, # MCMC sweeps between samples
|
|
80
|
+
timeout=60.0, # wall-clock limit in seconds
|
|
81
|
+
seed=42,
|
|
82
|
+
)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Inspect results
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
# Per-node hierarchy scores: posterior mean and uncertainty
|
|
89
|
+
scores = result.score_means # np.ndarray, shape [n_nodes]
|
|
90
|
+
uncertainty = result.score_stds # np.ndarray, shape [n_nodes]
|
|
91
|
+
|
|
92
|
+
# Network-level parameter summary: mean and std of each inferred parameter
|
|
93
|
+
net_df = result.network_properties()
|
|
94
|
+
|
|
95
|
+
# Integer community labels for the consensus sample
|
|
96
|
+
partition = result.consensus_partition()
|
|
97
|
+
|
|
98
|
+
# n×n co-assignment frequency matrix
|
|
99
|
+
comatrix = result.coincidence_matrix()
|
|
100
|
+
|
|
101
|
+
# High-level summary dict
|
|
102
|
+
print(result.summary)
|
|
103
|
+
# {'num_samples': 500, 'mean_num_groups': 3.2, 'mdl_value': 412.1}
|
|
104
|
+
|
|
105
|
+
# Full posterior samples DataFrame
|
|
106
|
+
samples_df = result.samples_df()
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Visualization
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
import matplotlib.pyplot as plt
|
|
113
|
+
|
|
114
|
+
fig, ax = ds.plot_node_properties(result)
|
|
115
|
+
plt.savefig("node_scores.png")
|
|
116
|
+
|
|
117
|
+
fig, ax = ds.plot_MCMC_entropy(result) # convergence diagnostic
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Export the graph with inferred attributes for use in Gephi or other tools:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
ds.write_gml_with_inferences(G, "output.gml", result)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Fixing model components
|
|
127
|
+
|
|
128
|
+
Pass fixed values in `Config` to test submodels. For example, to fix group structure from node attributes and infer hierarchy only:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
config = ds.Config(group_attribute="department", interaction="independent")
|
|
132
|
+
result = ds.fit(config, G)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Or fix the number of groups:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
config = ds.Config(num_groups=4)
|
|
139
|
+
result = ds.fit(config, G)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Parallel tempering
|
|
143
|
+
|
|
144
|
+
For complex posteriors, enable parallel tempering:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
result = ds.fit(config, G, num_tempering_chains=4, beta=1.0)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Further usage examples can be found in the `examples/python/` directory of the
|
|
151
|
+
repository and the [package documentation][rtd-link].
|
|
@@ -20,4 +20,50 @@ Q: Does the fact that the meaning of the scores, stored in the [0,1] interval, c
|
|
|
20
20
|
- Should implement the rest of the API, host the function on pypi, and get the documentation live.
|
|
21
21
|
- Add some basic functionality tests (checks against exact Mathematica results)
|
|
22
22
|
- Include versions of the model where the community and hierarchy can each (or both) be fixed, in case we are interested in how it might change due to some externally observed variable.
|
|
23
|
-
- Add the consensus clustering code (in c++ since it can be pretty intensive)
|
|
23
|
+
- Add the consensus clustering code (in c++ since it can be pretty intensive)
|
|
24
|
+
|
|
25
|
+
2/8/26:
|
|
26
|
+
- DONE: Hosted on pypi with documentation
|
|
27
|
+
- Add ability for the downstream functions like node_properties to take samples_df as an argument
|
|
28
|
+
|
|
29
|
+
2/11/26:
|
|
30
|
+
- Add a wrapper as an R package
|
|
31
|
+
|
|
32
|
+
2/16/26:
|
|
33
|
+
- Make sure that we raise an error if there are no edges, also convert multigraphs
|
|
34
|
+
- DONE: Handle the case where nothing is recorded as dominant, default to this.
|
|
35
|
+
|
|
36
|
+
2/18/26:
|
|
37
|
+
- DONE: _get_node_data() function, and take the group and hierarchy information, raise errors or return None
|
|
38
|
+
- Make function to plot the status decomposition of the graph
|
|
39
|
+
- Test against Mathematica for a multigraph
|
|
40
|
+
- Change type -> interaction_type
|
|
41
|
+
|
|
42
|
+
3/15/26:
|
|
43
|
+
- STAN implementation of the fixed groups model gives different and more sensible results
|
|
44
|
+
- Re-implement this STAN model using the form we have derived integrated over the community scores.
|
|
45
|
+
- Debug the directedstructure implementation until the outcomes match
|
|
46
|
+
- Test out a way to just use the g++ compiler directly to configure the sampling code
|
|
47
|
+
- Send the new version of the code to Elizabeth along with requests of what should be installed
|
|
48
|
+
- Note that make test on the current multigraph example results in a seg fault that we need to track down (should run this with valgrind and sort out the error)
|
|
49
|
+
|
|
50
|
+
Node 17 with weight 6.000000
|
|
51
|
+
Node 0 with weight 0.000000
|
|
52
|
+
make: *** [Makefile:63: test] Segmentation fault (core dumped)
|
|
53
|
+
|
|
54
|
+
3/10/26:
|
|
55
|
+
- Found the gap between the STAN and c++ implementations for the ties
|
|
56
|
+
- Want to implement the ability to pass fixed groups or scores
|
|
57
|
+
- Set up the code such that it is possible to take an input and
|
|
58
|
+
|
|
59
|
+
3/11/26:
|
|
60
|
+
- Need to bring the stripped and original implementations into alignment.
|
|
61
|
+
- For some reason I'm now seeing that the c++ testing script is not exploring possible community structures at all while the python wrapped version seemingly is.
|
|
62
|
+
|
|
63
|
+
3/12/26:
|
|
64
|
+
- Throw error when the input graph has no edges
|
|
65
|
+
- Consider renaming interaction -> type
|
|
66
|
+
|
|
67
|
+
3/20/26:
|
|
68
|
+
- Run the package on the synthetic network examples
|
|
69
|
+
- Debug why snr150_sigC5_rep09.gml, even in independent mode, is generating way too many communities.
|