pqlattice 0.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. pqlattice/__init__.py +6 -0
  2. pqlattice/_backends/__init__.py +0 -0
  3. pqlattice/_backends/_fast.py +75 -0
  4. pqlattice/_backends/_native.py +33 -0
  5. pqlattice/_backends/_protocol.py +15 -0
  6. pqlattice/_utils.py +201 -0
  7. pqlattice/integer/__init__.py +8 -0
  8. pqlattice/integer/_integer.py +55 -0
  9. pqlattice/integer/_modintring.py +246 -0
  10. pqlattice/integer/_modring.py +165 -0
  11. pqlattice/integer/_primality.py +78 -0
  12. pqlattice/integer/_primes.py +57 -0
  13. pqlattice/lattice/__init__.py +44 -0
  14. pqlattice/lattice/_bkz.py +87 -0
  15. pqlattice/lattice/_cvp.py +62 -0
  16. pqlattice/lattice/_embeddings.py +149 -0
  17. pqlattice/lattice/_gso.py +43 -0
  18. pqlattice/lattice/_hkz.py +20 -0
  19. pqlattice/lattice/_lattice.py +137 -0
  20. pqlattice/lattice/_lll.py +93 -0
  21. pqlattice/lattice/_svp.py +89 -0
  22. pqlattice/lattice/embeddings.py +3 -0
  23. pqlattice/linalg/__init__.py +37 -0
  24. pqlattice/linalg/_linalg.py +306 -0
  25. pqlattice/linalg/_modint.py +209 -0
  26. pqlattice/linalg/_utils.py +167 -0
  27. pqlattice/polynomial/__init__.py +5 -0
  28. pqlattice/polynomial/_modpolyqring.py +185 -0
  29. pqlattice/polynomial/_modpolyring.py +267 -0
  30. pqlattice/polynomial/_poly.py +250 -0
  31. pqlattice/polynomial/poly.py +3 -0
  32. pqlattice/py.typed +0 -0
  33. pqlattice/random/__init__.py +7 -0
  34. pqlattice/random/_distribution.py +303 -0
  35. pqlattice/random/_lattice.py +53 -0
  36. pqlattice/random/_lwe.py +109 -0
  37. pqlattice/random/_lwr.py +41 -0
  38. pqlattice/random/_prime.py +53 -0
  39. pqlattice/random/distribution.py +3 -0
  40. pqlattice/settings.py +66 -0
  41. pqlattice/typing/__init__.py +4 -0
  42. pqlattice/typing/_types.py +18 -0
  43. pqlattice/typing/_types_validator.py +57 -0
  44. pqlattice-0.1.2.dist-info/METADATA +33 -0
  45. pqlattice-0.1.2.dist-info/RECORD +47 -0
  46. pqlattice-0.1.2.dist-info/WHEEL +4 -0
  47. pqlattice-0.1.2.dist-info/licenses/LICENSE +7 -0
@@ -0,0 +1,137 @@
1
+ import math
2
+ from functools import reduce
3
+
4
+ import numpy as np
5
+
6
+ from .._utils import as_integer
7
+ from ..linalg._linalg import det
8
+ from ..linalg._utils import norm2, per_row_norm
9
+ from ..typing import SquareMatrix, Vector, validate_aliases
10
+
11
+
12
+ @validate_aliases
13
+ def volume(lattice_basis: SquareMatrix) -> int:
14
+ """_summary_
15
+
16
+ Parameters
17
+ ----------
18
+ lattice_basis : SquareMatrix
19
+ _description_
20
+
21
+ Returns
22
+ -------
23
+ int
24
+ _description_
25
+ """
26
+ return abs(det(lattice_basis))
27
+
28
+
29
+ @validate_aliases
30
+ def rank(lattice_basis: SquareMatrix) -> int:
31
+ """_summary_
32
+
33
+ Parameters
34
+ ----------
35
+ lattice_basis : SquareMatrix
36
+ _description_
37
+
38
+ Returns
39
+ -------
40
+ int
41
+ _description_
42
+ """
43
+ return lattice_basis.shape[0]
44
+
45
+
46
+ @validate_aliases
47
+ def discriminant(lattice_basis: SquareMatrix) -> int:
48
+ """_summary_
49
+
50
+ Parameters
51
+ ----------
52
+ lattice_basis : SquareMatrix
53
+ _description_
54
+
55
+ Returns
56
+ -------
57
+ int
58
+ _description_
59
+ """
60
+ v = volume(lattice_basis)
61
+ return v * v
62
+
63
+
64
+ @validate_aliases
65
+ def hadamard_ratio(lattice_basis: SquareMatrix) -> float:
66
+ """_summary_
67
+
68
+ Parameters
69
+ ----------
70
+ lattice_basis : SquareMatrix
71
+ _description_
72
+
73
+ Returns
74
+ -------
75
+ float
76
+ _description_
77
+ """
78
+ return (volume(lattice_basis) / reduce(lambda a, b: a * b, per_row_norm(lattice_basis))) ** (1 / rank(lattice_basis))
79
+
80
+
81
+ @validate_aliases
82
+ def gaussian_heuristic(lattice_basis: SquareMatrix) -> float:
83
+ """_summary_
84
+
85
+ Parameters
86
+ ----------
87
+ lattice_basis : SquareMatrix
88
+ _description_
89
+
90
+ Returns
91
+ -------
92
+ float
93
+ _description_
94
+ """
95
+ n = rank(lattice_basis)
96
+ return math.sqrt(n / (2 * math.pi * math.e)) * (volume(lattice_basis) ** (1 / n))
97
+
98
+
99
+ @validate_aliases
100
+ def glr_2dim(lattice_basis: SquareMatrix) -> SquareMatrix:
101
+ """_summary_
102
+
103
+ Parameters
104
+ ----------
105
+ lattice_basis : SquareMatrix
106
+ _description_
107
+
108
+ Returns
109
+ -------
110
+ SquareMatrix
111
+ _description_
112
+
113
+ Raises
114
+ ------
115
+ ValueError
116
+ _description_
117
+ """
118
+ if lattice_basis.shape != (2, 2):
119
+ raise ValueError("Lattice has to have rank 2 for gaussian reduction")
120
+
121
+ w1: Vector = lattice_basis[0]
122
+ w2: Vector = lattice_basis[1]
123
+
124
+ v1 = w1.astype(float)
125
+ v2 = w2.astype(float)
126
+ if norm2(v1) > norm2(v2):
127
+ v1, v2 = v2, v1
128
+
129
+ while norm2(v2) > norm2(v1):
130
+ m = np.rint(np.dot(v1, v2) / np.dot(v1, v1))
131
+ if m == 0:
132
+ return as_integer([v1, v2])
133
+ v2 = v2 - m * v1
134
+ if norm2(v1) > norm2(v2):
135
+ v1, v2 = v2, v1
136
+
137
+ return np.array([v1, v2])
@@ -0,0 +1,93 @@
1
+ from fractions import Fraction
2
+
3
+ import numpy as np
4
+
5
+ from .._utils import as_integer
6
+ from ..typing import Matrix, SquareMatrix, validate_aliases
7
+ from ._gso import gso, project_coeffs
8
+
9
+
10
+ @validate_aliases
11
+ def lll(lattice_basis: Matrix, delta: float = 0.99) -> Matrix:
12
+ """_summary_
13
+
14
+ Parameters
15
+ ----------
16
+ lattice_basis : Matrix
17
+ _description_
18
+ delta : float, optional
19
+ _description_, by default 0.99
20
+
21
+ Returns
22
+ -------
23
+ Matrix
24
+ _description_
25
+ """
26
+ rows, _ = lattice_basis.shape
27
+ B: Matrix = as_integer(lattice_basis)
28
+ while True:
29
+ B_star, _ = gso(B)
30
+ # Reduction Step
31
+ for i in range(1, rows):
32
+ for j in range(i - 1, -1, -1):
33
+ c_ij = round(project_coeffs(B_star[j], B[i]))
34
+ assert isinstance(c_ij, int)
35
+ B[i] = B[i] - c_ij * B[j]
36
+ # Swap step
37
+ exists = False
38
+ for i in range(rows - 1):
39
+ u = project_coeffs(B_star[i], B[i + 1])
40
+ r = u * B_star[i] + B_star[i + 1]
41
+ if delta * np.dot(B_star[i], B_star[i]) > np.dot(r, r):
42
+ B[[i, i + 1]] = B[[i + 1, i]]
43
+ exists = True
44
+ break
45
+ if not exists:
46
+ break
47
+ return B
48
+
49
+
50
+ @validate_aliases
51
+ def is_size_reduced(lattice_basis: SquareMatrix) -> bool:
52
+ """_summary_
53
+
54
+ Parameters
55
+ ----------
56
+ lattice_basis : SquareMatrix
57
+ _description_
58
+
59
+ Returns
60
+ -------
61
+ bool
62
+ _description_
63
+ """
64
+ _, U = gso(lattice_basis)
65
+ return bool(np.all(np.triu(U, 1) <= Fraction(1, 2)))
66
+
67
+
68
+ @validate_aliases
69
+ def lovasz_condition(lattice_basis: SquareMatrix, delta: float) -> bool:
70
+ norm2 = lambda a: np.sum(a * a, axis=1) # type: ignore
71
+ G, U = gso(lattice_basis)
72
+ lhs = delta * norm2(G[:-1])
73
+ rhs = norm2(G[1:] + np.diag(U, 1)[:, np.newaxis] * G[:-1])
74
+ return bool(np.all(lhs <= rhs))
75
+
76
+
77
+ @validate_aliases
78
+ def is_lll_reduced(lattice_basis: SquareMatrix, delta: float = 0.99) -> bool:
79
+ """_summary_
80
+
81
+ Parameters
82
+ ----------
83
+ lattice_basis : SquareMatrix
84
+ _description_
85
+ delta : float, optional
86
+ _description_, by default 0.99
87
+
88
+ Returns
89
+ -------
90
+ bool
91
+ _description_
92
+ """
93
+ return is_size_reduced(lattice_basis) and lovasz_condition(lattice_basis, delta)
@@ -0,0 +1,89 @@
1
+ from fractions import Fraction
2
+
3
+ from .._utils import as_integer, as_rational
4
+ from ..typing import SquareMatrix, Vector, validate_aliases
5
+ from ._gso import gso
6
+ from ._lll import lll
7
+
8
+
9
+ @validate_aliases
10
+ def schnorr_euchner_svp(mu: SquareMatrix, B: Vector) -> Vector:
11
+ n = len(B)
12
+ best_norm: Fraction = B[0]
13
+ best_coeffs: Vector = as_integer([1] + [0] * (n - 1))
14
+
15
+ r: Vector = as_rational([0] * (n + 1))
16
+ c: Vector = as_rational([0] * n)
17
+ x: Vector = as_integer([0] * n)
18
+ v: Vector = as_rational([0] * n)
19
+
20
+ last_move = [0] * n
21
+ k = n - 1
22
+
23
+ r[n] = Fraction(0)
24
+ v[n - 1] = Fraction(0)
25
+ c[n - 1] = Fraction(0)
26
+ x[n - 1] = 0
27
+ last_move[n - 1] = 0
28
+
29
+ while True:
30
+ y: Fraction = x[k] - c[k]
31
+ current_norm: Fraction = r[k + 1] + (y * y * B[k])
32
+ # print(f"{k=}: {current_norm=:.5f}, {best_norm=:.5f}")
33
+ if current_norm < best_norm:
34
+ if k == 0:
35
+ if current_norm > 0:
36
+ best_norm = current_norm
37
+ best_coeffs = x.copy()
38
+
39
+ k += 1
40
+ last_move[k] += 1
41
+
42
+ step_sign = -1 if (last_move[k] % 2 == 0) else 1
43
+ step_mag = (last_move[k] + 1) // 2
44
+ x[k] = round(c[k]) + (step_sign * step_mag)
45
+
46
+ else:
47
+ k -= 1
48
+ center_val = Fraction(0)
49
+ for j in range(k + 1, n):
50
+ center_val += mu[j][k] * x[j]
51
+
52
+ c[k] = -center_val
53
+ x[k] = round(c[k])
54
+ last_move[k] = 0
55
+ r[k + 1] = current_norm
56
+ else:
57
+ k += 1
58
+ if k == n:
59
+ break
60
+
61
+ last_move[k] += 1
62
+ step_sign = -1 if (last_move[k] % 2 == 0) else 1
63
+ step_mag = (last_move[k] + 1) // 2
64
+
65
+ x[k] = round(c[k]) + (step_sign * step_mag)
66
+
67
+ return best_coeffs
68
+
69
+
70
+ @validate_aliases
71
+ def shortest_vector(lattice_basis: SquareMatrix) -> Vector:
72
+ """_summary_
73
+
74
+ Parameters
75
+ ----------
76
+ lattice_basis : SquareMatrix
77
+ _description_
78
+
79
+ Returns
80
+ -------
81
+ Vector
82
+ _description_
83
+ """
84
+ B = lll(lattice_basis)
85
+ B_star, U = gso(B)
86
+ B_norms2 = as_rational([sum(x * x for x in v) for v in B_star])
87
+ mu = U.T
88
+ coeffs = schnorr_euchner_svp(mu, B_norms2)
89
+ return coeffs @ B
@@ -0,0 +1,3 @@
1
+ from ._embeddings import bai_galbraith, kannan, lwe_basis, ntru, sis_basis, subset_sum
2
+
3
+ __all__ = ["sis_basis", "lwe_basis", "ntru", "kannan", "bai_galbraith", "subset_sum"]
@@ -0,0 +1,37 @@
1
+ from .. import settings
2
+ from ..typing import Matrix, SquareMatrix
3
+ from ._linalg import cofactor, cofactor_matrix, det, left_kernel, left_nullity, minor, rank, right_kernel, right_nullity
4
+ from ._modint import mod_left_kernel, mod_left_nullity, mod_matinv, mod_ref, mod_right_kernel, mod_right_nullity, mod_rref
5
+ from ._utils import norm, norm2, per_row_norm, per_row_norm2, row_add, row_scale, row_swap
6
+
7
+
8
+ def hnf(matrix: SquareMatrix) -> tuple[Matrix, SquareMatrix]:
9
+ return settings.get_backend().hnf(matrix)
10
+
11
+
12
+ __all__ = [
13
+ "hnf",
14
+ "det",
15
+ "left_kernel",
16
+ "right_kernel",
17
+ "left_nullity",
18
+ "right_nullity",
19
+ "rank",
20
+ "minor",
21
+ "cofactor",
22
+ "cofactor_matrix",
23
+ "norm",
24
+ "norm2",
25
+ "per_row_norm",
26
+ "per_row_norm2",
27
+ "row_add",
28
+ "row_scale",
29
+ "row_swap",
30
+ "mod_ref",
31
+ "mod_rref",
32
+ "mod_right_kernel",
33
+ "mod_left_kernel",
34
+ "mod_left_nullity",
35
+ "mod_right_nullity",
36
+ "mod_matinv",
37
+ ]
@@ -0,0 +1,306 @@
1
+ from functools import reduce
2
+
3
+ import numpy as np
4
+
5
+ from .._utils import as_integer
6
+ from ..typing import Matrix, SquareMatrix, Vector, validate_aliases
7
+ from ._utils import row_add, row_scale, row_swap
8
+
9
+
10
+ @validate_aliases
11
+ def hnf(A: Matrix) -> tuple[Matrix, SquareMatrix]:
12
+ """_summary_
13
+
14
+ Parameters
15
+ ----------
16
+ A : Matrix
17
+ _description_
18
+
19
+ Returns
20
+ -------
21
+ tuple[Matrix, SquareMatrix]
22
+ _description_
23
+ """
24
+ H, U, _ = _hnf(A)
25
+ return H, U
26
+
27
+
28
+ @validate_aliases
29
+ def _hnf(a: Matrix) -> tuple[Matrix, SquareMatrix, int]:
30
+ H = np.array(a, dtype=object)
31
+ m, n = H.shape
32
+ U = np.eye(m, dtype=object)
33
+ pivot_row = 0
34
+ pivot_col = 0
35
+ det_U = 1
36
+
37
+ while pivot_row < m and pivot_col < n:
38
+ # pivot selection
39
+ if np.all(H[pivot_row:, pivot_col] == 0):
40
+ pivot_col += 1
41
+ continue
42
+
43
+ candidates = [(abs(H[i, pivot_col]), i) for i in range(pivot_row, m) if H[i, pivot_col] != 0]
44
+ _, best_row = min(candidates)
45
+
46
+ row_swap(H, pivot_row, best_row)
47
+ row_swap(U, pivot_row, best_row)
48
+ det_U *= -1
49
+
50
+ # clear below pivot
51
+ for i in range(pivot_row + 1, m):
52
+ while H[i, pivot_col] != 0:
53
+ factor = H[i, pivot_col] // H[pivot_row, pivot_col]
54
+
55
+ row_add(H, i, pivot_row, -factor)
56
+ row_add(U, i, pivot_row, -factor)
57
+
58
+ if H[i, pivot_col] != 0:
59
+ row_swap(H, pivot_row, i)
60
+ row_swap(U, pivot_row, i)
61
+ det_U *= -1
62
+
63
+ if H[pivot_row, pivot_col] < 0:
64
+ row_scale(H, pivot_row, -1)
65
+ row_scale(U, pivot_row, -1)
66
+ det_U *= -1
67
+
68
+ pivot_val = H[pivot_row, pivot_col]
69
+
70
+ for i in range(pivot_row):
71
+ factor = H[i, pivot_col] // pivot_val
72
+ row_add(H, i, pivot_row, -factor)
73
+ row_add(U, i, pivot_row, -factor)
74
+
75
+ pivot_row += 1
76
+ pivot_col += 1
77
+
78
+ return H, U, det_U
79
+
80
+
81
+ @validate_aliases
82
+ def right_image(A: Matrix) -> Matrix:
83
+ """
84
+ _summary_
85
+
86
+ Parameters
87
+ ----------
88
+ A : Matrix
89
+ _description_
90
+
91
+ Returns
92
+ -------
93
+ Matrix
94
+ _description_
95
+ """
96
+ return left_image(A.T).T
97
+
98
+
99
+ @validate_aliases
100
+ def left_image(A: Matrix) -> Matrix:
101
+ """
102
+ _summary_
103
+
104
+ Parameters
105
+ ----------
106
+ A : Matrix
107
+ _description_
108
+
109
+ Returns
110
+ -------
111
+ Matrix
112
+ _description_
113
+ """
114
+ H, _ = hnf(A)
115
+
116
+ m, _ = H.shape
117
+ k = 0
118
+ for i in range(m):
119
+ if np.all(H[i] == 0):
120
+ k = i
121
+ break
122
+
123
+ return as_integer(H[:k])
124
+
125
+
126
+ @validate_aliases
127
+ def left_kernel(A: Matrix):
128
+ """
129
+ {x : xA = 0}
130
+
131
+ Parameters
132
+ ----------
133
+ A : Matrix
134
+ _description_
135
+
136
+ Returns
137
+ -------
138
+ _type_
139
+ _description_
140
+ """
141
+ H, U = hnf(A)
142
+ kernel_basis: list[Vector] = []
143
+
144
+ m, _ = H.shape
145
+ for i in range(m):
146
+ if np.all(H[i] == 0):
147
+ kernel_basis.append(U[i])
148
+
149
+ return np.array(kernel_basis, dtype=object)
150
+
151
+
152
+ @validate_aliases
153
+ def right_kernel(A: Matrix) -> Matrix:
154
+ """
155
+ {x : Ax = 0}
156
+
157
+ Parameters
158
+ ----------
159
+ A : Matrix
160
+ _description_
161
+
162
+ Returns
163
+ -------
164
+ Matrix
165
+ _description_
166
+ """
167
+ return left_kernel(A.T)
168
+
169
+
170
+ @validate_aliases
171
+ def left_nullity(a: Matrix) -> int:
172
+ """_summary_
173
+
174
+ Parameters
175
+ ----------
176
+ a : Matrix
177
+ _description_
178
+
179
+ Returns
180
+ -------
181
+ int
182
+ _description_
183
+ """
184
+ kernel = left_kernel(a)
185
+ return kernel.shape[0]
186
+
187
+
188
+ @validate_aliases
189
+ def right_nullity(a: Matrix) -> int:
190
+ """_summary_
191
+
192
+ Parameters
193
+ ----------
194
+ a : Matrix
195
+ _description_
196
+
197
+ Returns
198
+ -------
199
+ int
200
+ _description_
201
+ """
202
+ kernel = right_kernel(a)
203
+ return kernel.shape[0]
204
+
205
+
206
+ def rank(a: Matrix) -> int:
207
+ """_summary_
208
+
209
+ Parameters
210
+ ----------
211
+ a : Matrix
212
+ _description_
213
+
214
+ Returns
215
+ -------
216
+ int
217
+ _description_
218
+ """
219
+ m, n = a.shape
220
+ l_rank = m - left_nullity(a)
221
+ r_rank = n - right_nullity(a)
222
+ assert l_rank == r_rank
223
+ return l_rank
224
+
225
+
226
+ @validate_aliases
227
+ def det(A: SquareMatrix) -> int:
228
+ """_summary_
229
+
230
+ Parameters
231
+ ----------
232
+ A : SquareMatrix
233
+ _description_
234
+
235
+ Returns
236
+ -------
237
+ int
238
+ _description_
239
+ """
240
+ H, _, det_U = _hnf(A)
241
+
242
+ return reduce(lambda a, b: a * b, np.diagonal(H), 1) * det_U
243
+
244
+
245
+ @validate_aliases
246
+ def minor(A: SquareMatrix, i: int, j: int) -> int:
247
+ """_summary_
248
+
249
+ Parameters
250
+ ----------
251
+ A : SquareMatrix
252
+ _description_
253
+ i : int
254
+ _description_
255
+ j : int
256
+ _description_
257
+
258
+ Returns
259
+ -------
260
+ int
261
+ _description_
262
+ """
263
+ return det(np.delete(np.delete(A, i, axis=0), j, axis=1))
264
+
265
+
266
+ @validate_aliases
267
+ def cofactor(A: SquareMatrix, i: int, j: int) -> int:
268
+ """_summary_
269
+
270
+ Parameters
271
+ ----------
272
+ A : SquareMatrix
273
+ _description_
274
+ i : int
275
+ _description_
276
+ j : int
277
+ _description_
278
+
279
+ Returns
280
+ -------
281
+ int
282
+ _description_
283
+ """
284
+ return minor(A, i, j) * ((-1) ** (i + 1 + j + 1))
285
+
286
+
287
+ @validate_aliases
288
+ def cofactor_matrix(A: SquareMatrix) -> SquareMatrix:
289
+ """_summary_
290
+
291
+ Parameters
292
+ ----------
293
+ A : SquareMatrix
294
+ _description_
295
+
296
+ Returns
297
+ -------
298
+ SquareMatrix
299
+ _description_
300
+ """
301
+ n = A.shape[0]
302
+ C = np.zeros((n, n), dtype=object)
303
+ for i in range(n):
304
+ for j in range(n):
305
+ C[i, j] = cofactor(A, i, j)
306
+ return C