Coverage for src/pqlattice/polynomial/_poly.py: 76%

1 import numpy as np

2 from numpy.typing import ArrayLike

3

4 from .._utils import as_integer

5 from ..typing import Vector, validate_aliases

6

7

8@validate_aliases

9 def make_poly(data: ArrayLike) -> Vector:

10 """_summary_

11

12 Parameters

13 ----------

14 data : Iterable[int | float]

15 _description_

16

17 Returns

18 -------

19 Vector

20 _description_

21

22 Raises

23 ------

24 ValueError

25 _description_

26 """

27 arr = as_integer(data)

28

29 if arr.ndim != 1:

30 raise ValueError(f"Expected 1D iterable, got {arr.ndim}D")

31

32 return arr

33

34

35@validate_aliases

36 def is_zero_poly(p: Vector) -> bool:

37 """_summary_

38

39 Parameters

40 ----------

41 p : Vector

42 _description_

43

44 Returns

45 -------

46 bool

47 _description_

48

49 Raises

50 ------

51 ValueError

52 _description_

53 """

54 if len(p) == 0:

55 raise ValueError("Empty coefficient array is not a proper polynomial")

56

57 return np.count_nonzero(p) == 0

58

59

60@validate_aliases

61 def deg(p: Vector) -> int:

62 """_summary_

63

64 Parameters

65 ----------

66 p : Vector

67 _description_

68

69 Returns

70 -------

71 int

72 _description_

73

74 Raises

75 ------

76 ValueError

77 _description_

78 """

79 if len(p) == 0:

80 raise ValueError("Empty coefficient array is not a proper polynomial")

81 nonzeros = np.nonzero(p)[0]

82 if len(nonzeros) == 0:

83 return -1

84 # raise ValueError("Degree of zero polynomial is undefined")

85 else:

86 return nonzeros[-1]

87

88

89@validate_aliases

90 def pad(p: Vector, max_deg: int) -> Vector:

91 """_summary_

92

93 Parameters

94 ----------

95 p : Vector

96 _description_

97 max_deg : int

98 _description_

99

100 Returns

101 -------

102 Vector

103 _description_

104

105 Raises

106 ------

107 ValueError

108 _description_

109 """

110 if is_zero_poly(p):

111 return zero_poly(max_deg)

112

113 d = deg(p)

114 if max_deg < d:

115 raise ValueError("max_deg has to be greater or equal to the degree of a given polynomial p")

116

117 return as_integer(np.pad(trim(p), (0, max_deg - d)))

118

119

120@validate_aliases

121 def trim(p: Vector) -> Vector:

122 """_summary_

123

124 Parameters

125 ----------

126 p : Vector

127 _description_

128

129 Returns

130 -------

131 Vector

132 _description_

133 """

134 if is_zero_poly(p):

135 return as_integer([0])

136

137 return p[: deg(p) + 1].copy()

138

139

140@validate_aliases

141 def add(p: Vector, q: Vector) -> Vector:

142 """_summary_

143

144 Parameters

145 ----------

146 p : Vector

147 _description_

148 q : Vector

149 _description_

150

151 Returns

152 -------

153 Vector

154 _description_

155 """

156 max_deg = max(deg(p), deg(q), 0)

157 return trim(pad(p, max_deg) + pad(q, max_deg))

158

159

160@validate_aliases

161 def sub(p: Vector, q: Vector) -> Vector:

162 """_summary_

163

164 Parameters

165 ----------

166 p : Vector

167 _description_

168 q : Vector

169 _description_

170

171 Returns

172 -------

173 Vector

174 _description_

175 """

176 max_deg = max(deg(p), deg(q), 0)

177 return trim(pad(p, max_deg) - pad(q, max_deg))

178

179

180@validate_aliases

181 def mul(p: Vector, q: Vector) -> Vector:

182 """_summary_

183

184 Parameters

185 ----------

186 p : Vector

187 _description_

188 q : Vector

189 _description_

190

191 Returns

192 -------

193 Vector

194 _description_

195 """

196 return trim(np.polymul(p[::-1], q[::-1])[::-1])

197

198

199@validate_aliases

200 def monomial(coeff: int, degree: int) -> Vector:

201 """_summary_

202

203 Parameters

204 ----------

205 coeff : int

206 _description_

207 degree : int

208 _description_

209

210 Returns

211 -------

212 Vector

213 _description_

214

215 Raises

216 ------

217 ValueError

218 _description_

219 """

220 if degree < 0:

221 raise ValueError("degree has to be non negative")

222

223 p = as_integer([0] * (degree + 1))

224 p[degree] = coeff

225 return p

226

227

228@validate_aliases

229 def zero_poly(max_deg: int = 0) -> Vector:

230 """_summary_

231

232 Parameters

233 ----------

234 max_deg : int, optional

235 _description_, by default 0

236

237 Returns

238 -------

239 Vector

240 _description_

241

242 Raises

243 ------

244 ValueError

245 _description_

246 """

247 if max_deg < 0:

248 raise ValueError("degree has to be non negative")

249

250 return as_integer([0] * (max_deg + 1))