You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
789 lines
21 KiB
789 lines
21 KiB
5 months ago
|
"""Options manager for :class:`~.Poly` and public API functions. """
|
||
|
|
||
|
from __future__ import annotations
|
||
|
|
||
|
__all__ = ["Options"]
|
||
|
|
||
|
from sympy.core import Basic, sympify
|
||
|
from sympy.polys.polyerrors import GeneratorsError, OptionError, FlagError
|
||
|
from sympy.utilities import numbered_symbols, topological_sort, public
|
||
|
from sympy.utilities.iterables import has_dups, is_sequence
|
||
|
|
||
|
import sympy.polys
|
||
|
|
||
|
import re
|
||
|
|
||
|
class Option:
|
||
|
"""Base class for all kinds of options. """
|
||
|
|
||
|
option: str | None = None
|
||
|
|
||
|
is_Flag = False
|
||
|
|
||
|
requires: list[str] = []
|
||
|
excludes: list[str] = []
|
||
|
|
||
|
after: list[str] = []
|
||
|
before: list[str] = []
|
||
|
|
||
|
@classmethod
|
||
|
def default(cls):
|
||
|
return None
|
||
|
|
||
|
@classmethod
|
||
|
def preprocess(cls, option):
|
||
|
return None
|
||
|
|
||
|
@classmethod
|
||
|
def postprocess(cls, options):
|
||
|
pass
|
||
|
|
||
|
|
||
|
class Flag(Option):
|
||
|
"""Base class for all kinds of flags. """
|
||
|
|
||
|
is_Flag = True
|
||
|
|
||
|
|
||
|
class BooleanOption(Option):
|
||
|
"""An option that must have a boolean value or equivalent assigned. """
|
||
|
|
||
|
@classmethod
|
||
|
def preprocess(cls, value):
|
||
|
if value in [True, False]:
|
||
|
return bool(value)
|
||
|
else:
|
||
|
raise OptionError("'%s' must have a boolean value assigned, got %s" % (cls.option, value))
|
||
|
|
||
|
|
||
|
class OptionType(type):
|
||
|
"""Base type for all options that does registers options. """
|
||
|
|
||
|
def __init__(cls, *args, **kwargs):
|
||
|
@property
|
||
|
def getter(self):
|
||
|
try:
|
||
|
return self[cls.option]
|
||
|
except KeyError:
|
||
|
return cls.default()
|
||
|
|
||
|
setattr(Options, cls.option, getter)
|
||
|
Options.__options__[cls.option] = cls
|
||
|
|
||
|
|
||
|
@public
|
||
|
class Options(dict):
|
||
|
"""
|
||
|
Options manager for polynomial manipulation module.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy.polys.polyoptions import Options
|
||
|
>>> from sympy.polys.polyoptions import build_options
|
||
|
|
||
|
>>> from sympy.abc import x, y, z
|
||
|
|
||
|
>>> Options((x, y, z), {'domain': 'ZZ'})
|
||
|
{'auto': False, 'domain': ZZ, 'gens': (x, y, z)}
|
||
|
|
||
|
>>> build_options((x, y, z), {'domain': 'ZZ'})
|
||
|
{'auto': False, 'domain': ZZ, 'gens': (x, y, z)}
|
||
|
|
||
|
**Options**
|
||
|
|
||
|
* Expand --- boolean option
|
||
|
* Gens --- option
|
||
|
* Wrt --- option
|
||
|
* Sort --- option
|
||
|
* Order --- option
|
||
|
* Field --- boolean option
|
||
|
* Greedy --- boolean option
|
||
|
* Domain --- option
|
||
|
* Split --- boolean option
|
||
|
* Gaussian --- boolean option
|
||
|
* Extension --- option
|
||
|
* Modulus --- option
|
||
|
* Symmetric --- boolean option
|
||
|
* Strict --- boolean option
|
||
|
|
||
|
**Flags**
|
||
|
|
||
|
* Auto --- boolean flag
|
||
|
* Frac --- boolean flag
|
||
|
* Formal --- boolean flag
|
||
|
* Polys --- boolean flag
|
||
|
* Include --- boolean flag
|
||
|
* All --- boolean flag
|
||
|
* Gen --- flag
|
||
|
* Series --- boolean flag
|
||
|
|
||
|
"""
|
||
|
|
||
|
__order__ = None
|
||
|
__options__: dict[str, type[Option]] = {}
|
||
|
|
||
|
def __init__(self, gens, args, flags=None, strict=False):
|
||
|
dict.__init__(self)
|
||
|
|
||
|
if gens and args.get('gens', ()):
|
||
|
raise OptionError(
|
||
|
"both '*gens' and keyword argument 'gens' supplied")
|
||
|
elif gens:
|
||
|
args = dict(args)
|
||
|
args['gens'] = gens
|
||
|
|
||
|
defaults = args.pop('defaults', {})
|
||
|
|
||
|
def preprocess_options(args):
|
||
|
for option, value in args.items():
|
||
|
try:
|
||
|
cls = self.__options__[option]
|
||
|
except KeyError:
|
||
|
raise OptionError("'%s' is not a valid option" % option)
|
||
|
|
||
|
if issubclass(cls, Flag):
|
||
|
if flags is None or option not in flags:
|
||
|
if strict:
|
||
|
raise OptionError("'%s' flag is not allowed in this context" % option)
|
||
|
|
||
|
if value is not None:
|
||
|
self[option] = cls.preprocess(value)
|
||
|
|
||
|
preprocess_options(args)
|
||
|
|
||
|
for key, value in dict(defaults).items():
|
||
|
if key in self:
|
||
|
del defaults[key]
|
||
|
else:
|
||
|
for option in self.keys():
|
||
|
cls = self.__options__[option]
|
||
|
|
||
|
if key in cls.excludes:
|
||
|
del defaults[key]
|
||
|
break
|
||
|
|
||
|
preprocess_options(defaults)
|
||
|
|
||
|
for option in self.keys():
|
||
|
cls = self.__options__[option]
|
||
|
|
||
|
for require_option in cls.requires:
|
||
|
if self.get(require_option) is None:
|
||
|
raise OptionError("'%s' option is only allowed together with '%s'" % (option, require_option))
|
||
|
|
||
|
for exclude_option in cls.excludes:
|
||
|
if self.get(exclude_option) is not None:
|
||
|
raise OptionError("'%s' option is not allowed together with '%s'" % (option, exclude_option))
|
||
|
|
||
|
for option in self.__order__:
|
||
|
self.__options__[option].postprocess(self)
|
||
|
|
||
|
@classmethod
|
||
|
def _init_dependencies_order(cls):
|
||
|
"""Resolve the order of options' processing. """
|
||
|
if cls.__order__ is None:
|
||
|
vertices, edges = [], set()
|
||
|
|
||
|
for name, option in cls.__options__.items():
|
||
|
vertices.append(name)
|
||
|
|
||
|
for _name in option.after:
|
||
|
edges.add((_name, name))
|
||
|
|
||
|
for _name in option.before:
|
||
|
edges.add((name, _name))
|
||
|
|
||
|
try:
|
||
|
cls.__order__ = topological_sort((vertices, list(edges)))
|
||
|
except ValueError:
|
||
|
raise RuntimeError(
|
||
|
"cycle detected in sympy.polys options framework")
|
||
|
|
||
|
def clone(self, updates={}):
|
||
|
"""Clone ``self`` and update specified options. """
|
||
|
obj = dict.__new__(self.__class__)
|
||
|
|
||
|
for option, value in self.items():
|
||
|
obj[option] = value
|
||
|
|
||
|
for option, value in updates.items():
|
||
|
obj[option] = value
|
||
|
|
||
|
return obj
|
||
|
|
||
|
def __setattr__(self, attr, value):
|
||
|
if attr in self.__options__:
|
||
|
self[attr] = value
|
||
|
else:
|
||
|
super().__setattr__(attr, value)
|
||
|
|
||
|
@property
|
||
|
def args(self):
|
||
|
args = {}
|
||
|
|
||
|
for option, value in self.items():
|
||
|
if value is not None and option != 'gens':
|
||
|
cls = self.__options__[option]
|
||
|
|
||
|
if not issubclass(cls, Flag):
|
||
|
args[option] = value
|
||
|
|
||
|
return args
|
||
|
|
||
|
@property
|
||
|
def options(self):
|
||
|
options = {}
|
||
|
|
||
|
for option, cls in self.__options__.items():
|
||
|
if not issubclass(cls, Flag):
|
||
|
options[option] = getattr(self, option)
|
||
|
|
||
|
return options
|
||
|
|
||
|
@property
|
||
|
def flags(self):
|
||
|
flags = {}
|
||
|
|
||
|
for option, cls in self.__options__.items():
|
||
|
if issubclass(cls, Flag):
|
||
|
flags[option] = getattr(self, option)
|
||
|
|
||
|
return flags
|
||
|
|
||
|
|
||
|
class Expand(BooleanOption, metaclass=OptionType):
|
||
|
"""``expand`` option to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'expand'
|
||
|
|
||
|
requires: list[str] = []
|
||
|
excludes: list[str] = []
|
||
|
|
||
|
@classmethod
|
||
|
def default(cls):
|
||
|
return True
|
||
|
|
||
|
|
||
|
class Gens(Option, metaclass=OptionType):
|
||
|
"""``gens`` option to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'gens'
|
||
|
|
||
|
requires: list[str] = []
|
||
|
excludes: list[str] = []
|
||
|
|
||
|
@classmethod
|
||
|
def default(cls):
|
||
|
return ()
|
||
|
|
||
|
@classmethod
|
||
|
def preprocess(cls, gens):
|
||
|
if isinstance(gens, Basic):
|
||
|
gens = (gens,)
|
||
|
elif len(gens) == 1 and is_sequence(gens[0]):
|
||
|
gens = gens[0]
|
||
|
|
||
|
if gens == (None,):
|
||
|
gens = ()
|
||
|
elif has_dups(gens):
|
||
|
raise GeneratorsError("duplicated generators: %s" % str(gens))
|
||
|
elif any(gen.is_commutative is False for gen in gens):
|
||
|
raise GeneratorsError("non-commutative generators: %s" % str(gens))
|
||
|
|
||
|
return tuple(gens)
|
||
|
|
||
|
|
||
|
class Wrt(Option, metaclass=OptionType):
|
||
|
"""``wrt`` option to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'wrt'
|
||
|
|
||
|
requires: list[str] = []
|
||
|
excludes: list[str] = []
|
||
|
|
||
|
_re_split = re.compile(r"\s*,\s*|\s+")
|
||
|
|
||
|
@classmethod
|
||
|
def preprocess(cls, wrt):
|
||
|
if isinstance(wrt, Basic):
|
||
|
return [str(wrt)]
|
||
|
elif isinstance(wrt, str):
|
||
|
wrt = wrt.strip()
|
||
|
if wrt.endswith(','):
|
||
|
raise OptionError('Bad input: missing parameter.')
|
||
|
if not wrt:
|
||
|
return []
|
||
|
return list(cls._re_split.split(wrt))
|
||
|
elif hasattr(wrt, '__getitem__'):
|
||
|
return list(map(str, wrt))
|
||
|
else:
|
||
|
raise OptionError("invalid argument for 'wrt' option")
|
||
|
|
||
|
|
||
|
class Sort(Option, metaclass=OptionType):
|
||
|
"""``sort`` option to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'sort'
|
||
|
|
||
|
requires: list[str] = []
|
||
|
excludes: list[str] = []
|
||
|
|
||
|
@classmethod
|
||
|
def default(cls):
|
||
|
return []
|
||
|
|
||
|
@classmethod
|
||
|
def preprocess(cls, sort):
|
||
|
if isinstance(sort, str):
|
||
|
return [ gen.strip() for gen in sort.split('>') ]
|
||
|
elif hasattr(sort, '__getitem__'):
|
||
|
return list(map(str, sort))
|
||
|
else:
|
||
|
raise OptionError("invalid argument for 'sort' option")
|
||
|
|
||
|
|
||
|
class Order(Option, metaclass=OptionType):
|
||
|
"""``order`` option to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'order'
|
||
|
|
||
|
requires: list[str] = []
|
||
|
excludes: list[str] = []
|
||
|
|
||
|
@classmethod
|
||
|
def default(cls):
|
||
|
return sympy.polys.orderings.lex
|
||
|
|
||
|
@classmethod
|
||
|
def preprocess(cls, order):
|
||
|
return sympy.polys.orderings.monomial_key(order)
|
||
|
|
||
|
|
||
|
class Field(BooleanOption, metaclass=OptionType):
|
||
|
"""``field`` option to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'field'
|
||
|
|
||
|
requires: list[str] = []
|
||
|
excludes = ['domain', 'split', 'gaussian']
|
||
|
|
||
|
|
||
|
class Greedy(BooleanOption, metaclass=OptionType):
|
||
|
"""``greedy`` option to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'greedy'
|
||
|
|
||
|
requires: list[str] = []
|
||
|
excludes = ['domain', 'split', 'gaussian', 'extension', 'modulus', 'symmetric']
|
||
|
|
||
|
|
||
|
class Composite(BooleanOption, metaclass=OptionType):
|
||
|
"""``composite`` option to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'composite'
|
||
|
|
||
|
@classmethod
|
||
|
def default(cls):
|
||
|
return None
|
||
|
|
||
|
requires: list[str] = []
|
||
|
excludes = ['domain', 'split', 'gaussian', 'extension', 'modulus', 'symmetric']
|
||
|
|
||
|
|
||
|
class Domain(Option, metaclass=OptionType):
|
||
|
"""``domain`` option to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'domain'
|
||
|
|
||
|
requires: list[str] = []
|
||
|
excludes = ['field', 'greedy', 'split', 'gaussian', 'extension']
|
||
|
|
||
|
after = ['gens']
|
||
|
|
||
|
_re_realfield = re.compile(r"^(R|RR)(_(\d+))?$")
|
||
|
_re_complexfield = re.compile(r"^(C|CC)(_(\d+))?$")
|
||
|
_re_finitefield = re.compile(r"^(FF|GF)\((\d+)\)$")
|
||
|
_re_polynomial = re.compile(r"^(Z|ZZ|Q|QQ|ZZ_I|QQ_I|R|RR|C|CC)\[(.+)\]$")
|
||
|
_re_fraction = re.compile(r"^(Z|ZZ|Q|QQ)\((.+)\)$")
|
||
|
_re_algebraic = re.compile(r"^(Q|QQ)\<(.+)\>$")
|
||
|
|
||
|
@classmethod
|
||
|
def preprocess(cls, domain):
|
||
|
if isinstance(domain, sympy.polys.domains.Domain):
|
||
|
return domain
|
||
|
elif hasattr(domain, 'to_domain'):
|
||
|
return domain.to_domain()
|
||
|
elif isinstance(domain, str):
|
||
|
if domain in ['Z', 'ZZ']:
|
||
|
return sympy.polys.domains.ZZ
|
||
|
|
||
|
if domain in ['Q', 'QQ']:
|
||
|
return sympy.polys.domains.QQ
|
||
|
|
||
|
if domain == 'ZZ_I':
|
||
|
return sympy.polys.domains.ZZ_I
|
||
|
|
||
|
if domain == 'QQ_I':
|
||
|
return sympy.polys.domains.QQ_I
|
||
|
|
||
|
if domain == 'EX':
|
||
|
return sympy.polys.domains.EX
|
||
|
|
||
|
r = cls._re_realfield.match(domain)
|
||
|
|
||
|
if r is not None:
|
||
|
_, _, prec = r.groups()
|
||
|
|
||
|
if prec is None:
|
||
|
return sympy.polys.domains.RR
|
||
|
else:
|
||
|
return sympy.polys.domains.RealField(int(prec))
|
||
|
|
||
|
r = cls._re_complexfield.match(domain)
|
||
|
|
||
|
if r is not None:
|
||
|
_, _, prec = r.groups()
|
||
|
|
||
|
if prec is None:
|
||
|
return sympy.polys.domains.CC
|
||
|
else:
|
||
|
return sympy.polys.domains.ComplexField(int(prec))
|
||
|
|
||
|
r = cls._re_finitefield.match(domain)
|
||
|
|
||
|
if r is not None:
|
||
|
return sympy.polys.domains.FF(int(r.groups()[1]))
|
||
|
|
||
|
r = cls._re_polynomial.match(domain)
|
||
|
|
||
|
if r is not None:
|
||
|
ground, gens = r.groups()
|
||
|
|
||
|
gens = list(map(sympify, gens.split(',')))
|
||
|
|
||
|
if ground in ['Z', 'ZZ']:
|
||
|
return sympy.polys.domains.ZZ.poly_ring(*gens)
|
||
|
elif ground in ['Q', 'QQ']:
|
||
|
return sympy.polys.domains.QQ.poly_ring(*gens)
|
||
|
elif ground in ['R', 'RR']:
|
||
|
return sympy.polys.domains.RR.poly_ring(*gens)
|
||
|
elif ground == 'ZZ_I':
|
||
|
return sympy.polys.domains.ZZ_I.poly_ring(*gens)
|
||
|
elif ground == 'QQ_I':
|
||
|
return sympy.polys.domains.QQ_I.poly_ring(*gens)
|
||
|
else:
|
||
|
return sympy.polys.domains.CC.poly_ring(*gens)
|
||
|
|
||
|
r = cls._re_fraction.match(domain)
|
||
|
|
||
|
if r is not None:
|
||
|
ground, gens = r.groups()
|
||
|
|
||
|
gens = list(map(sympify, gens.split(',')))
|
||
|
|
||
|
if ground in ['Z', 'ZZ']:
|
||
|
return sympy.polys.domains.ZZ.frac_field(*gens)
|
||
|
else:
|
||
|
return sympy.polys.domains.QQ.frac_field(*gens)
|
||
|
|
||
|
r = cls._re_algebraic.match(domain)
|
||
|
|
||
|
if r is not None:
|
||
|
gens = list(map(sympify, r.groups()[1].split(',')))
|
||
|
return sympy.polys.domains.QQ.algebraic_field(*gens)
|
||
|
|
||
|
raise OptionError('expected a valid domain specification, got %s' % domain)
|
||
|
|
||
|
@classmethod
|
||
|
def postprocess(cls, options):
|
||
|
if 'gens' in options and 'domain' in options and options['domain'].is_Composite and \
|
||
|
(set(options['domain'].symbols) & set(options['gens'])):
|
||
|
raise GeneratorsError(
|
||
|
"ground domain and generators interfere together")
|
||
|
elif ('gens' not in options or not options['gens']) and \
|
||
|
'domain' in options and options['domain'] == sympy.polys.domains.EX:
|
||
|
raise GeneratorsError("you have to provide generators because EX domain was requested")
|
||
|
|
||
|
|
||
|
class Split(BooleanOption, metaclass=OptionType):
|
||
|
"""``split`` option to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'split'
|
||
|
|
||
|
requires: list[str] = []
|
||
|
excludes = ['field', 'greedy', 'domain', 'gaussian', 'extension',
|
||
|
'modulus', 'symmetric']
|
||
|
|
||
|
@classmethod
|
||
|
def postprocess(cls, options):
|
||
|
if 'split' in options:
|
||
|
raise NotImplementedError("'split' option is not implemented yet")
|
||
|
|
||
|
|
||
|
class Gaussian(BooleanOption, metaclass=OptionType):
|
||
|
"""``gaussian`` option to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'gaussian'
|
||
|
|
||
|
requires: list[str] = []
|
||
|
excludes = ['field', 'greedy', 'domain', 'split', 'extension',
|
||
|
'modulus', 'symmetric']
|
||
|
|
||
|
@classmethod
|
||
|
def postprocess(cls, options):
|
||
|
if 'gaussian' in options and options['gaussian'] is True:
|
||
|
options['domain'] = sympy.polys.domains.QQ_I
|
||
|
Extension.postprocess(options)
|
||
|
|
||
|
|
||
|
class Extension(Option, metaclass=OptionType):
|
||
|
"""``extension`` option to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'extension'
|
||
|
|
||
|
requires: list[str] = []
|
||
|
excludes = ['greedy', 'domain', 'split', 'gaussian', 'modulus',
|
||
|
'symmetric']
|
||
|
|
||
|
@classmethod
|
||
|
def preprocess(cls, extension):
|
||
|
if extension == 1:
|
||
|
return bool(extension)
|
||
|
elif extension == 0:
|
||
|
raise OptionError("'False' is an invalid argument for 'extension'")
|
||
|
else:
|
||
|
if not hasattr(extension, '__iter__'):
|
||
|
extension = {extension}
|
||
|
else:
|
||
|
if not extension:
|
||
|
extension = None
|
||
|
else:
|
||
|
extension = set(extension)
|
||
|
|
||
|
return extension
|
||
|
|
||
|
@classmethod
|
||
|
def postprocess(cls, options):
|
||
|
if 'extension' in options and options['extension'] is not True:
|
||
|
options['domain'] = sympy.polys.domains.QQ.algebraic_field(
|
||
|
*options['extension'])
|
||
|
|
||
|
|
||
|
class Modulus(Option, metaclass=OptionType):
|
||
|
"""``modulus`` option to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'modulus'
|
||
|
|
||
|
requires: list[str] = []
|
||
|
excludes = ['greedy', 'split', 'domain', 'gaussian', 'extension']
|
||
|
|
||
|
@classmethod
|
||
|
def preprocess(cls, modulus):
|
||
|
modulus = sympify(modulus)
|
||
|
|
||
|
if modulus.is_Integer and modulus > 0:
|
||
|
return int(modulus)
|
||
|
else:
|
||
|
raise OptionError(
|
||
|
"'modulus' must a positive integer, got %s" % modulus)
|
||
|
|
||
|
@classmethod
|
||
|
def postprocess(cls, options):
|
||
|
if 'modulus' in options:
|
||
|
modulus = options['modulus']
|
||
|
symmetric = options.get('symmetric', True)
|
||
|
options['domain'] = sympy.polys.domains.FF(modulus, symmetric)
|
||
|
|
||
|
|
||
|
class Symmetric(BooleanOption, metaclass=OptionType):
|
||
|
"""``symmetric`` option to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'symmetric'
|
||
|
|
||
|
requires = ['modulus']
|
||
|
excludes = ['greedy', 'domain', 'split', 'gaussian', 'extension']
|
||
|
|
||
|
|
||
|
class Strict(BooleanOption, metaclass=OptionType):
|
||
|
"""``strict`` option to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'strict'
|
||
|
|
||
|
@classmethod
|
||
|
def default(cls):
|
||
|
return True
|
||
|
|
||
|
|
||
|
class Auto(BooleanOption, Flag, metaclass=OptionType):
|
||
|
"""``auto`` flag to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'auto'
|
||
|
|
||
|
after = ['field', 'domain', 'extension', 'gaussian']
|
||
|
|
||
|
@classmethod
|
||
|
def default(cls):
|
||
|
return True
|
||
|
|
||
|
@classmethod
|
||
|
def postprocess(cls, options):
|
||
|
if ('domain' in options or 'field' in options) and 'auto' not in options:
|
||
|
options['auto'] = False
|
||
|
|
||
|
|
||
|
class Frac(BooleanOption, Flag, metaclass=OptionType):
|
||
|
"""``auto`` option to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'frac'
|
||
|
|
||
|
@classmethod
|
||
|
def default(cls):
|
||
|
return False
|
||
|
|
||
|
|
||
|
class Formal(BooleanOption, Flag, metaclass=OptionType):
|
||
|
"""``formal`` flag to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'formal'
|
||
|
|
||
|
@classmethod
|
||
|
def default(cls):
|
||
|
return False
|
||
|
|
||
|
|
||
|
class Polys(BooleanOption, Flag, metaclass=OptionType):
|
||
|
"""``polys`` flag to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'polys'
|
||
|
|
||
|
|
||
|
class Include(BooleanOption, Flag, metaclass=OptionType):
|
||
|
"""``include`` flag to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'include'
|
||
|
|
||
|
@classmethod
|
||
|
def default(cls):
|
||
|
return False
|
||
|
|
||
|
|
||
|
class All(BooleanOption, Flag, metaclass=OptionType):
|
||
|
"""``all`` flag to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'all'
|
||
|
|
||
|
@classmethod
|
||
|
def default(cls):
|
||
|
return False
|
||
|
|
||
|
|
||
|
class Gen(Flag, metaclass=OptionType):
|
||
|
"""``gen`` flag to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'gen'
|
||
|
|
||
|
@classmethod
|
||
|
def default(cls):
|
||
|
return 0
|
||
|
|
||
|
@classmethod
|
||
|
def preprocess(cls, gen):
|
||
|
if isinstance(gen, (Basic, int)):
|
||
|
return gen
|
||
|
else:
|
||
|
raise OptionError("invalid argument for 'gen' option")
|
||
|
|
||
|
|
||
|
class Series(BooleanOption, Flag, metaclass=OptionType):
|
||
|
"""``series`` flag to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'series'
|
||
|
|
||
|
@classmethod
|
||
|
def default(cls):
|
||
|
return False
|
||
|
|
||
|
|
||
|
class Symbols(Flag, metaclass=OptionType):
|
||
|
"""``symbols`` flag to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'symbols'
|
||
|
|
||
|
@classmethod
|
||
|
def default(cls):
|
||
|
return numbered_symbols('s', start=1)
|
||
|
|
||
|
@classmethod
|
||
|
def preprocess(cls, symbols):
|
||
|
if hasattr(symbols, '__iter__'):
|
||
|
return iter(symbols)
|
||
|
else:
|
||
|
raise OptionError("expected an iterator or iterable container, got %s" % symbols)
|
||
|
|
||
|
|
||
|
class Method(Flag, metaclass=OptionType):
|
||
|
"""``method`` flag to polynomial manipulation functions. """
|
||
|
|
||
|
option = 'method'
|
||
|
|
||
|
@classmethod
|
||
|
def preprocess(cls, method):
|
||
|
if isinstance(method, str):
|
||
|
return method.lower()
|
||
|
else:
|
||
|
raise OptionError("expected a string, got %s" % method)
|
||
|
|
||
|
|
||
|
def build_options(gens, args=None):
|
||
|
"""Construct options from keyword arguments or ... options. """
|
||
|
if args is None:
|
||
|
gens, args = (), gens
|
||
|
|
||
|
if len(args) != 1 or 'opt' not in args or gens:
|
||
|
return Options(gens, args)
|
||
|
else:
|
||
|
return args['opt']
|
||
|
|
||
|
|
||
|
def allowed_flags(args, flags):
|
||
|
"""
|
||
|
Allow specified flags to be used in the given context.
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy.polys.polyoptions import allowed_flags
|
||
|
>>> from sympy.polys.domains import ZZ
|
||
|
|
||
|
>>> allowed_flags({'domain': ZZ}, [])
|
||
|
|
||
|
>>> allowed_flags({'domain': ZZ, 'frac': True}, [])
|
||
|
Traceback (most recent call last):
|
||
|
...
|
||
|
FlagError: 'frac' flag is not allowed in this context
|
||
|
|
||
|
>>> allowed_flags({'domain': ZZ, 'frac': True}, ['frac'])
|
||
|
|
||
|
"""
|
||
|
flags = set(flags)
|
||
|
|
||
|
for arg in args.keys():
|
||
|
try:
|
||
|
if Options.__options__[arg].is_Flag and arg not in flags:
|
||
|
raise FlagError(
|
||
|
"'%s' flag is not allowed in this context" % arg)
|
||
|
except KeyError:
|
||
|
raise OptionError("'%s' is not a valid option" % arg)
|
||
|
|
||
|
|
||
|
def set_defaults(options, **defaults):
|
||
|
"""Update options with default values. """
|
||
|
if 'defaults' not in options:
|
||
|
options = dict(options)
|
||
|
options['defaults'] = defaults
|
||
|
|
||
|
return options
|
||
|
|
||
|
Options._init_dependencies_order()
|