Repository navigation
Expand file tree
/
Copy pathfast_walsh_hadamard_transform.py
More file actions
209 lines (171 loc) · 6.74 KB
/
Copy pathfast_walsh_hadamard_transform.py
File metadata and controls
209 lines (171 loc) · 6.74 KB
1
2
3
4
5
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
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
"""
Fast Walsh-Hadamard Transform (FWHT) for Bitwise Convolutions.
Reference: https://en.wikipedia.org/wiki/Fast_Walsh%E2%80%93Hadamard_transform
Reference: https://cp-algorithms.com/algebra/walsh-hadamard-transform.html
Computes bitwise XOR, AND, and OR convolutions of two numeric sequences in O(N log N)
time, where N is a positive power of 2.
"""
def fwht_xor(sequence: list[int], inverse: bool = False) -> list[int]:
"""
Perform Fast Walsh-Hadamard Transform (or inverse) for XOR operation.
Time Complexity: O(N log N)
>>> fwht_xor([1, 2, 3, 4])
[10, -2, -4, 0]
>>> fwht_xor([10, -2, -4, 0], inverse=True)
[1, 2, 3, 4]
>>> fwht_xor([1, 2, 3])
Traceback (most recent call last):
...
ValueError: Length of sequence must be a positive power of 2.
>>> fwht_xor([])
Traceback (most recent call last):
...
ValueError: Length of sequence must be a positive power of 2.
>>> fwht_xor([1, 2, 3, 5], inverse=True)
Traceback (most recent call last):
...
ValueError: Inverse XOR transform requires elements divisible by sequence length.
"""
sequence_length = len(sequence)
if sequence_length == 0 or (sequence_length & (sequence_length - 1)) != 0:
raise ValueError("Length of sequence must be a positive power of 2.")
result = list(sequence)
half_block = 1
while half_block < sequence_length:
block_size = half_block * 2
for block_start in range(0, sequence_length, block_size):
for offset in range(half_block):
left_index = block_start + offset
right_index = left_index + half_block
left_val = result[left_index]
right_val = result[right_index]
result[left_index] = left_val + right_val
result[right_index] = left_val - right_val
half_block *= 2
if inverse:
if any(element % sequence_length != 0 for element in result):
raise ValueError(
"Inverse XOR transform requires elements divisible by sequence length."
)
result = [element // sequence_length for element in result]
return result
def xor_convolution(sequence_a: list[int], sequence_b: list[int]) -> list[int]:
"""
Compute bitwise XOR convolution C[k] = sum_{i ^ j = k} (A[i] * B[j]).
Time Complexity: O(N log N)
>>> xor_convolution([1, 2], [3, 4])
[11, 10]
>>> xor_convolution([1, 2], [3])
Traceback (most recent call last):
...
ValueError: Input sequences must have equal length.
"""
if len(sequence_a) != len(sequence_b):
raise ValueError("Input sequences must have equal length.")
transformed_a = fwht_xor(sequence_a)
transformed_b = fwht_xor(sequence_b)
pointwise_product = [
transformed_a[index] * transformed_b[index] for index in range(len(sequence_a))
]
return fwht_xor(pointwise_product, inverse=True)
def fwht_or(sequence: list[int], inverse: bool = False) -> list[int]:
"""
Perform Fast Walsh-Hadamard Transform for OR operation.
Time Complexity: O(N log N)
>>> fwht_or([1, 2])
[1, 3]
>>> fwht_or([1, 3], inverse=True)
[1, 2]
>>> fwht_or([1, 2, 3])
Traceback (most recent call last):
...
ValueError: Length of sequence must be a positive power of 2.
"""
sequence_length = len(sequence)
if sequence_length == 0 or (sequence_length & (sequence_length - 1)) != 0:
raise ValueError("Length of sequence must be a positive power of 2.")
result = list(sequence)
half_block = 1
while half_block < sequence_length:
block_size = half_block * 2
for block_start in range(0, sequence_length, block_size):
for offset in range(half_block):
left_index = block_start + offset
right_index = left_index + half_block
if not inverse:
result[right_index] += result[left_index]
else:
result[right_index] -= result[left_index]
half_block *= 2
return result
def or_convolution(sequence_a: list[int], sequence_b: list[int]) -> list[int]:
"""
Compute bitwise OR convolution C[k] = sum_{i | j = k} (A[i] * B[j]).
Time Complexity: O(N log N)
>>> or_convolution([1, 2], [3, 4])
[3, 18]
>>> or_convolution([1, 2], [3])
Traceback (most recent call last):
...
ValueError: Input sequences must have equal length.
"""
if len(sequence_a) != len(sequence_b):
raise ValueError("Input sequences must have equal length.")
transformed_a = fwht_or(sequence_a)
transformed_b = fwht_or(sequence_b)
pointwise_product = [
transformed_a[index] * transformed_b[index] for index in range(len(sequence_a))
]
return fwht_or(pointwise_product, inverse=True)
def fwht_and(sequence: list[int], inverse: bool = False) -> list[int]:
"""
Perform Fast Walsh-Hadamard Transform for AND operation.
Time Complexity: O(N log N)
>>> fwht_and([1, 2])
[3, 2]
>>> fwht_and([3, 2], inverse=True)
[1, 2]
>>> fwht_and([1, 2, 3])
Traceback (most recent call last):
...
ValueError: Length of sequence must be a positive power of 2.
"""
sequence_length = len(sequence)
if sequence_length == 0 or (sequence_length & (sequence_length - 1)) != 0:
raise ValueError("Length of sequence must be a positive power of 2.")
result = list(sequence)
half_block = 1
while half_block < sequence_length:
block_size = half_block * 2
for block_start in range(0, sequence_length, block_size):
for offset in range(half_block):
left_index = block_start + offset
right_index = left_index + half_block
if not inverse:
result[left_index] += result[right_index]
else:
result[left_index] -= result[right_index]
half_block *= 2
return result
def and_convolution(sequence_a: list[int], sequence_b: list[int]) -> list[int]:
"""
Compute bitwise AND convolution C[k] = sum_{i & j = k} (A[i] * B[j]).
Time Complexity: O(N log N)
>>> and_convolution([1, 2], [3, 4])
[13, 8]
>>> and_convolution([1, 2], [3])
Traceback (most recent call last):
...
ValueError: Input sequences must have equal length.
"""
if len(sequence_a) != len(sequence_b):
raise ValueError("Input sequences must have equal length.")
transformed_a = fwht_and(sequence_a)
transformed_b = fwht_and(sequence_b)
pointwise_product = [
transformed_a[index] * transformed_b[index] for index in range(len(sequence_a))
]
return fwht_and(pointwise_product, inverse=True)
if __name__ == "__main__":
import doctest
doctest.testmod()