source: coopr.pyomo/trunk/coopr/pyomo/base/intrinsic_functions.py @ 3714

Revision 3714, 5.5 KB checked in by jdsiiro, 3 years ago (diff)

More tweaks of expression generation

  • only clone arguments to intrinsic functions if someone else holds a reference to the argument
  • avoid unnecessary call to as)numeric(_self) when generating operator expressions
Line 
1#  _________________________________________________________________________
2#
3#  Coopr: A COmmon Optimization Python Repository
4#  Copyright (c) 2008 Sandia Corporation.
5#  This software is distributed under the BSD License.
6#  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
7#  the U.S. Government retains certain rights in this software.
8#  For more information, see the Coopr README.txt file.
9#  _________________________________________________________________________
10
11#
12# TODO:  Do we need to have different Expression objects for different
13#        intrinsic expressions?
14# TODO:  If an expression has a fixed value, should we simply call the
15#        intrinsic function with the value of that expression?
16# TODO:  Do we need to register these expression objects?
17#
18
19__all__ = ['log', 'log10', 'sin', 'cos', 'tan', 'cosh', 'sinh', 'tanh',
20           'asin', 'acos', 'atan', 'exp', 'sqrt', 'asinh', 'acosh', 'atanh']
21
22import math
23
24from component import Component
25from expr import _IntrinsicFunctionExpression
26from numvalue import NumericValue, as_numeric
27
28
29def generate_intrinsic_function_expression(args, nargs, name, fcn):
30    def _clone_if_needed(obj):
31        count = sys.getrefcount(obj) - generate_intrinsic_function_expression.UNREFERENCED_EXPR_COUNT
32        if generate_intrinsic_function_expression.clone_if_needed_callback:
33            generate_intrinsic_function_expression.clone_if_needed_callback(count)
34        if count == 0:
35            return obj
36        elif count > 0:
37            generate_intrinsic_function_expression.clone_counter += 1
38            return obj.clone()
39        else:
40            raise RuntimeError, "Expression entered generate_expression() " \
41                "with too few references (%s<0); this is indicative of a " \
42                "SERIOUS ERROR in the expression reuse detection scheme." \
43                % ( count, )
44
45    # Special handling: if there are no Pyomo Modeling Objects in the
46    # argument list, then evaluate the expression and return the result.
47    pyomo_expression = False
48    for arg in args:
49        # FIXME: does anyone know why we also test for 'Component' here? [JDS]
50        if isinstance(arg, NumericValue) or isinstance(arg, Component):
51            # TODO: efficiency: we already know this is a NumericValue -
52            # so we should be able to avoid the call to as_numeric()
53            # below (expecially since most intrinsic functions are unary
54            # operators.
55            pyomo_expression = True
56            break
57    if not pyomo_expression:
58        return fcn(*args)
59
60    new_args = []
61    for arg in args:
62        new_arg = as_numeric(arg)
63        if new_arg.is_expression():
64            new_arg = _clone_if_needed(new_arg)
65        elif new_arg.is_indexed():
66            raise ValueError, "Argument for intrinsic function '%s' is an "\
67                "n-ary numeric value: %s\n    Have you given variable or "\
68                "parameter '%s' an index?" % (name, new_arg.name, new_arg.name)
69        new_args.append(arg)
70    return _IntrinsicFunctionExpression(name, nargs, tuple(new_args), fcn)
71
72# [testing] clone_check_callback allows test functions to intercept the
73# call to _clone_if_needed and see the returned value from
74# sys.getrefcount.
75generate_intrinsic_function_expression.clone_if_needed_callback = None
76
77# [debugging] clone_counter is a count of the number of calls to
78# expr.clone() made during expression generation.
79generate_intrinsic_function_expression.clone_counter = 0
80
81# [configuration] UNREFERENCED_EXPR_COUNT is a "magic number" that
82# indicates the stack depth between "normal" modeling and
83# _clone_if_needed().  If an expression enters _clone_if_needed() with
84# UNREFERENCED_EXPR_COUNT references, then there are no other variables
85# that hold a reference to the expression and cloning is not necessary.
86# If there are more references than UNREFERENCED_EXPR_COUNT, then we
87# must clone the expression before operating on it.  It should be an
88# error to hit _clone_if_needed() with fewer than
89# UNREFERENCED_EXPR_COUNT references.
90generate_intrinsic_function_expression.UNREFERENCED_EXPR_COUNT = 7
91
92
93def log(*args):
94    return generate_intrinsic_function_expression(args, 1, 'log', math.log)
95
96def log10(*args):
97    return generate_intrinsic_function_expression(args, 1, 'log10', math.log10)
98
99def sin(*args):
100    return generate_intrinsic_function_expression(args, 1, 'sin', math.sin)
101
102def cos(*args):
103    return generate_intrinsic_function_expression(args, 1, 'cos', math.cos)
104
105def tan(*args):
106    return generate_intrinsic_function_expression(args, 1, 'tan', math.tan)
107
108def sinh(*args):
109    return generate_intrinsic_function_expression(args, 1, 'sinh', math.sinh)
110
111def cosh(*args):
112    return generate_intrinsic_function_expression(args, 1, 'cosh', math.cosh)
113
114def tanh(*args):
115    return generate_intrinsic_function_expression(args, 1, 'tanh', math.tanh)
116
117def asin(*args):
118    return generate_intrinsic_function_expression(args, 1, 'asin', math.asin)
119
120def acos(*args):
121    return generate_intrinsic_function_expression(args, 1, 'acos', math.acos)
122
123def atan(*args):
124    return generate_intrinsic_function_expression(args, 1, 'atan', math.atan)
125
126def exp(*args):
127    return generate_intrinsic_function_expression(args, 1, 'exp', math.exp)
128
129def sqrt(*args):
130    return generate_intrinsic_function_expression(args, 1, 'sqrt', math.sqrt)
131
132def asinh(*args):
133    return generate_intrinsic_function_expression(args, 1, 'asinh', math.asinh)
134
135def acosh(*args):
136    return generate_intrinsic_function_expression(args, 1, 'acosh', math.acosh)
137
138def atanh(*args):
139    return generate_intrinsic_function_expression(args, 1, 'atanh', math.atanh)
Note: See TracBrowser for help on using the repository browser.