Page MenuHomec4science

expression_evaluator.py
No OneTemporary

File Metadata

Created
Thu, May 16, 16:35

expression_evaluator.py

# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see <http://www.gnu.org/licenses/>.
#
from numbers import Number
import numpy as np
from copy import deepcopy as copy
from .keys import (expressionKeysUnary, expressionKeysUnaryParam,
expressionKeysBinary, expressionKeysBinaryParam)
from rrompy.utilities.base.types import Tuple, TupleAny, paramList
from rrompy.utilities.exception_manager import RROMPyException
from rrompy.sampling.sample_list import sampleList
from rrompy.parameter.parameter_list import parameterList, checkParameterList
__all__ = ["expressionEvaluator"]
def manageNotExpression(expr):
if isinstance(expr, (str,)) and expr == "x": return None
elif isinstance(expr, (Number,)): return expr
elif isinstance(expr, (parameterList, sampleList)): return expr.data
else:
try:
return np.array(expr)
except:
raise RROMPyException(("Expression '{}' not "
"recognized.").format(expr)) from None
def expressionEvaluator(expr:TupleAny, x:paramList,
force_shape : Tuple[int] = None):
"""
Evaluate expression object by plugging in values of x. An expression object
is a tuple containing numbers and/or keywords representing variables
and operands.
Examples:
* exp(-.5*x_0) -> ('exp', ('x', '()', 0, '*', -.5))
* -x_1+x_1^2*x_0 -> ('x', '()', 1, '*', -1., '+', ('x', '()', 1),
'**', 2., '*', ('x', '()', 0))
* 10^(prod(x^2)) -> (10., "**", ("prod", {"axis" : 1},
("data", "x", "**", 2)))
"""
if not isinstance(x, (parameterList,)): x = checkParameterList(x)
exprSimp = [None] * len(expr)
for j, ex in enumerate(expr):
if isinstance(ex, (tuple,)):
exprSimp[j] = expressionEvaluator(ex, x)
else:
exprSimp[j] = ex
z, zc = None, None
pile, pilePars = [], []
j = -1
while j + 1 < len(exprSimp):
j += 1
ex = exprSimp[j]
if not isinstance(ex, (np.ndarray, parameterList, list, tuple,)):
if ex in expressionKeysUnary.keys():
pile = pile + [ex]
pilePars = pilePars + [None]
continue
if ex in expressionKeysUnaryParam.keys():
pile = pile + [ex]
j += 1
if j >= len(exprSimp) or not isinstance(exprSimp[j], (dict,)):
raise RROMPyException(("Parameters missing for unary "
"operand '{}'.").format(ex))
pilePars = pilePars + [exprSimp[j]]
continue
if ex in expressionKeysBinary.keys():
if len(pile) > 0 or z is None or zc is not None:
raise RROMPyException(("Binary operand '{}' must follow "
"numerical expression.").format(ex))
zc = copy(z)
pile = pile + [ex]
pilePars = pilePars + [None]
continue
if ex in expressionKeysBinaryParam.keys():
if len(pile) > 0 or z is None or zc is not None:
raise RROMPyException(("Binary operand '{}' must follow "
"numerical expression.").format(ex))
zc = copy(z)
pile = pile + [ex]
j += 1
if j >= len(exprSimp) or not isinstance(exprSimp[j], (dict,)):
raise RROMPyException(("Parameters missing for binary "
"operand '{}'.").format(ex))
pilePars = pilePars + [exprSimp[j]]
continue
z = manageNotExpression(ex)
if z is None: z = checkParameterList(x, return_data = True)
if len(pile) > 0:
for pl, plp in zip(pile[::-1], pilePars[::-1]):
if zc is None:
if plp is None:
z = expressionKeysUnary[pl](z)
else:
z = expressionKeysUnaryParam[pl](z, plp)
else:
if plp is None:
z = expressionKeysBinary[pl](zc, z)
else:
z = expressionKeysBinaryParam[pl](zc, z, plp)
zc, pile, pilePars = None, [], []
if len(pile) > 0:
raise RROMPyException(("Missing numerical expression feeding into "
"'{}'.").format(pile[-1]))
if force_shape is not None:
if hasattr(z, "__len__") and len(z) > 1:
if isinstance(z, (parameterList, sampleList)): z = z.data
if isinstance(z, (list, tuple,)): z = np.array(z)
if z.size == np.prod(force_shape):
z = np.reshape(z, force_shape)
else:
zdim = len(z.shape)
if z.shape != force_shape[: zdim]:
raise RROMPyException(("Error in reshaping result: shapes "
"{} and {} not compatible.").format(
z.shape, force_shape))
else:
z = np.tile(z, [1] * zdim + force_shape[zdim :])
else:
if hasattr(z, "__len__"): z = z[0]
z = z * np.ones(force_shape)
return z

Event Timeline