Skip to content

laplacian_z_noise

AddLaplacianZNoise

Bases: BaseTransform

Applies Laplacian-eigenvector-modulated multiplicative noise to the z-coordinate of each node.

This transform samples a linear combination of Laplacian eigenvectors, rescales it to lie in the range [-max_noise, max_noise], and multiplies each node's z-coordinate with this noise coefficient.

If the Laplacian positional encoding is missing or has fewer dimensions than requested, it will be recomputed.

Parameters:

Name Type Description Default
max_noise float

The maximum relative deviation applied to the z-coordinate. A value of 0.15 means ±15% scaling.

required
n_eigenvectors int

Number of Laplacian eigenvectors to compute. Defaults to 20.

20
Example

transform = AddLaplacianZNoise(max_noise=0.1) data = transform(data)

Source code in src/torch_structure/transforms/laplacian_z_noise.py
 6
 7
 8
 9
10
11
12
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
class AddLaplacianZNoise(BaseTransform):
    r"""
    Applies Laplacian-eigenvector-modulated multiplicative noise to the
    z-coordinate of each node.

    This transform samples a linear combination of Laplacian eigenvectors,
    rescales it to lie in the range ``[-max_noise, max_noise]``, and
    multiplies each node's z-coordinate with this noise coefficient.

    If the Laplacian positional encoding is missing or has fewer dimensions
    than requested, it will be recomputed.

    Args:
        max_noise (float): The maximum relative deviation applied to the
            z-coordinate. A value of 0.15 means ±15% scaling.
        n_eigenvectors (int, optional): Number of Laplacian eigenvectors to
            compute. Defaults to 20.

    Example:
        >>> transform = AddLaplacianZNoise(max_noise=0.1)
        >>> data = transform(data)
    """

    def __init__(self, max_noise, n_eigenvectors=20, keep_lpe=False):
        super().__init__()
        self.max_noise = max_noise
        self.n_eigenvectors = n_eigenvectors
        self.keep_lpe = keep_lpe
        self._add_lpe = torch_geometric.transforms.AddLaplacianEigenvectorPE(
            self.n_eigenvectors, attr_name="laplacian_pe", is_undirected=False
        )

    def forward(self, data):
        """Apply the Laplacian-eigenvector-modulated z-noise to ``data.coords`` in place."""
        if not hasattr(data, "coords"):
            raise AttributeError("Data object has no attribute 'coords'.")

        device = data.coords.device

        # Ensure data has the specified number of eigenvectors
        compute_lpe = (
            not hasattr(data, "laplacian_pe")
            or data.laplacian_pe.size(1) < self.n_eigenvectors
        )
        if compute_lpe:
            data = self._add_lpe(data).to(device=device)
        coefficients = torch.randn(self.n_eigenvectors, device=device)
        laplacian_sum = torch.matmul(
            data.laplacian_pe[:, : self.n_eigenvectors], coefficients
        )
        noise = laplacian_sum * (self.max_noise / laplacian_sum.abs().max())
        data.coords[:, 2] = (1 + noise) * data.coords[:, 2]

        if not self.keep_lpe and compute_lpe:
            del data.laplacian_pe

        return data

    def __repr__(self):
        return (
            f"{self.__class__.__name__}(max_noise={self.max_noise}, "
            f"n_eigenvectors={self.n_eigenvectors})"
        )

forward(data)

Apply the Laplacian-eigenvector-modulated z-noise to data.coords in place.

Source code in src/torch_structure/transforms/laplacian_z_noise.py
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
def forward(self, data):
    """Apply the Laplacian-eigenvector-modulated z-noise to ``data.coords`` in place."""
    if not hasattr(data, "coords"):
        raise AttributeError("Data object has no attribute 'coords'.")

    device = data.coords.device

    # Ensure data has the specified number of eigenvectors
    compute_lpe = (
        not hasattr(data, "laplacian_pe")
        or data.laplacian_pe.size(1) < self.n_eigenvectors
    )
    if compute_lpe:
        data = self._add_lpe(data).to(device=device)
    coefficients = torch.randn(self.n_eigenvectors, device=device)
    laplacian_sum = torch.matmul(
        data.laplacian_pe[:, : self.n_eigenvectors], coefficients
    )
    noise = laplacian_sum * (self.max_noise / laplacian_sum.abs().max())
    data.coords[:, 2] = (1 + noise) * data.coords[:, 2]

    if not self.keep_lpe and compute_lpe:
        del data.laplacian_pe

    return data