Source code for pymor.algorithms.simplify
# This file is part of the pyMOR project (http://www.pymor.org).
# Copyright 2013-2020 pyMOR developers and contributors. All rights reserved.
# License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
from pymor.algorithms.rules import RuleTable, match_class
from pymor.models.interface import Model
from pymor.operators.constructions import LincombOperator, ConcatenationOperator
from pymor.operators.interface import Operator
[docs]def expand(obj):
"""Expand concatenations of LincombOperators.
To any given |Operator| or |Model|, the following
transformations are applied recursively:
- :class:`Concatenations <pymor.operators.constructions.ConcatenationOperator>`
of |LincombOperators| are expanded. E.g. ::
(O1 + O2) @ (O3 + O4)
becomes::
O1 @ O3 + O1 @ O4 + O2 @ O3 + O2 @ O4
- |LincombOperators| inside |LincombOperators| are merged into a single
|LincombOperator|
- |ConcatenationOperators| inside |ConcatenationOperators| are merged into a
single |ConcatenationOperator|.
Parameters
----------
obj
Either a |Model| or an |Operator| to which the expansion rules are
applied recursively for all :meth:`children <pymor.algorithms.rules.RuleTable.get_children>`.
Returns
-------
The transformed object.
"""
return ExpandRules().apply(obj)
[docs]class ExpandRules(RuleTable):
def __init__(self):
super().__init__(use_caching=True)
@match_class(LincombOperator)
def action_LincombOperator(self, op):
# recursively expand all children
op = self.replace_children(op)
# merge child LincombOperators
if any(isinstance(o, LincombOperator) for o in op.operators):
ops, coeffs = [], []
for c, o in zip(op.coefficients, op.operators):
if isinstance(o, LincombOperator):
coeffs.extend(c * cc for cc in o.coefficients)
ops.extend(o.operators)
else:
coeffs.append(c)
ops.append(o)
op = op.with_(operators=ops, coefficients=coeffs)
return op
@match_class(ConcatenationOperator)
def action_ConcatenationOperator(self, op):
op = self.replace_children(op)
# merge child ConcatenationOperators
if any(isinstance(o, ConcatenationOperator) for o in op.operators):
ops = []
for o in ops:
if isinstance(o, ConcatenationOperator):
ops.extend(o.operators)
else:
ops.append(o)
op = op.with_operators(ops)
# expand concatenations with LincombOperators
if any(isinstance(o, LincombOperator) for o in op.operators):
i = next(iter(i for i, o in enumerate(op.operators) if isinstance(o, LincombOperator)))
left, right = op.operators[:i], op.operators[i+1:]
ops = [ConcatenationOperator(left + (o,) + right) for o in op.operators[i].operators]
op = op.operators[i].with_(operators=ops)
# there can still be LincombOperators within the summands so we recurse ..
op = self.apply(op)
return op
@match_class(Model, Operator)
def action_recurse(self, op):
return self.replace_children(op)