@@ -2,8 +2,15 @@ from flint.flintlib.flint cimport (
2
2
FLINT_BITS as _FLINT_BITS,
3
3
FLINT_VERSION as _FLINT_VERSION,
4
4
__FLINT_RELEASE as _FLINT_RELEASE,
5
+ slong
5
6
)
7
+ from flint.flintlib.mpoly cimport ordering_t
6
8
from flint.flint_base.flint_context cimport thectx
9
+ from flint.flint_base.flint_base cimport Ordering
10
+ from flint.utils.typecheck cimport typecheck
11
+ cimport libc.stdlib
12
+
13
+ from typing import Optional
7
14
8
15
9
16
FLINT_BITS = _FLINT_BITS
@@ -114,16 +121,163 @@ cdef class flint_poly(flint_elem):
114
121
v = - fac[0 ]
115
122
roots.append((v, m))
116
123
return roots
117
-
124
+
118
125
def complex_roots (self ):
119
126
raise AttributeError (" Complex roots are not supported for this polynomial" )
120
127
121
128
129
+ cdef class flint_mpoly_context(flint_elem):
130
+ """
131
+ Base class for multivariate ring contexts
132
+ """
133
+
134
+ _ctx_cache = None
135
+
136
+ def __init__ (self , int nvars , names ):
137
+ if nvars < 0 :
138
+ raise ValueError (" cannot have a negative amount of variables" )
139
+ elif len (names) != nvars:
140
+ raise ValueError (" number of variables must match number of variable names" )
141
+ self .py_names = tuple (name.encode(" ascii" ) if not isinstance (name, bytes) else name for name in names)
142
+ self .c_names = < const char ** > libc.stdlib.malloc(nvars * sizeof(const char * ))
143
+ for i in range (nvars):
144
+ self .c_names[i] = self .py_names[i]
145
+
146
+ def __dealloc__ (self ):
147
+ libc.stdlib.free(self .c_names)
148
+ self .c_names = NULL
149
+
150
+ def __str__ (self ):
151
+ return self .__repr__ ()
152
+
153
+ def __repr__ (self ):
154
+ return f" {self.__class__.__name__}({self.nvars()}, '{repr(self.ordering())}', {self.names()})"
155
+
156
+ def name (self , long i ):
157
+ if not 0 <= i < len (self .py_names):
158
+ raise IndexError (" variable name index out of range" )
159
+ return self .py_names[i].decode(" ascii" )
160
+
161
+ def names (self ):
162
+ return tuple (name.decode(" ascii" ) for name in self .py_names)
163
+
164
+ def gens (self ):
165
+ return tuple (self .gen(i) for i in range (self .nvars()))
166
+
167
+ def variable_to_index (self , var: Union[int , str]):
168
+ """ Convert a variable name string or possible index to its index in the context."""
169
+ if isinstance (var, str ):
170
+ try :
171
+ i = self .names().index(var)
172
+ except ValueError :
173
+ raise ValueError (" variable not in context" )
174
+ elif isinstance (var, int ):
175
+ if not 0 <= var < self .nvars():
176
+ raise IndexError (" generator index out of range" )
177
+ i = var
178
+ else :
179
+ raise TypeError (" invalid variable type" )
180
+
181
+ return i
182
+
183
+ @staticmethod
184
+ def create_variable_names (slong nvars , names: str ):
185
+ """
186
+ Create a tuple of variable names based on the comma separated `names` string.
187
+
188
+ If `names` contains a single value, and `nvars` > 1, then the variables are numbered, e.g.
189
+
190
+ >>> flint_mpoly_context.create_variable_names(3, "x")
191
+ ('x0', 'x1', 'x2')
192
+
193
+ """
194
+ nametup = tuple (name.strip() for name in names.split(' ,' ))
195
+ if len (nametup) != nvars:
196
+ if len (nametup) == 1 :
197
+ nametup = tuple (nametup[0 ] + str (i) for i in range (nvars))
198
+ else :
199
+ raise ValueError (" number of variables does not equal number of names" )
200
+ return nametup
201
+
202
+ @classmethod
203
+ def get_context (cls , slong nvars = 1 , ordering = Ordering.lex, names: Optional[str] = "x", nametup: Optional[tuple] = None ):
204
+ """
205
+ Retrieve a context via the number of variables, `nvars`, the ordering, `ordering`, and either a variable
206
+ name string, `names`, or a tuple of variable names, `nametup`.
207
+ """
208
+
209
+ # A type hint of `ordering: Ordering` results in the error "TypeError: an integer is required" if a Ordering
210
+ # object is not provided. This is pretty obtuse so we check it's type ourselves
211
+ if not isinstance (ordering, Ordering):
212
+ raise TypeError (f" `ordering` ('{ordering}') is not an instance of flint.Ordering" )
213
+
214
+ if nametup is not None :
215
+ key = nvars, ordering, nametup
216
+ elif nametup is None and names is not None :
217
+ key = nvars, ordering, cls .create_variable_names(nvars, names)
218
+ else :
219
+ raise ValueError (" must provide either `names` or `nametup`" )
220
+
221
+ ctx = cls ._ctx_cache.get(key)
222
+ if ctx is None :
223
+ ctx = cls ._ctx_cache.setdefault(key, cls (* key))
224
+ return ctx
225
+
226
+ @classmethod
227
+ def from_context (cls , ctx: flint_mpoly_context ):
228
+ return cls .get_context(
229
+ nvars = ctx.nvars(),
230
+ ordering = ctx.ordering(),
231
+ names = None ,
232
+ nametup = ctx.names()
233
+ )
234
+
235
+
122
236
cdef class flint_mpoly(flint_elem):
123
237
"""
124
238
Base class for multivariate polynomials.
125
239
"""
126
240
241
+ def leading_coefficient (self ):
242
+ return self .coefficient(0 )
243
+
244
+ def to_dict (self ):
245
+ return {self .monomial(i): self .coefficient(i) for i in range (len (self ))}
246
+
247
+ def __contains__ (self , x ):
248
+ """
249
+ Returns True if `self` contains a term with exponent vector `x` and a non-zero coefficient.
250
+
251
+ >>> from flint import fmpq_mpoly_ctx, Ordering
252
+ >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x')
253
+ >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3})
254
+ >>> (1, 1) in p
255
+ True
256
+ >>> (5, 1) in p
257
+ False
258
+
259
+ """
260
+ return bool (self [x])
261
+
262
+ def __iter__ (self ):
263
+ return iter (self .monoms())
264
+
265
+ def __pos__ (self ):
266
+ return self
267
+
268
+ def terms (self ):
269
+ """
270
+ Return the exponent vectors and coefficient of each term.
271
+
272
+ >>> from flint import fmpq_mpoly_ctx, Ordering
273
+ >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x')
274
+ >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4})
275
+ >>> list(f.terms())
276
+ [((1, 1), 4), ((1, 0), 2), ((0, 1), 3), ((0, 0), 1)]
277
+
278
+ """
279
+ return zip (self .monoms(), self .coeffs())
280
+
127
281
128
282
cdef class flint_series(flint_elem):
129
283
"""
@@ -190,3 +344,26 @@ cdef class flint_mat(flint_elem):
190
344
191
345
# supports mpmath conversions
192
346
tolist = table
347
+
348
+
349
+ cdef ordering_t ordering_py_to_c(ordering): # Cython does not like an "Ordering" type hint here
350
+ if not isinstance (ordering, Ordering):
351
+ raise TypeError (f" `ordering` ('{ordering}') is not an instance of flint.Ordering" )
352
+
353
+ if ordering == Ordering.lex:
354
+ return ordering_t.ORD_LEX
355
+ elif ordering == Ordering.deglex:
356
+ return ordering_t.ORD_DEGLEX
357
+ elif ordering == Ordering.degrevlex:
358
+ return ordering_t.ORD_DEGREVLEX
359
+
360
+
361
+ cdef ordering_c_to_py(ordering_t ordering):
362
+ if ordering == ordering_t.ORD_LEX:
363
+ return Ordering.lex
364
+ elif ordering == ordering_t.ORD_DEGLEX:
365
+ return Ordering.deglex
366
+ elif ordering == ordering_t.ORD_DEGREVLEX:
367
+ return Ordering.degrevlex
368
+ else :
369
+ raise ValueError (" unimplemented term order %d " % ordering)
0 commit comments