From 9cd91c1ef098802269cc9d2d8b4d29bec90607c7 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner <inform@tiker.net> Date: Sat, 25 May 2013 17:36:57 -0400 Subject: [PATCH] Finish up documentation. --- doc/geometric-algebra.rst | 4 + doc/index.rst | 4 +- doc/mappers.rst | 123 +++++++++++++++++++++++++++++ doc/misc.rst | 11 +++ doc/primitives.rst | 7 +- pymbolic/__init__.py | 2 + pymbolic/algorithm.py | 2 +- pymbolic/geometric_algebra.py | 4 +- pymbolic/mapper/__init__.py | 84 ++++++++++++++++---- pymbolic/mapper/c_code.py | 35 ++++++++ pymbolic/mapper/collector.py | 8 +- pymbolic/mapper/constant_folder.py | 18 ----- pymbolic/mapper/dependency.py | 15 +++- pymbolic/mapper/differentiator.py | 28 ++++++- pymbolic/mapper/evaluator.py | 19 +++++ pymbolic/mapper/expander.py | 12 +++ pymbolic/mapper/stringifier.py | 46 ++++++++++- pymbolic/primitives.py | 14 ++++ setup.py | 1 - 19 files changed, 387 insertions(+), 50 deletions(-) create mode 100644 doc/mappers.rst diff --git a/doc/geometric-algebra.rst b/doc/geometric-algebra.rst index e52ec88..4688dea 100644 --- a/doc/geometric-algebra.rst +++ b/doc/geometric-algebra.rst @@ -3,6 +3,8 @@ Geometric Algebra .. automodule:: pymbolic.geometric_algebra +Also see :ref:`ga-examples`. + Spaces ------ @@ -57,6 +59,8 @@ Multivectors .. automethod:: as_scalar .. automethod:: as_vector +.. _ga-examples: + Example usage ------------- diff --git a/doc/index.rst b/doc/index.rst index ad8d411..706b9f8 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,5 +1,5 @@ -Welcome to pymbolic's documentation! -==================================== +Welcome to pymbolic! +==================== .. automodule:: pymbolic diff --git a/doc/mappers.rst b/doc/mappers.rst new file mode 100644 index 0000000..11a0ef2 --- /dev/null +++ b/doc/mappers.rst @@ -0,0 +1,123 @@ +Mappers +======= + +.. automodule:: pymbolic.mapper + +Basic dispatch +-------------- + +.. autoclass:: Mapper + + .. automethod:: __call__ + + .. method:: rec(expr, *args, **kwargs) + + Identical to :meth:`__call__`, but intended for use in recursive dispatch + in mapper methods. + + .. automethod:: handle_unsupported_expression + + .. rubric:: Handling objects that don't declare mapper methods + + In particular, this includes many non-subclasses of + :class:`pymbolic.primitives.Expression`. + + .. automethod:: map_foreign + + These are abstract methods for foreign objects that should be overridden + in subclasses: + + .. method:: map_constant(expr, *args, **kwargs) + + Mapper method for constants. + See :func:`pymbolic.primitives.register_constant_class`. + + .. method:: map_list(expr, *args, **kwargs) + + .. method:: map_tuple(expr, *args, **kwargs) + + .. method:: map_numpy_array(expr, *args, **kwargs) + +Base classes for new mappers +---------------------------- + +.. autoclass:: CombineMapper + +.. autoclass:: IdentityMapper + +.. autoclass:: WalkMapper + +.. autoclass:: CSECachingMapperMixin + +More specialized mappers +------------------------ + +Converting to strings and code +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: pymbolic.mapper.stringifier + +.. _prec-constants: + +Precedence constants +******************** + +.. data:: PREC_CALL +.. data:: PREC_POWER +.. data:: PREC_UNARY +.. data:: PREC_PRODUCT +.. data:: PREC_SUM +.. data:: PREC_COMPARISON +.. data:: PREC_LOGICAL_AND +.. data:: PREC_LOGICAL_OR +.. data:: PREC_NONE + +Mappers +******* + +.. autoclass:: StringifyMapper + + .. automethod:: __call__ + +.. autoclass:: CSESplittingStringifyMapperMixin + +.. automodule:: pymbolic.mapper.c_code + +.. autoclass:: CCodeMapper + +Some minimal mathematics +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: pymbolic.mapper.evaluator + +.. autoclass:: EvaluationMapper + +.. automodule:: pymbolic.mapper.differentiator + +.. autoclass:: DifferentiationMapper + +.. automodule:: pymbolic.mapper.expander + +.. autoclass:: ExpandMapper + +.. automodule:: pymbolic.mapper.collector + +.. autoclass:: TermCollector + +.. automodule:: pymbolic.mapper.constant_folder + +.. autoclass:: ConstantFoldingMapper +.. autoclass:: CommutativeConstantFoldingMapper + +Finding expression properties +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: pymbolic.mapper.dependency + +.. autoclass:: DependencyMapper + +.. automodule:: pymbolic.mapper.flop_counter + +.. autoclass:: FlopCounter + +.. vim: sw=4 diff --git a/doc/misc.rst b/doc/misc.rst index 21f0ae1..6a4c34e 100644 --- a/doc/misc.rst +++ b/doc/misc.rst @@ -67,3 +67,14 @@ Frequently Asked Questions The FAQ is maintained collaboratively on the `Wiki FAQ page <http://wiki.tiker.net/Pymbolic/FrequentlyAskedQuestions>`_. +Glossary +======== + +.. glossary:: + + mix-in + See `Wikipedia article <https://en.wikipedia.org/wiki/Mixin>`_. + + Be sure to mention the mix-in before the base classe being mixed in the + list of base classes. This way, the mix-in can override base class + behavior. diff --git a/doc/primitives.rst b/doc/primitives.rst index 19f4d1c..497759e 100644 --- a/doc/primitives.rst +++ b/doc/primitives.rst @@ -17,6 +17,7 @@ Expression base class .. automethod:: __eq__ .. automethod:: __hash__ + .. automethod:: __str__ .. automethod:: __repr__ Sums, products and such @@ -92,13 +93,15 @@ Code generation helpers :undoc-members: :members: mapper_method -Predicates, Constants ---------------------- +Helper functions +---------------- .. autofunction:: is_zero .. autofunction:: is_constant .. autofunction:: register_constant_class .. autofunction:: unregister_constant_class +.. autofunction:: variables +.. autofunction:: make_common_subexpression Interaction with :mod:`numpy` arrays ------------------------------------ diff --git a/pymbolic/__init__.py b/pymbolic/__init__.py index 69c5d0b..fcd1cc7 100644 --- a/pymbolic/__init__.py +++ b/pymbolic/__init__.py @@ -65,6 +65,8 @@ you put in. It has a few of those built in, but that's not really the point: >>> print pmbl.differentiate(u, 'x') 5*(x + 1)**4 +.. _custom-manipulation: + Manipulating expressions ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/pymbolic/algorithm.py b/pymbolic/algorithm.py index 6104264..7443a49 100644 --- a/pymbolic/algorithm.py +++ b/pymbolic/algorithm.py @@ -72,7 +72,7 @@ def extended_euclidean(q, r): """Return a tuple *(p, a, b)* such that :math:`p = aq + br`, where *p* is the greatest common divisor of *q* and *r*. - See also `Wikipedia <https://en.wikipedia.org/wiki/Euclidean_algorithm>`_. + See also the `Wikipedia article on the Euclidean algorithm <https://en.wikipedia.org/wiki/Euclidean_algorithm>`_. """ import pymbolic.traits as traits diff --git a/pymbolic/geometric_algebra.py b/pymbolic/geometric_algebra.py index 43cefe7..d8556df 100644 --- a/pymbolic/geometric_algebra.py +++ b/pymbolic/geometric_algebra.py @@ -329,8 +329,8 @@ class MultiVector(object): ``A*B``, Geometric product :math:`AB` ``A^B``, Outer product :math:`A\wedge B` of multivectors ``A|B``, Inner product :math:`A\cdot B` of multivectors - ``A<<B``, Left contraction of multivectors - ``A>>B``, Right contraction of multivectors + ``A<<B``, Left contraction :math:`A\lrcorner B` of multivectors + ``A>>B``, Right contraction :math:`A\llcorner B` of multivectors .. warning :: diff --git a/pymbolic/mapper/__init__.py b/pymbolic/mapper/__init__.py index 67eb165..0a2ef0f 100644 --- a/pymbolic/mapper/__init__.py +++ b/pymbolic/mapper/__init__.py @@ -46,12 +46,33 @@ class UnsupportedExpressionError(ValueError): # {{{ mapper base class Mapper(object): + """A visitor for trees of :class:`pymbolic.primitives.Expression` + subclasses. Each expression-derived object is dispatched to the + method named by the :attr:`pymbolic.primitives.Expression.mapper_method` + attribute. + """ + def handle_unsupported_expression(self, expr, *args): + """Mapper method that is invoked for + :class:`pymbolic.primitives.Expression` subclasses for which a mapper + method does not exist in this mapper. + """ + raise UnsupportedExpressionError( "%s cannot handle expressions of type %s" % ( - self.__class__, expr.__class__)) + type(self), type(expr))) def __call__(self, expr, *args, **kwargs): + """Dispatch *expr* to its corresponding mapper method. Pass on ``*args`` + and ``**kwargs`` unmodified. + + This method is intended as the top-level dispatch entry point and may + be overridden by subclasses to present a different/more convenient + interface. :meth:`rec` on the other hand is intended as the recursive + dispatch method to be used to recurse within mapper method + implementations. + """ + try: method = getattr(self, expr.mapper_method) except AttributeError: @@ -65,6 +86,8 @@ class Mapper(object): return method(expr, *args, **kwargs) + rec = __call__ + def map_variable(self, expr, *args): return self.map_algebraic_leaf(expr, *args) @@ -84,6 +107,8 @@ class Mapper(object): return self.map_quotient(expr, *args) def map_foreign(self, expr, *args): + """Mapper method dispatch for non-:mod:`pymbolic` objects.""" + if isinstance(expr, primitives.VALID_CONSTANT_CLASSES): return self.map_constant(expr, *args) elif isinstance(expr, list): @@ -102,14 +127,26 @@ class Mapper(object): -class RecursiveMapper(Mapper): - rec = Mapper.__call__ - - +RecursiveMapper = Mapper # {{{ combine mapper class CombineMapper(RecursiveMapper): + """A mapper whose goal it is to *combine* all branches of the expression tree + into one final result. The default implementation of all mapper methods simply + recurse (:meth:`Mapper.rec`) on all branches emanating from the current expression, + and then call :meth:`combine` on a tuple of results. + + .. method:: combine(values) + + Combine the mapped results of multiple expressions (given in *values*) + into a single result, often by summing or taking set unions. + + The :class:`pymbolic.mapper.flop_counter.FlopCounter` is a very simple example. + (Look at its source for an idea of how to derive from :class:`CombineMapper`.) + The :class:`pymbolic.mapper.dependency.DependencyMapper` is another example. + """ + def map_call(self, expr, *args): return self.combine( (self.rec(expr.function, *args),) + @@ -192,7 +229,13 @@ class CombineMapper(RecursiveMapper): # {{{ identity mapper -class IdentityMapperBase(object): +class IdentityMapper(Mapper): + """A :class:`Mapper` whose default mapper methods + make a deep copy of each subexpression. + + See :ref:`custom-manipulation` for an example of the + manipulations that can be implemented this way. + """ def map_constant(self, expr, *args): # leaf -- no need to rebuild return expr @@ -321,19 +364,17 @@ class IdentityMapperBase(object): self.rec(expr.then, *args), self.rec(expr.else_, *args)) - - -class IdentityMapper(IdentityMapperBase, RecursiveMapper): - pass - -class NonrecursiveIdentityMapper(IdentityMapperBase, Mapper): - pass - # }}} # {{{ walk mapper class WalkMapper(RecursiveMapper): + """A mapper whose default mapper method implementations simply recurse + without propagating any result. Also calls :meth:`visit` for each + visited subexpression. + + .. method:: visit(expr, *args) + """ def map_constant(self, expr, *args): self.visit(expr) @@ -506,6 +547,21 @@ class CallbackMapper(RecursiveMapper): # {{{ cse caching mixin class CSECachingMapperMixin(object): + """A :term:`mix-in` that helps + subclassed mappers implement caching for + :class:`pymbolic.primitives.CommonSubexpression` + instances. + + Instead of the common mapper method for + :class:`pymbolic.primitives.CommonSubexpression`, + subclasses should implement the following method: + + .. method:: map_common_subexpression_uncached(expr) + + This method deliberately does not support extra arguments in mapper dispatch, + to avoid spurious dependencies of the cache on these arguments. + """ + def map_common_subexpression(self, expr): try: ccd = self._cse_cache_dict diff --git a/pymbolic/mapper/c_code.py b/pymbolic/mapper/c_code.py index 12fdf02..df4de2d 100644 --- a/pymbolic/mapper/c_code.py +++ b/pymbolic/mapper/c_code.py @@ -28,6 +28,41 @@ from pymbolic.mapper.stringifier import SimplifyingSortingStringifyMapper class CCodeMapper(SimplifyingSortingStringifyMapper): + """Generate C code for expressions, while extracting + :class:`pymbolic.primitives.CommonSubexpression` instances. + + As an example, define a fairly simple expression *expr*: + + .. doctest:: + + >>> import pymbolic.primitives as p + >>> x = p.Variable("x") + >>> CSE = p.CommonSubexpression + >>> u = CSE(3*x**2-5, "u") + >>> expr = u/(u+3)*(u+5) + >>> print expr + CSE(3*x**2 + -5) / (CSE(3*x**2 + -5) + 3)*(CSE(3*x**2 + -5) + 5) + + Notice that if we were to directly generate code from this, the + subexpression *u* would be evaluated multiple times. + + .. doctest:: + + >>> from pymbolic.mapper.c_code import CCodeMapper as CCM + >>> ccm = CCM() + >>> result = ccm(expr) + + >>> for name, value in ccm.cse_name_list: + ... print "%s = %s;" % (name, value) + ... + _cse_u = 3 * x * x + -5; + >>> print result + _cse_u / (_cse_u + 3) * (_cse_u + 5) + + See :class:`pymbolic.mapper.stringifier.CSESplittingStringifyMapperMixin` + for the ``cse_*`` attributes. + """ + def __init__(self, constant_mapper=repr, reverse=True, cse_prefix="_cse", complex_constant_base_type="double", cse_name_list=[]): diff --git a/pymbolic/mapper/collector.py b/pymbolic/mapper/collector.py index 1fb313a..4c7f289 100644 --- a/pymbolic/mapper/collector.py +++ b/pymbolic/mapper/collector.py @@ -30,6 +30,10 @@ from pymbolic.mapper import IdentityMapper class TermCollector(IdentityMapper): """A term collector that assumes that multiplication is commutative. + + Allows specifying *parameters* (a set of + :class:`pymbolic.primitive.Variable` instances) that are viewed as being + coefficients and are not used for term collection. """ def __init__(self, parameters=set()): @@ -43,7 +47,7 @@ class TermCollector(IdentityMapper): """Returns a pair consisting of: - a frozenset of (base, exponent) pairs - a product of coefficients (i.e. constants and parameters) - + The set takes care of order-invariant comparison for us and is hashable. The argument `product' has to be fully expanded already. @@ -102,6 +106,6 @@ class TermCollector(IdentityMapper): def rep2term(rep): return pymbolic.flattened_product(base**exp for base, exp in rep) - result = pymbolic.flattened_sum(coeff*rep2term(termrep) + result = pymbolic.flattened_sum(coeff*rep2term(termrep) for termrep, coeff in term2coeff.iteritems()) return result diff --git a/pymbolic/mapper/constant_folder.py b/pymbolic/mapper/constant_folder.py index 303fc61..d682369 100644 --- a/pymbolic/mapper/constant_folder.py +++ b/pymbolic/mapper/constant_folder.py @@ -24,7 +24,6 @@ THE SOFTWARE. from pymbolic.mapper import \ IdentityMapper, \ - NonrecursiveIdentityMapper, \ CSECachingMapperMixin @@ -95,14 +94,6 @@ class ConstantFoldingMapper( map_common_subexpression_uncached = \ IdentityMapper.map_common_subexpression -class NonrecursiveConstantFoldingMapper( - CSECachingMapperMixin, - NonrecursiveIdentityMapper, - ConstantFoldingMapperBase): - - map_common_subexpression_uncached = \ - NonrecursiveIdentityMapper.map_common_subexpression - class CommutativeConstantFoldingMapper( CSECachingMapperMixin, CommutativeConstantFoldingMapperBase, @@ -110,12 +101,3 @@ class CommutativeConstantFoldingMapper( map_common_subexpression_uncached = \ IdentityMapper.map_common_subexpression - -class NonrecursiveCommutativeConstantFoldingMapper( - CSECachingMapperMixin, - CommutativeConstantFoldingMapperBase, - NonrecursiveIdentityMapper,): - - map_common_subexpression_uncached = \ - NonrecursiveIdentityMapper.map_common_subexpression - diff --git a/pymbolic/mapper/dependency.py b/pymbolic/mapper/dependency.py index c8661e0..0acd62e 100644 --- a/pymbolic/mapper/dependency.py +++ b/pymbolic/mapper/dependency.py @@ -28,12 +28,23 @@ from pymbolic.mapper import CombineMapper, CSECachingMapperMixin class DependencyMapper(CSECachingMapperMixin, CombineMapper): - def __init__(self, - include_subscripts=True, + """Maps an expression to the :class:`set` of expressions it + is based on. The ``include_*`` arguments to the constructor + determine which types of objects occur in this output set. + If all are *False*, only :class:`pymbolic.primitives.Variable` + instances are included. + """ + + def __init__(self, + include_subscripts=True, include_lookups=True, include_calls=True, include_cses=False, composite_leaves=None): + """ + :arg composite_leaves: Setting this is equivalent to setting + all preceding ``include_*`` flags. + """ if composite_leaves == False: include_subscripts = False diff --git a/pymbolic/mapper/differentiator.py b/pymbolic/mapper/differentiator.py index a6ca688..4b7cedb 100644 --- a/pymbolic/mapper/differentiator.py +++ b/pymbolic/mapper/differentiator.py @@ -58,7 +58,27 @@ def map_math_functions_by_name(i, func, pars): class DifferentiationMapper(pymbolic.mapper.RecursiveMapper): + """Example usage: + + .. doctest:: + + >>> import pymbolic.primitives as p + >>> x = p.Variable("x") + >>> expr = x*(x+5)**3/(x-1)**2 + + >>> from pymbolic.mapper.differentiator import DifferentiationMapper as DM + >>> print DM(x)(expr) + (((x + 5)**3 + x*3*(x + 5)**2)*(x + -1)**2 + (-1)*2*(x + -1)*x*(x + 5)**3) / (x + -1)**2**2 + """ + def __init__(self, variable, func_map=map_math_functions_by_name): + """ + :arg variable: A :class:`pymbolic.primitives.Variable` instance + by which to differentiate. + :arg func_map: A function for computing derivatives of function + calls, signature ``(arg_index, function_variable, parameters)``. + """ + self.variable = variable self.function_map = func_map @@ -86,7 +106,7 @@ class DifferentiationMapper(pymbolic.mapper.RecursiveMapper): def map_product(self, expr): return pymbolic.flattened_sum( pymbolic.flattened_product( - expr.children[0:i] + + expr.children[0:i] + (self.rec(child),) + expr.children[i+1:]) for i, child in enumerate(expr.children)) @@ -143,7 +163,7 @@ class DifferentiationMapper(pymbolic.mapper.RecursiveMapper): return \ Polynomial(expr.base, tuple(deriv_coeff), expr.unit) + \ Polynomial(expr.base, tuple(deriv_base), expr.unit) - + def map_numpy_array(self, expr): import numpy result = numpy.empty(expr.shape, dtype=object) @@ -155,8 +175,8 @@ class DifferentiationMapper(pymbolic.mapper.RecursiveMapper): -def differentiate(expression, - variable, +def differentiate(expression, + variable, func_mapper=map_math_functions_by_name): if not isinstance(variable, (primitives.Variable, primitives.Subscript)): variable = primitives.make_variable(variable) diff --git a/pymbolic/mapper/evaluator.py b/pymbolic/mapper/evaluator.py index da4532f..d745709 100644 --- a/pymbolic/mapper/evaluator.py +++ b/pymbolic/mapper/evaluator.py @@ -34,7 +34,26 @@ class UnknownVariableError(Exception): class EvaluationMapper(RecursiveMapper): + """Example usage: + + .. doctest:: + + >>> import pymbolic.primitives as p + >>> x = p.Variable("x") + + >>> u = 5*x**2 - 3*x + >>> print u + 5*x**2 + (-1)*3*x + + >>> from pymbolic.mapper.evaluator import EvaluationMapper as EM + >>> EM(context={"x": 5})(u) + 110 + """ + def __init__(self, context={}): + """ + :arg context: a mapping from variable names to values + """ self.context = context self.common_subexp_cache = {} diff --git a/pymbolic/mapper/expander.py b/pymbolic/mapper/expander.py index 4129b46..457094c 100644 --- a/pymbolic/mapper/expander.py +++ b/pymbolic/mapper/expander.py @@ -33,6 +33,18 @@ from pymbolic.primitives import \ class ExpandMapper(IdentityMapper): + """Example usage: + + .. doctest:: + + >>> import pymbolic.primitives as p + >>> x = p.Variable("x") + >>> expr = (x+1)**7 + >>> from pymbolic.mapper.expander import ExpandMapper as EM + >>> print EM()(expr) # doctest: +SKIP + 7*x**6 + 21*x**5 + 21*x**2 + 35*x**3 + 1 + 35*x**4 + 7*x + x**7 + """ + def __init__(self, collector=TermCollector()): self.collector = collector diff --git a/pymbolic/mapper/stringifier.py b/pymbolic/mapper/stringifier.py index cf0ed99..18a4d4d 100644 --- a/pymbolic/mapper/stringifier.py +++ b/pymbolic/mapper/stringifier.py @@ -39,8 +39,22 @@ PREC_NONE = 0 -class StringifyMapper(pymbolic.mapper.RecursiveMapper): +class StringifyMapper(pymbolic.mapper.Mapper): + """A mapper to turn an expression tree into a string. + + :class:`pymbolic.primitives.Expression.__str__` is often implemented using + this mapper. + + When it encounters an unsupported :class:`pymbolic.primitives.Expression` + subclass, it calls its :meth:`pymbolic.primitives.Expression.stringifier` + method to get a :class:`StringifyMapper` that potentially does. + """ + def __init__(self, constant_mapper=str): + """ + :arg constant_mapper: A function of a single *expr* argument being used + to map constants into strings. + """ self.constant_mapper = constant_mapper # replaceable string composition interface -------------------------------- @@ -266,13 +280,41 @@ class StringifyMapper(pymbolic.mapper.RecursiveMapper): def __call__(self, expr, prec=PREC_NONE): - return pymbolic.mapper.RecursiveMapper.__call__(self, expr, prec) + """Return a string corresponding to *expr*. If the enclosing + precedence level *prec* is higher than *prec* (see :ref:`prec-constants`), + parenthesize the result. + """ + + return pymbolic.mapper.Mapper.__call__(self, expr, prec) class CSESplittingStringifyMapperMixin(object): + """A :term:`mix-in` for subclasses of + :class:`StringifyMapper` that collects + "variable assignments" for + :class:`pymbolic.primitives.CommonSubexpression` objects. + + .. attribute:: cse_to_name + + A :class:`dict` mapping expressions to CSE variable names. + + .. attribute:: cse_names + + A :class:`set` of names already assigned. + + .. attribute:: cse_name_list + + A :class:`list` of tuples of names and their string representations, + in order of their dependencies. When generating code, walk down these names + in order, and the generated code will never reference + an undefined variable. + + See :class:`pymbolic.mapper.c_code.CCodeMapper` for an example + of the use of this mix-in. + """ def map_common_subexpression(self, expr, enclosing_prec): try: self.cse_to_name diff --git a/pymbolic/primitives.py b/pymbolic/primitives.py index d379c87..b2c7599 100644 --- a/pymbolic/primitives.py +++ b/pymbolic/primitives.py @@ -194,6 +194,10 @@ class Expression(object): return StringifyMapper def __str__(self): + """Use the :meth:`stringifier` to return a human-readable + string representation of *self*. + """ + from pymbolic.mapper.stringifier import PREC_NONE return self.stringifier()()(self, PREC_NONE) @@ -803,6 +807,8 @@ class CommonSubexpression(Expression): .. attribute:: child .. attribute:: prefix + + See :class:`pymbolic.mapper.c_code.CCodeMapper` for an example. """ def __init__(self, child, prefix=None): @@ -1079,6 +1085,11 @@ def wrap_in_cse(expr, prefix=None): def make_common_subexpression(field, prefix=None): + """Wrap *field* in a :class:`CommonSubexpression` with + *prefix*. If *field* is a :mod:`numpy` object array, + each individual entry is instead wrapped. + """ + try: from pytools.obj_array import log_shape except ImportError: @@ -1131,6 +1142,9 @@ def make_sym_vector(name, components): def variables(s): + """Return a list of variables for each (space-delimited) identifier + in *s*. + """ return [Variable(s_i) for s_i in s.split() if s_i] # }}} diff --git a/setup.py b/setup.py index 20d63ff..71f5bda 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,6 @@ setup(name="pymbolic", 'Natural Language :: English', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - # We use conditional expressions, so 2.5 is the bare minimum. 'Programming Language :: Python :: 2.5', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', -- GitLab