Skip to content

index_map

IndexMap class for mapping node indices to IDs.

IndexMap

Maps node indices to IDs.

This class keeps a mapping from any node ID, e.g. names (strings) or higher-order IDs (tuples), to an index of the corresponding node in the initial list of IDs, enabling fast lookup of node IDs from a Data object.

Attributes:

Name Type Description
node_ids numpy.ndarray | None

numpy.ndarray storing the node IDs, enabling fast lookup of multiple node IDs from indices.

id_to_idx dict

dict mapping each node ID to its index.

id_shape tuple

tuple storing the shape of the ID. The default shape is (-1,) for first-order IDs. For higher-order IDs, the shape will be (-1, k) with order k.

Examples:

Initialize an IndexMap object with a list of string IDs:

>>> index_map = IndexMap(["A", "B", "C"])
>>> print(index_map)
A -> 0
B -> 1
C -> 2

Add additional IDs to the mapping:

>>> index_map.add_id("D")
>>> print(index_map.to_idx("D"))
3

Map indices to IDs. Use to_id for single indices and to_ids for multiple indices. Note that the shape of the given index list will be preserved in the output:

>>> print(index_map.to_id(1))
B
>>> print(index_map.to_ids([0, 2]))
['A' 'C']

Map IDs to indices. Works analogously to the reversed mapping and can, e.g., be used to create an edge_index tensor from a list of edges given by source and destination node IDs:

>>> edge_index = index_map.to_idxs([["A", "B"], ["B", "C"], ["C", "D"]]).T

Create a higher-order ID mapping:

>>> index_map = IndexMap([("A", "B"), ("A", "C"), ("B", "C")])
>>> print(index_map)
('A', 'B') -> 0
('A', 'C') -> 1
('B', 'C') -> 2

The methods above work analogously for higher-order IDs:

>>> print(index_map.to_id(1))
('A', 'C')
>>> print(index_map.to_ids([[0], [2]]))
[[['A' 'B']]
 [['B' 'C']]]
Source code in src/pathpyG/core/index_map.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
class IndexMap:
    """Maps node indices to IDs.

    This class keeps a mapping from any node ID, e.g. names (strings) or higher-order IDs (tuples),
    to an index of the corresponding node in the initial list of IDs, enabling fast lookup of node IDs
    from a [`Data`][torch_geometric.data.Data] object.

    Attributes:
        node_ids: [`numpy.ndarray`][numpy.ndarray] storing the node IDs, enabling fast lookup of multiple node IDs from indices.
        id_to_idx: [`dict`][dict] mapping each node ID to its index.
        id_shape: [`tuple`][tuple] storing the shape of the ID. The default shape is (-1,) for first-order IDs.
            For higher-order IDs, the shape will be `(-1, k)` with order `k`.

    Examples:
        Initialize an `IndexMap` object with a list of string IDs:

        >>> index_map = IndexMap(["A", "B", "C"])
        >>> print(index_map)
        A -> 0
        B -> 1
        C -> 2

        Add additional IDs to the mapping:

        >>> index_map.add_id("D")
        >>> print(index_map.to_idx("D"))
        3

        Map indices to IDs. Use `to_id` for single indices and `to_ids` for multiple indices.
        Note that the shape of the given index list will be preserved in the output:

        >>> print(index_map.to_id(1))
        B
        >>> print(index_map.to_ids([0, 2]))
        ['A' 'C']

        Map IDs to indices. Works analogously to the reversed mapping and can, e.g., be used to
        create an `edge_index` tensor from a list of edges given by source and destination node IDs:

        >>> edge_index = index_map.to_idxs([["A", "B"], ["B", "C"], ["C", "D"]]).T

        Create a higher-order ID mapping:

        >>> index_map = IndexMap([("A", "B"), ("A", "C"), ("B", "C")])
        >>> print(index_map)
        ('A', 'B') -> 0
        ('A', 'C') -> 1
        ('B', 'C') -> 2

        The methods above work analogously for higher-order IDs:

        >>> print(index_map.to_id(1))
        ('A', 'C')
        >>> print(index_map.to_ids([[0], [2]]))
        [[['A' 'B']]
         [['B' 'C']]]
    """

    def __init__(self, node_ids: Union[np.ndarray, List[str], List[tuple], None] = None) -> None:
        """Initialize mapping from indices to node IDs.

        The mapping will keep the ordering of the IDs as provided by `node_ids`. If the IDs are not unique,
        an error will be raised.

        Args:
            node_ids: List or array of node IDs to initialize mapping.

        Raises:
            ValueError: If IDs are not unique.

        Examples:
            Initialize an `IndexMap` object with a list of string IDs:

            >>> index_map = IndexMap(["A", "C", "B"])
            >>> print(index_map)
            A -> 0
            C -> 1
            B -> 2

            Handle non-unique IDs and sort IDs lexicographically:

            >>> node_ids = ["A", "C", "B", "A"]
            >>> index_map = IndexMap(np.unique(node_ids))
            >>> print(index_map)
            A -> 0
            B -> 1
            C -> 2
        """
        self.node_ids: np.ndarray | None = None
        self.id_to_idx: dict = {}
        self.id_shape: tuple = (-1,)  # If the index map is higher order, this will be the shape of the ID
        if node_ids is not None:
            self.add_ids(node_ids)

    @property
    def has_ids(self) -> bool:
        """Return whether mapping has IDs.

        Returns:
            Whether mapping has IDs.

        Examples:
            Check if mapping has IDs:

            >>> index_map = IndexMap()
            >>> print(index_map.has_ids)
            False

            >>> index_map = IndexMap(["A", "B", "C"])
            >>> print(index_map.has_ids)
            True
        """
        return self.node_ids is not None

    @property
    def has_tuple_ids(self) -> bool:
        """Return whether IDs are tuples of first-order IDs, as in higher-order graphs.

        Returns:
            Whether IDs are tuples.

        Examples:
            Check if mapping has tuple IDs:

            >>> index_map = IndexMap(["A", "B", "C"])
            >>> print(index_map.has_tuple_ids)
            False

            >>> index_map = IndexMap([("A", "B"), ("B", "C")])
            >>> print(index_map.has_tuple_ids)
            True
        """
        return len(self.id_shape) > 1

    def num_ids(self) -> int:
        """Return number of IDs. If mapping is not defined, return 0.

        Returns:
            Number of IDs.

        Examples:
            Get number of IDs:

            >>> index_map = IndexMap()
            >>> print(index_map.num_ids())
            0

            >>> index_map = IndexMap(["A", "B", "C"])
            >>> print(index_map.num_ids())
            3

            >>> index_map = IndexMap([("A", "B"), ("A", "C"), ("B", "C")])
            >>> print(index_map.num_ids())
            3
        """
        if self.node_ids is None:
            return 0
        else:
            return len(self.node_ids)

    def add_id(self, node_id: str | tuple | list) -> None:
        """Assigns additional ID to the next consecutive index.

        Args:
            node_id: ID to assign.

        Raises:
            ValueError: If ID is already present in the mapping.

        Examples:
            Add an additional ID to the mapping:

            >>> index_map = IndexMap(["A", "B", "C"])
            >>> index_map.add_id("D")
            >>> print(index_map)
            A -> 0
            B -> 1
            C -> 2
            D -> 3
        """
        if node_id not in self.id_to_idx:
            idx = self.num_ids()
            if isinstance(node_id, (list, tuple)):
                node_id_arr = to_numpy(node_id)
                self.id_shape = (-1, *node_id_arr.shape)
                node_id_arr = node_id_arr.reshape(1, *node_id_arr.shape)
            else:
                node_id_arr = to_numpy([node_id])
            self.node_ids = np.concatenate((self.node_ids, node_id_arr)) if self.node_ids is not None else node_id_arr
            self.id_to_idx[node_id] = idx
        else:
            raise ValueError("ID already present in the mapping.")

    def add_ids(self, node_ids: list | np.ndarray) -> None:
        """Assigns additional IDs to next consecutive indices. The order of IDs is preserved.

        Args:
            node_ids: IDs to assign

        Raises:
            ValueError: If IDs are not unique or already present in the mapping.

        Examples:
            Add additional IDs to the mapping:

            >>> index_map = IndexMap(["A", "B", "C"])
            >>> index_map.add_ids(["E", "D"])
            >>> print(index_map)
            A -> 0
            B -> 1
            C -> 2
            E -> 3
            D -> 4
        """
        cur_num_ids = self.num_ids()
        if isinstance(node_ids, list) and isinstance(node_ids[0], (list, tuple)):
            self.id_shape = (-1, *to_numpy(node_ids[0]).shape)

        if not isinstance(node_ids, np.ndarray):
            node_ids = to_numpy(node_ids)

        all_ids = np.concatenate((self.node_ids, node_ids)) if self.node_ids is not None else node_ids
        unique_ids = np.unique(all_ids, axis=0 if self.id_shape != (-1,) else None)

        if len(unique_ids) != len(all_ids):
            raise ValueError("IDs are not unique or already present in the mapping.")

        self.node_ids = all_ids
        self.id_to_idx.update(
            {tuple(v.tolist()) if self.id_shape != (-1,) else v: i + cur_num_ids for i, v in enumerate(node_ids)}
        )

    def to_id(self, idx: int) -> Union[int, str, tuple]:
        """Map index to ID if mapping is defined, return index otherwise.

        Args:
            idx: Index to map.

        Returns:
            ID if mapping is defined, index otherwise.

        Examples:
            Map index to ID:

            >>> index_map = IndexMap(["A", "B", "C"])
            >>> print(index_map.to_id(1))
            B

            No mapping defined:

            >>> index_map = IndexMap()
            >>> print(index_map.to_id(1))
            1
        """
        if self.has_ids:
            if self.id_shape == (-1,):
                if isinstance(self.node_ids, np.ndarray) and self.node_ids.dtype.type is np.str_:
                    return str(self.node_ids[idx])
                else:
                    return self.node_ids[idx]  # type: ignore
            else:
                return tuple(self.node_ids[idx].tolist())  # type: ignore
        else:
            return idx

    def to_ids(self, idxs: list | tuple | np.ndarray) -> np.ndarray:
        """Map list of indices to IDs if mapping is defined, return indices otherwise.

        The shape of the given index list will be preserved in the output.

        Args:
            idxs: Indices to map.

        Returns:
            IDs if mapping is defined, indices otherwise.

        Examples:
            Map list of indices to IDs:

            >>> index_map = IndexMap(["A", "B", "C"])
            >>> print(index_map.to_ids([0, 2]))
            ['A' 'C']

            No mapping defined:

            >>> index_map = IndexMap()
            >>> print(index_map.to_ids(torch.tensor([0, 2])))
            tensor([0, 2])

            Map edge_index tensor to array of edges:

            >>> edge_index = torch.tensor([[0, 2, 2, 3], [1, 1, 3, 0]])
            >>> index_map = IndexMap(["A", "B", "C", "D"])
            >>> print(index_map.to_ids(edge_index.T))
            [['A' 'B']
             ['C' 'B']
             ['C' 'D']
             ['D' 'A']]
        """
        if self.node_ids is not None:
            if not isinstance(idxs, np.ndarray):
                idxs = to_numpy(idxs)
            return self.node_ids[idxs]
        else:
            return idxs  # type: ignore[return-value]

    def to_idx(self, node: str | int | tuple[str] | tuple[int]) -> int | tuple[int]:
        """Map argument (ID or index) to index if mapping is defined, return argument otherwise.

        Args:
            node: ID or index to map.

        Returns:
            Index if mapping is defined, argument otherwise.

        Examples:
            Map ID to index:

            >>> index_map = IndexMap(["A", "B", "C"])
            >>> print(index_map.to_idx("B"))
            1

            No mapping defined:

            >>> index_map = IndexMap()
            >>> print(index_map.to_idx(1))
            1
        """
        n: str | int | tuple[str] | tuple[int] = node
        if self.has_ids:
            if self.id_shape != (-1,):
                n = tuple(n)  # type: ignore[arg-type,assignment]
            return self.id_to_idx[n]
        else:
            return n  # type: ignore[return-value]

    def to_idxs(self, nodes: list | tuple | np.ndarray, device: Optional[torch.device] = None) -> torch.Tensor:
        """Map list of arguments (IDs or indices) to indices if mapping is defined, return argument otherwise.

        The shape of the given argument list will be preserved in the output.

        Args:
            nodes: IDs or indices to map.
            device: Device on which to create the output tensor.

        Returns:
            Indices if mapping is defined, arguments otherwise.

        Examples:
            Map list of IDs to indices:

            >>> index_map = IndexMap(["A", "B", "C"])
            >>> print(index_map.to_idxs(["B", "A"]))
            tensor([1, 0])

            No mapping defined:

            >>> index_map = IndexMap()
            >>> print(index_map.to_idxs(torch.tensor([1, 0])))
            tensor([1, 0])

            Map list of edges to edge_index tensor:

            >>> edges = [["A", "B"], ["B", "C"], ["C", "D"]]
            >>> index_map = IndexMap(np.unique(edges))
            >>> print(index_map.to_idxs(edges).T)
            tensor([[0, 1, 2],
                    [1, 2, 3]])
        """
        if self.has_ids:
            if not isinstance(nodes, np.ndarray):
                nodes = to_numpy(nodes)

            shape = nodes.shape
            if self.id_shape == (-1,):
                return torch.tensor([self.id_to_idx[node] for node in nodes.flatten()], device=device).reshape(shape)
            else:
                # Leading dimensions index the IDs, trailing ones span a single tuple ID. The number
                # of IDs is computed explicitly since `reshape(-1, 0)` is ambiguous for empty tuples.
                lead_shape = shape[: len(shape) - len(self.id_shape) + 1]
                ids = nodes.reshape(int(np.prod(lead_shape)), *self.id_shape[1:])
                return torch.tensor(
                    [self.id_to_idx[tuple(node.tolist())] for node in ids], dtype=torch.long, device=device
                ).reshape(lead_shape)
        else:
            return torch.tensor(nodes, device=device)

    def __str__(self) -> str:
        """Return string representation of the mapping.

        Returns:
            String representation of the mapping.

        Examples:
            Print string representation of the mapping:

            >>> index_map = IndexMap(["A", "B", "C"])
            >>> print(index_map)
            A -> 0
            B -> 1
            C -> 2
        """
        s = ""
        for v in self.id_to_idx:
            s += str(v) + " -> " + str(self.to_idx(v)) + "\n"
        return s

has_ids property

Return whether mapping has IDs.

Returns:

Type Description
bool

Whether mapping has IDs.

Examples:

Check if mapping has IDs:

>>> index_map = IndexMap()
>>> print(index_map.has_ids)
False
>>> index_map = IndexMap(["A", "B", "C"])
>>> print(index_map.has_ids)
True

has_tuple_ids property

Return whether IDs are tuples of first-order IDs, as in higher-order graphs.

Returns:

Type Description
bool

Whether IDs are tuples.

Examples:

Check if mapping has tuple IDs:

>>> index_map = IndexMap(["A", "B", "C"])
>>> print(index_map.has_tuple_ids)
False
>>> index_map = IndexMap([("A", "B"), ("B", "C")])
>>> print(index_map.has_tuple_ids)
True

__init__

Initialize mapping from indices to node IDs.

The mapping will keep the ordering of the IDs as provided by node_ids. If the IDs are not unique, an error will be raised.

Parameters:

Name Type Description Default
node_ids typing.Union[numpy.ndarray, typing.List[str], typing.List[tuple], None]

List or array of node IDs to initialize mapping.

None

Raises:

Type Description
ValueError

If IDs are not unique.

Examples:

Initialize an IndexMap object with a list of string IDs:

>>> index_map = IndexMap(["A", "C", "B"])
>>> print(index_map)
A -> 0
C -> 1
B -> 2

Handle non-unique IDs and sort IDs lexicographically:

>>> node_ids = ["A", "C", "B", "A"]
>>> index_map = IndexMap(np.unique(node_ids))
>>> print(index_map)
A -> 0
B -> 1
C -> 2
Source code in src/pathpyG/core/index_map.py
def __init__(self, node_ids: Union[np.ndarray, List[str], List[tuple], None] = None) -> None:
    """Initialize mapping from indices to node IDs.

    The mapping will keep the ordering of the IDs as provided by `node_ids`. If the IDs are not unique,
    an error will be raised.

    Args:
        node_ids: List or array of node IDs to initialize mapping.

    Raises:
        ValueError: If IDs are not unique.

    Examples:
        Initialize an `IndexMap` object with a list of string IDs:

        >>> index_map = IndexMap(["A", "C", "B"])
        >>> print(index_map)
        A -> 0
        C -> 1
        B -> 2

        Handle non-unique IDs and sort IDs lexicographically:

        >>> node_ids = ["A", "C", "B", "A"]
        >>> index_map = IndexMap(np.unique(node_ids))
        >>> print(index_map)
        A -> 0
        B -> 1
        C -> 2
    """
    self.node_ids: np.ndarray | None = None
    self.id_to_idx: dict = {}
    self.id_shape: tuple = (-1,)  # If the index map is higher order, this will be the shape of the ID
    if node_ids is not None:
        self.add_ids(node_ids)

__str__

Return string representation of the mapping.

Returns:

Type Description
str

String representation of the mapping.

Examples:

Print string representation of the mapping:

>>> index_map = IndexMap(["A", "B", "C"])
>>> print(index_map)
A -> 0
B -> 1
C -> 2
Source code in src/pathpyG/core/index_map.py
def __str__(self) -> str:
    """Return string representation of the mapping.

    Returns:
        String representation of the mapping.

    Examples:
        Print string representation of the mapping:

        >>> index_map = IndexMap(["A", "B", "C"])
        >>> print(index_map)
        A -> 0
        B -> 1
        C -> 2
    """
    s = ""
    for v in self.id_to_idx:
        s += str(v) + " -> " + str(self.to_idx(v)) + "\n"
    return s

add_id

Assigns additional ID to the next consecutive index.

Parameters:

Name Type Description Default
node_id str | tuple | list

ID to assign.

required

Raises:

Type Description
ValueError

If ID is already present in the mapping.

Examples:

Add an additional ID to the mapping:

>>> index_map = IndexMap(["A", "B", "C"])
>>> index_map.add_id("D")
>>> print(index_map)
A -> 0
B -> 1
C -> 2
D -> 3
Source code in src/pathpyG/core/index_map.py
def add_id(self, node_id: str | tuple | list) -> None:
    """Assigns additional ID to the next consecutive index.

    Args:
        node_id: ID to assign.

    Raises:
        ValueError: If ID is already present in the mapping.

    Examples:
        Add an additional ID to the mapping:

        >>> index_map = IndexMap(["A", "B", "C"])
        >>> index_map.add_id("D")
        >>> print(index_map)
        A -> 0
        B -> 1
        C -> 2
        D -> 3
    """
    if node_id not in self.id_to_idx:
        idx = self.num_ids()
        if isinstance(node_id, (list, tuple)):
            node_id_arr = to_numpy(node_id)
            self.id_shape = (-1, *node_id_arr.shape)
            node_id_arr = node_id_arr.reshape(1, *node_id_arr.shape)
        else:
            node_id_arr = to_numpy([node_id])
        self.node_ids = np.concatenate((self.node_ids, node_id_arr)) if self.node_ids is not None else node_id_arr
        self.id_to_idx[node_id] = idx
    else:
        raise ValueError("ID already present in the mapping.")

add_ids

Assigns additional IDs to next consecutive indices. The order of IDs is preserved.

Parameters:

Name Type Description Default
node_ids list | numpy.ndarray

IDs to assign

required

Raises:

Type Description
ValueError

If IDs are not unique or already present in the mapping.

Examples:

Add additional IDs to the mapping:

>>> index_map = IndexMap(["A", "B", "C"])
>>> index_map.add_ids(["E", "D"])
>>> print(index_map)
A -> 0
B -> 1
C -> 2
E -> 3
D -> 4
Source code in src/pathpyG/core/index_map.py
def add_ids(self, node_ids: list | np.ndarray) -> None:
    """Assigns additional IDs to next consecutive indices. The order of IDs is preserved.

    Args:
        node_ids: IDs to assign

    Raises:
        ValueError: If IDs are not unique or already present in the mapping.

    Examples:
        Add additional IDs to the mapping:

        >>> index_map = IndexMap(["A", "B", "C"])
        >>> index_map.add_ids(["E", "D"])
        >>> print(index_map)
        A -> 0
        B -> 1
        C -> 2
        E -> 3
        D -> 4
    """
    cur_num_ids = self.num_ids()
    if isinstance(node_ids, list) and isinstance(node_ids[0], (list, tuple)):
        self.id_shape = (-1, *to_numpy(node_ids[0]).shape)

    if not isinstance(node_ids, np.ndarray):
        node_ids = to_numpy(node_ids)

    all_ids = np.concatenate((self.node_ids, node_ids)) if self.node_ids is not None else node_ids
    unique_ids = np.unique(all_ids, axis=0 if self.id_shape != (-1,) else None)

    if len(unique_ids) != len(all_ids):
        raise ValueError("IDs are not unique or already present in the mapping.")

    self.node_ids = all_ids
    self.id_to_idx.update(
        {tuple(v.tolist()) if self.id_shape != (-1,) else v: i + cur_num_ids for i, v in enumerate(node_ids)}
    )

num_ids

Return number of IDs. If mapping is not defined, return 0.

Returns:

Type Description
int

Number of IDs.

Examples:

Get number of IDs:

>>> index_map = IndexMap()
>>> print(index_map.num_ids())
0
>>> index_map = IndexMap(["A", "B", "C"])
>>> print(index_map.num_ids())
3
>>> index_map = IndexMap([("A", "B"), ("A", "C"), ("B", "C")])
>>> print(index_map.num_ids())
3
Source code in src/pathpyG/core/index_map.py
def num_ids(self) -> int:
    """Return number of IDs. If mapping is not defined, return 0.

    Returns:
        Number of IDs.

    Examples:
        Get number of IDs:

        >>> index_map = IndexMap()
        >>> print(index_map.num_ids())
        0

        >>> index_map = IndexMap(["A", "B", "C"])
        >>> print(index_map.num_ids())
        3

        >>> index_map = IndexMap([("A", "B"), ("A", "C"), ("B", "C")])
        >>> print(index_map.num_ids())
        3
    """
    if self.node_ids is None:
        return 0
    else:
        return len(self.node_ids)

to_id

Map index to ID if mapping is defined, return index otherwise.

Parameters:

Name Type Description Default
idx int

Index to map.

required

Returns:

Type Description
typing.Union[int, str, tuple]

ID if mapping is defined, index otherwise.

Examples:

Map index to ID:

>>> index_map = IndexMap(["A", "B", "C"])
>>> print(index_map.to_id(1))
B

No mapping defined:

>>> index_map = IndexMap()
>>> print(index_map.to_id(1))
1
Source code in src/pathpyG/core/index_map.py
def to_id(self, idx: int) -> Union[int, str, tuple]:
    """Map index to ID if mapping is defined, return index otherwise.

    Args:
        idx: Index to map.

    Returns:
        ID if mapping is defined, index otherwise.

    Examples:
        Map index to ID:

        >>> index_map = IndexMap(["A", "B", "C"])
        >>> print(index_map.to_id(1))
        B

        No mapping defined:

        >>> index_map = IndexMap()
        >>> print(index_map.to_id(1))
        1
    """
    if self.has_ids:
        if self.id_shape == (-1,):
            if isinstance(self.node_ids, np.ndarray) and self.node_ids.dtype.type is np.str_:
                return str(self.node_ids[idx])
            else:
                return self.node_ids[idx]  # type: ignore
        else:
            return tuple(self.node_ids[idx].tolist())  # type: ignore
    else:
        return idx

to_ids

Map list of indices to IDs if mapping is defined, return indices otherwise.

The shape of the given index list will be preserved in the output.

Parameters:

Name Type Description Default
idxs list | tuple | numpy.ndarray

Indices to map.

required

Returns:

Type Description
numpy.ndarray

IDs if mapping is defined, indices otherwise.

Examples:

Map list of indices to IDs:

>>> index_map = IndexMap(["A", "B", "C"])
>>> print(index_map.to_ids([0, 2]))
['A' 'C']

No mapping defined:

>>> index_map = IndexMap()
>>> print(index_map.to_ids(torch.tensor([0, 2])))
tensor([0, 2])

Map edge_index tensor to array of edges:

>>> edge_index = torch.tensor([[0, 2, 2, 3], [1, 1, 3, 0]])
>>> index_map = IndexMap(["A", "B", "C", "D"])
>>> print(index_map.to_ids(edge_index.T))
[['A' 'B']
 ['C' 'B']
 ['C' 'D']
 ['D' 'A']]
Source code in src/pathpyG/core/index_map.py
def to_ids(self, idxs: list | tuple | np.ndarray) -> np.ndarray:
    """Map list of indices to IDs if mapping is defined, return indices otherwise.

    The shape of the given index list will be preserved in the output.

    Args:
        idxs: Indices to map.

    Returns:
        IDs if mapping is defined, indices otherwise.

    Examples:
        Map list of indices to IDs:

        >>> index_map = IndexMap(["A", "B", "C"])
        >>> print(index_map.to_ids([0, 2]))
        ['A' 'C']

        No mapping defined:

        >>> index_map = IndexMap()
        >>> print(index_map.to_ids(torch.tensor([0, 2])))
        tensor([0, 2])

        Map edge_index tensor to array of edges:

        >>> edge_index = torch.tensor([[0, 2, 2, 3], [1, 1, 3, 0]])
        >>> index_map = IndexMap(["A", "B", "C", "D"])
        >>> print(index_map.to_ids(edge_index.T))
        [['A' 'B']
         ['C' 'B']
         ['C' 'D']
         ['D' 'A']]
    """
    if self.node_ids is not None:
        if not isinstance(idxs, np.ndarray):
            idxs = to_numpy(idxs)
        return self.node_ids[idxs]
    else:
        return idxs  # type: ignore[return-value]

to_idx

Map argument (ID or index) to index if mapping is defined, return argument otherwise.

Parameters:

Name Type Description Default
node str | int | tuple[str] | tuple[int]

ID or index to map.

required

Returns:

Type Description
int | tuple[int]

Index if mapping is defined, argument otherwise.

Examples:

Map ID to index:

>>> index_map = IndexMap(["A", "B", "C"])
>>> print(index_map.to_idx("B"))
1

No mapping defined:

>>> index_map = IndexMap()
>>> print(index_map.to_idx(1))
1
Source code in src/pathpyG/core/index_map.py
def to_idx(self, node: str | int | tuple[str] | tuple[int]) -> int | tuple[int]:
    """Map argument (ID or index) to index if mapping is defined, return argument otherwise.

    Args:
        node: ID or index to map.

    Returns:
        Index if mapping is defined, argument otherwise.

    Examples:
        Map ID to index:

        >>> index_map = IndexMap(["A", "B", "C"])
        >>> print(index_map.to_idx("B"))
        1

        No mapping defined:

        >>> index_map = IndexMap()
        >>> print(index_map.to_idx(1))
        1
    """
    n: str | int | tuple[str] | tuple[int] = node
    if self.has_ids:
        if self.id_shape != (-1,):
            n = tuple(n)  # type: ignore[arg-type,assignment]
        return self.id_to_idx[n]
    else:
        return n  # type: ignore[return-value]

to_idxs

Map list of arguments (IDs or indices) to indices if mapping is defined, return argument otherwise.

The shape of the given argument list will be preserved in the output.

Parameters:

Name Type Description Default
nodes list | tuple | numpy.ndarray

IDs or indices to map.

required
device typing.Optional[torch.device]

Device on which to create the output tensor.

None

Returns:

Type Description
torch.Tensor

Indices if mapping is defined, arguments otherwise.

Examples:

Map list of IDs to indices:

>>> index_map = IndexMap(["A", "B", "C"])
>>> print(index_map.to_idxs(["B", "A"]))
tensor([1, 0])

No mapping defined:

>>> index_map = IndexMap()
>>> print(index_map.to_idxs(torch.tensor([1, 0])))
tensor([1, 0])

Map list of edges to edge_index tensor:

>>> edges = [["A", "B"], ["B", "C"], ["C", "D"]]
>>> index_map = IndexMap(np.unique(edges))
>>> print(index_map.to_idxs(edges).T)
tensor([[0, 1, 2],
        [1, 2, 3]])
Source code in src/pathpyG/core/index_map.py
def to_idxs(self, nodes: list | tuple | np.ndarray, device: Optional[torch.device] = None) -> torch.Tensor:
    """Map list of arguments (IDs or indices) to indices if mapping is defined, return argument otherwise.

    The shape of the given argument list will be preserved in the output.

    Args:
        nodes: IDs or indices to map.
        device: Device on which to create the output tensor.

    Returns:
        Indices if mapping is defined, arguments otherwise.

    Examples:
        Map list of IDs to indices:

        >>> index_map = IndexMap(["A", "B", "C"])
        >>> print(index_map.to_idxs(["B", "A"]))
        tensor([1, 0])

        No mapping defined:

        >>> index_map = IndexMap()
        >>> print(index_map.to_idxs(torch.tensor([1, 0])))
        tensor([1, 0])

        Map list of edges to edge_index tensor:

        >>> edges = [["A", "B"], ["B", "C"], ["C", "D"]]
        >>> index_map = IndexMap(np.unique(edges))
        >>> print(index_map.to_idxs(edges).T)
        tensor([[0, 1, 2],
                [1, 2, 3]])
    """
    if self.has_ids:
        if not isinstance(nodes, np.ndarray):
            nodes = to_numpy(nodes)

        shape = nodes.shape
        if self.id_shape == (-1,):
            return torch.tensor([self.id_to_idx[node] for node in nodes.flatten()], device=device).reshape(shape)
        else:
            # Leading dimensions index the IDs, trailing ones span a single tuple ID. The number
            # of IDs is computed explicitly since `reshape(-1, 0)` is ambiguous for empty tuples.
            lead_shape = shape[: len(shape) - len(self.id_shape) + 1]
            ids = nodes.reshape(int(np.prod(lead_shape)), *self.id_shape[1:])
            return torch.tensor(
                [self.id_to_idx[tuple(node.tolist())] for node in ids], dtype=torch.long, device=device
            ).reshape(lead_shape)
    else:
        return torch.tensor(nodes, device=device)