Skip to content

declearn.optimizer.modules.NoiseModule

Bases: OptiModule

Abstract noise-addition module for DP purposes.

This module uses either fast numpy pseudo-random number generation, or slower cryptographically secure pseudo-random numbers (CSPRN).

Source code in declearn/optimizer/modules/_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
 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
class NoiseModule(OptiModule, metaclass=ABCMeta, register=False):
    """Abstract noise-addition module for DP purposes.

    This module uses either fast numpy pseudo-random number generation,
    or slower cryptographically secure pseudo-random numbers (CSPRN).
    """

    name: ClassVar[str] = "abstract-noise"

    def __init__(
        self,
        safe_mode: bool = True,
        seed: Optional[int] = None,
    ) -> None:
        """Instantiate the noise module.

        Parameters
        ----------
        safe_mode: bool, default=True
            Whether to use cryptographically-secure pseudo-random numbers
            (CSPRN) rather than the default numpy generator.
            For experimental purposes, set flag to False, as generating CSPRN
            is significantly slower.
        seed: int or None, default=None
            Seed used for initiliazing the non-secure random number generator.
            If `safe_mode=True`, seed is ignored.
        """
        rng = SystemRandom if safe_mode else np.random.default_rng
        self._rng = rng(seed)
        self.seed = seed

    @property
    def safe_mode(self) -> bool:
        """Whether this module uses CSPRN rather than base numpy RNG."""
        return isinstance(self._rng, SystemRandom)

    def get_config(
        self,
    ) -> Dict[str, Any]:
        return {"safe_mode": self.safe_mode, "seed": self.seed}

    def run(
        self,
        gradients: Vector,
    ) -> Vector:
        if not NumpyVector in gradients.compatible_vector_types:
            raise TypeError(  # pragma: no cover
                f"{self.__class__.__name__} requires input gradients to "
                "be compatible with NumpyVector, which is not the case "
                f"of {type(gradients).__name__}."
            )
        # Gather gradients' specs.
        shapes = gradients.shapes()
        dtypes = gradients.dtypes()
        # Conduct noise sampling for each and every gradient coordinate.
        noise = {
            key: self._sample_noise(shapes[key], dtypes[key])
            for key in gradients.coefs
        }
        # Add the sampled noise to the gradients and return them.
        # Silence warnings about sparse gradients getting densified.
        with warnings.catch_warnings():
            warnings.filterwarnings("ignore", ".*densifying.*", RuntimeWarning)
            return gradients + NumpyVector(noise)

    @abstractmethod
    def _sample_noise(
        self,
        shape: Tuple[int, ...],
        dtype: str,
    ) -> np.ndarray:
        """Sample a noise tensor from a module-specific distribution."""

safe_mode: bool property

Whether this module uses CSPRN rather than base numpy RNG.

__init__(safe_mode=True, seed=None)

Instantiate the noise module.

Parameters:

Name Type Description Default
safe_mode bool

Whether to use cryptographically-secure pseudo-random numbers (CSPRN) rather than the default numpy generator. For experimental purposes, set flag to False, as generating CSPRN is significantly slower.

True
seed Optional[int]

Seed used for initiliazing the non-secure random number generator. If safe_mode=True, seed is ignored.

None
Source code in declearn/optimizer/modules/_noise.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
def __init__(
    self,
    safe_mode: bool = True,
    seed: Optional[int] = None,
) -> None:
    """Instantiate the noise module.

    Parameters
    ----------
    safe_mode: bool, default=True
        Whether to use cryptographically-secure pseudo-random numbers
        (CSPRN) rather than the default numpy generator.
        For experimental purposes, set flag to False, as generating CSPRN
        is significantly slower.
    seed: int or None, default=None
        Seed used for initiliazing the non-secure random number generator.
        If `safe_mode=True`, seed is ignored.
    """
    rng = SystemRandom if safe_mode else np.random.default_rng
    self._rng = rng(seed)
    self.seed = seed