an evolving research environment for the systematic study of graph reduction methods.
this project is a panoramic, research-oriented pipeline for evaluating graph reduction algorithms. it's designed to support thesis-level experimentation by allowing the user to compare different algorithms that reduce graph complexity and the ways they influence graph properties in large-scale networks.
the core goal of this framework is to become a tool for the systematic study of reduction techniques and potentially evolve into a research environment as an automated laboratory for graph theory.
note: the project is an unfolding consequence of my bachelor's thesis.pdf. it is still very much a work in progress (hence the demo) but is constantly evolving to hopefully become part of my postgraduate research at jagiellonian university.
- multi-source graph ingestion: loading graphs from various formats (edgelists, memory objects) via a unified gateway. additionally, the framework uses lazy loading to boost performance while handling large datasets. supported file extensions:
.edgelist,.txt,.edges,.csv,.graphml,.gexf,.gml,.adjlist - polymorphic transformations: support for different types of graph reduction
- sparsification - selection of the most significant nodes/edges and discarding others. implemented algorithms:
random,k_neighbor,local_degree,merw,pagerank - coarsening - aggregation/merging of nodes to construct a smaller graph. implemented algorithms:
mock_coarsening,merw_coarsening - condensation (work in progress) - learning a synthetic graph from scratch
- sparsification - selection of the most significant nodes/edges and discarding others. implemented algorithms:
- automated metric registry: calculating structural properties in the original and modified graph on the fly. available metrics:
clustering,community_preservation,connectivity,degree_distribution,diameter,edge_density,avg_path_length,avg_stretch,apsp,spectral_similarity,effective_resistance - experiment management: orchestrating full experiments from start to finish, where a graph is imported, transformed, analyzed, and the results are persisted as an audit trail
- efficiency benchmarking: automatic tracking of wall-clock time for both transformation and metric phases to evaluate theoretical vs. empirical complexity
- visualization: basic metric value plots and graph figures
the framework is built using domain-driven design principles and organized into distinct layers to ensure that experimental logic remains decoupled from infrastructure concerns. the plugin-forward architecture is highly adaptable and makes the program open for future extension.
src/domain/layer contains the core truth of the system, such as theGraphmodel,Metricdefinitions, and the abstractGraphTransformlogic; it is purely focused on graph theory and the mathematics behind the graph algorithmssrc/application/layer orchestrates the workflow via theExperimentService, handling the "business logic" of running a research job without needing to know how graphs are stored and where they come fromsrc/infrastructure/layer takes care of details like reading graphs (GraphGateway), persisting results (Repository), and managing database transactions (UnitOfWork)src/interfaces/layer provides entry points for the user including a CLI for automated batches and an API for potential integration with web dashboards
the program follows a defined lifecycle for every experiment.
- import: the
GraphGatewayreads raw data and creates a lazy object - orchestration: the
ExperimentServicereceives a command via a CLI/API and fetches the original graph from theRepository - transformation: the system looks up the requested algorithm in a
Registryand executes it, producing a newGraphobject while keeping the original graph intact - analysis: a series of
Metricsis run against the graph and return detailed dictionaries of values and metadata from the calculation - commitment: the
UnitOfWorkensures that both the new graph and the experiment results are saved to storage simultaneously, preventing dirty data resulting from errors
- clone or download the repository to your local machine
- open the terminal and navigate to the project's root directory
- run the following command:
pip install -e .this makes the dwindle command available system-wide. a virtual environment is recommended.
before running an experiment, list the algorithms and metrics the framework is aware of:
dwindle list-algorithms
dwindle list-metricsto perform an experiment on one graph file, run:
dwindle run --graph <path> --algorithm <name> [options]required:
--graph— path to your graph file; the format is inferred from the file extension--algorithm— name of the reduction algorithm to apply (fromlist-algorithms)
optional:
--metrics— comma-separated list of metrics to compute, e.g.diameter, clustering, community_preservation--params— algorithm parameters, either as space-separatedkey=valuepairs or a single json object--output— write results to a file instead of printing; supports.jsonand.csv--directed— treat the graph as directed (default: undirected)--weighted— treat the third column in the edgelist as edge weights (default: unweighted)--plugin— path to a python file to import before registry discovery; can be repeated to load multiple plugins (see extending via plugins)
to run one algorithm across an entire directory of graphs and collect results into a single csv:
dwindle batch --dir <directory> --algorithm <name> --output <file.csv> [options]required:
--dir— path to a directory containing graph files--algorithm— reduction algorithm to apply to every graph
optional:
--metrics— comma-separated list of metrics to compute for each graph--params— algorithm parameters applied uniformly to every graph--output— output csv path (default:batch_results.csv)--pattern— filename glob to filter which files inside the directory are processed (default: all recognised extensions)--recursive— recurse into subdirectories--directed/--weighted— applied uniformly to all graphs
run random sparsification and print results to the terminal:
dwindle run --graph my_graph.edgelist --algorithm random --weighted --params p=0.4 seed=420 --metrics diameter,clusteringpass parameters as a json object and save results to a flat csv for further analysis:
dwindle run --graph my_graph.edgelist --algorithm k_neighbor --weighted --params '{"rho": 0.5}' --metrics edge_density,spectral_similarity --output results.csvrun one algorithm across a whole dataset and collect everything into a single csv:
dwindle batch --dir ./dataset/ --algorithm k_neighbor --params rho=0.5 --metrics edge_density,spectral_similarity --recursive --output study.csvload a custom algorithm from outside the project before running:
dwindle --plugin ~/research/my_sparsifier.py run --graph my_graph.edgelist --algorithm my-algo --metrics clusteringthe framework is algorithm-agnostic. the --plugin flag lets you load any .py file before the internal registries are populated, so you can add algorithms, metrics, or transforms without touching the project source.
a plugin file is a plain python module that uses the same registration decorators as the built-in implementations. you can register new sparsifiers, coarsening transforms, or metrics:
# ~/research/my_sparsifier.py
import networkx as nx
from src.domain.sparsifiers.registry import register_sparsifier
from src.domain.sparsifiers.base import Sparsifier
@register_sparsifier("my-algo")
class MySparsifier(Sparsifier):
def run(self, graph, params) -> graph:
...# ~/research/my_coarsening.py
from src.domain.transforms.registry import register_transform
from src.domain.transforms.base import GraphTransform
@register_transform("my-coarsening")
class MyCoarsening(GraphTransform):
def run(self, graph, params) -> graph:
...# ~/research/my_metric.py
from src.domain.metrics.registry import register_metric
from src.domain.metrics.base import Metric
@register_metric("my-metric")
class MyMetric(Metric):
def compute(self, graph) -> dict:
...pass the file as a global flag (before the subcommand) so it applies to every command, including list-algorithms and list-metrics:
# use the algorithm immediately
dwindle --plugin ~/research/my_sparsifier.py run --graph g.edgelist --algorithm my-algo
# confirm it's visible in the registry
dwindle --plugin ~/research/my_sparsifier.py list-algorithms
# load several plugins at once
dwindle --plugin ~/algo.py --plugin ~/metric.py run --graph g.edgelist --algorithm my-algo --metrics my-metricthe plugin's parent directory is automatically added to sys.path, so any local imports inside the plugin resolve relative to its own location regardless of where graph-reduce is invoked from.