Coverage for adaro_rl / attacks / random.py: 100%
44 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-14 07:50 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-14 07:50 +0000
1from typing import Tuple
2import numpy as np
3import torch
5from .base_attack import BaseAttack
8class RandomUniformAttack(BaseAttack):
9 """
10 Random Uniform Attack
12 This attack applies random perturbations sampled uniformly from the range [-1, 1],
13 then scales them according to the specified norm and perturbation budget (`eps`).
15 Parameters
16 ----------
17 obs_space : int | Tuple
18 Shape of the observation space.
19 perturb_space : int | Tuple
20 Shape of the perturbation space.
21 eps : int | float | np.ndarray
22 Perturbation budget.
23 norm : Optional[int | float], optional
24 Norm to use for scaling the perturbation (e.g., 0, 2, np.inf, or None).
25 is_proportional_mask : int | torch.Tensor, optional
26 Mask that determines if perturbations should be scaled proportionally
27 to the observation (1) or absolutely (0). Default is None.
28 device : str, optional
29 Computation device. Default is 'cpu'.
30 """
32 def __init__(
33 self,
34 obs_space: int | Tuple,
35 perturb_space: int | Tuple,
36 eps: int | float | np.ndarray,
37 norm: int | float = None,
38 is_proportional_mask: int | torch.Tensor = None,
39 device="cpu",
40 seed=None,
41 ):
42 super().__init__(
43 obs_space, perturb_space, eps, norm, is_proportional_mask, device, seed
44 )
46 def generate_perturbation(self, observation_batch: torch.Tensor):
47 """
48 Generate random uniform perturbations for a batch of observations.
50 Parameters
51 ----------
52 observation_batch : torch.Tensor
53 Batch of input observations.
55 Returns
56 -------
57 torch.Tensor
58 Batch of perturbations with same shape as input observations.
59 """
61 original_type = observation_batch.dtype
62 int_conversion = np.issubdtype(original_type, np.integer)
64 batch_size = observation_batch.shape[0]
66 perturbation_map_batch = (
67 self.rng.random((batch_size, *self.perturb_shape)) * 2 - 1
68 )
70 perturbation_batch = self._scale_perturbation(perturbation_map_batch)
72 if int_conversion:
73 perturbation_batch = perturbation_batch.round()
75 return perturbation_batch
78class RandomSignAttack(BaseAttack):
79 """
80 Random Sign Attack
82 This attack samples uniform random values in [-1, 1], keeps only the sign,
83 and then scales the perturbation according to the specified norm and budget.
85 Parameters
86 ----------
87 obs_space : int | Tuple
88 Shape of the observation space.
89 perturb_space : int | Tuple
90 Shape of the perturbation space.
91 eps : int | float | np.ndarray
92 Perturbation budget.
93 norm : Optional[int | float], optional
94 Norm to use for scaling the perturbation (e.g., 0, 2, np.inf, or None).
95 is_proportional_mask : int | torch.Tensor, optional
96 Mask that determines if perturbations should be scaled proportionally
97 to the observation (1) or absolutely (0). Default is None.
98 device : str, optional
99 Computation device. Default is 'cpu'.
100 """
102 def __init__(
103 self,
104 obs_space: int | Tuple,
105 perturb_space: int | Tuple,
106 eps: int | float | np.ndarray,
107 norm: int | float = None,
108 is_proportional_mask: int | torch.Tensor = None,
109 device="cpu",
110 seed=None,
111 ):
112 super().__init__(
113 obs_space, perturb_space, eps, norm, is_proportional_mask, device, seed
114 )
116 def generate_perturbation(self, observation_batch: torch.Tensor):
117 """
118 Generate random sign-based perturbations for a batch of observations.
120 Parameters
121 ----------
122 observation_batch : torch.Tensor
123 Batch of input observations.
125 Returns
126 -------
127 torch.Tensor
128 Batch of perturbations with same shape as input observations.
129 """
130 original_type = observation_batch.dtype
131 int_conversion = np.issubdtype(original_type, np.integer)
133 batch_size = observation_batch.shape[0]
135 perturbation_map_batch = (
136 self.rng.random((batch_size, *self.perturb_shape)) * 2 - 1
137 )
139 perturbation_batch = self._scale_perturbation(
140 perturbation_map_batch, signed=True
141 )
143 if int_conversion:
144 perturbation_batch = perturbation_batch.round()
146 return perturbation_batch
149class RandomNormalAttack(BaseAttack):
150 """
151 Random Normal Attack
153 This attack samples perturbations from a truncated normal distribution
154 (centered at 0, clipped in [-3, 3]), then scales them according to the
155 specified norm and perturbation budget (`eps`).
157 Parameters
158 ----------
159 obs_space : int | Tuple
160 Shape of the observation space.
161 perturb_space : int | Tuple
162 Shape of the perturbation space.
163 eps : int | float | np.ndarray
164 Perturbation budget.
165 norm : Optional[int | float], optional
166 Norm to use for scaling the perturbation (e.g., 0, 2, np.inf, or None).
167 is_proportional_mask : int | torch.Tensor, optional
168 Mask that determines if perturbations should be scaled proportionally
169 to the observation (1) or absolutely (0). Default is None.
170 device : str, optional
171 Computation device. Default is 'cpu'.
172 """
174 def __init__(
175 self,
176 obs_space: int | Tuple,
177 perturb_space: int | Tuple,
178 eps: int | float | np.ndarray,
179 norm: int | float = None,
180 is_proportional_mask: int | torch.Tensor = None,
181 device="cpu",
182 seed=None,
183 ):
184 super().__init__(
185 obs_space, perturb_space, eps, norm, is_proportional_mask, device, seed
186 )
188 def generate_perturbation(self, observation_batch: torch.Tensor):
189 """
190 Generate perturbations using a truncated normal distribution.
192 Parameters
193 ----------
194 observation_batch : torch.Tensor
195 Batch of input observations.
197 Returns
198 -------
199 torch.Tensor
200 Batch of perturbations with same shape as input observations.
201 """
202 original_type = observation_batch.dtype
203 int_conversion = np.issubdtype(original_type, np.integer)
205 batch_size = observation_batch.shape[0]
207 perturbation_map_batch = _truncated_normal(
208 self.rng, 0, 1, -3, 3, (batch_size,) + self.perturb_shape
209 )
211 perturbation_batch = self._scale_perturbation(perturbation_map_batch)
213 if int_conversion:
214 perturbation_batch = perturbation_batch.round()
216 return perturbation_batch
219def _truncated_normal(rng, mean, stddev, lower, upper, shape):
220 """
221 Generate samples from a truncated normal distribution.
223 Parameters
224 ----------
225 mean : float
226 Mean of the normal distribution before truncation.
227 stddev : float
228 Standard deviation of the normal distribution.
229 lower : float
230 Minimum value for truncation.
231 upper : float
232 Maximum value for truncation.
233 shape : tuple
234 Shape of the output array.
236 Returns
237 -------
238 np.ndarray
239 Array of truncated normal samples.
240 """
241 samples = rng.normal(loc=mean, scale=stddev, size=shape)
242 samples = np.clip(samples, lower, upper)
243 return samples