Skip to content

Commit b73a25f

Browse files
kmontemayorclaude
andcommitted
perf(distributed): derive with_edge from edge_feature_info instead of hardcoding True
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent b6edda5 commit b73a25f

2 files changed

Lines changed: 115 additions & 2 deletions

File tree

gigl/distributed/base_dist_loader.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ def create_sampling_config(
416416
batch_size=batch_size,
417417
shuffle=shuffle,
418418
drop_last=drop_last,
419-
with_edge=True,
419+
with_edge=dataset_schema.edge_feature_info is not None,
420420
collect_features=True,
421421
with_neg=False,
422422
with_weight=with_weight,

tests/unit/distributed/distributed_neighborloader_test.py

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import unittest
2-
from collections.abc import Mapping
2+
from collections.abc import Mapping, MutableMapping
33

44
import torch
55
import torch.multiprocessing as mp
@@ -398,6 +398,31 @@ def _run_cora_supervised_node_classification(
398398
shutdown_rpc()
399399

400400

401+
def _run_featureless_edge_ids_absent(
402+
_,
403+
dataset: DistDataset,
404+
holder: MutableMapping,
405+
):
406+
# A featureless dataset should sample with with_edge=False, so GLT never
407+
# attaches sampled edge ids (``data.edge``) to the produced batches.
408+
create_test_process_group()
409+
loader = DistNeighborLoader(
410+
dataset=dataset,
411+
num_neighbors=[2, 2],
412+
pin_memory_device=torch.device("cpu"),
413+
)
414+
count = 0
415+
edge_ids_absent = True
416+
for datum in loader:
417+
assert isinstance(datum, Data)
418+
if getattr(datum, "edge", None) is not None:
419+
edge_ids_absent = False
420+
count += 1
421+
holder["edge_ids_absent"] = edge_ids_absent
422+
holder["count"] = count
423+
shutdown_rpc()
424+
425+
401426
class DistributedNeighborLoaderTest(TestCase):
402427
def setUp(self):
403428
super().setUp()
@@ -861,5 +886,93 @@ def test_distributed_neighbor_loader_invalid_inputs_colocated(
861886
DistNeighborLoader(**kwargs)
862887

863888

889+
class WithEdgeDerivationTest(TestCase):
890+
"""Regression coverage for deriving ``with_edge`` from edge-feature presence."""
891+
892+
def test_config_with_edge_false_when_no_edge_features(self) -> None:
893+
from gigl.distributed.base_dist_loader import BaseDistLoader
894+
from gigl.distributed.utils.neighborloader import DatasetSchema
895+
896+
schema = DatasetSchema(
897+
is_homogeneous_with_labeled_edge_type=False,
898+
edge_types=None,
899+
node_feature_info=None,
900+
edge_feature_info=None, # no edge features
901+
edge_dir="out",
902+
)
903+
config = BaseDistLoader.create_sampling_config(
904+
num_neighbors=[2, 2], dataset_schema=schema
905+
)
906+
self.assertFalse(config.with_edge)
907+
908+
def test_config_with_edge_true_when_edge_features_present(self) -> None:
909+
from gigl.distributed.base_dist_loader import BaseDistLoader
910+
from gigl.distributed.utils.neighborloader import DatasetSchema
911+
from gigl.types.graph import FeatureInfo
912+
913+
schema = DatasetSchema(
914+
is_homogeneous_with_labeled_edge_type=False,
915+
edge_types=None,
916+
node_feature_info=None,
917+
edge_feature_info=FeatureInfo(dim=4, dtype=torch.float32),
918+
edge_dir="out",
919+
)
920+
config = BaseDistLoader.create_sampling_config(
921+
num_neighbors=[2, 2], dataset_schema=schema
922+
)
923+
self.assertTrue(config.with_edge)
924+
925+
def test_config_with_edge_true_for_heterogeneous_edge_feature_dict(self) -> None:
926+
from gigl.distributed.base_dist_loader import BaseDistLoader
927+
from gigl.distributed.utils.neighborloader import DatasetSchema
928+
from gigl.types.graph import FeatureInfo
929+
930+
schema = DatasetSchema(
931+
is_homogeneous_with_labeled_edge_type=False,
932+
edge_types=[_USER_TO_STORY, _STORY_TO_USER],
933+
node_feature_info=None,
934+
edge_feature_info={
935+
_USER_TO_STORY: FeatureInfo(dim=4, dtype=torch.float32),
936+
},
937+
edge_dir="out",
938+
)
939+
config = BaseDistLoader.create_sampling_config(
940+
num_neighbors=[2, 2], dataset_schema=schema
941+
)
942+
self.assertTrue(config.with_edge)
943+
944+
def test_featureless_dataset_produces_batches_without_edge_ids(self) -> None:
945+
# Homogeneous dataset with node features but no edge features: the loader
946+
# must still yield batches, and none carry sampled edge ids.
947+
partition_output = PartitionOutput(
948+
node_partition_book=torch.zeros(5),
949+
edge_partition_book=torch.zeros(5),
950+
partitioned_edge_index=GraphPartitionData(
951+
edge_index=torch.tensor([[0, 1, 2, 3, 4], [1, 2, 3, 4, 0]]),
952+
edge_ids=None,
953+
),
954+
partitioned_node_features=FeaturePartitionData(
955+
feats=torch.zeros(5, 2), ids=torch.arange(5)
956+
),
957+
partitioned_edge_features=None,
958+
partitioned_positive_labels=None,
959+
partitioned_negative_labels=None,
960+
partitioned_node_labels=None,
961+
)
962+
dataset = DistDataset(rank=0, world_size=1, edge_dir="out")
963+
dataset.build(partition_output=partition_output)
964+
965+
manager = mp.Manager()
966+
holder: MutableMapping = manager.dict()
967+
proc = mp.spawn(
968+
fn=_run_featureless_edge_ids_absent,
969+
args=(dataset, holder),
970+
join=False,
971+
)
972+
proc.join(timeout=120)
973+
self.assertGreater(holder["count"], 0)
974+
self.assertTrue(holder["edge_ids_absent"])
975+
976+
864977
if __name__ == "__main__":
865978
absltest.main()

0 commit comments

Comments
 (0)