A semi-supervised pipeline for mapping source files to architectural modules from a file-level dependency graph. Two Graph Neural Network encoders — GCN and GAT — are trained with an iterative self-training loop, alongside an MLP baseline (features only, no graph) and an NBA (Naive Bayes) baseline for comparison against a non-neural method.
Software architecture documentation tends to go stale as a system evolves, and manually recovering the file-to-module mapping does not scale to large codebases. We address this by inferring the mapping directly from the dependency structure and the code's own textual content, with an approach that can extend a partial, incomplete mapping to unlabeled files without requiring full ground truth for the entire system.
Each system is modeled as a heterogeneous, multi-relational file-dependency graph:
- Nodes are source files.
- Edges are typed static dependencies (e.g., calls, imports, inheritance) between files.
- Node features combine Word2Vec embeddings of the code's identifiers/text with a folder-location signal derived from each file's path.
GCN/GAT encoders run an iterative self-training loop: warm up on a labeled seed set, then repeatedly promote high-confidence pseudo-labels and retrain. The MLP baseline uses the same features without any graph structure. NBA runs an independent Naive-Bayes-based iterative mapping for comparison.
c2a_mapping/
├── baselines/ # NBA (Naive Bayes) baseline
├── data/ # Sample datasets
├── data_pipeline/ # Feature extraction, graph construction, label encoding
├── evaluation/ # Metric helpers, misclassification analysis
├── experiments/ # config.json + GNN experiment runner
├── models/ # GCN, GAT, MLP definitions
├── training/ # Self-training loop
├── run_mapping.py # Get automatic source file mapping
└── requirements.txt
- RQ1 — Effectiveness & Scaling (primary): How effectively does GNN-based mapping perform relative to non-graph baselines, and how does it scale?
- RQ2 — Dependency-graph contribution (secondary, still verifiable): What does the dependency graph contribute beyond textual features, and where does it help or hurt on a per-file basis?
- RQ3 — Design sensitivity: How sensitive is the approach to modeling dependency types separately, aggregation depth, and edge direction?
cd c2a_mapping/
python experiments/run_experiments.py # GCN, GAT, MLP — all datasets, default config
python baselines/run_nba.py # NBA baseline, same datasetsCompare results/{gcn,gat}_hetero_results.csv and results/mlp_results.csv against results/nba_results.csv on f1_macro/f1_micro/precision_macro/recall_macro.
cd c2a_mapping/
python evaluation/save_misclassified.py bash # per-file misclassification rate, from an existing predictions.csvReads results/{stem}_predictions.csv (written by run_experiments.py) and writes results/{stem}_misclassified.csv: how often each file was misclassified across runs, letting you see where the model — and by extension the dependency graph — struggles on a given system.
python experiments/run_experiments.py --ablation homo_directed
python experiments/run_experiments.py --ablation hetero_undirected
python experiments/run_experiments.py --ablation hetero_reversed
python experiments/run_experiments.py --ablation hetero_directed_1layerSee the Ablations table below for what each one varies.
cd c2a_mapping/
python run_mapping.py --dataset bash --model gatEvery file with a known Module is used as training data (no held-out split — there's nothing to hold out for); every other file gets a predicted_module. No metrics are computed, since there's no assumption that a full ground truth exists. Output: results/{stem}_mapping.csv (File, predicted_module only). The NBA baseline has the same option: python baselines/run_nba.py --mapping-only.
cd c2a_mapping/
pip install -r requirements.txtTested against torch==2.4.0 / torch-geometric==2.7.0; both support CUDA or CPU.
cd c2a_mapping/
python experiments/run_experiments.py --dataset bash --runs 5experiments/run_experiments.py:
--ablation <name>— one of the ablations below (default:hetero_directed).--dataset <stem>— a single dataset instead of every configured one.--runs <n>— override the run count for that dataset.
baselines/run_nba.py:
--datasets <stem,stem,...>orall.--runs <n>— runs per dataset (default:nba.num_runsinconfig.json).--mapping-only— skip evaluation, predict every unlabeled file (see above).
run_mapping.py:
--dataset <stem>(required).--model gcn|gat|mlp(default:gat).--data-dir/--cache-dir— override the defaultdata//cache/w2vlocations.
Results are written per model/ablation to results/{gcn,gat}_{hetero,homo,undirected,reversed}_results.csv and results/mlp_results.csv.
| Name | Mode | Directed |
|---|---|---|
hetero_directed (default) |
one conv per relation type | yes |
homo_directed |
merged adjacency | yes |
hetero_undirected |
one conv per relation type | no |
hetero_reversed |
one conv per relation type, edges flipped | yes |
hetero_directed_1layer |
one conv per relation type, 1 message-passing layer | yes |
From experiments/config.json:
- GCN / GAT: hidden size 256, 2 message-passing layers, dropout 0.01 (GAT: 4 attention heads).
- MLP: hidden size 256, dropout 0.0.
- Training: learning rate 0.001, self-training promotion threshold 0.95, 100 warmup epochs, 4 self-training rounds of 30 epochs each.
- Runs: 200 per dataset/model by default.
Each dataset consists of two CSVs:
c2a_mapping/data/
├── {stem}.csv # columns: File, Entity, Module, Member_Name
└── {stem}_deps.csv # columns: Source_File, Target_File, Dependency_Type, Dependency_Count
Module may be missing for some files — those become unlabeled nodes that run_mapping.py / run_nba.py --mapping-only will predict rather than requiring for evaluation.
Output files, all under results/:
{model}_{ablation}_results.csv— metrics per run (f1_macro,f1_micro,precision_macro,recall_macro, plusmapped_-prefixed variants for the confidently-mapped subset).{stem}_predictions.csv— per-file, per-runtrue_module/pred_module/correct, the input to RQ2's misclassification analysis.{stem}_mapping.csv/{stem}_nba_mapping.csv—File/predicted_moduleonly, no ground truth required.nba_results.csv— NBA's own results, with the same mapped-subset naming as above andfull_-prefixed forced-prediction variants.
Most systems used here are available in the GAER repository. argouml, commons, and sweetHome are included directly in c2a_mapping/data/ because they aren't present there.