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.
Files changed (127) hide show
  1. directedstructure-0.2.3/.github/workflows/release.yml +79 -0
  2. {directedstructure-0.1.1 → directedstructure-0.2.3}/.gitignore +2 -0
  3. directedstructure-0.2.3/CMakeLists.txt +66 -0
  4. directedstructure-0.2.3/Makefile +102 -0
  5. {directedstructure-0.1.1 → directedstructure-0.2.3}/PKG-INFO +87 -24
  6. directedstructure-0.2.3/README.md +151 -0
  7. {directedstructure-0.1.1 → directedstructure-0.2.3}/TODO.md +47 -1
  8. directedstructure-0.2.3/bindings/python/bindings.cpp +356 -0
  9. directedstructure-0.2.3/examples/data/generate_synthetic_networks.py +139 -0
  10. directedstructure-0.2.3/examples/data/networks/championship_2021.gml +6196 -0
  11. directedstructure-0.2.3/examples/data/networks/english-football-2025.gml +59986 -0
  12. directedstructure-0.2.3/examples/data/networks/example_3_nodes.gml +103 -0
  13. directedstructure-0.2.3/examples/data/networks/snr025_sigC2_rep07.gml +2951 -0
  14. directedstructure-0.2.3/examples/data/networks/snr150_sigC5_rep09.gml +3003 -0
  15. directedstructure-0.2.3/examples/directed_undirected_communities.py +76 -0
  16. directedstructure-0.2.3/examples/highschool_30_group_assignments_comparison.png +0 -0
  17. directedstructure-0.2.3/examples/highschool_30_neutral_inference.gml +7461 -0
  18. directedstructure-0.2.3/examples/highschool_30_original_inference.gml +13306 -0
  19. directedstructure-0.2.3/examples/highschool_51_group_assignments_comparison.png +0 -0
  20. directedstructure-0.2.3/examples/highschool_51_neutral_inference.gml +4660 -0
  21. directedstructure-0.2.3/examples/highschool_51_original_inference.gml +7490 -0
  22. directedstructure-0.2.3/examples/network_properties.csv +11 -0
  23. directedstructure-0.2.3/examples/node_properties.csv +493 -0
  24. directedstructure-0.2.3/examples/python/directed_undirected_communities.py +76 -0
  25. directedstructure-0.2.3/examples/python/prediction.py +1 -0
  26. directedstructure-0.2.3/examples/python/testing.py +110 -0
  27. directedstructure-0.1.1/examples/testing.py → directedstructure-0.2.3/examples/python/testing_fixed.py +10 -2
  28. directedstructure-0.2.3/examples/sampling.py +1 -0
  29. directedstructure-0.2.3/examples/speed_test.py +1 -0
  30. directedstructure-0.2.3/examples/testing.png +0 -0
  31. directedstructure-0.2.3/examples/testing.py +242 -0
  32. directedstructure-0.2.3/examples/testing_coincidence_comparison.png +0 -0
  33. directedstructure-0.2.3/examples/testing_fixed.py +104 -0
  34. directedstructure-0.2.3/examples/testing_graph.png +0 -0
  35. directedstructure-0.2.3/examples/testing_inferences.gml +3621 -0
  36. directedstructure-0.2.3/examples/testing_node_properties.png +0 -0
  37. directedstructure-0.2.3/examples/testing_node_properties_from_samples.png +0 -0
  38. directedstructure-0.2.3/examples/testing_num_groups.png +0 -0
  39. directedstructure-0.2.3/examples/testing_samples.csv +1001 -0
  40. directedstructure-0.2.3/node_properties.csv +457 -0
  41. {directedstructure-0.1.1 → directedstructure-0.2.3}/pyproject.toml +21 -13
  42. {directedstructure-0.1.1 → directedstructure-0.2.3/python/directedstructure}/src/directedstructure/__init__.py +8 -8
  43. directedstructure-0.2.3/python/directedstructure/src/directedstructure/_version.py +24 -0
  44. directedstructure-0.2.3/python/directedstructure/src/directedstructure/core.py +491 -0
  45. {directedstructure-0.1.1 → directedstructure-0.2.3/python/directedstructure}/src/directedstructure/input_output.py +344 -77
  46. {directedstructure-0.1.1 → directedstructure-0.2.3}/src/MCMC_bookkeeping.cpp +30 -59
  47. directedstructure-0.2.3/src/MCMC_core.cpp +547 -0
  48. {directedstructure-0.1.1 → directedstructure-0.2.3}/src/MCMC_core.h +16 -50
  49. {directedstructure-0.1.1 → directedstructure-0.2.3}/src/MCMC_proposals.cpp +181 -161
  50. directedstructure-0.2.3/src/fit.cpp +186 -0
  51. directedstructure-0.2.3/src/fit.h +57 -0
  52. directedstructure-0.2.3/src/fit_result.cpp +113 -0
  53. directedstructure-0.2.3/src/fit_result.h +48 -0
  54. directedstructure-0.2.3/src/globals.cpp +16 -0
  55. {directedstructure-0.1.1 → directedstructure-0.2.3}/src/globals.h +12 -15
  56. {directedstructure-0.1.1 → directedstructure-0.2.3}/src/helpers.cpp +6 -4
  57. directedstructure-0.2.3/tests/README.md +125 -0
  58. directedstructure-0.2.3/tests/data/inputs/wins_ties_3node_point_eval.csv +12 -0
  59. directedstructure-0.2.3/tests/data/outputs/triangle_coupled_coincidence.csv +3 -0
  60. directedstructure-0.2.3/tests/data/outputs/triangle_independent_coincidence.csv +3 -0
  61. directedstructure-0.2.3/tests/data/outputs/wins_ties_3node_coincidence.csv +3 -0
  62. directedstructure-0.2.3/tests/data/outputs/wins_ties_3node_point_eval_components.csv +2 -0
  63. directedstructure-0.2.3/tests/mcmc_core_test.cpp +470 -0
  64. directedstructure-0.2.3/tests/python/test_package.py +637 -0
  65. directedstructure-0.1.1/.github/workflows/cd.yml +0 -95
  66. directedstructure-0.1.1/.github/workflows/ci.yml +0 -77
  67. directedstructure-0.1.1/.github/workflows/release.yml +0 -114
  68. directedstructure-0.1.1/CMakeLists.txt +0 -25
  69. directedstructure-0.1.1/Makefile +0 -62
  70. directedstructure-0.1.1/README.md +0 -88
  71. directedstructure-0.1.1/examples/data/samples/friends.csv +0 -4491
  72. directedstructure-0.1.1/examples/data/samples/highschool_51.csv +0 -1075
  73. directedstructure-0.1.1/examples/testing.png +0 -0
  74. directedstructure-0.1.1/examples/visualization.py +0 -12
  75. directedstructure-0.1.1/src/MCMC_core.cpp +0 -728
  76. directedstructure-0.1.1/src/MCMC_merge_split.cpp +0 -738
  77. directedstructure-0.1.1/src/consensus.cpp +0 -132
  78. directedstructure-0.1.1/src/consensus.h +0 -27
  79. directedstructure-0.1.1/src/directedstructure/_version.py +0 -34
  80. directedstructure-0.1.1/src/directedstructure/_version.pyi +0 -4
  81. directedstructure-0.1.1/src/directedstructure/bayes_factor.py +0 -101
  82. directedstructure-0.1.1/src/directedstructure/clusterings.py +0 -130
  83. directedstructure-0.1.1/src/directedstructure/core.py +0 -571
  84. directedstructure-0.1.1/src/directedstructure/prediction.py +0 -126
  85. directedstructure-0.1.1/src/directedstructure/sample.py +0 -338
  86. directedstructure-0.1.1/src/directedstructure/sample_analysis.py +0 -126
  87. directedstructure-0.1.1/tests/mcmc_core_test.cpp +0 -515
  88. directedstructure-0.1.1/tests/test_package.py +0 -9
  89. {directedstructure-0.1.1 → directedstructure-0.2.3}/.copier-answers.yml +0 -0
  90. {directedstructure-0.1.1 → directedstructure-0.2.3}/.git_archival.txt +0 -0
  91. {directedstructure-0.1.1 → directedstructure-0.2.3}/.gitattributes +0 -0
  92. {directedstructure-0.1.1 → directedstructure-0.2.3}/.github/CONTRIBUTING.md +0 -0
  93. {directedstructure-0.1.1 → directedstructure-0.2.3}/.github/dependabot.yml +0 -0
  94. {directedstructure-0.1.1 → directedstructure-0.2.3}/.github/release.yml +0 -0
  95. {directedstructure-0.1.1 → directedstructure-0.2.3}/.pre-commit-config.yaml +0 -0
  96. {directedstructure-0.1.1 → directedstructure-0.2.3}/.readthedocs.yaml +0 -0
  97. {directedstructure-0.1.1 → directedstructure-0.2.3}/LICENSE +0 -0
  98. {directedstructure-0.1.1 → directedstructure-0.2.3}/docs/conf.py +0 -0
  99. {directedstructure-0.1.1 → directedstructure-0.2.3}/docs/examples/prediction.md +0 -0
  100. {directedstructure-0.1.1 → directedstructure-0.2.3}/docs/examples/speed_test.md +0 -0
  101. {directedstructure-0.1.1 → directedstructure-0.2.3}/docs/examples/visualization.md +0 -0
  102. {directedstructure-0.1.1 → directedstructure-0.2.3}/docs/index.md +0 -0
  103. {directedstructure-0.1.1 → directedstructure-0.2.3}/docs/modules.md +0 -0
  104. {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/business_depts.gml +0 -0
  105. {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/friends.gml +0 -0
  106. {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/friends_raw.gml +0 -0
  107. {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/highschool_30.gml +0 -0
  108. {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/highschool_30_raw.gml +0 -0
  109. {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/highschool_51.gml +0 -0
  110. {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/highschool_51_raw.gml +0 -0
  111. {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/highschool_84.gml +0 -0
  112. {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/networks/highschool_84_raw.gml +0 -0
  113. {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/process_friendship_nominations.py +0 -0
  114. {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/take_samples.py +0 -0
  115. {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/data/test_2.gml +0 -0
  116. {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/prediction.py +0 -0
  117. {directedstructure-0.1.1/examples → directedstructure-0.2.3/examples/python}/consensus.py +0 -0
  118. {directedstructure-0.1.1/examples → directedstructure-0.2.3/examples/python}/sampling.py +0 -0
  119. {directedstructure-0.1.1/examples → directedstructure-0.2.3/examples/python}/speed_test.py +0 -0
  120. {directedstructure-0.1.1 → directedstructure-0.2.3}/examples/testing copy.png +0 -0
  121. {directedstructure-0.1.1 → directedstructure-0.2.3}/noxfile.py +0 -0
  122. {directedstructure-0.1.1 → directedstructure-0.2.3/python/directedstructure}/src/directedstructure/MCMC_core.pyi +0 -0
  123. {directedstructure-0.1.1 → directedstructure-0.2.3/python/directedstructure}/src/directedstructure/_core.pyi +0 -0
  124. {directedstructure-0.1.1 → directedstructure-0.2.3/python/directedstructure}/src/directedstructure/_graph_cache.py +0 -0
  125. {directedstructure-0.1.1 → directedstructure-0.2.3/python/directedstructure}/src/directedstructure/_util.py +0 -0
  126. {directedstructure-0.1.1 → directedstructure-0.2.3/python/directedstructure}/src/directedstructure/py.typed +0 -0
  127. {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
@@ -169,6 +169,8 @@ cython_debug/
169
169
 
170
170
  # setuptools_scm
171
171
  src/*/_version.py
172
+ python/directedstructure/src/directedstructure/_version.py
173
+ python/directedstructure/src/directedstructure/_version.pyi
172
174
 
173
175
 
174
176
  # ruff
@@ -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.1.1
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>=2.2.0
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 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.
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 here [link paper].
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
- `directedstructure` can be built locally by cloning this repository and running
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
- ## Typical usage
75
- Once installed, the package can be used to identify the network structures.
78
+ ## Usage
76
79
 
77
- ### Load a network
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
- # Load a network using NetworkX (this can also be read from an edgelist or other format)
87
- G = nx.read_gml("examples/data/friends.gml")
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
- ### Infer node properties
117
+ ### Inspect results
118
+
91
119
  ```python
92
- node_properties_df = ds.node_properties(G) # pandas DataFrame of inferred community identity and hierarchical position of each node in the network
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
- ### Infer network properties
141
+ ### Visualization
142
+
97
143
  ```python
98
- network_properties_df = ds.network_properties(G) # pandas DataFrame of network properties (for example depth of hierarchy within and between communities) inferred by the model, as well as their uncertainities
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
- samples_df = ds.samples(G)
163
+ config = ds.Config(group_attribute="department", interaction="independent")
164
+ result = ds.fit(config, G)
108
165
  ```
109
166
 
110
- With these samples we can ask more detailed questions like what is the posterior distribution of possible numbers of groups?
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
- ### Customization
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
- Neutral interactions can also be considered within the model if further interaction type information is provided (either type = dominant or type = neutral). If no type information is provided all interactions will be assumed to be dominant.
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` directory of the
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.