.. _postfixeval-py: ############## postfixeval.py ############## SIMO optimization postfix evaluator *********************** class PostfixEvaluator: *********************** Class for evaluating expressions in postfix stacks:: >>> epsilon = 0.00001 >>> from simo.optimization.tools.postfixeval import PostfixEvaluator >>> evaluator = PostfixEvaluator() def evaluate(self, postfx, opindx, data, exprtype, exprind, dims=3, aggr_over_units=True): =================================================================== Evaluate postfix expression, where postfx is the expression in Reverse Polish Notation (RPN) or postfix form. postfx -- expression in postfix form opindx -- unique operand index data -- unique operand data in a dict exprtype -- expression type, either 's' for subobjective or 'c' for constraint exprind -- expression indice dims -- data dimensions, int aggr_over_units -- aggregate over units, boolean Operand index contains the indices of unique subobjective or constraint operands:: >>> operand_index = {0: 0, ... 1: 1, ... 2: 2} Value arrays in the data dictionary can be arrays of two possible shapes: either the multiple data values of multiple objects (dims == 3) or single date for single object (dims == 1). When the values are for a single date of single unit (dims=1), the values are always in a simple list :: >>> values1 = [1.0, 2.0] When values contain the values of multiple dates for multiple objects, the data dimensions are: (object, branch, timestep) :: >>> import numpy >>> nan = numpy.NaN >>> values2 = {0: numpy.array([[[nan, nan, nan, 1]], ... [[nan, nan, nan, 2]], ... [[nan, nan, nan, 3]]], ... dtype=float), ... 1: numpy.array([[[nan, nan, nan, 1]], ... [[nan, nan, nan, 2]], ... [[nan, nan, nan, 3]]], ... dtype=float)} Unit range is a numpy integer array with range [0..n[ :: >>> unitrange = numpy.array([0,1,2], dtype=int) Branch can be a 1D numpy array of size n or an integer scalar :: >>> branch = numpy.array([0], dtype=int) Date locations index is a mapping from operand indice to date locations in the data arrays :: >>> dlocs = {0: (0, 3), 1: (0, 3), 2: (0, 3)} Evaluate: sum(X + Y), one dimension :: >>> postfix = [('data', 'X', (0, 0)), ... ('data', 'Y', (1, 1)), ... ('ari', (lambda a,b: a + b),)] >>> x = evaluator.evaluate(unitrange, branch, postfix, dlocs, values1, 1) >>> abs(x - 3) < epsilon True Evaluate: sum(X / Y), one dimension :: >>> postfix = [('data', 'X', (0, 0)), ... ('data', 'Y', (1, 1)), ... ('ari', (lambda a,b: a / b),)] >>> x = evaluator.evaluate(unitrange, branch, postfix, dlocs, values1, 1) >>> abs(x - 0.5) < epsilon True Evaluate: sum(X + Y), three dimensions :: >>> postfix = [('data', 'X', (0, 0)), ... ('data', 'Y', (1, 1)), ... ('ari', (lambda a,b: a + b),), ... ('aggr', numpy.nansum,)] >>> x = evaluator.evaluate(unitrange, branch, postfix, dlocs, values2) >>> abs(x - 12) < epsilon True Evaluate: sum(X * Y) :: >>> postfix = [('data', 'X', (0, 0)), ... ('data', 'Y', (1, 1)), ... ('ari', (lambda a,b: a * b),), ... ('aggr', numpy.nansum,)] >>> x = evaluator.evaluate(unitrange, branch, postfix, dlocs, values2) >>> abs(x - 14) < epsilon True Evaluate: sum(X) == sum(Y) :: >>> postfix = [('data', 'X', (0, 0)), ... ('aggr', numpy.nansum,), ... ('data', 'Y', (1, 1)), ... ('aggr', numpy.nansum,), ... ('eq', (lambda a,b: a == b),)] >>> evaluator.evaluate(unitrange, branch, postfix, dlocs, values2) True Evaluate: sum(X + Y / Z) :: >>> values3 = {0: numpy.array([[[nan, nan, nan, 1]], ... [[nan, nan, nan, 2]], ... [[nan, nan, nan, 3]]], ... dtype=float), ... 1: numpy.array([[[nan, nan, nan, 1]], ... [[nan, nan, nan, 2]], ... [[nan, nan, nan, 3]]], ... dtype=float), ... 2: numpy.array([[[nan, nan, nan, 2]], ... [[nan, nan, nan, 5]], ... [[nan, nan, nan, 10]]], ... dtype=float)} >>> postfix = [('data', 'X', (0, 0)), ... ('data', 'Y', (1, 1)), ... ('data', 'Z', (2, 2)), ... ('ari', (lambda a,b: a / b)), ... ('ari', (lambda a,b: a + b)), ... ('aggr', numpy.nansum,)] >>> x = evaluator.evaluate(unitrange, branch, postfix, dlocs, values3) >>> abs(x - 7.2) < epsilon True Evaluate: sum(X * Y / Z) :: >>> postfix = [('data', 'X', (0, 0)), ... ('data', 'Y', (1, 1)), ... ('ari', (lambda a,b: a * b)), ... ('data', 'Z', (2, 2)), ... ('ari', (lambda a,b: a / b)), ... ('aggr', numpy.nansum,)] >>> x = evaluator.evaluate(unitrange, branch, postfix, dlocs, values3) >>> abs(x - 2.2) < epsilon True Evaluate: sum(X + Y) < sum(Z) :: >>> postfix = [('data', 'X', (0, 0)), ... ('data', 'Y', (1, 1)), ... ('ari', (lambda a,b: a + b)), ... ('aggr', numpy.nansum,), ... ('data', 'Z', (2, 2)), ... ('aggr', numpy.nansum,), ... ('eq', (lambda a,b: a < b))] >>> evaluator.evaluate(unitrange, branch, postfix, dlocs, values3) True Evaluate: sum(X + Y) < sum(Z) :: >>> postfix = [('data', 'X', (0, 0)), ... ('data', 'Y', (1, 1)), ... ('ari', (lambda a,b: a + b)), ... ('aggr', numpy.nansum,), ... ('data', 'Z', (2, 2)), ... ('aggr', numpy.nansum,), ... ('eq', (lambda a,b: a < b))] >>> evaluator.evaluate(unitrange, branch, postfix, dlocs, values3) True Evaluate: (sum(X) / sum(Y)) == 1.0 :: >>> postfix = [('data', 'X', (0, 0)), ... ('aggr', numpy.nansum,), ... ('data', 'Y', (1, 1)), ... ('aggr', numpy.nansum,), ... ('ari', (lambda a,b: a / b)), ... ('value', 1.0,), ... ('eq', (lambda a,b: a == b))] >>> evaluator.evaluate(unitrange, branch, postfix, dlocs, values3) True Evaluate: sum((X + Y) * X) == 84.0 :: >>> postfix = [('data', 'X', (0, 0)), ... ('data', 'Y', (1, 1)), ... ('ari', (lambda a,b: a + b)), ... ('data', 'Z', (2, 2)), ... ('ari', (lambda a,b: a * b)), ... ('aggr', numpy.nansum,), ... ('value', 84.0), ... ('eq', (lambda a,b: a == b))] >>> evaluator.evaluate(unitrange, branch, postfix, dlocs, values3) True Evaluate: sum(X * Y), but at single unit level (no aggregation over units) :: >>> postfix = [('data', 'X', (0, 0)), ... ('data', 'Y', (1, 1)), ... ('ari', (lambda a,b: a * b),), ... ('aggr', numpy.nansum,)] >>> x = evaluator.evaluate(unitrange, branch, postfix, dlocs, values2, aggr_over_units=False) >>> print x [ 1. 4. 9.] Evaluate: sum(X * Y), but at single unit level (no aggregation over units) and missing date locs :: >>> dlocs = {0: (None, None), 1: (None, None), 2: (None, None)} >>> postfix = [('data', 'X', (0, 0)), ... ('data', 'Y', (1, 1)), ... ('ari', (lambda a,b: a * b),), ... ('aggr', numpy.nansum,)] >>> x = evaluator.evaluate(unitrange, branch, postfix, dlocs, values2, aggr_over_units=False) >>> print x [ 0. 0. 0.]