(self, x: torch.Tensor, T: int | None = None)
| 217 | self.is_two_complement = is_two_complement |
| 218 | |
| 219 | def forward(self, x: torch.Tensor, T: int | None = None): |
| 220 | if not torch.allclose(x, x.round()): |
| 221 | raise ValueError("Input x must be integer-valued (whole numbers).") |
| 222 | |
| 223 | x = x.to(torch.int64) |
| 224 | |
| 225 | if self.is_bidirectional: |
| 226 | x_min = x.min().item() |
| 227 | x_max = x.max().item() |
| 228 | x_abs_max = max(abs(x_min), abs(x_max)) |
| 229 | else: |
| 230 | if not torch.all(x >= 0): |
| 231 | raise ValueError("Input x must be non-negative when is_bidirectional=False.") |
| 232 | x_abs_max = x.max().item() |
| 233 | |
| 234 | if T is not None: |
| 235 | if not isinstance(T, int) or T <= 0: |
| 236 | raise ValueError("T must be a positive integer.") |
| 237 | self.T = T |
| 238 | else: |
| 239 | if self.is_bidirectional and self.is_two_complement: |
| 240 | # Use two's complement to represent signed integers, hence +1 for sign bit |
| 241 | self.T = max(2, math.ceil(math.log2(x_abs_max + 1)) + 1) |
| 242 | else: |
| 243 | self.T = max(1, math.ceil(math.log2(x_abs_max + 1))) |
| 244 | |
| 245 | if self.is_bidirectional and not self.is_two_complement: |
| 246 | negative_mask = x < 0 |
| 247 | |
| 248 | self.neuronal_charge(x) |
| 249 | |
| 250 | self.spike_seq = torch.zeros((self.T,) + x.shape, dtype=torch.float32, device=x.device) |
| 251 | |
| 252 | for t in range(self.T): |
| 253 | spike = self.neuronal_fire() |
| 254 | self.neuronal_reset(spike) |
| 255 | if self.is_bidirectional and not self.is_two_complement: |
| 256 | self.spike_seq[t] = torch.where(negative_mask, -spike, spike) |
| 257 | else: |
| 258 | self.spike_seq[t] = spike |
| 259 | |
| 260 | return self.spike_seq # shape: [T, *x.shape] |
| 261 | |
| 262 | def neuronal_charge(self, x: torch.Tensor): |
| 263 | if self.is_bidirectional and self.is_two_complement: |
nothing calls this directly
no test coverage detected