Unverified Commit a9b503aa authored by Stephan Rave's avatar Stephan Rave Committed by GitHub

Merge pull request #1098 from pymor/expand

Add algorithm to expand concatenations of LincombOperators
parents 79daf7a7 d33e6fa5
Pipeline #65170 passed with stages
in 58 minutes and 36 seconds
......@@ -92,6 +92,7 @@ common = '''
.. |EmpiricalInterpolatedOperator| replace:: :class:`~pymor.operators.ei.EmpiricalInterpolatedOperator`
.. |EmpiricalInterpolatedOperators| replace:: :class:`EmpiricalInterpolatedOperators <pymor.operators.ei.EmpiricalInterpolatedOperator>`
.. |ConcatenationOperator| replace:: :class:`~pymor.operators.constructions.ConcatenationOperator`
.. |ConcatenationOperators| replace:: :class:`ConcatenationOperators <pymor.operators.constructions.ConcatenationOperator>`
.. |NumpyVectorSpace| replace:: :func:`~pymor.vectorarrays.numpy.NumpyVectorSpace`
.. |NumpyVectorSpaces| replace:: :func:`NumpyVectorSpaces <pymor.vectorarrays.numpy.NumpyVectorSpace>`
......
# 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
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)
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)
......@@ -20,6 +20,7 @@ from pymor.algorithms.newton import newton
from pymor.algorithms.pod import pod
from pymor.algorithms.preassemble import preassemble
from pymor.algorithms.projection import project, project_to_subbasis
from pymor.algorithms.simplify import expand
from pymor.analyticalproblems.burgers import burgers_problem, burgers_problem_2d
from pymor.analyticalproblems.domaindescriptions import (RectDomain, CylindricalDomain, TorusDomain, LineDomain,
......
# 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 itertools import product
import numpy as np
from pymor.algorithms.to_matrix import to_matrix
from pymor.algorithms.simplify import expand
from pymor.operators.constructions import LincombOperator, ConcatenationOperator
from pymor.operators.numpy import NumpyMatrixOperator
from pymor.parameters.functionals import ProjectionParameterFunctional
def test_expand():
ops = [NumpyMatrixOperator(np.eye(1) * i) for i in range(8)]
pfs = [ProjectionParameterFunctional('p', 9, i) for i in range(8)]
prods = [o * p for o, p in zip(ops, pfs)]
op = ((prods[0] + prods[1] + prods[2]) @ (prods[3] + prods[4] + prods[5]) @
(prods[6] + prods[7]))
eop = expand(op)
assert isinstance(eop, LincombOperator)
assert len(eop.operators) == 3 * 3 * 2
assert all(isinstance(o, ConcatenationOperator) and len(o.operators) == 3
for o in eop.operators)
assert ({to_matrix(o)[0, 0] for o in eop.operators}
== {i0 * i1 * i2 for i0, i1, i2 in product([0, 1, 2], [3, 4, 5], [6, 7])})
assert ({frozenset(p.index for p in pf.factors) for pf in eop.coefficients}
== {frozenset([i0, i1, i2]) for i0, i1, i2 in product([0, 1, 2], [3, 4, 5], [6, 7])})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment