"""
Operator arithmetic term definition module
=================================================
This module defines operator terms in partial differential equations.
The corresponding objects hold the symbolic representation of the terms and their decomposition
on given function basis.
Description of the classes
--------------------------
* :class:`OperatorTerm`: Operator term in a partial differential equation, acting on fields of the equation.
* :class:`ComposedOperatorsTerm`: Term representing the composition of multiple operators, acting on fields of the equation.
"""
from layercake.arithmetic.terms.base import SingleArithmeticTerm
from layercake.arithmetic.utils import sproduct
[docs]
class OperatorTerm(SingleArithmeticTerm):
"""Operator term in a partial differential equation, acting on fields of the equation,
and of the form :math:`\\pm \\, a \\, H \\psi(u_1, u_2)`, where :math:`H` is the operator,
:math:`u_1, u_2` are the coordinates of the model, :math:`a` is a prefactor,
and :math:`\\psi` is a field of the equation.
Parameters
----------
field: ~field.Field or ~field.ParameterField
A field appearing in the partial differential equation, and on which the
operator acts.
operator: object
Object or function returning the action of the operator on symbolic Sympy expressions.
Must also have a `latex` attribute.
operator_args: tuple
Tuple of arguments to pass to the `operator` object or function.
inner_product_definition: InnerProductDefinition, optional
Object defining the integral representation of the inner product that is used
to compute the term representation on a given function basis.
If not provided, it will use the inner product definition found in the `field` object.
Default to using the inner product definition found in the `field` object.
prefactor: ~parameter.Parameter or ~expressions.Expression, optional
Prefactor in front of the operator.
Must be specified as a model parameter or a symbolic expression.
name: str, optional
Name of the term.
sign: int, optional
Sign in front of the term. Either +1 or -1.
Default to +1.
Attributes
----------
field: ~field.Field or ~field.ParameterField
The field appearing in the partial differential equation, and on which the
operator acts.
name: str
Name of the term.
sign: int
Sign in front of the term. Either +1 or -1.
inner_products: None or ~sympy.matrices.immutable.ImmutableSparseMatrix or ~sympy.tensor.array.ImmutableSparseNDimArray or sparse.COO(float)
The inner products tensor of the term.
Set initially to `None` (not computed).
inner_product_definition: InnerProductDefinition
Object defining the integral representation of the inner product that is used to compute the term representation on a given function basis.
prefactor: ~parameter.Parameter or ~expressions.Expression
Prefactor in front of the operator.
"""
def __init__(self, field, operator, operator_args, inner_product_definition=None, prefactor=None, name='', sign=1):
SingleArithmeticTerm.__init__(self, field, inner_product_definition, prefactor, name, sign=sign)
if not isinstance(operator_args, (tuple, list)):
operator_args = tuple([operator_args])
self._operator = operator(*operator_args)
@property
def symbolic_expression(self):
"""~sympy.core.expr.Expr: The symbolic expression of the operator. Only contains symbols."""
if self.prefactor is None:
return sproduct(self.sign * self._operator, self.field.symbol)
else:
return sproduct(self.sign * self.prefactor.symbol, self._operator, self.field.symbol)
@property
def numerical_expression(self):
"""~sympy.core.expr.Expr: The numeric expression of the operator, with parameters replaced by their numerical value."""
if self.prefactor is None:
return sproduct(self.sign * self._operator, self.field.symbol)
else:
if hasattr(self.prefactor, 'numerical_expression'):
return sproduct(self.sign * self.prefactor.numerical_expression, self._operator, self.field.symbol)
else:
return sproduct(self.sign * self.prefactor, self._operator, self.field.symbol)
@property
def latex(self):
"""str: Return a LaTeX representation of the operator."""
if self.sign > 0:
s = f'+ '
else:
s = f'- '
op = f'{self._operator.latex} {self.field.latex} '
if self.prefactor is None:
return s + op
if hasattr(self.prefactor, 'latex'):
if self.prefactor.latex is not None:
s += f'{self.prefactor.latex} '
return s + op
if hasattr(self.prefactor, 'symbol'):
if self.prefactor.symbol is not None:
s += f'{self.prefactor.symbol} '
return s + op
s += f'{self.prefactor} '
return s + op
[docs]
class ComposedOperatorsTerm(SingleArithmeticTerm):
"""Term representing the composition :math:`\\circ` of multiple operators :math:`H_i`
acting on fields of a partial differential equation, and of the form
:math:`\\pm \\, a \\, H_1 \\circ H_2 \\ldots \\circ H_n \\psi(u_1, u_2)`, where :math:`u_1, u_2` are
the coordinates of the model, :math:`a` is a prefactor, and :math:`\\psi` is a field of the equation.
Parameters
----------
field: ~field.Field or ~field.ParameterField
A field appearing in the partial differential equation, and on which the composed
operators act.
operators: list(object)
List of objects or functions returning the action of the operator on symbolic Sympy expressions.
Each component of the list must also have a `latex` attribute.
operators_args: list(tuple)
Tuples of arguments to pass to the `operators` objects or functions, one tuple per operator.
inner_product_definition: InnerProductDefinition, optional
Object defining the integral representation of the inner product that is used
to compute the term representation on a given function basis.
If not provided, it will use the inner product definition found in the `field` object.
Default to using the inner product definition found in the `field` object.
prefactor: ~parameter.Parameter or ~expressions.Expression, optional
Prefactor in front of the operator.
Must be specified as a model parameter or a symbolic expression.
name: str, optional
Name of the term.
sign: int, optional
Sign in front of the term. Either +1 or -1.
Default to +1.
Attributes
----------
field: ~field.Field or ~field.ParameterField
The field appearing in the partial differential equation, and on which the
operator acts.
name: str
Name of the term.
sign: int
Sign in front of the term. Either +1 or -1.
inner_products: None or ~sympy.matrices.immutable.ImmutableSparseMatrix or ~sympy.tensor.array.ImmutableSparseNDimArray or sparse.COO(float)
The inner products tensor of the term.
Set initially to `None` (not computed).
inner_product_definition: InnerProductDefinition
Object defining the integral representation of the inner product that is used to compute
the term representation on a given function basis.
prefactor: ~parameter.Parameter or ~expressions.Expression
Prefactor in front of the operator.
"""
def __init__(self, field, operators, operators_args, inner_product_definition=None, prefactor=None, name='', sign=1):
if len(operators_args) != len(operators):
raise ValueError('Too many or too few operators arguments provided')
SingleArithmeticTerm.__init__(self, field, inner_product_definition, prefactor, name, sign)
self.prefactor = prefactor
self._operators = list()
for op, args in zip(operators, operators_args):
if not isinstance(args, (tuple, list)):
args = tuple([args])
self._operators.append(op(*args))
@property
def symbolic_expression(self):
"""~sympy.core.expr.Expr: The symbolic expression of the operators. Only contains symbols."""
expr = sproduct(*self._operators)
expr = sproduct(expr, self.field.symbol)
if self.prefactor is not None:
expr = sproduct(self.prefactor.symbol, expr)
return sproduct(self.sign, expr)
@property
def numerical_expression(self):
"""~sympy.core.expr.Expr: The numeric expression of the operators, with parameters replaced by their numerical value."""
expr = sproduct(*self._operators)
expr = sproduct(expr, self.field.symbol)
if self.prefactor is not None:
if hasattr(self.prefactor, 'numerical_expression'):
expr = sproduct(self.prefactor.numerical_expression, expr)
else:
expr = sproduct(self.prefactor, expr)
return sproduct(self.sign, expr)
@property
def latex(self):
"""str: Return a LaTeX representation of the operators."""
if self.sign > 0:
s = f'+ '
else:
s = f'- '
op = f'{self.field.latex} '
for oper in self._operators[::-1]:
op = f'{oper.latex} ' + op
if self.prefactor is None:
return s + op
if hasattr(self.prefactor, 'latex'):
if self.prefactor.latex is not None:
s += f'{self.prefactor.latex} '
return s + op
if hasattr(self.prefactor, 'symbol'):
if self.prefactor.symbol is not None:
s += f'{self.prefactor.symbol} '
return s + op
s += f'{self.prefactor} '
return s + op