Source code for layercake.arithmetic.terms.operations


"""

    Arithmetic operations module
    ============================

    This module defines terms resulting from arithmetic operation being applied to arithmetic terms.
    The corresponding objects hold the symbolic representation of the terms and their decomposition
    on given function basis.

    Description of the classes
    --------------------------

    * :class:`ProductOfTerms`: Product of a list of terms in a partial differential equation.
    * :class:`AdditionOfTerms`: Addition of a list of terms in a partial differential equation.

"""

from layercake.arithmetic.terms.base import OperationOnTerms
from layercake.arithmetic.utils import sproduct, sadd


[docs] class ProductOfTerms(OperationOnTerms): """Term representing the product of multiple arithmetic terms. Parameters ---------- *terms: ArithmeticTerms Arithmetic terms to take the product of. name: str, optional Name of the term. rank: int, optional Can be used to force the rank of the term, i.e. force the rank of the tensor storing the term(s) decomposition on the provided function basis. Compute the rank automatically if not provided. sign: int, optional Sign in front of the term. Either +1 or -1. Default to +1. Attributes ---------- 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. """ def __init__(self, *terms, name='', rank=None, sign=1): OperationOnTerms.__init__(self, *terms, name=name, rank=rank, sign=sign) def _compute_rank(self): """Routine to compute the rank of the inner products tensor, if not enforced by the user. Returns ------- int The rank of the inner products tensor. """ self._rank = 1 for term in self._terms: self._rank += term.rank - 1 def _create_inner_products_basis_list(self, basis): """Function defining the list of symbolic function basis specified in order to compute the inner products for the operation on terms. Must be defined in subclasses. Parameters ---------- basis: SymbolicBasis Basis to put on the left-hand side of the inner products. Returns ------- tuple(SymbolicBasis) List of symbolic function basis used to compute the inner products. """ basis_list = [basis] for term in self._terms: for _ in range(term.rank - 1): basis_list.append(term.field.basis) return tuple(basis_list)
[docs] def operation(self, *terms, evaluate=False): """Operation (here the product) acting on the terms. Parameters ---------- *terms: ArithmeticTerms Terms on which the operation must be applied. evaluate: bool Whether to let |Sympy| evaluate the operation or not. Default to `False`. Returns ------- ~sympy.core.expr.Expr The result of the operation on the terms, as a |Sympy| symbolic expression. """ return sproduct(*terms, evaluate=evaluate)
@property def latex(self): """str: Return a LaTeX representation of the terms.""" if self.sign > 0: s = f'+ ' else: s = f'- ' latexes = [t.latex for t in self.terms] for lat in latexes: if lat[0] == '-': s += f'({lat}) ' else: s += f'{lat[1:]} ' return s
[docs] class AdditionOfTerms(OperationOnTerms): """Term representing the addition of multiple arithmetic terms. Warnings -------- Provided terms must have the same rank, and involve the same field. Parameters ---------- *terms: ArithmeticTerms Arithmetic terms to take the addition of. name: str, optional Name of the term. rank: int, optional Can be used to force the rank of the term, i.e. force the rank of the tensor storing the term(s) decomposition on the provided function basis. Compute the rank automatically if not provided. sign: int, optional Sign in front of the term. Either +1 or -1. Default to +1. Attributes ---------- 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. """ def __init__(self, *terms, name='', rank=None, sign=1): OperationOnTerms.__init__(self, *terms, name=name, rank=rank, sign=sign) for i, term1 in enumerate(terms): for term2 in terms[i:]: if term2.field is not term1.field: raise ValueError(f'AdditionOfTerms must always involve the same field, ' f'but two different fields {term1.field} and {term2.field} have been provided.') @property def symbolic_expression(self): """~sympy.core.expr.Expr: The symbolic expression of the result of the operation on the terms, but as a symbolic functional. Only contains symbols.""" return sproduct(self.sign, self.operation(*map(lambda t: t.symbolic_expression, self._terms))) @property def numerical_expression(self): """~sympy.core.expr.Expr: The numerical expression of the result of the operation on the terms, as a symbolic functional, and with parameters replaced by their numerical value.""" return sproduct(self.sign, self.operation(*map(lambda t: t.numerical_expression, self._terms))) @property def _symbolic_expressions_list(self): return [self.symbolic_expression] @property def _numerical_expressions_list(self): return [self.numerical_expression] @property def _symbolic_functions_list(self): return [self.symbolic_function] @property def _numerical_functions_list(self): return [self.numerical_function] def _compute_rank(self): """Routine to compute the rank of the inner products tensor, if not enforced by the user. Returns ------- int The rank of the inner products tensor. """ self._rank = 0 prank = 0 for i, term in enumerate(self._terms): trank = term.rank if i > 0: if trank != prank: raise ValueError('All the terms provided to AdditionOfTerms term must be of the same rank.') else: self._rank = trank prank = term.rank def _create_inner_products_basis_list(self, basis): """Function defining the list of symbolic function basis specified in order to compute the inner products for the operation on terms. Must be defined in subclasses. Parameters ---------- basis: SymbolicBasis Basis to put on the left-hand side of the inner products. Returns ------- tuple(SymbolicBasis) List of symbolic function basis used to compute the inner products. """ return basis, self._terms[0].field.basis
[docs] def operation(self, *terms, evaluate=False): """Operation (here the addition) acting on the terms. Parameters ---------- *terms: ArithmeticTerms Terms on which the operation must be applied. evaluate: bool Whether to let |Sympy| evaluate the operation or not. Default to `False`. Returns ------- ~sympy.core.expr.Expr The result of the operation on the terms, as a |Sympy| symbolic expression. """ return sadd(*terms, evaluate=evaluate)
@property def latex(self): """str: Return a LaTeX representation of the terms.""" latexes = [t.latex for t in self.terms] s = '' for lat in latexes: s += f'{lat} ' return s