Input: Dataset D with n = 533 subjects, 10-fold stratified split
Output: Mean ± Std of metrics across 10 folds
Partition 533 subjects into 10 stratified folds
for each fold k = 1 to 10 do
D_train ← 9 folds (~480 subjects), D_test ← 1 fold (~53 subjects)
D_train_s, D_val ← stratified_split(D_train, ratio=0.9/0.1)
// ── Stage 1: Preprocessing
C_age = k-means(D_train_s.age, k=2)
C_edu= k-means(D_train_s.edu, k=2)
// Build adjacency matrix: TRAIN nodes only
A_train←{Samesex(D_train_s.sex),SameCluster(D_train_s.edu,C_edu), SameCluster(D_train_s.age, C_age)} //S-G-S,S-E-S,S-A-S
// ── Stage 2: End-to-End Training (HDGNN-Mamba2)
for epoch = 1 to 100 do
for each subject s in D_train_s do
z_s ← Mamba2_GNN({X_s})
end for
in_emb_train ← {z_s | s ∈ D_train_s}
//Dynamic edge update (every Δ epochs, train embeddings only)
A′_train ← DEU( A_train, sim( in_emb_train ), Δ=10 ) // Dynamic edge update, train only
Z_train = HGNN( A′_train, {in_emb_train} ) // Population graph, train only
Z_cache ← Detach(Z_train) // Training inner subset, detached
L_cl ← SupervisedcontrastiveLoss(Z_cache, D_train_s.y) // Standard supervised contrastive loss
L_ce ← CrossEntropy(Z_train, D_train_s.y)
L ← L_ce + λ·L_cl
L.backward(); optimizer.step()
val_loss, val_acc ← Evaluate(D_val)
if val_loss not improved for 20 epochs then
break
end if
RestoreBestModelState()
end for
//── Stage 3: Test Evaluation
// Build test adjacency: test nodes connect to train nodes via phenotype
A_test ←∅
for each test subject t in D_test do
z_t ← Mamba2_GNN(X_t) // Test subject feature
// Connect test node to top-K train nodes by phenotype similarity
sim_train ← Similarity(t, D_train_s, [sex, age, edu])
top_k ← TopK(sim_train, k=5)
A_test ← A_test ∪ {(t, neighbor) | neighbor ∈ top_k}
end for
// Concatenate train and test, forward through HGNN (no grad)
Z_all ← HGNN_Inference(A_train ⋃A_test, concat(in_emb_train, {z_t}))
ŷ_test ← argmax(Z_all[n_train_s:]) // Test predictions
Compute metrics: ACC, SEN, SPE, AUROC, AUPRC
end for
return Mean ± Std of metrics across 10 folds