Skip to content

Commit 2ba6a29

Browse files
committed
Disallow positional partial application
This caused too many headaches during testing. Since the order of variables within a context is not stable during joint context construction, it's simply one giant walking gotcha.
1 parent 941041c commit 2ba6a29

File tree

1 file changed

+24
-21
lines changed

1 file changed

+24
-21
lines changed

src/flint/types/fmpz_mpoly.pyx

+24-21
Original file line numberDiff line numberDiff line change
@@ -680,17 +680,19 @@ cdef class fmpz_mpoly(flint_mpoly):
680680
fmpz vres
681681
fmpz_mpoly_struct ** C
682682
ulong *exponents
683-
slong i, prev_i, nvars = self.ctx.nvars(), nargs = len(args)
683+
slong i, nvars = self.ctx.nvars(), nargs = len(args)
684684

685-
if args and kwargs:
686-
raise ValueError("only supply positional or keyword arguments")
687-
elif not args and not kwargs:
685+
if not args and not kwargs:
688686
return self
687+
elif args and kwargs:
688+
raise ValueError("only supply positional or keyword arguments")
689+
elif len(args) < nvars and not kwargs:
690+
raise ValueError("partial application requires keyword arguments")
689691

690692
if kwargs:
691693
# Sort and filter the provided keyword args to only valid ones
692-
args = tuple((i, kwargs[x]) for i, x in enumerate(self.ctx.names()) if x in kwargs)
693-
nargs = len(args)
694+
partial_args = tuple((i, kwargs[x]) for i, x in enumerate(self.ctx.names()) if x in kwargs)
695+
nargs = len(partial_args)
694696

695697
# If we've been provided with an invalid keyword arg then the length of our filter
696698
# args will be less than what we've been provided with.
@@ -699,25 +701,20 @@ cdef class fmpz_mpoly(flint_mpoly):
699701
if nargs < len(kwargs):
700702
raise ValueError("unknown keyword argument provided")
701703
elif nargs == nvars:
702-
args = tuple(x for _, x in args)
703-
partial = False
704-
else:
705-
partial = True
704+
args = tuple(arg for _, arg in partial_args)
705+
kwargs = None
706706
elif nargs > nvars:
707707
raise ValueError("more arguments provided than variables")
708-
elif nargs < nvars:
709-
# Positional partial application
710-
args = tuple((i, x) for i, x in enumerate(args))
711-
partial = True
712708

713-
if partial:
714-
args_fmpz = tuple(any_as_fmpz(v) for _, v in args)
709+
if kwargs:
710+
args_fmpz = tuple(any_as_fmpz(v) for _, v in partial_args)
715711
else:
716712
args_fmpz = tuple(any_as_fmpz(v) for v in args)
717713

718714
all_fmpz = NotImplemented not in args_fmpz
719715

720-
if not partial and all_fmpz:
716+
if args and all_fmpz:
717+
# Normal evaluation
721718
try:
722719
V = <fmpz_struct **> libc.stdlib.malloc(nvars * sizeof(fmpz_struct *))
723720
for i in range(nvars):
@@ -728,19 +725,21 @@ cdef class fmpz_mpoly(flint_mpoly):
728725
return vres
729726
finally:
730727
libc.stdlib.free(V)
731-
elif partial and all_fmpz:
728+
elif kwargs and all_fmpz:
729+
# Partial application with args in Z. We evaluate the polynomial one variable at a time
732730
res = fmpz_mpoly.__new__(fmpz_mpoly)
733731
res2 = fmpz_mpoly.__new__(fmpz_mpoly)
734732
res.ctx = self.ctx
735733
res2.ctx = self.ctx
736734

737735
fmpz_mpoly_set(res2.val, self.val, self.ctx.val)
738-
for (i, _), arg in zip(args, args_fmpz):
736+
for (i, _), arg in zip(partial_args, args_fmpz):
739737
if fmpz_mpoly_evaluate_one_fmpz(res.val, res2.val, i, (<fmpz>arg).val, self.ctx.val) == 0:
740738
raise ValueError("unreasonably large polynomial")
741739
fmpz_mpoly_set(res2.val, res.val, self.ctx.val)
742740
return res
743-
elif not partial and not all_fmpz:
741+
elif args and not all_fmpz:
742+
# Complete function composition
744743
res_ctx = (<fmpz_mpoly> args[0]).ctx
745744
if not all(typecheck(args[i], fmpz_mpoly) and (<fmpz_mpoly> args[i]).ctx is res_ctx for i in range(1, len(args))):
746745
raise ValueError("all arguments must share the same context")
@@ -757,11 +756,15 @@ cdef class fmpz_mpoly(flint_mpoly):
757756
finally:
758757
libc.stdlib.free(C)
759758
else:
759+
# Partial function composition. We do this by composing with all arguments, however the ones
760+
# that have not be provided are set to the trivial monomial. This is why we require all the
761+
# polynomial itself, and all arguments exist in the same context, otherwise we have no way of
762+
# finding the correct monomial to use.
760763
if not all(typecheck(arg, fmpz_mpoly) and (<fmpz_mpoly> arg).ctx is self.ctx for _, arg in args):
761764
raise ValueError("the mpoly and all arguments must share the same context")
762765

763766
polys = [None] * nvars
764-
for i, poly in args:
767+
for i, poly in partial_args:
765768
polys[i] = poly
766769

767770
for i in range(nvars):

0 commit comments

Comments
 (0)