def product(*args, **kwargs): """Product of multiple :py:class:`MonomialSum`s""" rename_map = kwargs.pop('rename_map', None) if rename_map is None: rename_map = make_rename_map() if kwargs: raise ValueError("Unrecognised keyword argument: " + kwargs.pop()) result = MonomialSum() for monomials in product(*args): renamer = make_renamer(rename_map) sum_indices = [] atomics = [] rest = one for s, a, r in monomials: s_, applier = renamer(s) sum_indices.extend(s_) atomics.extend(map(applier, a)) rest = Product(applier(r), rest) result.add(sum_indices, atomics, rest) return result
def collect_monomials(expressions, classifier): """Refactorises expressions into a sum-of-products form, using distributivity rules (i.e. a*(b + c) -> a*b + a*c). Expansion proceeds until all "compound" expressions are broken up. :arg expressions: GEM expressions to refactorise :arg classifier: a function that can classify any GEM expression as ``ATOMIC``, ``COMPOUND``, or ``OTHER``. This classification drives the factorisation. :returns: list of :py:class:`MonomialSum`s :raises FactorisationError: Failed to break up some "compound" expressions with expansion. """ # Get ComponentTensors out of the way expressions = remove_componenttensors(expressions) # Get ListTensors out of the way must_unroll = [] # indices to unroll for node in traversal(expressions): if isinstance(node, Indexed): child, = node.children if isinstance(child, ListTensor) and classifier(node) == COMPOUND: must_unroll.extend(node.multiindex) if must_unroll: must_unroll = set(must_unroll) expressions = unroll_indexsum(expressions, predicate=lambda i: i in must_unroll) expressions = remove_componenttensors(expressions) # Expand Conditional nodes which are COMPOUND conditional_predicate = lambda node: classifier(node) == COMPOUND expressions = expand_conditional(expressions, conditional_predicate) # Finally, refactorise expressions mapper = Memoizer(_collect_monomials) mapper.classifier = classifier mapper.rename_map = make_rename_map() return list(map(mapper, expressions))