@@ -576,103 +576,89 @@ cdef class fmpq_mpoly(flint_mpoly):
576576 def __rmod__ (self , other ):
577577 return divmod (other, self )[1 ]
578578
579- def __call__ (self , *args , **kwargs ):
579+ def __call__ (self , *args ) -> fmpq:
580+ cdef:
581+ fmpq_vec V
582+ fmpq vres
583+ slong nvars = self .ctx.nvars(), nargs = len (args)
584+
585+ if nargs < nvars:
586+ raise ValueError("Not enough arguments provided")
587+ elif nargs > nvars:
588+ raise ValueError("More arguments provided than variables")
589+
590+ args_fmpq = tuple (any_as_fmpq(v) for v in args)
591+ for arg in args_fmpq:
592+ if arg is NotImplemented:
593+ raise TypeError(f"Cannot coerce argument ('{arg}') to fmpq")
594+
595+ V = fmpq_vec(args_fmpq, double_indirect = True )
596+ vres = fmpq.__new__ (fmpq)
597+ if fmpq_mpoly_evaluate_all_fmpq(vres.val , self.val , V.double_indirect , self.ctx.val ) == 0:
598+ raise ValueError("Unreasonably large polynomial") # pragma: no cover
599+ return vres
600+
601+ def subs(self , dict_args ) -> fmpq_mpoly:
602+ """
603+ Partial evaluate this polynomial.
604+ """
580605 cdef:
581606 fmpq_mpoly res
582607 fmpq_mpoly res2
608+ slong i , nargs
609+
610+ partial_args = tuple ((i, dict_args[x]) for i, x in enumerate (self .ctx.names()) if x in dict_args)
611+ nargs = len (partial_args)
612+
613+ # If we've been provided with an invalid keyword arg then the length of our filter
614+ # args will be less than what we've been provided with.
615+ # If the length is equal to the number of variables then all arguments have been provided
616+ # otherwise we need to do partial application
617+ if nargs < len(dict_args ):
618+ raise ValueError (" Unknown keyword argument provided" )
619+
620+ args_fmpq = tuple (any_as_fmpq(v) for _, v in partial_args)
621+ for arg in args_fmpq:
622+ if arg is NotImplemented :
623+ raise TypeError (f" Cannot coerce argument ('{arg}') to fmpq" )
624+
625+ # Partial application with args in Z. We evaluate the polynomial one variable at a time
626+ res = create_fmpq_mpoly(self .ctx)
627+ res2 = create_fmpq_mpoly(self .ctx)
628+
629+ fmpq_mpoly_set(res2.val, self .val, self .ctx.val)
630+ for (i, _), arg in zip (partial_args, args_fmpq):
631+ if fmpq_mpoly_evaluate_one_fmpq(res.val, res2.val, i, (< fmpq> arg).val, self .ctx.val) == 0 :
632+ raise ValueError (" Unreasonably large polynomial" ) # pragma: no cover
633+ fmpq_mpoly_set(res2.val, res.val, self .ctx.val)
634+ return res
635+
636+ def compose (self , *args ) -> fmpq_mpoly:
637+ """
638+ Compose this polynomial with other fmpq_mpolys. Takes positional arguments.
639+ """
640+ cdef:
641+ fmpq_mpoly res
583642 fmpq_mpoly_ctx res_ctx
584- fmpq_vec V
585- fmpq vres
586643 fmpq_mpoly_vec C
587644 slong i , nvars = self .ctx.nvars(), nargs = len (args)
588645
589- if not args and not kwargs:
590- return self
591- elif args and kwargs:
592- raise ValueError (" only supply positional or keyword arguments" )
593- elif len (args) < nvars and not kwargs:
594- raise ValueError (" partial application requires keyword arguments" )
595-
596- if kwargs:
597- # Sort and filter the provided keyword args to only valid ones
598- partial_args = tuple ((i, kwargs[x]) for i, x in enumerate (self .ctx.names()) if x in kwargs)
599- nargs = len (partial_args)
600-
601- # If we've been provided with an invalid keyword arg then the length of our filter
602- # args will be less than what we've been provided with.
603- # If the length is equal to the number of variables then all arguments have been provided
604- # otherwise we need to do partial application
605- if nargs < len (kwargs):
606- raise ValueError (" unknown keyword argument provided" )
607- elif nargs == nvars:
608- args = tuple (arg for _, arg in partial_args)
609- kwargs = None
646+ if nargs < nvars:
647+ raise ValueError("Not enough arguments provided")
610648 elif nargs > nvars:
611- raise ValueError (" more arguments provided than variables" )
649+ raise ValueError("More arguments provided than variables")
650+ elif not all(typecheck(arg , fmpq_mpoly ) for arg in args ):
651+ raise TypeError (" All arguments must be fmpq_mpolys" )
612652
613- if kwargs:
614- args_fmpq = tuple (any_as_fmpq(v) for _, v in partial_args)
615- else :
616- args_fmpq = tuple (any_as_fmpq(v) for v in args)
617-
618- all_fmpq = NotImplemented not in args_fmpq
619-
620- if args and all_fmpq:
621- # Normal evaluation
622- V = fmpq_vec(args_fmpq, double_indirect = True )
623- vres = fmpq.__new__ (fmpq)
624- if fmpq_mpoly_evaluate_all_fmpq(vres.val, self .val, V.double_indirect, self .ctx.val) == 0 :
625- raise ValueError (" unreasonably large polynomial" ) # pragma: no cover
626- return vres
627- elif kwargs and all_fmpq:
628- # Partial application with args in Z. We evaluate the polynomial one variable at a time
629- res = create_fmpq_mpoly(self .ctx)
630- res2 = create_fmpq_mpoly(self .ctx)
653+ res_ctx = (< fmpq_mpoly> args[0 ]).ctx
654+ if not all ((< fmpq_mpoly> args[i]).ctx is res_ctx for i in range (1 , len (args))):
655+ raise IncompatibleContextError(" All arguments must share the same context" )
631656
632- fmpq_mpoly_set(res2.val, self .val, self .ctx.val)
633- for (i, _), arg in zip (partial_args, args_fmpq):
634- if fmpq_mpoly_evaluate_one_fmpq(res.val, res2.val, i, (< fmpq> arg).val, self .ctx.val) == 0 :
635- raise ValueError (" unreasonably large polynomial" ) # pragma: no cover
636- fmpq_mpoly_set(res2.val, res.val, self .ctx.val)
637- return res
638- elif args and not all_fmpq:
639- # Complete function composition
640- if not all (typecheck(arg, fmpq_mpoly) for arg in args):
641- raise TypeError (" all arguments must be fmpq_mpolys" )
642- res_ctx = (< fmpq_mpoly> args[0 ]).ctx
643- if not all ((< fmpq_mpoly> args[i]).ctx is res_ctx for i in range (1 , len (args))):
644- raise IncompatibleContextError(" all arguments must share the same context" )
645-
646- C = fmpq_mpoly_vec(args, self .ctx, double_indirect = True )
647- res = create_fmpq_mpoly(self .ctx)
648- if fmpq_mpoly_compose_fmpq_mpoly(res.val, self .val, C.double_indirect, self .ctx.val, res_ctx.val) == 0 :
649- raise ValueError (" unreasonably large polynomial" ) # pragma: no cover
650- return res
651- else :
652- # Partial function composition. We do this by composing with all arguments, however the ones
653- # that have not be provided are set to the trivial monomial. This is why we require all the
654- # polynomial itself, and all arguments exist in the same context, otherwise we have no way of
655- # finding the correct monomial to use.
656- if not all (typecheck(arg, fmpq_mpoly) for _, arg in partial_args):
657- raise TypeError (" all arguments must be fmpq_mpolys for partial composition" )
658- if not all ((< fmpq_mpoly> arg).ctx is self .ctx for _, arg in partial_args):
659- raise IncompatibleContextError(" all arguments must share the same context" )
660-
661- polys = [None ] * nvars
662- for i, poly in partial_args:
663- polys[i] = poly
664-
665- for i in range (nvars):
666- if polys[i] is None :
667- vec = [0 ] * nvars
668- vec[i] = 1
669- polys[i] = self .ctx.from_dict({tuple (vec): 1 })
670-
671- C = fmpq_mpoly_vec(polys, self .ctx, double_indirect = True )
672- res = create_fmpq_mpoly(self .ctx)
673- if fmpq_mpoly_compose_fmpq_mpoly(res.val, self .val, C.double_indirect, self .ctx.val, self .ctx.val) == 0 :
674- raise ValueError (" unreasonably large polynomial" ) # pragma: no cover
675- return res
657+ C = fmpq_mpoly_vec(args, self .ctx, double_indirect = True )
658+ res = create_fmpq_mpoly(self .ctx)
659+ if fmpq_mpoly_compose_fmpq_mpoly(res.val, self .val, C.double_indirect, self .ctx.val, res_ctx.val) == 0 :
660+ raise ValueError (" Unreasonably large polynomial" ) # pragma: no cover
661+ return res
676662
677663 def context (self ):
678664 """
0 commit comments