Skip to content

Commit 162ded1

Browse files
Merge pull request #190 from Jake-Moss/mpoly_update
Refactor fmpz_mpoly and fmpq_mpoly arithmatic functions
2 parents f2bda3c + 5f80eff commit 162ded1

File tree

4 files changed

+166
-513
lines changed

4 files changed

+166
-513
lines changed

src/flint/types/fmpq_mpoly.pyx

+83-255
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,22 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context):
9494
fmpq_mpoly_ctx_init(self.val, nvars, ordering_py_to_c(ordering))
9595
super().__init__(nvars, names)
9696

97+
def any_as_scalar(self, other):
98+
if isinstance(other, int):
99+
return any_as_fmpq(other)
100+
elif typecheck(other, fmpz):
101+
return any_as_fmpq(other)
102+
elif typecheck(other, fmpq):
103+
res = fmpq.__new__(fmpq)
104+
fmpq_set((<fmpq>res).val, (<fmpq>other).val)
105+
return res
106+
else:
107+
return NotImplemented
108+
109+
def scalar_as_mpoly(self, other: fmpq):
110+
# non-fmpq scalars should first be converted via self.any_as_scalar
111+
return self.constant(<fmpq>other)
112+
97113
def nvars(self):
98114
"""
99115
Return the number of variables in the context
@@ -336,205 +352,77 @@ cdef class fmpq_mpoly(flint_mpoly):
336352
fmpq_mpoly_neg(res.val, (<fmpq_mpoly>self).val, res.ctx.val)
337353
return res
338354

339-
def __add__(self, other):
340-
cdef fmpq_mpoly res
341-
if typecheck(other, fmpq_mpoly):
342-
if (<fmpq_mpoly>self).ctx is not (<fmpq_mpoly>other).ctx:
343-
raise IncompatibleContextError(f"{(<fmpq_mpoly>self).ctx} is not {(<fmpq_mpoly>other).ctx}")
344-
res = create_fmpq_mpoly(self.ctx)
345-
fmpq_mpoly_add(res.val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, res.ctx.val)
346-
return res
347-
else:
348-
other = any_as_fmpq(other)
349-
if other is not NotImplemented:
350-
res = create_fmpq_mpoly(self.ctx)
351-
fmpq_mpoly_add_fmpq(res.val, (<fmpq_mpoly>self).val, (<fmpq>other).val, res.ctx.val)
352-
return res
353-
return NotImplemented
354-
355-
def __radd__(self, other):
356-
cdef fmpq_mpoly res
357-
other = any_as_fmpq(other)
358-
if other is not NotImplemented:
359-
res = create_fmpq_mpoly(self.ctx)
360-
fmpq_mpoly_add_fmpq(res.val, (<fmpq_mpoly>self).val, (<fmpq>other).val, res.ctx.val)
361-
return res
362-
return NotImplemented
363355

364-
def __sub__(self, other):
365-
cdef fmpq_mpoly res
366-
if typecheck(other, fmpq_mpoly):
367-
if (<fmpq_mpoly>self).ctx is not (<fmpq_mpoly>other).ctx:
368-
raise IncompatibleContextError(f"{(<fmpq_mpoly>self).ctx} is not {(<fmpq_mpoly>other).ctx}")
369-
res = create_fmpq_mpoly(self.ctx)
370-
fmpq_mpoly_sub(res.val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, res.ctx.val)
371-
return res
372-
else:
373-
other = any_as_fmpq(other)
374-
if other is not NotImplemented:
375-
res = create_fmpq_mpoly(self.ctx)
376-
fmpq_mpoly_sub_fmpq(res.val, (<fmpq_mpoly>self).val, (<fmpq>other).val, res.ctx.val)
377-
return res
378-
return NotImplemented
379-
380-
def __rsub__(self, other):
381-
cdef fmpq_mpoly res
382-
other = any_as_fmpq(other)
383-
if other is not NotImplemented:
384-
res = create_fmpq_mpoly(self.ctx)
385-
fmpq_mpoly_sub_fmpq(res.val, (<fmpq_mpoly>self).val, (<fmpq>other).val, res.ctx.val)
386-
return -res
387-
return NotImplemented
388-
389-
def __mul__(self, other):
390-
cdef fmpq_mpoly res
391-
if typecheck(other, fmpq_mpoly):
392-
if (<fmpq_mpoly>self).ctx is not (<fmpq_mpoly>other).ctx:
393-
raise IncompatibleContextError(f"{(<fmpq_mpoly>self).ctx} is not {(<fmpq_mpoly>other).ctx}")
394-
res = create_fmpq_mpoly(self.ctx)
395-
fmpq_mpoly_mul(res.val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, res.ctx.val)
396-
return res
397-
else:
398-
other = any_as_fmpq(other)
399-
if other is not NotImplemented:
400-
res = create_fmpq_mpoly(self.ctx)
401-
fmpq_mpoly_scalar_mul_fmpq(res.val, (<fmpq_mpoly>self).val, (<fmpq>other).val, res.ctx.val)
402-
return res
403-
return NotImplemented
404-
405-
def __rmul__(self, other):
356+
def _add_scalar_(self, other: fmpq):
406357
cdef fmpq_mpoly res
407-
other = any_as_fmpq(other)
408-
if other is not NotImplemented:
409-
res = create_fmpq_mpoly(self.ctx)
410-
fmpq_mpoly_scalar_mul_fmpq(res.val, (<fmpq_mpoly>self).val, (<fmpq>other).val, res.ctx.val)
411-
return res
412-
return NotImplemented
358+
res = create_fmpq_mpoly(self.ctx)
359+
fmpq_mpoly_add_fmpq(res.val, self.val, other.val, self.ctx.val)
360+
return res
413361

414-
def __pow__(self, other, modulus):
362+
def _add_mpoly_(self, other: fmpq_mpoly):
415363
cdef fmpq_mpoly res
416-
if modulus is not None:
417-
raise NotImplementedError
418-
other = any_as_fmpz(other)
419-
if other is NotImplemented:
420-
return other
421-
if other < 0:
422-
raise ValueError("cannot raise fmpq_mpoly to negative power")
423364
res = create_fmpq_mpoly(self.ctx)
424-
if fmpq_mpoly_pow_fmpz(res.val, (<fmpq_mpoly>self).val, (<fmpz>other).val, res.ctx.val) == 0:
425-
raise ValueError("unreasonably large polynomial") # pragma: no cover
365+
fmpq_mpoly_add(res.val, self.val, other.val, res.ctx.val)
426366
return res
427367

428-
def __divmod__(self, other):
429-
cdef fmpq_mpoly res, res2
430-
if typecheck(other, fmpq_mpoly):
431-
if not other:
432-
raise ZeroDivisionError("fmpq_mpoly division by zero")
433-
elif (<fmpq_mpoly>self).ctx is not (<fmpq_mpoly>other).ctx:
434-
raise IncompatibleContextError(f"{(<fmpq_mpoly>self).ctx} is not {(<fmpq_mpoly>other).ctx}")
435-
res = create_fmpq_mpoly(self.ctx)
436-
res2 = create_fmpq_mpoly(self.ctx)
437-
fmpq_mpoly_divrem(res.val, res2.val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, res.ctx.val)
438-
return (res, res2)
439-
else:
440-
other = any_as_fmpq(other)
441-
if other is not NotImplemented:
442-
other = fmpq_mpoly(other, self.ctx)
443-
if not other:
444-
raise ZeroDivisionError("fmpq_mpoly division by zero")
445-
res = create_fmpq_mpoly(self.ctx)
446-
res2 = create_fmpq_mpoly(self.ctx)
447-
fmpq_mpoly_divrem(res.val, res2.val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, res.ctx.val)
448-
return (res, res2)
449-
return NotImplemented
450-
451-
def __rdivmod__(self, other):
452-
cdef fmpq_mpoly res, res2
453-
if not self:
454-
raise ZeroDivisionError("fmpq_mpoly division by zero")
455-
other = any_as_fmpq(other)
456-
if other is not NotImplemented:
457-
other = fmpq_mpoly(other, self.ctx)
458-
res = create_fmpq_mpoly(self.ctx)
459-
res2 = create_fmpq_mpoly(self.ctx)
460-
fmpq_mpoly_divrem(res.val, res2.val, (<fmpq_mpoly>other).val, (<fmpq_mpoly>self).val, res.ctx.val)
461-
return (res, res2)
462-
return NotImplemented
463-
464-
def __floordiv__(self, other):
368+
def _sub_scalar_(self, other: fmpq):
465369
cdef fmpq_mpoly res
466-
if typecheck(other, fmpq_mpoly):
467-
if not other:
468-
raise ZeroDivisionError("fmpq_mpoly division by zero")
469-
elif (<fmpq_mpoly>self).ctx is not (<fmpq_mpoly>other).ctx:
470-
raise IncompatibleContextError(f"{(<fmpq_mpoly>self).ctx} is not {(<fmpq_mpoly>other).ctx}")
471-
res = create_fmpq_mpoly(self.ctx)
472-
fmpq_mpoly_div(res.val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, res.ctx.val)
473-
return res
474-
else:
475-
other = any_as_fmpq(other)
476-
if other is not NotImplemented:
477-
if not other:
478-
raise ZeroDivisionError("fmpq_mpoly division by zero")
479-
other = fmpq_mpoly(other, self.ctx)
480-
res = create_fmpq_mpoly(self.ctx)
481-
fmpq_mpoly_div(res.val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, res.ctx.val)
482-
return res
483-
return NotImplemented
484-
485-
def __rfloordiv__(self, other):
486-
cdef fmpq_mpoly res
487-
if not self:
488-
raise ZeroDivisionError("fmpq_mpoly division by zero")
489-
other = any_as_fmpq(other)
490-
if other is not NotImplemented:
491-
other = fmpq_mpoly(other, self.ctx)
492-
res = create_fmpq_mpoly(self.ctx)
493-
fmpq_mpoly_div(res.val, (<fmpq_mpoly>other).val, self.val, res.ctx.val)
494-
return res
495-
return NotImplemented
370+
res = create_fmpq_mpoly(self.ctx)
371+
fmpq_mpoly_sub_fmpq(res.val, self.val, other.val, self.ctx.val)
372+
return res
496373

497-
def __truediv__(self, other):
498-
cdef:
499-
fmpq_mpoly res
374+
def _sub_mpoly_(self, other: fmpq_mpoly):
375+
cdef fmpq_mpoly res
376+
res = create_fmpq_mpoly(self.ctx)
377+
fmpq_mpoly_sub(res.val, self.val, other.val, res.ctx.val)
378+
return res
500379

501-
if typecheck(other, fmpq_mpoly):
502-
if not other:
503-
raise ZeroDivisionError("fmpq_mpoly division by zero")
504-
elif self.ctx is not (<fmpq_mpoly>other).ctx:
505-
raise IncompatibleContextError(f"{self.ctx} is not {(<fmpq_mpoly>other).ctx}")
380+
def _mul_scalar_(self, other: fmpq):
381+
cdef fmpq_mpoly res
382+
res = create_fmpq_mpoly(self.ctx)
383+
fmpq_mpoly_scalar_mul_fmpq(res.val, self.val, other.val, self.ctx.val)
384+
return res
506385

507-
res = create_fmpq_mpoly(self.ctx)
508-
if fmpq_mpoly_divides(res.val, self.val, (<fmpq_mpoly>other).val, self.ctx.val):
509-
return res
510-
else:
511-
raise DomainError("fmpq_mpoly division is not exact")
512-
else:
513-
o = any_as_fmpq(other)
514-
if o is NotImplemented:
515-
return NotImplemented
516-
elif not o:
517-
raise ZeroDivisionError("fmpq_mpoly division by zero")
518-
res = create_fmpq_mpoly(self.ctx)
519-
fmpq_mpoly_scalar_div_fmpq(res.val, self.val, (<fmpq>o).val, self.ctx.val)
520-
return res
386+
def _mul_mpoly_(self, other: fmpq_mpoly):
387+
cdef fmpq_mpoly res
388+
res = create_fmpq_mpoly(self.ctx)
389+
fmpq_mpoly_mul(res.val, self.val, other.val, res.ctx.val)
390+
return res
521391

522-
def __rtruediv__(self, other):
392+
def _pow_(self, other: fmpz):
523393
cdef fmpq_mpoly res
524-
if not self:
525-
raise ZeroDivisionError("fmpq_mpoly division by zero")
526-
o = any_as_fmpq(other)
527-
if o is NotImplemented:
528-
return NotImplemented
529394
res = create_fmpq_mpoly(self.ctx)
530-
fmpq_mpoly_set_fmpq(res.val, (<fmpq>o).val, self.ctx.val)
531-
return res / self
395+
if fmpq_mpoly_pow_fmpz(res.val, self.val, other.val, res.ctx.val) == 0:
396+
raise ValueError("unreasonably large polynomial") # pragma: no cover
397+
return res
532398

533-
def __mod__(self, other):
534-
return divmod(self, other)[1]
399+
def _divmod_mpoly_(self, other: fmpq_mpoly):
400+
cdef fmpq_mpoly quotient, remainder
401+
quotient = create_fmpq_mpoly(self.ctx)
402+
remainder = create_fmpq_mpoly(self.ctx)
403+
fmpq_mpoly_divrem(quotient.val, remainder.val, self.val, other.val, self.ctx.val)
404+
return (quotient, remainder)
405+
406+
def _floordiv_mpoly_(self, other: fmpq_mpoly):
407+
cdef fmpq_mpoly quotient
408+
quotient = create_fmpq_mpoly(self.ctx)
409+
fmpq_mpoly_div(quotient.val, self.val, other.val, self.ctx.val)
410+
return quotient
411+
412+
def _truediv_mpoly_(self, other: fmpq_mpoly):
413+
cdef fmpq_mpoly quotient
414+
quotient = create_fmpq_mpoly(self.ctx)
415+
if fmpq_mpoly_divides(quotient.val, self.val, other.val, self.ctx.val):
416+
return quotient
417+
else:
418+
raise DomainError("fmpq_mpoly division is not exact")
535419

536-
def __rmod__(self, other):
537-
return divmod(other, self)[1]
420+
def _mod_mpoly_(self, other: fmpq_mpoly):
421+
cdef fmpq_mpoly quotient, remainder
422+
quotient = create_fmpq_mpoly(self.ctx)
423+
remainder = create_fmpq_mpoly(self.ctx)
424+
fmpq_mpoly_divrem(quotient.val, remainder.val, self.val, other.val, self.ctx.val)
425+
return remainder
538426

539427
def __call__(self, *args) -> fmpq:
540428
cdef:
@@ -551,83 +439,23 @@ cdef class fmpq_mpoly(flint_mpoly):
551439
raise ValueError("unreasonably large polynomial") # pragma: no cover
552440
return vres
553441

554-
def iadd(self, other):
555-
"""
556-
In-place addition, mutates self.
442+
def _iadd_scalar_(self, other: fmpq):
443+
fmpq_mpoly_add_fmpq(self.val, self.val, other.val, self.ctx.val)
557444

558-
>>> from flint import Ordering
559-
>>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x')
560-
>>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4})
561-
>>> f
562-
4*x0*x1 + 2*x0 + 3*x1
563-
>>> f.iadd(5)
564-
>>> f
565-
4*x0*x1 + 2*x0 + 3*x1 + 5
445+
def _iadd_mpoly_(self, other: fmpq_mpoly):
446+
fmpq_mpoly_add(self.val, self.val, other.val, self.ctx.val)
566447

567-
"""
568-
if typecheck(other, fmpq_mpoly):
569-
if (<fmpq_mpoly>self).ctx is not (<fmpq_mpoly>other).ctx:
570-
raise IncompatibleContextError(f"{(<fmpq_mpoly>self).ctx} is not {(<fmpq_mpoly>other).ctx}")
571-
fmpq_mpoly_add((<fmpq_mpoly>self).val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, self.ctx.val)
572-
return
573-
else:
574-
other = any_as_fmpq(other)
575-
if other is not NotImplemented:
576-
fmpq_mpoly_add_fmpq((<fmpq_mpoly>self).val, (<fmpq_mpoly>self).val, (<fmpq>other).val, self.ctx.val)
577-
return
578-
raise NotImplementedError(f"in-place addition not implemented between {type(self)} and {type(other)}")
579-
580-
def isub(self, other):
581-
"""
582-
In-place subtraction, mutates self.
583-
584-
>>> from flint import Ordering
585-
>>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x')
586-
>>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4})
587-
>>> f
588-
4*x0*x1 + 2*x0 + 3*x1
589-
>>> f.isub(5)
590-
>>> f
591-
4*x0*x1 + 2*x0 + 3*x1 - 5
592-
593-
"""
594-
if typecheck(other, fmpq_mpoly):
595-
if (<fmpq_mpoly>self).ctx is not (<fmpq_mpoly>other).ctx:
596-
raise IncompatibleContextError(f"{(<fmpq_mpoly>self).ctx} is not {(<fmpq_mpoly>other).ctx}")
597-
fmpq_mpoly_sub((<fmpq_mpoly>self).val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, self.ctx.val)
598-
return
599-
else:
600-
other = any_as_fmpq(other)
601-
if other is not NotImplemented:
602-
fmpq_mpoly_sub_fmpq((<fmpq_mpoly>self).val, (<fmpq_mpoly>self).val, (<fmpq>other).val, self.ctx.val)
603-
return
604-
raise NotImplementedError(f"in-place subtraction not implemented between {type(self)} and {type(other)}")
448+
def _isub_scalar_(self, other: fmpq):
449+
fmpq_mpoly_sub_fmpq(self.val, self.val, other.val, self.ctx.val)
605450

606-
def imul(self, other):
607-
"""
608-
In-place multiplication, mutates self.
451+
def _isub_mpoly_(self, other: fmpq_mpoly):
452+
fmpq_mpoly_sub(self.val, self.val, other.val, self.ctx.val)
609453

610-
>>> from flint import Ordering
611-
>>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x')
612-
>>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4})
613-
>>> f
614-
4*x0*x1 + 2*x0 + 3*x1
615-
>>> f.imul(2)
616-
>>> f
617-
8*x0*x1 + 4*x0 + 6*x1
454+
def _imul_scalar_(self, other: fmpq):
455+
fmpq_mpoly_scalar_mul_fmpq(self.val, self.val, other.val, self.ctx.val)
618456

619-
"""
620-
if typecheck(other, fmpq_mpoly):
621-
if (<fmpq_mpoly>self).ctx is not (<fmpq_mpoly>other).ctx:
622-
raise IncompatibleContextError(f"{(<fmpq_mpoly>self).ctx} is not {(<fmpq_mpoly>other).ctx}")
623-
fmpq_mpoly_mul((<fmpq_mpoly>self).val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, self.ctx.val)
624-
return
625-
else:
626-
other = any_as_fmpq(other)
627-
if other is not NotImplemented:
628-
fmpq_mpoly_scalar_mul_fmpq(self.val, (<fmpq_mpoly>self).val, (<fmpq>other).val, self.ctx.val)
629-
return
630-
raise NotImplementedError(f"in-place multiplication not implemented between {type(self)} and {type(other)}")
457+
def _imul_mpoly_(self, other: fmpq_mpoly):
458+
fmpq_mpoly_mul(self.val, self.val, other.val, self.ctx.val)
631459

632460
def monoms(self):
633461
"""

src/flint/types/fmpz_mod_mpoly.pyx

+4-2
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,9 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context):
137137
)
138138
return any_as_fmpz((<nmod>other).val)
139139
elif typecheck(other, fmpz):
140-
return fmpz(other)
140+
res = fmpz.__new__(fmpz)
141+
fmpz_set((<fmpz>res).val, (<fmpz>other).val)
142+
return res
141143
elif typecheck(other, fmpz_mod):
142144
if (<fmpz_mod>other).ctx.modulus() != self.modulus():
143145
raise DomainError(
@@ -150,7 +152,7 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context):
150152
return NotImplemented
151153

152154
def scalar_as_mpoly(self, other: fmpz):
153-
# non-fmpz scalars should first be converted via cls.any_as_scalar
155+
# non-fmpz scalars should first be converted via self.any_as_scalar
154156
return self.constant(<fmpz>other)
155157

156158
def nvars(self):

0 commit comments

Comments
 (0)