SIMO optimization postfix evaluator
Class for evaluating expressions in postfix stacks:
>>> epsilon = 0.00001
>>> from simo.optimization.tools.postfixeval import PostfixEvaluator
>>> evaluator = PostfixEvaluator()
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.]