-
Notifications
You must be signed in to change notification settings - Fork 32
Refactor fmpz_mpoly and fmpq_mpoly arithmatic functions #190
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -94,6 +94,22 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): | |
fmpq_mpoly_ctx_init(self.val, nvars, ordering_py_to_c(ordering)) | ||
super().__init__(nvars, names) | ||
|
||
def any_as_scalar(self, other): | ||
if isinstance(other, int): | ||
return any_as_fmpq(other) | ||
elif typecheck(other, fmpz): | ||
return any_as_fmpq(other) | ||
elif typecheck(other, fmpq): | ||
res = fmpq.__new__(fmpq) | ||
fmpq_set((<fmpq>res).val, (<fmpq>other).val) | ||
return res | ||
else: | ||
return NotImplemented | ||
|
||
def scalar_as_mpoly(self, other: fmpq): | ||
# non-fmpq scalars should first be converted via self.any_as_scalar | ||
return self.constant(<fmpq>other) | ||
|
||
def nvars(self): | ||
""" | ||
Return the number of variables in the context | ||
|
@@ -336,205 +352,77 @@ cdef class fmpq_mpoly(flint_mpoly): | |
fmpq_mpoly_neg(res.val, (<fmpq_mpoly>self).val, res.ctx.val) | ||
return res | ||
|
||
def __add__(self, other): | ||
cdef fmpq_mpoly res | ||
if typecheck(other, fmpq_mpoly): | ||
if (<fmpq_mpoly>self).ctx is not (<fmpq_mpoly>other).ctx: | ||
raise IncompatibleContextError(f"{(<fmpq_mpoly>self).ctx} is not {(<fmpq_mpoly>other).ctx}") | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_add(res.val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, res.ctx.val) | ||
return res | ||
else: | ||
other = any_as_fmpq(other) | ||
if other is not NotImplemented: | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_add_fmpq(res.val, (<fmpq_mpoly>self).val, (<fmpq>other).val, res.ctx.val) | ||
return res | ||
return NotImplemented | ||
|
||
def __radd__(self, other): | ||
cdef fmpq_mpoly res | ||
other = any_as_fmpq(other) | ||
if other is not NotImplemented: | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_add_fmpq(res.val, (<fmpq_mpoly>self).val, (<fmpq>other).val, res.ctx.val) | ||
return res | ||
return NotImplemented | ||
|
||
def __sub__(self, other): | ||
cdef fmpq_mpoly res | ||
if typecheck(other, fmpq_mpoly): | ||
if (<fmpq_mpoly>self).ctx is not (<fmpq_mpoly>other).ctx: | ||
raise IncompatibleContextError(f"{(<fmpq_mpoly>self).ctx} is not {(<fmpq_mpoly>other).ctx}") | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_sub(res.val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, res.ctx.val) | ||
return res | ||
else: | ||
other = any_as_fmpq(other) | ||
if other is not NotImplemented: | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_sub_fmpq(res.val, (<fmpq_mpoly>self).val, (<fmpq>other).val, res.ctx.val) | ||
return res | ||
return NotImplemented | ||
|
||
def __rsub__(self, other): | ||
cdef fmpq_mpoly res | ||
other = any_as_fmpq(other) | ||
if other is not NotImplemented: | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_sub_fmpq(res.val, (<fmpq_mpoly>self).val, (<fmpq>other).val, res.ctx.val) | ||
return -res | ||
return NotImplemented | ||
|
||
def __mul__(self, other): | ||
cdef fmpq_mpoly res | ||
if typecheck(other, fmpq_mpoly): | ||
if (<fmpq_mpoly>self).ctx is not (<fmpq_mpoly>other).ctx: | ||
raise IncompatibleContextError(f"{(<fmpq_mpoly>self).ctx} is not {(<fmpq_mpoly>other).ctx}") | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_mul(res.val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, res.ctx.val) | ||
return res | ||
else: | ||
other = any_as_fmpq(other) | ||
if other is not NotImplemented: | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_scalar_mul_fmpq(res.val, (<fmpq_mpoly>self).val, (<fmpq>other).val, res.ctx.val) | ||
return res | ||
return NotImplemented | ||
|
||
def __rmul__(self, other): | ||
def _add_scalar_(self, other: fmpq): | ||
cdef fmpq_mpoly res | ||
other = any_as_fmpq(other) | ||
if other is not NotImplemented: | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_scalar_mul_fmpq(res.val, (<fmpq_mpoly>self).val, (<fmpq>other).val, res.ctx.val) | ||
return res | ||
return NotImplemented | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_add_fmpq(res.val, self.val, other.val, self.ctx.val) | ||
return res | ||
|
||
def __pow__(self, other, modulus): | ||
def _add_mpoly_(self, other: fmpq_mpoly): | ||
cdef fmpq_mpoly res | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe these methods should all be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've just tested this locally and it's possible, just needed a little massaging due to C-methods not being visible at runtime. The non-commutative operands can't use runtime reflection after coercion anymore, it needs a explicit C reflected function e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually should just be able to use the normal dunder methods after the coercion rather than have a reflected cdef. Though using the cdefs all the way down is probably slightly faster. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
if modulus is not None: | ||
raise NotImplementedError | ||
other = any_as_fmpz(other) | ||
if other is NotImplemented: | ||
return other | ||
if other < 0: | ||
raise ValueError("cannot raise fmpq_mpoly to negative power") | ||
res = create_fmpq_mpoly(self.ctx) | ||
if fmpq_mpoly_pow_fmpz(res.val, (<fmpq_mpoly>self).val, (<fmpz>other).val, res.ctx.val) == 0: | ||
raise ValueError("unreasonably large polynomial") # pragma: no cover | ||
fmpq_mpoly_add(res.val, self.val, other.val, res.ctx.val) | ||
return res | ||
|
||
def __divmod__(self, other): | ||
cdef fmpq_mpoly res, res2 | ||
if typecheck(other, fmpq_mpoly): | ||
if not other: | ||
raise ZeroDivisionError("fmpq_mpoly division by zero") | ||
elif (<fmpq_mpoly>self).ctx is not (<fmpq_mpoly>other).ctx: | ||
raise IncompatibleContextError(f"{(<fmpq_mpoly>self).ctx} is not {(<fmpq_mpoly>other).ctx}") | ||
res = create_fmpq_mpoly(self.ctx) | ||
res2 = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_divrem(res.val, res2.val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, res.ctx.val) | ||
return (res, res2) | ||
else: | ||
other = any_as_fmpq(other) | ||
if other is not NotImplemented: | ||
other = fmpq_mpoly(other, self.ctx) | ||
if not other: | ||
raise ZeroDivisionError("fmpq_mpoly division by zero") | ||
res = create_fmpq_mpoly(self.ctx) | ||
res2 = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_divrem(res.val, res2.val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, res.ctx.val) | ||
return (res, res2) | ||
return NotImplemented | ||
|
||
def __rdivmod__(self, other): | ||
cdef fmpq_mpoly res, res2 | ||
if not self: | ||
raise ZeroDivisionError("fmpq_mpoly division by zero") | ||
other = any_as_fmpq(other) | ||
if other is not NotImplemented: | ||
other = fmpq_mpoly(other, self.ctx) | ||
res = create_fmpq_mpoly(self.ctx) | ||
res2 = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_divrem(res.val, res2.val, (<fmpq_mpoly>other).val, (<fmpq_mpoly>self).val, res.ctx.val) | ||
return (res, res2) | ||
return NotImplemented | ||
|
||
def __floordiv__(self, other): | ||
def _sub_scalar_(self, other: fmpq): | ||
cdef fmpq_mpoly res | ||
if typecheck(other, fmpq_mpoly): | ||
if not other: | ||
raise ZeroDivisionError("fmpq_mpoly division by zero") | ||
elif (<fmpq_mpoly>self).ctx is not (<fmpq_mpoly>other).ctx: | ||
raise IncompatibleContextError(f"{(<fmpq_mpoly>self).ctx} is not {(<fmpq_mpoly>other).ctx}") | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_div(res.val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, res.ctx.val) | ||
return res | ||
else: | ||
other = any_as_fmpq(other) | ||
if other is not NotImplemented: | ||
if not other: | ||
raise ZeroDivisionError("fmpq_mpoly division by zero") | ||
other = fmpq_mpoly(other, self.ctx) | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_div(res.val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, res.ctx.val) | ||
return res | ||
return NotImplemented | ||
|
||
def __rfloordiv__(self, other): | ||
cdef fmpq_mpoly res | ||
if not self: | ||
raise ZeroDivisionError("fmpq_mpoly division by zero") | ||
other = any_as_fmpq(other) | ||
if other is not NotImplemented: | ||
other = fmpq_mpoly(other, self.ctx) | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_div(res.val, (<fmpq_mpoly>other).val, self.val, res.ctx.val) | ||
return res | ||
return NotImplemented | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_sub_fmpq(res.val, self.val, other.val, self.ctx.val) | ||
return res | ||
|
||
def __truediv__(self, other): | ||
cdef: | ||
fmpq_mpoly res | ||
def _sub_mpoly_(self, other: fmpq_mpoly): | ||
cdef fmpq_mpoly res | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_sub(res.val, self.val, other.val, res.ctx.val) | ||
return res | ||
|
||
if typecheck(other, fmpq_mpoly): | ||
if not other: | ||
raise ZeroDivisionError("fmpq_mpoly division by zero") | ||
elif self.ctx is not (<fmpq_mpoly>other).ctx: | ||
raise IncompatibleContextError(f"{self.ctx} is not {(<fmpq_mpoly>other).ctx}") | ||
def _mul_scalar_(self, other: fmpq): | ||
cdef fmpq_mpoly res | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_scalar_mul_fmpq(res.val, self.val, other.val, self.ctx.val) | ||
return res | ||
|
||
res = create_fmpq_mpoly(self.ctx) | ||
if fmpq_mpoly_divides(res.val, self.val, (<fmpq_mpoly>other).val, self.ctx.val): | ||
return res | ||
else: | ||
raise DomainError("fmpq_mpoly division is not exact") | ||
else: | ||
o = any_as_fmpq(other) | ||
if o is NotImplemented: | ||
return NotImplemented | ||
elif not o: | ||
raise ZeroDivisionError("fmpq_mpoly division by zero") | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_scalar_div_fmpq(res.val, self.val, (<fmpq>o).val, self.ctx.val) | ||
return res | ||
def _mul_mpoly_(self, other: fmpq_mpoly): | ||
cdef fmpq_mpoly res | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_mul(res.val, self.val, other.val, res.ctx.val) | ||
return res | ||
|
||
def __rtruediv__(self, other): | ||
def _pow_(self, other: fmpz): | ||
cdef fmpq_mpoly res | ||
if not self: | ||
raise ZeroDivisionError("fmpq_mpoly division by zero") | ||
o = any_as_fmpq(other) | ||
if o is NotImplemented: | ||
return NotImplemented | ||
res = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_set_fmpq(res.val, (<fmpq>o).val, self.ctx.val) | ||
return res / self | ||
if fmpq_mpoly_pow_fmpz(res.val, self.val, other.val, res.ctx.val) == 0: | ||
raise ValueError("unreasonably large polynomial") # pragma: no cover | ||
return res | ||
|
||
def __mod__(self, other): | ||
return divmod(self, other)[1] | ||
def _divmod_mpoly_(self, other: fmpq_mpoly): | ||
cdef fmpq_mpoly quotient, remainder | ||
quotient = create_fmpq_mpoly(self.ctx) | ||
remainder = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_divrem(quotient.val, remainder.val, self.val, other.val, self.ctx.val) | ||
return (quotient, remainder) | ||
|
||
def _floordiv_mpoly_(self, other: fmpq_mpoly): | ||
cdef fmpq_mpoly quotient | ||
quotient = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_div(quotient.val, self.val, other.val, self.ctx.val) | ||
return quotient | ||
|
||
def _truediv_mpoly_(self, other: fmpq_mpoly): | ||
cdef fmpq_mpoly quotient | ||
quotient = create_fmpq_mpoly(self.ctx) | ||
if fmpq_mpoly_divides(quotient.val, self.val, other.val, self.ctx.val): | ||
return quotient | ||
else: | ||
raise DomainError("fmpq_mpoly division is not exact") | ||
|
||
def __rmod__(self, other): | ||
return divmod(other, self)[1] | ||
def _mod_mpoly_(self, other: fmpq_mpoly): | ||
cdef fmpq_mpoly quotient, remainder | ||
quotient = create_fmpq_mpoly(self.ctx) | ||
remainder = create_fmpq_mpoly(self.ctx) | ||
fmpq_mpoly_divrem(quotient.val, remainder.val, self.val, other.val, self.ctx.val) | ||
return remainder | ||
|
||
def __call__(self, *args) -> fmpq: | ||
cdef: | ||
|
@@ -551,83 +439,23 @@ cdef class fmpq_mpoly(flint_mpoly): | |
raise ValueError("unreasonably large polynomial") # pragma: no cover | ||
return vres | ||
|
||
def iadd(self, other): | ||
""" | ||
In-place addition, mutates self. | ||
def _iadd_scalar_(self, other: fmpq): | ||
fmpq_mpoly_add_fmpq(self.val, self.val, other.val, self.ctx.val) | ||
|
||
>>> from flint import Ordering | ||
>>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') | ||
>>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) | ||
>>> f | ||
4*x0*x1 + 2*x0 + 3*x1 | ||
>>> f.iadd(5) | ||
>>> f | ||
4*x0*x1 + 2*x0 + 3*x1 + 5 | ||
def _iadd_mpoly_(self, other: fmpq_mpoly): | ||
fmpq_mpoly_add(self.val, self.val, other.val, self.ctx.val) | ||
|
||
""" | ||
if typecheck(other, fmpq_mpoly): | ||
if (<fmpq_mpoly>self).ctx is not (<fmpq_mpoly>other).ctx: | ||
raise IncompatibleContextError(f"{(<fmpq_mpoly>self).ctx} is not {(<fmpq_mpoly>other).ctx}") | ||
fmpq_mpoly_add((<fmpq_mpoly>self).val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, self.ctx.val) | ||
return | ||
else: | ||
other = any_as_fmpq(other) | ||
if other is not NotImplemented: | ||
fmpq_mpoly_add_fmpq((<fmpq_mpoly>self).val, (<fmpq_mpoly>self).val, (<fmpq>other).val, self.ctx.val) | ||
return | ||
raise NotImplementedError(f"in-place addition not implemented between {type(self)} and {type(other)}") | ||
|
||
def isub(self, other): | ||
""" | ||
In-place subtraction, mutates self. | ||
|
||
>>> from flint import Ordering | ||
>>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') | ||
>>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) | ||
>>> f | ||
4*x0*x1 + 2*x0 + 3*x1 | ||
>>> f.isub(5) | ||
>>> f | ||
4*x0*x1 + 2*x0 + 3*x1 - 5 | ||
|
||
""" | ||
if typecheck(other, fmpq_mpoly): | ||
if (<fmpq_mpoly>self).ctx is not (<fmpq_mpoly>other).ctx: | ||
raise IncompatibleContextError(f"{(<fmpq_mpoly>self).ctx} is not {(<fmpq_mpoly>other).ctx}") | ||
fmpq_mpoly_sub((<fmpq_mpoly>self).val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, self.ctx.val) | ||
return | ||
else: | ||
other = any_as_fmpq(other) | ||
if other is not NotImplemented: | ||
fmpq_mpoly_sub_fmpq((<fmpq_mpoly>self).val, (<fmpq_mpoly>self).val, (<fmpq>other).val, self.ctx.val) | ||
return | ||
raise NotImplementedError(f"in-place subtraction not implemented between {type(self)} and {type(other)}") | ||
def _isub_scalar_(self, other: fmpq): | ||
fmpq_mpoly_sub_fmpq(self.val, self.val, other.val, self.ctx.val) | ||
|
||
def imul(self, other): | ||
""" | ||
In-place multiplication, mutates self. | ||
def _isub_mpoly_(self, other: fmpq_mpoly): | ||
fmpq_mpoly_sub(self.val, self.val, other.val, self.ctx.val) | ||
|
||
>>> from flint import Ordering | ||
>>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') | ||
>>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) | ||
>>> f | ||
4*x0*x1 + 2*x0 + 3*x1 | ||
>>> f.imul(2) | ||
>>> f | ||
8*x0*x1 + 4*x0 + 6*x1 | ||
def _imul_scalar_(self, other: fmpq): | ||
fmpq_mpoly_scalar_mul_fmpq(self.val, self.val, other.val, self.ctx.val) | ||
|
||
""" | ||
if typecheck(other, fmpq_mpoly): | ||
if (<fmpq_mpoly>self).ctx is not (<fmpq_mpoly>other).ctx: | ||
raise IncompatibleContextError(f"{(<fmpq_mpoly>self).ctx} is not {(<fmpq_mpoly>other).ctx}") | ||
fmpq_mpoly_mul((<fmpq_mpoly>self).val, (<fmpq_mpoly>self).val, (<fmpq_mpoly>other).val, self.ctx.val) | ||
return | ||
else: | ||
other = any_as_fmpq(other) | ||
if other is not NotImplemented: | ||
fmpq_mpoly_scalar_mul_fmpq(self.val, (<fmpq_mpoly>self).val, (<fmpq>other).val, self.ctx.val) | ||
return | ||
raise NotImplementedError(f"in-place multiplication not implemented between {type(self)} and {type(other)}") | ||
def _imul_mpoly_(self, other: fmpq_mpoly): | ||
fmpq_mpoly_mul(self.val, self.val, other.val, self.ctx.val) | ||
|
||
def monoms(self): | ||
""" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it is an
fmpq
already can we not just return it?Why does a copy need to be made?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought it best to be consistent with the other
any_as_*
methods, but since they are immutable on apython-flint
level perhaps we can just return the same object.any_as_fmpq
is just not used there because no conversion is required(With
any_as_fmpz
modified to be acpdef
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe
any_as_fmpz
should be changed to not make a copy. I'm not sure that is compatible with how it is currently used though like whether the object returned is ever mutated.