Spike count to bitwise-coded spike sequence. Emits one bit per timestep from the binary representation of the count. Supports non-negative integers or signed integers in two's complement or non-complementary binary representation if is_bidirectional=True. In **non-complementa
| 191 | self.x_remain = self.x_remain - spike |
| 192 | |
| 193 | class SpikeCountBitwiseNode(SpikeCountBaseLIFNode): |
| 194 | """ |
| 195 | Spike count to bitwise-coded spike sequence. |
| 196 | Emits one bit per timestep from the binary representation of the count. |
| 197 | Supports non-negative integers or signed integers in two's complement or non-complementary binary representation |
| 198 | if is_bidirectional=True. |
| 199 | |
| 200 | In **non-complementary binary representation**: |
| 201 | - For a positive integer `x`, its binary form is used directly (e.g., `+5 -> [1, 0, 1]`). |
| 202 | - For a negative integer `-x`, the binary form of the absolute value `x` is used, with `1`s replaced by `-1`s (e.g., `-5 -> [-1, 0, -1]`). |
| 203 | |
| 204 | In **two's complement binary representation** (if `is_two_complement=True`): |
| 205 | - For positive integers, the binary representation is the same as regular binary (e.g., `+5 -> [0, 1, 0, 1]`). |
| 206 | - For negative integers, the two's complement representation is used (e.g., `-5 -> [1, 0, 1, 1]`). |
| 207 | In this case, the most significant bit (MSB) indicates the sign (0 for positive, 1 for negative), and the value is adjusted to reflect the two's complement format. |
| 208 | """ |
| 209 | def __init__(self, is_bidirectional: bool = False, is_two_complement: bool = False): |
| 210 | super().__init__() |
| 211 | self.T = None |
| 212 | self.x_remain = None |
| 213 | self.spike_seq = None |
| 214 | self._bit_idx = 0 |
| 215 | self.is_bidirectional = is_bidirectional |
| 216 | self.is_bitwise_coding = True |
| 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) |
no outgoing calls