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,209 @@
1
+ import numpy as np
2
+
3
+ from .._utils import as_integer
4
+ from ..integer._modintring import ModIntRing
5
+ from ..typing import Matrix, SquareMatrix, Vector, validate_aliases
6
+ from ._utils import row_add, row_scale, row_swap
7
+
8
+
9
+ @validate_aliases
10
+ def mod_ref(A: Matrix, modulus: int) -> tuple[Matrix, SquareMatrix]:
11
+ """_summary_
12
+
13
+ Parameters
14
+ ----------
15
+ A : Matrix
16
+ _description_
17
+ modulus : int
18
+ _description_
19
+
20
+ Returns
21
+ -------
22
+ tuple[Matrix, SquareMatrix]
23
+ _description_
24
+ """
25
+ R = ModIntRing(modulus)
26
+ m, n = A.shape
27
+
28
+ M = R.mod(as_integer(A))
29
+ U = as_integer(np.identity(m))
30
+
31
+ for j in range(min(m, n)):
32
+ col = M[:, j]
33
+ nonzero = np.nonzero(col[j:])[0]
34
+ if len(nonzero) == 0:
35
+ continue
36
+ pivot_i = nonzero[0] + j
37
+
38
+ if pivot_i != j:
39
+ row_swap(M, pivot_i, j)
40
+ row_swap(U, pivot_i, j)
41
+
42
+ pivot: int = M[j, j]
43
+ pivot_inv = R.inv(pivot)
44
+ row_scale(M, j, pivot_inv)
45
+ row_scale(U, j, pivot_inv)
46
+
47
+ M = R.mod(M)
48
+ U = R.mod(U)
49
+
50
+ for i in range(j + 1, m):
51
+ if M[i, j] != 0:
52
+ row_add(U, i, j, -M[i, j])
53
+ row_add(M, i, j, -M[i, j])
54
+ M = R.mod(M)
55
+ U = R.mod(U)
56
+
57
+ return R.mod(M), R.mod(U)
58
+
59
+
60
+ @validate_aliases
61
+ def mod_rref(A: Matrix, modulus: int) -> tuple[Matrix, SquareMatrix]:
62
+ """_summary_
63
+
64
+ Parameters
65
+ ----------
66
+ A : Matrix
67
+ _description_
68
+ modulus : int
69
+ _description_
70
+
71
+ Returns
72
+ -------
73
+ tuple[Matrix, SquareMatrix]
74
+ _description_
75
+ """
76
+ R = ModIntRing(modulus)
77
+ m, n = A.shape
78
+ M, U = mod_ref(A, modulus)
79
+
80
+ h = m - 1
81
+ for j in range(n - 1, -1, -1):
82
+ if M[h, j] != 1:
83
+ if M[h, j] == 0:
84
+ h -= 1
85
+ continue
86
+
87
+ for i in range(h - 1, -1, -1):
88
+ coeff = M[i, j]
89
+ row_add(M, i, h, -coeff)
90
+ row_add(U, i, h, -coeff)
91
+
92
+ h -= 1
93
+
94
+ return R.mod(M), R.mod(U)
95
+
96
+
97
+ @validate_aliases
98
+ def mod_left_kernel(A: Matrix, modulus: int) -> Matrix:
99
+ """_summary_
100
+
101
+ Parameters
102
+ ----------
103
+ A : Matrix
104
+ _description_
105
+ modulus : int
106
+ _description_
107
+
108
+ Returns
109
+ -------
110
+ Matrix
111
+ _description_
112
+ """
113
+ return mod_right_kernel(A.T, modulus)
114
+
115
+
116
+ @validate_aliases
117
+ def mod_right_kernel(A: Matrix, modulus: int) -> Matrix:
118
+ """_summary_
119
+
120
+ Parameters
121
+ ----------
122
+ A : Matrix
123
+ _description_
124
+ modulus : int
125
+ _description_
126
+
127
+ Returns
128
+ -------
129
+ Matrix
130
+ _description_
131
+ """
132
+ M, U = mod_rref(A.T, modulus)
133
+ kernel_basis: list[Vector] = []
134
+
135
+ m, _ = M.shape
136
+ for i in range(m):
137
+ if np.all(M[i] == 0):
138
+ kernel_basis.append(U[i])
139
+
140
+ return as_integer(kernel_basis)
141
+
142
+
143
+ def mod_left_image(A: Matrix, modulus: int) -> Matrix:
144
+ raise NotImplementedError()
145
+
146
+
147
+ def mod_right_image(A: Matrix, modulus: int) -> Matrix:
148
+ raise NotImplementedError()
149
+
150
+
151
+ @validate_aliases
152
+ def mod_left_nullity(A: Matrix, modulus: int) -> int:
153
+ """_summary_
154
+
155
+ Parameters
156
+ ----------
157
+ A : Matrix
158
+ _description_
159
+ modulus : int
160
+ _description_
161
+
162
+ Returns
163
+ -------
164
+ int
165
+ _description_
166
+ """
167
+ kernel = mod_left_kernel(A, modulus)
168
+ return kernel.shape[0]
169
+
170
+
171
+ @validate_aliases
172
+ def mod_right_nullity(A: Matrix, modulus: int) -> int:
173
+ """_summary_
174
+
175
+ Parameters
176
+ ----------
177
+ A : Matrix
178
+ _description_
179
+ modulus : int
180
+ _description_
181
+
182
+ Returns
183
+ -------
184
+ int
185
+ _description_
186
+ """
187
+ kernel = mod_right_kernel(A, modulus)
188
+ return kernel.shape[0]
189
+
190
+
191
+ def mod_matinv(A: SquareMatrix, modulus: int) -> SquareMatrix:
192
+ """_summary_
193
+
194
+ Parameters
195
+ ----------
196
+ A : SquareMatrix
197
+ _description_
198
+ modulus : int
199
+ _description_
200
+
201
+ Returns
202
+ -------
203
+ SquareMatrix
204
+ _description_
205
+ """
206
+ n = A.shape[0]
207
+ AId = np.hstack((A, as_integer(np.identity(n))))
208
+ R, _ = mod_rref(AId, modulus)
209
+ return as_integer(R[:, n:])
@@ -0,0 +1,167 @@
1
+ import math
2
+
3
+ from ..typing import Matrix, Vector, validate_aliases
4
+
5
+
6
+ @validate_aliases
7
+ def row_swap(m: Matrix, i: int, k: int) -> None:
8
+ """_summary_
9
+
10
+ Parameters
11
+ ----------
12
+ m : Matrix
13
+ _description_
14
+ i : int
15
+ _description_
16
+ k : int
17
+ _description_
18
+ """
19
+ m[[i, k]] = m[[k, i]]
20
+
21
+
22
+ @validate_aliases
23
+ def row_scale(m: Matrix, i: int, s: float | int) -> None:
24
+ """_summary_
25
+
26
+ Parameters
27
+ ----------
28
+ m : Matrix
29
+ _description_
30
+ i : int
31
+ _description_
32
+ s : float | int
33
+ _description_
34
+ """
35
+ m[i] *= s
36
+
37
+
38
+ @validate_aliases
39
+ def row_add(m: Matrix, i: int, k: int, s: float | int) -> None:
40
+ """_summary_
41
+
42
+ Parameters
43
+ ----------
44
+ m : Matrix
45
+ _description_
46
+ i : int
47
+ _description_
48
+ k : int
49
+ _description_
50
+ s : float | int
51
+ _description_
52
+ """
53
+ m[i] += s * m[k]
54
+
55
+
56
+ @validate_aliases
57
+ def col_swap(m: Matrix, i: int, k: int) -> None:
58
+ """_summary_
59
+
60
+ Parameters
61
+ ----------
62
+ m : Matrix
63
+ _description_
64
+ i : int
65
+ _description_
66
+ k : int
67
+ _description_
68
+ """
69
+ m[:, [i, k]] = m[:, [k, i]]
70
+
71
+
72
+ @validate_aliases
73
+ def col_scale(m: Matrix, i: int, s: float | int) -> None:
74
+ """_summary_
75
+
76
+ Parameters
77
+ ----------
78
+ m : Matrix
79
+ _description_
80
+ i : int
81
+ _description_
82
+ s : float | int
83
+ _description_
84
+ """
85
+ m[:, i] *= s
86
+
87
+
88
+ @validate_aliases
89
+ def col_add(m: Matrix, i: int, k: int, s: float | int) -> None:
90
+ """_summary_
91
+
92
+ Parameters
93
+ ----------
94
+ m : Matrix
95
+ _description_
96
+ i : int
97
+ _description_
98
+ k : int
99
+ _description_
100
+ s : float | int
101
+ _description_
102
+ """
103
+ m[:, i] += s * m[:, k]
104
+
105
+
106
+ def norm2(v: Vector) -> int:
107
+ """_summary_
108
+
109
+ Parameters
110
+ ----------
111
+ v : Vector
112
+ _description_
113
+
114
+ Returns
115
+ -------
116
+ int
117
+ _description_
118
+ """
119
+ return int(v @ v.T)
120
+
121
+
122
+ def norm(v: Vector) -> float:
123
+ """_summary_
124
+
125
+ Parameters
126
+ ----------
127
+ v : Vector
128
+ _description_
129
+
130
+ Returns
131
+ -------
132
+ float
133
+ _description_
134
+ """
135
+ return math.sqrt(norm2(v))
136
+
137
+
138
+ def per_row_norm2(A: Matrix) -> list[int]:
139
+ """_summary_
140
+
141
+ Parameters
142
+ ----------
143
+ A : Matrix
144
+ _description_
145
+
146
+ Returns
147
+ -------
148
+ list[int]
149
+ _description_
150
+ """
151
+ return [norm2(row) for row in A]
152
+
153
+
154
+ def per_row_norm(A: Matrix) -> list[float]:
155
+ """_summary_
156
+
157
+ Parameters
158
+ ----------
159
+ A : Matrix
160
+ _description_
161
+
162
+ Returns
163
+ -------
164
+ list[float]
165
+ _description_
166
+ """
167
+ return [math.sqrt(n2) for n2 in per_row_norm2(A)]
@@ -0,0 +1,5 @@
1
+ from . import poly
2
+ from ._modpolyqring import ModIntPolyQuotientRing, construct_ring
3
+ from ._modpolyring import ModIntPolyRing
4
+
5
+ __all__ = ["poly", "ModIntPolyRing", "ModIntPolyQuotientRing", "construct_ring"]
@@ -0,0 +1,185 @@
1
+ from ..integer._modring import mod, modinv
2
+ from ..typing import Vector, validate_aliases
3
+ from ._modpolyring import ModIntPolyRing
4
+ from ._poly import zero_poly
5
+
6
+
7
+ class ModIntPolyQuotientRing:
8
+ @validate_aliases
9
+ def __init__(self, poly_modulus: Vector, int_modulus: int) -> None:
10
+ """_summary_
11
+
12
+ Parameters
13
+ ----------
14
+ poly_modulus : Vector
15
+ _description_
16
+ int_modulus : int
17
+ _description_
18
+ """
19
+ self.poly_modulus = poly_modulus
20
+ self.int_modulus = int_modulus
21
+ self.Zm = ModIntPolyRing(int_modulus)
22
+
23
+ @property
24
+ def quotient(self) -> Vector:
25
+ """_summary_
26
+
27
+ Returns
28
+ -------
29
+ Vector
30
+ _description_
31
+ """
32
+ return self.poly_modulus
33
+
34
+ @validate_aliases
35
+ def reduce(self, polynomial: Vector) -> Vector:
36
+ """_summary_
37
+
38
+ Parameters
39
+ ----------
40
+ polynomial : Vector
41
+ _description_
42
+
43
+ Returns
44
+ -------
45
+ Vector
46
+ _description_
47
+ """
48
+ return self.Zm.rem(polynomial, self.poly_modulus)
49
+
50
+ @validate_aliases
51
+ def center_lift(self, polynomial: Vector) -> Vector:
52
+ """
53
+ Hoffstein page. 414 - 415
54
+
55
+ Parameters
56
+ ----------
57
+ polynomial : Vector
58
+ _description_
59
+
60
+ Returns
61
+ -------
62
+ Vector
63
+ _description_
64
+ """
65
+ return mod(polynomial + self.int_modulus // 2, self.int_modulus) - self.int_modulus // 2
66
+
67
+ @validate_aliases
68
+ def add(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:
69
+ """_summary_
70
+
71
+ Parameters
72
+ ----------
73
+ polynomial_a : Vector
74
+ _description_
75
+ polynomial_b : Vector
76
+ _description_
77
+
78
+ Returns
79
+ -------
80
+ Vector
81
+ _description_
82
+ """
83
+ return self.reduce(self.Zm.add(polynomial_a, polynomial_b))
84
+
85
+ @validate_aliases
86
+ def sub(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:
87
+ """_summary_
88
+
89
+ Parameters
90
+ ----------
91
+ polynomial_a : Vector
92
+ _description_
93
+ polynomial_b : Vector
94
+ _description_
95
+
96
+ Returns
97
+ -------
98
+ Vector
99
+ _description_
100
+ """
101
+ return self.reduce(self.Zm.sub(polynomial_a, polynomial_b))
102
+
103
+ @validate_aliases
104
+ def mul(self, polynomial_a: Vector, polynomial_b: Vector) -> Vector:
105
+ """_summary_
106
+
107
+ Parameters
108
+ ----------
109
+ polynomial_a : Vector
110
+ _description_
111
+ polynomial_b : Vector
112
+ _description_
113
+
114
+ Returns
115
+ -------
116
+ Vector
117
+ _description_
118
+ """
119
+ return self.reduce(self.Zm.mul(polynomial_a, polynomial_b))
120
+
121
+ @validate_aliases
122
+ def inv(self, polynomial: Vector) -> Vector:
123
+ """_summary_
124
+
125
+ Parameters
126
+ ----------
127
+ polynomial : Vector
128
+ _description_
129
+
130
+ Returns
131
+ -------
132
+ Vector
133
+ _description_
134
+
135
+ Raises
136
+ ------
137
+ ValueError
138
+ _description_
139
+ """
140
+ if not self.Zm.coprime(polynomial, self.poly_modulus):
141
+ raise ValueError("Inverse does not exists")
142
+
143
+ gcd, u, _ = self.Zm.eea(polynomial, self.poly_modulus)
144
+
145
+ c = modinv(gcd, self.int_modulus)
146
+
147
+ return self.reduce(u * c)
148
+
149
+
150
+ def construct_ring(p: str, N: int, q: int) -> ModIntPolyQuotientRing:
151
+ """_summary_
152
+
153
+ Parameters
154
+ ----------
155
+ p : str
156
+ _description_
157
+ N : int
158
+ _description_
159
+ q : int
160
+ _description_
161
+
162
+ Returns
163
+ -------
164
+ ModIntPolyQuotientRing
165
+ _description_
166
+
167
+ Raises
168
+ ------
169
+ ValueError
170
+ _description_
171
+ """
172
+ g = zero_poly(N)
173
+ match p:
174
+ case "-" | "X^N - 1":
175
+ g[[0, N]] = -1, 1
176
+ pass
177
+ case "+" | "X^N + 1":
178
+ g[[0, N]] = 1, 1
179
+ pass
180
+ case "prime" | "X^N - x - 1":
181
+ g[[0, 1, N]] = -1, -1, 1
182
+ case _:
183
+ raise ValueError(f"Unknown symbol: {p}")
184
+
185
+ return ModIntPolyQuotientRing(g, q)